wordpad: Allow objects & images to be added with native riched20.
[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 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
27
28 typedef struct {
29     const IUriVtbl  *lpIUriVtbl;
30     LONG ref;
31     BSTR        raw_uri;
32
33     /* Information about the canonicalized URI's buffer. */
34     WCHAR       *canon_uri;
35     DWORD       canon_size;
36     DWORD       canon_len;
37
38     INT         scheme_start;
39     DWORD       scheme_len;
40     URL_SCHEME  scheme_type;
41
42     INT         userinfo_start;
43     DWORD       userinfo_len;
44     INT         userinfo_split;
45 } Uri;
46
47 typedef struct {
48     const IUriBuilderVtbl  *lpIUriBuilderVtbl;
49     LONG ref;
50 } UriBuilder;
51
52 typedef struct {
53     BSTR            uri;
54
55     BOOL            is_relative;
56     BOOL            is_opaque;
57     BOOL            has_implicit_scheme;
58
59     const WCHAR     *scheme;
60     DWORD           scheme_len;
61     URL_SCHEME      scheme_type;
62
63     const WCHAR     *userinfo;
64     DWORD           userinfo_len;
65     INT             userinfo_split;
66 } parse_data;
67
68 static const CHAR hexDigits[] = "0123456789ABCDEF";
69
70 /* List of scheme types/scheme names that are recognized by the IUri interface as of IE 7. */
71 static const struct {
72     URL_SCHEME  scheme;
73     WCHAR       scheme_name[16];
74 } recognized_schemes[] = {
75     {URL_SCHEME_FTP,            {'f','t','p',0}},
76     {URL_SCHEME_HTTP,           {'h','t','t','p',0}},
77     {URL_SCHEME_GOPHER,         {'g','o','p','h','e','r',0}},
78     {URL_SCHEME_MAILTO,         {'m','a','i','l','t','o',0}},
79     {URL_SCHEME_NEWS,           {'n','e','w','s',0}},
80     {URL_SCHEME_NNTP,           {'n','n','t','p',0}},
81     {URL_SCHEME_TELNET,         {'t','e','l','n','e','t',0}},
82     {URL_SCHEME_WAIS,           {'w','a','i','s',0}},
83     {URL_SCHEME_FILE,           {'f','i','l','e',0}},
84     {URL_SCHEME_MK,             {'m','k',0}},
85     {URL_SCHEME_HTTPS,          {'h','t','t','p','s',0}},
86     {URL_SCHEME_SHELL,          {'s','h','e','l','l',0}},
87     {URL_SCHEME_SNEWS,          {'s','n','e','w','s',0}},
88     {URL_SCHEME_LOCAL,          {'l','o','c','a','l',0}},
89     {URL_SCHEME_JAVASCRIPT,     {'j','a','v','a','s','c','r','i','p','t',0}},
90     {URL_SCHEME_VBSCRIPT,       {'v','b','s','c','r','i','p','t',0}},
91     {URL_SCHEME_ABOUT,          {'a','b','o','u','t',0}},
92     {URL_SCHEME_RES,            {'r','e','s',0}},
93     {URL_SCHEME_MSSHELLROOTED,  {'m','s','-','s','h','e','l','l','-','r','o','o','t','e','d',0}},
94     {URL_SCHEME_MSSHELLIDLIST,  {'m','s','-','s','h','e','l','l','-','i','d','l','i','s','t',0}},
95     {URL_SCHEME_MSHELP,         {'h','c','p',0}},
96     {URL_SCHEME_WILDCARD,       {'*',0}}
97 };
98
99 static inline BOOL is_alpha(WCHAR val) {
100         return ((val >= 'a' && val <= 'z') || (val >= 'A' && val <= 'Z'));
101 }
102
103 static inline BOOL is_num(WCHAR val) {
104         return (val >= '0' && val <= '9');
105 }
106
107 /* A URI is implicitly a file path if it begins with
108  * a drive letter (eg X:) or starts with "\\" (UNC path).
109  */
110 static inline BOOL is_implicit_file_path(const WCHAR *str) {
111     if(is_alpha(str[0]) && str[1] == ':')
112         return TRUE;
113     else if(str[0] == '\\' && str[1] == '\\')
114         return TRUE;
115
116     return FALSE;
117 }
118
119 /* Checks if the URI is a hierarchical URI. A hierarchical
120  * URI is one that has "//" after the scheme.
121  */
122 static BOOL check_hierarchical(const WCHAR **ptr) {
123     const WCHAR *start = *ptr;
124
125     if(**ptr != '/')
126         return FALSE;
127
128     ++(*ptr);
129     if(**ptr != '/') {
130         *ptr = start;
131         return FALSE;
132     }
133
134     ++(*ptr);
135     return TRUE;
136 }
137
138 /* unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~" */
139 static inline BOOL is_unreserved(WCHAR val) {
140     return (is_alpha(val) || is_num(val) || val == '-' || val == '.' ||
141             val == '_' || val == '~');
142 }
143
144 /* sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
145  *               / "*" / "+" / "," / ";" / "="
146  */
147 static inline BOOL is_subdelim(WCHAR val) {
148     return (val == '!' || val == '$' || val == '&' ||
149             val == '\'' || val == '(' || val == ')' ||
150             val == '*' || val == '+' || val == ',' ||
151             val == ';' || val == '=');
152 }
153
154 /* gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@" */
155 static inline BOOL is_gendelim(WCHAR val) {
156     return (val == ':' || val == '/' || val == '?' ||
157             val == '#' || val == '[' || val == ']' ||
158             val == '@');
159 }
160
161 /* Characters that delimit the end of the authority
162  * section of a URI. Sometimes a '\\' is considered
163  * an authority delimeter.
164  */
165 static inline BOOL is_auth_delim(WCHAR val, BOOL acceptSlash) {
166     return (val == '#' || val == '/' || val == '?' ||
167             val == '\0' || (acceptSlash && val == '\\'));
168 }
169
170 /* reserved = gen-delims / sub-delims */
171 static inline BOOL is_reserved(WCHAR val) {
172     return (is_subdelim(val) || is_gendelim(val));
173 }
174
175 static inline BOOL is_hexdigit(WCHAR val) {
176     return ((val >= 'a' && val <= 'f') ||
177             (val >= 'A' && val <= 'F') ||
178             (val >= '0' && val <= '9'));
179 }
180
181 /* Taken from dlls/jscript/lex.c */
182 static int hex_to_int(WCHAR val) {
183     if(val >= '0' && val <= '9')
184         return val - '0';
185     else if(val >= 'a' && val <= 'f')
186         return val - 'a' + 10;
187     else if(val >= 'A' && val <= 'F')
188         return val - 'A' + 10;
189
190     return -1;
191 }
192
193 /* Helper function for converting a percent encoded string
194  * representation of a WCHAR value into its actual WCHAR value. If
195  * the two characters following the '%' aren't valid hex values then
196  * this function returns the NULL character.
197  *
198  * Eg.
199  *  "%2E" will result in '.' being returned by this function.
200  */
201 static WCHAR decode_pct_val(const WCHAR *ptr) {
202     WCHAR ret = '\0';
203
204     if(*ptr == '%' && is_hexdigit(*(ptr + 1)) && is_hexdigit(*(ptr + 2))) {
205         INT a = hex_to_int(*(ptr + 1));
206         INT b = hex_to_int(*(ptr + 2));
207
208         ret = a << 4;
209         ret += b;
210     }
211
212     return ret;
213 }
214
215 /* Helper function for percent encoding a given character
216  * and storing the encoded value into a given buffer (dest).
217  *
218  * It's up to the calling function to ensure that there is
219  * at least enough space in 'dest' for the percent encoded
220  * value to be stored (so dest + 3 spaces available).
221  */
222 static inline void pct_encode_val(WCHAR val, WCHAR *dest) {
223     dest[0] = '%';
224     dest[1] = hexDigits[(val >> 4) & 0xf];
225     dest[2] = hexDigits[val & 0xf];
226 }
227
228 /* Checks if the characters pointed to by 'ptr' are
229  * a percent encoded data octet.
230  *
231  * pct-encoded = "%" HEXDIG HEXDIG
232  */
233 static BOOL check_pct_encoded(const WCHAR **ptr) {
234     const WCHAR *start = *ptr;
235
236     if(**ptr != '%')
237         return FALSE;
238
239     ++(*ptr);
240     if(!is_hexdigit(**ptr)) {
241         *ptr = start;
242         return FALSE;
243     }
244
245     ++(*ptr);
246     if(!is_hexdigit(**ptr)) {
247         *ptr = start;
248         return FALSE;
249     }
250
251     ++(*ptr);
252     return TRUE;
253 }
254
255 /* Tries to parse the scheme name of the URI.
256  *
257  * scheme = ALPHA *(ALPHA | NUM | '+' | '-' | '.') as defined by RFC 3896.
258  * NOTE: Windows accepts a number as the first character of a scheme.
259  */
260 static BOOL parse_scheme_name(const WCHAR **ptr, parse_data *data) {
261     const WCHAR *start = *ptr;
262
263     data->scheme = NULL;
264     data->scheme_len = 0;
265
266     while(**ptr) {
267         if(**ptr == '*' && *ptr == start) {
268             /* Might have found a wildcard scheme. If it is the next
269              * char has to be a ':' for it to be a valid URI
270              */
271             ++(*ptr);
272             break;
273         } else if(!is_num(**ptr) && !is_alpha(**ptr) && **ptr != '+' &&
274            **ptr != '-' && **ptr != '.')
275             break;
276
277         (*ptr)++;
278     }
279
280     if(*ptr == start)
281         return FALSE;
282
283     /* Schemes must end with a ':' */
284     if(**ptr != ':') {
285         *ptr = start;
286         return FALSE;
287     }
288
289     data->scheme = start;
290     data->scheme_len = *ptr - start;
291
292     ++(*ptr);
293     return TRUE;
294 }
295
296 /* Tries to deduce the corresponding URL_SCHEME for the given URI. Stores
297  * the deduced URL_SCHEME in data->scheme_type.
298  */
299 static BOOL parse_scheme_type(parse_data *data) {
300     /* If there's scheme data then see if it's a recognized scheme. */
301     if(data->scheme && data->scheme_len) {
302         DWORD i;
303
304         for(i = 0; i < sizeof(recognized_schemes)/sizeof(recognized_schemes[0]); ++i) {
305             if(lstrlenW(recognized_schemes[i].scheme_name) == data->scheme_len) {
306                 /* Has to be a case insensitive compare. */
307                 if(!StrCmpNIW(recognized_schemes[i].scheme_name, data->scheme, data->scheme_len)) {
308                     data->scheme_type = recognized_schemes[i].scheme;
309                     return TRUE;
310                 }
311             }
312         }
313
314         /* If we get here it means it's not a recognized scheme. */
315         data->scheme_type = URL_SCHEME_UNKNOWN;
316         return TRUE;
317     } else if(data->is_relative) {
318         /* Relative URI's have no scheme. */
319         data->scheme_type = URL_SCHEME_UNKNOWN;
320         return TRUE;
321     } else {
322         /* Should never reach here! what happened... */
323         FIXME("(%p): Unable to determine scheme type for URI %s\n", data, debugstr_w(data->uri));
324         return FALSE;
325     }
326 }
327
328 /* Tries to parse (or deduce) the scheme_name of a URI. If it can't
329  * parse a scheme from the URI it will try to deduce the scheme_name and scheme_type
330  * using the flags specified in 'flags' (if any). Flags that affect how this function
331  * operates are the Uri_CREATE_ALLOW_* flags.
332  *
333  * All parsed/deduced information will be stored in 'data' when the function returns.
334  *
335  * Returns TRUE if it was able to successfully parse the information.
336  */
337 static BOOL parse_scheme(const WCHAR **ptr, parse_data *data, DWORD flags) {
338     static const WCHAR fileW[] = {'f','i','l','e',0};
339     static const WCHAR wildcardW[] = {'*',0};
340
341     /* First check to see if the uri could implicitly be a file path. */
342     if(is_implicit_file_path(*ptr)) {
343         if(flags & Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME) {
344             data->scheme = fileW;
345             data->scheme_len = lstrlenW(fileW);
346             data->has_implicit_scheme = TRUE;
347
348             TRACE("(%p %p %x): URI is an implicit file path.\n", ptr, data, flags);
349         } else {
350             /* Window's does not consider anything that can implicitly be a file
351              * path to be a valid URI if the ALLOW_IMPLICIT_FILE_SCHEME flag is not set...
352              */
353             TRACE("(%p %p %x): URI is implicitly a file path, but, the ALLOW_IMPLICIT_FILE_SCHEME flag wasn't set.\n",
354                     ptr, data, flags);
355             return FALSE;
356         }
357     } else if(!parse_scheme_name(ptr, data)) {
358         /* No Scheme was found, this means it could be:
359          *      a) an implicit Wildcard scheme
360          *      b) a relative URI
361          *      c) a invalid URI.
362          */
363         if(flags & Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME) {
364             data->scheme = wildcardW;
365             data->scheme_len = lstrlenW(wildcardW);
366             data->has_implicit_scheme = TRUE;
367
368             TRACE("(%p %p %x): URI is an implicit wildcard scheme.\n", ptr, data, flags);
369         } else if (flags & Uri_CREATE_ALLOW_RELATIVE) {
370             data->is_relative = TRUE;
371             TRACE("(%p %p %x): URI is relative.\n", ptr, data, flags);
372         } else {
373             TRACE("(%p %p %x): Malformed URI found. Unable to deduce scheme name.\n", ptr, data, flags);
374             return FALSE;
375         }
376     }
377
378     if(!data->is_relative)
379         TRACE("(%p %p %x): Found scheme=%s scheme_len=%d\n", ptr, data, flags,
380                 debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
381
382     if(!parse_scheme_type(data))
383         return FALSE;
384
385     TRACE("(%p %p %x): Assigned %d as the URL_SCHEME.\n", ptr, data, flags, data->scheme_type);
386     return TRUE;
387 }
388
389 /* Parses the userinfo part of the URI (if it exists). The userinfo field of
390  * a URI can consist of "username:password@", or just "username@".
391  *
392  * RFC def:
393  * userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )
394  *
395  * NOTES:
396  *  1)  If there is more than one ':' in the userinfo part of the URI Windows
397  *      uses the first occurence of ':' to delimit the username and password
398  *      components.
399  *
400  *      ex:
401  *          ftp://user:pass:word@winehq.org
402  *
403  *      Would yield, "user" as the username and "pass:word" as the password.
404  *
405  *  2)  Windows allows any character to appear in the "userinfo" part of
406  *      a URI, as long as it's not an authority delimeter character set.
407  */
408 static void parse_userinfo(const WCHAR **ptr, parse_data *data, DWORD flags) {
409     data->userinfo = *ptr;
410     data->userinfo_split = -1;
411
412     while(**ptr != '@') {
413         if(**ptr == ':' && data->userinfo_split == -1)
414             data->userinfo_split = *ptr - data->userinfo;
415         else if(**ptr == '%') {
416             /* If it's a known scheme type, it has to be a valid percent
417              * encoded value.
418              */
419             if(!check_pct_encoded(ptr)) {
420                 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
421                     *ptr = data->userinfo;
422                     data->userinfo = NULL;
423                     data->userinfo_split = -1;
424
425                     TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
426                     return;
427                 }
428             } else
429                 continue;
430         } else if(is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN))
431             break;
432
433         ++(*ptr);
434     }
435
436     if(**ptr != '@') {
437         *ptr = data->userinfo;
438         data->userinfo = NULL;
439         data->userinfo_split = -1;
440
441         TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
442         return;
443     }
444
445     data->userinfo_len = *ptr - data->userinfo;
446     TRACE("(%p %p %x): Found userinfo=%s userinfo_len=%d split=%d.\n", ptr, data, flags,
447             debugstr_wn(data->userinfo, data->userinfo_len), data->userinfo_len, data->userinfo_split);
448     ++(*ptr);
449 }
450
451 /* Parses the authority information from the URI.
452  *
453  * authority   = [ userinfo "@" ] host [ ":" port ]
454  */
455 static BOOL parse_authority(const WCHAR **ptr, parse_data *data, DWORD flags) {
456     parse_userinfo(ptr, data, flags);
457
458     /* TODO: Parse host and port information. */
459
460     return TRUE;
461 }
462
463 /* Determines how the URI should be parsed after the scheme information.
464  *
465  * If the scheme is followed, by "//" then, it is treated as an hierarchical URI
466  * which then the authority and path information will be parsed out. Otherwise, the
467  * URI will be treated as an opaque URI which the authority information is not parsed
468  * out.
469  *
470  * RFC 3896 definition of hier-part:
471  *
472  * hier-part   = "//" authority path-abempty
473  *                 / path-absolute
474  *                 / path-rootless
475  *                 / path-empty
476  *
477  * MSDN opaque URI definition:
478  *  scheme ":" path [ "#" fragment ]
479  *
480  * NOTES:
481  *  If the URI is of an unknown scheme type and has a "//" following the scheme then it
482  *  is treated as a hierarchical URI, but, if the CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is
483  *  set then it is considered an opaque URI reguardless of what follows the scheme information
484  *  (per MSDN documentation).
485  */
486 static BOOL parse_hierpart(const WCHAR **ptr, parse_data *data, DWORD flags) {
487     /* Checks if the authority information needs to be parsed.
488      *
489      * Relative URI's aren't hierarchical URI's, but, they could trick
490      * "check_hierarchical" into thinking it is, so we need to explicitly
491      * make sure it's not relative. Also, if the URI is an implicit file
492      * scheme it might not contain a "//", but, it's considered hierarchical
493      * anyways. Wildcard Schemes are always considered hierarchical
494      */
495     if(data->scheme_type == URL_SCHEME_WILDCARD ||
496        data->scheme_type == URL_SCHEME_FILE ||
497        (!data->is_relative && check_hierarchical(ptr))) {
498         /* Only treat it as a hierarchical URI if the scheme_type is known or
499          * the Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is not set.
500          */
501         if(data->scheme_type != URL_SCHEME_UNKNOWN ||
502            !(flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES)) {
503             TRACE("(%p %p %x): Treating URI as an hierarchical URI.\n", ptr, data, flags);
504             data->is_opaque = FALSE;
505
506             /* TODO: Handle hierarchical URI's, parse authority then parse the path. */
507             if(!parse_authority(ptr, data, flags))
508                 return FALSE;
509
510             return TRUE;
511         }
512     }
513
514     /* If it reaches here, then the URI will be treated as an opaque
515      * URI.
516      */
517
518     TRACE("(%p %p %x): Treating URI as an opaque URI.\n", ptr, data, flags);
519
520     data->is_opaque = TRUE;
521     /* TODO: Handle opaque URI's, parse path. */
522     return TRUE;
523 }
524
525 /* Parses and validates the components of the specified by data->uri
526  * and stores the information it parses into 'data'.
527  *
528  * Returns TRUE if it successfully parsed the URI. False otherwise.
529  */
530 static BOOL parse_uri(parse_data *data, DWORD flags) {
531     const WCHAR *ptr;
532     const WCHAR **pptr;
533
534     ptr = data->uri;
535     pptr = &ptr;
536
537     TRACE("(%p %x): BEGINNING TO PARSE URI %s.\n", data, flags, debugstr_w(data->uri));
538
539     if(!parse_scheme(pptr, data, flags))
540         return FALSE;
541
542     if(!parse_hierpart(pptr, data, flags))
543         return FALSE;
544
545     TRACE("(%p %x): FINISHED PARSING URI.\n", data, flags);
546     return TRUE;
547 }
548
549 /* Canonicalizes the userinfo of the URI represented by the parse_data.
550  *
551  * Canonicalization of the userinfo is a simple process. If there are any percent
552  * encoded characters that fall in the "unreserved" character set, they are decoded
553  * to their actual value. If a character is not in the "unreserved" or "reserved" sets
554  * then it is percent encoded. Other than that the characters are copied over without
555  * change.
556  */
557 static BOOL canonicalize_userinfo(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
558     DWORD i = 0;
559
560     uri->userinfo_start = uri->userinfo_split = -1;
561     uri->userinfo_len = 0;
562
563     if(!data->userinfo)
564         /* URI doesn't have userinfo, so nothing to do here. */
565         return TRUE;
566
567     uri->userinfo_start = uri->canon_len;
568
569     while(i < data->userinfo_len) {
570         if(data->userinfo[i] == ':' && uri->userinfo_split == -1)
571             /* Windows only considers the first ':' as the delimiter. */
572             uri->userinfo_split = uri->canon_len - uri->userinfo_start;
573         else if(data->userinfo[i] == '%') {
574             /* Only decode % encoded values for known scheme types. */
575             if(data->scheme_type != URL_SCHEME_UNKNOWN) {
576                 /* See if the value really needs decoded. */
577                 WCHAR val = decode_pct_val(data->userinfo + i);
578                 if(is_unreserved(val)) {
579                     if(!computeOnly)
580                         uri->canon_uri[uri->canon_len] = val;
581
582                     ++uri->canon_len;
583
584                     /* Move pass the hex characters. */
585                     i += 3;
586                     continue;
587                 }
588             }
589         } else if(!is_reserved(data->userinfo[i]) && !is_unreserved(data->userinfo[i]) &&
590                   data->userinfo[i] != '\\') {
591             /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
592              * is NOT set.
593              */
594             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
595                 if(!computeOnly)
596                     pct_encode_val(data->userinfo[i], uri->canon_uri + uri->canon_len);
597
598                 uri->canon_len += 3;
599                 ++i;
600                 continue;
601             }
602         }
603
604         if(!computeOnly)
605             /* Nothing special, so just copy the character over. */
606             uri->canon_uri[uri->canon_len] = data->userinfo[i];
607
608         ++uri->canon_len;
609         ++i;
610     }
611
612     uri->userinfo_len = uri->canon_len - uri->userinfo_start;
613     if(!computeOnly)
614         TRACE("(%p %p %x %d): Canonicalized userinfo, userinfo_start=%d, userinfo=%s, userinfo_split=%d userinfo_len=%d.\n",
615                 data, uri, flags, computeOnly, uri->userinfo_start, debugstr_wn(uri->canon_uri + uri->userinfo_start, uri->userinfo_len),
616                 uri->userinfo_split, uri->userinfo_len);
617
618     /* Now insert the '@' after the userinfo. */
619     if(!computeOnly)
620         uri->canon_uri[uri->canon_len] = '@';
621
622     ++uri->canon_len;
623     return TRUE;
624 }
625
626 /* Canonicalizes the authority of the URI represented by the parse_data. */
627 static BOOL canonicalize_authority(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
628     if(!canonicalize_userinfo(data, uri, flags, computeOnly))
629         return FALSE;
630
631     /* TODO: canonicalize the host and port information. */
632
633     return TRUE;
634 }
635
636 /* Determines how the URI represented by the parse_data should be canonicalized.
637  *
638  * Essentially, if the parse_data represents an hierarchical URI then it calls
639  * canonicalize_authority and the canonicalization functions for the path. If the
640  * URI is opaque it canonicalizes the path of the URI.
641  */
642 static BOOL canonicalize_hierpart(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
643     if(!data->is_opaque) {
644         /* "//" is only added for non-wildcard scheme types. */
645         if(data->scheme_type != URL_SCHEME_WILDCARD) {
646             if(!computeOnly) {
647                 INT pos = uri->canon_len;
648
649                 uri->canon_uri[pos] = '/';
650                 uri->canon_uri[pos+1] = '/';
651            }
652            uri->canon_len += 2;
653         }
654
655         if(!canonicalize_authority(data, uri, flags, computeOnly))
656             return FALSE;
657
658        /* TODO: Canonicalize the path of the URI. */
659
660     } else {
661         /* Opaque URI's don't have userinfo. */
662         uri->userinfo_start = uri->userinfo_split = -1;
663         uri->userinfo_len = 0;
664     }
665
666     return TRUE;
667 }
668
669 /* Canonicalizes the scheme information specified in the parse_data using the specified flags. */
670 static BOOL canonicalize_scheme(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
671     uri->scheme_start = -1;
672     uri->scheme_len = 0;
673
674     if(!data->scheme) {
675         /* The only type of URI that doesn't have to have a scheme is a relative
676          * URI.
677          */
678         if(!data->is_relative) {
679             FIXME("(%p %p %x): Unable to determine the scheme type of %s.\n", data,
680                     uri, flags, debugstr_w(data->uri));
681             return FALSE;
682         }
683     } else {
684         if(!computeOnly) {
685             DWORD i;
686             INT pos = uri->canon_len;
687
688             for(i = 0; i < data->scheme_len; ++i) {
689                 /* Scheme name must be lower case after canonicalization. */
690                 uri->canon_uri[i + pos] = tolowerW(data->scheme[i]);
691             }
692
693             uri->canon_uri[i + pos] = ':';
694             uri->scheme_start = pos;
695
696             TRACE("(%p %p %x): Canonicalized scheme=%s, len=%d.\n", data, uri, flags,
697                     debugstr_wn(uri->canon_uri,  uri->scheme_len), data->scheme_len);
698         }
699
700         /* This happens in both computation modes. */
701         uri->canon_len += data->scheme_len + 1;
702         uri->scheme_len = data->scheme_len;
703     }
704     return TRUE;
705 }
706
707 /* Compute's what the length of the URI specified by the parse_data will be
708  * after canonicalization occurs using the specified flags.
709  *
710  * This function will return a non-zero value indicating the length of the canonicalized
711  * URI, or -1 on error.
712  */
713 static int compute_canonicalized_length(const parse_data *data, DWORD flags) {
714     Uri uri;
715
716     memset(&uri, 0, sizeof(Uri));
717
718     TRACE("(%p %x): Beginning to compute canonicalized length for URI %s\n", data, flags,
719             debugstr_w(data->uri));
720
721     if(!canonicalize_scheme(data, &uri, flags, TRUE)) {
722         ERR("(%p %x): Failed to compute URI scheme length.\n", data, flags);
723         return -1;
724     }
725
726     if(!canonicalize_hierpart(data, &uri, flags, TRUE)) {
727         ERR("(%p %x): Failed to compute URI hierpart length.\n", data, flags);
728         return -1;
729     }
730
731     TRACE("(%p %x): Finished computing canonicalized URI length. length=%d\n", data, flags, uri.canon_len);
732
733     return uri.canon_len;
734 }
735
736 /* Canonicalizes the URI data specified in the parse_data, using the given flags. If the
737  * canonicalization succeededs it will store all the canonicalization information
738  * in the pointer to the Uri.
739  *
740  * To canonicalize a URI this function first computes what the length of the URI
741  * specified by the parse_data will be. Once this is done it will then perfom the actual
742  * canonicalization of the URI.
743  */
744 static HRESULT canonicalize_uri(const parse_data *data, Uri *uri, DWORD flags) {
745     INT len;
746
747     uri->canon_uri = NULL;
748     len = uri->canon_size = uri->canon_len = 0;
749
750     TRACE("(%p %p %x): beginning to canonicalize URI %s.\n", data, uri, flags, debugstr_w(data->uri));
751
752     /* First try to compute the length of the URI. */
753     len = compute_canonicalized_length(data, flags);
754     if(len == -1) {
755         ERR("(%p %p %x): Could not compute the canonicalized length of %s.\n", data, uri, flags,
756                 debugstr_w(data->uri));
757         return E_INVALIDARG;
758     }
759
760     uri->canon_uri = heap_alloc((len+1)*sizeof(WCHAR));
761     if(!uri->canon_uri)
762         return E_OUTOFMEMORY;
763
764     if(!canonicalize_scheme(data, uri, flags, FALSE)) {
765         ERR("(%p %p %x): Unable to canonicalize the scheme of the URI.\n", data, uri, flags);
766         heap_free(uri->canon_uri);
767         return E_INVALIDARG;
768     }
769     uri->scheme_type = data->scheme_type;
770
771     if(!canonicalize_hierpart(data, uri, flags, FALSE)) {
772         ERR("(%p %p %x): Unable to canonicalize the heirpart of the URI\n", data, uri, flags);
773         heap_free(uri->canon_uri);
774         return E_INVALIDARG;
775     }
776
777     uri->canon_uri[uri->canon_len] = '\0';
778     TRACE("(%p %p %x): finished canonicalizing the URI.\n", data, uri, flags);
779
780     return S_OK;
781 }
782
783 #define URI(x)         ((IUri*)  &(x)->lpIUriVtbl)
784 #define URIBUILDER(x)  ((IUriBuilder*)  &(x)->lpIUriBuilderVtbl)
785
786 #define URI_THIS(iface) DEFINE_THIS(Uri, IUri, iface)
787
788 static HRESULT WINAPI Uri_QueryInterface(IUri *iface, REFIID riid, void **ppv)
789 {
790     Uri *This = URI_THIS(iface);
791
792     if(IsEqualGUID(&IID_IUnknown, riid)) {
793         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
794         *ppv = URI(This);
795     }else if(IsEqualGUID(&IID_IUri, riid)) {
796         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
797         *ppv = URI(This);
798     }else {
799         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
800         *ppv = NULL;
801         return E_NOINTERFACE;
802     }
803
804     IUnknown_AddRef((IUnknown*)*ppv);
805     return S_OK;
806 }
807
808 static ULONG WINAPI Uri_AddRef(IUri *iface)
809 {
810     Uri *This = URI_THIS(iface);
811     LONG ref = InterlockedIncrement(&This->ref);
812
813     TRACE("(%p) ref=%d\n", This, ref);
814
815     return ref;
816 }
817
818 static ULONG WINAPI Uri_Release(IUri *iface)
819 {
820     Uri *This = URI_THIS(iface);
821     LONG ref = InterlockedDecrement(&This->ref);
822
823     TRACE("(%p) ref=%d\n", This, ref);
824
825     if(!ref) {
826         SysFreeString(This->raw_uri);
827         heap_free(This->canon_uri);
828         heap_free(This);
829     }
830
831     return ref;
832 }
833
834 static HRESULT WINAPI Uri_GetPropertyBSTR(IUri *iface, Uri_PROPERTY uriProp, BSTR *pbstrProperty, DWORD dwFlags)
835 {
836     Uri *This = URI_THIS(iface);
837     HRESULT hres;
838     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
839
840     if(!pbstrProperty)
841         return E_POINTER;
842
843     if(uriProp > Uri_PROPERTY_STRING_LAST) {
844         /* Windows allocates an empty BSTR for invalid Uri_PROPERTY's. */
845         *pbstrProperty = SysAllocStringLen(NULL, 0);
846         if(!(*pbstrProperty))
847             return E_OUTOFMEMORY;
848
849         /* It only returns S_FALSE for the ZONE property... */
850         if(uriProp == Uri_PROPERTY_ZONE)
851             return S_FALSE;
852         else
853             return S_OK;
854     }
855
856     /* Don't have support for flags yet. */
857     if(dwFlags) {
858         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
859         return E_NOTIMPL;
860     }
861
862     switch(uriProp) {
863     case Uri_PROPERTY_PASSWORD:
864         if(This->userinfo_split > -1) {
865             *pbstrProperty = SysAllocStringLen(
866                 This->canon_uri+This->userinfo_start+This->userinfo_split+1,
867                 This->userinfo_len-This->userinfo_split-1);
868             hres = S_OK;
869         } else {
870             *pbstrProperty = SysAllocStringLen(NULL, 0);
871             hres = S_FALSE;
872         }
873
874         if(!(*pbstrProperty))
875             return E_OUTOFMEMORY;
876
877         break;
878     case Uri_PROPERTY_RAW_URI:
879         *pbstrProperty = SysAllocString(This->raw_uri);
880         if(!(*pbstrProperty))
881             hres = E_OUTOFMEMORY;
882         else
883             hres = S_OK;
884         break;
885     case Uri_PROPERTY_SCHEME_NAME:
886         if(This->scheme_start > -1) {
887             *pbstrProperty = SysAllocStringLen(This->canon_uri + This->scheme_start, This->scheme_len);
888             hres = S_OK;
889         } else {
890             *pbstrProperty = SysAllocStringLen(NULL, 0);
891             hres = S_FALSE;
892         }
893
894         if(!(*pbstrProperty))
895             hres = E_OUTOFMEMORY;
896
897         break;
898     case Uri_PROPERTY_USER_INFO:
899         if(This->userinfo_start > -1) {
900             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->userinfo_start, This->userinfo_len);
901             hres = S_OK;
902         } else {
903             *pbstrProperty = SysAllocStringLen(NULL, 0);
904             hres = S_FALSE;
905         }
906
907         if(!(*pbstrProperty))
908             hres = E_OUTOFMEMORY;
909
910         break;
911     case Uri_PROPERTY_USER_NAME:
912         if(This->userinfo_start > -1) {
913             /* If userinfo_split is set, that means a password exists
914              * so the username is only from userinfo_start to userinfo_split.
915              */
916             if(This->userinfo_split > -1) {
917                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_split);
918                 hres = S_OK;
919             } else {
920                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_len);
921                 hres = S_OK;
922             }
923         } else {
924             *pbstrProperty = SysAllocStringLen(NULL, 0);
925             hres = S_FALSE;
926         }
927
928         if(!(*pbstrProperty))
929             return E_OUTOFMEMORY;
930
931         break;
932     default:
933         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
934         hres = E_NOTIMPL;
935     }
936
937     return hres;
938 }
939
940 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
941 {
942     Uri *This = URI_THIS(iface);
943     HRESULT hres;
944     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
945
946     if(!pcchProperty)
947         return E_INVALIDARG;
948
949     /* Can only return a length for a property if it's a string. */
950     if(uriProp > Uri_PROPERTY_STRING_LAST)
951         return E_INVALIDARG;
952
953     /* Don't have support for flags yet. */
954     if(dwFlags) {
955         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
956         return E_NOTIMPL;
957     }
958
959     switch(uriProp) {
960     case Uri_PROPERTY_PASSWORD:
961         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_len-This->userinfo_split-1 : 0;
962         hres = (This->userinfo_split > -1) ? S_OK : S_FALSE;
963         break;
964     case Uri_PROPERTY_RAW_URI:
965         *pcchProperty = SysStringLen(This->raw_uri);
966         hres = S_OK;
967         break;
968     case Uri_PROPERTY_SCHEME_NAME:
969         *pcchProperty = This->scheme_len;
970         hres = (This->scheme_start > -1) ? S_OK : S_FALSE;
971         break;
972     case Uri_PROPERTY_USER_INFO:
973         *pcchProperty = This->userinfo_len;
974         hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
975         break;
976     case Uri_PROPERTY_USER_NAME:
977         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_split : This->userinfo_len;
978         hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
979         break;
980     default:
981         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
982         hres = E_NOTIMPL;
983     }
984
985     return hres;
986 }
987
988 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
989 {
990     Uri *This = URI_THIS(iface);
991     HRESULT hres;
992
993     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
994
995     if(!pcchProperty)
996         return E_INVALIDARG;
997
998     /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
999      * From what I can tell, instead of checking which URLZONE the URI belongs to it
1000      * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
1001      * function.
1002      */
1003     if(uriProp == Uri_PROPERTY_ZONE) {
1004         *pcchProperty = URLZONE_INVALID;
1005         return E_NOTIMPL;
1006     }
1007
1008     if(uriProp < Uri_PROPERTY_DWORD_START) {
1009         *pcchProperty = 0;
1010         return E_INVALIDARG;
1011     }
1012
1013     switch(uriProp) {
1014     case Uri_PROPERTY_SCHEME:
1015         *pcchProperty = This->scheme_type;
1016         hres = S_OK;
1017         break;
1018     default:
1019         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
1020         hres = E_NOTIMPL;
1021     }
1022
1023     return hres;
1024 }
1025
1026 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
1027 {
1028     Uri *This = URI_THIS(iface);
1029     FIXME("(%p)->(%d %p)\n", This, uriProp, pfHasProperty);
1030
1031     if(!pfHasProperty)
1032         return E_INVALIDARG;
1033
1034     return E_NOTIMPL;
1035 }
1036
1037 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
1038 {
1039     Uri *This = URI_THIS(iface);
1040     FIXME("(%p)->(%p)\n", This, pstrAbsoluteUri);
1041
1042     if(!pstrAbsoluteUri)
1043         return E_POINTER;
1044
1045     return E_NOTIMPL;
1046 }
1047
1048 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
1049 {
1050     Uri *This = URI_THIS(iface);
1051     FIXME("(%p)->(%p)\n", This, pstrAuthority);
1052
1053     if(!pstrAuthority)
1054         return E_POINTER;
1055
1056     return E_NOTIMPL;
1057 }
1058
1059 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
1060 {
1061     Uri *This = URI_THIS(iface);
1062     FIXME("(%p)->(%p)\n", This, pstrDisplayUri);
1063
1064     if(!pstrDisplayUri)
1065         return E_POINTER;
1066
1067     return E_NOTIMPL;
1068 }
1069
1070 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
1071 {
1072     Uri *This = URI_THIS(iface);
1073     FIXME("(%p)->(%p)\n", This, pstrDomain);
1074
1075     if(!pstrDomain)
1076         return E_POINTER;
1077
1078     return E_NOTIMPL;
1079 }
1080
1081 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
1082 {
1083     Uri *This = URI_THIS(iface);
1084     FIXME("(%p)->(%p)\n", This, pstrExtension);
1085
1086     if(!pstrExtension)
1087         return E_POINTER;
1088
1089     return E_NOTIMPL;
1090 }
1091
1092 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
1093 {
1094     Uri *This = URI_THIS(iface);
1095     FIXME("(%p)->(%p)\n", This, pstrFragment);
1096
1097     if(!pstrFragment)
1098         return E_POINTER;
1099
1100     return E_NOTIMPL;
1101 }
1102
1103 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
1104 {
1105     Uri *This = URI_THIS(iface);
1106     FIXME("(%p)->(%p)\n", This, pstrHost);
1107
1108     if(!pstrHost)
1109         return E_POINTER;
1110
1111     return E_NOTIMPL;
1112 }
1113
1114 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
1115 {
1116     TRACE("(%p)->(%p)\n", iface, pstrPassword);
1117     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_PASSWORD, pstrPassword, 0);
1118 }
1119
1120 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
1121 {
1122     Uri *This = URI_THIS(iface);
1123     FIXME("(%p)->(%p)\n", This, pstrPath);
1124
1125     if(!pstrPath)
1126         return E_POINTER;
1127
1128     return E_NOTIMPL;
1129 }
1130
1131 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
1132 {
1133     Uri *This = URI_THIS(iface);
1134     FIXME("(%p)->(%p)\n", This, pstrPathAndQuery);
1135
1136     if(!pstrPathAndQuery)
1137         return E_POINTER;
1138
1139     return E_NOTIMPL;
1140 }
1141
1142 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
1143 {
1144     Uri *This = URI_THIS(iface);
1145     FIXME("(%p)->(%p)\n", This, pstrQuery);
1146
1147     if(!pstrQuery)
1148         return E_POINTER;
1149
1150     return E_NOTIMPL;
1151 }
1152
1153 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
1154 {
1155     Uri *This = URI_THIS(iface);
1156     TRACE("(%p)->(%p)\n", This, pstrRawUri);
1157
1158     /* Just forward the call to GetPropertyBSTR. */
1159     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
1160 }
1161
1162 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
1163 {
1164     Uri *This = URI_THIS(iface);
1165     TRACE("(%p)->(%p)\n", This, pstrSchemeName);
1166     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_SCHEME_NAME, pstrSchemeName, 0);
1167 }
1168
1169 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
1170 {
1171     TRACE("(%p)->(%p)\n", iface, pstrUserInfo);
1172     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_INFO, pstrUserInfo, 0);
1173 }
1174
1175 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
1176 {
1177     TRACE("(%p)->(%p)\n", iface, pstrUserName);
1178     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_NAME, pstrUserName, 0);
1179 }
1180
1181 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
1182 {
1183     Uri *This = URI_THIS(iface);
1184     FIXME("(%p)->(%p)\n", This, pdwHostType);
1185
1186     if(!pdwHostType)
1187         return E_INVALIDARG;
1188
1189     return E_NOTIMPL;
1190 }
1191
1192 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
1193 {
1194     Uri *This = URI_THIS(iface);
1195     FIXME("(%p)->(%p)\n", This, pdwPort);
1196
1197     if(!pdwPort)
1198         return E_INVALIDARG;
1199
1200     return E_NOTIMPL;
1201 }
1202
1203 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
1204 {
1205     Uri *This = URI_THIS(iface);
1206     TRACE("(%p)->(%p)\n", This, pdwScheme);
1207     return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_SCHEME, pdwScheme, 0);
1208 }
1209
1210 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
1211 {
1212     Uri *This = URI_THIS(iface);
1213     FIXME("(%p)->(%p)\n", This, pdwZone);
1214
1215     if(!pdwZone)
1216         return E_INVALIDARG;
1217
1218     /* Microsoft doesn't seem to have this implemented yet... See
1219      * the comment in Uri_GetPropertyDWORD for more about this.
1220      */
1221     *pdwZone = URLZONE_INVALID;
1222     return E_NOTIMPL;
1223 }
1224
1225 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
1226 {
1227     Uri *This = URI_THIS(iface);
1228     FIXME("(%p)->(%p)\n", This, pdwProperties);
1229
1230     if(!pdwProperties)
1231         return E_INVALIDARG;
1232
1233     return E_NOTIMPL;
1234 }
1235
1236 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
1237 {
1238     Uri *This = URI_THIS(iface);
1239     TRACE("(%p)->(%p %p)\n", This, pUri, pfEqual);
1240
1241     if(!pfEqual)
1242         return E_POINTER;
1243
1244     if(!pUri) {
1245         *pfEqual = FALSE;
1246
1247         /* For some reason Windows returns S_OK here... */
1248         return S_OK;
1249     }
1250
1251     FIXME("(%p)->(%p %p)\n", This, pUri, pfEqual);
1252     return E_NOTIMPL;
1253 }
1254
1255 #undef URI_THIS
1256
1257 static const IUriVtbl UriVtbl = {
1258     Uri_QueryInterface,
1259     Uri_AddRef,
1260     Uri_Release,
1261     Uri_GetPropertyBSTR,
1262     Uri_GetPropertyLength,
1263     Uri_GetPropertyDWORD,
1264     Uri_HasProperty,
1265     Uri_GetAbsoluteUri,
1266     Uri_GetAuthority,
1267     Uri_GetDisplayUri,
1268     Uri_GetDomain,
1269     Uri_GetExtension,
1270     Uri_GetFragment,
1271     Uri_GetHost,
1272     Uri_GetPassword,
1273     Uri_GetPath,
1274     Uri_GetPathAndQuery,
1275     Uri_GetQuery,
1276     Uri_GetRawUri,
1277     Uri_GetSchemeName,
1278     Uri_GetUserInfo,
1279     Uri_GetUserName,
1280     Uri_GetHostType,
1281     Uri_GetPort,
1282     Uri_GetScheme,
1283     Uri_GetZone,
1284     Uri_GetProperties,
1285     Uri_IsEqual
1286 };
1287
1288 /***********************************************************************
1289  *           CreateUri (urlmon.@)
1290  */
1291 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
1292 {
1293     Uri *ret;
1294     HRESULT hr;
1295     parse_data data;
1296
1297     TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
1298
1299     if(!ppURI)
1300         return E_INVALIDARG;
1301
1302     if(!pwzURI) {
1303         *ppURI = NULL;
1304         return E_INVALIDARG;
1305     }
1306
1307     ret = heap_alloc(sizeof(Uri));
1308     if(!ret)
1309         return E_OUTOFMEMORY;
1310
1311     ret->lpIUriVtbl = &UriVtbl;
1312     ret->ref = 1;
1313
1314     /* Create a copy of pwzURI and store it as the raw_uri. */
1315     ret->raw_uri = SysAllocString(pwzURI);
1316     if(!ret->raw_uri) {
1317         heap_free(ret);
1318         return E_OUTOFMEMORY;
1319     }
1320
1321     memset(&data, 0, sizeof(parse_data));
1322     data.uri = ret->raw_uri;
1323
1324     /* Validate and parse the URI into it's components. */
1325     if(!parse_uri(&data, dwFlags)) {
1326         /* Encountered an unsupported or invalid URI */
1327         SysFreeString(ret->raw_uri);
1328         heap_free(ret);
1329         *ppURI = NULL;
1330         return E_INVALIDARG;
1331     }
1332
1333     /* Canonicalize the URI. */
1334     hr = canonicalize_uri(&data, ret, dwFlags);
1335     if(FAILED(hr)) {
1336         SysFreeString(ret->raw_uri);
1337         heap_free(ret);
1338         *ppURI = NULL;
1339         return hr;
1340     }
1341
1342     *ppURI = URI(ret);
1343     return S_OK;
1344 }
1345
1346 #define URIBUILDER_THIS(iface) DEFINE_THIS(UriBuilder, IUriBuilder, iface)
1347
1348 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
1349 {
1350     UriBuilder *This = URIBUILDER_THIS(iface);
1351
1352     if(IsEqualGUID(&IID_IUnknown, riid)) {
1353         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
1354         *ppv = URIBUILDER(This);
1355     }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
1356         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
1357         *ppv = URIBUILDER(This);
1358     }else {
1359         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1360         *ppv = NULL;
1361         return E_NOINTERFACE;
1362     }
1363
1364     IUnknown_AddRef((IUnknown*)*ppv);
1365     return S_OK;
1366 }
1367
1368 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
1369 {
1370     UriBuilder *This = URIBUILDER_THIS(iface);
1371     LONG ref = InterlockedIncrement(&This->ref);
1372
1373     TRACE("(%p) ref=%d\n", This, ref);
1374
1375     return ref;
1376 }
1377
1378 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
1379 {
1380     UriBuilder *This = URIBUILDER_THIS(iface);
1381     LONG ref = InterlockedDecrement(&This->ref);
1382
1383     TRACE("(%p) ref=%d\n", This, ref);
1384
1385     if(!ref)
1386         heap_free(This);
1387
1388     return ref;
1389 }
1390
1391 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
1392                                                  DWORD        dwAllowEncodingPropertyMask,
1393                                                  DWORD_PTR    dwReserved,
1394                                                  IUri       **ppIUri)
1395 {
1396     UriBuilder *This = URIBUILDER_THIS(iface);
1397     FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1398     return E_NOTIMPL;
1399 }
1400
1401 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
1402                                            DWORD        dwCreateFlags,
1403                                            DWORD        dwAllowEncodingPropertyMask,
1404                                            DWORD_PTR    dwReserved,
1405                                            IUri       **ppIUri)
1406 {
1407     UriBuilder *This = URIBUILDER_THIS(iface);
1408     FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1409     return E_NOTIMPL;
1410 }
1411
1412 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
1413                                          DWORD        dwCreateFlags,
1414                                          DWORD        dwUriBuilderFlags,
1415                                          DWORD        dwAllowEncodingPropertyMask,
1416                                          DWORD_PTR    dwReserved,
1417                                          IUri       **ppIUri)
1418 {
1419     UriBuilder *This = URIBUILDER_THIS(iface);
1420     FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
1421         dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1422     return E_NOTIMPL;
1423 }
1424
1425 static HRESULT WINAPI  UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
1426 {
1427     UriBuilder *This = URIBUILDER_THIS(iface);
1428     FIXME("(%p)->(%p)\n", This, ppIUri);
1429     return E_NOTIMPL;
1430 }
1431
1432 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
1433 {
1434     UriBuilder *This = URIBUILDER_THIS(iface);
1435     FIXME("(%p)->(%p)\n", This, pIUri);
1436     return E_NOTIMPL;
1437 }
1438
1439 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
1440 {
1441     UriBuilder *This = URIBUILDER_THIS(iface);
1442     FIXME("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
1443     return E_NOTIMPL;
1444 }
1445
1446 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
1447 {
1448     UriBuilder *This = URIBUILDER_THIS(iface);
1449     FIXME("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
1450     return E_NOTIMPL;
1451 }
1452
1453 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
1454 {
1455     UriBuilder *This = URIBUILDER_THIS(iface);
1456     FIXME("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
1457     return E_NOTIMPL;
1458 }
1459
1460 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
1461 {
1462     UriBuilder *This = URIBUILDER_THIS(iface);
1463     FIXME("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
1464     return E_NOTIMPL;
1465 }
1466
1467 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
1468 {
1469     UriBuilder *This = URIBUILDER_THIS(iface);
1470     FIXME("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
1471     return E_NOTIMPL;
1472 }
1473
1474 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
1475 {
1476     UriBuilder *This = URIBUILDER_THIS(iface);
1477     FIXME("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
1478     return E_NOTIMPL;
1479 }
1480
1481 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
1482 {
1483     UriBuilder *This = URIBUILDER_THIS(iface);
1484     FIXME("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
1485     return E_NOTIMPL;
1486 }
1487
1488 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
1489 {
1490     UriBuilder *This = URIBUILDER_THIS(iface);
1491     FIXME("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
1492     return E_NOTIMPL;
1493 }
1494
1495 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
1496 {
1497     UriBuilder *This = URIBUILDER_THIS(iface);
1498     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1499     return E_NOTIMPL;
1500 }
1501
1502 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
1503 {
1504     UriBuilder *This = URIBUILDER_THIS(iface);
1505     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1506     return E_NOTIMPL;
1507 }
1508
1509 static HRESULT WINAPI UriBuilder_SetPassword(IUriBuilder *iface, LPCWSTR pwzNewValue)
1510 {
1511     UriBuilder *This = URIBUILDER_THIS(iface);
1512     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1513     return E_NOTIMPL;
1514 }
1515
1516 static HRESULT WINAPI UriBuilder_SetPath(IUriBuilder *iface, LPCWSTR pwzNewValue)
1517 {
1518     UriBuilder *This = URIBUILDER_THIS(iface);
1519     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1520     return E_NOTIMPL;
1521 }
1522
1523 static HRESULT WINAPI UriBuilder_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
1524 {
1525     UriBuilder *This = URIBUILDER_THIS(iface);
1526     FIXME("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
1527     return E_NOTIMPL;
1528 }
1529
1530 static HRESULT WINAPI UriBuilder_SetQuery(IUriBuilder *iface, LPCWSTR pwzNewValue)
1531 {
1532     UriBuilder *This = URIBUILDER_THIS(iface);
1533     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1534     return E_NOTIMPL;
1535 }
1536
1537 static HRESULT WINAPI UriBuilder_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
1538 {
1539     UriBuilder *This = URIBUILDER_THIS(iface);
1540     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1541     return E_NOTIMPL;
1542 }
1543
1544 static HRESULT WINAPI UriBuilder_SetUserName(IUriBuilder *iface, LPCWSTR pwzNewValue)
1545 {
1546     UriBuilder *This = URIBUILDER_THIS(iface);
1547     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1548     return E_NOTIMPL;
1549 }
1550
1551 static HRESULT WINAPI UriBuilder_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
1552 {
1553     UriBuilder *This = URIBUILDER_THIS(iface);
1554     FIXME("(%p)->(0x%08x)\n", This, dwPropertyMask);
1555     return E_NOTIMPL;
1556 }
1557
1558 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
1559 {
1560     UriBuilder *This = URIBUILDER_THIS(iface);
1561     FIXME("(%p)->(%p)\n", This, pfModified);
1562     return E_NOTIMPL;
1563 }
1564
1565 #undef URIBUILDER_THIS
1566
1567 static const IUriBuilderVtbl UriBuilderVtbl = {
1568     UriBuilder_QueryInterface,
1569     UriBuilder_AddRef,
1570     UriBuilder_Release,
1571     UriBuilder_CreateUriSimple,
1572     UriBuilder_CreateUri,
1573     UriBuilder_CreateUriWithFlags,
1574     UriBuilder_GetIUri,
1575     UriBuilder_SetIUri,
1576     UriBuilder_GetFragment,
1577     UriBuilder_GetHost,
1578     UriBuilder_GetPassword,
1579     UriBuilder_GetPath,
1580     UriBuilder_GetPort,
1581     UriBuilder_GetQuery,
1582     UriBuilder_GetSchemeName,
1583     UriBuilder_GetUserName,
1584     UriBuilder_SetFragment,
1585     UriBuilder_SetHost,
1586     UriBuilder_SetPassword,
1587     UriBuilder_SetPath,
1588     UriBuilder_SetPort,
1589     UriBuilder_SetQuery,
1590     UriBuilder_SetSchemeName,
1591     UriBuilder_SetUserName,
1592     UriBuilder_RemoveProperties,
1593     UriBuilder_HasBeenModified,
1594 };
1595
1596 /***********************************************************************
1597  *           CreateIUriBuilder (urlmon.@)
1598  */
1599 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
1600 {
1601     UriBuilder *ret;
1602
1603     TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
1604
1605     ret = heap_alloc(sizeof(UriBuilder));
1606     if(!ret)
1607         return E_OUTOFMEMORY;
1608
1609     ret->lpIUriBuilderVtbl = &UriBuilderVtbl;
1610     ret->ref = 1;
1611
1612     *ppIUriBuilder = URIBUILDER(ret);
1613     return S_OK;
1614 }