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 static inline WCHAR *heap_strdupAtoW(const char *str)
52 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
53 ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
54 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
60 /* The following schemes were identified in the native version of
61 * SHLWAPI.DLL version 5.50
64 URL_SCHEME scheme_number;
65 WCHAR scheme_name[12];
66 } shlwapi_schemes[] = {
67 {URL_SCHEME_FTP, {'f','t','p',0}},
68 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
69 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
70 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
71 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
72 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
73 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
74 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
75 {URL_SCHEME_FILE, {'f','i','l','e',0}},
76 {URL_SCHEME_MK, {'m','k',0}},
77 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
78 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
79 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
80 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
81 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
82 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
83 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
84 {URL_SCHEME_RES, {'r','e','s',0}},
88 LPCWSTR pScheme; /* [out] start of scheme */
89 DWORD szScheme; /* [out] size of scheme (until colon) */
90 LPCWSTR pUserName; /* [out] start of Username */
91 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
92 LPCWSTR pPassword; /* [out] start of Password */
93 DWORD szPassword; /* [out] size of Password (until "@") */
94 LPCWSTR pHostName; /* [out] start of Hostname */
95 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
96 LPCWSTR pPort; /* [out] start of Port */
97 DWORD szPort; /* [out] size of Port (until "/" or eos) */
98 LPCWSTR pQuery; /* [out] start of Query */
99 DWORD szQuery; /* [out] size of Query (until eos) */
107 } WINE_URL_SCAN_TYPE;
109 static const CHAR hexDigits[] = "0123456789ABCDEF";
111 static const WCHAR fileW[] = {'f','i','l','e','\0'};
113 static const unsigned char HashDataLookup[256] = {
114 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
115 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
116 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
117 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
118 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
119 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
120 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
121 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
122 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
123 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
124 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
125 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
126 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
127 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
128 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
129 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
130 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
131 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
132 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
133 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
135 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
139 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
140 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
141 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
142 return shlwapi_schemes[i].scheme_number;
145 return URL_SCHEME_UNKNOWN;
148 /*************************************************************************
151 * Parse a Url into its constituent parts.
155 * y [O] Undocumented structure holding the parsed information
158 * Success: S_OK. y contains the parsed Url details.
159 * Failure: An HRESULT error code.
161 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
163 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
167 TRACE("%s %p\n", debugstr_a(x), y);
169 if(y->cbSize != sizeof(*y))
172 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
175 if (*ptr != ':' || ptr <= x+1) {
176 y->pszProtocol = NULL;
177 return URL_E_INVALID_SYNTAX;
181 y->cchProtocol = ptr-x;
182 y->pszSuffix = ptr+1;
183 y->cchSuffix = strlen(y->pszSuffix);
185 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
186 scheme, sizeof(scheme)/sizeof(WCHAR));
187 y->nScheme = get_scheme_code(scheme, len);
192 /*************************************************************************
195 * Unicode version of ParseURLA.
197 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
199 const WCHAR *ptr = x;
201 TRACE("%s %p\n", debugstr_w(x), y);
203 if(y->cbSize != sizeof(*y))
206 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
209 if (*ptr != ':' || ptr <= x+1) {
210 y->pszProtocol = NULL;
211 return URL_E_INVALID_SYNTAX;
215 y->cchProtocol = ptr-x;
216 y->pszSuffix = ptr+1;
217 y->cchSuffix = strlenW(y->pszSuffix);
218 y->nScheme = get_scheme_code(x, ptr-x);
223 /*************************************************************************
224 * UrlCanonicalizeA [SHLWAPI.@]
226 * Canonicalize a Url.
229 * pszUrl [I] Url to cCanonicalize
230 * pszCanonicalized [O] Destination for converted Url.
231 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
232 * dwFlags [I] Flags controlling the conversion.
235 * Success: S_OK. The pszCanonicalized contains the converted Url.
236 * Failure: E_POINTER, if *pcchCanonicalized is too small.
238 * MSDN incorrectly describes the flags for this function. They should be:
239 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
240 *| URL_ESCAPE_SPACES_ONLY 0x04000000
241 *| URL_ESCAPE_PERCENT 0x00001000
242 *| URL_ESCAPE_UNSAFE 0x10000000
243 *| URL_UNESCAPE 0x10000000
244 *| URL_DONT_SIMPLIFY 0x08000000
245 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
247 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
248 LPDWORD pcchCanonicalized, DWORD dwFlags)
250 LPWSTR url, canonical;
253 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
254 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
256 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
259 url = heap_strdupAtoW(pszUrl);
260 canonical = HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized*sizeof(WCHAR));
261 if(!url || !canonical) {
262 HeapFree(GetProcessHeap(), 0, url);
263 HeapFree(GetProcessHeap(), 0, canonical);
264 return E_OUTOFMEMORY;
267 ret = UrlCanonicalizeW(url, canonical, pcchCanonicalized, dwFlags);
269 WideCharToMultiByte(CP_ACP, 0, canonical, -1, pszCanonicalized,
270 *pcchCanonicalized+1, NULL, NULL);
272 HeapFree(GetProcessHeap(), 0, url);
273 HeapFree(GetProcessHeap(), 0, canonical);
277 /*************************************************************************
278 * UrlCanonicalizeW [SHLWAPI.@]
280 * See UrlCanonicalizeA.
282 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
283 LPDWORD pcchCanonicalized, DWORD dwFlags)
288 LPWSTR lpszUrlCpy, url, wk2, mp, mp2;
290 DWORD nByteLen, nLen, nWkLen;
294 static const WCHAR wszFile[] = {'f','i','l','e',':'};
295 static const WCHAR wszRes[] = {'r','e','s',':'};
296 static const WCHAR wszHttp[] = {'h','t','t','p',':'};
297 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
298 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
300 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
301 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
303 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
307 *pszCanonicalized = 0;
311 /* Remove '\t' characters from URL */
312 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
313 url = HeapAlloc(GetProcessHeap(), 0, nByteLen);
315 return E_OUTOFMEMORY;
325 /* Allocate memory for simplified URL (before escaping) */
326 nByteLen = (wk2-url)*sizeof(WCHAR);
327 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
328 nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR));
330 HeapFree(GetProcessHeap(), 0, url);
331 return E_OUTOFMEMORY;
334 is_file_url = !strncmpW(wszFile, url, sizeof(wszFile)/sizeof(WCHAR));
336 if ((nByteLen >= sizeof(wszHttp) &&
337 !memcmp(wszHttp, url, sizeof(wszHttp))) || is_file_url)
340 if((dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY)) && is_file_url)
343 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, url, sizeof(wszRes))) {
344 dwFlags &= ~URL_FILE_USE_PATHURL;
351 * 1 have 2[+] alnum 2,3
352 * 2 have scheme (found :) 4,6,3
353 * 3 failed (no location)
355 * 5 have 1[+] alnum 6,3
356 * 6 have location (found /) save root location
363 if(url[1] == ':') { /* Assume path */
364 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
365 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
366 if (dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY))
372 dwFlags |= URL_ESCAPE_UNSAFE;
380 if (!isalnumW(*wk1)) {state = 3; break;}
382 if (!isalnumW(*wk1)) {state = 3; break;}
388 if (*wk1++ == ':') state = 2;
392 if (*wk1 != '/') {state = 6; break;}
394 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
396 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
397 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
398 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
402 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL)){
404 }else if(is_file_url){
405 const WCHAR *body = wk1;
410 if(isalnumW(*body) && *(body+1) == ':'){
411 if(!(dwFlags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL))){
418 if(dwFlags & URL_WININET_COMPATIBILITY){
419 if(*wk1 == '/' && *(wk1+1) != '/'){
426 if(*wk1 == '/' && *(wk1+1) != '/'){
439 nWkLen = strlenW(wk1);
440 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
447 if(*mp == '/' || *mp == '\\')
454 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
456 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
467 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
468 while(*wk1 == '/' || *wk1 == '\\') {
478 if(dwFlags & URL_DONT_SIMPLIFY) {
483 /* Now at root location, cannot back up any more. */
484 /* "root" will point at the '/' */
488 mp = strchrW(wk1, '/');
489 mp2 = strchrW(wk1, '\\');
490 if(mp2 && (!mp || mp2 < mp))
493 nWkLen = strlenW(wk1);
494 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
501 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
511 while (*wk1 == '.') {
512 TRACE("found '/.'\n");
513 if (wk1[1] == '/' || wk1[1] == '\\') {
514 /* case of /./ -> skip the ./ */
517 else if (wk1[1] == '.' && (wk1[2] == '/'
518 || wk1[2] == '\\' || wk1[2] == '?'
519 || wk1[2] == '#' || !wk1[2])) {
520 /* case /../ -> need to backup wk2 */
521 TRACE("found '/../'\n");
522 *(wk2-1) = '\0'; /* set end of string */
523 mp = strrchrW(root, '/');
524 mp2 = strrchrW(root, '\\');
525 if(mp2 && (!mp || mp2 < mp))
527 if (mp && (mp >= root)) {
528 /* found valid backup point */
530 if(wk1[2] != '/' && wk1[2] != '\\')
536 /* did not find point, restore '/' */
548 FIXME("how did we get here - state=%d\n", state);
549 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
550 HeapFree(GetProcessHeap(), 0, url);
554 TRACE("Simplified, orig <%s>, simple <%s>\n",
555 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
557 nLen = lstrlenW(lpszUrlCpy);
558 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
559 lpszUrlCpy[--nLen]=0;
561 if((dwFlags & URL_UNESCAPE) ||
562 ((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
563 && !memcmp(wszFile, url, sizeof(wszFile))))
564 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
566 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
567 URL_ESCAPE_SPACES_ONLY |
569 URL_DONT_ESCAPE_EXTRA_INFO |
570 URL_ESCAPE_SEGMENT_ONLY ))) {
571 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
572 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
574 } else { /* No escaping needed, just copy the string */
575 nLen = lstrlenW(lpszUrlCpy);
576 if(nLen < *pcchCanonicalized)
577 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
582 *pcchCanonicalized = nLen;
585 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
586 HeapFree(GetProcessHeap(), 0, url);
589 TRACE("result %s\n", debugstr_w(pszCanonicalized));
594 /*************************************************************************
595 * UrlCombineA [SHLWAPI.@]
600 * pszBase [I] Base Url
601 * pszRelative [I] Url to combine with pszBase
602 * pszCombined [O] Destination for combined Url
603 * pcchCombined [O] Destination for length of pszCombined
604 * dwFlags [I] URL_ flags from "shlwapi.h"
607 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
608 * contains its length.
609 * Failure: An HRESULT error code indicating the error.
611 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
612 LPSTR pszCombined, LPDWORD pcchCombined,
615 LPWSTR base, relative, combined;
616 DWORD ret, len, len2;
618 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
619 debugstr_a(pszBase),debugstr_a(pszRelative),
620 pcchCombined?*pcchCombined:0,dwFlags);
622 if(!pszBase || !pszRelative || !pcchCombined)
625 base = HeapAlloc(GetProcessHeap(), 0,
626 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
627 relative = base + INTERNET_MAX_URL_LENGTH;
628 combined = relative + INTERNET_MAX_URL_LENGTH;
630 MultiByteToWideChar(CP_ACP, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
631 MultiByteToWideChar(CP_ACP, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
634 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
637 HeapFree(GetProcessHeap(), 0, base);
641 len2 = WideCharToMultiByte(CP_ACP, 0, combined, len, NULL, 0, NULL, NULL);
642 if (len2 > *pcchCombined) {
643 *pcchCombined = len2;
644 HeapFree(GetProcessHeap(), 0, base);
647 WideCharToMultiByte(CP_ACP, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
649 *pcchCombined = len2;
650 HeapFree(GetProcessHeap(), 0, base);
654 /*************************************************************************
655 * UrlCombineW [SHLWAPI.@]
659 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
660 LPWSTR pszCombined, LPDWORD pcchCombined,
663 PARSEDURLW base, relative;
664 DWORD myflags, sizeloc = 0;
665 DWORD i, len, res1, res2, process_case = 0;
666 LPWSTR work, preliminary, mbase, mrelative;
667 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
670 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
671 debugstr_w(pszBase),debugstr_w(pszRelative),
672 pcchCombined?*pcchCombined:0,dwFlags);
674 if(!pszBase || !pszRelative || !pcchCombined)
677 base.cbSize = sizeof(base);
678 relative.cbSize = sizeof(relative);
680 /* Get space for duplicates of the input and the output */
681 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
683 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
684 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
687 /* Canonicalize the base input prior to looking for the scheme */
688 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
689 len = INTERNET_MAX_URL_LENGTH;
690 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
692 /* Canonicalize the relative input prior to looking for the scheme */
693 len = INTERNET_MAX_URL_LENGTH;
694 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
696 /* See if the base has a scheme */
697 res1 = ParseURLW(mbase, &base);
699 /* if pszBase has no scheme, then return pszRelative */
700 TRACE("no scheme detected in Base\n");
704 BOOL manual_search = FALSE;
706 work = (LPWSTR)base.pszProtocol;
707 for(i=0; i<base.cchProtocol; i++)
708 work[i] = tolowerW(work[i]);
710 /* mk is a special case */
711 if(base.nScheme == URL_SCHEME_MK) {
712 static const WCHAR wsz[] = {':',':',0};
714 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
719 delta = ptr-base.pszSuffix;
720 base.cchProtocol += delta;
721 base.pszSuffix += delta;
722 base.cchSuffix -= delta;
725 /* get size of location field (if it exists) */
726 work = (LPWSTR)base.pszSuffix;
728 if (*work++ == '/') {
729 if (*work++ == '/') {
730 /* At this point have start of location and
731 * it ends at next '/' or end of string.
733 while(*work && (*work != '/')) work++;
734 sizeloc = (DWORD)(work - base.pszSuffix);
739 /* If there is a '#' and the characters immediately preceding it are
740 * ".htm[l]", then begin looking for the last leaf starting from
741 * the '#'. Otherwise the '#' is not meaningful and just start
742 * looking from the end. */
743 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
744 const WCHAR htmlW[] = {'.','h','t','m','l',0};
745 const int len_htmlW = 5;
746 const WCHAR htmW[] = {'.','h','t','m',0};
747 const int len_htmW = 4;
749 if (base.nScheme == URL_SCHEME_HTTP || base.nScheme == URL_SCHEME_HTTPS)
750 manual_search = TRUE;
751 else if (work - base.pszSuffix > len_htmW) {
753 if (strncmpiW(work, htmW, len_htmW) == 0)
754 manual_search = TRUE;
758 if (!manual_search &&
759 work - base.pszSuffix > len_htmlW) {
761 if (strncmpiW(work, htmlW, len_htmlW) == 0)
762 manual_search = TRUE;
768 /* search backwards starting from the current position */
769 while (*work != '/' && work > base.pszSuffix + sizeloc)
771 base.cchSuffix = work - base.pszSuffix + 1;
773 /* search backwards starting from the end of the string */
774 work = strrchrW((base.pszSuffix+sizeloc), '/');
776 len = (DWORD)(work - base.pszSuffix + 1);
777 base.cchSuffix = len;
779 base.cchSuffix = sizeloc;
784 * .pszSuffix points to location (starting with '//')
785 * .cchSuffix length of location (above) and rest less the last
787 * sizeloc length of location (above) up to but not including
791 res2 = ParseURLW(mrelative, &relative);
793 /* no scheme in pszRelative */
794 TRACE("no scheme detected in Relative\n");
795 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
796 relative.cchSuffix = strlenW(mrelative);
797 if (*pszRelative == ':') {
798 /* case that is either left alone or uses pszBase */
799 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
806 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
807 /* case that becomes "file:///" */
808 strcpyW(preliminary, myfilestr);
812 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
813 /* pszRelative has location and rest */
817 if (*mrelative == '/') {
818 /* case where pszRelative is root to location */
822 if (*mrelative == '#') {
823 if(!(work = strchrW(base.pszSuffix+base.cchSuffix, '#')))
824 work = (LPWSTR)base.pszSuffix + strlenW(base.pszSuffix);
826 memcpy(preliminary, base.pszProtocol, (work-base.pszProtocol)*sizeof(WCHAR));
827 preliminary[work-base.pszProtocol] = '\0';
831 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
834 work = (LPWSTR)relative.pszProtocol;
835 for(i=0; i<relative.cchProtocol; i++)
836 work[i] = tolowerW(work[i]);
839 /* handle cases where pszRelative has scheme */
840 if ((base.cchProtocol == relative.cchProtocol) &&
841 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
843 /* since the schemes are the same */
844 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
845 /* case where pszRelative replaces location and following */
849 if (*relative.pszSuffix == '/') {
850 /* case where pszRelative is root to location */
854 /* replace either just location if base's location starts with a
855 * slash or otherwise everything */
856 process_case = (*base.pszSuffix == '/') ? 5 : 1;
859 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
860 /* case where pszRelative replaces scheme, location,
861 * and following and handles PLUGGABLE
868 } while(FALSE); /* a little trick to allow easy exit from nested if's */
871 switch (process_case) {
874 * Return pszRelative appended to what ever is in pszCombined,
875 * (which may the string "file:///"
877 strcatW(preliminary, mrelative);
880 case 2: /* case where pszRelative replaces scheme, and location */
881 strcpyW(preliminary, mrelative);
885 * Return the pszBase scheme with pszRelative. Basically
886 * keeps the scheme and replaces the domain and following.
888 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
889 work = preliminary + base.cchProtocol + 1;
890 strcpyW(work, relative.pszSuffix);
894 * Return the pszBase scheme and location but everything
895 * after the location is pszRelative. (Replace document
898 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
899 work = preliminary + base.cchProtocol + 1 + sizeloc;
900 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
902 strcpyW(work, relative.pszSuffix);
906 * Return the pszBase without its document (if any) and
907 * append pszRelative after its scheme.
909 memcpy(preliminary, base.pszProtocol,
910 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
911 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
914 strcpyW(work, relative.pszSuffix);
918 FIXME("How did we get here????? process_case=%d\n", process_case);
923 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
924 if(*pcchCombined == 0)
926 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
927 if(SUCCEEDED(ret) && pszCombined) {
928 lstrcpyW(pszCombined, mrelative);
930 TRACE("return-%d len=%d, %s\n",
931 process_case, *pcchCombined, debugstr_w(pszCombined));
933 HeapFree(GetProcessHeap(), 0, preliminary);
937 /*************************************************************************
938 * UrlEscapeA [SHLWAPI.@]
941 HRESULT WINAPI UrlEscapeA(
947 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
948 WCHAR *escapedW = bufW;
951 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
953 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
956 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
958 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
959 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
960 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
963 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
964 if(*pcchEscaped > lenA) {
965 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
966 pszEscaped[lenA] = 0;
969 *pcchEscaped = lenA + 1;
973 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
974 RtlFreeUnicodeString(&urlW);
978 #define WINE_URL_BASH_AS_SLASH 0x01
979 #define WINE_URL_COLLAPSE_SLASHES 0x02
980 #define WINE_URL_ESCAPE_SLASH 0x04
981 #define WINE_URL_ESCAPE_HASH 0x08
982 #define WINE_URL_ESCAPE_QUESTION 0x10
983 #define WINE_URL_STOP_ON_HASH 0x20
984 #define WINE_URL_STOP_ON_QUESTION 0x40
986 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
992 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
999 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
1002 if (ch <= 31 || ch >= 127)
1023 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
1027 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
1031 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
1041 /*************************************************************************
1042 * UrlEscapeW [SHLWAPI.@]
1044 * Converts unsafe characters in a Url into escape sequences.
1047 * pszUrl [I] Url to modify
1048 * pszEscaped [O] Destination for modified Url
1049 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1050 * dwFlags [I] URL_ flags from "shlwapi.h"
1053 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1054 * contains its length.
1055 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1056 * pcchEscaped is set to the required length.
1058 * Converts unsafe characters into their escape sequences.
1061 * - By default this function stops converting at the first '?' or
1063 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1064 * converted, but the conversion continues past a '?' or '#'.
1065 * - Note that this function did not work well (or at all) in shlwapi version 4.
1068 * Only the following flags are implemented:
1069 *| URL_ESCAPE_SPACES_ONLY
1070 *| URL_DONT_ESCAPE_EXTRA_INFO
1071 *| URL_ESCAPE_SEGMENT_ONLY
1072 *| URL_ESCAPE_PERCENT
1074 HRESULT WINAPI UrlEscapeW(
1077 LPDWORD pcchEscaped,
1081 DWORD needed = 0, ret;
1082 BOOL stop_escaping = FALSE;
1083 WCHAR next[5], *dst, *dst_ptr;
1085 PARSEDURLW parsed_url;
1088 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1090 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1091 pszEscaped, pcchEscaped, dwFlags);
1093 if(!pszUrl || !pcchEscaped)
1094 return E_INVALIDARG;
1096 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1097 URL_ESCAPE_SEGMENT_ONLY |
1098 URL_DONT_ESCAPE_EXTRA_INFO |
1099 URL_ESCAPE_PERCENT))
1100 FIXME("Unimplemented flags: %08x\n", dwFlags);
1102 dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1104 return E_OUTOFMEMORY;
1107 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1108 /* if SPACES_ONLY specified, reset the other controls */
1109 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1110 URL_ESCAPE_PERCENT |
1111 URL_ESCAPE_SEGMENT_ONLY);
1114 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1115 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1119 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1120 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1122 parsed_url.cbSize = sizeof(parsed_url);
1123 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1124 parsed_url.nScheme = URL_SCHEME_INVALID;
1126 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1128 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1129 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1131 switch(parsed_url.nScheme) {
1132 case URL_SCHEME_FILE:
1133 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1134 int_flags &= ~WINE_URL_STOP_ON_HASH;
1137 case URL_SCHEME_HTTP:
1138 case URL_SCHEME_HTTPS:
1139 int_flags |= WINE_URL_BASH_AS_SLASH;
1140 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1141 int_flags |= WINE_URL_ESCAPE_SLASH;
1144 case URL_SCHEME_MAILTO:
1145 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1146 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1149 case URL_SCHEME_INVALID:
1152 case URL_SCHEME_FTP:
1154 if(parsed_url.pszSuffix[0] != '/')
1155 int_flags |= WINE_URL_ESCAPE_SLASH;
1160 for(src = pszUrl; *src; ) {
1164 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1165 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1166 while(cur == '/' || cur == '\\') {
1170 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1171 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1172 src += localhost_len + 1;
1179 next[0] = next[1] = next[2] = '/';
1186 next[0] = next[1] = '/';
1193 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1194 stop_escaping = TRUE;
1196 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1197 stop_escaping = TRUE;
1199 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1201 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1203 next[1] = hexDigits[(cur >> 4) & 0xf];
1204 next[2] = hexDigits[cur & 0xf];
1213 if(needed + len <= *pcchEscaped) {
1214 memcpy(dst, next, len*sizeof(WCHAR));
1220 if(needed < *pcchEscaped) {
1222 memcpy(pszEscaped, dst_ptr, (needed+1)*sizeof(WCHAR));
1226 needed++; /* add one for the '\0' */
1229 *pcchEscaped = needed;
1231 HeapFree(GetProcessHeap(), 0, dst_ptr);
1236 /*************************************************************************
1237 * UrlUnescapeA [SHLWAPI.@]
1239 * Converts Url escape sequences back to ordinary characters.
1242 * pszUrl [I/O] Url to convert
1243 * pszUnescaped [O] Destination for converted Url
1244 * pcchUnescaped [I/O] Size of output string
1245 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1248 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1249 * dwFlags includes URL_ESCAPE_INPLACE.
1250 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1251 * this case pcchUnescaped is set to the size required.
1253 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1254 * the first occurrence of either a '?' or '#' character.
1256 HRESULT WINAPI UrlUnescapeA(
1259 LPDWORD pcchUnescaped,
1266 BOOL stop_unescaping = FALSE;
1268 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1269 pcchUnescaped, dwFlags);
1271 if (!pszUrl) return E_INVALIDARG;
1273 if(dwFlags & URL_UNESCAPE_INPLACE)
1277 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1281 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1282 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1283 (*src == '#' || *src == '?')) {
1284 stop_unescaping = TRUE;
1286 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1287 && stop_unescaping == FALSE) {
1290 memcpy(buf, src + 1, 2);
1292 ih = strtol(buf, NULL, 16);
1294 src += 2; /* Advance to end of escape */
1298 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1302 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1306 needed++; /* add one for the '\0' */
1309 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1310 *pcchUnescaped = needed;
1313 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1314 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1320 /*************************************************************************
1321 * UrlUnescapeW [SHLWAPI.@]
1325 HRESULT WINAPI UrlUnescapeW(
1327 LPWSTR pszUnescaped,
1328 LPDWORD pcchUnescaped,
1335 BOOL stop_unescaping = FALSE;
1337 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1338 pcchUnescaped, dwFlags);
1340 if(!pszUrl) return E_INVALIDARG;
1342 if(dwFlags & URL_UNESCAPE_INPLACE)
1346 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1350 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1351 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1352 (*src == '#' || *src == '?')) {
1353 stop_unescaping = TRUE;
1355 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1356 && stop_unescaping == FALSE) {
1358 WCHAR buf[5] = {'0','x',0};
1359 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1361 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1363 src += 2; /* Advance to end of escape */
1367 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1371 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1375 needed++; /* add one for the '\0' */
1378 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1379 *pcchUnescaped = needed;
1382 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1383 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1389 /*************************************************************************
1390 * UrlGetLocationA [SHLWAPI.@]
1392 * Get the location from a Url.
1395 * pszUrl [I] Url to get the location from
1398 * A pointer to the start of the location in pszUrl, or NULL if there is
1402 * - MSDN erroneously states that "The location is the segment of the Url
1403 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1404 * stop at '?' and always return a NULL in this case.
1405 * - MSDN also erroneously states that "If a file URL has a query string,
1406 * the returned string is the query string". In all tested cases, if the
1407 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1410 *| NULL file://aa/b/cd#hohoh
1411 *| #hohoh http://aa/b/cd#hohoh
1412 *| NULL fi://aa/b/cd#hohoh
1413 *| #hohoh ff://aa/b/cd#hohoh
1415 LPCSTR WINAPI UrlGetLocationA(
1421 base.cbSize = sizeof(base);
1422 res1 = ParseURLA(pszUrl, &base);
1423 if (res1) return NULL; /* invalid scheme */
1425 /* if scheme is file: then never return pointer */
1426 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1428 /* Look for '#' and return its addr */
1429 return strchr(base.pszSuffix, '#');
1432 /*************************************************************************
1433 * UrlGetLocationW [SHLWAPI.@]
1435 * See UrlGetLocationA.
1437 LPCWSTR WINAPI UrlGetLocationW(
1443 base.cbSize = sizeof(base);
1444 res1 = ParseURLW(pszUrl, &base);
1445 if (res1) return NULL; /* invalid scheme */
1447 /* if scheme is file: then never return pointer */
1448 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1450 /* Look for '#' and return its addr */
1451 return strchrW(base.pszSuffix, '#');
1454 /*************************************************************************
1455 * UrlCompareA [SHLWAPI.@]
1460 * pszUrl1 [I] First Url to compare
1461 * pszUrl2 [I] Url to compare to pszUrl1
1462 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1465 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1466 * than, equal to, or less than pszUrl1 respectively.
1468 INT WINAPI UrlCompareA(
1473 INT ret, len, len1, len2;
1476 return strcmp(pszUrl1, pszUrl2);
1477 len1 = strlen(pszUrl1);
1478 if (pszUrl1[len1-1] == '/') len1--;
1479 len2 = strlen(pszUrl2);
1480 if (pszUrl2[len2-1] == '/') len2--;
1482 return strncmp(pszUrl1, pszUrl2, len1);
1483 len = min(len1, len2);
1484 ret = strncmp(pszUrl1, pszUrl2, len);
1485 if (ret) return ret;
1486 if (len1 > len2) return 1;
1490 /*************************************************************************
1491 * UrlCompareW [SHLWAPI.@]
1495 INT WINAPI UrlCompareW(
1501 size_t len, len1, len2;
1504 return strcmpW(pszUrl1, pszUrl2);
1505 len1 = strlenW(pszUrl1);
1506 if (pszUrl1[len1-1] == '/') len1--;
1507 len2 = strlenW(pszUrl2);
1508 if (pszUrl2[len2-1] == '/') len2--;
1510 return strncmpW(pszUrl1, pszUrl2, len1);
1511 len = min(len1, len2);
1512 ret = strncmpW(pszUrl1, pszUrl2, len);
1513 if (ret) return ret;
1514 if (len1 > len2) return 1;
1518 /*************************************************************************
1519 * HashData [SHLWAPI.@]
1521 * Hash an input block into a variable sized digest.
1524 * lpSrc [I] Input block
1525 * nSrcLen [I] Length of lpSrc
1526 * lpDest [I] Output for hash digest
1527 * nDestLen [I] Length of lpDest
1530 * Success: TRUE. lpDest is filled with the computed hash value.
1531 * Failure: FALSE, if any argument is invalid.
1533 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1534 unsigned char *lpDest, DWORD nDestLen)
1536 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1538 if (!lpSrc || !lpDest)
1539 return E_INVALIDARG;
1541 while (destCount >= 0)
1543 lpDest[destCount] = (destCount & 0xff);
1547 while (srcCount >= 0)
1549 destCount = nDestLen - 1;
1550 while (destCount >= 0)
1552 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1560 /*************************************************************************
1561 * UrlHashA [SHLWAPI.@]
1563 * Produce a Hash from a Url.
1566 * pszUrl [I] Url to hash
1567 * lpDest [O] Destinationh for hash
1568 * nDestLen [I] Length of lpDest
1571 * Success: S_OK. lpDest is filled with the computed hash value.
1572 * Failure: E_INVALIDARG, if any argument is invalid.
1574 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1576 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1577 return E_INVALIDARG;
1579 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1583 /*************************************************************************
1584 * UrlHashW [SHLWAPI.@]
1588 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1590 char szUrl[MAX_PATH];
1592 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1594 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1595 return E_INVALIDARG;
1597 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1598 * return the same digests for the same URL.
1600 WideCharToMultiByte(CP_ACP, 0, pszUrl, -1, szUrl, MAX_PATH, NULL, NULL);
1601 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1605 /*************************************************************************
1606 * UrlApplySchemeA [SHLWAPI.@]
1608 * Apply a scheme to a Url.
1611 * pszIn [I] Url to apply scheme to
1612 * pszOut [O] Destination for modified Url
1613 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1614 * dwFlags [I] URL_ flags from "shlwapi.h"
1617 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1618 * Failure: An HRESULT error code describing the error.
1620 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1626 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1627 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1629 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1631 in = HeapAlloc(GetProcessHeap(), 0,
1632 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1633 out = in + INTERNET_MAX_URL_LENGTH;
1635 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1636 len = INTERNET_MAX_URL_LENGTH;
1638 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1640 HeapFree(GetProcessHeap(), 0, in);
1644 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1645 if (len > *pcchOut) {
1650 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1655 HeapFree(GetProcessHeap(), 0, in);
1659 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1664 DWORD value_len, data_len, dwType, i;
1665 WCHAR reg_path[MAX_PATH];
1666 WCHAR value[MAX_PATH], data[MAX_PATH];
1669 MultiByteToWideChar(CP_ACP, 0,
1670 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1671 -1, reg_path, MAX_PATH);
1672 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1674 while(value_len = data_len = MAX_PATH,
1675 RegEnumValueW(newkey, index, value, &value_len,
1676 0, &dwType, (LPVOID)data, &data_len) == 0) {
1677 TRACE("guess %d %s is %s\n",
1678 index, debugstr_w(value), debugstr_w(data));
1681 for(i=0; i<value_len; i++) {
1684 /* remember that TRUE is not-equal */
1685 j = ChrCmpIW(Wxx, Wyy);
1688 if ((i == value_len) && !j) {
1689 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1690 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1691 RegCloseKey(newkey);
1694 strcpyW(pszOut, data);
1695 strcatW(pszOut, pszIn);
1696 *pcchOut = strlenW(pszOut);
1697 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1698 RegCloseKey(newkey);
1703 RegCloseKey(newkey);
1707 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl)
1712 WCHAR file_colonW[] = {'f','i','l','e',':',0};
1713 WCHAR three_slashesW[] = {'/','/','/',0};
1714 PARSEDURLW parsed_url;
1716 parsed_url.cbSize = sizeof(parsed_url);
1717 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
1718 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
1719 needed = strlenW(pszPath);
1720 if (needed >= *pcchUrl) {
1721 *pcchUrl = needed + 1;
1730 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
1731 strcpyW(pszNewUrl, file_colonW);
1732 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
1733 strcatW(pszNewUrl, three_slashesW);
1734 strcatW(pszNewUrl, pszPath);
1735 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
1736 HeapFree(GetProcessHeap(), 0, pszNewUrl);
1740 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1743 DWORD data_len, dwType;
1744 WCHAR data[MAX_PATH];
1746 static const WCHAR prefix_keyW[] =
1747 {'S','o','f','t','w','a','r','e',
1748 '\\','M','i','c','r','o','s','o','f','t',
1749 '\\','W','i','n','d','o','w','s',
1750 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1752 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1754 /* get and prepend default */
1755 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1756 data_len = sizeof(data);
1757 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1758 RegCloseKey(newkey);
1759 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1760 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1763 strcpyW(pszOut, data);
1764 strcatW(pszOut, pszIn);
1765 *pcchOut = strlenW(pszOut);
1766 TRACE("used default %s\n", debugstr_w(pszOut));
1770 /*************************************************************************
1771 * UrlApplySchemeW [SHLWAPI.@]
1773 * See UrlApplySchemeA.
1775 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1777 PARSEDURLW in_scheme;
1781 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1782 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1784 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1786 if (dwFlags & URL_APPLY_GUESSFILE) {
1787 if (*pcchOut > 1 && ':' == pszIn[1]) {
1789 ret = URL_CreateFromPath(pszIn, pszOut, &res1);
1790 if (ret == S_OK || ret == E_POINTER){
1794 else if (ret == S_FALSE)
1801 in_scheme.cbSize = sizeof(in_scheme);
1802 /* See if the base has a scheme */
1803 res1 = ParseURLW(pszIn, &in_scheme);
1805 /* no scheme in input, need to see if we need to guess */
1806 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1807 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1812 /* If we are here, then either invalid scheme,
1813 * or no scheme and can't/failed guess.
1815 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1817 (dwFlags & URL_APPLY_DEFAULT)) {
1818 /* find and apply default scheme */
1819 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1825 /*************************************************************************
1826 * UrlIsA [SHLWAPI.@]
1828 * Determine if a Url is of a certain class.
1831 * pszUrl [I] Url to check
1832 * Urlis [I] URLIS_ constant from "shlwapi.h"
1835 * TRUE if pszUrl belongs to the class type in Urlis.
1838 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1844 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1852 base.cbSize = sizeof(base);
1853 res1 = ParseURLA(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 (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1868 "file:", 5) == CSTR_EQUAL);
1870 case URLIS_DIRECTORY:
1871 last = pszUrl + strlen(pszUrl) - 1;
1872 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1875 return PathIsURLA(pszUrl);
1877 case URLIS_NOHISTORY:
1878 case URLIS_APPLIABLE:
1879 case URLIS_HASQUERY:
1881 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1886 /*************************************************************************
1887 * UrlIsW [SHLWAPI.@]
1891 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1893 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
1898 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1906 base.cbSize = sizeof(base);
1907 res1 = ParseURLW(pszUrl, &base);
1908 if (res1) return FALSE; /* invalid scheme */
1909 switch (base.nScheme)
1911 case URL_SCHEME_MAILTO:
1912 case URL_SCHEME_SHELL:
1913 case URL_SCHEME_JAVASCRIPT:
1914 case URL_SCHEME_VBSCRIPT:
1915 case URL_SCHEME_ABOUT:
1921 return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1922 file_colon, 5) == CSTR_EQUAL);
1924 case URLIS_DIRECTORY:
1925 last = pszUrl + strlenW(pszUrl) - 1;
1926 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1929 return PathIsURLW(pszUrl);
1931 case URLIS_NOHISTORY:
1932 case URLIS_APPLIABLE:
1933 case URLIS_HASQUERY:
1935 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1940 /*************************************************************************
1941 * UrlIsNoHistoryA [SHLWAPI.@]
1943 * Determine if a Url should not be stored in the users history list.
1946 * pszUrl [I] Url to check
1949 * TRUE, if pszUrl should be excluded from the history list,
1952 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1954 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1957 /*************************************************************************
1958 * UrlIsNoHistoryW [SHLWAPI.@]
1960 * See UrlIsNoHistoryA.
1962 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1964 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1967 /*************************************************************************
1968 * UrlIsOpaqueA [SHLWAPI.@]
1970 * Determine if a Url is opaque.
1973 * pszUrl [I] Url to check
1976 * TRUE if pszUrl is opaque,
1980 * An opaque Url is one that does not start with "<protocol>://".
1982 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1984 return UrlIsA(pszUrl, URLIS_OPAQUE);
1987 /*************************************************************************
1988 * UrlIsOpaqueW [SHLWAPI.@]
1992 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1994 return UrlIsW(pszUrl, URLIS_OPAQUE);
1997 /*************************************************************************
1998 * Scans for characters of type "type" and when not matching found,
1999 * returns pointer to it and length in size.
2001 * Characters tested based on RFC 1738
2003 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
2005 static DWORD alwayszero = 0;
2014 if ( (islowerW(*start) && isalphaW(*start)) ||
2033 if ( isalphaW(*start) ||
2035 /* user/password only characters */
2040 /* *extra* characters */
2047 /* *safe* characters */
2056 } else if (*start == '%') {
2057 if (isxdigitW(*(start+1)) &&
2058 isxdigitW(*(start+2))) {
2070 if (isdigitW(*start)) {
2081 if (isalnumW(*start) ||
2094 FIXME("unknown type %d\n", type);
2095 return (LPWSTR)&alwayszero;
2097 /* TRACE("scanned %d characters next char %p<%c>\n",
2098 *size, start, *start); */
2102 /*************************************************************************
2103 * Attempt to parse URL into pieces.
2105 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2109 memset(pl, 0, sizeof(WINE_PARSE_URL));
2110 pl->pScheme = pszUrl;
2111 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2112 if (!*work || (*work != ':')) goto ErrorExit;
2114 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2115 pl->pUserName = work + 2;
2116 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2117 if (*work == ':' ) {
2118 /* parse password */
2120 pl->pPassword = work;
2121 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2123 /* what we just parsed must be the hostname and port
2124 * so reset pointers and clear then let it parse */
2125 pl->szUserName = pl->szPassword = 0;
2126 work = pl->pUserName - 1;
2127 pl->pUserName = pl->pPassword = 0;
2129 } else if (*work == '@') {
2133 } else if (!*work || (*work == '/') || (*work == '.')) {
2134 /* what was parsed was hostname, so reset pointers and let it parse */
2135 pl->szUserName = pl->szPassword = 0;
2136 work = pl->pUserName - 1;
2137 pl->pUserName = pl->pPassword = 0;
2138 } else goto ErrorExit;
2140 /* now start parsing hostname or hostnumber */
2142 pl->pHostName = work;
2143 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2148 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2151 /* see if query string */
2152 pl->pQuery = strchrW(work, '?');
2153 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2156 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2157 pl->pScheme, pl->szScheme,
2158 pl->pUserName, pl->szUserName,
2159 pl->pPassword, pl->szPassword,
2160 pl->pHostName, pl->szHostName,
2161 pl->pPort, pl->szPort,
2162 pl->pQuery, pl->szQuery);
2165 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2166 return E_INVALIDARG;
2169 /*************************************************************************
2170 * UrlGetPartA [SHLWAPI.@]
2172 * Retrieve part of a Url.
2175 * pszIn [I] Url to parse
2176 * pszOut [O] Destination for part of pszIn requested
2177 * pcchOut [I] Size of pszOut
2178 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2179 * needed size of pszOut INCLUDING '\0'.
2180 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2181 * dwFlags [I] URL_ flags from "shlwapi.h"
2184 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2185 * Failure: An HRESULT error code describing the error.
2187 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2188 DWORD dwPart, DWORD dwFlags)
2191 DWORD ret, len, len2;
2193 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2194 return E_INVALIDARG;
2196 in = HeapAlloc(GetProcessHeap(), 0,
2197 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2198 out = in + INTERNET_MAX_URL_LENGTH;
2200 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2202 len = INTERNET_MAX_URL_LENGTH;
2203 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2206 HeapFree(GetProcessHeap(), 0, in);
2210 len2 = WideCharToMultiByte(CP_ACP, 0, out, len, NULL, 0, NULL, NULL);
2211 if (len2 > *pcchOut) {
2213 HeapFree(GetProcessHeap(), 0, in);
2216 len2 = WideCharToMultiByte(CP_ACP, 0, out, len+1, pszOut, *pcchOut, NULL, NULL);
2218 HeapFree(GetProcessHeap(), 0, in);
2222 /*************************************************************************
2223 * UrlGetPartW [SHLWAPI.@]
2227 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2228 DWORD dwPart, DWORD dwFlags)
2232 DWORD scheme, size, schsize;
2233 LPCWSTR addr, schaddr;
2235 TRACE("(%s %p %p(%d) %08x %08x)\n",
2236 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2238 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2239 return E_INVALIDARG;
2243 addr = strchrW(pszIn, ':');
2245 scheme = URL_SCHEME_UNKNOWN;
2247 scheme = get_scheme_code(pszIn, addr-pszIn);
2249 ret = URL_ParseUrl(pszIn, &pl);
2252 case URL_PART_SCHEME:
2261 case URL_PART_HOSTNAME:
2263 case URL_SCHEME_FTP:
2264 case URL_SCHEME_HTTP:
2265 case URL_SCHEME_GOPHER:
2266 case URL_SCHEME_TELNET:
2267 case URL_SCHEME_FILE:
2268 case URL_SCHEME_HTTPS:
2275 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2276 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2281 if (!pl.szHostName) {
2285 addr = pl.pHostName;
2286 size = pl.szHostName;
2289 case URL_PART_USERNAME:
2290 if (!pl.szUserName) {
2294 addr = pl.pUserName;
2295 size = pl.szUserName;
2298 case URL_PART_PASSWORD:
2299 if (!pl.szPassword) {
2303 addr = pl.pPassword;
2304 size = pl.szPassword;
2316 case URL_PART_QUERY:
2327 return E_INVALIDARG;
2330 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2331 if(!pl.pScheme || !pl.szScheme) {
2335 schaddr = pl.pScheme;
2336 schsize = pl.szScheme;
2337 if (*pcchOut < schsize + size + 2) {
2338 *pcchOut = schsize + size + 2;
2341 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2342 pszOut[schsize] = ':';
2343 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2344 pszOut[schsize+1+size] = 0;
2345 *pcchOut = schsize + 1 + size;
2348 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2349 memcpy(pszOut, addr, size*sizeof(WCHAR));
2353 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2358 /*************************************************************************
2359 * PathIsURLA [SHLWAPI.@]
2361 * Check if the given path is a Url.
2364 * lpszPath [I] Path to check.
2367 * TRUE if lpszPath is a Url.
2368 * FALSE if lpszPath is NULL or not a Url.
2370 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2375 TRACE("%s\n", debugstr_a(lpstrPath));
2377 if (!lpstrPath || !*lpstrPath) return FALSE;
2380 base.cbSize = sizeof(base);
2381 hres = ParseURLA(lpstrPath, &base);
2382 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2385 /*************************************************************************
2386 * PathIsURLW [SHLWAPI.@]
2390 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2395 TRACE("%s\n", debugstr_w(lpstrPath));
2397 if (!lpstrPath || !*lpstrPath) return FALSE;
2400 base.cbSize = sizeof(base);
2401 hres = ParseURLW(lpstrPath, &base);
2402 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2405 /*************************************************************************
2406 * UrlCreateFromPathA [SHLWAPI.@]
2408 * See UrlCreateFromPathW
2410 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2412 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2414 UNICODE_STRING pathW;
2416 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2418 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2419 return E_INVALIDARG;
2420 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2421 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2422 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2424 if(ret == S_OK || ret == S_FALSE) {
2425 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2426 if(*pcchUrl > lenA) {
2427 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2431 *pcchUrl = lenA + 1;
2435 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2436 RtlFreeUnicodeString(&pathW);
2440 /*************************************************************************
2441 * UrlCreateFromPathW [SHLWAPI.@]
2443 * Create a Url from a file path.
2446 * pszPath [I] Path to convert
2447 * pszUrl [O] Destination for the converted Url
2448 * pcchUrl [I/O] Length of pszUrl
2449 * dwReserved [I] Reserved, must be 0
2452 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2453 * Failure: An HRESULT error code.
2455 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2459 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2461 /* Validate arguments */
2462 if (dwReserved != 0)
2463 return E_INVALIDARG;
2464 if (!pszUrl || !pcchUrl)
2465 return E_INVALIDARG;
2467 ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl);
2470 strcpyW(pszUrl, pszPath);
2475 /*************************************************************************
2476 * SHAutoComplete [SHLWAPI.@]
2478 * Enable auto-completion for an edit control.
2481 * hwndEdit [I] Handle of control to enable auto-completion for
2482 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2485 * Success: S_OK. Auto-completion is enabled for the control.
2486 * Failure: An HRESULT error code indicating the error.
2488 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2494 /*************************************************************************
2495 * MLBuildResURLA [SHLWAPI.405]
2497 * Create a Url pointing to a resource in a module.
2500 * lpszLibName [I] Name of the module containing the resource
2501 * hMod [I] Callers module handle
2502 * dwFlags [I] Undocumented flags for loading the module
2503 * lpszRes [I] Resource name
2504 * lpszDest [O] Destination for resulting Url
2505 * dwDestLen [I] Length of lpszDest
2508 * Success: S_OK. lpszDest contains the resource Url.
2509 * Failure: E_INVALIDARG, if any argument is invalid, or
2510 * E_FAIL if dwDestLen is too small.
2512 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2513 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2515 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2519 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2522 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2524 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2525 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2527 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2528 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2529 if (SUCCEEDED(hRet) && lpszDest)
2530 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, NULL, NULL);
2535 /*************************************************************************
2536 * MLBuildResURLA [SHLWAPI.406]
2538 * See MLBuildResURLA.
2540 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2541 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2543 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2544 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2545 HRESULT hRet = E_FAIL;
2547 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2548 debugstr_w(lpszRes), lpszDest, dwDestLen);
2550 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2551 !lpszDest || (dwFlags && dwFlags != 2))
2552 return E_INVALIDARG;
2554 if (dwDestLen >= szResLen + 1)
2556 dwDestLen -= (szResLen + 1);
2557 memcpy(lpszDest, szRes, sizeof(szRes));
2559 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2563 WCHAR szBuff[MAX_PATH];
2566 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2567 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2569 DWORD dwPathLen = strlenW(szBuff) + 1;
2571 if (dwDestLen >= dwPathLen)
2575 dwDestLen -= dwPathLen;
2576 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2578 dwResLen = strlenW(lpszRes) + 1;
2579 if (dwDestLen >= dwResLen + 1)
2581 lpszDest[szResLen + dwPathLen-1] = '/';
2582 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2587 MLFreeLibrary(hMod);
2593 /***********************************************************************
2594 * UrlFixupW [SHLWAPI.462]
2596 * Checks the scheme part of a URL and attempts to correct misspellings.
2599 * lpszUrl [I] Pointer to the URL to be corrected
2600 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2601 * dwMaxChars [I] Maximum size of corrected URL
2604 * success: S_OK if URL corrected or already correct
2605 * failure: S_FALSE if unable to correct / COM error code if other error
2608 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2612 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2617 srcLen = lstrlenW(url) + 1;
2619 /* For now just copy the URL directly */
2620 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);