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
36 #include "wine/debug.h"
38 HMODULE WINAPI
MLLoadLibraryW(LPCWSTR
,HMODULE
,DWORD
);
39 BOOL WINAPI
MLFreeLibrary(HMODULE
);
40 HRESULT WINAPI
MLBuildResURLW(LPCWSTR
,HMODULE
,DWORD
,LPCWSTR
,LPWSTR
,DWORD
);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
48 URL_SCHEME scheme_number
;
49 WCHAR scheme_name
[12];
50 } shlwapi_schemes
[] = {
51 {URL_SCHEME_FTP
, {'f','t','p',0}},
52 {URL_SCHEME_HTTP
, {'h','t','t','p',0}},
53 {URL_SCHEME_GOPHER
, {'g','o','p','h','e','r',0}},
54 {URL_SCHEME_MAILTO
, {'m','a','i','l','t','o',0}},
55 {URL_SCHEME_NEWS
, {'n','e','w','s',0}},
56 {URL_SCHEME_NNTP
, {'n','n','t','p',0}},
57 {URL_SCHEME_TELNET
, {'t','e','l','n','e','t',0}},
58 {URL_SCHEME_WAIS
, {'w','a','i','s',0}},
59 {URL_SCHEME_FILE
, {'f','i','l','e',0}},
60 {URL_SCHEME_MK
, {'m','k',0}},
61 {URL_SCHEME_HTTPS
, {'h','t','t','p','s',0}},
62 {URL_SCHEME_SHELL
, {'s','h','e','l','l',0}},
63 {URL_SCHEME_SNEWS
, {'s','n','e','w','s',0}},
64 {URL_SCHEME_LOCAL
, {'l','o','c','a','l',0}},
65 {URL_SCHEME_JAVASCRIPT
, {'j','a','v','a','s','c','r','i','p','t',0}},
66 {URL_SCHEME_VBSCRIPT
, {'v','b','s','c','r','i','p','t',0}},
67 {URL_SCHEME_ABOUT
, {'a','b','o','u','t',0}},
68 {URL_SCHEME_RES
, {'r','e','s',0}},
72 LPCWSTR pScheme
; /* [out] start of scheme */
73 DWORD szScheme
; /* [out] size of scheme (until colon) */
74 LPCWSTR pUserName
; /* [out] start of Username */
75 DWORD szUserName
; /* [out] size of Username (until ":" or "@") */
76 LPCWSTR pPassword
; /* [out] start of Password */
77 DWORD szPassword
; /* [out] size of Password (until "@") */
78 LPCWSTR pHostName
; /* [out] start of Hostname */
79 DWORD szHostName
; /* [out] size of Hostname (until ":" or "/") */
80 LPCWSTR pPort
; /* [out] start of Port */
81 DWORD szPort
; /* [out] size of Port (until "/" or eos) */
82 LPCWSTR pQuery
; /* [out] start of Query */
83 DWORD szQuery
; /* [out] size of Query (until eos) */
93 static const CHAR hexDigits
[] = "0123456789ABCDEF";
95 static const WCHAR fileW
[] = {'f','i','l','e','\0'};
97 static const unsigned char HashDataLookup
[256] = {
98 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
99 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
100 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
101 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
102 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
103 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
104 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
105 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
106 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
107 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
108 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
109 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
110 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
111 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
112 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
113 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
114 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
115 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
116 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
117 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
119 static DWORD
get_scheme_code(LPCWSTR scheme
, DWORD scheme_len
)
123 for(i
=0; i
< sizeof(shlwapi_schemes
)/sizeof(shlwapi_schemes
[0]); i
++) {
124 if(scheme_len
== strlenW(shlwapi_schemes
[i
].scheme_name
)
125 && !memcmp(scheme
, shlwapi_schemes
[i
].scheme_name
, scheme_len
*sizeof(WCHAR
)))
126 return shlwapi_schemes
[i
].scheme_number
;
129 return URL_SCHEME_UNKNOWN
;
132 static BOOL
URL_JustLocation(LPCWSTR str
)
134 while(*str
&& (*str
== '/')) str
++;
136 while (*str
&& ((*str
== '-') ||
138 isalnumW(*str
))) str
++;
139 if (*str
== '/') return FALSE
;
145 /*************************************************************************
148 * Parse a Url into its constituent parts.
152 * y [O] Undocumented structure holding the parsed information
155 * Success: S_OK. y contains the parsed Url details.
156 * Failure: An HRESULT error code.
158 HRESULT WINAPI
ParseURLA(LPCSTR x
, PARSEDURLA
*y
)
160 WCHAR scheme
[INTERNET_MAX_SCHEME_LENGTH
];
163 y
->nScheme
= URL_SCHEME_INVALID
;
164 if (y
->cbSize
!= sizeof(*y
)) return E_INVALIDARG
;
165 /* FIXME: leading white space generates error of 0x80041001 which
168 if (*x
<= ' ') return 0x80041001;
174 y
->cchProtocol
= cnt
;
183 /* check for no scheme in string start */
184 /* (apparently schemes *must* be larger than a single character) */
185 if ((*x
== '\0') || (y
->cchProtocol
<= 1)) {
186 y
->pszProtocol
= NULL
;
190 /* found scheme, set length of remainder */
191 y
->cchSuffix
= lstrlenA(y
->pszSuffix
);
193 len
= MultiByteToWideChar(CP_ACP
, 0, y
->pszProtocol
, y
->cchProtocol
,
194 scheme
, sizeof(scheme
)/sizeof(WCHAR
));
195 y
->nScheme
= get_scheme_code(scheme
, len
);
200 /*************************************************************************
203 * Unicode version of ParseURLA.
205 HRESULT WINAPI
ParseURLW(LPCWSTR x
, PARSEDURLW
*y
)
209 y
->nScheme
= URL_SCHEME_INVALID
;
210 if (y
->cbSize
!= sizeof(*y
)) return E_INVALIDARG
;
211 /* FIXME: leading white space generates error of 0x80041001 which
214 if (*x
<= ' ') return 0x80041001;
220 y
->cchProtocol
= cnt
;
229 /* check for no scheme in string start */
230 /* (apparently schemes *must* be larger than a single character) */
231 if ((*x
== '\0') || (y
->cchProtocol
<= 1)) {
232 y
->pszProtocol
= NULL
;
236 /* found scheme, set length of remainder */
237 y
->cchSuffix
= lstrlenW(y
->pszSuffix
);
238 y
->nScheme
= get_scheme_code(y
->pszProtocol
, y
->cchProtocol
);
243 /*************************************************************************
244 * UrlCanonicalizeA [SHLWAPI.@]
246 * Canonicalize a Url.
249 * pszUrl [I] Url to cCanonicalize
250 * pszCanonicalized [O] Destination for converted Url.
251 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
252 * dwFlags [I] Flags controlling the conversion.
255 * Success: S_OK. The pszCanonicalized contains the converted Url.
256 * Failure: E_POINTER, if *pcchCanonicalized is too small.
258 * MSDN incorrectly describes the flags for this function. They should be:
259 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
260 *| URL_ESCAPE_SPACES_ONLY 0x04000000
261 *| URL_ESCAPE_PERCENT 0x00001000
262 *| URL_ESCAPE_UNSAFE 0x10000000
263 *| URL_UNESCAPE 0x10000000
264 *| URL_DONT_SIMPLIFY 0x08000000
265 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
267 HRESULT WINAPI
UrlCanonicalizeA(LPCSTR pszUrl
, LPSTR pszCanonicalized
,
268 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
270 LPWSTR base
, canonical
;
274 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl
), pszCanonicalized
,
275 pcchCanonicalized
, dwFlags
, pcchCanonicalized
? *pcchCanonicalized
: -1);
277 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
280 base
= HeapAlloc(GetProcessHeap(), 0,
281 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
282 canonical
= base
+ INTERNET_MAX_URL_LENGTH
;
284 MultiByteToWideChar(0, 0, pszUrl
, -1, base
, INTERNET_MAX_URL_LENGTH
);
285 len
= INTERNET_MAX_URL_LENGTH
;
287 ret
= UrlCanonicalizeW(base
, canonical
, &len
, dwFlags
);
289 *pcchCanonicalized
= len
* 2;
290 HeapFree(GetProcessHeap(), 0, base
);
294 len2
= WideCharToMultiByte(0, 0, canonical
, -1, 0, 0, 0, 0);
295 if (len2
> *pcchCanonicalized
) {
296 *pcchCanonicalized
= len2
;
297 HeapFree(GetProcessHeap(), 0, base
);
300 WideCharToMultiByte(0, 0, canonical
, -1, pszCanonicalized
, *pcchCanonicalized
, 0, 0);
301 *pcchCanonicalized
= len
;
302 HeapFree(GetProcessHeap(), 0, base
);
306 /*************************************************************************
307 * UrlCanonicalizeW [SHLWAPI.@]
309 * See UrlCanonicalizeA.
311 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
312 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
316 LPWSTR lpszUrlCpy
, wk1
, wk2
, mp
, mp2
, root
;
321 static const WCHAR wszFile
[] = {'f','i','l','e',':'};
322 static const WCHAR wszLocalhost
[] = {'l','o','c','a','l','h','o','s','t'};
324 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl
), pszCanonicalized
,
325 pcchCanonicalized
, dwFlags
, pcchCanonicalized
? *pcchCanonicalized
: -1);
327 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
331 *pszCanonicalized
= 0;
335 nByteLen
= (lstrlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
336 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0,
337 INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
339 if((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszFile
)
340 && !memcmp(wszFile
, pszUrl
, sizeof(wszFile
)))
346 * 1 have 2[+] alnum 2,3
347 * 2 have scheme (found :) 4,6,3
348 * 3 failed (no location)
350 * 5 have 1[+] alnum 6,3
351 * 6 have location (found /) save root location
354 wk1
= (LPWSTR
)pszUrl
;
358 if(pszUrl
[1] == ':') { /* Assume path */
359 static const WCHAR wszFilePrefix
[] = {'f','i','l','e',':','/','/','/'};
361 memcpy(wk2
, wszFilePrefix
, sizeof(wszFilePrefix
));
362 wk2
+= sizeof(wszFilePrefix
)/sizeof(WCHAR
);
363 if (dwFlags
& URL_FILE_USE_PATHURL
)
369 dwFlags
|= URL_ESCAPE_UNSAFE
;
376 if (!isalnumW(*wk1
)) {state
= 3; break;}
378 if (!isalnumW(*wk1
)) {state
= 3; break;}
384 if (*wk1
++ == ':') state
= 2;
388 if (*wk1
!= '/') {state
= 6; break;}
390 if((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszLocalhost
)
391 && !memcmp(wszLocalhost
, wk1
, sizeof(wszLocalhost
))){
392 wk1
+= sizeof(wszLocalhost
)/sizeof(WCHAR
);
393 while(*wk1
== '\\' && (dwFlags
& URL_FILE_USE_PATHURL
))
396 if(*wk1
== '/' && (dwFlags
& URL_FILE_USE_PATHURL
))
401 nWkLen
= strlenW(wk1
);
402 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
408 if(*mp
== '/' || *mp
== '\\')
414 if (!isalnumW(*wk1
) && (*wk1
!= '-') && (*wk1
!= '.') && (*wk1
!= ':'))
416 while(isalnumW(*wk1
) || (*wk1
== '-') || (*wk1
== '.') || (*wk1
== ':'))
423 if (*wk1
!= '/' && *wk1
!= '\\') {state
= 3; break;}
424 while(*wk1
== '/' || *wk1
== '\\') {
431 if(dwFlags
& URL_DONT_SIMPLIFY
) {
436 /* Now at root location, cannot back up any more. */
437 /* "root" will point at the '/' */
441 mp
= strchrW(wk1
, '/');
442 mp2
= strchrW(wk1
, '\\');
443 if(mp2
&& (!mp
|| mp2
< mp
))
446 nWkLen
= strlenW(wk1
);
447 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
454 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
462 TRACE("found '/.'\n");
463 if (wk1
[1] == '/' || wk1
[1] == '\\') {
464 /* case of /./ -> skip the ./ */
467 else if (wk1
[1] == '.') {
468 /* found /.. look for next / */
469 TRACE("found '/..'\n");
470 if (wk1
[2] == '/' || wk1
[2] == '\\' ||wk1
[2] == '?'
471 || wk1
[2] == '#' || !wk1
[2]) {
472 /* case /../ -> need to backup wk2 */
473 TRACE("found '/../'\n");
474 *(wk2
-1) = '\0'; /* set end of string */
475 mp
= strrchrW(root
, slash
);
476 if (mp
&& (mp
>= root
)) {
477 /* found valid backup point */
479 if(wk1
[2] != '/' && wk1
[2] != '\\')
485 /* did not find point, restore '/' */
495 FIXME("how did we get here - state=%d\n", state
);
496 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
500 TRACE("Simplified, orig <%s>, simple <%s>\n",
501 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
503 nLen
= lstrlenW(lpszUrlCpy
);
504 while ((nLen
> 0) && ((lpszUrlCpy
[nLen
-1] <= ' ')))
505 lpszUrlCpy
[--nLen
]=0;
507 if(dwFlags
& (URL_UNESCAPE
| URL_FILE_USE_PATHURL
))
508 UrlUnescapeW(lpszUrlCpy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
510 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
511 URL_ESCAPE_SPACES_ONLY
|
513 URL_DONT_ESCAPE_EXTRA_INFO
|
514 URL_ESCAPE_SEGMENT_ONLY
))) {
515 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
516 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
518 } else { /* No escaping needed, just copy the string */
519 nLen
= lstrlenW(lpszUrlCpy
);
520 if(nLen
< *pcchCanonicalized
)
521 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
526 *pcchCanonicalized
= nLen
;
529 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
532 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
537 /*************************************************************************
538 * UrlCombineA [SHLWAPI.@]
543 * pszBase [I] Base Url
544 * pszRelative [I] Url to combine with pszBase
545 * pszCombined [O] Destination for combined Url
546 * pcchCombined [O] Destination for length of pszCombined
547 * dwFlags [I] URL_ flags from "shlwapi.h"
550 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
551 * contains its length.
552 * Failure: An HRESULT error code indicating the error.
554 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
555 LPSTR pszCombined
, LPDWORD pcchCombined
,
558 LPWSTR base
, relative
, combined
;
559 DWORD ret
, len
, len2
;
561 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
562 debugstr_a(pszBase
),debugstr_a(pszRelative
),
563 pcchCombined
?*pcchCombined
:0,dwFlags
);
565 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
568 base
= HeapAlloc(GetProcessHeap(), 0,
569 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
570 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
571 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
573 MultiByteToWideChar(0, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
574 MultiByteToWideChar(0, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
577 ret
= UrlCombineW(base
, relative
, pszCombined
?combined
:NULL
, &len
, dwFlags
);
580 HeapFree(GetProcessHeap(), 0, base
);
584 len2
= WideCharToMultiByte(0, 0, combined
, len
, 0, 0, 0, 0);
585 if (len2
> *pcchCombined
) {
586 *pcchCombined
= len2
;
587 HeapFree(GetProcessHeap(), 0, base
);
590 WideCharToMultiByte(0, 0, combined
, len
+1, pszCombined
, (*pcchCombined
)+1,
592 *pcchCombined
= len2
;
593 HeapFree(GetProcessHeap(), 0, base
);
597 /*************************************************************************
598 * UrlCombineW [SHLWAPI.@]
602 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
603 LPWSTR pszCombined
, LPDWORD pcchCombined
,
606 PARSEDURLW base
, relative
;
607 DWORD myflags
, sizeloc
= 0;
608 DWORD len
, res1
, res2
, process_case
= 0;
609 LPWSTR work
, preliminary
, mbase
, mrelative
;
610 static const WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
611 static const WCHAR single_slash
[] = {'/','\0'};
614 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
615 debugstr_w(pszBase
),debugstr_w(pszRelative
),
616 pcchCombined
?*pcchCombined
:0,dwFlags
);
618 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
621 base
.cbSize
= sizeof(base
);
622 relative
.cbSize
= sizeof(relative
);
624 /* Get space for duplicates of the input and the output */
625 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
627 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
628 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
631 /* Canonicalize the base input prior to looking for the scheme */
632 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
633 len
= INTERNET_MAX_URL_LENGTH
;
634 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
636 /* Canonicalize the relative input prior to looking for the scheme */
637 len
= INTERNET_MAX_URL_LENGTH
;
638 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
640 /* See if the base has a scheme */
641 res1
= ParseURLW(mbase
, &base
);
643 /* if pszBase has no scheme, then return pszRelative */
644 TRACE("no scheme detected in Base\n");
648 /* mk is a special case */
649 if(base
.nScheme
== URL_SCHEME_MK
) {
650 static const WCHAR wsz
[] = {':',':',0};
652 WCHAR
*ptr
= strstrW(base
.pszSuffix
, wsz
);
657 delta
= ptr
-base
.pszSuffix
;
658 base
.cchProtocol
+= delta
;
659 base
.pszSuffix
+= delta
;
660 base
.cchSuffix
-= delta
;
663 /* get size of location field (if it exists) */
664 work
= (LPWSTR
)base
.pszSuffix
;
666 if (*work
++ == '/') {
667 if (*work
++ == '/') {
668 /* At this point have start of location and
669 * it ends at next '/' or end of string.
671 while(*work
&& (*work
!= '/')) work
++;
672 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
677 /* Change .sizep2 to not have the last leaf in it,
678 * Note: we need to start after the location (if it exists)
680 work
= strrchrW((base
.pszSuffix
+sizeloc
), '/');
682 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
683 base
.cchSuffix
= len
;
688 * .pszSuffix points to location (starting with '//')
689 * .cchSuffix length of location (above) and rest less the last
691 * sizeloc length of location (above) up to but not including
695 res2
= ParseURLW(mrelative
, &relative
);
697 /* no scheme in pszRelative */
698 TRACE("no scheme detected in Relative\n");
699 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
700 relative
.cchSuffix
= strlenW(mrelative
);
701 if (*pszRelative
== ':') {
702 /* case that is either left alone or uses pszBase */
703 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
710 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == ':')) {
711 /* case that becomes "file:///" */
712 strcpyW(preliminary
, myfilestr
);
716 if ((*mrelative
== '/') && (*(mrelative
+1) == '/')) {
717 /* pszRelative has location and rest */
721 if (*mrelative
== '/') {
722 /* case where pszRelative is root to location */
726 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
730 /* handle cases where pszRelative has scheme */
731 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
732 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
734 /* since the schemes are the same */
735 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
736 /* case where pszRelative replaces location and following */
740 if (*relative
.pszSuffix
== '/') {
741 /* case where pszRelative is root to location */
745 /* replace either just location if base's location starts with a
746 * slash or otherwise everything */
747 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
750 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
751 /* case where pszRelative replaces scheme, location,
752 * and following and handles PLUGGABLE
759 } while(FALSE
); /* a little trick to allow easy exit from nested if's */
762 switch (process_case
) {
765 * Return pszRelative appended to what ever is in pszCombined,
766 * (which may the string "file:///"
768 strcatW(preliminary
, mrelative
);
772 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
773 * and pszRelative starts with "//", then append a "/"
775 strcpyW(preliminary
, mrelative
);
776 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
777 URL_JustLocation(relative
.pszSuffix
))
778 strcatW(preliminary
, single_slash
);
782 * Return the pszBase scheme with pszRelative. Basically
783 * keeps the scheme and replaces the domain and following.
785 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
786 work
= preliminary
+ base
.cchProtocol
+ 1;
787 strcpyW(work
, relative
.pszSuffix
);
791 * Return the pszBase scheme and location but everything
792 * after the location is pszRelative. (Replace document
795 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
796 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
797 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
799 strcpyW(work
, relative
.pszSuffix
);
803 * Return the pszBase without its document (if any) and
804 * append pszRelative after its scheme.
806 memcpy(preliminary
, base
.pszProtocol
,
807 (base
.cchProtocol
+1+base
.cchSuffix
)*sizeof(WCHAR
));
808 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
811 strcpyW(work
, relative
.pszSuffix
);
815 FIXME("How did we get here????? process_case=%d\n", process_case
);
820 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
821 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, (dwFlags
& ~URL_FILE_USE_PATHURL
));
822 if(SUCCEEDED(ret
) && pszCombined
) {
823 lstrcpyW(pszCombined
, mrelative
);
825 TRACE("return-%d len=%d, %s\n",
826 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
828 HeapFree(GetProcessHeap(), 0, preliminary
);
832 /*************************************************************************
833 * UrlEscapeA [SHLWAPI.@]
836 HRESULT WINAPI
UrlEscapeA(
842 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
843 WCHAR
*escapedW
= bufW
;
846 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
848 if (!pszEscaped
|| !pcchEscaped
|| !*pcchEscaped
)
851 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
853 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
854 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
855 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
858 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
859 if(*pcchEscaped
> lenA
) {
860 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
861 pszEscaped
[lenA
] = 0;
864 *pcchEscaped
= lenA
+ 1;
868 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
869 RtlFreeUnicodeString(&urlW
);
873 #define WINE_URL_BASH_AS_SLASH 0x01
874 #define WINE_URL_COLLAPSE_SLASHES 0x02
875 #define WINE_URL_ESCAPE_SLASH 0x04
876 #define WINE_URL_ESCAPE_HASH 0x08
877 #define WINE_URL_ESCAPE_QUESTION 0x10
878 #define WINE_URL_STOP_ON_HASH 0x20
879 #define WINE_URL_STOP_ON_QUESTION 0x40
881 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
, DWORD int_flags
)
887 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
894 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
897 if (ch
<= 31 || ch
>= 127)
918 if (int_flags
& WINE_URL_ESCAPE_SLASH
) return TRUE
;
922 if (int_flags
& WINE_URL_ESCAPE_QUESTION
) return TRUE
;
926 if (int_flags
& WINE_URL_ESCAPE_HASH
) return TRUE
;
936 /*************************************************************************
937 * UrlEscapeW [SHLWAPI.@]
939 * Converts unsafe characters in a Url into escape sequences.
942 * pszUrl [I] Url to modify
943 * pszEscaped [O] Destination for modified Url
944 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
945 * dwFlags [I] URL_ flags from "shlwapi.h"
948 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
949 * contains its length.
950 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
951 * pcchEscaped is set to the required length.
953 * Converts unsafe characters into their escape sequences.
956 * - By default this function stops converting at the first '?' or
958 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
959 * converted, but the conversion continues past a '?' or '#'.
960 * - Note that this function did not work well (or at all) in shlwapi version 4.
963 * Only the following flags are implemented:
964 *| URL_ESCAPE_SPACES_ONLY
965 *| URL_DONT_ESCAPE_EXTRA_INFO
966 *| URL_ESCAPE_SEGMENT_ONLY
967 *| URL_ESCAPE_PERCENT
969 HRESULT WINAPI
UrlEscapeW(
976 DWORD needed
= 0, ret
;
977 BOOL stop_escaping
= FALSE
;
978 WCHAR next
[5], *dst
= pszEscaped
;
980 PARSEDURLW parsed_url
;
983 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
985 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl
), pszEscaped
,
986 pcchEscaped
, dwFlags
);
988 if(!pszUrl
|| !pcchEscaped
)
991 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
992 URL_ESCAPE_SEGMENT_ONLY
|
993 URL_DONT_ESCAPE_EXTRA_INFO
|
995 FIXME("Unimplemented flags: %08x\n", dwFlags
);
998 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
999 /* if SPACES_ONLY specified, reset the other controls */
1000 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
1001 URL_ESCAPE_PERCENT
|
1002 URL_ESCAPE_SEGMENT_ONLY
);
1005 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1006 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
1010 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
1011 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
1013 parsed_url
.cbSize
= sizeof(parsed_url
);
1014 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
1015 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
1017 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
1019 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
1020 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
1022 switch(parsed_url
.nScheme
) {
1023 case URL_SCHEME_FILE
:
1024 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
1025 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
1028 case URL_SCHEME_HTTP
:
1029 case URL_SCHEME_HTTPS
:
1030 int_flags
|= WINE_URL_BASH_AS_SLASH
;
1031 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
1032 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1035 case URL_SCHEME_MAILTO
:
1036 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
1037 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
1040 case URL_SCHEME_INVALID
:
1043 case URL_SCHEME_FTP
:
1045 if(parsed_url
.pszSuffix
[0] != '/')
1046 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1051 for(src
= pszUrl
; *src
; ) {
1055 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
1056 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
1057 while(cur
== '/' || cur
== '\\') {
1061 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
1062 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
1063 src
+= localhost_len
+ 1;
1070 next
[0] = next
[1] = next
[2] = '/';
1077 next
[0] = next
[1] = '/';
1084 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1085 stop_escaping
= TRUE
;
1087 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1088 stop_escaping
= TRUE
;
1090 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1092 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1094 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1095 next
[2] = hexDigits
[cur
& 0xf];
1104 if(needed
+ len
<= *pcchEscaped
) {
1105 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1111 if(needed
< *pcchEscaped
) {
1115 needed
++; /* add one for the '\0' */
1118 *pcchEscaped
= needed
;
1123 /*************************************************************************
1124 * UrlUnescapeA [SHLWAPI.@]
1126 * Converts Url escape sequences back to ordinary characters.
1129 * pszUrl [I/O] Url to convert
1130 * pszUnescaped [O] Destination for converted Url
1131 * pcchUnescaped [I/O] Size of output string
1132 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1135 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1136 * dwFlags includes URL_ESCAPE_INPLACE.
1137 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1138 * this case pcchUnescaped is set to the size required.
1140 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1141 * the first occurrence of either a '?' or '#' character.
1143 HRESULT WINAPI
UrlUnescapeA(
1146 LPDWORD pcchUnescaped
,
1153 BOOL stop_unescaping
= FALSE
;
1155 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl
), pszUnescaped
,
1156 pcchUnescaped
, dwFlags
);
1158 if(!pszUrl
|| (!pszUnescaped
&& !(dwFlags
& URL_UNESCAPE_INPLACE
)) || !pcchUnescaped
)
1159 return E_INVALIDARG
;
1161 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1166 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1167 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1168 (*src
== '#' || *src
== '?')) {
1169 stop_unescaping
= TRUE
;
1171 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1172 && stop_unescaping
== FALSE
) {
1175 memcpy(buf
, src
+ 1, 2);
1177 ih
= strtol(buf
, NULL
, 16);
1179 src
+= 2; /* Advance to end of escape */
1183 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1187 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1191 needed
++; /* add one for the '\0' */
1194 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1195 *pcchUnescaped
= needed
;
1198 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1199 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1205 /*************************************************************************
1206 * UrlUnescapeW [SHLWAPI.@]
1210 HRESULT WINAPI
UrlUnescapeW(
1212 LPWSTR pszUnescaped
,
1213 LPDWORD pcchUnescaped
,
1220 BOOL stop_unescaping
= FALSE
;
1222 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl
), pszUnescaped
,
1223 pcchUnescaped
, dwFlags
);
1225 if(!pszUrl
|| (!pszUnescaped
&& !(dwFlags
& URL_UNESCAPE_INPLACE
))|| !pcchUnescaped
)
1226 return E_INVALIDARG
;
1228 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1233 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1234 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1235 (*src
== '#' || *src
== '?')) {
1236 stop_unescaping
= TRUE
;
1238 } else if(*src
== '%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1239 && stop_unescaping
== FALSE
) {
1241 WCHAR buf
[5] = {'0','x',0};
1242 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1244 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1246 src
+= 2; /* Advance to end of escape */
1250 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1254 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1258 needed
++; /* add one for the '\0' */
1261 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1262 *pcchUnescaped
= needed
;
1265 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1266 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1272 /*************************************************************************
1273 * UrlGetLocationA [SHLWAPI.@]
1275 * Get the location from a Url.
1278 * pszUrl [I] Url to get the location from
1281 * A pointer to the start of the location in pszUrl, or NULL if there is
1285 * - MSDN erroneously states that "The location is the segment of the Url
1286 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1287 * stop at '?' and always return a NULL in this case.
1288 * - MSDN also erroneously states that "If a file URL has a query string,
1289 * the returned string is the query string". In all tested cases, if the
1290 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1293 *| NULL file://aa/b/cd#hohoh
1294 *| #hohoh http://aa/b/cd#hohoh
1295 *| NULL fi://aa/b/cd#hohoh
1296 *| #hohoh ff://aa/b/cd#hohoh
1298 LPCSTR WINAPI
UrlGetLocationA(
1304 base
.cbSize
= sizeof(base
);
1305 res1
= ParseURLA(pszUrl
, &base
);
1306 if (res1
) return NULL
; /* invalid scheme */
1308 /* if scheme is file: then never return pointer */
1309 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1311 /* Look for '#' and return its addr */
1312 return strchr(base
.pszSuffix
, '#');
1315 /*************************************************************************
1316 * UrlGetLocationW [SHLWAPI.@]
1318 * See UrlGetLocationA.
1320 LPCWSTR WINAPI
UrlGetLocationW(
1326 base
.cbSize
= sizeof(base
);
1327 res1
= ParseURLW(pszUrl
, &base
);
1328 if (res1
) return NULL
; /* invalid scheme */
1330 /* if scheme is file: then never return pointer */
1331 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1333 /* Look for '#' and return its addr */
1334 return strchrW(base
.pszSuffix
, '#');
1337 /*************************************************************************
1338 * UrlCompareA [SHLWAPI.@]
1343 * pszUrl1 [I] First Url to compare
1344 * pszUrl2 [I] Url to compare to pszUrl1
1345 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1348 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1349 * than, equal to, or less than pszUrl1 respectively.
1351 INT WINAPI
UrlCompareA(
1356 INT ret
, len
, len1
, len2
;
1359 return strcmp(pszUrl1
, pszUrl2
);
1360 len1
= strlen(pszUrl1
);
1361 if (pszUrl1
[len1
-1] == '/') len1
--;
1362 len2
= strlen(pszUrl2
);
1363 if (pszUrl2
[len2
-1] == '/') len2
--;
1365 return strncmp(pszUrl1
, pszUrl2
, len1
);
1366 len
= min(len1
, len2
);
1367 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1368 if (ret
) return ret
;
1369 if (len1
> len2
) return 1;
1373 /*************************************************************************
1374 * UrlCompareW [SHLWAPI.@]
1378 INT WINAPI
UrlCompareW(
1384 size_t len
, len1
, len2
;
1387 return strcmpW(pszUrl1
, pszUrl2
);
1388 len1
= strlenW(pszUrl1
);
1389 if (pszUrl1
[len1
-1] == '/') len1
--;
1390 len2
= strlenW(pszUrl2
);
1391 if (pszUrl2
[len2
-1] == '/') len2
--;
1393 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1394 len
= min(len1
, len2
);
1395 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1396 if (ret
) return ret
;
1397 if (len1
> len2
) return 1;
1401 /*************************************************************************
1402 * HashData [SHLWAPI.@]
1404 * Hash an input block into a variable sized digest.
1407 * lpSrc [I] Input block
1408 * nSrcLen [I] Length of lpSrc
1409 * lpDest [I] Output for hash digest
1410 * nDestLen [I] Length of lpDest
1413 * Success: TRUE. lpDest is filled with the computed hash value.
1414 * Failure: FALSE, if any argument is invalid.
1416 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1417 unsigned char *lpDest
, DWORD nDestLen
)
1419 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1421 if (IsBadReadPtr(lpSrc
, nSrcLen
) ||
1422 IsBadWritePtr(lpDest
, nDestLen
))
1423 return E_INVALIDARG
;
1425 while (destCount
>= 0)
1427 lpDest
[destCount
] = (destCount
& 0xff);
1431 while (srcCount
>= 0)
1433 destCount
= nDestLen
- 1;
1434 while (destCount
>= 0)
1436 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1444 /*************************************************************************
1445 * UrlHashA [SHLWAPI.@]
1447 * Produce a Hash from a Url.
1450 * pszUrl [I] Url to hash
1451 * lpDest [O] Destinationh for hash
1452 * nDestLen [I] Length of lpDest
1455 * Success: S_OK. lpDest is filled with the computed hash value.
1456 * Failure: E_INVALIDARG, if any argument is invalid.
1458 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1460 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1461 return E_INVALIDARG
;
1463 HashData((const BYTE
*)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1467 /*************************************************************************
1468 * UrlHashW [SHLWAPI.@]
1472 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1474 char szUrl
[MAX_PATH
];
1476 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1478 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1479 return E_INVALIDARG
;
1481 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1482 * return the same digests for the same URL.
1484 WideCharToMultiByte(0, 0, pszUrl
, -1, szUrl
, MAX_PATH
, 0, 0);
1485 HashData((const BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1489 /*************************************************************************
1490 * UrlApplySchemeA [SHLWAPI.@]
1492 * Apply a scheme to a Url.
1495 * pszIn [I] Url to apply scheme to
1496 * pszOut [O] Destination for modified Url
1497 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1498 * dwFlags [I] URL_ flags from "shlwapi.h"
1501 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1502 * Failure: An HRESULT error code describing the error.
1504 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1507 DWORD ret
, len
, len2
;
1509 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1510 debugstr_a(pszIn
), *pcchOut
, dwFlags
);
1512 in
= HeapAlloc(GetProcessHeap(), 0,
1513 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1514 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1516 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1517 len
= INTERNET_MAX_URL_LENGTH
;
1519 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1520 if ((ret
!= S_OK
) && (ret
!= S_FALSE
)) {
1521 HeapFree(GetProcessHeap(), 0, in
);
1525 len2
= WideCharToMultiByte(0, 0, out
, len
+1, 0, 0, 0, 0);
1526 if (len2
> *pcchOut
) {
1528 HeapFree(GetProcessHeap(), 0, in
);
1531 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1533 HeapFree(GetProcessHeap(), 0, in
);
1537 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1542 DWORD value_len
, data_len
, dwType
, i
;
1543 WCHAR reg_path
[MAX_PATH
];
1544 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1547 MultiByteToWideChar(0, 0,
1548 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1549 -1, reg_path
, MAX_PATH
);
1550 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1552 while(value_len
= data_len
= MAX_PATH
,
1553 RegEnumValueW(newkey
, index
, value
, &value_len
,
1554 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1555 TRACE("guess %d %s is %s\n",
1556 index
, debugstr_w(value
), debugstr_w(data
));
1559 for(i
=0; i
<value_len
; i
++) {
1562 /* remember that TRUE is not-equal */
1563 j
= ChrCmpIW(Wxx
, Wyy
);
1566 if ((i
== value_len
) && !j
) {
1567 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1568 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1569 RegCloseKey(newkey
);
1572 strcpyW(pszOut
, data
);
1573 strcatW(pszOut
, pszIn
);
1574 *pcchOut
= strlenW(pszOut
);
1575 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1576 RegCloseKey(newkey
);
1581 RegCloseKey(newkey
);
1585 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1588 DWORD data_len
, dwType
;
1589 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1591 static const WCHAR prefix_keyW
[] =
1592 {'S','o','f','t','w','a','r','e',
1593 '\\','M','i','c','r','o','s','o','f','t',
1594 '\\','W','i','n','d','o','w','s',
1595 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1597 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1599 /* get and prepend default */
1600 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, prefix_keyW
, 0, 1, &newkey
);
1601 data_len
= MAX_PATH
;
1604 RegQueryValueExW(newkey
, value
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1605 RegCloseKey(newkey
);
1606 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1607 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1610 strcpyW(pszOut
, data
);
1611 strcatW(pszOut
, pszIn
);
1612 *pcchOut
= strlenW(pszOut
);
1613 TRACE("used default %s\n", debugstr_w(pszOut
));
1617 /*************************************************************************
1618 * UrlApplySchemeW [SHLWAPI.@]
1620 * See UrlApplySchemeA.
1622 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1624 PARSEDURLW in_scheme
;
1628 TRACE("(in %s, out size %d, flags %08x)\n",
1629 debugstr_w(pszIn
), *pcchOut
, dwFlags
);
1631 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1632 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1633 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwFlags
);
1634 strcpyW(pszOut
, pszIn
);
1635 *pcchOut
= strlenW(pszOut
);
1639 in_scheme
.cbSize
= sizeof(in_scheme
);
1640 /* See if the base has a scheme */
1641 res1
= ParseURLW(pszIn
, &in_scheme
);
1643 /* no scheme in input, need to see if we need to guess */
1644 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1645 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != -1)
1650 /* we have a scheme, see if valid (known scheme) */
1651 if (in_scheme
.nScheme
) {
1652 /* have valid scheme, so just copy and exit */
1653 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1654 *pcchOut
= strlenW(pszIn
) + 1;
1657 strcpyW(pszOut
, pszIn
);
1658 *pcchOut
= strlenW(pszOut
);
1659 TRACE("valid scheme, returning copy\n");
1664 /* If we are here, then either invalid scheme,
1665 * or no scheme and can't/failed guess.
1667 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1669 (dwFlags
& URL_APPLY_DEFAULT
)) {
1670 /* find and apply default scheme */
1671 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1674 /* just copy and give proper return code */
1675 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1676 *pcchOut
= strlenW(pszIn
) + 1;
1679 strcpyW(pszOut
, pszIn
);
1680 *pcchOut
= strlenW(pszOut
);
1681 TRACE("returning copy, left alone\n");
1685 /*************************************************************************
1686 * UrlIsA [SHLWAPI.@]
1688 * Determine if a Url is of a certain class.
1691 * pszUrl [I] Url to check
1692 * Urlis [I] URLIS_ constant from "shlwapi.h"
1695 * TRUE if pszUrl belongs to the class type in Urlis.
1698 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1704 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1709 base
.cbSize
= sizeof(base
);
1710 res1
= ParseURLA(pszUrl
, &base
);
1711 if (res1
) return FALSE
; /* invalid scheme */
1712 switch (base
.nScheme
)
1714 case URL_SCHEME_MAILTO
:
1715 case URL_SCHEME_SHELL
:
1716 case URL_SCHEME_JAVASCRIPT
:
1717 case URL_SCHEME_VBSCRIPT
:
1718 case URL_SCHEME_ABOUT
:
1724 return !StrCmpNA("file:", pszUrl
, 5);
1726 case URLIS_DIRECTORY
:
1727 last
= pszUrl
+ strlen(pszUrl
) - 1;
1728 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1731 return PathIsURLA(pszUrl
);
1733 case URLIS_NOHISTORY
:
1734 case URLIS_APPLIABLE
:
1735 case URLIS_HASQUERY
:
1737 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1742 /*************************************************************************
1743 * UrlIsW [SHLWAPI.@]
1747 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1749 static const WCHAR stemp
[] = { 'f','i','l','e',':',0 };
1754 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1759 base
.cbSize
= sizeof(base
);
1760 res1
= ParseURLW(pszUrl
, &base
);
1761 if (res1
) return FALSE
; /* invalid scheme */
1762 switch (base
.nScheme
)
1764 case URL_SCHEME_MAILTO
:
1765 case URL_SCHEME_SHELL
:
1766 case URL_SCHEME_JAVASCRIPT
:
1767 case URL_SCHEME_VBSCRIPT
:
1768 case URL_SCHEME_ABOUT
:
1774 return !strncmpW(stemp
, pszUrl
, 5);
1776 case URLIS_DIRECTORY
:
1777 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1778 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1781 return PathIsURLW(pszUrl
);
1783 case URLIS_NOHISTORY
:
1784 case URLIS_APPLIABLE
:
1785 case URLIS_HASQUERY
:
1787 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1792 /*************************************************************************
1793 * UrlIsNoHistoryA [SHLWAPI.@]
1795 * Determine if a Url should not be stored in the users history list.
1798 * pszUrl [I] Url to check
1801 * TRUE, if pszUrl should be excluded from the history list,
1804 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1806 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1809 /*************************************************************************
1810 * UrlIsNoHistoryW [SHLWAPI.@]
1812 * See UrlIsNoHistoryA.
1814 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1816 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1819 /*************************************************************************
1820 * UrlIsOpaqueA [SHLWAPI.@]
1822 * Determine if a Url is opaque.
1825 * pszUrl [I] Url to check
1828 * TRUE if pszUrl is opaque,
1832 * An opaque Url is one that does not start with "<protocol>://".
1834 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1836 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1839 /*************************************************************************
1840 * UrlIsOpaqueW [SHLWAPI.@]
1844 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1846 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1849 /*************************************************************************
1850 * Scans for characters of type "type" and when not matching found,
1851 * returns pointer to it and length in size.
1853 * Characters tested based on RFC 1738
1855 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1857 static DWORD alwayszero
= 0;
1866 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1881 if ( isalphaW(*start
) ||
1883 /* user/password only characters */
1888 /* *extra* characters */
1895 /* *safe* characters */
1903 } else if (*start
== '%') {
1904 if (isxdigitW(*(start
+1)) &&
1905 isxdigitW(*(start
+2))) {
1917 if (isdigitW(*start
)) {
1928 if (isalnumW(*start
) ||
1939 FIXME("unknown type %d\n", type
);
1940 return (LPWSTR
)&alwayszero
;
1942 /* TRACE("scanned %d characters next char %p<%c>\n",
1943 *size, start, *start); */
1947 /*************************************************************************
1948 * Attempt to parse URL into pieces.
1950 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
1954 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
1955 pl
->pScheme
= pszUrl
;
1956 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
1957 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
1959 if ((*work
!= '/') || (*(work
+1) != '/')) goto ErrorExit
;
1960 pl
->pUserName
= work
+ 2;
1961 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
1962 if (*work
== ':' ) {
1963 /* parse password */
1965 pl
->pPassword
= work
;
1966 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
1968 /* what we just parsed must be the hostname and port
1969 * so reset pointers and clear then let it parse */
1970 pl
->szUserName
= pl
->szPassword
= 0;
1971 work
= pl
->pUserName
- 1;
1972 pl
->pUserName
= pl
->pPassword
= 0;
1974 } else if (*work
== '@') {
1978 } else if (!*work
|| (*work
== '/') || (*work
== '.')) {
1979 /* what was parsed was hostname, so reset pointers and let it parse */
1980 pl
->szUserName
= pl
->szPassword
= 0;
1981 work
= pl
->pUserName
- 1;
1982 pl
->pUserName
= pl
->pPassword
= 0;
1983 } else goto ErrorExit
;
1985 /* now start parsing hostname or hostnumber */
1987 pl
->pHostName
= work
;
1988 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
1993 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
1996 /* see if query string */
1997 pl
->pQuery
= strchrW(work
, '?');
1998 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
2000 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2001 pl
->pScheme
, pl
->szScheme
,
2002 pl
->pUserName
, pl
->szUserName
,
2003 pl
->pPassword
, pl
->szPassword
,
2004 pl
->pHostName
, pl
->szHostName
,
2005 pl
->pPort
, pl
->szPort
,
2006 pl
->pQuery
, pl
->szQuery
);
2009 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
2010 return E_INVALIDARG
;
2013 /*************************************************************************
2014 * UrlGetPartA [SHLWAPI.@]
2016 * Retrieve part of a Url.
2019 * pszIn [I] Url to parse
2020 * pszOut [O] Destination for part of pszIn requested
2021 * pcchOut [I] Size of pszOut
2022 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2023 * needed size of pszOut INCLUDING '\0'.
2024 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2025 * dwFlags [I] URL_ flags from "shlwapi.h"
2028 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2029 * Failure: An HRESULT error code describing the error.
2031 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
2032 DWORD dwPart
, DWORD dwFlags
)
2035 DWORD ret
, len
, len2
;
2037 in
= HeapAlloc(GetProcessHeap(), 0,
2038 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
2039 out
= in
+ INTERNET_MAX_URL_LENGTH
;
2041 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
2043 len
= INTERNET_MAX_URL_LENGTH
;
2044 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
2047 HeapFree(GetProcessHeap(), 0, in
);
2051 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
2052 if (len2
> *pcchOut
) {
2054 HeapFree(GetProcessHeap(), 0, in
);
2057 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
2059 HeapFree(GetProcessHeap(), 0, in
);
2063 /*************************************************************************
2064 * UrlGetPartW [SHLWAPI.@]
2068 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2069 DWORD dwPart
, DWORD dwFlags
)
2073 DWORD size
, schsize
;
2074 LPCWSTR addr
, schaddr
;
2076 TRACE("(%s %p %p(%d) %08x %08x)\n",
2077 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2079 ret
= URL_ParseUrl(pszIn
, &pl
);
2081 schaddr
= pl
.pScheme
;
2082 schsize
= pl
.szScheme
;
2085 case URL_PART_SCHEME
:
2086 if (!pl
.szScheme
) return E_INVALIDARG
;
2091 case URL_PART_HOSTNAME
:
2092 if (!pl
.szHostName
) return E_INVALIDARG
;
2093 addr
= pl
.pHostName
;
2094 size
= pl
.szHostName
;
2097 case URL_PART_USERNAME
:
2098 if (!pl
.szUserName
) return E_INVALIDARG
;
2099 addr
= pl
.pUserName
;
2100 size
= pl
.szUserName
;
2103 case URL_PART_PASSWORD
:
2104 if (!pl
.szPassword
) return E_INVALIDARG
;
2105 addr
= pl
.pPassword
;
2106 size
= pl
.szPassword
;
2110 if (!pl
.szPort
) return E_INVALIDARG
;
2115 case URL_PART_QUERY
:
2116 if (!pl
.szQuery
) return E_INVALIDARG
;
2122 return E_INVALIDARG
;
2125 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2126 if (*pcchOut
< schsize
+ size
+ 2) {
2127 *pcchOut
= schsize
+ size
+ 2;
2130 memcpy(pszOut
, schaddr
, schsize
*sizeof(WCHAR
));
2131 pszOut
[schsize
] = ':';
2132 memcpy(pszOut
+schsize
+1, addr
, size
*sizeof(WCHAR
));
2133 pszOut
[schsize
+1+size
] = 0;
2134 *pcchOut
= schsize
+ 1 + size
;
2137 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2138 memcpy(pszOut
, addr
, size
*sizeof(WCHAR
));
2142 TRACE("len=%d %s\n", *pcchOut
, debugstr_w(pszOut
));
2147 /*************************************************************************
2148 * PathIsURLA [SHLWAPI.@]
2150 * Check if the given path is a Url.
2153 * lpszPath [I] Path to check.
2156 * TRUE if lpszPath is a Url.
2157 * FALSE if lpszPath is NULL or not a Url.
2159 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2163 TRACE("%s\n", debugstr_a(lpstrPath
));
2165 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2168 base
.cbSize
= sizeof(base
);
2169 ParseURLA(lpstrPath
, &base
);
2170 return (base
.nScheme
!= URL_SCHEME_INVALID
);
2173 /*************************************************************************
2174 * PathIsURLW [SHLWAPI.@]
2178 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2182 TRACE("%s\n", debugstr_w(lpstrPath
));
2184 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2187 base
.cbSize
= sizeof(base
);
2188 ParseURLW(lpstrPath
, &base
);
2189 return (base
.nScheme
!= URL_SCHEME_INVALID
);
2192 /*************************************************************************
2193 * UrlCreateFromPathA [SHLWAPI.@]
2195 * See UrlCreateFromPathW
2197 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2199 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2201 UNICODE_STRING pathW
;
2203 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2205 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2206 return E_INVALIDARG
;
2207 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2208 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2209 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2211 if(ret
== S_OK
|| ret
== S_FALSE
) {
2212 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2213 if(*pcchUrl
> lenA
) {
2214 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2218 *pcchUrl
= lenA
+ 1;
2222 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2223 RtlFreeUnicodeString(&pathW
);
2227 /*************************************************************************
2228 * UrlCreateFromPathW [SHLWAPI.@]
2230 * Create a Url from a file path.
2233 * pszPath [I] Path to convert
2234 * pszUrl [O] Destination for the converted Url
2235 * pcchUrl [I/O] Length of pszUrl
2236 * dwReserved [I] Reserved, must be 0
2239 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2240 * Failure: An HRESULT error code.
2242 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2247 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
2248 WCHAR three_slashesW
[] = {'/','/','/',0};
2249 PARSEDURLW parsed_url
;
2251 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2253 /* Validate arguments */
2254 if (dwReserved
!= 0)
2255 return E_INVALIDARG
;
2256 if (!pszUrl
|| !pcchUrl
)
2257 return E_INVALIDARG
;
2260 parsed_url
.cbSize
= sizeof(parsed_url
);
2261 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
2262 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
2263 needed
= strlenW(pszPath
);
2264 if (needed
>= *pcchUrl
) {
2265 *pcchUrl
= needed
+ 1;
2269 strcpyW(pszUrl
, pszPath
);
2275 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
2276 strcpyW(pszNewUrl
, file_colonW
);
2277 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
2278 strcatW(pszNewUrl
, three_slashesW
);
2279 strcatW(pszNewUrl
, pszPath
);
2280 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
2282 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
2286 /*************************************************************************
2287 * SHAutoComplete [SHLWAPI.@]
2289 * Enable auto-completion for an edit control.
2292 * hwndEdit [I] Handle of control to enable auto-completion for
2293 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2296 * Success: S_OK. Auto-completion is enabled for the control.
2297 * Failure: An HRESULT error code indicating the error.
2299 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2301 FIXME("SHAutoComplete stub\n");
2305 /*************************************************************************
2306 * MLBuildResURLA [SHLWAPI.405]
2308 * Create a Url pointing to a resource in a module.
2311 * lpszLibName [I] Name of the module containing the resource
2312 * hMod [I] Callers module handle
2313 * dwFlags [I] Undocumented flags for loading the module
2314 * lpszRes [I] Resource name
2315 * lpszDest [O] Destination for resulting Url
2316 * dwDestLen [I] Length of lpszDest
2319 * Success: S_OK. lpszDest contains the resource Url.
2320 * Failure: E_INVALIDARG, if any argument is invalid, or
2321 * E_FAIL if dwDestLen is too small.
2323 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2324 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2326 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2330 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2333 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2335 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2336 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2338 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2339 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2340 if (SUCCEEDED(hRet
) && lpszDest
)
2341 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, 0, 0);
2346 /*************************************************************************
2347 * MLBuildResURLA [SHLWAPI.406]
2349 * See MLBuildResURLA.
2351 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2352 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2354 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2355 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2356 HRESULT hRet
= E_FAIL
;
2358 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2359 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2361 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2362 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2363 return E_INVALIDARG
;
2365 if (dwDestLen
>= szResLen
+ 1)
2367 dwDestLen
-= (szResLen
+ 1);
2368 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2370 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2374 WCHAR szBuff
[MAX_PATH
];
2377 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2378 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2380 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2382 if (dwDestLen
>= dwPathLen
)
2386 dwDestLen
-= dwPathLen
;
2387 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2389 dwResLen
= strlenW(lpszRes
) + 1;
2390 if (dwDestLen
>= dwResLen
+ 1)
2392 lpszDest
[szResLen
+ dwPathLen
+ dwResLen
] = '/';
2393 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2398 MLFreeLibrary(hMod
);