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;
281 static const WCHAR wszFile[] = {'f','i','l','e',':'};
282 static const WCHAR wszRes[] = {'r','e','s',':'};
283 static const WCHAR wszHttp[] = {'h','t','t','p',':'};
284 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
285 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
287 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
288 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
290 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
294 *pszCanonicalized = 0;
298 /* Remove '\t' characters from URL */
299 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
300 url = HeapAlloc(GetProcessHeap(), 0, nByteLen);
302 return E_OUTOFMEMORY;
312 /* Allocate memory for simplified URL (before escaping) */
313 nByteLen = (wk2-url)*sizeof(WCHAR);
314 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
315 nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR));
317 HeapFree(GetProcessHeap(), 0, url);
318 return E_OUTOFMEMORY;
321 if ((nByteLen >= sizeof(wszHttp) &&
322 !memcmp(wszHttp, url, sizeof(wszHttp))) ||
323 (nByteLen >= sizeof(wszFile) &&
324 !memcmp(wszFile, url, sizeof(wszFile))))
327 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
328 && !memcmp(wszFile, url, sizeof(wszFile)))
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)
360 dwFlags |= URL_ESCAPE_UNSAFE;
367 if (!isalnumW(*wk1)) {state = 3; break;}
369 if (!isalnumW(*wk1)) {state = 3; break;}
375 if (*wk1++ == ':') state = 2;
379 if (*wk1 != '/') {state = 6; break;}
381 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
382 && !strncmpW(wszFile, url, sizeof(wszFile)/sizeof(WCHAR))
383 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
384 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
385 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
388 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
393 nWkLen = strlenW(wk1);
394 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
401 if(*mp == '/' || *mp == '\\')
408 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
410 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
421 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
422 while(*wk1 == '/' || *wk1 == '\\') {
432 if(dwFlags & URL_DONT_SIMPLIFY) {
437 /* Now at root location, cannot back up any more. */
438 /* "root" will point at the '/' */
442 mp = strchrW(wk1, '/');
443 mp2 = strchrW(wk1, '\\');
444 if(mp2 && (!mp || mp2 < mp))
447 nWkLen = strlenW(wk1);
448 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
455 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
465 while (*wk1 == '.') {
466 TRACE("found '/.'\n");
467 if (wk1[1] == '/' || wk1[1] == '\\') {
468 /* case of /./ -> skip the ./ */
471 else if (wk1[1] == '.' && (wk1[2] == '/'
472 || wk1[2] == '\\' || wk1[2] == '?'
473 || wk1[2] == '#' || !wk1[2])) {
474 /* case /../ -> need to backup wk2 */
475 TRACE("found '/../'\n");
476 *(wk2-1) = '\0'; /* set end of string */
477 mp = strrchrW(root, '/');
478 mp2 = strrchrW(root, '\\');
479 if(mp2 && (!mp || mp2 < mp))
481 if (mp && (mp >= root)) {
482 /* found valid backup point */
484 if(wk1[2] != '/' && wk1[2] != '\\')
490 /* did not find point, restore '/' */
502 FIXME("how did we get here - state=%d\n", state);
503 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
504 HeapFree(GetProcessHeap(), 0, url);
508 TRACE("Simplified, orig <%s>, simple <%s>\n",
509 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
511 nLen = lstrlenW(lpszUrlCpy);
512 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
513 lpszUrlCpy[--nLen]=0;
515 if((dwFlags & URL_UNESCAPE) ||
516 ((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
517 && !memcmp(wszFile, url, sizeof(wszFile))))
518 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
520 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
521 URL_ESCAPE_SPACES_ONLY |
523 URL_DONT_ESCAPE_EXTRA_INFO |
524 URL_ESCAPE_SEGMENT_ONLY ))) {
525 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
526 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
528 } else { /* No escaping needed, just copy the string */
529 nLen = lstrlenW(lpszUrlCpy);
530 if(nLen < *pcchCanonicalized)
531 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
536 *pcchCanonicalized = nLen;
539 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
540 HeapFree(GetProcessHeap(), 0, url);
543 TRACE("result %s\n", debugstr_w(pszCanonicalized));
548 /*************************************************************************
549 * UrlCombineA [SHLWAPI.@]
554 * pszBase [I] Base Url
555 * pszRelative [I] Url to combine with pszBase
556 * pszCombined [O] Destination for combined Url
557 * pcchCombined [O] Destination for length of pszCombined
558 * dwFlags [I] URL_ flags from "shlwapi.h"
561 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
562 * contains its length.
563 * Failure: An HRESULT error code indicating the error.
565 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
566 LPSTR pszCombined, LPDWORD pcchCombined,
569 LPWSTR base, relative, combined;
570 DWORD ret, len, len2;
572 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
573 debugstr_a(pszBase),debugstr_a(pszRelative),
574 pcchCombined?*pcchCombined:0,dwFlags);
576 if(!pszBase || !pszRelative || !pcchCombined)
579 base = HeapAlloc(GetProcessHeap(), 0,
580 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
581 relative = base + INTERNET_MAX_URL_LENGTH;
582 combined = relative + INTERNET_MAX_URL_LENGTH;
584 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
585 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
588 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
591 HeapFree(GetProcessHeap(), 0, base);
595 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
596 if (len2 > *pcchCombined) {
597 *pcchCombined = len2;
598 HeapFree(GetProcessHeap(), 0, base);
601 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
603 *pcchCombined = len2;
604 HeapFree(GetProcessHeap(), 0, base);
608 /*************************************************************************
609 * UrlCombineW [SHLWAPI.@]
613 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
614 LPWSTR pszCombined, LPDWORD pcchCombined,
617 PARSEDURLW base, relative;
618 DWORD myflags, sizeloc = 0;
619 DWORD len, res1, res2, process_case = 0;
620 LPWSTR work, preliminary, mbase, mrelative;
621 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
624 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
625 debugstr_w(pszBase),debugstr_w(pszRelative),
626 pcchCombined?*pcchCombined:0,dwFlags);
628 if(!pszBase || !pszRelative || !pcchCombined)
631 base.cbSize = sizeof(base);
632 relative.cbSize = sizeof(relative);
634 /* Get space for duplicates of the input and the output */
635 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
637 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
638 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
641 /* Canonicalize the base input prior to looking for the scheme */
642 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
643 len = INTERNET_MAX_URL_LENGTH;
644 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
646 /* Canonicalize the relative input prior to looking for the scheme */
647 len = INTERNET_MAX_URL_LENGTH;
648 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
650 /* See if the base has a scheme */
651 res1 = ParseURLW(mbase, &base);
653 /* if pszBase has no scheme, then return pszRelative */
654 TRACE("no scheme detected in Base\n");
658 BOOL manual_search = FALSE;
660 /* mk is a special case */
661 if(base.nScheme == URL_SCHEME_MK) {
662 static const WCHAR wsz[] = {':',':',0};
664 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
669 delta = ptr-base.pszSuffix;
670 base.cchProtocol += delta;
671 base.pszSuffix += delta;
672 base.cchSuffix -= delta;
675 /* get size of location field (if it exists) */
676 work = (LPWSTR)base.pszSuffix;
678 if (*work++ == '/') {
679 if (*work++ == '/') {
680 /* At this point have start of location and
681 * it ends at next '/' or end of string.
683 while(*work && (*work != '/')) work++;
684 sizeloc = (DWORD)(work - base.pszSuffix);
689 /* If there is a '#' and the characters immediately preceding it are
690 * ".htm[l]", then begin looking for the last leaf starting from
691 * the '#'. Otherwise the '#' is not meaningful and just start
692 * looking from the end. */
693 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
694 const WCHAR htmlW[] = {'.','h','t','m','l',0};
695 const int len_htmlW = 5;
696 const WCHAR htmW[] = {'.','h','t','m',0};
697 const int len_htmW = 4;
699 if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
701 if (strncmpiW(work, htmW, len_htmW) == 0)
702 manual_search = TRUE;
706 if (!manual_search &&
707 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
709 if (strncmpiW(work, htmlW, len_htmlW) == 0)
710 manual_search = TRUE;
716 /* search backwards starting from the current position */
717 while (*work != '/' && work > base.pszSuffix + sizeloc)
719 if (work > base.pszSuffix + sizeloc)
720 base.cchSuffix = work - base.pszSuffix + 1;
722 /* search backwards starting from the end of the string */
723 work = strrchrW((base.pszSuffix+sizeloc), '/');
725 len = (DWORD)(work - base.pszSuffix + 1);
726 base.cchSuffix = len;
732 * .pszSuffix points to location (starting with '//')
733 * .cchSuffix length of location (above) and rest less the last
735 * sizeloc length of location (above) up to but not including
739 res2 = ParseURLW(mrelative, &relative);
741 /* no scheme in pszRelative */
742 TRACE("no scheme detected in Relative\n");
743 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
744 relative.cchSuffix = strlenW(mrelative);
745 if (*pszRelative == ':') {
746 /* case that is either left alone or uses pszBase */
747 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
754 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
755 /* case that becomes "file:///" */
756 strcpyW(preliminary, myfilestr);
760 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
761 /* pszRelative has location and rest */
765 if (*mrelative == '/') {
766 /* case where pszRelative is root to location */
770 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
774 /* handle cases where pszRelative has scheme */
775 if ((base.cchProtocol == relative.cchProtocol) &&
776 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
778 /* since the schemes are the same */
779 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
780 /* case where pszRelative replaces location and following */
784 if (*relative.pszSuffix == '/') {
785 /* case where pszRelative is root to location */
789 /* replace either just location if base's location starts with a
790 * slash or otherwise everything */
791 process_case = (*base.pszSuffix == '/') ? 5 : 1;
794 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
795 /* case where pszRelative replaces scheme, location,
796 * and following and handles PLUGGABLE
803 } while(FALSE); /* a little trick to allow easy exit from nested if's */
806 switch (process_case) {
809 * Return pszRelative appended to what ever is in pszCombined,
810 * (which may the string "file:///"
812 strcatW(preliminary, mrelative);
815 case 2: /* case where pszRelative replaces scheme, and location */
816 strcpyW(preliminary, mrelative);
820 * Return the pszBase scheme with pszRelative. Basically
821 * keeps the scheme and replaces the domain and following.
823 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
824 work = preliminary + base.cchProtocol + 1;
825 strcpyW(work, relative.pszSuffix);
829 * Return the pszBase scheme and location but everything
830 * after the location is pszRelative. (Replace document
833 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
834 work = preliminary + base.cchProtocol + 1 + sizeloc;
835 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
837 strcpyW(work, relative.pszSuffix);
841 * Return the pszBase without its document (if any) and
842 * append pszRelative after its scheme.
844 memcpy(preliminary, base.pszProtocol,
845 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
846 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
849 strcpyW(work, relative.pszSuffix);
853 FIXME("How did we get here????? process_case=%d\n", process_case);
858 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
859 if(*pcchCombined == 0)
861 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
862 if(SUCCEEDED(ret) && pszCombined) {
863 lstrcpyW(pszCombined, mrelative);
865 TRACE("return-%d len=%d, %s\n",
866 process_case, *pcchCombined, debugstr_w(pszCombined));
868 HeapFree(GetProcessHeap(), 0, preliminary);
872 /*************************************************************************
873 * UrlEscapeA [SHLWAPI.@]
876 HRESULT WINAPI UrlEscapeA(
882 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
883 WCHAR *escapedW = bufW;
886 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
888 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
891 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
893 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
894 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
895 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
898 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
899 if(*pcchEscaped > lenA) {
900 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
901 pszEscaped[lenA] = 0;
904 *pcchEscaped = lenA + 1;
908 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
909 RtlFreeUnicodeString(&urlW);
913 #define WINE_URL_BASH_AS_SLASH 0x01
914 #define WINE_URL_COLLAPSE_SLASHES 0x02
915 #define WINE_URL_ESCAPE_SLASH 0x04
916 #define WINE_URL_ESCAPE_HASH 0x08
917 #define WINE_URL_ESCAPE_QUESTION 0x10
918 #define WINE_URL_STOP_ON_HASH 0x20
919 #define WINE_URL_STOP_ON_QUESTION 0x40
921 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
927 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
934 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
937 if (ch <= 31 || ch >= 127)
958 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
962 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
966 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
976 /*************************************************************************
977 * UrlEscapeW [SHLWAPI.@]
979 * Converts unsafe characters in a Url into escape sequences.
982 * pszUrl [I] Url to modify
983 * pszEscaped [O] Destination for modified Url
984 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
985 * dwFlags [I] URL_ flags from "shlwapi.h"
988 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
989 * contains its length.
990 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
991 * pcchEscaped is set to the required length.
993 * Converts unsafe characters into their escape sequences.
996 * - By default this function stops converting at the first '?' or
998 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
999 * converted, but the conversion continues past a '?' or '#'.
1000 * - Note that this function did not work well (or at all) in shlwapi version 4.
1003 * Only the following flags are implemented:
1004 *| URL_ESCAPE_SPACES_ONLY
1005 *| URL_DONT_ESCAPE_EXTRA_INFO
1006 *| URL_ESCAPE_SEGMENT_ONLY
1007 *| URL_ESCAPE_PERCENT
1009 HRESULT WINAPI UrlEscapeW(
1012 LPDWORD pcchEscaped,
1016 DWORD needed = 0, ret;
1017 BOOL stop_escaping = FALSE;
1018 WCHAR next[5], *dst = pszEscaped;
1020 PARSEDURLW parsed_url;
1023 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1025 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1026 pszEscaped, pcchEscaped, dwFlags);
1028 if(!pszUrl || !pcchEscaped)
1029 return E_INVALIDARG;
1031 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1032 URL_ESCAPE_SEGMENT_ONLY |
1033 URL_DONT_ESCAPE_EXTRA_INFO |
1034 URL_ESCAPE_PERCENT))
1035 FIXME("Unimplemented flags: %08x\n", dwFlags);
1037 if(pszUrl == pszEscaped) {
1038 dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1040 return E_OUTOFMEMORY;
1044 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1045 /* if SPACES_ONLY specified, reset the other controls */
1046 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1047 URL_ESCAPE_PERCENT |
1048 URL_ESCAPE_SEGMENT_ONLY);
1051 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1052 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1056 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1057 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1059 parsed_url.cbSize = sizeof(parsed_url);
1060 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1061 parsed_url.nScheme = URL_SCHEME_INVALID;
1063 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1065 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1066 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1068 switch(parsed_url.nScheme) {
1069 case URL_SCHEME_FILE:
1070 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1071 int_flags &= ~WINE_URL_STOP_ON_HASH;
1074 case URL_SCHEME_HTTP:
1075 case URL_SCHEME_HTTPS:
1076 int_flags |= WINE_URL_BASH_AS_SLASH;
1077 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1078 int_flags |= WINE_URL_ESCAPE_SLASH;
1081 case URL_SCHEME_MAILTO:
1082 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1083 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1086 case URL_SCHEME_INVALID:
1089 case URL_SCHEME_FTP:
1091 if(parsed_url.pszSuffix[0] != '/')
1092 int_flags |= WINE_URL_ESCAPE_SLASH;
1097 for(src = pszUrl; *src; ) {
1101 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1102 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1103 while(cur == '/' || cur == '\\') {
1107 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1108 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1109 src += localhost_len + 1;
1116 next[0] = next[1] = next[2] = '/';
1123 next[0] = next[1] = '/';
1130 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1131 stop_escaping = TRUE;
1133 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1134 stop_escaping = TRUE;
1136 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1138 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1140 next[1] = hexDigits[(cur >> 4) & 0xf];
1141 next[2] = hexDigits[cur & 0xf];
1150 if(needed + len <= *pcchEscaped) {
1151 memcpy(dst, next, len*sizeof(WCHAR));
1157 if(needed < *pcchEscaped) {
1159 if(pszUrl == pszEscaped)
1160 memcpy(pszEscaped, dst-needed, (needed+1)*sizeof(WCHAR));
1164 needed++; /* add one for the '\0' */
1167 *pcchEscaped = needed;
1169 if(pszUrl == pszEscaped)
1170 HeapFree(GetProcessHeap(), 0, dst);
1175 /*************************************************************************
1176 * UrlUnescapeA [SHLWAPI.@]
1178 * Converts Url escape sequences back to ordinary characters.
1181 * pszUrl [I/O] Url to convert
1182 * pszUnescaped [O] Destination for converted Url
1183 * pcchUnescaped [I/O] Size of output string
1184 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1187 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1188 * dwFlags includes URL_ESCAPE_INPLACE.
1189 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1190 * this case pcchUnescaped is set to the size required.
1192 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1193 * the first occurrence of either a '?' or '#' character.
1195 HRESULT WINAPI UrlUnescapeA(
1198 LPDWORD pcchUnescaped,
1205 BOOL stop_unescaping = FALSE;
1207 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1208 pcchUnescaped, dwFlags);
1210 if (!pszUrl) return E_INVALIDARG;
1212 if(dwFlags & URL_UNESCAPE_INPLACE)
1216 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1220 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1221 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1222 (*src == '#' || *src == '?')) {
1223 stop_unescaping = TRUE;
1225 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1226 && stop_unescaping == FALSE) {
1229 memcpy(buf, src + 1, 2);
1231 ih = strtol(buf, NULL, 16);
1233 src += 2; /* Advance to end of escape */
1237 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1241 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1245 needed++; /* add one for the '\0' */
1248 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1249 *pcchUnescaped = needed;
1252 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1253 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1259 /*************************************************************************
1260 * UrlUnescapeW [SHLWAPI.@]
1264 HRESULT WINAPI UrlUnescapeW(
1266 LPWSTR pszUnescaped,
1267 LPDWORD pcchUnescaped,
1274 BOOL stop_unescaping = FALSE;
1276 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1277 pcchUnescaped, dwFlags);
1279 if(!pszUrl) return E_INVALIDARG;
1281 if(dwFlags & URL_UNESCAPE_INPLACE)
1285 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1289 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1290 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1291 (*src == '#' || *src == '?')) {
1292 stop_unescaping = TRUE;
1294 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1295 && stop_unescaping == FALSE) {
1297 WCHAR buf[5] = {'0','x',0};
1298 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1300 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1302 src += 2; /* Advance to end of escape */
1306 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1310 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1314 needed++; /* add one for the '\0' */
1317 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1318 *pcchUnescaped = needed;
1321 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1322 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1328 /*************************************************************************
1329 * UrlGetLocationA [SHLWAPI.@]
1331 * Get the location from a Url.
1334 * pszUrl [I] Url to get the location from
1337 * A pointer to the start of the location in pszUrl, or NULL if there is
1341 * - MSDN erroneously states that "The location is the segment of the Url
1342 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1343 * stop at '?' and always return a NULL in this case.
1344 * - MSDN also erroneously states that "If a file URL has a query string,
1345 * the returned string is the query string". In all tested cases, if the
1346 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1349 *| NULL file://aa/b/cd#hohoh
1350 *| #hohoh http://aa/b/cd#hohoh
1351 *| NULL fi://aa/b/cd#hohoh
1352 *| #hohoh ff://aa/b/cd#hohoh
1354 LPCSTR WINAPI UrlGetLocationA(
1360 base.cbSize = sizeof(base);
1361 res1 = ParseURLA(pszUrl, &base);
1362 if (res1) return NULL; /* invalid scheme */
1364 /* if scheme is file: then never return pointer */
1365 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1367 /* Look for '#' and return its addr */
1368 return strchr(base.pszSuffix, '#');
1371 /*************************************************************************
1372 * UrlGetLocationW [SHLWAPI.@]
1374 * See UrlGetLocationA.
1376 LPCWSTR WINAPI UrlGetLocationW(
1382 base.cbSize = sizeof(base);
1383 res1 = ParseURLW(pszUrl, &base);
1384 if (res1) return NULL; /* invalid scheme */
1386 /* if scheme is file: then never return pointer */
1387 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1389 /* Look for '#' and return its addr */
1390 return strchrW(base.pszSuffix, '#');
1393 /*************************************************************************
1394 * UrlCompareA [SHLWAPI.@]
1399 * pszUrl1 [I] First Url to compare
1400 * pszUrl2 [I] Url to compare to pszUrl1
1401 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1404 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1405 * than, equal to, or less than pszUrl1 respectively.
1407 INT WINAPI UrlCompareA(
1412 INT ret, len, len1, len2;
1415 return strcmp(pszUrl1, pszUrl2);
1416 len1 = strlen(pszUrl1);
1417 if (pszUrl1[len1-1] == '/') len1--;
1418 len2 = strlen(pszUrl2);
1419 if (pszUrl2[len2-1] == '/') len2--;
1421 return strncmp(pszUrl1, pszUrl2, len1);
1422 len = min(len1, len2);
1423 ret = strncmp(pszUrl1, pszUrl2, len);
1424 if (ret) return ret;
1425 if (len1 > len2) return 1;
1429 /*************************************************************************
1430 * UrlCompareW [SHLWAPI.@]
1434 INT WINAPI UrlCompareW(
1440 size_t len, len1, len2;
1443 return strcmpW(pszUrl1, pszUrl2);
1444 len1 = strlenW(pszUrl1);
1445 if (pszUrl1[len1-1] == '/') len1--;
1446 len2 = strlenW(pszUrl2);
1447 if (pszUrl2[len2-1] == '/') len2--;
1449 return strncmpW(pszUrl1, pszUrl2, len1);
1450 len = min(len1, len2);
1451 ret = strncmpW(pszUrl1, pszUrl2, len);
1452 if (ret) return ret;
1453 if (len1 > len2) return 1;
1457 /*************************************************************************
1458 * HashData [SHLWAPI.@]
1460 * Hash an input block into a variable sized digest.
1463 * lpSrc [I] Input block
1464 * nSrcLen [I] Length of lpSrc
1465 * lpDest [I] Output for hash digest
1466 * nDestLen [I] Length of lpDest
1469 * Success: TRUE. lpDest is filled with the computed hash value.
1470 * Failure: FALSE, if any argument is invalid.
1472 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1473 unsigned char *lpDest, DWORD nDestLen)
1475 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1477 if (!lpSrc || !lpDest)
1478 return E_INVALIDARG;
1480 while (destCount >= 0)
1482 lpDest[destCount] = (destCount & 0xff);
1486 while (srcCount >= 0)
1488 destCount = nDestLen - 1;
1489 while (destCount >= 0)
1491 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1499 /*************************************************************************
1500 * UrlHashA [SHLWAPI.@]
1502 * Produce a Hash from a Url.
1505 * pszUrl [I] Url to hash
1506 * lpDest [O] Destinationh for hash
1507 * nDestLen [I] Length of lpDest
1510 * Success: S_OK. lpDest is filled with the computed hash value.
1511 * Failure: E_INVALIDARG, if any argument is invalid.
1513 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1515 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1516 return E_INVALIDARG;
1518 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1522 /*************************************************************************
1523 * UrlHashW [SHLWAPI.@]
1527 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1529 char szUrl[MAX_PATH];
1531 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1533 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1534 return E_INVALIDARG;
1536 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1537 * return the same digests for the same URL.
1539 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1540 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1544 /*************************************************************************
1545 * UrlApplySchemeA [SHLWAPI.@]
1547 * Apply a scheme to a Url.
1550 * pszIn [I] Url to apply scheme to
1551 * pszOut [O] Destination for modified Url
1552 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1553 * dwFlags [I] URL_ flags from "shlwapi.h"
1556 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1557 * Failure: An HRESULT error code describing the error.
1559 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1565 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1566 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1568 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1570 in = HeapAlloc(GetProcessHeap(), 0,
1571 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1572 out = in + INTERNET_MAX_URL_LENGTH;
1574 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1575 len = INTERNET_MAX_URL_LENGTH;
1577 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1579 HeapFree(GetProcessHeap(), 0, in);
1583 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1584 if (len > *pcchOut) {
1589 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1594 HeapFree(GetProcessHeap(), 0, in);
1598 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1603 DWORD value_len, data_len, dwType, i;
1604 WCHAR reg_path[MAX_PATH];
1605 WCHAR value[MAX_PATH], data[MAX_PATH];
1608 MultiByteToWideChar(0, 0,
1609 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1610 -1, reg_path, MAX_PATH);
1611 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1613 while(value_len = data_len = MAX_PATH,
1614 RegEnumValueW(newkey, index, value, &value_len,
1615 0, &dwType, (LPVOID)data, &data_len) == 0) {
1616 TRACE("guess %d %s is %s\n",
1617 index, debugstr_w(value), debugstr_w(data));
1620 for(i=0; i<value_len; i++) {
1623 /* remember that TRUE is not-equal */
1624 j = ChrCmpIW(Wxx, Wyy);
1627 if ((i == value_len) && !j) {
1628 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1629 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1630 RegCloseKey(newkey);
1633 strcpyW(pszOut, data);
1634 strcatW(pszOut, pszIn);
1635 *pcchOut = strlenW(pszOut);
1636 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1637 RegCloseKey(newkey);
1642 RegCloseKey(newkey);
1646 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1649 DWORD data_len, dwType;
1650 WCHAR data[MAX_PATH];
1652 static const WCHAR prefix_keyW[] =
1653 {'S','o','f','t','w','a','r','e',
1654 '\\','M','i','c','r','o','s','o','f','t',
1655 '\\','W','i','n','d','o','w','s',
1656 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1658 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1660 /* get and prepend default */
1661 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1662 data_len = sizeof(data);
1663 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1664 RegCloseKey(newkey);
1665 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1666 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1669 strcpyW(pszOut, data);
1670 strcatW(pszOut, pszIn);
1671 *pcchOut = strlenW(pszOut);
1672 TRACE("used default %s\n", debugstr_w(pszOut));
1676 /*************************************************************************
1677 * UrlApplySchemeW [SHLWAPI.@]
1679 * See UrlApplySchemeA.
1681 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1683 PARSEDURLW in_scheme;
1687 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1688 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1690 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1692 if (dwFlags & URL_APPLY_GUESSFILE) {
1693 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1694 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1695 strcpyW(pszOut, pszIn);
1696 *pcchOut = strlenW(pszOut);
1700 in_scheme.cbSize = sizeof(in_scheme);
1701 /* See if the base has a scheme */
1702 res1 = ParseURLW(pszIn, &in_scheme);
1704 /* no scheme in input, need to see if we need to guess */
1705 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1706 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1711 /* we have a scheme, see if valid (known scheme) */
1712 if (in_scheme.nScheme) {
1713 /* have valid scheme, so just copy and exit */
1714 if (strlenW(pszIn) + 1 > *pcchOut) {
1715 *pcchOut = strlenW(pszIn) + 1;
1718 strcpyW(pszOut, pszIn);
1719 *pcchOut = strlenW(pszOut);
1720 TRACE("valid scheme, returning copy\n");
1725 /* If we are here, then either invalid scheme,
1726 * or no scheme and can't/failed guess.
1728 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1730 (dwFlags & URL_APPLY_DEFAULT)) {
1731 /* find and apply default scheme */
1732 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1738 /*************************************************************************
1739 * UrlIsA [SHLWAPI.@]
1741 * Determine if a Url is of a certain class.
1744 * pszUrl [I] Url to check
1745 * Urlis [I] URLIS_ constant from "shlwapi.h"
1748 * TRUE if pszUrl belongs to the class type in Urlis.
1751 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1757 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1765 base.cbSize = sizeof(base);
1766 res1 = ParseURLA(pszUrl, &base);
1767 if (res1) return FALSE; /* invalid scheme */
1768 switch (base.nScheme)
1770 case URL_SCHEME_MAILTO:
1771 case URL_SCHEME_SHELL:
1772 case URL_SCHEME_JAVASCRIPT:
1773 case URL_SCHEME_VBSCRIPT:
1774 case URL_SCHEME_ABOUT:
1780 return !StrCmpNA("file:", pszUrl, 5);
1782 case URLIS_DIRECTORY:
1783 last = pszUrl + strlen(pszUrl) - 1;
1784 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1787 return PathIsURLA(pszUrl);
1789 case URLIS_NOHISTORY:
1790 case URLIS_APPLIABLE:
1791 case URLIS_HASQUERY:
1793 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1798 /*************************************************************************
1799 * UrlIsW [SHLWAPI.@]
1803 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1805 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1810 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1818 base.cbSize = sizeof(base);
1819 res1 = ParseURLW(pszUrl, &base);
1820 if (res1) return FALSE; /* invalid scheme */
1821 switch (base.nScheme)
1823 case URL_SCHEME_MAILTO:
1824 case URL_SCHEME_SHELL:
1825 case URL_SCHEME_JAVASCRIPT:
1826 case URL_SCHEME_VBSCRIPT:
1827 case URL_SCHEME_ABOUT:
1833 return !strncmpW(stemp, pszUrl, 5);
1835 case URLIS_DIRECTORY:
1836 last = pszUrl + strlenW(pszUrl) - 1;
1837 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1840 return PathIsURLW(pszUrl);
1842 case URLIS_NOHISTORY:
1843 case URLIS_APPLIABLE:
1844 case URLIS_HASQUERY:
1846 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1851 /*************************************************************************
1852 * UrlIsNoHistoryA [SHLWAPI.@]
1854 * Determine if a Url should not be stored in the users history list.
1857 * pszUrl [I] Url to check
1860 * TRUE, if pszUrl should be excluded from the history list,
1863 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1865 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1868 /*************************************************************************
1869 * UrlIsNoHistoryW [SHLWAPI.@]
1871 * See UrlIsNoHistoryA.
1873 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1875 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1878 /*************************************************************************
1879 * UrlIsOpaqueA [SHLWAPI.@]
1881 * Determine if a Url is opaque.
1884 * pszUrl [I] Url to check
1887 * TRUE if pszUrl is opaque,
1891 * An opaque Url is one that does not start with "<protocol>://".
1893 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1895 return UrlIsA(pszUrl, URLIS_OPAQUE);
1898 /*************************************************************************
1899 * UrlIsOpaqueW [SHLWAPI.@]
1903 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1905 return UrlIsW(pszUrl, URLIS_OPAQUE);
1908 /*************************************************************************
1909 * Scans for characters of type "type" and when not matching found,
1910 * returns pointer to it and length in size.
1912 * Characters tested based on RFC 1738
1914 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1916 static DWORD alwayszero = 0;
1925 if ( (islowerW(*start) && isalphaW(*start)) ||
1940 if ( isalphaW(*start) ||
1942 /* user/password only characters */
1947 /* *extra* characters */
1954 /* *safe* characters */
1963 } else if (*start == '%') {
1964 if (isxdigitW(*(start+1)) &&
1965 isxdigitW(*(start+2))) {
1977 if (isdigitW(*start)) {
1988 if (isalnumW(*start) ||
2000 FIXME("unknown type %d\n", type);
2001 return (LPWSTR)&alwayszero;
2003 /* TRACE("scanned %d characters next char %p<%c>\n",
2004 *size, start, *start); */
2008 /*************************************************************************
2009 * Attempt to parse URL into pieces.
2011 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2015 memset(pl, 0, sizeof(WINE_PARSE_URL));
2016 pl->pScheme = pszUrl;
2017 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2018 if (!*work || (*work != ':')) goto ErrorExit;
2020 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2021 pl->pUserName = work + 2;
2022 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2023 if (*work == ':' ) {
2024 /* parse password */
2026 pl->pPassword = work;
2027 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2029 /* what we just parsed must be the hostname and port
2030 * so reset pointers and clear then let it parse */
2031 pl->szUserName = pl->szPassword = 0;
2032 work = pl->pUserName - 1;
2033 pl->pUserName = pl->pPassword = 0;
2035 } else if (*work == '@') {
2039 } else if (!*work || (*work == '/') || (*work == '.')) {
2040 /* what was parsed was hostname, so reset pointers and let it parse */
2041 pl->szUserName = pl->szPassword = 0;
2042 work = pl->pUserName - 1;
2043 pl->pUserName = pl->pPassword = 0;
2044 } else goto ErrorExit;
2046 /* now start parsing hostname or hostnumber */
2048 pl->pHostName = work;
2049 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2054 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2057 /* see if query string */
2058 pl->pQuery = strchrW(work, '?');
2059 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2062 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2063 pl->pScheme, pl->szScheme,
2064 pl->pUserName, pl->szUserName,
2065 pl->pPassword, pl->szPassword,
2066 pl->pHostName, pl->szHostName,
2067 pl->pPort, pl->szPort,
2068 pl->pQuery, pl->szQuery);
2071 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2072 return E_INVALIDARG;
2075 /*************************************************************************
2076 * UrlGetPartA [SHLWAPI.@]
2078 * Retrieve part of a Url.
2081 * pszIn [I] Url to parse
2082 * pszOut [O] Destination for part of pszIn requested
2083 * pcchOut [I] Size of pszOut
2084 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2085 * needed size of pszOut INCLUDING '\0'.
2086 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2087 * dwFlags [I] URL_ flags from "shlwapi.h"
2090 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2091 * Failure: An HRESULT error code describing the error.
2093 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2094 DWORD dwPart, DWORD dwFlags)
2097 DWORD ret, len, len2;
2099 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2100 return E_INVALIDARG;
2102 in = HeapAlloc(GetProcessHeap(), 0,
2103 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2104 out = in + INTERNET_MAX_URL_LENGTH;
2106 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2108 len = INTERNET_MAX_URL_LENGTH;
2109 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2112 HeapFree(GetProcessHeap(), 0, in);
2116 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2117 if (len2 > *pcchOut) {
2119 HeapFree(GetProcessHeap(), 0, in);
2122 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2124 HeapFree(GetProcessHeap(), 0, in);
2128 /*************************************************************************
2129 * UrlGetPartW [SHLWAPI.@]
2133 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2134 DWORD dwPart, DWORD dwFlags)
2138 DWORD scheme, size, schsize;
2139 LPCWSTR addr, schaddr;
2141 TRACE("(%s %p %p(%d) %08x %08x)\n",
2142 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2144 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2145 return E_INVALIDARG;
2149 addr = strchrW(pszIn, ':');
2151 scheme = URL_SCHEME_UNKNOWN;
2153 scheme = get_scheme_code(pszIn, addr-pszIn);
2155 ret = URL_ParseUrl(pszIn, &pl);
2158 case URL_PART_SCHEME:
2159 if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
2167 case URL_PART_HOSTNAME:
2169 case URL_SCHEME_FTP:
2170 case URL_SCHEME_HTTP:
2171 case URL_SCHEME_GOPHER:
2172 case URL_SCHEME_TELNET:
2173 case URL_SCHEME_FILE:
2174 case URL_SCHEME_HTTPS:
2181 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2182 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2187 if (!pl.szHostName) {
2191 addr = pl.pHostName;
2192 size = pl.szHostName;
2195 case URL_PART_USERNAME:
2196 if (!pl.szUserName) {
2200 addr = pl.pUserName;
2201 size = pl.szUserName;
2204 case URL_PART_PASSWORD:
2205 if (!pl.szPassword) {
2209 addr = pl.pPassword;
2210 size = pl.szPassword;
2222 case URL_PART_QUERY:
2233 return E_INVALIDARG;
2236 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2237 if(!pl.pScheme || !pl.szScheme) {
2241 schaddr = pl.pScheme;
2242 schsize = pl.szScheme;
2243 if (*pcchOut < schsize + size + 2) {
2244 *pcchOut = schsize + size + 2;
2247 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2248 pszOut[schsize] = ':';
2249 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2250 pszOut[schsize+1+size] = 0;
2251 *pcchOut = schsize + 1 + size;
2254 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2255 memcpy(pszOut, addr, size*sizeof(WCHAR));
2259 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2264 /*************************************************************************
2265 * PathIsURLA [SHLWAPI.@]
2267 * Check if the given path is a Url.
2270 * lpszPath [I] Path to check.
2273 * TRUE if lpszPath is a Url.
2274 * FALSE if lpszPath is NULL or not a Url.
2276 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2281 TRACE("%s\n", debugstr_a(lpstrPath));
2283 if (!lpstrPath || !*lpstrPath) return FALSE;
2286 base.cbSize = sizeof(base);
2287 hres = ParseURLA(lpstrPath, &base);
2288 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2291 /*************************************************************************
2292 * PathIsURLW [SHLWAPI.@]
2296 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2301 TRACE("%s\n", debugstr_w(lpstrPath));
2303 if (!lpstrPath || !*lpstrPath) return FALSE;
2306 base.cbSize = sizeof(base);
2307 hres = ParseURLW(lpstrPath, &base);
2308 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2311 /*************************************************************************
2312 * UrlCreateFromPathA [SHLWAPI.@]
2314 * See UrlCreateFromPathW
2316 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2318 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2320 UNICODE_STRING pathW;
2322 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2324 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2325 return E_INVALIDARG;
2326 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2327 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2328 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2330 if(ret == S_OK || ret == S_FALSE) {
2331 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2332 if(*pcchUrl > lenA) {
2333 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2337 *pcchUrl = lenA + 1;
2341 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2342 RtlFreeUnicodeString(&pathW);
2346 /*************************************************************************
2347 * UrlCreateFromPathW [SHLWAPI.@]
2349 * Create a Url from a file path.
2352 * pszPath [I] Path to convert
2353 * pszUrl [O] Destination for the converted Url
2354 * pcchUrl [I/O] Length of pszUrl
2355 * dwReserved [I] Reserved, must be 0
2358 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2359 * Failure: An HRESULT error code.
2361 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2366 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2367 WCHAR three_slashesW[] = {'/','/','/',0};
2368 PARSEDURLW parsed_url;
2370 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2372 /* Validate arguments */
2373 if (dwReserved != 0)
2374 return E_INVALIDARG;
2375 if (!pszUrl || !pcchUrl)
2376 return E_INVALIDARG;
2379 parsed_url.cbSize = sizeof(parsed_url);
2380 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2381 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2382 needed = strlenW(pszPath);
2383 if (needed >= *pcchUrl) {
2384 *pcchUrl = needed + 1;
2388 strcpyW(pszUrl, pszPath);
2394 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2395 strcpyW(pszNewUrl, file_colonW);
2396 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2397 strcatW(pszNewUrl, three_slashesW);
2398 strcatW(pszNewUrl, pszPath);
2399 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2401 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2405 /*************************************************************************
2406 * SHAutoComplete [SHLWAPI.@]
2408 * Enable auto-completion for an edit control.
2411 * hwndEdit [I] Handle of control to enable auto-completion for
2412 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2415 * Success: S_OK. Auto-completion is enabled for the control.
2416 * Failure: An HRESULT error code indicating the error.
2418 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2424 /*************************************************************************
2425 * MLBuildResURLA [SHLWAPI.405]
2427 * Create a Url pointing to a resource in a module.
2430 * lpszLibName [I] Name of the module containing the resource
2431 * hMod [I] Callers module handle
2432 * dwFlags [I] Undocumented flags for loading the module
2433 * lpszRes [I] Resource name
2434 * lpszDest [O] Destination for resulting Url
2435 * dwDestLen [I] Length of lpszDest
2438 * Success: S_OK. lpszDest contains the resource Url.
2439 * Failure: E_INVALIDARG, if any argument is invalid, or
2440 * E_FAIL if dwDestLen is too small.
2442 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2443 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2445 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2449 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2452 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2454 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2455 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2457 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2458 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2459 if (SUCCEEDED(hRet) && lpszDest)
2460 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2465 /*************************************************************************
2466 * MLBuildResURLA [SHLWAPI.406]
2468 * See MLBuildResURLA.
2470 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2471 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2473 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2474 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2475 HRESULT hRet = E_FAIL;
2477 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2478 debugstr_w(lpszRes), lpszDest, dwDestLen);
2480 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2481 !lpszDest || (dwFlags && dwFlags != 2))
2482 return E_INVALIDARG;
2484 if (dwDestLen >= szResLen + 1)
2486 dwDestLen -= (szResLen + 1);
2487 memcpy(lpszDest, szRes, sizeof(szRes));
2489 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2493 WCHAR szBuff[MAX_PATH];
2496 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2497 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2499 DWORD dwPathLen = strlenW(szBuff) + 1;
2501 if (dwDestLen >= dwPathLen)
2505 dwDestLen -= dwPathLen;
2506 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2508 dwResLen = strlenW(lpszRes) + 1;
2509 if (dwDestLen >= dwResLen + 1)
2511 lpszDest[szResLen + dwPathLen-1] = '/';
2512 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2517 MLFreeLibrary(hMod);
2523 /***********************************************************************
2524 * UrlFixupW [SHLWAPI.462]
2526 * Checks the scheme part of a URL and attempts to correct misspellings.
2529 * lpszUrl [I] Pointer to the URL to be corrected
2530 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2531 * dwMaxChars [I] Maximum size of corrected URL
2534 * success: S_OK if URL corrected or already correct
2535 * failure: S_FALSE if unable to correct / COM error code if other error
2538 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2542 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2547 srcLen = lstrlenW(url) + 1;
2549 /* For now just copy the URL directly */
2550 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);