urlmon: Added a canonicalization function for the scheme of a URI.
[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 } Uri;
42
43 typedef struct {
44     const IUriBuilderVtbl  *lpIUriBuilderVtbl;
45     LONG ref;
46 } UriBuilder;
47
48 typedef struct {
49     BSTR            uri;
50
51     BOOL            is_relative;
52
53     const WCHAR     *scheme;
54     DWORD           scheme_len;
55     URL_SCHEME      scheme_type;
56 } parse_data;
57
58 /* List of scheme types/scheme names that are recognized by the IUri interface as of IE 7. */
59 static const struct {
60     URL_SCHEME  scheme;
61     WCHAR       scheme_name[16];
62 } recognized_schemes[] = {
63     {URL_SCHEME_FTP,            {'f','t','p',0}},
64     {URL_SCHEME_HTTP,           {'h','t','t','p',0}},
65     {URL_SCHEME_GOPHER,         {'g','o','p','h','e','r',0}},
66     {URL_SCHEME_MAILTO,         {'m','a','i','l','t','o',0}},
67     {URL_SCHEME_NEWS,           {'n','e','w','s',0}},
68     {URL_SCHEME_NNTP,           {'n','n','t','p',0}},
69     {URL_SCHEME_TELNET,         {'t','e','l','n','e','t',0}},
70     {URL_SCHEME_WAIS,           {'w','a','i','s',0}},
71     {URL_SCHEME_FILE,           {'f','i','l','e',0}},
72     {URL_SCHEME_MK,             {'m','k',0}},
73     {URL_SCHEME_HTTPS,          {'h','t','t','p','s',0}},
74     {URL_SCHEME_SHELL,          {'s','h','e','l','l',0}},
75     {URL_SCHEME_SNEWS,          {'s','n','e','w','s',0}},
76     {URL_SCHEME_LOCAL,          {'l','o','c','a','l',0}},
77     {URL_SCHEME_JAVASCRIPT,     {'j','a','v','a','s','c','r','i','p','t',0}},
78     {URL_SCHEME_VBSCRIPT,       {'v','b','s','c','r','i','p','t',0}},
79     {URL_SCHEME_ABOUT,          {'a','b','o','u','t',0}},
80     {URL_SCHEME_RES,            {'r','e','s',0}},
81     {URL_SCHEME_MSSHELLROOTED,  {'m','s','-','s','h','e','l','l','-','r','o','o','t','e','d',0}},
82     {URL_SCHEME_MSSHELLIDLIST,  {'m','s','-','s','h','e','l','l','-','i','d','l','i','s','t',0}},
83     {URL_SCHEME_MSHELP,         {'h','c','p',0}},
84     {URL_SCHEME_WILDCARD,       {'*',0}}
85 };
86
87 static inline BOOL is_alpha(WCHAR val) {
88         return ((val >= 'a' && val <= 'z') || (val >= 'A' && val <= 'Z'));
89 }
90
91 static inline BOOL is_num(WCHAR val) {
92         return (val >= '0' && val <= '9');
93 }
94
95 /* A URI is implicitly a file path if it begins with
96  * a drive letter (eg X:) or starts with "\\" (UNC path).
97  */
98 static inline BOOL is_implicit_file_path(const WCHAR *str) {
99     if(is_alpha(str[0]) && str[1] == ':')
100         return TRUE;
101     else if(str[0] == '\\' && str[1] == '\\')
102         return TRUE;
103
104     return FALSE;
105 }
106
107 /* Tries to parse the scheme name of the URI.
108  *
109  * scheme = ALPHA *(ALPHA | NUM | '+' | '-' | '.') as defined by RFC 3896.
110  * NOTE: Windows accepts a number as the first character of a scheme.
111  */
112 static BOOL parse_scheme_name(const WCHAR **ptr, parse_data *data) {
113     const WCHAR *start = *ptr;
114
115     data->scheme = NULL;
116     data->scheme_len = 0;
117
118     while(**ptr) {
119         if(!is_num(**ptr) && !is_alpha(**ptr) && **ptr != '+' &&
120            **ptr != '-' && **ptr != '.')
121             break;
122
123         (*ptr)++;
124     }
125
126     if(*ptr == start)
127         return FALSE;
128
129     /* Schemes must end with a ':' */
130     if(**ptr != ':') {
131         *ptr = start;
132         return FALSE;
133     }
134
135     data->scheme = start;
136     data->scheme_len = *ptr - start;
137
138     ++(*ptr);
139     return TRUE;
140 }
141
142 /* Tries to deduce the corresponding URL_SCHEME for the given URI. Stores
143  * the deduced URL_SCHEME in data->scheme_type.
144  */
145 static BOOL parse_scheme_type(parse_data *data) {
146     /* If there's scheme data then see if it's a recognized scheme. */
147     if(data->scheme && data->scheme_len) {
148         DWORD i;
149
150         for(i = 0; i < sizeof(recognized_schemes)/sizeof(recognized_schemes[0]); ++i) {
151             if(lstrlenW(recognized_schemes[i].scheme_name) == data->scheme_len) {
152                 /* Has to be a case insensitive compare. */
153                 if(!StrCmpNIW(recognized_schemes[i].scheme_name, data->scheme, data->scheme_len)) {
154                     data->scheme_type = recognized_schemes[i].scheme;
155                     return TRUE;
156                 }
157             }
158         }
159
160         /* If we get here it means it's not a recognized scheme. */
161         data->scheme_type = URL_SCHEME_UNKNOWN;
162         return TRUE;
163     } else if(data->is_relative) {
164         /* Relative URI's have no scheme. */
165         data->scheme_type = URL_SCHEME_UNKNOWN;
166         return TRUE;
167     } else {
168         /* Should never reach here! what happened... */
169         FIXME("(%p): Unable to determine scheme type for URI %s\n", data, debugstr_w(data->uri));
170         return FALSE;
171     }
172 }
173
174 /* Tries to parse (or deduce) the scheme_name of a URI. If it can't
175  * parse a scheme from the URI it will try to deduce the scheme_name and scheme_type
176  * using the flags specified in 'flags' (if any). Flags that affect how this function
177  * operates are the Uri_CREATE_ALLOW_* flags.
178  *
179  * All parsed/deduced information will be stored in 'data' when the function returns.
180  *
181  * Returns TRUE if it was able to successfully parse the information.
182  */
183 static BOOL parse_scheme(const WCHAR **ptr, parse_data *data, DWORD flags) {
184     static const WCHAR fileW[] = {'f','i','l','e',0};
185     static const WCHAR wildcardW[] = {'*',0};
186
187     /* First check to see if the uri could implicitly be a file path. */
188     if(is_implicit_file_path(*ptr)) {
189         if(flags & Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME) {
190             data->scheme = fileW;
191             data->scheme_len = lstrlenW(fileW);
192             TRACE("(%p %p %x): URI is an implicit file path.\n", ptr, data, flags);
193         } else {
194             /* Window's does not consider anything that can implicitly be a file
195              * path to be a valid URI if the ALLOW_IMPLICIT_FILE_SCHEME flag is not set...
196              */
197             TRACE("(%p %p %x): URI is implicitly a file path, but, the ALLOW_IMPLICIT_FILE_SCHEME flag wasn't set.\n",
198                     ptr, data, flags);
199             return FALSE;
200         }
201     } else if(!parse_scheme_name(ptr, data)) {
202         /* No Scheme was found, this means it could be:
203          *      a) an implicit Wildcard scheme
204          *      b) a relative URI
205          *      c) a invalid URI.
206          */
207         if(flags & Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME) {
208             data->scheme = wildcardW;
209             data->scheme_len = lstrlenW(wildcardW);
210
211             TRACE("(%p %p %x): URI is an implicit wildcard scheme.\n", ptr, data, flags);
212         } else if (flags & Uri_CREATE_ALLOW_RELATIVE) {
213             data->is_relative = TRUE;
214             TRACE("(%p %p %x): URI is relative.\n", ptr, data, flags);
215         } else {
216             TRACE("(%p %p %x): Malformed URI found. Unable to deduce scheme name.\n", ptr, data, flags);
217             return FALSE;
218         }
219     }
220
221     if(!data->is_relative)
222         TRACE("(%p %p %x): Found scheme=%s scheme_len=%d\n", ptr, data, flags,
223                 debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
224
225     if(!parse_scheme_type(data))
226         return FALSE;
227
228     TRACE("(%p %p %x): Assigned %d as the URL_SCHEME.\n", ptr, data, flags, data->scheme_type);
229     return TRUE;
230 }
231
232 /* Parses and validates the components of the specified by data->uri
233  * and stores the information it parses into 'data'.
234  *
235  * Returns TRUE if it successfully parsed the URI. False otherwise.
236  */
237 static BOOL parse_uri(parse_data *data, DWORD flags) {
238     const WCHAR *ptr;
239     const WCHAR **pptr;
240
241     ptr = data->uri;
242     pptr = &ptr;
243
244     TRACE("(%p %x): BEGINNING TO PARSE URI %s.\n", data, flags, debugstr_w(data->uri));
245
246     if(!parse_scheme(pptr, data, flags))
247         return FALSE;
248
249     TRACE("(%p %x): FINISHED PARSING URI.\n", data, flags);
250     return TRUE;
251 }
252
253 /* Canonicalizes the scheme information specified in the parse_data using the specified flags. */
254 static BOOL canonicalize_scheme(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
255     uri->scheme_start = -1;
256     uri->scheme_len = 0;
257
258     if(!data->scheme) {
259         /* The only type of URI that doesn't have to have a scheme is a relative
260          * URI.
261          */
262         if(!data->is_relative) {
263             FIXME("(%p %p %x): Unable to determine the scheme type of %s.\n", data,
264                     uri, flags, debugstr_w(data->uri));
265             return FALSE;
266         }
267     } else {
268         if(!computeOnly) {
269             DWORD i;
270             INT pos = uri->canon_len;
271
272             for(i = 0; i < data->scheme_len; ++i) {
273                 /* Scheme name must be lower case after canonicalization. */
274                 uri->canon_uri[i + pos] = tolowerW(data->scheme[i]);
275             }
276
277             uri->canon_uri[i + pos] = ':';
278             uri->scheme_start = pos;
279
280             TRACE("(%p %p %x): Canonicalized scheme=%s, len=%d.\n", data, uri, flags,
281                     debugstr_wn(uri->canon_uri,  uri->scheme_len), data->scheme_len);
282         }
283
284         /* This happens in both compute only and non-compute only. */
285         uri->canon_len += data->scheme_len + 1;
286         uri->scheme_len = data->scheme_len;
287     }
288     return TRUE;
289 }
290
291 /* Compute's what the length of the URI specified by the parse_data will be
292  * after canonicalization occurs using the specified flags.
293  *
294  * This function will return a non-zero value indicating the length of the canonicalized
295  * URI, or -1 on error.
296  */
297 static int compute_canonicalized_length(const parse_data *data, DWORD flags) {
298     Uri uri;
299
300     memset(&uri, 0, sizeof(Uri));
301
302     TRACE("(%p %x): Beginning to compute canonicalized length for URI %s\n", data, flags,
303             debugstr_w(data->uri));
304
305     if(!canonicalize_scheme(data, &uri, flags, TRUE)) {
306         ERR("(%p %x): Failed to compute URI scheme length.\n", data, flags);
307         return -1;
308     }
309
310     TRACE("(%p %x): Finished computing canonicalized URI length. length=%d\n", data, flags, uri.canon_len);
311
312     return uri.canon_len;
313 }
314
315 /* Canonicalizes the URI data specified in the parse_data, using the given flags. If the
316  * canonicalization succeededs it will store all the canonicalization information
317  * in the pointer to the Uri.
318  *
319  * To canonicalize a URI this function first computes what the length of the URI
320  * specified by the parse_data will be. Once this is done it will then perfom the actual
321  * canonicalization of the URI.
322  */
323 static HRESULT canonicalize_uri(const parse_data *data, Uri *uri, DWORD flags) {
324     INT len;
325
326     uri->canon_uri = NULL;
327     len = uri->canon_size = uri->canon_len = 0;
328
329     TRACE("(%p %p %x): beginning to canonicalize URI %s.\n", data, uri, flags, debugstr_w(data->uri));
330
331     /* First try to compute the length of the URI. */
332     len = compute_canonicalized_length(data, flags);
333     if(len == -1) {
334         ERR("(%p %p %x): Could not compute the canonicalized length of %s.\n", data, uri, flags,
335                 debugstr_w(data->uri));
336         return E_INVALIDARG;
337     }
338
339     uri->canon_uri = heap_alloc((len+1)*sizeof(WCHAR));
340     if(!uri->canon_uri)
341         return E_OUTOFMEMORY;
342
343     if(!canonicalize_scheme(data, uri, flags, FALSE)) {
344         ERR("(%p %p %x): Unable to canonicalize the scheme of the URI.\n", data, uri, flags);
345         heap_free(uri->canon_uri);
346         return E_INVALIDARG;
347     }
348     uri->scheme_type = data->scheme_type;
349
350     uri->canon_uri[uri->canon_len] = '\0';
351     TRACE("(%p %p %x): finished canonicalizing the URI.\n", data, uri, flags);
352
353     return S_OK;
354 }
355
356 #define URI(x)         ((IUri*)  &(x)->lpIUriVtbl)
357 #define URIBUILDER(x)  ((IUriBuilder*)  &(x)->lpIUriBuilderVtbl)
358
359 #define URI_THIS(iface) DEFINE_THIS(Uri, IUri, iface)
360
361 static HRESULT WINAPI Uri_QueryInterface(IUri *iface, REFIID riid, void **ppv)
362 {
363     Uri *This = URI_THIS(iface);
364
365     if(IsEqualGUID(&IID_IUnknown, riid)) {
366         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
367         *ppv = URI(This);
368     }else if(IsEqualGUID(&IID_IUri, riid)) {
369         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
370         *ppv = URI(This);
371     }else {
372         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
373         *ppv = NULL;
374         return E_NOINTERFACE;
375     }
376
377     IUnknown_AddRef((IUnknown*)*ppv);
378     return S_OK;
379 }
380
381 static ULONG WINAPI Uri_AddRef(IUri *iface)
382 {
383     Uri *This = URI_THIS(iface);
384     LONG ref = InterlockedIncrement(&This->ref);
385
386     TRACE("(%p) ref=%d\n", This, ref);
387
388     return ref;
389 }
390
391 static ULONG WINAPI Uri_Release(IUri *iface)
392 {
393     Uri *This = URI_THIS(iface);
394     LONG ref = InterlockedDecrement(&This->ref);
395
396     TRACE("(%p) ref=%d\n", This, ref);
397
398     if(!ref) {
399         SysFreeString(This->raw_uri);
400         heap_free(This->canon_uri);
401         heap_free(This);
402     }
403
404     return ref;
405 }
406
407 static HRESULT WINAPI Uri_GetPropertyBSTR(IUri *iface, Uri_PROPERTY uriProp, BSTR *pbstrProperty, DWORD dwFlags)
408 {
409     Uri *This = URI_THIS(iface);
410     HRESULT hres;
411     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
412
413     if(!pbstrProperty)
414         return E_POINTER;
415
416     if(uriProp > Uri_PROPERTY_STRING_LAST) {
417         /* Windows allocates an empty BSTR for invalid Uri_PROPERTY's. */
418         *pbstrProperty = SysAllocStringLen(NULL, 0);
419
420         /* It only returns S_FALSE for the ZONE property... */
421         if(uriProp == Uri_PROPERTY_ZONE)
422             return S_FALSE;
423         else
424             return S_OK;
425     }
426
427     /* Don't have support for flags yet. */
428     if(dwFlags) {
429         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
430         return E_NOTIMPL;
431     }
432
433     switch(uriProp) {
434     case Uri_PROPERTY_RAW_URI:
435         *pbstrProperty = SysAllocString(This->raw_uri);
436         if(!(*pbstrProperty))
437             hres = E_OUTOFMEMORY;
438         else
439             hres = S_OK;
440         break;
441     default:
442         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
443         hres = E_NOTIMPL;
444     }
445
446     return hres;
447 }
448
449 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
450 {
451     Uri *This = URI_THIS(iface);
452     HRESULT hres;
453     TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
454
455     if(!pcchProperty)
456         return E_INVALIDARG;
457
458     /* Can only return a length for a property if it's a string. */
459     if(uriProp > Uri_PROPERTY_STRING_LAST)
460         return E_INVALIDARG;
461
462     /* Don't have support for flags yet. */
463     if(dwFlags) {
464         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
465         return E_NOTIMPL;
466     }
467
468     switch(uriProp) {
469     case Uri_PROPERTY_RAW_URI:
470         *pcchProperty = SysStringLen(This->raw_uri);
471         hres = S_OK;
472         break;
473     default:
474         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
475         hres = E_NOTIMPL;
476     }
477
478     return hres;
479 }
480
481 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
482 {
483     Uri *This = URI_THIS(iface);
484     FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
485
486     if(!pcchProperty)
487         return E_INVALIDARG;
488
489     /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
490      * From what I can tell, instead of checking which URLZONE the URI belongs to it
491      * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
492      * function.
493      */
494     if(uriProp == Uri_PROPERTY_ZONE) {
495         *pcchProperty = URLZONE_INVALID;
496         return E_NOTIMPL;
497     }
498
499     if(uriProp < Uri_PROPERTY_DWORD_START) {
500         *pcchProperty = 0;
501         return E_INVALIDARG;
502     }
503
504     return E_NOTIMPL;
505 }
506
507 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
508 {
509     Uri *This = URI_THIS(iface);
510     FIXME("(%p)->(%d %p)\n", This, uriProp, pfHasProperty);
511
512     if(!pfHasProperty)
513         return E_INVALIDARG;
514
515     return E_NOTIMPL;
516 }
517
518 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
519 {
520     Uri *This = URI_THIS(iface);
521     FIXME("(%p)->(%p)\n", This, pstrAbsoluteUri);
522
523     if(!pstrAbsoluteUri)
524         return E_POINTER;
525
526     return E_NOTIMPL;
527 }
528
529 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
530 {
531     Uri *This = URI_THIS(iface);
532     FIXME("(%p)->(%p)\n", This, pstrAuthority);
533
534     if(!pstrAuthority)
535         return E_POINTER;
536
537     return E_NOTIMPL;
538 }
539
540 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
541 {
542     Uri *This = URI_THIS(iface);
543     FIXME("(%p)->(%p)\n", This, pstrDisplayUri);
544
545     if(!pstrDisplayUri)
546         return E_POINTER;
547
548     return E_NOTIMPL;
549 }
550
551 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
552 {
553     Uri *This = URI_THIS(iface);
554     FIXME("(%p)->(%p)\n", This, pstrDomain);
555
556     if(!pstrDomain)
557         return E_POINTER;
558
559     return E_NOTIMPL;
560 }
561
562 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
563 {
564     Uri *This = URI_THIS(iface);
565     FIXME("(%p)->(%p)\n", This, pstrExtension);
566
567     if(!pstrExtension)
568         return E_POINTER;
569
570     return E_NOTIMPL;
571 }
572
573 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
574 {
575     Uri *This = URI_THIS(iface);
576     FIXME("(%p)->(%p)\n", This, pstrFragment);
577
578     if(!pstrFragment)
579         return E_POINTER;
580
581     return E_NOTIMPL;
582 }
583
584 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
585 {
586     Uri *This = URI_THIS(iface);
587     FIXME("(%p)->(%p)\n", This, pstrHost);
588
589     if(!pstrHost)
590         return E_POINTER;
591
592     return E_NOTIMPL;
593 }
594
595 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
596 {
597     Uri *This = URI_THIS(iface);
598     FIXME("(%p)->(%p)\n", This, pstrPassword);
599
600     if(!pstrPassword)
601         return E_POINTER;
602
603     return E_NOTIMPL;
604 }
605
606 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
607 {
608     Uri *This = URI_THIS(iface);
609     FIXME("(%p)->(%p)\n", This, pstrPath);
610
611     if(!pstrPath)
612         return E_POINTER;
613
614     return E_NOTIMPL;
615 }
616
617 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
618 {
619     Uri *This = URI_THIS(iface);
620     FIXME("(%p)->(%p)\n", This, pstrPathAndQuery);
621
622     if(!pstrPathAndQuery)
623         return E_POINTER;
624
625     return E_NOTIMPL;
626 }
627
628 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
629 {
630     Uri *This = URI_THIS(iface);
631     FIXME("(%p)->(%p)\n", This, pstrQuery);
632
633     if(!pstrQuery)
634         return E_POINTER;
635
636     return E_NOTIMPL;
637 }
638
639 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
640 {
641     Uri *This = URI_THIS(iface);
642     TRACE("(%p)->(%p)\n", This, pstrRawUri);
643
644     /* Just forward the call to GetPropertyBSTR. */
645     return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
646 }
647
648 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
649 {
650     Uri *This = URI_THIS(iface);
651     FIXME("(%p)->(%p)\n", This, pstrSchemeName);
652
653     if(!pstrSchemeName)
654         return E_POINTER;
655
656     return E_NOTIMPL;
657 }
658
659 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
660 {
661     Uri *This = URI_THIS(iface);
662     FIXME("(%p)->(%p)\n", This, pstrUserInfo);
663
664     if(!pstrUserInfo)
665         return E_POINTER;
666
667     return E_NOTIMPL;
668 }
669
670 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
671 {
672     Uri *This = URI_THIS(iface);
673     FIXME("(%p)->(%p)\n", This, pstrUserName);
674
675     if(!pstrUserName)
676         return E_POINTER;
677
678     return E_NOTIMPL;
679 }
680
681 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
682 {
683     Uri *This = URI_THIS(iface);
684     FIXME("(%p)->(%p)\n", This, pdwHostType);
685
686     if(!pdwHostType)
687         return E_INVALIDARG;
688
689     return E_NOTIMPL;
690 }
691
692 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
693 {
694     Uri *This = URI_THIS(iface);
695     FIXME("(%p)->(%p)\n", This, pdwPort);
696
697     if(!pdwPort)
698         return E_INVALIDARG;
699
700     return E_NOTIMPL;
701 }
702
703 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
704 {
705     Uri *This = URI_THIS(iface);
706     FIXME("(%p)->(%p)\n", This, pdwScheme);
707
708     if(!pdwScheme)
709         return E_INVALIDARG;
710
711     return E_NOTIMPL;
712 }
713
714 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
715 {
716     Uri *This = URI_THIS(iface);
717     FIXME("(%p)->(%p)\n", This, pdwZone);
718
719     if(!pdwZone)
720         return E_INVALIDARG;
721
722     /* Microsoft doesn't seem to have this implemented yet... See
723      * the comment in Uri_GetPropertyDWORD for more about this.
724      */
725     *pdwZone = URLZONE_INVALID;
726     return E_NOTIMPL;
727 }
728
729 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
730 {
731     Uri *This = URI_THIS(iface);
732     FIXME("(%p)->(%p)\n", This, pdwProperties);
733
734     if(!pdwProperties)
735         return E_INVALIDARG;
736
737     return E_NOTIMPL;
738 }
739
740 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
741 {
742     Uri *This = URI_THIS(iface);
743     TRACE("(%p)->(%p %p)\n", This, pUri, pfEqual);
744
745     if(!pfEqual)
746         return E_POINTER;
747
748     if(!pUri) {
749         *pfEqual = FALSE;
750
751         /* For some reason Windows returns S_OK here... */
752         return S_OK;
753     }
754
755     FIXME("(%p)->(%p %p)\n", This, pUri, pfEqual);
756     return E_NOTIMPL;
757 }
758
759 #undef URI_THIS
760
761 static const IUriVtbl UriVtbl = {
762     Uri_QueryInterface,
763     Uri_AddRef,
764     Uri_Release,
765     Uri_GetPropertyBSTR,
766     Uri_GetPropertyLength,
767     Uri_GetPropertyDWORD,
768     Uri_HasProperty,
769     Uri_GetAbsoluteUri,
770     Uri_GetAuthority,
771     Uri_GetDisplayUri,
772     Uri_GetDomain,
773     Uri_GetExtension,
774     Uri_GetFragment,
775     Uri_GetHost,
776     Uri_GetPassword,
777     Uri_GetPath,
778     Uri_GetPathAndQuery,
779     Uri_GetQuery,
780     Uri_GetRawUri,
781     Uri_GetSchemeName,
782     Uri_GetUserInfo,
783     Uri_GetUserName,
784     Uri_GetHostType,
785     Uri_GetPort,
786     Uri_GetScheme,
787     Uri_GetZone,
788     Uri_GetProperties,
789     Uri_IsEqual
790 };
791
792 /***********************************************************************
793  *           CreateUri (urlmon.@)
794  */
795 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
796 {
797     Uri *ret;
798     HRESULT hr;
799     parse_data data;
800
801     TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
802
803     if(!ppURI)
804         return E_INVALIDARG;
805
806     if(!pwzURI) {
807         *ppURI = NULL;
808         return E_INVALIDARG;
809     }
810
811     ret = heap_alloc(sizeof(Uri));
812     if(!ret)
813         return E_OUTOFMEMORY;
814
815     ret->lpIUriVtbl = &UriVtbl;
816     ret->ref = 1;
817
818     /* Create a copy of pwzURI and store it as the raw_uri. */
819     ret->raw_uri = SysAllocString(pwzURI);
820     if(!ret->raw_uri) {
821         heap_free(ret);
822         return E_OUTOFMEMORY;
823     }
824
825     memset(&data, 0, sizeof(parse_data));
826     data.uri = ret->raw_uri;
827
828     /* Validate and parse the URI into it's components. */
829     if(!parse_uri(&data, dwFlags)) {
830         /* Encountered an unsupported or invalid URI */
831         SysFreeString(ret->raw_uri);
832         heap_free(ret);
833         *ppURI = NULL;
834         return E_INVALIDARG;
835     }
836
837     /* Canonicalize the URI. */
838     hr = canonicalize_uri(&data, ret, dwFlags);
839     if(FAILED(hr)) {
840         SysFreeString(ret->raw_uri);
841         heap_free(ret);
842         *ppURI = NULL;
843         return hr;
844     }
845
846     *ppURI = URI(ret);
847     return S_OK;
848 }
849
850 #define URIBUILDER_THIS(iface) DEFINE_THIS(UriBuilder, IUriBuilder, iface)
851
852 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
853 {
854     UriBuilder *This = URIBUILDER_THIS(iface);
855
856     if(IsEqualGUID(&IID_IUnknown, riid)) {
857         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
858         *ppv = URIBUILDER(This);
859     }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
860         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
861         *ppv = URIBUILDER(This);
862     }else {
863         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
864         *ppv = NULL;
865         return E_NOINTERFACE;
866     }
867
868     IUnknown_AddRef((IUnknown*)*ppv);
869     return S_OK;
870 }
871
872 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
873 {
874     UriBuilder *This = URIBUILDER_THIS(iface);
875     LONG ref = InterlockedIncrement(&This->ref);
876
877     TRACE("(%p) ref=%d\n", This, ref);
878
879     return ref;
880 }
881
882 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
883 {
884     UriBuilder *This = URIBUILDER_THIS(iface);
885     LONG ref = InterlockedDecrement(&This->ref);
886
887     TRACE("(%p) ref=%d\n", This, ref);
888
889     if(!ref)
890         heap_free(This);
891
892     return ref;
893 }
894
895 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
896                                                  DWORD        dwAllowEncodingPropertyMask,
897                                                  DWORD_PTR    dwReserved,
898                                                  IUri       **ppIUri)
899 {
900     UriBuilder *This = URIBUILDER_THIS(iface);
901     FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
902     return E_NOTIMPL;
903 }
904
905 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
906                                            DWORD        dwCreateFlags,
907                                            DWORD        dwAllowEncodingPropertyMask,
908                                            DWORD_PTR    dwReserved,
909                                            IUri       **ppIUri)
910 {
911     UriBuilder *This = URIBUILDER_THIS(iface);
912     FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
913     return E_NOTIMPL;
914 }
915
916 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
917                                          DWORD        dwCreateFlags,
918                                          DWORD        dwUriBuilderFlags,
919                                          DWORD        dwAllowEncodingPropertyMask,
920                                          DWORD_PTR    dwReserved,
921                                          IUri       **ppIUri)
922 {
923     UriBuilder *This = URIBUILDER_THIS(iface);
924     FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
925         dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
926     return E_NOTIMPL;
927 }
928
929 static HRESULT WINAPI  UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
930 {
931     UriBuilder *This = URIBUILDER_THIS(iface);
932     FIXME("(%p)->(%p)\n", This, ppIUri);
933     return E_NOTIMPL;
934 }
935
936 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
937 {
938     UriBuilder *This = URIBUILDER_THIS(iface);
939     FIXME("(%p)->(%p)\n", This, pIUri);
940     return E_NOTIMPL;
941 }
942
943 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
944 {
945     UriBuilder *This = URIBUILDER_THIS(iface);
946     FIXME("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
947     return E_NOTIMPL;
948 }
949
950 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
951 {
952     UriBuilder *This = URIBUILDER_THIS(iface);
953     FIXME("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
954     return E_NOTIMPL;
955 }
956
957 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
958 {
959     UriBuilder *This = URIBUILDER_THIS(iface);
960     FIXME("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
961     return E_NOTIMPL;
962 }
963
964 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
965 {
966     UriBuilder *This = URIBUILDER_THIS(iface);
967     FIXME("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
968     return E_NOTIMPL;
969 }
970
971 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
972 {
973     UriBuilder *This = URIBUILDER_THIS(iface);
974     FIXME("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
975     return E_NOTIMPL;
976 }
977
978 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
979 {
980     UriBuilder *This = URIBUILDER_THIS(iface);
981     FIXME("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
982     return E_NOTIMPL;
983 }
984
985 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
986 {
987     UriBuilder *This = URIBUILDER_THIS(iface);
988     FIXME("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
989     return E_NOTIMPL;
990 }
991
992 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
993 {
994     UriBuilder *This = URIBUILDER_THIS(iface);
995     FIXME("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
996     return E_NOTIMPL;
997 }
998
999 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
1000 {
1001     UriBuilder *This = URIBUILDER_THIS(iface);
1002     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1003     return E_NOTIMPL;
1004 }
1005
1006 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
1007 {
1008     UriBuilder *This = URIBUILDER_THIS(iface);
1009     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1010     return E_NOTIMPL;
1011 }
1012
1013 static HRESULT WINAPI UriBuilder_SetPassword(IUriBuilder *iface, LPCWSTR pwzNewValue)
1014 {
1015     UriBuilder *This = URIBUILDER_THIS(iface);
1016     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1017     return E_NOTIMPL;
1018 }
1019
1020 static HRESULT WINAPI UriBuilder_SetPath(IUriBuilder *iface, LPCWSTR pwzNewValue)
1021 {
1022     UriBuilder *This = URIBUILDER_THIS(iface);
1023     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1024     return E_NOTIMPL;
1025 }
1026
1027 static HRESULT WINAPI UriBuilder_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
1028 {
1029     UriBuilder *This = URIBUILDER_THIS(iface);
1030     FIXME("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
1031     return E_NOTIMPL;
1032 }
1033
1034 static HRESULT WINAPI UriBuilder_SetQuery(IUriBuilder *iface, LPCWSTR pwzNewValue)
1035 {
1036     UriBuilder *This = URIBUILDER_THIS(iface);
1037     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1038     return E_NOTIMPL;
1039 }
1040
1041 static HRESULT WINAPI UriBuilder_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
1042 {
1043     UriBuilder *This = URIBUILDER_THIS(iface);
1044     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1045     return E_NOTIMPL;
1046 }
1047
1048 static HRESULT WINAPI UriBuilder_SetUserName(IUriBuilder *iface, LPCWSTR pwzNewValue)
1049 {
1050     UriBuilder *This = URIBUILDER_THIS(iface);
1051     FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1052     return E_NOTIMPL;
1053 }
1054
1055 static HRESULT WINAPI UriBuilder_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
1056 {
1057     UriBuilder *This = URIBUILDER_THIS(iface);
1058     FIXME("(%p)->(0x%08x)\n", This, dwPropertyMask);
1059     return E_NOTIMPL;
1060 }
1061
1062 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
1063 {
1064     UriBuilder *This = URIBUILDER_THIS(iface);
1065     FIXME("(%p)->(%p)\n", This, pfModified);
1066     return E_NOTIMPL;
1067 }
1068
1069 #undef URIBUILDER_THIS
1070
1071 static const IUriBuilderVtbl UriBuilderVtbl = {
1072     UriBuilder_QueryInterface,
1073     UriBuilder_AddRef,
1074     UriBuilder_Release,
1075     UriBuilder_CreateUriSimple,
1076     UriBuilder_CreateUri,
1077     UriBuilder_CreateUriWithFlags,
1078     UriBuilder_GetIUri,
1079     UriBuilder_SetIUri,
1080     UriBuilder_GetFragment,
1081     UriBuilder_GetHost,
1082     UriBuilder_GetPassword,
1083     UriBuilder_GetPath,
1084     UriBuilder_GetPort,
1085     UriBuilder_GetQuery,
1086     UriBuilder_GetSchemeName,
1087     UriBuilder_GetUserName,
1088     UriBuilder_SetFragment,
1089     UriBuilder_SetHost,
1090     UriBuilder_SetPassword,
1091     UriBuilder_SetPath,
1092     UriBuilder_SetPort,
1093     UriBuilder_SetQuery,
1094     UriBuilder_SetSchemeName,
1095     UriBuilder_SetUserName,
1096     UriBuilder_RemoveProperties,
1097     UriBuilder_HasBeenModified,
1098 };
1099
1100 /***********************************************************************
1101  *           CreateIUriBuilder (urlmon.@)
1102  */
1103 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
1104 {
1105     UriBuilder *ret;
1106
1107     TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
1108
1109     ret = heap_alloc(sizeof(UriBuilder));
1110     if(!ret)
1111         return E_OUTOFMEMORY;
1112
1113     ret->lpIUriBuilderVtbl = &UriBuilderVtbl;
1114     ret->ref = 1;
1115
1116     *ppIUriBuilder = URIBUILDER(ret);
1117     return S_OK;
1118 }