shell32/tests: Do not leak shell folder references.
[wine.git] / dlls / urlmon / uri.c
blob48b485b4bd07022f0ee466dbcc57c9812c5e4119
1 /*
2 * Copyright 2010 Jacek Caban for CodeWeavers
3 * Copyright 2010 Thomas Mullaly
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.
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.
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
20 #include <limits.h>
22 #include "urlmon_main.h"
23 #include "wine/debug.h"
25 #define NO_SHLWAPI_REG
26 #include "shlwapi.h"
28 #include "strsafe.h"
30 #define URI_DISPLAY_NO_ABSOLUTE_URI 0x1
31 #define URI_DISPLAY_NO_DEFAULT_PORT_AUTH 0x2
33 #define ALLOW_NULL_TERM_SCHEME 0x01
34 #define ALLOW_NULL_TERM_USER_NAME 0x02
35 #define ALLOW_NULL_TERM_PASSWORD 0x04
36 #define ALLOW_BRACKETLESS_IP_LITERAL 0x08
37 #define SKIP_IP_FUTURE_CHECK 0x10
38 #define IGNORE_PORT_DELIMITER 0x20
40 #define RAW_URI_FORCE_PORT_DISP 0x1
41 #define RAW_URI_CONVERT_TO_DOS_PATH 0x2
43 #define COMBINE_URI_FORCE_FLAG_USE 0x1
45 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
47 static const IID IID_IUriObj = {0x4b364760,0x9f51,0x11df,{0x98,0x1c,0x08,0x00,0x20,0x0c,0x9a,0x66}};
49 typedef struct {
50 IUri IUri_iface;
51 IUriBuilderFactory IUriBuilderFactory_iface;
52 IPersistStream IPersistStream_iface;
53 IMarshal IMarshal_iface;
55 LONG ref;
57 BSTR raw_uri;
59 /* Information about the canonicalized URI's buffer. */
60 WCHAR *canon_uri;
61 DWORD canon_size;
62 DWORD canon_len;
63 BOOL display_modifiers;
64 DWORD create_flags;
66 INT scheme_start;
67 DWORD scheme_len;
68 URL_SCHEME scheme_type;
70 INT userinfo_start;
71 DWORD userinfo_len;
72 INT userinfo_split;
74 INT host_start;
75 DWORD host_len;
76 Uri_HOST_TYPE host_type;
78 INT port_offset;
79 DWORD port;
80 BOOL has_port;
82 INT authority_start;
83 DWORD authority_len;
85 INT domain_offset;
87 INT path_start;
88 DWORD path_len;
89 INT extension_offset;
91 INT query_start;
92 DWORD query_len;
94 INT fragment_start;
95 DWORD fragment_len;
96 } Uri;
98 typedef struct {
99 IUriBuilder IUriBuilder_iface;
100 LONG ref;
102 Uri *uri;
103 DWORD modified_props;
105 WCHAR *fragment;
106 DWORD fragment_len;
108 WCHAR *host;
109 DWORD host_len;
111 WCHAR *password;
112 DWORD password_len;
114 WCHAR *path;
115 DWORD path_len;
117 BOOL has_port;
118 DWORD port;
120 WCHAR *query;
121 DWORD query_len;
123 WCHAR *scheme;
124 DWORD scheme_len;
126 WCHAR *username;
127 DWORD username_len;
128 } UriBuilder;
130 typedef struct {
131 const WCHAR *str;
132 DWORD len;
133 } h16;
135 typedef struct {
136 /* IPv6 addresses can hold up to 8 h16 components. */
137 h16 components[8];
138 DWORD h16_count;
140 /* An IPv6 can have 1 elision ("::"). */
141 const WCHAR *elision;
143 /* An IPv6 can contain 1 IPv4 address as the last 32bits of the address. */
144 const WCHAR *ipv4;
145 DWORD ipv4_len;
147 INT components_size;
148 INT elision_size;
149 } ipv6_address;
151 typedef struct {
152 BSTR uri;
154 BOOL is_relative;
155 BOOL is_opaque;
156 BOOL has_implicit_scheme;
157 BOOL has_implicit_ip;
158 UINT implicit_ipv4;
159 BOOL must_have_path;
161 const WCHAR *scheme;
162 DWORD scheme_len;
163 URL_SCHEME scheme_type;
165 const WCHAR *username;
166 DWORD username_len;
168 const WCHAR *password;
169 DWORD password_len;
171 const WCHAR *host;
172 DWORD host_len;
173 Uri_HOST_TYPE host_type;
175 BOOL has_ipv6;
176 ipv6_address ipv6_address;
178 BOOL has_port;
179 const WCHAR *port;
180 DWORD port_len;
181 DWORD port_value;
183 const WCHAR *path;
184 DWORD path_len;
186 const WCHAR *query;
187 DWORD query_len;
189 const WCHAR *fragment;
190 DWORD fragment_len;
191 } parse_data;
193 static const CHAR hexDigits[] = "0123456789ABCDEF";
195 /* List of scheme types/scheme names that are recognized by the IUri interface as of IE 7. */
196 static const struct {
197 URL_SCHEME scheme;
198 WCHAR scheme_name[16];
199 } recognized_schemes[] = {
200 {URL_SCHEME_FTP, {'f','t','p',0}},
201 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
202 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
203 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
204 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
205 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
206 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
207 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
208 {URL_SCHEME_FILE, {'f','i','l','e',0}},
209 {URL_SCHEME_MK, {'m','k',0}},
210 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
211 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
212 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
213 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
214 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
215 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
216 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
217 {URL_SCHEME_RES, {'r','e','s',0}},
218 {URL_SCHEME_MSSHELLROOTED, {'m','s','-','s','h','e','l','l','-','r','o','o','t','e','d',0}},
219 {URL_SCHEME_MSSHELLIDLIST, {'m','s','-','s','h','e','l','l','-','i','d','l','i','s','t',0}},
220 {URL_SCHEME_MSHELP, {'h','c','p',0}},
221 {URL_SCHEME_WILDCARD, {'*',0}}
224 /* List of default ports Windows recognizes. */
225 static const struct {
226 URL_SCHEME scheme;
227 USHORT port;
228 } default_ports[] = {
229 {URL_SCHEME_FTP, 21},
230 {URL_SCHEME_HTTP, 80},
231 {URL_SCHEME_GOPHER, 70},
232 {URL_SCHEME_NNTP, 119},
233 {URL_SCHEME_TELNET, 23},
234 {URL_SCHEME_WAIS, 210},
235 {URL_SCHEME_HTTPS, 443},
238 /* List of 3-character top level domain names Windows seems to recognize.
239 * There might be more, but, these are the only ones I've found so far.
241 static const struct {
242 WCHAR tld_name[4];
243 } recognized_tlds[] = {
244 {{'c','o','m',0}},
245 {{'e','d','u',0}},
246 {{'g','o','v',0}},
247 {{'i','n','t',0}},
248 {{'m','i','l',0}},
249 {{'n','e','t',0}},
250 {{'o','r','g',0}}
253 static Uri *get_uri_obj(IUri *uri)
255 Uri *ret;
256 HRESULT hres;
258 hres = IUri_QueryInterface(uri, &IID_IUriObj, (void**)&ret);
259 return SUCCEEDED(hres) ? ret : NULL;
262 static inline BOOL is_alpha(WCHAR val) {
263 return ((val >= 'a' && val <= 'z') || (val >= 'A' && val <= 'Z'));
266 static inline BOOL is_num(WCHAR val) {
267 return (val >= '0' && val <= '9');
270 static inline BOOL is_drive_path(const WCHAR *str) {
271 return (is_alpha(str[0]) && (str[1] == ':' || str[1] == '|'));
274 static inline BOOL is_unc_path(const WCHAR *str) {
275 return (str[0] == '\\' && str[1] == '\\');
278 static inline BOOL is_forbidden_dos_path_char(WCHAR val) {
279 return (val == '>' || val == '<' || val == '\"');
282 /* A URI is implicitly a file path if it begins with
283 * a drive letter (e.g. X:) or starts with "\\" (UNC path).
285 static inline BOOL is_implicit_file_path(const WCHAR *str) {
286 return (is_unc_path(str) || (is_alpha(str[0]) && str[1] == ':'));
289 /* Checks if the URI is a hierarchical URI. A hierarchical
290 * URI is one that has "//" after the scheme.
292 static BOOL check_hierarchical(const WCHAR **ptr) {
293 const WCHAR *start = *ptr;
295 if(**ptr != '/')
296 return FALSE;
298 ++(*ptr);
299 if(**ptr != '/') {
300 *ptr = start;
301 return FALSE;
304 ++(*ptr);
305 return TRUE;
308 /* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" */
309 static inline BOOL is_unreserved(WCHAR val) {
310 return (is_alpha(val) || is_num(val) || val == '-' || val == '.' ||
311 val == '_' || val == '~');
314 /* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
315 * / "*" / "+" / "," / ";" / "="
317 static inline BOOL is_subdelim(WCHAR val) {
318 return (val == '!' || val == '$' || val == '&' ||
319 val == '\'' || val == '(' || val == ')' ||
320 val == '*' || val == '+' || val == ',' ||
321 val == ';' || val == '=');
324 /* gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" */
325 static inline BOOL is_gendelim(WCHAR val) {
326 return (val == ':' || val == '/' || val == '?' ||
327 val == '#' || val == '[' || val == ']' ||
328 val == '@');
331 /* Characters that delimit the end of the authority
332 * section of a URI. Sometimes a '\\' is considered
333 * an authority delimiter.
335 static inline BOOL is_auth_delim(WCHAR val, BOOL acceptSlash) {
336 return (val == '#' || val == '/' || val == '?' ||
337 val == '\0' || (acceptSlash && val == '\\'));
340 /* reserved = gen-delims / sub-delims */
341 static inline BOOL is_reserved(WCHAR val) {
342 return (is_subdelim(val) || is_gendelim(val));
345 static inline BOOL is_hexdigit(WCHAR val) {
346 return ((val >= 'a' && val <= 'f') ||
347 (val >= 'A' && val <= 'F') ||
348 (val >= '0' && val <= '9'));
351 static inline BOOL is_path_delim(URL_SCHEME scheme, WCHAR val) {
352 return (!val || (val == '#' && scheme != URL_SCHEME_FILE) || val == '?');
355 static inline BOOL is_slash(WCHAR c)
357 return c == '/' || c == '\\';
360 static BOOL is_default_port(URL_SCHEME scheme, DWORD port) {
361 DWORD i;
363 for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
364 if(default_ports[i].scheme == scheme && default_ports[i].port)
365 return TRUE;
368 return FALSE;
371 /* List of schemes types Windows seems to expect to be hierarchical. */
372 static inline BOOL is_hierarchical_scheme(URL_SCHEME type) {
373 return(type == URL_SCHEME_HTTP || type == URL_SCHEME_FTP ||
374 type == URL_SCHEME_GOPHER || type == URL_SCHEME_NNTP ||
375 type == URL_SCHEME_TELNET || type == URL_SCHEME_WAIS ||
376 type == URL_SCHEME_FILE || type == URL_SCHEME_HTTPS ||
377 type == URL_SCHEME_RES);
380 /* Checks if 'flags' contains an invalid combination of Uri_CREATE flags. */
381 static inline BOOL has_invalid_flag_combination(DWORD flags) {
382 return((flags & Uri_CREATE_DECODE_EXTRA_INFO && flags & Uri_CREATE_NO_DECODE_EXTRA_INFO) ||
383 (flags & Uri_CREATE_CANONICALIZE && flags & Uri_CREATE_NO_CANONICALIZE) ||
384 (flags & Uri_CREATE_CRACK_UNKNOWN_SCHEMES && flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES) ||
385 (flags & Uri_CREATE_PRE_PROCESS_HTML_URI && flags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI) ||
386 (flags & Uri_CREATE_IE_SETTINGS && flags & Uri_CREATE_NO_IE_SETTINGS));
389 /* Applies each default Uri_CREATE flags to 'flags' if it
390 * doesn't cause a flag conflict.
392 static void apply_default_flags(DWORD *flags) {
393 if(!(*flags & Uri_CREATE_NO_CANONICALIZE))
394 *flags |= Uri_CREATE_CANONICALIZE;
395 if(!(*flags & Uri_CREATE_NO_DECODE_EXTRA_INFO))
396 *flags |= Uri_CREATE_DECODE_EXTRA_INFO;
397 if(!(*flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES))
398 *flags |= Uri_CREATE_CRACK_UNKNOWN_SCHEMES;
399 if(!(*flags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI))
400 *flags |= Uri_CREATE_PRE_PROCESS_HTML_URI;
401 if(!(*flags & Uri_CREATE_IE_SETTINGS))
402 *flags |= Uri_CREATE_NO_IE_SETTINGS;
405 /* Determines if the URI is hierarchical using the information already parsed into
406 * data and using the current location of parsing in the URI string.
408 * Windows considers a URI hierarchical if one of the following is true:
409 * A.) It's a wildcard scheme.
410 * B.) It's an implicit file scheme.
411 * C.) It's a known hierarchical scheme and it has two '\\' after the scheme name.
412 * (the '\\' will be converted into "//" during canonicalization).
413 * D.) "//" appears after the scheme name (or at the beginning if no scheme is given).
415 static inline BOOL is_hierarchical_uri(const WCHAR **ptr, const parse_data *data) {
416 const WCHAR *start = *ptr;
418 if(data->scheme_type == URL_SCHEME_WILDCARD)
419 return TRUE;
420 else if(data->scheme_type == URL_SCHEME_FILE && data->has_implicit_scheme)
421 return TRUE;
422 else if(is_hierarchical_scheme(data->scheme_type) && (*ptr)[0] == '\\' && (*ptr)[1] == '\\') {
423 *ptr += 2;
424 return TRUE;
425 } else if(data->scheme_type != URL_SCHEME_MAILTO && check_hierarchical(ptr))
426 return TRUE;
428 *ptr = start;
429 return FALSE;
432 /* Computes the size of the given IPv6 address.
433 * Each h16 component is 16 bits. If there is an IPv4 address, it's
434 * 32 bits. If there's an elision it can be 16 to 128 bits, depending
435 * on the number of other components.
437 * Modeled after google-url's CheckIPv6ComponentsSize function
439 static void compute_ipv6_comps_size(ipv6_address *address) {
440 address->components_size = address->h16_count * 2;
442 if(address->ipv4)
443 /* IPv4 address is 4 bytes. */
444 address->components_size += 4;
446 if(address->elision) {
447 /* An elision can be anywhere from 2 bytes up to 16 bytes.
448 * Its size depends on the size of the h16 and IPv4 components.
450 address->elision_size = 16 - address->components_size;
451 if(address->elision_size < 2)
452 address->elision_size = 2;
453 } else
454 address->elision_size = 0;
457 /* Taken from dlls/jscript/lex.c */
458 static int hex_to_int(WCHAR val) {
459 if(val >= '0' && val <= '9')
460 return val - '0';
461 else if(val >= 'a' && val <= 'f')
462 return val - 'a' + 10;
463 else if(val >= 'A' && val <= 'F')
464 return val - 'A' + 10;
466 return -1;
469 /* Helper function for converting a percent encoded string
470 * representation of a WCHAR value into its actual WCHAR value. If
471 * the two characters following the '%' aren't valid hex values then
472 * this function returns the NULL character.
474 * E.g.
475 * "%2E" will result in '.' being returned by this function.
477 static WCHAR decode_pct_val(const WCHAR *ptr) {
478 WCHAR ret = '\0';
480 if(*ptr == '%' && is_hexdigit(*(ptr + 1)) && is_hexdigit(*(ptr + 2))) {
481 INT a = hex_to_int(*(ptr + 1));
482 INT b = hex_to_int(*(ptr + 2));
484 ret = a << 4;
485 ret += b;
488 return ret;
491 /* Helper function for percent encoding a given character
492 * and storing the encoded value into a given buffer (dest).
494 * It's up to the calling function to ensure that there is
495 * at least enough space in 'dest' for the percent encoded
496 * value to be stored (so dest + 3 spaces available).
498 static inline void pct_encode_val(WCHAR val, WCHAR *dest) {
499 dest[0] = '%';
500 dest[1] = hexDigits[(val >> 4) & 0xf];
501 dest[2] = hexDigits[val & 0xf];
504 /* Attempts to parse the domain name from the host.
506 * This function also includes the Top-level Domain (TLD) name
507 * of the host when it tries to find the domain name. If it finds
508 * a valid domain name it will assign 'domain_start' the offset
509 * into 'host' where the domain name starts.
511 * It's implied that if there is a domain name its range is:
512 * [host+domain_start, host+host_len).
514 void find_domain_name(const WCHAR *host, DWORD host_len,
515 INT *domain_start) {
516 const WCHAR *last_tld, *sec_last_tld, *end;
518 end = host+host_len-1;
520 *domain_start = -1;
522 /* There has to be at least enough room for a '.' followed by a
523 * 3-character TLD for a domain to even exist in the host name.
525 if(host_len < 4)
526 return;
528 last_tld = memrchrW(host, '.', host_len);
529 if(!last_tld)
530 /* http://hostname -> has no domain name. */
531 return;
533 sec_last_tld = memrchrW(host, '.', last_tld-host);
534 if(!sec_last_tld) {
535 /* If the '.' is at the beginning of the host there
536 * has to be at least 3 characters in the TLD for it
537 * to be valid.
538 * Ex: .com -> .com as the domain name.
539 * .co -> has no domain name.
541 if(last_tld-host == 0) {
542 if(end-(last_tld-1) < 3)
543 return;
544 } else if(last_tld-host == 3) {
545 DWORD i;
547 /* If there are three characters in front of last_tld and
548 * they are on the list of recognized TLDs, then this
549 * host doesn't have a domain (since the host only contains
550 * a TLD name.
551 * Ex: edu.uk -> has no domain name.
552 * foo.uk -> foo.uk as the domain name.
554 for(i = 0; i < sizeof(recognized_tlds)/sizeof(recognized_tlds[0]); ++i) {
555 if(!StrCmpNIW(host, recognized_tlds[i].tld_name, 3))
556 return;
558 } else if(last_tld-host < 3)
559 /* Anything less than 3 characters is considered part
560 * of the TLD name.
561 * Ex: ak.uk -> Has no domain name.
563 return;
565 /* Otherwise the domain name is the whole host name. */
566 *domain_start = 0;
567 } else if(end+1-last_tld > 3) {
568 /* If the last_tld has more than 3 characters, then it's automatically
569 * considered the TLD of the domain name.
570 * Ex: www.winehq.org.uk.test -> uk.test as the domain name.
572 *domain_start = (sec_last_tld+1)-host;
573 } else if(last_tld - (sec_last_tld+1) < 4) {
574 DWORD i;
575 /* If the sec_last_tld is 3 characters long it HAS to be on the list of
576 * recognized to still be considered part of the TLD name, otherwise
577 * it's considered the domain name.
578 * Ex: www.google.com.uk -> google.com.uk as the domain name.
579 * www.google.foo.uk -> foo.uk as the domain name.
581 if(last_tld - (sec_last_tld+1) == 3) {
582 for(i = 0; i < sizeof(recognized_tlds)/sizeof(recognized_tlds[0]); ++i) {
583 if(!StrCmpNIW(sec_last_tld+1, recognized_tlds[i].tld_name, 3)) {
584 const WCHAR *domain = memrchrW(host, '.', sec_last_tld-host);
586 if(!domain)
587 *domain_start = 0;
588 else
589 *domain_start = (domain+1) - host;
590 TRACE("Found domain name %s\n", debugstr_wn(host+*domain_start,
591 (host+host_len)-(host+*domain_start)));
592 return;
596 *domain_start = (sec_last_tld+1)-host;
597 } else {
598 /* Since the sec_last_tld is less than 3 characters it's considered
599 * part of the TLD.
600 * Ex: www.google.fo.uk -> google.fo.uk as the domain name.
602 const WCHAR *domain = memrchrW(host, '.', sec_last_tld-host);
604 if(!domain)
605 *domain_start = 0;
606 else
607 *domain_start = (domain+1) - host;
609 } else {
610 /* The second to last TLD has more than 3 characters making it
611 * the domain name.
612 * Ex: www.google.test.us -> test.us as the domain name.
614 *domain_start = (sec_last_tld+1)-host;
617 TRACE("Found domain name %s\n", debugstr_wn(host+*domain_start,
618 (host+host_len)-(host+*domain_start)));
621 /* Removes the dot segments from a hierarchical URIs path component. This
622 * function performs the removal in place.
624 * This function returns the new length of the path string.
626 static DWORD remove_dot_segments(WCHAR *path, DWORD path_len) {
627 WCHAR *out = path;
628 const WCHAR *in = out;
629 const WCHAR *end = out + path_len;
630 DWORD len;
632 while(in < end) {
633 /* Move the first path segment in the input buffer to the end of
634 * the output buffer, and any subsequent characters up to, including
635 * the next "/" character (if any) or the end of the input buffer.
637 while(in < end && !is_slash(*in))
638 *out++ = *in++;
639 if(in == end)
640 break;
641 *out++ = *in++;
643 while(in < end) {
644 if(*in != '.')
645 break;
647 /* Handle ending "/." */
648 if(in + 1 == end) {
649 ++in;
650 break;
653 /* Handle "/./" */
654 if(is_slash(in[1])) {
655 in += 2;
656 continue;
659 /* If we don't have "/../" or ending "/.." */
660 if(in[1] != '.' || (in + 2 != end && !is_slash(in[2])))
661 break;
663 /* Find the slash preceding out pointer and move out pointer to it */
664 if(out > path+1 && is_slash(*--out))
665 --out;
666 while(out > path && !is_slash(*(--out)));
667 if(is_slash(*out))
668 ++out;
669 in += 2;
670 if(in != end)
671 ++in;
675 len = out - path;
676 TRACE("(%p %d): Path after dot segments removed %s len=%d\n", path, path_len,
677 debugstr_wn(path, len), len);
678 return len;
681 /* Attempts to find the file extension in a given path. */
682 static INT find_file_extension(const WCHAR *path, DWORD path_len) {
683 const WCHAR *end;
685 for(end = path+path_len-1; end >= path && *end != '/' && *end != '\\'; --end) {
686 if(*end == '.')
687 return end-path;
690 return -1;
693 /* Computes the location where the elision should occur in the IPv6
694 * address using the numerical values of each component stored in
695 * 'values'. If the address shouldn't contain an elision then 'index'
696 * is assigned -1 as its value. Otherwise 'index' will contain the
697 * starting index (into values) where the elision should be, and 'count'
698 * will contain the number of cells the elision covers.
700 * NOTES:
701 * Windows will expand an elision if the elision only represents one h16
702 * component of the address.
704 * Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
706 * If the IPv6 address contains an IPv4 address, the IPv4 address is also
707 * considered for being included as part of an elision if all its components
708 * are zeros.
710 * Ex: [1:2:3:4:5:6:0.0.0.0] -> [1:2:3:4:5:6::]
712 static void compute_elision_location(const ipv6_address *address, const USHORT values[8],
713 INT *index, DWORD *count) {
714 DWORD i, max_len, cur_len;
715 INT max_index, cur_index;
717 max_len = cur_len = 0;
718 max_index = cur_index = -1;
719 for(i = 0; i < 8; ++i) {
720 BOOL check_ipv4 = (address->ipv4 && i == 6);
721 BOOL is_end = (check_ipv4 || i == 7);
723 if(check_ipv4) {
724 /* Check if the IPv4 address contains only zeros. */
725 if(values[i] == 0 && values[i+1] == 0) {
726 if(cur_index == -1)
727 cur_index = i;
729 cur_len += 2;
730 ++i;
732 } else if(values[i] == 0) {
733 if(cur_index == -1)
734 cur_index = i;
736 ++cur_len;
739 if(is_end || values[i] != 0) {
740 /* We only consider it for an elision if it's
741 * more than 1 component long.
743 if(cur_len > 1 && cur_len > max_len) {
744 /* Found the new elision location. */
745 max_len = cur_len;
746 max_index = cur_index;
749 /* Reset the current range for the next range of zeros. */
750 cur_index = -1;
751 cur_len = 0;
755 *index = max_index;
756 *count = max_len;
759 /* Removes all the leading and trailing white spaces or
760 * control characters from the URI and removes all control
761 * characters inside of the URI string.
763 static BSTR pre_process_uri(LPCWSTR uri) {
764 const WCHAR *start, *end, *ptr;
765 WCHAR *ptr2;
766 DWORD len;
767 BSTR ret;
769 start = uri;
770 /* Skip leading controls and whitespace. */
771 while(*start && (iscntrlW(*start) || isspaceW(*start))) ++start;
773 /* URI consisted only of control/whitespace. */
774 if(!*start)
775 return SysAllocStringLen(NULL, 0);
777 end = start + strlenW(start);
778 while(--end > start && (iscntrlW(*end) || isspaceW(*end)));
780 len = ++end - start;
781 for(ptr = start; ptr < end; ptr++) {
782 if(iscntrlW(*ptr))
783 len--;
786 ret = SysAllocStringLen(NULL, len);
787 if(!ret)
788 return NULL;
790 for(ptr = start, ptr2=ret; ptr < end; ptr++) {
791 if(!iscntrlW(*ptr))
792 *ptr2++ = *ptr;
795 return ret;
798 /* Converts the specified IPv4 address into an uint value.
800 * This function assumes that the IPv4 address has already been validated.
802 static UINT ipv4toui(const WCHAR *ip, DWORD len) {
803 UINT ret = 0;
804 DWORD comp_value = 0;
805 const WCHAR *ptr;
807 for(ptr = ip; ptr < ip+len; ++ptr) {
808 if(*ptr == '.') {
809 ret <<= 8;
810 ret += comp_value;
811 comp_value = 0;
812 } else
813 comp_value = comp_value*10 + (*ptr-'0');
816 ret <<= 8;
817 ret += comp_value;
819 return ret;
822 /* Converts an IPv4 address in numerical form into its fully qualified
823 * string form. This function returns the number of characters written
824 * to 'dest'. If 'dest' is NULL this function will return the number of
825 * characters that would have been written.
827 * It's up to the caller to ensure there's enough space in 'dest' for the
828 * address.
830 static DWORD ui2ipv4(WCHAR *dest, UINT address) {
831 static const WCHAR formatW[] =
832 {'%','u','.','%','u','.','%','u','.','%','u',0};
833 DWORD ret = 0;
834 UCHAR digits[4];
836 digits[0] = (address >> 24) & 0xff;
837 digits[1] = (address >> 16) & 0xff;
838 digits[2] = (address >> 8) & 0xff;
839 digits[3] = address & 0xff;
841 if(!dest) {
842 WCHAR tmp[16];
843 ret = sprintfW(tmp, formatW, digits[0], digits[1], digits[2], digits[3]);
844 } else
845 ret = sprintfW(dest, formatW, digits[0], digits[1], digits[2], digits[3]);
847 return ret;
850 static DWORD ui2str(WCHAR *dest, UINT value) {
851 static const WCHAR formatW[] = {'%','u',0};
852 DWORD ret = 0;
854 if(!dest) {
855 WCHAR tmp[11];
856 ret = sprintfW(tmp, formatW, value);
857 } else
858 ret = sprintfW(dest, formatW, value);
860 return ret;
863 /* Converts a h16 component (from an IPv6 address) into its
864 * numerical value.
866 * This function assumes that the h16 component has already been validated.
868 static USHORT h16tous(h16 component) {
869 DWORD i;
870 USHORT ret = 0;
872 for(i = 0; i < component.len; ++i) {
873 ret <<= 4;
874 ret += hex_to_int(component.str[i]);
877 return ret;
880 /* Converts an IPv6 address into its 128 bits (16 bytes) numerical value.
882 * This function assumes that the ipv6_address has already been validated.
884 static BOOL ipv6_to_number(const ipv6_address *address, USHORT number[8]) {
885 DWORD i, cur_component = 0;
886 BOOL already_passed_elision = FALSE;
888 for(i = 0; i < address->h16_count; ++i) {
889 if(address->elision) {
890 if(address->components[i].str > address->elision && !already_passed_elision) {
891 /* Means we just passed the elision and need to add its values to
892 * 'number' before we do anything else.
894 INT j;
895 for(j = 0; j < address->elision_size; j+=2)
896 number[cur_component++] = 0;
898 already_passed_elision = TRUE;
902 number[cur_component++] = h16tous(address->components[i]);
905 /* Case when the elision appears after the h16 components. */
906 if(!already_passed_elision && address->elision) {
907 INT j;
908 for(j = 0; j < address->elision_size; j+=2)
909 number[cur_component++] = 0;
912 if(address->ipv4) {
913 UINT value = ipv4toui(address->ipv4, address->ipv4_len);
915 if(cur_component != 6) {
916 ERR("(%p %p): Failed sanity check with %d\n", address, number, cur_component);
917 return FALSE;
920 number[cur_component++] = (value >> 16) & 0xffff;
921 number[cur_component] = value & 0xffff;
924 return TRUE;
927 /* Checks if the characters pointed to by 'ptr' are
928 * a percent encoded data octet.
930 * pct-encoded = "%" HEXDIG HEXDIG
932 static BOOL check_pct_encoded(const WCHAR **ptr) {
933 const WCHAR *start = *ptr;
935 if(**ptr != '%')
936 return FALSE;
938 ++(*ptr);
939 if(!is_hexdigit(**ptr)) {
940 *ptr = start;
941 return FALSE;
944 ++(*ptr);
945 if(!is_hexdigit(**ptr)) {
946 *ptr = start;
947 return FALSE;
950 ++(*ptr);
951 return TRUE;
954 /* dec-octet = DIGIT ; 0-9
955 * / %x31-39 DIGIT ; 10-99
956 * / "1" 2DIGIT ; 100-199
957 * / "2" %x30-34 DIGIT ; 200-249
958 * / "25" %x30-35 ; 250-255
960 static BOOL check_dec_octet(const WCHAR **ptr) {
961 const WCHAR *c1, *c2, *c3;
963 c1 = *ptr;
964 /* A dec-octet must be at least 1 digit long. */
965 if(*c1 < '0' || *c1 > '9')
966 return FALSE;
968 ++(*ptr);
970 c2 = *ptr;
971 /* Since the 1-digit requirement was met, it doesn't
972 * matter if this is a DIGIT value, it's considered a
973 * dec-octet.
975 if(*c2 < '0' || *c2 > '9')
976 return TRUE;
978 ++(*ptr);
980 c3 = *ptr;
981 /* Same explanation as above. */
982 if(*c3 < '0' || *c3 > '9')
983 return TRUE;
985 /* Anything > 255 isn't a valid IP dec-octet. */
986 if(*c1 >= '2' && *c2 >= '5' && *c3 >= '5') {
987 *ptr = c1;
988 return FALSE;
991 ++(*ptr);
992 return TRUE;
995 /* Checks if there is an implicit IPv4 address in the host component of the URI.
996 * The max value of an implicit IPv4 address is UINT_MAX.
998 * Ex:
999 * "234567" would be considered an implicit IPv4 address.
1001 static BOOL check_implicit_ipv4(const WCHAR **ptr, UINT *val) {
1002 const WCHAR *start = *ptr;
1003 ULONGLONG ret = 0;
1004 *val = 0;
1006 while(is_num(**ptr)) {
1007 ret = ret*10 + (**ptr - '0');
1009 if(ret > UINT_MAX) {
1010 *ptr = start;
1011 return FALSE;
1013 ++(*ptr);
1016 if(*ptr == start)
1017 return FALSE;
1019 *val = ret;
1020 return TRUE;
1023 /* Checks if the string contains an IPv4 address.
1025 * This function has a strict mode or a non-strict mode of operation
1026 * When 'strict' is set to FALSE this function will return TRUE if
1027 * the string contains at least 'dec-octet "." dec-octet' since partial
1028 * IPv4 addresses will be normalized out into full IPv4 addresses. When
1029 * 'strict' is set this function expects there to be a full IPv4 address.
1031 * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
1033 static BOOL check_ipv4address(const WCHAR **ptr, BOOL strict) {
1034 const WCHAR *start = *ptr;
1036 if(!check_dec_octet(ptr)) {
1037 *ptr = start;
1038 return FALSE;
1041 if(**ptr != '.') {
1042 *ptr = start;
1043 return FALSE;
1046 ++(*ptr);
1047 if(!check_dec_octet(ptr)) {
1048 *ptr = start;
1049 return FALSE;
1052 if(**ptr != '.') {
1053 if(strict) {
1054 *ptr = start;
1055 return FALSE;
1056 } else
1057 return TRUE;
1060 ++(*ptr);
1061 if(!check_dec_octet(ptr)) {
1062 *ptr = start;
1063 return FALSE;
1066 if(**ptr != '.') {
1067 if(strict) {
1068 *ptr = start;
1069 return FALSE;
1070 } else
1071 return TRUE;
1074 ++(*ptr);
1075 if(!check_dec_octet(ptr)) {
1076 *ptr = start;
1077 return FALSE;
1080 /* Found a four digit ip address. */
1081 return TRUE;
1083 /* Tries to parse the scheme name of the URI.
1085 * scheme = ALPHA *(ALPHA | NUM | '+' | '-' | '.') as defined by RFC 3896.
1086 * NOTE: Windows accepts a number as the first character of a scheme.
1088 static BOOL parse_scheme_name(const WCHAR **ptr, parse_data *data, DWORD extras) {
1089 const WCHAR *start = *ptr;
1091 data->scheme = NULL;
1092 data->scheme_len = 0;
1094 while(**ptr) {
1095 if(**ptr == '*' && *ptr == start) {
1096 /* Might have found a wildcard scheme. If it is the next
1097 * char has to be a ':' for it to be a valid URI
1099 ++(*ptr);
1100 break;
1101 } else if(!is_num(**ptr) && !is_alpha(**ptr) && **ptr != '+' &&
1102 **ptr != '-' && **ptr != '.')
1103 break;
1105 (*ptr)++;
1108 if(*ptr == start)
1109 return FALSE;
1111 /* Schemes must end with a ':' */
1112 if(**ptr != ':' && !((extras & ALLOW_NULL_TERM_SCHEME) && !**ptr)) {
1113 *ptr = start;
1114 return FALSE;
1117 data->scheme = start;
1118 data->scheme_len = *ptr - start;
1120 ++(*ptr);
1121 return TRUE;
1124 /* Tries to deduce the corresponding URL_SCHEME for the given URI. Stores
1125 * the deduced URL_SCHEME in data->scheme_type.
1127 static BOOL parse_scheme_type(parse_data *data) {
1128 /* If there's scheme data then see if it's a recognized scheme. */
1129 if(data->scheme && data->scheme_len) {
1130 DWORD i;
1132 for(i = 0; i < sizeof(recognized_schemes)/sizeof(recognized_schemes[0]); ++i) {
1133 if(lstrlenW(recognized_schemes[i].scheme_name) == data->scheme_len) {
1134 /* Has to be a case insensitive compare. */
1135 if(!StrCmpNIW(recognized_schemes[i].scheme_name, data->scheme, data->scheme_len)) {
1136 data->scheme_type = recognized_schemes[i].scheme;
1137 return TRUE;
1142 /* If we get here it means it's not a recognized scheme. */
1143 data->scheme_type = URL_SCHEME_UNKNOWN;
1144 return TRUE;
1145 } else if(data->is_relative) {
1146 /* Relative URI's have no scheme. */
1147 data->scheme_type = URL_SCHEME_UNKNOWN;
1148 return TRUE;
1149 } else {
1150 /* Should never reach here! what happened... */
1151 FIXME("(%p): Unable to determine scheme type for URI %s\n", data, debugstr_w(data->uri));
1152 return FALSE;
1156 /* Tries to parse (or deduce) the scheme_name of a URI. If it can't
1157 * parse a scheme from the URI it will try to deduce the scheme_name and scheme_type
1158 * using the flags specified in 'flags' (if any). Flags that affect how this function
1159 * operates are the Uri_CREATE_ALLOW_* flags.
1161 * All parsed/deduced information will be stored in 'data' when the function returns.
1163 * Returns TRUE if it was able to successfully parse the information.
1165 static BOOL parse_scheme(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1166 static const WCHAR fileW[] = {'f','i','l','e',0};
1167 static const WCHAR wildcardW[] = {'*',0};
1169 /* First check to see if the uri could implicitly be a file path. */
1170 if(is_implicit_file_path(*ptr)) {
1171 if(flags & Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME) {
1172 data->scheme = fileW;
1173 data->scheme_len = lstrlenW(fileW);
1174 data->has_implicit_scheme = TRUE;
1176 TRACE("(%p %p %x): URI is an implicit file path.\n", ptr, data, flags);
1177 } else {
1178 /* Windows does not consider anything that can implicitly be a file
1179 * path to be a valid URI if the ALLOW_IMPLICIT_FILE_SCHEME flag is not set...
1181 TRACE("(%p %p %x): URI is implicitly a file path, but, the ALLOW_IMPLICIT_FILE_SCHEME flag wasn't set.\n",
1182 ptr, data, flags);
1183 return FALSE;
1185 } else if(!parse_scheme_name(ptr, data, extras)) {
1186 /* No scheme was found, this means it could be:
1187 * a) an implicit Wildcard scheme
1188 * b) a relative URI
1189 * c) an invalid URI.
1191 if(flags & Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME) {
1192 data->scheme = wildcardW;
1193 data->scheme_len = lstrlenW(wildcardW);
1194 data->has_implicit_scheme = TRUE;
1196 TRACE("(%p %p %x): URI is an implicit wildcard scheme.\n", ptr, data, flags);
1197 } else if (flags & Uri_CREATE_ALLOW_RELATIVE) {
1198 data->is_relative = TRUE;
1199 TRACE("(%p %p %x): URI is relative.\n", ptr, data, flags);
1200 } else {
1201 TRACE("(%p %p %x): Malformed URI found. Unable to deduce scheme name.\n", ptr, data, flags);
1202 return FALSE;
1206 if(!data->is_relative)
1207 TRACE("(%p %p %x): Found scheme=%s scheme_len=%d\n", ptr, data, flags,
1208 debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
1210 if(!parse_scheme_type(data))
1211 return FALSE;
1213 TRACE("(%p %p %x): Assigned %d as the URL_SCHEME.\n", ptr, data, flags, data->scheme_type);
1214 return TRUE;
1217 static BOOL parse_username(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1218 data->username = *ptr;
1220 while(**ptr != ':' && **ptr != '@') {
1221 if(**ptr == '%') {
1222 if(!check_pct_encoded(ptr)) {
1223 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1224 *ptr = data->username;
1225 data->username = NULL;
1226 return FALSE;
1228 } else
1229 continue;
1230 } else if(extras & ALLOW_NULL_TERM_USER_NAME && !**ptr)
1231 break;
1232 else if(is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)) {
1233 *ptr = data->username;
1234 data->username = NULL;
1235 return FALSE;
1238 ++(*ptr);
1241 data->username_len = *ptr - data->username;
1242 return TRUE;
1245 static BOOL parse_password(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1246 data->password = *ptr;
1248 while(**ptr != '@') {
1249 if(**ptr == '%') {
1250 if(!check_pct_encoded(ptr)) {
1251 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1252 *ptr = data->password;
1253 data->password = NULL;
1254 return FALSE;
1256 } else
1257 continue;
1258 } else if(extras & ALLOW_NULL_TERM_PASSWORD && !**ptr)
1259 break;
1260 else if(is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)) {
1261 *ptr = data->password;
1262 data->password = NULL;
1263 return FALSE;
1266 ++(*ptr);
1269 data->password_len = *ptr - data->password;
1270 return TRUE;
1273 /* Parses the userinfo part of the URI (if it exists). The userinfo field of
1274 * a URI can consist of "username:password@", or just "username@".
1276 * RFC def:
1277 * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
1279 * NOTES:
1280 * 1) If there is more than one ':' in the userinfo part of the URI Windows
1281 * uses the first occurrence of ':' to delimit the username and password
1282 * components.
1284 * ex:
1285 * ftp://user:pass:word@winehq.org
1287 * would yield "user" as the username and "pass:word" as the password.
1289 * 2) Windows allows any character to appear in the "userinfo" part of
1290 * a URI, as long as it's not an authority delimiter character set.
1292 static void parse_userinfo(const WCHAR **ptr, parse_data *data, DWORD flags) {
1293 const WCHAR *start = *ptr;
1295 if(!parse_username(ptr, data, flags, 0)) {
1296 TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
1297 return;
1300 if(**ptr == ':') {
1301 ++(*ptr);
1302 if(!parse_password(ptr, data, flags, 0)) {
1303 *ptr = start;
1304 data->username = NULL;
1305 data->username_len = 0;
1306 TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
1307 return;
1311 if(**ptr != '@') {
1312 *ptr = start;
1313 data->username = NULL;
1314 data->username_len = 0;
1315 data->password = NULL;
1316 data->password_len = 0;
1318 TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
1319 return;
1322 if(data->username)
1323 TRACE("(%p %p %x): Found username %s len=%d.\n", ptr, data, flags,
1324 debugstr_wn(data->username, data->username_len), data->username_len);
1326 if(data->password)
1327 TRACE("(%p %p %x): Found password %s len=%d.\n", ptr, data, flags,
1328 debugstr_wn(data->password, data->password_len), data->password_len);
1330 ++(*ptr);
1333 /* Attempts to parse a port from the URI.
1335 * NOTES:
1336 * Windows seems to have a cap on what the maximum value
1337 * for a port can be. The max value is USHORT_MAX.
1339 * port = *DIGIT
1341 static BOOL parse_port(const WCHAR **ptr, parse_data *data, DWORD flags) {
1342 UINT port = 0;
1343 data->port = *ptr;
1345 while(!is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)) {
1346 if(!is_num(**ptr)) {
1347 *ptr = data->port;
1348 data->port = NULL;
1349 return FALSE;
1352 port = port*10 + (**ptr-'0');
1354 if(port > USHRT_MAX) {
1355 *ptr = data->port;
1356 data->port = NULL;
1357 return FALSE;
1360 ++(*ptr);
1363 data->has_port = TRUE;
1364 data->port_value = port;
1365 data->port_len = *ptr - data->port;
1367 TRACE("(%p %p %x): Found port %s len=%d value=%u\n", ptr, data, flags,
1368 debugstr_wn(data->port, data->port_len), data->port_len, data->port_value);
1369 return TRUE;
1372 /* Attempts to parse a IPv4 address from the URI.
1374 * NOTES:
1375 * Windows normalizes IPv4 addresses, This means there are three
1376 * possibilities for the URI to contain an IPv4 address.
1377 * 1) A well formed address (ex. 192.2.2.2).
1378 * 2) A partially formed address. For example "192.0" would
1379 * normalize to "192.0.0.0" during canonicalization.
1380 * 3) An implicit IPv4 address. For example "256" would
1381 * normalize to "0.0.1.0" during canonicalization. Also
1382 * note that the maximum value for an implicit IP address
1383 * is UINT_MAX, if the value in the URI exceeds this then
1384 * it is not considered an IPv4 address.
1386 static BOOL parse_ipv4address(const WCHAR **ptr, parse_data *data, DWORD flags) {
1387 const BOOL is_unknown = data->scheme_type == URL_SCHEME_UNKNOWN;
1388 data->host = *ptr;
1390 if(!check_ipv4address(ptr, FALSE)) {
1391 if(!check_implicit_ipv4(ptr, &data->implicit_ipv4)) {
1392 TRACE("(%p %p %x): URI didn't contain anything looking like an IPv4 address.\n",
1393 ptr, data, flags);
1394 *ptr = data->host;
1395 data->host = NULL;
1396 return FALSE;
1397 } else
1398 data->has_implicit_ip = TRUE;
1401 data->host_len = *ptr - data->host;
1402 data->host_type = Uri_HOST_IPV4;
1404 /* Check if what we found is the only part of the host name (if it isn't
1405 * we don't have an IPv4 address).
1407 if(**ptr == ':') {
1408 ++(*ptr);
1409 if(!parse_port(ptr, data, flags)) {
1410 *ptr = data->host;
1411 data->host = NULL;
1412 return FALSE;
1414 } else if(!is_auth_delim(**ptr, !is_unknown)) {
1415 /* Found more data which belongs to the host, so this isn't an IPv4. */
1416 *ptr = data->host;
1417 data->host = NULL;
1418 data->has_implicit_ip = FALSE;
1419 return FALSE;
1422 TRACE("(%p %p %x): IPv4 address found. host=%s host_len=%d host_type=%d\n",
1423 ptr, data, flags, debugstr_wn(data->host, data->host_len),
1424 data->host_len, data->host_type);
1425 return TRUE;
1428 /* Attempts to parse the reg-name from the URI.
1430 * Because of the way Windows handles ':' this function also
1431 * handles parsing the port.
1433 * reg-name = *( unreserved / pct-encoded / sub-delims )
1435 * NOTE:
1436 * Windows allows everything, but, the characters in "auth_delims" and ':'
1437 * to appear in a reg-name, unless it's an unknown scheme type then ':' is
1438 * allowed to appear (even if a valid port isn't after it).
1440 * Windows doesn't like host names which start with '[' and end with ']'
1441 * and don't contain a valid IP literal address in between them.
1443 * On Windows if a '[' is encountered in the host name the ':' no longer
1444 * counts as a delimiter until you reach the next ']' or an "authority delimiter".
1446 * A reg-name CAN be empty.
1448 static BOOL parse_reg_name(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1449 const BOOL has_start_bracket = **ptr == '[';
1450 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1451 const BOOL is_res = data->scheme_type == URL_SCHEME_RES;
1452 BOOL inside_brackets = has_start_bracket;
1454 /* res URIs don't have ports. */
1455 BOOL ignore_col = (extras & IGNORE_PORT_DELIMITER) || is_res;
1457 /* We have to be careful with file schemes. */
1458 if(data->scheme_type == URL_SCHEME_FILE) {
1459 /* This is because an implicit file scheme could be "C:\\test" and it
1460 * would trick this function into thinking the host is "C", when after
1461 * canonicalization the host would end up being an empty string. A drive
1462 * path can also have a '|' instead of a ':' after the drive letter.
1464 if(is_drive_path(*ptr)) {
1465 /* Regular old drive paths have no host type (or host name). */
1466 data->host_type = Uri_HOST_UNKNOWN;
1467 data->host = *ptr;
1468 data->host_len = 0;
1469 return TRUE;
1470 } else if(is_unc_path(*ptr))
1471 /* Skip past the "\\" of a UNC path. */
1472 *ptr += 2;
1475 data->host = *ptr;
1477 /* For res URIs, everything before the first '/' is
1478 * considered the host.
1480 while((!is_res && !is_auth_delim(**ptr, known_scheme)) ||
1481 (is_res && **ptr && **ptr != '/')) {
1482 if(**ptr == ':' && !ignore_col) {
1483 /* We can ignore ':' if were inside brackets.*/
1484 if(!inside_brackets) {
1485 const WCHAR *tmp = (*ptr)++;
1487 /* Attempt to parse the port. */
1488 if(!parse_port(ptr, data, flags)) {
1489 /* Windows expects there to be a valid port for known scheme types. */
1490 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1491 *ptr = data->host;
1492 data->host = NULL;
1493 TRACE("(%p %p %x %x): Expected valid port\n", ptr, data, flags, extras);
1494 return FALSE;
1495 } else
1496 /* Windows gives up on trying to parse a port when it
1497 * encounters an invalid port.
1499 ignore_col = TRUE;
1500 } else {
1501 data->host_len = tmp - data->host;
1502 break;
1505 } else if(**ptr == '%' && (known_scheme && !is_res)) {
1506 /* Has to be a legit % encoded value. */
1507 if(!check_pct_encoded(ptr)) {
1508 *ptr = data->host;
1509 data->host = NULL;
1510 return FALSE;
1511 } else
1512 continue;
1513 } else if(is_res && is_forbidden_dos_path_char(**ptr)) {
1514 *ptr = data->host;
1515 data->host = NULL;
1516 return FALSE;
1517 } else if(**ptr == ']')
1518 inside_brackets = FALSE;
1519 else if(**ptr == '[')
1520 inside_brackets = TRUE;
1522 ++(*ptr);
1525 if(has_start_bracket) {
1526 /* Make sure the last character of the host wasn't a ']'. */
1527 if(*(*ptr-1) == ']') {
1528 TRACE("(%p %p %x %x): Expected an IP literal inside of the host\n",
1529 ptr, data, flags, extras);
1530 *ptr = data->host;
1531 data->host = NULL;
1532 return FALSE;
1536 /* Don't overwrite our length if we found a port earlier. */
1537 if(!data->port)
1538 data->host_len = *ptr - data->host;
1540 /* If the host is empty, then it's an unknown host type. */
1541 if(data->host_len == 0 || is_res)
1542 data->host_type = Uri_HOST_UNKNOWN;
1543 else
1544 data->host_type = Uri_HOST_DNS;
1546 TRACE("(%p %p %x %x): Parsed reg-name. host=%s len=%d\n", ptr, data, flags, extras,
1547 debugstr_wn(data->host, data->host_len), data->host_len);
1548 return TRUE;
1551 /* Attempts to parse an IPv6 address out of the URI.
1553 * IPv6address = 6( h16 ":" ) ls32
1554 * / "::" 5( h16 ":" ) ls32
1555 * / [ h16 ] "::" 4( h16 ":" ) ls32
1556 * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
1557 * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
1558 * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
1559 * / [ *4( h16 ":" ) h16 ] "::" ls32
1560 * / [ *5( h16 ":" ) h16 ] "::" h16
1561 * / [ *6( h16 ":" ) h16 ] "::"
1563 * ls32 = ( h16 ":" h16 ) / IPv4address
1564 * ; least-significant 32 bits of address.
1566 * h16 = 1*4HEXDIG
1567 * ; 16 bits of address represented in hexadecimal.
1569 * Modeled after google-url's 'DoParseIPv6' function.
1571 static BOOL parse_ipv6address(const WCHAR **ptr, parse_data *data, DWORD flags) {
1572 const WCHAR *start, *cur_start;
1573 ipv6_address ip;
1575 start = cur_start = *ptr;
1576 memset(&ip, 0, sizeof(ipv6_address));
1578 for(;; ++(*ptr)) {
1579 /* Check if we're on the last character of the host. */
1580 BOOL is_end = (is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)
1581 || **ptr == ']');
1583 BOOL is_split = (**ptr == ':');
1584 BOOL is_elision = (is_split && !is_end && *(*ptr+1) == ':');
1586 /* Check if we're at the end of a component, or
1587 * if we're at the end of the IPv6 address.
1589 if(is_split || is_end) {
1590 DWORD cur_len = 0;
1592 cur_len = *ptr - cur_start;
1594 /* h16 can't have a length > 4. */
1595 if(cur_len > 4) {
1596 *ptr = start;
1598 TRACE("(%p %p %x): h16 component to long.\n",
1599 ptr, data, flags);
1600 return FALSE;
1603 if(cur_len == 0) {
1604 /* An h16 component can't have the length of 0 unless
1605 * the elision is at the beginning of the address, or
1606 * at the end of the address.
1608 if(!((*ptr == start && is_elision) ||
1609 (is_end && (*ptr-2) == ip.elision))) {
1610 *ptr = start;
1611 TRACE("(%p %p %x): IPv6 component cannot have a length of 0.\n",
1612 ptr, data, flags);
1613 return FALSE;
1617 if(cur_len > 0) {
1618 /* An IPv6 address can have no more than 8 h16 components. */
1619 if(ip.h16_count >= 8) {
1620 *ptr = start;
1621 TRACE("(%p %p %x): Not a IPv6 address, too many h16 components.\n",
1622 ptr, data, flags);
1623 return FALSE;
1626 ip.components[ip.h16_count].str = cur_start;
1627 ip.components[ip.h16_count].len = cur_len;
1629 TRACE("(%p %p %x): Found h16 component %s, len=%d, h16_count=%d\n",
1630 ptr, data, flags, debugstr_wn(cur_start, cur_len), cur_len,
1631 ip.h16_count);
1632 ++ip.h16_count;
1636 if(is_end)
1637 break;
1639 if(is_elision) {
1640 /* A IPv6 address can only have 1 elision ('::'). */
1641 if(ip.elision) {
1642 *ptr = start;
1644 TRACE("(%p %p %x): IPv6 address cannot have 2 elisions.\n",
1645 ptr, data, flags);
1646 return FALSE;
1649 ip.elision = *ptr;
1650 ++(*ptr);
1653 if(is_split)
1654 cur_start = *ptr+1;
1655 else {
1656 if(!check_ipv4address(ptr, TRUE)) {
1657 if(!is_hexdigit(**ptr)) {
1658 /* Not a valid character for an IPv6 address. */
1659 *ptr = start;
1660 return FALSE;
1662 } else {
1663 /* Found an IPv4 address. */
1664 ip.ipv4 = cur_start;
1665 ip.ipv4_len = *ptr - cur_start;
1667 TRACE("(%p %p %x): Found an attached IPv4 address %s len=%d.\n",
1668 ptr, data, flags, debugstr_wn(ip.ipv4, ip.ipv4_len),
1669 ip.ipv4_len);
1671 /* IPv4 addresses can only appear at the end of a IPv6. */
1672 break;
1677 compute_ipv6_comps_size(&ip);
1679 /* Make sure the IPv6 address adds up to 16 bytes. */
1680 if(ip.components_size + ip.elision_size != 16) {
1681 *ptr = start;
1682 TRACE("(%p %p %x): Invalid IPv6 address, did not add up to 16 bytes.\n",
1683 ptr, data, flags);
1684 return FALSE;
1687 if(ip.elision_size == 2) {
1688 /* For some reason on Windows if an elision that represents
1689 * only one h16 component is encountered at the very begin or
1690 * end of an IPv6 address, Windows does not consider it a
1691 * valid IPv6 address.
1693 * Ex: [::2:3:4:5:6:7] is not valid, even though the sum
1694 * of all the components == 128bits.
1696 if(ip.elision < ip.components[0].str ||
1697 ip.elision > ip.components[ip.h16_count-1].str) {
1698 *ptr = start;
1699 TRACE("(%p %p %x): Invalid IPv6 address. Detected elision of 2 bytes at the beginning or end of the address.\n",
1700 ptr, data, flags);
1701 return FALSE;
1705 data->host_type = Uri_HOST_IPV6;
1706 data->has_ipv6 = TRUE;
1707 data->ipv6_address = ip;
1709 TRACE("(%p %p %x): Found valid IPv6 literal %s len=%d\n",
1710 ptr, data, flags, debugstr_wn(start, *ptr-start),
1711 (int)(*ptr-start));
1712 return TRUE;
1715 /* IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) */
1716 static BOOL parse_ipvfuture(const WCHAR **ptr, parse_data *data, DWORD flags) {
1717 const WCHAR *start = *ptr;
1719 /* IPvFuture has to start with a 'v' or 'V'. */
1720 if(**ptr != 'v' && **ptr != 'V')
1721 return FALSE;
1723 /* Following the v there must be at least 1 hex digit. */
1724 ++(*ptr);
1725 if(!is_hexdigit(**ptr)) {
1726 *ptr = start;
1727 return FALSE;
1730 ++(*ptr);
1731 while(is_hexdigit(**ptr))
1732 ++(*ptr);
1734 /* End of the hexdigit sequence must be a '.' */
1735 if(**ptr != '.') {
1736 *ptr = start;
1737 return FALSE;
1740 ++(*ptr);
1741 if(!is_unreserved(**ptr) && !is_subdelim(**ptr) && **ptr != ':') {
1742 *ptr = start;
1743 return FALSE;
1746 ++(*ptr);
1747 while(is_unreserved(**ptr) || is_subdelim(**ptr) || **ptr == ':')
1748 ++(*ptr);
1750 data->host_type = Uri_HOST_UNKNOWN;
1752 TRACE("(%p %p %x): Parsed IPvFuture address %s len=%d\n", ptr, data, flags,
1753 debugstr_wn(start, *ptr-start), (int)(*ptr-start));
1755 return TRUE;
1758 /* IP-literal = "[" ( IPv6address / IPvFuture ) "]" */
1759 static BOOL parse_ip_literal(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1760 data->host = *ptr;
1762 if(**ptr != '[' && !(extras & ALLOW_BRACKETLESS_IP_LITERAL)) {
1763 data->host = NULL;
1764 return FALSE;
1765 } else if(**ptr == '[')
1766 ++(*ptr);
1768 if(!parse_ipv6address(ptr, data, flags)) {
1769 if(extras & SKIP_IP_FUTURE_CHECK || !parse_ipvfuture(ptr, data, flags)) {
1770 *ptr = data->host;
1771 data->host = NULL;
1772 return FALSE;
1776 if(**ptr != ']' && !(extras & ALLOW_BRACKETLESS_IP_LITERAL)) {
1777 *ptr = data->host;
1778 data->host = NULL;
1779 return FALSE;
1780 } else if(!**ptr && extras & ALLOW_BRACKETLESS_IP_LITERAL) {
1781 /* The IP literal didn't contain brackets and was followed by
1782 * a NULL terminator, so no reason to even check the port.
1784 data->host_len = *ptr - data->host;
1785 return TRUE;
1788 ++(*ptr);
1789 if(**ptr == ':') {
1790 ++(*ptr);
1791 /* If a valid port is not found, then let it trickle down to
1792 * parse_reg_name.
1794 if(!parse_port(ptr, data, flags)) {
1795 *ptr = data->host;
1796 data->host = NULL;
1797 return FALSE;
1799 } else
1800 data->host_len = *ptr - data->host;
1802 return TRUE;
1805 /* Parses the host information from the URI.
1807 * host = IP-literal / IPv4address / reg-name
1809 static BOOL parse_host(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1810 if(!parse_ip_literal(ptr, data, flags, extras)) {
1811 if(!parse_ipv4address(ptr, data, flags)) {
1812 if(!parse_reg_name(ptr, data, flags, extras)) {
1813 TRACE("(%p %p %x %x): Malformed URI, Unknown host type.\n",
1814 ptr, data, flags, extras);
1815 return FALSE;
1820 return TRUE;
1823 /* Parses the authority information from the URI.
1825 * authority = [ userinfo "@" ] host [ ":" port ]
1827 static BOOL parse_authority(const WCHAR **ptr, parse_data *data, DWORD flags) {
1828 parse_userinfo(ptr, data, flags);
1830 /* Parsing the port will happen during one of the host parsing
1831 * routines (if the URI has a port).
1833 if(!parse_host(ptr, data, flags, 0))
1834 return FALSE;
1836 return TRUE;
1839 /* Attempts to parse the path information of a hierarchical URI. */
1840 static BOOL parse_path_hierarchical(const WCHAR **ptr, parse_data *data, DWORD flags) {
1841 const WCHAR *start = *ptr;
1842 static const WCHAR slash[] = {'/',0};
1843 const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
1845 if(is_path_delim(data->scheme_type, **ptr)) {
1846 if(data->scheme_type == URL_SCHEME_WILDCARD && !data->must_have_path) {
1847 data->path = NULL;
1848 data->path_len = 0;
1849 } else if(!(flags & Uri_CREATE_NO_CANONICALIZE)) {
1850 /* If the path component is empty, then a '/' is added. */
1851 data->path = slash;
1852 data->path_len = 1;
1854 } else {
1855 while(!is_path_delim(data->scheme_type, **ptr)) {
1856 if(**ptr == '%' && data->scheme_type != URL_SCHEME_UNKNOWN && !is_file) {
1857 if(!check_pct_encoded(ptr)) {
1858 *ptr = start;
1859 return FALSE;
1860 } else
1861 continue;
1862 } else if(is_forbidden_dos_path_char(**ptr) && is_file &&
1863 (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
1864 /* File schemes with USE_DOS_PATH set aren't allowed to have
1865 * a '<' or '>' or '\"' appear in them.
1867 *ptr = start;
1868 return FALSE;
1869 } else if(**ptr == '\\') {
1870 /* Not allowed to have a backslash if NO_CANONICALIZE is set
1871 * and the scheme is known type (but not a file scheme).
1873 if(flags & Uri_CREATE_NO_CANONICALIZE) {
1874 if(data->scheme_type != URL_SCHEME_FILE &&
1875 data->scheme_type != URL_SCHEME_UNKNOWN) {
1876 *ptr = start;
1877 return FALSE;
1882 ++(*ptr);
1885 /* The only time a URI doesn't have a path is when
1886 * the NO_CANONICALIZE flag is set and the raw URI
1887 * didn't contain one.
1889 if(*ptr == start) {
1890 data->path = NULL;
1891 data->path_len = 0;
1892 } else {
1893 data->path = start;
1894 data->path_len = *ptr - start;
1898 if(data->path)
1899 TRACE("(%p %p %x): Parsed path %s len=%d\n", ptr, data, flags,
1900 debugstr_wn(data->path, data->path_len), data->path_len);
1901 else
1902 TRACE("(%p %p %x): The URI contained no path\n", ptr, data, flags);
1904 return TRUE;
1907 /* Parses the path of an opaque URI (much less strict than the parser
1908 * for a hierarchical URI).
1910 * NOTE:
1911 * Windows allows invalid % encoded data to appear in opaque URI paths
1912 * for unknown scheme types.
1914 * File schemes with USE_DOS_PATH set aren't allowed to have '<', '>', or '\"'
1915 * appear in them.
1917 static BOOL parse_path_opaque(const WCHAR **ptr, parse_data *data, DWORD flags) {
1918 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1919 const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
1920 const BOOL is_mailto = data->scheme_type == URL_SCHEME_MAILTO;
1922 if (is_mailto && (*ptr)[0] == '/' && (*ptr)[1] == '/')
1924 if ((*ptr)[2]) data->path = *ptr + 2;
1925 else data->path = NULL;
1927 else
1928 data->path = *ptr;
1930 while(!is_path_delim(data->scheme_type, **ptr)) {
1931 if(**ptr == '%' && known_scheme) {
1932 if(!check_pct_encoded(ptr)) {
1933 *ptr = data->path;
1934 data->path = NULL;
1935 return FALSE;
1936 } else
1937 continue;
1938 } else if(is_forbidden_dos_path_char(**ptr) && is_file &&
1939 (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
1940 *ptr = data->path;
1941 data->path = NULL;
1942 return FALSE;
1945 ++(*ptr);
1948 if (data->path) data->path_len = *ptr - data->path;
1949 TRACE("(%p %p %x): Parsed opaque URI path %s len=%d\n", ptr, data, flags,
1950 debugstr_wn(data->path, data->path_len), data->path_len);
1951 return TRUE;
1954 /* Determines how the URI should be parsed after the scheme information.
1956 * If the scheme is followed by "//", then it is treated as a hierarchical URI
1957 * which then the authority and path information will be parsed out. Otherwise, the
1958 * URI will be treated as an opaque URI which the authority information is not parsed
1959 * out.
1961 * RFC 3896 definition of hier-part:
1963 * hier-part = "//" authority path-abempty
1964 * / path-absolute
1965 * / path-rootless
1966 * / path-empty
1968 * MSDN opaque URI definition:
1969 * scheme ":" path [ "#" fragment ]
1971 * NOTES:
1972 * If the URI is of an unknown scheme type and has a "//" following the scheme then it
1973 * is treated as a hierarchical URI, but, if the CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is
1974 * set then it is considered an opaque URI regardless of what follows the scheme information
1975 * (per MSDN documentation).
1977 static BOOL parse_hierpart(const WCHAR **ptr, parse_data *data, DWORD flags) {
1978 const WCHAR *start = *ptr;
1980 data->must_have_path = FALSE;
1982 /* For javascript: URIs, simply set everything as a path */
1983 if(data->scheme_type == URL_SCHEME_JAVASCRIPT) {
1984 data->path = *ptr;
1985 data->path_len = strlenW(*ptr);
1986 data->is_opaque = TRUE;
1987 *ptr += data->path_len;
1988 return TRUE;
1991 /* Checks if the authority information needs to be parsed. */
1992 if(is_hierarchical_uri(ptr, data)) {
1993 /* Only treat it as a hierarchical URI if the scheme_type is known or
1994 * the Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is not set.
1996 if(data->scheme_type != URL_SCHEME_UNKNOWN ||
1997 !(flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES)) {
1998 TRACE("(%p %p %x): Treating URI as an hierarchical URI.\n", ptr, data, flags);
1999 data->is_opaque = FALSE;
2001 if(data->scheme_type == URL_SCHEME_WILDCARD && !data->has_implicit_scheme) {
2002 if(**ptr == '/' && *(*ptr+1) == '/') {
2003 data->must_have_path = TRUE;
2004 *ptr += 2;
2008 /* TODO: Handle hierarchical URI's, parse authority then parse the path. */
2009 if(!parse_authority(ptr, data, flags))
2010 return FALSE;
2012 return parse_path_hierarchical(ptr, data, flags);
2013 } else
2014 /* Reset ptr to its starting position so opaque path parsing
2015 * begins at the correct location.
2017 *ptr = start;
2020 /* If it reaches here, then the URI will be treated as an opaque
2021 * URI.
2024 TRACE("(%p %p %x): Treating URI as an opaque URI.\n", ptr, data, flags);
2026 data->is_opaque = TRUE;
2027 if(!parse_path_opaque(ptr, data, flags))
2028 return FALSE;
2030 return TRUE;
2033 /* Attempts to parse the query string from the URI.
2035 * NOTES:
2036 * If NO_DECODE_EXTRA_INFO flag is set, then invalid percent encoded
2037 * data is allowed to appear in the query string. For unknown scheme types
2038 * invalid percent encoded data is allowed to appear regardless.
2040 static BOOL parse_query(const WCHAR **ptr, parse_data *data, DWORD flags) {
2041 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2043 if(**ptr != '?') {
2044 TRACE("(%p %p %x): URI didn't contain a query string.\n", ptr, data, flags);
2045 return TRUE;
2048 data->query = *ptr;
2050 ++(*ptr);
2051 while(**ptr && **ptr != '#') {
2052 if(**ptr == '%' && known_scheme &&
2053 !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
2054 if(!check_pct_encoded(ptr)) {
2055 *ptr = data->query;
2056 data->query = NULL;
2057 return FALSE;
2058 } else
2059 continue;
2062 ++(*ptr);
2065 data->query_len = *ptr - data->query;
2067 TRACE("(%p %p %x): Parsed query string %s len=%d\n", ptr, data, flags,
2068 debugstr_wn(data->query, data->query_len), data->query_len);
2069 return TRUE;
2072 /* Attempts to parse the fragment from the URI.
2074 * NOTES:
2075 * If NO_DECODE_EXTRA_INFO flag is set, then invalid percent encoded
2076 * data is allowed to appear in the query string. For unknown scheme types
2077 * invalid percent encoded data is allowed to appear regardless.
2079 static BOOL parse_fragment(const WCHAR **ptr, parse_data *data, DWORD flags) {
2080 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2082 if(**ptr != '#') {
2083 TRACE("(%p %p %x): URI didn't contain a fragment.\n", ptr, data, flags);
2084 return TRUE;
2087 data->fragment = *ptr;
2089 ++(*ptr);
2090 while(**ptr) {
2091 if(**ptr == '%' && known_scheme &&
2092 !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
2093 if(!check_pct_encoded(ptr)) {
2094 *ptr = data->fragment;
2095 data->fragment = NULL;
2096 return FALSE;
2097 } else
2098 continue;
2101 ++(*ptr);
2104 data->fragment_len = *ptr - data->fragment;
2106 TRACE("(%p %p %x): Parsed fragment %s len=%d\n", ptr, data, flags,
2107 debugstr_wn(data->fragment, data->fragment_len), data->fragment_len);
2108 return TRUE;
2111 /* Parses and validates the components of the specified by data->uri
2112 * and stores the information it parses into 'data'.
2114 * Returns TRUE if it successfully parsed the URI. False otherwise.
2116 static BOOL parse_uri(parse_data *data, DWORD flags) {
2117 const WCHAR *ptr;
2118 const WCHAR **pptr;
2120 ptr = data->uri;
2121 pptr = &ptr;
2123 TRACE("(%p %x): BEGINNING TO PARSE URI %s.\n", data, flags, debugstr_w(data->uri));
2125 if(!parse_scheme(pptr, data, flags, 0))
2126 return FALSE;
2128 if(!parse_hierpart(pptr, data, flags))
2129 return FALSE;
2131 if(!parse_query(pptr, data, flags))
2132 return FALSE;
2134 if(!parse_fragment(pptr, data, flags))
2135 return FALSE;
2137 TRACE("(%p %x): FINISHED PARSING URI.\n", data, flags);
2138 return TRUE;
2141 static BOOL canonicalize_username(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2142 const WCHAR *ptr;
2144 if(!data->username) {
2145 uri->userinfo_start = -1;
2146 return TRUE;
2149 uri->userinfo_start = uri->canon_len;
2150 for(ptr = data->username; ptr < data->username+data->username_len; ++ptr) {
2151 if(*ptr == '%') {
2152 /* Only decode % encoded values for known scheme types. */
2153 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2154 /* See if the value really needs decoding. */
2155 WCHAR val = decode_pct_val(ptr);
2156 if(is_unreserved(val)) {
2157 if(!computeOnly)
2158 uri->canon_uri[uri->canon_len] = val;
2160 ++uri->canon_len;
2162 /* Move pass the hex characters. */
2163 ptr += 2;
2164 continue;
2167 } else if(!is_reserved(*ptr) && !is_unreserved(*ptr) && *ptr != '\\') {
2168 /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
2169 * is NOT set.
2171 if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
2172 if(!computeOnly)
2173 pct_encode_val(*ptr, uri->canon_uri + uri->canon_len);
2175 uri->canon_len += 3;
2176 continue;
2180 if(!computeOnly)
2181 /* Nothing special, so just copy the character over. */
2182 uri->canon_uri[uri->canon_len] = *ptr;
2183 ++uri->canon_len;
2186 return TRUE;
2189 static BOOL canonicalize_password(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2190 const WCHAR *ptr;
2192 if(!data->password) {
2193 uri->userinfo_split = -1;
2194 return TRUE;
2197 if(uri->userinfo_start == -1)
2198 /* Has a password, but, doesn't have a username. */
2199 uri->userinfo_start = uri->canon_len;
2201 uri->userinfo_split = uri->canon_len - uri->userinfo_start;
2203 /* Add the ':' to the userinfo component. */
2204 if(!computeOnly)
2205 uri->canon_uri[uri->canon_len] = ':';
2206 ++uri->canon_len;
2208 for(ptr = data->password; ptr < data->password+data->password_len; ++ptr) {
2209 if(*ptr == '%') {
2210 /* Only decode % encoded values for known scheme types. */
2211 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2212 /* See if the value really needs decoding. */
2213 WCHAR val = decode_pct_val(ptr);
2214 if(is_unreserved(val)) {
2215 if(!computeOnly)
2216 uri->canon_uri[uri->canon_len] = val;
2218 ++uri->canon_len;
2220 /* Move pass the hex characters. */
2221 ptr += 2;
2222 continue;
2225 } else if(!is_reserved(*ptr) && !is_unreserved(*ptr) && *ptr != '\\') {
2226 /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
2227 * is NOT set.
2229 if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
2230 if(!computeOnly)
2231 pct_encode_val(*ptr, uri->canon_uri + uri->canon_len);
2233 uri->canon_len += 3;
2234 continue;
2238 if(!computeOnly)
2239 /* Nothing special, so just copy the character over. */
2240 uri->canon_uri[uri->canon_len] = *ptr;
2241 ++uri->canon_len;
2244 return TRUE;
2247 /* Canonicalizes the userinfo of the URI represented by the parse_data.
2249 * Canonicalization of the userinfo is a simple process. If there are any percent
2250 * encoded characters that fall in the "unreserved" character set, they are decoded
2251 * to their actual value. If a character is not in the "unreserved" or "reserved" sets
2252 * then it is percent encoded. Other than that the characters are copied over without
2253 * change.
2255 static BOOL canonicalize_userinfo(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2256 uri->userinfo_start = uri->userinfo_split = -1;
2257 uri->userinfo_len = 0;
2259 if(!data->username && !data->password)
2260 /* URI doesn't have userinfo, so nothing to do here. */
2261 return TRUE;
2263 if(!canonicalize_username(data, uri, flags, computeOnly))
2264 return FALSE;
2266 if(!canonicalize_password(data, uri, flags, computeOnly))
2267 return FALSE;
2269 uri->userinfo_len = uri->canon_len - uri->userinfo_start;
2270 if(!computeOnly)
2271 TRACE("(%p %p %x %d): Canonicalized userinfo, userinfo_start=%d, userinfo=%s, userinfo_split=%d userinfo_len=%d.\n",
2272 data, uri, flags, computeOnly, uri->userinfo_start, debugstr_wn(uri->canon_uri + uri->userinfo_start, uri->userinfo_len),
2273 uri->userinfo_split, uri->userinfo_len);
2275 /* Now insert the '@' after the userinfo. */
2276 if(!computeOnly)
2277 uri->canon_uri[uri->canon_len] = '@';
2278 ++uri->canon_len;
2280 return TRUE;
2283 /* Attempts to canonicalize a reg_name.
2285 * Things that happen:
2286 * 1) If Uri_CREATE_NO_CANONICALIZE flag is not set, then the reg_name is
2287 * lower cased. Unless it's an unknown scheme type, which case it's
2288 * no lower cased regardless.
2290 * 2) Unreserved % encoded characters are decoded for known
2291 * scheme types.
2293 * 3) Forbidden characters are % encoded as long as
2294 * Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS flag is not set and
2295 * it isn't an unknown scheme type.
2297 * 4) If it's a file scheme and the host is "localhost" it's removed.
2299 * 5) If it's a file scheme and Uri_CREATE_FILE_USE_DOS_PATH is set,
2300 * then the UNC path characters are added before the host name.
2302 static BOOL canonicalize_reg_name(const parse_data *data, Uri *uri,
2303 DWORD flags, BOOL computeOnly) {
2304 static const WCHAR localhostW[] =
2305 {'l','o','c','a','l','h','o','s','t',0};
2306 const WCHAR *ptr;
2307 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2309 if(data->scheme_type == URL_SCHEME_FILE &&
2310 data->host_len == lstrlenW(localhostW)) {
2311 if(!StrCmpNIW(data->host, localhostW, data->host_len)) {
2312 uri->host_start = -1;
2313 uri->host_len = 0;
2314 uri->host_type = Uri_HOST_UNKNOWN;
2315 return TRUE;
2319 if(data->scheme_type == URL_SCHEME_FILE && flags & Uri_CREATE_FILE_USE_DOS_PATH) {
2320 if(!computeOnly) {
2321 uri->canon_uri[uri->canon_len] = '\\';
2322 uri->canon_uri[uri->canon_len+1] = '\\';
2324 uri->canon_len += 2;
2325 uri->authority_start = uri->canon_len;
2328 uri->host_start = uri->canon_len;
2330 for(ptr = data->host; ptr < data->host+data->host_len; ++ptr) {
2331 if(*ptr == '%' && known_scheme) {
2332 WCHAR val = decode_pct_val(ptr);
2333 if(is_unreserved(val)) {
2334 /* If NO_CANONICALIZE is not set, then windows lower cases the
2335 * decoded value.
2337 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && isupperW(val)) {
2338 if(!computeOnly)
2339 uri->canon_uri[uri->canon_len] = tolowerW(val);
2340 } else {
2341 if(!computeOnly)
2342 uri->canon_uri[uri->canon_len] = val;
2344 ++uri->canon_len;
2346 /* Skip past the % encoded character. */
2347 ptr += 2;
2348 continue;
2349 } else {
2350 /* Just copy the % over. */
2351 if(!computeOnly)
2352 uri->canon_uri[uri->canon_len] = *ptr;
2353 ++uri->canon_len;
2355 } else if(*ptr == '\\') {
2356 /* Only unknown scheme types could have made it here with a '\\' in the host name. */
2357 if(!computeOnly)
2358 uri->canon_uri[uri->canon_len] = *ptr;
2359 ++uri->canon_len;
2360 } else if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
2361 !is_unreserved(*ptr) && !is_reserved(*ptr) && known_scheme) {
2362 if(!computeOnly) {
2363 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
2365 /* The percent encoded value gets lower cased also. */
2366 if(!(flags & Uri_CREATE_NO_CANONICALIZE)) {
2367 uri->canon_uri[uri->canon_len+1] = tolowerW(uri->canon_uri[uri->canon_len+1]);
2368 uri->canon_uri[uri->canon_len+2] = tolowerW(uri->canon_uri[uri->canon_len+2]);
2372 uri->canon_len += 3;
2373 } else {
2374 if(!computeOnly) {
2375 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && known_scheme)
2376 uri->canon_uri[uri->canon_len] = tolowerW(*ptr);
2377 else
2378 uri->canon_uri[uri->canon_len] = *ptr;
2381 ++uri->canon_len;
2385 uri->host_len = uri->canon_len - uri->host_start;
2387 if(!computeOnly)
2388 TRACE("(%p %p %x %d): Canonicalize reg_name=%s len=%d\n", data, uri, flags,
2389 computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2390 uri->host_len);
2392 if(!computeOnly)
2393 find_domain_name(uri->canon_uri+uri->host_start, uri->host_len,
2394 &(uri->domain_offset));
2396 return TRUE;
2399 /* Attempts to canonicalize an implicit IPv4 address. */
2400 static BOOL canonicalize_implicit_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2401 uri->host_start = uri->canon_len;
2403 TRACE("%u\n", data->implicit_ipv4);
2404 /* For unknown scheme types Windows doesn't convert
2405 * the value into an IP address, but it still considers
2406 * it an IPv4 address.
2408 if(data->scheme_type == URL_SCHEME_UNKNOWN) {
2409 if(!computeOnly)
2410 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2411 uri->canon_len += data->host_len;
2412 } else {
2413 if(!computeOnly)
2414 uri->canon_len += ui2ipv4(uri->canon_uri+uri->canon_len, data->implicit_ipv4);
2415 else
2416 uri->canon_len += ui2ipv4(NULL, data->implicit_ipv4);
2419 uri->host_len = uri->canon_len - uri->host_start;
2420 uri->host_type = Uri_HOST_IPV4;
2422 if(!computeOnly)
2423 TRACE("%p %p %x %d): Canonicalized implicit IP address=%s len=%d\n",
2424 data, uri, flags, computeOnly,
2425 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2426 uri->host_len);
2428 return TRUE;
2431 /* Attempts to canonicalize an IPv4 address.
2433 * If the parse_data represents a URI that has an implicit IPv4 address
2434 * (ex. http://256/, this function will convert 256 into 0.0.1.0). If
2435 * the implicit IP address exceeds the value of UINT_MAX (maximum value
2436 * for an IPv4 address) it's canonicalized as if it were a reg-name.
2438 * If the parse_data contains a partial or full IPv4 address it normalizes it.
2439 * A partial IPv4 address is something like "192.0" and would be normalized to
2440 * "192.0.0.0". With a full (or partial) IPv4 address like "192.002.01.003" would
2441 * be normalized to "192.2.1.3".
2443 * NOTES:
2444 * Windows ONLY normalizes IPv4 address for known scheme types (one that isn't
2445 * URL_SCHEME_UNKNOWN). For unknown scheme types, it simply copies the data from
2446 * the original URI into the canonicalized URI, but, it still recognizes URI's
2447 * host type as HOST_IPV4.
2449 static BOOL canonicalize_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2450 if(data->has_implicit_ip)
2451 return canonicalize_implicit_ipv4address(data, uri, flags, computeOnly);
2452 else {
2453 uri->host_start = uri->canon_len;
2455 /* Windows only normalizes for known scheme types. */
2456 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2457 /* parse_data contains a partial or full IPv4 address, so normalize it. */
2458 DWORD i, octetDigitCount = 0, octetCount = 0;
2459 BOOL octetHasDigit = FALSE;
2461 for(i = 0; i < data->host_len; ++i) {
2462 if(data->host[i] == '0' && !octetHasDigit) {
2463 /* Can ignore leading zeros if:
2464 * 1) It isn't the last digit of the octet.
2465 * 2) i+1 != data->host_len
2466 * 3) i+1 != '.'
2468 if(octetDigitCount == 2 ||
2469 i+1 == data->host_len ||
2470 data->host[i+1] == '.') {
2471 if(!computeOnly)
2472 uri->canon_uri[uri->canon_len] = data->host[i];
2473 ++uri->canon_len;
2474 TRACE("Adding zero\n");
2476 } else if(data->host[i] == '.') {
2477 if(!computeOnly)
2478 uri->canon_uri[uri->canon_len] = data->host[i];
2479 ++uri->canon_len;
2481 octetDigitCount = 0;
2482 octetHasDigit = FALSE;
2483 ++octetCount;
2484 } else {
2485 if(!computeOnly)
2486 uri->canon_uri[uri->canon_len] = data->host[i];
2487 ++uri->canon_len;
2489 ++octetDigitCount;
2490 octetHasDigit = TRUE;
2494 /* Make sure the canonicalized IP address has 4 dec-octets.
2495 * If doesn't add "0" ones until there is 4;
2497 for( ; octetCount < 3; ++octetCount) {
2498 if(!computeOnly) {
2499 uri->canon_uri[uri->canon_len] = '.';
2500 uri->canon_uri[uri->canon_len+1] = '0';
2503 uri->canon_len += 2;
2505 } else {
2506 /* Windows doesn't normalize addresses in unknown schemes. */
2507 if(!computeOnly)
2508 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2509 uri->canon_len += data->host_len;
2512 uri->host_len = uri->canon_len - uri->host_start;
2513 if(!computeOnly)
2514 TRACE("(%p %p %x %d): Canonicalized IPv4 address, ip=%s len=%d\n",
2515 data, uri, flags, computeOnly,
2516 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2517 uri->host_len);
2520 return TRUE;
2523 /* Attempts to canonicalize the IPv6 address of the URI.
2525 * Multiple things happen during the canonicalization of an IPv6 address:
2526 * 1) Any leading zero's in a h16 component are removed.
2527 * Ex: [0001:0022::] -> [1:22::]
2529 * 2) The longest sequence of zero h16 components are compressed
2530 * into a "::" (elision). If there's a tie, the first is chosen.
2532 * Ex: [0:0:0:0:1:6:7:8] -> [::1:6:7:8]
2533 * [0:0:0:0:1:2::] -> [::1:2:0:0]
2534 * [0:0:1:2:0:0:7:8] -> [::1:2:0:0:7:8]
2536 * 3) If an IPv4 address is attached to the IPv6 address, it's
2537 * also normalized.
2538 * Ex: [::001.002.022.000] -> [::1.2.22.0]
2540 * 4) If an elision is present, but, only represents one h16 component
2541 * it's expanded.
2543 * Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
2545 * 5) If the IPv6 address contains an IPv4 address and there exists
2546 * at least 1 non-zero h16 component the IPv4 address is converted
2547 * into two h16 components, otherwise it's normalized and kept as is.
2549 * Ex: [::192.200.003.4] -> [::192.200.3.4]
2550 * [ffff::192.200.003.4] -> [ffff::c0c8:3041]
2552 * NOTE:
2553 * For unknown scheme types Windows simply copies the address over without any
2554 * changes.
2556 * IPv4 address can be included in an elision if all its components are 0's.
2558 static BOOL canonicalize_ipv6address(const parse_data *data, Uri *uri,
2559 DWORD flags, BOOL computeOnly) {
2560 uri->host_start = uri->canon_len;
2562 if(data->scheme_type == URL_SCHEME_UNKNOWN) {
2563 if(!computeOnly)
2564 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2565 uri->canon_len += data->host_len;
2566 } else {
2567 USHORT values[8];
2568 INT elision_start;
2569 DWORD i, elision_len;
2571 if(!ipv6_to_number(&(data->ipv6_address), values)) {
2572 TRACE("(%p %p %x %d): Failed to compute numerical value for IPv6 address.\n",
2573 data, uri, flags, computeOnly);
2574 return FALSE;
2577 if(!computeOnly)
2578 uri->canon_uri[uri->canon_len] = '[';
2579 ++uri->canon_len;
2581 /* Find where the elision should occur (if any). */
2582 compute_elision_location(&(data->ipv6_address), values, &elision_start, &elision_len);
2584 TRACE("%p %p %x %d): Elision starts at %d, len=%u\n", data, uri, flags,
2585 computeOnly, elision_start, elision_len);
2587 for(i = 0; i < 8; ++i) {
2588 BOOL in_elision = (elision_start > -1 && i >= elision_start &&
2589 i < elision_start+elision_len);
2590 BOOL do_ipv4 = (i == 6 && data->ipv6_address.ipv4 && !in_elision &&
2591 data->ipv6_address.h16_count == 0);
2593 if(i == elision_start) {
2594 if(!computeOnly) {
2595 uri->canon_uri[uri->canon_len] = ':';
2596 uri->canon_uri[uri->canon_len+1] = ':';
2598 uri->canon_len += 2;
2601 /* We can ignore the current component if we're in the elision. */
2602 if(in_elision)
2603 continue;
2605 /* We only add a ':' if we're not at i == 0, or when we're at
2606 * the very end of elision range since the ':' colon was handled
2607 * earlier. Otherwise we would end up with ":::" after elision.
2609 if(i != 0 && !(elision_start > -1 && i == elision_start+elision_len)) {
2610 if(!computeOnly)
2611 uri->canon_uri[uri->canon_len] = ':';
2612 ++uri->canon_len;
2615 if(do_ipv4) {
2616 UINT val;
2617 DWORD len;
2619 /* Combine the two parts of the IPv4 address values. */
2620 val = values[i];
2621 val <<= 16;
2622 val += values[i+1];
2624 if(!computeOnly)
2625 len = ui2ipv4(uri->canon_uri+uri->canon_len, val);
2626 else
2627 len = ui2ipv4(NULL, val);
2629 uri->canon_len += len;
2630 ++i;
2631 } else {
2632 /* Write a regular h16 component to the URI. */
2634 /* Short circuit for the trivial case. */
2635 if(values[i] == 0) {
2636 if(!computeOnly)
2637 uri->canon_uri[uri->canon_len] = '0';
2638 ++uri->canon_len;
2639 } else {
2640 static const WCHAR formatW[] = {'%','x',0};
2642 if(!computeOnly)
2643 uri->canon_len += sprintfW(uri->canon_uri+uri->canon_len,
2644 formatW, values[i]);
2645 else {
2646 WCHAR tmp[5];
2647 uri->canon_len += sprintfW(tmp, formatW, values[i]);
2653 /* Add the closing ']'. */
2654 if(!computeOnly)
2655 uri->canon_uri[uri->canon_len] = ']';
2656 ++uri->canon_len;
2659 uri->host_len = uri->canon_len - uri->host_start;
2661 if(!computeOnly)
2662 TRACE("(%p %p %x %d): Canonicalized IPv6 address %s, len=%d\n", data, uri, flags,
2663 computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2664 uri->host_len);
2666 return TRUE;
2669 /* Attempts to canonicalize the host of the URI (if any). */
2670 static BOOL canonicalize_host(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2671 uri->host_start = -1;
2672 uri->host_len = 0;
2673 uri->domain_offset = -1;
2675 if(data->host) {
2676 switch(data->host_type) {
2677 case Uri_HOST_DNS:
2678 uri->host_type = Uri_HOST_DNS;
2679 if(!canonicalize_reg_name(data, uri, flags, computeOnly))
2680 return FALSE;
2682 break;
2683 case Uri_HOST_IPV4:
2684 uri->host_type = Uri_HOST_IPV4;
2685 if(!canonicalize_ipv4address(data, uri, flags, computeOnly))
2686 return FALSE;
2688 break;
2689 case Uri_HOST_IPV6:
2690 if(!canonicalize_ipv6address(data, uri, flags, computeOnly))
2691 return FALSE;
2693 uri->host_type = Uri_HOST_IPV6;
2694 break;
2695 case Uri_HOST_UNKNOWN:
2696 if(data->host_len > 0 || data->scheme_type != URL_SCHEME_FILE) {
2697 uri->host_start = uri->canon_len;
2699 /* Nothing happens to unknown host types. */
2700 if(!computeOnly)
2701 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2702 uri->canon_len += data->host_len;
2703 uri->host_len = data->host_len;
2706 uri->host_type = Uri_HOST_UNKNOWN;
2707 break;
2708 default:
2709 FIXME("(%p %p %x %d): Canonicalization for host type %d not supported.\n", data,
2710 uri, flags, computeOnly, data->host_type);
2711 return FALSE;
2715 return TRUE;
2718 static BOOL canonicalize_port(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2719 BOOL has_default_port = FALSE;
2720 USHORT default_port = 0;
2721 DWORD i;
2723 uri->port_offset = -1;
2725 /* Check if the scheme has a default port. */
2726 for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
2727 if(default_ports[i].scheme == data->scheme_type) {
2728 has_default_port = TRUE;
2729 default_port = default_ports[i].port;
2730 break;
2734 uri->has_port = data->has_port || has_default_port;
2736 /* Possible cases:
2737 * 1) Has a port which is the default port.
2738 * 2) Has a port (not the default).
2739 * 3) Doesn't have a port, but, scheme has a default port.
2740 * 4) No port.
2742 if(has_default_port && data->has_port && data->port_value == default_port) {
2743 /* If it's the default port and this flag isn't set, don't do anything. */
2744 if(flags & Uri_CREATE_NO_CANONICALIZE) {
2745 uri->port_offset = uri->canon_len-uri->authority_start;
2746 if(!computeOnly)
2747 uri->canon_uri[uri->canon_len] = ':';
2748 ++uri->canon_len;
2750 if(data->port) {
2751 /* Copy the original port over. */
2752 if(!computeOnly)
2753 memcpy(uri->canon_uri+uri->canon_len, data->port, data->port_len*sizeof(WCHAR));
2754 uri->canon_len += data->port_len;
2755 } else {
2756 if(!computeOnly)
2757 uri->canon_len += ui2str(uri->canon_uri+uri->canon_len, data->port_value);
2758 else
2759 uri->canon_len += ui2str(NULL, data->port_value);
2763 uri->port = default_port;
2764 } else if(data->has_port) {
2765 uri->port_offset = uri->canon_len-uri->authority_start;
2766 if(!computeOnly)
2767 uri->canon_uri[uri->canon_len] = ':';
2768 ++uri->canon_len;
2770 if(flags & Uri_CREATE_NO_CANONICALIZE && data->port) {
2771 /* Copy the original over without changes. */
2772 if(!computeOnly)
2773 memcpy(uri->canon_uri+uri->canon_len, data->port, data->port_len*sizeof(WCHAR));
2774 uri->canon_len += data->port_len;
2775 } else {
2776 if(!computeOnly)
2777 uri->canon_len += ui2str(uri->canon_uri+uri->canon_len, data->port_value);
2778 else
2779 uri->canon_len += ui2str(NULL, data->port_value);
2782 uri->port = data->port_value;
2783 } else if(has_default_port)
2784 uri->port = default_port;
2786 return TRUE;
2789 /* Canonicalizes the authority of the URI represented by the parse_data. */
2790 static BOOL canonicalize_authority(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2791 uri->authority_start = uri->canon_len;
2792 uri->authority_len = 0;
2794 if(!canonicalize_userinfo(data, uri, flags, computeOnly))
2795 return FALSE;
2797 if(!canonicalize_host(data, uri, flags, computeOnly))
2798 return FALSE;
2800 if(!canonicalize_port(data, uri, flags, computeOnly))
2801 return FALSE;
2803 if(uri->host_start != -1 || (data->is_relative && (data->password || data->username)))
2804 uri->authority_len = uri->canon_len - uri->authority_start;
2805 else
2806 uri->authority_start = -1;
2808 return TRUE;
2811 /* Attempts to canonicalize the path of a hierarchical URI.
2813 * Things that happen:
2814 * 1). Forbidden characters are percent encoded, unless the NO_ENCODE_FORBIDDEN
2815 * flag is set or it's a file URI. Forbidden characters are always encoded
2816 * for file schemes regardless and forbidden characters are never encoded
2817 * for unknown scheme types.
2819 * 2). For known scheme types '\\' are changed to '/'.
2821 * 3). Percent encoded, unreserved characters are decoded to their actual values.
2822 * Unless the scheme type is unknown. For file schemes any percent encoded
2823 * character in the unreserved or reserved set is decoded.
2825 * 4). For File schemes if the path is starts with a drive letter and doesn't
2826 * start with a '/' then one is appended.
2827 * Ex: file://c:/test.mp3 -> file:///c:/test.mp3
2829 * 5). Dot segments are removed from the path for all scheme types
2830 * unless NO_CANONICALIZE flag is set. Dot segments aren't removed
2831 * for wildcard scheme types.
2833 * NOTES:
2834 * file://c:/test%20test -> file:///c:/test%2520test
2835 * file://c:/test%3Etest -> file:///c:/test%253Etest
2836 * if Uri_CREATE_FILE_USE_DOS_PATH is not set:
2837 * file:///c:/test%20test -> file:///c:/test%20test
2838 * file:///c:/test%test -> file:///c:/test%25test
2840 static DWORD canonicalize_path_hierarchical(const WCHAR *path, DWORD path_len, URL_SCHEME scheme_type, BOOL has_host, DWORD flags,
2841 BOOL is_implicit_scheme, WCHAR *ret_path) {
2842 const BOOL known_scheme = scheme_type != URL_SCHEME_UNKNOWN;
2843 const BOOL is_file = scheme_type == URL_SCHEME_FILE;
2844 const BOOL is_res = scheme_type == URL_SCHEME_RES;
2845 const WCHAR *ptr;
2846 BOOL escape_pct = FALSE;
2847 DWORD len = 0;
2849 if(!path)
2850 return 0;
2852 ptr = path;
2854 if(is_file && !has_host) {
2855 /* Check if a '/' needs to be appended for the file scheme. */
2856 if(path_len > 1 && is_drive_path(ptr) && !(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2857 if(ret_path)
2858 ret_path[len] = '/';
2859 len++;
2860 escape_pct = TRUE;
2861 } else if(*ptr == '/') {
2862 if(!(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2863 /* Copy the extra '/' over. */
2864 if(ret_path)
2865 ret_path[len] = '/';
2866 len++;
2868 ++ptr;
2871 if(is_drive_path(ptr)) {
2872 if(ret_path) {
2873 ret_path[len] = *ptr;
2874 /* If there's a '|' after the drive letter, convert it to a ':'. */
2875 ret_path[len+1] = ':';
2877 ptr += 2;
2878 len += 2;
2882 if(!is_file && *path && *path != '/') {
2883 /* Prepend a '/' to the path if it doesn't have one. */
2884 if(ret_path)
2885 ret_path[len] = '/';
2886 len++;
2889 for(; ptr < path+path_len; ++ptr) {
2890 BOOL do_default_action = TRUE;
2892 if(*ptr == '%' && !is_res) {
2893 const WCHAR *tmp = ptr;
2894 WCHAR val;
2896 /* Check if the % represents a valid encoded char, or if it needs encoding. */
2897 BOOL force_encode = !check_pct_encoded(&tmp) && is_file && !(flags&Uri_CREATE_FILE_USE_DOS_PATH);
2898 val = decode_pct_val(ptr);
2900 if(force_encode || escape_pct) {
2901 /* Escape the percent sign in the file URI. */
2902 if(ret_path)
2903 pct_encode_val(*ptr, ret_path+len);
2904 len += 3;
2905 do_default_action = FALSE;
2906 } else if((is_unreserved(val) && known_scheme) ||
2907 (is_file && !is_implicit_scheme && (is_unreserved(val) || is_reserved(val) ||
2908 (val && flags&Uri_CREATE_FILE_USE_DOS_PATH && !is_forbidden_dos_path_char(val))))) {
2909 if(ret_path)
2910 ret_path[len] = val;
2911 len++;
2913 ptr += 2;
2914 continue;
2916 } else if(*ptr == '/' && is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2917 /* Convert the '/' back to a '\\'. */
2918 if(ret_path)
2919 ret_path[len] = '\\';
2920 len++;
2921 do_default_action = FALSE;
2922 } else if(*ptr == '\\' && known_scheme) {
2923 if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH))) {
2924 /* Convert '\\' into a '/'. */
2925 if(ret_path)
2926 ret_path[len] = '/';
2927 len++;
2928 do_default_action = FALSE;
2930 } else if(known_scheme && !is_res && !is_unreserved(*ptr) && !is_reserved(*ptr) &&
2931 (!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) || is_file)) {
2932 if(!is_file || !(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2933 /* Escape the forbidden character. */
2934 if(ret_path)
2935 pct_encode_val(*ptr, ret_path+len);
2936 len += 3;
2937 do_default_action = FALSE;
2941 if(do_default_action) {
2942 if(ret_path)
2943 ret_path[len] = *ptr;
2944 len++;
2948 /* Removing the dot segments only happens when it's not in
2949 * computeOnly mode and it's not a wildcard scheme. File schemes
2950 * with USE_DOS_PATH set don't get dot segments removed.
2952 if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) &&
2953 scheme_type != URL_SCHEME_WILDCARD) {
2954 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && ret_path) {
2955 /* Remove the dot segments (if any) and reset everything to the new
2956 * correct length.
2958 len = remove_dot_segments(ret_path, len);
2962 if(ret_path)
2963 TRACE("Canonicalized path %s len=%d\n", debugstr_wn(ret_path, len), len);
2964 return len;
2967 /* Attempts to canonicalize the path for an opaque URI.
2969 * For known scheme types:
2970 * 1) forbidden characters are percent encoded if
2971 * NO_ENCODE_FORBIDDEN_CHARACTERS isn't set.
2973 * 2) Percent encoded, unreserved characters are decoded
2974 * to their actual values, for known scheme types.
2976 * 3) '\\' are changed to '/' for known scheme types
2977 * except for mailto schemes.
2979 * 4) For file schemes, if USE_DOS_PATH is set all '/'
2980 * are converted to backslashes.
2982 * 5) For file schemes, if USE_DOS_PATH isn't set all '\'
2983 * are converted to forward slashes.
2985 static BOOL canonicalize_path_opaque(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2986 const WCHAR *ptr;
2987 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2988 const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
2989 const BOOL is_mk = data->scheme_type == URL_SCHEME_MK;
2991 if(!data->path) {
2992 uri->path_start = -1;
2993 uri->path_len = 0;
2994 return TRUE;
2997 uri->path_start = uri->canon_len;
2999 if(is_mk){
3000 /* hijack this flag for SCHEME_MK to tell the function when to start
3001 * converting slashes */
3002 flags |= Uri_CREATE_FILE_USE_DOS_PATH;
3005 /* For javascript: URIs, simply copy path part without any canonicalization */
3006 if(data->scheme_type == URL_SCHEME_JAVASCRIPT) {
3007 if(!computeOnly)
3008 memcpy(uri->canon_uri+uri->canon_len, data->path, data->path_len*sizeof(WCHAR));
3009 uri->path_len = data->path_len;
3010 uri->canon_len += data->path_len;
3011 return TRUE;
3014 /* Windows doesn't allow a "//" to appear after the scheme
3015 * of a URI, if it's an opaque URI.
3017 if(data->scheme && *(data->path) == '/' && *(data->path+1) == '/') {
3018 /* So it inserts a "/." before the "//" if it exists. */
3019 if(!computeOnly) {
3020 uri->canon_uri[uri->canon_len] = '/';
3021 uri->canon_uri[uri->canon_len+1] = '.';
3024 uri->canon_len += 2;
3027 for(ptr = data->path; ptr < data->path+data->path_len; ++ptr) {
3028 BOOL do_default_action = TRUE;
3030 if(*ptr == '%' && known_scheme) {
3031 WCHAR val = decode_pct_val(ptr);
3033 if(is_unreserved(val)) {
3034 if(!computeOnly)
3035 uri->canon_uri[uri->canon_len] = val;
3036 ++uri->canon_len;
3038 ptr += 2;
3039 continue;
3041 } else if(*ptr == '/' && is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
3042 if(!computeOnly)
3043 uri->canon_uri[uri->canon_len] = '\\';
3044 ++uri->canon_len;
3045 do_default_action = FALSE;
3046 } else if(*ptr == '\\') {
3047 if((data->is_relative || is_mk || is_file) && !(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
3048 /* Convert to a '/'. */
3049 if(!computeOnly)
3050 uri->canon_uri[uri->canon_len] = '/';
3051 ++uri->canon_len;
3052 do_default_action = FALSE;
3054 } else if(is_mk && *ptr == ':' && ptr + 1 < data->path + data->path_len && *(ptr + 1) == ':') {
3055 flags &= ~Uri_CREATE_FILE_USE_DOS_PATH;
3056 } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr) &&
3057 !(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
3058 if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH))) {
3059 if(!computeOnly)
3060 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3061 uri->canon_len += 3;
3062 do_default_action = FALSE;
3066 if(do_default_action) {
3067 if(!computeOnly)
3068 uri->canon_uri[uri->canon_len] = *ptr;
3069 ++uri->canon_len;
3073 if(is_mk && !computeOnly && !(flags & Uri_CREATE_NO_CANONICALIZE)) {
3074 DWORD new_len = remove_dot_segments(uri->canon_uri + uri->path_start,
3075 uri->canon_len - uri->path_start);
3076 uri->canon_len = uri->path_start + new_len;
3079 uri->path_len = uri->canon_len - uri->path_start;
3081 if(!computeOnly)
3082 TRACE("(%p %p %x %d): Canonicalized opaque URI path %s len=%d\n", data, uri, flags, computeOnly,
3083 debugstr_wn(uri->canon_uri+uri->path_start, uri->path_len), uri->path_len);
3084 return TRUE;
3087 /* Determines how the URI represented by the parse_data should be canonicalized.
3089 * Essentially, if the parse_data represents an hierarchical URI then it calls
3090 * canonicalize_authority and the canonicalization functions for the path. If the
3091 * URI is opaque it canonicalizes the path of the URI.
3093 static BOOL canonicalize_hierpart(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3094 if(!data->is_opaque || (data->is_relative && (data->password || data->username))) {
3095 /* "//" is only added for non-wildcard scheme types.
3097 * A "//" is only added to a relative URI if it has a
3098 * host or port component (this only happens if a IUriBuilder
3099 * is generating an IUri).
3101 if((data->is_relative && (data->host || data->has_port)) ||
3102 (!data->is_relative && data->scheme_type != URL_SCHEME_WILDCARD)) {
3103 if(data->scheme_type == URL_SCHEME_WILDCARD)
3104 FIXME("Here\n");
3106 if(!computeOnly) {
3107 INT pos = uri->canon_len;
3109 uri->canon_uri[pos] = '/';
3110 uri->canon_uri[pos+1] = '/';
3112 uri->canon_len += 2;
3115 if(!canonicalize_authority(data, uri, flags, computeOnly))
3116 return FALSE;
3118 if(data->is_relative && (data->password || data->username)) {
3119 if(!canonicalize_path_opaque(data, uri, flags, computeOnly))
3120 return FALSE;
3121 } else {
3122 if(!computeOnly)
3123 uri->path_start = uri->canon_len;
3124 uri->path_len = canonicalize_path_hierarchical(data->path, data->path_len, data->scheme_type, data->host_len != 0,
3125 flags, data->has_implicit_scheme, computeOnly ? NULL : uri->canon_uri+uri->canon_len);
3126 uri->canon_len += uri->path_len;
3127 if(!computeOnly && !uri->path_len)
3128 uri->path_start = -1;
3130 } else {
3131 /* Opaque URI's don't have an authority. */
3132 uri->userinfo_start = uri->userinfo_split = -1;
3133 uri->userinfo_len = 0;
3134 uri->host_start = -1;
3135 uri->host_len = 0;
3136 uri->host_type = Uri_HOST_UNKNOWN;
3137 uri->has_port = FALSE;
3138 uri->authority_start = -1;
3139 uri->authority_len = 0;
3140 uri->domain_offset = -1;
3141 uri->port_offset = -1;
3143 if(is_hierarchical_scheme(data->scheme_type)) {
3144 DWORD i;
3146 /* Absolute URIs aren't displayed for known scheme types
3147 * which should be hierarchical URIs.
3149 uri->display_modifiers |= URI_DISPLAY_NO_ABSOLUTE_URI;
3151 /* Windows also sets the port for these (if they have one). */
3152 for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
3153 if(data->scheme_type == default_ports[i].scheme) {
3154 uri->has_port = TRUE;
3155 uri->port = default_ports[i].port;
3156 break;
3161 if(!canonicalize_path_opaque(data, uri, flags, computeOnly))
3162 return FALSE;
3165 if(uri->path_start > -1 && !computeOnly)
3166 /* Finding file extensions happens for both types of URIs. */
3167 uri->extension_offset = find_file_extension(uri->canon_uri+uri->path_start, uri->path_len);
3168 else
3169 uri->extension_offset = -1;
3171 return TRUE;
3174 /* Attempts to canonicalize the query string of the URI.
3176 * Things that happen:
3177 * 1) For known scheme types forbidden characters
3178 * are percent encoded, unless the NO_DECODE_EXTRA_INFO flag is set
3179 * or NO_ENCODE_FORBIDDEN_CHARACTERS is set.
3181 * 2) For known scheme types, percent encoded, unreserved characters
3182 * are decoded as long as the NO_DECODE_EXTRA_INFO flag isn't set.
3184 static BOOL canonicalize_query(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3185 const WCHAR *ptr, *end;
3186 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
3188 if(!data->query) {
3189 uri->query_start = -1;
3190 uri->query_len = 0;
3191 return TRUE;
3194 uri->query_start = uri->canon_len;
3196 end = data->query+data->query_len;
3197 for(ptr = data->query; ptr < end; ++ptr) {
3198 if(*ptr == '%') {
3199 if(known_scheme && !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3200 WCHAR val = decode_pct_val(ptr);
3201 if(is_unreserved(val)) {
3202 if(!computeOnly)
3203 uri->canon_uri[uri->canon_len] = val;
3204 ++uri->canon_len;
3206 ptr += 2;
3207 continue;
3210 } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr)) {
3211 if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
3212 !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3213 if(!computeOnly)
3214 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3215 uri->canon_len += 3;
3216 continue;
3220 if(!computeOnly)
3221 uri->canon_uri[uri->canon_len] = *ptr;
3222 ++uri->canon_len;
3225 uri->query_len = uri->canon_len - uri->query_start;
3227 if(!computeOnly)
3228 TRACE("(%p %p %x %d): Canonicalized query string %s len=%d\n", data, uri, flags,
3229 computeOnly, debugstr_wn(uri->canon_uri+uri->query_start, uri->query_len),
3230 uri->query_len);
3231 return TRUE;
3234 static BOOL canonicalize_fragment(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3235 const WCHAR *ptr, *end;
3236 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
3238 if(!data->fragment) {
3239 uri->fragment_start = -1;
3240 uri->fragment_len = 0;
3241 return TRUE;
3244 uri->fragment_start = uri->canon_len;
3246 end = data->fragment + data->fragment_len;
3247 for(ptr = data->fragment; ptr < end; ++ptr) {
3248 if(*ptr == '%') {
3249 if(known_scheme && !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3250 WCHAR val = decode_pct_val(ptr);
3251 if(is_unreserved(val)) {
3252 if(!computeOnly)
3253 uri->canon_uri[uri->canon_len] = val;
3254 ++uri->canon_len;
3256 ptr += 2;
3257 continue;
3260 } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr)) {
3261 if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
3262 !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3263 if(!computeOnly)
3264 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3265 uri->canon_len += 3;
3266 continue;
3270 if(!computeOnly)
3271 uri->canon_uri[uri->canon_len] = *ptr;
3272 ++uri->canon_len;
3275 uri->fragment_len = uri->canon_len - uri->fragment_start;
3277 if(!computeOnly)
3278 TRACE("(%p %p %x %d): Canonicalized fragment %s len=%d\n", data, uri, flags,
3279 computeOnly, debugstr_wn(uri->canon_uri+uri->fragment_start, uri->fragment_len),
3280 uri->fragment_len);
3281 return TRUE;
3284 /* Canonicalizes the scheme information specified in the parse_data using the specified flags. */
3285 static BOOL canonicalize_scheme(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3286 uri->scheme_start = -1;
3287 uri->scheme_len = 0;
3289 if(!data->scheme) {
3290 /* The only type of URI that doesn't have to have a scheme is a relative
3291 * URI.
3293 if(!data->is_relative) {
3294 FIXME("(%p %p %x): Unable to determine the scheme type of %s.\n", data,
3295 uri, flags, debugstr_w(data->uri));
3296 return FALSE;
3298 } else {
3299 if(!computeOnly) {
3300 DWORD i;
3301 INT pos = uri->canon_len;
3303 for(i = 0; i < data->scheme_len; ++i) {
3304 /* Scheme name must be lower case after canonicalization. */
3305 uri->canon_uri[i + pos] = tolowerW(data->scheme[i]);
3308 uri->canon_uri[i + pos] = ':';
3309 uri->scheme_start = pos;
3311 TRACE("(%p %p %x): Canonicalized scheme=%s, len=%d.\n", data, uri, flags,
3312 debugstr_wn(uri->canon_uri+uri->scheme_start, data->scheme_len), data->scheme_len);
3315 /* This happens in both computation modes. */
3316 uri->canon_len += data->scheme_len + 1;
3317 uri->scheme_len = data->scheme_len;
3319 return TRUE;
3322 /* Computes what the length of the URI specified by the parse_data will be
3323 * after canonicalization occurs using the specified flags.
3325 * This function will return a non-zero value indicating the length of the canonicalized
3326 * URI, or -1 on error.
3328 static int compute_canonicalized_length(const parse_data *data, DWORD flags) {
3329 Uri uri;
3331 memset(&uri, 0, sizeof(Uri));
3333 TRACE("(%p %x): Beginning to compute canonicalized length for URI %s\n", data, flags,
3334 debugstr_w(data->uri));
3336 if(!canonicalize_scheme(data, &uri, flags, TRUE)) {
3337 ERR("(%p %x): Failed to compute URI scheme length.\n", data, flags);
3338 return -1;
3341 if(!canonicalize_hierpart(data, &uri, flags, TRUE)) {
3342 ERR("(%p %x): Failed to compute URI hierpart length.\n", data, flags);
3343 return -1;
3346 if(!canonicalize_query(data, &uri, flags, TRUE)) {
3347 ERR("(%p %x): Failed to compute query string length.\n", data, flags);
3348 return -1;
3351 if(!canonicalize_fragment(data, &uri, flags, TRUE)) {
3352 ERR("(%p %x): Failed to compute fragment length.\n", data, flags);
3353 return -1;
3356 TRACE("(%p %x): Finished computing canonicalized URI length. length=%d\n", data, flags, uri.canon_len);
3358 return uri.canon_len;
3361 /* Canonicalizes the URI data specified in the parse_data, using the given flags. If the
3362 * canonicalization succeeds it will store all the canonicalization information
3363 * in the pointer to the Uri.
3365 * To canonicalize a URI this function first computes what the length of the URI
3366 * specified by the parse_data will be. Once this is done it will then perform the actual
3367 * canonicalization of the URI.
3369 static HRESULT canonicalize_uri(const parse_data *data, Uri *uri, DWORD flags) {
3370 INT len;
3372 uri->canon_uri = NULL;
3373 uri->canon_size = uri->canon_len = 0;
3375 TRACE("(%p %p %x): beginning to canonicalize URI %s.\n", data, uri, flags, debugstr_w(data->uri));
3377 /* First try to compute the length of the URI. */
3378 len = compute_canonicalized_length(data, flags);
3379 if(len == -1) {
3380 ERR("(%p %p %x): Could not compute the canonicalized length of %s.\n", data, uri, flags,
3381 debugstr_w(data->uri));
3382 return E_INVALIDARG;
3385 uri->canon_uri = heap_alloc((len+1)*sizeof(WCHAR));
3386 if(!uri->canon_uri)
3387 return E_OUTOFMEMORY;
3389 uri->canon_size = len;
3390 if(!canonicalize_scheme(data, uri, flags, FALSE)) {
3391 ERR("(%p %p %x): Unable to canonicalize the scheme of the URI.\n", data, uri, flags);
3392 return E_INVALIDARG;
3394 uri->scheme_type = data->scheme_type;
3396 if(!canonicalize_hierpart(data, uri, flags, FALSE)) {
3397 ERR("(%p %p %x): Unable to canonicalize the heirpart of the URI\n", data, uri, flags);
3398 return E_INVALIDARG;
3401 if(!canonicalize_query(data, uri, flags, FALSE)) {
3402 ERR("(%p %p %x): Unable to canonicalize query string of the URI.\n",
3403 data, uri, flags);
3404 return E_INVALIDARG;
3407 if(!canonicalize_fragment(data, uri, flags, FALSE)) {
3408 ERR("(%p %p %x): Unable to canonicalize fragment of the URI.\n",
3409 data, uri, flags);
3410 return E_INVALIDARG;
3413 /* There's a possibility we didn't use all the space we allocated
3414 * earlier.
3416 if(uri->canon_len < uri->canon_size) {
3417 /* This happens if the URI is hierarchical and dot
3418 * segments were removed from its path.
3420 WCHAR *tmp = heap_realloc(uri->canon_uri, (uri->canon_len+1)*sizeof(WCHAR));
3421 if(!tmp)
3422 return E_OUTOFMEMORY;
3424 uri->canon_uri = tmp;
3425 uri->canon_size = uri->canon_len;
3428 uri->canon_uri[uri->canon_len] = '\0';
3429 TRACE("(%p %p %x): finished canonicalizing the URI. uri=%s\n", data, uri, flags, debugstr_w(uri->canon_uri));
3431 return S_OK;
3434 static HRESULT get_builder_component(LPWSTR *component, DWORD *component_len,
3435 LPCWSTR source, DWORD source_len,
3436 LPCWSTR *output, DWORD *output_len)
3438 if(!output_len) {
3439 if(output)
3440 *output = NULL;
3441 return E_POINTER;
3444 if(!output) {
3445 *output_len = 0;
3446 return E_POINTER;
3449 if(!(*component) && source) {
3450 /* Allocate 'component', and copy the contents from 'source'
3451 * into the new allocation.
3453 *component = heap_alloc((source_len+1)*sizeof(WCHAR));
3454 if(!(*component))
3455 return E_OUTOFMEMORY;
3457 memcpy(*component, source, source_len*sizeof(WCHAR));
3458 (*component)[source_len] = '\0';
3459 *component_len = source_len;
3462 *output = *component;
3463 *output_len = *component_len;
3464 return *output ? S_OK : S_FALSE;
3467 /* Allocates 'component' and copies the string from 'new_value' into 'component'.
3468 * If 'prefix' is set and 'new_value' isn't NULL, then it checks if 'new_value'
3469 * starts with 'prefix'. If it doesn't then 'prefix' is prepended to 'component'.
3471 * If everything is successful, then will set 'success_flag' in 'flags'.
3473 static HRESULT set_builder_component(LPWSTR *component, DWORD *component_len, LPCWSTR new_value,
3474 WCHAR prefix, DWORD *flags, DWORD success_flag)
3476 heap_free(*component);
3478 if(!new_value) {
3479 *component = NULL;
3480 *component_len = 0;
3481 } else {
3482 BOOL add_prefix = FALSE;
3483 DWORD len = lstrlenW(new_value);
3484 DWORD pos = 0;
3486 if(prefix && *new_value != prefix) {
3487 add_prefix = TRUE;
3488 *component = heap_alloc((len+2)*sizeof(WCHAR));
3489 } else
3490 *component = heap_alloc((len+1)*sizeof(WCHAR));
3492 if(!(*component))
3493 return E_OUTOFMEMORY;
3495 if(add_prefix)
3496 (*component)[pos++] = prefix;
3498 memcpy(*component+pos, new_value, (len+1)*sizeof(WCHAR));
3499 *component_len = len+pos;
3502 *flags |= success_flag;
3503 return S_OK;
3506 static void reset_builder(UriBuilder *builder) {
3507 if(builder->uri)
3508 IUri_Release(&builder->uri->IUri_iface);
3509 builder->uri = NULL;
3511 heap_free(builder->fragment);
3512 builder->fragment = NULL;
3513 builder->fragment_len = 0;
3515 heap_free(builder->host);
3516 builder->host = NULL;
3517 builder->host_len = 0;
3519 heap_free(builder->password);
3520 builder->password = NULL;
3521 builder->password_len = 0;
3523 heap_free(builder->path);
3524 builder->path = NULL;
3525 builder->path_len = 0;
3527 heap_free(builder->query);
3528 builder->query = NULL;
3529 builder->query_len = 0;
3531 heap_free(builder->scheme);
3532 builder->scheme = NULL;
3533 builder->scheme_len = 0;
3535 heap_free(builder->username);
3536 builder->username = NULL;
3537 builder->username_len = 0;
3539 builder->has_port = FALSE;
3540 builder->port = 0;
3541 builder->modified_props = 0;
3544 static HRESULT validate_scheme_name(const UriBuilder *builder, parse_data *data, DWORD flags) {
3545 const WCHAR *component;
3546 const WCHAR *ptr;
3547 const WCHAR **pptr;
3548 DWORD expected_len;
3550 if(builder->scheme) {
3551 ptr = builder->scheme;
3552 expected_len = builder->scheme_len;
3553 } else if(builder->uri && builder->uri->scheme_start > -1) {
3554 ptr = builder->uri->canon_uri+builder->uri->scheme_start;
3555 expected_len = builder->uri->scheme_len;
3556 } else {
3557 static const WCHAR nullW[] = {0};
3558 ptr = nullW;
3559 expected_len = 0;
3562 component = ptr;
3563 pptr = &ptr;
3564 if(parse_scheme(pptr, data, flags, ALLOW_NULL_TERM_SCHEME) &&
3565 data->scheme_len == expected_len) {
3566 if(data->scheme)
3567 TRACE("(%p %p %x): Found valid scheme component %s len=%d.\n", builder, data, flags,
3568 debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
3569 } else {
3570 TRACE("(%p %p %x): Invalid scheme component found %s.\n", builder, data, flags,
3571 debugstr_wn(component, expected_len));
3572 return INET_E_INVALID_URL;
3575 return S_OK;
3578 static HRESULT validate_username(const UriBuilder *builder, parse_data *data, DWORD flags) {
3579 const WCHAR *ptr;
3580 const WCHAR **pptr;
3581 DWORD expected_len;
3583 if(builder->username) {
3584 ptr = builder->username;
3585 expected_len = builder->username_len;
3586 } else if(!(builder->modified_props & Uri_HAS_USER_NAME) && builder->uri &&
3587 builder->uri->userinfo_start > -1 && builder->uri->userinfo_split != 0) {
3588 /* Just use the username from the base Uri. */
3589 data->username = builder->uri->canon_uri+builder->uri->userinfo_start;
3590 data->username_len = (builder->uri->userinfo_split > -1) ?
3591 builder->uri->userinfo_split : builder->uri->userinfo_len;
3592 ptr = NULL;
3593 } else {
3594 ptr = NULL;
3595 expected_len = 0;
3598 if(ptr) {
3599 const WCHAR *component = ptr;
3600 pptr = &ptr;
3601 if(parse_username(pptr, data, flags, ALLOW_NULL_TERM_USER_NAME) &&
3602 data->username_len == expected_len)
3603 TRACE("(%p %p %x): Found valid username component %s len=%d.\n", builder, data, flags,
3604 debugstr_wn(data->username, data->username_len), data->username_len);
3605 else {
3606 TRACE("(%p %p %x): Invalid username component found %s.\n", builder, data, flags,
3607 debugstr_wn(component, expected_len));
3608 return INET_E_INVALID_URL;
3612 return S_OK;
3615 static HRESULT validate_password(const UriBuilder *builder, parse_data *data, DWORD flags) {
3616 const WCHAR *ptr;
3617 const WCHAR **pptr;
3618 DWORD expected_len;
3620 if(builder->password) {
3621 ptr = builder->password;
3622 expected_len = builder->password_len;
3623 } else if(!(builder->modified_props & Uri_HAS_PASSWORD) && builder->uri &&
3624 builder->uri->userinfo_split > -1) {
3625 data->password = builder->uri->canon_uri+builder->uri->userinfo_start+builder->uri->userinfo_split+1;
3626 data->password_len = builder->uri->userinfo_len-builder->uri->userinfo_split-1;
3627 ptr = NULL;
3628 } else {
3629 ptr = NULL;
3630 expected_len = 0;
3633 if(ptr) {
3634 const WCHAR *component = ptr;
3635 pptr = &ptr;
3636 if(parse_password(pptr, data, flags, ALLOW_NULL_TERM_PASSWORD) &&
3637 data->password_len == expected_len)
3638 TRACE("(%p %p %x): Found valid password component %s len=%d.\n", builder, data, flags,
3639 debugstr_wn(data->password, data->password_len), data->password_len);
3640 else {
3641 TRACE("(%p %p %x): Invalid password component found %s.\n", builder, data, flags,
3642 debugstr_wn(component, expected_len));
3643 return INET_E_INVALID_URL;
3647 return S_OK;
3650 static HRESULT validate_userinfo(const UriBuilder *builder, parse_data *data, DWORD flags) {
3651 HRESULT hr;
3653 hr = validate_username(builder, data, flags);
3654 if(FAILED(hr))
3655 return hr;
3657 hr = validate_password(builder, data, flags);
3658 if(FAILED(hr))
3659 return hr;
3661 return S_OK;
3664 static HRESULT validate_host(const UriBuilder *builder, parse_data *data, DWORD flags) {
3665 const WCHAR *ptr;
3666 const WCHAR **pptr;
3667 DWORD expected_len;
3669 if(builder->host) {
3670 ptr = builder->host;
3671 expected_len = builder->host_len;
3672 } else if(!(builder->modified_props & Uri_HAS_HOST) && builder->uri && builder->uri->host_start > -1) {
3673 ptr = builder->uri->canon_uri + builder->uri->host_start;
3674 expected_len = builder->uri->host_len;
3675 } else
3676 ptr = NULL;
3678 if(ptr) {
3679 const WCHAR *component = ptr;
3680 DWORD extras = ALLOW_BRACKETLESS_IP_LITERAL|IGNORE_PORT_DELIMITER|SKIP_IP_FUTURE_CHECK;
3681 pptr = &ptr;
3683 if(parse_host(pptr, data, flags, extras) && data->host_len == expected_len)
3684 TRACE("(%p %p %x): Found valid host name %s len=%d type=%d.\n", builder, data, flags,
3685 debugstr_wn(data->host, data->host_len), data->host_len, data->host_type);
3686 else {
3687 TRACE("(%p %p %x): Invalid host name found %s.\n", builder, data, flags,
3688 debugstr_wn(component, expected_len));
3689 return INET_E_INVALID_URL;
3693 return S_OK;
3696 static void setup_port(const UriBuilder *builder, parse_data *data, DWORD flags) {
3697 if(builder->modified_props & Uri_HAS_PORT) {
3698 if(builder->has_port) {
3699 data->has_port = TRUE;
3700 data->port_value = builder->port;
3702 } else if(builder->uri && builder->uri->has_port) {
3703 data->has_port = TRUE;
3704 data->port_value = builder->uri->port;
3707 if(data->has_port)
3708 TRACE("(%p %p %x): Using %u as port for IUri.\n", builder, data, flags, data->port_value);
3711 static HRESULT validate_path(const UriBuilder *builder, parse_data *data, DWORD flags) {
3712 const WCHAR *ptr = NULL;
3713 const WCHAR *component;
3714 const WCHAR **pptr;
3715 DWORD expected_len;
3716 BOOL check_len = TRUE;
3717 BOOL valid = FALSE;
3719 if(builder->path) {
3720 ptr = builder->path;
3721 expected_len = builder->path_len;
3722 } else if(!(builder->modified_props & Uri_HAS_PATH) &&
3723 builder->uri && builder->uri->path_start > -1) {
3724 ptr = builder->uri->canon_uri+builder->uri->path_start;
3725 expected_len = builder->uri->path_len;
3726 } else {
3727 static const WCHAR nullW[] = {0};
3728 ptr = nullW;
3729 check_len = FALSE;
3730 expected_len = -1;
3733 component = ptr;
3734 pptr = &ptr;
3736 /* How the path is validated depends on what type of
3737 * URI it is.
3739 valid = data->is_opaque ?
3740 parse_path_opaque(pptr, data, flags) : parse_path_hierarchical(pptr, data, flags);
3742 if(!valid || (check_len && expected_len != data->path_len)) {
3743 TRACE("(%p %p %x): Invalid path component %s.\n", builder, data, flags,
3744 debugstr_wn(component, expected_len) );
3745 return INET_E_INVALID_URL;
3748 TRACE("(%p %p %x): Valid path component %s len=%d.\n", builder, data, flags,
3749 debugstr_wn(data->path, data->path_len), data->path_len);
3751 return S_OK;
3754 static HRESULT validate_query(const UriBuilder *builder, parse_data *data, DWORD flags) {
3755 const WCHAR *ptr = NULL;
3756 const WCHAR **pptr;
3757 DWORD expected_len;
3759 if(builder->query) {
3760 ptr = builder->query;
3761 expected_len = builder->query_len;
3762 } else if(!(builder->modified_props & Uri_HAS_QUERY) && builder->uri &&
3763 builder->uri->query_start > -1) {
3764 ptr = builder->uri->canon_uri+builder->uri->query_start;
3765 expected_len = builder->uri->query_len;
3768 if(ptr) {
3769 const WCHAR *component = ptr;
3770 pptr = &ptr;
3772 if(parse_query(pptr, data, flags) && expected_len == data->query_len)
3773 TRACE("(%p %p %x): Valid query component %s len=%d.\n", builder, data, flags,
3774 debugstr_wn(data->query, data->query_len), data->query_len);
3775 else {
3776 TRACE("(%p %p %x): Invalid query component %s.\n", builder, data, flags,
3777 debugstr_wn(component, expected_len));
3778 return INET_E_INVALID_URL;
3782 return S_OK;
3785 static HRESULT validate_fragment(const UriBuilder *builder, parse_data *data, DWORD flags) {
3786 const WCHAR *ptr = NULL;
3787 const WCHAR **pptr;
3788 DWORD expected_len;
3790 if(builder->fragment) {
3791 ptr = builder->fragment;
3792 expected_len = builder->fragment_len;
3793 } else if(!(builder->modified_props & Uri_HAS_FRAGMENT) && builder->uri &&
3794 builder->uri->fragment_start > -1) {
3795 ptr = builder->uri->canon_uri+builder->uri->fragment_start;
3796 expected_len = builder->uri->fragment_len;
3799 if(ptr) {
3800 const WCHAR *component = ptr;
3801 pptr = &ptr;
3803 if(parse_fragment(pptr, data, flags) && expected_len == data->fragment_len)
3804 TRACE("(%p %p %x): Valid fragment component %s len=%d.\n", builder, data, flags,
3805 debugstr_wn(data->fragment, data->fragment_len), data->fragment_len);
3806 else {
3807 TRACE("(%p %p %x): Invalid fragment component %s.\n", builder, data, flags,
3808 debugstr_wn(component, expected_len));
3809 return INET_E_INVALID_URL;
3813 return S_OK;
3816 static HRESULT validate_components(const UriBuilder *builder, parse_data *data, DWORD flags) {
3817 HRESULT hr;
3819 memset(data, 0, sizeof(parse_data));
3821 TRACE("(%p %p %x): Beginning to validate builder components.\n", builder, data, flags);
3823 hr = validate_scheme_name(builder, data, flags);
3824 if(FAILED(hr))
3825 return hr;
3827 /* Extra validation for file schemes. */
3828 if(data->scheme_type == URL_SCHEME_FILE) {
3829 if((builder->password || (builder->uri && builder->uri->userinfo_split > -1)) ||
3830 (builder->username || (builder->uri && builder->uri->userinfo_start > -1))) {
3831 TRACE("(%p %p %x): File schemes can't contain a username or password.\n",
3832 builder, data, flags);
3833 return INET_E_INVALID_URL;
3837 hr = validate_userinfo(builder, data, flags);
3838 if(FAILED(hr))
3839 return hr;
3841 hr = validate_host(builder, data, flags);
3842 if(FAILED(hr))
3843 return hr;
3845 setup_port(builder, data, flags);
3847 /* The URI is opaque if it doesn't have an authority component. */
3848 if(!data->is_relative)
3849 data->is_opaque = !data->username && !data->password && !data->host && !data->has_port
3850 && data->scheme_type != URL_SCHEME_FILE;
3851 else
3852 data->is_opaque = !data->host && !data->has_port;
3854 hr = validate_path(builder, data, flags);
3855 if(FAILED(hr))
3856 return hr;
3858 hr = validate_query(builder, data, flags);
3859 if(FAILED(hr))
3860 return hr;
3862 hr = validate_fragment(builder, data, flags);
3863 if(FAILED(hr))
3864 return hr;
3866 TRACE("(%p %p %x): Finished validating builder components.\n", builder, data, flags);
3868 return S_OK;
3871 static HRESULT compare_file_paths(const Uri *a, const Uri *b, BOOL *ret)
3873 WCHAR *canon_path_a, *canon_path_b;
3874 DWORD len_a, len_b;
3876 if(!a->path_len) {
3877 *ret = !b->path_len;
3878 return S_OK;
3881 if(!b->path_len) {
3882 *ret = FALSE;
3883 return S_OK;
3886 /* Fast path */
3887 if(a->path_len == b->path_len && !memicmpW(a->canon_uri+a->path_start, b->canon_uri+b->path_start, a->path_len)) {
3888 *ret = TRUE;
3889 return S_OK;
3892 len_a = canonicalize_path_hierarchical(a->canon_uri+a->path_start, a->path_len, a->scheme_type, FALSE, 0, FALSE, NULL);
3893 len_b = canonicalize_path_hierarchical(b->canon_uri+b->path_start, b->path_len, b->scheme_type, FALSE, 0, FALSE, NULL);
3895 canon_path_a = heap_alloc(len_a*sizeof(WCHAR));
3896 if(!canon_path_a)
3897 return E_OUTOFMEMORY;
3898 canon_path_b = heap_alloc(len_b*sizeof(WCHAR));
3899 if(!canon_path_b) {
3900 heap_free(canon_path_a);
3901 return E_OUTOFMEMORY;
3904 len_a = canonicalize_path_hierarchical(a->canon_uri+a->path_start, a->path_len, a->scheme_type, FALSE, 0, FALSE, canon_path_a);
3905 len_b = canonicalize_path_hierarchical(b->canon_uri+b->path_start, b->path_len, b->scheme_type, FALSE, 0, FALSE, canon_path_b);
3907 *ret = len_a == len_b && !memicmpW(canon_path_a, canon_path_b, len_a);
3909 heap_free(canon_path_a);
3910 heap_free(canon_path_b);
3911 return S_OK;
3914 /* Checks if the two Uri's are logically equivalent. It's a simple
3915 * comparison, since they are both of type Uri, and it can access
3916 * the properties of each Uri directly without the need to go
3917 * through the "IUri_Get*" interface calls.
3919 static HRESULT compare_uris(const Uri *a, const Uri *b, BOOL *ret) {
3920 const BOOL known_scheme = a->scheme_type != URL_SCHEME_UNKNOWN;
3921 const BOOL are_hierarchical = a->authority_start > -1 && b->authority_start > -1;
3922 HRESULT hres;
3924 *ret = FALSE;
3926 if(a->scheme_type != b->scheme_type)
3927 return S_OK;
3929 /* Only compare the scheme names (if any) if their unknown scheme types. */
3930 if(!known_scheme) {
3931 if((a->scheme_start > -1 && b->scheme_start > -1) &&
3932 (a->scheme_len == b->scheme_len)) {
3933 /* Make sure the schemes are the same. */
3934 if(StrCmpNW(a->canon_uri+a->scheme_start, b->canon_uri+b->scheme_start, a->scheme_len))
3935 return S_OK;
3936 } else if(a->scheme_len != b->scheme_len)
3937 /* One of the Uri's has a scheme name, while the other doesn't. */
3938 return S_OK;
3941 /* If they have a userinfo component, perform case sensitive compare. */
3942 if((a->userinfo_start > -1 && b->userinfo_start > -1) &&
3943 (a->userinfo_len == b->userinfo_len)) {
3944 if(StrCmpNW(a->canon_uri+a->userinfo_start, b->canon_uri+b->userinfo_start, a->userinfo_len))
3945 return S_OK;
3946 } else if(a->userinfo_len != b->userinfo_len)
3947 /* One of the Uri's had a userinfo, while the other one doesn't. */
3948 return S_OK;
3950 /* Check if they have a host name. */
3951 if((a->host_start > -1 && b->host_start > -1) &&
3952 (a->host_len == b->host_len)) {
3953 /* Perform a case insensitive compare if they are a known scheme type. */
3954 if(known_scheme) {
3955 if(StrCmpNIW(a->canon_uri+a->host_start, b->canon_uri+b->host_start, a->host_len))
3956 return S_OK;
3957 } else if(StrCmpNW(a->canon_uri+a->host_start, b->canon_uri+b->host_start, a->host_len))
3958 return S_OK;
3959 } else if(a->host_len != b->host_len)
3960 /* One of the Uri's had a host, while the other one didn't. */
3961 return S_OK;
3963 if(a->has_port && b->has_port) {
3964 if(a->port != b->port)
3965 return S_OK;
3966 } else if(a->has_port || b->has_port)
3967 /* One had a port, while the other one didn't. */
3968 return S_OK;
3970 /* Windows is weird with how it handles paths. For example
3971 * One URI could be "http://google.com" (after canonicalization)
3972 * and one could be "http://google.com/" and the IsEqual function
3973 * would still evaluate to TRUE, but, only if they are both hierarchical
3974 * URIs.
3976 if(a->scheme_type == URL_SCHEME_FILE) {
3977 BOOL cmp;
3979 hres = compare_file_paths(a, b, &cmp);
3980 if(FAILED(hres) || !cmp)
3981 return hres;
3982 } else if((a->path_start > -1 && b->path_start > -1) &&
3983 (a->path_len == b->path_len)) {
3984 if(StrCmpNW(a->canon_uri+a->path_start, b->canon_uri+b->path_start, a->path_len))
3985 return S_OK;
3986 } else if(are_hierarchical && a->path_len == -1 && b->path_len == 0) {
3987 if(*(a->canon_uri+a->path_start) != '/')
3988 return S_OK;
3989 } else if(are_hierarchical && b->path_len == 1 && a->path_len == 0) {
3990 if(*(b->canon_uri+b->path_start) != '/')
3991 return S_OK;
3992 } else if(a->path_len != b->path_len)
3993 return S_OK;
3995 /* Compare the query strings of the two URIs. */
3996 if((a->query_start > -1 && b->query_start > -1) &&
3997 (a->query_len == b->query_len)) {
3998 if(StrCmpNW(a->canon_uri+a->query_start, b->canon_uri+b->query_start, a->query_len))
3999 return S_OK;
4000 } else if(a->query_len != b->query_len)
4001 return S_OK;
4003 if((a->fragment_start > -1 && b->fragment_start > -1) &&
4004 (a->fragment_len == b->fragment_len)) {
4005 if(StrCmpNW(a->canon_uri+a->fragment_start, b->canon_uri+b->fragment_start, a->fragment_len))
4006 return S_OK;
4007 } else if(a->fragment_len != b->fragment_len)
4008 return S_OK;
4010 /* If we get here, the two URIs are equivalent. */
4011 *ret = TRUE;
4012 return S_OK;
4015 static void convert_to_dos_path(const WCHAR *path, DWORD path_len,
4016 WCHAR *output, DWORD *output_len)
4018 const WCHAR *ptr = path;
4020 if(path_len > 3 && *ptr == '/' && is_drive_path(path+1))
4021 /* Skip over the leading / before the drive path. */
4022 ++ptr;
4024 for(; ptr < path+path_len; ++ptr) {
4025 if(*ptr == '/') {
4026 if(output)
4027 *output++ = '\\';
4028 (*output_len)++;
4029 } else {
4030 if(output)
4031 *output++ = *ptr;
4032 (*output_len)++;
4037 /* Generates a raw uri string using the parse_data. */
4038 static DWORD generate_raw_uri(const parse_data *data, BSTR uri, DWORD flags) {
4039 DWORD length = 0;
4041 if(data->scheme) {
4042 if(uri) {
4043 memcpy(uri, data->scheme, data->scheme_len*sizeof(WCHAR));
4044 uri[data->scheme_len] = ':';
4046 length += data->scheme_len+1;
4049 if(!data->is_opaque) {
4050 /* For the "//" which appears before the authority component. */
4051 if(uri) {
4052 uri[length] = '/';
4053 uri[length+1] = '/';
4055 length += 2;
4057 /* Check if we need to add the "\\" before the host name
4058 * of a UNC server name in a DOS path.
4060 if(flags & RAW_URI_CONVERT_TO_DOS_PATH &&
4061 data->scheme_type == URL_SCHEME_FILE && data->host) {
4062 if(uri) {
4063 uri[length] = '\\';
4064 uri[length+1] = '\\';
4066 length += 2;
4070 if(data->username) {
4071 if(uri)
4072 memcpy(uri+length, data->username, data->username_len*sizeof(WCHAR));
4073 length += data->username_len;
4076 if(data->password) {
4077 if(uri) {
4078 uri[length] = ':';
4079 memcpy(uri+length+1, data->password, data->password_len*sizeof(WCHAR));
4081 length += data->password_len+1;
4084 if(data->password || data->username) {
4085 if(uri)
4086 uri[length] = '@';
4087 ++length;
4090 if(data->host) {
4091 /* IPv6 addresses get the brackets added around them if they don't already
4092 * have them.
4094 const BOOL add_brackets = data->host_type == Uri_HOST_IPV6 && *(data->host) != '[';
4095 if(add_brackets) {
4096 if(uri)
4097 uri[length] = '[';
4098 ++length;
4101 if(uri)
4102 memcpy(uri+length, data->host, data->host_len*sizeof(WCHAR));
4103 length += data->host_len;
4105 if(add_brackets) {
4106 if(uri)
4107 uri[length] = ']';
4108 length++;
4112 if(data->has_port) {
4113 /* The port isn't included in the raw uri if it's the default
4114 * port for the scheme type.
4116 DWORD i;
4117 BOOL is_default = FALSE;
4119 for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
4120 if(data->scheme_type == default_ports[i].scheme &&
4121 data->port_value == default_ports[i].port)
4122 is_default = TRUE;
4125 if(!is_default || flags & RAW_URI_FORCE_PORT_DISP) {
4126 if(uri)
4127 uri[length] = ':';
4128 ++length;
4130 if(uri)
4131 length += ui2str(uri+length, data->port_value);
4132 else
4133 length += ui2str(NULL, data->port_value);
4137 /* Check if a '/' should be added before the path for hierarchical URIs. */
4138 if(!data->is_opaque && data->path && *(data->path) != '/') {
4139 if(uri)
4140 uri[length] = '/';
4141 ++length;
4144 if(data->path) {
4145 if(!data->is_opaque && data->scheme_type == URL_SCHEME_FILE &&
4146 flags & RAW_URI_CONVERT_TO_DOS_PATH) {
4147 DWORD len = 0;
4149 if(uri)
4150 convert_to_dos_path(data->path, data->path_len, uri+length, &len);
4151 else
4152 convert_to_dos_path(data->path, data->path_len, NULL, &len);
4154 length += len;
4155 } else {
4156 if(uri)
4157 memcpy(uri+length, data->path, data->path_len*sizeof(WCHAR));
4158 length += data->path_len;
4162 if(data->query) {
4163 if(uri)
4164 memcpy(uri+length, data->query, data->query_len*sizeof(WCHAR));
4165 length += data->query_len;
4168 if(data->fragment) {
4169 if(uri)
4170 memcpy(uri+length, data->fragment, data->fragment_len*sizeof(WCHAR));
4171 length += data->fragment_len;
4174 if(uri)
4175 TRACE("(%p %p): Generated raw uri=%s len=%d\n", data, uri, debugstr_wn(uri, length), length);
4176 else
4177 TRACE("(%p %p): Computed raw uri len=%d\n", data, uri, length);
4179 return length;
4182 static HRESULT generate_uri(const UriBuilder *builder, const parse_data *data, Uri *uri, DWORD flags) {
4183 HRESULT hr;
4184 DWORD length = generate_raw_uri(data, NULL, 0);
4185 uri->raw_uri = SysAllocStringLen(NULL, length);
4186 if(!uri->raw_uri)
4187 return E_OUTOFMEMORY;
4189 generate_raw_uri(data, uri->raw_uri, 0);
4191 hr = canonicalize_uri(data, uri, flags);
4192 if(FAILED(hr)) {
4193 if(hr == E_INVALIDARG)
4194 return INET_E_INVALID_URL;
4195 return hr;
4198 uri->create_flags = flags;
4199 return S_OK;
4202 static inline Uri* impl_from_IUri(IUri *iface)
4204 return CONTAINING_RECORD(iface, Uri, IUri_iface);
4207 static inline void destroy_uri_obj(Uri *This)
4209 SysFreeString(This->raw_uri);
4210 heap_free(This->canon_uri);
4211 heap_free(This);
4214 static HRESULT WINAPI Uri_QueryInterface(IUri *iface, REFIID riid, void **ppv)
4216 Uri *This = impl_from_IUri(iface);
4218 if(IsEqualGUID(&IID_IUnknown, riid)) {
4219 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
4220 *ppv = &This->IUri_iface;
4221 }else if(IsEqualGUID(&IID_IUri, riid)) {
4222 TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
4223 *ppv = &This->IUri_iface;
4224 }else if(IsEqualGUID(&IID_IUriBuilderFactory, riid)) {
4225 TRACE("(%p)->(IID_IUriBuilderFactory %p)\n", This, ppv);
4226 *ppv = &This->IUriBuilderFactory_iface;
4227 }else if(IsEqualGUID(&IID_IPersistStream, riid)) {
4228 TRACE("(%p)->(IID_IPersistStream %p)\n", This, ppv);
4229 *ppv = &This->IPersistStream_iface;
4230 }else if(IsEqualGUID(&IID_IMarshal, riid)) {
4231 TRACE("(%p)->(IID_IMarshal %p)\n", This, ppv);
4232 *ppv = &This->IMarshal_iface;
4233 }else if(IsEqualGUID(&IID_IUriObj, riid)) {
4234 TRACE("(%p)->(IID_IUriObj %p)\n", This, ppv);
4235 *ppv = This;
4236 return S_OK;
4237 }else {
4238 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
4239 *ppv = NULL;
4240 return E_NOINTERFACE;
4243 IUnknown_AddRef((IUnknown*)*ppv);
4244 return S_OK;
4247 static ULONG WINAPI Uri_AddRef(IUri *iface)
4249 Uri *This = impl_from_IUri(iface);
4250 LONG ref = InterlockedIncrement(&This->ref);
4252 TRACE("(%p) ref=%d\n", This, ref);
4254 return ref;
4257 static ULONG WINAPI Uri_Release(IUri *iface)
4259 Uri *This = impl_from_IUri(iface);
4260 LONG ref = InterlockedDecrement(&This->ref);
4262 TRACE("(%p) ref=%d\n", This, ref);
4264 if(!ref)
4265 destroy_uri_obj(This);
4267 return ref;
4270 static HRESULT WINAPI Uri_GetPropertyBSTR(IUri *iface, Uri_PROPERTY uriProp, BSTR *pbstrProperty, DWORD dwFlags)
4272 Uri *This = impl_from_IUri(iface);
4273 HRESULT hres;
4274 TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pbstrProperty, dwFlags);
4276 if(!This->create_flags)
4277 return E_UNEXPECTED;
4278 if(!pbstrProperty)
4279 return E_POINTER;
4281 if(uriProp > Uri_PROPERTY_STRING_LAST) {
4282 /* It only returns S_FALSE for the ZONE property... */
4283 if(uriProp == Uri_PROPERTY_ZONE) {
4284 *pbstrProperty = SysAllocStringLen(NULL, 0);
4285 if(!(*pbstrProperty))
4286 return E_OUTOFMEMORY;
4287 return S_FALSE;
4290 *pbstrProperty = NULL;
4291 return E_INVALIDARG;
4294 /* Don't have support for flags yet. */
4295 if(dwFlags) {
4296 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
4297 return E_NOTIMPL;
4300 switch(uriProp) {
4301 case Uri_PROPERTY_ABSOLUTE_URI:
4302 if(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI) {
4303 *pbstrProperty = SysAllocStringLen(NULL, 0);
4304 hres = S_FALSE;
4305 } else {
4306 if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1) {
4307 if(This->userinfo_len == 0) {
4308 /* Don't include the '@' after the userinfo component. */
4309 *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-1);
4310 hres = S_OK;
4311 if(*pbstrProperty) {
4312 /* Copy everything before it. */
4313 memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4315 /* And everything after it. */
4316 memcpy(*pbstrProperty+This->userinfo_start, This->canon_uri+This->userinfo_start+1,
4317 (This->canon_len-This->userinfo_start-1)*sizeof(WCHAR));
4319 } else if(This->userinfo_split == 0 && This->userinfo_len == 1) {
4320 /* Don't include the ":@" */
4321 *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-2);
4322 hres = S_OK;
4323 if(*pbstrProperty) {
4324 memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4325 memcpy(*pbstrProperty+This->userinfo_start, This->canon_uri+This->userinfo_start+2,
4326 (This->canon_len-This->userinfo_start-2)*sizeof(WCHAR));
4328 } else {
4329 *pbstrProperty = SysAllocString(This->canon_uri);
4330 hres = S_OK;
4332 } else {
4333 *pbstrProperty = SysAllocString(This->canon_uri);
4334 hres = S_OK;
4338 if(!(*pbstrProperty))
4339 hres = E_OUTOFMEMORY;
4341 break;
4342 case Uri_PROPERTY_AUTHORITY:
4343 if(This->authority_start > -1) {
4344 if(This->port_offset > -1 && is_default_port(This->scheme_type, This->port) &&
4345 This->display_modifiers & URI_DISPLAY_NO_DEFAULT_PORT_AUTH)
4346 /* Don't include the port in the authority component. */
4347 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->authority_start, This->port_offset);
4348 else
4349 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->authority_start, This->authority_len);
4350 hres = S_OK;
4351 } else {
4352 *pbstrProperty = SysAllocStringLen(NULL, 0);
4353 hres = S_FALSE;
4356 if(!(*pbstrProperty))
4357 hres = E_OUTOFMEMORY;
4359 break;
4360 case Uri_PROPERTY_DISPLAY_URI:
4361 /* The Display URI contains everything except for the userinfo for known
4362 * scheme types.
4364 if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1) {
4365 *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-This->userinfo_len);
4367 if(*pbstrProperty) {
4368 /* Copy everything before the userinfo over. */
4369 memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4370 /* Copy everything after the userinfo over. */
4371 memcpy(*pbstrProperty+This->userinfo_start,
4372 This->canon_uri+This->userinfo_start+This->userinfo_len+1,
4373 (This->canon_len-(This->userinfo_start+This->userinfo_len+1))*sizeof(WCHAR));
4375 } else
4376 *pbstrProperty = SysAllocString(This->canon_uri);
4378 if(!(*pbstrProperty))
4379 hres = E_OUTOFMEMORY;
4380 else
4381 hres = S_OK;
4383 break;
4384 case Uri_PROPERTY_DOMAIN:
4385 if(This->domain_offset > -1) {
4386 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+This->domain_offset,
4387 This->host_len-This->domain_offset);
4388 hres = S_OK;
4389 } else {
4390 *pbstrProperty = SysAllocStringLen(NULL, 0);
4391 hres = S_FALSE;
4394 if(!(*pbstrProperty))
4395 hres = E_OUTOFMEMORY;
4397 break;
4398 case Uri_PROPERTY_EXTENSION:
4399 if(This->extension_offset > -1) {
4400 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start+This->extension_offset,
4401 This->path_len-This->extension_offset);
4402 hres = S_OK;
4403 } else {
4404 *pbstrProperty = SysAllocStringLen(NULL, 0);
4405 hres = S_FALSE;
4408 if(!(*pbstrProperty))
4409 hres = E_OUTOFMEMORY;
4411 break;
4412 case Uri_PROPERTY_FRAGMENT:
4413 if(This->fragment_start > -1) {
4414 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->fragment_start, This->fragment_len);
4415 hres = S_OK;
4416 } else {
4417 *pbstrProperty = SysAllocStringLen(NULL, 0);
4418 hres = S_FALSE;
4421 if(!(*pbstrProperty))
4422 hres = E_OUTOFMEMORY;
4424 break;
4425 case Uri_PROPERTY_HOST:
4426 if(This->host_start > -1) {
4427 /* The '[' and ']' aren't included for IPv6 addresses. */
4428 if(This->host_type == Uri_HOST_IPV6)
4429 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+1, This->host_len-2);
4430 else
4431 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start, This->host_len);
4433 hres = S_OK;
4434 } else {
4435 *pbstrProperty = SysAllocStringLen(NULL, 0);
4436 hres = S_FALSE;
4439 if(!(*pbstrProperty))
4440 hres = E_OUTOFMEMORY;
4442 break;
4443 case Uri_PROPERTY_PASSWORD:
4444 if(This->userinfo_split > -1) {
4445 *pbstrProperty = SysAllocStringLen(
4446 This->canon_uri+This->userinfo_start+This->userinfo_split+1,
4447 This->userinfo_len-This->userinfo_split-1);
4448 hres = S_OK;
4449 } else {
4450 *pbstrProperty = SysAllocStringLen(NULL, 0);
4451 hres = S_FALSE;
4454 if(!(*pbstrProperty))
4455 return E_OUTOFMEMORY;
4457 break;
4458 case Uri_PROPERTY_PATH:
4459 if(This->path_start > -1) {
4460 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start, This->path_len);
4461 hres = S_OK;
4462 } else {
4463 *pbstrProperty = SysAllocStringLen(NULL, 0);
4464 hres = S_FALSE;
4467 if(!(*pbstrProperty))
4468 hres = E_OUTOFMEMORY;
4470 break;
4471 case Uri_PROPERTY_PATH_AND_QUERY:
4472 if(This->path_start > -1) {
4473 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start, This->path_len+This->query_len);
4474 hres = S_OK;
4475 } else if(This->query_start > -1) {
4476 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->query_start, This->query_len);
4477 hres = S_OK;
4478 } else {
4479 *pbstrProperty = SysAllocStringLen(NULL, 0);
4480 hres = S_FALSE;
4483 if(!(*pbstrProperty))
4484 hres = E_OUTOFMEMORY;
4486 break;
4487 case Uri_PROPERTY_QUERY:
4488 if(This->query_start > -1) {
4489 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->query_start, This->query_len);
4490 hres = S_OK;
4491 } else {
4492 *pbstrProperty = SysAllocStringLen(NULL, 0);
4493 hres = S_FALSE;
4496 if(!(*pbstrProperty))
4497 hres = E_OUTOFMEMORY;
4499 break;
4500 case Uri_PROPERTY_RAW_URI:
4501 *pbstrProperty = SysAllocString(This->raw_uri);
4502 if(!(*pbstrProperty))
4503 hres = E_OUTOFMEMORY;
4504 else
4505 hres = S_OK;
4506 break;
4507 case Uri_PROPERTY_SCHEME_NAME:
4508 if(This->scheme_start > -1) {
4509 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->scheme_start, This->scheme_len);
4510 hres = S_OK;
4511 } else {
4512 *pbstrProperty = SysAllocStringLen(NULL, 0);
4513 hres = S_FALSE;
4516 if(!(*pbstrProperty))
4517 hres = E_OUTOFMEMORY;
4519 break;
4520 case Uri_PROPERTY_USER_INFO:
4521 if(This->userinfo_start > -1) {
4522 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->userinfo_start, This->userinfo_len);
4523 hres = S_OK;
4524 } else {
4525 *pbstrProperty = SysAllocStringLen(NULL, 0);
4526 hres = S_FALSE;
4529 if(!(*pbstrProperty))
4530 hres = E_OUTOFMEMORY;
4532 break;
4533 case Uri_PROPERTY_USER_NAME:
4534 if(This->userinfo_start > -1 && This->userinfo_split != 0) {
4535 /* If userinfo_split is set, that means a password exists
4536 * so the username is only from userinfo_start to userinfo_split.
4538 if(This->userinfo_split > -1) {
4539 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_split);
4540 hres = S_OK;
4541 } else {
4542 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_len);
4543 hres = S_OK;
4545 } else {
4546 *pbstrProperty = SysAllocStringLen(NULL, 0);
4547 hres = S_FALSE;
4550 if(!(*pbstrProperty))
4551 return E_OUTOFMEMORY;
4553 break;
4554 default:
4555 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
4556 hres = E_NOTIMPL;
4559 return hres;
4562 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
4564 Uri *This = impl_from_IUri(iface);
4565 HRESULT hres;
4566 TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pcchProperty, dwFlags);
4568 if(!This->create_flags)
4569 return E_UNEXPECTED;
4570 if(!pcchProperty)
4571 return E_INVALIDARG;
4573 /* Can only return a length for a property if it's a string. */
4574 if(uriProp > Uri_PROPERTY_STRING_LAST)
4575 return E_INVALIDARG;
4577 /* Don't have support for flags yet. */
4578 if(dwFlags) {
4579 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4580 return E_NOTIMPL;
4583 switch(uriProp) {
4584 case Uri_PROPERTY_ABSOLUTE_URI:
4585 if(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI) {
4586 *pcchProperty = 0;
4587 hres = S_FALSE;
4588 } else {
4589 if(This->scheme_type != URL_SCHEME_UNKNOWN) {
4590 if(This->userinfo_start > -1 && This->userinfo_len == 0)
4591 /* Don't include the '@' in the length. */
4592 *pcchProperty = This->canon_len-1;
4593 else if(This->userinfo_start > -1 && This->userinfo_len == 1 &&
4594 This->userinfo_split == 0)
4595 /* Don't include the ":@" in the length. */
4596 *pcchProperty = This->canon_len-2;
4597 else
4598 *pcchProperty = This->canon_len;
4599 } else
4600 *pcchProperty = This->canon_len;
4602 hres = S_OK;
4605 break;
4606 case Uri_PROPERTY_AUTHORITY:
4607 if(This->port_offset > -1 &&
4608 This->display_modifiers & URI_DISPLAY_NO_DEFAULT_PORT_AUTH &&
4609 is_default_port(This->scheme_type, This->port))
4610 /* Only count up until the port in the authority. */
4611 *pcchProperty = This->port_offset;
4612 else
4613 *pcchProperty = This->authority_len;
4614 hres = (This->authority_start > -1) ? S_OK : S_FALSE;
4615 break;
4616 case Uri_PROPERTY_DISPLAY_URI:
4617 if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1)
4618 *pcchProperty = This->canon_len-This->userinfo_len-1;
4619 else
4620 *pcchProperty = This->canon_len;
4622 hres = S_OK;
4623 break;
4624 case Uri_PROPERTY_DOMAIN:
4625 if(This->domain_offset > -1)
4626 *pcchProperty = This->host_len - This->domain_offset;
4627 else
4628 *pcchProperty = 0;
4630 hres = (This->domain_offset > -1) ? S_OK : S_FALSE;
4631 break;
4632 case Uri_PROPERTY_EXTENSION:
4633 if(This->extension_offset > -1) {
4634 *pcchProperty = This->path_len - This->extension_offset;
4635 hres = S_OK;
4636 } else {
4637 *pcchProperty = 0;
4638 hres = S_FALSE;
4641 break;
4642 case Uri_PROPERTY_FRAGMENT:
4643 *pcchProperty = This->fragment_len;
4644 hres = (This->fragment_start > -1) ? S_OK : S_FALSE;
4645 break;
4646 case Uri_PROPERTY_HOST:
4647 *pcchProperty = This->host_len;
4649 /* '[' and ']' aren't included in the length. */
4650 if(This->host_type == Uri_HOST_IPV6)
4651 *pcchProperty -= 2;
4653 hres = (This->host_start > -1) ? S_OK : S_FALSE;
4654 break;
4655 case Uri_PROPERTY_PASSWORD:
4656 *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_len-This->userinfo_split-1 : 0;
4657 hres = (This->userinfo_split > -1) ? S_OK : S_FALSE;
4658 break;
4659 case Uri_PROPERTY_PATH:
4660 *pcchProperty = This->path_len;
4661 hres = (This->path_start > -1) ? S_OK : S_FALSE;
4662 break;
4663 case Uri_PROPERTY_PATH_AND_QUERY:
4664 *pcchProperty = This->path_len+This->query_len;
4665 hres = (This->path_start > -1 || This->query_start > -1) ? S_OK : S_FALSE;
4666 break;
4667 case Uri_PROPERTY_QUERY:
4668 *pcchProperty = This->query_len;
4669 hres = (This->query_start > -1) ? S_OK : S_FALSE;
4670 break;
4671 case Uri_PROPERTY_RAW_URI:
4672 *pcchProperty = SysStringLen(This->raw_uri);
4673 hres = S_OK;
4674 break;
4675 case Uri_PROPERTY_SCHEME_NAME:
4676 *pcchProperty = This->scheme_len;
4677 hres = (This->scheme_start > -1) ? S_OK : S_FALSE;
4678 break;
4679 case Uri_PROPERTY_USER_INFO:
4680 *pcchProperty = This->userinfo_len;
4681 hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
4682 break;
4683 case Uri_PROPERTY_USER_NAME:
4684 *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_split : This->userinfo_len;
4685 if(This->userinfo_split == 0)
4686 hres = S_FALSE;
4687 else
4688 hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
4689 break;
4690 default:
4691 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4692 hres = E_NOTIMPL;
4695 return hres;
4698 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
4700 Uri *This = impl_from_IUri(iface);
4701 HRESULT hres;
4703 TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pcchProperty, dwFlags);
4705 if(!This->create_flags)
4706 return E_UNEXPECTED;
4707 if(!pcchProperty)
4708 return E_INVALIDARG;
4710 /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
4711 * From what I can tell, instead of checking which URLZONE the URI belongs to it
4712 * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
4713 * function.
4715 if(uriProp == Uri_PROPERTY_ZONE) {
4716 *pcchProperty = URLZONE_INVALID;
4717 return E_NOTIMPL;
4720 if(uriProp < Uri_PROPERTY_DWORD_START) {
4721 *pcchProperty = 0;
4722 return E_INVALIDARG;
4725 switch(uriProp) {
4726 case Uri_PROPERTY_HOST_TYPE:
4727 *pcchProperty = This->host_type;
4728 hres = S_OK;
4729 break;
4730 case Uri_PROPERTY_PORT:
4731 if(!This->has_port) {
4732 *pcchProperty = 0;
4733 hres = S_FALSE;
4734 } else {
4735 *pcchProperty = This->port;
4736 hres = S_OK;
4739 break;
4740 case Uri_PROPERTY_SCHEME:
4741 *pcchProperty = This->scheme_type;
4742 hres = S_OK;
4743 break;
4744 default:
4745 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4746 hres = E_NOTIMPL;
4749 return hres;
4752 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
4754 Uri *This = impl_from_IUri(iface);
4756 TRACE("(%p %s)->(%d %p)\n", This, debugstr_w(This->canon_uri), uriProp, pfHasProperty);
4758 if(!pfHasProperty)
4759 return E_INVALIDARG;
4761 switch(uriProp) {
4762 case Uri_PROPERTY_ABSOLUTE_URI:
4763 *pfHasProperty = !(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI);
4764 break;
4765 case Uri_PROPERTY_AUTHORITY:
4766 *pfHasProperty = This->authority_start > -1;
4767 break;
4768 case Uri_PROPERTY_DISPLAY_URI:
4769 *pfHasProperty = TRUE;
4770 break;
4771 case Uri_PROPERTY_DOMAIN:
4772 *pfHasProperty = This->domain_offset > -1;
4773 break;
4774 case Uri_PROPERTY_EXTENSION:
4775 *pfHasProperty = This->extension_offset > -1;
4776 break;
4777 case Uri_PROPERTY_FRAGMENT:
4778 *pfHasProperty = This->fragment_start > -1;
4779 break;
4780 case Uri_PROPERTY_HOST:
4781 *pfHasProperty = This->host_start > -1;
4782 break;
4783 case Uri_PROPERTY_PASSWORD:
4784 *pfHasProperty = This->userinfo_split > -1;
4785 break;
4786 case Uri_PROPERTY_PATH:
4787 *pfHasProperty = This->path_start > -1;
4788 break;
4789 case Uri_PROPERTY_PATH_AND_QUERY:
4790 *pfHasProperty = (This->path_start > -1 || This->query_start > -1);
4791 break;
4792 case Uri_PROPERTY_QUERY:
4793 *pfHasProperty = This->query_start > -1;
4794 break;
4795 case Uri_PROPERTY_RAW_URI:
4796 *pfHasProperty = TRUE;
4797 break;
4798 case Uri_PROPERTY_SCHEME_NAME:
4799 *pfHasProperty = This->scheme_start > -1;
4800 break;
4801 case Uri_PROPERTY_USER_INFO:
4802 *pfHasProperty = This->userinfo_start > -1;
4803 break;
4804 case Uri_PROPERTY_USER_NAME:
4805 if(This->userinfo_split == 0)
4806 *pfHasProperty = FALSE;
4807 else
4808 *pfHasProperty = This->userinfo_start > -1;
4809 break;
4810 case Uri_PROPERTY_HOST_TYPE:
4811 *pfHasProperty = TRUE;
4812 break;
4813 case Uri_PROPERTY_PORT:
4814 *pfHasProperty = This->has_port;
4815 break;
4816 case Uri_PROPERTY_SCHEME:
4817 *pfHasProperty = TRUE;
4818 break;
4819 case Uri_PROPERTY_ZONE:
4820 *pfHasProperty = FALSE;
4821 break;
4822 default:
4823 FIXME("(%p)->(%d %p): Unsupported property type.\n", This, uriProp, pfHasProperty);
4824 return E_NOTIMPL;
4827 return S_OK;
4830 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
4832 TRACE("(%p)->(%p)\n", iface, pstrAbsoluteUri);
4833 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_ABSOLUTE_URI, pstrAbsoluteUri, 0);
4836 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
4838 TRACE("(%p)->(%p)\n", iface, pstrAuthority);
4839 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_AUTHORITY, pstrAuthority, 0);
4842 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
4844 TRACE("(%p)->(%p)\n", iface, pstrDisplayUri);
4845 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_DISPLAY_URI, pstrDisplayUri, 0);
4848 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
4850 TRACE("(%p)->(%p)\n", iface, pstrDomain);
4851 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_DOMAIN, pstrDomain, 0);
4854 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
4856 TRACE("(%p)->(%p)\n", iface, pstrExtension);
4857 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_EXTENSION, pstrExtension, 0);
4860 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
4862 TRACE("(%p)->(%p)\n", iface, pstrFragment);
4863 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_FRAGMENT, pstrFragment, 0);
4866 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
4868 TRACE("(%p)->(%p)\n", iface, pstrHost);
4869 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_HOST, pstrHost, 0);
4872 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
4874 TRACE("(%p)->(%p)\n", iface, pstrPassword);
4875 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PASSWORD, pstrPassword, 0);
4878 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
4880 TRACE("(%p)->(%p)\n", iface, pstrPath);
4881 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PATH, pstrPath, 0);
4884 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
4886 TRACE("(%p)->(%p)\n", iface, pstrPathAndQuery);
4887 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PATH_AND_QUERY, pstrPathAndQuery, 0);
4890 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
4892 TRACE("(%p)->(%p)\n", iface, pstrQuery);
4893 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_QUERY, pstrQuery, 0);
4896 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
4898 TRACE("(%p)->(%p)\n", iface, pstrRawUri);
4899 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
4902 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
4904 TRACE("(%p)->(%p)\n", iface, pstrSchemeName);
4905 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_SCHEME_NAME, pstrSchemeName, 0);
4908 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
4910 TRACE("(%p)->(%p)\n", iface, pstrUserInfo);
4911 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_INFO, pstrUserInfo, 0);
4914 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
4916 TRACE("(%p)->(%p)\n", iface, pstrUserName);
4917 return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_NAME, pstrUserName, 0);
4920 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
4922 TRACE("(%p)->(%p)\n", iface, pdwHostType);
4923 return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_HOST_TYPE, pdwHostType, 0);
4926 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
4928 TRACE("(%p)->(%p)\n", iface, pdwPort);
4929 return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_PORT, pdwPort, 0);
4932 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
4934 TRACE("(%p)->(%p)\n", iface, pdwScheme);
4935 return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_SCHEME, pdwScheme, 0);
4938 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
4940 TRACE("(%p)->(%p)\n", iface, pdwZone);
4941 return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_ZONE,pdwZone, 0);
4944 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
4946 Uri *This = impl_from_IUri(iface);
4947 TRACE("(%p %s)->(%p)\n", This, debugstr_w(This->canon_uri), pdwProperties);
4949 if(!This->create_flags)
4950 return E_UNEXPECTED;
4951 if(!pdwProperties)
4952 return E_INVALIDARG;
4954 /* All URIs have these. */
4955 *pdwProperties = Uri_HAS_DISPLAY_URI|Uri_HAS_RAW_URI|Uri_HAS_SCHEME|Uri_HAS_HOST_TYPE;
4957 if(!(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI))
4958 *pdwProperties |= Uri_HAS_ABSOLUTE_URI;
4960 if(This->scheme_start > -1)
4961 *pdwProperties |= Uri_HAS_SCHEME_NAME;
4963 if(This->authority_start > -1) {
4964 *pdwProperties |= Uri_HAS_AUTHORITY;
4965 if(This->userinfo_start > -1) {
4966 *pdwProperties |= Uri_HAS_USER_INFO;
4967 if(This->userinfo_split != 0)
4968 *pdwProperties |= Uri_HAS_USER_NAME;
4970 if(This->userinfo_split > -1)
4971 *pdwProperties |= Uri_HAS_PASSWORD;
4972 if(This->host_start > -1)
4973 *pdwProperties |= Uri_HAS_HOST;
4974 if(This->domain_offset > -1)
4975 *pdwProperties |= Uri_HAS_DOMAIN;
4978 if(This->has_port)
4979 *pdwProperties |= Uri_HAS_PORT;
4980 if(This->path_start > -1)
4981 *pdwProperties |= Uri_HAS_PATH|Uri_HAS_PATH_AND_QUERY;
4982 if(This->query_start > -1)
4983 *pdwProperties |= Uri_HAS_QUERY|Uri_HAS_PATH_AND_QUERY;
4985 if(This->extension_offset > -1)
4986 *pdwProperties |= Uri_HAS_EXTENSION;
4988 if(This->fragment_start > -1)
4989 *pdwProperties |= Uri_HAS_FRAGMENT;
4991 return S_OK;
4994 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
4996 Uri *This = impl_from_IUri(iface);
4997 Uri *other;
4999 TRACE("(%p %s)->(%p %p)\n", This, debugstr_w(This->canon_uri), pUri, pfEqual);
5001 if(!This->create_flags)
5002 return E_UNEXPECTED;
5003 if(!pfEqual)
5004 return E_POINTER;
5006 if(!pUri) {
5007 *pfEqual = FALSE;
5009 /* For some reason Windows returns S_OK here... */
5010 return S_OK;
5013 /* Try to convert it to a Uri (allows for a more simple comparison). */
5014 if(!(other = get_uri_obj(pUri))) {
5015 FIXME("(%p)->(%p %p) No support for unknown IUri's yet.\n", iface, pUri, pfEqual);
5016 return E_NOTIMPL;
5019 TRACE("comparing to %s\n", debugstr_w(other->canon_uri));
5020 return compare_uris(This, other, pfEqual);
5023 static const IUriVtbl UriVtbl = {
5024 Uri_QueryInterface,
5025 Uri_AddRef,
5026 Uri_Release,
5027 Uri_GetPropertyBSTR,
5028 Uri_GetPropertyLength,
5029 Uri_GetPropertyDWORD,
5030 Uri_HasProperty,
5031 Uri_GetAbsoluteUri,
5032 Uri_GetAuthority,
5033 Uri_GetDisplayUri,
5034 Uri_GetDomain,
5035 Uri_GetExtension,
5036 Uri_GetFragment,
5037 Uri_GetHost,
5038 Uri_GetPassword,
5039 Uri_GetPath,
5040 Uri_GetPathAndQuery,
5041 Uri_GetQuery,
5042 Uri_GetRawUri,
5043 Uri_GetSchemeName,
5044 Uri_GetUserInfo,
5045 Uri_GetUserName,
5046 Uri_GetHostType,
5047 Uri_GetPort,
5048 Uri_GetScheme,
5049 Uri_GetZone,
5050 Uri_GetProperties,
5051 Uri_IsEqual
5054 static inline Uri* impl_from_IUriBuilderFactory(IUriBuilderFactory *iface)
5056 return CONTAINING_RECORD(iface, Uri, IUriBuilderFactory_iface);
5059 static HRESULT WINAPI UriBuilderFactory_QueryInterface(IUriBuilderFactory *iface, REFIID riid, void **ppv)
5061 Uri *This = impl_from_IUriBuilderFactory(iface);
5062 return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5065 static ULONG WINAPI UriBuilderFactory_AddRef(IUriBuilderFactory *iface)
5067 Uri *This = impl_from_IUriBuilderFactory(iface);
5068 return IUri_AddRef(&This->IUri_iface);
5071 static ULONG WINAPI UriBuilderFactory_Release(IUriBuilderFactory *iface)
5073 Uri *This = impl_from_IUriBuilderFactory(iface);
5074 return IUri_Release(&This->IUri_iface);
5077 static HRESULT WINAPI UriBuilderFactory_CreateIUriBuilder(IUriBuilderFactory *iface,
5078 DWORD dwFlags,
5079 DWORD_PTR dwReserved,
5080 IUriBuilder **ppIUriBuilder)
5082 Uri *This = impl_from_IUriBuilderFactory(iface);
5083 TRACE("(%p)->(%08x %08x %p)\n", This, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
5085 if(!ppIUriBuilder)
5086 return E_POINTER;
5088 if(dwFlags || dwReserved) {
5089 *ppIUriBuilder = NULL;
5090 return E_INVALIDARG;
5093 return CreateIUriBuilder(NULL, 0, 0, ppIUriBuilder);
5096 static HRESULT WINAPI UriBuilderFactory_CreateInitializedIUriBuilder(IUriBuilderFactory *iface,
5097 DWORD dwFlags,
5098 DWORD_PTR dwReserved,
5099 IUriBuilder **ppIUriBuilder)
5101 Uri *This = impl_from_IUriBuilderFactory(iface);
5102 TRACE("(%p)->(%08x %08x %p)\n", This, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
5104 if(!ppIUriBuilder)
5105 return E_POINTER;
5107 if(dwFlags || dwReserved) {
5108 *ppIUriBuilder = NULL;
5109 return E_INVALIDARG;
5112 return CreateIUriBuilder(&This->IUri_iface, 0, 0, ppIUriBuilder);
5115 static const IUriBuilderFactoryVtbl UriBuilderFactoryVtbl = {
5116 UriBuilderFactory_QueryInterface,
5117 UriBuilderFactory_AddRef,
5118 UriBuilderFactory_Release,
5119 UriBuilderFactory_CreateIUriBuilder,
5120 UriBuilderFactory_CreateInitializedIUriBuilder
5123 static inline Uri* impl_from_IPersistStream(IPersistStream *iface)
5125 return CONTAINING_RECORD(iface, Uri, IPersistStream_iface);
5128 static HRESULT WINAPI PersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ppvObject)
5130 Uri *This = impl_from_IPersistStream(iface);
5131 return IUri_QueryInterface(&This->IUri_iface, riid, ppvObject);
5134 static ULONG WINAPI PersistStream_AddRef(IPersistStream *iface)
5136 Uri *This = impl_from_IPersistStream(iface);
5137 return IUri_AddRef(&This->IUri_iface);
5140 static ULONG WINAPI PersistStream_Release(IPersistStream *iface)
5142 Uri *This = impl_from_IPersistStream(iface);
5143 return IUri_Release(&This->IUri_iface);
5146 static HRESULT WINAPI PersistStream_GetClassID(IPersistStream *iface, CLSID *pClassID)
5148 Uri *This = impl_from_IPersistStream(iface);
5149 TRACE("(%p)->(%p)\n", This, pClassID);
5151 if(!pClassID)
5152 return E_INVALIDARG;
5154 *pClassID = CLSID_CUri;
5155 return S_OK;
5158 static HRESULT WINAPI PersistStream_IsDirty(IPersistStream *iface)
5160 Uri *This = impl_from_IPersistStream(iface);
5161 TRACE("(%p)\n", This);
5162 return S_FALSE;
5165 struct persist_uri {
5166 DWORD size;
5167 DWORD unk1[2];
5168 DWORD create_flags;
5169 DWORD unk2[3];
5170 DWORD fields_no;
5171 BYTE data[1];
5174 static HRESULT WINAPI PersistStream_Load(IPersistStream *iface, IStream *pStm)
5176 Uri *This = impl_from_IPersistStream(iface);
5177 struct persist_uri *data;
5178 parse_data parse;
5179 DWORD size;
5180 HRESULT hr;
5182 TRACE("(%p)->(%p)\n", This, pStm);
5184 if(This->create_flags)
5185 return E_UNEXPECTED;
5186 if(!pStm)
5187 return E_INVALIDARG;
5189 hr = IStream_Read(pStm, &size, sizeof(DWORD), NULL);
5190 if(FAILED(hr))
5191 return hr;
5192 data = heap_alloc(size);
5193 if(!data)
5194 return E_OUTOFMEMORY;
5195 hr = IStream_Read(pStm, data->unk1, size-sizeof(DWORD)-2, NULL);
5196 if(FAILED(hr)) {
5197 heap_free(data);
5198 return hr;
5201 if(size < sizeof(struct persist_uri)) {
5202 heap_free(data);
5203 return S_OK;
5206 if(*(DWORD*)data->data != Uri_PROPERTY_RAW_URI) {
5207 heap_free(data);
5208 ERR("Can't find raw_uri\n");
5209 return E_UNEXPECTED;
5212 This->raw_uri = SysAllocString((WCHAR*)(data->data+sizeof(DWORD)*2));
5213 if(!This->raw_uri) {
5214 heap_free(data);
5215 return E_OUTOFMEMORY;
5217 This->create_flags = data->create_flags;
5218 heap_free(data);
5219 TRACE("%x %s\n", This->create_flags, debugstr_w(This->raw_uri));
5221 memset(&parse, 0, sizeof(parse_data));
5222 parse.uri = This->raw_uri;
5223 if(!parse_uri(&parse, This->create_flags)) {
5224 SysFreeString(This->raw_uri);
5225 This->create_flags = 0;
5226 return E_UNEXPECTED;
5229 hr = canonicalize_uri(&parse, This, This->create_flags);
5230 if(FAILED(hr)) {
5231 SysFreeString(This->raw_uri);
5232 This->create_flags = 0;
5233 return hr;
5236 return S_OK;
5239 static inline BYTE* persist_stream_add_strprop(Uri *This, BYTE *p, DWORD type, DWORD len, WCHAR *data)
5241 len *= sizeof(WCHAR);
5242 *(DWORD*)p = type;
5243 p += sizeof(DWORD);
5244 *(DWORD*)p = len+sizeof(WCHAR);
5245 p += sizeof(DWORD);
5246 memcpy(p, data, len);
5247 p += len;
5248 *(WCHAR*)p = 0;
5249 return p+sizeof(WCHAR);
5252 static inline void persist_stream_save(Uri *This, IStream *pStm, BOOL marshal, struct persist_uri *data)
5254 BYTE *p = NULL;
5256 data->create_flags = This->create_flags;
5258 if(This->create_flags) {
5259 data->fields_no = 1;
5260 p = persist_stream_add_strprop(This, data->data, Uri_PROPERTY_RAW_URI,
5261 SysStringLen(This->raw_uri), This->raw_uri);
5263 if(This->scheme_type!=URL_SCHEME_HTTP && This->scheme_type!=URL_SCHEME_HTTPS
5264 && This->scheme_type!=URL_SCHEME_FTP)
5265 return;
5267 if(This->fragment_len) {
5268 data->fields_no++;
5269 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_FRAGMENT,
5270 This->fragment_len, This->canon_uri+This->fragment_start);
5273 if(This->host_len) {
5274 data->fields_no++;
5275 if(This->host_type == Uri_HOST_IPV6)
5276 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_HOST,
5277 This->host_len-2, This->canon_uri+This->host_start+1);
5278 else
5279 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_HOST,
5280 This->host_len, This->canon_uri+This->host_start);
5283 if(This->userinfo_split > -1) {
5284 data->fields_no++;
5285 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PASSWORD,
5286 This->userinfo_len-This->userinfo_split-1,
5287 This->canon_uri+This->userinfo_start+This->userinfo_split+1);
5290 if(This->path_len) {
5291 data->fields_no++;
5292 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PATH,
5293 This->path_len, This->canon_uri+This->path_start);
5294 } else if(marshal) {
5295 WCHAR no_path = '/';
5296 data->fields_no++;
5297 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PATH, 1, &no_path);
5300 if(This->has_port) {
5301 data->fields_no++;
5302 *(DWORD*)p = Uri_PROPERTY_PORT;
5303 p += sizeof(DWORD);
5304 *(DWORD*)p = sizeof(DWORD);
5305 p += sizeof(DWORD);
5306 *(DWORD*)p = This->port;
5307 p += sizeof(DWORD);
5310 if(This->query_len) {
5311 data->fields_no++;
5312 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_QUERY,
5313 This->query_len, This->canon_uri+This->query_start);
5316 if(This->scheme_len) {
5317 data->fields_no++;
5318 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_SCHEME_NAME,
5319 This->scheme_len, This->canon_uri+This->scheme_start);
5322 if(This->userinfo_start>-1 && This->userinfo_split!=0) {
5323 data->fields_no++;
5324 if(This->userinfo_split > -1)
5325 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_USER_NAME,
5326 This->userinfo_split, This->canon_uri+This->userinfo_start);
5327 else
5328 p = persist_stream_add_strprop(This, p, Uri_PROPERTY_USER_NAME,
5329 This->userinfo_len, This->canon_uri+This->userinfo_start);
5333 static HRESULT WINAPI PersistStream_Save(IPersistStream *iface, IStream *pStm, BOOL fClearDirty)
5335 Uri *This = impl_from_IPersistStream(iface);
5336 struct persist_uri *data;
5337 ULARGE_INTEGER size;
5338 HRESULT hres;
5340 TRACE("(%p)->(%p %x)\n", This, pStm, fClearDirty);
5342 if(!pStm)
5343 return E_INVALIDARG;
5345 hres = IPersistStream_GetSizeMax(&This->IPersistStream_iface, &size);
5346 if(FAILED(hres))
5347 return hres;
5349 data = heap_alloc_zero(size.u.LowPart);
5350 if(!data)
5351 return E_OUTOFMEMORY;
5352 data->size = size.u.LowPart;
5353 persist_stream_save(This, pStm, FALSE, data);
5355 hres = IStream_Write(pStm, data, data->size-2, NULL);
5356 heap_free(data);
5357 return hres;
5360 static HRESULT WINAPI PersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *pcbSize)
5362 Uri *This = impl_from_IPersistStream(iface);
5363 TRACE("(%p)->(%p)\n", This, pcbSize);
5365 if(!pcbSize)
5366 return E_INVALIDARG;
5368 pcbSize->u.LowPart = 2+sizeof(struct persist_uri);
5369 pcbSize->u.HighPart = 0;
5370 if(This->create_flags)
5371 pcbSize->u.LowPart += (SysStringLen(This->raw_uri)+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5372 else /* there's no place for fields no */
5373 pcbSize->u.LowPart -= sizeof(DWORD);
5374 if(This->scheme_type!=URL_SCHEME_HTTP && This->scheme_type!=URL_SCHEME_HTTPS
5375 && This->scheme_type!=URL_SCHEME_FTP)
5376 return S_OK;
5378 if(This->fragment_len)
5379 pcbSize->u.LowPart += (This->fragment_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5380 if(This->host_len) {
5381 if(This->host_type == Uri_HOST_IPV6)
5382 pcbSize->u.LowPart += (This->host_len-1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5383 else
5384 pcbSize->u.LowPart += (This->host_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5386 if(This->userinfo_split > -1)
5387 pcbSize->u.LowPart += (This->userinfo_len-This->userinfo_split)*sizeof(WCHAR) + 2*sizeof(DWORD);
5388 if(This->path_len)
5389 pcbSize->u.LowPart += (This->path_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5390 if(This->has_port)
5391 pcbSize->u.LowPart += 3*sizeof(DWORD);
5392 if(This->query_len)
5393 pcbSize->u.LowPart += (This->query_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5394 if(This->scheme_len)
5395 pcbSize->u.LowPart += (This->scheme_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5396 if(This->userinfo_start>-1 && This->userinfo_split!=0) {
5397 if(This->userinfo_split > -1)
5398 pcbSize->u.LowPart += (This->userinfo_split+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5399 else
5400 pcbSize->u.LowPart += (This->userinfo_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5402 return S_OK;
5405 static const IPersistStreamVtbl PersistStreamVtbl = {
5406 PersistStream_QueryInterface,
5407 PersistStream_AddRef,
5408 PersistStream_Release,
5409 PersistStream_GetClassID,
5410 PersistStream_IsDirty,
5411 PersistStream_Load,
5412 PersistStream_Save,
5413 PersistStream_GetSizeMax
5416 static inline Uri* impl_from_IMarshal(IMarshal *iface)
5418 return CONTAINING_RECORD(iface, Uri, IMarshal_iface);
5421 static HRESULT WINAPI Marshal_QueryInterface(IMarshal *iface, REFIID riid, void **ppvObject)
5423 Uri *This = impl_from_IMarshal(iface);
5424 return IUri_QueryInterface(&This->IUri_iface, riid, ppvObject);
5427 static ULONG WINAPI Marshal_AddRef(IMarshal *iface)
5429 Uri *This = impl_from_IMarshal(iface);
5430 return IUri_AddRef(&This->IUri_iface);
5433 static ULONG WINAPI Marshal_Release(IMarshal *iface)
5435 Uri *This = impl_from_IMarshal(iface);
5436 return IUri_Release(&This->IUri_iface);
5439 static HRESULT WINAPI Marshal_GetUnmarshalClass(IMarshal *iface, REFIID riid, void *pv,
5440 DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid)
5442 Uri *This = impl_from_IMarshal(iface);
5443 TRACE("(%p)->(%s %p %x %p %x %p)\n", This, debugstr_guid(riid), pv,
5444 dwDestContext, pvDestContext, mshlflags, pCid);
5446 if(!pCid || (dwDestContext!=MSHCTX_LOCAL && dwDestContext!=MSHCTX_NOSHAREDMEM
5447 && dwDestContext!=MSHCTX_INPROC))
5448 return E_INVALIDARG;
5450 *pCid = CLSID_CUri;
5451 return S_OK;
5454 struct inproc_marshal_uri {
5455 DWORD size;
5456 DWORD mshlflags;
5457 DWORD unk[4]; /* process identifier? */
5458 Uri *uri;
5461 static HRESULT WINAPI Marshal_GetMarshalSizeMax(IMarshal *iface, REFIID riid, void *pv,
5462 DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize)
5464 Uri *This = impl_from_IMarshal(iface);
5465 ULARGE_INTEGER size;
5466 HRESULT hres;
5467 TRACE("(%p)->(%s %p %x %p %x %p)\n", This, debugstr_guid(riid), pv,
5468 dwDestContext, pvDestContext, mshlflags, pSize);
5470 if(!pSize || (dwDestContext!=MSHCTX_LOCAL && dwDestContext!=MSHCTX_NOSHAREDMEM
5471 && dwDestContext!=MSHCTX_INPROC))
5472 return E_INVALIDARG;
5474 if(dwDestContext == MSHCTX_INPROC) {
5475 *pSize = sizeof(struct inproc_marshal_uri);
5476 return S_OK;
5479 hres = IPersistStream_GetSizeMax(&This->IPersistStream_iface, &size);
5480 if(FAILED(hres))
5481 return hres;
5482 if(!This->path_len && (This->scheme_type==URL_SCHEME_HTTP
5483 || This->scheme_type==URL_SCHEME_HTTPS
5484 || This->scheme_type==URL_SCHEME_FTP))
5485 size.u.LowPart += 3*sizeof(DWORD);
5486 *pSize = size.u.LowPart+2*sizeof(DWORD);
5487 return S_OK;
5490 static HRESULT WINAPI Marshal_MarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid,
5491 void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags)
5493 Uri *This = impl_from_IMarshal(iface);
5494 DWORD *data;
5495 DWORD size;
5496 HRESULT hres;
5498 TRACE("(%p)->(%p %s %p %x %p %x)\n", This, pStm, debugstr_guid(riid), pv,
5499 dwDestContext, pvDestContext, mshlflags);
5501 if(!pStm || mshlflags!=MSHLFLAGS_NORMAL || (dwDestContext!=MSHCTX_LOCAL
5502 && dwDestContext!=MSHCTX_NOSHAREDMEM && dwDestContext!=MSHCTX_INPROC))
5503 return E_INVALIDARG;
5505 if(dwDestContext == MSHCTX_INPROC) {
5506 struct inproc_marshal_uri data;
5508 data.size = sizeof(data);
5509 data.mshlflags = MSHCTX_INPROC;
5510 data.unk[0] = 0;
5511 data.unk[1] = 0;
5512 data.unk[2] = 0;
5513 data.unk[3] = 0;
5514 data.uri = This;
5516 hres = IStream_Write(pStm, &data, data.size, NULL);
5517 if(FAILED(hres))
5518 return hres;
5520 IUri_AddRef(&This->IUri_iface);
5521 return S_OK;
5524 hres = IMarshal_GetMarshalSizeMax(iface, riid, pv, dwDestContext,
5525 pvDestContext, mshlflags, &size);
5526 if(FAILED(hres))
5527 return hres;
5529 data = heap_alloc_zero(size);
5530 if(!data)
5531 return E_OUTOFMEMORY;
5533 data[0] = size;
5534 data[1] = dwDestContext;
5535 data[2] = size-2*sizeof(DWORD);
5536 persist_stream_save(This, pStm, TRUE, (struct persist_uri*)(data+2));
5538 hres = IStream_Write(pStm, data, data[0]-2, NULL);
5539 heap_free(data);
5540 return hres;
5543 static HRESULT WINAPI Marshal_UnmarshalInterface(IMarshal *iface,
5544 IStream *pStm, REFIID riid, void **ppv)
5546 Uri *This = impl_from_IMarshal(iface);
5547 DWORD header[2];
5548 HRESULT hres;
5550 TRACE("(%p)->(%p %s %p)\n", This, pStm, debugstr_guid(riid), ppv);
5552 if(This->create_flags)
5553 return E_UNEXPECTED;
5554 if(!pStm || !riid || !ppv)
5555 return E_INVALIDARG;
5557 hres = IStream_Read(pStm, header, sizeof(header), NULL);
5558 if(FAILED(hres))
5559 return hres;
5561 if(header[1]!=MSHCTX_LOCAL && header[1]!=MSHCTX_NOSHAREDMEM
5562 && header[1]!=MSHCTX_INPROC)
5563 return E_UNEXPECTED;
5565 if(header[1] == MSHCTX_INPROC) {
5566 struct inproc_marshal_uri data;
5567 parse_data parse;
5569 hres = IStream_Read(pStm, data.unk, sizeof(data)-2*sizeof(DWORD), NULL);
5570 if(FAILED(hres))
5571 return hres;
5573 This->raw_uri = SysAllocString(data.uri->raw_uri);
5574 if(!This->raw_uri) {
5575 return E_OUTOFMEMORY;
5578 memset(&parse, 0, sizeof(parse_data));
5579 parse.uri = This->raw_uri;
5581 if(!parse_uri(&parse, data.uri->create_flags))
5582 return E_INVALIDARG;
5584 hres = canonicalize_uri(&parse, This, data.uri->create_flags);
5585 if(FAILED(hres))
5586 return hres;
5588 This->create_flags = data.uri->create_flags;
5589 IUri_Release(&data.uri->IUri_iface);
5591 return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5594 hres = IPersistStream_Load(&This->IPersistStream_iface, pStm);
5595 if(FAILED(hres))
5596 return hres;
5598 return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5601 static HRESULT WINAPI Marshal_ReleaseMarshalData(IMarshal *iface, IStream *pStm)
5603 Uri *This = impl_from_IMarshal(iface);
5604 LARGE_INTEGER off;
5605 DWORD header[2];
5606 HRESULT hres;
5608 TRACE("(%p)->(%p)\n", This, pStm);
5610 if(!pStm)
5611 return E_INVALIDARG;
5613 hres = IStream_Read(pStm, header, 2*sizeof(DWORD), NULL);
5614 if(FAILED(hres))
5615 return hres;
5617 if(header[1] == MSHCTX_INPROC) {
5618 struct inproc_marshal_uri data;
5620 hres = IStream_Read(pStm, data.unk, sizeof(data)-2*sizeof(DWORD), NULL);
5621 if(FAILED(hres))
5622 return hres;
5624 IUri_Release(&data.uri->IUri_iface);
5625 return S_OK;
5628 off.u.LowPart = header[0]-sizeof(header)-2;
5629 off.u.HighPart = 0;
5630 return IStream_Seek(pStm, off, STREAM_SEEK_CUR, NULL);
5633 static HRESULT WINAPI Marshal_DisconnectObject(IMarshal *iface, DWORD dwReserved)
5635 Uri *This = impl_from_IMarshal(iface);
5636 TRACE("(%p)->(%x)\n", This, dwReserved);
5637 return S_OK;
5640 static const IMarshalVtbl MarshalVtbl = {
5641 Marshal_QueryInterface,
5642 Marshal_AddRef,
5643 Marshal_Release,
5644 Marshal_GetUnmarshalClass,
5645 Marshal_GetMarshalSizeMax,
5646 Marshal_MarshalInterface,
5647 Marshal_UnmarshalInterface,
5648 Marshal_ReleaseMarshalData,
5649 Marshal_DisconnectObject
5652 HRESULT Uri_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
5654 Uri *ret = heap_alloc_zero(sizeof(Uri));
5656 TRACE("(%p %p)\n", pUnkOuter, ppobj);
5658 *ppobj = ret;
5659 if(!ret)
5660 return E_OUTOFMEMORY;
5662 ret->IUri_iface.lpVtbl = &UriVtbl;
5663 ret->IUriBuilderFactory_iface.lpVtbl = &UriBuilderFactoryVtbl;
5664 ret->IPersistStream_iface.lpVtbl = &PersistStreamVtbl;
5665 ret->IMarshal_iface.lpVtbl = &MarshalVtbl;
5666 ret->ref = 1;
5668 *ppobj = &ret->IUri_iface;
5669 return S_OK;
5672 /***********************************************************************
5673 * CreateUri (urlmon.@)
5675 * Creates a new IUri object using the URI represented by pwzURI. This function
5676 * parses and validates the components of pwzURI and then canonicalizes the
5677 * parsed components.
5679 * PARAMS
5680 * pwzURI [I] The URI to parse, validate, and canonicalize.
5681 * dwFlags [I] Flags which can affect how the parsing/canonicalization is performed.
5682 * dwReserved [I] Reserved (not used).
5683 * ppURI [O] The resulting IUri after parsing/canonicalization occurs.
5685 * RETURNS
5686 * Success: Returns S_OK. ppURI contains the pointer to the newly allocated IUri.
5687 * Failure: E_INVALIDARG if there are invalid flag combinations in dwFlags, or an
5688 * invalid parameter, or pwzURI doesn't represent a valid URI.
5689 * E_OUTOFMEMORY if any memory allocation fails.
5691 * NOTES
5692 * Default flags:
5693 * Uri_CREATE_CANONICALIZE, Uri_CREATE_DECODE_EXTRA_INFO, Uri_CREATE_CRACK_UNKNOWN_SCHEMES,
5694 * Uri_CREATE_PRE_PROCESS_HTML_URI, Uri_CREATE_NO_IE_SETTINGS.
5696 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
5698 const DWORD supported_flags = Uri_CREATE_ALLOW_RELATIVE|Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME|
5699 Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME|Uri_CREATE_NO_CANONICALIZE|Uri_CREATE_CANONICALIZE|
5700 Uri_CREATE_DECODE_EXTRA_INFO|Uri_CREATE_NO_DECODE_EXTRA_INFO|Uri_CREATE_CRACK_UNKNOWN_SCHEMES|
5701 Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES|Uri_CREATE_PRE_PROCESS_HTML_URI|Uri_CREATE_NO_PRE_PROCESS_HTML_URI|
5702 Uri_CREATE_NO_IE_SETTINGS|Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS|Uri_CREATE_FILE_USE_DOS_PATH;
5703 Uri *ret;
5704 HRESULT hr;
5705 parse_data data;
5707 TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
5709 if(!ppURI)
5710 return E_INVALIDARG;
5712 if(!pwzURI) {
5713 *ppURI = NULL;
5714 return E_INVALIDARG;
5717 /* Check for invalid flags. */
5718 if(has_invalid_flag_combination(dwFlags)) {
5719 *ppURI = NULL;
5720 return E_INVALIDARG;
5723 /* Currently unsupported. */
5724 if(dwFlags & ~supported_flags)
5725 FIXME("Ignoring unsupported flag(s) %x\n", dwFlags & ~supported_flags);
5727 hr = Uri_Construct(NULL, (void**)&ret);
5728 if(FAILED(hr)) {
5729 *ppURI = NULL;
5730 return hr;
5733 /* Explicitly set the default flags if it doesn't cause a flag conflict. */
5734 apply_default_flags(&dwFlags);
5736 /* Pre process the URI, unless told otherwise. */
5737 if(!(dwFlags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI))
5738 ret->raw_uri = pre_process_uri(pwzURI);
5739 else
5740 ret->raw_uri = SysAllocString(pwzURI);
5742 if(!ret->raw_uri) {
5743 heap_free(ret);
5744 return E_OUTOFMEMORY;
5747 memset(&data, 0, sizeof(parse_data));
5748 data.uri = ret->raw_uri;
5750 /* Validate and parse the URI into its components. */
5751 if(!parse_uri(&data, dwFlags)) {
5752 /* Encountered an unsupported or invalid URI */
5753 IUri_Release(&ret->IUri_iface);
5754 *ppURI = NULL;
5755 return E_INVALIDARG;
5758 /* Canonicalize the URI. */
5759 hr = canonicalize_uri(&data, ret, dwFlags);
5760 if(FAILED(hr)) {
5761 IUri_Release(&ret->IUri_iface);
5762 *ppURI = NULL;
5763 return hr;
5766 ret->create_flags = dwFlags;
5768 *ppURI = &ret->IUri_iface;
5769 return S_OK;
5772 /***********************************************************************
5773 * CreateUriWithFragment (urlmon.@)
5775 * Creates a new IUri object. This is almost the same as CreateUri, expect that
5776 * it allows you to explicitly specify a fragment (pwzFragment) for pwzURI.
5778 * PARAMS
5779 * pwzURI [I] The URI to parse and perform canonicalization on.
5780 * pwzFragment [I] The explicit fragment string which should be added to pwzURI.
5781 * dwFlags [I] The flags which will be passed to CreateUri.
5782 * dwReserved [I] Reserved (not used).
5783 * ppURI [O] The resulting IUri after parsing/canonicalization.
5785 * RETURNS
5786 * Success: S_OK. ppURI contains the pointer to the newly allocated IUri.
5787 * Failure: E_INVALIDARG if pwzURI already contains a fragment and pwzFragment
5788 * isn't NULL. Will also return E_INVALIDARG for the same reasons as
5789 * CreateUri will. E_OUTOFMEMORY if any allocation fails.
5791 HRESULT WINAPI CreateUriWithFragment(LPCWSTR pwzURI, LPCWSTR pwzFragment, DWORD dwFlags,
5792 DWORD_PTR dwReserved, IUri **ppURI)
5794 HRESULT hres;
5795 TRACE("(%s %s %x %x %p)\n", debugstr_w(pwzURI), debugstr_w(pwzFragment), dwFlags, (DWORD)dwReserved, ppURI);
5797 if(!ppURI)
5798 return E_INVALIDARG;
5800 if(!pwzURI) {
5801 *ppURI = NULL;
5802 return E_INVALIDARG;
5805 /* Check if a fragment should be appended to the URI string. */
5806 if(pwzFragment) {
5807 WCHAR *uriW;
5808 DWORD uri_len, frag_len;
5809 BOOL add_pound;
5811 /* Check if the original URI already has a fragment component. */
5812 if(StrChrW(pwzURI, '#')) {
5813 *ppURI = NULL;
5814 return E_INVALIDARG;
5817 uri_len = lstrlenW(pwzURI);
5818 frag_len = lstrlenW(pwzFragment);
5820 /* If the fragment doesn't start with a '#', one will be added. */
5821 add_pound = *pwzFragment != '#';
5823 if(add_pound)
5824 uriW = heap_alloc((uri_len+frag_len+2)*sizeof(WCHAR));
5825 else
5826 uriW = heap_alloc((uri_len+frag_len+1)*sizeof(WCHAR));
5828 if(!uriW)
5829 return E_OUTOFMEMORY;
5831 memcpy(uriW, pwzURI, uri_len*sizeof(WCHAR));
5832 if(add_pound)
5833 uriW[uri_len++] = '#';
5834 memcpy(uriW+uri_len, pwzFragment, (frag_len+1)*sizeof(WCHAR));
5836 hres = CreateUri(uriW, dwFlags, 0, ppURI);
5838 heap_free(uriW);
5839 } else
5840 /* A fragment string wasn't specified, so just forward the call. */
5841 hres = CreateUri(pwzURI, dwFlags, 0, ppURI);
5843 return hres;
5846 static HRESULT build_uri(const UriBuilder *builder, IUri **uri, DWORD create_flags,
5847 DWORD use_orig_flags, DWORD encoding_mask)
5849 HRESULT hr;
5850 parse_data data;
5851 Uri *ret;
5853 if(!uri)
5854 return E_POINTER;
5856 if(encoding_mask && (!builder->uri || builder->modified_props)) {
5857 *uri = NULL;
5858 return E_NOTIMPL;
5861 /* Decide what flags should be used when creating the Uri. */
5862 if((use_orig_flags & UriBuilder_USE_ORIGINAL_FLAGS) && builder->uri)
5863 create_flags = builder->uri->create_flags;
5864 else {
5865 if(has_invalid_flag_combination(create_flags)) {
5866 *uri = NULL;
5867 return E_INVALIDARG;
5870 /* Set the default flags if they don't cause a conflict. */
5871 apply_default_flags(&create_flags);
5874 /* Return the base IUri if no changes have been made and the create_flags match. */
5875 if(builder->uri && !builder->modified_props && builder->uri->create_flags == create_flags) {
5876 *uri = &builder->uri->IUri_iface;
5877 IUri_AddRef(*uri);
5878 return S_OK;
5881 hr = validate_components(builder, &data, create_flags);
5882 if(FAILED(hr)) {
5883 *uri = NULL;
5884 return hr;
5887 hr = Uri_Construct(NULL, (void**)&ret);
5888 if(FAILED(hr)) {
5889 *uri = NULL;
5890 return hr;
5893 hr = generate_uri(builder, &data, ret, create_flags);
5894 if(FAILED(hr)) {
5895 IUri_Release(&ret->IUri_iface);
5896 *uri = NULL;
5897 return hr;
5900 *uri = &ret->IUri_iface;
5901 return S_OK;
5904 static inline UriBuilder* impl_from_IUriBuilder(IUriBuilder *iface)
5906 return CONTAINING_RECORD(iface, UriBuilder, IUriBuilder_iface);
5909 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
5911 UriBuilder *This = impl_from_IUriBuilder(iface);
5913 if(IsEqualGUID(&IID_IUnknown, riid)) {
5914 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
5915 *ppv = &This->IUriBuilder_iface;
5916 }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
5917 TRACE("(%p)->(IID_IUriBuilder %p)\n", This, ppv);
5918 *ppv = &This->IUriBuilder_iface;
5919 }else {
5920 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
5921 *ppv = NULL;
5922 return E_NOINTERFACE;
5925 IUnknown_AddRef((IUnknown*)*ppv);
5926 return S_OK;
5929 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
5931 UriBuilder *This = impl_from_IUriBuilder(iface);
5932 LONG ref = InterlockedIncrement(&This->ref);
5934 TRACE("(%p) ref=%d\n", This, ref);
5936 return ref;
5939 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
5941 UriBuilder *This = impl_from_IUriBuilder(iface);
5942 LONG ref = InterlockedDecrement(&This->ref);
5944 TRACE("(%p) ref=%d\n", This, ref);
5946 if(!ref) {
5947 if(This->uri) IUri_Release(&This->uri->IUri_iface);
5948 heap_free(This->fragment);
5949 heap_free(This->host);
5950 heap_free(This->password);
5951 heap_free(This->path);
5952 heap_free(This->query);
5953 heap_free(This->scheme);
5954 heap_free(This->username);
5955 heap_free(This);
5958 return ref;
5961 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
5962 DWORD dwAllowEncodingPropertyMask,
5963 DWORD_PTR dwReserved,
5964 IUri **ppIUri)
5966 UriBuilder *This = impl_from_IUriBuilder(iface);
5967 HRESULT hr;
5968 TRACE("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5970 hr = build_uri(This, ppIUri, 0, UriBuilder_USE_ORIGINAL_FLAGS, dwAllowEncodingPropertyMask);
5971 if(hr == E_NOTIMPL)
5972 FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5973 return hr;
5976 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
5977 DWORD dwCreateFlags,
5978 DWORD dwAllowEncodingPropertyMask,
5979 DWORD_PTR dwReserved,
5980 IUri **ppIUri)
5982 UriBuilder *This = impl_from_IUriBuilder(iface);
5983 HRESULT hr;
5984 TRACE("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5986 if(dwCreateFlags == -1)
5987 hr = build_uri(This, ppIUri, 0, UriBuilder_USE_ORIGINAL_FLAGS, dwAllowEncodingPropertyMask);
5988 else
5989 hr = build_uri(This, ppIUri, dwCreateFlags, 0, dwAllowEncodingPropertyMask);
5991 if(hr == E_NOTIMPL)
5992 FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5993 return hr;
5996 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
5997 DWORD dwCreateFlags,
5998 DWORD dwUriBuilderFlags,
5999 DWORD dwAllowEncodingPropertyMask,
6000 DWORD_PTR dwReserved,
6001 IUri **ppIUri)
6003 UriBuilder *This = impl_from_IUriBuilder(iface);
6004 HRESULT hr;
6005 TRACE("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
6006 dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
6008 hr = build_uri(This, ppIUri, dwCreateFlags, dwUriBuilderFlags, dwAllowEncodingPropertyMask);
6009 if(hr == E_NOTIMPL)
6010 FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
6011 dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
6012 return hr;
6015 static HRESULT WINAPI UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
6017 UriBuilder *This = impl_from_IUriBuilder(iface);
6018 TRACE("(%p)->(%p)\n", This, ppIUri);
6020 if(!ppIUri)
6021 return E_POINTER;
6023 if(This->uri) {
6024 IUri *uri = &This->uri->IUri_iface;
6025 IUri_AddRef(uri);
6026 *ppIUri = uri;
6027 } else
6028 *ppIUri = NULL;
6030 return S_OK;
6033 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
6035 UriBuilder *This = impl_from_IUriBuilder(iface);
6036 TRACE("(%p)->(%p)\n", This, pIUri);
6038 if(pIUri) {
6039 Uri *uri;
6041 if((uri = get_uri_obj(pIUri))) {
6042 /* Only reset the builder if its Uri isn't the same as
6043 * the Uri passed to the function.
6045 if(This->uri != uri) {
6046 reset_builder(This);
6048 This->uri = uri;
6049 if(uri->has_port)
6050 This->port = uri->port;
6052 IUri_AddRef(pIUri);
6054 } else {
6055 FIXME("(%p)->(%p) Unknown IUri types not supported yet.\n", This, pIUri);
6056 return E_NOTIMPL;
6058 } else if(This->uri)
6059 /* Only reset the builder if its Uri isn't NULL. */
6060 reset_builder(This);
6062 return S_OK;
6065 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
6067 UriBuilder *This = impl_from_IUriBuilder(iface);
6068 TRACE("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
6070 if(!This->uri || This->uri->fragment_start == -1 || This->modified_props & Uri_HAS_FRAGMENT)
6071 return get_builder_component(&This->fragment, &This->fragment_len, NULL, 0, ppwzFragment, pcchFragment);
6072 else
6073 return get_builder_component(&This->fragment, &This->fragment_len, This->uri->canon_uri+This->uri->fragment_start,
6074 This->uri->fragment_len, ppwzFragment, pcchFragment);
6077 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
6079 UriBuilder *This = impl_from_IUriBuilder(iface);
6080 TRACE("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
6082 if(!This->uri || This->uri->host_start == -1 || This->modified_props & Uri_HAS_HOST)
6083 return get_builder_component(&This->host, &This->host_len, NULL, 0, ppwzHost, pcchHost);
6084 else {
6085 if(This->uri->host_type == Uri_HOST_IPV6)
6086 /* Don't include the '[' and ']' around the address. */
6087 return get_builder_component(&This->host, &This->host_len, This->uri->canon_uri+This->uri->host_start+1,
6088 This->uri->host_len-2, ppwzHost, pcchHost);
6089 else
6090 return get_builder_component(&This->host, &This->host_len, This->uri->canon_uri+This->uri->host_start,
6091 This->uri->host_len, ppwzHost, pcchHost);
6095 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
6097 UriBuilder *This = impl_from_IUriBuilder(iface);
6098 TRACE("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
6100 if(!This->uri || This->uri->userinfo_split == -1 || This->modified_props & Uri_HAS_PASSWORD)
6101 return get_builder_component(&This->password, &This->password_len, NULL, 0, ppwzPassword, pcchPassword);
6102 else {
6103 const WCHAR *start = This->uri->canon_uri+This->uri->userinfo_start+This->uri->userinfo_split+1;
6104 DWORD len = This->uri->userinfo_len-This->uri->userinfo_split-1;
6105 return get_builder_component(&This->password, &This->password_len, start, len, ppwzPassword, pcchPassword);
6109 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
6111 UriBuilder *This = impl_from_IUriBuilder(iface);
6112 TRACE("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
6114 if(!This->uri || This->uri->path_start == -1 || This->modified_props & Uri_HAS_PATH)
6115 return get_builder_component(&This->path, &This->path_len, NULL, 0, ppwzPath, pcchPath);
6116 else
6117 return get_builder_component(&This->path, &This->path_len, This->uri->canon_uri+This->uri->path_start,
6118 This->uri->path_len, ppwzPath, pcchPath);
6121 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
6123 UriBuilder *This = impl_from_IUriBuilder(iface);
6124 TRACE("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
6126 if(!pfHasPort) {
6127 if(pdwPort)
6128 *pdwPort = 0;
6129 return E_POINTER;
6132 if(!pdwPort) {
6133 *pfHasPort = FALSE;
6134 return E_POINTER;
6137 *pfHasPort = This->has_port;
6138 *pdwPort = This->port;
6139 return S_OK;
6142 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
6144 UriBuilder *This = impl_from_IUriBuilder(iface);
6145 TRACE("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
6147 if(!This->uri || This->uri->query_start == -1 || This->modified_props & Uri_HAS_QUERY)
6148 return get_builder_component(&This->query, &This->query_len, NULL, 0, ppwzQuery, pcchQuery);
6149 else
6150 return get_builder_component(&This->query, &This->query_len, This->uri->canon_uri+This->uri->query_start,
6151 This->uri->query_len, ppwzQuery, pcchQuery);
6154 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
6156 UriBuilder *This = impl_from_IUriBuilder(iface);
6157 TRACE("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
6159 if(!This->uri || This->uri->scheme_start == -1 || This->modified_props & Uri_HAS_SCHEME_NAME)
6160 return get_builder_component(&This->scheme, &This->scheme_len, NULL, 0, ppwzSchemeName, pcchSchemeName);
6161 else
6162 return get_builder_component(&This->scheme, &This->scheme_len, This->uri->canon_uri+This->uri->scheme_start,
6163 This->uri->scheme_len, ppwzSchemeName, pcchSchemeName);
6166 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
6168 UriBuilder *This = impl_from_IUriBuilder(iface);
6169 TRACE("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
6171 if(!This->uri || This->uri->userinfo_start == -1 || This->uri->userinfo_split == 0 ||
6172 This->modified_props & Uri_HAS_USER_NAME)
6173 return get_builder_component(&This->username, &This->username_len, NULL, 0, ppwzUserName, pcchUserName);
6174 else {
6175 const WCHAR *start = This->uri->canon_uri+This->uri->userinfo_start;
6177 /* Check if there's a password in the userinfo section. */
6178 if(This->uri->userinfo_split > -1)
6179 /* Don't include the password. */
6180 return get_builder_component(&This->username, &This->username_len, start,
6181 This->uri->userinfo_split, ppwzUserName, pcchUserName);
6182 else
6183 return get_builder_component(&This->username, &This->username_len, start,
6184 This->uri->userinfo_len, ppwzUserName, pcchUserName);
6188 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
6190 UriBuilder *This = impl_from_IUriBuilder(iface);
6191 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6192 return set_builder_component(&This->fragment, &This->fragment_len, pwzNewValue, '#',
6193 &This->modified_props, Uri_HAS_FRAGMENT);
6196 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
6198 UriBuilder *This = impl_from_IUriBuilder(iface);
6199 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6201 /* Host name can't be set to NULL. */
6202 if(!pwzNewValue)
6203 return E_INVALIDARG;
6205 return set_builder_component(&This->host, &This->host_len, pwzNewValue, 0,
6206 &This->modified_props, Uri_HAS_HOST);
6209 static HRESULT WINAPI UriBuilder_SetPassword(IUriBuilder *iface, LPCWSTR pwzNewValue)
6211 UriBuilder *This = impl_from_IUriBuilder(iface);
6212 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6213 return set_builder_component(&This->password, &This->password_len, pwzNewValue, 0,
6214 &This->modified_props, Uri_HAS_PASSWORD);
6217 static HRESULT WINAPI UriBuilder_SetPath(IUriBuilder *iface, LPCWSTR pwzNewValue)
6219 UriBuilder *This = impl_from_IUriBuilder(iface);
6220 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6221 return set_builder_component(&This->path, &This->path_len, pwzNewValue, 0,
6222 &This->modified_props, Uri_HAS_PATH);
6225 static HRESULT WINAPI UriBuilder_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
6227 UriBuilder *This = impl_from_IUriBuilder(iface);
6228 TRACE("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
6230 This->has_port = fHasPort;
6231 This->port = dwNewValue;
6232 This->modified_props |= Uri_HAS_PORT;
6233 return S_OK;
6236 static HRESULT WINAPI UriBuilder_SetQuery(IUriBuilder *iface, LPCWSTR pwzNewValue)
6238 UriBuilder *This = impl_from_IUriBuilder(iface);
6239 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6240 return set_builder_component(&This->query, &This->query_len, pwzNewValue, '?',
6241 &This->modified_props, Uri_HAS_QUERY);
6244 static HRESULT WINAPI UriBuilder_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
6246 UriBuilder *This = impl_from_IUriBuilder(iface);
6247 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6249 /* Only set the scheme name if it's not NULL or empty. */
6250 if(!pwzNewValue || !*pwzNewValue)
6251 return E_INVALIDARG;
6253 return set_builder_component(&This->scheme, &This->scheme_len, pwzNewValue, 0,
6254 &This->modified_props, Uri_HAS_SCHEME_NAME);
6257 static HRESULT WINAPI UriBuilder_SetUserName(IUriBuilder *iface, LPCWSTR pwzNewValue)
6259 UriBuilder *This = impl_from_IUriBuilder(iface);
6260 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6261 return set_builder_component(&This->username, &This->username_len, pwzNewValue, 0,
6262 &This->modified_props, Uri_HAS_USER_NAME);
6265 static HRESULT WINAPI UriBuilder_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
6267 const DWORD accepted_flags = Uri_HAS_AUTHORITY|Uri_HAS_DOMAIN|Uri_HAS_EXTENSION|Uri_HAS_FRAGMENT|Uri_HAS_HOST|
6268 Uri_HAS_PASSWORD|Uri_HAS_PATH|Uri_HAS_PATH_AND_QUERY|Uri_HAS_QUERY|
6269 Uri_HAS_USER_INFO|Uri_HAS_USER_NAME;
6271 UriBuilder *This = impl_from_IUriBuilder(iface);
6272 TRACE("(%p)->(0x%08x)\n", This, dwPropertyMask);
6274 if(dwPropertyMask & ~accepted_flags)
6275 return E_INVALIDARG;
6277 if(dwPropertyMask & Uri_HAS_FRAGMENT)
6278 UriBuilder_SetFragment(iface, NULL);
6280 /* Even though you can't set the host name to NULL or an
6281 * empty string, you can still remove it... for some reason.
6283 if(dwPropertyMask & Uri_HAS_HOST)
6284 set_builder_component(&This->host, &This->host_len, NULL, 0,
6285 &This->modified_props, Uri_HAS_HOST);
6287 if(dwPropertyMask & Uri_HAS_PASSWORD)
6288 UriBuilder_SetPassword(iface, NULL);
6290 if(dwPropertyMask & Uri_HAS_PATH)
6291 UriBuilder_SetPath(iface, NULL);
6293 if(dwPropertyMask & Uri_HAS_PORT)
6294 UriBuilder_SetPort(iface, FALSE, 0);
6296 if(dwPropertyMask & Uri_HAS_QUERY)
6297 UriBuilder_SetQuery(iface, NULL);
6299 if(dwPropertyMask & Uri_HAS_USER_NAME)
6300 UriBuilder_SetUserName(iface, NULL);
6302 return S_OK;
6305 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
6307 UriBuilder *This = impl_from_IUriBuilder(iface);
6308 TRACE("(%p)->(%p)\n", This, pfModified);
6310 if(!pfModified)
6311 return E_POINTER;
6313 *pfModified = This->modified_props > 0;
6314 return S_OK;
6317 static const IUriBuilderVtbl UriBuilderVtbl = {
6318 UriBuilder_QueryInterface,
6319 UriBuilder_AddRef,
6320 UriBuilder_Release,
6321 UriBuilder_CreateUriSimple,
6322 UriBuilder_CreateUri,
6323 UriBuilder_CreateUriWithFlags,
6324 UriBuilder_GetIUri,
6325 UriBuilder_SetIUri,
6326 UriBuilder_GetFragment,
6327 UriBuilder_GetHost,
6328 UriBuilder_GetPassword,
6329 UriBuilder_GetPath,
6330 UriBuilder_GetPort,
6331 UriBuilder_GetQuery,
6332 UriBuilder_GetSchemeName,
6333 UriBuilder_GetUserName,
6334 UriBuilder_SetFragment,
6335 UriBuilder_SetHost,
6336 UriBuilder_SetPassword,
6337 UriBuilder_SetPath,
6338 UriBuilder_SetPort,
6339 UriBuilder_SetQuery,
6340 UriBuilder_SetSchemeName,
6341 UriBuilder_SetUserName,
6342 UriBuilder_RemoveProperties,
6343 UriBuilder_HasBeenModified,
6346 /***********************************************************************
6347 * CreateIUriBuilder (urlmon.@)
6349 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
6351 UriBuilder *ret;
6353 TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
6355 if(!ppIUriBuilder)
6356 return E_POINTER;
6358 ret = heap_alloc_zero(sizeof(UriBuilder));
6359 if(!ret)
6360 return E_OUTOFMEMORY;
6362 ret->IUriBuilder_iface.lpVtbl = &UriBuilderVtbl;
6363 ret->ref = 1;
6365 if(pIUri) {
6366 Uri *uri;
6368 if((uri = get_uri_obj(pIUri))) {
6369 if(!uri->create_flags) {
6370 heap_free(ret);
6371 return E_UNEXPECTED;
6373 IUri_AddRef(pIUri);
6374 ret->uri = uri;
6376 if(uri->has_port)
6377 /* Windows doesn't set 'has_port' to TRUE in this case. */
6378 ret->port = uri->port;
6380 } else {
6381 heap_free(ret);
6382 *ppIUriBuilder = NULL;
6383 FIXME("(%p %x %x %p): Unknown IUri types not supported yet.\n", pIUri, dwFlags,
6384 (DWORD)dwReserved, ppIUriBuilder);
6385 return E_NOTIMPL;
6389 *ppIUriBuilder = &ret->IUriBuilder_iface;
6390 return S_OK;
6393 /* Merges the base path with the relative path and stores the resulting path
6394 * and path len in 'result' and 'result_len'.
6396 static HRESULT merge_paths(parse_data *data, const WCHAR *base, DWORD base_len, const WCHAR *relative,
6397 DWORD relative_len, WCHAR **result, DWORD *result_len, DWORD flags)
6399 const WCHAR *end = NULL;
6400 DWORD base_copy_len = 0;
6401 WCHAR *ptr;
6403 if(base_len) {
6404 if(data->scheme_type == URL_SCHEME_MK && *relative == '/') {
6405 /* Find '::' segment */
6406 for(end = base; end < base+base_len-1; end++) {
6407 if(end[0] == ':' && end[1] == ':') {
6408 end++;
6409 break;
6413 /* If not found, try finding the end of @xxx: */
6414 if(end == base+base_len-1)
6415 end = *base == '@' ? memchr(base, ':', base_len) : NULL;
6416 }else {
6417 /* Find the characters that will be copied over from the base path. */
6418 end = memrchrW(base, '/', base_len);
6419 if(!end && data->scheme_type == URL_SCHEME_FILE)
6420 /* Try looking for a '\\'. */
6421 end = memrchrW(base, '\\', base_len);
6425 if(end) {
6426 base_copy_len = (end+1)-base;
6427 *result = heap_alloc((base_copy_len+relative_len+1)*sizeof(WCHAR));
6428 } else
6429 *result = heap_alloc((relative_len+1)*sizeof(WCHAR));
6431 if(!(*result)) {
6432 *result_len = 0;
6433 return E_OUTOFMEMORY;
6436 ptr = *result;
6437 if(end) {
6438 memcpy(ptr, base, base_copy_len*sizeof(WCHAR));
6439 ptr += base_copy_len;
6442 memcpy(ptr, relative, relative_len*sizeof(WCHAR));
6443 ptr += relative_len;
6444 *ptr = '\0';
6446 *result_len = (ptr-*result);
6447 TRACE("ret %s\n", debugstr_wn(*result, *result_len));
6448 return S_OK;
6451 static HRESULT combine_uri(Uri *base, Uri *relative, DWORD flags, IUri **result, DWORD extras) {
6452 Uri *ret;
6453 HRESULT hr;
6454 parse_data data;
6455 Uri *proc_uri = base;
6456 DWORD create_flags = 0, len = 0;
6458 memset(&data, 0, sizeof(parse_data));
6460 /* Base case is when the relative Uri has a scheme name,
6461 * if it does, then 'result' will contain the same data
6462 * as the relative Uri.
6464 if(relative->scheme_start > -1) {
6465 data.uri = SysAllocString(relative->raw_uri);
6466 if(!data.uri) {
6467 *result = NULL;
6468 return E_OUTOFMEMORY;
6471 parse_uri(&data, Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME);
6473 hr = Uri_Construct(NULL, (void**)&ret);
6474 if(FAILED(hr)) {
6475 *result = NULL;
6476 return hr;
6479 if(extras & COMBINE_URI_FORCE_FLAG_USE) {
6480 if(flags & URL_DONT_SIMPLIFY)
6481 create_flags |= Uri_CREATE_NO_CANONICALIZE;
6482 if(flags & URL_DONT_UNESCAPE_EXTRA_INFO)
6483 create_flags |= Uri_CREATE_NO_DECODE_EXTRA_INFO;
6486 ret->raw_uri = data.uri;
6487 hr = canonicalize_uri(&data, ret, create_flags);
6488 if(FAILED(hr)) {
6489 IUri_Release(&ret->IUri_iface);
6490 *result = NULL;
6491 return hr;
6494 apply_default_flags(&create_flags);
6495 ret->create_flags = create_flags;
6497 *result = &ret->IUri_iface;
6498 } else {
6499 WCHAR *path = NULL;
6500 DWORD raw_flags = 0;
6502 if(base->scheme_start > -1) {
6503 data.scheme = base->canon_uri+base->scheme_start;
6504 data.scheme_len = base->scheme_len;
6505 data.scheme_type = base->scheme_type;
6506 } else {
6507 data.is_relative = TRUE;
6508 data.scheme_type = URL_SCHEME_UNKNOWN;
6509 create_flags |= Uri_CREATE_ALLOW_RELATIVE;
6512 if(relative->authority_start > -1)
6513 proc_uri = relative;
6515 if(proc_uri->authority_start > -1) {
6516 if(proc_uri->userinfo_start > -1 && proc_uri->userinfo_split != 0) {
6517 data.username = proc_uri->canon_uri+proc_uri->userinfo_start;
6518 data.username_len = (proc_uri->userinfo_split > -1) ? proc_uri->userinfo_split : proc_uri->userinfo_len;
6521 if(proc_uri->userinfo_split > -1) {
6522 data.password = proc_uri->canon_uri+proc_uri->userinfo_start+proc_uri->userinfo_split+1;
6523 data.password_len = proc_uri->userinfo_len-proc_uri->userinfo_split-1;
6526 if(proc_uri->host_start > -1) {
6527 data.host = proc_uri->canon_uri+proc_uri->host_start;
6528 data.host_len = proc_uri->host_len;
6529 data.host_type = proc_uri->host_type;
6532 if(proc_uri->has_port) {
6533 data.has_port = TRUE;
6534 data.port_value = proc_uri->port;
6536 } else if(base->scheme_type != URL_SCHEME_FILE)
6537 data.is_opaque = TRUE;
6539 if(proc_uri == relative || relative->path_start == -1 || !relative->path_len) {
6540 if(proc_uri->path_start > -1) {
6541 data.path = proc_uri->canon_uri+proc_uri->path_start;
6542 data.path_len = proc_uri->path_len;
6543 } else if(!data.is_opaque) {
6544 /* Just set the path as a '/' if the base didn't have
6545 * one and if it's a hierarchical URI.
6547 static const WCHAR slashW[] = {'/',0};
6548 data.path = slashW;
6549 data.path_len = 1;
6552 if(relative->query_start > -1)
6553 proc_uri = relative;
6555 if(proc_uri->query_start > -1) {
6556 data.query = proc_uri->canon_uri+proc_uri->query_start;
6557 data.query_len = proc_uri->query_len;
6559 } else {
6560 const WCHAR *ptr, **pptr;
6561 DWORD path_offset = 0, path_len = 0;
6563 /* There's two possibilities on what will happen to the path component
6564 * of the result IUri. First, if the relative path begins with a '/'
6565 * then the resulting path will just be the relative path. Second, if
6566 * relative path doesn't begin with a '/' then the base path and relative
6567 * path are merged together.
6569 if(relative->path_len && *(relative->canon_uri+relative->path_start) == '/' && data.scheme_type != URL_SCHEME_MK) {
6570 WCHAR *tmp = NULL;
6571 BOOL copy_drive_path = FALSE;
6573 /* If the relative IUri's path starts with a '/', then we
6574 * don't use the base IUri's path. Unless the base IUri
6575 * is a file URI, in which case it uses the drive path of
6576 * the base IUri (if it has any) in the new path.
6578 if(base->scheme_type == URL_SCHEME_FILE) {
6579 if(base->path_len > 3 && *(base->canon_uri+base->path_start) == '/' &&
6580 is_drive_path(base->canon_uri+base->path_start+1)) {
6581 path_len += 3;
6582 copy_drive_path = TRUE;
6586 path_len += relative->path_len;
6588 path = heap_alloc((path_len+1)*sizeof(WCHAR));
6589 if(!path) {
6590 *result = NULL;
6591 return E_OUTOFMEMORY;
6594 tmp = path;
6596 /* Copy the base paths, drive path over. */
6597 if(copy_drive_path) {
6598 memcpy(tmp, base->canon_uri+base->path_start, 3*sizeof(WCHAR));
6599 tmp += 3;
6602 memcpy(tmp, relative->canon_uri+relative->path_start, relative->path_len*sizeof(WCHAR));
6603 path[path_len] = '\0';
6604 } else {
6605 /* Merge the base path with the relative path. */
6606 hr = merge_paths(&data, base->canon_uri+base->path_start, base->path_len,
6607 relative->canon_uri+relative->path_start, relative->path_len,
6608 &path, &path_len, flags);
6609 if(FAILED(hr)) {
6610 *result = NULL;
6611 return hr;
6614 /* If the resulting IUri is a file URI, the drive path isn't
6615 * reduced out when the dot segments are removed.
6617 if(path_len >= 3 && data.scheme_type == URL_SCHEME_FILE && !data.host) {
6618 if(*path == '/' && is_drive_path(path+1))
6619 path_offset = 2;
6620 else if(is_drive_path(path))
6621 path_offset = 1;
6625 /* Check if the dot segments need to be removed from the path. */
6626 if(!(flags & URL_DONT_SIMPLIFY) && !data.is_opaque) {
6627 DWORD offset = (path_offset > 0) ? path_offset+1 : 0;
6628 DWORD new_len = remove_dot_segments(path+offset,path_len-offset);
6630 if(new_len != path_len) {
6631 WCHAR *tmp = heap_realloc(path, (offset+new_len+1)*sizeof(WCHAR));
6632 if(!tmp) {
6633 heap_free(path);
6634 *result = NULL;
6635 return E_OUTOFMEMORY;
6638 tmp[new_len+offset] = '\0';
6639 path = tmp;
6640 path_len = new_len+offset;
6644 if(relative->query_start > -1) {
6645 data.query = relative->canon_uri+relative->query_start;
6646 data.query_len = relative->query_len;
6649 /* Make sure the path component is valid. */
6650 ptr = path;
6651 pptr = &ptr;
6652 if((data.is_opaque && !parse_path_opaque(pptr, &data, 0)) ||
6653 (!data.is_opaque && !parse_path_hierarchical(pptr, &data, 0))) {
6654 heap_free(path);
6655 *result = NULL;
6656 return E_INVALIDARG;
6660 if(relative->fragment_start > -1) {
6661 data.fragment = relative->canon_uri+relative->fragment_start;
6662 data.fragment_len = relative->fragment_len;
6665 if(flags & URL_DONT_SIMPLIFY)
6666 raw_flags |= RAW_URI_FORCE_PORT_DISP;
6667 if(flags & URL_FILE_USE_PATHURL)
6668 raw_flags |= RAW_URI_CONVERT_TO_DOS_PATH;
6670 len = generate_raw_uri(&data, data.uri, raw_flags);
6671 data.uri = SysAllocStringLen(NULL, len);
6672 if(!data.uri) {
6673 heap_free(path);
6674 *result = NULL;
6675 return E_OUTOFMEMORY;
6678 generate_raw_uri(&data, data.uri, raw_flags);
6680 hr = Uri_Construct(NULL, (void**)&ret);
6681 if(FAILED(hr)) {
6682 SysFreeString(data.uri);
6683 heap_free(path);
6684 *result = NULL;
6685 return hr;
6688 if(flags & URL_DONT_SIMPLIFY)
6689 create_flags |= Uri_CREATE_NO_CANONICALIZE;
6690 if(flags & URL_FILE_USE_PATHURL)
6691 create_flags |= Uri_CREATE_FILE_USE_DOS_PATH;
6693 ret->raw_uri = data.uri;
6694 hr = canonicalize_uri(&data, ret, create_flags);
6695 if(FAILED(hr)) {
6696 IUri_Release(&ret->IUri_iface);
6697 *result = NULL;
6698 return hr;
6701 if(flags & URL_DONT_SIMPLIFY)
6702 ret->display_modifiers |= URI_DISPLAY_NO_DEFAULT_PORT_AUTH;
6704 apply_default_flags(&create_flags);
6705 ret->create_flags = create_flags;
6706 *result = &ret->IUri_iface;
6708 heap_free(path);
6711 return S_OK;
6714 /***********************************************************************
6715 * CoInternetCombineIUri (urlmon.@)
6717 HRESULT WINAPI CoInternetCombineIUri(IUri *pBaseUri, IUri *pRelativeUri, DWORD dwCombineFlags,
6718 IUri **ppCombinedUri, DWORD_PTR dwReserved)
6720 HRESULT hr;
6721 IInternetProtocolInfo *info;
6722 Uri *relative, *base;
6723 TRACE("(%p %p %x %p %x)\n", pBaseUri, pRelativeUri, dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6725 if(!ppCombinedUri)
6726 return E_INVALIDARG;
6728 if(!pBaseUri || !pRelativeUri) {
6729 *ppCombinedUri = NULL;
6730 return E_INVALIDARG;
6733 relative = get_uri_obj(pRelativeUri);
6734 base = get_uri_obj(pBaseUri);
6735 if(!relative || !base) {
6736 *ppCombinedUri = NULL;
6737 FIXME("(%p %p %x %p %x) Unknown IUri types not supported yet.\n",
6738 pBaseUri, pRelativeUri, dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6739 return E_NOTIMPL;
6742 info = get_protocol_info(base->canon_uri);
6743 if(info) {
6744 WCHAR result[INTERNET_MAX_URL_LENGTH+1];
6745 DWORD result_len = 0;
6747 hr = IInternetProtocolInfo_CombineUrl(info, base->canon_uri, relative->canon_uri, dwCombineFlags,
6748 result, INTERNET_MAX_URL_LENGTH+1, &result_len, 0);
6749 IInternetProtocolInfo_Release(info);
6750 if(SUCCEEDED(hr)) {
6751 hr = CreateUri(result, Uri_CREATE_ALLOW_RELATIVE, 0, ppCombinedUri);
6752 if(SUCCEEDED(hr))
6753 return hr;
6757 return combine_uri(base, relative, dwCombineFlags, ppCombinedUri, 0);
6760 /***********************************************************************
6761 * CoInternetCombineUrlEx (urlmon.@)
6763 HRESULT WINAPI CoInternetCombineUrlEx(IUri *pBaseUri, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags,
6764 IUri **ppCombinedUri, DWORD_PTR dwReserved)
6766 IUri *relative;
6767 Uri *base;
6768 HRESULT hr;
6769 IInternetProtocolInfo *info;
6771 TRACE("(%p %s %x %p %x) stub\n", pBaseUri, debugstr_w(pwzRelativeUrl), dwCombineFlags,
6772 ppCombinedUri, (DWORD)dwReserved);
6774 if(!ppCombinedUri)
6775 return E_POINTER;
6777 if(!pwzRelativeUrl) {
6778 *ppCombinedUri = NULL;
6779 return E_UNEXPECTED;
6782 if(!pBaseUri) {
6783 *ppCombinedUri = NULL;
6784 return E_INVALIDARG;
6787 base = get_uri_obj(pBaseUri);
6788 if(!base) {
6789 *ppCombinedUri = NULL;
6790 FIXME("(%p %s %x %p %x) Unknown IUri's not supported yet.\n", pBaseUri, debugstr_w(pwzRelativeUrl),
6791 dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6792 return E_NOTIMPL;
6795 info = get_protocol_info(base->canon_uri);
6796 if(info) {
6797 WCHAR result[INTERNET_MAX_URL_LENGTH+1];
6798 DWORD result_len = 0;
6800 hr = IInternetProtocolInfo_CombineUrl(info, base->canon_uri, pwzRelativeUrl, dwCombineFlags,
6801 result, INTERNET_MAX_URL_LENGTH+1, &result_len, 0);
6802 IInternetProtocolInfo_Release(info);
6803 if(SUCCEEDED(hr)) {
6804 hr = CreateUri(result, Uri_CREATE_ALLOW_RELATIVE, 0, ppCombinedUri);
6805 if(SUCCEEDED(hr))
6806 return hr;
6810 hr = CreateUri(pwzRelativeUrl, Uri_CREATE_ALLOW_RELATIVE|Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME, 0, &relative);
6811 if(FAILED(hr)) {
6812 *ppCombinedUri = NULL;
6813 return hr;
6816 hr = combine_uri(base, get_uri_obj(relative), dwCombineFlags, ppCombinedUri, COMBINE_URI_FORCE_FLAG_USE);
6818 IUri_Release(relative);
6819 return hr;
6822 static HRESULT parse_canonicalize(const Uri *uri, DWORD flags, LPWSTR output,
6823 DWORD output_len, DWORD *result_len)
6825 const WCHAR *ptr = NULL;
6826 WCHAR *path = NULL;
6827 const WCHAR **pptr;
6828 WCHAR buffer[INTERNET_MAX_URL_LENGTH+1];
6829 DWORD len = 0;
6830 BOOL reduce_path;
6832 /* URL_UNESCAPE only has effect if none of the URL_ESCAPE flags are set. */
6833 const BOOL allow_unescape = !(flags & URL_ESCAPE_UNSAFE) &&
6834 !(flags & URL_ESCAPE_SPACES_ONLY) &&
6835 !(flags & URL_ESCAPE_PERCENT);
6838 /* Check if the dot segments need to be removed from the
6839 * path component.
6841 if(uri->scheme_start > -1 && uri->path_start > -1) {
6842 ptr = uri->canon_uri+uri->scheme_start+uri->scheme_len+1;
6843 pptr = &ptr;
6845 reduce_path = !(flags & URL_DONT_SIMPLIFY) &&
6846 ptr && check_hierarchical(pptr);
6848 for(ptr = uri->canon_uri; ptr < uri->canon_uri+uri->canon_len; ++ptr) {
6849 BOOL do_default_action = TRUE;
6851 /* Keep track of the path if we need to remove dot segments from
6852 * it later.
6854 if(reduce_path && !path && ptr == uri->canon_uri+uri->path_start)
6855 path = buffer+len;
6857 /* Check if it's time to reduce the path. */
6858 if(reduce_path && ptr == uri->canon_uri+uri->path_start+uri->path_len) {
6859 DWORD current_path_len = (buffer+len) - path;
6860 DWORD new_path_len = remove_dot_segments(path, current_path_len);
6862 /* Update the current length. */
6863 len -= (current_path_len-new_path_len);
6864 reduce_path = FALSE;
6867 if(*ptr == '%') {
6868 const WCHAR decoded = decode_pct_val(ptr);
6869 if(decoded) {
6870 if(allow_unescape && (flags & URL_UNESCAPE)) {
6871 buffer[len++] = decoded;
6872 ptr += 2;
6873 do_default_action = FALSE;
6877 /* See if %'s needed to encoded. */
6878 if(do_default_action && (flags & URL_ESCAPE_PERCENT)) {
6879 pct_encode_val(*ptr, buffer+len);
6880 len += 3;
6881 do_default_action = FALSE;
6883 } else if(*ptr == ' ') {
6884 if((flags & URL_ESCAPE_SPACES_ONLY) &&
6885 !(flags & URL_ESCAPE_UNSAFE)) {
6886 pct_encode_val(*ptr, buffer+len);
6887 len += 3;
6888 do_default_action = FALSE;
6890 } else if(!is_reserved(*ptr) && !is_unreserved(*ptr)) {
6891 if(flags & URL_ESCAPE_UNSAFE) {
6892 pct_encode_val(*ptr, buffer+len);
6893 len += 3;
6894 do_default_action = FALSE;
6898 if(do_default_action)
6899 buffer[len++] = *ptr;
6902 /* Sometimes the path is the very last component of the IUri, so
6903 * see if the dot segments need to be reduced now.
6905 if(reduce_path && path) {
6906 DWORD current_path_len = (buffer+len) - path;
6907 DWORD new_path_len = remove_dot_segments(path, current_path_len);
6909 /* Update the current length. */
6910 len -= (current_path_len-new_path_len);
6913 buffer[len++] = 0;
6915 /* The null terminator isn't included in the length. */
6916 *result_len = len-1;
6917 if(len > output_len)
6918 return STRSAFE_E_INSUFFICIENT_BUFFER;
6919 else
6920 memcpy(output, buffer, len*sizeof(WCHAR));
6922 return S_OK;
6925 static HRESULT parse_friendly(IUri *uri, LPWSTR output, DWORD output_len,
6926 DWORD *result_len)
6928 HRESULT hr;
6929 DWORD display_len;
6930 BSTR display;
6932 hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_DISPLAY_URI, &display_len, 0);
6933 if(FAILED(hr)) {
6934 *result_len = 0;
6935 return hr;
6938 *result_len = display_len;
6939 if(display_len+1 > output_len)
6940 return STRSAFE_E_INSUFFICIENT_BUFFER;
6942 hr = IUri_GetDisplayUri(uri, &display);
6943 if(FAILED(hr)) {
6944 *result_len = 0;
6945 return hr;
6948 memcpy(output, display, (display_len+1)*sizeof(WCHAR));
6949 SysFreeString(display);
6950 return S_OK;
6953 static HRESULT parse_rootdocument(const Uri *uri, LPWSTR output, DWORD output_len,
6954 DWORD *result_len)
6956 static const WCHAR colon_slashesW[] = {':','/','/'};
6958 WCHAR *ptr;
6959 DWORD len = 0;
6961 /* Windows only returns the root document if the URI has an authority
6962 * and it's not an unknown scheme type or a file scheme type.
6964 if(uri->authority_start == -1 ||
6965 uri->scheme_type == URL_SCHEME_UNKNOWN ||
6966 uri->scheme_type == URL_SCHEME_FILE) {
6967 *result_len = 0;
6968 if(!output_len)
6969 return STRSAFE_E_INSUFFICIENT_BUFFER;
6971 output[0] = 0;
6972 return S_OK;
6975 len = uri->scheme_len+uri->authority_len;
6976 /* For the "://" and '/' which will be added. */
6977 len += 4;
6979 if(len+1 > output_len) {
6980 *result_len = len;
6981 return STRSAFE_E_INSUFFICIENT_BUFFER;
6984 ptr = output;
6985 memcpy(ptr, uri->canon_uri+uri->scheme_start, uri->scheme_len*sizeof(WCHAR));
6987 /* Add the "://". */
6988 ptr += uri->scheme_len;
6989 memcpy(ptr, colon_slashesW, sizeof(colon_slashesW));
6991 /* Add the authority. */
6992 ptr += sizeof(colon_slashesW)/sizeof(WCHAR);
6993 memcpy(ptr, uri->canon_uri+uri->authority_start, uri->authority_len*sizeof(WCHAR));
6995 /* Add the '/' after the authority. */
6996 ptr += uri->authority_len;
6997 *ptr = '/';
6998 ptr[1] = 0;
7000 *result_len = len;
7001 return S_OK;
7004 static HRESULT parse_document(const Uri *uri, LPWSTR output, DWORD output_len,
7005 DWORD *result_len)
7007 DWORD len = 0;
7009 /* It has to be a known scheme type, but, it can't be a file
7010 * scheme. It also has to hierarchical.
7012 if(uri->scheme_type == URL_SCHEME_UNKNOWN ||
7013 uri->scheme_type == URL_SCHEME_FILE ||
7014 uri->authority_start == -1) {
7015 *result_len = 0;
7016 if(output_len < 1)
7017 return STRSAFE_E_INSUFFICIENT_BUFFER;
7019 output[0] = 0;
7020 return S_OK;
7023 if(uri->fragment_start > -1)
7024 len = uri->fragment_start;
7025 else
7026 len = uri->canon_len;
7028 *result_len = len;
7029 if(len+1 > output_len)
7030 return STRSAFE_E_INSUFFICIENT_BUFFER;
7032 memcpy(output, uri->canon_uri, len*sizeof(WCHAR));
7033 output[len] = 0;
7034 return S_OK;
7037 static HRESULT parse_path_from_url(const Uri *uri, LPWSTR output, DWORD output_len,
7038 DWORD *result_len)
7040 const WCHAR *path_ptr;
7041 WCHAR buffer[INTERNET_MAX_URL_LENGTH+1];
7042 WCHAR *ptr;
7044 if(uri->scheme_type != URL_SCHEME_FILE) {
7045 *result_len = 0;
7046 if(output_len > 0)
7047 output[0] = 0;
7048 return E_INVALIDARG;
7051 ptr = buffer;
7052 if(uri->host_start > -1) {
7053 static const WCHAR slash_slashW[] = {'\\','\\'};
7055 memcpy(ptr, slash_slashW, sizeof(slash_slashW));
7056 ptr += sizeof(slash_slashW)/sizeof(WCHAR);
7057 memcpy(ptr, uri->canon_uri+uri->host_start, uri->host_len*sizeof(WCHAR));
7058 ptr += uri->host_len;
7061 path_ptr = uri->canon_uri+uri->path_start;
7062 if(uri->path_len > 3 && *path_ptr == '/' && is_drive_path(path_ptr+1))
7063 /* Skip past the '/' in front of the drive path. */
7064 ++path_ptr;
7066 for(; path_ptr < uri->canon_uri+uri->path_start+uri->path_len; ++path_ptr, ++ptr) {
7067 BOOL do_default_action = TRUE;
7069 if(*path_ptr == '%') {
7070 const WCHAR decoded = decode_pct_val(path_ptr);
7071 if(decoded) {
7072 *ptr = decoded;
7073 path_ptr += 2;
7074 do_default_action = FALSE;
7076 } else if(*path_ptr == '/') {
7077 *ptr = '\\';
7078 do_default_action = FALSE;
7081 if(do_default_action)
7082 *ptr = *path_ptr;
7085 *ptr = 0;
7087 *result_len = ptr-buffer;
7088 if(*result_len+1 > output_len)
7089 return STRSAFE_E_INSUFFICIENT_BUFFER;
7091 memcpy(output, buffer, (*result_len+1)*sizeof(WCHAR));
7092 return S_OK;
7095 static HRESULT parse_url_from_path(IUri *uri, LPWSTR output, DWORD output_len,
7096 DWORD *result_len)
7098 HRESULT hr;
7099 BSTR received;
7100 DWORD len = 0;
7102 hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_ABSOLUTE_URI, &len, 0);
7103 if(FAILED(hr)) {
7104 *result_len = 0;
7105 return hr;
7108 *result_len = len;
7109 if(len+1 > output_len)
7110 return STRSAFE_E_INSUFFICIENT_BUFFER;
7112 hr = IUri_GetAbsoluteUri(uri, &received);
7113 if(FAILED(hr)) {
7114 *result_len = 0;
7115 return hr;
7118 memcpy(output, received, (len+1)*sizeof(WCHAR));
7119 SysFreeString(received);
7121 return S_OK;
7124 static HRESULT parse_schema(IUri *uri, LPWSTR output, DWORD output_len,
7125 DWORD *result_len)
7127 HRESULT hr;
7128 DWORD len;
7129 BSTR received;
7131 hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_SCHEME_NAME, &len, 0);
7132 if(FAILED(hr)) {
7133 *result_len = 0;
7134 return hr;
7137 *result_len = len;
7138 if(len+1 > output_len)
7139 return STRSAFE_E_INSUFFICIENT_BUFFER;
7141 hr = IUri_GetSchemeName(uri, &received);
7142 if(FAILED(hr)) {
7143 *result_len = 0;
7144 return hr;
7147 memcpy(output, received, (len+1)*sizeof(WCHAR));
7148 SysFreeString(received);
7150 return S_OK;
7153 static HRESULT parse_site(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7155 HRESULT hr;
7156 DWORD len;
7157 BSTR received;
7159 hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_HOST, &len, 0);
7160 if(FAILED(hr)) {
7161 *result_len = 0;
7162 return hr;
7165 *result_len = len;
7166 if(len+1 > output_len)
7167 return STRSAFE_E_INSUFFICIENT_BUFFER;
7169 hr = IUri_GetHost(uri, &received);
7170 if(FAILED(hr)) {
7171 *result_len = 0;
7172 return hr;
7175 memcpy(output, received, (len+1)*sizeof(WCHAR));
7176 SysFreeString(received);
7178 return S_OK;
7181 static HRESULT parse_domain(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7183 HRESULT hr;
7184 DWORD len;
7185 BSTR received;
7187 hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_DOMAIN, &len, 0);
7188 if(FAILED(hr)) {
7189 *result_len = 0;
7190 return hr;
7193 *result_len = len;
7194 if(len+1 > output_len)
7195 return STRSAFE_E_INSUFFICIENT_BUFFER;
7197 hr = IUri_GetDomain(uri, &received);
7198 if(FAILED(hr)) {
7199 *result_len = 0;
7200 return hr;
7203 memcpy(output, received, (len+1)*sizeof(WCHAR));
7204 SysFreeString(received);
7206 return S_OK;
7209 static HRESULT parse_anchor(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7211 HRESULT hr;
7212 DWORD len;
7213 BSTR received;
7215 hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_FRAGMENT, &len, 0);
7216 if(FAILED(hr)) {
7217 *result_len = 0;
7218 return hr;
7221 *result_len = len;
7222 if(len+1 > output_len)
7223 return STRSAFE_E_INSUFFICIENT_BUFFER;
7225 hr = IUri_GetFragment(uri, &received);
7226 if(FAILED(hr)) {
7227 *result_len = 0;
7228 return hr;
7231 memcpy(output, received, (len+1)*sizeof(WCHAR));
7232 SysFreeString(received);
7234 return S_OK;
7237 /***********************************************************************
7238 * CoInternetParseIUri (urlmon.@)
7240 HRESULT WINAPI CoInternetParseIUri(IUri *pIUri, PARSEACTION ParseAction, DWORD dwFlags,
7241 LPWSTR pwzResult, DWORD cchResult, DWORD *pcchResult,
7242 DWORD_PTR dwReserved)
7244 HRESULT hr;
7245 Uri *uri;
7246 IInternetProtocolInfo *info;
7248 TRACE("(%p %d %x %p %d %p %x)\n", pIUri, ParseAction, dwFlags, pwzResult,
7249 cchResult, pcchResult, (DWORD)dwReserved);
7251 if(!pcchResult)
7252 return E_POINTER;
7254 if(!pwzResult || !pIUri) {
7255 *pcchResult = 0;
7256 return E_INVALIDARG;
7259 if(!(uri = get_uri_obj(pIUri))) {
7260 *pcchResult = 0;
7261 FIXME("(%p %d %x %p %d %p %x) Unknown IUri's not supported for this action.\n",
7262 pIUri, ParseAction, dwFlags, pwzResult, cchResult, pcchResult, (DWORD)dwReserved);
7263 return E_NOTIMPL;
7266 info = get_protocol_info(uri->canon_uri);
7267 if(info) {
7268 hr = IInternetProtocolInfo_ParseUrl(info, uri->canon_uri, ParseAction, dwFlags,
7269 pwzResult, cchResult, pcchResult, 0);
7270 IInternetProtocolInfo_Release(info);
7271 if(SUCCEEDED(hr)) return hr;
7274 switch(ParseAction) {
7275 case PARSE_CANONICALIZE:
7276 hr = parse_canonicalize(uri, dwFlags, pwzResult, cchResult, pcchResult);
7277 break;
7278 case PARSE_FRIENDLY:
7279 hr = parse_friendly(pIUri, pwzResult, cchResult, pcchResult);
7280 break;
7281 case PARSE_ROOTDOCUMENT:
7282 hr = parse_rootdocument(uri, pwzResult, cchResult, pcchResult);
7283 break;
7284 case PARSE_DOCUMENT:
7285 hr = parse_document(uri, pwzResult, cchResult, pcchResult);
7286 break;
7287 case PARSE_PATH_FROM_URL:
7288 hr = parse_path_from_url(uri, pwzResult, cchResult, pcchResult);
7289 break;
7290 case PARSE_URL_FROM_PATH:
7291 hr = parse_url_from_path(pIUri, pwzResult, cchResult, pcchResult);
7292 break;
7293 case PARSE_SCHEMA:
7294 hr = parse_schema(pIUri, pwzResult, cchResult, pcchResult);
7295 break;
7296 case PARSE_SITE:
7297 hr = parse_site(pIUri, pwzResult, cchResult, pcchResult);
7298 break;
7299 case PARSE_DOMAIN:
7300 hr = parse_domain(pIUri, pwzResult, cchResult, pcchResult);
7301 break;
7302 case PARSE_LOCATION:
7303 case PARSE_ANCHOR:
7304 hr = parse_anchor(pIUri, pwzResult, cchResult, pcchResult);
7305 break;
7306 case PARSE_SECURITY_URL:
7307 case PARSE_MIME:
7308 case PARSE_SERVER:
7309 case PARSE_SECURITY_DOMAIN:
7310 *pcchResult = 0;
7311 hr = E_FAIL;
7312 break;
7313 default:
7314 *pcchResult = 0;
7315 hr = E_NOTIMPL;
7316 FIXME("(%p %d %x %p %d %p %x) Partial stub.\n", pIUri, ParseAction, dwFlags,
7317 pwzResult, cchResult, pcchResult, (DWORD)dwReserved);
7320 return hr;