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(0, 0, canonical, -1, pszCanonicalized,
270 *pcchCanonicalized+1, 0, 0);
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(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
631 MultiByteToWideChar(0, 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(0, 0, combined, len, 0, 0, 0, 0);
642 if (len2 > *pcchCombined) {
643 *pcchCombined = len2;
644 HeapFree(GetProcessHeap(), 0, base);
647 WideCharToMultiByte(0, 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 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 /* mk is a special case */
707 if(base.nScheme == URL_SCHEME_MK) {
708 static const WCHAR wsz[] = {':',':',0};
710 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
715 delta = ptr-base.pszSuffix;
716 base.cchProtocol += delta;
717 base.pszSuffix += delta;
718 base.cchSuffix -= delta;
721 /* get size of location field (if it exists) */
722 work = (LPWSTR)base.pszSuffix;
724 if (*work++ == '/') {
725 if (*work++ == '/') {
726 /* At this point have start of location and
727 * it ends at next '/' or end of string.
729 while(*work && (*work != '/')) work++;
730 sizeloc = (DWORD)(work - base.pszSuffix);
735 /* If there is a '#' and the characters immediately preceding it are
736 * ".htm[l]", then begin looking for the last leaf starting from
737 * the '#'. Otherwise the '#' is not meaningful and just start
738 * looking from the end. */
739 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
740 const WCHAR htmlW[] = {'.','h','t','m','l',0};
741 const int len_htmlW = 5;
742 const WCHAR htmW[] = {'.','h','t','m',0};
743 const int len_htmW = 4;
745 if (base.nScheme == URL_SCHEME_HTTP || base.nScheme == URL_SCHEME_HTTPS)
746 manual_search = TRUE;
747 else if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
749 if (strncmpiW(work, htmW, len_htmW) == 0)
750 manual_search = TRUE;
754 if (!manual_search &&
755 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
757 if (strncmpiW(work, htmlW, len_htmlW) == 0)
758 manual_search = TRUE;
764 /* search backwards starting from the current position */
765 while (*work != '/' && work > base.pszSuffix + sizeloc)
767 base.cchSuffix = work - base.pszSuffix + 1;
769 /* search backwards starting from the end of the string */
770 work = strrchrW((base.pszSuffix+sizeloc), '/');
772 len = (DWORD)(work - base.pszSuffix + 1);
773 base.cchSuffix = len;
775 base.cchSuffix = sizeloc;
780 * .pszSuffix points to location (starting with '//')
781 * .cchSuffix length of location (above) and rest less the last
783 * sizeloc length of location (above) up to but not including
787 res2 = ParseURLW(mrelative, &relative);
789 /* no scheme in pszRelative */
790 TRACE("no scheme detected in Relative\n");
791 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
792 relative.cchSuffix = strlenW(mrelative);
793 if (*pszRelative == ':') {
794 /* case that is either left alone or uses pszBase */
795 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
802 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
803 /* case that becomes "file:///" */
804 strcpyW(preliminary, myfilestr);
808 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
809 /* pszRelative has location and rest */
813 if (*mrelative == '/') {
814 /* case where pszRelative is root to location */
818 if (*mrelative == '#') {
819 if(!(work = strchrW(base.pszSuffix+base.cchSuffix, '#')))
820 work = (LPWSTR)base.pszSuffix + strlenW(base.pszSuffix);
822 memcpy(preliminary, base.pszProtocol, (work-base.pszProtocol)*sizeof(WCHAR));
823 preliminary[work-base.pszProtocol] = '\0';
827 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
831 /* handle cases where pszRelative has scheme */
832 if ((base.cchProtocol == relative.cchProtocol) &&
833 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
835 /* since the schemes are the same */
836 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
837 /* case where pszRelative replaces location and following */
841 if (*relative.pszSuffix == '/') {
842 /* case where pszRelative is root to location */
846 /* replace either just location if base's location starts with a
847 * slash or otherwise everything */
848 process_case = (*base.pszSuffix == '/') ? 5 : 1;
851 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
852 /* case where pszRelative replaces scheme, location,
853 * and following and handles PLUGGABLE
860 } while(FALSE); /* a little trick to allow easy exit from nested if's */
863 switch (process_case) {
866 * Return pszRelative appended to what ever is in pszCombined,
867 * (which may the string "file:///"
869 strcatW(preliminary, mrelative);
872 case 2: /* case where pszRelative replaces scheme, and location */
873 strcpyW(preliminary, mrelative);
877 * Return the pszBase scheme with pszRelative. Basically
878 * keeps the scheme and replaces the domain and following.
880 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
881 work = preliminary + base.cchProtocol + 1;
882 strcpyW(work, relative.pszSuffix);
886 * Return the pszBase scheme and location but everything
887 * after the location is pszRelative. (Replace document
890 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
891 work = preliminary + base.cchProtocol + 1 + sizeloc;
892 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
894 strcpyW(work, relative.pszSuffix);
898 * Return the pszBase without its document (if any) and
899 * append pszRelative after its scheme.
901 memcpy(preliminary, base.pszProtocol,
902 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
903 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
906 strcpyW(work, relative.pszSuffix);
910 FIXME("How did we get here????? process_case=%d\n", process_case);
915 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
916 if(*pcchCombined == 0)
918 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
919 if(SUCCEEDED(ret) && pszCombined) {
920 lstrcpyW(pszCombined, mrelative);
922 TRACE("return-%d len=%d, %s\n",
923 process_case, *pcchCombined, debugstr_w(pszCombined));
925 HeapFree(GetProcessHeap(), 0, preliminary);
929 /*************************************************************************
930 * UrlEscapeA [SHLWAPI.@]
933 HRESULT WINAPI UrlEscapeA(
939 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
940 WCHAR *escapedW = bufW;
943 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
945 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
948 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
950 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
951 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
952 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
955 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
956 if(*pcchEscaped > lenA) {
957 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
958 pszEscaped[lenA] = 0;
961 *pcchEscaped = lenA + 1;
965 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
966 RtlFreeUnicodeString(&urlW);
970 #define WINE_URL_BASH_AS_SLASH 0x01
971 #define WINE_URL_COLLAPSE_SLASHES 0x02
972 #define WINE_URL_ESCAPE_SLASH 0x04
973 #define WINE_URL_ESCAPE_HASH 0x08
974 #define WINE_URL_ESCAPE_QUESTION 0x10
975 #define WINE_URL_STOP_ON_HASH 0x20
976 #define WINE_URL_STOP_ON_QUESTION 0x40
978 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
984 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
991 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
994 if (ch <= 31 || ch >= 127)
1015 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
1019 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
1023 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
1033 /*************************************************************************
1034 * UrlEscapeW [SHLWAPI.@]
1036 * Converts unsafe characters in a Url into escape sequences.
1039 * pszUrl [I] Url to modify
1040 * pszEscaped [O] Destination for modified Url
1041 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1042 * dwFlags [I] URL_ flags from "shlwapi.h"
1045 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1046 * contains its length.
1047 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1048 * pcchEscaped is set to the required length.
1050 * Converts unsafe characters into their escape sequences.
1053 * - By default this function stops converting at the first '?' or
1055 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1056 * converted, but the conversion continues past a '?' or '#'.
1057 * - Note that this function did not work well (or at all) in shlwapi version 4.
1060 * Only the following flags are implemented:
1061 *| URL_ESCAPE_SPACES_ONLY
1062 *| URL_DONT_ESCAPE_EXTRA_INFO
1063 *| URL_ESCAPE_SEGMENT_ONLY
1064 *| URL_ESCAPE_PERCENT
1066 HRESULT WINAPI UrlEscapeW(
1069 LPDWORD pcchEscaped,
1073 DWORD needed = 0, ret;
1074 BOOL stop_escaping = FALSE;
1075 WCHAR next[5], *dst = pszEscaped, *dst_ptr = NULL;
1077 PARSEDURLW parsed_url;
1080 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1082 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1083 pszEscaped, pcchEscaped, dwFlags);
1085 if(!pszUrl || !pcchEscaped)
1086 return E_INVALIDARG;
1088 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1089 URL_ESCAPE_SEGMENT_ONLY |
1090 URL_DONT_ESCAPE_EXTRA_INFO |
1091 URL_ESCAPE_PERCENT))
1092 FIXME("Unimplemented flags: %08x\n", dwFlags);
1094 if(pszUrl == pszEscaped) {
1095 dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1097 return E_OUTOFMEMORY;
1101 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1102 /* if SPACES_ONLY specified, reset the other controls */
1103 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1104 URL_ESCAPE_PERCENT |
1105 URL_ESCAPE_SEGMENT_ONLY);
1108 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1109 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1113 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1114 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1116 parsed_url.cbSize = sizeof(parsed_url);
1117 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1118 parsed_url.nScheme = URL_SCHEME_INVALID;
1120 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1122 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1123 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1125 switch(parsed_url.nScheme) {
1126 case URL_SCHEME_FILE:
1127 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1128 int_flags &= ~WINE_URL_STOP_ON_HASH;
1131 case URL_SCHEME_HTTP:
1132 case URL_SCHEME_HTTPS:
1133 int_flags |= WINE_URL_BASH_AS_SLASH;
1134 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1135 int_flags |= WINE_URL_ESCAPE_SLASH;
1138 case URL_SCHEME_MAILTO:
1139 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1140 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1143 case URL_SCHEME_INVALID:
1146 case URL_SCHEME_FTP:
1148 if(parsed_url.pszSuffix[0] != '/')
1149 int_flags |= WINE_URL_ESCAPE_SLASH;
1154 for(src = pszUrl; *src; ) {
1158 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1159 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1160 while(cur == '/' || cur == '\\') {
1164 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1165 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1166 src += localhost_len + 1;
1173 next[0] = next[1] = next[2] = '/';
1180 next[0] = next[1] = '/';
1187 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1188 stop_escaping = TRUE;
1190 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1191 stop_escaping = TRUE;
1193 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1195 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1197 next[1] = hexDigits[(cur >> 4) & 0xf];
1198 next[2] = hexDigits[cur & 0xf];
1207 if(needed + len <= *pcchEscaped) {
1208 memcpy(dst, next, len*sizeof(WCHAR));
1214 if(needed < *pcchEscaped) {
1216 if(pszUrl == pszEscaped)
1217 memcpy(pszEscaped, dst-needed, (needed+1)*sizeof(WCHAR));
1221 needed++; /* add one for the '\0' */
1224 *pcchEscaped = needed;
1226 if(pszUrl == pszEscaped)
1227 HeapFree(GetProcessHeap(), 0, dst_ptr);
1232 /*************************************************************************
1233 * UrlUnescapeA [SHLWAPI.@]
1235 * Converts Url escape sequences back to ordinary characters.
1238 * pszUrl [I/O] Url to convert
1239 * pszUnescaped [O] Destination for converted Url
1240 * pcchUnescaped [I/O] Size of output string
1241 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1244 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1245 * dwFlags includes URL_ESCAPE_INPLACE.
1246 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1247 * this case pcchUnescaped is set to the size required.
1249 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1250 * the first occurrence of either a '?' or '#' character.
1252 HRESULT WINAPI UrlUnescapeA(
1255 LPDWORD pcchUnescaped,
1262 BOOL stop_unescaping = FALSE;
1264 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1265 pcchUnescaped, dwFlags);
1267 if (!pszUrl) return E_INVALIDARG;
1269 if(dwFlags & URL_UNESCAPE_INPLACE)
1273 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1277 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1278 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1279 (*src == '#' || *src == '?')) {
1280 stop_unescaping = TRUE;
1282 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1283 && stop_unescaping == FALSE) {
1286 memcpy(buf, src + 1, 2);
1288 ih = strtol(buf, NULL, 16);
1290 src += 2; /* Advance to end of escape */
1294 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1298 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1302 needed++; /* add one for the '\0' */
1305 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1306 *pcchUnescaped = needed;
1309 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1310 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1316 /*************************************************************************
1317 * UrlUnescapeW [SHLWAPI.@]
1321 HRESULT WINAPI UrlUnescapeW(
1323 LPWSTR pszUnescaped,
1324 LPDWORD pcchUnescaped,
1331 BOOL stop_unescaping = FALSE;
1333 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1334 pcchUnescaped, dwFlags);
1336 if(!pszUrl) return E_INVALIDARG;
1338 if(dwFlags & URL_UNESCAPE_INPLACE)
1342 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1346 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1347 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1348 (*src == '#' || *src == '?')) {
1349 stop_unescaping = TRUE;
1351 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1352 && stop_unescaping == FALSE) {
1354 WCHAR buf[5] = {'0','x',0};
1355 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1357 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1359 src += 2; /* Advance to end of escape */
1363 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1367 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1371 needed++; /* add one for the '\0' */
1374 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1375 *pcchUnescaped = needed;
1378 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1379 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1385 /*************************************************************************
1386 * UrlGetLocationA [SHLWAPI.@]
1388 * Get the location from a Url.
1391 * pszUrl [I] Url to get the location from
1394 * A pointer to the start of the location in pszUrl, or NULL if there is
1398 * - MSDN erroneously states that "The location is the segment of the Url
1399 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1400 * stop at '?' and always return a NULL in this case.
1401 * - MSDN also erroneously states that "If a file URL has a query string,
1402 * the returned string is the query string". In all tested cases, if the
1403 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1406 *| NULL file://aa/b/cd#hohoh
1407 *| #hohoh http://aa/b/cd#hohoh
1408 *| NULL fi://aa/b/cd#hohoh
1409 *| #hohoh ff://aa/b/cd#hohoh
1411 LPCSTR WINAPI UrlGetLocationA(
1417 base.cbSize = sizeof(base);
1418 res1 = ParseURLA(pszUrl, &base);
1419 if (res1) return NULL; /* invalid scheme */
1421 /* if scheme is file: then never return pointer */
1422 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1424 /* Look for '#' and return its addr */
1425 return strchr(base.pszSuffix, '#');
1428 /*************************************************************************
1429 * UrlGetLocationW [SHLWAPI.@]
1431 * See UrlGetLocationA.
1433 LPCWSTR WINAPI UrlGetLocationW(
1439 base.cbSize = sizeof(base);
1440 res1 = ParseURLW(pszUrl, &base);
1441 if (res1) return NULL; /* invalid scheme */
1443 /* if scheme is file: then never return pointer */
1444 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1446 /* Look for '#' and return its addr */
1447 return strchrW(base.pszSuffix, '#');
1450 /*************************************************************************
1451 * UrlCompareA [SHLWAPI.@]
1456 * pszUrl1 [I] First Url to compare
1457 * pszUrl2 [I] Url to compare to pszUrl1
1458 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1461 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1462 * than, equal to, or less than pszUrl1 respectively.
1464 INT WINAPI UrlCompareA(
1469 INT ret, len, len1, len2;
1472 return strcmp(pszUrl1, pszUrl2);
1473 len1 = strlen(pszUrl1);
1474 if (pszUrl1[len1-1] == '/') len1--;
1475 len2 = strlen(pszUrl2);
1476 if (pszUrl2[len2-1] == '/') len2--;
1478 return strncmp(pszUrl1, pszUrl2, len1);
1479 len = min(len1, len2);
1480 ret = strncmp(pszUrl1, pszUrl2, len);
1481 if (ret) return ret;
1482 if (len1 > len2) return 1;
1486 /*************************************************************************
1487 * UrlCompareW [SHLWAPI.@]
1491 INT WINAPI UrlCompareW(
1497 size_t len, len1, len2;
1500 return strcmpW(pszUrl1, pszUrl2);
1501 len1 = strlenW(pszUrl1);
1502 if (pszUrl1[len1-1] == '/') len1--;
1503 len2 = strlenW(pszUrl2);
1504 if (pszUrl2[len2-1] == '/') len2--;
1506 return strncmpW(pszUrl1, pszUrl2, len1);
1507 len = min(len1, len2);
1508 ret = strncmpW(pszUrl1, pszUrl2, len);
1509 if (ret) return ret;
1510 if (len1 > len2) return 1;
1514 /*************************************************************************
1515 * HashData [SHLWAPI.@]
1517 * Hash an input block into a variable sized digest.
1520 * lpSrc [I] Input block
1521 * nSrcLen [I] Length of lpSrc
1522 * lpDest [I] Output for hash digest
1523 * nDestLen [I] Length of lpDest
1526 * Success: TRUE. lpDest is filled with the computed hash value.
1527 * Failure: FALSE, if any argument is invalid.
1529 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1530 unsigned char *lpDest, DWORD nDestLen)
1532 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1534 if (!lpSrc || !lpDest)
1535 return E_INVALIDARG;
1537 while (destCount >= 0)
1539 lpDest[destCount] = (destCount & 0xff);
1543 while (srcCount >= 0)
1545 destCount = nDestLen - 1;
1546 while (destCount >= 0)
1548 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1556 /*************************************************************************
1557 * UrlHashA [SHLWAPI.@]
1559 * Produce a Hash from a Url.
1562 * pszUrl [I] Url to hash
1563 * lpDest [O] Destinationh for hash
1564 * nDestLen [I] Length of lpDest
1567 * Success: S_OK. lpDest is filled with the computed hash value.
1568 * Failure: E_INVALIDARG, if any argument is invalid.
1570 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1572 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1573 return E_INVALIDARG;
1575 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1579 /*************************************************************************
1580 * UrlHashW [SHLWAPI.@]
1584 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1586 char szUrl[MAX_PATH];
1588 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1590 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1591 return E_INVALIDARG;
1593 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1594 * return the same digests for the same URL.
1596 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1597 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1601 /*************************************************************************
1602 * UrlApplySchemeA [SHLWAPI.@]
1604 * Apply a scheme to a Url.
1607 * pszIn [I] Url to apply scheme to
1608 * pszOut [O] Destination for modified Url
1609 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1610 * dwFlags [I] URL_ flags from "shlwapi.h"
1613 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1614 * Failure: An HRESULT error code describing the error.
1616 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1622 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1623 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1625 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1627 in = HeapAlloc(GetProcessHeap(), 0,
1628 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1629 out = in + INTERNET_MAX_URL_LENGTH;
1631 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1632 len = INTERNET_MAX_URL_LENGTH;
1634 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1636 HeapFree(GetProcessHeap(), 0, in);
1640 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1641 if (len > *pcchOut) {
1646 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1651 HeapFree(GetProcessHeap(), 0, in);
1655 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1660 DWORD value_len, data_len, dwType, i;
1661 WCHAR reg_path[MAX_PATH];
1662 WCHAR value[MAX_PATH], data[MAX_PATH];
1665 MultiByteToWideChar(0, 0,
1666 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1667 -1, reg_path, MAX_PATH);
1668 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1670 while(value_len = data_len = MAX_PATH,
1671 RegEnumValueW(newkey, index, value, &value_len,
1672 0, &dwType, (LPVOID)data, &data_len) == 0) {
1673 TRACE("guess %d %s is %s\n",
1674 index, debugstr_w(value), debugstr_w(data));
1677 for(i=0; i<value_len; i++) {
1680 /* remember that TRUE is not-equal */
1681 j = ChrCmpIW(Wxx, Wyy);
1684 if ((i == value_len) && !j) {
1685 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1686 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1687 RegCloseKey(newkey);
1690 strcpyW(pszOut, data);
1691 strcatW(pszOut, pszIn);
1692 *pcchOut = strlenW(pszOut);
1693 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1694 RegCloseKey(newkey);
1699 RegCloseKey(newkey);
1703 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl)
1708 WCHAR file_colonW[] = {'f','i','l','e',':',0};
1709 WCHAR three_slashesW[] = {'/','/','/',0};
1710 PARSEDURLW parsed_url;
1712 parsed_url.cbSize = sizeof(parsed_url);
1713 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
1714 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
1715 needed = strlenW(pszPath);
1716 if (needed >= *pcchUrl) {
1717 *pcchUrl = needed + 1;
1726 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
1727 strcpyW(pszNewUrl, file_colonW);
1728 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
1729 strcatW(pszNewUrl, three_slashesW);
1730 strcatW(pszNewUrl, pszPath);
1731 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
1732 HeapFree(GetProcessHeap(), 0, pszNewUrl);
1736 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1739 DWORD data_len, dwType;
1740 WCHAR data[MAX_PATH];
1742 static const WCHAR prefix_keyW[] =
1743 {'S','o','f','t','w','a','r','e',
1744 '\\','M','i','c','r','o','s','o','f','t',
1745 '\\','W','i','n','d','o','w','s',
1746 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1748 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1750 /* get and prepend default */
1751 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1752 data_len = sizeof(data);
1753 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1754 RegCloseKey(newkey);
1755 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1756 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1759 strcpyW(pszOut, data);
1760 strcatW(pszOut, pszIn);
1761 *pcchOut = strlenW(pszOut);
1762 TRACE("used default %s\n", debugstr_w(pszOut));
1766 /*************************************************************************
1767 * UrlApplySchemeW [SHLWAPI.@]
1769 * See UrlApplySchemeA.
1771 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1773 PARSEDURLW in_scheme;
1777 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1778 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1780 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1782 if (dwFlags & URL_APPLY_GUESSFILE) {
1783 if (*pcchOut > 1 && ':' == pszIn[1]) {
1785 ret = URL_CreateFromPath(pszIn, pszOut, &res1);
1786 if (ret == S_OK || ret == E_POINTER){
1790 else if (ret == S_FALSE)
1797 in_scheme.cbSize = sizeof(in_scheme);
1798 /* See if the base has a scheme */
1799 res1 = ParseURLW(pszIn, &in_scheme);
1801 /* no scheme in input, need to see if we need to guess */
1802 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1803 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1808 /* If we are here, then either invalid scheme,
1809 * or no scheme and can't/failed guess.
1811 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1813 (dwFlags & URL_APPLY_DEFAULT)) {
1814 /* find and apply default scheme */
1815 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1821 /*************************************************************************
1822 * UrlIsA [SHLWAPI.@]
1824 * Determine if a Url is of a certain class.
1827 * pszUrl [I] Url to check
1828 * Urlis [I] URLIS_ constant from "shlwapi.h"
1831 * TRUE if pszUrl belongs to the class type in Urlis.
1834 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1840 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1848 base.cbSize = sizeof(base);
1849 res1 = ParseURLA(pszUrl, &base);
1850 if (res1) return FALSE; /* invalid scheme */
1851 switch (base.nScheme)
1853 case URL_SCHEME_MAILTO:
1854 case URL_SCHEME_SHELL:
1855 case URL_SCHEME_JAVASCRIPT:
1856 case URL_SCHEME_VBSCRIPT:
1857 case URL_SCHEME_ABOUT:
1863 return !StrCmpNA("file:", pszUrl, 5);
1865 case URLIS_DIRECTORY:
1866 last = pszUrl + strlen(pszUrl) - 1;
1867 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1870 return PathIsURLA(pszUrl);
1872 case URLIS_NOHISTORY:
1873 case URLIS_APPLIABLE:
1874 case URLIS_HASQUERY:
1876 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1881 /*************************************************************************
1882 * UrlIsW [SHLWAPI.@]
1886 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1888 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1893 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1901 base.cbSize = sizeof(base);
1902 res1 = ParseURLW(pszUrl, &base);
1903 if (res1) return FALSE; /* invalid scheme */
1904 switch (base.nScheme)
1906 case URL_SCHEME_MAILTO:
1907 case URL_SCHEME_SHELL:
1908 case URL_SCHEME_JAVASCRIPT:
1909 case URL_SCHEME_VBSCRIPT:
1910 case URL_SCHEME_ABOUT:
1916 return !strncmpW(stemp, pszUrl, 5);
1918 case URLIS_DIRECTORY:
1919 last = pszUrl + strlenW(pszUrl) - 1;
1920 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1923 return PathIsURLW(pszUrl);
1925 case URLIS_NOHISTORY:
1926 case URLIS_APPLIABLE:
1927 case URLIS_HASQUERY:
1929 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1934 /*************************************************************************
1935 * UrlIsNoHistoryA [SHLWAPI.@]
1937 * Determine if a Url should not be stored in the users history list.
1940 * pszUrl [I] Url to check
1943 * TRUE, if pszUrl should be excluded from the history list,
1946 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1948 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1951 /*************************************************************************
1952 * UrlIsNoHistoryW [SHLWAPI.@]
1954 * See UrlIsNoHistoryA.
1956 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1958 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1961 /*************************************************************************
1962 * UrlIsOpaqueA [SHLWAPI.@]
1964 * Determine if a Url is opaque.
1967 * pszUrl [I] Url to check
1970 * TRUE if pszUrl is opaque,
1974 * An opaque Url is one that does not start with "<protocol>://".
1976 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1978 return UrlIsA(pszUrl, URLIS_OPAQUE);
1981 /*************************************************************************
1982 * UrlIsOpaqueW [SHLWAPI.@]
1986 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1988 return UrlIsW(pszUrl, URLIS_OPAQUE);
1991 /*************************************************************************
1992 * Scans for characters of type "type" and when not matching found,
1993 * returns pointer to it and length in size.
1995 * Characters tested based on RFC 1738
1997 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1999 static DWORD alwayszero = 0;
2008 if ( (islowerW(*start) && isalphaW(*start)) ||
2027 if ( isalphaW(*start) ||
2029 /* user/password only characters */
2034 /* *extra* characters */
2041 /* *safe* characters */
2050 } else if (*start == '%') {
2051 if (isxdigitW(*(start+1)) &&
2052 isxdigitW(*(start+2))) {
2064 if (isdigitW(*start)) {
2075 if (isalnumW(*start) ||
2088 FIXME("unknown type %d\n", type);
2089 return (LPWSTR)&alwayszero;
2091 /* TRACE("scanned %d characters next char %p<%c>\n",
2092 *size, start, *start); */
2096 /*************************************************************************
2097 * Attempt to parse URL into pieces.
2099 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2103 memset(pl, 0, sizeof(WINE_PARSE_URL));
2104 pl->pScheme = pszUrl;
2105 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2106 if (!*work || (*work != ':')) goto ErrorExit;
2108 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2109 pl->pUserName = work + 2;
2110 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2111 if (*work == ':' ) {
2112 /* parse password */
2114 pl->pPassword = work;
2115 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2117 /* what we just parsed must be the hostname and port
2118 * so reset pointers and clear then let it parse */
2119 pl->szUserName = pl->szPassword = 0;
2120 work = pl->pUserName - 1;
2121 pl->pUserName = pl->pPassword = 0;
2123 } else if (*work == '@') {
2127 } else if (!*work || (*work == '/') || (*work == '.')) {
2128 /* what was parsed was hostname, so reset pointers and let it parse */
2129 pl->szUserName = pl->szPassword = 0;
2130 work = pl->pUserName - 1;
2131 pl->pUserName = pl->pPassword = 0;
2132 } else goto ErrorExit;
2134 /* now start parsing hostname or hostnumber */
2136 pl->pHostName = work;
2137 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2142 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2145 /* see if query string */
2146 pl->pQuery = strchrW(work, '?');
2147 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2150 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2151 pl->pScheme, pl->szScheme,
2152 pl->pUserName, pl->szUserName,
2153 pl->pPassword, pl->szPassword,
2154 pl->pHostName, pl->szHostName,
2155 pl->pPort, pl->szPort,
2156 pl->pQuery, pl->szQuery);
2159 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2160 return E_INVALIDARG;
2163 /*************************************************************************
2164 * UrlGetPartA [SHLWAPI.@]
2166 * Retrieve part of a Url.
2169 * pszIn [I] Url to parse
2170 * pszOut [O] Destination for part of pszIn requested
2171 * pcchOut [I] Size of pszOut
2172 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2173 * needed size of pszOut INCLUDING '\0'.
2174 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2175 * dwFlags [I] URL_ flags from "shlwapi.h"
2178 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2179 * Failure: An HRESULT error code describing the error.
2181 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2182 DWORD dwPart, DWORD dwFlags)
2185 DWORD ret, len, len2;
2187 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2188 return E_INVALIDARG;
2190 in = HeapAlloc(GetProcessHeap(), 0,
2191 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2192 out = in + INTERNET_MAX_URL_LENGTH;
2194 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2196 len = INTERNET_MAX_URL_LENGTH;
2197 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2200 HeapFree(GetProcessHeap(), 0, in);
2204 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2205 if (len2 > *pcchOut) {
2207 HeapFree(GetProcessHeap(), 0, in);
2210 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2212 HeapFree(GetProcessHeap(), 0, in);
2216 /*************************************************************************
2217 * UrlGetPartW [SHLWAPI.@]
2221 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2222 DWORD dwPart, DWORD dwFlags)
2226 DWORD scheme, size, schsize;
2227 LPCWSTR addr, schaddr;
2229 TRACE("(%s %p %p(%d) %08x %08x)\n",
2230 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2232 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2233 return E_INVALIDARG;
2237 addr = strchrW(pszIn, ':');
2239 scheme = URL_SCHEME_UNKNOWN;
2241 scheme = get_scheme_code(pszIn, addr-pszIn);
2243 ret = URL_ParseUrl(pszIn, &pl);
2246 case URL_PART_SCHEME:
2255 case URL_PART_HOSTNAME:
2257 case URL_SCHEME_FTP:
2258 case URL_SCHEME_HTTP:
2259 case URL_SCHEME_GOPHER:
2260 case URL_SCHEME_TELNET:
2261 case URL_SCHEME_FILE:
2262 case URL_SCHEME_HTTPS:
2269 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2270 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2275 if (!pl.szHostName) {
2279 addr = pl.pHostName;
2280 size = pl.szHostName;
2283 case URL_PART_USERNAME:
2284 if (!pl.szUserName) {
2288 addr = pl.pUserName;
2289 size = pl.szUserName;
2292 case URL_PART_PASSWORD:
2293 if (!pl.szPassword) {
2297 addr = pl.pPassword;
2298 size = pl.szPassword;
2310 case URL_PART_QUERY:
2321 return E_INVALIDARG;
2324 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2325 if(!pl.pScheme || !pl.szScheme) {
2329 schaddr = pl.pScheme;
2330 schsize = pl.szScheme;
2331 if (*pcchOut < schsize + size + 2) {
2332 *pcchOut = schsize + size + 2;
2335 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2336 pszOut[schsize] = ':';
2337 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2338 pszOut[schsize+1+size] = 0;
2339 *pcchOut = schsize + 1 + size;
2342 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2343 memcpy(pszOut, addr, size*sizeof(WCHAR));
2347 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2352 /*************************************************************************
2353 * PathIsURLA [SHLWAPI.@]
2355 * Check if the given path is a Url.
2358 * lpszPath [I] Path to check.
2361 * TRUE if lpszPath is a Url.
2362 * FALSE if lpszPath is NULL or not a Url.
2364 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2369 TRACE("%s\n", debugstr_a(lpstrPath));
2371 if (!lpstrPath || !*lpstrPath) return FALSE;
2374 base.cbSize = sizeof(base);
2375 hres = ParseURLA(lpstrPath, &base);
2376 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2379 /*************************************************************************
2380 * PathIsURLW [SHLWAPI.@]
2384 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2389 TRACE("%s\n", debugstr_w(lpstrPath));
2391 if (!lpstrPath || !*lpstrPath) return FALSE;
2394 base.cbSize = sizeof(base);
2395 hres = ParseURLW(lpstrPath, &base);
2396 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2399 /*************************************************************************
2400 * UrlCreateFromPathA [SHLWAPI.@]
2402 * See UrlCreateFromPathW
2404 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2406 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2408 UNICODE_STRING pathW;
2410 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2412 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2413 return E_INVALIDARG;
2414 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2415 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2416 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2418 if(ret == S_OK || ret == S_FALSE) {
2419 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2420 if(*pcchUrl > lenA) {
2421 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2425 *pcchUrl = lenA + 1;
2429 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2430 RtlFreeUnicodeString(&pathW);
2434 /*************************************************************************
2435 * UrlCreateFromPathW [SHLWAPI.@]
2437 * Create a Url from a file path.
2440 * pszPath [I] Path to convert
2441 * pszUrl [O] Destination for the converted Url
2442 * pcchUrl [I/O] Length of pszUrl
2443 * dwReserved [I] Reserved, must be 0
2446 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2447 * Failure: An HRESULT error code.
2449 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2453 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2455 /* Validate arguments */
2456 if (dwReserved != 0)
2457 return E_INVALIDARG;
2458 if (!pszUrl || !pcchUrl)
2459 return E_INVALIDARG;
2461 ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl);
2464 strcpyW(pszUrl, pszPath);
2469 /*************************************************************************
2470 * SHAutoComplete [SHLWAPI.@]
2472 * Enable auto-completion for an edit control.
2475 * hwndEdit [I] Handle of control to enable auto-completion for
2476 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2479 * Success: S_OK. Auto-completion is enabled for the control.
2480 * Failure: An HRESULT error code indicating the error.
2482 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2488 /*************************************************************************
2489 * MLBuildResURLA [SHLWAPI.405]
2491 * Create a Url pointing to a resource in a module.
2494 * lpszLibName [I] Name of the module containing the resource
2495 * hMod [I] Callers module handle
2496 * dwFlags [I] Undocumented flags for loading the module
2497 * lpszRes [I] Resource name
2498 * lpszDest [O] Destination for resulting Url
2499 * dwDestLen [I] Length of lpszDest
2502 * Success: S_OK. lpszDest contains the resource Url.
2503 * Failure: E_INVALIDARG, if any argument is invalid, or
2504 * E_FAIL if dwDestLen is too small.
2506 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2507 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2509 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2513 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2516 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2518 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2519 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2521 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2522 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2523 if (SUCCEEDED(hRet) && lpszDest)
2524 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2529 /*************************************************************************
2530 * MLBuildResURLA [SHLWAPI.406]
2532 * See MLBuildResURLA.
2534 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2535 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2537 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2538 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2539 HRESULT hRet = E_FAIL;
2541 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2542 debugstr_w(lpszRes), lpszDest, dwDestLen);
2544 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2545 !lpszDest || (dwFlags && dwFlags != 2))
2546 return E_INVALIDARG;
2548 if (dwDestLen >= szResLen + 1)
2550 dwDestLen -= (szResLen + 1);
2551 memcpy(lpszDest, szRes, sizeof(szRes));
2553 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2557 WCHAR szBuff[MAX_PATH];
2560 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2561 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2563 DWORD dwPathLen = strlenW(szBuff) + 1;
2565 if (dwDestLen >= dwPathLen)
2569 dwDestLen -= dwPathLen;
2570 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2572 dwResLen = strlenW(lpszRes) + 1;
2573 if (dwDestLen >= dwResLen + 1)
2575 lpszDest[szResLen + dwPathLen-1] = '/';
2576 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2581 MLFreeLibrary(hMod);
2587 /***********************************************************************
2588 * UrlFixupW [SHLWAPI.462]
2590 * Checks the scheme part of a URL and attempts to correct misspellings.
2593 * lpszUrl [I] Pointer to the URL to be corrected
2594 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2595 * dwMaxChars [I] Maximum size of corrected URL
2598 * success: S_OK if URL corrected or already correct
2599 * failure: S_FALSE if unable to correct / COM error code if other error
2602 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2606 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2611 srcLen = lstrlenW(url) + 1;
2613 /* For now just copy the URL directly */
2614 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);