4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
33 #define NO_SHLWAPI_STREAM
35 #include "wine/debug.h"
37 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
38 BOOL WINAPI MLFreeLibrary(HMODULE);
39 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
41 WINE_DEFAULT_DEBUG_CHANNEL(shell);
43 /* The following schemes were identified in the native version of
44 * SHLWAPI.DLL version 5.50
47 URL_SCHEME_INVALID = -1,
48 URL_SCHEME_UNKNOWN = 0,
63 URL_SCHEME_JAVASCRIPT,
71 URL_SCHEME scheme_number;
75 static const SHL_2_inet_scheme shlwapi_schemes[] = {
76 {URL_SCHEME_FTP, "ftp"},
77 {URL_SCHEME_HTTP, "http"},
78 {URL_SCHEME_GOPHER, "gopher"},
79 {URL_SCHEME_MAILTO, "mailto"},
80 {URL_SCHEME_NEWS, "news"},
81 {URL_SCHEME_NNTP, "nntp"},
82 {URL_SCHEME_TELNET, "telnet"},
83 {URL_SCHEME_WAIS, "wais"},
84 {URL_SCHEME_FILE, "file"},
85 {URL_SCHEME_MK, "mk"},
86 {URL_SCHEME_HTTPS, "https"},
87 {URL_SCHEME_SHELL, "shell"},
88 {URL_SCHEME_SNEWS, "snews"},
89 {URL_SCHEME_LOCAL, "local"},
90 {URL_SCHEME_JAVASCRIPT, "javascript"},
91 {URL_SCHEME_VBSCRIPT, "vbscript"},
92 {URL_SCHEME_ABOUT, "about"},
93 {URL_SCHEME_RES, "res"},
98 LPCWSTR pScheme; /* [out] start of scheme */
99 DWORD szScheme; /* [out] size of scheme (until colon) */
100 LPCWSTR pUserName; /* [out] start of Username */
101 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
102 LPCWSTR pPassword; /* [out] start of Password */
103 DWORD szPassword; /* [out] size of Password (until "@") */
104 LPCWSTR pHostName; /* [out] start of Hostname */
105 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
106 LPCWSTR pPort; /* [out] start of Port */
107 DWORD szPort; /* [out] size of Port (until "/" or eos) */
108 LPCWSTR pQuery; /* [out] start of Query */
109 DWORD szQuery; /* [out] size of Query (until eos) */
117 } WINE_URL_SCAN_TYPE;
120 INT size; /* [in] (always 0x18) */
121 LPCSTR ap1; /* [out] start of scheme */
122 INT sizep1; /* [out] size of scheme (until colon) */
123 LPCSTR ap2; /* [out] pointer following first colon */
124 INT sizep2; /* [out] size of remainder */
125 INT fcncde; /* [out] function match of p1 (0 if unknown) */
129 INT size; /* [in] (always 0x18) */
130 LPCWSTR ap1; /* [out] start of scheme */
131 INT sizep1; /* [out] size of scheme (until colon) */
132 LPCWSTR ap2; /* [out] pointer following first colon */
133 INT sizep2; /* [out] size of remainder */
134 INT fcncde; /* [out] function match of p1 (0 if unknown) */
137 static const CHAR hexDigits[] = "0123456789ABCDEF";
139 static const WCHAR fileW[] = {'f','i','l','e','\0'};
141 static const unsigned char HashDataLookup[256] = {
142 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
143 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
144 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
145 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
146 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
147 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
148 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
149 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
150 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
151 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
152 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
153 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
154 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
155 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
156 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
157 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
158 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
159 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
160 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
161 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
163 static inline BOOL URL_NeedEscapeA(CHAR ch, DWORD dwFlags)
169 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
176 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
179 if (ch <= 31 || ch >= 127)
201 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
208 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags)
214 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
221 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == L'%'))
224 if (ch <= 31 || ch >= 127)
246 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
253 static BOOL URL_JustLocation(LPCWSTR str)
255 while(*str && (*str == L'/')) str++;
257 while (*str && ((*str == L'-') ||
259 isalnumW(*str))) str++;
260 if (*str == L'/') return FALSE;
266 /*************************************************************************
269 * Parse a Url into its constituent parts.
273 * y [O] Undocumented structure holding the parsed information
276 * Success: S_OK. y contains the parsed Url details.
277 * Failure: An HRESULT error code.
279 DWORD WINAPI ParseURLA(LPCSTR x, UNKNOWN_SHLWAPI_1 *y)
282 const SHL_2_inet_scheme *inet_pro;
284 y->fcncde = URL_SCHEME_INVALID;
285 if (y->size != 0x18) return E_INVALIDARG;
286 /* FIXME: leading white space generates error of 0x80041001 which
289 if (*x <= ' ') return 0x80041001;
304 /* check for no scheme in string start */
305 /* (apparently schemes *must* be larger than a single character) */
306 if ((*x == '\0') || (y->sizep1 <= 1)) {
311 /* found scheme, set length of remainder */
312 y->sizep2 = lstrlenA(y->ap2);
314 /* see if known scheme and return indicator number */
315 y->fcncde = URL_SCHEME_UNKNOWN;
316 inet_pro = shlwapi_schemes;
317 while (inet_pro->scheme_name) {
318 if (!strncasecmp(inet_pro->scheme_name, y->ap1,
319 min(y->sizep1, lstrlenA(inet_pro->scheme_name)))) {
320 y->fcncde = inet_pro->scheme_number;
328 /*************************************************************************
331 * Unicode version of ParseURLA.
333 DWORD WINAPI ParseURLW(LPCWSTR x, UNKNOWN_SHLWAPI_2 *y)
336 const SHL_2_inet_scheme *inet_pro;
340 y->fcncde = URL_SCHEME_INVALID;
341 if (y->size != 0x18) return E_INVALIDARG;
342 /* FIXME: leading white space generates error of 0x80041001 which
345 if (*x <= L' ') return 0x80041001;
360 /* check for no scheme in string start */
361 /* (apparently schemes *must* be larger than a single character) */
362 if ((*x == L'\0') || (y->sizep1 <= 1)) {
367 /* found scheme, set length of remainder */
368 y->sizep2 = lstrlenW(y->ap2);
370 /* see if known scheme and return indicator number */
371 len = WideCharToMultiByte(0, 0, y->ap1, y->sizep1, 0, 0, 0, 0);
372 cmpstr = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len+1);
373 WideCharToMultiByte(0, 0, y->ap1, y->sizep1, cmpstr, len+1, 0, 0);
374 y->fcncde = URL_SCHEME_UNKNOWN;
375 inet_pro = shlwapi_schemes;
376 while (inet_pro->scheme_name) {
377 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
378 min(len, lstrlenA(inet_pro->scheme_name)))) {
379 y->fcncde = inet_pro->scheme_number;
384 HeapFree(GetProcessHeap(), 0, cmpstr);
388 /*************************************************************************
389 * UrlCanonicalizeA [SHLWAPI.@]
391 * Canonicalize a Url.
394 * pszUrl [I] Url to cCanonicalize
395 * pszCanonicalized [O] Destination for converted Url.
396 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
397 * dwFlags [I] Flags controlling the conversion.
400 * Success: S_OK. The pszCanonicalized contains the converted Url.
401 * Failure: E_POINTER, if *pcchCanonicalized is too small.
403 * MSDN incorrectly describes the flags for this function. They should be:
404 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
405 *| URL_ESCAPE_SPACES_ONLY 0x04000000
406 *| URL_ESCAPE_PERCENT 0x00001000
407 *| URL_ESCAPE_UNSAFE 0x10000000
408 *| URL_UNESCAPE 0x10000000
409 *| URL_DONT_SIMPLIFY 0x08000000
410 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
412 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
413 LPDWORD pcchCanonicalized, DWORD dwFlags)
415 LPWSTR base, canonical;
416 DWORD ret, len, len2;
418 TRACE("(%s %p %p 0x%08lx) using W version\n",
419 debugstr_a(pszUrl), pszCanonicalized,
420 pcchCanonicalized, dwFlags);
422 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
423 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
424 canonical = base + INTERNET_MAX_URL_LENGTH;
426 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
427 len = INTERNET_MAX_URL_LENGTH;
429 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
431 HeapFree(GetProcessHeap(), 0, base);
435 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
436 if (len2 > *pcchCanonicalized) {
437 *pcchCanonicalized = len;
438 HeapFree(GetProcessHeap(), 0, base);
441 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
442 *pcchCanonicalized, 0, 0);
443 *pcchCanonicalized = len2;
444 HeapFree(GetProcessHeap(), 0, base);
448 /*************************************************************************
449 * UrlCanonicalizeW [SHLWAPI.@]
451 * See UrlCanonicalizeA.
453 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
454 LPDWORD pcchCanonicalized, DWORD dwFlags)
458 LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
459 INT nLen, nByteLen, state;
461 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
462 pcchCanonicalized, dwFlags);
464 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
465 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
467 if (dwFlags & URL_DONT_SIMPLIFY)
468 memcpy(lpszUrlCpy, pszUrl, nByteLen);
474 * 1 have 2[+] alnum 2,3
475 * 2 have scheme (found :) 4,6,3
476 * 3 failed (no location)
478 * 5 have 1[+] alnum 6,3
479 * 6 have location (found /) save root location
482 wk1 = (LPWSTR)pszUrl;
488 if (!isalnumW(*wk1)) {state = 3; break;}
490 if (!isalnumW(*wk1)) {state = 3; break;}
496 if (*wk1++ == L':') state = 2;
499 if (*wk1 != L'/') {state = 3; break;}
501 if (*wk1 != L'/') {state = 6; break;}
511 if (!isalnumW(*wk1) && (*wk1 != L'-')) {state = 3; break;}
512 while(isalnumW(*wk1) || (*wk1 == L'-')) *wk2++ = *wk1++;
516 if (*wk1 != L'/') {state = 3; break;}
521 /* Now at root location, cannot back up any more. */
522 /* "root" will point at the '/' */
525 TRACE("wk1=%c\n", (CHAR)*wk1);
526 mp = strchrW(wk1, L'/');
534 strncpyW(wk2, wk1, nLen);
538 TRACE("found '/.'\n");
539 if (*(wk1+1) == L'/') {
540 /* case of /./ -> skip the ./ */
543 else if (*(wk1+1) == L'.') {
544 /* found /.. look for next / */
545 TRACE("found '/..'\n");
546 if (*(wk1+2) == L'/') {
547 /* case /../ -> need to backup wk2 */
548 TRACE("found '/../'\n");
549 *(wk2-1) = L'\0'; /* set end of string */
550 mp = strrchrW(root, L'/');
551 if (mp && (mp >= root)) {
552 /* found valid backup point */
557 /* did not find point, restore '/' */
567 FIXME("how did we get here - state=%d\n", state);
572 TRACE("Simplified, orig <%s>, simple <%s>\n",
573 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
576 if(dwFlags & URL_UNESCAPE)
577 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
579 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
580 URL_ESCAPE_SPACES_ONLY |
582 URL_DONT_ESCAPE_EXTRA_INFO |
583 URL_ESCAPE_SEGMENT_ONLY ))) {
584 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
585 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
587 } else { /* No escaping needed, just copy the string */
588 nLen = lstrlenW(lpszUrlCpy);
589 if(nLen < *pcchCanonicalized)
590 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
595 *pcchCanonicalized = nLen;
598 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
601 TRACE("result %s\n", debugstr_w(pszCanonicalized));
606 /*************************************************************************
607 * UrlCombineA [SHLWAPI.@]
612 * pszBase [I] Base Url
613 * pszRelative [I] Url to combine with pszBase
614 * pszCombined [O] Destination for combined Url
615 * pcchCombined [O] Destination for length of pszCombined
616 * dwFlags [I] URL_ flags from "shlwapi.h"
619 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
620 * contains its length.
621 * Failure: An HRESULT error code indicating the error.
623 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
624 LPSTR pszCombined, LPDWORD pcchCombined,
627 LPWSTR base, relative, combined;
628 DWORD ret, len, len2;
630 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
631 debugstr_a(pszBase),debugstr_a(pszRelative),
632 *pcchCombined,dwFlags);
634 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
635 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
636 relative = base + INTERNET_MAX_URL_LENGTH;
637 combined = relative + INTERNET_MAX_URL_LENGTH;
639 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
640 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
641 len = INTERNET_MAX_URL_LENGTH;
643 ret = UrlCombineW(base, relative, combined, &len, dwFlags);
645 HeapFree(GetProcessHeap(), 0, base);
649 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
650 if (len2 > *pcchCombined) {
651 *pcchCombined = len2;
652 HeapFree(GetProcessHeap(), 0, base);
655 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, *pcchCombined,
657 *pcchCombined = len2;
658 HeapFree(GetProcessHeap(), 0, base);
662 /*************************************************************************
663 * UrlCombineW [SHLWAPI.@]
667 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
668 LPWSTR pszCombined, LPDWORD pcchCombined,
671 UNKNOWN_SHLWAPI_2 base, relative;
672 DWORD myflags, sizeloc = 0;
673 DWORD len, res1, res2, process_case = 0;
674 LPWSTR work, preliminary, mbase, mrelative;
675 WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
676 WCHAR single_slash[] = {'/','\0'};
679 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
680 debugstr_w(pszBase),debugstr_w(pszRelative),
681 *pcchCombined,dwFlags);
686 /* Get space for duplicates of the input and the output */
687 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
689 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
690 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
691 *preliminary = L'\0';
693 /* Canonicalize the base input prior to looking for the scheme */
694 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
695 len = INTERNET_MAX_URL_LENGTH;
696 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
698 /* Canonicalize the relative input prior to looking for the scheme */
699 len = INTERNET_MAX_URL_LENGTH;
700 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
702 /* See if the base has a scheme */
703 res1 = ParseURLW(mbase, &base);
705 /* if pszBase has no scheme, then return pszRelative */
706 TRACE("no scheme detected in Base\n");
711 /* get size of location field (if it exists) */
712 work = (LPWSTR)base.ap2;
714 if (*work++ == L'/') {
715 if (*work++ == L'/') {
716 /* At this point have start of location and
717 * it ends at next '/' or end of string.
719 while(*work && (*work != L'/')) work++;
720 sizeloc = (DWORD)(work - base.ap2);
724 /* Change .sizep2 to not have the last leaf in it,
725 * Note: we need to start after the location (if it exists)
727 work = strrchrW((base.ap2+sizeloc), L'/');
729 len = (DWORD)(work - base.ap2 + 1);
734 * .ap2 points to location (starting with '//')
735 * .sizep2 length of location (above) and rest less the last
737 * sizeloc length of location (above) up to but not including
741 res2 = ParseURLW(mrelative, &relative);
743 /* no scheme in pszRelative */
744 TRACE("no scheme detected in Relative\n");
745 relative.ap2 = mrelative; /* case 3,4,5 depends on this */
746 relative.sizep2 = strlenW(mrelative);
747 if (*pszRelative == L':') {
748 /* case that is either left alone or uses pszBase */
749 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
756 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
757 /* case that becomes "file:///" */
758 strcpyW(preliminary, myfilestr);
762 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
763 /* pszRelative has location and rest */
767 if (*mrelative == L'/') {
768 /* case where pszRelative is root to location */
772 process_case = (*base.ap2 == L'/') ? 5 : 3;
776 /* handle cases where pszRelative has scheme */
777 if ((base.sizep1 == relative.sizep1) &&
778 (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) {
780 /* since the schemes are the same */
781 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
782 /* case where pszRelative replaces location and following */
786 if (*relative.ap2 == L'/') {
787 /* case where pszRelative is root to location */
791 /* case where scheme is followed by document path */
795 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
796 /* case where pszRelative replaces scheme, location,
797 * and following and handles PLUGGABLE
804 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
808 switch (process_case) {
811 * Return pszRelative appended to what ever is in pszCombined,
812 * (which may the string "file:///"
814 len = strlenW(mrelative) + strlenW(preliminary);
815 if (len+1 > *pcchCombined) {
820 strcatW(preliminary, mrelative);
824 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
825 * and pszRelative starts with "//", then append a "/"
827 len = strlenW(mrelative) + 1;
828 if (len+1 > *pcchCombined) {
833 strcpyW(preliminary, mrelative);
834 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
835 URL_JustLocation(relative.ap2))
836 strcatW(preliminary, single_slash);
840 * Return the pszBase scheme with pszRelative. Basicly
841 * keeps the scheme and replaces the domain and following.
843 len = base.sizep1 + 1 + relative.sizep2 + 1;
844 if (len+1 > *pcchCombined) {
849 strncpyW(preliminary, base.ap1, base.sizep1 + 1);
850 work = preliminary + base.sizep1 + 1;
851 strcpyW(work, relative.ap2);
852 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
853 URL_JustLocation(relative.ap2))
854 strcatW(work, single_slash);
858 * Return the pszBase scheme and location but everything
859 * after the location is pszRelative. (Replace document
862 len = base.sizep1 + 1 + sizeloc + relative.sizep2 + 1;
863 if (len+1 > *pcchCombined) {
868 strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc);
869 work = preliminary + base.sizep1 + 1 + sizeloc;
870 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
872 strcpyW(work, relative.ap2);
876 * Return the pszBase without its document (if any) and
877 * append pszRelative after its scheme.
879 len = base.sizep1 + 1 + base.sizep2 + relative.sizep2;
880 if (len+1 > *pcchCombined) {
885 strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2);
886 work = preliminary + base.sizep1+1+base.sizep2 - 1;
889 strcpyW(work, relative.ap2);
893 FIXME("How did we get here????? process_case=%ld\n", process_case);
899 * Now that the combining is done, process the escape options if
900 * necessary, otherwise just copy the string.
902 myflags = dwFlags & (URL_ESCAPE_PERCENT |
903 URL_ESCAPE_SPACES_ONLY |
904 URL_DONT_ESCAPE_EXTRA_INFO |
905 URL_ESCAPE_SEGMENT_ONLY);
907 ret = UrlEscapeW(preliminary, pszCombined,
908 pcchCombined, myflags);
910 len = (strlenW(preliminary) + 1) * sizeof(WCHAR);
911 memcpy(pszCombined, preliminary, len);
912 *pcchCombined = strlenW(preliminary);
914 TRACE("return-%ld len=%ld, %s\n",
915 process_case, *pcchCombined, debugstr_w(pszCombined));
917 HeapFree(GetProcessHeap(), 0, preliminary);
921 /*************************************************************************
922 * UrlEscapeA [SHLWAPI.@]
924 * Converts unsafe characters in a Url into escape sequences.
927 * pszUrl [I] Url to modify
928 * pszEscaped [O] Destination for modified Url
929 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
930 * dwFlags [I] URL_ flags from "shlwapi.h"
933 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
934 * contains its length.
935 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
936 * pcchEscaped is set to the required length.
938 * Converts unsafe characters into their escape sequences.
941 * - By default this function stops converting at the first '?' or
942 * '#' character (MSDN does not document this).
943 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
944 * converted, but the conversion continues past a '?' or '#'.
945 * - Note that this function did not work well (or at all) in shlwapi version 4.
948 * Only the following flags are implemented:
949 *| URL_ESCAPE_SPACES_ONLY
950 *| URL_DONT_ESCAPE_EXTRA_INFO
951 *| URL_ESCAPE_SEGMENT_ONLY
952 *| URL_ESCAPE_PERCENT
954 HRESULT WINAPI UrlEscapeA(
961 DWORD needed = 0, ret;
962 BOOL stop_escaping = FALSE;
963 char next[3], *dst = pszEscaped;
966 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
967 *pcchEscaped, dwFlags);
969 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
970 URL_ESCAPE_SEGMENT_ONLY |
971 URL_DONT_ESCAPE_EXTRA_INFO |
973 FIXME("Unimplemented flags: %08lx\n", dwFlags);
976 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
977 /* if SPACES_ONLY specified, reset the other controls */
978 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
980 URL_ESCAPE_SEGMENT_ONLY);
983 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
984 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
986 for(src = pszUrl; *src; src++) {
987 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
988 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
989 (*src == '#' || *src == '?'))
990 stop_escaping = TRUE;
992 if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
993 /* TRACE("escaping %c\n", *src); */
995 next[1] = hexDigits[(*src >> 4) & 0xf];
996 next[2] = hexDigits[*src & 0xf];
999 /* TRACE("passing %c\n", *src); */
1004 if(needed + len <= *pcchEscaped) {
1005 memcpy(dst, next, len);
1011 if(needed < *pcchEscaped) {
1015 needed++; /* add one for the '\0' */
1018 *pcchEscaped = needed;
1022 /*************************************************************************
1023 * UrlEscapeW [SHLWAPI.@]
1027 HRESULT WINAPI UrlEscapeW(
1030 LPDWORD pcchEscaped,
1034 DWORD needed = 0, ret;
1035 BOOL stop_escaping = FALSE;
1036 WCHAR next[5], *dst = pszEscaped;
1039 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
1040 pcchEscaped, dwFlags);
1042 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1043 URL_ESCAPE_SEGMENT_ONLY |
1044 URL_DONT_ESCAPE_EXTRA_INFO |
1045 URL_ESCAPE_PERCENT))
1046 FIXME("Unimplemented flags: %08lx\n", dwFlags);
1049 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1050 /* if SPACES_ONLY specified, reset the other controls */
1051 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1052 URL_ESCAPE_PERCENT |
1053 URL_ESCAPE_SEGMENT_ONLY);
1056 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1057 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1059 for(src = pszUrl; *src; src++) {
1061 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1062 * (*src == L'#' || *src == L'?'))
1063 * stop_escaping = TRUE;
1065 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1066 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1067 (*src == L'#' || *src == L'?'))
1068 stop_escaping = TRUE;
1070 if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
1071 /* TRACE("escaping %c\n", *src); */
1074 * I would have assumed that the W form would escape
1075 * the character with 4 hex digits (or even 8),
1076 * however, experiments show that native shlwapi escapes
1077 * with only 2 hex digits.
1078 * next[1] = hexDigits[(*src >> 12) & 0xf];
1079 * next[2] = hexDigits[(*src >> 8) & 0xf];
1080 * next[3] = hexDigits[(*src >> 4) & 0xf];
1081 * next[4] = hexDigits[*src & 0xf];
1084 next[1] = hexDigits[(*src >> 4) & 0xf];
1085 next[2] = hexDigits[*src & 0xf];
1088 /* TRACE("passing %c\n", *src); */
1093 if(needed + len <= *pcchEscaped) {
1094 memcpy(dst, next, len*sizeof(WCHAR));
1100 if(needed < *pcchEscaped) {
1104 needed++; /* add one for the '\0' */
1107 *pcchEscaped = needed;
1112 /*************************************************************************
1113 * UrlUnescapeA [SHLWAPI.@]
1115 * Converts Url escape sequences back to ordinary characters.
1118 * pszUrl [I/O] Url to convert
1119 * pszUnescaped [O] Destination for converted Url
1120 * pcchUnescaped [I/O] Size of output string
1121 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1124 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1125 * dwFlags includes URL_ESCAPE_INPLACE.
1126 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1127 * this case pcchUnescaped is set to the size required.
1129 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1130 * the first occurrence of either a '?' or '#' character.
1132 HRESULT WINAPI UrlUnescapeA(
1135 LPDWORD pcchUnescaped,
1142 BOOL stop_unescaping = FALSE;
1144 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1145 pcchUnescaped, dwFlags);
1147 if(dwFlags & URL_UNESCAPE_INPLACE)
1148 dst = (char*)pszUrl;
1152 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1153 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1154 (*src == '#' || *src == '?')) {
1155 stop_unescaping = TRUE;
1157 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1158 && stop_unescaping == FALSE) {
1161 memcpy(buf, src + 1, 2);
1163 ih = strtol(buf, NULL, 16);
1165 src += 2; /* Advance to end of escape */
1169 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1173 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1177 needed++; /* add one for the '\0' */
1180 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1181 *pcchUnescaped = needed;
1184 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1185 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1191 /*************************************************************************
1192 * UrlUnescapeW [SHLWAPI.@]
1196 HRESULT WINAPI UrlUnescapeW(
1198 LPWSTR pszUnescaped,
1199 LPDWORD pcchUnescaped,
1206 BOOL stop_unescaping = FALSE;
1208 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1209 pcchUnescaped, dwFlags);
1211 if(dwFlags & URL_UNESCAPE_INPLACE)
1212 dst = (WCHAR*)pszUrl;
1216 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1217 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1218 (*src == L'#' || *src == L'?')) {
1219 stop_unescaping = TRUE;
1221 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1222 && stop_unescaping == FALSE) {
1225 memcpy(buf, src + 1, 2*sizeof(WCHAR));
1227 ih = StrToIntW(buf);
1229 src += 2; /* Advance to end of escape */
1233 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1237 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1241 needed++; /* add one for the '\0' */
1244 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1245 *pcchUnescaped = needed;
1248 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1249 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1255 /*************************************************************************
1256 * UrlGetLocationA [SHLWAPI.@]
1258 * Get the location from a Url.
1261 * pszUrl [I] Url to get the location from
1264 * A pointer to the start of the location in pszUrl, or NULL if there is
1268 * - MSDN erroneously states that "The location is the segment of the Url
1269 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1270 * stop at '?' and always return a NULL in this case.
1271 * - MSDN also erroneously states that "If a file URL has a query string,
1272 * the returned string is the query string". In all tested cases, if the
1273 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1276 *| NULL file://aa/b/cd#hohoh
1277 *| #hohoh http://aa/b/cd#hohoh
1278 *| NULL fi://aa/b/cd#hohoh
1279 *| #hohoh ff://aa/b/cd#hohoh
1281 LPCSTR WINAPI UrlGetLocationA(
1284 UNKNOWN_SHLWAPI_1 base;
1288 res1 = ParseURLA(pszUrl, &base);
1289 if (res1) return NULL; /* invalid scheme */
1291 /* if scheme is file: then never return pointer */
1292 if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL;
1294 /* Look for '#' and return its addr */
1295 return strchr(base.ap2, '#');
1298 /*************************************************************************
1299 * UrlGetLocationW [SHLWAPI.@]
1301 * See UrlGetLocationA.
1303 LPCWSTR WINAPI UrlGetLocationW(
1306 UNKNOWN_SHLWAPI_2 base;
1310 res1 = ParseURLW(pszUrl, &base);
1311 if (res1) return NULL; /* invalid scheme */
1313 /* if scheme is file: then never return pointer */
1314 if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL;
1316 /* Look for '#' and return its addr */
1317 return strchrW(base.ap2, L'#');
1320 /*************************************************************************
1321 * UrlCompareA [SHLWAPI.@]
1326 * pszUrl1 [I] First Url to compare
1327 * pszUrl2 [I] Url to compare to pszUrl1
1328 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1331 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1332 * than, equal to, or less than pszUrl1 respectively.
1334 INT WINAPI UrlCompareA(
1339 INT ret, len, len1, len2;
1342 return strcmp(pszUrl1, pszUrl2);
1343 len1 = strlen(pszUrl1);
1344 if (pszUrl1[len1-1] == '/') len1--;
1345 len2 = strlen(pszUrl2);
1346 if (pszUrl2[len2-1] == '/') len2--;
1348 return strncmp(pszUrl1, pszUrl2, len1);
1349 len = min(len1, len2);
1350 ret = strncmp(pszUrl1, pszUrl2, len);
1351 if (ret) return ret;
1352 if (len1 > len2) return 1;
1356 /*************************************************************************
1357 * UrlCompareW [SHLWAPI.@]
1361 INT WINAPI UrlCompareW(
1367 size_t len, len1, len2;
1370 return strcmpW(pszUrl1, pszUrl2);
1371 len1 = strlenW(pszUrl1);
1372 if (pszUrl1[len1-1] == '/') len1--;
1373 len2 = strlenW(pszUrl2);
1374 if (pszUrl2[len2-1] == '/') len2--;
1376 return strncmpW(pszUrl1, pszUrl2, len1);
1377 len = min(len1, len2);
1378 ret = strncmpW(pszUrl1, pszUrl2, len);
1379 if (ret) return ret;
1380 if (len1 > len2) return 1;
1384 /*************************************************************************
1385 * HashData [SHLWAPI.@]
1387 * Hash an input block into a variable sized digest.
1390 * lpSrc [I] Input block
1391 * nSrcLen [I] Length of lpSrc
1392 * lpDest [I] Output for hash digest
1393 * nDestLen [I] Length of lpDest
1396 * Success: TRUE. lpDest is filled with the computed hash value.
1397 * Failure: FALSE, if any argument is invalid.
1399 BOOL WINAPI HashData(const unsigned char *lpSrc, INT nSrcLen,
1400 unsigned char *lpDest, INT nDestLen)
1402 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1404 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1405 IsBadWritePtr(lpDest, nDestLen))
1408 while (destCount >= 0)
1410 lpDest[destCount] = (destCount & 0xff);
1414 while (srcCount >= 0)
1416 destCount = nDestLen - 1;
1417 while (destCount >= 0)
1419 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1427 /*************************************************************************
1428 * UrlHashA [SHLWAPI.@]
1430 * Produce a Hash from a Url.
1433 * pszUrl [I] Url to hash
1434 * lpDest [O] Destinationh for hash
1435 * nDestLen [I] Length of lpDest
1438 * Success: S_OK. lpDest is filled with the computed hash value.
1439 * Failure: E_INVALIDARG, if any argument is invalid.
1441 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1443 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1444 return E_INVALIDARG;
1446 HashData((PBYTE)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1450 /*************************************************************************
1451 * UrlHashW [SHLWAPI.@]
1455 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1457 char szUrl[MAX_PATH];
1459 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1461 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1462 return E_INVALIDARG;
1464 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1465 * return the same digests for the same URL.
1467 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1468 HashData((PBYTE)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1472 /*************************************************************************
1473 * UrlApplySchemeA [SHLWAPI.@]
1475 * Apply a scheme to a Url.
1478 * pszIn [I] Url to apply scheme to
1479 * pszOut [O] Destination for modified Url
1480 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1481 * dwFlags [I] URL_ flags from "shlwapi.h"
1484 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1485 * Failure: An HRESULT error code describing the error.
1487 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1490 DWORD ret, len, len2;
1492 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1493 debugstr_a(pszIn), *pcchOut, dwFlags);
1495 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1496 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1497 out = in + INTERNET_MAX_URL_LENGTH;
1499 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1500 len = INTERNET_MAX_URL_LENGTH;
1502 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1503 if ((ret != S_OK) && (ret != S_FALSE)) {
1504 HeapFree(GetProcessHeap(), 0, in);
1508 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1509 if (len2 > *pcchOut) {
1511 HeapFree(GetProcessHeap(), 0, in);
1514 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1516 HeapFree(GetProcessHeap(), 0, in);
1520 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1525 DWORD value_len, data_len, dwType, i;
1526 WCHAR reg_path[MAX_PATH];
1527 WCHAR value[MAX_PATH], data[MAX_PATH];
1530 MultiByteToWideChar(0, 0,
1531 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1532 -1, reg_path, MAX_PATH);
1533 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1535 while(value_len = data_len = MAX_PATH,
1536 RegEnumValueW(newkey, index, value, &value_len,
1537 0, &dwType, (LPVOID)data, &data_len) == 0) {
1538 TRACE("guess %d %s is %s\n",
1539 index, debugstr_w(value), debugstr_w(data));
1542 for(i=0; i<value_len; i++) {
1545 /* remember that TRUE is not-equal */
1546 j = ChrCmpIW(Wxx, Wyy);
1549 if ((i == value_len) && !j) {
1550 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1551 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1552 RegCloseKey(newkey);
1555 strcpyW(pszOut, data);
1556 strcatW(pszOut, pszIn);
1557 *pcchOut = strlenW(pszOut);
1558 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1559 RegCloseKey(newkey);
1564 RegCloseKey(newkey);
1568 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1571 DWORD data_len, dwType;
1572 WCHAR reg_path[MAX_PATH];
1573 WCHAR value[MAX_PATH], data[MAX_PATH];
1575 /* get and prepend default */
1576 MultiByteToWideChar(0, 0,
1577 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1578 -1, reg_path, MAX_PATH);
1579 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1580 data_len = MAX_PATH;
1583 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1584 RegCloseKey(newkey);
1585 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1586 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1589 strcpyW(pszOut, data);
1590 strcatW(pszOut, pszIn);
1591 *pcchOut = strlenW(pszOut);
1592 TRACE("used default %s\n", debugstr_w(pszOut));
1596 /*************************************************************************
1597 * UrlApplySchemeW [SHLWAPI.@]
1599 * See UrlApplySchemeA.
1601 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1603 UNKNOWN_SHLWAPI_2 in_scheme;
1607 TRACE("(in %s, out size %ld, flags %08lx)\n",
1608 debugstr_w(pszIn), *pcchOut, dwFlags);
1610 if (dwFlags & URL_APPLY_GUESSFILE) {
1611 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1612 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1613 strcpyW(pszOut, pszIn);
1614 *pcchOut = strlenW(pszOut);
1618 in_scheme.size = 24;
1619 /* See if the base has a scheme */
1620 res1 = ParseURLW(pszIn, &in_scheme);
1622 /* no scheme in input, need to see if we need to guess */
1623 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1624 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1629 /* we have a scheme, see if valid (known scheme) */
1630 if (in_scheme.fcncde) {
1631 /* have valid scheme, so just copy and exit */
1632 if (strlenW(pszIn) + 1 > *pcchOut) {
1633 *pcchOut = strlenW(pszIn) + 1;
1636 strcpyW(pszOut, pszIn);
1637 *pcchOut = strlenW(pszOut);
1638 TRACE("valid scheme, returing copy\n");
1643 /* If we are here, then either invalid scheme,
1644 * or no scheme and can't/failed guess.
1646 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1648 (dwFlags & URL_APPLY_DEFAULT)) {
1649 /* find and apply default scheme */
1650 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1653 /* just copy and give proper return code */
1654 if (strlenW(pszIn) + 1 > *pcchOut) {
1655 *pcchOut = strlenW(pszIn) + 1;
1658 strcpyW(pszOut, pszIn);
1659 *pcchOut = strlenW(pszOut);
1660 TRACE("returning copy, left alone\n");
1664 /*************************************************************************
1665 * UrlIsA [SHLWAPI.@]
1667 * Determine if a Url is of a certain class.
1670 * pszUrl [I] Url to check
1671 * Urlis [I] URLIS_ constant from "shlwapi.h"
1674 * TRUE if pszUrl belongs to the class type in Urlis.
1677 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1679 UNKNOWN_SHLWAPI_1 base;
1686 res1 = ParseURLA(pszUrl, &base);
1687 if (res1) return FALSE; /* invalid scheme */
1688 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1689 /* has scheme followed by 2 '/' */
1694 case URLIS_NOHISTORY:
1696 case URLIS_APPLIABLE:
1697 case URLIS_DIRECTORY:
1698 case URLIS_HASQUERY:
1700 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1705 /*************************************************************************
1706 * UrlIsW [SHLWAPI.@]
1710 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1712 UNKNOWN_SHLWAPI_2 base;
1719 res1 = ParseURLW(pszUrl, &base);
1720 if (res1) return FALSE; /* invalid scheme */
1721 if ((*base.ap2 == L'/') && (*(base.ap2+1) == L'/'))
1722 /* has scheme followed by 2 '/' */
1727 case URLIS_NOHISTORY:
1729 case URLIS_APPLIABLE:
1730 case URLIS_DIRECTORY:
1731 case URLIS_HASQUERY:
1733 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1738 /*************************************************************************
1739 * UrlIsNoHistoryA [SHLWAPI.@]
1741 * Determine if a Url should not be stored in the users history list.
1744 * pszUrl [I] Url to check
1747 * TRUE, if pszUrl should be excluded from the history list,
1750 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1752 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1755 /*************************************************************************
1756 * UrlIsNoHistoryW [SHLWAPI.@]
1758 * See UrlIsNoHistoryA.
1760 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1762 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1765 /*************************************************************************
1766 * UrlIsOpaqueA [SHLWAPI.@]
1768 * Determine if a Url is opaque.
1771 * pszUrl [I] Url to check
1774 * TRUE if pszUrl is opaque,
1778 * An opaque Url is one that does not start with "<protocol>://".
1780 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1782 return UrlIsA(pszUrl, URLIS_OPAQUE);
1785 /*************************************************************************
1786 * UrlIsOpaqueW [SHLWAPI.@]
1790 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1792 return UrlIsW(pszUrl, URLIS_OPAQUE);
1795 /*************************************************************************
1796 * Scans for characters of type "type" and when not matching found,
1797 * returns pointer to it and length in size.
1799 * Characters tested based on RFC 1738
1801 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1803 static DWORD alwayszero = 0;
1812 if ( (islowerW(*start) && isalphaW(*start)) ||
1827 if ( isalphaW(*start) ||
1829 /* user/password only characters */
1834 /* *extra* characters */
1837 (*start == L'\'') ||
1841 /* *safe* characters */
1849 } else if (*start == L'%') {
1850 if (isxdigitW(*(start+1)) &&
1851 isxdigitW(*(start+2))) {
1863 if (isdigitW(*start)) {
1874 if (isalnumW(*start) ||
1876 (*start == L'.') ) {
1885 FIXME("unknown type %d\n", type);
1886 return (LPWSTR)&alwayszero;
1888 /* TRACE("scanned %ld characters next char %p<%c>\n",
1889 *size, start, *start); */
1893 /*************************************************************************
1894 * Attempt to parse URL into pieces.
1896 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1900 memset(pl, 0, sizeof(WINE_PARSE_URL));
1901 pl->pScheme = pszUrl;
1902 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1903 if (!*work || (*work != L':')) goto ERROR;
1905 if ((*work != L'/') || (*(work+1) != L'/')) goto ERROR;
1906 pl->pUserName = work + 2;
1907 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1908 if (*work == L':' ) {
1909 /* parse password */
1911 pl->pPassword = work;
1912 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1913 if (*work != L'@') {
1914 /* what we just parsed must be the hostname and port
1915 * so reset pointers and clear then let it parse */
1916 pl->szUserName = pl->szPassword = 0;
1917 work = pl->pUserName - 1;
1918 pl->pUserName = pl->pPassword = 0;
1920 } else if (*work == L'@') {
1924 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1925 /* what was parsed was hostname, so reset pointers and let it parse */
1926 pl->szUserName = pl->szPassword = 0;
1927 work = pl->pUserName - 1;
1928 pl->pUserName = pl->pPassword = 0;
1931 /* now start parsing hostname or hostnumber */
1933 pl->pHostName = work;
1934 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1935 if (*work == L':') {
1939 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1941 if (*work == L'/') {
1942 /* see if query string */
1943 pl->pQuery = strchrW(work, L'?');
1944 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1946 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1947 pl->pScheme, pl->szScheme,
1948 pl->pUserName, pl->szUserName,
1949 pl->pPassword, pl->szPassword,
1950 pl->pHostName, pl->szHostName,
1951 pl->pPort, pl->szPort,
1952 pl->pQuery, pl->szQuery);
1955 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1956 return E_INVALIDARG;
1959 /*************************************************************************
1960 * UrlGetPartA [SHLWAPI.@]
1962 * Retrieve part of a Url.
1965 * pszIn [I] Url to parse
1966 * pszOut [O] Destination for part of pszIn requested
1967 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1968 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1969 * dwFlags [I] URL_ flags from "shlwapi.h"
1972 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1973 * Failure: An HRESULT error code describing the error.
1975 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1976 DWORD dwPart, DWORD dwFlags)
1979 DWORD ret, len, len2;
1981 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1982 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1983 out = in + INTERNET_MAX_URL_LENGTH;
1985 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1987 len = INTERNET_MAX_URL_LENGTH;
1988 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
1991 HeapFree(GetProcessHeap(), 0, in);
1995 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
1996 if (len2 > *pcchOut) {
1998 HeapFree(GetProcessHeap(), 0, in);
2001 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2003 HeapFree(GetProcessHeap(), 0, in);
2007 /*************************************************************************
2008 * UrlGetPartW [SHLWAPI.@]
2012 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2013 DWORD dwPart, DWORD dwFlags)
2017 DWORD size, schsize;
2018 LPCWSTR addr, schaddr;
2021 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2022 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2024 ret = URL_ParseUrl(pszIn, &pl);
2026 schaddr = pl.pScheme;
2027 schsize = pl.szScheme;
2030 case URL_PART_SCHEME:
2031 if (!pl.szScheme) return E_INVALIDARG;
2036 case URL_PART_HOSTNAME:
2037 if (!pl.szHostName) return E_INVALIDARG;
2038 addr = pl.pHostName;
2039 size = pl.szHostName;
2042 case URL_PART_USERNAME:
2043 if (!pl.szUserName) return E_INVALIDARG;
2044 addr = pl.pUserName;
2045 size = pl.szUserName;
2048 case URL_PART_PASSWORD:
2049 if (!pl.szPassword) return E_INVALIDARG;
2050 addr = pl.pPassword;
2051 size = pl.szPassword;
2055 if (!pl.szPort) return E_INVALIDARG;
2060 case URL_PART_QUERY:
2061 if (!pl.szQuery) return E_INVALIDARG;
2067 return E_INVALIDARG;
2070 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2071 if (*pcchOut < size + schsize + 2) {
2072 *pcchOut = size + schsize + 2;
2075 strncpyW(pszOut, schaddr, schsize);
2076 work = pszOut + schsize;
2078 strncpyW(work+1, addr, size);
2079 *pcchOut = size + schsize + 1;
2084 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2085 strncpyW(pszOut, addr, size);
2087 work = pszOut + size;
2090 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2095 /*************************************************************************
2096 * PathIsURLA [SHLWAPI.@]
2098 * Check if the given path is a Url.
2101 * lpszPath [I] Path to check.
2104 * TRUE if lpszPath is a Url.
2105 * FALSE if lpszPath is NULL or not a Url.
2107 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2109 UNKNOWN_SHLWAPI_1 base;
2112 if (!lpstrPath || !*lpstrPath) return FALSE;
2115 base.size = sizeof(base);
2116 res1 = ParseURLA(lpstrPath, &base);
2117 return (base.fcncde > 0);
2120 /*************************************************************************
2121 * PathIsURLW [SHLWAPI.@]
2125 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2127 UNKNOWN_SHLWAPI_2 base;
2130 if (!lpstrPath || !*lpstrPath) return FALSE;
2133 base.size = sizeof(base);
2134 res1 = ParseURLW(lpstrPath, &base);
2135 return (base.fcncde > 0);
2138 /*************************************************************************
2139 * UrlCreateFromPathA [SHLWAPI.@]
2141 * Create a Url from a file path.
2144 * pszPath [I] Path to convert
2145 * pszUrl [O] Destination for the converted Url
2146 * pcchUrl [I/O] Length of pszUrl
2147 * dwReserved [I] Reserved, must be 0
2150 * Success: S_OK. pszUrl contains the converted path.
2151 * Failure: An HRESULT error code.
2153 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2155 DWORD nCharBeforeColon = 0;
2157 DWORD dwChRequired = 0;
2158 LPSTR pszNewUrl = NULL;
2159 LPCSTR pszConstPointer = NULL;
2160 LPSTR pszPointer = NULL;
2164 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved);
2166 /* Validate arguments */
2167 if (dwReserved != 0)
2169 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved);
2170 return E_INVALIDARG;
2172 if (!pszUrl || !pcchUrl || !pszUrl)
2174 ERR("Invalid argument\n");
2175 return E_INVALIDARG;
2178 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2180 if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) ||
2181 *pszConstPointer == '.' || *pszConstPointer == '-')
2185 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2187 dwChRequired = lstrlenA(pszPath);
2188 if (dwChRequired > *pcchUrl)
2190 *pcchUrl = dwChRequired;
2195 *pcchUrl = dwChRequired;
2196 StrCpyA(pszUrl, pszPath);
2200 /* then must need converting to file: format */
2202 /* Strip off leading slashes */
2203 while (*pszPath == '\\' || *pszPath == '/')
2209 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2210 TRACE("pszUrl: %s\n", debugstr_a(pszPath));
2211 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1);
2212 ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2213 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl));
2214 TRACE("%ld\n", dwChRequired);
2215 if (ret != E_POINTER && FAILED(ret))
2217 dwChRequired += 5; /* "file:" */
2218 if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':'))
2220 dwChRequired += 3; /* "///" */
2226 case 0: /* no slashes */
2228 case 2: /* two slashes */
2235 default: /* three slashes */
2240 if (dwChRequired > *pcchUrl)
2242 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2243 StrCpyA(pszUrl, "file:");
2244 pszPointer = pszUrl + lstrlenA(pszUrl);
2245 for (i=0; i < nSlashes; i++)
2250 StrCpyA(pszPointer, pszNewUrl);
2251 TRACE("<- %s\n", debugstr_a(pszUrl));
2255 /*************************************************************************
2256 * UrlCreateFromPathW [SHLWAPI.@]
2258 * See UrlCreateFromPathA.
2260 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2262 DWORD nCharBeforeColon = 0;
2264 DWORD dwChRequired = 0;
2265 LPWSTR pszNewUrl = NULL;
2266 LPCWSTR pszConstPointer = NULL;
2267 LPWSTR pszPointer = NULL;
2271 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2273 /* Validate arguments */
2274 if (dwReserved != 0)
2275 return E_INVALIDARG;
2276 if (!pszUrl || !pcchUrl || !pszUrl)
2277 return E_INVALIDARG;
2279 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2281 if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) ||
2282 *pszConstPointer == '.' || *pszConstPointer == '-')
2286 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2288 dwChRequired = lstrlenW(pszPath);
2289 *pcchUrl = dwChRequired;
2290 if (dwChRequired > *pcchUrl)
2294 StrCpyW(pszUrl, pszPath);
2298 /* then must need converting to file: format */
2300 /* Strip off leading slashes */
2301 while (*pszPath == '\\' || *pszPath == '/')
2307 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2308 ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2309 if (ret != E_POINTER && FAILED(ret))
2311 dwChRequired += 5; /* "file:" */
2312 if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':'))
2314 dwChRequired += 3; /* "///" */
2320 case 0: /* no slashes */
2322 case 2: /* two slashes */
2329 default: /* three slashes */
2334 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2335 if (dwChRequired > *pcchUrl)
2337 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR));
2338 StrCpyW(pszNewUrl, fileW);
2339 pszPointer = pszNewUrl + 4;
2342 for (i=0; i < nSlashes; i++)
2347 StrCpyW(pszPointer, pszPath);
2348 StrCpyW(pszUrl, pszNewUrl);
2352 /*************************************************************************
2353 * SHAutoComplete [SHLWAPI.@]
2355 * Enable auto-completion for an edit control.
2358 * hwndEdit [I] Handle of control to enable auto-completion for
2359 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2362 * Success: S_OK. Auto-completion is enabled for the control.
2363 * Failure: An HRESULT error code indicating the error.
2365 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2367 FIXME("SHAutoComplete stub\n");
2371 /*************************************************************************
2372 * MLBuildResURLA [SHLWAPI.405]
2374 * Create a Url pointing to a resource in a module.
2377 * lpszLibName [I] Name of the module containing the resource
2378 * hMod [I] Callers module handle
2379 * dwFlags [I] Undocumented flags for loading the module
2380 * lpszRes [I] Resource name
2381 * lpszDest [O] Destination for resulting Url
2382 * dwDestLen [I] Length of lpszDest
2385 * Success: S_OK. lpszDest constains the resource Url.
2386 * Failure: E_INVALIDARG, if any argument is invalid, or
2387 * E_FAIL if dwDestLen is too small.
2389 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2390 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2392 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2396 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2399 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2401 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2402 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2404 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2405 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2406 if (SUCCEEDED(hRet) && lpszDest)
2407 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2412 /*************************************************************************
2413 * MLBuildResURLA [SHLWAPI.406]
2415 * See MLBuildResURLA.
2417 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2418 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2420 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2421 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2422 HRESULT hRet = E_FAIL;
2424 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2425 debugstr_w(lpszRes), lpszDest, dwDestLen);
2427 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2428 !lpszDest || (dwFlags && dwFlags != 2))
2429 return E_INVALIDARG;
2431 if (dwDestLen >= szResLen + 1)
2433 dwDestLen -= (szResLen + 1);
2434 memcpy(lpszDest, szRes, sizeof(szRes));
2436 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2440 WCHAR szBuff[MAX_PATH];
2442 if (GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR)))
2444 DWORD dwPathLen = strlenW(szBuff) + 1;
2446 if (dwDestLen >= dwPathLen)
2450 dwDestLen -= dwPathLen;
2451 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2453 dwResLen = strlenW(lpszRes) + 1;
2454 if (dwDestLen >= dwResLen + 1)
2456 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2457 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2462 MLFreeLibrary(hMod);