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