4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
34 #define NO_SHLWAPI_STREAM
37 #include "wine/debug.h"
39 HMODULE WINAPI
MLLoadLibraryW(LPCWSTR
,HMODULE
,DWORD
);
40 BOOL WINAPI
MLFreeLibrary(HMODULE
);
41 HRESULT WINAPI
MLBuildResURLW(LPCWSTR
,HMODULE
,DWORD
,LPCWSTR
,LPWSTR
,DWORD
);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
45 /* The following schemes were identified in the native version of
46 * SHLWAPI.DLL version 5.50
49 URL_SCHEME scheme_number
;
50 WCHAR scheme_name
[12];
51 } shlwapi_schemes
[] = {
52 {URL_SCHEME_FTP
, {'f','t','p',0}},
53 {URL_SCHEME_HTTP
, {'h','t','t','p',0}},
54 {URL_SCHEME_GOPHER
, {'g','o','p','h','e','r',0}},
55 {URL_SCHEME_MAILTO
, {'m','a','i','l','t','o',0}},
56 {URL_SCHEME_NEWS
, {'n','e','w','s',0}},
57 {URL_SCHEME_NNTP
, {'n','n','t','p',0}},
58 {URL_SCHEME_TELNET
, {'t','e','l','n','e','t',0}},
59 {URL_SCHEME_WAIS
, {'w','a','i','s',0}},
60 {URL_SCHEME_FILE
, {'f','i','l','e',0}},
61 {URL_SCHEME_MK
, {'m','k',0}},
62 {URL_SCHEME_HTTPS
, {'h','t','t','p','s',0}},
63 {URL_SCHEME_SHELL
, {'s','h','e','l','l',0}},
64 {URL_SCHEME_SNEWS
, {'s','n','e','w','s',0}},
65 {URL_SCHEME_LOCAL
, {'l','o','c','a','l',0}},
66 {URL_SCHEME_JAVASCRIPT
, {'j','a','v','a','s','c','r','i','p','t',0}},
67 {URL_SCHEME_VBSCRIPT
, {'v','b','s','c','r','i','p','t',0}},
68 {URL_SCHEME_ABOUT
, {'a','b','o','u','t',0}},
69 {URL_SCHEME_RES
, {'r','e','s',0}},
73 LPCWSTR pScheme
; /* [out] start of scheme */
74 DWORD szScheme
; /* [out] size of scheme (until colon) */
75 LPCWSTR pUserName
; /* [out] start of Username */
76 DWORD szUserName
; /* [out] size of Username (until ":" or "@") */
77 LPCWSTR pPassword
; /* [out] start of Password */
78 DWORD szPassword
; /* [out] size of Password (until "@") */
79 LPCWSTR pHostName
; /* [out] start of Hostname */
80 DWORD szHostName
; /* [out] size of Hostname (until ":" or "/") */
81 LPCWSTR pPort
; /* [out] start of Port */
82 DWORD szPort
; /* [out] size of Port (until "/" or eos) */
83 LPCWSTR pQuery
; /* [out] start of Query */
84 DWORD szQuery
; /* [out] size of Query (until eos) */
94 static const CHAR hexDigits
[] = "0123456789ABCDEF";
96 static const WCHAR fileW
[] = {'f','i','l','e','\0'};
98 static const unsigned char HashDataLookup
[256] = {
99 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
100 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
101 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
102 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
103 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
104 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
105 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
106 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
107 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
108 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
109 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
110 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
111 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
112 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
113 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
114 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
115 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
116 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
117 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
118 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
120 static DWORD
get_scheme_code(LPCWSTR scheme
, DWORD scheme_len
)
124 for(i
=0; i
< sizeof(shlwapi_schemes
)/sizeof(shlwapi_schemes
[0]); i
++) {
125 if(scheme_len
== strlenW(shlwapi_schemes
[i
].scheme_name
)
126 && !memcmp(scheme
, shlwapi_schemes
[i
].scheme_name
, scheme_len
*sizeof(WCHAR
)))
127 return shlwapi_schemes
[i
].scheme_number
;
130 return URL_SCHEME_UNKNOWN
;
133 /*************************************************************************
136 * Parse a Url into its constituent parts.
140 * y [O] Undocumented structure holding the parsed information
143 * Success: S_OK. y contains the parsed Url details.
144 * Failure: An HRESULT error code.
146 HRESULT WINAPI
ParseURLA(LPCSTR x
, PARSEDURLA
*y
)
148 WCHAR scheme
[INTERNET_MAX_SCHEME_LENGTH
];
152 TRACE("%s %p\n", debugstr_a(x
), y
);
154 if(y
->cbSize
!= sizeof(*y
))
157 while(*ptr
&& (isalnum(*ptr
) || *ptr
== '-'))
160 if (*ptr
!= ':' || ptr
<= x
+1) {
161 y
->pszProtocol
= NULL
;
162 return URL_E_INVALID_SYNTAX
;
166 y
->cchProtocol
= ptr
-x
;
167 y
->pszSuffix
= ptr
+1;
168 y
->cchSuffix
= strlen(y
->pszSuffix
);
170 len
= MultiByteToWideChar(CP_ACP
, 0, x
, ptr
-x
,
171 scheme
, sizeof(scheme
)/sizeof(WCHAR
));
172 y
->nScheme
= get_scheme_code(scheme
, len
);
177 /*************************************************************************
180 * Unicode version of ParseURLA.
182 HRESULT WINAPI
ParseURLW(LPCWSTR x
, PARSEDURLW
*y
)
184 const WCHAR
*ptr
= x
;
186 TRACE("%s %p\n", debugstr_w(x
), y
);
188 if(y
->cbSize
!= sizeof(*y
))
191 while(*ptr
&& (isalnumW(*ptr
) || *ptr
== '-'))
194 if (*ptr
!= ':' || ptr
<= x
+1) {
195 y
->pszProtocol
= NULL
;
196 return URL_E_INVALID_SYNTAX
;
200 y
->cchProtocol
= ptr
-x
;
201 y
->pszSuffix
= ptr
+1;
202 y
->cchSuffix
= strlenW(y
->pszSuffix
);
203 y
->nScheme
= get_scheme_code(x
, ptr
-x
);
208 /*************************************************************************
209 * UrlCanonicalizeA [SHLWAPI.@]
211 * Canonicalize a Url.
214 * pszUrl [I] Url to cCanonicalize
215 * pszCanonicalized [O] Destination for converted Url.
216 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
217 * dwFlags [I] Flags controlling the conversion.
220 * Success: S_OK. The pszCanonicalized contains the converted Url.
221 * Failure: E_POINTER, if *pcchCanonicalized is too small.
223 * MSDN incorrectly describes the flags for this function. They should be:
224 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
225 *| URL_ESCAPE_SPACES_ONLY 0x04000000
226 *| URL_ESCAPE_PERCENT 0x00001000
227 *| URL_ESCAPE_UNSAFE 0x10000000
228 *| URL_UNESCAPE 0x10000000
229 *| URL_DONT_SIMPLIFY 0x08000000
230 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
232 HRESULT WINAPI
UrlCanonicalizeA(LPCSTR pszUrl
, LPSTR pszCanonicalized
,
233 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
235 LPWSTR base
, canonical
;
239 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl
), pszCanonicalized
,
240 pcchCanonicalized
, dwFlags
, pcchCanonicalized
? *pcchCanonicalized
: -1);
242 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
245 base
= HeapAlloc(GetProcessHeap(), 0,
246 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
247 canonical
= base
+ INTERNET_MAX_URL_LENGTH
;
249 MultiByteToWideChar(0, 0, pszUrl
, -1, base
, INTERNET_MAX_URL_LENGTH
);
250 len
= INTERNET_MAX_URL_LENGTH
;
252 ret
= UrlCanonicalizeW(base
, canonical
, &len
, dwFlags
);
254 *pcchCanonicalized
= len
* 2;
255 HeapFree(GetProcessHeap(), 0, base
);
259 len2
= WideCharToMultiByte(0, 0, canonical
, -1, 0, 0, 0, 0);
260 if (len2
> *pcchCanonicalized
) {
261 *pcchCanonicalized
= len2
;
262 HeapFree(GetProcessHeap(), 0, base
);
265 WideCharToMultiByte(0, 0, canonical
, -1, pszCanonicalized
, *pcchCanonicalized
, 0, 0);
266 *pcchCanonicalized
= len
;
267 HeapFree(GetProcessHeap(), 0, base
);
271 /*************************************************************************
272 * UrlCanonicalizeW [SHLWAPI.@]
274 * See UrlCanonicalizeA.
276 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
277 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
281 LPWSTR lpszUrlCpy
, wk1
, wk2
, mp
, mp2
, root
;
283 DWORD nByteLen
, nLen
, nWkLen
;
286 static const WCHAR wszFile
[] = {'f','i','l','e',':'};
287 static const WCHAR wszRes
[] = {'r','e','s',':'};
288 static const WCHAR wszLocalhost
[] = {'l','o','c','a','l','h','o','s','t'};
290 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl
), pszCanonicalized
,
291 pcchCanonicalized
, dwFlags
, pcchCanonicalized
? *pcchCanonicalized
: -1);
293 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
297 *pszCanonicalized
= 0;
301 nByteLen
= (strlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
302 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0,
303 INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
305 if((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszFile
)
306 && !memcmp(wszFile
, pszUrl
, sizeof(wszFile
)))
309 if(nByteLen
>= sizeof(wszRes
) && !memcmp(wszRes
, pszUrl
, sizeof(wszRes
))) {
310 dwFlags
&= ~URL_FILE_USE_PATHURL
;
317 * 1 have 2[+] alnum 2,3
318 * 2 have scheme (found :) 4,6,3
319 * 3 failed (no location)
321 * 5 have 1[+] alnum 6,3
322 * 6 have location (found /) save root location
325 wk1
= (LPWSTR
)pszUrl
;
329 if(pszUrl
[1] == ':') { /* Assume path */
330 static const WCHAR wszFilePrefix
[] = {'f','i','l','e',':','/','/','/'};
332 memcpy(wk2
, wszFilePrefix
, sizeof(wszFilePrefix
));
333 wk2
+= sizeof(wszFilePrefix
)/sizeof(WCHAR
);
334 if (dwFlags
& URL_FILE_USE_PATHURL
)
340 dwFlags
|= URL_ESCAPE_UNSAFE
;
347 if (!isalnumW(*wk1
)) {state
= 3; break;}
349 if (!isalnumW(*wk1
)) {state
= 3; break;}
355 if (*wk1
++ == ':') state
= 2;
359 if (*wk1
!= '/') {state
= 6; break;}
361 if((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszLocalhost
)
362 && !memcmp(wszLocalhost
, wk1
, sizeof(wszLocalhost
))){
363 wk1
+= sizeof(wszLocalhost
)/sizeof(WCHAR
);
364 while(*wk1
== '\\' && (dwFlags
& URL_FILE_USE_PATHURL
))
367 if(*wk1
== '/' && (dwFlags
& URL_FILE_USE_PATHURL
))
372 nWkLen
= strlenW(wk1
);
373 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
380 if(*mp
== '/' || *mp
== '\\')
387 if (!isalnumW(*wk1
) && (*wk1
!= '-') && (*wk1
!= '.') && (*wk1
!= ':'))
389 while(isalnumW(*wk1
) || (*wk1
== '-') || (*wk1
== '.') || (*wk1
== ':'))
400 if (*wk1
!= '/' && *wk1
!= '\\') {state
= 3; break;}
401 while(*wk1
== '/' || *wk1
== '\\') {
411 if(dwFlags
& URL_DONT_SIMPLIFY
) {
416 /* Now at root location, cannot back up any more. */
417 /* "root" will point at the '/' */
421 mp
= strchrW(wk1
, '/');
422 mp2
= strchrW(wk1
, '\\');
423 if(mp2
&& (!mp
|| mp2
< mp
))
426 nWkLen
= strlenW(wk1
);
427 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
434 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
445 TRACE("found '/.'\n");
446 if (wk1
[1] == '/' || wk1
[1] == '\\') {
447 /* case of /./ -> skip the ./ */
450 else if (wk1
[1] == '.') {
451 /* found /.. look for next / */
452 TRACE("found '/..'\n");
453 if (wk1
[2] == '/' || wk1
[2] == '\\' ||wk1
[2] == '?'
454 || wk1
[2] == '#' || !wk1
[2]) {
455 /* case /../ -> need to backup wk2 */
456 TRACE("found '/../'\n");
457 *(wk2
-1) = '\0'; /* set end of string */
458 mp
= strrchrW(root
, '/');
459 mp2
= strrchrW(root
, '\\');
460 if(mp2
&& (!mp
|| mp2
< mp
))
462 if (mp
&& (mp
>= root
)) {
463 /* found valid backup point */
465 if(wk1
[2] != '/' && wk1
[2] != '\\')
471 /* did not find point, restore '/' */
481 FIXME("how did we get here - state=%d\n", state
);
482 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
486 TRACE("Simplified, orig <%s>, simple <%s>\n",
487 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
489 nLen
= lstrlenW(lpszUrlCpy
);
490 while ((nLen
> 0) && ((lpszUrlCpy
[nLen
-1] <= ' ')))
491 lpszUrlCpy
[--nLen
]=0;
493 if(dwFlags
& (URL_UNESCAPE
| URL_FILE_USE_PATHURL
))
494 UrlUnescapeW(lpszUrlCpy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
496 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
497 URL_ESCAPE_SPACES_ONLY
|
499 URL_DONT_ESCAPE_EXTRA_INFO
|
500 URL_ESCAPE_SEGMENT_ONLY
))) {
501 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
502 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
504 } else { /* No escaping needed, just copy the string */
505 nLen
= lstrlenW(lpszUrlCpy
);
506 if(nLen
< *pcchCanonicalized
)
507 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
512 *pcchCanonicalized
= nLen
;
515 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
518 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
523 /*************************************************************************
524 * UrlCombineA [SHLWAPI.@]
529 * pszBase [I] Base Url
530 * pszRelative [I] Url to combine with pszBase
531 * pszCombined [O] Destination for combined Url
532 * pcchCombined [O] Destination for length of pszCombined
533 * dwFlags [I] URL_ flags from "shlwapi.h"
536 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
537 * contains its length.
538 * Failure: An HRESULT error code indicating the error.
540 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
541 LPSTR pszCombined
, LPDWORD pcchCombined
,
544 LPWSTR base
, relative
, combined
;
545 DWORD ret
, len
, len2
;
547 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
548 debugstr_a(pszBase
),debugstr_a(pszRelative
),
549 pcchCombined
?*pcchCombined
:0,dwFlags
);
551 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
554 base
= HeapAlloc(GetProcessHeap(), 0,
555 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
556 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
557 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
559 MultiByteToWideChar(0, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
560 MultiByteToWideChar(0, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
563 ret
= UrlCombineW(base
, relative
, pszCombined
?combined
:NULL
, &len
, dwFlags
);
566 HeapFree(GetProcessHeap(), 0, base
);
570 len2
= WideCharToMultiByte(0, 0, combined
, len
, 0, 0, 0, 0);
571 if (len2
> *pcchCombined
) {
572 *pcchCombined
= len2
;
573 HeapFree(GetProcessHeap(), 0, base
);
576 WideCharToMultiByte(0, 0, combined
, len
+1, pszCombined
, (*pcchCombined
)+1,
578 *pcchCombined
= len2
;
579 HeapFree(GetProcessHeap(), 0, base
);
583 /*************************************************************************
584 * UrlCombineW [SHLWAPI.@]
588 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
589 LPWSTR pszCombined
, LPDWORD pcchCombined
,
592 PARSEDURLW base
, relative
;
593 DWORD myflags
, sizeloc
= 0;
594 DWORD len
, res1
, res2
, process_case
= 0;
595 LPWSTR work
, preliminary
, mbase
, mrelative
;
596 static const WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
599 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
600 debugstr_w(pszBase
),debugstr_w(pszRelative
),
601 pcchCombined
?*pcchCombined
:0,dwFlags
);
603 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
606 base
.cbSize
= sizeof(base
);
607 relative
.cbSize
= sizeof(relative
);
609 /* Get space for duplicates of the input and the output */
610 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
612 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
613 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
616 /* Canonicalize the base input prior to looking for the scheme */
617 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
618 len
= INTERNET_MAX_URL_LENGTH
;
619 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
621 /* Canonicalize the relative input prior to looking for the scheme */
622 len
= INTERNET_MAX_URL_LENGTH
;
623 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
625 /* See if the base has a scheme */
626 res1
= ParseURLW(mbase
, &base
);
628 /* if pszBase has no scheme, then return pszRelative */
629 TRACE("no scheme detected in Base\n");
633 /* mk is a special case */
634 if(base
.nScheme
== URL_SCHEME_MK
) {
635 static const WCHAR wsz
[] = {':',':',0};
637 WCHAR
*ptr
= strstrW(base
.pszSuffix
, wsz
);
642 delta
= ptr
-base
.pszSuffix
;
643 base
.cchProtocol
+= delta
;
644 base
.pszSuffix
+= delta
;
645 base
.cchSuffix
-= delta
;
648 /* get size of location field (if it exists) */
649 work
= (LPWSTR
)base
.pszSuffix
;
651 if (*work
++ == '/') {
652 if (*work
++ == '/') {
653 /* At this point have start of location and
654 * it ends at next '/' or end of string.
656 while(*work
&& (*work
!= '/')) work
++;
657 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
662 /* Change .sizep2 to not have the last leaf in it,
663 * Note: we need to start after the location (if it exists)
665 work
= strrchrW((base
.pszSuffix
+sizeloc
), '/');
667 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
668 base
.cchSuffix
= len
;
673 * .pszSuffix points to location (starting with '//')
674 * .cchSuffix length of location (above) and rest less the last
676 * sizeloc length of location (above) up to but not including
680 res2
= ParseURLW(mrelative
, &relative
);
682 /* no scheme in pszRelative */
683 TRACE("no scheme detected in Relative\n");
684 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
685 relative
.cchSuffix
= strlenW(mrelative
);
686 if (*pszRelative
== ':') {
687 /* case that is either left alone or uses pszBase */
688 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
695 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == ':')) {
696 /* case that becomes "file:///" */
697 strcpyW(preliminary
, myfilestr
);
701 if ((*mrelative
== '/') && (*(mrelative
+1) == '/')) {
702 /* pszRelative has location and rest */
706 if (*mrelative
== '/') {
707 /* case where pszRelative is root to location */
711 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
715 /* handle cases where pszRelative has scheme */
716 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
717 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
719 /* since the schemes are the same */
720 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
721 /* case where pszRelative replaces location and following */
725 if (*relative
.pszSuffix
== '/') {
726 /* case where pszRelative is root to location */
730 /* replace either just location if base's location starts with a
731 * slash or otherwise everything */
732 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
735 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
736 /* case where pszRelative replaces scheme, location,
737 * and following and handles PLUGGABLE
744 } while(FALSE
); /* a little trick to allow easy exit from nested if's */
747 switch (process_case
) {
750 * Return pszRelative appended to what ever is in pszCombined,
751 * (which may the string "file:///"
753 strcatW(preliminary
, mrelative
);
756 case 2: /* case where pszRelative replaces scheme, and location */
757 strcpyW(preliminary
, mrelative
);
761 * Return the pszBase scheme with pszRelative. Basically
762 * keeps the scheme and replaces the domain and following.
764 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
765 work
= preliminary
+ base
.cchProtocol
+ 1;
766 strcpyW(work
, relative
.pszSuffix
);
770 * Return the pszBase scheme and location but everything
771 * after the location is pszRelative. (Replace document
774 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
775 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
776 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
778 strcpyW(work
, relative
.pszSuffix
);
782 * Return the pszBase without its document (if any) and
783 * append pszRelative after its scheme.
785 memcpy(preliminary
, base
.pszProtocol
,
786 (base
.cchProtocol
+1+base
.cchSuffix
)*sizeof(WCHAR
));
787 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
790 strcpyW(work
, relative
.pszSuffix
);
794 FIXME("How did we get here????? process_case=%d\n", process_case
);
799 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
800 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, (dwFlags
& ~URL_FILE_USE_PATHURL
));
801 if(SUCCEEDED(ret
) && pszCombined
) {
802 lstrcpyW(pszCombined
, mrelative
);
804 TRACE("return-%d len=%d, %s\n",
805 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
807 HeapFree(GetProcessHeap(), 0, preliminary
);
811 /*************************************************************************
812 * UrlEscapeA [SHLWAPI.@]
815 HRESULT WINAPI
UrlEscapeA(
821 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
822 WCHAR
*escapedW
= bufW
;
825 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
827 if (!pszEscaped
|| !pcchEscaped
|| !*pcchEscaped
)
830 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
832 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
833 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
834 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
837 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
838 if(*pcchEscaped
> lenA
) {
839 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
840 pszEscaped
[lenA
] = 0;
843 *pcchEscaped
= lenA
+ 1;
847 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
848 RtlFreeUnicodeString(&urlW
);
852 #define WINE_URL_BASH_AS_SLASH 0x01
853 #define WINE_URL_COLLAPSE_SLASHES 0x02
854 #define WINE_URL_ESCAPE_SLASH 0x04
855 #define WINE_URL_ESCAPE_HASH 0x08
856 #define WINE_URL_ESCAPE_QUESTION 0x10
857 #define WINE_URL_STOP_ON_HASH 0x20
858 #define WINE_URL_STOP_ON_QUESTION 0x40
860 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
, DWORD int_flags
)
866 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
873 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
876 if (ch
<= 31 || ch
>= 127)
897 if (int_flags
& WINE_URL_ESCAPE_SLASH
) return TRUE
;
901 if (int_flags
& WINE_URL_ESCAPE_QUESTION
) return TRUE
;
905 if (int_flags
& WINE_URL_ESCAPE_HASH
) return TRUE
;
915 /*************************************************************************
916 * UrlEscapeW [SHLWAPI.@]
918 * Converts unsafe characters in a Url into escape sequences.
921 * pszUrl [I] Url to modify
922 * pszEscaped [O] Destination for modified Url
923 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
924 * dwFlags [I] URL_ flags from "shlwapi.h"
927 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
928 * contains its length.
929 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
930 * pcchEscaped is set to the required length.
932 * Converts unsafe characters into their escape sequences.
935 * - By default this function stops converting at the first '?' or
937 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
938 * converted, but the conversion continues past a '?' or '#'.
939 * - Note that this function did not work well (or at all) in shlwapi version 4.
942 * Only the following flags are implemented:
943 *| URL_ESCAPE_SPACES_ONLY
944 *| URL_DONT_ESCAPE_EXTRA_INFO
945 *| URL_ESCAPE_SEGMENT_ONLY
946 *| URL_ESCAPE_PERCENT
948 HRESULT WINAPI
UrlEscapeW(
955 DWORD needed
= 0, ret
;
956 BOOL stop_escaping
= FALSE
;
957 WCHAR next
[5], *dst
= pszEscaped
;
959 PARSEDURLW parsed_url
;
962 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
964 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl
), pszEscaped
,
965 pcchEscaped
, dwFlags
);
967 if(!pszUrl
|| !pcchEscaped
)
970 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
971 URL_ESCAPE_SEGMENT_ONLY
|
972 URL_DONT_ESCAPE_EXTRA_INFO
|
974 FIXME("Unimplemented flags: %08x\n", dwFlags
);
977 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
978 /* if SPACES_ONLY specified, reset the other controls */
979 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
981 URL_ESCAPE_SEGMENT_ONLY
);
984 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
985 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
989 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
990 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
992 parsed_url
.cbSize
= sizeof(parsed_url
);
993 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
994 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
996 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
998 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
999 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
1001 switch(parsed_url
.nScheme
) {
1002 case URL_SCHEME_FILE
:
1003 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
1004 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
1007 case URL_SCHEME_HTTP
:
1008 case URL_SCHEME_HTTPS
:
1009 int_flags
|= WINE_URL_BASH_AS_SLASH
;
1010 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
1011 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1014 case URL_SCHEME_MAILTO
:
1015 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
1016 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
1019 case URL_SCHEME_INVALID
:
1022 case URL_SCHEME_FTP
:
1024 if(parsed_url
.pszSuffix
[0] != '/')
1025 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1030 for(src
= pszUrl
; *src
; ) {
1034 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
1035 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
1036 while(cur
== '/' || cur
== '\\') {
1040 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
1041 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
1042 src
+= localhost_len
+ 1;
1049 next
[0] = next
[1] = next
[2] = '/';
1056 next
[0] = next
[1] = '/';
1063 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1064 stop_escaping
= TRUE
;
1066 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1067 stop_escaping
= TRUE
;
1069 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1071 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1073 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1074 next
[2] = hexDigits
[cur
& 0xf];
1083 if(needed
+ len
<= *pcchEscaped
) {
1084 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1090 if(needed
< *pcchEscaped
) {
1094 needed
++; /* add one for the '\0' */
1097 *pcchEscaped
= needed
;
1102 /*************************************************************************
1103 * UrlUnescapeA [SHLWAPI.@]
1105 * Converts Url escape sequences back to ordinary characters.
1108 * pszUrl [I/O] Url to convert
1109 * pszUnescaped [O] Destination for converted Url
1110 * pcchUnescaped [I/O] Size of output string
1111 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1114 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1115 * dwFlags includes URL_ESCAPE_INPLACE.
1116 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1117 * this case pcchUnescaped is set to the size required.
1119 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1120 * the first occurrence of either a '?' or '#' character.
1122 HRESULT WINAPI
UrlUnescapeA(
1125 LPDWORD pcchUnescaped
,
1132 BOOL stop_unescaping
= FALSE
;
1134 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl
), pszUnescaped
,
1135 pcchUnescaped
, dwFlags
);
1137 if (!pszUrl
) return E_INVALIDARG
;
1139 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1143 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1147 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1148 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1149 (*src
== '#' || *src
== '?')) {
1150 stop_unescaping
= TRUE
;
1152 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1153 && stop_unescaping
== FALSE
) {
1156 memcpy(buf
, src
+ 1, 2);
1158 ih
= strtol(buf
, NULL
, 16);
1160 src
+= 2; /* Advance to end of escape */
1164 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1168 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1172 needed
++; /* add one for the '\0' */
1175 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1176 *pcchUnescaped
= needed
;
1179 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1180 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1186 /*************************************************************************
1187 * UrlUnescapeW [SHLWAPI.@]
1191 HRESULT WINAPI
UrlUnescapeW(
1193 LPWSTR pszUnescaped
,
1194 LPDWORD pcchUnescaped
,
1201 BOOL stop_unescaping
= FALSE
;
1203 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl
), pszUnescaped
,
1204 pcchUnescaped
, dwFlags
);
1206 if(!pszUrl
) return E_INVALIDARG
;
1208 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1212 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1216 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1217 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1218 (*src
== '#' || *src
== '?')) {
1219 stop_unescaping
= TRUE
;
1221 } else if(*src
== '%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1222 && stop_unescaping
== FALSE
) {
1224 WCHAR buf
[5] = {'0','x',0};
1225 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1227 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1229 src
+= 2; /* Advance to end of escape */
1233 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1237 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1241 needed
++; /* add one for the '\0' */
1244 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1245 *pcchUnescaped
= needed
;
1248 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1249 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1255 /*************************************************************************
1256 * UrlGetLocationA [SHLWAPI.@]
1258 * Get the location from a Url.
1261 * pszUrl [I] Url to get the location from
1264 * A pointer to the start of the location in pszUrl, or NULL if there is
1268 * - MSDN erroneously states that "The location is the segment of the Url
1269 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1270 * stop at '?' and always return a NULL in this case.
1271 * - MSDN also erroneously states that "If a file URL has a query string,
1272 * the returned string is the query string". In all tested cases, if the
1273 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1276 *| NULL file://aa/b/cd#hohoh
1277 *| #hohoh http://aa/b/cd#hohoh
1278 *| NULL fi://aa/b/cd#hohoh
1279 *| #hohoh ff://aa/b/cd#hohoh
1281 LPCSTR WINAPI
UrlGetLocationA(
1287 base
.cbSize
= sizeof(base
);
1288 res1
= ParseURLA(pszUrl
, &base
);
1289 if (res1
) return NULL
; /* invalid scheme */
1291 /* if scheme is file: then never return pointer */
1292 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1294 /* Look for '#' and return its addr */
1295 return strchr(base
.pszSuffix
, '#');
1298 /*************************************************************************
1299 * UrlGetLocationW [SHLWAPI.@]
1301 * See UrlGetLocationA.
1303 LPCWSTR WINAPI
UrlGetLocationW(
1309 base
.cbSize
= sizeof(base
);
1310 res1
= ParseURLW(pszUrl
, &base
);
1311 if (res1
) return NULL
; /* invalid scheme */
1313 /* if scheme is file: then never return pointer */
1314 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1316 /* Look for '#' and return its addr */
1317 return strchrW(base
.pszSuffix
, '#');
1320 /*************************************************************************
1321 * UrlCompareA [SHLWAPI.@]
1326 * pszUrl1 [I] First Url to compare
1327 * pszUrl2 [I] Url to compare to pszUrl1
1328 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1331 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1332 * than, equal to, or less than pszUrl1 respectively.
1334 INT WINAPI
UrlCompareA(
1339 INT ret
, len
, len1
, len2
;
1342 return strcmp(pszUrl1
, pszUrl2
);
1343 len1
= strlen(pszUrl1
);
1344 if (pszUrl1
[len1
-1] == '/') len1
--;
1345 len2
= strlen(pszUrl2
);
1346 if (pszUrl2
[len2
-1] == '/') len2
--;
1348 return strncmp(pszUrl1
, pszUrl2
, len1
);
1349 len
= min(len1
, len2
);
1350 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1351 if (ret
) return ret
;
1352 if (len1
> len2
) return 1;
1356 /*************************************************************************
1357 * UrlCompareW [SHLWAPI.@]
1361 INT WINAPI
UrlCompareW(
1367 size_t len
, len1
, len2
;
1370 return strcmpW(pszUrl1
, pszUrl2
);
1371 len1
= strlenW(pszUrl1
);
1372 if (pszUrl1
[len1
-1] == '/') len1
--;
1373 len2
= strlenW(pszUrl2
);
1374 if (pszUrl2
[len2
-1] == '/') len2
--;
1376 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1377 len
= min(len1
, len2
);
1378 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1379 if (ret
) return ret
;
1380 if (len1
> len2
) return 1;
1384 /*************************************************************************
1385 * HashData [SHLWAPI.@]
1387 * Hash an input block into a variable sized digest.
1390 * lpSrc [I] Input block
1391 * nSrcLen [I] Length of lpSrc
1392 * lpDest [I] Output for hash digest
1393 * nDestLen [I] Length of lpDest
1396 * Success: TRUE. lpDest is filled with the computed hash value.
1397 * Failure: FALSE, if any argument is invalid.
1399 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1400 unsigned char *lpDest
, DWORD nDestLen
)
1402 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1404 if (!lpSrc
|| !lpDest
)
1405 return E_INVALIDARG
;
1407 while (destCount
>= 0)
1409 lpDest
[destCount
] = (destCount
& 0xff);
1413 while (srcCount
>= 0)
1415 destCount
= nDestLen
- 1;
1416 while (destCount
>= 0)
1418 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1426 /*************************************************************************
1427 * UrlHashA [SHLWAPI.@]
1429 * Produce a Hash from a Url.
1432 * pszUrl [I] Url to hash
1433 * lpDest [O] Destinationh for hash
1434 * nDestLen [I] Length of lpDest
1437 * Success: S_OK. lpDest is filled with the computed hash value.
1438 * Failure: E_INVALIDARG, if any argument is invalid.
1440 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1442 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1443 return E_INVALIDARG
;
1445 HashData((const BYTE
*)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1449 /*************************************************************************
1450 * UrlHashW [SHLWAPI.@]
1454 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1456 char szUrl
[MAX_PATH
];
1458 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1460 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1461 return E_INVALIDARG
;
1463 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1464 * return the same digests for the same URL.
1466 WideCharToMultiByte(0, 0, pszUrl
, -1, szUrl
, MAX_PATH
, 0, 0);
1467 HashData((const BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1471 /*************************************************************************
1472 * UrlApplySchemeA [SHLWAPI.@]
1474 * Apply a scheme to a Url.
1477 * pszIn [I] Url to apply scheme to
1478 * pszOut [O] Destination for modified Url
1479 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1480 * dwFlags [I] URL_ flags from "shlwapi.h"
1483 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1484 * Failure: An HRESULT error code describing the error.
1486 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1492 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn
),
1493 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1495 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1497 in
= HeapAlloc(GetProcessHeap(), 0,
1498 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1499 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1501 MultiByteToWideChar(CP_ACP
, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1502 len
= INTERNET_MAX_URL_LENGTH
;
1504 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1506 HeapFree(GetProcessHeap(), 0, in
);
1510 len
= WideCharToMultiByte(CP_ACP
, 0, out
, -1, NULL
, 0, NULL
, NULL
);
1511 if (len
> *pcchOut
) {
1516 WideCharToMultiByte(CP_ACP
, 0, out
, -1, pszOut
, *pcchOut
, NULL
, NULL
);
1521 HeapFree(GetProcessHeap(), 0, in
);
1525 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1530 DWORD value_len
, data_len
, dwType
, i
;
1531 WCHAR reg_path
[MAX_PATH
];
1532 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1535 MultiByteToWideChar(0, 0,
1536 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1537 -1, reg_path
, MAX_PATH
);
1538 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1540 while(value_len
= data_len
= MAX_PATH
,
1541 RegEnumValueW(newkey
, index
, value
, &value_len
,
1542 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1543 TRACE("guess %d %s is %s\n",
1544 index
, debugstr_w(value
), debugstr_w(data
));
1547 for(i
=0; i
<value_len
; i
++) {
1550 /* remember that TRUE is not-equal */
1551 j
= ChrCmpIW(Wxx
, Wyy
);
1554 if ((i
== value_len
) && !j
) {
1555 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1556 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1557 RegCloseKey(newkey
);
1560 strcpyW(pszOut
, data
);
1561 strcatW(pszOut
, pszIn
);
1562 *pcchOut
= strlenW(pszOut
);
1563 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1564 RegCloseKey(newkey
);
1569 RegCloseKey(newkey
);
1573 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1576 DWORD data_len
, dwType
;
1577 WCHAR data
[MAX_PATH
];
1579 static const WCHAR prefix_keyW
[] =
1580 {'S','o','f','t','w','a','r','e',
1581 '\\','M','i','c','r','o','s','o','f','t',
1582 '\\','W','i','n','d','o','w','s',
1583 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1585 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1587 /* get and prepend default */
1588 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefix_keyW
, 0, 1, &newkey
);
1589 data_len
= sizeof(data
);
1590 RegQueryValueExW(newkey
, NULL
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1591 RegCloseKey(newkey
);
1592 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1593 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1596 strcpyW(pszOut
, data
);
1597 strcatW(pszOut
, pszIn
);
1598 *pcchOut
= strlenW(pszOut
);
1599 TRACE("used default %s\n", debugstr_w(pszOut
));
1603 /*************************************************************************
1604 * UrlApplySchemeW [SHLWAPI.@]
1606 * See UrlApplySchemeA.
1608 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1610 PARSEDURLW in_scheme
;
1614 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn
),
1615 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1617 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1619 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1620 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1621 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwFlags
);
1622 strcpyW(pszOut
, pszIn
);
1623 *pcchOut
= strlenW(pszOut
);
1627 in_scheme
.cbSize
= sizeof(in_scheme
);
1628 /* See if the base has a scheme */
1629 res1
= ParseURLW(pszIn
, &in_scheme
);
1631 /* no scheme in input, need to see if we need to guess */
1632 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1633 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != E_FAIL
)
1638 /* we have a scheme, see if valid (known scheme) */
1639 if (in_scheme
.nScheme
) {
1640 /* have valid scheme, so just copy and exit */
1641 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1642 *pcchOut
= strlenW(pszIn
) + 1;
1645 strcpyW(pszOut
, pszIn
);
1646 *pcchOut
= strlenW(pszOut
);
1647 TRACE("valid scheme, returning copy\n");
1652 /* If we are here, then either invalid scheme,
1653 * or no scheme and can't/failed guess.
1655 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1657 (dwFlags
& URL_APPLY_DEFAULT
)) {
1658 /* find and apply default scheme */
1659 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1665 /*************************************************************************
1666 * UrlIsA [SHLWAPI.@]
1668 * Determine if a Url is of a certain class.
1671 * pszUrl [I] Url to check
1672 * Urlis [I] URLIS_ constant from "shlwapi.h"
1675 * TRUE if pszUrl belongs to the class type in Urlis.
1678 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1684 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1689 base
.cbSize
= sizeof(base
);
1690 res1
= ParseURLA(pszUrl
, &base
);
1691 if (res1
) return FALSE
; /* invalid scheme */
1692 switch (base
.nScheme
)
1694 case URL_SCHEME_MAILTO
:
1695 case URL_SCHEME_SHELL
:
1696 case URL_SCHEME_JAVASCRIPT
:
1697 case URL_SCHEME_VBSCRIPT
:
1698 case URL_SCHEME_ABOUT
:
1704 return !StrCmpNA("file:", pszUrl
, 5);
1706 case URLIS_DIRECTORY
:
1707 last
= pszUrl
+ strlen(pszUrl
) - 1;
1708 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1711 return PathIsURLA(pszUrl
);
1713 case URLIS_NOHISTORY
:
1714 case URLIS_APPLIABLE
:
1715 case URLIS_HASQUERY
:
1717 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1722 /*************************************************************************
1723 * UrlIsW [SHLWAPI.@]
1727 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1729 static const WCHAR stemp
[] = { 'f','i','l','e',':',0 };
1734 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1739 base
.cbSize
= sizeof(base
);
1740 res1
= ParseURLW(pszUrl
, &base
);
1741 if (res1
) return FALSE
; /* invalid scheme */
1742 switch (base
.nScheme
)
1744 case URL_SCHEME_MAILTO
:
1745 case URL_SCHEME_SHELL
:
1746 case URL_SCHEME_JAVASCRIPT
:
1747 case URL_SCHEME_VBSCRIPT
:
1748 case URL_SCHEME_ABOUT
:
1754 return !strncmpW(stemp
, pszUrl
, 5);
1756 case URLIS_DIRECTORY
:
1757 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1758 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1761 return PathIsURLW(pszUrl
);
1763 case URLIS_NOHISTORY
:
1764 case URLIS_APPLIABLE
:
1765 case URLIS_HASQUERY
:
1767 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1772 /*************************************************************************
1773 * UrlIsNoHistoryA [SHLWAPI.@]
1775 * Determine if a Url should not be stored in the users history list.
1778 * pszUrl [I] Url to check
1781 * TRUE, if pszUrl should be excluded from the history list,
1784 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1786 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1789 /*************************************************************************
1790 * UrlIsNoHistoryW [SHLWAPI.@]
1792 * See UrlIsNoHistoryA.
1794 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1796 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1799 /*************************************************************************
1800 * UrlIsOpaqueA [SHLWAPI.@]
1802 * Determine if a Url is opaque.
1805 * pszUrl [I] Url to check
1808 * TRUE if pszUrl is opaque,
1812 * An opaque Url is one that does not start with "<protocol>://".
1814 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1816 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1819 /*************************************************************************
1820 * UrlIsOpaqueW [SHLWAPI.@]
1824 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1826 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1829 /*************************************************************************
1830 * Scans for characters of type "type" and when not matching found,
1831 * returns pointer to it and length in size.
1833 * Characters tested based on RFC 1738
1835 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1837 static DWORD alwayszero
= 0;
1846 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1861 if ( isalphaW(*start
) ||
1863 /* user/password only characters */
1868 /* *extra* characters */
1875 /* *safe* characters */
1884 } else if (*start
== '%') {
1885 if (isxdigitW(*(start
+1)) &&
1886 isxdigitW(*(start
+2))) {
1898 if (isdigitW(*start
)) {
1909 if (isalnumW(*start
) ||
1921 FIXME("unknown type %d\n", type
);
1922 return (LPWSTR
)&alwayszero
;
1924 /* TRACE("scanned %d characters next char %p<%c>\n",
1925 *size, start, *start); */
1929 /*************************************************************************
1930 * Attempt to parse URL into pieces.
1932 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
1936 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
1937 pl
->pScheme
= pszUrl
;
1938 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
1939 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
1941 if ((*work
!= '/') || (*(work
+1) != '/')) goto ErrorExit
;
1942 pl
->pUserName
= work
+ 2;
1943 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
1944 if (*work
== ':' ) {
1945 /* parse password */
1947 pl
->pPassword
= work
;
1948 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
1950 /* what we just parsed must be the hostname and port
1951 * so reset pointers and clear then let it parse */
1952 pl
->szUserName
= pl
->szPassword
= 0;
1953 work
= pl
->pUserName
- 1;
1954 pl
->pUserName
= pl
->pPassword
= 0;
1956 } else if (*work
== '@') {
1960 } else if (!*work
|| (*work
== '/') || (*work
== '.')) {
1961 /* what was parsed was hostname, so reset pointers and let it parse */
1962 pl
->szUserName
= pl
->szPassword
= 0;
1963 work
= pl
->pUserName
- 1;
1964 pl
->pUserName
= pl
->pPassword
= 0;
1965 } else goto ErrorExit
;
1967 /* now start parsing hostname or hostnumber */
1969 pl
->pHostName
= work
;
1970 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
1975 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
1978 /* see if query string */
1979 pl
->pQuery
= strchrW(work
, '?');
1980 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
1982 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1983 pl
->pScheme
, pl
->szScheme
,
1984 pl
->pUserName
, pl
->szUserName
,
1985 pl
->pPassword
, pl
->szPassword
,
1986 pl
->pHostName
, pl
->szHostName
,
1987 pl
->pPort
, pl
->szPort
,
1988 pl
->pQuery
, pl
->szQuery
);
1991 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
1992 return E_INVALIDARG
;
1995 /*************************************************************************
1996 * UrlGetPartA [SHLWAPI.@]
1998 * Retrieve part of a Url.
2001 * pszIn [I] Url to parse
2002 * pszOut [O] Destination for part of pszIn requested
2003 * pcchOut [I] Size of pszOut
2004 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2005 * needed size of pszOut INCLUDING '\0'.
2006 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2007 * dwFlags [I] URL_ flags from "shlwapi.h"
2010 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2011 * Failure: An HRESULT error code describing the error.
2013 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
2014 DWORD dwPart
, DWORD dwFlags
)
2017 DWORD ret
, len
, len2
;
2019 in
= HeapAlloc(GetProcessHeap(), 0,
2020 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
2021 out
= in
+ INTERNET_MAX_URL_LENGTH
;
2023 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
2025 len
= INTERNET_MAX_URL_LENGTH
;
2026 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
2029 HeapFree(GetProcessHeap(), 0, in
);
2033 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
2034 if (len2
> *pcchOut
) {
2036 HeapFree(GetProcessHeap(), 0, in
);
2039 len2
= WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
2041 HeapFree(GetProcessHeap(), 0, in
);
2045 /*************************************************************************
2046 * UrlGetPartW [SHLWAPI.@]
2050 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2051 DWORD dwPart
, DWORD dwFlags
)
2055 DWORD scheme
, size
, schsize
;
2056 LPCWSTR addr
, schaddr
;
2058 TRACE("(%s %p %p(%d) %08x %08x)\n",
2059 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2061 addr
= strchrW(pszIn
, ':');
2065 scheme
= get_scheme_code(pszIn
, addr
-pszIn
);
2067 ret
= URL_ParseUrl(pszIn
, &pl
);
2069 schaddr
= pl
.pScheme
;
2070 schsize
= pl
.szScheme
;
2073 case URL_PART_SCHEME
:
2074 if (!pl
.szScheme
) return E_INVALIDARG
;
2079 case URL_PART_HOSTNAME
:
2081 case URL_SCHEME_FTP
:
2082 case URL_SCHEME_HTTP
:
2083 case URL_SCHEME_GOPHER
:
2084 case URL_SCHEME_TELNET
:
2085 case URL_SCHEME_FILE
:
2086 case URL_SCHEME_HTTPS
:
2092 if(scheme
==URL_SCHEME_FILE
&& (!pl
.szHostName
||
2093 (pl
.szHostName
==1 && *(pl
.pHostName
+1)==':'))) {
2100 if (!pl
.szHostName
) return E_INVALIDARG
;
2101 addr
= pl
.pHostName
;
2102 size
= pl
.szHostName
;
2105 case URL_PART_USERNAME
:
2106 if (!pl
.szUserName
) return E_INVALIDARG
;
2107 addr
= pl
.pUserName
;
2108 size
= pl
.szUserName
;
2111 case URL_PART_PASSWORD
:
2112 if (!pl
.szPassword
) return E_INVALIDARG
;
2113 addr
= pl
.pPassword
;
2114 size
= pl
.szPassword
;
2118 if (!pl
.szPort
) return E_INVALIDARG
;
2123 case URL_PART_QUERY
:
2124 if (!pl
.szQuery
) return E_INVALIDARG
;
2130 return E_INVALIDARG
;
2133 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2134 if (*pcchOut
< schsize
+ size
+ 2) {
2135 *pcchOut
= schsize
+ size
+ 2;
2138 memcpy(pszOut
, schaddr
, schsize
*sizeof(WCHAR
));
2139 pszOut
[schsize
] = ':';
2140 memcpy(pszOut
+schsize
+1, addr
, size
*sizeof(WCHAR
));
2141 pszOut
[schsize
+1+size
] = 0;
2142 *pcchOut
= schsize
+ 1 + size
;
2145 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2146 memcpy(pszOut
, addr
, size
*sizeof(WCHAR
));
2150 TRACE("len=%d %s\n", *pcchOut
, debugstr_w(pszOut
));
2151 }else if(dwPart
==URL_PART_HOSTNAME
&& scheme
==URL_SCHEME_FILE
) {
2161 /*************************************************************************
2162 * PathIsURLA [SHLWAPI.@]
2164 * Check if the given path is a Url.
2167 * lpszPath [I] Path to check.
2170 * TRUE if lpszPath is a Url.
2171 * FALSE if lpszPath is NULL or not a Url.
2173 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2178 TRACE("%s\n", debugstr_a(lpstrPath
));
2180 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2183 base
.cbSize
= sizeof(base
);
2184 hres
= ParseURLA(lpstrPath
, &base
);
2185 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2188 /*************************************************************************
2189 * PathIsURLW [SHLWAPI.@]
2193 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2198 TRACE("%s\n", debugstr_w(lpstrPath
));
2200 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2203 base
.cbSize
= sizeof(base
);
2204 hres
= ParseURLW(lpstrPath
, &base
);
2205 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2208 /*************************************************************************
2209 * UrlCreateFromPathA [SHLWAPI.@]
2211 * See UrlCreateFromPathW
2213 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2215 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2217 UNICODE_STRING pathW
;
2219 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2221 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2222 return E_INVALIDARG
;
2223 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2224 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2225 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2227 if(ret
== S_OK
|| ret
== S_FALSE
) {
2228 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2229 if(*pcchUrl
> lenA
) {
2230 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2234 *pcchUrl
= lenA
+ 1;
2238 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2239 RtlFreeUnicodeString(&pathW
);
2243 /*************************************************************************
2244 * UrlCreateFromPathW [SHLWAPI.@]
2246 * Create a Url from a file path.
2249 * pszPath [I] Path to convert
2250 * pszUrl [O] Destination for the converted Url
2251 * pcchUrl [I/O] Length of pszUrl
2252 * dwReserved [I] Reserved, must be 0
2255 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2256 * Failure: An HRESULT error code.
2258 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2263 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
2264 WCHAR three_slashesW
[] = {'/','/','/',0};
2265 PARSEDURLW parsed_url
;
2267 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2269 /* Validate arguments */
2270 if (dwReserved
!= 0)
2271 return E_INVALIDARG
;
2272 if (!pszUrl
|| !pcchUrl
)
2273 return E_INVALIDARG
;
2276 parsed_url
.cbSize
= sizeof(parsed_url
);
2277 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
2278 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
2279 needed
= strlenW(pszPath
);
2280 if (needed
>= *pcchUrl
) {
2281 *pcchUrl
= needed
+ 1;
2285 strcpyW(pszUrl
, pszPath
);
2291 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
2292 strcpyW(pszNewUrl
, file_colonW
);
2293 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
2294 strcatW(pszNewUrl
, three_slashesW
);
2295 strcatW(pszNewUrl
, pszPath
);
2296 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
2298 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
2302 /*************************************************************************
2303 * SHAutoComplete [SHLWAPI.@]
2305 * Enable auto-completion for an edit control.
2308 * hwndEdit [I] Handle of control to enable auto-completion for
2309 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2312 * Success: S_OK. Auto-completion is enabled for the control.
2313 * Failure: An HRESULT error code indicating the error.
2315 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2317 FIXME("SHAutoComplete stub\n");
2321 /*************************************************************************
2322 * MLBuildResURLA [SHLWAPI.405]
2324 * Create a Url pointing to a resource in a module.
2327 * lpszLibName [I] Name of the module containing the resource
2328 * hMod [I] Callers module handle
2329 * dwFlags [I] Undocumented flags for loading the module
2330 * lpszRes [I] Resource name
2331 * lpszDest [O] Destination for resulting Url
2332 * dwDestLen [I] Length of lpszDest
2335 * Success: S_OK. lpszDest contains the resource Url.
2336 * Failure: E_INVALIDARG, if any argument is invalid, or
2337 * E_FAIL if dwDestLen is too small.
2339 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2340 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2342 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2346 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2349 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2351 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2352 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2354 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2355 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2356 if (SUCCEEDED(hRet
) && lpszDest
)
2357 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, 0, 0);
2362 /*************************************************************************
2363 * MLBuildResURLA [SHLWAPI.406]
2365 * See MLBuildResURLA.
2367 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2368 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2370 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2371 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2372 HRESULT hRet
= E_FAIL
;
2374 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2375 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2377 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2378 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2379 return E_INVALIDARG
;
2381 if (dwDestLen
>= szResLen
+ 1)
2383 dwDestLen
-= (szResLen
+ 1);
2384 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2386 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2390 WCHAR szBuff
[MAX_PATH
];
2393 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2394 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2396 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2398 if (dwDestLen
>= dwPathLen
)
2402 dwDestLen
-= dwPathLen
;
2403 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2405 dwResLen
= strlenW(lpszRes
) + 1;
2406 if (dwDestLen
>= dwResLen
+ 1)
2408 lpszDest
[szResLen
+ dwPathLen
-1] = '/';
2409 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2414 MLFreeLibrary(hMod
);
2420 /***********************************************************************
2421 * UrlFixupW [SHLWAPI.462]
2423 * Checks the scheme part of a URL and attempts to correct misspellings.
2426 * lpszUrl [I] Pointer to the URL to be corrected
2427 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2428 * dwMaxChars [I] Maximum size of corrected URL
2431 * success: S_OK if URL corrected or already correct
2432 * failure: S_FALSE if unable to correct / COM error code if other error
2435 HRESULT WINAPI
UrlFixupW(LPCWSTR url
, LPWSTR translatedUrl
, DWORD maxChars
)
2439 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url
), translatedUrl
, maxChars
);
2444 srcLen
= lstrlenW(url
);
2446 /* For now just copy the URL directly */
2447 lstrcpynW(translatedUrl
, url
, (maxChars
< srcLen
) ? maxChars
: srcLen
);