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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
34 #define NO_SHLWAPI_STREAM
37 #include "wine/debug.h"
39 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
40 BOOL WINAPI MLFreeLibrary(HMODULE);
41 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell);
45 /* The following schemes were identified in the native version of
46 * SHLWAPI.DLL version 5.50
49 URL_SCHEME scheme_number;
50 WCHAR scheme_name[12];
51 } shlwapi_schemes[] = {
52 {URL_SCHEME_FTP, {'f','t','p',0}},
53 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
54 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
55 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
56 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
57 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
58 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
59 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
60 {URL_SCHEME_FILE, {'f','i','l','e',0}},
61 {URL_SCHEME_MK, {'m','k',0}},
62 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
63 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
64 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
65 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
66 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
67 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
68 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
69 {URL_SCHEME_RES, {'r','e','s',0}},
73 LPCWSTR pScheme; /* [out] start of scheme */
74 DWORD szScheme; /* [out] size of scheme (until colon) */
75 LPCWSTR pUserName; /* [out] start of Username */
76 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
77 LPCWSTR pPassword; /* [out] start of Password */
78 DWORD szPassword; /* [out] size of Password (until "@") */
79 LPCWSTR pHostName; /* [out] start of Hostname */
80 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
81 LPCWSTR pPort; /* [out] start of Port */
82 DWORD szPort; /* [out] size of Port (until "/" or eos) */
83 LPCWSTR pQuery; /* [out] start of Query */
84 DWORD szQuery; /* [out] size of Query (until eos) */
94 static const CHAR hexDigits[] = "0123456789ABCDEF";
96 static const WCHAR fileW[] = {'f','i','l','e','\0'};
98 static const unsigned char HashDataLookup[256] = {
99 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
100 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
101 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
102 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
103 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
104 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
105 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
106 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
107 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
108 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
109 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
110 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
111 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
112 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
113 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
114 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
115 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
116 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
117 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
118 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
120 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
124 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
125 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
126 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
127 return shlwapi_schemes[i].scheme_number;
130 return URL_SCHEME_UNKNOWN;
133 /*************************************************************************
136 * Parse a Url into its constituent parts.
140 * y [O] Undocumented structure holding the parsed information
143 * Success: S_OK. y contains the parsed Url details.
144 * Failure: An HRESULT error code.
146 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
148 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
152 TRACE("%s %p\n", debugstr_a(x), y);
154 if(y->cbSize != sizeof(*y))
157 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
160 if (*ptr != ':' || ptr <= x+1) {
161 y->pszProtocol = NULL;
162 return URL_E_INVALID_SYNTAX;
166 y->cchProtocol = ptr-x;
167 y->pszSuffix = ptr+1;
168 y->cchSuffix = strlen(y->pszSuffix);
170 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
171 scheme, sizeof(scheme)/sizeof(WCHAR));
172 y->nScheme = get_scheme_code(scheme, len);
177 /*************************************************************************
180 * Unicode version of ParseURLA.
182 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
184 const WCHAR *ptr = x;
186 TRACE("%s %p\n", debugstr_w(x), y);
188 if(y->cbSize != sizeof(*y))
191 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
194 if (*ptr != ':' || ptr <= x+1) {
195 y->pszProtocol = NULL;
196 return URL_E_INVALID_SYNTAX;
200 y->cchProtocol = ptr-x;
201 y->pszSuffix = ptr+1;
202 y->cchSuffix = strlenW(y->pszSuffix);
203 y->nScheme = get_scheme_code(x, ptr-x);
208 /*************************************************************************
209 * UrlCanonicalizeA [SHLWAPI.@]
211 * Canonicalize a Url.
214 * pszUrl [I] Url to cCanonicalize
215 * pszCanonicalized [O] Destination for converted Url.
216 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
217 * dwFlags [I] Flags controlling the conversion.
220 * Success: S_OK. The pszCanonicalized contains the converted Url.
221 * Failure: E_POINTER, if *pcchCanonicalized is too small.
223 * MSDN incorrectly describes the flags for this function. They should be:
224 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
225 *| URL_ESCAPE_SPACES_ONLY 0x04000000
226 *| URL_ESCAPE_PERCENT 0x00001000
227 *| URL_ESCAPE_UNSAFE 0x10000000
228 *| URL_UNESCAPE 0x10000000
229 *| URL_DONT_SIMPLIFY 0x08000000
230 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
232 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
233 LPDWORD pcchCanonicalized, DWORD dwFlags)
235 LPWSTR url, canonical;
239 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
240 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
242 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
245 len = strlen(pszUrl)+1;
246 url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
247 canonical = HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized*sizeof(WCHAR));
248 if(!url || !canonical) {
249 HeapFree(GetProcessHeap(), 0, url);
250 HeapFree(GetProcessHeap(), 0, canonical);
251 return E_OUTOFMEMORY;
254 MultiByteToWideChar(0, 0, pszUrl, -1, url, len);
256 ret = UrlCanonicalizeW(url, canonical, pcchCanonicalized, dwFlags);
258 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized,
259 *pcchCanonicalized+1, 0, 0);
261 HeapFree(GetProcessHeap(), 0, canonical);
265 /*************************************************************************
266 * UrlCanonicalizeW [SHLWAPI.@]
268 * See UrlCanonicalizeA.
270 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
271 LPDWORD pcchCanonicalized, DWORD dwFlags)
276 LPWSTR lpszUrlCpy, url, wk2, mp, mp2;
278 DWORD nByteLen, nLen, nWkLen;
282 static const WCHAR wszFile[] = {'f','i','l','e',':'};
283 static const WCHAR wszRes[] = {'r','e','s',':'};
284 static const WCHAR wszHttp[] = {'h','t','t','p',':'};
285 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
286 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
288 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
289 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
291 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
295 *pszCanonicalized = 0;
299 /* Remove '\t' characters from URL */
300 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
301 url = HeapAlloc(GetProcessHeap(), 0, nByteLen);
303 return E_OUTOFMEMORY;
313 /* Allocate memory for simplified URL (before escaping) */
314 nByteLen = (wk2-url)*sizeof(WCHAR);
315 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
316 nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR));
318 HeapFree(GetProcessHeap(), 0, url);
319 return E_OUTOFMEMORY;
322 is_file_url = !strncmpW(wszFile, url, sizeof(wszFile)/sizeof(WCHAR));
324 if ((nByteLen >= sizeof(wszHttp) &&
325 !memcmp(wszHttp, url, sizeof(wszHttp))) || is_file_url)
328 if((dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY)) && is_file_url)
331 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, url, sizeof(wszRes))) {
332 dwFlags &= ~URL_FILE_USE_PATHURL;
339 * 1 have 2[+] alnum 2,3
340 * 2 have scheme (found :) 4,6,3
341 * 3 failed (no location)
343 * 5 have 1[+] alnum 6,3
344 * 6 have location (found /) save root location
351 if(url[1] == ':') { /* Assume path */
352 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
353 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
354 if (dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY))
360 dwFlags |= URL_ESCAPE_UNSAFE;
368 if (!isalnumW(*wk1)) {state = 3; break;}
370 if (!isalnumW(*wk1)) {state = 3; break;}
376 if (*wk1++ == ':') state = 2;
380 if (*wk1 != '/') {state = 6; break;}
382 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
384 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
385 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
386 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
390 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL)){
392 }else if(is_file_url){
393 const WCHAR *body = wk1;
398 if(isalnumW(*body) && *(body+1) == ':'){
399 if(!(dwFlags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL))){
406 if(dwFlags & URL_WININET_COMPATIBILITY){
407 if(*wk1 == '/' && *(wk1+1) != '/'){
414 if(*wk1 == '/' && *(wk1+1) != '/'){
427 nWkLen = strlenW(wk1);
428 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
435 if(*mp == '/' || *mp == '\\')
442 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
444 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
455 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
456 while(*wk1 == '/' || *wk1 == '\\') {
466 if(dwFlags & URL_DONT_SIMPLIFY) {
471 /* Now at root location, cannot back up any more. */
472 /* "root" will point at the '/' */
476 mp = strchrW(wk1, '/');
477 mp2 = strchrW(wk1, '\\');
478 if(mp2 && (!mp || mp2 < mp))
481 nWkLen = strlenW(wk1);
482 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
489 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
499 while (*wk1 == '.') {
500 TRACE("found '/.'\n");
501 if (wk1[1] == '/' || wk1[1] == '\\') {
502 /* case of /./ -> skip the ./ */
505 else if (wk1[1] == '.' && (wk1[2] == '/'
506 || wk1[2] == '\\' || wk1[2] == '?'
507 || wk1[2] == '#' || !wk1[2])) {
508 /* case /../ -> need to backup wk2 */
509 TRACE("found '/../'\n");
510 *(wk2-1) = '\0'; /* set end of string */
511 mp = strrchrW(root, '/');
512 mp2 = strrchrW(root, '\\');
513 if(mp2 && (!mp || mp2 < mp))
515 if (mp && (mp >= root)) {
516 /* found valid backup point */
518 if(wk1[2] != '/' && wk1[2] != '\\')
524 /* did not find point, restore '/' */
536 FIXME("how did we get here - state=%d\n", state);
537 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
538 HeapFree(GetProcessHeap(), 0, url);
542 TRACE("Simplified, orig <%s>, simple <%s>\n",
543 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
545 nLen = lstrlenW(lpszUrlCpy);
546 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
547 lpszUrlCpy[--nLen]=0;
549 if((dwFlags & URL_UNESCAPE) ||
550 ((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
551 && !memcmp(wszFile, url, sizeof(wszFile))))
552 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
554 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
555 URL_ESCAPE_SPACES_ONLY |
557 URL_DONT_ESCAPE_EXTRA_INFO |
558 URL_ESCAPE_SEGMENT_ONLY ))) {
559 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
560 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
562 } else { /* No escaping needed, just copy the string */
563 nLen = lstrlenW(lpszUrlCpy);
564 if(nLen < *pcchCanonicalized)
565 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
570 *pcchCanonicalized = nLen;
573 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
574 HeapFree(GetProcessHeap(), 0, url);
577 TRACE("result %s\n", debugstr_w(pszCanonicalized));
582 /*************************************************************************
583 * UrlCombineA [SHLWAPI.@]
588 * pszBase [I] Base Url
589 * pszRelative [I] Url to combine with pszBase
590 * pszCombined [O] Destination for combined Url
591 * pcchCombined [O] Destination for length of pszCombined
592 * dwFlags [I] URL_ flags from "shlwapi.h"
595 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
596 * contains its length.
597 * Failure: An HRESULT error code indicating the error.
599 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
600 LPSTR pszCombined, LPDWORD pcchCombined,
603 LPWSTR base, relative, combined;
604 DWORD ret, len, len2;
606 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
607 debugstr_a(pszBase),debugstr_a(pszRelative),
608 pcchCombined?*pcchCombined:0,dwFlags);
610 if(!pszBase || !pszRelative || !pcchCombined)
613 base = HeapAlloc(GetProcessHeap(), 0,
614 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
615 relative = base + INTERNET_MAX_URL_LENGTH;
616 combined = relative + INTERNET_MAX_URL_LENGTH;
618 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
619 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
622 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
625 HeapFree(GetProcessHeap(), 0, base);
629 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
630 if (len2 > *pcchCombined) {
631 *pcchCombined = len2;
632 HeapFree(GetProcessHeap(), 0, base);
635 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
637 *pcchCombined = len2;
638 HeapFree(GetProcessHeap(), 0, base);
642 /*************************************************************************
643 * UrlCombineW [SHLWAPI.@]
647 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
648 LPWSTR pszCombined, LPDWORD pcchCombined,
651 PARSEDURLW base, relative;
652 DWORD myflags, sizeloc = 0;
653 DWORD len, res1, res2, process_case = 0;
654 LPWSTR work, preliminary, mbase, mrelative;
655 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
658 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
659 debugstr_w(pszBase),debugstr_w(pszRelative),
660 pcchCombined?*pcchCombined:0,dwFlags);
662 if(!pszBase || !pszRelative || !pcchCombined)
665 base.cbSize = sizeof(base);
666 relative.cbSize = sizeof(relative);
668 /* Get space for duplicates of the input and the output */
669 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
671 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
672 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
675 /* Canonicalize the base input prior to looking for the scheme */
676 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
677 len = INTERNET_MAX_URL_LENGTH;
678 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
680 /* Canonicalize the relative input prior to looking for the scheme */
681 len = INTERNET_MAX_URL_LENGTH;
682 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
684 /* See if the base has a scheme */
685 res1 = ParseURLW(mbase, &base);
687 /* if pszBase has no scheme, then return pszRelative */
688 TRACE("no scheme detected in Base\n");
692 BOOL manual_search = FALSE;
694 /* mk is a special case */
695 if(base.nScheme == URL_SCHEME_MK) {
696 static const WCHAR wsz[] = {':',':',0};
698 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
703 delta = ptr-base.pszSuffix;
704 base.cchProtocol += delta;
705 base.pszSuffix += delta;
706 base.cchSuffix -= delta;
709 /* get size of location field (if it exists) */
710 work = (LPWSTR)base.pszSuffix;
712 if (*work++ == '/') {
713 if (*work++ == '/') {
714 /* At this point have start of location and
715 * it ends at next '/' or end of string.
717 while(*work && (*work != '/')) work++;
718 sizeloc = (DWORD)(work - base.pszSuffix);
723 /* If there is a '#' and the characters immediately preceding it are
724 * ".htm[l]", then begin looking for the last leaf starting from
725 * the '#'. Otherwise the '#' is not meaningful and just start
726 * looking from the end. */
727 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
728 const WCHAR htmlW[] = {'.','h','t','m','l',0};
729 const int len_htmlW = 5;
730 const WCHAR htmW[] = {'.','h','t','m',0};
731 const int len_htmW = 4;
733 if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
735 if (strncmpiW(work, htmW, len_htmW) == 0)
736 manual_search = TRUE;
740 if (!manual_search &&
741 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
743 if (strncmpiW(work, htmlW, len_htmlW) == 0)
744 manual_search = TRUE;
750 /* search backwards starting from the current position */
751 while (*work != '/' && work > base.pszSuffix + sizeloc)
753 if (work > base.pszSuffix + sizeloc)
754 base.cchSuffix = work - base.pszSuffix + 1;
756 /* search backwards starting from the end of the string */
757 work = strrchrW((base.pszSuffix+sizeloc), '/');
759 len = (DWORD)(work - base.pszSuffix + 1);
760 base.cchSuffix = len;
766 * .pszSuffix points to location (starting with '//')
767 * .cchSuffix length of location (above) and rest less the last
769 * sizeloc length of location (above) up to but not including
773 res2 = ParseURLW(mrelative, &relative);
775 /* no scheme in pszRelative */
776 TRACE("no scheme detected in Relative\n");
777 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
778 relative.cchSuffix = strlenW(mrelative);
779 if (*pszRelative == ':') {
780 /* case that is either left alone or uses pszBase */
781 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
788 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
789 /* case that becomes "file:///" */
790 strcpyW(preliminary, myfilestr);
794 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
795 /* pszRelative has location and rest */
799 if (*mrelative == '/') {
800 /* case where pszRelative is root to location */
804 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
808 /* handle cases where pszRelative has scheme */
809 if ((base.cchProtocol == relative.cchProtocol) &&
810 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
812 /* since the schemes are the same */
813 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
814 /* case where pszRelative replaces location and following */
818 if (*relative.pszSuffix == '/') {
819 /* case where pszRelative is root to location */
823 /* replace either just location if base's location starts with a
824 * slash or otherwise everything */
825 process_case = (*base.pszSuffix == '/') ? 5 : 1;
828 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
829 /* case where pszRelative replaces scheme, location,
830 * and following and handles PLUGGABLE
837 } while(FALSE); /* a little trick to allow easy exit from nested if's */
840 switch (process_case) {
843 * Return pszRelative appended to what ever is in pszCombined,
844 * (which may the string "file:///"
846 strcatW(preliminary, mrelative);
849 case 2: /* case where pszRelative replaces scheme, and location */
850 strcpyW(preliminary, mrelative);
854 * Return the pszBase scheme with pszRelative. Basically
855 * keeps the scheme and replaces the domain and following.
857 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
858 work = preliminary + base.cchProtocol + 1;
859 strcpyW(work, relative.pszSuffix);
863 * Return the pszBase scheme and location but everything
864 * after the location is pszRelative. (Replace document
867 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
868 work = preliminary + base.cchProtocol + 1 + sizeloc;
869 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
871 strcpyW(work, relative.pszSuffix);
875 * Return the pszBase without its document (if any) and
876 * append pszRelative after its scheme.
878 memcpy(preliminary, base.pszProtocol,
879 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
880 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
883 strcpyW(work, relative.pszSuffix);
887 FIXME("How did we get here????? process_case=%d\n", process_case);
892 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
893 if(*pcchCombined == 0)
895 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
896 if(SUCCEEDED(ret) && pszCombined) {
897 lstrcpyW(pszCombined, mrelative);
899 TRACE("return-%d len=%d, %s\n",
900 process_case, *pcchCombined, debugstr_w(pszCombined));
902 HeapFree(GetProcessHeap(), 0, preliminary);
906 /*************************************************************************
907 * UrlEscapeA [SHLWAPI.@]
910 HRESULT WINAPI UrlEscapeA(
916 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
917 WCHAR *escapedW = bufW;
920 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
922 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
925 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
927 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
928 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
929 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
932 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
933 if(*pcchEscaped > lenA) {
934 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
935 pszEscaped[lenA] = 0;
938 *pcchEscaped = lenA + 1;
942 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
943 RtlFreeUnicodeString(&urlW);
947 #define WINE_URL_BASH_AS_SLASH 0x01
948 #define WINE_URL_COLLAPSE_SLASHES 0x02
949 #define WINE_URL_ESCAPE_SLASH 0x04
950 #define WINE_URL_ESCAPE_HASH 0x08
951 #define WINE_URL_ESCAPE_QUESTION 0x10
952 #define WINE_URL_STOP_ON_HASH 0x20
953 #define WINE_URL_STOP_ON_QUESTION 0x40
955 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
961 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
968 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
971 if (ch <= 31 || ch >= 127)
992 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
996 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
1000 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
1010 /*************************************************************************
1011 * UrlEscapeW [SHLWAPI.@]
1013 * Converts unsafe characters in a Url into escape sequences.
1016 * pszUrl [I] Url to modify
1017 * pszEscaped [O] Destination for modified Url
1018 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1019 * dwFlags [I] URL_ flags from "shlwapi.h"
1022 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1023 * contains its length.
1024 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1025 * pcchEscaped is set to the required length.
1027 * Converts unsafe characters into their escape sequences.
1030 * - By default this function stops converting at the first '?' or
1032 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1033 * converted, but the conversion continues past a '?' or '#'.
1034 * - Note that this function did not work well (or at all) in shlwapi version 4.
1037 * Only the following flags are implemented:
1038 *| URL_ESCAPE_SPACES_ONLY
1039 *| URL_DONT_ESCAPE_EXTRA_INFO
1040 *| URL_ESCAPE_SEGMENT_ONLY
1041 *| URL_ESCAPE_PERCENT
1043 HRESULT WINAPI UrlEscapeW(
1046 LPDWORD pcchEscaped,
1050 DWORD needed = 0, ret;
1051 BOOL stop_escaping = FALSE;
1052 WCHAR next[5], *dst = pszEscaped;
1054 PARSEDURLW parsed_url;
1057 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1059 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1060 pszEscaped, pcchEscaped, dwFlags);
1062 if(!pszUrl || !pcchEscaped)
1063 return E_INVALIDARG;
1065 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1066 URL_ESCAPE_SEGMENT_ONLY |
1067 URL_DONT_ESCAPE_EXTRA_INFO |
1068 URL_ESCAPE_PERCENT))
1069 FIXME("Unimplemented flags: %08x\n", dwFlags);
1071 if(pszUrl == pszEscaped) {
1072 dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1074 return E_OUTOFMEMORY;
1078 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1079 /* if SPACES_ONLY specified, reset the other controls */
1080 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1081 URL_ESCAPE_PERCENT |
1082 URL_ESCAPE_SEGMENT_ONLY);
1085 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1086 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1090 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1091 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1093 parsed_url.cbSize = sizeof(parsed_url);
1094 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1095 parsed_url.nScheme = URL_SCHEME_INVALID;
1097 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1099 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1100 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1102 switch(parsed_url.nScheme) {
1103 case URL_SCHEME_FILE:
1104 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1105 int_flags &= ~WINE_URL_STOP_ON_HASH;
1108 case URL_SCHEME_HTTP:
1109 case URL_SCHEME_HTTPS:
1110 int_flags |= WINE_URL_BASH_AS_SLASH;
1111 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1112 int_flags |= WINE_URL_ESCAPE_SLASH;
1115 case URL_SCHEME_MAILTO:
1116 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1117 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1120 case URL_SCHEME_INVALID:
1123 case URL_SCHEME_FTP:
1125 if(parsed_url.pszSuffix[0] != '/')
1126 int_flags |= WINE_URL_ESCAPE_SLASH;
1131 for(src = pszUrl; *src; ) {
1135 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1136 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1137 while(cur == '/' || cur == '\\') {
1141 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1142 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1143 src += localhost_len + 1;
1150 next[0] = next[1] = next[2] = '/';
1157 next[0] = next[1] = '/';
1164 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1165 stop_escaping = TRUE;
1167 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1168 stop_escaping = TRUE;
1170 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1172 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1174 next[1] = hexDigits[(cur >> 4) & 0xf];
1175 next[2] = hexDigits[cur & 0xf];
1184 if(needed + len <= *pcchEscaped) {
1185 memcpy(dst, next, len*sizeof(WCHAR));
1191 if(needed < *pcchEscaped) {
1193 if(pszUrl == pszEscaped)
1194 memcpy(pszEscaped, dst-needed, (needed+1)*sizeof(WCHAR));
1198 needed++; /* add one for the '\0' */
1201 *pcchEscaped = needed;
1203 if(pszUrl == pszEscaped)
1204 HeapFree(GetProcessHeap(), 0, dst);
1209 /*************************************************************************
1210 * UrlUnescapeA [SHLWAPI.@]
1212 * Converts Url escape sequences back to ordinary characters.
1215 * pszUrl [I/O] Url to convert
1216 * pszUnescaped [O] Destination for converted Url
1217 * pcchUnescaped [I/O] Size of output string
1218 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1221 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1222 * dwFlags includes URL_ESCAPE_INPLACE.
1223 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1224 * this case pcchUnescaped is set to the size required.
1226 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1227 * the first occurrence of either a '?' or '#' character.
1229 HRESULT WINAPI UrlUnescapeA(
1232 LPDWORD pcchUnescaped,
1239 BOOL stop_unescaping = FALSE;
1241 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1242 pcchUnescaped, dwFlags);
1244 if (!pszUrl) return E_INVALIDARG;
1246 if(dwFlags & URL_UNESCAPE_INPLACE)
1250 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1254 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1255 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1256 (*src == '#' || *src == '?')) {
1257 stop_unescaping = TRUE;
1259 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1260 && stop_unescaping == FALSE) {
1263 memcpy(buf, src + 1, 2);
1265 ih = strtol(buf, NULL, 16);
1267 src += 2; /* Advance to end of escape */
1271 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1275 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1279 needed++; /* add one for the '\0' */
1282 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1283 *pcchUnescaped = needed;
1286 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1287 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1293 /*************************************************************************
1294 * UrlUnescapeW [SHLWAPI.@]
1298 HRESULT WINAPI UrlUnescapeW(
1300 LPWSTR pszUnescaped,
1301 LPDWORD pcchUnescaped,
1308 BOOL stop_unescaping = FALSE;
1310 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1311 pcchUnescaped, dwFlags);
1313 if(!pszUrl) return E_INVALIDARG;
1315 if(dwFlags & URL_UNESCAPE_INPLACE)
1319 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1323 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1324 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1325 (*src == '#' || *src == '?')) {
1326 stop_unescaping = TRUE;
1328 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1329 && stop_unescaping == FALSE) {
1331 WCHAR buf[5] = {'0','x',0};
1332 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1334 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1336 src += 2; /* Advance to end of escape */
1340 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1344 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1348 needed++; /* add one for the '\0' */
1351 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1352 *pcchUnescaped = needed;
1355 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1356 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1362 /*************************************************************************
1363 * UrlGetLocationA [SHLWAPI.@]
1365 * Get the location from a Url.
1368 * pszUrl [I] Url to get the location from
1371 * A pointer to the start of the location in pszUrl, or NULL if there is
1375 * - MSDN erroneously states that "The location is the segment of the Url
1376 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1377 * stop at '?' and always return a NULL in this case.
1378 * - MSDN also erroneously states that "If a file URL has a query string,
1379 * the returned string is the query string". In all tested cases, if the
1380 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1383 *| NULL file://aa/b/cd#hohoh
1384 *| #hohoh http://aa/b/cd#hohoh
1385 *| NULL fi://aa/b/cd#hohoh
1386 *| #hohoh ff://aa/b/cd#hohoh
1388 LPCSTR WINAPI UrlGetLocationA(
1394 base.cbSize = sizeof(base);
1395 res1 = ParseURLA(pszUrl, &base);
1396 if (res1) return NULL; /* invalid scheme */
1398 /* if scheme is file: then never return pointer */
1399 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1401 /* Look for '#' and return its addr */
1402 return strchr(base.pszSuffix, '#');
1405 /*************************************************************************
1406 * UrlGetLocationW [SHLWAPI.@]
1408 * See UrlGetLocationA.
1410 LPCWSTR WINAPI UrlGetLocationW(
1416 base.cbSize = sizeof(base);
1417 res1 = ParseURLW(pszUrl, &base);
1418 if (res1) return NULL; /* invalid scheme */
1420 /* if scheme is file: then never return pointer */
1421 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1423 /* Look for '#' and return its addr */
1424 return strchrW(base.pszSuffix, '#');
1427 /*************************************************************************
1428 * UrlCompareA [SHLWAPI.@]
1433 * pszUrl1 [I] First Url to compare
1434 * pszUrl2 [I] Url to compare to pszUrl1
1435 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1438 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1439 * than, equal to, or less than pszUrl1 respectively.
1441 INT WINAPI UrlCompareA(
1446 INT ret, len, len1, len2;
1449 return strcmp(pszUrl1, pszUrl2);
1450 len1 = strlen(pszUrl1);
1451 if (pszUrl1[len1-1] == '/') len1--;
1452 len2 = strlen(pszUrl2);
1453 if (pszUrl2[len2-1] == '/') len2--;
1455 return strncmp(pszUrl1, pszUrl2, len1);
1456 len = min(len1, len2);
1457 ret = strncmp(pszUrl1, pszUrl2, len);
1458 if (ret) return ret;
1459 if (len1 > len2) return 1;
1463 /*************************************************************************
1464 * UrlCompareW [SHLWAPI.@]
1468 INT WINAPI UrlCompareW(
1474 size_t len, len1, len2;
1477 return strcmpW(pszUrl1, pszUrl2);
1478 len1 = strlenW(pszUrl1);
1479 if (pszUrl1[len1-1] == '/') len1--;
1480 len2 = strlenW(pszUrl2);
1481 if (pszUrl2[len2-1] == '/') len2--;
1483 return strncmpW(pszUrl1, pszUrl2, len1);
1484 len = min(len1, len2);
1485 ret = strncmpW(pszUrl1, pszUrl2, len);
1486 if (ret) return ret;
1487 if (len1 > len2) return 1;
1491 /*************************************************************************
1492 * HashData [SHLWAPI.@]
1494 * Hash an input block into a variable sized digest.
1497 * lpSrc [I] Input block
1498 * nSrcLen [I] Length of lpSrc
1499 * lpDest [I] Output for hash digest
1500 * nDestLen [I] Length of lpDest
1503 * Success: TRUE. lpDest is filled with the computed hash value.
1504 * Failure: FALSE, if any argument is invalid.
1506 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1507 unsigned char *lpDest, DWORD nDestLen)
1509 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1511 if (!lpSrc || !lpDest)
1512 return E_INVALIDARG;
1514 while (destCount >= 0)
1516 lpDest[destCount] = (destCount & 0xff);
1520 while (srcCount >= 0)
1522 destCount = nDestLen - 1;
1523 while (destCount >= 0)
1525 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1533 /*************************************************************************
1534 * UrlHashA [SHLWAPI.@]
1536 * Produce a Hash from a Url.
1539 * pszUrl [I] Url to hash
1540 * lpDest [O] Destinationh for hash
1541 * nDestLen [I] Length of lpDest
1544 * Success: S_OK. lpDest is filled with the computed hash value.
1545 * Failure: E_INVALIDARG, if any argument is invalid.
1547 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1549 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1550 return E_INVALIDARG;
1552 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1556 /*************************************************************************
1557 * UrlHashW [SHLWAPI.@]
1561 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1563 char szUrl[MAX_PATH];
1565 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1567 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1568 return E_INVALIDARG;
1570 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1571 * return the same digests for the same URL.
1573 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1574 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1578 /*************************************************************************
1579 * UrlApplySchemeA [SHLWAPI.@]
1581 * Apply a scheme to a Url.
1584 * pszIn [I] Url to apply scheme to
1585 * pszOut [O] Destination for modified Url
1586 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1587 * dwFlags [I] URL_ flags from "shlwapi.h"
1590 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1591 * Failure: An HRESULT error code describing the error.
1593 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1599 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1600 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1602 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1604 in = HeapAlloc(GetProcessHeap(), 0,
1605 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1606 out = in + INTERNET_MAX_URL_LENGTH;
1608 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1609 len = INTERNET_MAX_URL_LENGTH;
1611 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1613 HeapFree(GetProcessHeap(), 0, in);
1617 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1618 if (len > *pcchOut) {
1623 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1628 HeapFree(GetProcessHeap(), 0, in);
1632 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1637 DWORD value_len, data_len, dwType, i;
1638 WCHAR reg_path[MAX_PATH];
1639 WCHAR value[MAX_PATH], data[MAX_PATH];
1642 MultiByteToWideChar(0, 0,
1643 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1644 -1, reg_path, MAX_PATH);
1645 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1647 while(value_len = data_len = MAX_PATH,
1648 RegEnumValueW(newkey, index, value, &value_len,
1649 0, &dwType, (LPVOID)data, &data_len) == 0) {
1650 TRACE("guess %d %s is %s\n",
1651 index, debugstr_w(value), debugstr_w(data));
1654 for(i=0; i<value_len; i++) {
1657 /* remember that TRUE is not-equal */
1658 j = ChrCmpIW(Wxx, Wyy);
1661 if ((i == value_len) && !j) {
1662 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1663 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1664 RegCloseKey(newkey);
1667 strcpyW(pszOut, data);
1668 strcatW(pszOut, pszIn);
1669 *pcchOut = strlenW(pszOut);
1670 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1671 RegCloseKey(newkey);
1676 RegCloseKey(newkey);
1680 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1683 DWORD data_len, dwType;
1684 WCHAR data[MAX_PATH];
1686 static const WCHAR prefix_keyW[] =
1687 {'S','o','f','t','w','a','r','e',
1688 '\\','M','i','c','r','o','s','o','f','t',
1689 '\\','W','i','n','d','o','w','s',
1690 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1692 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1694 /* get and prepend default */
1695 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1696 data_len = sizeof(data);
1697 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1698 RegCloseKey(newkey);
1699 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1700 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1703 strcpyW(pszOut, data);
1704 strcatW(pszOut, pszIn);
1705 *pcchOut = strlenW(pszOut);
1706 TRACE("used default %s\n", debugstr_w(pszOut));
1710 /*************************************************************************
1711 * UrlApplySchemeW [SHLWAPI.@]
1713 * See UrlApplySchemeA.
1715 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1717 PARSEDURLW in_scheme;
1721 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1722 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1724 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1726 if (dwFlags & URL_APPLY_GUESSFILE) {
1727 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1728 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1729 strcpyW(pszOut, pszIn);
1730 *pcchOut = strlenW(pszOut);
1734 in_scheme.cbSize = sizeof(in_scheme);
1735 /* See if the base has a scheme */
1736 res1 = ParseURLW(pszIn, &in_scheme);
1738 /* no scheme in input, need to see if we need to guess */
1739 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1740 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1745 /* we have a scheme, see if valid (known scheme) */
1746 if (in_scheme.nScheme) {
1747 /* have valid scheme, so just copy and exit */
1748 if (strlenW(pszIn) + 1 > *pcchOut) {
1749 *pcchOut = strlenW(pszIn) + 1;
1752 strcpyW(pszOut, pszIn);
1753 *pcchOut = strlenW(pszOut);
1754 TRACE("valid scheme, returning copy\n");
1759 /* If we are here, then either invalid scheme,
1760 * or no scheme and can't/failed guess.
1762 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1764 (dwFlags & URL_APPLY_DEFAULT)) {
1765 /* find and apply default scheme */
1766 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1772 /*************************************************************************
1773 * UrlIsA [SHLWAPI.@]
1775 * Determine if a Url is of a certain class.
1778 * pszUrl [I] Url to check
1779 * Urlis [I] URLIS_ constant from "shlwapi.h"
1782 * TRUE if pszUrl belongs to the class type in Urlis.
1785 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1791 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1799 base.cbSize = sizeof(base);
1800 res1 = ParseURLA(pszUrl, &base);
1801 if (res1) return FALSE; /* invalid scheme */
1802 switch (base.nScheme)
1804 case URL_SCHEME_MAILTO:
1805 case URL_SCHEME_SHELL:
1806 case URL_SCHEME_JAVASCRIPT:
1807 case URL_SCHEME_VBSCRIPT:
1808 case URL_SCHEME_ABOUT:
1814 return !StrCmpNA("file:", pszUrl, 5);
1816 case URLIS_DIRECTORY:
1817 last = pszUrl + strlen(pszUrl) - 1;
1818 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1821 return PathIsURLA(pszUrl);
1823 case URLIS_NOHISTORY:
1824 case URLIS_APPLIABLE:
1825 case URLIS_HASQUERY:
1827 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1832 /*************************************************************************
1833 * UrlIsW [SHLWAPI.@]
1837 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1839 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1844 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1852 base.cbSize = sizeof(base);
1853 res1 = ParseURLW(pszUrl, &base);
1854 if (res1) return FALSE; /* invalid scheme */
1855 switch (base.nScheme)
1857 case URL_SCHEME_MAILTO:
1858 case URL_SCHEME_SHELL:
1859 case URL_SCHEME_JAVASCRIPT:
1860 case URL_SCHEME_VBSCRIPT:
1861 case URL_SCHEME_ABOUT:
1867 return !strncmpW(stemp, pszUrl, 5);
1869 case URLIS_DIRECTORY:
1870 last = pszUrl + strlenW(pszUrl) - 1;
1871 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1874 return PathIsURLW(pszUrl);
1876 case URLIS_NOHISTORY:
1877 case URLIS_APPLIABLE:
1878 case URLIS_HASQUERY:
1880 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1885 /*************************************************************************
1886 * UrlIsNoHistoryA [SHLWAPI.@]
1888 * Determine if a Url should not be stored in the users history list.
1891 * pszUrl [I] Url to check
1894 * TRUE, if pszUrl should be excluded from the history list,
1897 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1899 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1902 /*************************************************************************
1903 * UrlIsNoHistoryW [SHLWAPI.@]
1905 * See UrlIsNoHistoryA.
1907 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1909 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1912 /*************************************************************************
1913 * UrlIsOpaqueA [SHLWAPI.@]
1915 * Determine if a Url is opaque.
1918 * pszUrl [I] Url to check
1921 * TRUE if pszUrl is opaque,
1925 * An opaque Url is one that does not start with "<protocol>://".
1927 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1929 return UrlIsA(pszUrl, URLIS_OPAQUE);
1932 /*************************************************************************
1933 * UrlIsOpaqueW [SHLWAPI.@]
1937 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1939 return UrlIsW(pszUrl, URLIS_OPAQUE);
1942 /*************************************************************************
1943 * Scans for characters of type "type" and when not matching found,
1944 * returns pointer to it and length in size.
1946 * Characters tested based on RFC 1738
1948 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1950 static DWORD alwayszero = 0;
1959 if ( (islowerW(*start) && isalphaW(*start)) ||
1974 if ( isalphaW(*start) ||
1976 /* user/password only characters */
1981 /* *extra* characters */
1988 /* *safe* characters */
1997 } else if (*start == '%') {
1998 if (isxdigitW(*(start+1)) &&
1999 isxdigitW(*(start+2))) {
2011 if (isdigitW(*start)) {
2022 if (isalnumW(*start) ||
2034 FIXME("unknown type %d\n", type);
2035 return (LPWSTR)&alwayszero;
2037 /* TRACE("scanned %d characters next char %p<%c>\n",
2038 *size, start, *start); */
2042 /*************************************************************************
2043 * Attempt to parse URL into pieces.
2045 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2049 memset(pl, 0, sizeof(WINE_PARSE_URL));
2050 pl->pScheme = pszUrl;
2051 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2052 if (!*work || (*work != ':')) goto ErrorExit;
2054 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2055 pl->pUserName = work + 2;
2056 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2057 if (*work == ':' ) {
2058 /* parse password */
2060 pl->pPassword = work;
2061 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2063 /* what we just parsed must be the hostname and port
2064 * so reset pointers and clear then let it parse */
2065 pl->szUserName = pl->szPassword = 0;
2066 work = pl->pUserName - 1;
2067 pl->pUserName = pl->pPassword = 0;
2069 } else if (*work == '@') {
2073 } else if (!*work || (*work == '/') || (*work == '.')) {
2074 /* what was parsed was hostname, so reset pointers and let it parse */
2075 pl->szUserName = pl->szPassword = 0;
2076 work = pl->pUserName - 1;
2077 pl->pUserName = pl->pPassword = 0;
2078 } else goto ErrorExit;
2080 /* now start parsing hostname or hostnumber */
2082 pl->pHostName = work;
2083 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2088 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2091 /* see if query string */
2092 pl->pQuery = strchrW(work, '?');
2093 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2096 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2097 pl->pScheme, pl->szScheme,
2098 pl->pUserName, pl->szUserName,
2099 pl->pPassword, pl->szPassword,
2100 pl->pHostName, pl->szHostName,
2101 pl->pPort, pl->szPort,
2102 pl->pQuery, pl->szQuery);
2105 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2106 return E_INVALIDARG;
2109 /*************************************************************************
2110 * UrlGetPartA [SHLWAPI.@]
2112 * Retrieve part of a Url.
2115 * pszIn [I] Url to parse
2116 * pszOut [O] Destination for part of pszIn requested
2117 * pcchOut [I] Size of pszOut
2118 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2119 * needed size of pszOut INCLUDING '\0'.
2120 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2121 * dwFlags [I] URL_ flags from "shlwapi.h"
2124 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2125 * Failure: An HRESULT error code describing the error.
2127 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2128 DWORD dwPart, DWORD dwFlags)
2131 DWORD ret, len, len2;
2133 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2134 return E_INVALIDARG;
2136 in = HeapAlloc(GetProcessHeap(), 0,
2137 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2138 out = in + INTERNET_MAX_URL_LENGTH;
2140 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2142 len = INTERNET_MAX_URL_LENGTH;
2143 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2146 HeapFree(GetProcessHeap(), 0, in);
2150 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2151 if (len2 > *pcchOut) {
2153 HeapFree(GetProcessHeap(), 0, in);
2156 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2158 HeapFree(GetProcessHeap(), 0, in);
2162 /*************************************************************************
2163 * UrlGetPartW [SHLWAPI.@]
2167 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2168 DWORD dwPart, DWORD dwFlags)
2172 DWORD scheme, size, schsize;
2173 LPCWSTR addr, schaddr;
2175 TRACE("(%s %p %p(%d) %08x %08x)\n",
2176 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2178 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2179 return E_INVALIDARG;
2183 addr = strchrW(pszIn, ':');
2185 scheme = URL_SCHEME_UNKNOWN;
2187 scheme = get_scheme_code(pszIn, addr-pszIn);
2189 ret = URL_ParseUrl(pszIn, &pl);
2192 case URL_PART_SCHEME:
2193 if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
2201 case URL_PART_HOSTNAME:
2203 case URL_SCHEME_FTP:
2204 case URL_SCHEME_HTTP:
2205 case URL_SCHEME_GOPHER:
2206 case URL_SCHEME_TELNET:
2207 case URL_SCHEME_FILE:
2208 case URL_SCHEME_HTTPS:
2215 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2216 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2221 if (!pl.szHostName) {
2225 addr = pl.pHostName;
2226 size = pl.szHostName;
2229 case URL_PART_USERNAME:
2230 if (!pl.szUserName) {
2234 addr = pl.pUserName;
2235 size = pl.szUserName;
2238 case URL_PART_PASSWORD:
2239 if (!pl.szPassword) {
2243 addr = pl.pPassword;
2244 size = pl.szPassword;
2256 case URL_PART_QUERY:
2267 return E_INVALIDARG;
2270 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2271 if(!pl.pScheme || !pl.szScheme) {
2275 schaddr = pl.pScheme;
2276 schsize = pl.szScheme;
2277 if (*pcchOut < schsize + size + 2) {
2278 *pcchOut = schsize + size + 2;
2281 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2282 pszOut[schsize] = ':';
2283 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2284 pszOut[schsize+1+size] = 0;
2285 *pcchOut = schsize + 1 + size;
2288 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2289 memcpy(pszOut, addr, size*sizeof(WCHAR));
2293 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2298 /*************************************************************************
2299 * PathIsURLA [SHLWAPI.@]
2301 * Check if the given path is a Url.
2304 * lpszPath [I] Path to check.
2307 * TRUE if lpszPath is a Url.
2308 * FALSE if lpszPath is NULL or not a Url.
2310 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2315 TRACE("%s\n", debugstr_a(lpstrPath));
2317 if (!lpstrPath || !*lpstrPath) return FALSE;
2320 base.cbSize = sizeof(base);
2321 hres = ParseURLA(lpstrPath, &base);
2322 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2325 /*************************************************************************
2326 * PathIsURLW [SHLWAPI.@]
2330 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2335 TRACE("%s\n", debugstr_w(lpstrPath));
2337 if (!lpstrPath || !*lpstrPath) return FALSE;
2340 base.cbSize = sizeof(base);
2341 hres = ParseURLW(lpstrPath, &base);
2342 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2345 /*************************************************************************
2346 * UrlCreateFromPathA [SHLWAPI.@]
2348 * See UrlCreateFromPathW
2350 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2352 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2354 UNICODE_STRING pathW;
2356 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2358 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2359 return E_INVALIDARG;
2360 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2361 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2362 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2364 if(ret == S_OK || ret == S_FALSE) {
2365 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2366 if(*pcchUrl > lenA) {
2367 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2371 *pcchUrl = lenA + 1;
2375 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2376 RtlFreeUnicodeString(&pathW);
2380 /*************************************************************************
2381 * UrlCreateFromPathW [SHLWAPI.@]
2383 * Create a Url from a file path.
2386 * pszPath [I] Path to convert
2387 * pszUrl [O] Destination for the converted Url
2388 * pcchUrl [I/O] Length of pszUrl
2389 * dwReserved [I] Reserved, must be 0
2392 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2393 * Failure: An HRESULT error code.
2395 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2400 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2401 WCHAR three_slashesW[] = {'/','/','/',0};
2402 PARSEDURLW parsed_url;
2404 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2406 /* Validate arguments */
2407 if (dwReserved != 0)
2408 return E_INVALIDARG;
2409 if (!pszUrl || !pcchUrl)
2410 return E_INVALIDARG;
2413 parsed_url.cbSize = sizeof(parsed_url);
2414 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2415 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2416 needed = strlenW(pszPath);
2417 if (needed >= *pcchUrl) {
2418 *pcchUrl = needed + 1;
2422 strcpyW(pszUrl, pszPath);
2428 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2429 strcpyW(pszNewUrl, file_colonW);
2430 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2431 strcatW(pszNewUrl, three_slashesW);
2432 strcatW(pszNewUrl, pszPath);
2433 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2435 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2439 /*************************************************************************
2440 * SHAutoComplete [SHLWAPI.@]
2442 * Enable auto-completion for an edit control.
2445 * hwndEdit [I] Handle of control to enable auto-completion for
2446 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2449 * Success: S_OK. Auto-completion is enabled for the control.
2450 * Failure: An HRESULT error code indicating the error.
2452 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2458 /*************************************************************************
2459 * MLBuildResURLA [SHLWAPI.405]
2461 * Create a Url pointing to a resource in a module.
2464 * lpszLibName [I] Name of the module containing the resource
2465 * hMod [I] Callers module handle
2466 * dwFlags [I] Undocumented flags for loading the module
2467 * lpszRes [I] Resource name
2468 * lpszDest [O] Destination for resulting Url
2469 * dwDestLen [I] Length of lpszDest
2472 * Success: S_OK. lpszDest contains the resource Url.
2473 * Failure: E_INVALIDARG, if any argument is invalid, or
2474 * E_FAIL if dwDestLen is too small.
2476 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2477 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2479 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2483 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2486 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2488 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2489 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2491 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2492 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2493 if (SUCCEEDED(hRet) && lpszDest)
2494 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2499 /*************************************************************************
2500 * MLBuildResURLA [SHLWAPI.406]
2502 * See MLBuildResURLA.
2504 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2505 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2507 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2508 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2509 HRESULT hRet = E_FAIL;
2511 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2512 debugstr_w(lpszRes), lpszDest, dwDestLen);
2514 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2515 !lpszDest || (dwFlags && dwFlags != 2))
2516 return E_INVALIDARG;
2518 if (dwDestLen >= szResLen + 1)
2520 dwDestLen -= (szResLen + 1);
2521 memcpy(lpszDest, szRes, sizeof(szRes));
2523 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2527 WCHAR szBuff[MAX_PATH];
2530 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2531 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2533 DWORD dwPathLen = strlenW(szBuff) + 1;
2535 if (dwDestLen >= dwPathLen)
2539 dwDestLen -= dwPathLen;
2540 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2542 dwResLen = strlenW(lpszRes) + 1;
2543 if (dwDestLen >= dwResLen + 1)
2545 lpszDest[szResLen + dwPathLen-1] = '/';
2546 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2551 MLFreeLibrary(hMod);
2557 /***********************************************************************
2558 * UrlFixupW [SHLWAPI.462]
2560 * Checks the scheme part of a URL and attempts to correct misspellings.
2563 * lpszUrl [I] Pointer to the URL to be corrected
2564 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2565 * dwMaxChars [I] Maximum size of corrected URL
2568 * success: S_OK if URL corrected or already correct
2569 * failure: S_FALSE if unable to correct / COM error code if other error
2572 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2576 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2581 srcLen = lstrlenW(url) + 1;
2583 /* For now just copy the URL directly */
2584 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);