urlmon: Implemented IUri_GetDomain.
[wine] / dlls / urlmon / uri.c
1 /*
2  * Copyright 2010 Jacek Caban for CodeWeavers
3  * Copyright 2010 Thomas Mullaly
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include "urlmon_main.h"
21 #include "wine/debug.h"
22
23 #define NO_SHLWAPI_REG
24 #include "shlwapi.h"
25
26 #define UINT_MAX 0xffffffff
27 #define USHORT_MAX 0xffff
28
29 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
30
31 typedef struct {
32     const IUriVtbl  *lpIUriVtbl;
33     LONG ref;
34
35     BSTR            raw_uri;
36
37     /* Information about the canonicalized URI's buffer. */
38     WCHAR           *canon_uri;
39     DWORD           canon_size;
40     DWORD           canon_len;
41
42     INT             scheme_start;
43     DWORD           scheme_len;
44     URL_SCHEME      scheme_type;
45
46     INT             userinfo_start;
47     DWORD           userinfo_len;
48     INT             userinfo_split;
49
50     INT             host_start;
51     DWORD           host_len;
52     Uri_HOST_TYPE   host_type;
53
54     USHORT          port;
55     BOOL            has_port;
56
57     INT             authority_start;
58     DWORD           authority_len;
59
60     INT             domain_offset;
61 } Uri;
62
63 typedef struct {
64     const IUriBuilderVtbl  *lpIUriBuilderVtbl;
65     LONG ref;
66 } UriBuilder;
67
68 typedef struct {
69     const WCHAR *str;
70     DWORD       len;
71 } h16;
72
73 typedef struct {
74     /* IPv6 addresses can hold up to 8 h16 components. */
75     h16         components[8];
76     DWORD       h16_count;
77
78     /* An IPv6 can have 1 elision ("::"). */
79     const WCHAR *elision;
80
81     /* An IPv6 can contain 1 IPv4 address as the last 32bits of the address. */
82     const WCHAR *ipv4;
83     DWORD       ipv4_len;
84
85     INT         components_size;
86     INT         elision_size;
87 } ipv6_address;
88
89 typedef struct {
90     BSTR            uri;
91
92     BOOL            is_relative;
93     BOOL            is_opaque;
94     BOOL            has_implicit_scheme;
95     BOOL            has_implicit_ip;
96     UINT            implicit_ipv4;
97
98     const WCHAR     *scheme;
99     DWORD           scheme_len;
100     URL_SCHEME      scheme_type;
101
102     const WCHAR     *userinfo;
103     DWORD           userinfo_len;
104     INT             userinfo_split;
105
106     const WCHAR     *host;
107     DWORD           host_len;
108     Uri_HOST_TYPE   host_type;
109
110     BOOL            has_ipv6;
111     ipv6_address    ipv6_address;
112
113     const WCHAR     *port;
114     DWORD           port_len;
115     USHORT          port_value;
116 } parse_data;
117
118 static const CHAR hexDigits[] = "0123456789ABCDEF";
119
120 /* List of scheme types/scheme names that are recognized by the IUri interface as of IE 7. */
121 static const struct {
122     URL_SCHEME  scheme;
123     WCHAR       scheme_name[16];
124 } recognized_schemes[] = {
125     {URL_SCHEME_FTP,            {'f','t','p',0}},
126     {URL_SCHEME_HTTP,           {'h','t','t','p',0}},
127     {URL_SCHEME_GOPHER,         {'g','o','p','h','e','r',0}},
128     {URL_SCHEME_MAILTO,         {'m','a','i','l','t','o',0}},
129     {URL_SCHEME_NEWS,           {'n','e','w','s',0}},
130     {URL_SCHEME_NNTP,           {'n','n','t','p',0}},
131     {URL_SCHEME_TELNET,         {'t','e','l','n','e','t',0}},
132     {URL_SCHEME_WAIS,           {'w','a','i','s',0}},
133     {URL_SCHEME_FILE,           {'f','i','l','e',0}},
134     {URL_SCHEME_MK,             {'m','k',0}},
135     {URL_SCHEME_HTTPS,          {'h','t','t','p','s',0}},
136     {URL_SCHEME_SHELL,          {'s','h','e','l','l',0}},
137     {URL_SCHEME_SNEWS,          {'s','n','e','w','s',0}},
138     {URL_SCHEME_LOCAL,          {'l','o','c','a','l',0}},
139     {URL_SCHEME_JAVASCRIPT,     {'j','a','v','a','s','c','r','i','p','t',0}},
140     {URL_SCHEME_VBSCRIPT,       {'v','b','s','c','r','i','p','t',0}},
141     {URL_SCHEME_ABOUT,          {'a','b','o','u','t',0}},
142     {URL_SCHEME_RES,            {'r','e','s',0}},
143     {URL_SCHEME_MSSHELLROOTED,  {'m','s','-','s','h','e','l','l','-','r','o','o','t','e','d',0}},
144     {URL_SCHEME_MSSHELLIDLIST,  {'m','s','-','s','h','e','l','l','-','i','d','l','i','s','t',0}},
145     {URL_SCHEME_MSHELP,         {'h','c','p',0}},
146     {URL_SCHEME_WILDCARD,       {'*',0}}
147 };
148
149 /* List of default ports Windows recognizes. */
150 static const struct {
151     URL_SCHEME  scheme;
152     USHORT      port;
153 } default_ports[] = {
154     {URL_SCHEME_FTP,    21},
155     {URL_SCHEME_HTTP,   80},
156     {URL_SCHEME_GOPHER, 70},
157     {URL_SCHEME_NNTP,   119},
158     {URL_SCHEME_TELNET, 23},
159     {URL_SCHEME_WAIS,   210},
160     {URL_SCHEME_HTTPS,  443},
161 };
162
163 /* List of 3 character top level domain names Windows seems to recognize.
164  * There might be more, but, these are the only ones I've found so far.
165  */
166 static const struct {
167     WCHAR tld_name[4];
168 } recognized_tlds[] = {
169     {{'c','o','m',0}},
170     {{'e','d','u',0}},
171     {{'g','o','v',0}},
172     {{'i','n','t',0}},
173     {{'m','i','l',0}},
174     {{'n','e','t',0}},
175     {{'o','r','g',0}}
176 };
177
178 static inline BOOL is_alpha(WCHAR val) {
179         return ((val >= 'a' && val <= 'z') || (val >= 'A' && val <= 'Z'));
180 }
181
182 static inline BOOL is_num(WCHAR val) {
183         return (val >= '0' && val <= '9');
184 }
185
186 /* A URI is implicitly a file path if it begins with
187  * a drive letter (eg X:) or starts with "\\" (UNC path).
188  */
189 static inline BOOL is_implicit_file_path(const WCHAR *str) {
190     if(is_alpha(str[0]) && str[1] == ':')
191         return TRUE;
192     else if(str[0] == '\\' && str[1] == '\\')
193         return TRUE;
194
195     return FALSE;
196 }
197
198 /* Checks if the URI is a hierarchical URI. A hierarchical
199  * URI is one that has "//" after the scheme.
200  */
201 static BOOL check_hierarchical(const WCHAR **ptr) {
202     const WCHAR *start = *ptr;
203
204     if(**ptr != '/')
205         return FALSE;
206
207     ++(*ptr);
208     if(**ptr != '/') {
209         *ptr = start;
210         return FALSE;
211     }
212
213     ++(*ptr);
214     return TRUE;
215 }
216
217 /* unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~" */
218 static inline BOOL is_unreserved(WCHAR val) {
219     return (is_alpha(val) || is_num(val) || val == '-' || val == '.' ||
220             val == '_' || val == '~');
221 }
222
223 /* sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
224  *               / "*" / "+" / "," / ";" / "="
225  */
226 static inline BOOL is_subdelim(WCHAR val) {
227     return (val == '!' || val == '$' || val == '&' ||
228             val == '\'' || val == '(' || val == ')' ||
229             val == '*' || val == '+' || val == ',' ||
230             val == ';' || val == '=');
231 }
232
233 /* gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@" */
234 static inline BOOL is_gendelim(WCHAR val) {
235     return (val == ':' || val == '/' || val == '?' ||
236             val == '#' || val == '[' || val == ']' ||
237             val == '@');
238 }
239
240 /* Characters that delimit the end of the authority
241  * section of a URI. Sometimes a '\\' is considered
242  * an authority delimeter.
243  */
244 static inline BOOL is_auth_delim(WCHAR val, BOOL acceptSlash) {
245     return (val == '#' || val == '/' || val == '?' ||
246             val == '\0' || (acceptSlash && val == '\\'));
247 }
248
249 /* reserved = gen-delims / sub-delims */
250 static inline BOOL is_reserved(WCHAR val) {
251     return (is_subdelim(val) || is_gendelim(val));
252 }
253
254 static inline BOOL is_hexdigit(WCHAR val) {
255     return ((val >= 'a' && val <= 'f') ||
256             (val >= 'A' && val <= 'F') ||
257             (val >= '0' && val <= '9'));
258 }
259
260 /* Computes the size of the given IPv6 address.
261  * Each h16 component is 16bits, if there is an IPv4 address, it's
262  * 32bits. If there's an elision it can be 16bits to 128bits, depending
263  * on the number of other components.
264  *
265  * Modeled after google-url's CheckIPv6ComponentsSize function
266  */
267 static void compute_ipv6_comps_size(ipv6_address *address) {
268     address->components_size = address->h16_count * 2;
269
270     if(address->ipv4)
271         /* IPv4 address is 4 bytes. */
272         address->components_size += 4;
273
274     if(address->elision) {
275         /* An elision can be anywhere from 2 bytes up to 16 bytes.
276          * It size depends on the size of the h16 and IPv4 components.
277          */
278         address->elision_size = 16 - address->components_size;
279         if(address->elision_size < 2)
280             address->elision_size = 2;
281     } else
282         address->elision_size = 0;
283 }
284
285 /* Taken from dlls/jscript/lex.c */
286 static int hex_to_int(WCHAR val) {
287     if(val >= '0' && val <= '9')
288         return val - '0';
289     else if(val >= 'a' && val <= 'f')
290         return val - 'a' + 10;
291     else if(val >= 'A' && val <= 'F')
292         return val - 'A' + 10;
293
294     return -1;
295 }
296
297 /* Helper function for converting a percent encoded string
298  * representation of a WCHAR value into its actual WCHAR value. If
299  * the two characters following the '%' aren't valid hex values then
300  * this function returns the NULL character.
301  *
302  * Eg.
303  *  "%2E" will result in '.' being returned by this function.
304  */
305 static WCHAR decode_pct_val(const WCHAR *ptr) {
306     WCHAR ret = '\0';
307
308     if(*ptr == '%' && is_hexdigit(*(ptr + 1)) && is_hexdigit(*(ptr + 2))) {
309         INT a = hex_to_int(*(ptr + 1));
310         INT b = hex_to_int(*(ptr + 2));
311
312         ret = a << 4;
313         ret += b;
314     }
315
316     return ret;
317 }
318
319 /* Helper function for percent encoding a given character
320  * and storing the encoded value into a given buffer (dest).
321  *
322  * It's up to the calling function to ensure that there is
323  * at least enough space in 'dest' for the percent encoded
324  * value to be stored (so dest + 3 spaces available).
325  */
326 static inline void pct_encode_val(WCHAR val, WCHAR *dest) {
327     dest[0] = '%';
328     dest[1] = hexDigits[(val >> 4) & 0xf];
329     dest[2] = hexDigits[val & 0xf];
330 }
331
332 /* Scans the range of characters [str, end] and returns the last occurence
333  * of 'ch' or returns NULL.
334  */
335 static const WCHAR *str_last_of(const WCHAR *str, const WCHAR *end, WCHAR ch) {
336     const WCHAR *ptr = end;
337
338     while(ptr >= str) {
339         if(*ptr == ch)
340             return ptr;
341         --ptr;
342     }
343
344     return NULL;
345 }
346
347 /* Attempts to parse the domain name from the host.
348  *
349  * This function also includes the Top-level Domain (TLD) name
350  * of the host when it tries to find the domain name. If it finds
351  * a valid domain name it will assign 'domain_start' the offset
352  * into 'host' where the domain name starts.
353  *
354  * It's implied that if a domain name its range is implied to be
355  * [host+domain_start, host+host_len).
356  */
357 static void find_domain_name(const WCHAR *host, DWORD host_len,
358                              INT *domain_start) {
359     const WCHAR *last_tld, *sec_last_tld, *end;
360
361     end = host+host_len-1;
362
363     *domain_start = -1;
364
365     /* There has to be at least enough room for a '.' followed by a
366      * 3 character TLD for a domain to even exist in the host name.
367      */
368     if(host_len < 4)
369         return;
370
371     last_tld = str_last_of(host, end, '.');
372     if(!last_tld)
373         /* http://hostname -> has no domain name. */
374         return;
375
376     sec_last_tld = str_last_of(host, last_tld-1, '.');
377     if(!sec_last_tld) {
378         /* If the '.' is at the beginning of the host there
379          * has to be at least 3 characters in the TLD for it
380          * to be valid.
381          *  Ex: .com -> .com as the domain name.
382          *      .co  -> has no domain name.
383          */
384         if(last_tld-host == 0) {
385             if(end-(last_tld-1) < 3)
386                 return;
387         } else if(last_tld-host == 3) {
388             DWORD i;
389
390             /* If there's three characters in front of last_tld and
391              * they are on the list of recognized TLDs, then this
392              * host doesn't have a domain (since the host only contains
393              * a TLD name.
394              *  Ex: edu.uk -> has no domain name.
395              *      foo.uk -> foo.uk as the domain name.
396              */
397             for(i = 0; i < sizeof(recognized_tlds)/sizeof(recognized_tlds[0]); ++i) {
398                 if(!StrCmpNIW(host, recognized_tlds[i].tld_name, 3))
399                     return;
400             }
401         } else if(last_tld-host < 3)
402             /* Anything less then 3 characters is considered part
403              * of the TLD name.
404              *  Ex: ak.uk -> Has no domain name.
405              */
406             return;
407
408         /* Otherwise the domain name is the whole host name. */
409         *domain_start = 0;
410     } else if(end+1-last_tld > 3) {
411         /* If the last_tld has more then 3 characters then it's automatically
412          * considered the TLD of the domain name.
413          *  Ex: www.winehq.org.uk.test -> uk.test as the domain name.
414          */
415         *domain_start = (sec_last_tld+1)-host;
416     } else if(last_tld - (sec_last_tld+1) < 4) {
417         DWORD i;
418         /* If the sec_last_tld is 3 characters long it HAS to be on the list of
419          * recognized to still be considered part of the TLD name, otherwise
420          * its considered the domain name.
421          *  Ex: www.google.com.uk -> google.com.uk as the domain name.
422          *      www.google.foo.uk -> foo.uk as the domain name.
423          */
424         if(last_tld - (sec_last_tld+1) == 3) {
425             for(i = 0; i < sizeof(recognized_tlds)/sizeof(recognized_tlds[0]); ++i) {
426                 if(!StrCmpNIW(sec_last_tld+1, recognized_tlds[i].tld_name, 3)) {
427                     const WCHAR *domain = str_last_of(host, sec_last_tld-1, '.');
428
429                     if(!domain)
430                         *domain_start = 0;
431                     else
432                         *domain_start = (domain+1) - host;
433                     TRACE("Found domain name %s\n", debugstr_wn(host+*domain_start,
434                                                         (host+host_len)-(host+*domain_start)));
435                     return;
436                 }
437             }
438
439             *domain_start = (sec_last_tld+1)-host;
440         } else {
441             /* Since the sec_last_tld is less then 3 characters it's considered
442              * part of the TLD.
443              *  Ex: www.google.fo.uk -> google.fo.uk as the domain name.
444              */
445             const WCHAR *domain = str_last_of(host, sec_last_tld-1, '.');
446
447             if(!domain)
448                 *domain_start = 0;
449             else
450                 *domain_start = (domain+1) - host;
451         }
452     } else {
453         /* The second to last TLD has more then 3 characters making it
454          * the domain name.
455          *  Ex: www.google.test.us -> test.us as the domain name.
456          */
457         *domain_start = (sec_last_tld+1)-host;
458     }
459
460     TRACE("Found domain name %s\n", debugstr_wn(host+*domain_start,
461                                         (host+host_len)-(host+*domain_start)));
462 }
463
464 /* Computes the location where the elision should occur in the IPv6
465  * address using the numerical values of each component stored in
466  * 'values'. If the address shouldn't contain an elision then 'index'
467  * is assigned -1 as it's value. Otherwise 'index' will contain the
468  * starting index (into values) where the elision should be, and 'count'
469  * will contain the number of cells the elision covers.
470  *
471  * NOTES:
472  *  Windows will expand an elision if the elision only represents 1 h16
473  *  component of the URI.
474  *
475  *  Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
476  *
477  *  If the IPv6 address contains an IPv4 address, the IPv4 address is also
478  *  considered for being included as part of an elision if all it's components
479  *  are zeros.
480  *
481  *  Ex: [1:2:3:4:5:6:0.0.0.0] -> [1:2:3:4:5:6::]
482  */
483 static void compute_elision_location(const ipv6_address *address, const USHORT values[8],
484                                      INT *index, DWORD *count) {
485     DWORD i, max_len, cur_len;
486     INT max_index, cur_index;
487
488     max_len = cur_len = 0;
489     max_index = cur_index = -1;
490     for(i = 0; i < 8; ++i) {
491         BOOL check_ipv4 = (address->ipv4 && i == 6);
492         BOOL is_end = (check_ipv4 || i == 7);
493
494         if(check_ipv4) {
495             /* Check if the IPv4 address contains only zeros. */
496             if(values[i] == 0 && values[i+1] == 0) {
497                 if(cur_index == -1)
498                     cur_index = i;
499
500                 cur_len += 2;
501                 ++i;
502             }
503         } else if(values[i] == 0) {
504             if(cur_index == -1)
505                 cur_index = i;
506
507             ++cur_len;
508         }
509
510         if(is_end || values[i] != 0) {
511             /* We only consider it for an elision if it's
512              * more then 1 component long.
513              */
514             if(cur_len > 1 && cur_len > max_len) {
515                 /* Found the new elision location. */
516                 max_len = cur_len;
517                 max_index = cur_index;
518             }
519
520             /* Reset the current range for the next range of zeros. */
521             cur_index = -1;
522             cur_len = 0;
523         }
524     }
525
526     *index = max_index;
527     *count = max_len;
528 }
529
530 /* Converts the specified IPv4 address into an uint value.
531  *
532  * This function assumes that the IPv4 address has already been validated.
533  */
534 static UINT ipv4toui(const WCHAR *ip, DWORD len) {
535     UINT ret = 0;
536     DWORD comp_value = 0;
537     const WCHAR *ptr;
538
539     for(ptr = ip; ptr < ip+len; ++ptr) {
540         if(*ptr == '.') {
541             ret <<= 8;
542             ret += comp_value;
543             comp_value = 0;
544         } else
545             comp_value = comp_value*10 + (*ptr-'0');
546     }
547
548     ret <<= 8;
549     ret += comp_value;
550
551     return ret;
552 }
553
554 /* Converts an IPv4 address in numerical form into it's fully qualified
555  * string form. This function returns the number of characters written
556  * to 'dest'. If 'dest' is NULL this function will return the number of
557  * characters that would have been written.
558  *
559  * It's up to the caller to ensure there's enough space in 'dest' for the
560  * address.
561  */
562 static DWORD ui2ipv4(WCHAR *dest, UINT address) {
563     static const WCHAR formatW[] =
564         {'%','u','.','%','u','.','%','u','.','%','u',0};
565     DWORD ret = 0;
566     UCHAR digits[4];
567
568     digits[0] = (address >> 24) & 0xff;
569     digits[1] = (address >> 16) & 0xff;
570     digits[2] = (address >> 8) & 0xff;
571     digits[3] = address & 0xff;
572
573     if(!dest) {
574         WCHAR tmp[16];
575         ret = sprintfW(tmp, formatW, digits[0], digits[1], digits[2], digits[3]);
576     } else
577         ret = sprintfW(dest, formatW, digits[0], digits[1], digits[2], digits[3]);
578
579     return ret;
580 }
581
582 /* Converts an h16 component (from an IPv6 address) into it's
583  * numerical value.
584  *
585  * This function assumes that the h16 component has already been validated.
586  */
587 static USHORT h16tous(h16 component) {
588     DWORD i;
589     USHORT ret = 0;
590
591     for(i = 0; i < component.len; ++i) {
592         ret <<= 4;
593         ret += hex_to_int(component.str[i]);
594     }
595
596     return ret;
597 }
598
599 /* Converts an IPv6 address into it's 128 bits (16 bytes) numerical value.
600  *
601  * This function assumes that the ipv6_address has already been validated.
602  */
603 static BOOL ipv6_to_number(const ipv6_address *address, USHORT number[8]) {
604     DWORD i, cur_component = 0;
605     BOOL already_passed_elision = FALSE;
606
607     for(i = 0; i < address->h16_count; ++i) {
608         if(address->elision) {
609             if(address->components[i].str > address->elision && !already_passed_elision) {
610                 /* Means we just passed the elision and need to add it's values to
611                  * 'number' before we do anything else.
612                  */
613                 DWORD j = 0;
614                 for(j = 0; j < address->elision_size; j+=2)
615                     number[cur_component++] = 0;
616
617                 already_passed_elision = TRUE;
618             }
619         }
620
621         number[cur_component++] = h16tous(address->components[i]);
622     }
623
624     /* Case when the elision appears after the h16 components. */
625     if(!already_passed_elision && address->elision) {
626         for(i = 0; i < address->elision_size; i+=2)
627             number[cur_component++] = 0;
628         already_passed_elision = TRUE;
629     }
630
631     if(address->ipv4) {
632         UINT value = ipv4toui(address->ipv4, address->ipv4_len);
633
634         if(cur_component != 6) {
635             ERR("(%p %p): Failed sanity check with %d\n", address, number, cur_component);
636             return FALSE;
637         }
638
639         number[cur_component++] = (value >> 16) & 0xffff;
640         number[cur_component] = value & 0xffff;
641     }
642
643     return TRUE;
644 }
645
646 /* Checks if the characters pointed to by 'ptr' are
647  * a percent encoded data octet.
648  *
649  * pct-encoded = "%" HEXDIG HEXDIG
650  */
651 static BOOL check_pct_encoded(const WCHAR **ptr) {
652     const WCHAR *start = *ptr;
653
654     if(**ptr != '%')
655         return FALSE;
656
657     ++(*ptr);
658     if(!is_hexdigit(**ptr)) {
659         *ptr = start;
660         return FALSE;
661     }
662
663     ++(*ptr);
664     if(!is_hexdigit(**ptr)) {
665         *ptr = start;
666         return FALSE;
667     }
668
669     ++(*ptr);
670     return TRUE;
671 }
672
673 /* dec-octet   = DIGIT                 ; 0-9
674  *             / %x31-39 DIGIT         ; 10-99
675  *             / "1" 2DIGIT            ; 100-199
676  *             / "2" %x30-34 DIGIT     ; 200-249
677  *             / "25" %x30-35          ; 250-255
678  */
679 static BOOL check_dec_octet(const WCHAR **ptr) {
680     const WCHAR *c1, *c2, *c3;
681
682     c1 = *ptr;
683     /* A dec-octet must be at least 1 digit long. */
684     if(*c1 < '0' || *c1 > '9')
685         return FALSE;
686
687     ++(*ptr);
688
689     c2 = *ptr;
690     /* Since the 1 digit requirment was meet, it doesn't
691      * matter if this is a DIGIT value, it's considered a
692      * dec-octet.
693      */
694     if(*c2 < '0' || *c2 > '9')
695         return TRUE;
696
697     ++(*ptr);
698
699     c3 = *ptr;
700     /* Same explanation as above. */
701     if(*c3 < '0' || *c3 > '9')
702         return TRUE;
703
704     /* Anything > 255 isn't a valid IP dec-octet. */
705     if(*c1 >= '2' && *c2 >= '5' && *c3 >= '5') {
706         *ptr = c1;
707         return FALSE;
708     }
709
710     ++(*ptr);
711     return TRUE;
712 }
713
714 /* Checks if there is an implicit IPv4 address in the host component of the URI.
715  * The max value of an implicit IPv4 address is UINT_MAX.
716  *
717  *  Ex:
718  *      "234567" would be considered an implicit IPv4 address.
719  */
720 static BOOL check_implicit_ipv4(const WCHAR **ptr, UINT *val) {
721     const WCHAR *start = *ptr;
722     ULONGLONG ret = 0;
723     *val = 0;
724
725     while(is_num(**ptr)) {
726         ret = ret*10 + (**ptr - '0');
727
728         if(ret > UINT_MAX) {
729             *ptr = start;
730             return FALSE;
731         }
732         ++(*ptr);
733     }
734
735     if(*ptr == start)
736         return FALSE;
737
738     *val = ret;
739     return TRUE;
740 }
741
742 /* Checks if the string contains an IPv4 address.
743  *
744  * This function has a strict mode or a non-strict mode of operation
745  * When 'strict' is set to FALSE this function will return TRUE if
746  * the string contains at least 'dec-octet "." dec-octet' since partial
747  * IPv4 addresses will be normalized out into full IPv4 addresses. When
748  * 'strict' is set this function expects there to be a full IPv4 address.
749  *
750  * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
751  */
752 static BOOL check_ipv4address(const WCHAR **ptr, BOOL strict) {
753     const WCHAR *start = *ptr;
754
755     if(!check_dec_octet(ptr)) {
756         *ptr = start;
757         return FALSE;
758     }
759
760     if(**ptr != '.') {
761         *ptr = start;
762         return FALSE;
763     }
764
765     ++(*ptr);
766     if(!check_dec_octet(ptr)) {
767         *ptr = start;
768         return FALSE;
769     }
770
771     if(**ptr != '.') {
772         if(strict) {
773             *ptr = start;
774             return FALSE;
775         } else
776             return TRUE;
777     }
778
779     ++(*ptr);
780     if(!check_dec_octet(ptr)) {
781         *ptr = start;
782         return FALSE;
783     }
784
785     if(**ptr != '.') {
786         if(strict) {
787             *ptr = start;
788             return FALSE;
789         } else
790             return TRUE;
791     }
792
793     ++(*ptr);
794     if(!check_dec_octet(ptr)) {
795         *ptr = start;
796         return FALSE;
797     }
798
799     /* Found a four digit ip address. */
800     return TRUE;
801 }
802 /* Tries to parse the scheme name of the URI.
803  *
804  * scheme = ALPHA *(ALPHA | NUM | '+' | '-' | '.') as defined by RFC 3896.
805  * NOTE: Windows accepts a number as the first character of a scheme.
806  */
807 static BOOL parse_scheme_name(const WCHAR **ptr, parse_data *data) {
808     const WCHAR *start = *ptr;
809
810     data->scheme = NULL;
811     data->scheme_len = 0;
812
813     while(**ptr) {
814         if(**ptr == '*' && *ptr == start) {
815             /* Might have found a wildcard scheme. If it is the next
816              * char has to be a ':' for it to be a valid URI
817              */
818             ++(*ptr);
819             break;
820         } else if(!is_num(**ptr) && !is_alpha(**ptr) && **ptr != '+' &&
821            **ptr != '-' && **ptr != '.')
822             break;
823
824         (*ptr)++;
825     }
826
827     if(*ptr == start)
828         return FALSE;
829
830     /* Schemes must end with a ':' */
831     if(**ptr != ':') {
832         *ptr = start;
833         return FALSE;
834     }
835
836     data->scheme = start;
837     data->scheme_len = *ptr - start;
838
839     ++(*ptr);
840     return TRUE;
841 }
842
843 /* Tries to deduce the corresponding URL_SCHEME for the given URI. Stores
844  * the deduced URL_SCHEME in data->scheme_type.
845  */
846 static BOOL parse_scheme_type(parse_data *data) {
847     /* If there's scheme data then see if it's a recognized scheme. */
848     if(data->scheme && data->scheme_len) {
849         DWORD i;
850
851         for(i = 0; i < sizeof(recognized_schemes)/sizeof(recognized_schemes[0]); ++i) {
852             if(lstrlenW(recognized_schemes[i].scheme_name) == data->scheme_len) {
853                 /* Has to be a case insensitive compare. */
854                 if(!StrCmpNIW(recognized_schemes[i].scheme_name, data->scheme, data->scheme_len)) {
855                     data->scheme_type = recognized_schemes[i].scheme;
856                     return TRUE;
857                 }
858             }
859         }
860
861         /* If we get here it means it's not a recognized scheme. */
862         data->scheme_type = URL_SCHEME_UNKNOWN;
863         return TRUE;
864     } else if(data->is_relative) {
865         /* Relative URI's have no scheme. */
866         data->scheme_type = URL_SCHEME_UNKNOWN;
867         return TRUE;
868     } else {
869         /* Should never reach here! what happened... */
870         FIXME("(%p): Unable to determine scheme type for URI %s\n", data, debugstr_w(data->uri));
871         return FALSE;
872     }
873 }
874
875 /* Tries to parse (or deduce) the scheme_name of a URI. If it can't
876  * parse a scheme from the URI it will try to deduce the scheme_name and scheme_type
877  * using the flags specified in 'flags' (if any). Flags that affect how this function
878  * operates are the Uri_CREATE_ALLOW_* flags.
879  *
880  * All parsed/deduced information will be stored in 'data' when the function returns.
881  *
882  * Returns TRUE if it was able to successfully parse the information.
883  */
884 static BOOL parse_scheme(const WCHAR **ptr, parse_data *data, DWORD flags) {
885     static const WCHAR fileW[] = {'f','i','l','e',0};
886     static const WCHAR wildcardW[] = {'*',0};
887
888     /* First check to see if the uri could implicitly be a file path. */
889     if(is_implicit_file_path(*ptr)) {
890         if(flags & Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME) {
891             data->scheme = fileW;
892             data->scheme_len = lstrlenW(fileW);
893             data->has_implicit_scheme = TRUE;
894
895             TRACE("(%p %p %x): URI is an implicit file path.\n", ptr, data, flags);
896         } else {
897             /* Window's does not consider anything that can implicitly be a file
898              * path to be a valid URI if the ALLOW_IMPLICIT_FILE_SCHEME flag is not set...
899              */
900             TRACE("(%p %p %x): URI is implicitly a file path, but, the ALLOW_IMPLICIT_FILE_SCHEME flag wasn't set.\n",
901                     ptr, data, flags);
902             return FALSE;
903         }
904     } else if(!parse_scheme_name(ptr, data)) {
905         /* No Scheme was found, this means it could be:
906          *      a) an implicit Wildcard scheme
907          *      b) a relative URI
908          *      c) a invalid URI.
909          */
910         if(flags & Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME) {
911             data->scheme = wildcardW;
912             data->scheme_len = lstrlenW(wildcardW);
913             data->has_implicit_scheme = TRUE;
914
915             TRACE("(%p %p %x): URI is an implicit wildcard scheme.\n", ptr, data, flags);
916         } else if (flags & Uri_CREATE_ALLOW_RELATIVE) {
917             data->is_relative = TRUE;
918             TRACE("(%p %p %x): URI is relative.\n", ptr, data, flags);
919         } else {
920             TRACE("(%p %p %x): Malformed URI found. Unable to deduce scheme name.\n", ptr, data, flags);
921             return FALSE;
922         }
923     }
924
925     if(!data->is_relative)
926         TRACE("(%p %p %x): Found scheme=%s scheme_len=%d\n", ptr, data, flags,
927                 debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
928
929     if(!parse_scheme_type(data))
930         return FALSE;
931
932     TRACE("(%p %p %x): Assigned %d as the URL_SCHEME.\n", ptr, data, flags, data->scheme_type);
933     return TRUE;
934 }
935
936 /* Parses the userinfo part of the URI (if it exists). The userinfo field of
937  * a URI can consist of "username:password@", or just "username@".
938  *
939  * RFC def:
940  * userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )
941  *
942  * NOTES:
943  *  1)  If there is more than one ':' in the userinfo part of the URI Windows
944  *      uses the first occurence of ':' to delimit the username and password
945  *      components.
946  *
947  *      ex:
948  *          ftp://user:pass:word@winehq.org
949  *
950  *      Would yield, "user" as the username and "pass:word" as the password.
951  *
952  *  2)  Windows allows any character to appear in the "userinfo" part of
953  *      a URI, as long as it's not an authority delimeter character set.
954  */
955 static void parse_userinfo(const WCHAR **ptr, parse_data *data, DWORD flags) {
956     data->userinfo = *ptr;
957     data->userinfo_split = -1;
958
959     while(**ptr != '@') {
960         if(**ptr == ':' && data->userinfo_split == -1)
961             data->userinfo_split = *ptr - data->userinfo;
962         else if(**ptr == '%') {
963             /* If it's a known scheme type, it has to be a valid percent
964              * encoded value.
965              */
966             if(!check_pct_encoded(ptr)) {
967                 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
968                     *ptr = data->userinfo;
969                     data->userinfo = NULL;
970                     data->userinfo_split = -1;
971
972                     TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
973                     return;
974                 }
975             } else
976                 continue;
977         } else if(is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN))
978             break;
979
980         ++(*ptr);
981     }
982
983     if(**ptr != '@') {
984         *ptr = data->userinfo;
985         data->userinfo = NULL;
986         data->userinfo_split = -1;
987
988         TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
989         return;
990     }
991
992     data->userinfo_len = *ptr - data->userinfo;
993     TRACE("(%p %p %x): Found userinfo=%s userinfo_len=%d split=%d.\n", ptr, data, flags,
994             debugstr_wn(data->userinfo, data->userinfo_len), data->userinfo_len, data->userinfo_split);
995     ++(*ptr);
996 }
997
998 /* Attempts to parse a port from the URI.
999  *
1000  * NOTES:
1001  *  Windows seems to have a cap on what the maximum value
1002  *  for a port can be. The max value is USHORT_MAX.
1003  *
1004  * port = *DIGIT
1005  */
1006 static BOOL parse_port(const WCHAR **ptr, parse_data *data, DWORD flags) {
1007     UINT port = 0;
1008     data->port = *ptr;
1009
1010     while(!is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)) {
1011         if(!is_num(**ptr)) {
1012             *ptr = data->port;
1013             data->port = NULL;
1014             return FALSE;
1015         }
1016
1017         port = port*10 + (**ptr-'0');
1018
1019         if(port > USHORT_MAX) {
1020             *ptr = data->port;
1021             data->port = NULL;
1022             return FALSE;
1023         }
1024
1025         ++(*ptr);
1026     }
1027
1028     data->port_value = port;
1029     data->port_len = *ptr - data->port;
1030
1031     TRACE("(%p %p %x): Found port %s len=%d value=%u\n", ptr, data, flags,
1032         debugstr_wn(data->port, data->port_len), data->port_len, data->port_value);
1033     return TRUE;
1034 }
1035
1036 /* Attempts to parse a IPv4 address from the URI.
1037  *
1038  * NOTES:
1039  *  Window's normalizes IPv4 addresses, This means there's three
1040  *  possibilities for the URI to contain an IPv4 address.
1041  *      1)  A well formed address (ex. 192.2.2.2).
1042  *      2)  A partially formed address. For example "192.0" would
1043  *          normalize to "192.0.0.0" during canonicalization.
1044  *      3)  An implicit IPv4 address. For example "256" would
1045  *          normalize to "0.0.1.0" during canonicalization. Also
1046  *          note that the maximum value for an implicit IP address
1047  *          is UINT_MAX, if the value in the URI exceeds this then
1048  *          it is not considered an IPv4 address.
1049  */
1050 static BOOL parse_ipv4address(const WCHAR **ptr, parse_data *data, DWORD flags) {
1051     const BOOL is_unknown = data->scheme_type == URL_SCHEME_UNKNOWN;
1052     data->host = *ptr;
1053
1054     if(!check_ipv4address(ptr, FALSE)) {
1055         if(!check_implicit_ipv4(ptr, &data->implicit_ipv4)) {
1056             TRACE("(%p %p %x): URI didn't contain anything looking like an IPv4 address.\n",
1057                 ptr, data, flags);
1058             *ptr = data->host;
1059             data->host = NULL;
1060             return FALSE;
1061         } else
1062             data->has_implicit_ip = TRUE;
1063     }
1064
1065     /* Check if what we found is the only part of the host name (if it isn't
1066      * we don't have an IPv4 address).
1067      */
1068     if(**ptr == ':') {
1069         ++(*ptr);
1070         if(!parse_port(ptr, data, flags)) {
1071             *ptr = data->host;
1072             data->host = NULL;
1073             return FALSE;
1074         }
1075     } else if(!is_auth_delim(**ptr, !is_unknown)) {
1076         /* Found more data which belongs the host, so this isn't an IPv4. */
1077         *ptr = data->host;
1078         data->host = NULL;
1079         data->has_implicit_ip = FALSE;
1080         return FALSE;
1081     }
1082
1083     data->host_len = *ptr - data->host;
1084     data->host_type = Uri_HOST_IPV4;
1085
1086     TRACE("(%p %p %x): IPv4 address found. host=%s host_len=%d host_type=%d\n",
1087         ptr, data, flags, debugstr_wn(data->host, data->host_len),
1088         data->host_len, data->host_type);
1089     return TRUE;
1090 }
1091
1092 /* Attempts to parse the reg-name from the URI.
1093  *
1094  * Because of the way Windows handles ':' this function also
1095  * handles parsing the port.
1096  *
1097  * reg-name = *( unreserved / pct-encoded / sub-delims )
1098  *
1099  * NOTE:
1100  *  Windows allows everything, but, the characters in "auth_delims" and ':'
1101  *  to appear in a reg-name, unless it's an unknown scheme type then ':' is
1102  *  allowed to appear (even if a valid port isn't after it).
1103  *
1104  *  Windows doesn't like host names which start with '[' and end with ']'
1105  *  and don't contain a valid IP literal address in between them.
1106  *
1107  *  On Windows if an '[' is encountered in the host name the ':' no longer
1108  *  counts as a delimiter until you reach the next ']' or an "authority delimeter".
1109  *
1110  *  A reg-name CAN be empty.
1111  */
1112 static BOOL parse_reg_name(const WCHAR **ptr, parse_data *data, DWORD flags) {
1113     const BOOL has_start_bracket = **ptr == '[';
1114     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1115     BOOL inside_brackets = has_start_bracket;
1116     BOOL ignore_col = FALSE;
1117
1118     /* We have to be careful with file schemes. */
1119     if(data->scheme_type == URL_SCHEME_FILE) {
1120         /* This is because an implicit file scheme could be "C:\\test" and it
1121          * would trick this function into thinking the host is "C", when after
1122          * canonicalization the host would end up being an empty string.
1123          */
1124         if(is_alpha(**ptr) && *(*ptr+1) == ':') {
1125             /* Regular old drive paths don't have a host type (or host name). */
1126             data->host_type = Uri_HOST_UNKNOWN;
1127             data->host = *ptr;
1128             data->host_len = 0;
1129             return TRUE;
1130         } else if(**ptr == '\\' && *(*ptr+1) == '\\')
1131             /* Skip past the "\\" of a UNC path. */
1132             *ptr += 2;
1133     }
1134
1135     data->host = *ptr;
1136
1137     while(!is_auth_delim(**ptr, known_scheme)) {
1138         if(**ptr == ':' && !ignore_col) {
1139             /* We can ignore ':' if were inside brackets.*/
1140             if(!inside_brackets) {
1141                 const WCHAR *tmp = (*ptr)++;
1142
1143                 /* Attempt to parse the port. */
1144                 if(!parse_port(ptr, data, flags)) {
1145                     /* Windows expects there to be a valid port for known scheme types. */
1146                     if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1147                         *ptr = data->host;
1148                         data->host = NULL;
1149                         TRACE("(%p %p %x): Expected valid port\n", ptr, data, flags);
1150                         return FALSE;
1151                     } else
1152                         /* Windows gives up on trying to parse a port when it
1153                          * encounters 1 invalid port.
1154                          */
1155                         ignore_col = TRUE;
1156                 } else {
1157                     data->host_len = tmp - data->host;
1158                     break;
1159                 }
1160             }
1161         } else if(**ptr == '%' && known_scheme) {
1162             /* Has to be a legit % encoded value. */
1163             if(!check_pct_encoded(ptr)) {
1164                 *ptr = data->host;
1165                 data->host = NULL;
1166                 return FALSE;
1167             } else
1168                 continue;
1169         } else if(**ptr == ']')
1170             inside_brackets = FALSE;
1171         else if(**ptr == '[')
1172             inside_brackets = TRUE;
1173
1174         ++(*ptr);
1175     }
1176
1177     if(has_start_bracket) {
1178         /* Make sure the last character of the host wasn't a ']'. */
1179         if(*(*ptr-1) == ']') {
1180             TRACE("(%p %p %x): Expected an IP literal inside of the host\n",
1181                 ptr, data, flags);
1182             *ptr = data->host;
1183             data->host = NULL;
1184             return FALSE;
1185         }
1186     }
1187
1188     /* Don't overwrite our length if we found a port earlier. */
1189     if(!data->port)
1190         data->host_len = *ptr - data->host;
1191
1192     /* If the host is empty, then it's an unknown host type. */
1193     if(data->host_len == 0)
1194         data->host_type = Uri_HOST_UNKNOWN;
1195     else
1196         data->host_type = Uri_HOST_DNS;
1197
1198     TRACE("(%p %p %x): Parsed reg-name. host=%s len=%d\n", ptr, data, flags,
1199         debugstr_wn(data->host, data->host_len), data->host_len);
1200     return TRUE;
1201 }
1202
1203 /* Attempts to parse an IPv6 address out of the URI.
1204  *
1205  * IPv6address =                               6( h16 ":" ) ls32
1206  *                /                       "::" 5( h16 ":" ) ls32
1207  *                / [               h16 ] "::" 4( h16 ":" ) ls32
1208  *                / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
1209  *                / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
1210  *                / [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
1211  *                / [ *4( h16 ":" ) h16 ] "::"              ls32
1212  *                / [ *5( h16 ":" ) h16 ] "::"              h16
1213  *                / [ *6( h16 ":" ) h16 ] "::"
1214  *
1215  * ls32        = ( h16 ":" h16 ) / IPv4address
1216  *             ; least-significant 32 bits of address.
1217  *
1218  * h16         = 1*4HEXDIG
1219  *             ; 16 bits of address represented in hexadecimal.
1220  *
1221  * Modeled after google-url's 'DoParseIPv6' function.
1222  */
1223 static BOOL parse_ipv6address(const WCHAR **ptr, parse_data *data, DWORD flags) {
1224     const WCHAR *start, *cur_start;
1225     ipv6_address ip;
1226
1227     start = cur_start = *ptr;
1228     memset(&ip, 0, sizeof(ipv6_address));
1229
1230     for(;; ++(*ptr)) {
1231         /* Check if we're on the last character of the host. */
1232         BOOL is_end = (is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)
1233                         || **ptr == ']');
1234
1235         BOOL is_split = (**ptr == ':');
1236         BOOL is_elision = (is_split && !is_end && *(*ptr+1) == ':');
1237
1238         /* Check if we're at the end of of the a component, or
1239          * if we're at the end of the IPv6 address.
1240          */
1241         if(is_split || is_end) {
1242             DWORD cur_len = 0;
1243
1244             cur_len = *ptr - cur_start;
1245
1246             /* h16 can't have a length > 4. */
1247             if(cur_len > 4) {
1248                 *ptr = start;
1249
1250                 TRACE("(%p %p %x): h16 component to long.\n",
1251                     ptr, data, flags);
1252                 return FALSE;
1253             }
1254
1255             if(cur_len == 0) {
1256                 /* An h16 component can't have the length of 0 unless
1257                  * the elision is at the beginning of the address, or
1258                  * at the end of the address.
1259                  */
1260                 if(!((*ptr == start && is_elision) ||
1261                     (is_end && (*ptr-2) == ip.elision))) {
1262                     *ptr = start;
1263                     TRACE("(%p %p %x): IPv6 component can not have a length of 0.\n",
1264                         ptr, data, flags);
1265                     return FALSE;
1266                 }
1267             }
1268
1269             if(cur_len > 0) {
1270                 /* An IPv6 address can have no more than 8 h16 components. */
1271                 if(ip.h16_count >= 8) {
1272                     *ptr = start;
1273                     TRACE("(%p %p %x): Not a IPv6 address, to many h16 components.\n",
1274                         ptr, data, flags);
1275                     return FALSE;
1276                 }
1277
1278                 ip.components[ip.h16_count].str = cur_start;
1279                 ip.components[ip.h16_count].len = cur_len;
1280
1281                 TRACE("(%p %p %x): Found h16 component %s, len=%d, h16_count=%d\n",
1282                     ptr, data, flags, debugstr_wn(cur_start, cur_len), cur_len,
1283                     ip.h16_count);
1284                 ++ip.h16_count;
1285             }
1286         }
1287
1288         if(is_end)
1289             break;
1290
1291         if(is_elision) {
1292             /* A IPv6 address can only have 1 elision ('::'). */
1293             if(ip.elision) {
1294                 *ptr = start;
1295
1296                 TRACE("(%p %p %x): IPv6 address cannot have 2 elisions.\n",
1297                     ptr, data, flags);
1298                 return FALSE;
1299             }
1300
1301             ip.elision = *ptr;
1302             ++(*ptr);
1303         }
1304
1305         if(is_split)
1306             cur_start = *ptr+1;
1307         else {
1308             if(!check_ipv4address(ptr, TRUE)) {
1309                 if(!is_hexdigit(**ptr)) {
1310                     /* Not a valid character for an IPv6 address. */
1311                     *ptr = start;
1312                     return FALSE;
1313                 }
1314             } else {
1315                 /* Found an IPv4 address. */
1316                 ip.ipv4 = cur_start;
1317                 ip.ipv4_len = *ptr - cur_start;
1318
1319                 TRACE("(%p %p %x): Found an attached IPv4 address %s len=%d.\n",
1320                     ptr, data, flags, debugstr_wn(ip.ipv4, ip.ipv4_len),
1321                     ip.ipv4_len);
1322
1323                 /* IPv4 addresses can only appear at the end of a IPv6. */
1324                 break;
1325             }
1326         }
1327     }
1328
1329     compute_ipv6_comps_size(&ip);
1330
1331     /* Make sure the IPv6 address adds up to 16 bytes. */
1332     if(ip.components_size + ip.elision_size != 16) {
1333         *ptr = start;
1334         TRACE("(%p %p %x): Invalid IPv6 address, did not add up to 16 bytes.\n",
1335             ptr, data, flags);
1336         return FALSE;
1337     }
1338
1339     if(ip.elision_size == 2) {
1340         /* For some reason on Windows if an elision that represents
1341          * only 1 h16 component is encountered at the very begin or
1342          * end of an IPv6 address, Windows does not consider it a
1343          * valid IPv6 address.
1344          *
1345          *  Ex: [::2:3:4:5:6:7] is not valid, even though the sum
1346          *      of all the components == 128bits.
1347          */
1348          if(ip.elision < ip.components[0].str ||
1349             ip.elision > ip.components[ip.h16_count-1].str) {
1350             *ptr = start;
1351             TRACE("(%p %p %x): Invalid IPv6 address. Detected elision of 2 bytes at the beginning or end of the address.\n",
1352                 ptr, data, flags);
1353             return FALSE;
1354         }
1355     }
1356
1357     data->host_type = Uri_HOST_IPV6;
1358     data->has_ipv6 = TRUE;
1359     data->ipv6_address = ip;
1360
1361     TRACE("(%p %p %x): Found valid IPv6 literal %s len=%d\n",
1362         ptr, data, flags, debugstr_wn(start, *ptr-start),
1363         *ptr-start);
1364     return TRUE;
1365 }
1366
1367 /*  IPvFuture  = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) */
1368 static BOOL parse_ipvfuture(const WCHAR **ptr, parse_data *data, DWORD flags) {
1369     const WCHAR *start = *ptr;
1370
1371     /* IPvFuture has to start with a 'v' or 'V'. */
1372     if(**ptr != 'v' && **ptr != 'V')
1373         return FALSE;
1374
1375     /* Following the v their must be atleast 1 hexdigit. */
1376     ++(*ptr);
1377     if(!is_hexdigit(**ptr)) {
1378         *ptr = start;
1379         return FALSE;
1380     }
1381
1382     ++(*ptr);
1383     while(is_hexdigit(**ptr))
1384         ++(*ptr);
1385
1386     /* End of the hexdigit sequence must be a '.' */
1387     if(**ptr != '.') {
1388         *ptr = start;
1389         return FALSE;
1390     }
1391
1392     ++(*ptr);
1393     if(!is_unreserved(**ptr) && !is_subdelim(**ptr) && **ptr != ':') {
1394         *ptr = start;
1395         return FALSE;
1396     }
1397
1398     ++(*ptr);
1399     while(is_unreserved(**ptr) || is_subdelim(**ptr) || **ptr == ':')
1400         ++(*ptr);
1401
1402     data->host_type = Uri_HOST_UNKNOWN;
1403
1404     TRACE("(%p %p %x): Parsed IPvFuture address %s len=%d\n", ptr, data, flags,
1405         debugstr_wn(start, *ptr-start), *ptr-start);
1406
1407     return TRUE;
1408 }
1409
1410 /* IP-literal = "[" ( IPv6address / IPvFuture  ) "]" */
1411 static BOOL parse_ip_literal(const WCHAR **ptr, parse_data *data, DWORD flags) {
1412     data->host = *ptr;
1413
1414     if(**ptr != '[') {
1415         data->host = NULL;
1416         return FALSE;
1417     }
1418
1419     ++(*ptr);
1420     if(!parse_ipv6address(ptr, data, flags)) {
1421         if(!parse_ipvfuture(ptr, data, flags)) {
1422             *ptr = data->host;
1423             data->host = NULL;
1424             return FALSE;
1425         }
1426     }
1427
1428     if(**ptr != ']') {
1429         *ptr = data->host;
1430         data->host = NULL;
1431         return FALSE;
1432     }
1433
1434     ++(*ptr);
1435     if(**ptr == ':') {
1436         ++(*ptr);
1437         /* If a valid port is not found, then let it trickle down to
1438          * parse_reg_name.
1439          */
1440         if(!parse_port(ptr, data, flags)) {
1441             *ptr = data->host;
1442             data->host = NULL;
1443             return FALSE;
1444         }
1445     } else
1446         data->host_len = *ptr - data->host;
1447
1448     return TRUE;
1449 }
1450
1451 /* Parses the host information from the URI.
1452  *
1453  * host = IP-literal / IPv4address / reg-name
1454  */
1455 static BOOL parse_host(const WCHAR **ptr, parse_data *data, DWORD flags) {
1456     if(!parse_ip_literal(ptr, data, flags)) {
1457         if(!parse_ipv4address(ptr, data, flags)) {
1458             if(!parse_reg_name(ptr, data, flags)) {
1459                 TRACE("(%p %p %x): Malformed URI, Unknown host type.\n",
1460                     ptr, data, flags);
1461                 return FALSE;
1462             }
1463         }
1464     }
1465
1466     return TRUE;
1467 }
1468
1469 /* Parses the authority information from the URI.
1470  *
1471  * authority   = [ userinfo "@" ] host [ ":" port ]
1472  */
1473 static BOOL parse_authority(const WCHAR **ptr, parse_data *data, DWORD flags) {
1474     parse_userinfo(ptr, data, flags);
1475
1476     /* Parsing the port will happen during one of the host parsing
1477      * routines (if the URI has a port).
1478      */
1479     if(!parse_host(ptr, data, flags))
1480         return FALSE;
1481
1482     return TRUE;
1483 }
1484
1485 /* Determines how the URI should be parsed after the scheme information.
1486  *
1487  * If the scheme is followed, by "//" then, it is treated as an hierarchical URI
1488  * which then the authority and path information will be parsed out. Otherwise, the
1489  * URI will be treated as an opaque URI which the authority information is not parsed
1490  * out.
1491  *
1492  * RFC 3896 definition of hier-part:
1493  *
1494  * hier-part   = "//" authority path-abempty
1495  *                 / path-absolute
1496  *                 / path-rootless
1497  *                 / path-empty
1498  *
1499  * MSDN opaque URI definition:
1500  *  scheme ":" path [ "#" fragment ]
1501  *
1502  * NOTES:
1503  *  If the URI is of an unknown scheme type and has a "//" following the scheme then it
1504  *  is treated as a hierarchical URI, but, if the CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is
1505  *  set then it is considered an opaque URI reguardless of what follows the scheme information
1506  *  (per MSDN documentation).
1507  */
1508 static BOOL parse_hierpart(const WCHAR **ptr, parse_data *data, DWORD flags) {
1509     /* Checks if the authority information needs to be parsed.
1510      *
1511      * Relative URI's aren't hierarchical URI's, but, they could trick
1512      * "check_hierarchical" into thinking it is, so we need to explicitly
1513      * make sure it's not relative. Also, if the URI is an implicit file
1514      * scheme it might not contain a "//", but, it's considered hierarchical
1515      * anyways. Wildcard Schemes are always considered hierarchical
1516      */
1517     if(data->scheme_type == URL_SCHEME_WILDCARD ||
1518        data->scheme_type == URL_SCHEME_FILE ||
1519        (!data->is_relative && check_hierarchical(ptr))) {
1520         /* Only treat it as a hierarchical URI if the scheme_type is known or
1521          * the Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is not set.
1522          */
1523         if(data->scheme_type != URL_SCHEME_UNKNOWN ||
1524            !(flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES)) {
1525             TRACE("(%p %p %x): Treating URI as an hierarchical URI.\n", ptr, data, flags);
1526             data->is_opaque = FALSE;
1527
1528             /* TODO: Handle hierarchical URI's, parse authority then parse the path. */
1529             if(!parse_authority(ptr, data, flags))
1530                 return FALSE;
1531
1532             return TRUE;
1533         }
1534     }
1535
1536     /* If it reaches here, then the URI will be treated as an opaque
1537      * URI.
1538      */
1539
1540     TRACE("(%p %p %x): Treating URI as an opaque URI.\n", ptr, data, flags);
1541
1542     data->is_opaque = TRUE;
1543     /* TODO: Handle opaque URI's, parse path. */
1544     return TRUE;
1545 }
1546
1547 /* Parses and validates the components of the specified by data->uri
1548  * and stores the information it parses into 'data'.
1549  *
1550  * Returns TRUE if it successfully parsed the URI. False otherwise.
1551  */
1552 static BOOL parse_uri(parse_data *data, DWORD flags) {
1553     const WCHAR *ptr;
1554     const WCHAR **pptr;
1555
1556     ptr = data->uri;
1557     pptr = &ptr;
1558
1559     TRACE("(%p %x): BEGINNING TO PARSE URI %s.\n", data, flags, debugstr_w(data->uri));
1560
1561     if(!parse_scheme(pptr, data, flags))
1562         return FALSE;
1563
1564     if(!parse_hierpart(pptr, data, flags))
1565         return FALSE;
1566
1567     TRACE("(%p %x): FINISHED PARSING URI.\n", data, flags);
1568     return TRUE;
1569 }
1570
1571 /* Canonicalizes the userinfo of the URI represented by the parse_data.
1572  *
1573  * Canonicalization of the userinfo is a simple process. If there are any percent
1574  * encoded characters that fall in the "unreserved" character set, they are decoded
1575  * to their actual value. If a character is not in the "unreserved" or "reserved" sets
1576  * then it is percent encoded. Other than that the characters are copied over without
1577  * change.
1578  */
1579 static BOOL canonicalize_userinfo(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
1580     DWORD i = 0;
1581
1582     uri->userinfo_start = uri->userinfo_split = -1;
1583     uri->userinfo_len = 0;
1584
1585     if(!data->userinfo)
1586         /* URI doesn't have userinfo, so nothing to do here. */
1587         return TRUE;
1588
1589     uri->userinfo_start = uri->canon_len;
1590
1591     while(i < data->userinfo_len) {
1592         if(data->userinfo[i] == ':' && uri->userinfo_split == -1)
1593             /* Windows only considers the first ':' as the delimiter. */
1594             uri->userinfo_split = uri->canon_len - uri->userinfo_start;
1595         else if(data->userinfo[i] == '%') {
1596             /* Only decode % encoded values for known scheme types. */
1597             if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1598                 /* See if the value really needs decoded. */
1599                 WCHAR val = decode_pct_val(data->userinfo + i);
1600                 if(is_unreserved(val)) {
1601                     if(!computeOnly)
1602                         uri->canon_uri[uri->canon_len] = val;
1603
1604                     ++uri->canon_len;
1605
1606                     /* Move pass the hex characters. */
1607                     i += 3;
1608                     continue;
1609                 }
1610             }
1611         } else if(!is_reserved(data->userinfo[i]) && !is_unreserved(data->userinfo[i]) &&
1612                   data->userinfo[i] != '\\') {
1613             /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
1614              * is NOT set.
1615              */
1616             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
1617                 if(!computeOnly)
1618                     pct_encode_val(data->userinfo[i], uri->canon_uri + uri->canon_len);
1619
1620                 uri->canon_len += 3;
1621                 ++i;
1622                 continue;
1623             }
1624         }
1625
1626         if(!computeOnly)
1627             /* Nothing special, so just copy the character over. */
1628             uri->canon_uri[uri->canon_len] = data->userinfo[i];
1629
1630         ++uri->canon_len;
1631         ++i;
1632     }
1633
1634     uri->userinfo_len = uri->canon_len - uri->userinfo_start;
1635     if(!computeOnly)
1636         TRACE("(%p %p %x %d): Canonicalized userinfo, userinfo_start=%d, userinfo=%s, userinfo_split=%d userinfo_len=%d.\n",
1637                 data, uri, flags, computeOnly, uri->userinfo_start, debugstr_wn(uri->canon_uri + uri->userinfo_start, uri->userinfo_len),
1638                 uri->userinfo_split, uri->userinfo_len);
1639
1640     /* Now insert the '@' after the userinfo. */
1641     if(!computeOnly)
1642         uri->canon_uri[uri->canon_len] = '@';
1643
1644     ++uri->canon_len;
1645     return TRUE;
1646 }
1647
1648 /* Attempts to canonicalize a reg_name.
1649  *
1650  * Things that happen:
1651  *  1)  If Uri_CREATE_NO_CANONICALIZE flag is not set, then the reg_name is
1652  *      lower cased. Unless it's an unknown scheme type, which case it's
1653  *      no lower cased reguardless.
1654  *
1655  *  2)  Unreserved % encoded characters are decoded for known
1656  *      scheme types.
1657  *
1658  *  3)  Forbidden characters are % encoded as long as
1659  *      Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS flag is not set and
1660  *      it isn't an unknown scheme type.
1661  *
1662  *  4)  If it's a file scheme and the host is "localhost" it's removed.
1663  */
1664 static BOOL canonicalize_reg_name(const parse_data *data, Uri *uri,
1665                                   DWORD flags, BOOL computeOnly) {
1666     static const WCHAR localhostW[] =
1667             {'l','o','c','a','l','h','o','s','t',0};
1668     const WCHAR *ptr;
1669     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1670
1671     uri->host_start = uri->canon_len;
1672
1673     if(data->scheme_type == URL_SCHEME_FILE &&
1674        data->host_len == lstrlenW(localhostW)) {
1675         if(!StrCmpNIW(data->host, localhostW, data->host_len)) {
1676             uri->host_start = -1;
1677             uri->host_len = 0;
1678             uri->host_type = Uri_HOST_UNKNOWN;
1679             return TRUE;
1680         }
1681     }
1682
1683     for(ptr = data->host; ptr < data->host+data->host_len; ++ptr) {
1684         if(*ptr == '%' && known_scheme) {
1685             WCHAR val = decode_pct_val(ptr);
1686             if(is_unreserved(val)) {
1687                 /* If NO_CANONICALZE is not set, then windows lower cases the
1688                  * decoded value.
1689                  */
1690                 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && isupperW(val)) {
1691                     if(!computeOnly)
1692                         uri->canon_uri[uri->canon_len] = tolowerW(val);
1693                 } else {
1694                     if(!computeOnly)
1695                         uri->canon_uri[uri->canon_len] = val;
1696                 }
1697                 ++uri->canon_len;
1698
1699                 /* Skip past the % encoded character. */
1700                 ptr += 2;
1701                 continue;
1702             } else {
1703                 /* Just copy the % over. */
1704                 if(!computeOnly)
1705                     uri->canon_uri[uri->canon_len] = *ptr;
1706                 ++uri->canon_len;
1707             }
1708         } else if(*ptr == '\\') {
1709             /* Only unknown scheme types could have made it here with a '\\' in the host name. */
1710             if(!computeOnly)
1711                 uri->canon_uri[uri->canon_len] = *ptr;
1712             ++uri->canon_len;
1713         } else if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
1714                   !is_unreserved(*ptr) && !is_reserved(*ptr) && known_scheme) {
1715             if(!computeOnly) {
1716                 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
1717
1718                 /* The percent encoded value gets lower cased also. */
1719                 if(!(flags & Uri_CREATE_NO_CANONICALIZE)) {
1720                     uri->canon_uri[uri->canon_len+1] = tolowerW(uri->canon_uri[uri->canon_len+1]);
1721                     uri->canon_uri[uri->canon_len+2] = tolowerW(uri->canon_uri[uri->canon_len+2]);
1722                 }
1723             }
1724
1725             uri->canon_len += 3;
1726         } else {
1727             if(!computeOnly) {
1728                 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && known_scheme)
1729                     uri->canon_uri[uri->canon_len] = tolowerW(*ptr);
1730                 else
1731                     uri->canon_uri[uri->canon_len] = *ptr;
1732             }
1733
1734             ++uri->canon_len;
1735         }
1736     }
1737
1738     uri->host_len = uri->canon_len - uri->host_start;
1739
1740     if(!computeOnly)
1741         TRACE("(%p %p %x %d): Canonicalize reg_name=%s len=%d\n", data, uri, flags,
1742             computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
1743             uri->host_len);
1744
1745     if(!computeOnly)
1746         find_domain_name(uri->canon_uri+uri->host_start, uri->host_len,
1747             &(uri->domain_offset));
1748
1749     return TRUE;
1750 }
1751
1752 /* Attempts to canonicalize an implicit IPv4 address. */
1753 static BOOL canonicalize_implicit_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
1754     uri->host_start = uri->canon_len;
1755
1756     TRACE("%u\n", data->implicit_ipv4);
1757     /* For unknown scheme types Window's doesn't convert
1758      * the value into an IP address, but, it still considers
1759      * it an IPv4 address.
1760      */
1761     if(data->scheme_type == URL_SCHEME_UNKNOWN) {
1762         if(!computeOnly)
1763             memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
1764         uri->canon_len += data->host_len;
1765     } else {
1766         if(!computeOnly)
1767             uri->canon_len += ui2ipv4(uri->canon_uri+uri->canon_len, data->implicit_ipv4);
1768         else
1769             uri->canon_len += ui2ipv4(NULL, data->implicit_ipv4);
1770     }
1771
1772     uri->host_len = uri->canon_len - uri->host_start;
1773     uri->host_type = Uri_HOST_IPV4;
1774
1775     if(!computeOnly)
1776         TRACE("%p %p %x %d): Canonicalized implicit IP address=%s len=%d\n",
1777             data, uri, flags, computeOnly,
1778             debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
1779             uri->host_len);
1780
1781     return TRUE;
1782 }
1783
1784 /* Attempts to canonicalize an IPv4 address.
1785  *
1786  * If the parse_data represents a URI that has an implicit IPv4 address
1787  * (ex. http://256/, this function will convert 256 into 0.0.1.0). If
1788  * the implicit IP address exceeds the value of UINT_MAX (maximum value
1789  * for an IPv4 address) it's canonicalized as if were a reg-name.
1790  *
1791  * If the parse_data contains a partial or full IPv4 address it normalizes it.
1792  * A partial IPv4 address is something like "192.0" and would be normalized to
1793  * "192.0.0.0". With a full (or partial) IPv4 address like "192.002.01.003" would
1794  * be normalized to "192.2.1.3".
1795  *
1796  * NOTES:
1797  *  Window's ONLY normalizes IPv4 address for known scheme types (one that isn't
1798  *  URL_SCHEME_UNKNOWN). For unknown scheme types, it simply copies the data from
1799  *  the original URI into the canonicalized URI, but, it still recognizes URI's
1800  *  host type as HOST_IPV4.
1801  */
1802 static BOOL canonicalize_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
1803     if(data->has_implicit_ip)
1804         return canonicalize_implicit_ipv4address(data, uri, flags, computeOnly);
1805     else {
1806         uri->host_start = uri->canon_len;
1807
1808         /* Windows only normalizes for known scheme types. */
1809         if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1810             /* parse_data contains a partial or full IPv4 address, so normalize it. */
1811             DWORD i, octetDigitCount = 0, octetCount = 0;
1812             BOOL octetHasDigit = FALSE;
1813
1814             for(i = 0; i < data->host_len; ++i) {
1815                 if(data->host[i] == '0' && !octetHasDigit) {
1816                     /* Can ignore leading zeros if:
1817                      *  1) It isn't the last digit of the octet.
1818                      *  2) i+1 != data->host_len
1819                      *  3) i+1 != '.'
1820                      */
1821                     if(octetDigitCount == 2 ||
1822                        i+1 == data->host_len ||
1823                        data->host[i+1] == '.') {
1824                         if(!computeOnly)
1825                             uri->canon_uri[uri->canon_len] = data->host[i];
1826                         ++uri->canon_len;
1827                         TRACE("Adding zero\n");
1828                     }
1829                 } else if(data->host[i] == '.') {
1830                     if(!computeOnly)
1831                         uri->canon_uri[uri->canon_len] = data->host[i];
1832                     ++uri->canon_len;
1833
1834                     octetDigitCount = 0;
1835                     octetHasDigit = FALSE;
1836                     ++octetCount;
1837                 } else {
1838                     if(!computeOnly)
1839                         uri->canon_uri[uri->canon_len] = data->host[i];
1840                     ++uri->canon_len;
1841
1842                     ++octetDigitCount;
1843                     octetHasDigit = TRUE;
1844                 }
1845             }
1846
1847             /* Make sure the canonicalized IP address has 4 dec-octets.
1848              * If doesn't add "0" ones until there is 4;
1849              */
1850             for( ; octetCount < 3; ++octetCount) {
1851                 if(!computeOnly) {
1852                     uri->canon_uri[uri->canon_len] = '.';
1853                     uri->canon_uri[uri->canon_len+1] = '0';
1854                 }
1855
1856                 uri->canon_len += 2;
1857             }
1858         } else {
1859             /* Windows doesn't normalize addresses in unknown schemes. */
1860             if(!computeOnly)
1861                 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
1862             uri->canon_len += data->host_len;
1863         }
1864
1865         uri->host_len = uri->canon_len - uri->host_start;
1866         if(!computeOnly)
1867             TRACE("(%p %p %x %d): Canonicalized IPv4 address, ip=%s len=%d\n",
1868                 data, uri, flags, computeOnly,
1869                 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
1870                 uri->host_len);
1871     }
1872
1873     return TRUE;
1874 }
1875
1876 /* Attempts to canonicalize the IPv6 address of the URI.
1877  *
1878  * Multiple things happen during the canonicalization of an IPv6 address:
1879  *  1)  Any leading zero's in an h16 component are removed.
1880  *      Ex: [0001:0022::] -> [1:22::]
1881  *
1882  *  2)  The longest sequence of zero h16 components are compressed
1883  *      into a "::" (elision). If there's a tie, the first is choosen.
1884  *
1885  *      Ex: [0:0:0:0:1:6:7:8]   -> [::1:6:7:8]
1886  *          [0:0:0:0:1:2::]     -> [::1:2:0:0]
1887  *          [0:0:1:2:0:0:7:8]   -> [::1:2:0:0:7:8]
1888  *
1889  *  3)  If an IPv4 address is attached to the IPv6 address, it's
1890  *      also normalized.
1891  *      Ex: [::001.002.022.000] -> [::1.2.22.0]
1892  *
1893  *  4)  If an elision is present, but, only represents 1 h16 component
1894  *      it's expanded.
1895  *
1896  *      Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
1897  *
1898  *  5)  If the IPv6 address contains an IPv4 address and there exists
1899  *      at least 1 non-zero h16 component the IPv4 address is converted
1900  *      into two h16 components, otherwise it's normalized and kept as is.
1901  *
1902  *      Ex: [::192.200.003.4]       -> [::192.200.3.4]
1903  *          [ffff::192.200.003.4]   -> [ffff::c0c8:3041]
1904  *
1905  * NOTE:
1906  *  For unknown scheme types Windows simply copies the address over without any
1907  *  changes.
1908  *
1909  *  IPv4 address can be included in an elision if all its components are 0's.
1910  */
1911 static BOOL canonicalize_ipv6address(const parse_data *data, Uri *uri,
1912                                      DWORD flags, BOOL computeOnly) {
1913     uri->host_start = uri->canon_len;
1914
1915     if(data->scheme_type == URL_SCHEME_UNKNOWN) {
1916         if(!computeOnly)
1917             memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
1918         uri->canon_len += data->host_len;
1919     } else {
1920         USHORT values[8];
1921         INT elision_start;
1922         DWORD i, elision_len;
1923
1924         if(!ipv6_to_number(&(data->ipv6_address), values)) {
1925             TRACE("(%p %p %x %d): Failed to compute numerical value for IPv6 address.\n",
1926                 data, uri, flags, computeOnly);
1927             return FALSE;
1928         }
1929
1930         if(!computeOnly)
1931             uri->canon_uri[uri->canon_len] = '[';
1932         ++uri->canon_len;
1933
1934         /* Find where the elision should occur (if any). */
1935         compute_elision_location(&(data->ipv6_address), values, &elision_start, &elision_len);
1936
1937         TRACE("%p %p %x %d): Elision starts at %d, len=%u\n", data, uri, flags,
1938             computeOnly, elision_start, elision_len);
1939
1940         for(i = 0; i < 8; ++i) {
1941             BOOL in_elision = (elision_start > -1 && i >= elision_start &&
1942                                i < elision_start+elision_len);
1943             BOOL do_ipv4 = (i == 6 && data->ipv6_address.ipv4 && !in_elision &&
1944                             data->ipv6_address.h16_count == 0);
1945
1946             if(i == elision_start) {
1947                 if(!computeOnly) {
1948                     uri->canon_uri[uri->canon_len] = ':';
1949                     uri->canon_uri[uri->canon_len+1] = ':';
1950                 }
1951                 uri->canon_len += 2;
1952             }
1953
1954             /* We can ignore the current component if we're in the elision. */
1955             if(in_elision)
1956                 continue;
1957
1958             /* We only add a ':' if we're not at i == 0, or when we're at
1959              * the very end of elision range since the ':' colon was handled
1960              * earlier. Otherwise we would end up with ":::" after elision.
1961              */
1962             if(i != 0 && !(elision_start > -1 && i == elision_start+elision_len)) {
1963                 if(!computeOnly)
1964                     uri->canon_uri[uri->canon_len] = ':';
1965                 ++uri->canon_len;
1966             }
1967
1968             if(do_ipv4) {
1969                 UINT val;
1970                 DWORD len;
1971
1972                 /* Combine the two parts of the IPv4 address values. */
1973                 val = values[i];
1974                 val <<= 16;
1975                 val += values[i+1];
1976
1977                 if(!computeOnly)
1978                     len = ui2ipv4(uri->canon_uri+uri->canon_len, val);
1979                 else
1980                     len = ui2ipv4(NULL, val);
1981
1982                 uri->canon_len += len;
1983                 ++i;
1984             } else {
1985                 /* Write a regular h16 component to the URI. */
1986
1987                 /* Short circuit for the trivial case. */
1988                 if(values[i] == 0) {
1989                     if(!computeOnly)
1990                         uri->canon_uri[uri->canon_len] = '0';
1991                     ++uri->canon_len;
1992                 } else {
1993                     static const WCHAR formatW[] = {'%','x',0};
1994
1995                     if(!computeOnly)
1996                         uri->canon_len += sprintfW(uri->canon_uri+uri->canon_len,
1997                                             formatW, values[i]);
1998                     else {
1999                         WCHAR tmp[5];
2000                         uri->canon_len += sprintfW(tmp, formatW, values[i]);
2001                     }
2002                 }
2003             }
2004         }
2005
2006         /* Add the closing ']'. */
2007         if(!computeOnly)
2008             uri->canon_uri[uri->canon_len] = ']';
2009         ++uri->canon_len;
2010     }
2011
2012     uri->host_len = uri->canon_len - uri->host_start;
2013
2014     if(!computeOnly)
2015         TRACE("(%p %p %x %d): Canonicalized IPv6 address %s, len=%d\n", data, uri, flags,
2016             computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2017             uri->host_len);
2018
2019     return TRUE;
2020 }
2021
2022 /* Attempts to canonicalize the host of the URI (if any). */
2023 static BOOL canonicalize_host(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2024     uri->host_start = -1;
2025     uri->host_len = 0;
2026     uri->domain_offset = -1;
2027
2028     if(data->host) {
2029         switch(data->host_type) {
2030         case Uri_HOST_DNS:
2031             uri->host_type = Uri_HOST_DNS;
2032             if(!canonicalize_reg_name(data, uri, flags, computeOnly))
2033                 return FALSE;
2034
2035             break;
2036         case Uri_HOST_IPV4:
2037             uri->host_type = Uri_HOST_IPV4;
2038             if(!canonicalize_ipv4address(data, uri, flags, computeOnly))
2039                 return FALSE;
2040
2041             break;
2042         case Uri_HOST_IPV6:
2043             if(!canonicalize_ipv6address(data, uri, flags, computeOnly))
2044                 return FALSE;
2045
2046             uri->host_type = Uri_HOST_IPV6;
2047             break;
2048         case Uri_HOST_UNKNOWN:
2049             if(data->host_len > 0 || data->scheme_type != URL_SCHEME_FILE) {
2050                 uri->host_start = uri->canon_len;
2051
2052                 /* Nothing happens to unknown host types. */
2053                 if(!computeOnly)
2054                     memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2055                 uri->canon_len += data->host_len;
2056                 uri->host_len = data->host_len;
2057             }
2058
2059             uri->host_type = Uri_HOST_UNKNOWN;
2060             break;
2061         default:
2062             FIXME("(%p %p %x %d): Canonicalization for host type %d not supported.\n", data,
2063                     uri, flags, computeOnly, data->host_type);
2064             return FALSE;
2065        }
2066    }
2067
2068    return TRUE;
2069 }
2070
2071 static BOOL canonicalize_port(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2072     BOOL has_default_port = FALSE;
2073     USHORT default_port = 0;
2074     DWORD i;
2075
2076     uri->has_port = FALSE;
2077
2078     /* Check if the scheme has a default port. */
2079     for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
2080         if(default_ports[i].scheme == data->scheme_type) {
2081             has_default_port = TRUE;
2082             default_port = default_ports[i].port;
2083             break;
2084         }
2085     }
2086
2087     if(data->port || has_default_port)
2088         uri->has_port = TRUE;
2089
2090     /* Possible cases:
2091      *  1)  Has a port which is the default port.
2092      *  2)  Has a port (not the default).
2093      *  3)  Doesn't have a port, but, scheme has a default port.
2094      *  4)  No port.
2095      */
2096     if(has_default_port && data->port && data->port_value == default_port) {
2097         /* If it's the default port and this flag isn't set, don't do anything. */
2098         if(flags & Uri_CREATE_NO_CANONICALIZE) {
2099             /* Copy the original port over. */
2100             if(!computeOnly) {
2101                 uri->canon_uri[uri->canon_len] = ':';
2102                 memcpy(uri->canon_uri+uri->canon_len+1, data->port, data->port_len*sizeof(WCHAR));
2103             }
2104             uri->canon_len += data->port_len+1;
2105         }
2106
2107         uri->port = default_port;
2108     } else if(data->port) {
2109         if(!computeOnly)
2110             uri->canon_uri[uri->canon_len] = ':';
2111         ++uri->canon_len;
2112
2113         if(flags & Uri_CREATE_NO_CANONICALIZE) {
2114             /* Copy the original over without changes. */
2115             if(!computeOnly)
2116                 memcpy(uri->canon_uri+uri->canon_len, data->port, data->port_len*sizeof(WCHAR));
2117             uri->canon_len += data->port_len;
2118         } else {
2119             const WCHAR formatW[] = {'%','u',0};
2120             INT len = 0;
2121             if(!computeOnly)
2122                 len = sprintfW(uri->canon_uri+uri->canon_len, formatW, data->port_value);
2123             else {
2124                 WCHAR tmp[6];
2125                 len = sprintfW(tmp, formatW, data->port_value);
2126             }
2127             uri->canon_len += len;
2128         }
2129
2130         uri->port = data->port_value;
2131     } else if(has_default_port)
2132         uri->port = default_port;
2133
2134     return TRUE;
2135 }
2136
2137 /* Canonicalizes the authority of the URI represented by the parse_data. */
2138 static BOOL canonicalize_authority(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2139     uri->authority_start = uri->canon_len;
2140     uri->authority_len = 0;
2141
2142     if(!canonicalize_userinfo(data, uri, flags, computeOnly))
2143         return FALSE;
2144
2145     if(!canonicalize_host(data, uri, flags, computeOnly))
2146         return FALSE;
2147
2148     if(!canonicalize_port(data, uri, flags, computeOnly))
2149         return FALSE;
2150
2151     if(uri->host_start != -1)
2152         uri->authority_len = uri->canon_len - uri->authority_start;
2153     else
2154         uri->authority_start = -1;
2155
2156     return TRUE;
2157 }
2158
2159 /* Determines how the URI represented by the parse_data should be canonicalized.
2160  *
2161  * Essentially, if the parse_data represents an hierarchical URI then it calls
2162  * canonicalize_authority and the canonicalization functions for the path. If the
2163  * URI is opaque it canonicalizes the path of the URI.
2164  */
2165 static BOOL canonicalize_hierpart(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2166     if(!data->is_opaque) {
2167         /* "//" is only added for non-wildcard scheme types. */
2168         if(data->scheme_type != URL_SCHEME_WILDCARD) {
2169             if(!computeOnly) {
2170                 INT pos = uri->canon_len;
2171
2172                 uri->canon_uri[pos] = '/';
2173                 uri->canon_uri[pos+1] = '/';
2174            }
2175            uri->canon_len += 2;
2176         }
2177
2178         if(!canonicalize_authority(data, uri, flags, computeOnly))
2179             return FALSE;
2180
2181        /* TODO: Canonicalize the path of the URI. */
2182
2183     } else {
2184         /* Opaque URI's don't have an authority. */
2185         uri->userinfo_start = uri->userinfo_split = -1;
2186         uri->userinfo_len = 0;
2187         uri->host_start = -1;
2188         uri->host_len = 0;
2189         uri->host_type = Uri_HOST_UNKNOWN;
2190         uri->has_port = FALSE;
2191         uri->authority_start = -1;
2192         uri->authority_len = 0;
2193         uri->domain_offset = -1;
2194     }
2195
2196     return TRUE;
2197 }
2198
2199 /* Canonicalizes the scheme information specified in the parse_data using the specified flags. */
2200 static BOOL canonicalize_scheme(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2201     uri->scheme_start = -1;
2202     uri->scheme_len = 0;
2203
2204     if(!data->scheme) {
2205         /* The only type of URI that doesn't have to have a scheme is a relative
2206          * URI.
2207          */
2208         if(!data->is_relative) {
2209             FIXME("(%p %p %x): Unable to determine the scheme type of %s.\n", data,
2210                     uri, flags, debugstr_w(data->uri));
2211             return FALSE;
2212         }
2213     } else {
2214         if(!computeOnly) {
2215             DWORD i;
2216             INT pos = uri->canon_len;
2217
2218             for(i = 0; i < data->scheme_len; ++i) {
2219                 /* Scheme name must be lower case after canonicalization. */
2220                 uri->canon_uri[i + pos] = tolowerW(data->scheme[i]);
2221             }
2222
2223             uri->canon_uri[i + pos] = ':';
2224             uri->scheme_start = pos;
2225
2226             TRACE("(%p %p %x): Canonicalized scheme=%s, len=%d.\n", data, uri, flags,
2227                     debugstr_wn(uri->canon_uri,  uri->scheme_len), data->scheme_len);
2228         }
2229
2230         /* This happens in both computation modes. */
2231         uri->canon_len += data->scheme_len + 1;
2232         uri->scheme_len = data->scheme_len;
2233     }
2234     return TRUE;
2235 }
2236
2237 /* Compute's what the length of the URI specified by the parse_data will be
2238  * after canonicalization occurs using the specified flags.
2239  *
2240  * This function will return a non-zero value indicating the length of the canonicalized
2241  * URI, or -1 on error.
2242  */
2243 static int compute_canonicalized_length(const parse_data *data, DWORD flags) {
2244     Uri uri;
2245
2246     memset(&uri, 0, sizeof(Uri));
2247
2248     TRACE("(%p %x): Beginning to compute canonicalized length for URI %s\n", data, flags,
2249             debugstr_w(data->uri));
2250
2251     if(!canonicalize_scheme(data, &uri, flags, TRUE)) {
2252         ERR("(%p %x): Failed to compute URI scheme length.\n", data, flags);
2253         return -1;
2254     }
2255
2256     if(!canonicalize_hierpart(data, &uri, flags, TRUE)) {
2257         ERR("(%p %x): Failed to compute URI hierpart length.\n", data, flags);
2258         return -1;
2259     }
2260
2261     TRACE("(%p %x): Finished computing canonicalized URI length. length=%d\n", data, flags, uri.canon_len);
2262
2263     return uri.canon_len;
2264 }
2265
2266 /* Canonicalizes the URI data specified in the parse_data, using the given flags. If the
2267  * canonicalization succeededs it will store all the canonicalization information
2268  * in the pointer to the Uri.
2269  *
2270  * To canonicalize a URI this function first computes what the length of the URI
2271  * specified by the parse_data will be. Once this is done it will then perfom the actual
2272  * canonicalization of the URI.
2273  */
2274 static HRESULT canonicalize_uri(const parse_data *data, Uri *uri, DWORD flags) {
2275     INT len;
2276
2277     uri->canon_uri = NULL;
2278     len = uri->canon_size = uri->canon_len = 0;
2279
2280     TRACE("(%p %p %x): beginning to canonicalize URI %s.\n", data, uri, flags, debugstr_w(data->uri));
2281
2282     /* First try to compute the length of the URI. */
2283     len = compute_canonicalized_length(data, flags);
2284     if(len == -1) {
2285         ERR("(%p %p %x): Could not compute the canonicalized length of %s.\n", data, uri, flags,
2286                 debugstr_w(data->uri));
2287         return E_INVALIDARG;
2288     }
2289
2290     uri->canon_uri = heap_alloc((len+1)*sizeof(WCHAR));
2291     if(!uri->canon_uri)
2292         return E_OUTOFMEMORY;
2293
2294     if(!canonicalize_scheme(data, uri, flags, FALSE)) {
2295         ERR("(%p %p %x): Unable to canonicalize the scheme of the URI.\n", data, uri, flags);
2296         heap_free(uri->canon_uri);
2297         return E_INVALIDARG;
2298     }
2299     uri->scheme_type = data->scheme_type;
2300
2301     if(!canonicalize_hierpart(data, uri, flags, FALSE)) {
2302         ERR("(%p %p %x): Unable to canonicalize the heirpart of the URI\n", data, uri, flags);
2303         heap_free(uri->canon_uri);
2304         return E_INVALIDARG;
2305     }
2306
2307     uri->canon_uri[uri->canon_len] = '\0';
2308     TRACE("(%p %p %x): finished canonicalizing the URI. uri=%s\n", data, uri, flags, debugstr_w(uri->canon_uri));
2309
2310     return S_OK;
2311 }
2312
2313 #define URI(x)         ((IUri*)  &(x)->lpIUriVtbl)
2314 #define URIBUILDER(x)  ((IUriBuilder*)  &(x)->lpIUriBuilderVtbl)
2315
2316 #define URI_THIS(iface) DEFINE_THIS(Uri, IUri, iface)
2317
2318 static HRESULT WINAPI Uri_QueryInterface(IUri *iface, REFIID riid, void **ppv)
2319 {
2320     Uri *This = URI_THIS(iface);
2321
2322     if(IsEqualGUID(&IID_IUnknown, riid)) {
2323         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
2324         *ppv = URI(This);
2325     }else if(IsEqualGUID(&IID_IUri, riid)) {
2326         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
2327         *ppv = URI(This);
2328     }else {
2329         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
2330         *ppv = NULL;
2331         return E_NOINTERFACE;
2332     }
2333
2334     IUnknown_AddRef((IUnknown*)*ppv);
2335     return S_OK;
2336 }
2337
2338 static ULONG WINAPI Uri_AddRef(IUri *iface)
2339 {
2340     Uri *This = URI_THIS(iface);
2341     LONG ref = InterlockedIncrement(&This->ref);
2342
2343     TRACE("(%p) ref=%d\n", This, ref);
2344
2345     return ref;
2346 }
2347
2348 static ULONG WINAPI Uri_Release(IUri *iface)
2349 {
2350     Uri *This = URI_THIS(iface);
2351     LONG ref = InterlockedDecrement(&This->ref);
2352
2353     TRACE("(%p) ref=%d\n", This, ref);
2354
2355     if(!ref) {
2356         SysFreeString(This->raw_uri);
2357         heap_free(This->canon_uri);
2358         heap_free(This);
2359     }
2360
2361     return ref;
2362 }
2363
2364 static HRESULT WINAPI Uri_GetPropertyBSTR(IUri *iface, Uri_PROPERTY uriProp, BSTR *pbstrProperty, DWORD dwFlags)
2365 {
2366     Uri *This = URI_THIS(iface);
2367     HRESULT hres;
2368     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
2369
2370     if(!pbstrProperty)
2371         return E_POINTER;
2372
2373     if(uriProp > Uri_PROPERTY_STRING_LAST) {
2374         /* Windows allocates an empty BSTR for invalid Uri_PROPERTY's. */
2375         *pbstrProperty = SysAllocStringLen(NULL, 0);
2376         if(!(*pbstrProperty))
2377             return E_OUTOFMEMORY;
2378
2379         /* It only returns S_FALSE for the ZONE property... */
2380         if(uriProp == Uri_PROPERTY_ZONE)
2381             return S_FALSE;
2382         else
2383             return S_OK;
2384     }
2385
2386     /* Don't have support for flags yet. */
2387     if(dwFlags) {
2388         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
2389         return E_NOTIMPL;
2390     }
2391
2392     switch(uriProp) {
2393     case Uri_PROPERTY_AUTHORITY:
2394         if(This->authority_start > -1) {
2395             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->authority_start, This->authority_len);
2396             hres = S_OK;
2397         } else {
2398             *pbstrProperty = SysAllocStringLen(NULL, 0);
2399             hres = S_FALSE;
2400         }
2401
2402         if(!(*pbstrProperty))
2403             hres = E_OUTOFMEMORY;
2404
2405         break;
2406     case Uri_PROPERTY_DOMAIN:
2407         if(This->domain_offset > -1) {
2408             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+This->domain_offset,
2409                                                This->host_len-This->domain_offset);
2410             hres = S_OK;
2411         } else {
2412             *pbstrProperty = SysAllocStringLen(NULL, 0);
2413             hres = S_FALSE;
2414         }
2415
2416         if(!(*pbstrProperty))
2417             hres = E_OUTOFMEMORY;
2418
2419         break;
2420     case Uri_PROPERTY_HOST:
2421         if(This->host_start > -1) {
2422             /* The '[' and ']' aren't included for IPv6 addresses. */
2423             if(This->host_type == Uri_HOST_IPV6)
2424                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+1, This->host_len-2);
2425             else
2426                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start, This->host_len);
2427
2428             hres = S_OK;
2429         } else {
2430             *pbstrProperty = SysAllocStringLen(NULL, 0);
2431             hres = S_FALSE;
2432         }
2433
2434         if(!(*pbstrProperty))
2435             hres = E_OUTOFMEMORY;
2436
2437         break;
2438     case Uri_PROPERTY_PASSWORD:
2439         if(This->userinfo_split > -1) {
2440             *pbstrProperty = SysAllocStringLen(
2441                 This->canon_uri+This->userinfo_start+This->userinfo_split+1,
2442                 This->userinfo_len-This->userinfo_split-1);
2443             hres = S_OK;
2444         } else {
2445             *pbstrProperty = SysAllocStringLen(NULL, 0);
2446             hres = S_FALSE;
2447         }
2448
2449         if(!(*pbstrProperty))
2450             return E_OUTOFMEMORY;
2451
2452         break;
2453     case Uri_PROPERTY_RAW_URI:
2454         *pbstrProperty = SysAllocString(This->raw_uri);
2455         if(!(*pbstrProperty))
2456             hres = E_OUTOFMEMORY;
2457         else
2458             hres = S_OK;
2459         break;
2460     case Uri_PROPERTY_SCHEME_NAME:
2461         if(This->scheme_start > -1) {
2462             *pbstrProperty = SysAllocStringLen(This->canon_uri + This->scheme_start, This->scheme_len);
2463             hres = S_OK;
2464         } else {
2465             *pbstrProperty = SysAllocStringLen(NULL, 0);
2466             hres = S_FALSE;
2467         }
2468
2469         if(!(*pbstrProperty))
2470             hres = E_OUTOFMEMORY;
2471
2472         break;
2473     case Uri_PROPERTY_USER_INFO:
2474         if(This->userinfo_start > -1) {
2475             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->userinfo_start, This->userinfo_len);
2476             hres = S_OK;
2477         } else {
2478             *pbstrProperty = SysAllocStringLen(NULL, 0);
2479             hres = S_FALSE;
2480         }
2481
2482         if(!(*pbstrProperty))
2483             hres = E_OUTOFMEMORY;
2484
2485         break;
2486     case Uri_PROPERTY_USER_NAME:
2487         if(This->userinfo_start > -1) {
2488             /* If userinfo_split is set, that means a password exists
2489              * so the username is only from userinfo_start to userinfo_split.
2490              */
2491             if(This->userinfo_split > -1) {
2492                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_split);
2493                 hres = S_OK;
2494             } else {
2495                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_len);
2496                 hres = S_OK;
2497             }
2498         } else {
2499             *pbstrProperty = SysAllocStringLen(NULL, 0);
2500             hres = S_FALSE;
2501         }
2502
2503         if(!(*pbstrProperty))
2504             return E_OUTOFMEMORY;
2505
2506         break;
2507     default:
2508         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
2509         hres = E_NOTIMPL;
2510     }
2511
2512     return hres;
2513 }
2514
2515 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
2516 {
2517     Uri *This = URI_THIS(iface);
2518     HRESULT hres;
2519     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
2520
2521     if(!pcchProperty)
2522         return E_INVALIDARG;
2523
2524     /* Can only return a length for a property if it's a string. */
2525     if(uriProp > Uri_PROPERTY_STRING_LAST)
2526         return E_INVALIDARG;
2527
2528     /* Don't have support for flags yet. */
2529     if(dwFlags) {
2530         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
2531         return E_NOTIMPL;
2532     }
2533
2534     switch(uriProp) {
2535     case Uri_PROPERTY_AUTHORITY:
2536         *pcchProperty = This->authority_len;
2537         hres = (This->authority_start > -1) ? S_OK : S_FALSE;
2538         break;
2539     case Uri_PROPERTY_DOMAIN:
2540         if(This->domain_offset > -1)
2541             *pcchProperty = This->host_len - This->domain_offset;
2542         else
2543             *pcchProperty = 0;
2544
2545         hres = (This->domain_offset > -1) ? S_OK : S_FALSE;
2546         break;
2547     case Uri_PROPERTY_HOST:
2548         *pcchProperty = This->host_len;
2549
2550         /* '[' and ']' aren't included in the length. */
2551         if(This->host_type == Uri_HOST_IPV6)
2552             *pcchProperty -= 2;
2553
2554         hres = (This->host_start > -1) ? S_OK : S_FALSE;
2555         break;
2556     case Uri_PROPERTY_PASSWORD:
2557         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_len-This->userinfo_split-1 : 0;
2558         hres = (This->userinfo_split > -1) ? S_OK : S_FALSE;
2559         break;
2560     case Uri_PROPERTY_RAW_URI:
2561         *pcchProperty = SysStringLen(This->raw_uri);
2562         hres = S_OK;
2563         break;
2564     case Uri_PROPERTY_SCHEME_NAME:
2565         *pcchProperty = This->scheme_len;
2566         hres = (This->scheme_start > -1) ? S_OK : S_FALSE;
2567         break;
2568     case Uri_PROPERTY_USER_INFO:
2569         *pcchProperty = This->userinfo_len;
2570         hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
2571         break;
2572     case Uri_PROPERTY_USER_NAME:
2573         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_split : This->userinfo_len;
2574         hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
2575         break;
2576     default:
2577         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
2578         hres = E_NOTIMPL;
2579     }
2580
2581     return hres;
2582 }
2583
2584 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
2585 {
2586     Uri *This = URI_THIS(iface);
2587     HRESULT hres;
2588
2589     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
2590
2591     if(!pcchProperty)
2592         return E_INVALIDARG;
2593
2594     /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
2595      * From what I can tell, instead of checking which URLZONE the URI belongs to it
2596      * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
2597      * function.
2598      */
2599     if(uriProp == Uri_PROPERTY_ZONE) {
2600         *pcchProperty = URLZONE_INVALID;
2601         return E_NOTIMPL;
2602     }
2603
2604     if(uriProp < Uri_PROPERTY_DWORD_START) {
2605         *pcchProperty = 0;
2606         return E_INVALIDARG;
2607     }
2608
2609     switch(uriProp) {
2610     case Uri_PROPERTY_HOST_TYPE:
2611         *pcchProperty = This->host_type;
2612         hres = S_OK;
2613         break;
2614     case Uri_PROPERTY_PORT:
2615         if(!This->has_port) {
2616             *pcchProperty = 0;
2617             hres = S_FALSE;
2618         } else {
2619             *pcchProperty = This->port;
2620             hres = S_OK;
2621         }
2622
2623         break;
2624     case Uri_PROPERTY_SCHEME:
2625         *pcchProperty = This->scheme_type;
2626         hres = S_OK;
2627         break;
2628     default:
2629         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
2630         hres = E_NOTIMPL;
2631     }
2632
2633     return hres;
2634 }
2635
2636 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
2637 {
2638     Uri *This = URI_THIS(iface);
2639     FIXME("(%p)->(%d %p)\n", This, uriProp, pfHasProperty);
2640
2641     if(!pfHasProperty)
2642         return E_INVALIDARG;
2643
2644     return E_NOTIMPL;
2645 }
2646
2647 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
2648 {
2649     Uri *This = URI_THIS(iface);
2650     FIXME("(%p)->(%p)\n", This, pstrAbsoluteUri);
2651
2652     if(!pstrAbsoluteUri)
2653         return E_POINTER;
2654
2655     return E_NOTIMPL;
2656 }
2657
2658 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
2659 {
2660     TRACE("(%p)->(%p)\n", iface, pstrAuthority);
2661     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_AUTHORITY, pstrAuthority, 0);
2662 }
2663
2664 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
2665 {
2666     Uri *This = URI_THIS(iface);
2667     FIXME("(%p)->(%p)\n", This, pstrDisplayUri);
2668
2669     if(!pstrDisplayUri)
2670         return E_POINTER;
2671
2672     return E_NOTIMPL;
2673 }
2674
2675 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
2676 {
2677     TRACE("(%p)->(%p)\n", iface, pstrDomain);
2678     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_DOMAIN, pstrDomain, 0);
2679 }
2680
2681 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
2682 {
2683     Uri *This = URI_THIS(iface);
2684     FIXME("(%p)->(%p)\n", This, pstrExtension);
2685
2686     if(!pstrExtension)
2687         return E_POINTER;
2688
2689     return E_NOTIMPL;
2690 }
2691
2692 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
2693 {
2694     Uri *This = URI_THIS(iface);
2695     FIXME("(%p)->(%p)\n", This, pstrFragment);
2696
2697     if(!pstrFragment)
2698         return E_POINTER;
2699
2700     return E_NOTIMPL;
2701 }
2702
2703 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
2704 {
2705     TRACE("(%p)->(%p)\n", iface, pstrHost);
2706     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_HOST, pstrHost, 0);
2707 }
2708
2709 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
2710 {
2711     TRACE("(%p)->(%p)\n", iface, pstrPassword);
2712     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_PASSWORD, pstrPassword, 0);
2713 }
2714
2715 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
2716 {
2717     Uri *This = URI_THIS(iface);
2718     FIXME("(%p)->(%p)\n", This, pstrPath);
2719
2720     if(!pstrPath)
2721         return E_POINTER;
2722
2723     return E_NOTIMPL;
2724 }
2725
2726 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
2727 {
2728     Uri *This = URI_THIS(iface);
2729     FIXME("(%p)->(%p)\n", This, pstrPathAndQuery);
2730
2731     if(!pstrPathAndQuery)
2732         return E_POINTER;
2733
2734     return E_NOTIMPL;
2735 }
2736
2737 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
2738 {
2739     Uri *This = URI_THIS(iface);
2740     FIXME("(%p)->(%p)\n", This, pstrQuery);
2741
2742     if(!pstrQuery)
2743         return E_POINTER;
2744
2745     return E_NOTIMPL;
2746 }
2747
2748 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
2749 {
2750     Uri *This = URI_THIS(iface);
2751     TRACE("(%p)->(%p)\n", This, pstrRawUri);
2752
2753     /* Just forward the call to GetPropertyBSTR. */
2754     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
2755 }
2756
2757 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
2758 {
2759     Uri *This = URI_THIS(iface);
2760     TRACE("(%p)->(%p)\n", This, pstrSchemeName);
2761     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_SCHEME_NAME, pstrSchemeName, 0);
2762 }
2763
2764 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
2765 {
2766     TRACE("(%p)->(%p)\n", iface, pstrUserInfo);
2767     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_INFO, pstrUserInfo, 0);
2768 }
2769
2770 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
2771 {
2772     TRACE("(%p)->(%p)\n", iface, pstrUserName);
2773     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_NAME, pstrUserName, 0);
2774 }
2775
2776 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
2777 {
2778     TRACE("(%p)->(%p)\n", iface, pdwHostType);
2779     return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_HOST_TYPE, pdwHostType, 0);
2780 }
2781
2782 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
2783 {
2784     TRACE("(%p)->(%p)\n", iface, pdwPort);
2785     return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_PORT, pdwPort, 0);
2786 }
2787
2788 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
2789 {
2790     Uri *This = URI_THIS(iface);
2791     TRACE("(%p)->(%p)\n", This, pdwScheme);
2792     return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_SCHEME, pdwScheme, 0);
2793 }
2794
2795 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
2796 {
2797     TRACE("(%p)->(%p)\n", iface, pdwZone);
2798     return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_ZONE,pdwZone, 0);
2799 }
2800
2801 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
2802 {
2803     Uri *This = URI_THIS(iface);
2804     FIXME("(%p)->(%p)\n", This, pdwProperties);
2805
2806     if(!pdwProperties)
2807         return E_INVALIDARG;
2808
2809     return E_NOTIMPL;
2810 }
2811
2812 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
2813 {
2814     Uri *This = URI_THIS(iface);
2815     TRACE("(%p)->(%p %p)\n", This, pUri, pfEqual);
2816
2817     if(!pfEqual)
2818         return E_POINTER;
2819
2820     if(!pUri) {
2821         *pfEqual = FALSE;
2822
2823         /* For some reason Windows returns S_OK here... */
2824         return S_OK;
2825     }
2826
2827     FIXME("(%p)->(%p %p)\n", This, pUri, pfEqual);
2828     return E_NOTIMPL;
2829 }
2830
2831 #undef URI_THIS
2832
2833 static const IUriVtbl UriVtbl = {
2834     Uri_QueryInterface,
2835     Uri_AddRef,
2836     Uri_Release,
2837     Uri_GetPropertyBSTR,
2838     Uri_GetPropertyLength,
2839     Uri_GetPropertyDWORD,
2840     Uri_HasProperty,
2841     Uri_GetAbsoluteUri,
2842     Uri_GetAuthority,
2843     Uri_GetDisplayUri,
2844     Uri_GetDomain,
2845     Uri_GetExtension,
2846     Uri_GetFragment,
2847     Uri_GetHost,
2848     Uri_GetPassword,
2849     Uri_GetPath,
2850     Uri_GetPathAndQuery,
2851     Uri_GetQuery,
2852     Uri_GetRawUri,
2853     Uri_GetSchemeName,
2854     Uri_GetUserInfo,
2855     Uri_GetUserName,
2856     Uri_GetHostType,
2857     Uri_GetPort,
2858     Uri_GetScheme,
2859     Uri_GetZone,
2860     Uri_GetProperties,
2861     Uri_IsEqual
2862 };
2863
2864 /***********************************************************************
2865  *           CreateUri (urlmon.@)
2866  */
2867 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
2868 {
2869     Uri *ret;
2870     HRESULT hr;
2871     parse_data data;
2872
2873     TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
2874
2875     if(!ppURI)
2876         return E_INVALIDARG;
2877
2878     if(!pwzURI) {
2879         *ppURI = NULL;
2880         return E_INVALIDARG;
2881     }
2882
2883     ret = heap_alloc(sizeof(Uri));
2884     if(!ret)
2885         return E_OUTOFMEMORY;
2886
2887     ret->lpIUriVtbl = &UriVtbl;
2888     ret->ref = 1;
2889
2890     /* Create a copy of pwzURI and store it as the raw_uri. */
2891     ret->raw_uri = SysAllocString(pwzURI);
2892     if(!ret->raw_uri) {
2893         heap_free(ret);
2894         return E_OUTOFMEMORY;
2895     }
2896
2897     memset(&data, 0, sizeof(parse_data));
2898     data.uri = ret->raw_uri;
2899
2900     /* Validate and parse the URI into it's components. */
2901     if(!parse_uri(&data, dwFlags)) {
2902         /* Encountered an unsupported or invalid URI */
2903         SysFreeString(ret->raw_uri);
2904         heap_free(ret);
2905         *ppURI = NULL;
2906         return E_INVALIDARG;
2907     }
2908
2909     /* Canonicalize the URI. */
2910     hr = canonicalize_uri(&data, ret, dwFlags);
2911     if(FAILED(hr)) {
2912         SysFreeString(ret->raw_uri);
2913         heap_free(ret);
2914         *ppURI = NULL;
2915         return hr;
2916     }
2917
2918     *ppURI = URI(ret);
2919     return S_OK;
2920 }
2921
2922 #define URIBUILDER_THIS(iface) DEFINE_THIS(UriBuilder, IUriBuilder, iface)
2923
2924 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
2925 {
2926     UriBuilder *This = URIBUILDER_THIS(iface);
2927
2928     if(IsEqualGUID(&IID_IUnknown, riid)) {
2929         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
2930         *ppv = URIBUILDER(This);
2931     }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
2932         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
2933         *ppv = URIBUILDER(This);
2934     }else {
2935         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
2936         *ppv = NULL;
2937         return E_NOINTERFACE;
2938     }
2939
2940     IUnknown_AddRef((IUnknown*)*ppv);
2941     return S_OK;
2942 }
2943
2944 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
2945 {
2946     UriBuilder *This = URIBUILDER_THIS(iface);
2947     LONG ref = InterlockedIncrement(&This->ref);
2948
2949     TRACE("(%p) ref=%d\n", This, ref);
2950
2951     return ref;
2952 }
2953
2954 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
2955 {
2956     UriBuilder *This = URIBUILDER_THIS(iface);
2957     LONG ref = InterlockedDecrement(&This->ref);
2958
2959     TRACE("(%p) ref=%d\n", This, ref);
2960
2961     if(!ref)
2962         heap_free(This);
2963
2964     return ref;
2965 }
2966
2967 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
2968                                                  DWORD        dwAllowEncodingPropertyMask,
2969                                                  DWORD_PTR    dwReserved,
2970                                                  IUri       **ppIUri)
2971 {
2972     UriBuilder *This = URIBUILDER_THIS(iface);
2973     FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
2974     return E_NOTIMPL;
2975 }
2976
2977 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
2978                                            DWORD        dwCreateFlags,
2979                                            DWORD        dwAllowEncodingPropertyMask,
2980                                            DWORD_PTR    dwReserved,
2981                                            IUri       **ppIUri)
2982 {
2983     UriBuilder *This = URIBUILDER_THIS(iface);
2984     FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
2985     return E_NOTIMPL;
2986 }
2987
2988 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
2989                                          DWORD        dwCreateFlags,
2990                                          DWORD        dwUriBuilderFlags,
2991                                          DWORD        dwAllowEncodingPropertyMask,
2992                                          DWORD_PTR    dwReserved,
2993                                          IUri       **ppIUri)
2994 {
2995     UriBuilder *This = URIBUILDER_THIS(iface);
2996     FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
2997         dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
2998     return E_NOTIMPL;
2999 }
3000
3001 static HRESULT WINAPI  UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
3002 {
3003     UriBuilder *This = URIBUILDER_THIS(iface);
3004     FIXME("(%p)->(%p)\n", This, ppIUri);
3005     return E_NOTIMPL;
3006 }
3007
3008 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
3009 {
3010     UriBuilder *This = URIBUILDER_THIS(iface);
3011     FIXME("(%p)->(%p)\n", This, pIUri);
3012     return E_NOTIMPL;
3013 }
3014
3015 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
3016 {
3017     UriBuilder *This = URIBUILDER_THIS(iface);
3018     FIXME("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
3019     return E_NOTIMPL;
3020 }
3021
3022 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
3023 {
3024     UriBuilder *This = URIBUILDER_THIS(iface);
3025     FIXME("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
3026     return E_NOTIMPL;
3027 }
3028
3029 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
3030 {
3031     UriBuilder *This = URIBUILDER_THIS(iface);
3032     FIXME("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
3033     return E_NOTIMPL;
3034 }
3035
3036 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
3037 {
3038     UriBuilder *This = URIBUILDER_THIS(iface);
3039     FIXME("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
3040     return E_NOTIMPL;
3041 }
3042
3043 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
3044 {
3045     UriBuilder *This = URIBUILDER_THIS(iface);
3046     FIXME("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
3047     return E_NOTIMPL;
3048 }
3049
3050 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
3051 {
3052     UriBuilder *This = URIBUILDER_THIS(iface);
3053     FIXME("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
3054     return E_NOTIMPL;
3055 }
3056
3057 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
3058 {
3059     UriBuilder *This = URIBUILDER_THIS(iface);
3060     FIXME("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
3061     return E_NOTIMPL;
3062 }
3063
3064 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
3065 {
3066     UriBuilder *This = URIBUILDER_THIS(iface);
3067     FIXME("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
3068     return E_NOTIMPL;
3069 }
3070
3071 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
3072 {
3073     UriBuilder *This = URIBUILDER_THIS(iface);
3074     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
3075     return E_NOTIMPL;
3076 }
3077
3078 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
3079 {
3080     UriBuilder *This = URIBUILDER_THIS(iface);
3081     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
3082     return E_NOTIMPL;
3083 }
3084
3085 static HRESULT WINAPI UriBuilder_SetPassword(IUriBuilder *iface, LPCWSTR pwzNewValue)
3086 {
3087     UriBuilder *This = URIBUILDER_THIS(iface);
3088     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
3089     return E_NOTIMPL;
3090 }
3091
3092 static HRESULT WINAPI UriBuilder_SetPath(IUriBuilder *iface, LPCWSTR pwzNewValue)
3093 {
3094     UriBuilder *This = URIBUILDER_THIS(iface);
3095     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
3096     return E_NOTIMPL;
3097 }
3098
3099 static HRESULT WINAPI UriBuilder_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
3100 {
3101     UriBuilder *This = URIBUILDER_THIS(iface);
3102     FIXME("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
3103     return E_NOTIMPL;
3104 }
3105
3106 static HRESULT WINAPI UriBuilder_SetQuery(IUriBuilder *iface, LPCWSTR pwzNewValue)
3107 {
3108     UriBuilder *This = URIBUILDER_THIS(iface);
3109     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
3110     return E_NOTIMPL;
3111 }
3112
3113 static HRESULT WINAPI UriBuilder_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
3114 {
3115     UriBuilder *This = URIBUILDER_THIS(iface);
3116     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
3117     return E_NOTIMPL;
3118 }
3119
3120 static HRESULT WINAPI UriBuilder_SetUserName(IUriBuilder *iface, LPCWSTR pwzNewValue)
3121 {
3122     UriBuilder *This = URIBUILDER_THIS(iface);
3123     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
3124     return E_NOTIMPL;
3125 }
3126
3127 static HRESULT WINAPI UriBuilder_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
3128 {
3129     UriBuilder *This = URIBUILDER_THIS(iface);
3130     FIXME("(%p)->(0x%08x)\n", This, dwPropertyMask);
3131     return E_NOTIMPL;
3132 }
3133
3134 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
3135 {
3136     UriBuilder *This = URIBUILDER_THIS(iface);
3137     FIXME("(%p)->(%p)\n", This, pfModified);
3138     return E_NOTIMPL;
3139 }
3140
3141 #undef URIBUILDER_THIS
3142
3143 static const IUriBuilderVtbl UriBuilderVtbl = {
3144     UriBuilder_QueryInterface,
3145     UriBuilder_AddRef,
3146     UriBuilder_Release,
3147     UriBuilder_CreateUriSimple,
3148     UriBuilder_CreateUri,
3149     UriBuilder_CreateUriWithFlags,
3150     UriBuilder_GetIUri,
3151     UriBuilder_SetIUri,
3152     UriBuilder_GetFragment,
3153     UriBuilder_GetHost,
3154     UriBuilder_GetPassword,
3155     UriBuilder_GetPath,
3156     UriBuilder_GetPort,
3157     UriBuilder_GetQuery,
3158     UriBuilder_GetSchemeName,
3159     UriBuilder_GetUserName,
3160     UriBuilder_SetFragment,
3161     UriBuilder_SetHost,
3162     UriBuilder_SetPassword,
3163     UriBuilder_SetPath,
3164     UriBuilder_SetPort,
3165     UriBuilder_SetQuery,
3166     UriBuilder_SetSchemeName,
3167     UriBuilder_SetUserName,
3168     UriBuilder_RemoveProperties,
3169     UriBuilder_HasBeenModified,
3170 };
3171
3172 /***********************************************************************
3173  *           CreateIUriBuilder (urlmon.@)
3174  */
3175 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
3176 {
3177     UriBuilder *ret;
3178
3179     TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
3180
3181     ret = heap_alloc(sizeof(UriBuilder));
3182     if(!ret)
3183         return E_OUTOFMEMORY;
3184
3185     ret->lpIUriBuilderVtbl = &UriBuilderVtbl;
3186     ret->ref = 1;
3187
3188     *ppIUriBuilder = URIBUILDER(ret);
3189     return S_OK;
3190 }