urlmon: Implemented IUri_GetUserName.
[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_RAW_URI:
864         *pbstrProperty = SysAllocString(This->raw_uri);
865         if(!(*pbstrProperty))
866             hres = E_OUTOFMEMORY;
867         else
868             hres = S_OK;
869         break;
870     case Uri_PROPERTY_SCHEME_NAME:
871         if(This->scheme_start > -1) {
872             *pbstrProperty = SysAllocStringLen(This->canon_uri + This->scheme_start, This->scheme_len);
873             hres = S_OK;
874         } else {
875             *pbstrProperty = SysAllocStringLen(NULL, 0);
876             hres = S_FALSE;
877         }
878
879         if(!(*pbstrProperty))
880             hres = E_OUTOFMEMORY;
881
882         break;
883     case Uri_PROPERTY_USER_INFO:
884         if(This->userinfo_start > -1) {
885             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->userinfo_start, This->userinfo_len);
886             hres = S_OK;
887         } else {
888             *pbstrProperty = SysAllocStringLen(NULL, 0);
889             hres = S_FALSE;
890         }
891
892         if(!(*pbstrProperty))
893             hres = E_OUTOFMEMORY;
894
895         break;
896     case Uri_PROPERTY_USER_NAME:
897         if(This->userinfo_start > -1) {
898             /* If userinfo_split is set, that means a password exists
899              * so the username is only from userinfo_start to userinfo_split.
900              */
901             if(This->userinfo_split > -1) {
902                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_split);
903                 hres = S_OK;
904             } else {
905                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_len);
906                 hres = S_OK;
907             }
908         } else {
909             *pbstrProperty = SysAllocStringLen(NULL, 0);
910             hres = S_FALSE;
911         }
912
913         if(!(*pbstrProperty))
914             return E_OUTOFMEMORY;
915
916         break;
917     default:
918         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
919         hres = E_NOTIMPL;
920     }
921
922     return hres;
923 }
924
925 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
926 {
927     Uri *This = URI_THIS(iface);
928     HRESULT hres;
929     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
930
931     if(!pcchProperty)
932         return E_INVALIDARG;
933
934     /* Can only return a length for a property if it's a string. */
935     if(uriProp > Uri_PROPERTY_STRING_LAST)
936         return E_INVALIDARG;
937
938     /* Don't have support for flags yet. */
939     if(dwFlags) {
940         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
941         return E_NOTIMPL;
942     }
943
944     switch(uriProp) {
945     case Uri_PROPERTY_RAW_URI:
946         *pcchProperty = SysStringLen(This->raw_uri);
947         hres = S_OK;
948         break;
949     case Uri_PROPERTY_SCHEME_NAME:
950         *pcchProperty = This->scheme_len;
951         hres = (This->scheme_start > -1) ? S_OK : S_FALSE;
952         break;
953     case Uri_PROPERTY_USER_INFO:
954         *pcchProperty = This->userinfo_len;
955         hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
956         break;
957     case Uri_PROPERTY_USER_NAME:
958         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_split : This->userinfo_len;
959         hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
960         break;
961     default:
962         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
963         hres = E_NOTIMPL;
964     }
965
966     return hres;
967 }
968
969 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
970 {
971     Uri *This = URI_THIS(iface);
972     HRESULT hres;
973
974     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
975
976     if(!pcchProperty)
977         return E_INVALIDARG;
978
979     /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
980      * From what I can tell, instead of checking which URLZONE the URI belongs to it
981      * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
982      * function.
983      */
984     if(uriProp == Uri_PROPERTY_ZONE) {
985         *pcchProperty = URLZONE_INVALID;
986         return E_NOTIMPL;
987     }
988
989     if(uriProp < Uri_PROPERTY_DWORD_START) {
990         *pcchProperty = 0;
991         return E_INVALIDARG;
992     }
993
994     switch(uriProp) {
995     case Uri_PROPERTY_SCHEME:
996         *pcchProperty = This->scheme_type;
997         hres = S_OK;
998         break;
999     default:
1000         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
1001         hres = E_NOTIMPL;
1002     }
1003
1004     return hres;
1005 }
1006
1007 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
1008 {
1009     Uri *This = URI_THIS(iface);
1010     FIXME("(%p)->(%d %p)\n", This, uriProp, pfHasProperty);
1011
1012     if(!pfHasProperty)
1013         return E_INVALIDARG;
1014
1015     return E_NOTIMPL;
1016 }
1017
1018 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
1019 {
1020     Uri *This = URI_THIS(iface);
1021     FIXME("(%p)->(%p)\n", This, pstrAbsoluteUri);
1022
1023     if(!pstrAbsoluteUri)
1024         return E_POINTER;
1025
1026     return E_NOTIMPL;
1027 }
1028
1029 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
1030 {
1031     Uri *This = URI_THIS(iface);
1032     FIXME("(%p)->(%p)\n", This, pstrAuthority);
1033
1034     if(!pstrAuthority)
1035         return E_POINTER;
1036
1037     return E_NOTIMPL;
1038 }
1039
1040 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
1041 {
1042     Uri *This = URI_THIS(iface);
1043     FIXME("(%p)->(%p)\n", This, pstrDisplayUri);
1044
1045     if(!pstrDisplayUri)
1046         return E_POINTER;
1047
1048     return E_NOTIMPL;
1049 }
1050
1051 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
1052 {
1053     Uri *This = URI_THIS(iface);
1054     FIXME("(%p)->(%p)\n", This, pstrDomain);
1055
1056     if(!pstrDomain)
1057         return E_POINTER;
1058
1059     return E_NOTIMPL;
1060 }
1061
1062 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
1063 {
1064     Uri *This = URI_THIS(iface);
1065     FIXME("(%p)->(%p)\n", This, pstrExtension);
1066
1067     if(!pstrExtension)
1068         return E_POINTER;
1069
1070     return E_NOTIMPL;
1071 }
1072
1073 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
1074 {
1075     Uri *This = URI_THIS(iface);
1076     FIXME("(%p)->(%p)\n", This, pstrFragment);
1077
1078     if(!pstrFragment)
1079         return E_POINTER;
1080
1081     return E_NOTIMPL;
1082 }
1083
1084 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
1085 {
1086     Uri *This = URI_THIS(iface);
1087     FIXME("(%p)->(%p)\n", This, pstrHost);
1088
1089     if(!pstrHost)
1090         return E_POINTER;
1091
1092     return E_NOTIMPL;
1093 }
1094
1095 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
1096 {
1097     Uri *This = URI_THIS(iface);
1098     FIXME("(%p)->(%p)\n", This, pstrPassword);
1099
1100     if(!pstrPassword)
1101         return E_POINTER;
1102
1103     return E_NOTIMPL;
1104 }
1105
1106 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
1107 {
1108     Uri *This = URI_THIS(iface);
1109     FIXME("(%p)->(%p)\n", This, pstrPath);
1110
1111     if(!pstrPath)
1112         return E_POINTER;
1113
1114     return E_NOTIMPL;
1115 }
1116
1117 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
1118 {
1119     Uri *This = URI_THIS(iface);
1120     FIXME("(%p)->(%p)\n", This, pstrPathAndQuery);
1121
1122     if(!pstrPathAndQuery)
1123         return E_POINTER;
1124
1125     return E_NOTIMPL;
1126 }
1127
1128 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
1129 {
1130     Uri *This = URI_THIS(iface);
1131     FIXME("(%p)->(%p)\n", This, pstrQuery);
1132
1133     if(!pstrQuery)
1134         return E_POINTER;
1135
1136     return E_NOTIMPL;
1137 }
1138
1139 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
1140 {
1141     Uri *This = URI_THIS(iface);
1142     TRACE("(%p)->(%p)\n", This, pstrRawUri);
1143
1144     /* Just forward the call to GetPropertyBSTR. */
1145     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
1146 }
1147
1148 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
1149 {
1150     Uri *This = URI_THIS(iface);
1151     TRACE("(%p)->(%p)\n", This, pstrSchemeName);
1152     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_SCHEME_NAME, pstrSchemeName, 0);
1153 }
1154
1155 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
1156 {
1157     TRACE("(%p)->(%p)\n", iface, pstrUserInfo);
1158     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_INFO, pstrUserInfo, 0);
1159 }
1160
1161 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
1162 {
1163     TRACE("(%p)->(%p)\n", iface, pstrUserName);
1164     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_NAME, pstrUserName, 0);
1165 }
1166
1167 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
1168 {
1169     Uri *This = URI_THIS(iface);
1170     FIXME("(%p)->(%p)\n", This, pdwHostType);
1171
1172     if(!pdwHostType)
1173         return E_INVALIDARG;
1174
1175     return E_NOTIMPL;
1176 }
1177
1178 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
1179 {
1180     Uri *This = URI_THIS(iface);
1181     FIXME("(%p)->(%p)\n", This, pdwPort);
1182
1183     if(!pdwPort)
1184         return E_INVALIDARG;
1185
1186     return E_NOTIMPL;
1187 }
1188
1189 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
1190 {
1191     Uri *This = URI_THIS(iface);
1192     TRACE("(%p)->(%p)\n", This, pdwScheme);
1193     return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_SCHEME, pdwScheme, 0);
1194 }
1195
1196 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
1197 {
1198     Uri *This = URI_THIS(iface);
1199     FIXME("(%p)->(%p)\n", This, pdwZone);
1200
1201     if(!pdwZone)
1202         return E_INVALIDARG;
1203
1204     /* Microsoft doesn't seem to have this implemented yet... See
1205      * the comment in Uri_GetPropertyDWORD for more about this.
1206      */
1207     *pdwZone = URLZONE_INVALID;
1208     return E_NOTIMPL;
1209 }
1210
1211 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
1212 {
1213     Uri *This = URI_THIS(iface);
1214     FIXME("(%p)->(%p)\n", This, pdwProperties);
1215
1216     if(!pdwProperties)
1217         return E_INVALIDARG;
1218
1219     return E_NOTIMPL;
1220 }
1221
1222 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
1223 {
1224     Uri *This = URI_THIS(iface);
1225     TRACE("(%p)->(%p %p)\n", This, pUri, pfEqual);
1226
1227     if(!pfEqual)
1228         return E_POINTER;
1229
1230     if(!pUri) {
1231         *pfEqual = FALSE;
1232
1233         /* For some reason Windows returns S_OK here... */
1234         return S_OK;
1235     }
1236
1237     FIXME("(%p)->(%p %p)\n", This, pUri, pfEqual);
1238     return E_NOTIMPL;
1239 }
1240
1241 #undef URI_THIS
1242
1243 static const IUriVtbl UriVtbl = {
1244     Uri_QueryInterface,
1245     Uri_AddRef,
1246     Uri_Release,
1247     Uri_GetPropertyBSTR,
1248     Uri_GetPropertyLength,
1249     Uri_GetPropertyDWORD,
1250     Uri_HasProperty,
1251     Uri_GetAbsoluteUri,
1252     Uri_GetAuthority,
1253     Uri_GetDisplayUri,
1254     Uri_GetDomain,
1255     Uri_GetExtension,
1256     Uri_GetFragment,
1257     Uri_GetHost,
1258     Uri_GetPassword,
1259     Uri_GetPath,
1260     Uri_GetPathAndQuery,
1261     Uri_GetQuery,
1262     Uri_GetRawUri,
1263     Uri_GetSchemeName,
1264     Uri_GetUserInfo,
1265     Uri_GetUserName,
1266     Uri_GetHostType,
1267     Uri_GetPort,
1268     Uri_GetScheme,
1269     Uri_GetZone,
1270     Uri_GetProperties,
1271     Uri_IsEqual
1272 };
1273
1274 /***********************************************************************
1275  *           CreateUri (urlmon.@)
1276  */
1277 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
1278 {
1279     Uri *ret;
1280     HRESULT hr;
1281     parse_data data;
1282
1283     TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
1284
1285     if(!ppURI)
1286         return E_INVALIDARG;
1287
1288     if(!pwzURI) {
1289         *ppURI = NULL;
1290         return E_INVALIDARG;
1291     }
1292
1293     ret = heap_alloc(sizeof(Uri));
1294     if(!ret)
1295         return E_OUTOFMEMORY;
1296
1297     ret->lpIUriVtbl = &UriVtbl;
1298     ret->ref = 1;
1299
1300     /* Create a copy of pwzURI and store it as the raw_uri. */
1301     ret->raw_uri = SysAllocString(pwzURI);
1302     if(!ret->raw_uri) {
1303         heap_free(ret);
1304         return E_OUTOFMEMORY;
1305     }
1306
1307     memset(&data, 0, sizeof(parse_data));
1308     data.uri = ret->raw_uri;
1309
1310     /* Validate and parse the URI into it's components. */
1311     if(!parse_uri(&data, dwFlags)) {
1312         /* Encountered an unsupported or invalid URI */
1313         SysFreeString(ret->raw_uri);
1314         heap_free(ret);
1315         *ppURI = NULL;
1316         return E_INVALIDARG;
1317     }
1318
1319     /* Canonicalize the URI. */
1320     hr = canonicalize_uri(&data, ret, dwFlags);
1321     if(FAILED(hr)) {
1322         SysFreeString(ret->raw_uri);
1323         heap_free(ret);
1324         *ppURI = NULL;
1325         return hr;
1326     }
1327
1328     *ppURI = URI(ret);
1329     return S_OK;
1330 }
1331
1332 #define URIBUILDER_THIS(iface) DEFINE_THIS(UriBuilder, IUriBuilder, iface)
1333
1334 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
1335 {
1336     UriBuilder *This = URIBUILDER_THIS(iface);
1337
1338     if(IsEqualGUID(&IID_IUnknown, riid)) {
1339         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
1340         *ppv = URIBUILDER(This);
1341     }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
1342         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
1343         *ppv = URIBUILDER(This);
1344     }else {
1345         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1346         *ppv = NULL;
1347         return E_NOINTERFACE;
1348     }
1349
1350     IUnknown_AddRef((IUnknown*)*ppv);
1351     return S_OK;
1352 }
1353
1354 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
1355 {
1356     UriBuilder *This = URIBUILDER_THIS(iface);
1357     LONG ref = InterlockedIncrement(&This->ref);
1358
1359     TRACE("(%p) ref=%d\n", This, ref);
1360
1361     return ref;
1362 }
1363
1364 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
1365 {
1366     UriBuilder *This = URIBUILDER_THIS(iface);
1367     LONG ref = InterlockedDecrement(&This->ref);
1368
1369     TRACE("(%p) ref=%d\n", This, ref);
1370
1371     if(!ref)
1372         heap_free(This);
1373
1374     return ref;
1375 }
1376
1377 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
1378                                                  DWORD        dwAllowEncodingPropertyMask,
1379                                                  DWORD_PTR    dwReserved,
1380                                                  IUri       **ppIUri)
1381 {
1382     UriBuilder *This = URIBUILDER_THIS(iface);
1383     FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1384     return E_NOTIMPL;
1385 }
1386
1387 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
1388                                            DWORD        dwCreateFlags,
1389                                            DWORD        dwAllowEncodingPropertyMask,
1390                                            DWORD_PTR    dwReserved,
1391                                            IUri       **ppIUri)
1392 {
1393     UriBuilder *This = URIBUILDER_THIS(iface);
1394     FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1395     return E_NOTIMPL;
1396 }
1397
1398 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
1399                                          DWORD        dwCreateFlags,
1400                                          DWORD        dwUriBuilderFlags,
1401                                          DWORD        dwAllowEncodingPropertyMask,
1402                                          DWORD_PTR    dwReserved,
1403                                          IUri       **ppIUri)
1404 {
1405     UriBuilder *This = URIBUILDER_THIS(iface);
1406     FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
1407         dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1408     return E_NOTIMPL;
1409 }
1410
1411 static HRESULT WINAPI  UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
1412 {
1413     UriBuilder *This = URIBUILDER_THIS(iface);
1414     FIXME("(%p)->(%p)\n", This, ppIUri);
1415     return E_NOTIMPL;
1416 }
1417
1418 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
1419 {
1420     UriBuilder *This = URIBUILDER_THIS(iface);
1421     FIXME("(%p)->(%p)\n", This, pIUri);
1422     return E_NOTIMPL;
1423 }
1424
1425 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
1426 {
1427     UriBuilder *This = URIBUILDER_THIS(iface);
1428     FIXME("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
1429     return E_NOTIMPL;
1430 }
1431
1432 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
1433 {
1434     UriBuilder *This = URIBUILDER_THIS(iface);
1435     FIXME("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
1436     return E_NOTIMPL;
1437 }
1438
1439 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
1440 {
1441     UriBuilder *This = URIBUILDER_THIS(iface);
1442     FIXME("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
1443     return E_NOTIMPL;
1444 }
1445
1446 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
1447 {
1448     UriBuilder *This = URIBUILDER_THIS(iface);
1449     FIXME("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
1450     return E_NOTIMPL;
1451 }
1452
1453 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
1454 {
1455     UriBuilder *This = URIBUILDER_THIS(iface);
1456     FIXME("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
1457     return E_NOTIMPL;
1458 }
1459
1460 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
1461 {
1462     UriBuilder *This = URIBUILDER_THIS(iface);
1463     FIXME("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
1464     return E_NOTIMPL;
1465 }
1466
1467 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
1468 {
1469     UriBuilder *This = URIBUILDER_THIS(iface);
1470     FIXME("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
1471     return E_NOTIMPL;
1472 }
1473
1474 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
1475 {
1476     UriBuilder *This = URIBUILDER_THIS(iface);
1477     FIXME("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
1478     return E_NOTIMPL;
1479 }
1480
1481 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
1482 {
1483     UriBuilder *This = URIBUILDER_THIS(iface);
1484     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1485     return E_NOTIMPL;
1486 }
1487
1488 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
1489 {
1490     UriBuilder *This = URIBUILDER_THIS(iface);
1491     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1492     return E_NOTIMPL;
1493 }
1494
1495 static HRESULT WINAPI UriBuilder_SetPassword(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_SetPath(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_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
1510 {
1511     UriBuilder *This = URIBUILDER_THIS(iface);
1512     FIXME("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
1513     return E_NOTIMPL;
1514 }
1515
1516 static HRESULT WINAPI UriBuilder_SetQuery(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_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
1524 {
1525     UriBuilder *This = URIBUILDER_THIS(iface);
1526     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1527     return E_NOTIMPL;
1528 }
1529
1530 static HRESULT WINAPI UriBuilder_SetUserName(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_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
1538 {
1539     UriBuilder *This = URIBUILDER_THIS(iface);
1540     FIXME("(%p)->(0x%08x)\n", This, dwPropertyMask);
1541     return E_NOTIMPL;
1542 }
1543
1544 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
1545 {
1546     UriBuilder *This = URIBUILDER_THIS(iface);
1547     FIXME("(%p)->(%p)\n", This, pfModified);
1548     return E_NOTIMPL;
1549 }
1550
1551 #undef URIBUILDER_THIS
1552
1553 static const IUriBuilderVtbl UriBuilderVtbl = {
1554     UriBuilder_QueryInterface,
1555     UriBuilder_AddRef,
1556     UriBuilder_Release,
1557     UriBuilder_CreateUriSimple,
1558     UriBuilder_CreateUri,
1559     UriBuilder_CreateUriWithFlags,
1560     UriBuilder_GetIUri,
1561     UriBuilder_SetIUri,
1562     UriBuilder_GetFragment,
1563     UriBuilder_GetHost,
1564     UriBuilder_GetPassword,
1565     UriBuilder_GetPath,
1566     UriBuilder_GetPort,
1567     UriBuilder_GetQuery,
1568     UriBuilder_GetSchemeName,
1569     UriBuilder_GetUserName,
1570     UriBuilder_SetFragment,
1571     UriBuilder_SetHost,
1572     UriBuilder_SetPassword,
1573     UriBuilder_SetPath,
1574     UriBuilder_SetPort,
1575     UriBuilder_SetQuery,
1576     UriBuilder_SetSchemeName,
1577     UriBuilder_SetUserName,
1578     UriBuilder_RemoveProperties,
1579     UriBuilder_HasBeenModified,
1580 };
1581
1582 /***********************************************************************
1583  *           CreateIUriBuilder (urlmon.@)
1584  */
1585 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
1586 {
1587     UriBuilder *ret;
1588
1589     TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
1590
1591     ret = heap_alloc(sizeof(UriBuilder));
1592     if(!ret)
1593         return E_OUTOFMEMORY;
1594
1595     ret->lpIUriBuilderVtbl = &UriBuilderVtbl;
1596     ret->ref = 1;
1597
1598     *ppIUriBuilder = URIBUILDER(ret);
1599     return S_OK;
1600 }