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
;
271 DWORD ret
, len
, len2
;
273 TRACE("(%s %p %p 0x%08x) using W version\n",
274 debugstr_a(pszUrl
), pszCanonicalized
,
275 pcchCanonicalized
, dwFlags
);
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 HeapFree(GetProcessHeap(), 0, base
);
293 len2
= WideCharToMultiByte(0, 0, canonical
, len
, 0, 0, 0, 0);
294 if (len2
> *pcchCanonicalized
) {
295 *pcchCanonicalized
= len
;
296 HeapFree(GetProcessHeap(), 0, base
);
299 WideCharToMultiByte(0, 0, canonical
, len
+1, pszCanonicalized
,
300 *pcchCanonicalized
, 0, 0);
301 *pcchCanonicalized
= len2
;
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',':'};
323 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl
), pszCanonicalized
,
324 pcchCanonicalized
, dwFlags
);
326 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
330 *pszCanonicalized
= 0;
334 nByteLen
= (lstrlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
335 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0,
336 INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
338 if((dwFlags
& URL_FILE_USE_PATHURL
) && nByteLen
>= sizeof(wszFile
)
339 && !memcmp(wszFile
, pszUrl
, sizeof(wszFile
)))
345 * 1 have 2[+] alnum 2,3
346 * 2 have scheme (found :) 4,6,3
347 * 3 failed (no location)
349 * 5 have 1[+] alnum 6,3
350 * 6 have location (found /) save root location
353 wk1
= (LPWSTR
)pszUrl
;
357 if(pszUrl
[1] == ':') { /* Assume path */
358 static const WCHAR wszFilePrefix
[] = {'f','i','l','e',':','/','/','/'};
360 memcpy(wk2
, wszFilePrefix
, sizeof(wszFilePrefix
));
361 wk2
+= sizeof(wszFilePrefix
)/sizeof(WCHAR
);
362 if (dwFlags
& URL_FILE_USE_PATHURL
)
368 dwFlags
|= URL_ESCAPE_UNSAFE
;
375 if (!isalnumW(*wk1
)) {state
= 3; break;}
377 if (!isalnumW(*wk1
)) {state
= 3; break;}
383 if (*wk1
++ == ':') state
= 2;
386 if (*wk1
!= '/') {state
= 3; break;}
388 if (*wk1
!= '/') {state
= 6; break;}
390 if(*wk1
== '/' && (dwFlags
& URL_FILE_USE_PATHURL
))
395 nWkLen
= strlenW(wk1
);
396 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
402 if(*mp
== '/' || *mp
== '\\')
408 if (!isalnumW(*wk1
) && (*wk1
!= '-') && (*wk1
!= '.') && (*wk1
!= ':'))
410 while(isalnumW(*wk1
) || (*wk1
== '-') || (*wk1
== '.') || (*wk1
== ':'))
417 if (*wk1
!= '/' && *wk1
!= '\\') {state
= 3; break;}
418 while(*wk1
== '/' || *wk1
== '\\') {
425 if(dwFlags
& URL_DONT_SIMPLIFY
) {
430 /* Now at root location, cannot back up any more. */
431 /* "root" will point at the '/' */
435 mp
= strchrW(wk1
, '/');
436 mp2
= strchrW(wk1
, '\\');
437 if(mp2
&& (!mp
|| mp2
< mp
))
440 nWkLen
= strlenW(wk1
);
441 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
448 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
456 TRACE("found '/.'\n");
457 if (wk1
[1] == '/' || wk1
[1] == '\\') {
458 /* case of /./ -> skip the ./ */
461 else if (wk1
[1] == '.') {
462 /* found /.. look for next / */
463 TRACE("found '/..'\n");
464 if (wk1
[2] == '/' || wk1
[2] == '\\' ||wk1
[2] == '?'
465 || wk1
[2] == '#' || !wk1
[2]) {
466 /* case /../ -> need to backup wk2 */
467 TRACE("found '/../'\n");
468 *(wk2
-1) = '\0'; /* set end of string */
469 mp
= strrchrW(root
, slash
);
470 if (mp
&& (mp
>= root
)) {
471 /* found valid backup point */
473 if(wk1
[2] != '/' && wk1
[2] != '\\')
479 /* did not find point, restore '/' */
489 FIXME("how did we get here - state=%d\n", state
);
490 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
494 TRACE("Simplified, orig <%s>, simple <%s>\n",
495 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
497 nLen
= lstrlenW(lpszUrlCpy
);
498 while ((nLen
> 0) && ((lpszUrlCpy
[nLen
-1] == '\r')||(lpszUrlCpy
[nLen
-1] == '\n')))
499 lpszUrlCpy
[--nLen
]=0;
501 if(dwFlags
& (URL_UNESCAPE
| URL_FILE_USE_PATHURL
))
502 UrlUnescapeW(lpszUrlCpy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
504 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
505 URL_ESCAPE_SPACES_ONLY
|
507 URL_DONT_ESCAPE_EXTRA_INFO
|
508 URL_ESCAPE_SEGMENT_ONLY
))) {
509 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
510 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
512 } else { /* No escaping needed, just copy the string */
513 nLen
= lstrlenW(lpszUrlCpy
);
514 if(nLen
< *pcchCanonicalized
)
515 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
520 *pcchCanonicalized
= nLen
;
523 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
526 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
531 /*************************************************************************
532 * UrlCombineA [SHLWAPI.@]
537 * pszBase [I] Base Url
538 * pszRelative [I] Url to combine with pszBase
539 * pszCombined [O] Destination for combined Url
540 * pcchCombined [O] Destination for length of pszCombined
541 * dwFlags [I] URL_ flags from "shlwapi.h"
544 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
545 * contains its length.
546 * Failure: An HRESULT error code indicating the error.
548 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
549 LPSTR pszCombined
, LPDWORD pcchCombined
,
552 LPWSTR base
, relative
, combined
;
553 DWORD ret
, len
, len2
;
555 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
556 debugstr_a(pszBase
),debugstr_a(pszRelative
),
557 pcchCombined
?*pcchCombined
:0,dwFlags
);
559 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
562 base
= HeapAlloc(GetProcessHeap(), 0,
563 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
564 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
565 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
567 MultiByteToWideChar(0, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
568 MultiByteToWideChar(0, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
571 ret
= UrlCombineW(base
, relative
, pszCombined
?combined
:NULL
, &len
, dwFlags
);
574 HeapFree(GetProcessHeap(), 0, base
);
578 len2
= WideCharToMultiByte(0, 0, combined
, len
, 0, 0, 0, 0);
579 if (len2
> *pcchCombined
) {
580 *pcchCombined
= len2
;
581 HeapFree(GetProcessHeap(), 0, base
);
584 WideCharToMultiByte(0, 0, combined
, len
+1, pszCombined
, (*pcchCombined
)+1,
586 *pcchCombined
= len2
;
587 HeapFree(GetProcessHeap(), 0, base
);
591 /*************************************************************************
592 * UrlCombineW [SHLWAPI.@]
596 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
597 LPWSTR pszCombined
, LPDWORD pcchCombined
,
600 PARSEDURLW base
, relative
;
601 DWORD myflags
, sizeloc
= 0;
602 DWORD len
, res1
, res2
, process_case
= 0;
603 LPWSTR work
, preliminary
, mbase
, mrelative
;
604 static const WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
605 static const WCHAR single_slash
[] = {'/','\0'};
608 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
609 debugstr_w(pszBase
),debugstr_w(pszRelative
),
610 pcchCombined
?*pcchCombined
:0,dwFlags
);
612 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
615 base
.cbSize
= sizeof(base
);
616 relative
.cbSize
= sizeof(relative
);
618 /* Get space for duplicates of the input and the output */
619 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
621 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
622 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
625 /* Canonicalize the base input prior to looking for the scheme */
626 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
627 len
= INTERNET_MAX_URL_LENGTH
;
628 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
630 /* Canonicalize the relative input prior to looking for the scheme */
631 len
= INTERNET_MAX_URL_LENGTH
;
632 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
634 /* See if the base has a scheme */
635 res1
= ParseURLW(mbase
, &base
);
637 /* if pszBase has no scheme, then return pszRelative */
638 TRACE("no scheme detected in Base\n");
642 /* mk is a special case */
643 if(base
.nScheme
== URL_SCHEME_MK
) {
644 static const WCHAR wsz
[] = {':',':',0};
646 WCHAR
*ptr
= strstrW(base
.pszSuffix
, wsz
);
651 delta
= ptr
-base
.pszSuffix
;
652 base
.cchProtocol
+= delta
;
653 base
.pszSuffix
+= delta
;
654 base
.cchSuffix
-= delta
;
658 /* get size of location field (if it exists) */
659 work
= (LPWSTR
)base
.pszSuffix
;
661 if (*work
++ == '/') {
662 if (*work
++ == '/') {
663 /* At this point have start of location and
664 * it ends at next '/' or end of string.
666 while(*work
&& (*work
!= '/')) work
++;
667 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
671 /* Change .sizep2 to not have the last leaf in it,
672 * Note: we need to start after the location (if it exists)
674 work
= strrchrW((base
.pszSuffix
+sizeloc
), '/');
676 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
677 base
.cchSuffix
= len
;
682 * .pszSuffix points to location (starting with '//')
683 * .cchSuffix length of location (above) and rest less the last
685 * sizeloc length of location (above) up to but not including
689 res2
= ParseURLW(mrelative
, &relative
);
691 /* no scheme in pszRelative */
692 TRACE("no scheme detected in Relative\n");
693 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
694 relative
.cchSuffix
= strlenW(mrelative
);
695 if (*pszRelative
== ':') {
696 /* case that is either left alone or uses pszBase */
697 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
704 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == ':')) {
705 /* case that becomes "file:///" */
706 strcpyW(preliminary
, myfilestr
);
710 if ((*mrelative
== '/') && (*(mrelative
+1) == '/')) {
711 /* pszRelative has location and rest */
715 if (*mrelative
== '/') {
716 /* case where pszRelative is root to location */
720 process_case
= (*base
.pszSuffix
== '/') ? 5 : 3;
724 /* handle cases where pszRelative has scheme */
725 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
726 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
728 /* since the schemes are the same */
729 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
730 /* case where pszRelative replaces location and following */
734 if (*relative
.pszSuffix
== '/') {
735 /* case where pszRelative is root to location */
739 /* case where scheme is followed by document path */
743 if ((*relative
.pszSuffix
== '/') && (*(relative
.pszSuffix
+1) == '/')) {
744 /* case where pszRelative replaces scheme, location,
745 * and following and handles PLUGGABLE
752 } while(FALSE
); /* a litte trick to allow easy exit from nested if's */
756 switch (process_case
) {
759 * Return pszRelative appended to what ever is in pszCombined,
760 * (which may the string "file:///"
762 strcatW(preliminary
, mrelative
);
766 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
767 * and pszRelative starts with "//", then append a "/"
769 strcpyW(preliminary
, mrelative
);
770 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
771 URL_JustLocation(relative
.pszSuffix
))
772 strcatW(preliminary
, single_slash
);
776 * Return the pszBase scheme with pszRelative. Basically
777 * keeps the scheme and replaces the domain and following.
779 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
780 work
= preliminary
+ base
.cchProtocol
+ 1;
781 strcpyW(work
, relative
.pszSuffix
);
782 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
783 URL_JustLocation(relative
.pszSuffix
))
784 strcatW(work
, single_slash
);
788 * Return the pszBase scheme and location but everything
789 * after the location is pszRelative. (Replace document
792 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
793 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
794 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
796 strcpyW(work
, relative
.pszSuffix
);
800 * Return the pszBase without its document (if any) and
801 * append pszRelative after its scheme.
803 memcpy(preliminary
, base
.pszProtocol
,
804 (base
.cchProtocol
+1+base
.cchSuffix
)*sizeof(WCHAR
));
805 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
808 strcpyW(work
, relative
.pszSuffix
);
812 FIXME("How did we get here????? process_case=%d\n", process_case
);
817 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
818 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, (dwFlags
& ~URL_FILE_USE_PATHURL
));
819 if(SUCCEEDED(ret
) && pszCombined
) {
820 lstrcpyW(pszCombined
, mrelative
);
822 TRACE("return-%d len=%d, %s\n",
823 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
825 HeapFree(GetProcessHeap(), 0, preliminary
);
829 /*************************************************************************
830 * UrlEscapeA [SHLWAPI.@]
833 HRESULT WINAPI
UrlEscapeA(
839 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
840 WCHAR
*escapedW
= bufW
;
843 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
845 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
847 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
848 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
849 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
852 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
853 if(pszEscaped
&& *pcchEscaped
> lenA
) {
854 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
855 pszEscaped
[lenA
] = 0;
858 *pcchEscaped
= lenA
+ 1;
862 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
863 RtlFreeUnicodeString(&urlW
);
867 #define WINE_URL_BASH_AS_SLASH 0x01
868 #define WINE_URL_COLLAPSE_SLASHES 0x02
869 #define WINE_URL_ESCAPE_SLASH 0x04
870 #define WINE_URL_ESCAPE_HASH 0x08
871 #define WINE_URL_ESCAPE_QUESTION 0x10
872 #define WINE_URL_STOP_ON_HASH 0x20
873 #define WINE_URL_STOP_ON_QUESTION 0x40
875 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
, DWORD int_flags
)
881 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
888 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
891 if (ch
<= 31 || ch
>= 127)
912 if (int_flags
& WINE_URL_ESCAPE_SLASH
) return TRUE
;
916 if (int_flags
& WINE_URL_ESCAPE_QUESTION
) return TRUE
;
920 if (int_flags
& WINE_URL_ESCAPE_HASH
) return TRUE
;
930 /*************************************************************************
931 * UrlEscapeW [SHLWAPI.@]
933 * Converts unsafe characters in a Url into escape sequences.
936 * pszUrl [I] Url to modify
937 * pszEscaped [O] Destination for modified Url
938 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
939 * dwFlags [I] URL_ flags from "shlwapi.h"
942 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
943 * contains its length.
944 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
945 * pcchEscaped is set to the required length.
947 * Converts unsafe characters into their escape sequences.
950 * - By default this function stops converting at the first '?' or
952 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
953 * converted, but the conversion continues past a '?' or '#'.
954 * - Note that this function did not work well (or at all) in shlwapi version 4.
957 * Only the following flags are implemented:
958 *| URL_ESCAPE_SPACES_ONLY
959 *| URL_DONT_ESCAPE_EXTRA_INFO
960 *| URL_ESCAPE_SEGMENT_ONLY
961 *| URL_ESCAPE_PERCENT
963 HRESULT WINAPI
UrlEscapeW(
970 DWORD needed
= 0, ret
;
971 BOOL stop_escaping
= FALSE
;
972 WCHAR next
[5], *dst
= pszEscaped
;
974 PARSEDURLW parsed_url
;
977 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
979 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl
), pszEscaped
,
980 pcchEscaped
, dwFlags
);
982 if(!pszUrl
|| !pcchEscaped
)
985 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
986 URL_ESCAPE_SEGMENT_ONLY
|
987 URL_DONT_ESCAPE_EXTRA_INFO
|
989 FIXME("Unimplemented flags: %08x\n", dwFlags
);
992 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
993 /* if SPACES_ONLY specified, reset the other controls */
994 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
996 URL_ESCAPE_SEGMENT_ONLY
);
999 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1000 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
1004 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
1005 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
1007 parsed_url
.cbSize
= sizeof(parsed_url
);
1008 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
1009 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
1011 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
1013 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
1014 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
1016 switch(parsed_url
.nScheme
) {
1017 case URL_SCHEME_FILE
:
1018 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
1019 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
1022 case URL_SCHEME_HTTP
:
1023 case URL_SCHEME_HTTPS
:
1024 int_flags
|= WINE_URL_BASH_AS_SLASH
;
1025 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
1026 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1029 case URL_SCHEME_MAILTO
:
1030 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
1031 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
1034 case URL_SCHEME_INVALID
:
1037 case URL_SCHEME_FTP
:
1039 if(parsed_url
.pszSuffix
[0] != '/')
1040 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1045 for(src
= pszUrl
; *src
; ) {
1049 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
1050 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
1051 while(cur
== '/' || cur
== '\\') {
1055 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
1056 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
1057 src
+= localhost_len
+ 1;
1064 next
[0] = next
[1] = next
[2] = '/';
1071 next
[0] = next
[1] = '/';
1078 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1079 stop_escaping
= TRUE
;
1081 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1082 stop_escaping
= TRUE
;
1084 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1086 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1088 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1089 next
[2] = hexDigits
[cur
& 0xf];
1098 if(needed
+ len
<= *pcchEscaped
) {
1099 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1105 if(needed
< *pcchEscaped
) {
1109 needed
++; /* add one for the '\0' */
1112 *pcchEscaped
= needed
;
1117 /*************************************************************************
1118 * UrlUnescapeA [SHLWAPI.@]
1120 * Converts Url escape sequences back to ordinary characters.
1123 * pszUrl [I/O] Url to convert
1124 * pszUnescaped [O] Destination for converted Url
1125 * pcchUnescaped [I/O] Size of output string
1126 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1129 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1130 * dwFlags includes URL_ESCAPE_INPLACE.
1131 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1132 * this case pcchUnescaped is set to the size required.
1134 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1135 * the first occurrence of either a '?' or '#' character.
1137 HRESULT WINAPI
UrlUnescapeA(
1140 LPDWORD pcchUnescaped
,
1147 BOOL stop_unescaping
= FALSE
;
1149 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl
), pszUnescaped
,
1150 pcchUnescaped
, dwFlags
);
1152 if(!pszUrl
|| (!pszUnescaped
&& !(dwFlags
& URL_UNESCAPE_INPLACE
)) || !pcchUnescaped
)
1153 return E_INVALIDARG
;
1155 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1160 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1161 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1162 (*src
== '#' || *src
== '?')) {
1163 stop_unescaping
= TRUE
;
1165 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1166 && stop_unescaping
== FALSE
) {
1169 memcpy(buf
, src
+ 1, 2);
1171 ih
= strtol(buf
, NULL
, 16);
1173 src
+= 2; /* Advance to end of escape */
1177 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1181 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1185 needed
++; /* add one for the '\0' */
1188 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1189 *pcchUnescaped
= needed
;
1192 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1193 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1199 /*************************************************************************
1200 * UrlUnescapeW [SHLWAPI.@]
1204 HRESULT WINAPI
UrlUnescapeW(
1206 LPWSTR pszUnescaped
,
1207 LPDWORD pcchUnescaped
,
1214 BOOL stop_unescaping
= FALSE
;
1216 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl
), pszUnescaped
,
1217 pcchUnescaped
, dwFlags
);
1219 if(!pszUrl
|| (!pszUnescaped
&& !(dwFlags
& URL_UNESCAPE_INPLACE
))|| !pcchUnescaped
)
1220 return E_INVALIDARG
;
1222 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1227 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1228 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1229 (*src
== '#' || *src
== '?')) {
1230 stop_unescaping
= TRUE
;
1232 } else if(*src
== '%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1233 && stop_unescaping
== FALSE
) {
1235 WCHAR buf
[5] = {'0','x',0};
1236 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1238 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1240 src
+= 2; /* Advance to end of escape */
1244 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1248 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1252 needed
++; /* add one for the '\0' */
1255 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1256 *pcchUnescaped
= needed
;
1259 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1260 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1266 /*************************************************************************
1267 * UrlGetLocationA [SHLWAPI.@]
1269 * Get the location from a Url.
1272 * pszUrl [I] Url to get the location from
1275 * A pointer to the start of the location in pszUrl, or NULL if there is
1279 * - MSDN erroneously states that "The location is the segment of the Url
1280 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1281 * stop at '?' and always return a NULL in this case.
1282 * - MSDN also erroneously states that "If a file URL has a query string,
1283 * the returned string is the query string". In all tested cases, if the
1284 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1287 *| NULL file://aa/b/cd#hohoh
1288 *| #hohoh http://aa/b/cd#hohoh
1289 *| NULL fi://aa/b/cd#hohoh
1290 *| #hohoh ff://aa/b/cd#hohoh
1292 LPCSTR WINAPI
UrlGetLocationA(
1298 base
.cbSize
= sizeof(base
);
1299 res1
= ParseURLA(pszUrl
, &base
);
1300 if (res1
) return NULL
; /* invalid scheme */
1302 /* if scheme is file: then never return pointer */
1303 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1305 /* Look for '#' and return its addr */
1306 return strchr(base
.pszSuffix
, '#');
1309 /*************************************************************************
1310 * UrlGetLocationW [SHLWAPI.@]
1312 * See UrlGetLocationA.
1314 LPCWSTR WINAPI
UrlGetLocationW(
1320 base
.cbSize
= sizeof(base
);
1321 res1
= ParseURLW(pszUrl
, &base
);
1322 if (res1
) return NULL
; /* invalid scheme */
1324 /* if scheme is file: then never return pointer */
1325 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1327 /* Look for '#' and return its addr */
1328 return strchrW(base
.pszSuffix
, '#');
1331 /*************************************************************************
1332 * UrlCompareA [SHLWAPI.@]
1337 * pszUrl1 [I] First Url to compare
1338 * pszUrl2 [I] Url to compare to pszUrl1
1339 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1342 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1343 * than, equal to, or less than pszUrl1 respectively.
1345 INT WINAPI
UrlCompareA(
1350 INT ret
, len
, len1
, len2
;
1353 return strcmp(pszUrl1
, pszUrl2
);
1354 len1
= strlen(pszUrl1
);
1355 if (pszUrl1
[len1
-1] == '/') len1
--;
1356 len2
= strlen(pszUrl2
);
1357 if (pszUrl2
[len2
-1] == '/') len2
--;
1359 return strncmp(pszUrl1
, pszUrl2
, len1
);
1360 len
= min(len1
, len2
);
1361 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1362 if (ret
) return ret
;
1363 if (len1
> len2
) return 1;
1367 /*************************************************************************
1368 * UrlCompareW [SHLWAPI.@]
1372 INT WINAPI
UrlCompareW(
1378 size_t len
, len1
, len2
;
1381 return strcmpW(pszUrl1
, pszUrl2
);
1382 len1
= strlenW(pszUrl1
);
1383 if (pszUrl1
[len1
-1] == '/') len1
--;
1384 len2
= strlenW(pszUrl2
);
1385 if (pszUrl2
[len2
-1] == '/') len2
--;
1387 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1388 len
= min(len1
, len2
);
1389 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1390 if (ret
) return ret
;
1391 if (len1
> len2
) return 1;
1395 /*************************************************************************
1396 * HashData [SHLWAPI.@]
1398 * Hash an input block into a variable sized digest.
1401 * lpSrc [I] Input block
1402 * nSrcLen [I] Length of lpSrc
1403 * lpDest [I] Output for hash digest
1404 * nDestLen [I] Length of lpDest
1407 * Success: TRUE. lpDest is filled with the computed hash value.
1408 * Failure: FALSE, if any argument is invalid.
1410 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1411 unsigned char *lpDest
, DWORD nDestLen
)
1413 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1415 if (IsBadReadPtr(lpSrc
, nSrcLen
) ||
1416 IsBadWritePtr(lpDest
, nDestLen
))
1417 return E_INVALIDARG
;
1419 while (destCount
>= 0)
1421 lpDest
[destCount
] = (destCount
& 0xff);
1425 while (srcCount
>= 0)
1427 destCount
= nDestLen
- 1;
1428 while (destCount
>= 0)
1430 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1438 /*************************************************************************
1439 * UrlHashA [SHLWAPI.@]
1441 * Produce a Hash from a Url.
1444 * pszUrl [I] Url to hash
1445 * lpDest [O] Destinationh for hash
1446 * nDestLen [I] Length of lpDest
1449 * Success: S_OK. lpDest is filled with the computed hash value.
1450 * Failure: E_INVALIDARG, if any argument is invalid.
1452 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1454 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1455 return E_INVALIDARG
;
1457 HashData((const BYTE
*)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1461 /*************************************************************************
1462 * UrlHashW [SHLWAPI.@]
1466 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1468 char szUrl
[MAX_PATH
];
1470 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1472 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1473 return E_INVALIDARG
;
1475 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1476 * return the same digests for the same URL.
1478 WideCharToMultiByte(0, 0, pszUrl
, -1, szUrl
, MAX_PATH
, 0, 0);
1479 HashData((const BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1483 /*************************************************************************
1484 * UrlApplySchemeA [SHLWAPI.@]
1486 * Apply a scheme to a Url.
1489 * pszIn [I] Url to apply scheme to
1490 * pszOut [O] Destination for modified Url
1491 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1492 * dwFlags [I] URL_ flags from "shlwapi.h"
1495 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1496 * Failure: An HRESULT error code describing the error.
1498 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1501 DWORD ret
, len
, len2
;
1503 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1504 debugstr_a(pszIn
), *pcchOut
, dwFlags
);
1506 in
= HeapAlloc(GetProcessHeap(), 0,
1507 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1508 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1510 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1511 len
= INTERNET_MAX_URL_LENGTH
;
1513 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1514 if ((ret
!= S_OK
) && (ret
!= S_FALSE
)) {
1515 HeapFree(GetProcessHeap(), 0, in
);
1519 len2
= WideCharToMultiByte(0, 0, out
, len
+1, 0, 0, 0, 0);
1520 if (len2
> *pcchOut
) {
1522 HeapFree(GetProcessHeap(), 0, in
);
1525 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1527 HeapFree(GetProcessHeap(), 0, in
);
1531 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1536 DWORD value_len
, data_len
, dwType
, i
;
1537 WCHAR reg_path
[MAX_PATH
];
1538 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1541 MultiByteToWideChar(0, 0,
1542 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1543 -1, reg_path
, MAX_PATH
);
1544 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1546 while(value_len
= data_len
= MAX_PATH
,
1547 RegEnumValueW(newkey
, index
, value
, &value_len
,
1548 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1549 TRACE("guess %d %s is %s\n",
1550 index
, debugstr_w(value
), debugstr_w(data
));
1553 for(i
=0; i
<value_len
; i
++) {
1556 /* remember that TRUE is not-equal */
1557 j
= ChrCmpIW(Wxx
, Wyy
);
1560 if ((i
== value_len
) && !j
) {
1561 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1562 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1563 RegCloseKey(newkey
);
1566 strcpyW(pszOut
, data
);
1567 strcatW(pszOut
, pszIn
);
1568 *pcchOut
= strlenW(pszOut
);
1569 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1570 RegCloseKey(newkey
);
1575 RegCloseKey(newkey
);
1579 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1582 DWORD data_len
, dwType
;
1583 WCHAR reg_path
[MAX_PATH
];
1584 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1586 /* get and prepend default */
1587 MultiByteToWideChar(0, 0,
1588 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1589 -1, reg_path
, MAX_PATH
);
1590 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1591 data_len
= MAX_PATH
;
1594 RegQueryValueExW(newkey
, value
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1595 RegCloseKey(newkey
);
1596 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1597 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1600 strcpyW(pszOut
, data
);
1601 strcatW(pszOut
, pszIn
);
1602 *pcchOut
= strlenW(pszOut
);
1603 TRACE("used default %s\n", debugstr_w(pszOut
));
1607 /*************************************************************************
1608 * UrlApplySchemeW [SHLWAPI.@]
1610 * See UrlApplySchemeA.
1612 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1614 PARSEDURLW in_scheme
;
1618 TRACE("(in %s, out size %d, flags %08x)\n",
1619 debugstr_w(pszIn
), *pcchOut
, dwFlags
);
1621 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1622 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1623 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwFlags
);
1624 strcpyW(pszOut
, pszIn
);
1625 *pcchOut
= strlenW(pszOut
);
1629 in_scheme
.cbSize
= sizeof(in_scheme
);
1630 /* See if the base has a scheme */
1631 res1
= ParseURLW(pszIn
, &in_scheme
);
1633 /* no scheme in input, need to see if we need to guess */
1634 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1635 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != -1)
1640 /* we have a scheme, see if valid (known scheme) */
1641 if (in_scheme
.nScheme
) {
1642 /* have valid scheme, so just copy and exit */
1643 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1644 *pcchOut
= strlenW(pszIn
) + 1;
1647 strcpyW(pszOut
, pszIn
);
1648 *pcchOut
= strlenW(pszOut
);
1649 TRACE("valid scheme, returning copy\n");
1654 /* If we are here, then either invalid scheme,
1655 * or no scheme and can't/failed guess.
1657 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1659 (dwFlags
& URL_APPLY_DEFAULT
)) {
1660 /* find and apply default scheme */
1661 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1664 /* just copy and give proper return code */
1665 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1666 *pcchOut
= strlenW(pszIn
) + 1;
1669 strcpyW(pszOut
, pszIn
);
1670 *pcchOut
= strlenW(pszOut
);
1671 TRACE("returning copy, left alone\n");
1675 /*************************************************************************
1676 * UrlIsA [SHLWAPI.@]
1678 * Determine if a Url is of a certain class.
1681 * pszUrl [I] Url to check
1682 * Urlis [I] URLIS_ constant from "shlwapi.h"
1685 * TRUE if pszUrl belongs to the class type in Urlis.
1688 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1694 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1699 base
.cbSize
= sizeof(base
);
1700 res1
= ParseURLA(pszUrl
, &base
);
1701 if (res1
) return FALSE
; /* invalid scheme */
1702 switch (base
.nScheme
)
1704 case URL_SCHEME_MAILTO
:
1705 case URL_SCHEME_SHELL
:
1706 case URL_SCHEME_JAVASCRIPT
:
1707 case URL_SCHEME_VBSCRIPT
:
1708 case URL_SCHEME_ABOUT
:
1714 return !StrCmpNA("file:", pszUrl
, 5);
1716 case URLIS_DIRECTORY
:
1717 last
= pszUrl
+ strlen(pszUrl
) - 1;
1718 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1721 return PathIsURLA(pszUrl
);
1723 case URLIS_NOHISTORY
:
1724 case URLIS_APPLIABLE
:
1725 case URLIS_HASQUERY
:
1727 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1732 /*************************************************************************
1733 * UrlIsW [SHLWAPI.@]
1737 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1739 static const WCHAR stemp
[] = { 'f','i','l','e',':',0 };
1744 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1749 base
.cbSize
= sizeof(base
);
1750 res1
= ParseURLW(pszUrl
, &base
);
1751 if (res1
) return FALSE
; /* invalid scheme */
1752 switch (base
.nScheme
)
1754 case URL_SCHEME_MAILTO
:
1755 case URL_SCHEME_SHELL
:
1756 case URL_SCHEME_JAVASCRIPT
:
1757 case URL_SCHEME_VBSCRIPT
:
1758 case URL_SCHEME_ABOUT
:
1764 return !strncmpW(stemp
, pszUrl
, 5);
1766 case URLIS_DIRECTORY
:
1767 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1768 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1771 return PathIsURLW(pszUrl
);
1773 case URLIS_NOHISTORY
:
1774 case URLIS_APPLIABLE
:
1775 case URLIS_HASQUERY
:
1777 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1782 /*************************************************************************
1783 * UrlIsNoHistoryA [SHLWAPI.@]
1785 * Determine if a Url should not be stored in the users history list.
1788 * pszUrl [I] Url to check
1791 * TRUE, if pszUrl should be excluded from the history list,
1794 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1796 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1799 /*************************************************************************
1800 * UrlIsNoHistoryW [SHLWAPI.@]
1802 * See UrlIsNoHistoryA.
1804 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1806 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1809 /*************************************************************************
1810 * UrlIsOpaqueA [SHLWAPI.@]
1812 * Determine if a Url is opaque.
1815 * pszUrl [I] Url to check
1818 * TRUE if pszUrl is opaque,
1822 * An opaque Url is one that does not start with "<protocol>://".
1824 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1826 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1829 /*************************************************************************
1830 * UrlIsOpaqueW [SHLWAPI.@]
1834 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1836 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1839 /*************************************************************************
1840 * Scans for characters of type "type" and when not matching found,
1841 * returns pointer to it and length in size.
1843 * Characters tested based on RFC 1738
1845 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1847 static DWORD alwayszero
= 0;
1856 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1871 if ( isalphaW(*start
) ||
1873 /* user/password only characters */
1878 /* *extra* characters */
1885 /* *safe* characters */
1893 } else if (*start
== '%') {
1894 if (isxdigitW(*(start
+1)) &&
1895 isxdigitW(*(start
+2))) {
1907 if (isdigitW(*start
)) {
1918 if (isalnumW(*start
) ||
1929 FIXME("unknown type %d\n", type
);
1930 return (LPWSTR
)&alwayszero
;
1932 /* TRACE("scanned %d characters next char %p<%c>\n",
1933 *size, start, *start); */
1937 /*************************************************************************
1938 * Attempt to parse URL into pieces.
1940 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
1944 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
1945 pl
->pScheme
= pszUrl
;
1946 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
1947 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
1949 if ((*work
!= '/') || (*(work
+1) != '/')) goto ErrorExit
;
1950 pl
->pUserName
= work
+ 2;
1951 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
1952 if (*work
== ':' ) {
1953 /* parse password */
1955 pl
->pPassword
= work
;
1956 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
1958 /* what we just parsed must be the hostname and port
1959 * so reset pointers and clear then let it parse */
1960 pl
->szUserName
= pl
->szPassword
= 0;
1961 work
= pl
->pUserName
- 1;
1962 pl
->pUserName
= pl
->pPassword
= 0;
1964 } else if (*work
== '@') {
1968 } else if (!*work
|| (*work
== '/') || (*work
== '.')) {
1969 /* what was parsed was hostname, so reset pointers and let it parse */
1970 pl
->szUserName
= pl
->szPassword
= 0;
1971 work
= pl
->pUserName
- 1;
1972 pl
->pUserName
= pl
->pPassword
= 0;
1973 } else goto ErrorExit
;
1975 /* now start parsing hostname or hostnumber */
1977 pl
->pHostName
= work
;
1978 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
1983 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
1986 /* see if query string */
1987 pl
->pQuery
= strchrW(work
, '?');
1988 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
1990 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1991 pl
->pScheme
, pl
->szScheme
,
1992 pl
->pUserName
, pl
->szUserName
,
1993 pl
->pPassword
, pl
->szPassword
,
1994 pl
->pHostName
, pl
->szHostName
,
1995 pl
->pPort
, pl
->szPort
,
1996 pl
->pQuery
, pl
->szQuery
);
1999 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
2000 return E_INVALIDARG
;
2003 /*************************************************************************
2004 * UrlGetPartA [SHLWAPI.@]
2006 * Retrieve part of a Url.
2009 * pszIn [I] Url to parse
2010 * pszOut [O] Destination for part of pszIn requested
2011 * pcchOut [I] Size of pszOut
2012 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
2013 * needed size of pszOut INCLUDING '\0'.
2014 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2015 * dwFlags [I] URL_ flags from "shlwapi.h"
2018 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2019 * Failure: An HRESULT error code describing the error.
2021 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
2022 DWORD dwPart
, DWORD dwFlags
)
2025 DWORD ret
, len
, len2
;
2027 in
= HeapAlloc(GetProcessHeap(), 0,
2028 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
2029 out
= in
+ INTERNET_MAX_URL_LENGTH
;
2031 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
2033 len
= INTERNET_MAX_URL_LENGTH
;
2034 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
2037 HeapFree(GetProcessHeap(), 0, in
);
2041 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
2042 if (len2
> *pcchOut
) {
2044 HeapFree(GetProcessHeap(), 0, in
);
2047 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
2049 HeapFree(GetProcessHeap(), 0, in
);
2053 /*************************************************************************
2054 * UrlGetPartW [SHLWAPI.@]
2058 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2059 DWORD dwPart
, DWORD dwFlags
)
2063 DWORD size
, schsize
;
2064 LPCWSTR addr
, schaddr
;
2066 TRACE("(%s %p %p(%d) %08x %08x)\n",
2067 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2069 ret
= URL_ParseUrl(pszIn
, &pl
);
2071 schaddr
= pl
.pScheme
;
2072 schsize
= pl
.szScheme
;
2075 case URL_PART_SCHEME
:
2076 if (!pl
.szScheme
) return E_INVALIDARG
;
2081 case URL_PART_HOSTNAME
:
2082 if (!pl
.szHostName
) return E_INVALIDARG
;
2083 addr
= pl
.pHostName
;
2084 size
= pl
.szHostName
;
2087 case URL_PART_USERNAME
:
2088 if (!pl
.szUserName
) return E_INVALIDARG
;
2089 addr
= pl
.pUserName
;
2090 size
= pl
.szUserName
;
2093 case URL_PART_PASSWORD
:
2094 if (!pl
.szPassword
) return E_INVALIDARG
;
2095 addr
= pl
.pPassword
;
2096 size
= pl
.szPassword
;
2100 if (!pl
.szPort
) return E_INVALIDARG
;
2105 case URL_PART_QUERY
:
2106 if (!pl
.szQuery
) return E_INVALIDARG
;
2112 return E_INVALIDARG
;
2115 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2116 if (*pcchOut
< schsize
+ size
+ 2) {
2117 *pcchOut
= schsize
+ size
+ 2;
2120 memcpy(pszOut
, schaddr
, schsize
*sizeof(WCHAR
));
2121 pszOut
[schsize
] = ':';
2122 memcpy(pszOut
+schsize
+1, addr
, size
*sizeof(WCHAR
));
2123 pszOut
[schsize
+1+size
] = 0;
2124 *pcchOut
= schsize
+ 1 + size
;
2127 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2128 memcpy(pszOut
, addr
, size
*sizeof(WCHAR
));
2132 TRACE("len=%d %s\n", *pcchOut
, debugstr_w(pszOut
));
2137 /*************************************************************************
2138 * PathIsURLA [SHLWAPI.@]
2140 * Check if the given path is a Url.
2143 * lpszPath [I] Path to check.
2146 * TRUE if lpszPath is a Url.
2147 * FALSE if lpszPath is NULL or not a Url.
2149 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2154 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2157 base
.cbSize
= sizeof(base
);
2158 res1
= ParseURLA(lpstrPath
, &base
);
2159 return (base
.nScheme
!= URL_SCHEME_INVALID
);
2162 /*************************************************************************
2163 * PathIsURLW [SHLWAPI.@]
2167 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2172 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2175 base
.cbSize
= sizeof(base
);
2176 res1
= ParseURLW(lpstrPath
, &base
);
2177 return (base
.nScheme
!= URL_SCHEME_INVALID
);
2180 /*************************************************************************
2181 * UrlCreateFromPathA [SHLWAPI.@]
2183 * See UrlCreateFromPathW
2185 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2187 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2189 UNICODE_STRING pathW
;
2191 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2193 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2194 return E_INVALIDARG
;
2195 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2196 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2197 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2199 if(ret
== S_OK
|| ret
== S_FALSE
) {
2200 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2201 if(*pcchUrl
> lenA
) {
2202 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2206 *pcchUrl
= lenA
+ 1;
2210 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2211 RtlFreeUnicodeString(&pathW
);
2215 /*************************************************************************
2216 * UrlCreateFromPathW [SHLWAPI.@]
2218 * Create a Url from a file path.
2221 * pszPath [I] Path to convert
2222 * pszUrl [O] Destination for the converted Url
2223 * pcchUrl [I/O] Length of pszUrl
2224 * dwReserved [I] Reserved, must be 0
2227 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2228 * Failure: An HRESULT error code.
2230 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2235 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
2236 WCHAR three_slashesW
[] = {'/','/','/',0};
2237 PARSEDURLW parsed_url
;
2239 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2241 /* Validate arguments */
2242 if (dwReserved
!= 0)
2243 return E_INVALIDARG
;
2244 if (!pszUrl
|| !pcchUrl
)
2245 return E_INVALIDARG
;
2248 parsed_url
.cbSize
= sizeof(parsed_url
);
2249 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
2250 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
2251 needed
= strlenW(pszPath
);
2252 if (needed
>= *pcchUrl
) {
2253 *pcchUrl
= needed
+ 1;
2257 strcpyW(pszUrl
, pszPath
);
2263 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
2264 strcpyW(pszNewUrl
, file_colonW
);
2265 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
2266 strcatW(pszNewUrl
, three_slashesW
);
2267 strcatW(pszNewUrl
, pszPath
);
2268 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
2270 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
2274 /*************************************************************************
2275 * SHAutoComplete [SHLWAPI.@]
2277 * Enable auto-completion for an edit control.
2280 * hwndEdit [I] Handle of control to enable auto-completion for
2281 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2284 * Success: S_OK. Auto-completion is enabled for the control.
2285 * Failure: An HRESULT error code indicating the error.
2287 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2289 FIXME("SHAutoComplete stub\n");
2293 /*************************************************************************
2294 * MLBuildResURLA [SHLWAPI.405]
2296 * Create a Url pointing to a resource in a module.
2299 * lpszLibName [I] Name of the module containing the resource
2300 * hMod [I] Callers module handle
2301 * dwFlags [I] Undocumented flags for loading the module
2302 * lpszRes [I] Resource name
2303 * lpszDest [O] Destination for resulting Url
2304 * dwDestLen [I] Length of lpszDest
2307 * Success: S_OK. lpszDest constains the resource Url.
2308 * Failure: E_INVALIDARG, if any argument is invalid, or
2309 * E_FAIL if dwDestLen is too small.
2311 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2312 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2314 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2318 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2321 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2323 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2324 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2326 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2327 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2328 if (SUCCEEDED(hRet
) && lpszDest
)
2329 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, 0, 0);
2334 /*************************************************************************
2335 * MLBuildResURLA [SHLWAPI.406]
2337 * See MLBuildResURLA.
2339 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2340 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2342 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2343 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2344 HRESULT hRet
= E_FAIL
;
2346 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2347 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2349 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2350 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2351 return E_INVALIDARG
;
2353 if (dwDestLen
>= szResLen
+ 1)
2355 dwDestLen
-= (szResLen
+ 1);
2356 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2358 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2362 WCHAR szBuff
[MAX_PATH
];
2365 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2366 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2368 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2370 if (dwDestLen
>= dwPathLen
)
2374 dwDestLen
-= dwPathLen
;
2375 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2377 dwResLen
= strlenW(lpszRes
) + 1;
2378 if (dwDestLen
>= dwResLen
+ 1)
2380 lpszDest
[szResLen
+ dwPathLen
+ dwResLen
] = '/';
2381 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2386 MLFreeLibrary(hMod
);