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'};
655 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
656 debugstr_w(pszBase
),debugstr_w(pszRelative
),
657 pcchCombined
?*pcchCombined
:0,dwFlags
);
659 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
662 base
.cbSize
= sizeof(base
);
663 relative
.cbSize
= sizeof(relative
);
665 /* Get space for duplicates of the input and the output */
666 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
668 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
669 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
672 /* Canonicalize the base input prior to looking for the scheme */
673 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
674 len
= INTERNET_MAX_URL_LENGTH
;
675 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
677 /* Canonicalize the relative input prior to looking for the scheme */
678 len
= INTERNET_MAX_URL_LENGTH
;
679 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
681 /* See if the base has a scheme */
682 res1
= ParseURLW(mbase
, &base
);
684 /* if pszBase has no scheme, then return pszRelative */
685 TRACE("no scheme detected in Base\n");
689 BOOL manual_search
= FALSE
;
691 work
= (LPWSTR
)base
.pszProtocol
;
692 for(i
=0; i
<base
.cchProtocol
; i
++)
693 work
[i
] = tolowerW(work
[i
]);
695 /* mk is a special case */
696 if(base
.nScheme
== URL_SCHEME_MK
) {
697 static const WCHAR wsz
[] = {':',':',0};
699 WCHAR
*ptr
= strstrW(base
.pszSuffix
, wsz
);
704 delta
= ptr
-base
.pszSuffix
;
705 base
.cchProtocol
+= delta
;
706 base
.pszSuffix
+= delta
;
707 base
.cchSuffix
-= delta
;
710 /* get size of location field (if it exists) */
711 work
= (LPWSTR
)base
.pszSuffix
;
713 if (*work
++ == '/') {
714 if (*work
++ == '/') {
715 /* At this point have start of location and
716 * it ends at next '/' or end of string.
718 while(*work
&& (*work
!= '/')) work
++;
719 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
724 /* If there is a '#' and the characters immediately preceding it are
725 * ".htm[l]", then begin looking for the last leaf starting from
726 * the '#'. Otherwise the '#' is not meaningful and just start
727 * looking from the end. */
728 if ((work
= strchrW(base
.pszSuffix
+ sizeloc
, '#'))) {
729 const WCHAR htmlW
[] = {'.','h','t','m','l',0};
730 const int len_htmlW
= 5;
731 const WCHAR htmW
[] = {'.','h','t','m',0};
732 const int len_htmW
= 4;
734 if (base
.nScheme
== URL_SCHEME_HTTP
|| base
.nScheme
== URL_SCHEME_HTTPS
)
735 manual_search
= TRUE
;
736 else if (work
- base
.pszSuffix
> len_htmW
) {
738 if (strncmpiW(work
, htmW
, len_htmW
) == 0)
739 manual_search
= TRUE
;
743 if (!manual_search
&&
744 work
- base
.pszSuffix
> len_htmlW
) {
746 if (strncmpiW(work
, htmlW
, len_htmlW
) == 0)
747 manual_search
= TRUE
;
753 /* search backwards starting from the current position */
754 while (*work
!= '/' && work
> base
.pszSuffix
+ sizeloc
)
756 base
.cchSuffix
= work
- base
.pszSuffix
+ 1;
758 /* search backwards starting from the end of the string */
759 work
= strrchrW((base
.pszSuffix
+sizeloc
), '/');
761 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
762 base
.cchSuffix
= len
;
764 base
.cchSuffix
= sizeloc
;
769 * .pszSuffix points to location (starting with '//')
770 * .cchSuffix length of location (above) and rest less the last
772 * sizeloc length of location (above) up to but not including
776 res2
= ParseURLW(mrelative
, &relative
);
778 /* no scheme in pszRelative */
779 TRACE("no scheme detected in Relative\n");
780 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
781 relative
.cchSuffix
= strlenW(mrelative
);
782 if (*pszRelative
== ':') {
783 /* case that is either left alone or uses pszBase */
784 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
791 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == ':')) {
792 /* case that becomes "file:///" */
793 strcpyW(preliminary
, myfilestr
);
797 if ((*mrelative
== '/') && (*(mrelative
+1) == '/')) {
798 /* pszRelative has location and rest */
802 if (*mrelative
== '/') {
803 /* case where pszRelative is root to location */
807 if (*mrelative
== '#') {
808 if(!(work
= strchrW(base
.pszSuffix
+base
.cchSuffix
, '#')))
809 work
= (LPWSTR
)base
.pszSuffix
+ strlenW(base
.pszSuffix
);
811 memcpy(preliminary
, base
.pszProtocol
, (work
-base
.pszProtocol
)*sizeof(WCHAR
));
812 preliminary
[work
-base
.pszProtocol
] = '\0';
816 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
819 work
= (LPWSTR
)relative
.pszProtocol
;
820 for(i
=0; i
<relative
.cchProtocol
; i
++)
821 work
[i
] = tolowerW(work
[i
]);
824 /* handle cases where pszRelative has scheme */
825 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
826 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
828 /* since the schemes are the same */
829 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
830 /* case where pszRelative replaces location and following */
834 if (*relative
.pszSuffix
== '/') {
835 /* case where pszRelative is root to location */
839 /* replace either just location if base's location starts with a
840 * slash or otherwise everything */
841 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
844 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
845 /* case where pszRelative replaces scheme, location,
846 * and following and handles PLUGGABLE
853 } while(FALSE
); /* a little trick to allow easy exit from nested if's */
856 switch (process_case
) {
859 * Return pszRelative appended to what ever is in pszCombined,
860 * (which may the string "file:///"
862 strcatW(preliminary
, mrelative
);
865 case 2: /* case where pszRelative replaces scheme, and location */
866 strcpyW(preliminary
, mrelative
);
870 * Return the pszBase scheme with pszRelative. Basically
871 * keeps the scheme and replaces the domain and following.
873 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
874 work
= preliminary
+ base
.cchProtocol
+ 1;
875 strcpyW(work
, relative
.pszSuffix
);
879 * Return the pszBase scheme and location but everything
880 * after the location is pszRelative. (Replace document
883 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
884 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
885 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
887 strcpyW(work
, relative
.pszSuffix
);
891 * Return the pszBase without its document (if any) and
892 * append pszRelative after its scheme.
894 memcpy(preliminary
, base
.pszProtocol
,
895 (base
.cchProtocol
+1+base
.cchSuffix
)*sizeof(WCHAR
));
896 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
899 strcpyW(work
, relative
.pszSuffix
);
903 FIXME("How did we get here????? process_case=%d\n", process_case
);
908 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
909 if(*pcchCombined
== 0)
911 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, (dwFlags
& ~URL_FILE_USE_PATHURL
));
912 if(SUCCEEDED(ret
) && pszCombined
) {
913 lstrcpyW(pszCombined
, mrelative
);
915 TRACE("return-%d len=%d, %s\n",
916 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
918 HeapFree(GetProcessHeap(), 0, preliminary
);
922 /*************************************************************************
923 * UrlEscapeA [SHLWAPI.@]
926 HRESULT WINAPI
UrlEscapeA(
932 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
933 WCHAR
*escapedW
= bufW
;
936 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
938 if (!pszEscaped
|| !pcchEscaped
|| !*pcchEscaped
)
941 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
943 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
944 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
945 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
948 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
949 if(*pcchEscaped
> lenA
) {
950 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
951 pszEscaped
[lenA
] = 0;
954 *pcchEscaped
= lenA
+ 1;
958 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
959 RtlFreeUnicodeString(&urlW
);
963 #define WINE_URL_BASH_AS_SLASH 0x01
964 #define WINE_URL_COLLAPSE_SLASHES 0x02
965 #define WINE_URL_ESCAPE_SLASH 0x04
966 #define WINE_URL_ESCAPE_HASH 0x08
967 #define WINE_URL_ESCAPE_QUESTION 0x10
968 #define WINE_URL_STOP_ON_HASH 0x20
969 #define WINE_URL_STOP_ON_QUESTION 0x40
971 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
, DWORD int_flags
)
977 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
984 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
987 if (ch
<= 31 || ch
>= 127)
1008 if (int_flags
& WINE_URL_ESCAPE_SLASH
) return TRUE
;
1012 if (int_flags
& WINE_URL_ESCAPE_QUESTION
) return TRUE
;
1016 if (int_flags
& WINE_URL_ESCAPE_HASH
) return TRUE
;
1026 /*************************************************************************
1027 * UrlEscapeW [SHLWAPI.@]
1029 * Converts unsafe characters in a Url into escape sequences.
1032 * pszUrl [I] Url to modify
1033 * pszEscaped [O] Destination for modified Url
1034 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1035 * dwFlags [I] URL_ flags from "shlwapi.h"
1038 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1039 * contains its length.
1040 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1041 * pcchEscaped is set to the required length.
1043 * Converts unsafe characters into their escape sequences.
1046 * - By default this function stops converting at the first '?' or
1048 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1049 * converted, but the conversion continues past a '?' or '#'.
1050 * - Note that this function did not work well (or at all) in shlwapi version 4.
1053 * Only the following flags are implemented:
1054 *| URL_ESCAPE_SPACES_ONLY
1055 *| URL_DONT_ESCAPE_EXTRA_INFO
1056 *| URL_ESCAPE_SEGMENT_ONLY
1057 *| URL_ESCAPE_PERCENT
1059 HRESULT WINAPI
UrlEscapeW(
1062 LPDWORD pcchEscaped
,
1066 DWORD needed
= 0, ret
;
1067 BOOL stop_escaping
= FALSE
;
1068 WCHAR next
[5], *dst
, *dst_ptr
;
1070 PARSEDURLW parsed_url
;
1073 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
1075 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl
, debugstr_w(pszUrl
),
1076 pszEscaped
, pcchEscaped
, dwFlags
);
1078 if(!pszUrl
|| !pcchEscaped
)
1079 return E_INVALIDARG
;
1081 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
1082 URL_ESCAPE_SEGMENT_ONLY
|
1083 URL_DONT_ESCAPE_EXTRA_INFO
|
1084 URL_ESCAPE_PERCENT
))
1085 FIXME("Unimplemented flags: %08x\n", dwFlags
);
1087 dst_ptr
= dst
= HeapAlloc(GetProcessHeap(), 0, *pcchEscaped
*sizeof(WCHAR
));
1089 return E_OUTOFMEMORY
;
1092 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
1093 /* if SPACES_ONLY specified, reset the other controls */
1094 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
1095 URL_ESCAPE_PERCENT
|
1096 URL_ESCAPE_SEGMENT_ONLY
);
1099 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1100 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
1104 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
1105 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
1107 parsed_url
.cbSize
= sizeof(parsed_url
);
1108 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
1109 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
1111 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
1113 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
1114 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
1116 switch(parsed_url
.nScheme
) {
1117 case URL_SCHEME_FILE
:
1118 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
1119 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
1122 case URL_SCHEME_HTTP
:
1123 case URL_SCHEME_HTTPS
:
1124 int_flags
|= WINE_URL_BASH_AS_SLASH
;
1125 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
1126 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1129 case URL_SCHEME_MAILTO
:
1130 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
1131 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
1134 case URL_SCHEME_INVALID
:
1137 case URL_SCHEME_FTP
:
1139 if(parsed_url
.pszSuffix
[0] != '/')
1140 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1145 for(src
= pszUrl
; *src
; ) {
1149 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
1150 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
1151 while(cur
== '/' || cur
== '\\') {
1155 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
1156 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
1157 src
+= localhost_len
+ 1;
1164 next
[0] = next
[1] = next
[2] = '/';
1171 next
[0] = next
[1] = '/';
1178 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1179 stop_escaping
= TRUE
;
1181 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1182 stop_escaping
= TRUE
;
1184 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1186 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1188 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1189 next
[2] = hexDigits
[cur
& 0xf];
1198 if(needed
+ len
<= *pcchEscaped
) {
1199 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1205 if(needed
< *pcchEscaped
) {
1207 memcpy(pszEscaped
, dst_ptr
, (needed
+1)*sizeof(WCHAR
));
1211 needed
++; /* add one for the '\0' */
1214 *pcchEscaped
= needed
;
1216 HeapFree(GetProcessHeap(), 0, dst_ptr
);
1221 /*************************************************************************
1222 * UrlUnescapeA [SHLWAPI.@]
1224 * Converts Url escape sequences back to ordinary characters.
1227 * pszUrl [I/O] Url to convert
1228 * pszUnescaped [O] Destination for converted Url
1229 * pcchUnescaped [I/O] Size of output string
1230 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1233 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1234 * dwFlags includes URL_ESCAPE_INPLACE.
1235 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1236 * this case pcchUnescaped is set to the size required.
1238 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1239 * the first occurrence of either a '?' or '#' character.
1241 HRESULT WINAPI
UrlUnescapeA(
1244 LPDWORD pcchUnescaped
,
1251 BOOL stop_unescaping
= FALSE
;
1253 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl
), pszUnescaped
,
1254 pcchUnescaped
, dwFlags
);
1256 if (!pszUrl
) return E_INVALIDARG
;
1258 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1262 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1266 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1267 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1268 (*src
== '#' || *src
== '?')) {
1269 stop_unescaping
= TRUE
;
1271 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1272 && stop_unescaping
== FALSE
) {
1275 memcpy(buf
, src
+ 1, 2);
1277 ih
= strtol(buf
, NULL
, 16);
1279 src
+= 2; /* Advance to end of escape */
1283 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1287 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1291 needed
++; /* add one for the '\0' */
1294 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1295 *pcchUnescaped
= needed
;
1298 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1299 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1305 /*************************************************************************
1306 * UrlUnescapeW [SHLWAPI.@]
1310 HRESULT WINAPI
UrlUnescapeW(
1312 LPWSTR pszUnescaped
,
1313 LPDWORD pcchUnescaped
,
1320 BOOL stop_unescaping
= FALSE
;
1322 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl
), pszUnescaped
,
1323 pcchUnescaped
, dwFlags
);
1325 if(!pszUrl
) return E_INVALIDARG
;
1327 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1331 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1335 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1336 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1337 (*src
== '#' || *src
== '?')) {
1338 stop_unescaping
= TRUE
;
1340 } else if(*src
== '%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1341 && stop_unescaping
== FALSE
) {
1343 WCHAR buf
[5] = {'0','x',0};
1344 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1346 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1348 src
+= 2; /* Advance to end of escape */
1352 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1356 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1360 needed
++; /* add one for the '\0' */
1363 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1364 *pcchUnescaped
= needed
;
1367 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1368 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1374 /*************************************************************************
1375 * UrlGetLocationA [SHLWAPI.@]
1377 * Get the location from a Url.
1380 * pszUrl [I] Url to get the location from
1383 * A pointer to the start of the location in pszUrl, or NULL if there is
1387 * - MSDN erroneously states that "The location is the segment of the Url
1388 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1389 * stop at '?' and always return a NULL in this case.
1390 * - MSDN also erroneously states that "If a file URL has a query string,
1391 * the returned string is the query string". In all tested cases, if the
1392 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1395 *| NULL file://aa/b/cd#hohoh
1396 *| #hohoh http://aa/b/cd#hohoh
1397 *| NULL fi://aa/b/cd#hohoh
1398 *| #hohoh ff://aa/b/cd#hohoh
1400 LPCSTR WINAPI
UrlGetLocationA(
1406 base
.cbSize
= sizeof(base
);
1407 res1
= ParseURLA(pszUrl
, &base
);
1408 if (res1
) return NULL
; /* invalid scheme */
1410 /* if scheme is file: then never return pointer */
1411 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1413 /* Look for '#' and return its addr */
1414 return strchr(base
.pszSuffix
, '#');
1417 /*************************************************************************
1418 * UrlGetLocationW [SHLWAPI.@]
1420 * See UrlGetLocationA.
1422 LPCWSTR WINAPI
UrlGetLocationW(
1428 base
.cbSize
= sizeof(base
);
1429 res1
= ParseURLW(pszUrl
, &base
);
1430 if (res1
) return NULL
; /* invalid scheme */
1432 /* if scheme is file: then never return pointer */
1433 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1435 /* Look for '#' and return its addr */
1436 return strchrW(base
.pszSuffix
, '#');
1439 /*************************************************************************
1440 * UrlCompareA [SHLWAPI.@]
1445 * pszUrl1 [I] First Url to compare
1446 * pszUrl2 [I] Url to compare to pszUrl1
1447 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1450 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1451 * than, equal to, or less than pszUrl1 respectively.
1453 INT WINAPI
UrlCompareA(
1458 INT ret
, len
, len1
, len2
;
1461 return strcmp(pszUrl1
, pszUrl2
);
1462 len1
= strlen(pszUrl1
);
1463 if (pszUrl1
[len1
-1] == '/') len1
--;
1464 len2
= strlen(pszUrl2
);
1465 if (pszUrl2
[len2
-1] == '/') len2
--;
1467 return strncmp(pszUrl1
, pszUrl2
, len1
);
1468 len
= min(len1
, len2
);
1469 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1470 if (ret
) return ret
;
1471 if (len1
> len2
) return 1;
1475 /*************************************************************************
1476 * UrlCompareW [SHLWAPI.@]
1480 INT WINAPI
UrlCompareW(
1486 size_t len
, len1
, len2
;
1489 return strcmpW(pszUrl1
, pszUrl2
);
1490 len1
= strlenW(pszUrl1
);
1491 if (pszUrl1
[len1
-1] == '/') len1
--;
1492 len2
= strlenW(pszUrl2
);
1493 if (pszUrl2
[len2
-1] == '/') len2
--;
1495 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1496 len
= min(len1
, len2
);
1497 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1498 if (ret
) return ret
;
1499 if (len1
> len2
) return 1;
1503 /*************************************************************************
1504 * HashData [SHLWAPI.@]
1506 * Hash an input block into a variable sized digest.
1509 * lpSrc [I] Input block
1510 * nSrcLen [I] Length of lpSrc
1511 * lpDest [I] Output for hash digest
1512 * nDestLen [I] Length of lpDest
1515 * Success: TRUE. lpDest is filled with the computed hash value.
1516 * Failure: FALSE, if any argument is invalid.
1518 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1519 unsigned char *lpDest
, DWORD nDestLen
)
1521 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1523 if (!lpSrc
|| !lpDest
)
1524 return E_INVALIDARG
;
1526 while (destCount
>= 0)
1528 lpDest
[destCount
] = (destCount
& 0xff);
1532 while (srcCount
>= 0)
1534 destCount
= nDestLen
- 1;
1535 while (destCount
>= 0)
1537 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1545 /*************************************************************************
1546 * UrlHashA [SHLWAPI.@]
1548 * Produce a Hash from a Url.
1551 * pszUrl [I] Url to hash
1552 * lpDest [O] Destinationh for hash
1553 * nDestLen [I] Length of lpDest
1556 * Success: S_OK. lpDest is filled with the computed hash value.
1557 * Failure: E_INVALIDARG, if any argument is invalid.
1559 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1561 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1562 return E_INVALIDARG
;
1564 HashData((const BYTE
*)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1568 /*************************************************************************
1569 * UrlHashW [SHLWAPI.@]
1573 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1575 char szUrl
[MAX_PATH
];
1577 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1579 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1580 return E_INVALIDARG
;
1582 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1583 * return the same digests for the same URL.
1585 WideCharToMultiByte(CP_ACP
, 0, pszUrl
, -1, szUrl
, MAX_PATH
, NULL
, NULL
);
1586 HashData((const BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1590 /*************************************************************************
1591 * UrlApplySchemeA [SHLWAPI.@]
1593 * Apply a scheme to a Url.
1596 * pszIn [I] Url to apply scheme to
1597 * pszOut [O] Destination for modified Url
1598 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1599 * dwFlags [I] URL_ flags from "shlwapi.h"
1602 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1603 * Failure: An HRESULT error code describing the error.
1605 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1611 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn
),
1612 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1614 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1616 in
= HeapAlloc(GetProcessHeap(), 0,
1617 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1618 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1620 MultiByteToWideChar(CP_ACP
, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1621 len
= INTERNET_MAX_URL_LENGTH
;
1623 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1625 HeapFree(GetProcessHeap(), 0, in
);
1629 len
= WideCharToMultiByte(CP_ACP
, 0, out
, -1, NULL
, 0, NULL
, NULL
);
1630 if (len
> *pcchOut
) {
1635 WideCharToMultiByte(CP_ACP
, 0, out
, -1, pszOut
, *pcchOut
, NULL
, NULL
);
1640 HeapFree(GetProcessHeap(), 0, in
);
1644 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1649 DWORD value_len
, data_len
, dwType
, i
;
1650 WCHAR reg_path
[MAX_PATH
];
1651 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1654 MultiByteToWideChar(CP_ACP
, 0,
1655 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1656 -1, reg_path
, MAX_PATH
);
1657 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1659 while(value_len
= data_len
= MAX_PATH
,
1660 RegEnumValueW(newkey
, index
, value
, &value_len
,
1661 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1662 TRACE("guess %d %s is %s\n",
1663 index
, debugstr_w(value
), debugstr_w(data
));
1666 for(i
=0; i
<value_len
; i
++) {
1669 /* remember that TRUE is not-equal */
1670 j
= ChrCmpIW(Wxx
, Wyy
);
1673 if ((i
== value_len
) && !j
) {
1674 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1675 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1676 RegCloseKey(newkey
);
1679 strcpyW(pszOut
, data
);
1680 strcatW(pszOut
, pszIn
);
1681 *pcchOut
= strlenW(pszOut
);
1682 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1683 RegCloseKey(newkey
);
1688 RegCloseKey(newkey
);
1692 static HRESULT
URL_CreateFromPath(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
)
1697 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
1698 WCHAR three_slashesW
[] = {'/','/','/',0};
1699 PARSEDURLW parsed_url
;
1701 parsed_url
.cbSize
= sizeof(parsed_url
);
1702 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
1703 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
1704 needed
= strlenW(pszPath
);
1705 if (needed
>= *pcchUrl
) {
1706 *pcchUrl
= needed
+ 1;
1715 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
1716 strcpyW(pszNewUrl
, file_colonW
);
1717 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
1718 strcatW(pszNewUrl
, three_slashesW
);
1719 strcatW(pszNewUrl
, pszPath
);
1720 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
1721 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
1725 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1728 DWORD data_len
, dwType
;
1729 WCHAR data
[MAX_PATH
];
1731 static const WCHAR prefix_keyW
[] =
1732 {'S','o','f','t','w','a','r','e',
1733 '\\','M','i','c','r','o','s','o','f','t',
1734 '\\','W','i','n','d','o','w','s',
1735 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1737 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1739 /* get and prepend default */
1740 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefix_keyW
, 0, 1, &newkey
);
1741 data_len
= sizeof(data
);
1742 RegQueryValueExW(newkey
, NULL
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1743 RegCloseKey(newkey
);
1744 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1745 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1748 strcpyW(pszOut
, data
);
1749 strcatW(pszOut
, pszIn
);
1750 *pcchOut
= strlenW(pszOut
);
1751 TRACE("used default %s\n", debugstr_w(pszOut
));
1755 /*************************************************************************
1756 * UrlApplySchemeW [SHLWAPI.@]
1758 * See UrlApplySchemeA.
1760 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1762 PARSEDURLW in_scheme
;
1766 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn
),
1767 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1769 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1771 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1772 if (*pcchOut
> 1 && ':' == pszIn
[1]) {
1774 ret
= URL_CreateFromPath(pszIn
, pszOut
, &res1
);
1775 if (ret
== S_OK
|| ret
== E_POINTER
){
1779 else if (ret
== S_FALSE
)
1786 in_scheme
.cbSize
= sizeof(in_scheme
);
1787 /* See if the base has a scheme */
1788 res1
= ParseURLW(pszIn
, &in_scheme
);
1790 /* no scheme in input, need to see if we need to guess */
1791 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1792 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != E_FAIL
)
1797 /* If we are here, then either invalid scheme,
1798 * or no scheme and can't/failed guess.
1800 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1802 (dwFlags
& URL_APPLY_DEFAULT
)) {
1803 /* find and apply default scheme */
1804 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1810 /*************************************************************************
1811 * UrlIsA [SHLWAPI.@]
1813 * Determine if a Url is of a certain class.
1816 * pszUrl [I] Url to check
1817 * Urlis [I] URLIS_ constant from "shlwapi.h"
1820 * TRUE if pszUrl belongs to the class type in Urlis.
1823 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1829 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1837 base
.cbSize
= sizeof(base
);
1838 res1
= ParseURLA(pszUrl
, &base
);
1839 if (res1
) return FALSE
; /* invalid scheme */
1840 switch (base
.nScheme
)
1842 case URL_SCHEME_MAILTO
:
1843 case URL_SCHEME_SHELL
:
1844 case URL_SCHEME_JAVASCRIPT
:
1845 case URL_SCHEME_VBSCRIPT
:
1846 case URL_SCHEME_ABOUT
:
1852 return (CompareStringA(LOCALE_INVARIANT
, NORM_IGNORECASE
, pszUrl
, 5,
1853 "file:", 5) == CSTR_EQUAL
);
1855 case URLIS_DIRECTORY
:
1856 last
= pszUrl
+ strlen(pszUrl
) - 1;
1857 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1860 return PathIsURLA(pszUrl
);
1862 case URLIS_NOHISTORY
:
1863 case URLIS_APPLIABLE
:
1864 case URLIS_HASQUERY
:
1866 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1871 /*************************************************************************
1872 * UrlIsW [SHLWAPI.@]
1876 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1878 static const WCHAR file_colon
[] = { 'f','i','l','e',':',0 };
1883 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1891 base
.cbSize
= sizeof(base
);
1892 res1
= ParseURLW(pszUrl
, &base
);
1893 if (res1
) return FALSE
; /* invalid scheme */
1894 switch (base
.nScheme
)
1896 case URL_SCHEME_MAILTO
:
1897 case URL_SCHEME_SHELL
:
1898 case URL_SCHEME_JAVASCRIPT
:
1899 case URL_SCHEME_VBSCRIPT
:
1900 case URL_SCHEME_ABOUT
:
1906 return (CompareStringW(LOCALE_INVARIANT
, NORM_IGNORECASE
, pszUrl
, 5,
1907 file_colon
, 5) == CSTR_EQUAL
);
1909 case URLIS_DIRECTORY
:
1910 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1911 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1914 return PathIsURLW(pszUrl
);
1916 case URLIS_NOHISTORY
:
1917 case URLIS_APPLIABLE
:
1918 case URLIS_HASQUERY
:
1920 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1925 /*************************************************************************
1926 * UrlIsNoHistoryA [SHLWAPI.@]
1928 * Determine if a Url should not be stored in the users history list.
1931 * pszUrl [I] Url to check
1934 * TRUE, if pszUrl should be excluded from the history list,
1937 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1939 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1942 /*************************************************************************
1943 * UrlIsNoHistoryW [SHLWAPI.@]
1945 * See UrlIsNoHistoryA.
1947 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1949 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1952 /*************************************************************************
1953 * UrlIsOpaqueA [SHLWAPI.@]
1955 * Determine if a Url is opaque.
1958 * pszUrl [I] Url to check
1961 * TRUE if pszUrl is opaque,
1965 * An opaque Url is one that does not start with "<protocol>://".
1967 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1969 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1972 /*************************************************************************
1973 * UrlIsOpaqueW [SHLWAPI.@]
1977 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1979 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1982 /*************************************************************************
1983 * Scans for characters of type "type" and when not matching found,
1984 * returns pointer to it and length in size.
1986 * Characters tested based on RFC 1738
1988 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1990 static DWORD alwayszero
= 0;
1999 if ( (islowerW(*start
) && isalphaW(*start
)) ||
2018 if ( isalphaW(*start
) ||
2020 /* user/password only characters */
2025 /* *extra* characters */
2032 /* *safe* characters */
2041 } else if (*start
== '%') {
2042 if (isxdigitW(*(start
+1)) &&
2043 isxdigitW(*(start
+2))) {
2055 if (isdigitW(*start
)) {
2066 if (isalnumW(*start
) ||
2079 FIXME("unknown type %d\n", type
);
2080 return (LPWSTR
)&alwayszero
;
2082 /* TRACE("scanned %d characters next char %p<%c>\n",
2083 *size, start, *start); */
2087 /*************************************************************************
2088 * Attempt to parse URL into pieces.
2090 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
2094 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
2095 pl
->pScheme
= pszUrl
;
2096 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
2097 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
2099 if ((*work
!= '/') || (*(work
+1) != '/')) goto SuccessExit
;
2100 pl
->pUserName
= work
+ 2;
2101 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
2102 if (*work
== ':' ) {
2103 /* parse password */
2105 pl
->pPassword
= work
;
2106 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
2108 /* what we just parsed must be the hostname and port
2109 * so reset pointers and clear then let it parse */
2110 pl
->szUserName
= pl
->szPassword
= 0;
2111 work
= pl
->pUserName
- 1;
2112 pl
->pUserName
= pl
->pPassword
= 0;
2114 } else if (*work
== '@') {
2118 } else if (!*work
|| (*work
== '/') || (*work
== '.')) {
2119 /* what was parsed was hostname, so reset pointers and let it parse */
2120 pl
->szUserName
= pl
->szPassword
= 0;
2121 work
= pl
->pUserName
- 1;
2122 pl
->pUserName
= pl
->pPassword
= 0;
2123 } else goto ErrorExit
;
2125 /* now start parsing hostname or hostnumber */
2127 pl
->pHostName
= work
;
2128 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
2133 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
2136 /* see if query string */
2137 pl
->pQuery
= strchrW(work
, '?');
2138 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
2141 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2142 pl
->pScheme
, pl
->szScheme
,
2143 pl
->pUserName
, pl
->szUserName
,
2144 pl
->pPassword
, pl
->szPassword
,
2145 pl
->pHostName
, pl
->szHostName
,
2146 pl
->pPort
, pl
->szPort
,
2147 pl
->pQuery
, pl
->szQuery
);
2150 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
2151 return E_INVALIDARG
;
2154 /*************************************************************************
2155 * UrlGetPartA [SHLWAPI.@]
2157 * Retrieve part of a Url.
2160 * pszIn [I] Url to parse
2161 * pszOut [O] Destination for part of pszIn requested
2162 * pcchOut [I] Size of pszOut
2163 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2164 * needed size of pszOut INCLUDING '\0'.
2165 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2166 * dwFlags [I] URL_ flags from "shlwapi.h"
2169 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2170 * Failure: An HRESULT error code describing the error.
2172 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
2173 DWORD dwPart
, DWORD dwFlags
)
2176 DWORD ret
, len
, len2
;
2178 if(!pszIn
|| !pszOut
|| !pcchOut
|| *pcchOut
<= 0)
2179 return E_INVALIDARG
;
2181 in
= HeapAlloc(GetProcessHeap(), 0,
2182 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
2183 out
= in
+ INTERNET_MAX_URL_LENGTH
;
2185 MultiByteToWideChar(CP_ACP
, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
2187 len
= INTERNET_MAX_URL_LENGTH
;
2188 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
2191 HeapFree(GetProcessHeap(), 0, in
);
2195 len2
= WideCharToMultiByte(CP_ACP
, 0, out
, len
, NULL
, 0, NULL
, NULL
);
2196 if (len2
> *pcchOut
) {
2198 HeapFree(GetProcessHeap(), 0, in
);
2201 len2
= WideCharToMultiByte(CP_ACP
, 0, out
, len
+1, pszOut
, *pcchOut
, NULL
, NULL
);
2203 HeapFree(GetProcessHeap(), 0, in
);
2207 /*************************************************************************
2208 * UrlGetPartW [SHLWAPI.@]
2212 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2213 DWORD dwPart
, DWORD dwFlags
)
2217 DWORD scheme
, size
, schsize
;
2218 LPCWSTR addr
, schaddr
;
2220 TRACE("(%s %p %p(%d) %08x %08x)\n",
2221 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2223 if(!pszIn
|| !pszOut
|| !pcchOut
|| *pcchOut
<= 0)
2224 return E_INVALIDARG
;
2228 addr
= strchrW(pszIn
, ':');
2230 scheme
= URL_SCHEME_UNKNOWN
;
2232 scheme
= get_scheme_code(pszIn
, addr
-pszIn
);
2234 ret
= URL_ParseUrl(pszIn
, &pl
);
2237 case URL_PART_SCHEME
:
2246 case URL_PART_HOSTNAME
:
2248 case URL_SCHEME_FTP
:
2249 case URL_SCHEME_HTTP
:
2250 case URL_SCHEME_GOPHER
:
2251 case URL_SCHEME_TELNET
:
2252 case URL_SCHEME_FILE
:
2253 case URL_SCHEME_HTTPS
:
2260 if(scheme
==URL_SCHEME_FILE
&& (!pl
.szHostName
||
2261 (pl
.szHostName
==1 && *(pl
.pHostName
+1)==':'))) {
2266 if (!pl
.szHostName
) {
2270 addr
= pl
.pHostName
;
2271 size
= pl
.szHostName
;
2274 case URL_PART_USERNAME
:
2275 if (!pl
.szUserName
) {
2279 addr
= pl
.pUserName
;
2280 size
= pl
.szUserName
;
2283 case URL_PART_PASSWORD
:
2284 if (!pl
.szPassword
) {
2288 addr
= pl
.pPassword
;
2289 size
= pl
.szPassword
;
2301 case URL_PART_QUERY
:
2312 return E_INVALIDARG
;
2315 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2316 if(!pl
.pScheme
|| !pl
.szScheme
) {
2320 schaddr
= pl
.pScheme
;
2321 schsize
= pl
.szScheme
;
2322 if (*pcchOut
< schsize
+ size
+ 2) {
2323 *pcchOut
= schsize
+ size
+ 2;
2326 memcpy(pszOut
, schaddr
, schsize
*sizeof(WCHAR
));
2327 pszOut
[schsize
] = ':';
2328 memcpy(pszOut
+schsize
+1, addr
, size
*sizeof(WCHAR
));
2329 pszOut
[schsize
+1+size
] = 0;
2330 *pcchOut
= schsize
+ 1 + size
;
2333 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2334 memcpy(pszOut
, addr
, size
*sizeof(WCHAR
));
2338 TRACE("len=%d %s\n", *pcchOut
, debugstr_w(pszOut
));
2343 /*************************************************************************
2344 * PathIsURLA [SHLWAPI.@]
2346 * Check if the given path is a Url.
2349 * lpszPath [I] Path to check.
2352 * TRUE if lpszPath is a Url.
2353 * FALSE if lpszPath is NULL or not a Url.
2355 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2360 TRACE("%s\n", debugstr_a(lpstrPath
));
2362 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2365 base
.cbSize
= sizeof(base
);
2366 hres
= ParseURLA(lpstrPath
, &base
);
2367 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2370 /*************************************************************************
2371 * PathIsURLW [SHLWAPI.@]
2375 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2380 TRACE("%s\n", debugstr_w(lpstrPath
));
2382 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2385 base
.cbSize
= sizeof(base
);
2386 hres
= ParseURLW(lpstrPath
, &base
);
2387 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2390 /*************************************************************************
2391 * UrlCreateFromPathA [SHLWAPI.@]
2393 * See UrlCreateFromPathW
2395 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2397 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2399 UNICODE_STRING pathW
;
2401 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2403 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2404 return E_INVALIDARG
;
2405 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2406 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2407 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2409 if(ret
== S_OK
|| ret
== S_FALSE
) {
2410 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2411 if(*pcchUrl
> lenA
) {
2412 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2416 *pcchUrl
= lenA
+ 1;
2420 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2421 RtlFreeUnicodeString(&pathW
);
2425 /*************************************************************************
2426 * UrlCreateFromPathW [SHLWAPI.@]
2428 * Create a Url from a file path.
2431 * pszPath [I] Path to convert
2432 * pszUrl [O] Destination for the converted Url
2433 * pcchUrl [I/O] Length of pszUrl
2434 * dwReserved [I] Reserved, must be 0
2437 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2438 * Failure: An HRESULT error code.
2440 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2444 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2446 /* Validate arguments */
2447 if (dwReserved
!= 0)
2448 return E_INVALIDARG
;
2449 if (!pszUrl
|| !pcchUrl
)
2450 return E_INVALIDARG
;
2452 ret
= URL_CreateFromPath(pszPath
, pszUrl
, pcchUrl
);
2455 strcpyW(pszUrl
, pszPath
);
2460 /*************************************************************************
2461 * SHAutoComplete [SHLWAPI.@]
2463 * Enable auto-completion for an edit control.
2466 * hwndEdit [I] Handle of control to enable auto-completion for
2467 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2470 * Success: S_OK. Auto-completion is enabled for the control.
2471 * Failure: An HRESULT error code indicating the error.
2473 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2479 /*************************************************************************
2480 * MLBuildResURLA [SHLWAPI.405]
2482 * Create a Url pointing to a resource in a module.
2485 * lpszLibName [I] Name of the module containing the resource
2486 * hMod [I] Callers module handle
2487 * dwFlags [I] Undocumented flags for loading the module
2488 * lpszRes [I] Resource name
2489 * lpszDest [O] Destination for resulting Url
2490 * dwDestLen [I] Length of lpszDest
2493 * Success: S_OK. lpszDest contains the resource Url.
2494 * Failure: E_INVALIDARG, if any argument is invalid, or
2495 * E_FAIL if dwDestLen is too small.
2497 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2498 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2500 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2504 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2507 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2509 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2510 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2512 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2513 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2514 if (SUCCEEDED(hRet
) && lpszDest
)
2515 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, NULL
, NULL
);
2520 /*************************************************************************
2521 * MLBuildResURLA [SHLWAPI.406]
2523 * See MLBuildResURLA.
2525 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2526 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2528 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2529 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2530 HRESULT hRet
= E_FAIL
;
2532 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2533 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2535 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2536 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2537 return E_INVALIDARG
;
2539 if (dwDestLen
>= szResLen
+ 1)
2541 dwDestLen
-= (szResLen
+ 1);
2542 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2544 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2548 WCHAR szBuff
[MAX_PATH
];
2551 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2552 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2554 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2556 if (dwDestLen
>= dwPathLen
)
2560 dwDestLen
-= dwPathLen
;
2561 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2563 dwResLen
= strlenW(lpszRes
) + 1;
2564 if (dwDestLen
>= dwResLen
+ 1)
2566 lpszDest
[szResLen
+ dwPathLen
-1] = '/';
2567 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2572 MLFreeLibrary(hMod
);
2578 /***********************************************************************
2579 * UrlFixupW [SHLWAPI.462]
2581 * Checks the scheme part of a URL and attempts to correct misspellings.
2584 * lpszUrl [I] Pointer to the URL to be corrected
2585 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2586 * dwMaxChars [I] Maximum size of corrected URL
2589 * success: S_OK if URL corrected or already correct
2590 * failure: S_FALSE if unable to correct / COM error code if other error
2593 HRESULT WINAPI
UrlFixupW(LPCWSTR url
, LPWSTR translatedUrl
, DWORD maxChars
)
2597 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url
), translatedUrl
, maxChars
);
2602 srcLen
= lstrlenW(url
) + 1;
2604 /* For now just copy the URL directly */
2605 lstrcpynW(translatedUrl
, url
, (maxChars
< srcLen
) ? maxChars
: srcLen
);
2610 /*************************************************************************
2611 * IsInternetESCEnabled [SHLWAPI.@]
2613 BOOL WINAPI
IsInternetESCEnabled(void)