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 (base
.nScheme
== URL_SCHEME_HTTP
|| base
.nScheme
== URL_SCHEME_HTTPS
)
734 manual_search
= TRUE
;
735 else if (work
- base
.pszSuffix
> len_htmW
* sizeof(WCHAR
)) {
737 if (strncmpiW(work
, htmW
, len_htmW
) == 0)
738 manual_search
= TRUE
;
742 if (!manual_search
&&
743 work
- base
.pszSuffix
> len_htmlW
* sizeof(WCHAR
)) {
745 if (strncmpiW(work
, htmlW
, len_htmlW
) == 0)
746 manual_search
= TRUE
;
752 /* search backwards starting from the current position */
753 while (*work
!= '/' && work
> base
.pszSuffix
+ sizeloc
)
755 base
.cchSuffix
= work
- base
.pszSuffix
+ 1;
757 /* search backwards starting from the end of the string */
758 work
= strrchrW((base
.pszSuffix
+sizeloc
), '/');
760 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
761 base
.cchSuffix
= len
;
763 base
.cchSuffix
= sizeloc
;
768 * .pszSuffix points to location (starting with '//')
769 * .cchSuffix length of location (above) and rest less the last
771 * sizeloc length of location (above) up to but not including
775 res2
= ParseURLW(mrelative
, &relative
);
777 /* no scheme in pszRelative */
778 TRACE("no scheme detected in Relative\n");
779 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
780 relative
.cchSuffix
= strlenW(mrelative
);
781 if (*pszRelative
== ':') {
782 /* case that is either left alone or uses pszBase */
783 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
790 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == ':')) {
791 /* case that becomes "file:///" */
792 strcpyW(preliminary
, myfilestr
);
796 if ((*mrelative
== '/') && (*(mrelative
+1) == '/')) {
797 /* pszRelative has location and rest */
801 if (*mrelative
== '/') {
802 /* case where pszRelative is root to location */
806 if (*mrelative
== '#') {
807 if(!(work
= strchrW(base
.pszSuffix
+base
.cchSuffix
, '#')))
808 work
= (LPWSTR
)base
.pszSuffix
+ strlenW(base
.pszSuffix
);
810 memcpy(preliminary
, base
.pszProtocol
, (work
-base
.pszProtocol
)*sizeof(WCHAR
));
811 preliminary
[work
-base
.pszProtocol
] = '\0';
815 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
819 /* handle cases where pszRelative has scheme */
820 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
821 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
823 /* since the schemes are the same */
824 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
825 /* case where pszRelative replaces location and following */
829 if (*relative
.pszSuffix
== '/') {
830 /* case where pszRelative is root to location */
834 /* replace either just location if base's location starts with a
835 * slash or otherwise everything */
836 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
839 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
840 /* case where pszRelative replaces scheme, location,
841 * and following and handles PLUGGABLE
848 } while(FALSE
); /* a little trick to allow easy exit from nested if's */
851 switch (process_case
) {
854 * Return pszRelative appended to what ever is in pszCombined,
855 * (which may the string "file:///"
857 strcatW(preliminary
, mrelative
);
860 case 2: /* case where pszRelative replaces scheme, and location */
861 strcpyW(preliminary
, mrelative
);
865 * Return the pszBase scheme with pszRelative. Basically
866 * keeps the scheme and replaces the domain and following.
868 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
869 work
= preliminary
+ base
.cchProtocol
+ 1;
870 strcpyW(work
, relative
.pszSuffix
);
874 * Return the pszBase scheme and location but everything
875 * after the location is pszRelative. (Replace document
878 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
879 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
880 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
882 strcpyW(work
, relative
.pszSuffix
);
886 * Return the pszBase without its document (if any) and
887 * append pszRelative after its scheme.
889 memcpy(preliminary
, base
.pszProtocol
,
890 (base
.cchProtocol
+1+base
.cchSuffix
)*sizeof(WCHAR
));
891 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
894 strcpyW(work
, relative
.pszSuffix
);
898 FIXME("How did we get here????? process_case=%d\n", process_case
);
903 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
904 if(*pcchCombined
== 0)
906 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, (dwFlags
& ~URL_FILE_USE_PATHURL
));
907 if(SUCCEEDED(ret
) && pszCombined
) {
908 lstrcpyW(pszCombined
, mrelative
);
910 TRACE("return-%d len=%d, %s\n",
911 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
913 HeapFree(GetProcessHeap(), 0, preliminary
);
917 /*************************************************************************
918 * UrlEscapeA [SHLWAPI.@]
921 HRESULT WINAPI
UrlEscapeA(
927 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
928 WCHAR
*escapedW
= bufW
;
931 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
933 if (!pszEscaped
|| !pcchEscaped
|| !*pcchEscaped
)
936 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
938 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
939 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
940 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
943 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
944 if(*pcchEscaped
> lenA
) {
945 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
946 pszEscaped
[lenA
] = 0;
949 *pcchEscaped
= lenA
+ 1;
953 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
954 RtlFreeUnicodeString(&urlW
);
958 #define WINE_URL_BASH_AS_SLASH 0x01
959 #define WINE_URL_COLLAPSE_SLASHES 0x02
960 #define WINE_URL_ESCAPE_SLASH 0x04
961 #define WINE_URL_ESCAPE_HASH 0x08
962 #define WINE_URL_ESCAPE_QUESTION 0x10
963 #define WINE_URL_STOP_ON_HASH 0x20
964 #define WINE_URL_STOP_ON_QUESTION 0x40
966 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
, DWORD int_flags
)
972 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
979 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
982 if (ch
<= 31 || ch
>= 127)
1003 if (int_flags
& WINE_URL_ESCAPE_SLASH
) return TRUE
;
1007 if (int_flags
& WINE_URL_ESCAPE_QUESTION
) return TRUE
;
1011 if (int_flags
& WINE_URL_ESCAPE_HASH
) return TRUE
;
1021 /*************************************************************************
1022 * UrlEscapeW [SHLWAPI.@]
1024 * Converts unsafe characters in a Url into escape sequences.
1027 * pszUrl [I] Url to modify
1028 * pszEscaped [O] Destination for modified Url
1029 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1030 * dwFlags [I] URL_ flags from "shlwapi.h"
1033 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1034 * contains its length.
1035 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1036 * pcchEscaped is set to the required length.
1038 * Converts unsafe characters into their escape sequences.
1041 * - By default this function stops converting at the first '?' or
1043 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1044 * converted, but the conversion continues past a '?' or '#'.
1045 * - Note that this function did not work well (or at all) in shlwapi version 4.
1048 * Only the following flags are implemented:
1049 *| URL_ESCAPE_SPACES_ONLY
1050 *| URL_DONT_ESCAPE_EXTRA_INFO
1051 *| URL_ESCAPE_SEGMENT_ONLY
1052 *| URL_ESCAPE_PERCENT
1054 HRESULT WINAPI
UrlEscapeW(
1057 LPDWORD pcchEscaped
,
1061 DWORD needed
= 0, ret
;
1062 BOOL stop_escaping
= FALSE
;
1063 WCHAR next
[5], *dst
= pszEscaped
;
1065 PARSEDURLW parsed_url
;
1068 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
1070 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl
, debugstr_w(pszUrl
),
1071 pszEscaped
, pcchEscaped
, dwFlags
);
1073 if(!pszUrl
|| !pcchEscaped
)
1074 return E_INVALIDARG
;
1076 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
1077 URL_ESCAPE_SEGMENT_ONLY
|
1078 URL_DONT_ESCAPE_EXTRA_INFO
|
1079 URL_ESCAPE_PERCENT
))
1080 FIXME("Unimplemented flags: %08x\n", dwFlags
);
1082 if(pszUrl
== pszEscaped
) {
1083 dst
= HeapAlloc(GetProcessHeap(), 0, *pcchEscaped
*sizeof(WCHAR
));
1085 return E_OUTOFMEMORY
;
1089 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
1090 /* if SPACES_ONLY specified, reset the other controls */
1091 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
1092 URL_ESCAPE_PERCENT
|
1093 URL_ESCAPE_SEGMENT_ONLY
);
1096 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1097 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
1101 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
1102 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
1104 parsed_url
.cbSize
= sizeof(parsed_url
);
1105 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
1106 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
1108 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
1110 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
1111 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
1113 switch(parsed_url
.nScheme
) {
1114 case URL_SCHEME_FILE
:
1115 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
1116 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
1119 case URL_SCHEME_HTTP
:
1120 case URL_SCHEME_HTTPS
:
1121 int_flags
|= WINE_URL_BASH_AS_SLASH
;
1122 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
1123 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1126 case URL_SCHEME_MAILTO
:
1127 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
1128 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
1131 case URL_SCHEME_INVALID
:
1134 case URL_SCHEME_FTP
:
1136 if(parsed_url
.pszSuffix
[0] != '/')
1137 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1142 for(src
= pszUrl
; *src
; ) {
1146 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
1147 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
1148 while(cur
== '/' || cur
== '\\') {
1152 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
1153 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
1154 src
+= localhost_len
+ 1;
1161 next
[0] = next
[1] = next
[2] = '/';
1168 next
[0] = next
[1] = '/';
1175 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1176 stop_escaping
= TRUE
;
1178 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1179 stop_escaping
= TRUE
;
1181 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1183 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1185 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1186 next
[2] = hexDigits
[cur
& 0xf];
1195 if(needed
+ len
<= *pcchEscaped
) {
1196 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1202 if(needed
< *pcchEscaped
) {
1204 if(pszUrl
== pszEscaped
)
1205 memcpy(pszEscaped
, dst
-needed
, (needed
+1)*sizeof(WCHAR
));
1209 needed
++; /* add one for the '\0' */
1212 *pcchEscaped
= needed
;
1214 if(pszUrl
== pszEscaped
)
1215 HeapFree(GetProcessHeap(), 0, dst
);
1220 /*************************************************************************
1221 * UrlUnescapeA [SHLWAPI.@]
1223 * Converts Url escape sequences back to ordinary characters.
1226 * pszUrl [I/O] Url to convert
1227 * pszUnescaped [O] Destination for converted Url
1228 * pcchUnescaped [I/O] Size of output string
1229 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1232 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1233 * dwFlags includes URL_ESCAPE_INPLACE.
1234 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1235 * this case pcchUnescaped is set to the size required.
1237 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1238 * the first occurrence of either a '?' or '#' character.
1240 HRESULT WINAPI
UrlUnescapeA(
1243 LPDWORD pcchUnescaped
,
1250 BOOL stop_unescaping
= FALSE
;
1252 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl
), pszUnescaped
,
1253 pcchUnescaped
, dwFlags
);
1255 if (!pszUrl
) return E_INVALIDARG
;
1257 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1261 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1265 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1266 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1267 (*src
== '#' || *src
== '?')) {
1268 stop_unescaping
= TRUE
;
1270 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1271 && stop_unescaping
== FALSE
) {
1274 memcpy(buf
, src
+ 1, 2);
1276 ih
= strtol(buf
, NULL
, 16);
1278 src
+= 2; /* Advance to end of escape */
1282 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1286 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1290 needed
++; /* add one for the '\0' */
1293 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1294 *pcchUnescaped
= needed
;
1297 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1298 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1304 /*************************************************************************
1305 * UrlUnescapeW [SHLWAPI.@]
1309 HRESULT WINAPI
UrlUnescapeW(
1311 LPWSTR pszUnescaped
,
1312 LPDWORD pcchUnescaped
,
1319 BOOL stop_unescaping
= FALSE
;
1321 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl
), pszUnescaped
,
1322 pcchUnescaped
, dwFlags
);
1324 if(!pszUrl
) return E_INVALIDARG
;
1326 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1330 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1334 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1335 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1336 (*src
== '#' || *src
== '?')) {
1337 stop_unescaping
= TRUE
;
1339 } else if(*src
== '%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1340 && stop_unescaping
== FALSE
) {
1342 WCHAR buf
[5] = {'0','x',0};
1343 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1345 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1347 src
+= 2; /* Advance to end of escape */
1351 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1355 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1359 needed
++; /* add one for the '\0' */
1362 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1363 *pcchUnescaped
= needed
;
1366 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1367 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1373 /*************************************************************************
1374 * UrlGetLocationA [SHLWAPI.@]
1376 * Get the location from a Url.
1379 * pszUrl [I] Url to get the location from
1382 * A pointer to the start of the location in pszUrl, or NULL if there is
1386 * - MSDN erroneously states that "The location is the segment of the Url
1387 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1388 * stop at '?' and always return a NULL in this case.
1389 * - MSDN also erroneously states that "If a file URL has a query string,
1390 * the returned string is the query string". In all tested cases, if the
1391 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1394 *| NULL file://aa/b/cd#hohoh
1395 *| #hohoh http://aa/b/cd#hohoh
1396 *| NULL fi://aa/b/cd#hohoh
1397 *| #hohoh ff://aa/b/cd#hohoh
1399 LPCSTR WINAPI
UrlGetLocationA(
1405 base
.cbSize
= sizeof(base
);
1406 res1
= ParseURLA(pszUrl
, &base
);
1407 if (res1
) return NULL
; /* invalid scheme */
1409 /* if scheme is file: then never return pointer */
1410 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1412 /* Look for '#' and return its addr */
1413 return strchr(base
.pszSuffix
, '#');
1416 /*************************************************************************
1417 * UrlGetLocationW [SHLWAPI.@]
1419 * See UrlGetLocationA.
1421 LPCWSTR WINAPI
UrlGetLocationW(
1427 base
.cbSize
= sizeof(base
);
1428 res1
= ParseURLW(pszUrl
, &base
);
1429 if (res1
) return NULL
; /* invalid scheme */
1431 /* if scheme is file: then never return pointer */
1432 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1434 /* Look for '#' and return its addr */
1435 return strchrW(base
.pszSuffix
, '#');
1438 /*************************************************************************
1439 * UrlCompareA [SHLWAPI.@]
1444 * pszUrl1 [I] First Url to compare
1445 * pszUrl2 [I] Url to compare to pszUrl1
1446 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1449 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1450 * than, equal to, or less than pszUrl1 respectively.
1452 INT WINAPI
UrlCompareA(
1457 INT ret
, len
, len1
, len2
;
1460 return strcmp(pszUrl1
, pszUrl2
);
1461 len1
= strlen(pszUrl1
);
1462 if (pszUrl1
[len1
-1] == '/') len1
--;
1463 len2
= strlen(pszUrl2
);
1464 if (pszUrl2
[len2
-1] == '/') len2
--;
1466 return strncmp(pszUrl1
, pszUrl2
, len1
);
1467 len
= min(len1
, len2
);
1468 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1469 if (ret
) return ret
;
1470 if (len1
> len2
) return 1;
1474 /*************************************************************************
1475 * UrlCompareW [SHLWAPI.@]
1479 INT WINAPI
UrlCompareW(
1485 size_t len
, len1
, len2
;
1488 return strcmpW(pszUrl1
, pszUrl2
);
1489 len1
= strlenW(pszUrl1
);
1490 if (pszUrl1
[len1
-1] == '/') len1
--;
1491 len2
= strlenW(pszUrl2
);
1492 if (pszUrl2
[len2
-1] == '/') len2
--;
1494 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1495 len
= min(len1
, len2
);
1496 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1497 if (ret
) return ret
;
1498 if (len1
> len2
) return 1;
1502 /*************************************************************************
1503 * HashData [SHLWAPI.@]
1505 * Hash an input block into a variable sized digest.
1508 * lpSrc [I] Input block
1509 * nSrcLen [I] Length of lpSrc
1510 * lpDest [I] Output for hash digest
1511 * nDestLen [I] Length of lpDest
1514 * Success: TRUE. lpDest is filled with the computed hash value.
1515 * Failure: FALSE, if any argument is invalid.
1517 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1518 unsigned char *lpDest
, DWORD nDestLen
)
1520 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1522 if (!lpSrc
|| !lpDest
)
1523 return E_INVALIDARG
;
1525 while (destCount
>= 0)
1527 lpDest
[destCount
] = (destCount
& 0xff);
1531 while (srcCount
>= 0)
1533 destCount
= nDestLen
- 1;
1534 while (destCount
>= 0)
1536 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1544 /*************************************************************************
1545 * UrlHashA [SHLWAPI.@]
1547 * Produce a Hash from a Url.
1550 * pszUrl [I] Url to hash
1551 * lpDest [O] Destinationh for hash
1552 * nDestLen [I] Length of lpDest
1555 * Success: S_OK. lpDest is filled with the computed hash value.
1556 * Failure: E_INVALIDARG, if any argument is invalid.
1558 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1560 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1561 return E_INVALIDARG
;
1563 HashData((const BYTE
*)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1567 /*************************************************************************
1568 * UrlHashW [SHLWAPI.@]
1572 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1574 char szUrl
[MAX_PATH
];
1576 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1578 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1579 return E_INVALIDARG
;
1581 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1582 * return the same digests for the same URL.
1584 WideCharToMultiByte(0, 0, pszUrl
, -1, szUrl
, MAX_PATH
, 0, 0);
1585 HashData((const BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1589 /*************************************************************************
1590 * UrlApplySchemeA [SHLWAPI.@]
1592 * Apply a scheme to a Url.
1595 * pszIn [I] Url to apply scheme to
1596 * pszOut [O] Destination for modified Url
1597 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1598 * dwFlags [I] URL_ flags from "shlwapi.h"
1601 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1602 * Failure: An HRESULT error code describing the error.
1604 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1610 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn
),
1611 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1613 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1615 in
= HeapAlloc(GetProcessHeap(), 0,
1616 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1617 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1619 MultiByteToWideChar(CP_ACP
, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1620 len
= INTERNET_MAX_URL_LENGTH
;
1622 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1624 HeapFree(GetProcessHeap(), 0, in
);
1628 len
= WideCharToMultiByte(CP_ACP
, 0, out
, -1, NULL
, 0, NULL
, NULL
);
1629 if (len
> *pcchOut
) {
1634 WideCharToMultiByte(CP_ACP
, 0, out
, -1, pszOut
, *pcchOut
, NULL
, NULL
);
1639 HeapFree(GetProcessHeap(), 0, in
);
1643 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1648 DWORD value_len
, data_len
, dwType
, i
;
1649 WCHAR reg_path
[MAX_PATH
];
1650 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1653 MultiByteToWideChar(0, 0,
1654 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1655 -1, reg_path
, MAX_PATH
);
1656 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1658 while(value_len
= data_len
= MAX_PATH
,
1659 RegEnumValueW(newkey
, index
, value
, &value_len
,
1660 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1661 TRACE("guess %d %s is %s\n",
1662 index
, debugstr_w(value
), debugstr_w(data
));
1665 for(i
=0; i
<value_len
; i
++) {
1668 /* remember that TRUE is not-equal */
1669 j
= ChrCmpIW(Wxx
, Wyy
);
1672 if ((i
== value_len
) && !j
) {
1673 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1674 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1675 RegCloseKey(newkey
);
1678 strcpyW(pszOut
, data
);
1679 strcatW(pszOut
, pszIn
);
1680 *pcchOut
= strlenW(pszOut
);
1681 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1682 RegCloseKey(newkey
);
1687 RegCloseKey(newkey
);
1691 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1694 DWORD data_len
, dwType
;
1695 WCHAR data
[MAX_PATH
];
1697 static const WCHAR prefix_keyW
[] =
1698 {'S','o','f','t','w','a','r','e',
1699 '\\','M','i','c','r','o','s','o','f','t',
1700 '\\','W','i','n','d','o','w','s',
1701 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1703 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1705 /* get and prepend default */
1706 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefix_keyW
, 0, 1, &newkey
);
1707 data_len
= sizeof(data
);
1708 RegQueryValueExW(newkey
, NULL
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1709 RegCloseKey(newkey
);
1710 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1711 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1714 strcpyW(pszOut
, data
);
1715 strcatW(pszOut
, pszIn
);
1716 *pcchOut
= strlenW(pszOut
);
1717 TRACE("used default %s\n", debugstr_w(pszOut
));
1721 /*************************************************************************
1722 * UrlApplySchemeW [SHLWAPI.@]
1724 * See UrlApplySchemeA.
1726 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1728 PARSEDURLW in_scheme
;
1732 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn
),
1733 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1735 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1737 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1738 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1739 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwFlags
);
1740 strcpyW(pszOut
, pszIn
);
1741 *pcchOut
= strlenW(pszOut
);
1745 in_scheme
.cbSize
= sizeof(in_scheme
);
1746 /* See if the base has a scheme */
1747 res1
= ParseURLW(pszIn
, &in_scheme
);
1749 /* no scheme in input, need to see if we need to guess */
1750 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1751 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != E_FAIL
)
1756 /* we have a scheme, see if valid (known scheme) */
1757 if (in_scheme
.nScheme
) {
1758 /* have valid scheme, so just copy and exit */
1759 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1760 *pcchOut
= strlenW(pszIn
) + 1;
1763 strcpyW(pszOut
, pszIn
);
1764 *pcchOut
= strlenW(pszOut
);
1765 TRACE("valid scheme, returning copy\n");
1770 /* If we are here, then either invalid scheme,
1771 * or no scheme and can't/failed guess.
1773 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1775 (dwFlags
& URL_APPLY_DEFAULT
)) {
1776 /* find and apply default scheme */
1777 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1783 /*************************************************************************
1784 * UrlIsA [SHLWAPI.@]
1786 * Determine if a Url is of a certain class.
1789 * pszUrl [I] Url to check
1790 * Urlis [I] URLIS_ constant from "shlwapi.h"
1793 * TRUE if pszUrl belongs to the class type in Urlis.
1796 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1802 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1810 base
.cbSize
= sizeof(base
);
1811 res1
= ParseURLA(pszUrl
, &base
);
1812 if (res1
) return FALSE
; /* invalid scheme */
1813 switch (base
.nScheme
)
1815 case URL_SCHEME_MAILTO
:
1816 case URL_SCHEME_SHELL
:
1817 case URL_SCHEME_JAVASCRIPT
:
1818 case URL_SCHEME_VBSCRIPT
:
1819 case URL_SCHEME_ABOUT
:
1825 return !StrCmpNA("file:", pszUrl
, 5);
1827 case URLIS_DIRECTORY
:
1828 last
= pszUrl
+ strlen(pszUrl
) - 1;
1829 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1832 return PathIsURLA(pszUrl
);
1834 case URLIS_NOHISTORY
:
1835 case URLIS_APPLIABLE
:
1836 case URLIS_HASQUERY
:
1838 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1843 /*************************************************************************
1844 * UrlIsW [SHLWAPI.@]
1848 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1850 static const WCHAR stemp
[] = { 'f','i','l','e',':',0 };
1855 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1863 base
.cbSize
= sizeof(base
);
1864 res1
= ParseURLW(pszUrl
, &base
);
1865 if (res1
) return FALSE
; /* invalid scheme */
1866 switch (base
.nScheme
)
1868 case URL_SCHEME_MAILTO
:
1869 case URL_SCHEME_SHELL
:
1870 case URL_SCHEME_JAVASCRIPT
:
1871 case URL_SCHEME_VBSCRIPT
:
1872 case URL_SCHEME_ABOUT
:
1878 return !strncmpW(stemp
, pszUrl
, 5);
1880 case URLIS_DIRECTORY
:
1881 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1882 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1885 return PathIsURLW(pszUrl
);
1887 case URLIS_NOHISTORY
:
1888 case URLIS_APPLIABLE
:
1889 case URLIS_HASQUERY
:
1891 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1896 /*************************************************************************
1897 * UrlIsNoHistoryA [SHLWAPI.@]
1899 * Determine if a Url should not be stored in the users history list.
1902 * pszUrl [I] Url to check
1905 * TRUE, if pszUrl should be excluded from the history list,
1908 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1910 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1913 /*************************************************************************
1914 * UrlIsNoHistoryW [SHLWAPI.@]
1916 * See UrlIsNoHistoryA.
1918 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1920 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1923 /*************************************************************************
1924 * UrlIsOpaqueA [SHLWAPI.@]
1926 * Determine if a Url is opaque.
1929 * pszUrl [I] Url to check
1932 * TRUE if pszUrl is opaque,
1936 * An opaque Url is one that does not start with "<protocol>://".
1938 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1940 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1943 /*************************************************************************
1944 * UrlIsOpaqueW [SHLWAPI.@]
1948 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1950 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1953 /*************************************************************************
1954 * Scans for characters of type "type" and when not matching found,
1955 * returns pointer to it and length in size.
1957 * Characters tested based on RFC 1738
1959 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1961 static DWORD alwayszero
= 0;
1970 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1989 if ( isalphaW(*start
) ||
1991 /* user/password only characters */
1996 /* *extra* characters */
2003 /* *safe* characters */
2012 } else if (*start
== '%') {
2013 if (isxdigitW(*(start
+1)) &&
2014 isxdigitW(*(start
+2))) {
2026 if (isdigitW(*start
)) {
2037 if (isalnumW(*start
) ||
2049 FIXME("unknown type %d\n", type
);
2050 return (LPWSTR
)&alwayszero
;
2052 /* TRACE("scanned %d characters next char %p<%c>\n",
2053 *size, start, *start); */
2057 /*************************************************************************
2058 * Attempt to parse URL into pieces.
2060 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
2064 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
2065 pl
->pScheme
= pszUrl
;
2066 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
2067 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
2069 if ((*work
!= '/') || (*(work
+1) != '/')) goto SuccessExit
;
2070 pl
->pUserName
= work
+ 2;
2071 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
2072 if (*work
== ':' ) {
2073 /* parse password */
2075 pl
->pPassword
= work
;
2076 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
2078 /* what we just parsed must be the hostname and port
2079 * so reset pointers and clear then let it parse */
2080 pl
->szUserName
= pl
->szPassword
= 0;
2081 work
= pl
->pUserName
- 1;
2082 pl
->pUserName
= pl
->pPassword
= 0;
2084 } else if (*work
== '@') {
2088 } else if (!*work
|| (*work
== '/') || (*work
== '.')) {
2089 /* what was parsed was hostname, so reset pointers and let it parse */
2090 pl
->szUserName
= pl
->szPassword
= 0;
2091 work
= pl
->pUserName
- 1;
2092 pl
->pUserName
= pl
->pPassword
= 0;
2093 } else goto ErrorExit
;
2095 /* now start parsing hostname or hostnumber */
2097 pl
->pHostName
= work
;
2098 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
2103 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
2106 /* see if query string */
2107 pl
->pQuery
= strchrW(work
, '?');
2108 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
2111 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2112 pl
->pScheme
, pl
->szScheme
,
2113 pl
->pUserName
, pl
->szUserName
,
2114 pl
->pPassword
, pl
->szPassword
,
2115 pl
->pHostName
, pl
->szHostName
,
2116 pl
->pPort
, pl
->szPort
,
2117 pl
->pQuery
, pl
->szQuery
);
2120 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
2121 return E_INVALIDARG
;
2124 /*************************************************************************
2125 * UrlGetPartA [SHLWAPI.@]
2127 * Retrieve part of a Url.
2130 * pszIn [I] Url to parse
2131 * pszOut [O] Destination for part of pszIn requested
2132 * pcchOut [I] Size of pszOut
2133 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2134 * needed size of pszOut INCLUDING '\0'.
2135 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2136 * dwFlags [I] URL_ flags from "shlwapi.h"
2139 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2140 * Failure: An HRESULT error code describing the error.
2142 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
2143 DWORD dwPart
, DWORD dwFlags
)
2146 DWORD ret
, len
, len2
;
2148 if(!pszIn
|| !pszOut
|| !pcchOut
|| *pcchOut
<= 0)
2149 return E_INVALIDARG
;
2151 in
= HeapAlloc(GetProcessHeap(), 0,
2152 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
2153 out
= in
+ INTERNET_MAX_URL_LENGTH
;
2155 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
2157 len
= INTERNET_MAX_URL_LENGTH
;
2158 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
2161 HeapFree(GetProcessHeap(), 0, in
);
2165 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
2166 if (len2
> *pcchOut
) {
2168 HeapFree(GetProcessHeap(), 0, in
);
2171 len2
= WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
2173 HeapFree(GetProcessHeap(), 0, in
);
2177 /*************************************************************************
2178 * UrlGetPartW [SHLWAPI.@]
2182 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2183 DWORD dwPart
, DWORD dwFlags
)
2187 DWORD scheme
, size
, schsize
;
2188 LPCWSTR addr
, schaddr
;
2190 TRACE("(%s %p %p(%d) %08x %08x)\n",
2191 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2193 if(!pszIn
|| !pszOut
|| !pcchOut
|| *pcchOut
<= 0)
2194 return E_INVALIDARG
;
2198 addr
= strchrW(pszIn
, ':');
2200 scheme
= URL_SCHEME_UNKNOWN
;
2202 scheme
= get_scheme_code(pszIn
, addr
-pszIn
);
2204 ret
= URL_ParseUrl(pszIn
, &pl
);
2207 case URL_PART_SCHEME
:
2216 case URL_PART_HOSTNAME
:
2218 case URL_SCHEME_FTP
:
2219 case URL_SCHEME_HTTP
:
2220 case URL_SCHEME_GOPHER
:
2221 case URL_SCHEME_TELNET
:
2222 case URL_SCHEME_FILE
:
2223 case URL_SCHEME_HTTPS
:
2230 if(scheme
==URL_SCHEME_FILE
&& (!pl
.szHostName
||
2231 (pl
.szHostName
==1 && *(pl
.pHostName
+1)==':'))) {
2236 if (!pl
.szHostName
) {
2240 addr
= pl
.pHostName
;
2241 size
= pl
.szHostName
;
2244 case URL_PART_USERNAME
:
2245 if (!pl
.szUserName
) {
2249 addr
= pl
.pUserName
;
2250 size
= pl
.szUserName
;
2253 case URL_PART_PASSWORD
:
2254 if (!pl
.szPassword
) {
2258 addr
= pl
.pPassword
;
2259 size
= pl
.szPassword
;
2271 case URL_PART_QUERY
:
2282 return E_INVALIDARG
;
2285 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2286 if(!pl
.pScheme
|| !pl
.szScheme
) {
2290 schaddr
= pl
.pScheme
;
2291 schsize
= pl
.szScheme
;
2292 if (*pcchOut
< schsize
+ size
+ 2) {
2293 *pcchOut
= schsize
+ size
+ 2;
2296 memcpy(pszOut
, schaddr
, schsize
*sizeof(WCHAR
));
2297 pszOut
[schsize
] = ':';
2298 memcpy(pszOut
+schsize
+1, addr
, size
*sizeof(WCHAR
));
2299 pszOut
[schsize
+1+size
] = 0;
2300 *pcchOut
= schsize
+ 1 + size
;
2303 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2304 memcpy(pszOut
, addr
, size
*sizeof(WCHAR
));
2308 TRACE("len=%d %s\n", *pcchOut
, debugstr_w(pszOut
));
2313 /*************************************************************************
2314 * PathIsURLA [SHLWAPI.@]
2316 * Check if the given path is a Url.
2319 * lpszPath [I] Path to check.
2322 * TRUE if lpszPath is a Url.
2323 * FALSE if lpszPath is NULL or not a Url.
2325 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2330 TRACE("%s\n", debugstr_a(lpstrPath
));
2332 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2335 base
.cbSize
= sizeof(base
);
2336 hres
= ParseURLA(lpstrPath
, &base
);
2337 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2340 /*************************************************************************
2341 * PathIsURLW [SHLWAPI.@]
2345 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2350 TRACE("%s\n", debugstr_w(lpstrPath
));
2352 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2355 base
.cbSize
= sizeof(base
);
2356 hres
= ParseURLW(lpstrPath
, &base
);
2357 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2360 /*************************************************************************
2361 * UrlCreateFromPathA [SHLWAPI.@]
2363 * See UrlCreateFromPathW
2365 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2367 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2369 UNICODE_STRING pathW
;
2371 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2373 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2374 return E_INVALIDARG
;
2375 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2376 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2377 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2379 if(ret
== S_OK
|| ret
== S_FALSE
) {
2380 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2381 if(*pcchUrl
> lenA
) {
2382 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2386 *pcchUrl
= lenA
+ 1;
2390 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2391 RtlFreeUnicodeString(&pathW
);
2395 /*************************************************************************
2396 * UrlCreateFromPathW [SHLWAPI.@]
2398 * Create a Url from a file path.
2401 * pszPath [I] Path to convert
2402 * pszUrl [O] Destination for the converted Url
2403 * pcchUrl [I/O] Length of pszUrl
2404 * dwReserved [I] Reserved, must be 0
2407 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2408 * Failure: An HRESULT error code.
2410 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2415 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
2416 WCHAR three_slashesW
[] = {'/','/','/',0};
2417 PARSEDURLW parsed_url
;
2419 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2421 /* Validate arguments */
2422 if (dwReserved
!= 0)
2423 return E_INVALIDARG
;
2424 if (!pszUrl
|| !pcchUrl
)
2425 return E_INVALIDARG
;
2428 parsed_url
.cbSize
= sizeof(parsed_url
);
2429 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
2430 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
2431 needed
= strlenW(pszPath
);
2432 if (needed
>= *pcchUrl
) {
2433 *pcchUrl
= needed
+ 1;
2437 strcpyW(pszUrl
, pszPath
);
2443 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
2444 strcpyW(pszNewUrl
, file_colonW
);
2445 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
2446 strcatW(pszNewUrl
, three_slashesW
);
2447 strcatW(pszNewUrl
, pszPath
);
2448 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
2450 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
2454 /*************************************************************************
2455 * SHAutoComplete [SHLWAPI.@]
2457 * Enable auto-completion for an edit control.
2460 * hwndEdit [I] Handle of control to enable auto-completion for
2461 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2464 * Success: S_OK. Auto-completion is enabled for the control.
2465 * Failure: An HRESULT error code indicating the error.
2467 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2473 /*************************************************************************
2474 * MLBuildResURLA [SHLWAPI.405]
2476 * Create a Url pointing to a resource in a module.
2479 * lpszLibName [I] Name of the module containing the resource
2480 * hMod [I] Callers module handle
2481 * dwFlags [I] Undocumented flags for loading the module
2482 * lpszRes [I] Resource name
2483 * lpszDest [O] Destination for resulting Url
2484 * dwDestLen [I] Length of lpszDest
2487 * Success: S_OK. lpszDest contains the resource Url.
2488 * Failure: E_INVALIDARG, if any argument is invalid, or
2489 * E_FAIL if dwDestLen is too small.
2491 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2492 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2494 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2498 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2501 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2503 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2504 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2506 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2507 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2508 if (SUCCEEDED(hRet
) && lpszDest
)
2509 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, 0, 0);
2514 /*************************************************************************
2515 * MLBuildResURLA [SHLWAPI.406]
2517 * See MLBuildResURLA.
2519 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2520 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2522 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2523 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2524 HRESULT hRet
= E_FAIL
;
2526 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2527 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2529 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2530 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2531 return E_INVALIDARG
;
2533 if (dwDestLen
>= szResLen
+ 1)
2535 dwDestLen
-= (szResLen
+ 1);
2536 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2538 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2542 WCHAR szBuff
[MAX_PATH
];
2545 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2546 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2548 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2550 if (dwDestLen
>= dwPathLen
)
2554 dwDestLen
-= dwPathLen
;
2555 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2557 dwResLen
= strlenW(lpszRes
) + 1;
2558 if (dwDestLen
>= dwResLen
+ 1)
2560 lpszDest
[szResLen
+ dwPathLen
-1] = '/';
2561 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2566 MLFreeLibrary(hMod
);
2572 /***********************************************************************
2573 * UrlFixupW [SHLWAPI.462]
2575 * Checks the scheme part of a URL and attempts to correct misspellings.
2578 * lpszUrl [I] Pointer to the URL to be corrected
2579 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2580 * dwMaxChars [I] Maximum size of corrected URL
2583 * success: S_OK if URL corrected or already correct
2584 * failure: S_FALSE if unable to correct / COM error code if other error
2587 HRESULT WINAPI
UrlFixupW(LPCWSTR url
, LPWSTR translatedUrl
, DWORD maxChars
)
2591 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url
), translatedUrl
, maxChars
);
2596 srcLen
= lstrlenW(url
) + 1;
2598 /* For now just copy the URL directly */
2599 lstrcpynW(translatedUrl
, url
, (maxChars
< srcLen
) ? maxChars
: srcLen
);