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 "urlmon_main.h"
21 #include "wine/debug.h"
23 #define NO_SHLWAPI_REG
26 #define UINT_MAX 0xffffffff
27 #define USHORT_MAX 0xffff
29 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
31 static const IID IID_IUriObj = {0x4b364760,0x9f51,0x11df,{0x98,0x1c,0x08,0x00,0x20,0x0c,0x9a,0x66}};
34 const IUriVtbl *lpIUriVtbl;
39 /* Information about the canonicalized URI's buffer. */
43 BOOL display_absolute;
47 URL_SCHEME scheme_type;
55 Uri_HOST_TYPE host_type;
77 const IUriBuilderVtbl *lpIUriBuilderVtbl;
114 /* IPv6 addresses can hold up to 8 h16 components. */
118 /* An IPv6 can have 1 elision ("::"). */
119 const WCHAR *elision;
121 /* An IPv6 can contain 1 IPv4 address as the last 32bits of the address. */
134 BOOL has_implicit_scheme;
135 BOOL has_implicit_ip;
140 URL_SCHEME scheme_type;
142 const WCHAR *userinfo;
148 Uri_HOST_TYPE host_type;
151 ipv6_address ipv6_address;
163 const WCHAR *fragment;
167 static const CHAR hexDigits[] = "0123456789ABCDEF";
169 /* List of scheme types/scheme names that are recognized by the IUri interface as of IE 7. */
170 static const struct {
172 WCHAR scheme_name[16];
173 } recognized_schemes[] = {
174 {URL_SCHEME_FTP, {'f','t','p',0}},
175 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
176 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
177 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
178 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
179 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
180 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
181 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
182 {URL_SCHEME_FILE, {'f','i','l','e',0}},
183 {URL_SCHEME_MK, {'m','k',0}},
184 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
185 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
186 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
187 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
188 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
189 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
190 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
191 {URL_SCHEME_RES, {'r','e','s',0}},
192 {URL_SCHEME_MSSHELLROOTED, {'m','s','-','s','h','e','l','l','-','r','o','o','t','e','d',0}},
193 {URL_SCHEME_MSSHELLIDLIST, {'m','s','-','s','h','e','l','l','-','i','d','l','i','s','t',0}},
194 {URL_SCHEME_MSHELP, {'h','c','p',0}},
195 {URL_SCHEME_WILDCARD, {'*',0}}
198 /* List of default ports Windows recognizes. */
199 static const struct {
202 } default_ports[] = {
203 {URL_SCHEME_FTP, 21},
204 {URL_SCHEME_HTTP, 80},
205 {URL_SCHEME_GOPHER, 70},
206 {URL_SCHEME_NNTP, 119},
207 {URL_SCHEME_TELNET, 23},
208 {URL_SCHEME_WAIS, 210},
209 {URL_SCHEME_HTTPS, 443},
212 /* List of 3 character top level domain names Windows seems to recognize.
213 * There might be more, but, these are the only ones I've found so far.
215 static const struct {
217 } recognized_tlds[] = {
227 static Uri *get_uri_obj(IUri *uri)
232 hres = IUri_QueryInterface(uri, &IID_IUriObj, (void**)&ret);
233 return SUCCEEDED(hres) ? ret : NULL;
236 static inline BOOL is_alpha(WCHAR val) {
237 return ((val >= 'a' && val <= 'z') || (val >= 'A' && val <= 'Z'));
240 static inline BOOL is_num(WCHAR val) {
241 return (val >= '0' && val <= '9');
244 static inline BOOL is_drive_path(const WCHAR *str) {
245 return (is_alpha(str[0]) && (str[1] == ':' || str[1] == '|'));
248 static inline BOOL is_unc_path(const WCHAR *str) {
249 return (str[0] == '\\' && str[0] == '\\');
252 static inline BOOL is_forbidden_dos_path_char(WCHAR val) {
253 return (val == '>' || val == '<' || val == '\"');
256 /* A URI is implicitly a file path if it begins with
257 * a drive letter (eg X:) or starts with "\\" (UNC path).
259 static inline BOOL is_implicit_file_path(const WCHAR *str) {
260 return (is_unc_path(str) || (is_alpha(str[0]) && str[1] == ':'));
263 /* Checks if the URI is a hierarchical URI. A hierarchical
264 * URI is one that has "//" after the scheme.
266 static BOOL check_hierarchical(const WCHAR **ptr) {
267 const WCHAR *start = *ptr;
282 /* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" */
283 static inline BOOL is_unreserved(WCHAR val) {
284 return (is_alpha(val) || is_num(val) || val == '-' || val == '.' ||
285 val == '_' || val == '~');
288 /* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
289 * / "*" / "+" / "," / ";" / "="
291 static inline BOOL is_subdelim(WCHAR val) {
292 return (val == '!' || val == '$' || val == '&' ||
293 val == '\'' || val == '(' || val == ')' ||
294 val == '*' || val == '+' || val == ',' ||
295 val == ';' || val == '=');
298 /* gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" */
299 static inline BOOL is_gendelim(WCHAR val) {
300 return (val == ':' || val == '/' || val == '?' ||
301 val == '#' || val == '[' || val == ']' ||
305 /* Characters that delimit the end of the authority
306 * section of a URI. Sometimes a '\\' is considered
307 * an authority delimeter.
309 static inline BOOL is_auth_delim(WCHAR val, BOOL acceptSlash) {
310 return (val == '#' || val == '/' || val == '?' ||
311 val == '\0' || (acceptSlash && val == '\\'));
314 /* reserved = gen-delims / sub-delims */
315 static inline BOOL is_reserved(WCHAR val) {
316 return (is_subdelim(val) || is_gendelim(val));
319 static inline BOOL is_hexdigit(WCHAR val) {
320 return ((val >= 'a' && val <= 'f') ||
321 (val >= 'A' && val <= 'F') ||
322 (val >= '0' && val <= '9'));
325 static inline BOOL is_path_delim(WCHAR val) {
326 return (!val || val == '#' || val == '?');
329 /* List of schemes types Windows seems to expect to be hierarchical. */
330 static inline BOOL is_hierarchical_scheme(URL_SCHEME type) {
331 return(type == URL_SCHEME_HTTP || type == URL_SCHEME_FTP ||
332 type == URL_SCHEME_GOPHER || type == URL_SCHEME_NNTP ||
333 type == URL_SCHEME_TELNET || type == URL_SCHEME_WAIS ||
334 type == URL_SCHEME_FILE || type == URL_SCHEME_HTTPS ||
335 type == URL_SCHEME_RES);
338 /* Determines if the URI is hierarchical using the information already parsed into
339 * data and using the current location of parsing in the URI string.
341 * Windows considers a URI hierarchical if on of the following is true:
342 * A.) It's a wildcard scheme.
343 * B.) It's an implicit file scheme.
344 * C.) It's a known hierarchical scheme and it has two '\\' after the scheme name.
345 * (the '\\' will be converted into "//" during canonicalization).
346 * D.) It's not a relative URI and "//" appears after the scheme name.
348 static inline BOOL is_hierarchical_uri(const WCHAR **ptr, const parse_data *data) {
349 const WCHAR *start = *ptr;
351 if(data->scheme_type == URL_SCHEME_WILDCARD)
353 else if(data->scheme_type == URL_SCHEME_FILE && data->has_implicit_scheme)
355 else if(is_hierarchical_scheme(data->scheme_type) && (*ptr)[0] == '\\' && (*ptr)[1] == '\\') {
358 } else if(!data->is_relative && check_hierarchical(ptr))
365 /* Checks if the two Uri's are logically equivalent. It's a simple
366 * comparison, since they are both of type Uri, and it can access
367 * the properties of each Uri directly without the need to go
368 * through the "IUri_Get*" interface calls.
370 static BOOL are_equal_simple(const Uri *a, const Uri *b) {
371 if(a->scheme_type == b->scheme_type) {
372 const BOOL known_scheme = a->scheme_type != URL_SCHEME_UNKNOWN;
373 const BOOL are_hierarchical =
374 (a->authority_start > -1 && b->authority_start > -1);
376 if(a->scheme_type == URL_SCHEME_FILE) {
377 if(a->canon_len == b->canon_len)
378 return !StrCmpIW(a->canon_uri, b->canon_uri);
381 /* Only compare the scheme names (if any) if their unknown scheme types. */
383 if((a->scheme_start > -1 && b->scheme_start > -1) &&
384 (a->scheme_len == b->scheme_len)) {
385 /* Make sure the schemes are the same. */
386 if(StrCmpNW(a->canon_uri+a->scheme_start, b->canon_uri+b->scheme_start, a->scheme_len))
388 } else if(a->scheme_len != b->scheme_len)
389 /* One of the Uri's has a scheme name, while the other doesn't. */
393 /* If they have a userinfo component, perform case sensitive compare. */
394 if((a->userinfo_start > -1 && b->userinfo_start > -1) &&
395 (a->userinfo_len == b->userinfo_len)) {
396 if(StrCmpNW(a->canon_uri+a->userinfo_start, b->canon_uri+b->userinfo_start, a->userinfo_len))
398 } else if(a->userinfo_len != b->userinfo_len)
399 /* One of the Uri's had a userinfo, while the other one doesn't. */
402 /* Check if they have a host name. */
403 if((a->host_start > -1 && b->host_start > -1) &&
404 (a->host_len == b->host_len)) {
405 /* Perform a case insensitive compare if they are a known scheme type. */
407 if(StrCmpNIW(a->canon_uri+a->host_start, b->canon_uri+b->host_start, a->host_len))
409 } else if(StrCmpNW(a->canon_uri+a->host_start, b->canon_uri+b->host_start, a->host_len))
411 } else if(a->host_len != b->host_len)
412 /* One of the Uri's had a host, while the other one didn't. */
415 if(a->has_port && b->has_port) {
416 if(a->port != b->port)
418 } else if(a->has_port || b->has_port)
419 /* One had a port, while the other one didn't. */
422 /* Windows is weird with how it handles paths. For example
423 * One URI could be "http://google.com" (after canonicalization)
424 * and one could be "http://google.com/" and the IsEqual function
425 * would still evaluate to TRUE, but, only if they are both hierarchical
428 if((a->path_start > -1 && b->path_start > -1) &&
429 (a->path_len == b->path_len)) {
430 if(StrCmpNW(a->canon_uri+a->path_start, b->canon_uri+b->path_start, a->path_len))
432 } else if(are_hierarchical && a->path_len == -1 && b->path_len == 0) {
433 if(*(a->canon_uri+a->path_start) != '/')
435 } else if(are_hierarchical && b->path_len == 1 && a->path_len == 0) {
436 if(*(b->canon_uri+b->path_start) != '/')
438 } else if(a->path_len != b->path_len)
441 /* Compare the query strings of the two URIs. */
442 if((a->query_start > -1 && b->query_start > -1) &&
443 (a->query_len == b->query_len)) {
444 if(StrCmpNW(a->canon_uri+a->query_start, b->canon_uri+b->query_start, a->query_len))
446 } else if(a->query_len != b->query_len)
449 if((a->fragment_start > -1 && b->fragment_start > -1) &&
450 (a->fragment_len == b->fragment_len)) {
451 if(StrCmpNW(a->canon_uri+a->fragment_start, b->canon_uri+b->fragment_start, a->fragment_len))
453 } else if(a->fragment_len != b->fragment_len)
456 /* If we get here, the two URIs are equivalent. */
463 /* Computes the size of the given IPv6 address.
464 * Each h16 component is 16bits, if there is an IPv4 address, it's
465 * 32bits. If there's an elision it can be 16bits to 128bits, depending
466 * on the number of other components.
468 * Modeled after google-url's CheckIPv6ComponentsSize function
470 static void compute_ipv6_comps_size(ipv6_address *address) {
471 address->components_size = address->h16_count * 2;
474 /* IPv4 address is 4 bytes. */
475 address->components_size += 4;
477 if(address->elision) {
478 /* An elision can be anywhere from 2 bytes up to 16 bytes.
479 * It size depends on the size of the h16 and IPv4 components.
481 address->elision_size = 16 - address->components_size;
482 if(address->elision_size < 2)
483 address->elision_size = 2;
485 address->elision_size = 0;
488 /* Taken from dlls/jscript/lex.c */
489 static int hex_to_int(WCHAR val) {
490 if(val >= '0' && val <= '9')
492 else if(val >= 'a' && val <= 'f')
493 return val - 'a' + 10;
494 else if(val >= 'A' && val <= 'F')
495 return val - 'A' + 10;
500 /* Helper function for converting a percent encoded string
501 * representation of a WCHAR value into its actual WCHAR value. If
502 * the two characters following the '%' aren't valid hex values then
503 * this function returns the NULL character.
506 * "%2E" will result in '.' being returned by this function.
508 static WCHAR decode_pct_val(const WCHAR *ptr) {
511 if(*ptr == '%' && is_hexdigit(*(ptr + 1)) && is_hexdigit(*(ptr + 2))) {
512 INT a = hex_to_int(*(ptr + 1));
513 INT b = hex_to_int(*(ptr + 2));
522 /* Helper function for percent encoding a given character
523 * and storing the encoded value into a given buffer (dest).
525 * It's up to the calling function to ensure that there is
526 * at least enough space in 'dest' for the percent encoded
527 * value to be stored (so dest + 3 spaces available).
529 static inline void pct_encode_val(WCHAR val, WCHAR *dest) {
531 dest[1] = hexDigits[(val >> 4) & 0xf];
532 dest[2] = hexDigits[val & 0xf];
535 /* Scans the range of characters [str, end] and returns the last occurrence
536 * of 'ch' or returns NULL.
538 static const WCHAR *str_last_of(const WCHAR *str, const WCHAR *end, WCHAR ch) {
539 const WCHAR *ptr = end;
550 /* Attempts to parse the domain name from the host.
552 * This function also includes the Top-level Domain (TLD) name
553 * of the host when it tries to find the domain name. If it finds
554 * a valid domain name it will assign 'domain_start' the offset
555 * into 'host' where the domain name starts.
557 * It's implied that if a domain name its range is implied to be
558 * [host+domain_start, host+host_len).
560 static void find_domain_name(const WCHAR *host, DWORD host_len,
562 const WCHAR *last_tld, *sec_last_tld, *end;
564 end = host+host_len-1;
568 /* There has to be at least enough room for a '.' followed by a
569 * 3 character TLD for a domain to even exist in the host name.
574 last_tld = str_last_of(host, end, '.');
576 /* http://hostname -> has no domain name. */
579 sec_last_tld = str_last_of(host, last_tld-1, '.');
581 /* If the '.' is at the beginning of the host there
582 * has to be at least 3 characters in the TLD for it
584 * Ex: .com -> .com as the domain name.
585 * .co -> has no domain name.
587 if(last_tld-host == 0) {
588 if(end-(last_tld-1) < 3)
590 } else if(last_tld-host == 3) {
593 /* If there's three characters in front of last_tld and
594 * they are on the list of recognized TLDs, then this
595 * host doesn't have a domain (since the host only contains
597 * Ex: edu.uk -> has no domain name.
598 * foo.uk -> foo.uk as the domain name.
600 for(i = 0; i < sizeof(recognized_tlds)/sizeof(recognized_tlds[0]); ++i) {
601 if(!StrCmpNIW(host, recognized_tlds[i].tld_name, 3))
604 } else if(last_tld-host < 3)
605 /* Anything less than 3 characters is considered part
607 * Ex: ak.uk -> Has no domain name.
611 /* Otherwise the domain name is the whole host name. */
613 } else if(end+1-last_tld > 3) {
614 /* If the last_tld has more than 3 characters, then it's automatically
615 * considered the TLD of the domain name.
616 * Ex: www.winehq.org.uk.test -> uk.test as the domain name.
618 *domain_start = (sec_last_tld+1)-host;
619 } else if(last_tld - (sec_last_tld+1) < 4) {
621 /* If the sec_last_tld is 3 characters long it HAS to be on the list of
622 * recognized to still be considered part of the TLD name, otherwise
623 * its considered the domain name.
624 * Ex: www.google.com.uk -> google.com.uk as the domain name.
625 * www.google.foo.uk -> foo.uk as the domain name.
627 if(last_tld - (sec_last_tld+1) == 3) {
628 for(i = 0; i < sizeof(recognized_tlds)/sizeof(recognized_tlds[0]); ++i) {
629 if(!StrCmpNIW(sec_last_tld+1, recognized_tlds[i].tld_name, 3)) {
630 const WCHAR *domain = str_last_of(host, sec_last_tld-1, '.');
635 *domain_start = (domain+1) - host;
636 TRACE("Found domain name %s\n", debugstr_wn(host+*domain_start,
637 (host+host_len)-(host+*domain_start)));
642 *domain_start = (sec_last_tld+1)-host;
644 /* Since the sec_last_tld is less than 3 characters it's considered
646 * Ex: www.google.fo.uk -> google.fo.uk as the domain name.
648 const WCHAR *domain = str_last_of(host, sec_last_tld-1, '.');
653 *domain_start = (domain+1) - host;
656 /* The second to last TLD has more than 3 characters making it
658 * Ex: www.google.test.us -> test.us as the domain name.
660 *domain_start = (sec_last_tld+1)-host;
663 TRACE("Found domain name %s\n", debugstr_wn(host+*domain_start,
664 (host+host_len)-(host+*domain_start)));
667 /* Removes the dot segments from a hierarchical URIs path component. This
668 * function performs the removal in place.
670 * This is a modified version of Qt's QUrl function "removeDotsFromPath".
672 * This function returns the new length of the path string.
674 static DWORD remove_dot_segments(WCHAR *path, DWORD path_len) {
676 const WCHAR *in = out;
677 const WCHAR *end = out + path_len;
681 /* A. if the input buffer begins with a prefix of "/./" or "/.",
682 * where "." is a complete path segment, then replace that
683 * prefix with "/" in the input buffer; otherwise,
685 if(in <= end - 3 && in[0] == '/' && in[1] == '.' && in[2] == '/') {
688 } else if(in == end - 2 && in[0] == '/' && in[1] == '.') {
694 /* B. if the input buffer begins with a prefix of "/../" or "/..",
695 * where ".." is a complete path segment, then replace that
696 * prefix with "/" in the input buffer and remove the last
697 * segment and its preceding "/" (if any) from the output
700 if(in <= end - 4 && in[0] == '/' && in[1] == '.' && in[2] == '.' && in[3] == '/') {
701 while(out > path && *(--out) != '/');
705 } else if(in == end - 3 && in[0] == '/' && in[1] == '.' && in[2] == '.') {
706 while(out > path && *(--out) != '/');
715 /* C. move the first path segment in the input buffer to the end of
716 * the output buffer, including the initial "/" character (if
717 * any) and any subsequent characters up to, but not including,
718 * the next "/" character or the end of the input buffer.
721 while(in < end && *in != '/')
726 TRACE("(%p %d): Path after dot segments removed %s len=%d\n", path, path_len,
727 debugstr_wn(path, len), len);
731 /* Attempts to find the file extension in a given path. */
732 static INT find_file_extension(const WCHAR *path, DWORD path_len) {
735 for(end = path+path_len-1; end >= path && *end != '/' && *end != '\\'; --end) {
743 /* Computes the location where the elision should occur in the IPv6
744 * address using the numerical values of each component stored in
745 * 'values'. If the address shouldn't contain an elision then 'index'
746 * is assigned -1 as it's value. Otherwise 'index' will contain the
747 * starting index (into values) where the elision should be, and 'count'
748 * will contain the number of cells the elision covers.
751 * Windows will expand an elision if the elision only represents 1 h16
752 * component of the URI.
754 * Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
756 * If the IPv6 address contains an IPv4 address, the IPv4 address is also
757 * considered for being included as part of an elision if all it's components
760 * Ex: [1:2:3:4:5:6:0.0.0.0] -> [1:2:3:4:5:6::]
762 static void compute_elision_location(const ipv6_address *address, const USHORT values[8],
763 INT *index, DWORD *count) {
764 DWORD i, max_len, cur_len;
765 INT max_index, cur_index;
767 max_len = cur_len = 0;
768 max_index = cur_index = -1;
769 for(i = 0; i < 8; ++i) {
770 BOOL check_ipv4 = (address->ipv4 && i == 6);
771 BOOL is_end = (check_ipv4 || i == 7);
774 /* Check if the IPv4 address contains only zeros. */
775 if(values[i] == 0 && values[i+1] == 0) {
782 } else if(values[i] == 0) {
789 if(is_end || values[i] != 0) {
790 /* We only consider it for an elision if it's
791 * more than 1 component long.
793 if(cur_len > 1 && cur_len > max_len) {
794 /* Found the new elision location. */
796 max_index = cur_index;
799 /* Reset the current range for the next range of zeros. */
809 /* Removes all the leading and trailing white spaces or
810 * control characters from the URI and removes all control
811 * characters inside of the URI string.
813 static BSTR pre_process_uri(LPCWSTR uri) {
816 const WCHAR *start, *end;
822 /* Skip leading controls and whitespace. */
823 while(iscntrlW(*start) || isspaceW(*start)) ++start;
827 /* URI consisted only of control/whitespace. */
828 ret = SysAllocStringLen(NULL, 0);
830 while(iscntrlW(*end) || isspaceW(*end)) --end;
832 buf = heap_alloc(((end+1)-start)*sizeof(WCHAR));
836 for(ptr = buf; start < end+1; ++start) {
837 if(!iscntrlW(*start))
841 ret = SysAllocStringLen(buf, ptr-buf);
848 /* Converts the specified IPv4 address into an uint value.
850 * This function assumes that the IPv4 address has already been validated.
852 static UINT ipv4toui(const WCHAR *ip, DWORD len) {
854 DWORD comp_value = 0;
857 for(ptr = ip; ptr < ip+len; ++ptr) {
863 comp_value = comp_value*10 + (*ptr-'0');
872 /* Converts an IPv4 address in numerical form into it's fully qualified
873 * string form. This function returns the number of characters written
874 * to 'dest'. If 'dest' is NULL this function will return the number of
875 * characters that would have been written.
877 * It's up to the caller to ensure there's enough space in 'dest' for the
880 static DWORD ui2ipv4(WCHAR *dest, UINT address) {
881 static const WCHAR formatW[] =
882 {'%','u','.','%','u','.','%','u','.','%','u',0};
886 digits[0] = (address >> 24) & 0xff;
887 digits[1] = (address >> 16) & 0xff;
888 digits[2] = (address >> 8) & 0xff;
889 digits[3] = address & 0xff;
893 ret = sprintfW(tmp, formatW, digits[0], digits[1], digits[2], digits[3]);
895 ret = sprintfW(dest, formatW, digits[0], digits[1], digits[2], digits[3]);
900 /* Converts an h16 component (from an IPv6 address) into it's
903 * This function assumes that the h16 component has already been validated.
905 static USHORT h16tous(h16 component) {
909 for(i = 0; i < component.len; ++i) {
911 ret += hex_to_int(component.str[i]);
917 /* Converts an IPv6 address into it's 128 bits (16 bytes) numerical value.
919 * This function assumes that the ipv6_address has already been validated.
921 static BOOL ipv6_to_number(const ipv6_address *address, USHORT number[8]) {
922 DWORD i, cur_component = 0;
923 BOOL already_passed_elision = FALSE;
925 for(i = 0; i < address->h16_count; ++i) {
926 if(address->elision) {
927 if(address->components[i].str > address->elision && !already_passed_elision) {
928 /* Means we just passed the elision and need to add it's values to
929 * 'number' before we do anything else.
932 for(j = 0; j < address->elision_size; j+=2)
933 number[cur_component++] = 0;
935 already_passed_elision = TRUE;
939 number[cur_component++] = h16tous(address->components[i]);
942 /* Case when the elision appears after the h16 components. */
943 if(!already_passed_elision && address->elision) {
944 for(i = 0; i < address->elision_size; i+=2)
945 number[cur_component++] = 0;
946 already_passed_elision = TRUE;
950 UINT value = ipv4toui(address->ipv4, address->ipv4_len);
952 if(cur_component != 6) {
953 ERR("(%p %p): Failed sanity check with %d\n", address, number, cur_component);
957 number[cur_component++] = (value >> 16) & 0xffff;
958 number[cur_component] = value & 0xffff;
964 /* Checks if the characters pointed to by 'ptr' are
965 * a percent encoded data octet.
967 * pct-encoded = "%" HEXDIG HEXDIG
969 static BOOL check_pct_encoded(const WCHAR **ptr) {
970 const WCHAR *start = *ptr;
976 if(!is_hexdigit(**ptr)) {
982 if(!is_hexdigit(**ptr)) {
991 /* dec-octet = DIGIT ; 0-9
992 * / %x31-39 DIGIT ; 10-99
993 * / "1" 2DIGIT ; 100-199
994 * / "2" %x30-34 DIGIT ; 200-249
995 * / "25" %x30-35 ; 250-255
997 static BOOL check_dec_octet(const WCHAR **ptr) {
998 const WCHAR *c1, *c2, *c3;
1001 /* A dec-octet must be at least 1 digit long. */
1002 if(*c1 < '0' || *c1 > '9')
1008 /* Since the 1 digit requirment was meet, it doesn't
1009 * matter if this is a DIGIT value, it's considered a
1012 if(*c2 < '0' || *c2 > '9')
1018 /* Same explanation as above. */
1019 if(*c3 < '0' || *c3 > '9')
1022 /* Anything > 255 isn't a valid IP dec-octet. */
1023 if(*c1 >= '2' && *c2 >= '5' && *c3 >= '5') {
1032 /* Checks if there is an implicit IPv4 address in the host component of the URI.
1033 * The max value of an implicit IPv4 address is UINT_MAX.
1036 * "234567" would be considered an implicit IPv4 address.
1038 static BOOL check_implicit_ipv4(const WCHAR **ptr, UINT *val) {
1039 const WCHAR *start = *ptr;
1043 while(is_num(**ptr)) {
1044 ret = ret*10 + (**ptr - '0');
1046 if(ret > UINT_MAX) {
1060 /* Checks if the string contains an IPv4 address.
1062 * This function has a strict mode or a non-strict mode of operation
1063 * When 'strict' is set to FALSE this function will return TRUE if
1064 * the string contains at least 'dec-octet "." dec-octet' since partial
1065 * IPv4 addresses will be normalized out into full IPv4 addresses. When
1066 * 'strict' is set this function expects there to be a full IPv4 address.
1068 * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
1070 static BOOL check_ipv4address(const WCHAR **ptr, BOOL strict) {
1071 const WCHAR *start = *ptr;
1073 if(!check_dec_octet(ptr)) {
1084 if(!check_dec_octet(ptr)) {
1098 if(!check_dec_octet(ptr)) {
1112 if(!check_dec_octet(ptr)) {
1117 /* Found a four digit ip address. */
1120 /* Tries to parse the scheme name of the URI.
1122 * scheme = ALPHA *(ALPHA | NUM | '+' | '-' | '.') as defined by RFC 3896.
1123 * NOTE: Windows accepts a number as the first character of a scheme.
1125 static BOOL parse_scheme_name(const WCHAR **ptr, parse_data *data) {
1126 const WCHAR *start = *ptr;
1128 data->scheme = NULL;
1129 data->scheme_len = 0;
1132 if(**ptr == '*' && *ptr == start) {
1133 /* Might have found a wildcard scheme. If it is the next
1134 * char has to be a ':' for it to be a valid URI
1138 } else if(!is_num(**ptr) && !is_alpha(**ptr) && **ptr != '+' &&
1139 **ptr != '-' && **ptr != '.')
1148 /* Schemes must end with a ':' */
1154 data->scheme = start;
1155 data->scheme_len = *ptr - start;
1161 /* Tries to deduce the corresponding URL_SCHEME for the given URI. Stores
1162 * the deduced URL_SCHEME in data->scheme_type.
1164 static BOOL parse_scheme_type(parse_data *data) {
1165 /* If there's scheme data then see if it's a recognized scheme. */
1166 if(data->scheme && data->scheme_len) {
1169 for(i = 0; i < sizeof(recognized_schemes)/sizeof(recognized_schemes[0]); ++i) {
1170 if(lstrlenW(recognized_schemes[i].scheme_name) == data->scheme_len) {
1171 /* Has to be a case insensitive compare. */
1172 if(!StrCmpNIW(recognized_schemes[i].scheme_name, data->scheme, data->scheme_len)) {
1173 data->scheme_type = recognized_schemes[i].scheme;
1179 /* If we get here it means it's not a recognized scheme. */
1180 data->scheme_type = URL_SCHEME_UNKNOWN;
1182 } else if(data->is_relative) {
1183 /* Relative URI's have no scheme. */
1184 data->scheme_type = URL_SCHEME_UNKNOWN;
1187 /* Should never reach here! what happened... */
1188 FIXME("(%p): Unable to determine scheme type for URI %s\n", data, debugstr_w(data->uri));
1193 /* Tries to parse (or deduce) the scheme_name of a URI. If it can't
1194 * parse a scheme from the URI it will try to deduce the scheme_name and scheme_type
1195 * using the flags specified in 'flags' (if any). Flags that affect how this function
1196 * operates are the Uri_CREATE_ALLOW_* flags.
1198 * All parsed/deduced information will be stored in 'data' when the function returns.
1200 * Returns TRUE if it was able to successfully parse the information.
1202 static BOOL parse_scheme(const WCHAR **ptr, parse_data *data, DWORD flags) {
1203 static const WCHAR fileW[] = {'f','i','l','e',0};
1204 static const WCHAR wildcardW[] = {'*',0};
1206 /* First check to see if the uri could implicitly be a file path. */
1207 if(is_implicit_file_path(*ptr)) {
1208 if(flags & Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME) {
1209 data->scheme = fileW;
1210 data->scheme_len = lstrlenW(fileW);
1211 data->has_implicit_scheme = TRUE;
1213 TRACE("(%p %p %x): URI is an implicit file path.\n", ptr, data, flags);
1215 /* Window's does not consider anything that can implicitly be a file
1216 * path to be a valid URI if the ALLOW_IMPLICIT_FILE_SCHEME flag is not set...
1218 TRACE("(%p %p %x): URI is implicitly a file path, but, the ALLOW_IMPLICIT_FILE_SCHEME flag wasn't set.\n",
1222 } else if(!parse_scheme_name(ptr, data)) {
1223 /* No Scheme was found, this means it could be:
1224 * a) an implicit Wildcard scheme
1228 if(flags & Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME) {
1229 data->scheme = wildcardW;
1230 data->scheme_len = lstrlenW(wildcardW);
1231 data->has_implicit_scheme = TRUE;
1233 TRACE("(%p %p %x): URI is an implicit wildcard scheme.\n", ptr, data, flags);
1234 } else if (flags & Uri_CREATE_ALLOW_RELATIVE) {
1235 data->is_relative = TRUE;
1236 TRACE("(%p %p %x): URI is relative.\n", ptr, data, flags);
1238 TRACE("(%p %p %x): Malformed URI found. Unable to deduce scheme name.\n", ptr, data, flags);
1243 if(!data->is_relative)
1244 TRACE("(%p %p %x): Found scheme=%s scheme_len=%d\n", ptr, data, flags,
1245 debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
1247 if(!parse_scheme_type(data))
1250 TRACE("(%p %p %x): Assigned %d as the URL_SCHEME.\n", ptr, data, flags, data->scheme_type);
1254 /* Parses the userinfo part of the URI (if it exists). The userinfo field of
1255 * a URI can consist of "username:password@", or just "username@".
1258 * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
1261 * 1) If there is more than one ':' in the userinfo part of the URI Windows
1262 * uses the first occurrence of ':' to delimit the username and password
1266 * ftp://user:pass:word@winehq.org
1268 * Would yield, "user" as the username and "pass:word" as the password.
1270 * 2) Windows allows any character to appear in the "userinfo" part of
1271 * a URI, as long as it's not an authority delimeter character set.
1273 static void parse_userinfo(const WCHAR **ptr, parse_data *data, DWORD flags) {
1274 data->userinfo = *ptr;
1275 data->userinfo_split = -1;
1277 while(**ptr != '@') {
1278 if(**ptr == ':' && data->userinfo_split == -1)
1279 data->userinfo_split = *ptr - data->userinfo;
1280 else if(**ptr == '%') {
1281 /* If it's a known scheme type, it has to be a valid percent
1284 if(!check_pct_encoded(ptr)) {
1285 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1286 *ptr = data->userinfo;
1287 data->userinfo = NULL;
1288 data->userinfo_split = -1;
1290 TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
1295 } else if(is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN))
1302 *ptr = data->userinfo;
1303 data->userinfo = NULL;
1304 data->userinfo_split = -1;
1306 TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
1310 data->userinfo_len = *ptr - data->userinfo;
1311 TRACE("(%p %p %x): Found userinfo=%s userinfo_len=%d split=%d.\n", ptr, data, flags,
1312 debugstr_wn(data->userinfo, data->userinfo_len), data->userinfo_len, data->userinfo_split);
1316 /* Attempts to parse a port from the URI.
1319 * Windows seems to have a cap on what the maximum value
1320 * for a port can be. The max value is USHORT_MAX.
1324 static BOOL parse_port(const WCHAR **ptr, parse_data *data, DWORD flags) {
1328 while(!is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)) {
1329 if(!is_num(**ptr)) {
1335 port = port*10 + (**ptr-'0');
1337 if(port > USHORT_MAX) {
1346 data->port_value = port;
1347 data->port_len = *ptr - data->port;
1349 TRACE("(%p %p %x): Found port %s len=%d value=%u\n", ptr, data, flags,
1350 debugstr_wn(data->port, data->port_len), data->port_len, data->port_value);
1354 /* Attempts to parse a IPv4 address from the URI.
1357 * Window's normalizes IPv4 addresses, This means there's three
1358 * possibilities for the URI to contain an IPv4 address.
1359 * 1) A well formed address (ex. 192.2.2.2).
1360 * 2) A partially formed address. For example "192.0" would
1361 * normalize to "192.0.0.0" during canonicalization.
1362 * 3) An implicit IPv4 address. For example "256" would
1363 * normalize to "0.0.1.0" during canonicalization. Also
1364 * note that the maximum value for an implicit IP address
1365 * is UINT_MAX, if the value in the URI exceeds this then
1366 * it is not considered an IPv4 address.
1368 static BOOL parse_ipv4address(const WCHAR **ptr, parse_data *data, DWORD flags) {
1369 const BOOL is_unknown = data->scheme_type == URL_SCHEME_UNKNOWN;
1372 if(!check_ipv4address(ptr, FALSE)) {
1373 if(!check_implicit_ipv4(ptr, &data->implicit_ipv4)) {
1374 TRACE("(%p %p %x): URI didn't contain anything looking like an IPv4 address.\n",
1380 data->has_implicit_ip = TRUE;
1383 /* Check if what we found is the only part of the host name (if it isn't
1384 * we don't have an IPv4 address).
1388 if(!parse_port(ptr, data, flags)) {
1393 } else if(!is_auth_delim(**ptr, !is_unknown)) {
1394 /* Found more data which belongs the host, so this isn't an IPv4. */
1397 data->has_implicit_ip = FALSE;
1401 data->host_len = *ptr - data->host;
1402 data->host_type = Uri_HOST_IPV4;
1404 TRACE("(%p %p %x): IPv4 address found. host=%s host_len=%d host_type=%d\n",
1405 ptr, data, flags, debugstr_wn(data->host, data->host_len),
1406 data->host_len, data->host_type);
1410 /* Attempts to parse the reg-name from the URI.
1412 * Because of the way Windows handles ':' this function also
1413 * handles parsing the port.
1415 * reg-name = *( unreserved / pct-encoded / sub-delims )
1418 * Windows allows everything, but, the characters in "auth_delims" and ':'
1419 * to appear in a reg-name, unless it's an unknown scheme type then ':' is
1420 * allowed to appear (even if a valid port isn't after it).
1422 * Windows doesn't like host names which start with '[' and end with ']'
1423 * and don't contain a valid IP literal address in between them.
1425 * On Windows if an '[' is encountered in the host name the ':' no longer
1426 * counts as a delimiter until you reach the next ']' or an "authority delimeter".
1428 * A reg-name CAN be empty.
1430 static BOOL parse_reg_name(const WCHAR **ptr, parse_data *data, DWORD flags) {
1431 const BOOL has_start_bracket = **ptr == '[';
1432 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1433 BOOL inside_brackets = has_start_bracket;
1434 BOOL ignore_col = FALSE;
1436 /* We have to be careful with file schemes. */
1437 if(data->scheme_type == URL_SCHEME_FILE) {
1438 /* This is because an implicit file scheme could be "C:\\test" and it
1439 * would trick this function into thinking the host is "C", when after
1440 * canonicalization the host would end up being an empty string. A drive
1441 * path can also have a '|' instead of a ':' after the drive letter.
1443 if(is_drive_path(*ptr)) {
1444 /* Regular old drive paths don't have a host type (or host name). */
1445 data->host_type = Uri_HOST_UNKNOWN;
1449 } else if(is_unc_path(*ptr))
1450 /* Skip past the "\\" of a UNC path. */
1456 while(!is_auth_delim(**ptr, known_scheme)) {
1457 if(**ptr == ':' && !ignore_col) {
1458 /* We can ignore ':' if were inside brackets.*/
1459 if(!inside_brackets) {
1460 const WCHAR *tmp = (*ptr)++;
1462 /* Attempt to parse the port. */
1463 if(!parse_port(ptr, data, flags)) {
1464 /* Windows expects there to be a valid port for known scheme types. */
1465 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1468 TRACE("(%p %p %x): Expected valid port\n", ptr, data, flags);
1471 /* Windows gives up on trying to parse a port when it
1472 * encounters 1 invalid port.
1476 data->host_len = tmp - data->host;
1480 } else if(**ptr == '%' && known_scheme) {
1481 /* Has to be a legit % encoded value. */
1482 if(!check_pct_encoded(ptr)) {
1488 } else if(**ptr == ']')
1489 inside_brackets = FALSE;
1490 else if(**ptr == '[')
1491 inside_brackets = TRUE;
1496 if(has_start_bracket) {
1497 /* Make sure the last character of the host wasn't a ']'. */
1498 if(*(*ptr-1) == ']') {
1499 TRACE("(%p %p %x): Expected an IP literal inside of the host\n",
1507 /* Don't overwrite our length if we found a port earlier. */
1509 data->host_len = *ptr - data->host;
1511 /* If the host is empty, then it's an unknown host type. */
1512 if(data->host_len == 0)
1513 data->host_type = Uri_HOST_UNKNOWN;
1515 data->host_type = Uri_HOST_DNS;
1517 TRACE("(%p %p %x): Parsed reg-name. host=%s len=%d\n", ptr, data, flags,
1518 debugstr_wn(data->host, data->host_len), data->host_len);
1522 /* Attempts to parse an IPv6 address out of the URI.
1524 * IPv6address = 6( h16 ":" ) ls32
1525 * / "::" 5( h16 ":" ) ls32
1526 * / [ h16 ] "::" 4( h16 ":" ) ls32
1527 * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
1528 * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
1529 * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
1530 * / [ *4( h16 ":" ) h16 ] "::" ls32
1531 * / [ *5( h16 ":" ) h16 ] "::" h16
1532 * / [ *6( h16 ":" ) h16 ] "::"
1534 * ls32 = ( h16 ":" h16 ) / IPv4address
1535 * ; least-significant 32 bits of address.
1538 * ; 16 bits of address represented in hexadecimal.
1540 * Modeled after google-url's 'DoParseIPv6' function.
1542 static BOOL parse_ipv6address(const WCHAR **ptr, parse_data *data, DWORD flags) {
1543 const WCHAR *start, *cur_start;
1546 start = cur_start = *ptr;
1547 memset(&ip, 0, sizeof(ipv6_address));
1550 /* Check if we're on the last character of the host. */
1551 BOOL is_end = (is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)
1554 BOOL is_split = (**ptr == ':');
1555 BOOL is_elision = (is_split && !is_end && *(*ptr+1) == ':');
1557 /* Check if we're at the end of a component, or
1558 * if we're at the end of the IPv6 address.
1560 if(is_split || is_end) {
1563 cur_len = *ptr - cur_start;
1565 /* h16 can't have a length > 4. */
1569 TRACE("(%p %p %x): h16 component to long.\n",
1575 /* An h16 component can't have the length of 0 unless
1576 * the elision is at the beginning of the address, or
1577 * at the end of the address.
1579 if(!((*ptr == start && is_elision) ||
1580 (is_end && (*ptr-2) == ip.elision))) {
1582 TRACE("(%p %p %x): IPv6 component cannot have a length of 0.\n",
1589 /* An IPv6 address can have no more than 8 h16 components. */
1590 if(ip.h16_count >= 8) {
1592 TRACE("(%p %p %x): Not a IPv6 address, to many h16 components.\n",
1597 ip.components[ip.h16_count].str = cur_start;
1598 ip.components[ip.h16_count].len = cur_len;
1600 TRACE("(%p %p %x): Found h16 component %s, len=%d, h16_count=%d\n",
1601 ptr, data, flags, debugstr_wn(cur_start, cur_len), cur_len,
1611 /* A IPv6 address can only have 1 elision ('::'). */
1615 TRACE("(%p %p %x): IPv6 address cannot have 2 elisions.\n",
1627 if(!check_ipv4address(ptr, TRUE)) {
1628 if(!is_hexdigit(**ptr)) {
1629 /* Not a valid character for an IPv6 address. */
1634 /* Found an IPv4 address. */
1635 ip.ipv4 = cur_start;
1636 ip.ipv4_len = *ptr - cur_start;
1638 TRACE("(%p %p %x): Found an attached IPv4 address %s len=%d.\n",
1639 ptr, data, flags, debugstr_wn(ip.ipv4, ip.ipv4_len),
1642 /* IPv4 addresses can only appear at the end of a IPv6. */
1648 compute_ipv6_comps_size(&ip);
1650 /* Make sure the IPv6 address adds up to 16 bytes. */
1651 if(ip.components_size + ip.elision_size != 16) {
1653 TRACE("(%p %p %x): Invalid IPv6 address, did not add up to 16 bytes.\n",
1658 if(ip.elision_size == 2) {
1659 /* For some reason on Windows if an elision that represents
1660 * only 1 h16 component is encountered at the very begin or
1661 * end of an IPv6 address, Windows does not consider it a
1662 * valid IPv6 address.
1664 * Ex: [::2:3:4:5:6:7] is not valid, even though the sum
1665 * of all the components == 128bits.
1667 if(ip.elision < ip.components[0].str ||
1668 ip.elision > ip.components[ip.h16_count-1].str) {
1670 TRACE("(%p %p %x): Invalid IPv6 address. Detected elision of 2 bytes at the beginning or end of the address.\n",
1676 data->host_type = Uri_HOST_IPV6;
1677 data->has_ipv6 = TRUE;
1678 data->ipv6_address = ip;
1680 TRACE("(%p %p %x): Found valid IPv6 literal %s len=%d\n",
1681 ptr, data, flags, debugstr_wn(start, *ptr-start),
1686 /* IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) */
1687 static BOOL parse_ipvfuture(const WCHAR **ptr, parse_data *data, DWORD flags) {
1688 const WCHAR *start = *ptr;
1690 /* IPvFuture has to start with a 'v' or 'V'. */
1691 if(**ptr != 'v' && **ptr != 'V')
1694 /* Following the v there must be at least 1 hex digit. */
1696 if(!is_hexdigit(**ptr)) {
1702 while(is_hexdigit(**ptr))
1705 /* End of the hexdigit sequence must be a '.' */
1712 if(!is_unreserved(**ptr) && !is_subdelim(**ptr) && **ptr != ':') {
1718 while(is_unreserved(**ptr) || is_subdelim(**ptr) || **ptr == ':')
1721 data->host_type = Uri_HOST_UNKNOWN;
1723 TRACE("(%p %p %x): Parsed IPvFuture address %s len=%d\n", ptr, data, flags,
1724 debugstr_wn(start, *ptr-start), *ptr-start);
1729 /* IP-literal = "[" ( IPv6address / IPvFuture ) "]" */
1730 static BOOL parse_ip_literal(const WCHAR **ptr, parse_data *data, DWORD flags) {
1739 if(!parse_ipv6address(ptr, data, flags)) {
1740 if(!parse_ipvfuture(ptr, data, flags)) {
1756 /* If a valid port is not found, then let it trickle down to
1759 if(!parse_port(ptr, data, flags)) {
1765 data->host_len = *ptr - data->host;
1770 /* Parses the host information from the URI.
1772 * host = IP-literal / IPv4address / reg-name
1774 static BOOL parse_host(const WCHAR **ptr, parse_data *data, DWORD flags) {
1775 if(!parse_ip_literal(ptr, data, flags)) {
1776 if(!parse_ipv4address(ptr, data, flags)) {
1777 if(!parse_reg_name(ptr, data, flags)) {
1778 TRACE("(%p %p %x): Malformed URI, Unknown host type.\n",
1788 /* Parses the authority information from the URI.
1790 * authority = [ userinfo "@" ] host [ ":" port ]
1792 static BOOL parse_authority(const WCHAR **ptr, parse_data *data, DWORD flags) {
1793 parse_userinfo(ptr, data, flags);
1795 /* Parsing the port will happen during one of the host parsing
1796 * routines (if the URI has a port).
1798 if(!parse_host(ptr, data, flags))
1804 /* Attempts to parse the path information of a hierarchical URI. */
1805 static BOOL parse_path_hierarchical(const WCHAR **ptr, parse_data *data, DWORD flags) {
1806 const WCHAR *start = *ptr;
1807 static const WCHAR slash[] = {'/',0};
1808 const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
1810 if(is_path_delim(**ptr)) {
1811 if(data->scheme_type == URL_SCHEME_WILDCARD) {
1812 /* Wildcard schemes don't get a '/' attached if their path is
1817 } else if(!(flags & Uri_CREATE_NO_CANONICALIZE)) {
1818 /* If the path component is empty, then a '/' is added. */
1823 while(!is_path_delim(**ptr)) {
1824 if(**ptr == '%' && data->scheme_type != URL_SCHEME_UNKNOWN && !is_file) {
1825 if(!check_pct_encoded(ptr)) {
1830 } else if(is_forbidden_dos_path_char(**ptr) && is_file &&
1831 (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
1832 /* File schemes with USE_DOS_PATH set aren't allowed to have
1833 * a '<' or '>' or '\"' appear in them.
1837 } else if(**ptr == '\\') {
1838 /* Not allowed to have a backslash if NO_CANONICALIZE is set
1839 * and the scheme is known type (but not a file scheme).
1841 if(flags & Uri_CREATE_NO_CANONICALIZE) {
1842 if(data->scheme_type != URL_SCHEME_FILE &&
1843 data->scheme_type != URL_SCHEME_UNKNOWN) {
1853 /* The only time a URI doesn't have a path is when
1854 * the NO_CANONICALIZE flag is set and the raw URI
1855 * didn't contain one.
1862 data->path_len = *ptr - start;
1867 TRACE("(%p %p %x): Parsed path %s len=%d\n", ptr, data, flags,
1868 debugstr_wn(data->path, data->path_len), data->path_len);
1870 TRACE("(%p %p %x): The URI contained no path\n", ptr, data, flags);
1875 /* Parses the path of a opaque URI (much less strict then the parser
1876 * for a hierarchical URI).
1879 * Windows allows invalid % encoded data to appear in opaque URI paths
1880 * for unknown scheme types.
1882 * File schemes with USE_DOS_PATH set aren't allowed to have '<', '>', or '\"'
1885 static BOOL parse_path_opaque(const WCHAR **ptr, parse_data *data, DWORD flags) {
1886 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1887 const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
1891 while(!is_path_delim(**ptr)) {
1892 if(**ptr == '%' && known_scheme) {
1893 if(!check_pct_encoded(ptr)) {
1899 } else if(is_forbidden_dos_path_char(**ptr) && is_file &&
1900 (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
1909 data->path_len = *ptr - data->path;
1910 TRACE("(%p %p %x): Parsed opaque URI path %s len=%d\n", ptr, data, flags,
1911 debugstr_wn(data->path, data->path_len), data->path_len);
1915 /* Determines how the URI should be parsed after the scheme information.
1917 * If the scheme is followed, by "//" then, it is treated as an hierarchical URI
1918 * which then the authority and path information will be parsed out. Otherwise, the
1919 * URI will be treated as an opaque URI which the authority information is not parsed
1922 * RFC 3896 definition of hier-part:
1924 * hier-part = "//" authority path-abempty
1929 * MSDN opaque URI definition:
1930 * scheme ":" path [ "#" fragment ]
1933 * If the URI is of an unknown scheme type and has a "//" following the scheme then it
1934 * is treated as a hierarchical URI, but, if the CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is
1935 * set then it is considered an opaque URI reguardless of what follows the scheme information
1936 * (per MSDN documentation).
1938 static BOOL parse_hierpart(const WCHAR **ptr, parse_data *data, DWORD flags) {
1939 const WCHAR *start = *ptr;
1941 /* Checks if the authority information needs to be parsed. */
1942 if(is_hierarchical_uri(ptr, data)) {
1943 /* Only treat it as a hierarchical URI if the scheme_type is known or
1944 * the Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is not set.
1946 if(data->scheme_type != URL_SCHEME_UNKNOWN ||
1947 !(flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES)) {
1948 TRACE("(%p %p %x): Treating URI as an hierarchical URI.\n", ptr, data, flags);
1949 data->is_opaque = FALSE;
1951 /* TODO: Handle hierarchical URI's, parse authority then parse the path. */
1952 if(!parse_authority(ptr, data, flags))
1955 return parse_path_hierarchical(ptr, data, flags);
1957 /* Reset ptr to it's starting position so opaque path parsing
1958 * begins at the correct location.
1963 /* If it reaches here, then the URI will be treated as an opaque
1967 TRACE("(%p %p %x): Treating URI as an opaque URI.\n", ptr, data, flags);
1969 data->is_opaque = TRUE;
1970 if(!parse_path_opaque(ptr, data, flags))
1976 /* Attempts to parse the query string from the URI.
1979 * If NO_DECODE_EXTRA_INFO flag is set, then invalid percent encoded
1980 * data is allowed appear in the query string. For unknown scheme types
1981 * invalid percent encoded data is allowed to appear reguardless.
1983 static BOOL parse_query(const WCHAR **ptr, parse_data *data, DWORD flags) {
1984 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1987 TRACE("(%p %p %x): URI didn't contain a query string.\n", ptr, data, flags);
1994 while(**ptr && **ptr != '#') {
1995 if(**ptr == '%' && known_scheme &&
1996 !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
1997 if(!check_pct_encoded(ptr)) {
2008 data->query_len = *ptr - data->query;
2010 TRACE("(%p %p %x): Parsed query string %s len=%d\n", ptr, data, flags,
2011 debugstr_wn(data->query, data->query_len), data->query_len);
2015 /* Attempts to parse the fragment from the URI.
2018 * If NO_DECODE_EXTRA_INFO flag is set, then invalid percent encoded
2019 * data is allowed appear in the query string. For unknown scheme types
2020 * invalid percent encoded data is allowed to appear reguardless.
2022 static BOOL parse_fragment(const WCHAR **ptr, parse_data *data, DWORD flags) {
2023 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2026 TRACE("(%p %p %x): URI didn't contain a fragment.\n", ptr, data, flags);
2030 data->fragment = *ptr;
2034 if(**ptr == '%' && known_scheme &&
2035 !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
2036 if(!check_pct_encoded(ptr)) {
2037 *ptr = data->fragment;
2038 data->fragment = NULL;
2047 data->fragment_len = *ptr - data->fragment;
2049 TRACE("(%p %p %x): Parsed fragment %s len=%d\n", ptr, data, flags,
2050 debugstr_wn(data->fragment, data->fragment_len), data->fragment_len);
2054 /* Parses and validates the components of the specified by data->uri
2055 * and stores the information it parses into 'data'.
2057 * Returns TRUE if it successfully parsed the URI. False otherwise.
2059 static BOOL parse_uri(parse_data *data, DWORD flags) {
2066 TRACE("(%p %x): BEGINNING TO PARSE URI %s.\n", data, flags, debugstr_w(data->uri));
2068 if(!parse_scheme(pptr, data, flags))
2071 if(!parse_hierpart(pptr, data, flags))
2074 if(!parse_query(pptr, data, flags))
2077 if(!parse_fragment(pptr, data, flags))
2080 TRACE("(%p %x): FINISHED PARSING URI.\n", data, flags);
2084 /* Canonicalizes the userinfo of the URI represented by the parse_data.
2086 * Canonicalization of the userinfo is a simple process. If there are any percent
2087 * encoded characters that fall in the "unreserved" character set, they are decoded
2088 * to their actual value. If a character is not in the "unreserved" or "reserved" sets
2089 * then it is percent encoded. Other than that the characters are copied over without
2092 static BOOL canonicalize_userinfo(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2095 uri->userinfo_start = uri->userinfo_split = -1;
2096 uri->userinfo_len = 0;
2099 /* URI doesn't have userinfo, so nothing to do here. */
2102 uri->userinfo_start = uri->canon_len;
2104 while(i < data->userinfo_len) {
2105 if(data->userinfo[i] == ':' && uri->userinfo_split == -1)
2106 /* Windows only considers the first ':' as the delimiter. */
2107 uri->userinfo_split = uri->canon_len - uri->userinfo_start;
2108 else if(data->userinfo[i] == '%') {
2109 /* Only decode % encoded values for known scheme types. */
2110 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2111 /* See if the value really needs decoded. */
2112 WCHAR val = decode_pct_val(data->userinfo + i);
2113 if(is_unreserved(val)) {
2115 uri->canon_uri[uri->canon_len] = val;
2119 /* Move pass the hex characters. */
2124 } else if(!is_reserved(data->userinfo[i]) && !is_unreserved(data->userinfo[i]) &&
2125 data->userinfo[i] != '\\') {
2126 /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
2129 if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
2131 pct_encode_val(data->userinfo[i], uri->canon_uri + uri->canon_len);
2133 uri->canon_len += 3;
2140 /* Nothing special, so just copy the character over. */
2141 uri->canon_uri[uri->canon_len] = data->userinfo[i];
2147 uri->userinfo_len = uri->canon_len - uri->userinfo_start;
2149 TRACE("(%p %p %x %d): Canonicalized userinfo, userinfo_start=%d, userinfo=%s, userinfo_split=%d userinfo_len=%d.\n",
2150 data, uri, flags, computeOnly, uri->userinfo_start, debugstr_wn(uri->canon_uri + uri->userinfo_start, uri->userinfo_len),
2151 uri->userinfo_split, uri->userinfo_len);
2153 /* Now insert the '@' after the userinfo. */
2155 uri->canon_uri[uri->canon_len] = '@';
2161 /* Attempts to canonicalize a reg_name.
2163 * Things that happen:
2164 * 1) If Uri_CREATE_NO_CANONICALIZE flag is not set, then the reg_name is
2165 * lower cased. Unless it's an unknown scheme type, which case it's
2166 * no lower cased reguardless.
2168 * 2) Unreserved % encoded characters are decoded for known
2171 * 3) Forbidden characters are % encoded as long as
2172 * Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS flag is not set and
2173 * it isn't an unknown scheme type.
2175 * 4) If it's a file scheme and the host is "localhost" it's removed.
2177 static BOOL canonicalize_reg_name(const parse_data *data, Uri *uri,
2178 DWORD flags, BOOL computeOnly) {
2179 static const WCHAR localhostW[] =
2180 {'l','o','c','a','l','h','o','s','t',0};
2182 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2184 uri->host_start = uri->canon_len;
2186 if(data->scheme_type == URL_SCHEME_FILE &&
2187 data->host_len == lstrlenW(localhostW)) {
2188 if(!StrCmpNIW(data->host, localhostW, data->host_len)) {
2189 uri->host_start = -1;
2191 uri->host_type = Uri_HOST_UNKNOWN;
2196 for(ptr = data->host; ptr < data->host+data->host_len; ++ptr) {
2197 if(*ptr == '%' && known_scheme) {
2198 WCHAR val = decode_pct_val(ptr);
2199 if(is_unreserved(val)) {
2200 /* If NO_CANONICALZE is not set, then windows lower cases the
2203 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && isupperW(val)) {
2205 uri->canon_uri[uri->canon_len] = tolowerW(val);
2208 uri->canon_uri[uri->canon_len] = val;
2212 /* Skip past the % encoded character. */
2216 /* Just copy the % over. */
2218 uri->canon_uri[uri->canon_len] = *ptr;
2221 } else if(*ptr == '\\') {
2222 /* Only unknown scheme types could have made it here with a '\\' in the host name. */
2224 uri->canon_uri[uri->canon_len] = *ptr;
2226 } else if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
2227 !is_unreserved(*ptr) && !is_reserved(*ptr) && known_scheme) {
2229 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
2231 /* The percent encoded value gets lower cased also. */
2232 if(!(flags & Uri_CREATE_NO_CANONICALIZE)) {
2233 uri->canon_uri[uri->canon_len+1] = tolowerW(uri->canon_uri[uri->canon_len+1]);
2234 uri->canon_uri[uri->canon_len+2] = tolowerW(uri->canon_uri[uri->canon_len+2]);
2238 uri->canon_len += 3;
2241 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && known_scheme)
2242 uri->canon_uri[uri->canon_len] = tolowerW(*ptr);
2244 uri->canon_uri[uri->canon_len] = *ptr;
2251 uri->host_len = uri->canon_len - uri->host_start;
2254 TRACE("(%p %p %x %d): Canonicalize reg_name=%s len=%d\n", data, uri, flags,
2255 computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2259 find_domain_name(uri->canon_uri+uri->host_start, uri->host_len,
2260 &(uri->domain_offset));
2265 /* Attempts to canonicalize an implicit IPv4 address. */
2266 static BOOL canonicalize_implicit_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2267 uri->host_start = uri->canon_len;
2269 TRACE("%u\n", data->implicit_ipv4);
2270 /* For unknown scheme types Window's doesn't convert
2271 * the value into an IP address, but, it still considers
2272 * it an IPv4 address.
2274 if(data->scheme_type == URL_SCHEME_UNKNOWN) {
2276 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2277 uri->canon_len += data->host_len;
2280 uri->canon_len += ui2ipv4(uri->canon_uri+uri->canon_len, data->implicit_ipv4);
2282 uri->canon_len += ui2ipv4(NULL, data->implicit_ipv4);
2285 uri->host_len = uri->canon_len - uri->host_start;
2286 uri->host_type = Uri_HOST_IPV4;
2289 TRACE("%p %p %x %d): Canonicalized implicit IP address=%s len=%d\n",
2290 data, uri, flags, computeOnly,
2291 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2297 /* Attempts to canonicalize an IPv4 address.
2299 * If the parse_data represents a URI that has an implicit IPv4 address
2300 * (ex. http://256/, this function will convert 256 into 0.0.1.0). If
2301 * the implicit IP address exceeds the value of UINT_MAX (maximum value
2302 * for an IPv4 address) it's canonicalized as if were a reg-name.
2304 * If the parse_data contains a partial or full IPv4 address it normalizes it.
2305 * A partial IPv4 address is something like "192.0" and would be normalized to
2306 * "192.0.0.0". With a full (or partial) IPv4 address like "192.002.01.003" would
2307 * be normalized to "192.2.1.3".
2310 * Window's ONLY normalizes IPv4 address for known scheme types (one that isn't
2311 * URL_SCHEME_UNKNOWN). For unknown scheme types, it simply copies the data from
2312 * the original URI into the canonicalized URI, but, it still recognizes URI's
2313 * host type as HOST_IPV4.
2315 static BOOL canonicalize_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2316 if(data->has_implicit_ip)
2317 return canonicalize_implicit_ipv4address(data, uri, flags, computeOnly);
2319 uri->host_start = uri->canon_len;
2321 /* Windows only normalizes for known scheme types. */
2322 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2323 /* parse_data contains a partial or full IPv4 address, so normalize it. */
2324 DWORD i, octetDigitCount = 0, octetCount = 0;
2325 BOOL octetHasDigit = FALSE;
2327 for(i = 0; i < data->host_len; ++i) {
2328 if(data->host[i] == '0' && !octetHasDigit) {
2329 /* Can ignore leading zeros if:
2330 * 1) It isn't the last digit of the octet.
2331 * 2) i+1 != data->host_len
2334 if(octetDigitCount == 2 ||
2335 i+1 == data->host_len ||
2336 data->host[i+1] == '.') {
2338 uri->canon_uri[uri->canon_len] = data->host[i];
2340 TRACE("Adding zero\n");
2342 } else if(data->host[i] == '.') {
2344 uri->canon_uri[uri->canon_len] = data->host[i];
2347 octetDigitCount = 0;
2348 octetHasDigit = FALSE;
2352 uri->canon_uri[uri->canon_len] = data->host[i];
2356 octetHasDigit = TRUE;
2360 /* Make sure the canonicalized IP address has 4 dec-octets.
2361 * If doesn't add "0" ones until there is 4;
2363 for( ; octetCount < 3; ++octetCount) {
2365 uri->canon_uri[uri->canon_len] = '.';
2366 uri->canon_uri[uri->canon_len+1] = '0';
2369 uri->canon_len += 2;
2372 /* Windows doesn't normalize addresses in unknown schemes. */
2374 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2375 uri->canon_len += data->host_len;
2378 uri->host_len = uri->canon_len - uri->host_start;
2380 TRACE("(%p %p %x %d): Canonicalized IPv4 address, ip=%s len=%d\n",
2381 data, uri, flags, computeOnly,
2382 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2389 /* Attempts to canonicalize the IPv6 address of the URI.
2391 * Multiple things happen during the canonicalization of an IPv6 address:
2392 * 1) Any leading zero's in an h16 component are removed.
2393 * Ex: [0001:0022::] -> [1:22::]
2395 * 2) The longest sequence of zero h16 components are compressed
2396 * into a "::" (elision). If there's a tie, the first is choosen.
2398 * Ex: [0:0:0:0:1:6:7:8] -> [::1:6:7:8]
2399 * [0:0:0:0:1:2::] -> [::1:2:0:0]
2400 * [0:0:1:2:0:0:7:8] -> [::1:2:0:0:7:8]
2402 * 3) If an IPv4 address is attached to the IPv6 address, it's
2404 * Ex: [::001.002.022.000] -> [::1.2.22.0]
2406 * 4) If an elision is present, but, only represents 1 h16 component
2409 * Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
2411 * 5) If the IPv6 address contains an IPv4 address and there exists
2412 * at least 1 non-zero h16 component the IPv4 address is converted
2413 * into two h16 components, otherwise it's normalized and kept as is.
2415 * Ex: [::192.200.003.4] -> [::192.200.3.4]
2416 * [ffff::192.200.003.4] -> [ffff::c0c8:3041]
2419 * For unknown scheme types Windows simply copies the address over without any
2422 * IPv4 address can be included in an elision if all its components are 0's.
2424 static BOOL canonicalize_ipv6address(const parse_data *data, Uri *uri,
2425 DWORD flags, BOOL computeOnly) {
2426 uri->host_start = uri->canon_len;
2428 if(data->scheme_type == URL_SCHEME_UNKNOWN) {
2430 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2431 uri->canon_len += data->host_len;
2435 DWORD i, elision_len;
2437 if(!ipv6_to_number(&(data->ipv6_address), values)) {
2438 TRACE("(%p %p %x %d): Failed to compute numerical value for IPv6 address.\n",
2439 data, uri, flags, computeOnly);
2444 uri->canon_uri[uri->canon_len] = '[';
2447 /* Find where the elision should occur (if any). */
2448 compute_elision_location(&(data->ipv6_address), values, &elision_start, &elision_len);
2450 TRACE("%p %p %x %d): Elision starts at %d, len=%u\n", data, uri, flags,
2451 computeOnly, elision_start, elision_len);
2453 for(i = 0; i < 8; ++i) {
2454 BOOL in_elision = (elision_start > -1 && i >= elision_start &&
2455 i < elision_start+elision_len);
2456 BOOL do_ipv4 = (i == 6 && data->ipv6_address.ipv4 && !in_elision &&
2457 data->ipv6_address.h16_count == 0);
2459 if(i == elision_start) {
2461 uri->canon_uri[uri->canon_len] = ':';
2462 uri->canon_uri[uri->canon_len+1] = ':';
2464 uri->canon_len += 2;
2467 /* We can ignore the current component if we're in the elision. */
2471 /* We only add a ':' if we're not at i == 0, or when we're at
2472 * the very end of elision range since the ':' colon was handled
2473 * earlier. Otherwise we would end up with ":::" after elision.
2475 if(i != 0 && !(elision_start > -1 && i == elision_start+elision_len)) {
2477 uri->canon_uri[uri->canon_len] = ':';
2485 /* Combine the two parts of the IPv4 address values. */
2491 len = ui2ipv4(uri->canon_uri+uri->canon_len, val);
2493 len = ui2ipv4(NULL, val);
2495 uri->canon_len += len;
2498 /* Write a regular h16 component to the URI. */
2500 /* Short circuit for the trivial case. */
2501 if(values[i] == 0) {
2503 uri->canon_uri[uri->canon_len] = '0';
2506 static const WCHAR formatW[] = {'%','x',0};
2509 uri->canon_len += sprintfW(uri->canon_uri+uri->canon_len,
2510 formatW, values[i]);
2513 uri->canon_len += sprintfW(tmp, formatW, values[i]);
2519 /* Add the closing ']'. */
2521 uri->canon_uri[uri->canon_len] = ']';
2525 uri->host_len = uri->canon_len - uri->host_start;
2528 TRACE("(%p %p %x %d): Canonicalized IPv6 address %s, len=%d\n", data, uri, flags,
2529 computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2535 /* Attempts to canonicalize the host of the URI (if any). */
2536 static BOOL canonicalize_host(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2537 uri->host_start = -1;
2539 uri->domain_offset = -1;
2542 switch(data->host_type) {
2544 uri->host_type = Uri_HOST_DNS;
2545 if(!canonicalize_reg_name(data, uri, flags, computeOnly))
2550 uri->host_type = Uri_HOST_IPV4;
2551 if(!canonicalize_ipv4address(data, uri, flags, computeOnly))
2556 if(!canonicalize_ipv6address(data, uri, flags, computeOnly))
2559 uri->host_type = Uri_HOST_IPV6;
2561 case Uri_HOST_UNKNOWN:
2562 if(data->host_len > 0 || data->scheme_type != URL_SCHEME_FILE) {
2563 uri->host_start = uri->canon_len;
2565 /* Nothing happens to unknown host types. */
2567 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2568 uri->canon_len += data->host_len;
2569 uri->host_len = data->host_len;
2572 uri->host_type = Uri_HOST_UNKNOWN;
2575 FIXME("(%p %p %x %d): Canonicalization for host type %d not supported.\n", data,
2576 uri, flags, computeOnly, data->host_type);
2584 static BOOL canonicalize_port(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2585 BOOL has_default_port = FALSE;
2586 USHORT default_port = 0;
2589 uri->has_port = FALSE;
2591 /* Check if the scheme has a default port. */
2592 for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
2593 if(default_ports[i].scheme == data->scheme_type) {
2594 has_default_port = TRUE;
2595 default_port = default_ports[i].port;
2600 if(data->port || has_default_port)
2601 uri->has_port = TRUE;
2604 * 1) Has a port which is the default port.
2605 * 2) Has a port (not the default).
2606 * 3) Doesn't have a port, but, scheme has a default port.
2609 if(has_default_port && data->port && data->port_value == default_port) {
2610 /* If it's the default port and this flag isn't set, don't do anything. */
2611 if(flags & Uri_CREATE_NO_CANONICALIZE) {
2612 /* Copy the original port over. */
2614 uri->canon_uri[uri->canon_len] = ':';
2615 memcpy(uri->canon_uri+uri->canon_len+1, data->port, data->port_len*sizeof(WCHAR));
2617 uri->canon_len += data->port_len+1;
2620 uri->port = default_port;
2621 } else if(data->port) {
2623 uri->canon_uri[uri->canon_len] = ':';
2626 if(flags & Uri_CREATE_NO_CANONICALIZE) {
2627 /* Copy the original over without changes. */
2629 memcpy(uri->canon_uri+uri->canon_len, data->port, data->port_len*sizeof(WCHAR));
2630 uri->canon_len += data->port_len;
2632 const WCHAR formatW[] = {'%','u',0};
2635 len = sprintfW(uri->canon_uri+uri->canon_len, formatW, data->port_value);
2638 len = sprintfW(tmp, formatW, data->port_value);
2640 uri->canon_len += len;
2643 uri->port = data->port_value;
2644 } else if(has_default_port)
2645 uri->port = default_port;
2650 /* Canonicalizes the authority of the URI represented by the parse_data. */
2651 static BOOL canonicalize_authority(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2652 uri->authority_start = uri->canon_len;
2653 uri->authority_len = 0;
2655 if(!canonicalize_userinfo(data, uri, flags, computeOnly))
2658 if(!canonicalize_host(data, uri, flags, computeOnly))
2661 if(!canonicalize_port(data, uri, flags, computeOnly))
2664 if(uri->host_start != -1)
2665 uri->authority_len = uri->canon_len - uri->authority_start;
2667 uri->authority_start = -1;
2672 /* Attempts to canonicalize the path of a hierarchical URI.
2674 * Things that happen:
2675 * 1). Forbidden characters are percent encoded, unless the NO_ENCODE_FORBIDDEN
2676 * flag is set or it's a file URI. Forbidden characters are always encoded
2677 * for file schemes reguardless and forbidden characters are never encoded
2678 * for unknown scheme types.
2680 * 2). For known scheme types '\\' are changed to '/'.
2682 * 3). Percent encoded, unreserved characters are decoded to their actual values.
2683 * Unless the scheme type is unknown. For file schemes any percent encoded
2684 * character in the unreserved or reserved set is decoded.
2686 * 4). For File schemes if the path is starts with a drive letter and doesn't
2687 * start with a '/' then one is appended.
2688 * Ex: file://c:/test.mp3 -> file:///c:/test.mp3
2690 * 5). Dot segments are removed from the path for all scheme types
2691 * unless NO_CANONICALIZE flag is set. Dot segments aren't removed
2692 * for wildcard scheme types.
2695 * file://c:/test%20test -> file:///c:/test%2520test
2696 * file://c:/test%3Etest -> file:///c:/test%253Etest
2697 * file:///c:/test%20test -> file:///c:/test%20test
2698 * file:///c:/test%test -> file:///c:/test%25test
2700 static BOOL canonicalize_path_hierarchical(const parse_data *data, Uri *uri,
2701 DWORD flags, BOOL computeOnly) {
2703 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2704 const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
2706 BOOL escape_pct = FALSE;
2709 uri->path_start = -1;
2714 uri->path_start = uri->canon_len;
2717 if(is_file && uri->host_start == -1) {
2718 /* Check if a '/' needs to be appended for the file scheme. */
2719 if(data->path_len > 1 && is_drive_path(ptr) && !(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2721 uri->canon_uri[uri->canon_len] = '/';
2724 } else if(*ptr == '/') {
2725 if(!(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2726 /* Copy the extra '/' over. */
2728 uri->canon_uri[uri->canon_len] = '/';
2734 if(is_drive_path(ptr)) {
2736 uri->canon_uri[uri->canon_len] = *ptr;
2737 /* If theres a '|' after the drive letter, convert it to a ':'. */
2738 uri->canon_uri[uri->canon_len+1] = ':';
2741 uri->canon_len += 2;
2745 for(; ptr < data->path+data->path_len; ++ptr) {
2747 const WCHAR *tmp = ptr;
2750 /* Check if the % represents a valid encoded char, or if it needs encoded. */
2751 BOOL force_encode = !check_pct_encoded(&tmp) && is_file;
2752 val = decode_pct_val(ptr);
2754 if(force_encode || escape_pct) {
2755 /* Escape the percent sign in the file URI. */
2757 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
2758 uri->canon_len += 3;
2759 } else if((is_unreserved(val) && known_scheme) ||
2760 (is_file && (is_unreserved(val) || is_reserved(val)))) {
2762 uri->canon_uri[uri->canon_len] = val;
2769 uri->canon_uri[uri->canon_len] = *ptr;
2772 } else if(*ptr == '/' && is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2773 /* Convert the '/' back to a '\\'. */
2775 uri->canon_uri[uri->canon_len] = '\\';
2777 } else if(*ptr == '\\' && known_scheme) {
2778 if(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2779 /* Don't convert the '\\' to a '/'. */
2781 uri->canon_uri[uri->canon_len] = *ptr;
2785 uri->canon_uri[uri->canon_len] = '/';
2788 } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr) &&
2789 (!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) || is_file)) {
2790 if(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2791 /* Don't escape the character. */
2793 uri->canon_uri[uri->canon_len] = *ptr;
2796 /* Escape the forbidden character. */
2798 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
2799 uri->canon_len += 3;
2803 uri->canon_uri[uri->canon_len] = *ptr;
2808 uri->path_len = uri->canon_len - uri->path_start;
2810 /* Removing the dot segments only happens when it's not in
2811 * computeOnly mode and it's not a wildcard scheme. File schemes
2812 * with USE_DOS_PATH set don't get dot segments removed.
2814 if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) &&
2815 data->scheme_type != URL_SCHEME_WILDCARD) {
2816 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && !computeOnly) {
2817 /* Remove the dot segments (if any) and reset everything to the new
2820 DWORD new_len = remove_dot_segments(uri->canon_uri+uri->path_start, uri->path_len);
2821 uri->canon_len -= uri->path_len-new_len;
2822 uri->path_len = new_len;
2827 TRACE("Canonicalized path %s len=%d\n",
2828 debugstr_wn(uri->canon_uri+uri->path_start, uri->path_len),
2834 /* Attempts to canonicalize the path for an opaque URI.
2836 * For known scheme types:
2837 * 1) forbidden characters are percent encoded if
2838 * NO_ENCODE_FORBIDDEN_CHARACTERS isn't set.
2840 * 2) Percent encoded, unreserved characters are decoded
2841 * to their actual values, for known scheme types.
2843 * 3) '\\' are changed to '/' for known scheme types
2844 * except for mailto schemes.
2846 * 4) For file schemes, if USE_DOS_PATH is set all '/'
2847 * are converted to backslashes.
2849 * 5) For file schemes, if USE_DOS_PATH isn't set all '\'
2850 * are converted to forward slashes.
2852 static BOOL canonicalize_path_opaque(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2854 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2855 const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
2858 uri->path_start = -1;
2863 uri->path_start = uri->canon_len;
2865 /* Windows doesn't allow a "//" to appear after the scheme
2866 * of a URI, if it's an opaque URI.
2868 if(data->scheme && *(data->path) == '/' && *(data->path+1) == '/') {
2869 /* So it inserts a "/." before the "//" if it exists. */
2871 uri->canon_uri[uri->canon_len] = '/';
2872 uri->canon_uri[uri->canon_len+1] = '.';
2875 uri->canon_len += 2;
2878 for(ptr = data->path; ptr < data->path+data->path_len; ++ptr) {
2879 if(*ptr == '%' && known_scheme) {
2880 WCHAR val = decode_pct_val(ptr);
2882 if(is_unreserved(val)) {
2884 uri->canon_uri[uri->canon_len] = val;
2891 uri->canon_uri[uri->canon_len] = *ptr;
2894 } else if(*ptr == '/' && is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2896 uri->canon_uri[uri->canon_len] = '\\';
2898 } else if(*ptr == '\\' && is_file) {
2899 if(!(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2900 /* Convert to a '/'. */
2902 uri->canon_uri[uri->canon_len] = '/';
2905 /* Just copy it over. */
2907 uri->canon_uri[uri->canon_len] = *ptr;
2910 } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr) &&
2911 !(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
2912 if(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2913 /* Forbidden characters aren't percent encoded for file schemes
2914 * with USE_DOS_PATH set.
2917 uri->canon_uri[uri->canon_len] = *ptr;
2921 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
2922 uri->canon_len += 3;
2926 uri->canon_uri[uri->canon_len] = *ptr;
2931 uri->path_len = uri->canon_len - uri->path_start;
2933 TRACE("(%p %p %x %d): Canonicalized opaque URI path %s len=%d\n", data, uri, flags, computeOnly,
2934 debugstr_wn(uri->canon_uri+uri->path_start, uri->path_len), uri->path_len);
2938 /* Determines how the URI represented by the parse_data should be canonicalized.
2940 * Essentially, if the parse_data represents an hierarchical URI then it calls
2941 * canonicalize_authority and the canonicalization functions for the path. If the
2942 * URI is opaque it canonicalizes the path of the URI.
2944 static BOOL canonicalize_hierpart(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2945 uri->display_absolute = TRUE;
2947 if(!data->is_opaque) {
2948 /* "//" is only added for non-wildcard scheme types. */
2949 if(data->scheme_type != URL_SCHEME_WILDCARD) {
2951 INT pos = uri->canon_len;
2953 uri->canon_uri[pos] = '/';
2954 uri->canon_uri[pos+1] = '/';
2956 uri->canon_len += 2;
2959 if(!canonicalize_authority(data, uri, flags, computeOnly))
2962 /* TODO: Canonicalize the path of the URI. */
2963 if(!canonicalize_path_hierarchical(data, uri, flags, computeOnly))
2967 /* Opaque URI's don't have an authority. */
2968 uri->userinfo_start = uri->userinfo_split = -1;
2969 uri->userinfo_len = 0;
2970 uri->host_start = -1;
2972 uri->host_type = Uri_HOST_UNKNOWN;
2973 uri->has_port = FALSE;
2974 uri->authority_start = -1;
2975 uri->authority_len = 0;
2976 uri->domain_offset = -1;
2978 if(is_hierarchical_scheme(data->scheme_type)) {
2981 /* Absolute URIs aren't displayed for known scheme types
2982 * which should be hierarchical URIs.
2984 uri->display_absolute = FALSE;
2986 /* Windows also sets the port for these (if they have one). */
2987 for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
2988 if(data->scheme_type == default_ports[i].scheme) {
2989 uri->has_port = TRUE;
2990 uri->port = default_ports[i].port;
2996 if(!canonicalize_path_opaque(data, uri, flags, computeOnly))
3000 if(uri->path_start > -1 && !computeOnly)
3001 /* Finding file extensions happens for both types of URIs. */
3002 uri->extension_offset = find_file_extension(uri->canon_uri+uri->path_start, uri->path_len);
3004 uri->extension_offset = -1;
3009 /* Attempts to canonicalize the query string of the URI.
3011 * Things that happen:
3012 * 1) For known scheme types forbidden characters
3013 * are percent encoded, unless the NO_DECODE_EXTRA_INFO flag is set
3014 * or NO_ENCODE_FORBIDDEN_CHARACTERS is set.
3016 * 2) For known scheme types, percent encoded, unreserved characters
3017 * are decoded as long as the NO_DECODE_EXTRA_INFO flag isn't set.
3019 static BOOL canonicalize_query(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3020 const WCHAR *ptr, *end;
3021 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
3024 uri->query_start = -1;
3029 uri->query_start = uri->canon_len;
3031 end = data->query+data->query_len;
3032 for(ptr = data->query; ptr < end; ++ptr) {
3034 if(known_scheme && !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3035 WCHAR val = decode_pct_val(ptr);
3036 if(is_unreserved(val)) {
3038 uri->canon_uri[uri->canon_len] = val;
3045 } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr)) {
3046 if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
3047 !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3049 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3050 uri->canon_len += 3;
3056 uri->canon_uri[uri->canon_len] = *ptr;
3060 uri->query_len = uri->canon_len - uri->query_start;
3063 TRACE("(%p %p %x %d): Canonicalized query string %s len=%d\n", data, uri, flags,
3064 computeOnly, debugstr_wn(uri->canon_uri+uri->query_start, uri->query_len),
3069 static BOOL canonicalize_fragment(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3070 const WCHAR *ptr, *end;
3071 const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
3073 if(!data->fragment) {
3074 uri->fragment_start = -1;
3075 uri->fragment_len = 0;
3079 uri->fragment_start = uri->canon_len;
3081 end = data->fragment + data->fragment_len;
3082 for(ptr = data->fragment; ptr < end; ++ptr) {
3084 if(known_scheme && !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3085 WCHAR val = decode_pct_val(ptr);
3086 if(is_unreserved(val)) {
3088 uri->canon_uri[uri->canon_len] = val;
3095 } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr)) {
3096 if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
3097 !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3099 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3100 uri->canon_len += 3;
3106 uri->canon_uri[uri->canon_len] = *ptr;
3110 uri->fragment_len = uri->canon_len - uri->fragment_start;
3113 TRACE("(%p %p %x %d): Canonicalized fragment %s len=%d\n", data, uri, flags,
3114 computeOnly, debugstr_wn(uri->canon_uri+uri->fragment_start, uri->fragment_len),
3119 /* Canonicalizes the scheme information specified in the parse_data using the specified flags. */
3120 static BOOL canonicalize_scheme(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3121 uri->scheme_start = -1;
3122 uri->scheme_len = 0;
3125 /* The only type of URI that doesn't have to have a scheme is a relative
3128 if(!data->is_relative) {
3129 FIXME("(%p %p %x): Unable to determine the scheme type of %s.\n", data,
3130 uri, flags, debugstr_w(data->uri));
3136 INT pos = uri->canon_len;
3138 for(i = 0; i < data->scheme_len; ++i) {
3139 /* Scheme name must be lower case after canonicalization. */
3140 uri->canon_uri[i + pos] = tolowerW(data->scheme[i]);
3143 uri->canon_uri[i + pos] = ':';
3144 uri->scheme_start = pos;
3146 TRACE("(%p %p %x): Canonicalized scheme=%s, len=%d.\n", data, uri, flags,
3147 debugstr_wn(uri->canon_uri, uri->scheme_len), data->scheme_len);
3150 /* This happens in both computation modes. */
3151 uri->canon_len += data->scheme_len + 1;
3152 uri->scheme_len = data->scheme_len;
3157 /* Compute's what the length of the URI specified by the parse_data will be
3158 * after canonicalization occurs using the specified flags.
3160 * This function will return a non-zero value indicating the length of the canonicalized
3161 * URI, or -1 on error.
3163 static int compute_canonicalized_length(const parse_data *data, DWORD flags) {
3166 memset(&uri, 0, sizeof(Uri));
3168 TRACE("(%p %x): Beginning to compute canonicalized length for URI %s\n", data, flags,
3169 debugstr_w(data->uri));
3171 if(!canonicalize_scheme(data, &uri, flags, TRUE)) {
3172 ERR("(%p %x): Failed to compute URI scheme length.\n", data, flags);
3176 if(!canonicalize_hierpart(data, &uri, flags, TRUE)) {
3177 ERR("(%p %x): Failed to compute URI hierpart length.\n", data, flags);
3181 if(!canonicalize_query(data, &uri, flags, TRUE)) {
3182 ERR("(%p %x): Failed to compute query string length.\n", data, flags);
3186 if(!canonicalize_fragment(data, &uri, flags, TRUE)) {
3187 ERR("(%p %x): Failed to compute fragment length.\n", data, flags);
3191 TRACE("(%p %x): Finished computing canonicalized URI length. length=%d\n", data, flags, uri.canon_len);
3193 return uri.canon_len;
3196 /* Canonicalizes the URI data specified in the parse_data, using the given flags. If the
3197 * canonicalization succeededs it will store all the canonicalization information
3198 * in the pointer to the Uri.
3200 * To canonicalize a URI this function first computes what the length of the URI
3201 * specified by the parse_data will be. Once this is done it will then perfom the actual
3202 * canonicalization of the URI.
3204 static HRESULT canonicalize_uri(const parse_data *data, Uri *uri, DWORD flags) {
3207 uri->canon_uri = NULL;
3208 len = uri->canon_size = uri->canon_len = 0;
3210 TRACE("(%p %p %x): beginning to canonicalize URI %s.\n", data, uri, flags, debugstr_w(data->uri));
3212 /* First try to compute the length of the URI. */
3213 len = compute_canonicalized_length(data, flags);
3215 ERR("(%p %p %x): Could not compute the canonicalized length of %s.\n", data, uri, flags,
3216 debugstr_w(data->uri));
3217 return E_INVALIDARG;
3220 uri->canon_uri = heap_alloc((len+1)*sizeof(WCHAR));
3222 return E_OUTOFMEMORY;
3224 uri->canon_size = len;
3225 if(!canonicalize_scheme(data, uri, flags, FALSE)) {
3226 ERR("(%p %p %x): Unable to canonicalize the scheme of the URI.\n", data, uri, flags);
3227 heap_free(uri->canon_uri);
3228 return E_INVALIDARG;
3230 uri->scheme_type = data->scheme_type;
3232 if(!canonicalize_hierpart(data, uri, flags, FALSE)) {
3233 ERR("(%p %p %x): Unable to canonicalize the heirpart of the URI\n", data, uri, flags);
3234 heap_free(uri->canon_uri);
3235 return E_INVALIDARG;
3238 if(!canonicalize_query(data, uri, flags, FALSE)) {
3239 ERR("(%p %p %x): Unable to canonicalize query string of the URI.\n",
3241 return E_INVALIDARG;
3244 if(!canonicalize_fragment(data, uri, flags, FALSE)) {
3245 ERR("(%p %p %x): Unable to canonicalize fragment of the URI.\n",
3247 return E_INVALIDARG;
3250 /* There's a possibility we didn't use all the space we allocated
3253 if(uri->canon_len < uri->canon_size) {
3254 /* This happens if the URI is hierarchical and dot
3255 * segments were removed from it's path.
3257 WCHAR *tmp = heap_realloc(uri->canon_uri, (uri->canon_len+1)*sizeof(WCHAR));
3259 return E_OUTOFMEMORY;
3261 uri->canon_uri = tmp;
3262 uri->canon_size = uri->canon_len;
3265 uri->canon_uri[uri->canon_len] = '\0';
3266 TRACE("(%p %p %x): finished canonicalizing the URI. uri=%s\n", data, uri, flags, debugstr_w(uri->canon_uri));
3271 static HRESULT get_builder_component(LPWSTR *component, DWORD *component_len,
3272 LPCWSTR source, DWORD source_len,
3273 LPCWSTR *output, DWORD *output_len)
3286 if(!(*component) && source) {
3287 /* Allocate 'component', and copy the contents from 'source'
3288 * into the new allocation.
3290 *component = heap_alloc((source_len+1)*sizeof(WCHAR));
3292 return E_OUTOFMEMORY;
3294 memcpy(*component, source, source_len*sizeof(WCHAR));
3295 (*component)[source_len] = '\0';
3296 *component_len = source_len;
3299 *output = *component;
3300 *output_len = *component_len;
3301 return *output ? S_OK : S_FALSE;
3304 /* Allocates 'component' and copies the string from 'new_value' into 'component'.
3305 * If 'prefix' is set and 'new_value' isn't NULL, then it checks if 'new_value'
3306 * starts with 'prefix'. If it doesn't then 'prefix' is prepended to 'component'.
3308 * If everything is successful, then will set 'success_flag' in 'flags'.
3310 static HRESULT set_builder_component(LPWSTR *component, DWORD *component_len, LPCWSTR new_value,
3311 WCHAR prefix, DWORD *flags, DWORD success_flag)
3313 heap_free(*component);
3319 BOOL add_prefix = FALSE;
3320 DWORD len = lstrlenW(new_value);
3323 if(prefix && *new_value != prefix) {
3325 *component = heap_alloc((len+2)*sizeof(WCHAR));
3327 *component = heap_alloc((len+1)*sizeof(WCHAR));
3330 return E_OUTOFMEMORY;
3333 (*component)[pos++] = prefix;
3335 memcpy(*component+pos, new_value, (len+1)*sizeof(WCHAR));
3336 *component_len = len+pos;
3339 *flags |= success_flag;
3343 #define URI(x) ((IUri*) &(x)->lpIUriVtbl)
3344 #define URIBUILDER(x) ((IUriBuilder*) &(x)->lpIUriBuilderVtbl)
3346 static void reset_builder(UriBuilder *builder) {
3348 IUri_Release(URI(builder->uri));
3349 builder->uri = NULL;
3351 heap_free(builder->fragment);
3352 builder->fragment = NULL;
3353 builder->fragment_len = 0;
3355 heap_free(builder->host);
3356 builder->host = NULL;
3357 builder->host_len = 0;
3359 heap_free(builder->password);
3360 builder->password = NULL;
3361 builder->password_len = 0;
3363 heap_free(builder->path);
3364 builder->path = NULL;
3365 builder->path_len = 0;
3367 heap_free(builder->query);
3368 builder->query = NULL;
3369 builder->query_len = 0;
3371 heap_free(builder->scheme);
3372 builder->scheme = NULL;
3373 builder->scheme_len = 0;
3375 heap_free(builder->username);
3376 builder->username = NULL;
3377 builder->username_len = 0;
3379 builder->has_port = FALSE;
3381 builder->modified_props = 0;
3384 #define URI_THIS(iface) DEFINE_THIS(Uri, IUri, iface)
3386 static HRESULT WINAPI Uri_QueryInterface(IUri *iface, REFIID riid, void **ppv)
3388 Uri *This = URI_THIS(iface);
3390 if(IsEqualGUID(&IID_IUnknown, riid)) {
3391 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
3393 }else if(IsEqualGUID(&IID_IUri, riid)) {
3394 TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
3396 }else if(IsEqualGUID(&IID_IUriObj, riid)) {
3397 TRACE("(%p)->(IID_IUriObj %p)\n", This, ppv);
3401 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
3403 return E_NOINTERFACE;
3406 IUnknown_AddRef((IUnknown*)*ppv);
3410 static ULONG WINAPI Uri_AddRef(IUri *iface)
3412 Uri *This = URI_THIS(iface);
3413 LONG ref = InterlockedIncrement(&This->ref);
3415 TRACE("(%p) ref=%d\n", This, ref);
3420 static ULONG WINAPI Uri_Release(IUri *iface)
3422 Uri *This = URI_THIS(iface);
3423 LONG ref = InterlockedDecrement(&This->ref);
3425 TRACE("(%p) ref=%d\n", This, ref);
3428 SysFreeString(This->raw_uri);
3429 heap_free(This->canon_uri);
3436 static HRESULT WINAPI Uri_GetPropertyBSTR(IUri *iface, Uri_PROPERTY uriProp, BSTR *pbstrProperty, DWORD dwFlags)
3438 Uri *This = URI_THIS(iface);
3440 TRACE("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
3445 if(uriProp > Uri_PROPERTY_STRING_LAST) {
3446 /* Windows allocates an empty BSTR for invalid Uri_PROPERTY's. */
3447 *pbstrProperty = SysAllocStringLen(NULL, 0);
3448 if(!(*pbstrProperty))
3449 return E_OUTOFMEMORY;
3451 /* It only returns S_FALSE for the ZONE property... */
3452 if(uriProp == Uri_PROPERTY_ZONE)
3458 /* Don't have support for flags yet. */
3460 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
3465 case Uri_PROPERTY_ABSOLUTE_URI:
3466 if(!This->display_absolute) {
3467 *pbstrProperty = SysAllocStringLen(NULL, 0);
3470 if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1) {
3471 if(This->userinfo_len == 0) {
3472 /* Don't include the '@' after the userinfo component. */
3473 *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-1);
3475 if(*pbstrProperty) {
3476 /* Copy everything before it. */
3477 memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
3479 /* And everything after it. */
3480 memcpy(*pbstrProperty+This->userinfo_start, This->canon_uri+This->userinfo_start+1,
3481 (This->canon_len-This->userinfo_start-1)*sizeof(WCHAR));
3483 } else if(This->userinfo_split == 0 && This->userinfo_len == 1) {
3484 /* Don't include the ":@" */
3485 *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-2);
3487 if(*pbstrProperty) {
3488 memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
3489 memcpy(*pbstrProperty+This->userinfo_start, This->canon_uri+This->userinfo_start+2,
3490 (This->canon_len-This->userinfo_start-2)*sizeof(WCHAR));
3493 *pbstrProperty = SysAllocString(This->canon_uri);
3497 *pbstrProperty = SysAllocString(This->canon_uri);
3502 if(!(*pbstrProperty))
3503 hres = E_OUTOFMEMORY;
3506 case Uri_PROPERTY_AUTHORITY:
3507 if(This->authority_start > -1) {
3508 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->authority_start, This->authority_len);
3511 *pbstrProperty = SysAllocStringLen(NULL, 0);
3515 if(!(*pbstrProperty))
3516 hres = E_OUTOFMEMORY;
3519 case Uri_PROPERTY_DISPLAY_URI:
3520 /* The Display URI contains everything except for the userinfo for known
3523 if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1) {
3524 *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-This->userinfo_len);
3526 if(*pbstrProperty) {
3527 /* Copy everything before the userinfo over. */
3528 memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
3529 /* Copy everything after the userinfo over. */
3530 memcpy(*pbstrProperty+This->userinfo_start,
3531 This->canon_uri+This->userinfo_start+This->userinfo_len+1,
3532 (This->canon_len-(This->userinfo_start+This->userinfo_len+1))*sizeof(WCHAR));
3535 *pbstrProperty = SysAllocString(This->canon_uri);
3537 if(!(*pbstrProperty))
3538 hres = E_OUTOFMEMORY;
3543 case Uri_PROPERTY_DOMAIN:
3544 if(This->domain_offset > -1) {
3545 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+This->domain_offset,
3546 This->host_len-This->domain_offset);
3549 *pbstrProperty = SysAllocStringLen(NULL, 0);
3553 if(!(*pbstrProperty))
3554 hres = E_OUTOFMEMORY;
3557 case Uri_PROPERTY_EXTENSION:
3558 if(This->extension_offset > -1) {
3559 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start+This->extension_offset,
3560 This->path_len-This->extension_offset);
3563 *pbstrProperty = SysAllocStringLen(NULL, 0);
3567 if(!(*pbstrProperty))
3568 hres = E_OUTOFMEMORY;
3571 case Uri_PROPERTY_FRAGMENT:
3572 if(This->fragment_start > -1) {
3573 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->fragment_start, This->fragment_len);
3576 *pbstrProperty = SysAllocStringLen(NULL, 0);
3580 if(!(*pbstrProperty))
3581 hres = E_OUTOFMEMORY;
3584 case Uri_PROPERTY_HOST:
3585 if(This->host_start > -1) {
3586 /* The '[' and ']' aren't included for IPv6 addresses. */
3587 if(This->host_type == Uri_HOST_IPV6)
3588 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+1, This->host_len-2);
3590 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start, This->host_len);
3594 *pbstrProperty = SysAllocStringLen(NULL, 0);
3598 if(!(*pbstrProperty))
3599 hres = E_OUTOFMEMORY;
3602 case Uri_PROPERTY_PASSWORD:
3603 if(This->userinfo_split > -1) {
3604 *pbstrProperty = SysAllocStringLen(
3605 This->canon_uri+This->userinfo_start+This->userinfo_split+1,
3606 This->userinfo_len-This->userinfo_split-1);
3609 *pbstrProperty = SysAllocStringLen(NULL, 0);
3613 if(!(*pbstrProperty))
3614 return E_OUTOFMEMORY;
3617 case Uri_PROPERTY_PATH:
3618 if(This->path_start > -1) {
3619 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start, This->path_len);
3622 *pbstrProperty = SysAllocStringLen(NULL, 0);
3626 if(!(*pbstrProperty))
3627 hres = E_OUTOFMEMORY;
3630 case Uri_PROPERTY_PATH_AND_QUERY:
3631 if(This->path_start > -1) {
3632 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start, This->path_len+This->query_len);
3634 } else if(This->query_start > -1) {
3635 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->query_start, This->query_len);
3638 *pbstrProperty = SysAllocStringLen(NULL, 0);
3642 if(!(*pbstrProperty))
3643 hres = E_OUTOFMEMORY;
3646 case Uri_PROPERTY_QUERY:
3647 if(This->query_start > -1) {
3648 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->query_start, This->query_len);
3651 *pbstrProperty = SysAllocStringLen(NULL, 0);
3655 if(!(*pbstrProperty))
3656 hres = E_OUTOFMEMORY;
3659 case Uri_PROPERTY_RAW_URI:
3660 *pbstrProperty = SysAllocString(This->raw_uri);
3661 if(!(*pbstrProperty))
3662 hres = E_OUTOFMEMORY;
3666 case Uri_PROPERTY_SCHEME_NAME:
3667 if(This->scheme_start > -1) {
3668 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->scheme_start, This->scheme_len);
3671 *pbstrProperty = SysAllocStringLen(NULL, 0);
3675 if(!(*pbstrProperty))
3676 hres = E_OUTOFMEMORY;
3679 case Uri_PROPERTY_USER_INFO:
3680 if(This->userinfo_start > -1) {
3681 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->userinfo_start, This->userinfo_len);
3684 *pbstrProperty = SysAllocStringLen(NULL, 0);
3688 if(!(*pbstrProperty))
3689 hres = E_OUTOFMEMORY;
3692 case Uri_PROPERTY_USER_NAME:
3693 if(This->userinfo_start > -1 && This->userinfo_split != 0) {
3694 /* If userinfo_split is set, that means a password exists
3695 * so the username is only from userinfo_start to userinfo_split.
3697 if(This->userinfo_split > -1) {
3698 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_split);
3701 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_len);
3705 *pbstrProperty = SysAllocStringLen(NULL, 0);
3709 if(!(*pbstrProperty))
3710 return E_OUTOFMEMORY;
3714 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
3721 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
3723 Uri *This = URI_THIS(iface);
3725 TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
3728 return E_INVALIDARG;
3730 /* Can only return a length for a property if it's a string. */
3731 if(uriProp > Uri_PROPERTY_STRING_LAST)
3732 return E_INVALIDARG;
3734 /* Don't have support for flags yet. */
3736 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
3741 case Uri_PROPERTY_ABSOLUTE_URI:
3742 if(!This->display_absolute) {
3746 if(This->scheme_type != URL_SCHEME_UNKNOWN) {
3747 if(This->userinfo_start > -1 && This->userinfo_len == 0)
3748 /* Don't include the '@' in the length. */
3749 *pcchProperty = This->canon_len-1;
3750 else if(This->userinfo_start > -1 && This->userinfo_len == 1 &&
3751 This->userinfo_split == 0)
3752 /* Don't include the ":@" in the length. */
3753 *pcchProperty = This->canon_len-2;
3755 *pcchProperty = This->canon_len;
3757 *pcchProperty = This->canon_len;
3763 case Uri_PROPERTY_AUTHORITY:
3764 *pcchProperty = This->authority_len;
3765 hres = (This->authority_start > -1) ? S_OK : S_FALSE;
3767 case Uri_PROPERTY_DISPLAY_URI:
3768 if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1)
3769 *pcchProperty = This->canon_len-This->userinfo_len-1;
3771 *pcchProperty = This->canon_len;
3775 case Uri_PROPERTY_DOMAIN:
3776 if(This->domain_offset > -1)
3777 *pcchProperty = This->host_len - This->domain_offset;
3781 hres = (This->domain_offset > -1) ? S_OK : S_FALSE;
3783 case Uri_PROPERTY_EXTENSION:
3784 if(This->extension_offset > -1) {
3785 *pcchProperty = This->path_len - This->extension_offset;
3793 case Uri_PROPERTY_FRAGMENT:
3794 *pcchProperty = This->fragment_len;
3795 hres = (This->fragment_start > -1) ? S_OK : S_FALSE;
3797 case Uri_PROPERTY_HOST:
3798 *pcchProperty = This->host_len;
3800 /* '[' and ']' aren't included in the length. */
3801 if(This->host_type == Uri_HOST_IPV6)
3804 hres = (This->host_start > -1) ? S_OK : S_FALSE;
3806 case Uri_PROPERTY_PASSWORD:
3807 *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_len-This->userinfo_split-1 : 0;
3808 hres = (This->userinfo_split > -1) ? S_OK : S_FALSE;
3810 case Uri_PROPERTY_PATH:
3811 *pcchProperty = This->path_len;
3812 hres = (This->path_start > -1) ? S_OK : S_FALSE;
3814 case Uri_PROPERTY_PATH_AND_QUERY:
3815 *pcchProperty = This->path_len+This->query_len;
3816 hres = (This->path_start > -1 || This->query_start > -1) ? S_OK : S_FALSE;
3818 case Uri_PROPERTY_QUERY:
3819 *pcchProperty = This->query_len;
3820 hres = (This->query_start > -1) ? S_OK : S_FALSE;
3822 case Uri_PROPERTY_RAW_URI:
3823 *pcchProperty = SysStringLen(This->raw_uri);
3826 case Uri_PROPERTY_SCHEME_NAME:
3827 *pcchProperty = This->scheme_len;
3828 hres = (This->scheme_start > -1) ? S_OK : S_FALSE;
3830 case Uri_PROPERTY_USER_INFO:
3831 *pcchProperty = This->userinfo_len;
3832 hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
3834 case Uri_PROPERTY_USER_NAME:
3835 *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_split : This->userinfo_len;
3836 if(This->userinfo_split == 0)
3839 hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
3842 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
3849 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
3851 Uri *This = URI_THIS(iface);
3854 TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
3857 return E_INVALIDARG;
3859 /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
3860 * From what I can tell, instead of checking which URLZONE the URI belongs to it
3861 * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
3864 if(uriProp == Uri_PROPERTY_ZONE) {
3865 *pcchProperty = URLZONE_INVALID;
3869 if(uriProp < Uri_PROPERTY_DWORD_START) {
3871 return E_INVALIDARG;
3875 case Uri_PROPERTY_HOST_TYPE:
3876 *pcchProperty = This->host_type;
3879 case Uri_PROPERTY_PORT:
3880 if(!This->has_port) {
3884 *pcchProperty = This->port;
3889 case Uri_PROPERTY_SCHEME:
3890 *pcchProperty = This->scheme_type;
3894 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
3901 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
3903 Uri *This = URI_THIS(iface);
3904 TRACE("(%p)->(%d %p)\n", This, uriProp, pfHasProperty);
3907 return E_INVALIDARG;
3910 case Uri_PROPERTY_ABSOLUTE_URI:
3911 *pfHasProperty = This->display_absolute;
3913 case Uri_PROPERTY_AUTHORITY:
3914 *pfHasProperty = This->authority_start > -1;
3916 case Uri_PROPERTY_DISPLAY_URI:
3917 *pfHasProperty = TRUE;
3919 case Uri_PROPERTY_DOMAIN:
3920 *pfHasProperty = This->domain_offset > -1;
3922 case Uri_PROPERTY_EXTENSION:
3923 *pfHasProperty = This->extension_offset > -1;
3925 case Uri_PROPERTY_FRAGMENT:
3926 *pfHasProperty = This->fragment_start > -1;
3928 case Uri_PROPERTY_HOST:
3929 *pfHasProperty = This->host_start > -1;
3931 case Uri_PROPERTY_PASSWORD:
3932 *pfHasProperty = This->userinfo_split > -1;
3934 case Uri_PROPERTY_PATH:
3935 *pfHasProperty = This->path_start > -1;
3937 case Uri_PROPERTY_PATH_AND_QUERY:
3938 *pfHasProperty = (This->path_start > -1 || This->query_start > -1);
3940 case Uri_PROPERTY_QUERY:
3941 *pfHasProperty = This->query_start > -1;
3943 case Uri_PROPERTY_RAW_URI:
3944 *pfHasProperty = TRUE;
3946 case Uri_PROPERTY_SCHEME_NAME:
3947 *pfHasProperty = This->scheme_start > -1;
3949 case Uri_PROPERTY_USER_INFO:
3950 *pfHasProperty = This->userinfo_start > -1;
3952 case Uri_PROPERTY_USER_NAME:
3953 if(This->userinfo_split == 0)
3954 *pfHasProperty = FALSE;
3956 *pfHasProperty = This->userinfo_start > -1;
3958 case Uri_PROPERTY_HOST_TYPE:
3959 *pfHasProperty = TRUE;
3961 case Uri_PROPERTY_PORT:
3962 *pfHasProperty = This->has_port;
3964 case Uri_PROPERTY_SCHEME:
3965 *pfHasProperty = TRUE;
3967 case Uri_PROPERTY_ZONE:
3968 *pfHasProperty = FALSE;
3971 FIXME("(%p)->(%d %p): Unsupported property type.\n", This, uriProp, pfHasProperty);
3978 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
3980 TRACE("(%p)->(%p)\n", iface, pstrAbsoluteUri);
3981 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_ABSOLUTE_URI, pstrAbsoluteUri, 0);
3984 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
3986 TRACE("(%p)->(%p)\n", iface, pstrAuthority);
3987 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_AUTHORITY, pstrAuthority, 0);
3990 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
3992 TRACE("(%p)->(%p)\n", iface, pstrDisplayUri);
3993 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_DISPLAY_URI, pstrDisplayUri, 0);
3996 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
3998 TRACE("(%p)->(%p)\n", iface, pstrDomain);
3999 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_DOMAIN, pstrDomain, 0);
4002 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
4004 TRACE("(%p)->(%p)\n", iface, pstrExtension);
4005 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_EXTENSION, pstrExtension, 0);
4008 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
4010 TRACE("(%p)->(%p)\n", iface, pstrFragment);
4011 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_FRAGMENT, pstrFragment, 0);
4014 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
4016 TRACE("(%p)->(%p)\n", iface, pstrHost);
4017 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_HOST, pstrHost, 0);
4020 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
4022 TRACE("(%p)->(%p)\n", iface, pstrPassword);
4023 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_PASSWORD, pstrPassword, 0);
4026 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
4028 TRACE("(%p)->(%p)\n", iface, pstrPath);
4029 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_PATH, pstrPath, 0);
4032 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
4034 TRACE("(%p)->(%p)\n", iface, pstrPathAndQuery);
4035 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_PATH_AND_QUERY, pstrPathAndQuery, 0);
4038 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
4040 TRACE("(%p)->(%p)\n", iface, pstrQuery);
4041 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_QUERY, pstrQuery, 0);
4044 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
4046 TRACE("(%p)->(%p)\n", iface, pstrRawUri);
4047 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
4050 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
4052 TRACE("(%p)->(%p)\n", iface, pstrSchemeName);
4053 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_SCHEME_NAME, pstrSchemeName, 0);
4056 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
4058 TRACE("(%p)->(%p)\n", iface, pstrUserInfo);
4059 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_INFO, pstrUserInfo, 0);
4062 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
4064 TRACE("(%p)->(%p)\n", iface, pstrUserName);
4065 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_NAME, pstrUserName, 0);
4068 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
4070 TRACE("(%p)->(%p)\n", iface, pdwHostType);
4071 return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_HOST_TYPE, pdwHostType, 0);
4074 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
4076 TRACE("(%p)->(%p)\n", iface, pdwPort);
4077 return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_PORT, pdwPort, 0);
4080 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
4082 Uri *This = URI_THIS(iface);
4083 TRACE("(%p)->(%p)\n", This, pdwScheme);
4084 return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_SCHEME, pdwScheme, 0);
4087 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
4089 TRACE("(%p)->(%p)\n", iface, pdwZone);
4090 return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_ZONE,pdwZone, 0);
4093 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
4095 Uri *This = URI_THIS(iface);
4096 TRACE("(%p)->(%p)\n", This, pdwProperties);
4099 return E_INVALIDARG;
4101 /* All URIs have these. */
4102 *pdwProperties = Uri_HAS_DISPLAY_URI|Uri_HAS_RAW_URI|Uri_HAS_SCHEME|Uri_HAS_HOST_TYPE;
4104 if(This->display_absolute)
4105 *pdwProperties |= Uri_HAS_ABSOLUTE_URI;
4107 if(This->scheme_start > -1)
4108 *pdwProperties |= Uri_HAS_SCHEME_NAME;
4110 if(This->authority_start > -1) {
4111 *pdwProperties |= Uri_HAS_AUTHORITY;
4112 if(This->userinfo_start > -1) {
4113 *pdwProperties |= Uri_HAS_USER_INFO;
4114 if(This->userinfo_split != 0)
4115 *pdwProperties |= Uri_HAS_USER_NAME;
4117 if(This->userinfo_split > -1)
4118 *pdwProperties |= Uri_HAS_PASSWORD;
4119 if(This->host_start > -1)
4120 *pdwProperties |= Uri_HAS_HOST;
4121 if(This->domain_offset > -1)
4122 *pdwProperties |= Uri_HAS_DOMAIN;
4126 *pdwProperties |= Uri_HAS_PORT;
4127 if(This->path_start > -1)
4128 *pdwProperties |= Uri_HAS_PATH|Uri_HAS_PATH_AND_QUERY;
4129 if(This->query_start > -1)
4130 *pdwProperties |= Uri_HAS_QUERY|Uri_HAS_PATH_AND_QUERY;
4132 if(This->extension_offset > -1)
4133 *pdwProperties |= Uri_HAS_EXTENSION;
4135 if(This->fragment_start > -1)
4136 *pdwProperties |= Uri_HAS_FRAGMENT;
4141 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
4143 Uri *This = URI_THIS(iface);
4146 TRACE("(%p)->(%p %p)\n", This, pUri, pfEqual);
4154 /* For some reason Windows returns S_OK here... */
4158 /* Try to convert it to a Uri (allows for a more simple comparison). */
4159 if((other = get_uri_obj(pUri)))
4160 *pfEqual = are_equal_simple(This, other);
4162 /* Do it the hard way. */
4163 FIXME("(%p)->(%p %p) No support for unknown IUri's yet.\n", iface, pUri, pfEqual);
4172 static const IUriVtbl UriVtbl = {
4176 Uri_GetPropertyBSTR,
4177 Uri_GetPropertyLength,
4178 Uri_GetPropertyDWORD,
4189 Uri_GetPathAndQuery,
4203 /***********************************************************************
4204 * CreateUri (urlmon.@)
4206 * Creates a new IUri object using the URI represented by pwzURI. This function
4207 * parses and validates the components of pwzURI and then canonicalizes the
4208 * parsed components.
4211 * pwzURI [I] The URI to parse, validate, and canonicalize.
4212 * dwFlags [I] Flags which can affect how the parsing/canonicalization is performed.
4213 * dwReserved [I] Reserved (not used).
4214 * ppURI [O] The resulting IUri after parsing/canonicalization occurs.
4217 * Success: Returns S_OK. ppURI contains the pointer to the newly allocated IUri.
4218 * Failure: E_INVALIDARG if there's invalid flag combinations in dwFlags, or an
4219 * invalid parameters, or pwzURI doesn't represnt a valid URI.
4220 * E_OUTOFMEMORY if any memory allocation fails.
4224 * Uri_CREATE_CANONICALIZE, Uri_CREATE_DECODE_EXTRA_INFO, Uri_CREATE_CRACK_UNKNOWN_SCHEMES,
4225 * Uri_CREATE_PRE_PROCESS_HTML_URI, Uri_CREATE_NO_IE_SETTINGS.
4227 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
4229 const DWORD supported_flags = Uri_CREATE_ALLOW_RELATIVE|Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME|
4230 Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME|Uri_CREATE_NO_CANONICALIZE|Uri_CREATE_CANONICALIZE|
4231 Uri_CREATE_DECODE_EXTRA_INFO|Uri_CREATE_NO_DECODE_EXTRA_INFO|Uri_CREATE_CRACK_UNKNOWN_SCHEMES|
4232 Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES|Uri_CREATE_PRE_PROCESS_HTML_URI|Uri_CREATE_NO_PRE_PROCESS_HTML_URI|
4233 Uri_CREATE_NO_IE_SETTINGS|Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS|Uri_CREATE_FILE_USE_DOS_PATH;
4238 TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
4241 return E_INVALIDARG;
4243 if(!pwzURI || !*pwzURI) {
4245 return E_INVALIDARG;
4248 /* Check for invalid flags. */
4249 if((dwFlags & Uri_CREATE_DECODE_EXTRA_INFO && dwFlags & Uri_CREATE_NO_DECODE_EXTRA_INFO) ||
4250 (dwFlags & Uri_CREATE_CANONICALIZE && dwFlags & Uri_CREATE_NO_CANONICALIZE) ||
4251 (dwFlags & Uri_CREATE_CRACK_UNKNOWN_SCHEMES && dwFlags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES) ||
4252 (dwFlags & Uri_CREATE_PRE_PROCESS_HTML_URI && dwFlags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI) ||
4253 (dwFlags & Uri_CREATE_IE_SETTINGS && dwFlags & Uri_CREATE_NO_IE_SETTINGS)) {
4255 return E_INVALIDARG;
4258 /* Currently unsupported. */
4259 if(dwFlags & ~supported_flags)
4260 FIXME("Ignoring unsupported flag(s) %x\n", dwFlags & ~supported_flags);
4262 ret = heap_alloc(sizeof(Uri));
4264 return E_OUTOFMEMORY;
4266 ret->lpIUriVtbl = &UriVtbl;
4269 /* Pre process the URI, unless told otherwise. */
4270 if(!(dwFlags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI))
4271 ret->raw_uri = pre_process_uri(pwzURI);
4273 ret->raw_uri = SysAllocString(pwzURI);
4277 return E_OUTOFMEMORY;
4280 memset(&data, 0, sizeof(parse_data));
4281 data.uri = ret->raw_uri;
4283 /* Validate and parse the URI into it's components. */
4284 if(!parse_uri(&data, dwFlags)) {
4285 /* Encountered an unsupported or invalid URI */
4286 SysFreeString(ret->raw_uri);
4289 return E_INVALIDARG;
4292 /* Canonicalize the URI. */
4293 hr = canonicalize_uri(&data, ret, dwFlags);
4295 SysFreeString(ret->raw_uri);
4305 /***********************************************************************
4306 * CreateUriWithFragment (urlmon.@)
4308 * Creates a new IUri object. This is almost the same as CreateUri, expect that
4309 * it allows you to explicitly specify a fragment (pwzFragment) for pwzURI.
4312 * pwzURI [I] The URI to parse and perform canonicalization on.
4313 * pwzFragment [I] The explict fragment string which should be added to pwzURI.
4314 * dwFlags [I] The flags which will be passed to CreateUri.
4315 * dwReserved [I] Reserved (not used).
4316 * ppURI [O] The resulting IUri after parsing/canonicalization.
4319 * Success: S_OK. ppURI contains the pointer to the newly allocated IUri.
4320 * Failure: E_INVALIDARG if pwzURI already contains a fragment and pwzFragment
4321 * isn't NULL. Will also return E_INVALIDARG for the same reasons as
4322 * CreateUri will. E_OUTOFMEMORY if any allocations fail.
4324 HRESULT WINAPI CreateUriWithFragment(LPCWSTR pwzURI, LPCWSTR pwzFragment, DWORD dwFlags,
4325 DWORD_PTR dwReserved, IUri **ppURI)
4328 TRACE("(%s %s %x %x %p)\n", debugstr_w(pwzURI), debugstr_w(pwzFragment), dwFlags, (DWORD)dwReserved, ppURI);
4331 return E_INVALIDARG;
4335 return E_INVALIDARG;
4338 /* Check if a fragment should be appended to the URI string. */
4341 DWORD uri_len, frag_len;
4344 /* Check if the original URI already has a fragment component. */
4345 if(StrChrW(pwzURI, '#')) {
4347 return E_INVALIDARG;
4350 uri_len = lstrlenW(pwzURI);
4351 frag_len = lstrlenW(pwzFragment);
4353 /* If the fragment doesn't start with a '#', one will be added. */
4354 add_pound = *pwzFragment != '#';
4357 uriW = heap_alloc((uri_len+frag_len+2)*sizeof(WCHAR));
4359 uriW = heap_alloc((uri_len+frag_len+1)*sizeof(WCHAR));
4362 return E_OUTOFMEMORY;
4364 memcpy(uriW, pwzURI, uri_len*sizeof(WCHAR));
4366 uriW[uri_len++] = '#';
4367 memcpy(uriW+uri_len, pwzFragment, (frag_len+1)*sizeof(WCHAR));
4369 hres = CreateUri(uriW, dwFlags, 0, ppURI);
4373 /* A fragment string wasn't specified, so just forward the call. */
4374 hres = CreateUri(pwzURI, dwFlags, 0, ppURI);
4379 #define URIBUILDER_THIS(iface) DEFINE_THIS(UriBuilder, IUriBuilder, iface)
4381 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
4383 UriBuilder *This = URIBUILDER_THIS(iface);
4385 if(IsEqualGUID(&IID_IUnknown, riid)) {
4386 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
4387 *ppv = URIBUILDER(This);
4388 }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
4389 TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
4390 *ppv = URIBUILDER(This);
4392 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
4394 return E_NOINTERFACE;
4397 IUnknown_AddRef((IUnknown*)*ppv);
4401 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
4403 UriBuilder *This = URIBUILDER_THIS(iface);
4404 LONG ref = InterlockedIncrement(&This->ref);
4406 TRACE("(%p) ref=%d\n", This, ref);
4411 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
4413 UriBuilder *This = URIBUILDER_THIS(iface);
4414 LONG ref = InterlockedDecrement(&This->ref);
4416 TRACE("(%p) ref=%d\n", This, ref);
4419 if(This->uri) IUri_Release(URI(This->uri));
4420 heap_free(This->fragment);
4421 heap_free(This->host);
4422 heap_free(This->password);
4423 heap_free(This->path);
4424 heap_free(This->query);
4425 heap_free(This->scheme);
4426 heap_free(This->username);
4433 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
4434 DWORD dwAllowEncodingPropertyMask,
4435 DWORD_PTR dwReserved,
4438 UriBuilder *This = URIBUILDER_THIS(iface);
4439 TRACE("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
4444 /* Acts the same way as CreateUri. */
4445 if(dwAllowEncodingPropertyMask && (!This->uri || This->modified_props)) {
4450 FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
4454 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
4455 DWORD dwCreateFlags,
4456 DWORD dwAllowEncodingPropertyMask,
4457 DWORD_PTR dwReserved,
4460 UriBuilder *This = URIBUILDER_THIS(iface);
4461 TRACE("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
4466 /* The only time it doesn't return E_NOTIMPL when the dwAllow parameter
4467 * has flags set, is when the IUriBuilder has a IUri set and it hasn't
4468 * been modified (a call to a "Set*" hasn't been performed).
4470 if(dwAllowEncodingPropertyMask && (!This->uri || This->modified_props)) {
4475 FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
4479 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
4480 DWORD dwCreateFlags,
4481 DWORD dwUriBuilderFlags,
4482 DWORD dwAllowEncodingPropertyMask,
4483 DWORD_PTR dwReserved,
4486 UriBuilder *This = URIBUILDER_THIS(iface);
4487 TRACE("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
4488 dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
4493 /* Same as CreateUri. */
4494 if(dwAllowEncodingPropertyMask && (!This->uri || This->modified_props)) {
4499 FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
4500 dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
4504 static HRESULT WINAPI UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
4506 UriBuilder *This = URIBUILDER_THIS(iface);
4507 TRACE("(%p)->(%p)\n", This, ppIUri);
4513 IUri *uri = URI(This->uri);
4522 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
4524 UriBuilder *This = URIBUILDER_THIS(iface);
4525 TRACE("(%p)->(%p)\n", This, pIUri);
4530 if((uri = get_uri_obj(pIUri))) {
4531 /* Only reset the builder if it's Uri isn't the same as
4532 * the Uri passed to the function.
4534 if(This->uri != uri) {
4535 reset_builder(This);
4539 This->port = uri->port;
4544 FIXME("(%p)->(%p) Unknown IUri types not supported yet.\n", This, pIUri);
4547 } else if(This->uri)
4548 /* Only reset the builder if it's Uri isn't NULL. */
4549 reset_builder(This);
4554 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
4556 UriBuilder *This = URIBUILDER_THIS(iface);
4557 TRACE("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
4559 if(!This->uri || This->uri->fragment_start == -1 || This->modified_props & Uri_HAS_FRAGMENT)
4560 return get_builder_component(&This->fragment, &This->fragment_len, NULL, 0, ppwzFragment, pcchFragment);
4562 return get_builder_component(&This->fragment, &This->fragment_len, This->uri->canon_uri+This->uri->fragment_start,
4563 This->uri->fragment_len, ppwzFragment, pcchFragment);
4566 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
4568 UriBuilder *This = URIBUILDER_THIS(iface);
4569 TRACE("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
4571 if(!This->uri || This->uri->host_start == -1 || This->modified_props & Uri_HAS_HOST)
4572 return get_builder_component(&This->host, &This->host_len, NULL, 0, ppwzHost, pcchHost);
4574 if(This->uri->host_type == Uri_HOST_IPV6)
4575 /* Don't include the '[' and ']' around the address. */
4576 return get_builder_component(&This->host, &This->host_len, This->uri->canon_uri+This->uri->host_start+1,
4577 This->uri->host_len-2, ppwzHost, pcchHost);
4579 return get_builder_component(&This->host, &This->host_len, This->uri->canon_uri+This->uri->host_start,
4580 This->uri->host_len, ppwzHost, pcchHost);
4584 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
4586 UriBuilder *This = URIBUILDER_THIS(iface);
4587 TRACE("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
4589 if(!This->uri || This->uri->userinfo_split == -1 || This->modified_props & Uri_HAS_PASSWORD)
4590 return get_builder_component(&This->password, &This->password_len, NULL, 0, ppwzPassword, pcchPassword);
4592 const WCHAR *start = This->uri->canon_uri+This->uri->userinfo_start+This->uri->userinfo_split+1;
4593 DWORD len = This->uri->userinfo_len-This->uri->userinfo_split-1;
4594 return get_builder_component(&This->password, &This->password_len, start, len, ppwzPassword, pcchPassword);
4598 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
4600 UriBuilder *This = URIBUILDER_THIS(iface);
4601 TRACE("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
4603 if(!This->uri || This->uri->path_start == -1 || This->modified_props & Uri_HAS_PATH)
4604 return get_builder_component(&This->path, &This->path_len, NULL, 0, ppwzPath, pcchPath);
4606 return get_builder_component(&This->path, &This->path_len, This->uri->canon_uri+This->uri->path_start,
4607 This->uri->path_len, ppwzPath, pcchPath);
4610 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
4612 UriBuilder *This = URIBUILDER_THIS(iface);
4613 TRACE("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
4626 *pfHasPort = This->has_port;
4627 *pdwPort = This->port;
4631 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
4633 UriBuilder *This = URIBUILDER_THIS(iface);
4634 TRACE("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
4636 if(!This->uri || This->uri->query_start == -1 || This->modified_props & Uri_HAS_QUERY)
4637 return get_builder_component(&This->query, &This->query_len, NULL, 0, ppwzQuery, pcchQuery);
4639 return get_builder_component(&This->query, &This->query_len, This->uri->canon_uri+This->uri->query_start,
4640 This->uri->query_len, ppwzQuery, pcchQuery);
4643 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
4645 UriBuilder *This = URIBUILDER_THIS(iface);
4646 TRACE("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
4648 if(!This->uri || This->uri->scheme_start == -1 || This->modified_props & Uri_HAS_SCHEME_NAME)
4649 return get_builder_component(&This->scheme, &This->scheme_len, NULL, 0, ppwzSchemeName, pcchSchemeName);
4651 return get_builder_component(&This->scheme, &This->scheme_len, This->uri->canon_uri+This->uri->scheme_start,
4652 This->uri->scheme_len, ppwzSchemeName, pcchSchemeName);
4655 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
4657 UriBuilder *This = URIBUILDER_THIS(iface);
4658 TRACE("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
4660 if(!This->uri || This->uri->userinfo_start == -1 || This->uri->userinfo_split == 0 ||
4661 This->modified_props & Uri_HAS_USER_NAME)
4662 return get_builder_component(&This->username, &This->username_len, NULL, 0, ppwzUserName, pcchUserName);
4664 const WCHAR *start = This->uri->canon_uri+This->uri->userinfo_start;
4666 /* Check if there's a password in the userinfo section. */
4667 if(This->uri->userinfo_split > -1)
4668 /* Don't include the password. */
4669 return get_builder_component(&This->username, &This->username_len, start,
4670 This->uri->userinfo_split, ppwzUserName, pcchUserName);
4672 return get_builder_component(&This->username, &This->username_len, start,
4673 This->uri->userinfo_len, ppwzUserName, pcchUserName);
4677 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
4679 UriBuilder *This = URIBUILDER_THIS(iface);
4680 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
4681 return set_builder_component(&This->fragment, &This->fragment_len, pwzNewValue, '#',
4682 &This->modified_props, Uri_HAS_FRAGMENT);
4685 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
4687 UriBuilder *This = URIBUILDER_THIS(iface);
4688 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
4689 return set_builder_component(&This->host, &This->host_len, pwzNewValue, 0,
4690 &This->modified_props, Uri_HAS_HOST);
4693 static HRESULT WINAPI UriBuilder_SetPassword(IUriBuilder *iface, LPCWSTR pwzNewValue)
4695 UriBuilder *This = URIBUILDER_THIS(iface);
4696 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
4697 return set_builder_component(&This->password, &This->password_len, pwzNewValue, 0,
4698 &This->modified_props, Uri_HAS_PASSWORD);
4701 static HRESULT WINAPI UriBuilder_SetPath(IUriBuilder *iface, LPCWSTR pwzNewValue)
4703 UriBuilder *This = URIBUILDER_THIS(iface);
4704 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
4705 return set_builder_component(&This->path, &This->path_len, pwzNewValue, 0,
4706 &This->modified_props, Uri_HAS_PATH);
4709 static HRESULT WINAPI UriBuilder_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
4711 UriBuilder *This = URIBUILDER_THIS(iface);
4712 TRACE("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
4714 This->has_port = fHasPort;
4715 This->port = dwNewValue;
4716 This->modified_props |= Uri_HAS_PORT;
4720 static HRESULT WINAPI UriBuilder_SetQuery(IUriBuilder *iface, LPCWSTR pwzNewValue)
4722 UriBuilder *This = URIBUILDER_THIS(iface);
4723 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
4724 return set_builder_component(&This->query, &This->query_len, pwzNewValue, '?',
4725 &This->modified_props, Uri_HAS_QUERY);
4728 static HRESULT WINAPI UriBuilder_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
4730 UriBuilder *This = URIBUILDER_THIS(iface);
4731 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
4733 /* Only set the scheme name if it's not NULL or empty. */
4734 if(pwzNewValue && *pwzNewValue)
4735 return set_builder_component(&This->scheme, &This->scheme_len, pwzNewValue, 0,
4736 &This->modified_props, Uri_HAS_SCHEME_NAME);
4738 return E_INVALIDARG;
4741 static HRESULT WINAPI UriBuilder_SetUserName(IUriBuilder *iface, LPCWSTR pwzNewValue)
4743 UriBuilder *This = URIBUILDER_THIS(iface);
4744 TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
4745 return set_builder_component(&This->username, &This->username_len, pwzNewValue, 0,
4746 &This->modified_props, Uri_HAS_USER_NAME);
4749 static HRESULT WINAPI UriBuilder_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
4751 const DWORD accepted_flags = Uri_HAS_AUTHORITY|Uri_HAS_DOMAIN|Uri_HAS_EXTENSION|Uri_HAS_FRAGMENT|Uri_HAS_HOST|
4752 Uri_HAS_PASSWORD|Uri_HAS_PATH|Uri_HAS_PATH_AND_QUERY|Uri_HAS_QUERY|
4753 Uri_HAS_USER_INFO|Uri_HAS_USER_NAME;
4755 UriBuilder *This = URIBUILDER_THIS(iface);
4756 TRACE("(%p)->(0x%08x)\n", This, dwPropertyMask);
4758 if(dwPropertyMask & ~accepted_flags)
4759 return E_INVALIDARG;
4761 if(dwPropertyMask & Uri_HAS_FRAGMENT)
4762 UriBuilder_SetFragment(iface, NULL);
4764 if(dwPropertyMask & Uri_HAS_HOST)
4765 UriBuilder_SetHost(iface, NULL);
4767 if(dwPropertyMask & Uri_HAS_PASSWORD)
4768 UriBuilder_SetPassword(iface, NULL);
4770 if(dwPropertyMask & Uri_HAS_PATH)
4771 UriBuilder_SetPath(iface, NULL);
4773 if(dwPropertyMask & Uri_HAS_PORT)
4774 UriBuilder_SetPort(iface, FALSE, 0);
4776 if(dwPropertyMask & Uri_HAS_QUERY)
4777 UriBuilder_SetQuery(iface, NULL);
4779 if(dwPropertyMask & Uri_HAS_USER_NAME)
4780 UriBuilder_SetUserName(iface, NULL);
4785 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
4787 UriBuilder *This = URIBUILDER_THIS(iface);
4788 TRACE("(%p)->(%p)\n", This, pfModified);
4793 *pfModified = This->modified_props > 0;
4797 #undef URIBUILDER_THIS
4799 static const IUriBuilderVtbl UriBuilderVtbl = {
4800 UriBuilder_QueryInterface,
4803 UriBuilder_CreateUriSimple,
4804 UriBuilder_CreateUri,
4805 UriBuilder_CreateUriWithFlags,
4808 UriBuilder_GetFragment,
4810 UriBuilder_GetPassword,
4813 UriBuilder_GetQuery,
4814 UriBuilder_GetSchemeName,
4815 UriBuilder_GetUserName,
4816 UriBuilder_SetFragment,
4818 UriBuilder_SetPassword,
4821 UriBuilder_SetQuery,
4822 UriBuilder_SetSchemeName,
4823 UriBuilder_SetUserName,
4824 UriBuilder_RemoveProperties,
4825 UriBuilder_HasBeenModified,
4828 /***********************************************************************
4829 * CreateIUriBuilder (urlmon.@)
4831 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
4835 TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
4840 ret = heap_alloc_zero(sizeof(UriBuilder));
4842 return E_OUTOFMEMORY;
4844 ret->lpIUriBuilderVtbl = &UriBuilderVtbl;
4850 if((uri = get_uri_obj(pIUri))) {
4855 /* Windows doesn't set 'has_port' to TRUE in this case. */
4856 ret->port = uri->port;
4860 *ppIUriBuilder = NULL;
4861 FIXME("(%p %x %x %p): Unknown IUri types not supported yet.\n", pIUri, dwFlags,
4862 (DWORD)dwReserved, ppIUriBuilder);
4867 *ppIUriBuilder = URIBUILDER(ret);