4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
34 #define NO_SHLWAPI_STREAM
37 #include "wine/debug.h"
39 HMODULE WINAPI
MLLoadLibraryW(LPCWSTR
,HMODULE
,DWORD
);
40 BOOL WINAPI
MLFreeLibrary(HMODULE
);
41 HRESULT WINAPI
MLBuildResURLW(LPCWSTR
,HMODULE
,DWORD
,LPCWSTR
,LPWSTR
,DWORD
);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
45 static inline WCHAR
*heap_strdupAtoW(const char *str
)
52 len
= MultiByteToWideChar(CP_ACP
, 0, str
, -1, NULL
, 0);
53 ret
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
54 MultiByteToWideChar(CP_ACP
, 0, str
, -1, ret
, len
);
60 /* The following schemes were identified in the native version of
61 * SHLWAPI.DLL version 5.50
64 URL_SCHEME scheme_number
;
65 WCHAR scheme_name
[12];
66 } shlwapi_schemes
[] = {
67 {URL_SCHEME_FTP
, {'f','t','p',0}},
68 {URL_SCHEME_HTTP
, {'h','t','t','p',0}},
69 {URL_SCHEME_GOPHER
, {'g','o','p','h','e','r',0}},
70 {URL_SCHEME_MAILTO
, {'m','a','i','l','t','o',0}},
71 {URL_SCHEME_NEWS
, {'n','e','w','s',0}},
72 {URL_SCHEME_NNTP
, {'n','n','t','p',0}},
73 {URL_SCHEME_TELNET
, {'t','e','l','n','e','t',0}},
74 {URL_SCHEME_WAIS
, {'w','a','i','s',0}},
75 {URL_SCHEME_FILE
, {'f','i','l','e',0}},
76 {URL_SCHEME_MK
, {'m','k',0}},
77 {URL_SCHEME_HTTPS
, {'h','t','t','p','s',0}},
78 {URL_SCHEME_SHELL
, {'s','h','e','l','l',0}},
79 {URL_SCHEME_SNEWS
, {'s','n','e','w','s',0}},
80 {URL_SCHEME_LOCAL
, {'l','o','c','a','l',0}},
81 {URL_SCHEME_JAVASCRIPT
, {'j','a','v','a','s','c','r','i','p','t',0}},
82 {URL_SCHEME_VBSCRIPT
, {'v','b','s','c','r','i','p','t',0}},
83 {URL_SCHEME_ABOUT
, {'a','b','o','u','t',0}},
84 {URL_SCHEME_RES
, {'r','e','s',0}},
88 LPCWSTR pScheme
; /* [out] start of scheme */
89 DWORD szScheme
; /* [out] size of scheme (until colon) */
90 LPCWSTR pUserName
; /* [out] start of Username */
91 DWORD szUserName
; /* [out] size of Username (until ":" or "@") */
92 LPCWSTR pPassword
; /* [out] start of Password */
93 DWORD szPassword
; /* [out] size of Password (until "@") */
94 LPCWSTR pHostName
; /* [out] start of Hostname */
95 DWORD szHostName
; /* [out] size of Hostname (until ":" or "/") */
96 LPCWSTR pPort
; /* [out] start of Port */
97 DWORD szPort
; /* [out] size of Port (until "/" or eos) */
98 LPCWSTR pQuery
; /* [out] start of Query */
99 DWORD szQuery
; /* [out] size of Query (until eos) */
107 } WINE_URL_SCAN_TYPE
;
109 static const CHAR hexDigits
[] = "0123456789ABCDEF";
111 static const WCHAR fileW
[] = {'f','i','l','e','\0'};
113 static const unsigned char HashDataLookup
[256] = {
114 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
115 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
116 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
117 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
118 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
119 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
120 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
121 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
122 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
123 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
124 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
125 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
126 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
127 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
128 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
129 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
130 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
131 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
132 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
133 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
135 static DWORD
get_scheme_code(LPCWSTR scheme
, DWORD scheme_len
)
139 for(i
=0; i
< sizeof(shlwapi_schemes
)/sizeof(shlwapi_schemes
[0]); i
++) {
140 if(scheme_len
== strlenW(shlwapi_schemes
[i
].scheme_name
)
141 && !memicmpW(scheme
, shlwapi_schemes
[i
].scheme_name
, scheme_len
))
142 return shlwapi_schemes
[i
].scheme_number
;
145 return URL_SCHEME_UNKNOWN
;
148 /*************************************************************************
151 * Parse a Url into its constituent parts.
155 * y [O] Undocumented structure holding the parsed information
158 * Success: S_OK. y contains the parsed Url details.
159 * Failure: An HRESULT error code.
161 HRESULT WINAPI
ParseURLA(LPCSTR x
, PARSEDURLA
*y
)
163 WCHAR scheme
[INTERNET_MAX_SCHEME_LENGTH
];
167 TRACE("%s %p\n", debugstr_a(x
), y
);
169 if(y
->cbSize
!= sizeof(*y
))
172 while(*ptr
&& (isalnum(*ptr
) || *ptr
== '-' || *ptr
== '+' || *ptr
== '.'))
175 if (*ptr
!= ':' || ptr
<= x
+1) {
176 y
->pszProtocol
= NULL
;
177 return URL_E_INVALID_SYNTAX
;
181 y
->cchProtocol
= ptr
-x
;
182 y
->pszSuffix
= ptr
+1;
183 y
->cchSuffix
= strlen(y
->pszSuffix
);
185 len
= MultiByteToWideChar(CP_ACP
, 0, x
, ptr
-x
,
186 scheme
, sizeof(scheme
)/sizeof(WCHAR
));
187 y
->nScheme
= get_scheme_code(scheme
, len
);
192 /*************************************************************************
195 * Unicode version of ParseURLA.
197 HRESULT WINAPI
ParseURLW(LPCWSTR x
, PARSEDURLW
*y
)
199 const WCHAR
*ptr
= x
;
201 TRACE("%s %p\n", debugstr_w(x
), y
);
203 if(y
->cbSize
!= sizeof(*y
))
206 while(*ptr
&& (isalnumW(*ptr
) || *ptr
== '-' || *ptr
== '+' || *ptr
== '.'))
209 if (*ptr
!= ':' || ptr
<= x
+1) {
210 y
->pszProtocol
= NULL
;
211 return URL_E_INVALID_SYNTAX
;
215 y
->cchProtocol
= ptr
-x
;
216 y
->pszSuffix
= ptr
+1;
217 y
->cchSuffix
= strlenW(y
->pszSuffix
);
218 y
->nScheme
= get_scheme_code(x
, ptr
-x
);
223 /*************************************************************************
224 * UrlCanonicalizeA [SHLWAPI.@]
226 * Canonicalize a Url.
229 * pszUrl [I] Url to cCanonicalize
230 * pszCanonicalized [O] Destination for converted Url.
231 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
232 * dwFlags [I] Flags controlling the conversion.
235 * Success: S_OK. The pszCanonicalized contains the converted Url.
236 * Failure: E_POINTER, if *pcchCanonicalized is too small.
238 * MSDN incorrectly describes the flags for this function. They should be:
239 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
240 *| URL_ESCAPE_SPACES_ONLY 0x04000000
241 *| URL_ESCAPE_PERCENT 0x00001000
242 *| URL_ESCAPE_UNSAFE 0x10000000
243 *| URL_UNESCAPE 0x10000000
244 *| URL_DONT_SIMPLIFY 0x08000000
245 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
247 HRESULT WINAPI
UrlCanonicalizeA(LPCSTR pszUrl
, LPSTR pszCanonicalized
,
248 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
250 LPWSTR url
, canonical
;
253 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl
), pszCanonicalized
,
254 pcchCanonicalized
, dwFlags
, pcchCanonicalized
? *pcchCanonicalized
: -1);
256 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
|| !*pcchCanonicalized
)
259 url
= heap_strdupAtoW(pszUrl
);
260 canonical
= HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized
*sizeof(WCHAR
));
261 if(!url
|| !canonical
) {
262 HeapFree(GetProcessHeap(), 0, url
);
263 HeapFree(GetProcessHeap(), 0, canonical
);
264 return E_OUTOFMEMORY
;
267 ret
= UrlCanonicalizeW(url
, canonical
, pcchCanonicalized
, dwFlags
);
269 WideCharToMultiByte(CP_ACP
, 0, canonical
, -1, pszCanonicalized
,
270 *pcchCanonicalized
+1, NULL
, NULL
);
272 HeapFree(GetProcessHeap(), 0, url
);
273 HeapFree(GetProcessHeap(), 0, canonical
);
277 /*************************************************************************
278 * UrlCanonicalizeW [SHLWAPI.@]
280 * See UrlCanonicalizeA.
282 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
283 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
288 LPWSTR lpszUrlCpy
, url
, wk2
, mp
, mp2
;
290 DWORD nByteLen
, nLen
, nWkLen
;
294 static const WCHAR wszFile
[] = {'f','i','l','e',':'};
295 static const WCHAR wszRes
[] = {'r','e','s',':'};
296 static const WCHAR wszHttp
[] = {'h','t','t','p',':'};
297 static const WCHAR wszLocalhost
[] = {'l','o','c','a','l','h','o','s','t'};
298 static const WCHAR wszFilePrefix
[] = {'f','i','l','e',':','/','/','/'};
300 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl
), pszCanonicalized
,
301 pcchCanonicalized
, dwFlags
, pcchCanonicalized
? *pcchCanonicalized
: -1);
303 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
|| !*pcchCanonicalized
)
307 *pszCanonicalized
= 0;
311 /* Remove '\t' characters from URL */
312 nByteLen
= (strlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
313 url
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
315 return E_OUTOFMEMORY
;
325 /* Allocate memory for simplified URL (before escaping) */
326 nByteLen
= (wk2
-url
)*sizeof(WCHAR
);
327 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0,
328 nByteLen
+sizeof(wszFilePrefix
)+sizeof(WCHAR
));
330 HeapFree(GetProcessHeap(), 0, url
);
331 return E_OUTOFMEMORY
;
334 is_file_url
= !strncmpW(wszFile
, url
, sizeof(wszFile
)/sizeof(WCHAR
));
336 if ((nByteLen
>= sizeof(wszHttp
) &&
337 !memcmp(wszHttp
, url
, sizeof(wszHttp
))) || is_file_url
)
340 if((dwFlags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
)) && is_file_url
)
343 if(nByteLen
>= sizeof(wszRes
) && !memcmp(wszRes
, url
, sizeof(wszRes
))) {
344 dwFlags
&= ~URL_FILE_USE_PATHURL
;
351 * 1 have 2[+] alnum 2,3
352 * 2 have scheme (found :) 4,6,3
353 * 3 failed (no location)
355 * 5 have 1[+] alnum 6,3
356 * 6 have location (found /) save root location
363 if(url
[1] == ':') { /* Assume path */
364 memcpy(wk2
, wszFilePrefix
, sizeof(wszFilePrefix
));
365 wk2
+= sizeof(wszFilePrefix
)/sizeof(WCHAR
);
366 if (dwFlags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
))
372 dwFlags
|= URL_ESCAPE_UNSAFE
;
375 } else if(url
[0] == '/') {
383 if (!isalnumW(*wk1
)) {state
= 3; break;}
385 if (!isalnumW(*wk1
)) {state
= 3; break;}
391 if (*wk1
++ == ':') state
= 2;
395 if (*wk1
!= '/') {state
= 6; break;}
397 if((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszLocalhost
)
399 && !memcmp(wszLocalhost
, wk1
, sizeof(wszLocalhost
))){
400 wk1
+= sizeof(wszLocalhost
)/sizeof(WCHAR
);
401 while(*wk1
== '\\' && (dwFlags
& URL_FILE_USE_PATHURL
))
405 if(*wk1
== '/' && (dwFlags
& URL_FILE_USE_PATHURL
)){
407 }else if(is_file_url
){
408 const WCHAR
*body
= wk1
;
413 if(isalnumW(*body
) && *(body
+1) == ':'){
414 if(!(dwFlags
& (URL_WININET_COMPATIBILITY
| URL_FILE_USE_PATHURL
))){
421 if(dwFlags
& URL_WININET_COMPATIBILITY
){
422 if(*wk1
== '/' && *(wk1
+1) != '/'){
429 if(*wk1
== '/' && *(wk1
+1) != '/'){
442 nWkLen
= strlenW(wk1
);
443 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
450 if(*mp
== '/' || *mp
== '\\')
457 if (!isalnumW(*wk1
) && (*wk1
!= '-') && (*wk1
!= '.') && (*wk1
!= ':'))
459 while(isalnumW(*wk1
) || (*wk1
== '-') || (*wk1
== '.') || (*wk1
== ':'))
470 if (*wk1
!= '/' && *wk1
!= '\\') {state
= 3; break;}
471 while(*wk1
== '/' || *wk1
== '\\') {
481 if(dwFlags
& URL_DONT_SIMPLIFY
) {
486 /* Now at root location, cannot back up any more. */
487 /* "root" will point at the '/' */
491 mp
= strchrW(wk1
, '/');
492 mp2
= strchrW(wk1
, '\\');
493 if(mp2
&& (!mp
|| mp2
< mp
))
496 nWkLen
= strlenW(wk1
);
497 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
504 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
514 while (*wk1
== '.') {
515 TRACE("found '/.'\n");
516 if (wk1
[1] == '/' || wk1
[1] == '\\') {
517 /* case of /./ -> skip the ./ */
520 else if (wk1
[1] == '.' && (wk1
[2] == '/'
521 || wk1
[2] == '\\' || wk1
[2] == '?'
522 || wk1
[2] == '#' || !wk1
[2])) {
523 /* case /../ -> need to backup wk2 */
524 TRACE("found '/../'\n");
525 *(wk2
-1) = '\0'; /* set end of string */
526 mp
= strrchrW(root
, '/');
527 mp2
= strrchrW(root
, '\\');
528 if(mp2
&& (!mp
|| mp2
< mp
))
530 if (mp
&& (mp
>= root
)) {
531 /* found valid backup point */
533 if(wk1
[2] != '/' && wk1
[2] != '\\')
539 /* did not find point, restore '/' */
551 FIXME("how did we get here - state=%d\n", state
);
552 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
553 HeapFree(GetProcessHeap(), 0, url
);
557 TRACE("Simplified, orig <%s>, simple <%s>\n",
558 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
560 nLen
= lstrlenW(lpszUrlCpy
);
561 while ((nLen
> 0) && ((lpszUrlCpy
[nLen
-1] <= ' ')))
562 lpszUrlCpy
[--nLen
]=0;
564 if((dwFlags
& URL_UNESCAPE
) ||
565 ((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszFile
)
566 && !memcmp(wszFile
, url
, sizeof(wszFile
))))
567 UrlUnescapeW(lpszUrlCpy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
569 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
570 URL_ESCAPE_SPACES_ONLY
|
572 URL_DONT_ESCAPE_EXTRA_INFO
|
573 URL_ESCAPE_SEGMENT_ONLY
))) {
574 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
575 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
577 } else { /* No escaping needed, just copy the string */
578 nLen
= lstrlenW(lpszUrlCpy
);
579 if(nLen
< *pcchCanonicalized
)
580 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
585 *pcchCanonicalized
= nLen
;
588 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
589 HeapFree(GetProcessHeap(), 0, url
);
592 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
597 /*************************************************************************
598 * UrlCombineA [SHLWAPI.@]
603 * pszBase [I] Base Url
604 * pszRelative [I] Url to combine with pszBase
605 * pszCombined [O] Destination for combined Url
606 * pcchCombined [O] Destination for length of pszCombined
607 * dwFlags [I] URL_ flags from "shlwapi.h"
610 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
611 * contains its length.
612 * Failure: An HRESULT error code indicating the error.
614 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
615 LPSTR pszCombined
, LPDWORD pcchCombined
,
618 LPWSTR base
, relative
, combined
;
619 DWORD ret
, len
, len2
;
621 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
622 debugstr_a(pszBase
),debugstr_a(pszRelative
),
623 pcchCombined
?*pcchCombined
:0,dwFlags
);
625 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
628 base
= HeapAlloc(GetProcessHeap(), 0,
629 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
630 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
631 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
633 MultiByteToWideChar(CP_ACP
, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
634 MultiByteToWideChar(CP_ACP
, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
637 ret
= UrlCombineW(base
, relative
, pszCombined
?combined
:NULL
, &len
, dwFlags
);
640 HeapFree(GetProcessHeap(), 0, base
);
644 len2
= WideCharToMultiByte(CP_ACP
, 0, combined
, len
, NULL
, 0, NULL
, NULL
);
645 if (len2
> *pcchCombined
) {
646 *pcchCombined
= len2
;
647 HeapFree(GetProcessHeap(), 0, base
);
650 WideCharToMultiByte(CP_ACP
, 0, combined
, len
+1, pszCombined
, (*pcchCombined
)+1,
652 *pcchCombined
= len2
;
653 HeapFree(GetProcessHeap(), 0, base
);
657 /*************************************************************************
658 * UrlCombineW [SHLWAPI.@]
662 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
663 LPWSTR pszCombined
, LPDWORD pcchCombined
,
666 PARSEDURLW base
, relative
;
667 DWORD myflags
, sizeloc
= 0;
668 DWORD i
, len
, res1
, res2
, process_case
= 0;
669 LPWSTR work
, preliminary
, mbase
, mrelative
;
670 static const WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
671 static const WCHAR fragquerystr
[] = {'#','?',0};
674 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
675 debugstr_w(pszBase
),debugstr_w(pszRelative
),
676 pcchCombined
?*pcchCombined
:0,dwFlags
);
678 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
681 base
.cbSize
= sizeof(base
);
682 relative
.cbSize
= sizeof(relative
);
684 /* Get space for duplicates of the input and the output */
685 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
687 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
688 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
691 /* Canonicalize the base input prior to looking for the scheme */
692 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
693 len
= INTERNET_MAX_URL_LENGTH
;
694 UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
696 /* Canonicalize the relative input prior to looking for the scheme */
697 len
= INTERNET_MAX_URL_LENGTH
;
698 UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
700 /* See if the base has a scheme */
701 res1
= ParseURLW(mbase
, &base
);
703 /* if pszBase has no scheme, then return pszRelative */
704 TRACE("no scheme detected in Base\n");
708 BOOL manual_search
= FALSE
;
710 work
= (LPWSTR
)base
.pszProtocol
;
711 for(i
=0; i
<base
.cchProtocol
; i
++)
712 work
[i
] = tolowerW(work
[i
]);
714 /* mk is a special case */
715 if(base
.nScheme
== URL_SCHEME_MK
) {
716 static const WCHAR wsz
[] = {':',':',0};
718 WCHAR
*ptr
= strstrW(base
.pszSuffix
, wsz
);
723 delta
= ptr
-base
.pszSuffix
;
724 base
.cchProtocol
+= delta
;
725 base
.pszSuffix
+= delta
;
726 base
.cchSuffix
-= delta
;
729 /* get size of location field (if it exists) */
730 work
= (LPWSTR
)base
.pszSuffix
;
732 if (*work
++ == '/') {
733 if (*work
++ == '/') {
734 /* At this point have start of location and
735 * it ends at next '/' or end of string.
737 while(*work
&& (*work
!= '/')) work
++;
738 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
743 /* If there is a '?', then the remaining part can only contain a
744 * query string or fragment, so start looking for the last leaf
745 * from the '?'. Otherwise, if there is a '#' and the characters
746 * immediately preceding it are ".htm[l]", then begin looking for
747 * the last leaf starting from the '#'. Otherwise the '#' is not
748 * meaningful and just start looking from the end. */
749 if ((work
= strpbrkW(base
.pszSuffix
+ sizeloc
, fragquerystr
))) {
750 const WCHAR htmlW
[] = {'.','h','t','m','l',0};
751 const int len_htmlW
= 5;
752 const WCHAR htmW
[] = {'.','h','t','m',0};
753 const int len_htmW
= 4;
755 if (*work
== '?' || base
.nScheme
== URL_SCHEME_HTTP
|| base
.nScheme
== URL_SCHEME_HTTPS
)
756 manual_search
= TRUE
;
757 else if (work
- base
.pszSuffix
> len_htmW
) {
759 if (strncmpiW(work
, htmW
, len_htmW
) == 0)
760 manual_search
= TRUE
;
764 if (!manual_search
&&
765 work
- base
.pszSuffix
> len_htmlW
) {
767 if (strncmpiW(work
, htmlW
, len_htmlW
) == 0)
768 manual_search
= TRUE
;
774 /* search backwards starting from the current position */
775 while (*work
!= '/' && work
> base
.pszSuffix
+ sizeloc
)
777 base
.cchSuffix
= work
- base
.pszSuffix
+ 1;
779 /* search backwards starting from the end of the string */
780 work
= strrchrW((base
.pszSuffix
+sizeloc
), '/');
782 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
783 base
.cchSuffix
= len
;
785 base
.cchSuffix
= sizeloc
;
790 * .pszSuffix points to location (starting with '//')
791 * .cchSuffix length of location (above) and rest less the last
793 * sizeloc length of location (above) up to but not including
797 res2
= ParseURLW(mrelative
, &relative
);
799 /* no scheme in pszRelative */
800 TRACE("no scheme detected in Relative\n");
801 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
802 relative
.cchSuffix
= strlenW(mrelative
);
803 if (*pszRelative
== ':') {
804 /* case that is either left alone or uses pszBase */
805 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
812 if (isalnumW(*mrelative
) && (*(mrelative
+ 1) == ':')) {
813 /* case that becomes "file:///" */
814 strcpyW(preliminary
, myfilestr
);
818 if ((*mrelative
== '/') && (*(mrelative
+1) == '/')) {
819 /* pszRelative has location and rest */
823 if (*mrelative
== '/') {
824 /* case where pszRelative is root to location */
828 if (*mrelative
== '#') {
829 if(!(work
= strchrW(base
.pszSuffix
+base
.cchSuffix
, '#')))
830 work
= (LPWSTR
)base
.pszSuffix
+ strlenW(base
.pszSuffix
);
832 memcpy(preliminary
, base
.pszProtocol
, (work
-base
.pszProtocol
)*sizeof(WCHAR
));
833 preliminary
[work
-base
.pszProtocol
] = '\0';
837 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
840 work
= (LPWSTR
)relative
.pszProtocol
;
841 for(i
=0; i
<relative
.cchProtocol
; i
++)
842 work
[i
] = tolowerW(work
[i
]);
845 /* handle cases where pszRelative has scheme */
846 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
847 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
849 /* since the schemes are the same */
850 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
851 /* case where pszRelative replaces location and following */
855 if (*relative
.pszSuffix
== '/') {
856 /* case where pszRelative is root to location */
860 /* replace either just location if base's location starts with a
861 * slash or otherwise everything */
862 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
865 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
866 /* case where pszRelative replaces scheme, location,
867 * and following and handles PLUGGABLE
874 } while(FALSE
); /* a little trick to allow easy exit from nested if's */
877 switch (process_case
) {
880 * Return pszRelative appended to what ever is in pszCombined,
881 * (which may the string "file:///"
883 strcatW(preliminary
, mrelative
);
886 case 2: /* case where pszRelative replaces scheme, and location */
887 strcpyW(preliminary
, mrelative
);
891 * Return the pszBase scheme with pszRelative. Basically
892 * keeps the scheme and replaces the domain and following.
894 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
895 work
= preliminary
+ base
.cchProtocol
+ 1;
896 strcpyW(work
, relative
.pszSuffix
);
900 * Return the pszBase scheme and location but everything
901 * after the location is pszRelative. (Replace document
904 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
905 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
906 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
908 strcpyW(work
, relative
.pszSuffix
);
912 * Return the pszBase without its document (if any) and
913 * append pszRelative after its scheme.
915 memcpy(preliminary
, base
.pszProtocol
,
916 (base
.cchProtocol
+1+base
.cchSuffix
)*sizeof(WCHAR
));
917 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
920 if (relative
.pszSuffix
[0] == '.' && relative
.pszSuffix
[1] == 0)
923 strcpyW(work
, relative
.pszSuffix
);
927 FIXME("How did we get here????? process_case=%d\n", process_case
);
932 /* Reuse mrelative as temp storage as it's already allocated and not needed anymore */
933 if(*pcchCombined
== 0)
935 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, (dwFlags
& ~URL_FILE_USE_PATHURL
));
936 if(SUCCEEDED(ret
) && pszCombined
) {
937 lstrcpyW(pszCombined
, mrelative
);
939 TRACE("return-%d len=%d, %s\n",
940 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
942 HeapFree(GetProcessHeap(), 0, preliminary
);
946 /*************************************************************************
947 * UrlEscapeA [SHLWAPI.@]
950 HRESULT WINAPI
UrlEscapeA(
956 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
957 WCHAR
*escapedW
= bufW
;
960 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
962 if (!pszEscaped
|| !pcchEscaped
|| !*pcchEscaped
)
965 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
967 if(dwFlags
& URL_ESCAPE_AS_UTF8
) {
968 RtlFreeUnicodeString(&urlW
);
971 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
972 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
973 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
976 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
977 if(*pcchEscaped
> lenA
) {
978 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
979 pszEscaped
[lenA
] = 0;
982 *pcchEscaped
= lenA
+ 1;
986 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
987 RtlFreeUnicodeString(&urlW
);
991 #define WINE_URL_BASH_AS_SLASH 0x01
992 #define WINE_URL_COLLAPSE_SLASHES 0x02
993 #define WINE_URL_ESCAPE_SLASH 0x04
994 #define WINE_URL_ESCAPE_HASH 0x08
995 #define WINE_URL_ESCAPE_QUESTION 0x10
996 #define WINE_URL_STOP_ON_HASH 0x20
997 #define WINE_URL_STOP_ON_QUESTION 0x40
999 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD flags
, DWORD int_flags
)
1001 if (flags
& URL_ESCAPE_SPACES_ONLY
)
1004 if ((flags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
1007 if ((flags
& URL_ESCAPE_AS_UTF8
) && (ch
>= 0x80))
1010 if (ch
<= 31 || (ch
>= 127 && ch
<= 255) )
1032 return !!(int_flags
& WINE_URL_ESCAPE_SLASH
);
1034 return !!(int_flags
& WINE_URL_ESCAPE_QUESTION
);
1036 return !!(int_flags
& WINE_URL_ESCAPE_HASH
);
1043 /*************************************************************************
1044 * UrlEscapeW [SHLWAPI.@]
1046 * Converts unsafe characters in a Url into escape sequences.
1049 * pszUrl [I] Url to modify
1050 * pszEscaped [O] Destination for modified Url
1051 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1052 * dwFlags [I] URL_ flags from "shlwapi.h"
1055 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1056 * contains its length.
1057 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1058 * pcchEscaped is set to the required length.
1060 * Converts unsafe characters into their escape sequences.
1063 * - By default this function stops converting at the first '?' or
1065 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1066 * converted, but the conversion continues past a '?' or '#'.
1067 * - Note that this function did not work well (or at all) in shlwapi version 4.
1070 * Only the following flags are implemented:
1071 *| URL_ESCAPE_SPACES_ONLY
1072 *| URL_DONT_ESCAPE_EXTRA_INFO
1073 *| URL_ESCAPE_SEGMENT_ONLY
1074 *| URL_ESCAPE_PERCENT
1076 HRESULT WINAPI
UrlEscapeW(
1079 LPDWORD pcchEscaped
,
1083 DWORD needed
= 0, ret
;
1084 BOOL stop_escaping
= FALSE
;
1085 WCHAR next
[12], *dst
, *dst_ptr
;
1087 PARSEDURLW parsed_url
;
1090 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
1092 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl
, debugstr_w(pszUrl
),
1093 pszEscaped
, pcchEscaped
, dwFlags
);
1095 if(!pszUrl
|| !pcchEscaped
|| !pszEscaped
|| *pcchEscaped
== 0)
1096 return E_INVALIDARG
;
1098 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
1099 URL_ESCAPE_SEGMENT_ONLY
|
1100 URL_DONT_ESCAPE_EXTRA_INFO
|
1101 URL_ESCAPE_PERCENT
|
1102 URL_ESCAPE_AS_UTF8
))
1103 FIXME("Unimplemented flags: %08x\n", dwFlags
);
1105 dst_ptr
= dst
= HeapAlloc(GetProcessHeap(), 0, *pcchEscaped
*sizeof(WCHAR
));
1107 return E_OUTOFMEMORY
;
1110 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
1111 /* if SPACES_ONLY specified, reset the other controls */
1112 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
1113 URL_ESCAPE_PERCENT
|
1114 URL_ESCAPE_SEGMENT_ONLY
);
1117 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1118 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
1122 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
1123 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
1125 parsed_url
.cbSize
= sizeof(parsed_url
);
1126 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
1127 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
1129 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
1131 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
1132 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
1134 switch(parsed_url
.nScheme
) {
1135 case URL_SCHEME_FILE
:
1136 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
1137 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
1140 case URL_SCHEME_HTTP
:
1141 case URL_SCHEME_HTTPS
:
1142 int_flags
|= WINE_URL_BASH_AS_SLASH
;
1143 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
1144 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1147 case URL_SCHEME_MAILTO
:
1148 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
1149 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
1152 case URL_SCHEME_INVALID
:
1155 case URL_SCHEME_FTP
:
1157 if(parsed_url
.pszSuffix
[0] != '/')
1158 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1163 for(src
= pszUrl
; *src
; ) {
1167 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
1168 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
1169 while(cur
== '/' || cur
== '\\') {
1173 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
1174 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
1175 src
+= localhost_len
+ 1;
1182 next
[0] = next
[1] = next
[2] = '/';
1189 next
[0] = next
[1] = '/';
1196 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1197 stop_escaping
= TRUE
;
1199 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1200 stop_escaping
= TRUE
;
1202 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1204 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1205 if(dwFlags
& URL_ESCAPE_AS_UTF8
) {
1208 if ((cur
>= 0xd800 && cur
<= 0xdfff) &&
1209 (src
[1] >= 0xdc00 && src
[1] <= 0xdfff))
1212 len
= WideCharToMultiByte( CP_UTF8
, 0, src
, 2,
1213 utf
, sizeof(utf
), NULL
, NULL
);
1215 len
= WideCharToMultiByte( CP_UTF8
, WC_ERR_INVALID_CHARS
, src
, 2,
1216 utf
, sizeof(utf
), NULL
, NULL
);
1222 len
= WideCharToMultiByte( CP_UTF8
, 0, &cur
, 1,
1223 utf
, sizeof(utf
), NULL
, NULL
);
1225 len
= WideCharToMultiByte( CP_UTF8
, WC_ERR_INVALID_CHARS
, &cur
, 1,
1226 utf
, sizeof(utf
), NULL
, NULL
);
1236 for(i
= 0; i
< len
; i
++) {
1238 next
[i
*3+1] = hexDigits
[(utf
[i
] >> 4) & 0xf];
1239 next
[i
*3+2] = hexDigits
[utf
[i
] & 0xf];
1244 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1245 next
[2] = hexDigits
[cur
& 0xf];
1255 if(needed
+ len
<= *pcchEscaped
) {
1256 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1262 if(needed
< *pcchEscaped
) {
1264 memcpy(pszEscaped
, dst_ptr
, (needed
+1)*sizeof(WCHAR
));
1268 needed
++; /* add one for the '\0' */
1271 *pcchEscaped
= needed
;
1273 HeapFree(GetProcessHeap(), 0, dst_ptr
);
1278 /*************************************************************************
1279 * UrlUnescapeA [SHLWAPI.@]
1281 * Converts Url escape sequences back to ordinary characters.
1284 * pszUrl [I/O] Url to convert
1285 * pszUnescaped [O] Destination for converted Url
1286 * pcchUnescaped [I/O] Size of output string
1287 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1290 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1291 * dwFlags includes URL_ESCAPE_INPLACE.
1292 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1293 * this case pcchUnescaped is set to the size required.
1295 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1296 * the first occurrence of either a '?' or '#' character.
1298 HRESULT WINAPI
UrlUnescapeA(
1301 LPDWORD pcchUnescaped
,
1308 BOOL stop_unescaping
= FALSE
;
1310 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl
), pszUnescaped
,
1311 pcchUnescaped
, dwFlags
);
1313 if (!pszUrl
) return E_INVALIDARG
;
1315 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1319 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1323 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1324 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1325 (*src
== '#' || *src
== '?')) {
1326 stop_unescaping
= TRUE
;
1328 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1329 && stop_unescaping
== FALSE
) {
1332 memcpy(buf
, src
+ 1, 2);
1334 ih
= strtol(buf
, NULL
, 16);
1336 src
+= 2; /* Advance to end of escape */
1340 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1344 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1348 needed
++; /* add one for the '\0' */
1351 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1352 *pcchUnescaped
= needed
;
1355 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1356 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1362 /*************************************************************************
1363 * UrlUnescapeW [SHLWAPI.@]
1367 HRESULT WINAPI
UrlUnescapeW(
1369 LPWSTR pszUnescaped
,
1370 LPDWORD pcchUnescaped
,
1377 BOOL stop_unescaping
= FALSE
;
1379 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl
), pszUnescaped
,
1380 pcchUnescaped
, dwFlags
);
1382 if(!pszUrl
) return E_INVALIDARG
;
1384 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1388 if (!pszUnescaped
|| !pcchUnescaped
) return E_INVALIDARG
;
1392 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1393 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1394 (*src
== '#' || *src
== '?')) {
1395 stop_unescaping
= TRUE
;
1397 } else if(*src
== '%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1398 && stop_unescaping
== FALSE
) {
1400 WCHAR buf
[5] = {'0','x',0};
1401 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1403 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1405 src
+= 2; /* Advance to end of escape */
1409 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1413 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1417 needed
++; /* add one for the '\0' */
1420 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1421 *pcchUnescaped
= needed
;
1424 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1425 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1431 /*************************************************************************
1432 * UrlGetLocationA [SHLWAPI.@]
1434 * Get the location from a Url.
1437 * pszUrl [I] Url to get the location from
1440 * A pointer to the start of the location in pszUrl, or NULL if there is
1444 * - MSDN erroneously states that "The location is the segment of the Url
1445 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1446 * stop at '?' and always return a NULL in this case.
1447 * - MSDN also erroneously states that "If a file URL has a query string,
1448 * the returned string is the query string". In all tested cases, if the
1449 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1452 *| NULL file://aa/b/cd#hohoh
1453 *| #hohoh http://aa/b/cd#hohoh
1454 *| NULL fi://aa/b/cd#hohoh
1455 *| #hohoh ff://aa/b/cd#hohoh
1457 LPCSTR WINAPI
UrlGetLocationA(
1463 base
.cbSize
= sizeof(base
);
1464 res1
= ParseURLA(pszUrl
, &base
);
1465 if (res1
) return NULL
; /* invalid scheme */
1467 /* if scheme is file: then never return pointer */
1468 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1470 /* Look for '#' and return its addr */
1471 return strchr(base
.pszSuffix
, '#');
1474 /*************************************************************************
1475 * UrlGetLocationW [SHLWAPI.@]
1477 * See UrlGetLocationA.
1479 LPCWSTR WINAPI
UrlGetLocationW(
1485 base
.cbSize
= sizeof(base
);
1486 res1
= ParseURLW(pszUrl
, &base
);
1487 if (res1
) return NULL
; /* invalid scheme */
1489 /* if scheme is file: then never return pointer */
1490 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1492 /* Look for '#' and return its addr */
1493 return strchrW(base
.pszSuffix
, '#');
1496 /*************************************************************************
1497 * UrlCompareA [SHLWAPI.@]
1502 * pszUrl1 [I] First Url to compare
1503 * pszUrl2 [I] Url to compare to pszUrl1
1504 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1507 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1508 * than, equal to, or less than pszUrl1 respectively.
1510 INT WINAPI
UrlCompareA(
1515 INT ret
, len
, len1
, len2
;
1518 return strcmp(pszUrl1
, pszUrl2
);
1519 len1
= strlen(pszUrl1
);
1520 if (pszUrl1
[len1
-1] == '/') len1
--;
1521 len2
= strlen(pszUrl2
);
1522 if (pszUrl2
[len2
-1] == '/') len2
--;
1524 return strncmp(pszUrl1
, pszUrl2
, len1
);
1525 len
= min(len1
, len2
);
1526 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1527 if (ret
) return ret
;
1528 if (len1
> len2
) return 1;
1532 /*************************************************************************
1533 * UrlCompareW [SHLWAPI.@]
1537 INT WINAPI
UrlCompareW(
1543 size_t len
, len1
, len2
;
1546 return strcmpW(pszUrl1
, pszUrl2
);
1547 len1
= strlenW(pszUrl1
);
1548 if (pszUrl1
[len1
-1] == '/') len1
--;
1549 len2
= strlenW(pszUrl2
);
1550 if (pszUrl2
[len2
-1] == '/') len2
--;
1552 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1553 len
= min(len1
, len2
);
1554 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1555 if (ret
) return ret
;
1556 if (len1
> len2
) return 1;
1560 /*************************************************************************
1561 * HashData [SHLWAPI.@]
1563 * Hash an input block into a variable sized digest.
1566 * lpSrc [I] Input block
1567 * nSrcLen [I] Length of lpSrc
1568 * lpDest [I] Output for hash digest
1569 * nDestLen [I] Length of lpDest
1572 * Success: TRUE. lpDest is filled with the computed hash value.
1573 * Failure: FALSE, if any argument is invalid.
1575 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1576 unsigned char *lpDest
, DWORD nDestLen
)
1578 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1580 if (!lpSrc
|| !lpDest
)
1581 return E_INVALIDARG
;
1583 while (destCount
>= 0)
1585 lpDest
[destCount
] = (destCount
& 0xff);
1589 while (srcCount
>= 0)
1591 destCount
= nDestLen
- 1;
1592 while (destCount
>= 0)
1594 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1602 /*************************************************************************
1603 * UrlHashA [SHLWAPI.@]
1605 * Produce a Hash from a Url.
1608 * pszUrl [I] Url to hash
1609 * lpDest [O] Destinationh for hash
1610 * nDestLen [I] Length of lpDest
1613 * Success: S_OK. lpDest is filled with the computed hash value.
1614 * Failure: E_INVALIDARG, if any argument is invalid.
1616 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1618 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1619 return E_INVALIDARG
;
1621 HashData((const BYTE
*)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1625 /*************************************************************************
1626 * UrlHashW [SHLWAPI.@]
1630 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1632 char szUrl
[MAX_PATH
];
1634 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1636 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1637 return E_INVALIDARG
;
1639 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1640 * return the same digests for the same URL.
1642 WideCharToMultiByte(CP_ACP
, 0, pszUrl
, -1, szUrl
, MAX_PATH
, NULL
, NULL
);
1643 HashData((const BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1647 /*************************************************************************
1648 * UrlApplySchemeA [SHLWAPI.@]
1650 * Apply a scheme to a Url.
1653 * pszIn [I] Url to apply scheme to
1654 * pszOut [O] Destination for modified Url
1655 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1656 * dwFlags [I] URL_ flags from "shlwapi.h"
1659 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1660 * Failure: An HRESULT error code describing the error.
1662 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1668 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn
),
1669 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1671 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1673 in
= HeapAlloc(GetProcessHeap(), 0,
1674 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1675 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1677 MultiByteToWideChar(CP_ACP
, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1678 len
= INTERNET_MAX_URL_LENGTH
;
1680 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1682 HeapFree(GetProcessHeap(), 0, in
);
1686 len
= WideCharToMultiByte(CP_ACP
, 0, out
, -1, NULL
, 0, NULL
, NULL
);
1687 if (len
> *pcchOut
) {
1692 WideCharToMultiByte(CP_ACP
, 0, out
, -1, pszOut
, *pcchOut
, NULL
, NULL
);
1697 HeapFree(GetProcessHeap(), 0, in
);
1701 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1706 DWORD value_len
, data_len
, dwType
, i
;
1707 WCHAR reg_path
[MAX_PATH
];
1708 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1711 MultiByteToWideChar(CP_ACP
, 0,
1712 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1713 -1, reg_path
, MAX_PATH
);
1714 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1716 while(value_len
= data_len
= MAX_PATH
,
1717 RegEnumValueW(newkey
, index
, value
, &value_len
,
1718 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1719 TRACE("guess %d %s is %s\n",
1720 index
, debugstr_w(value
), debugstr_w(data
));
1723 for(i
=0; i
<value_len
; i
++) {
1726 /* remember that TRUE is not-equal */
1727 j
= ChrCmpIW(Wxx
, Wyy
);
1730 if ((i
== value_len
) && !j
) {
1731 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1732 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1733 RegCloseKey(newkey
);
1736 strcpyW(pszOut
, data
);
1737 strcatW(pszOut
, pszIn
);
1738 *pcchOut
= strlenW(pszOut
);
1739 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1740 RegCloseKey(newkey
);
1745 RegCloseKey(newkey
);
1749 static HRESULT
URL_CreateFromPath(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
)
1754 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
1755 WCHAR three_slashesW
[] = {'/','/','/',0};
1756 PARSEDURLW parsed_url
;
1758 parsed_url
.cbSize
= sizeof(parsed_url
);
1759 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
1760 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
1761 needed
= strlenW(pszPath
);
1762 if (needed
>= *pcchUrl
) {
1763 *pcchUrl
= needed
+ 1;
1772 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
1773 strcpyW(pszNewUrl
, file_colonW
);
1774 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
1775 strcatW(pszNewUrl
, three_slashesW
);
1776 strcatW(pszNewUrl
, pszPath
);
1777 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
1778 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
1782 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1785 DWORD data_len
, dwType
;
1786 WCHAR data
[MAX_PATH
];
1788 static const WCHAR prefix_keyW
[] =
1789 {'S','o','f','t','w','a','r','e',
1790 '\\','M','i','c','r','o','s','o','f','t',
1791 '\\','W','i','n','d','o','w','s',
1792 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1794 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1796 /* get and prepend default */
1797 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefix_keyW
, 0, 1, &newkey
);
1798 data_len
= sizeof(data
);
1799 RegQueryValueExW(newkey
, NULL
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1800 RegCloseKey(newkey
);
1801 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1802 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1805 strcpyW(pszOut
, data
);
1806 strcatW(pszOut
, pszIn
);
1807 *pcchOut
= strlenW(pszOut
);
1808 TRACE("used default %s\n", debugstr_w(pszOut
));
1812 /*************************************************************************
1813 * UrlApplySchemeW [SHLWAPI.@]
1815 * See UrlApplySchemeA.
1817 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1819 PARSEDURLW in_scheme
;
1823 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn
),
1824 pszOut
, pcchOut
, pcchOut
? *pcchOut
: 0, dwFlags
);
1826 if (!pszIn
|| !pszOut
|| !pcchOut
) return E_INVALIDARG
;
1828 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1829 if (*pcchOut
> 1 && ':' == pszIn
[1]) {
1831 ret
= URL_CreateFromPath(pszIn
, pszOut
, &res1
);
1832 if (ret
== S_OK
|| ret
== E_POINTER
){
1836 else if (ret
== S_FALSE
)
1843 in_scheme
.cbSize
= sizeof(in_scheme
);
1844 /* See if the base has a scheme */
1845 res1
= ParseURLW(pszIn
, &in_scheme
);
1847 /* no scheme in input, need to see if we need to guess */
1848 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1849 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != E_FAIL
)
1854 /* If we are here, then either invalid scheme,
1855 * or no scheme and can't/failed guess.
1857 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1859 (dwFlags
& URL_APPLY_DEFAULT
)) {
1860 /* find and apply default scheme */
1861 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1867 /*************************************************************************
1868 * UrlIsA [SHLWAPI.@]
1870 * Determine if a Url is of a certain class.
1873 * pszUrl [I] Url to check
1874 * Urlis [I] URLIS_ constant from "shlwapi.h"
1877 * TRUE if pszUrl belongs to the class type in Urlis.
1880 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1886 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1894 base
.cbSize
= sizeof(base
);
1895 res1
= ParseURLA(pszUrl
, &base
);
1896 if (res1
) return FALSE
; /* invalid scheme */
1897 switch (base
.nScheme
)
1899 case URL_SCHEME_MAILTO
:
1900 case URL_SCHEME_SHELL
:
1901 case URL_SCHEME_JAVASCRIPT
:
1902 case URL_SCHEME_VBSCRIPT
:
1903 case URL_SCHEME_ABOUT
:
1909 return (CompareStringA(LOCALE_INVARIANT
, NORM_IGNORECASE
, pszUrl
, 5,
1910 "file:", 5) == CSTR_EQUAL
);
1912 case URLIS_DIRECTORY
:
1913 last
= pszUrl
+ strlen(pszUrl
) - 1;
1914 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1917 return PathIsURLA(pszUrl
);
1919 case URLIS_NOHISTORY
:
1920 case URLIS_APPLIABLE
:
1921 case URLIS_HASQUERY
:
1923 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1928 /*************************************************************************
1929 * UrlIsW [SHLWAPI.@]
1933 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1935 static const WCHAR file_colon
[] = { 'f','i','l','e',':',0 };
1940 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1948 base
.cbSize
= sizeof(base
);
1949 res1
= ParseURLW(pszUrl
, &base
);
1950 if (res1
) return FALSE
; /* invalid scheme */
1951 switch (base
.nScheme
)
1953 case URL_SCHEME_MAILTO
:
1954 case URL_SCHEME_SHELL
:
1955 case URL_SCHEME_JAVASCRIPT
:
1956 case URL_SCHEME_VBSCRIPT
:
1957 case URL_SCHEME_ABOUT
:
1963 return (CompareStringW(LOCALE_INVARIANT
, NORM_IGNORECASE
, pszUrl
, 5,
1964 file_colon
, 5) == CSTR_EQUAL
);
1966 case URLIS_DIRECTORY
:
1967 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1968 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1971 return PathIsURLW(pszUrl
);
1973 case URLIS_NOHISTORY
:
1974 case URLIS_APPLIABLE
:
1975 case URLIS_HASQUERY
:
1977 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1982 /*************************************************************************
1983 * UrlIsNoHistoryA [SHLWAPI.@]
1985 * Determine if a Url should not be stored in the users history list.
1988 * pszUrl [I] Url to check
1991 * TRUE, if pszUrl should be excluded from the history list,
1994 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1996 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1999 /*************************************************************************
2000 * UrlIsNoHistoryW [SHLWAPI.@]
2002 * See UrlIsNoHistoryA.
2004 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
2006 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
2009 /*************************************************************************
2010 * UrlIsOpaqueA [SHLWAPI.@]
2012 * Determine if a Url is opaque.
2015 * pszUrl [I] Url to check
2018 * TRUE if pszUrl is opaque,
2022 * An opaque Url is one that does not start with "<protocol>://".
2024 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
2026 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
2029 /*************************************************************************
2030 * UrlIsOpaqueW [SHLWAPI.@]
2034 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
2036 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
2039 /*************************************************************************
2040 * Scans for characters of type "type" and when not matching found,
2041 * returns pointer to it and length in size.
2043 * Characters tested based on RFC 1738
2045 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
2047 static DWORD alwayszero
= 0;
2056 if ( (islowerW(*start
) && isalphaW(*start
)) ||
2075 if ( isalphaW(*start
) ||
2077 /* user/password only characters */
2082 /* *extra* characters */
2089 /* *safe* characters */
2098 } else if (*start
== '%') {
2099 if (isxdigitW(*(start
+1)) &&
2100 isxdigitW(*(start
+2))) {
2112 if (isdigitW(*start
)) {
2123 if (isalnumW(*start
) ||
2136 FIXME("unknown type %d\n", type
);
2137 return (LPWSTR
)&alwayszero
;
2139 /* TRACE("scanned %d characters next char %p<%c>\n",
2140 *size, start, *start); */
2144 /*************************************************************************
2145 * Attempt to parse URL into pieces.
2147 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
2151 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
2152 pl
->pScheme
= pszUrl
;
2153 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
2154 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
2156 if ((*work
!= '/') || (*(work
+1) != '/')) goto SuccessExit
;
2157 pl
->pUserName
= work
+ 2;
2158 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
2159 if (*work
== ':' ) {
2160 /* parse password */
2162 pl
->pPassword
= work
;
2163 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
2165 /* what we just parsed must be the hostname and port
2166 * so reset pointers and clear then let it parse */
2167 pl
->szUserName
= pl
->szPassword
= 0;
2168 work
= pl
->pUserName
- 1;
2169 pl
->pUserName
= pl
->pPassword
= 0;
2171 } else if (*work
== '@') {
2175 } else if (!*work
|| (*work
== '/') || (*work
== '.')) {
2176 /* what was parsed was hostname, so reset pointers and let it parse */
2177 pl
->szUserName
= pl
->szPassword
= 0;
2178 work
= pl
->pUserName
- 1;
2179 pl
->pUserName
= pl
->pPassword
= 0;
2180 } else goto ErrorExit
;
2182 /* now start parsing hostname or hostnumber */
2184 pl
->pHostName
= work
;
2185 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
2190 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
2193 /* see if query string */
2194 pl
->pQuery
= strchrW(work
, '?');
2195 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
2198 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2199 pl
->pScheme
, pl
->szScheme
,
2200 pl
->pUserName
, pl
->szUserName
,
2201 pl
->pPassword
, pl
->szPassword
,
2202 pl
->pHostName
, pl
->szHostName
,
2203 pl
->pPort
, pl
->szPort
,
2204 pl
->pQuery
, pl
->szQuery
);
2207 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
2208 return E_INVALIDARG
;
2211 /*************************************************************************
2212 * UrlGetPartA [SHLWAPI.@]
2214 * Retrieve part of a Url.
2217 * pszIn [I] Url to parse
2218 * pszOut [O] Destination for part of pszIn requested
2219 * pcchOut [I] Size of pszOut
2220 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2221 * needed size of pszOut INCLUDING '\0'.
2222 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2223 * dwFlags [I] URL_ flags from "shlwapi.h"
2226 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2227 * Failure: An HRESULT error code describing the error.
2229 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
2230 DWORD dwPart
, DWORD dwFlags
)
2233 DWORD ret
, len
, len2
;
2235 if(!pszIn
|| !pszOut
|| !pcchOut
|| *pcchOut
<= 0)
2236 return E_INVALIDARG
;
2238 in
= HeapAlloc(GetProcessHeap(), 0,
2239 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
2240 out
= in
+ INTERNET_MAX_URL_LENGTH
;
2242 MultiByteToWideChar(CP_ACP
, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
2244 len
= INTERNET_MAX_URL_LENGTH
;
2245 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
2248 HeapFree(GetProcessHeap(), 0, in
);
2252 len2
= WideCharToMultiByte(CP_ACP
, 0, out
, len
, NULL
, 0, NULL
, NULL
);
2253 if (len2
> *pcchOut
) {
2255 HeapFree(GetProcessHeap(), 0, in
);
2258 len2
= WideCharToMultiByte(CP_ACP
, 0, out
, len
+1, pszOut
, *pcchOut
, NULL
, NULL
);
2260 HeapFree(GetProcessHeap(), 0, in
);
2264 /*************************************************************************
2265 * UrlGetPartW [SHLWAPI.@]
2269 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2270 DWORD dwPart
, DWORD dwFlags
)
2274 DWORD scheme
, size
, schsize
;
2275 LPCWSTR addr
, schaddr
;
2277 TRACE("(%s %p %p(%d) %08x %08x)\n",
2278 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2280 if(!pszIn
|| !pszOut
|| !pcchOut
|| *pcchOut
<= 0)
2281 return E_INVALIDARG
;
2285 addr
= strchrW(pszIn
, ':');
2287 scheme
= URL_SCHEME_UNKNOWN
;
2289 scheme
= get_scheme_code(pszIn
, addr
-pszIn
);
2291 ret
= URL_ParseUrl(pszIn
, &pl
);
2294 case URL_PART_SCHEME
:
2303 case URL_PART_HOSTNAME
:
2305 case URL_SCHEME_FTP
:
2306 case URL_SCHEME_HTTP
:
2307 case URL_SCHEME_GOPHER
:
2308 case URL_SCHEME_TELNET
:
2309 case URL_SCHEME_FILE
:
2310 case URL_SCHEME_HTTPS
:
2317 if(scheme
==URL_SCHEME_FILE
&& (!pl
.szHostName
||
2318 (pl
.szHostName
==1 && *(pl
.pHostName
+1)==':'))) {
2323 if (!pl
.szHostName
) {
2327 addr
= pl
.pHostName
;
2328 size
= pl
.szHostName
;
2331 case URL_PART_USERNAME
:
2332 if (!pl
.szUserName
) {
2336 addr
= pl
.pUserName
;
2337 size
= pl
.szUserName
;
2340 case URL_PART_PASSWORD
:
2341 if (!pl
.szPassword
) {
2345 addr
= pl
.pPassword
;
2346 size
= pl
.szPassword
;
2358 case URL_PART_QUERY
:
2369 return E_INVALIDARG
;
2372 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2373 if(!pl
.pScheme
|| !pl
.szScheme
) {
2377 schaddr
= pl
.pScheme
;
2378 schsize
= pl
.szScheme
;
2379 if (*pcchOut
< schsize
+ size
+ 2) {
2380 *pcchOut
= schsize
+ size
+ 2;
2383 memcpy(pszOut
, schaddr
, schsize
*sizeof(WCHAR
));
2384 pszOut
[schsize
] = ':';
2385 memcpy(pszOut
+schsize
+1, addr
, size
*sizeof(WCHAR
));
2386 pszOut
[schsize
+1+size
] = 0;
2387 *pcchOut
= schsize
+ 1 + size
;
2390 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2391 memcpy(pszOut
, addr
, size
*sizeof(WCHAR
));
2395 TRACE("len=%d %s\n", *pcchOut
, debugstr_w(pszOut
));
2400 /*************************************************************************
2401 * PathIsURLA [SHLWAPI.@]
2403 * Check if the given path is a Url.
2406 * lpszPath [I] Path to check.
2409 * TRUE if lpszPath is a Url.
2410 * FALSE if lpszPath is NULL or not a Url.
2412 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2417 TRACE("%s\n", debugstr_a(lpstrPath
));
2419 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2422 base
.cbSize
= sizeof(base
);
2423 hres
= ParseURLA(lpstrPath
, &base
);
2424 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2427 /*************************************************************************
2428 * PathIsURLW [SHLWAPI.@]
2432 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2437 TRACE("%s\n", debugstr_w(lpstrPath
));
2439 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2442 base
.cbSize
= sizeof(base
);
2443 hres
= ParseURLW(lpstrPath
, &base
);
2444 return hres
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
2447 /*************************************************************************
2448 * UrlCreateFromPathA [SHLWAPI.@]
2450 * See UrlCreateFromPathW
2452 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2454 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2456 UNICODE_STRING pathW
;
2458 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2460 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2461 return E_INVALIDARG
;
2462 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2463 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2464 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2466 if(ret
== S_OK
|| ret
== S_FALSE
) {
2467 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2468 if(*pcchUrl
> lenA
) {
2469 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2473 *pcchUrl
= lenA
+ 1;
2477 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2478 RtlFreeUnicodeString(&pathW
);
2482 /*************************************************************************
2483 * UrlCreateFromPathW [SHLWAPI.@]
2485 * Create a Url from a file path.
2488 * pszPath [I] Path to convert
2489 * pszUrl [O] Destination for the converted Url
2490 * pcchUrl [I/O] Length of pszUrl
2491 * dwReserved [I] Reserved, must be 0
2494 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2495 * Failure: An HRESULT error code.
2497 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2501 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2503 /* Validate arguments */
2504 if (dwReserved
!= 0)
2505 return E_INVALIDARG
;
2506 if (!pszUrl
|| !pcchUrl
)
2507 return E_INVALIDARG
;
2509 ret
= URL_CreateFromPath(pszPath
, pszUrl
, pcchUrl
);
2512 strcpyW(pszUrl
, pszPath
);
2518 /*************************************************************************
2519 * SHAutoComplete [SHLWAPI.@]
2521 * Enable auto-completion for an edit control.
2524 * hwndEdit [I] Handle of control to enable auto-completion for
2525 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2528 * Success: S_OK. Auto-completion is enabled for the control.
2529 * Failure: An HRESULT error code indicating the error.
2531 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2538 /*************************************************************************
2539 * MLBuildResURLA [SHLWAPI.405]
2541 * Create a Url pointing to a resource in a module.
2544 * lpszLibName [I] Name of the module containing the resource
2545 * hMod [I] Callers module handle
2546 * dwFlags [I] Undocumented flags for loading the module
2547 * lpszRes [I] Resource name
2548 * lpszDest [O] Destination for resulting Url
2549 * dwDestLen [I] Length of lpszDest
2552 * Success: S_OK. lpszDest contains the resource Url.
2553 * Failure: E_INVALIDARG, if any argument is invalid, or
2554 * E_FAIL if dwDestLen is too small.
2556 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2557 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2559 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2563 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2566 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2568 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2569 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2571 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2572 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2573 if (SUCCEEDED(hRet
) && lpszDest
)
2574 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, NULL
, NULL
);
2579 /*************************************************************************
2580 * MLBuildResURLA [SHLWAPI.406]
2582 * See MLBuildResURLA.
2584 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2585 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2587 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2588 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2589 HRESULT hRet
= E_FAIL
;
2591 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2592 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2594 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2595 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2596 return E_INVALIDARG
;
2598 if (dwDestLen
>= szResLen
+ 1)
2600 dwDestLen
-= (szResLen
+ 1);
2601 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2603 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2607 WCHAR szBuff
[MAX_PATH
];
2610 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2611 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2613 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2615 if (dwDestLen
>= dwPathLen
)
2619 dwDestLen
-= dwPathLen
;
2620 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2622 dwResLen
= strlenW(lpszRes
) + 1;
2623 if (dwDestLen
>= dwResLen
+ 1)
2625 lpszDest
[szResLen
+ dwPathLen
-1] = '/';
2626 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2631 MLFreeLibrary(hMod
);
2637 /***********************************************************************
2638 * UrlFixupW [SHLWAPI.462]
2640 * Checks the scheme part of a URL and attempts to correct misspellings.
2643 * lpszUrl [I] Pointer to the URL to be corrected
2644 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2645 * dwMaxChars [I] Maximum size of corrected URL
2648 * success: S_OK if URL corrected or already correct
2649 * failure: S_FALSE if unable to correct / COM error code if other error
2652 HRESULT WINAPI
UrlFixupW(LPCWSTR url
, LPWSTR translatedUrl
, DWORD maxChars
)
2656 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url
), translatedUrl
, maxChars
);
2661 srcLen
= lstrlenW(url
) + 1;
2663 /* For now just copy the URL directly */
2664 lstrcpynW(translatedUrl
, url
, (maxChars
< srcLen
) ? maxChars
: srcLen
);
2669 /*************************************************************************
2670 * IsInternetESCEnabled [SHLWAPI.@]
2672 BOOL WINAPI
IsInternetESCEnabled(void)