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
26 HMODULE WINAPI
MLLoadLibraryW(LPCWSTR
,HMODULE
,DWORD
);
27 BOOL WINAPI
MLFreeLibrary(HMODULE
);
28 HRESULT WINAPI
MLBuildResURLW(LPCWSTR
,HMODULE
,DWORD
,LPCWSTR
,LPWSTR
,DWORD
);
30 static inline WCHAR
*heap_strdupAtoW(const char *str
)
37 len
= MultiByteToWideChar(CP_ACP
, 0, str
, -1, NULL
, 0);
38 ret
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
39 MultiByteToWideChar(CP_ACP
, 0, str
, -1, ret
, len
);
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
;
238 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl
), pszCanonicalized
,
239 pcchCanonicalized
, dwFlags
, pcchCanonicalized
? *pcchCanonicalized
: -1);
241 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
|| !*pcchCanonicalized
)
244 url
= heap_strdupAtoW(pszUrl
);
245 canonical
= HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized
*sizeof(WCHAR
));
246 if(!url
|| !canonical
) {
247 HeapFree(GetProcessHeap(), 0, url
);
248 HeapFree(GetProcessHeap(), 0, canonical
);
249 return E_OUTOFMEMORY
;
252 ret
= UrlCanonicalizeW(url
, canonical
, pcchCanonicalized
, dwFlags
);
254 WideCharToMultiByte(CP_ACP
, 0, canonical
, -1, pszCanonicalized
,
255 *pcchCanonicalized
+1, NULL
, NULL
);
257 HeapFree(GetProcessHeap(), 0, url
);
258 HeapFree(GetProcessHeap(), 0, canonical
);
262 /*************************************************************************
263 * UrlCanonicalizeW [SHLWAPI.@]
265 * See UrlCanonicalizeA.
267 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
268 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
273 LPWSTR lpszUrlCpy
, url
, wk2
, mp
, mp2
;
275 DWORD nByteLen
, nLen
, nWkLen
;
279 static const WCHAR wszFile
[] = {'f','i','l','e',':'};
280 static const WCHAR wszRes
[] = {'r','e','s',':'};
281 static const WCHAR wszHttp
[] = {'h','t','t','p',':'};
282 static const WCHAR wszLocalhost
[] = {'l','o','c','a','l','h','o','s','t'};
283 static const WCHAR wszFilePrefix
[] = {'f','i','l','e',':','/','/','/'};
285 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl
), pszCanonicalized
,
286 pcchCanonicalized
, dwFlags
, pcchCanonicalized
? *pcchCanonicalized
: -1);
288 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
|| !*pcchCanonicalized
)
292 *pszCanonicalized
= 0;
296 /* Remove '\t' characters from URL */
297 nByteLen
= (strlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
298 url
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
300 return E_OUTOFMEMORY
;
310 /* Allocate memory for simplified URL (before escaping) */
311 nByteLen
= (wk2
-url
)*sizeof(WCHAR
);
312 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0,
313 nByteLen
+sizeof(wszFilePrefix
)+sizeof(WCHAR
));
315 HeapFree(GetProcessHeap(), 0, url
);
316 return E_OUTOFMEMORY
;
319 is_file_url
= !strncmpW(wszFile
, url
, sizeof(wszFile
)/sizeof(WCHAR
));
321 if ((nByteLen
>= sizeof(wszHttp
) &&
322 !memcmp(wszHttp
, url
, sizeof(wszHttp
))) || is_file_url
)
325 if((dwFlags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
)) && is_file_url
)
328 if(nByteLen
>= sizeof(wszRes
) && !memcmp(wszRes
, url
, sizeof(wszRes
))) {
329 dwFlags
&= ~URL_FILE_USE_PATHURL
;
336 * 1 have 2[+] alnum 2,3
337 * 2 have scheme (found :) 4,6,3
338 * 3 failed (no location)
340 * 5 have 1[+] alnum 6,3
341 * 6 have location (found /) save root location
348 if(url
[1] == ':') { /* Assume path */
349 memcpy(wk2
, wszFilePrefix
, sizeof(wszFilePrefix
));
350 wk2
+= sizeof(wszFilePrefix
)/sizeof(WCHAR
);
351 if (dwFlags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
))
357 dwFlags
|= URL_ESCAPE_UNSAFE
;
365 if (!isalnumW(*wk1
)) {state
= 3; break;}
367 if (!isalnumW(*wk1
)) {state
= 3; break;}
373 if (*wk1
++ == ':') state
= 2;
377 if (*wk1
!= '/') {state
= 6; break;}
379 if((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszLocalhost
)
381 && !memcmp(wszLocalhost
, wk1
, sizeof(wszLocalhost
))){
382 wk1
+= sizeof(wszLocalhost
)/sizeof(WCHAR
);
383 while(*wk1
== '\\' && (dwFlags
& URL_FILE_USE_PATHURL
))
387 if(*wk1
== '/' && (dwFlags
& URL_FILE_USE_PATHURL
)){
389 }else if(is_file_url
){
390 const WCHAR
*body
= wk1
;
395 if(isalnumW(*body
) && *(body
+1) == ':'){
396 if(!(dwFlags
& (URL_WININET_COMPATIBILITY
| URL_FILE_USE_PATHURL
))){
403 if(dwFlags
& URL_WININET_COMPATIBILITY
){
404 if(*wk1
== '/' && *(wk1
+1) != '/'){
411 if(*wk1
== '/' && *(wk1
+1) != '/'){
424 nWkLen
= strlenW(wk1
);
425 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
432 if(*mp
== '/' || *mp
== '\\')
439 if (!isalnumW(*wk1
) && (*wk1
!= '-') && (*wk1
!= '.') && (*wk1
!= ':'))
441 while(isalnumW(*wk1
) || (*wk1
== '-') || (*wk1
== '.') || (*wk1
== ':'))
452 if (*wk1
!= '/' && *wk1
!= '\\') {state
= 3; break;}
453 while(*wk1
== '/' || *wk1
== '\\') {
463 if(dwFlags
& URL_DONT_SIMPLIFY
) {
468 /* Now at root location, cannot back up any more. */
469 /* "root" will point at the '/' */
473 mp
= strchrW(wk1
, '/');
474 mp2
= strchrW(wk1
, '\\');
475 if(mp2
&& (!mp
|| mp2
< mp
))
478 nWkLen
= strlenW(wk1
);
479 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
486 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
496 while (*wk1
== '.') {
497 TRACE("found '/.'\n");
498 if (wk1
[1] == '/' || wk1
[1] == '\\') {
499 /* case of /./ -> skip the ./ */
502 else if (wk1
[1] == '.' && (wk1
[2] == '/'
503 || wk1
[2] == '\\' || wk1
[2] == '?'
504 || wk1
[2] == '#' || !wk1
[2])) {
505 /* case /../ -> need to backup wk2 */
506 TRACE("found '/../'\n");
507 *(wk2
-1) = '\0'; /* set end of string */
508 mp
= strrchrW(root
, '/');
509 mp2
= strrchrW(root
, '\\');
510 if(mp2
&& (!mp
|| mp2
< mp
))
512 if (mp
&& (mp
>= root
)) {
513 /* found valid backup point */
515 if(wk1
[2] != '/' && wk1
[2] != '\\')
521 /* did not find point, restore '/' */
533 FIXME("how did we get here - state=%d\n", state
);
534 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
535 HeapFree(GetProcessHeap(), 0, url
);
539 TRACE("Simplified, orig <%s>, simple <%s>\n",
540 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
542 nLen
= lstrlenW(lpszUrlCpy
);
543 while ((nLen
> 0) && ((lpszUrlCpy
[nLen
-1] <= ' ')))
544 lpszUrlCpy
[--nLen
]=0;
546 if((dwFlags
& URL_UNESCAPE
) ||
547 ((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszFile
)
548 && !memcmp(wszFile
, url
, sizeof(wszFile
))))
549 UrlUnescapeW(lpszUrlCpy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
551 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
552 URL_ESCAPE_SPACES_ONLY
|
554 URL_DONT_ESCAPE_EXTRA_INFO
|
555 URL_ESCAPE_SEGMENT_ONLY
))) {
556 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
557 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
559 } else { /* No escaping needed, just copy the string */
560 nLen
= lstrlenW(lpszUrlCpy
);
561 if(nLen
< *pcchCanonicalized
)
562 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
567 *pcchCanonicalized
= nLen
;
570 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
571 HeapFree(GetProcessHeap(), 0, url
);
574 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
579 /*************************************************************************
580 * UrlCombineA [SHLWAPI.@]
585 * pszBase [I] Base Url
586 * pszRelative [I] Url to combine with pszBase
587 * pszCombined [O] Destination for combined Url
588 * pcchCombined [O] Destination for length of pszCombined
589 * dwFlags [I] URL_ flags from "shlwapi.h"
592 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
593 * contains its length.
594 * Failure: An HRESULT error code indicating the error.
596 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
597 LPSTR pszCombined
, LPDWORD pcchCombined
,
600 LPWSTR base
, relative
, combined
;
601 DWORD ret
, len
, len2
;
603 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
604 debugstr_a(pszBase
),debugstr_a(pszRelative
),
605 pcchCombined
?*pcchCombined
:0,dwFlags
);
607 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
610 base
= HeapAlloc(GetProcessHeap(), 0,
611 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
612 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
613 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
615 MultiByteToWideChar(CP_ACP
, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
616 MultiByteToWideChar(CP_ACP
, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
619 ret
= UrlCombineW(base
, relative
, pszCombined
?combined
:NULL
, &len
, dwFlags
);
622 HeapFree(GetProcessHeap(), 0, base
);
626 len2
= WideCharToMultiByte(CP_ACP
, 0, combined
, len
, NULL
, 0, NULL
, NULL
);
627 if (len2
> *pcchCombined
) {
628 *pcchCombined
= len2
;
629 HeapFree(GetProcessHeap(), 0, base
);
632 WideCharToMultiByte(CP_ACP
, 0, combined
, len
+1, pszCombined
, (*pcchCombined
)+1,
634 *pcchCombined
= len2
;
635 HeapFree(GetProcessHeap(), 0, base
);
639 /*************************************************************************
640 * UrlCombineW [SHLWAPI.@]
644 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
645 LPWSTR pszCombined
, LPDWORD pcchCombined
,
648 PARSEDURLW base
, relative
;
649 DWORD myflags
, sizeloc
= 0;
650 DWORD i
, len
, res1
, res2
, process_case
= 0;
651 LPWSTR work
, preliminary
, mbase
, mrelative
;
652 static const WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
653 static const WCHAR fragquerystr
[] = {'#','?',0};
656 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
657 debugstr_w(pszBase
),debugstr_w(pszRelative
),
658 pcchCombined
?*pcchCombined
:0,dwFlags
);
660 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
663 base
.cbSize
= sizeof(base
);
664 relative
.cbSize
= sizeof(relative
);
666 /* Get space for duplicates of the input and the output */
667 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
669 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
670 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
673 /* Canonicalize the base input prior to looking for the scheme */
674 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
675 len
= INTERNET_MAX_URL_LENGTH
;
676 UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
678 /* Canonicalize the relative input prior to looking for the scheme */
679 len
= INTERNET_MAX_URL_LENGTH
;
680 UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
682 /* See if the base has a scheme */
683 res1
= ParseURLW(mbase
, &base
);
685 /* if pszBase has no scheme, then return pszRelative */
686 TRACE("no scheme detected in Base\n");
690 BOOL manual_search
= FALSE
;
692 work
= (LPWSTR
)base
.pszProtocol
;
693 for(i
=0; i
<base
.cchProtocol
; i
++)
694 work
[i
] = tolowerW(work
[i
]);
696 /* mk is a special case */
697 if(base
.nScheme
== URL_SCHEME_MK
) {
698 static const WCHAR wsz
[] = {':',':',0};
700 WCHAR
*ptr
= strstrW(base
.pszSuffix
, wsz
);
705 delta
= ptr
-base
.pszSuffix
;
706 base
.cchProtocol
+= delta
;
707 base
.pszSuffix
+= delta
;
708 base
.cchSuffix
-= delta
;
711 /* get size of location field (if it exists) */
712 work
= (LPWSTR
)base
.pszSuffix
;
714 if (*work
++ == '/') {
715 if (*work
++ == '/') {
716 /* At this point have start of location and
717 * it ends at next '/' or end of string.
719 while(*work
&& (*work
!= '/')) work
++;
720 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
725 /* If there is a '?', then the remaining part can only contain a
726 * query string or fragment, so start looking for the last leaf
727 * from the '?'. Otherwise, if there is a '#' and the characters
728 * immediately preceding it are ".htm[l]", then begin looking for
729 * the last leaf starting from the '#'. Otherwise the '#' is not
730 * meaningful and just start looking from the end. */
731 if ((work
= strpbrkW(base
.pszSuffix
+ sizeloc
, fragquerystr
))) {
732 const WCHAR htmlW
[] = {'.','h','t','m','l',0};
733 const int len_htmlW
= 5;
734 const WCHAR htmW
[] = {'.','h','t','m',0};
735 const int len_htmW
= 4;
737 if (*work
== '?' || base
.nScheme
== URL_SCHEME_HTTP
|| base
.nScheme
== URL_SCHEME_HTTPS
)
738 manual_search
= TRUE
;
739 else if (work
- base
.pszSuffix
> len_htmW
) {
741 if (strncmpiW(work
, htmW
, len_htmW
) == 0)
742 manual_search
= TRUE
;
746 if (!manual_search
&&
747 work
- base
.pszSuffix
> len_htmlW
) {
749 if (strncmpiW(work
, htmlW
, len_htmlW
) == 0)
750 manual_search
= TRUE
;
756 /* search backwards starting from the current position */
757 while (*work
!= '/' && work
> base
.pszSuffix
+ sizeloc
)
759 base
.cchSuffix
= work
- base
.pszSuffix
+ 1;
761 /* search backwards starting from the end of the string */
762 work
= strrchrW((base
.pszSuffix
+sizeloc
), '/');
764 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
765 base
.cchSuffix
= len
;
767 base
.cchSuffix
= sizeloc
;
772 * .pszSuffix points to location (starting with '//')
773 * .cchSuffix length of location (above) and rest less the last
775 * sizeloc length of location (above) up to but not including
779 res2
= ParseURLW(mrelative
, &relative
);
781 /* no scheme in pszRelative */
782 TRACE("no scheme detected in Relative\n");
783 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
784 relative
.cchSuffix
= strlenW(mrelative
);
785 if (*pszRelative
== ':') {
786 /* case that is either left alone or uses pszBase */
787 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
794 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == ':')) {
795 /* case that becomes "file:///" */
796 strcpyW(preliminary
, myfilestr
);
800 if ((*mrelative
== '/') && (*(mrelative
+1) == '/')) {
801 /* pszRelative has location and rest */
805 if (*mrelative
== '/') {
806 /* case where pszRelative is root to location */
810 if (*mrelative
== '#') {
811 if(!(work
= strchrW(base
.pszSuffix
+base
.cchSuffix
, '#')))
812 work
= (LPWSTR
)base
.pszSuffix
+ strlenW(base
.pszSuffix
);
814 memcpy(preliminary
, base
.pszProtocol
, (work
-base
.pszProtocol
)*sizeof(WCHAR
));
815 preliminary
[work
-base
.pszProtocol
] = '\0';
819 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
822 work
= (LPWSTR
)relative
.pszProtocol
;
823 for(i
=0; i
<relative
.cchProtocol
; i
++)
824 work
[i
] = tolowerW(work
[i
]);
827 /* handle cases where pszRelative has scheme */
828 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
829 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
831 /* since the schemes are the same */
832 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
833 /* case where pszRelative replaces location and following */
837 if (*relative
.pszSuffix
== '/') {
838 /* case where pszRelative is root to location */
842 /* replace either just location if base's location starts with a
843 * slash or otherwise everything */
844 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
847 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
848 /* case where pszRelative replaces scheme, location,
849 * and following and handles PLUGGABLE
856 } while(FALSE
); /* a little trick to allow easy exit from nested if's */
859 switch (process_case
) {
862 * Return pszRelative appended to what ever is in pszCombined,
863 * (which may the string "file:///"
865 strcatW(preliminary
, mrelative
);
868 case 2: /* case where pszRelative replaces scheme, and location */
869 strcpyW(preliminary
, mrelative
);
873 * Return the pszBase scheme with pszRelative. Basically
874 * keeps the scheme and replaces the domain and following.
876 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
877 work
= preliminary
+ base
.cchProtocol
+ 1;
878 strcpyW(work
, relative
.pszSuffix
);
882 * Return the pszBase scheme and location but everything
883 * after the location is pszRelative. (Replace document
886 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
887 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
888 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
890 strcpyW(work
, relative
.pszSuffix
);
894 * Return the pszBase without its document (if any) and
895 * append pszRelative after its scheme.
897 memcpy(preliminary
, base
.pszProtocol
,
898 (base
.cchProtocol
+1+base
.cchSuffix
)*sizeof(WCHAR
));
899 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
902 if (relative
.pszSuffix
[0] == '.' && relative
.pszSuffix
[1] == 0)
905 strcpyW(work
, relative
.pszSuffix
);
909 FIXME("How did we get here????? process_case=%d\n", process_case
);
914 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
915 if(*pcchCombined
== 0)
917 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, (dwFlags
& ~URL_FILE_USE_PATHURL
));
918 if(SUCCEEDED(ret
) && pszCombined
) {
919 lstrcpyW(pszCombined
, mrelative
);
921 TRACE("return-%d len=%d, %s\n",
922 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
924 HeapFree(GetProcessHeap(), 0, preliminary
);
928 /*************************************************************************
929 * UrlEscapeA [SHLWAPI.@]
932 HRESULT WINAPI
UrlEscapeA(
938 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
939 WCHAR
*escapedW
= bufW
;
942 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
944 if (!pszEscaped
|| !pcchEscaped
|| !*pcchEscaped
)
947 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
949 if(dwFlags
& URL_ESCAPE_AS_UTF8
)
951 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
952 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
953 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
956 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
957 if(*pcchEscaped
> lenA
) {
958 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
959 pszEscaped
[lenA
] = 0;
962 *pcchEscaped
= lenA
+ 1;
966 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
967 RtlFreeUnicodeString(&urlW
);
971 #define WINE_URL_BASH_AS_SLASH 0x01
972 #define WINE_URL_COLLAPSE_SLASHES 0x02
973 #define WINE_URL_ESCAPE_SLASH 0x04
974 #define WINE_URL_ESCAPE_HASH 0x08
975 #define WINE_URL_ESCAPE_QUESTION 0x10
976 #define WINE_URL_STOP_ON_HASH 0x20
977 #define WINE_URL_STOP_ON_QUESTION 0x40
979 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD flags
, DWORD int_flags
)
981 if (flags
& URL_ESCAPE_SPACES_ONLY
)
984 if ((flags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
987 if ((flags
& URL_ESCAPE_AS_UTF8
) && (ch
>= 0x80))
990 if (ch
<= 31 || (ch
>= 127 && ch
<= 255) )
1012 return !!(int_flags
& WINE_URL_ESCAPE_SLASH
);
1014 return !!(int_flags
& WINE_URL_ESCAPE_QUESTION
);
1016 return !!(int_flags
& WINE_URL_ESCAPE_HASH
);
1023 /*************************************************************************
1024 * UrlEscapeW [SHLWAPI.@]
1026 * Converts unsafe characters in a Url into escape sequences.
1029 * pszUrl [I] Url to modify
1030 * pszEscaped [O] Destination for modified Url
1031 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1032 * dwFlags [I] URL_ flags from "shlwapi.h"
1035 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1036 * contains its length.
1037 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1038 * pcchEscaped is set to the required length.
1040 * Converts unsafe characters into their escape sequences.
1043 * - By default this function stops converting at the first '?' or
1045 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1046 * converted, but the conversion continues past a '?' or '#'.
1047 * - Note that this function did not work well (or at all) in shlwapi version 4.
1050 * Only the following flags are implemented:
1051 *| URL_ESCAPE_SPACES_ONLY
1052 *| URL_DONT_ESCAPE_EXTRA_INFO
1053 *| URL_ESCAPE_SEGMENT_ONLY
1054 *| URL_ESCAPE_PERCENT
1056 HRESULT WINAPI
UrlEscapeW(
1059 LPDWORD pcchEscaped
,
1063 DWORD needed
= 0, ret
;
1064 BOOL stop_escaping
= FALSE
;
1065 WCHAR next
[12], *dst
, *dst_ptr
;
1067 PARSEDURLW parsed_url
;
1070 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
1072 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl
, debugstr_w(pszUrl
),
1073 pszEscaped
, pcchEscaped
, dwFlags
);
1075 if(!pszUrl
|| !pcchEscaped
)
1076 return E_INVALIDARG
;
1078 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
1079 URL_ESCAPE_SEGMENT_ONLY
|
1080 URL_DONT_ESCAPE_EXTRA_INFO
|
1081 URL_ESCAPE_PERCENT
|
1082 URL_ESCAPE_AS_UTF8
))
1083 FIXME("Unimplemented flags: %08x\n", dwFlags
);
1085 dst_ptr
= dst
= HeapAlloc(GetProcessHeap(), 0, *pcchEscaped
*sizeof(WCHAR
));
1087 return E_OUTOFMEMORY
;
1090 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
1091 /* if SPACES_ONLY specified, reset the other controls */
1092 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
1093 URL_ESCAPE_PERCENT
|
1094 URL_ESCAPE_SEGMENT_ONLY
);
1097 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1098 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
1102 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
1103 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
1105 parsed_url
.cbSize
= sizeof(parsed_url
);
1106 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
1107 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
1109 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
1111 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
1112 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
1114 switch(parsed_url
.nScheme
) {
1115 case URL_SCHEME_FILE
:
1116 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
1117 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
1120 case URL_SCHEME_HTTP
:
1121 case URL_SCHEME_HTTPS
:
1122 int_flags
|= WINE_URL_BASH_AS_SLASH
;
1123 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
1124 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1127 case URL_SCHEME_MAILTO
:
1128 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
1129 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
1132 case URL_SCHEME_INVALID
:
1135 case URL_SCHEME_FTP
:
1137 if(parsed_url
.pszSuffix
[0] != '/')
1138 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1143 for(src
= pszUrl
; *src
; ) {
1147 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
1148 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
1149 while(cur
== '/' || cur
== '\\') {
1153 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
1154 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
1155 src
+= localhost_len
+ 1;
1162 next
[0] = next
[1] = next
[2] = '/';
1169 next
[0] = next
[1] = '/';
1176 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1177 stop_escaping
= TRUE
;
1179 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1180 stop_escaping
= TRUE
;
1182 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1184 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1185 if(dwFlags
& URL_ESCAPE_AS_UTF8
) {
1188 if ((cur
>= 0xd800 && cur
<= 0xdfff) &&
1189 (src
[1] >= 0xdc00 && src
[1] <= 0xdfff))
1191 len
= WideCharToMultiByte( CP_UTF8
, WC_ERR_INVALID_CHARS
, src
, 2,
1192 utf
, sizeof(utf
), NULL
, NULL
);
1196 len
= WideCharToMultiByte( CP_UTF8
, WC_ERR_INVALID_CHARS
, &cur
, 1,
1197 utf
, sizeof(utf
), NULL
, NULL
);
1206 for(i
= 0; i
< len
; i
++) {
1208 next
[i
*3+1] = hexDigits
[(utf
[i
] >> 4) & 0xf];
1209 next
[i
*3+2] = hexDigits
[utf
[i
] & 0xf];
1214 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1215 next
[2] = hexDigits
[cur
& 0xf];
1225 if(needed
+ len
<= *pcchEscaped
) {
1226 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1232 if(needed
< *pcchEscaped
) {
1234 memcpy(pszEscaped
, dst_ptr
, (needed
+1)*sizeof(WCHAR
));
1238 needed
++; /* add one for the '\0' */
1241 *pcchEscaped
= needed
;
1243 HeapFree(GetProcessHeap(), 0, dst_ptr
);
1248 /*************************************************************************
1249 * UrlUnescapeA [SHLWAPI.@]
1251 * Converts Url escape sequences back to ordinary characters.
1254 * pszUrl [I/O] Url to convert
1255 * pszUnescaped [O] Destination for converted Url
1256 * pcchUnescaped [I/O] Size of output string
1257 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1260 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1261 * dwFlags includes URL_ESCAPE_INPLACE.
1262 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1263 * this case pcchUnescaped is set to the size required.
1265 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1266 * the first occurrence of either a '?' or '#' character.
1268 HRESULT WINAPI
UrlUnescapeA(
1271 LPDWORD pcchUnescaped
,
1278 BOOL stop_unescaping
= FALSE
;
1280 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl
), pszUnescaped
,
1281 pcchUnescaped
, dwFlags
);
1283 if (!pszUrl
) return E_INVALIDARG
;
1285 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1289 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1293 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1294 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1295 (*src
== '#' || *src
== '?')) {
1296 stop_unescaping
= TRUE
;
1298 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1299 && stop_unescaping
== FALSE
) {
1302 memcpy(buf
, src
+ 1, 2);
1304 ih
= strtol(buf
, NULL
, 16);
1306 src
+= 2; /* Advance to end of escape */
1310 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1314 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1318 needed
++; /* add one for the '\0' */
1321 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1322 *pcchUnescaped
= needed
;
1325 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1326 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1332 /*************************************************************************
1333 * UrlUnescapeW [SHLWAPI.@]
1337 HRESULT WINAPI
UrlUnescapeW(
1339 LPWSTR pszUnescaped
,
1340 LPDWORD pcchUnescaped
,
1347 BOOL stop_unescaping
= FALSE
;
1349 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl
), pszUnescaped
,
1350 pcchUnescaped
, dwFlags
);
1352 if(!pszUrl
) return E_INVALIDARG
;
1354 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1358 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1362 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1363 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1364 (*src
== '#' || *src
== '?')) {
1365 stop_unescaping
= TRUE
;
1367 } else if(*src
== '%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1368 && stop_unescaping
== FALSE
) {
1370 WCHAR buf
[5] = {'0','x',0};
1371 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1373 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1375 src
+= 2; /* Advance to end of escape */
1379 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1383 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1387 needed
++; /* add one for the '\0' */
1390 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1391 *pcchUnescaped
= needed
;
1394 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1395 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1401 /*************************************************************************
1402 * UrlGetLocationA [SHLWAPI.@]
1404 * Get the location from a Url.
1407 * pszUrl [I] Url to get the location from
1410 * A pointer to the start of the location in pszUrl, or NULL if there is
1414 * - MSDN erroneously states that "The location is the segment of the Url
1415 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1416 * stop at '?' and always return a NULL in this case.
1417 * - MSDN also erroneously states that "If a file URL has a query string,
1418 * the returned string is the query string". In all tested cases, if the
1419 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1422 *| NULL file://aa/b/cd#hohoh
1423 *| #hohoh http://aa/b/cd#hohoh
1424 *| NULL fi://aa/b/cd#hohoh
1425 *| #hohoh ff://aa/b/cd#hohoh
1427 LPCSTR WINAPI
UrlGetLocationA(
1433 base
.cbSize
= sizeof(base
);
1434 res1
= ParseURLA(pszUrl
, &base
);
1435 if (res1
) return NULL
; /* invalid scheme */
1437 /* if scheme is file: then never return pointer */
1438 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1440 /* Look for '#' and return its addr */
1441 return strchr(base
.pszSuffix
, '#');
1444 /*************************************************************************
1445 * UrlGetLocationW [SHLWAPI.@]
1447 * See UrlGetLocationA.
1449 LPCWSTR WINAPI
UrlGetLocationW(
1455 base
.cbSize
= sizeof(base
);
1456 res1
= ParseURLW(pszUrl
, &base
);
1457 if (res1
) return NULL
; /* invalid scheme */
1459 /* if scheme is file: then never return pointer */
1460 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1462 /* Look for '#' and return its addr */
1463 return strchrW(base
.pszSuffix
, '#');
1466 /*************************************************************************
1467 * UrlCompareA [SHLWAPI.@]
1472 * pszUrl1 [I] First Url to compare
1473 * pszUrl2 [I] Url to compare to pszUrl1
1474 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1477 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1478 * than, equal to, or less than pszUrl1 respectively.
1480 INT WINAPI
UrlCompareA(
1485 INT ret
, len
, len1
, len2
;
1488 return strcmp(pszUrl1
, pszUrl2
);
1489 len1
= strlen(pszUrl1
);
1490 if (pszUrl1
[len1
-1] == '/') len1
--;
1491 len2
= strlen(pszUrl2
);
1492 if (pszUrl2
[len2
-1] == '/') len2
--;
1494 return strncmp(pszUrl1
, pszUrl2
, len1
);
1495 len
= min(len1
, len2
);
1496 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1497 if (ret
) return ret
;
1498 if (len1
> len2
) return 1;
1502 /*************************************************************************
1503 * UrlCompareW [SHLWAPI.@]
1507 INT WINAPI
UrlCompareW(
1513 size_t len
, len1
, len2
;
1516 return strcmpW(pszUrl1
, pszUrl2
);
1517 len1
= strlenW(pszUrl1
);
1518 if (pszUrl1
[len1
-1] == '/') len1
--;
1519 len2
= strlenW(pszUrl2
);
1520 if (pszUrl2
[len2
-1] == '/') len2
--;
1522 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1523 len
= min(len1
, len2
);
1524 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1525 if (ret
) return ret
;
1526 if (len1
> len2
) return 1;
1530 /*************************************************************************
1531 * HashData [SHLWAPI.@]
1533 * Hash an input block into a variable sized digest.
1536 * lpSrc [I] Input block
1537 * nSrcLen [I] Length of lpSrc
1538 * lpDest [I] Output for hash digest
1539 * nDestLen [I] Length of lpDest
1542 * Success: TRUE. lpDest is filled with the computed hash value.
1543 * Failure: FALSE, if any argument is invalid.
1545 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1546 unsigned char *lpDest
, DWORD nDestLen
)
1548 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1550 if (!lpSrc
|| !lpDest
)
1551 return E_INVALIDARG
;
1553 while (destCount
>= 0)
1555 lpDest
[destCount
] = (destCount
& 0xff);
1559 while (srcCount
>= 0)
1561 destCount
= nDestLen
- 1;
1562 while (destCount
>= 0)
1564 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1572 /*************************************************************************
1573 * UrlHashA [SHLWAPI.@]
1575 * Produce a Hash from a Url.
1578 * pszUrl [I] Url to hash
1579 * lpDest [O] Destinationh for hash
1580 * nDestLen [I] Length of lpDest
1583 * Success: S_OK. lpDest is filled with the computed hash value.
1584 * Failure: E_INVALIDARG, if any argument is invalid.
1586 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1588 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1589 return E_INVALIDARG
;
1591 HashData((const BYTE
*)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1595 /*************************************************************************
1596 * UrlHashW [SHLWAPI.@]
1600 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1602 char szUrl
[MAX_PATH
];
1604 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1606 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1607 return E_INVALIDARG
;
1609 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1610 * return the same digests for the same URL.
1612 WideCharToMultiByte(CP_ACP
, 0, pszUrl
, -1, szUrl
, MAX_PATH
, NULL
, NULL
);
1613 HashData((const BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1617 /*************************************************************************
1618 * UrlApplySchemeA [SHLWAPI.@]
1620 * Apply a scheme to a Url.
1623 * pszIn [I] Url to apply scheme to
1624 * pszOut [O] Destination for modified Url
1625 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1626 * dwFlags [I] URL_ flags from "shlwapi.h"
1629 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1630 * Failure: An HRESULT error code describing the error.
1632 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1638 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn
),
1639 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1641 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1643 in
= HeapAlloc(GetProcessHeap(), 0,
1644 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1645 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1647 MultiByteToWideChar(CP_ACP
, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1648 len
= INTERNET_MAX_URL_LENGTH
;
1650 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1652 HeapFree(GetProcessHeap(), 0, in
);
1656 len
= WideCharToMultiByte(CP_ACP
, 0, out
, -1, NULL
, 0, NULL
, NULL
);
1657 if (len
> *pcchOut
) {
1662 WideCharToMultiByte(CP_ACP
, 0, out
, -1, pszOut
, *pcchOut
, NULL
, NULL
);
1667 HeapFree(GetProcessHeap(), 0, in
);
1671 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1676 DWORD value_len
, data_len
, dwType
, i
;
1677 WCHAR reg_path
[MAX_PATH
];
1678 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1681 MultiByteToWideChar(CP_ACP
, 0,
1682 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1683 -1, reg_path
, MAX_PATH
);
1684 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1686 while(value_len
= data_len
= MAX_PATH
,
1687 RegEnumValueW(newkey
, index
, value
, &value_len
,
1688 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1689 TRACE("guess %d %s is %s\n",
1690 index
, debugstr_w(value
), debugstr_w(data
));
1693 for(i
=0; i
<value_len
; i
++) {
1696 /* remember that TRUE is not-equal */
1697 j
= ChrCmpIW(Wxx
, Wyy
);
1700 if ((i
== value_len
) && !j
) {
1701 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1702 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1703 RegCloseKey(newkey
);
1706 strcpyW(pszOut
, data
);
1707 strcatW(pszOut
, pszIn
);
1708 *pcchOut
= strlenW(pszOut
);
1709 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1710 RegCloseKey(newkey
);
1715 RegCloseKey(newkey
);
1719 static HRESULT
URL_CreateFromPath(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
)
1724 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
1725 WCHAR three_slashesW
[] = {'/','/','/',0};
1726 PARSEDURLW parsed_url
;
1728 parsed_url
.cbSize
= sizeof(parsed_url
);
1729 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
1730 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
1731 needed
= strlenW(pszPath
);
1732 if (needed
>= *pcchUrl
) {
1733 *pcchUrl
= needed
+ 1;
1742 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
1743 strcpyW(pszNewUrl
, file_colonW
);
1744 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
1745 strcatW(pszNewUrl
, three_slashesW
);
1746 strcatW(pszNewUrl
, pszPath
);
1747 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
1748 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
1752 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1755 DWORD data_len
, dwType
;
1756 WCHAR data
[MAX_PATH
];
1758 static const WCHAR prefix_keyW
[] =
1759 {'S','o','f','t','w','a','r','e',
1760 '\\','M','i','c','r','o','s','o','f','t',
1761 '\\','W','i','n','d','o','w','s',
1762 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1764 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1766 /* get and prepend default */
1767 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefix_keyW
, 0, 1, &newkey
);
1768 data_len
= sizeof(data
);
1769 RegQueryValueExW(newkey
, NULL
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1770 RegCloseKey(newkey
);
1771 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1772 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1775 strcpyW(pszOut
, data
);
1776 strcatW(pszOut
, pszIn
);
1777 *pcchOut
= strlenW(pszOut
);
1778 TRACE("used default %s\n", debugstr_w(pszOut
));
1782 /*************************************************************************
1783 * UrlApplySchemeW [SHLWAPI.@]
1785 * See UrlApplySchemeA.
1787 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1789 PARSEDURLW in_scheme
;
1793 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn
),
1794 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1796 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1798 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1799 if (*pcchOut
> 1 && ':' == pszIn
[1]) {
1801 ret
= URL_CreateFromPath(pszIn
, pszOut
, &res1
);
1802 if (ret
== S_OK
|| ret
== E_POINTER
){
1806 else if (ret
== S_FALSE
)
1813 in_scheme
.cbSize
= sizeof(in_scheme
);
1814 /* See if the base has a scheme */
1815 res1
= ParseURLW(pszIn
, &in_scheme
);
1817 /* no scheme in input, need to see if we need to guess */
1818 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1819 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != E_FAIL
)
1824 /* If we are here, then either invalid scheme,
1825 * or no scheme and can't/failed guess.
1827 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1829 (dwFlags
& URL_APPLY_DEFAULT
)) {
1830 /* find and apply default scheme */
1831 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1837 /*************************************************************************
1838 * UrlIsA [SHLWAPI.@]
1840 * Determine if a Url is of a certain class.
1843 * pszUrl [I] Url to check
1844 * Urlis [I] URLIS_ constant from "shlwapi.h"
1847 * TRUE if pszUrl belongs to the class type in Urlis.
1850 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1856 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1864 base
.cbSize
= sizeof(base
);
1865 res1
= ParseURLA(pszUrl
, &base
);
1866 if (res1
) return FALSE
; /* invalid scheme */
1867 switch (base
.nScheme
)
1869 case URL_SCHEME_MAILTO
:
1870 case URL_SCHEME_SHELL
:
1871 case URL_SCHEME_JAVASCRIPT
:
1872 case URL_SCHEME_VBSCRIPT
:
1873 case URL_SCHEME_ABOUT
:
1879 return (CompareStringA(LOCALE_INVARIANT
, NORM_IGNORECASE
, pszUrl
, 5,
1880 "file:", 5) == CSTR_EQUAL
);
1882 case URLIS_DIRECTORY
:
1883 last
= pszUrl
+ strlen(pszUrl
) - 1;
1884 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1887 return PathIsURLA(pszUrl
);
1889 case URLIS_NOHISTORY
:
1890 case URLIS_APPLIABLE
:
1891 case URLIS_HASQUERY
:
1893 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1898 /*************************************************************************
1899 * UrlIsW [SHLWAPI.@]
1903 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1905 static const WCHAR file_colon
[] = { 'f','i','l','e',':',0 };
1910 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1918 base
.cbSize
= sizeof(base
);
1919 res1
= ParseURLW(pszUrl
, &base
);
1920 if (res1
) return FALSE
; /* invalid scheme */
1921 switch (base
.nScheme
)
1923 case URL_SCHEME_MAILTO
:
1924 case URL_SCHEME_SHELL
:
1925 case URL_SCHEME_JAVASCRIPT
:
1926 case URL_SCHEME_VBSCRIPT
:
1927 case URL_SCHEME_ABOUT
:
1933 return (CompareStringW(LOCALE_INVARIANT
, NORM_IGNORECASE
, pszUrl
, 5,
1934 file_colon
, 5) == CSTR_EQUAL
);
1936 case URLIS_DIRECTORY
:
1937 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1938 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1941 return PathIsURLW(pszUrl
);
1943 case URLIS_NOHISTORY
:
1944 case URLIS_APPLIABLE
:
1945 case URLIS_HASQUERY
:
1947 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1952 /*************************************************************************
1953 * UrlIsNoHistoryA [SHLWAPI.@]
1955 * Determine if a Url should not be stored in the users history list.
1958 * pszUrl [I] Url to check
1961 * TRUE, if pszUrl should be excluded from the history list,
1964 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1966 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1969 /*************************************************************************
1970 * UrlIsNoHistoryW [SHLWAPI.@]
1972 * See UrlIsNoHistoryA.
1974 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1976 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1979 /*************************************************************************
1980 * UrlIsOpaqueA [SHLWAPI.@]
1982 * Determine if a Url is opaque.
1985 * pszUrl [I] Url to check
1988 * TRUE if pszUrl is opaque,
1992 * An opaque Url is one that does not start with "<protocol>://".
1994 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1996 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1999 /*************************************************************************
2000 * UrlIsOpaqueW [SHLWAPI.@]
2004 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
2006 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
2009 /*************************************************************************
2010 * Scans for characters of type "type" and when not matching found,
2011 * returns pointer to it and length in size.
2013 * Characters tested based on RFC 1738
2015 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
2017 static DWORD alwayszero
= 0;
2026 if ( (islowerW(*start
) && isalphaW(*start
)) ||
2045 if ( isalphaW(*start
) ||
2047 /* user/password only characters */
2052 /* *extra* characters */
2059 /* *safe* characters */
2068 } else if (*start
== '%') {
2069 if (isxdigitW(*(start
+1)) &&
2070 isxdigitW(*(start
+2))) {
2082 if (isdigitW(*start
)) {
2093 if (isalnumW(*start
) ||
2106 FIXME("unknown type %d\n", type
);
2107 return (LPWSTR
)&alwayszero
;
2109 /* TRACE("scanned %d characters next char %p<%c>\n",
2110 *size, start, *start); */
2114 /*************************************************************************
2115 * Attempt to parse URL into pieces.
2117 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
2121 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
2122 pl
->pScheme
= pszUrl
;
2123 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
2124 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
2126 if ((*work
!= '/') || (*(work
+1) != '/')) goto SuccessExit
;
2127 pl
->pUserName
= work
+ 2;
2128 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
2129 if (*work
== ':' ) {
2130 /* parse password */
2132 pl
->pPassword
= work
;
2133 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
2135 /* what we just parsed must be the hostname and port
2136 * so reset pointers and clear then let it parse */
2137 pl
->szUserName
= pl
->szPassword
= 0;
2138 work
= pl
->pUserName
- 1;
2139 pl
->pUserName
= pl
->pPassword
= 0;
2141 } else if (*work
== '@') {
2145 } else if (!*work
|| (*work
== '/') || (*work
== '.')) {
2146 /* what was parsed was hostname, so reset pointers and let it parse */
2147 pl
->szUserName
= pl
->szPassword
= 0;
2148 work
= pl
->pUserName
- 1;
2149 pl
->pUserName
= pl
->pPassword
= 0;
2150 } else goto ErrorExit
;
2152 /* now start parsing hostname or hostnumber */
2154 pl
->pHostName
= work
;
2155 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
2160 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
2163 /* see if query string */
2164 pl
->pQuery
= strchrW(work
, '?');
2165 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
2168 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2169 pl
->pScheme
, pl
->szScheme
,
2170 pl
->pUserName
, pl
->szUserName
,
2171 pl
->pPassword
, pl
->szPassword
,
2172 pl
->pHostName
, pl
->szHostName
,
2173 pl
->pPort
, pl
->szPort
,
2174 pl
->pQuery
, pl
->szQuery
);
2177 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
2178 return E_INVALIDARG
;
2181 /*************************************************************************
2182 * UrlGetPartA [SHLWAPI.@]
2184 * Retrieve part of a Url.
2187 * pszIn [I] Url to parse
2188 * pszOut [O] Destination for part of pszIn requested
2189 * pcchOut [I] Size of pszOut
2190 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2191 * needed size of pszOut INCLUDING '\0'.
2192 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2193 * dwFlags [I] URL_ flags from "shlwapi.h"
2196 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2197 * Failure: An HRESULT error code describing the error.
2199 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
2200 DWORD dwPart
, DWORD dwFlags
)
2203 DWORD ret
, len
, len2
;
2205 if(!pszIn
|| !pszOut
|| !pcchOut
|| *pcchOut
<= 0)
2206 return E_INVALIDARG
;
2208 in
= HeapAlloc(GetProcessHeap(), 0,
2209 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
2210 out
= in
+ INTERNET_MAX_URL_LENGTH
;
2212 MultiByteToWideChar(CP_ACP
, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
2214 len
= INTERNET_MAX_URL_LENGTH
;
2215 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
2218 HeapFree(GetProcessHeap(), 0, in
);
2222 len2
= WideCharToMultiByte(CP_ACP
, 0, out
, len
, NULL
, 0, NULL
, NULL
);
2223 if (len2
> *pcchOut
) {
2225 HeapFree(GetProcessHeap(), 0, in
);
2228 len2
= WideCharToMultiByte(CP_ACP
, 0, out
, len
+1, pszOut
, *pcchOut
, NULL
, NULL
);
2230 HeapFree(GetProcessHeap(), 0, in
);
2234 /*************************************************************************
2235 * UrlGetPartW [SHLWAPI.@]
2239 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2240 DWORD dwPart
, DWORD dwFlags
)
2244 DWORD scheme
, size
, schsize
;
2245 LPCWSTR addr
, schaddr
;
2247 TRACE("(%s %p %p(%d) %08x %08x)\n",
2248 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2250 if(!pszIn
|| !pszOut
|| !pcchOut
|| *pcchOut
<= 0)
2251 return E_INVALIDARG
;
2255 addr
= strchrW(pszIn
, ':');
2257 scheme
= URL_SCHEME_UNKNOWN
;
2259 scheme
= get_scheme_code(pszIn
, addr
-pszIn
);
2261 ret
= URL_ParseUrl(pszIn
, &pl
);
2264 case URL_PART_SCHEME
:
2273 case URL_PART_HOSTNAME
:
2275 case URL_SCHEME_FTP
:
2276 case URL_SCHEME_HTTP
:
2277 case URL_SCHEME_GOPHER
:
2278 case URL_SCHEME_TELNET
:
2279 case URL_SCHEME_FILE
:
2280 case URL_SCHEME_HTTPS
:
2287 if(scheme
==URL_SCHEME_FILE
&& (!pl
.szHostName
||
2288 (pl
.szHostName
==1 && *(pl
.pHostName
+1)==':'))) {
2293 if (!pl
.szHostName
) {
2297 addr
= pl
.pHostName
;
2298 size
= pl
.szHostName
;
2301 case URL_PART_USERNAME
:
2302 if (!pl
.szUserName
) {
2306 addr
= pl
.pUserName
;
2307 size
= pl
.szUserName
;
2310 case URL_PART_PASSWORD
:
2311 if (!pl
.szPassword
) {
2315 addr
= pl
.pPassword
;
2316 size
= pl
.szPassword
;
2328 case URL_PART_QUERY
:
2339 return E_INVALIDARG
;
2342 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2343 if(!pl
.pScheme
|| !pl
.szScheme
) {
2347 schaddr
= pl
.pScheme
;
2348 schsize
= pl
.szScheme
;
2349 if (*pcchOut
< schsize
+ size
+ 2) {
2350 *pcchOut
= schsize
+ size
+ 2;
2353 memcpy(pszOut
, schaddr
, schsize
*sizeof(WCHAR
));
2354 pszOut
[schsize
] = ':';
2355 memcpy(pszOut
+schsize
+1, addr
, size
*sizeof(WCHAR
));
2356 pszOut
[schsize
+1+size
] = 0;
2357 *pcchOut
= schsize
+ 1 + size
;
2360 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2361 memcpy(pszOut
, addr
, size
*sizeof(WCHAR
));
2365 TRACE("len=%d %s\n", *pcchOut
, debugstr_w(pszOut
));
2370 /*************************************************************************
2371 * PathIsURLA [SHLWAPI.@]
2373 * Check if the given path is a Url.
2376 * lpszPath [I] Path to check.
2379 * TRUE if lpszPath is a Url.
2380 * FALSE if lpszPath is NULL or not a Url.
2382 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2387 TRACE("%s\n", debugstr_a(lpstrPath
));
2389 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2392 base
.cbSize
= sizeof(base
);
2393 hres
= ParseURLA(lpstrPath
, &base
);
2394 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2397 /*************************************************************************
2398 * PathIsURLW [SHLWAPI.@]
2402 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2407 TRACE("%s\n", debugstr_w(lpstrPath
));
2409 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2412 base
.cbSize
= sizeof(base
);
2413 hres
= ParseURLW(lpstrPath
, &base
);
2414 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2417 /*************************************************************************
2418 * UrlCreateFromPathA [SHLWAPI.@]
2420 * See UrlCreateFromPathW
2422 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2424 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2426 UNICODE_STRING pathW
;
2428 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2430 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2431 return E_INVALIDARG
;
2432 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2433 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2434 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2436 if(ret
== S_OK
|| ret
== S_FALSE
) {
2437 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2438 if(*pcchUrl
> lenA
) {
2439 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2443 *pcchUrl
= lenA
+ 1;
2447 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2448 RtlFreeUnicodeString(&pathW
);
2452 /*************************************************************************
2453 * UrlCreateFromPathW [SHLWAPI.@]
2455 * Create a Url from a file path.
2458 * pszPath [I] Path to convert
2459 * pszUrl [O] Destination for the converted Url
2460 * pcchUrl [I/O] Length of pszUrl
2461 * dwReserved [I] Reserved, must be 0
2464 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2465 * Failure: An HRESULT error code.
2467 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2471 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2473 /* Validate arguments */
2474 if (dwReserved
!= 0)
2475 return E_INVALIDARG
;
2476 if (!pszUrl
|| !pcchUrl
)
2477 return E_INVALIDARG
;
2479 ret
= URL_CreateFromPath(pszPath
, pszUrl
, pcchUrl
);
2482 strcpyW(pszUrl
, pszPath
);
2487 /*************************************************************************
2488 * SHAutoComplete [SHLWAPI.@]
2490 * Enable auto-completion for an edit control.
2493 * hwndEdit [I] Handle of control to enable auto-completion for
2494 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2497 * Success: S_OK. Auto-completion is enabled for the control.
2498 * Failure: An HRESULT error code indicating the error.
2500 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2506 /*************************************************************************
2507 * MLBuildResURLA [SHLWAPI.405]
2509 * Create a Url pointing to a resource in a module.
2512 * lpszLibName [I] Name of the module containing the resource
2513 * hMod [I] Callers module handle
2514 * dwFlags [I] Undocumented flags for loading the module
2515 * lpszRes [I] Resource name
2516 * lpszDest [O] Destination for resulting Url
2517 * dwDestLen [I] Length of lpszDest
2520 * Success: S_OK. lpszDest contains the resource Url.
2521 * Failure: E_INVALIDARG, if any argument is invalid, or
2522 * E_FAIL if dwDestLen is too small.
2524 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2525 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2527 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2531 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2534 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2536 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2537 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2539 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2540 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2541 if (SUCCEEDED(hRet
) && lpszDest
)
2542 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, NULL
, NULL
);
2547 /*************************************************************************
2548 * MLBuildResURLA [SHLWAPI.406]
2550 * See MLBuildResURLA.
2552 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2553 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2555 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2556 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2557 HRESULT hRet
= E_FAIL
;
2559 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2560 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2562 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2563 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2564 return E_INVALIDARG
;
2566 if (dwDestLen
>= szResLen
+ 1)
2568 dwDestLen
-= (szResLen
+ 1);
2569 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2571 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2575 WCHAR szBuff
[MAX_PATH
];
2578 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2579 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2581 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2583 if (dwDestLen
>= dwPathLen
)
2587 dwDestLen
-= dwPathLen
;
2588 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2590 dwResLen
= strlenW(lpszRes
) + 1;
2591 if (dwDestLen
>= dwResLen
+ 1)
2593 lpszDest
[szResLen
+ dwPathLen
-1] = '/';
2594 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2599 MLFreeLibrary(hMod
);
2605 /***********************************************************************
2606 * UrlFixupW [SHLWAPI.462]
2608 * Checks the scheme part of a URL and attempts to correct misspellings.
2611 * lpszUrl [I] Pointer to the URL to be corrected
2612 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2613 * dwMaxChars [I] Maximum size of corrected URL
2616 * success: S_OK if URL corrected or already correct
2617 * failure: S_FALSE if unable to correct / COM error code if other error
2620 HRESULT WINAPI
UrlFixupW(LPCWSTR url
, LPWSTR translatedUrl
, DWORD maxChars
)
2624 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url
), translatedUrl
, maxChars
);
2629 srcLen
= lstrlenW(url
) + 1;
2631 /* For now just copy the URL directly */
2632 lstrcpynW(translatedUrl
, url
, (maxChars
< srcLen
) ? maxChars
: srcLen
);
2637 /*************************************************************************
2638 * IsInternetESCEnabled [SHLWAPI.@]
2640 BOOL WINAPI
IsInternetESCEnabled(void)