ucrtbase: Add __stdio_common_vfprintf_s.
[wine.git] / dlls / shlwapi / url.c
blobbda4eb2e3fc7146035ef6c08abe640d585b9689e
1 /*
2 * Url functions
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
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #define NO_SHLWAPI_STREAM
35 #include "shlwapi.h"
36 #include "intshcut.h"
37 #include "wine/debug.h"
39 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
40 BOOL WINAPI MLFreeLibrary(HMODULE);
41 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell);
45 static inline WCHAR *heap_strdupAtoW(const char *str)
47 LPWSTR ret = NULL;
49 if(str) {
50 DWORD len;
52 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
53 ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
54 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
57 return ret;
60 /* The following schemes were identified in the native version of
61 * SHLWAPI.DLL version 5.50
63 static const struct {
64 URL_SCHEME scheme_number;
65 WCHAR scheme_name[12];
66 } shlwapi_schemes[] = {
67 {URL_SCHEME_FTP, {'f','t','p',0}},
68 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
69 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
70 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
71 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
72 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
73 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
74 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
75 {URL_SCHEME_FILE, {'f','i','l','e',0}},
76 {URL_SCHEME_MK, {'m','k',0}},
77 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
78 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
79 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
80 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
81 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
82 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
83 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
84 {URL_SCHEME_RES, {'r','e','s',0}},
87 typedef struct {
88 LPCWSTR pScheme; /* [out] start of scheme */
89 DWORD szScheme; /* [out] size of scheme (until colon) */
90 LPCWSTR pUserName; /* [out] start of Username */
91 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
92 LPCWSTR pPassword; /* [out] start of Password */
93 DWORD szPassword; /* [out] size of Password (until "@") */
94 LPCWSTR pHostName; /* [out] start of Hostname */
95 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
96 LPCWSTR pPort; /* [out] start of Port */
97 DWORD szPort; /* [out] size of Port (until "/" or eos) */
98 LPCWSTR pQuery; /* [out] start of Query */
99 DWORD szQuery; /* [out] size of Query (until eos) */
100 } WINE_PARSE_URL;
102 typedef enum {
103 SCHEME,
104 HOST,
105 PORT,
106 USERPASS,
107 } WINE_URL_SCAN_TYPE;
109 static const CHAR hexDigits[] = "0123456789ABCDEF";
111 static const WCHAR fileW[] = {'f','i','l','e','\0'};
113 static const unsigned char HashDataLookup[256] = {
114 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
115 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
116 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
117 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
118 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
119 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
120 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
121 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
122 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
123 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
124 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
125 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
126 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
127 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
128 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
129 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
130 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
131 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
132 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
133 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
135 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
137 unsigned int i;
139 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
140 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
141 && !memicmpW(scheme, shlwapi_schemes[i].scheme_name, scheme_len))
142 return shlwapi_schemes[i].scheme_number;
145 return URL_SCHEME_UNKNOWN;
148 /*************************************************************************
149 * @ [SHLWAPI.1]
151 * Parse a Url into its constituent parts.
153 * PARAMS
154 * x [I] Url to parse
155 * y [O] Undocumented structure holding the parsed information
157 * RETURNS
158 * Success: S_OK. y contains the parsed Url details.
159 * Failure: An HRESULT error code.
161 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
163 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
164 const char *ptr = x;
165 int len;
167 TRACE("%s %p\n", debugstr_a(x), y);
169 if(y->cbSize != sizeof(*y))
170 return E_INVALIDARG;
172 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
173 ptr++;
175 if (*ptr != ':' || ptr <= x+1) {
176 y->pszProtocol = NULL;
177 return URL_E_INVALID_SYNTAX;
180 y->pszProtocol = x;
181 y->cchProtocol = ptr-x;
182 y->pszSuffix = ptr+1;
183 y->cchSuffix = strlen(y->pszSuffix);
185 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
186 scheme, sizeof(scheme)/sizeof(WCHAR));
187 y->nScheme = get_scheme_code(scheme, len);
189 return S_OK;
192 /*************************************************************************
193 * @ [SHLWAPI.2]
195 * Unicode version of ParseURLA.
197 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
199 const WCHAR *ptr = x;
201 TRACE("%s %p\n", debugstr_w(x), y);
203 if(y->cbSize != sizeof(*y))
204 return E_INVALIDARG;
206 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
207 ptr++;
209 if (*ptr != ':' || ptr <= x+1) {
210 y->pszProtocol = NULL;
211 return URL_E_INVALID_SYNTAX;
214 y->pszProtocol = x;
215 y->cchProtocol = ptr-x;
216 y->pszSuffix = ptr+1;
217 y->cchSuffix = strlenW(y->pszSuffix);
218 y->nScheme = get_scheme_code(x, ptr-x);
220 return S_OK;
223 /*************************************************************************
224 * UrlCanonicalizeA [SHLWAPI.@]
226 * Canonicalize a Url.
228 * PARAMS
229 * pszUrl [I] Url to cCanonicalize
230 * pszCanonicalized [O] Destination for converted Url.
231 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
232 * dwFlags [I] Flags controlling the conversion.
234 * RETURNS
235 * Success: S_OK. The pszCanonicalized contains the converted Url.
236 * Failure: E_POINTER, if *pcchCanonicalized is too small.
238 * MSDN incorrectly describes the flags for this function. They should be:
239 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
240 *| URL_ESCAPE_SPACES_ONLY 0x04000000
241 *| URL_ESCAPE_PERCENT 0x00001000
242 *| URL_ESCAPE_UNSAFE 0x10000000
243 *| URL_UNESCAPE 0x10000000
244 *| URL_DONT_SIMPLIFY 0x08000000
245 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
247 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
248 LPDWORD pcchCanonicalized, DWORD dwFlags)
250 LPWSTR url, canonical;
251 HRESULT ret;
253 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
254 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
256 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
257 return E_INVALIDARG;
259 url = heap_strdupAtoW(pszUrl);
260 canonical = HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized*sizeof(WCHAR));
261 if(!url || !canonical) {
262 HeapFree(GetProcessHeap(), 0, url);
263 HeapFree(GetProcessHeap(), 0, canonical);
264 return E_OUTOFMEMORY;
267 ret = UrlCanonicalizeW(url, canonical, pcchCanonicalized, dwFlags);
268 if(ret == S_OK)
269 WideCharToMultiByte(CP_ACP, 0, canonical, -1, pszCanonicalized,
270 *pcchCanonicalized+1, NULL, NULL);
272 HeapFree(GetProcessHeap(), 0, url);
273 HeapFree(GetProcessHeap(), 0, canonical);
274 return ret;
277 /*************************************************************************
278 * UrlCanonicalizeW [SHLWAPI.@]
280 * See UrlCanonicalizeA.
282 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
283 LPDWORD pcchCanonicalized, DWORD dwFlags)
285 HRESULT hr = S_OK;
286 DWORD EscapeFlags;
287 LPCWSTR wk1, root;
288 LPWSTR lpszUrlCpy, url, wk2, mp, mp2;
289 INT state;
290 DWORD nByteLen, nLen, nWkLen;
291 BOOL is_file_url;
292 WCHAR slash = '\0';
294 static const WCHAR wszFile[] = {'f','i','l','e',':'};
295 static const WCHAR wszRes[] = {'r','e','s',':'};
296 static const WCHAR wszHttp[] = {'h','t','t','p',':'};
297 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
298 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
300 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
301 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
303 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
304 return E_INVALIDARG;
306 if(!*pszUrl) {
307 *pszCanonicalized = 0;
308 return S_OK;
311 /* Remove '\t' characters from URL */
312 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
313 url = HeapAlloc(GetProcessHeap(), 0, nByteLen);
314 if(!url)
315 return E_OUTOFMEMORY;
317 wk1 = pszUrl;
318 wk2 = url;
319 do {
320 while(*wk1 == '\t')
321 wk1++;
322 *wk2++ = *wk1;
323 } while(*wk1++);
325 /* Allocate memory for simplified URL (before escaping) */
326 nByteLen = (wk2-url)*sizeof(WCHAR);
327 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
328 nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR));
329 if(!lpszUrlCpy) {
330 HeapFree(GetProcessHeap(), 0, url);
331 return E_OUTOFMEMORY;
334 is_file_url = !strncmpW(wszFile, url, sizeof(wszFile)/sizeof(WCHAR));
336 if ((nByteLen >= sizeof(wszHttp) &&
337 !memcmp(wszHttp, url, sizeof(wszHttp))) || is_file_url)
338 slash = '/';
340 if((dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY)) && is_file_url)
341 slash = '\\';
343 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, url, sizeof(wszRes))) {
344 dwFlags &= ~URL_FILE_USE_PATHURL;
345 slash = '\0';
349 * state =
350 * 0 initial 1,3
351 * 1 have 2[+] alnum 2,3
352 * 2 have scheme (found :) 4,6,3
353 * 3 failed (no location)
354 * 4 have // 5,3
355 * 5 have 1[+] alnum 6,3
356 * 6 have location (found /) save root location
359 wk1 = url;
360 wk2 = lpszUrlCpy;
361 state = 0;
363 if(url[1] == ':') { /* Assume path */
364 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
365 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
366 if (dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY))
368 slash = '\\';
369 --wk2;
371 else
372 dwFlags |= URL_ESCAPE_UNSAFE;
373 state = 5;
374 is_file_url = TRUE;
375 } else if(url[0] == '/') {
376 state = 5;
377 is_file_url = TRUE;
380 while (*wk1) {
381 switch (state) {
382 case 0:
383 if (!isalnumW(*wk1)) {state = 3; break;}
384 *wk2++ = *wk1++;
385 if (!isalnumW(*wk1)) {state = 3; break;}
386 *wk2++ = *wk1++;
387 state = 1;
388 break;
389 case 1:
390 *wk2++ = *wk1;
391 if (*wk1++ == ':') state = 2;
392 break;
393 case 2:
394 *wk2++ = *wk1++;
395 if (*wk1 != '/') {state = 6; break;}
396 *wk2++ = *wk1++;
397 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
398 && is_file_url
399 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
400 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
401 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
402 wk1++;
405 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL)){
406 wk1++;
407 }else if(is_file_url){
408 const WCHAR *body = wk1;
410 while(*body == '/')
411 ++body;
413 if(isalnumW(*body) && *(body+1) == ':'){
414 if(!(dwFlags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL))){
415 if(slash)
416 *wk2++ = slash;
417 else
418 *wk2++ = '/';
420 }else{
421 if(dwFlags & URL_WININET_COMPATIBILITY){
422 if(*wk1 == '/' && *(wk1+1) != '/'){
423 *wk2++ = '\\';
424 }else{
425 *wk2++ = '\\';
426 *wk2++ = '\\';
428 }else{
429 if(*wk1 == '/' && *(wk1+1) != '/'){
430 if(slash)
431 *wk2++ = slash;
432 else
433 *wk2++ = '/';
437 wk1 = body;
439 state = 4;
440 break;
441 case 3:
442 nWkLen = strlenW(wk1);
443 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
444 mp = wk2;
445 wk1 += nWkLen;
446 wk2 += nWkLen;
448 if(slash) {
449 while(mp < wk2) {
450 if(*mp == '/' || *mp == '\\')
451 *mp = slash;
452 mp++;
455 break;
456 case 4:
457 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
458 {state = 3; break;}
459 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
460 *wk2++ = *wk1++;
461 state = 5;
462 if (!*wk1) {
463 if(slash)
464 *wk2++ = slash;
465 else
466 *wk2++ = '/';
468 break;
469 case 5:
470 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
471 while(*wk1 == '/' || *wk1 == '\\') {
472 if(slash)
473 *wk2++ = slash;
474 else
475 *wk2++ = *wk1;
476 wk1++;
478 state = 6;
479 break;
480 case 6:
481 if(dwFlags & URL_DONT_SIMPLIFY) {
482 state = 3;
483 break;
486 /* Now at root location, cannot back up any more. */
487 /* "root" will point at the '/' */
489 root = wk2-1;
490 while (*wk1) {
491 mp = strchrW(wk1, '/');
492 mp2 = strchrW(wk1, '\\');
493 if(mp2 && (!mp || mp2 < mp))
494 mp = mp2;
495 if (!mp) {
496 nWkLen = strlenW(wk1);
497 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
498 wk1 += nWkLen;
499 wk2 += nWkLen;
500 continue;
502 nLen = mp - wk1;
503 if(nLen) {
504 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
505 wk2 += nLen;
506 wk1 += nLen;
508 if(slash)
509 *wk2++ = slash;
510 else
511 *wk2++ = *wk1;
512 wk1++;
514 while (*wk1 == '.') {
515 TRACE("found '/.'\n");
516 if (wk1[1] == '/' || wk1[1] == '\\') {
517 /* case of /./ -> skip the ./ */
518 wk1 += 2;
520 else if (wk1[1] == '.' && (wk1[2] == '/'
521 || wk1[2] == '\\' || wk1[2] == '?'
522 || wk1[2] == '#' || !wk1[2])) {
523 /* case /../ -> need to backup wk2 */
524 TRACE("found '/../'\n");
525 *(wk2-1) = '\0'; /* set end of string */
526 mp = strrchrW(root, '/');
527 mp2 = strrchrW(root, '\\');
528 if(mp2 && (!mp || mp2 < mp))
529 mp = mp2;
530 if (mp && (mp >= root)) {
531 /* found valid backup point */
532 wk2 = mp + 1;
533 if(wk1[2] != '/' && wk1[2] != '\\')
534 wk1 += 2;
535 else
536 wk1 += 3;
538 else {
539 /* did not find point, restore '/' */
540 *(wk2-1) = slash;
541 break;
544 else
545 break;
548 *wk2 = '\0';
549 break;
550 default:
551 FIXME("how did we get here - state=%d\n", state);
552 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
553 HeapFree(GetProcessHeap(), 0, url);
554 return E_INVALIDARG;
556 *wk2 = '\0';
557 TRACE("Simplified, orig <%s>, simple <%s>\n",
558 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
560 nLen = lstrlenW(lpszUrlCpy);
561 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
562 lpszUrlCpy[--nLen]=0;
564 if((dwFlags & URL_UNESCAPE) ||
565 ((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
566 && !memcmp(wszFile, url, sizeof(wszFile))))
567 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
569 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
570 URL_ESCAPE_SPACES_ONLY |
571 URL_ESCAPE_PERCENT |
572 URL_DONT_ESCAPE_EXTRA_INFO |
573 URL_ESCAPE_SEGMENT_ONLY ))) {
574 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
575 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
576 EscapeFlags);
577 } else { /* No escaping needed, just copy the string */
578 nLen = lstrlenW(lpszUrlCpy);
579 if(nLen < *pcchCanonicalized)
580 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
581 else {
582 hr = E_POINTER;
583 nLen++;
585 *pcchCanonicalized = nLen;
588 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
589 HeapFree(GetProcessHeap(), 0, url);
591 if (hr == S_OK)
592 TRACE("result %s\n", debugstr_w(pszCanonicalized));
594 return hr;
597 /*************************************************************************
598 * UrlCombineA [SHLWAPI.@]
600 * Combine two Urls.
602 * PARAMS
603 * pszBase [I] Base Url
604 * pszRelative [I] Url to combine with pszBase
605 * pszCombined [O] Destination for combined Url
606 * pcchCombined [O] Destination for length of pszCombined
607 * dwFlags [I] URL_ flags from "shlwapi.h"
609 * RETURNS
610 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
611 * contains its length.
612 * Failure: An HRESULT error code indicating the error.
614 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
615 LPSTR pszCombined, LPDWORD pcchCombined,
616 DWORD dwFlags)
618 LPWSTR base, relative, combined;
619 DWORD ret, len, len2;
621 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
622 debugstr_a(pszBase),debugstr_a(pszRelative),
623 pcchCombined?*pcchCombined:0,dwFlags);
625 if(!pszBase || !pszRelative || !pcchCombined)
626 return E_INVALIDARG;
628 base = HeapAlloc(GetProcessHeap(), 0,
629 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
630 relative = base + INTERNET_MAX_URL_LENGTH;
631 combined = relative + INTERNET_MAX_URL_LENGTH;
633 MultiByteToWideChar(CP_ACP, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
634 MultiByteToWideChar(CP_ACP, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
635 len = *pcchCombined;
637 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
638 if (ret != S_OK) {
639 *pcchCombined = len;
640 HeapFree(GetProcessHeap(), 0, base);
641 return ret;
644 len2 = WideCharToMultiByte(CP_ACP, 0, combined, len, NULL, 0, NULL, NULL);
645 if (len2 > *pcchCombined) {
646 *pcchCombined = len2;
647 HeapFree(GetProcessHeap(), 0, base);
648 return E_POINTER;
650 WideCharToMultiByte(CP_ACP, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
651 NULL, NULL);
652 *pcchCombined = len2;
653 HeapFree(GetProcessHeap(), 0, base);
654 return S_OK;
657 /*************************************************************************
658 * UrlCombineW [SHLWAPI.@]
660 * See UrlCombineA.
662 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
663 LPWSTR pszCombined, LPDWORD pcchCombined,
664 DWORD dwFlags)
666 PARSEDURLW base, relative;
667 DWORD myflags, sizeloc = 0;
668 DWORD i, len, res1, res2, process_case = 0;
669 LPWSTR work, preliminary, mbase, mrelative;
670 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
671 static const WCHAR fragquerystr[] = {'#','?',0};
672 HRESULT ret;
674 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
675 debugstr_w(pszBase),debugstr_w(pszRelative),
676 pcchCombined?*pcchCombined:0,dwFlags);
678 if(!pszBase || !pszRelative || !pcchCombined)
679 return E_INVALIDARG;
681 base.cbSize = sizeof(base);
682 relative.cbSize = sizeof(relative);
684 /* Get space for duplicates of the input and the output */
685 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
686 sizeof(WCHAR));
687 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
688 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
689 *preliminary = '\0';
691 /* Canonicalize the base input prior to looking for the scheme */
692 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
693 len = INTERNET_MAX_URL_LENGTH;
694 UrlCanonicalizeW(pszBase, mbase, &len, myflags);
696 /* Canonicalize the relative input prior to looking for the scheme */
697 len = INTERNET_MAX_URL_LENGTH;
698 UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
700 /* See if the base has a scheme */
701 res1 = ParseURLW(mbase, &base);
702 if (res1) {
703 /* if pszBase has no scheme, then return pszRelative */
704 TRACE("no scheme detected in Base\n");
705 process_case = 1;
707 else do {
708 BOOL manual_search = FALSE;
710 work = (LPWSTR)base.pszProtocol;
711 for(i=0; i<base.cchProtocol; i++)
712 work[i] = tolowerW(work[i]);
714 /* mk is a special case */
715 if(base.nScheme == URL_SCHEME_MK) {
716 static const WCHAR wsz[] = {':',':',0};
718 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
719 if(ptr) {
720 int delta;
722 ptr += 2;
723 delta = ptr-base.pszSuffix;
724 base.cchProtocol += delta;
725 base.pszSuffix += delta;
726 base.cchSuffix -= delta;
728 }else {
729 /* get size of location field (if it exists) */
730 work = (LPWSTR)base.pszSuffix;
731 sizeloc = 0;
732 if (*work++ == '/') {
733 if (*work++ == '/') {
734 /* At this point have start of location and
735 * it ends at next '/' or end of string.
737 while(*work && (*work != '/')) work++;
738 sizeloc = (DWORD)(work - base.pszSuffix);
743 /* If there is a '?', then the remaining part can only contain a
744 * query string or fragment, so start looking for the last leaf
745 * from the '?'. Otherwise, if there is a '#' and the characters
746 * immediately preceding it are ".htm[l]", then begin looking for
747 * the last leaf starting from the '#'. Otherwise the '#' is not
748 * meaningful and just start looking from the end. */
749 if ((work = strpbrkW(base.pszSuffix + sizeloc, fragquerystr))) {
750 const WCHAR htmlW[] = {'.','h','t','m','l',0};
751 const int len_htmlW = 5;
752 const WCHAR htmW[] = {'.','h','t','m',0};
753 const int len_htmW = 4;
755 if (*work == '?' || base.nScheme == URL_SCHEME_HTTP || base.nScheme == URL_SCHEME_HTTPS)
756 manual_search = TRUE;
757 else if (work - base.pszSuffix > len_htmW) {
758 work -= len_htmW;
759 if (strncmpiW(work, htmW, len_htmW) == 0)
760 manual_search = TRUE;
761 work += len_htmW;
764 if (!manual_search &&
765 work - base.pszSuffix > len_htmlW) {
766 work -= len_htmlW;
767 if (strncmpiW(work, htmlW, len_htmlW) == 0)
768 manual_search = TRUE;
769 work += len_htmlW;
773 if (manual_search) {
774 /* search backwards starting from the current position */
775 while (*work != '/' && work > base.pszSuffix + sizeloc)
776 --work;
777 base.cchSuffix = work - base.pszSuffix + 1;
778 }else {
779 /* search backwards starting from the end of the string */
780 work = strrchrW((base.pszSuffix+sizeloc), '/');
781 if (work) {
782 len = (DWORD)(work - base.pszSuffix + 1);
783 base.cchSuffix = len;
784 }else
785 base.cchSuffix = sizeloc;
789 * At this point:
790 * .pszSuffix points to location (starting with '//')
791 * .cchSuffix length of location (above) and rest less the last
792 * leaf (if any)
793 * sizeloc length of location (above) up to but not including
794 * the last '/'
797 res2 = ParseURLW(mrelative, &relative);
798 if (res2) {
799 /* no scheme in pszRelative */
800 TRACE("no scheme detected in Relative\n");
801 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
802 relative.cchSuffix = strlenW(mrelative);
803 if (*pszRelative == ':') {
804 /* case that is either left alone or uses pszBase */
805 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
806 process_case = 5;
807 break;
809 process_case = 1;
810 break;
812 if (isalnumW(*mrelative) && (*(mrelative + 1) == ':')) {
813 /* case that becomes "file:///" */
814 strcpyW(preliminary, myfilestr);
815 process_case = 1;
816 break;
818 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
819 /* pszRelative has location and rest */
820 process_case = 3;
821 break;
823 if (*mrelative == '/') {
824 /* case where pszRelative is root to location */
825 process_case = 4;
826 break;
828 if (*mrelative == '#') {
829 if(!(work = strchrW(base.pszSuffix+base.cchSuffix, '#')))
830 work = (LPWSTR)base.pszSuffix + strlenW(base.pszSuffix);
832 memcpy(preliminary, base.pszProtocol, (work-base.pszProtocol)*sizeof(WCHAR));
833 preliminary[work-base.pszProtocol] = '\0';
834 process_case = 1;
835 break;
837 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
838 break;
839 }else {
840 work = (LPWSTR)relative.pszProtocol;
841 for(i=0; i<relative.cchProtocol; i++)
842 work[i] = tolowerW(work[i]);
845 /* handle cases where pszRelative has scheme */
846 if ((base.cchProtocol == relative.cchProtocol) &&
847 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
849 /* since the schemes are the same */
850 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
851 /* case where pszRelative replaces location and following */
852 process_case = 3;
853 break;
855 if (*relative.pszSuffix == '/') {
856 /* case where pszRelative is root to location */
857 process_case = 4;
858 break;
860 /* replace either just location if base's location starts with a
861 * slash or otherwise everything */
862 process_case = (*base.pszSuffix == '/') ? 5 : 1;
863 break;
865 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
866 /* case where pszRelative replaces scheme, location,
867 * and following and handles PLUGGABLE
869 process_case = 2;
870 break;
872 process_case = 1;
873 break;
874 } while(FALSE); /* a little trick to allow easy exit from nested if's */
876 ret = S_OK;
877 switch (process_case) {
879 case 1: /*
880 * Return pszRelative appended to what ever is in pszCombined,
881 * (which may the string "file:///"
883 strcatW(preliminary, mrelative);
884 break;
886 case 2: /* case where pszRelative replaces scheme, and location */
887 strcpyW(preliminary, mrelative);
888 break;
890 case 3: /*
891 * Return the pszBase scheme with pszRelative. Basically
892 * keeps the scheme and replaces the domain and following.
894 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
895 work = preliminary + base.cchProtocol + 1;
896 strcpyW(work, relative.pszSuffix);
897 break;
899 case 4: /*
900 * Return the pszBase scheme and location but everything
901 * after the location is pszRelative. (Replace document
902 * from root on.)
904 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
905 work = preliminary + base.cchProtocol + 1 + sizeloc;
906 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
907 *(work++) = '/';
908 strcpyW(work, relative.pszSuffix);
909 break;
911 case 5: /*
912 * Return the pszBase without its document (if any) and
913 * append pszRelative after its scheme.
915 memcpy(preliminary, base.pszProtocol,
916 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
917 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
918 if (*work++ != '/')
919 *(work++) = '/';
920 strcpyW(work, relative.pszSuffix);
921 break;
923 default:
924 FIXME("How did we get here????? process_case=%d\n", process_case);
925 ret = E_INVALIDARG;
928 if (ret == S_OK) {
929 /* Reuse mrelative as temp storage as it's already allocated and not needed anymore */
930 if(*pcchCombined == 0)
931 *pcchCombined = 1;
932 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
933 if(SUCCEEDED(ret) && pszCombined) {
934 lstrcpyW(pszCombined, mrelative);
936 TRACE("return-%d len=%d, %s\n",
937 process_case, *pcchCombined, debugstr_w(pszCombined));
939 HeapFree(GetProcessHeap(), 0, preliminary);
940 return ret;
943 /*************************************************************************
944 * UrlEscapeA [SHLWAPI.@]
947 HRESULT WINAPI UrlEscapeA(
948 LPCSTR pszUrl,
949 LPSTR pszEscaped,
950 LPDWORD pcchEscaped,
951 DWORD dwFlags)
953 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
954 WCHAR *escapedW = bufW;
955 UNICODE_STRING urlW;
956 HRESULT ret;
957 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
959 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
960 return E_INVALIDARG;
962 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
963 return E_INVALIDARG;
964 if(dwFlags & URL_ESCAPE_AS_UTF8) {
965 RtlFreeUnicodeString(&urlW);
966 return E_NOTIMPL;
968 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
969 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
970 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
972 if(ret == S_OK) {
973 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
974 if(*pcchEscaped > lenA) {
975 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
976 pszEscaped[lenA] = 0;
977 *pcchEscaped = lenA;
978 } else {
979 *pcchEscaped = lenA + 1;
980 ret = E_POINTER;
983 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
984 RtlFreeUnicodeString(&urlW);
985 return ret;
988 #define WINE_URL_BASH_AS_SLASH 0x01
989 #define WINE_URL_COLLAPSE_SLASHES 0x02
990 #define WINE_URL_ESCAPE_SLASH 0x04
991 #define WINE_URL_ESCAPE_HASH 0x08
992 #define WINE_URL_ESCAPE_QUESTION 0x10
993 #define WINE_URL_STOP_ON_HASH 0x20
994 #define WINE_URL_STOP_ON_QUESTION 0x40
996 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD flags, DWORD int_flags)
998 if (flags & URL_ESCAPE_SPACES_ONLY)
999 return ch == ' ';
1001 if ((flags & URL_ESCAPE_PERCENT) && (ch == '%'))
1002 return TRUE;
1004 if ((flags & URL_ESCAPE_AS_UTF8) && (ch >= 0x80))
1005 return TRUE;
1007 if (ch <= 31 || (ch >= 127 && ch <= 255) )
1008 return TRUE;
1010 if (isalnumW(ch))
1011 return FALSE;
1013 switch (ch) {
1014 case ' ':
1015 case '<':
1016 case '>':
1017 case '\"':
1018 case '{':
1019 case '}':
1020 case '|':
1021 case '\\':
1022 case '^':
1023 case ']':
1024 case '[':
1025 case '`':
1026 case '&':
1027 return TRUE;
1028 case '/':
1029 return !!(int_flags & WINE_URL_ESCAPE_SLASH);
1030 case '?':
1031 return !!(int_flags & WINE_URL_ESCAPE_QUESTION);
1032 case '#':
1033 return !!(int_flags & WINE_URL_ESCAPE_HASH);
1034 default:
1035 return FALSE;
1040 /*************************************************************************
1041 * UrlEscapeW [SHLWAPI.@]
1043 * Converts unsafe characters in a Url into escape sequences.
1045 * PARAMS
1046 * pszUrl [I] Url to modify
1047 * pszEscaped [O] Destination for modified Url
1048 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1049 * dwFlags [I] URL_ flags from "shlwapi.h"
1051 * RETURNS
1052 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1053 * contains its length.
1054 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1055 * pcchEscaped is set to the required length.
1057 * Converts unsafe characters into their escape sequences.
1059 * NOTES
1060 * - By default this function stops converting at the first '?' or
1061 * '#' character.
1062 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1063 * converted, but the conversion continues past a '?' or '#'.
1064 * - Note that this function did not work well (or at all) in shlwapi version 4.
1066 * BUGS
1067 * Only the following flags are implemented:
1068 *| URL_ESCAPE_SPACES_ONLY
1069 *| URL_DONT_ESCAPE_EXTRA_INFO
1070 *| URL_ESCAPE_SEGMENT_ONLY
1071 *| URL_ESCAPE_PERCENT
1073 HRESULT WINAPI UrlEscapeW(
1074 LPCWSTR pszUrl,
1075 LPWSTR pszEscaped,
1076 LPDWORD pcchEscaped,
1077 DWORD dwFlags)
1079 LPCWSTR src;
1080 DWORD needed = 0, ret;
1081 BOOL stop_escaping = FALSE;
1082 WCHAR next[12], *dst, *dst_ptr;
1083 INT i, len;
1084 PARSEDURLW parsed_url;
1085 DWORD int_flags;
1086 DWORD slashes = 0;
1087 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1089 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1090 pszEscaped, pcchEscaped, dwFlags);
1092 if(!pszUrl || !pcchEscaped || !pszEscaped || *pcchEscaped == 0)
1093 return E_INVALIDARG;
1095 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1096 URL_ESCAPE_SEGMENT_ONLY |
1097 URL_DONT_ESCAPE_EXTRA_INFO |
1098 URL_ESCAPE_PERCENT |
1099 URL_ESCAPE_AS_UTF8))
1100 FIXME("Unimplemented flags: %08x\n", dwFlags);
1102 dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1103 if(!dst_ptr)
1104 return E_OUTOFMEMORY;
1106 /* fix up flags */
1107 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1108 /* if SPACES_ONLY specified, reset the other controls */
1109 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1110 URL_ESCAPE_PERCENT |
1111 URL_ESCAPE_SEGMENT_ONLY);
1113 else
1114 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1115 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1118 int_flags = 0;
1119 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1120 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1121 } else {
1122 parsed_url.cbSize = sizeof(parsed_url);
1123 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1124 parsed_url.nScheme = URL_SCHEME_INVALID;
1126 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1128 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1129 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1131 switch(parsed_url.nScheme) {
1132 case URL_SCHEME_FILE:
1133 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1134 int_flags &= ~WINE_URL_STOP_ON_HASH;
1135 break;
1137 case URL_SCHEME_HTTP:
1138 case URL_SCHEME_HTTPS:
1139 int_flags |= WINE_URL_BASH_AS_SLASH;
1140 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1141 int_flags |= WINE_URL_ESCAPE_SLASH;
1142 break;
1144 case URL_SCHEME_MAILTO:
1145 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1146 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1147 break;
1149 case URL_SCHEME_INVALID:
1150 break;
1152 case URL_SCHEME_FTP:
1153 default:
1154 if(parsed_url.pszSuffix[0] != '/')
1155 int_flags |= WINE_URL_ESCAPE_SLASH;
1156 break;
1160 for(src = pszUrl; *src; ) {
1161 WCHAR cur = *src;
1162 len = 0;
1164 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1165 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1166 while(cur == '/' || cur == '\\') {
1167 slashes++;
1168 cur = *++src;
1170 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1171 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1172 src += localhost_len + 1;
1173 slashes = 3;
1176 switch(slashes) {
1177 case 1:
1178 case 3:
1179 next[0] = next[1] = next[2] = '/';
1180 len = 3;
1181 break;
1182 case 0:
1183 len = 0;
1184 break;
1185 default:
1186 next[0] = next[1] = '/';
1187 len = 2;
1188 break;
1191 if(len == 0) {
1193 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1194 stop_escaping = TRUE;
1196 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1197 stop_escaping = TRUE;
1199 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1201 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1202 if(dwFlags & URL_ESCAPE_AS_UTF8) {
1203 char utf[16];
1205 if ((cur >= 0xd800 && cur <= 0xdfff) &&
1206 (src[1] >= 0xdc00 && src[1] <= 0xdfff))
1208 len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, src, 2,
1209 utf, sizeof(utf), NULL, NULL );
1210 src++;
1212 else
1213 len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, &cur, 1,
1214 utf, sizeof(utf), NULL, NULL );
1216 if (!len) {
1217 utf[0] = 0xef;
1218 utf[1] = 0xbf;
1219 utf[2] = 0xbd;
1220 len = 3;
1223 for(i = 0; i < len; i++) {
1224 next[i*3+0] = '%';
1225 next[i*3+1] = hexDigits[(utf[i] >> 4) & 0xf];
1226 next[i*3+2] = hexDigits[utf[i] & 0xf];
1228 len *= 3;
1229 } else {
1230 next[0] = '%';
1231 next[1] = hexDigits[(cur >> 4) & 0xf];
1232 next[2] = hexDigits[cur & 0xf];
1233 len = 3;
1235 } else {
1236 next[0] = cur;
1237 len = 1;
1239 src++;
1242 if(needed + len <= *pcchEscaped) {
1243 memcpy(dst, next, len*sizeof(WCHAR));
1244 dst += len;
1246 needed += len;
1249 if(needed < *pcchEscaped) {
1250 *dst = '\0';
1251 memcpy(pszEscaped, dst_ptr, (needed+1)*sizeof(WCHAR));
1253 ret = S_OK;
1254 } else {
1255 needed++; /* add one for the '\0' */
1256 ret = E_POINTER;
1258 *pcchEscaped = needed;
1260 HeapFree(GetProcessHeap(), 0, dst_ptr);
1261 return ret;
1265 /*************************************************************************
1266 * UrlUnescapeA [SHLWAPI.@]
1268 * Converts Url escape sequences back to ordinary characters.
1270 * PARAMS
1271 * pszUrl [I/O] Url to convert
1272 * pszUnescaped [O] Destination for converted Url
1273 * pcchUnescaped [I/O] Size of output string
1274 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1276 * RETURNS
1277 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1278 * dwFlags includes URL_ESCAPE_INPLACE.
1279 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1280 * this case pcchUnescaped is set to the size required.
1281 * NOTES
1282 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1283 * the first occurrence of either a '?' or '#' character.
1285 HRESULT WINAPI UrlUnescapeA(
1286 LPSTR pszUrl,
1287 LPSTR pszUnescaped,
1288 LPDWORD pcchUnescaped,
1289 DWORD dwFlags)
1291 char *dst, next;
1292 LPCSTR src;
1293 HRESULT ret;
1294 DWORD needed;
1295 BOOL stop_unescaping = FALSE;
1297 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1298 pcchUnescaped, dwFlags);
1300 if (!pszUrl) return E_INVALIDARG;
1302 if(dwFlags & URL_UNESCAPE_INPLACE)
1303 dst = pszUrl;
1304 else
1306 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1307 dst = pszUnescaped;
1310 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1311 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1312 (*src == '#' || *src == '?')) {
1313 stop_unescaping = TRUE;
1314 next = *src;
1315 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1316 && stop_unescaping == FALSE) {
1317 INT ih;
1318 char buf[3];
1319 memcpy(buf, src + 1, 2);
1320 buf[2] = '\0';
1321 ih = strtol(buf, NULL, 16);
1322 next = (CHAR) ih;
1323 src += 2; /* Advance to end of escape */
1324 } else
1325 next = *src;
1327 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1328 *dst++ = next;
1331 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1332 *dst = '\0';
1333 ret = S_OK;
1334 } else {
1335 needed++; /* add one for the '\0' */
1336 ret = E_POINTER;
1338 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1339 *pcchUnescaped = needed;
1341 if (ret == S_OK) {
1342 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1343 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1346 return ret;
1349 /*************************************************************************
1350 * UrlUnescapeW [SHLWAPI.@]
1352 * See UrlUnescapeA.
1354 HRESULT WINAPI UrlUnescapeW(
1355 LPWSTR pszUrl,
1356 LPWSTR pszUnescaped,
1357 LPDWORD pcchUnescaped,
1358 DWORD dwFlags)
1360 WCHAR *dst, next;
1361 LPCWSTR src;
1362 HRESULT ret;
1363 DWORD needed;
1364 BOOL stop_unescaping = FALSE;
1366 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1367 pcchUnescaped, dwFlags);
1369 if(!pszUrl) return E_INVALIDARG;
1371 if(dwFlags & URL_UNESCAPE_INPLACE)
1372 dst = pszUrl;
1373 else
1375 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1376 dst = pszUnescaped;
1379 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1380 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1381 (*src == '#' || *src == '?')) {
1382 stop_unescaping = TRUE;
1383 next = *src;
1384 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1385 && stop_unescaping == FALSE) {
1386 INT ih;
1387 WCHAR buf[5] = {'0','x',0};
1388 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1389 buf[4] = 0;
1390 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1391 next = (WCHAR) ih;
1392 src += 2; /* Advance to end of escape */
1393 } else
1394 next = *src;
1396 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1397 *dst++ = next;
1400 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1401 *dst = '\0';
1402 ret = S_OK;
1403 } else {
1404 needed++; /* add one for the '\0' */
1405 ret = E_POINTER;
1407 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1408 *pcchUnescaped = needed;
1410 if (ret == S_OK) {
1411 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1412 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1415 return ret;
1418 /*************************************************************************
1419 * UrlGetLocationA [SHLWAPI.@]
1421 * Get the location from a Url.
1423 * PARAMS
1424 * pszUrl [I] Url to get the location from
1426 * RETURNS
1427 * A pointer to the start of the location in pszUrl, or NULL if there is
1428 * no location.
1430 * NOTES
1431 * - MSDN erroneously states that "The location is the segment of the Url
1432 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1433 * stop at '?' and always return a NULL in this case.
1434 * - MSDN also erroneously states that "If a file URL has a query string,
1435 * the returned string is the query string". In all tested cases, if the
1436 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1437 *| Result Url
1438 *| ------ ---
1439 *| NULL file://aa/b/cd#hohoh
1440 *| #hohoh http://aa/b/cd#hohoh
1441 *| NULL fi://aa/b/cd#hohoh
1442 *| #hohoh ff://aa/b/cd#hohoh
1444 LPCSTR WINAPI UrlGetLocationA(
1445 LPCSTR pszUrl)
1447 PARSEDURLA base;
1448 DWORD res1;
1450 base.cbSize = sizeof(base);
1451 res1 = ParseURLA(pszUrl, &base);
1452 if (res1) return NULL; /* invalid scheme */
1454 /* if scheme is file: then never return pointer */
1455 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1457 /* Look for '#' and return its addr */
1458 return strchr(base.pszSuffix, '#');
1461 /*************************************************************************
1462 * UrlGetLocationW [SHLWAPI.@]
1464 * See UrlGetLocationA.
1466 LPCWSTR WINAPI UrlGetLocationW(
1467 LPCWSTR pszUrl)
1469 PARSEDURLW base;
1470 DWORD res1;
1472 base.cbSize = sizeof(base);
1473 res1 = ParseURLW(pszUrl, &base);
1474 if (res1) return NULL; /* invalid scheme */
1476 /* if scheme is file: then never return pointer */
1477 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1479 /* Look for '#' and return its addr */
1480 return strchrW(base.pszSuffix, '#');
1483 /*************************************************************************
1484 * UrlCompareA [SHLWAPI.@]
1486 * Compare two Urls.
1488 * PARAMS
1489 * pszUrl1 [I] First Url to compare
1490 * pszUrl2 [I] Url to compare to pszUrl1
1491 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1493 * RETURNS
1494 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1495 * than, equal to, or less than pszUrl1 respectively.
1497 INT WINAPI UrlCompareA(
1498 LPCSTR pszUrl1,
1499 LPCSTR pszUrl2,
1500 BOOL fIgnoreSlash)
1502 INT ret, len, len1, len2;
1504 if (!fIgnoreSlash)
1505 return strcmp(pszUrl1, pszUrl2);
1506 len1 = strlen(pszUrl1);
1507 if (pszUrl1[len1-1] == '/') len1--;
1508 len2 = strlen(pszUrl2);
1509 if (pszUrl2[len2-1] == '/') len2--;
1510 if (len1 == len2)
1511 return strncmp(pszUrl1, pszUrl2, len1);
1512 len = min(len1, len2);
1513 ret = strncmp(pszUrl1, pszUrl2, len);
1514 if (ret) return ret;
1515 if (len1 > len2) return 1;
1516 return -1;
1519 /*************************************************************************
1520 * UrlCompareW [SHLWAPI.@]
1522 * See UrlCompareA.
1524 INT WINAPI UrlCompareW(
1525 LPCWSTR pszUrl1,
1526 LPCWSTR pszUrl2,
1527 BOOL fIgnoreSlash)
1529 INT ret;
1530 size_t len, len1, len2;
1532 if (!fIgnoreSlash)
1533 return strcmpW(pszUrl1, pszUrl2);
1534 len1 = strlenW(pszUrl1);
1535 if (pszUrl1[len1-1] == '/') len1--;
1536 len2 = strlenW(pszUrl2);
1537 if (pszUrl2[len2-1] == '/') len2--;
1538 if (len1 == len2)
1539 return strncmpW(pszUrl1, pszUrl2, len1);
1540 len = min(len1, len2);
1541 ret = strncmpW(pszUrl1, pszUrl2, len);
1542 if (ret) return ret;
1543 if (len1 > len2) return 1;
1544 return -1;
1547 /*************************************************************************
1548 * HashData [SHLWAPI.@]
1550 * Hash an input block into a variable sized digest.
1552 * PARAMS
1553 * lpSrc [I] Input block
1554 * nSrcLen [I] Length of lpSrc
1555 * lpDest [I] Output for hash digest
1556 * nDestLen [I] Length of lpDest
1558 * RETURNS
1559 * Success: TRUE. lpDest is filled with the computed hash value.
1560 * Failure: FALSE, if any argument is invalid.
1562 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1563 unsigned char *lpDest, DWORD nDestLen)
1565 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1567 if (!lpSrc || !lpDest)
1568 return E_INVALIDARG;
1570 while (destCount >= 0)
1572 lpDest[destCount] = (destCount & 0xff);
1573 destCount--;
1576 while (srcCount >= 0)
1578 destCount = nDestLen - 1;
1579 while (destCount >= 0)
1581 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1582 destCount--;
1584 srcCount--;
1586 return S_OK;
1589 /*************************************************************************
1590 * UrlHashA [SHLWAPI.@]
1592 * Produce a Hash from a Url.
1594 * PARAMS
1595 * pszUrl [I] Url to hash
1596 * lpDest [O] Destinationh for hash
1597 * nDestLen [I] Length of lpDest
1599 * RETURNS
1600 * Success: S_OK. lpDest is filled with the computed hash value.
1601 * Failure: E_INVALIDARG, if any argument is invalid.
1603 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1605 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1606 return E_INVALIDARG;
1608 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1609 return S_OK;
1612 /*************************************************************************
1613 * UrlHashW [SHLWAPI.@]
1615 * See UrlHashA.
1617 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1619 char szUrl[MAX_PATH];
1621 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1623 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1624 return E_INVALIDARG;
1626 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1627 * return the same digests for the same URL.
1629 WideCharToMultiByte(CP_ACP, 0, pszUrl, -1, szUrl, MAX_PATH, NULL, NULL);
1630 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1631 return S_OK;
1634 /*************************************************************************
1635 * UrlApplySchemeA [SHLWAPI.@]
1637 * Apply a scheme to a Url.
1639 * PARAMS
1640 * pszIn [I] Url to apply scheme to
1641 * pszOut [O] Destination for modified Url
1642 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1643 * dwFlags [I] URL_ flags from "shlwapi.h"
1645 * RETURNS
1646 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1647 * Failure: An HRESULT error code describing the error.
1649 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1651 LPWSTR in, out;
1652 HRESULT ret;
1653 DWORD len;
1655 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1656 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1658 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1660 in = HeapAlloc(GetProcessHeap(), 0,
1661 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1662 out = in + INTERNET_MAX_URL_LENGTH;
1664 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1665 len = INTERNET_MAX_URL_LENGTH;
1667 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1668 if (ret != S_OK) {
1669 HeapFree(GetProcessHeap(), 0, in);
1670 return ret;
1673 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1674 if (len > *pcchOut) {
1675 ret = E_POINTER;
1676 goto cleanup;
1679 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1680 len--;
1682 cleanup:
1683 *pcchOut = len;
1684 HeapFree(GetProcessHeap(), 0, in);
1685 return ret;
1688 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1690 HKEY newkey;
1691 BOOL j;
1692 INT index;
1693 DWORD value_len, data_len, dwType, i;
1694 WCHAR reg_path[MAX_PATH];
1695 WCHAR value[MAX_PATH], data[MAX_PATH];
1696 WCHAR Wxx, Wyy;
1698 MultiByteToWideChar(CP_ACP, 0,
1699 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1700 -1, reg_path, MAX_PATH);
1701 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1702 index = 0;
1703 while(value_len = data_len = MAX_PATH,
1704 RegEnumValueW(newkey, index, value, &value_len,
1705 0, &dwType, (LPVOID)data, &data_len) == 0) {
1706 TRACE("guess %d %s is %s\n",
1707 index, debugstr_w(value), debugstr_w(data));
1709 j = FALSE;
1710 for(i=0; i<value_len; i++) {
1711 Wxx = pszIn[i];
1712 Wyy = value[i];
1713 /* remember that TRUE is not-equal */
1714 j = ChrCmpIW(Wxx, Wyy);
1715 if (j) break;
1717 if ((i == value_len) && !j) {
1718 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1719 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1720 RegCloseKey(newkey);
1721 return E_POINTER;
1723 strcpyW(pszOut, data);
1724 strcatW(pszOut, pszIn);
1725 *pcchOut = strlenW(pszOut);
1726 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1727 RegCloseKey(newkey);
1728 return S_OK;
1730 index++;
1732 RegCloseKey(newkey);
1733 return E_FAIL;
1736 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl)
1738 DWORD needed;
1739 HRESULT ret = S_OK;
1740 WCHAR *pszNewUrl;
1741 WCHAR file_colonW[] = {'f','i','l','e',':',0};
1742 WCHAR three_slashesW[] = {'/','/','/',0};
1743 PARSEDURLW parsed_url;
1745 parsed_url.cbSize = sizeof(parsed_url);
1746 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
1747 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
1748 needed = strlenW(pszPath);
1749 if (needed >= *pcchUrl) {
1750 *pcchUrl = needed + 1;
1751 return E_POINTER;
1752 } else {
1753 *pcchUrl = needed;
1754 return S_FALSE;
1759 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
1760 strcpyW(pszNewUrl, file_colonW);
1761 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
1762 strcatW(pszNewUrl, three_slashesW);
1763 strcatW(pszNewUrl, pszPath);
1764 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
1765 HeapFree(GetProcessHeap(), 0, pszNewUrl);
1766 return ret;
1769 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1771 HKEY newkey;
1772 DWORD data_len, dwType;
1773 WCHAR data[MAX_PATH];
1775 static const WCHAR prefix_keyW[] =
1776 {'S','o','f','t','w','a','r','e',
1777 '\\','M','i','c','r','o','s','o','f','t',
1778 '\\','W','i','n','d','o','w','s',
1779 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1780 '\\','U','R','L',
1781 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1783 /* get and prepend default */
1784 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1785 data_len = sizeof(data);
1786 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1787 RegCloseKey(newkey);
1788 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1789 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1790 return E_POINTER;
1792 strcpyW(pszOut, data);
1793 strcatW(pszOut, pszIn);
1794 *pcchOut = strlenW(pszOut);
1795 TRACE("used default %s\n", debugstr_w(pszOut));
1796 return S_OK;
1799 /*************************************************************************
1800 * UrlApplySchemeW [SHLWAPI.@]
1802 * See UrlApplySchemeA.
1804 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1806 PARSEDURLW in_scheme;
1807 DWORD res1;
1808 HRESULT ret;
1810 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1811 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1813 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1815 if (dwFlags & URL_APPLY_GUESSFILE) {
1816 if (*pcchOut > 1 && ':' == pszIn[1]) {
1817 res1 = *pcchOut;
1818 ret = URL_CreateFromPath(pszIn, pszOut, &res1);
1819 if (ret == S_OK || ret == E_POINTER){
1820 *pcchOut = res1;
1821 return ret;
1823 else if (ret == S_FALSE)
1825 return ret;
1830 in_scheme.cbSize = sizeof(in_scheme);
1831 /* See if the base has a scheme */
1832 res1 = ParseURLW(pszIn, &in_scheme);
1833 if (res1) {
1834 /* no scheme in input, need to see if we need to guess */
1835 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1836 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1837 return ret;
1841 /* If we are here, then either invalid scheme,
1842 * or no scheme and can't/failed guess.
1844 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1845 ((res1 != 0)) ) &&
1846 (dwFlags & URL_APPLY_DEFAULT)) {
1847 /* find and apply default scheme */
1848 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1851 return S_FALSE;
1854 /*************************************************************************
1855 * UrlIsA [SHLWAPI.@]
1857 * Determine if a Url is of a certain class.
1859 * PARAMS
1860 * pszUrl [I] Url to check
1861 * Urlis [I] URLIS_ constant from "shlwapi.h"
1863 * RETURNS
1864 * TRUE if pszUrl belongs to the class type in Urlis.
1865 * FALSE Otherwise.
1867 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1869 PARSEDURLA base;
1870 DWORD res1;
1871 LPCSTR last;
1873 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1875 if(!pszUrl)
1876 return FALSE;
1878 switch (Urlis) {
1880 case URLIS_OPAQUE:
1881 base.cbSize = sizeof(base);
1882 res1 = ParseURLA(pszUrl, &base);
1883 if (res1) return FALSE; /* invalid scheme */
1884 switch (base.nScheme)
1886 case URL_SCHEME_MAILTO:
1887 case URL_SCHEME_SHELL:
1888 case URL_SCHEME_JAVASCRIPT:
1889 case URL_SCHEME_VBSCRIPT:
1890 case URL_SCHEME_ABOUT:
1891 return TRUE;
1893 return FALSE;
1895 case URLIS_FILEURL:
1896 return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1897 "file:", 5) == CSTR_EQUAL);
1899 case URLIS_DIRECTORY:
1900 last = pszUrl + strlen(pszUrl) - 1;
1901 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1903 case URLIS_URL:
1904 return PathIsURLA(pszUrl);
1906 case URLIS_NOHISTORY:
1907 case URLIS_APPLIABLE:
1908 case URLIS_HASQUERY:
1909 default:
1910 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1912 return FALSE;
1915 /*************************************************************************
1916 * UrlIsW [SHLWAPI.@]
1918 * See UrlIsA.
1920 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1922 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
1923 PARSEDURLW base;
1924 DWORD res1;
1925 LPCWSTR last;
1927 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1929 if(!pszUrl)
1930 return FALSE;
1932 switch (Urlis) {
1934 case URLIS_OPAQUE:
1935 base.cbSize = sizeof(base);
1936 res1 = ParseURLW(pszUrl, &base);
1937 if (res1) return FALSE; /* invalid scheme */
1938 switch (base.nScheme)
1940 case URL_SCHEME_MAILTO:
1941 case URL_SCHEME_SHELL:
1942 case URL_SCHEME_JAVASCRIPT:
1943 case URL_SCHEME_VBSCRIPT:
1944 case URL_SCHEME_ABOUT:
1945 return TRUE;
1947 return FALSE;
1949 case URLIS_FILEURL:
1950 return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1951 file_colon, 5) == CSTR_EQUAL);
1953 case URLIS_DIRECTORY:
1954 last = pszUrl + strlenW(pszUrl) - 1;
1955 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1957 case URLIS_URL:
1958 return PathIsURLW(pszUrl);
1960 case URLIS_NOHISTORY:
1961 case URLIS_APPLIABLE:
1962 case URLIS_HASQUERY:
1963 default:
1964 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1966 return FALSE;
1969 /*************************************************************************
1970 * UrlIsNoHistoryA [SHLWAPI.@]
1972 * Determine if a Url should not be stored in the users history list.
1974 * PARAMS
1975 * pszUrl [I] Url to check
1977 * RETURNS
1978 * TRUE, if pszUrl should be excluded from the history list,
1979 * FALSE otherwise.
1981 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1983 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1986 /*************************************************************************
1987 * UrlIsNoHistoryW [SHLWAPI.@]
1989 * See UrlIsNoHistoryA.
1991 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1993 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1996 /*************************************************************************
1997 * UrlIsOpaqueA [SHLWAPI.@]
1999 * Determine if a Url is opaque.
2001 * PARAMS
2002 * pszUrl [I] Url to check
2004 * RETURNS
2005 * TRUE if pszUrl is opaque,
2006 * FALSE Otherwise.
2008 * NOTES
2009 * An opaque Url is one that does not start with "<protocol>://".
2011 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
2013 return UrlIsA(pszUrl, URLIS_OPAQUE);
2016 /*************************************************************************
2017 * UrlIsOpaqueW [SHLWAPI.@]
2019 * See UrlIsOpaqueA.
2021 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
2023 return UrlIsW(pszUrl, URLIS_OPAQUE);
2026 /*************************************************************************
2027 * Scans for characters of type "type" and when not matching found,
2028 * returns pointer to it and length in size.
2030 * Characters tested based on RFC 1738
2032 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
2034 static DWORD alwayszero = 0;
2035 BOOL cont = TRUE;
2037 *size = 0;
2039 switch(type){
2041 case SCHEME:
2042 while (cont) {
2043 if ( (islowerW(*start) && isalphaW(*start)) ||
2044 isdigitW(*start) ||
2045 (*start == '+') ||
2046 (*start == '-') ||
2047 (*start == '.')) {
2048 start++;
2049 (*size)++;
2051 else
2052 cont = FALSE;
2055 if(*start != ':')
2056 *size = 0;
2058 break;
2060 case USERPASS:
2061 while (cont) {
2062 if ( isalphaW(*start) ||
2063 isdigitW(*start) ||
2064 /* user/password only characters */
2065 (*start == ';') ||
2066 (*start == '?') ||
2067 (*start == '&') ||
2068 (*start == '=') ||
2069 /* *extra* characters */
2070 (*start == '!') ||
2071 (*start == '*') ||
2072 (*start == '\'') ||
2073 (*start == '(') ||
2074 (*start == ')') ||
2075 (*start == ',') ||
2076 /* *safe* characters */
2077 (*start == '$') ||
2078 (*start == '_') ||
2079 (*start == '+') ||
2080 (*start == '-') ||
2081 (*start == '.') ||
2082 (*start == ' ')) {
2083 start++;
2084 (*size)++;
2085 } else if (*start == '%') {
2086 if (isxdigitW(*(start+1)) &&
2087 isxdigitW(*(start+2))) {
2088 start += 3;
2089 *size += 3;
2090 } else
2091 cont = FALSE;
2092 } else
2093 cont = FALSE;
2095 break;
2097 case PORT:
2098 while (cont) {
2099 if (isdigitW(*start)) {
2100 start++;
2101 (*size)++;
2103 else
2104 cont = FALSE;
2106 break;
2108 case HOST:
2109 while (cont) {
2110 if (isalnumW(*start) ||
2111 (*start == '-') ||
2112 (*start == '.') ||
2113 (*start == ' ') ||
2114 (*start == '*') ) {
2115 start++;
2116 (*size)++;
2118 else
2119 cont = FALSE;
2121 break;
2122 default:
2123 FIXME("unknown type %d\n", type);
2124 return (LPWSTR)&alwayszero;
2126 /* TRACE("scanned %d characters next char %p<%c>\n",
2127 *size, start, *start); */
2128 return start;
2131 /*************************************************************************
2132 * Attempt to parse URL into pieces.
2134 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2136 LPCWSTR work;
2138 memset(pl, 0, sizeof(WINE_PARSE_URL));
2139 pl->pScheme = pszUrl;
2140 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2141 if (!*work || (*work != ':')) goto ErrorExit;
2142 work++;
2143 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2144 pl->pUserName = work + 2;
2145 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2146 if (*work == ':' ) {
2147 /* parse password */
2148 work++;
2149 pl->pPassword = work;
2150 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2151 if (*work != '@') {
2152 /* what we just parsed must be the hostname and port
2153 * so reset pointers and clear then let it parse */
2154 pl->szUserName = pl->szPassword = 0;
2155 work = pl->pUserName - 1;
2156 pl->pUserName = pl->pPassword = 0;
2158 } else if (*work == '@') {
2159 /* no password */
2160 pl->szPassword = 0;
2161 pl->pPassword = 0;
2162 } else if (!*work || (*work == '/') || (*work == '.')) {
2163 /* what was parsed was hostname, so reset pointers and let it parse */
2164 pl->szUserName = pl->szPassword = 0;
2165 work = pl->pUserName - 1;
2166 pl->pUserName = pl->pPassword = 0;
2167 } else goto ErrorExit;
2169 /* now start parsing hostname or hostnumber */
2170 work++;
2171 pl->pHostName = work;
2172 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2173 if (*work == ':') {
2174 /* parse port */
2175 work++;
2176 pl->pPort = work;
2177 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2179 if (*work == '/') {
2180 /* see if query string */
2181 pl->pQuery = strchrW(work, '?');
2182 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2184 SuccessExit:
2185 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2186 pl->pScheme, pl->szScheme,
2187 pl->pUserName, pl->szUserName,
2188 pl->pPassword, pl->szPassword,
2189 pl->pHostName, pl->szHostName,
2190 pl->pPort, pl->szPort,
2191 pl->pQuery, pl->szQuery);
2192 return S_OK;
2193 ErrorExit:
2194 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2195 return E_INVALIDARG;
2198 /*************************************************************************
2199 * UrlGetPartA [SHLWAPI.@]
2201 * Retrieve part of a Url.
2203 * PARAMS
2204 * pszIn [I] Url to parse
2205 * pszOut [O] Destination for part of pszIn requested
2206 * pcchOut [I] Size of pszOut
2207 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2208 * needed size of pszOut INCLUDING '\0'.
2209 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2210 * dwFlags [I] URL_ flags from "shlwapi.h"
2212 * RETURNS
2213 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2214 * Failure: An HRESULT error code describing the error.
2216 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2217 DWORD dwPart, DWORD dwFlags)
2219 LPWSTR in, out;
2220 DWORD ret, len, len2;
2222 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2223 return E_INVALIDARG;
2225 in = HeapAlloc(GetProcessHeap(), 0,
2226 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2227 out = in + INTERNET_MAX_URL_LENGTH;
2229 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2231 len = INTERNET_MAX_URL_LENGTH;
2232 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2234 if (FAILED(ret)) {
2235 HeapFree(GetProcessHeap(), 0, in);
2236 return ret;
2239 len2 = WideCharToMultiByte(CP_ACP, 0, out, len, NULL, 0, NULL, NULL);
2240 if (len2 > *pcchOut) {
2241 *pcchOut = len2+1;
2242 HeapFree(GetProcessHeap(), 0, in);
2243 return E_POINTER;
2245 len2 = WideCharToMultiByte(CP_ACP, 0, out, len+1, pszOut, *pcchOut, NULL, NULL);
2246 *pcchOut = len2-1;
2247 HeapFree(GetProcessHeap(), 0, in);
2248 return ret;
2251 /*************************************************************************
2252 * UrlGetPartW [SHLWAPI.@]
2254 * See UrlGetPartA.
2256 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2257 DWORD dwPart, DWORD dwFlags)
2259 WINE_PARSE_URL pl;
2260 HRESULT ret;
2261 DWORD scheme, size, schsize;
2262 LPCWSTR addr, schaddr;
2264 TRACE("(%s %p %p(%d) %08x %08x)\n",
2265 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2267 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2268 return E_INVALIDARG;
2270 *pszOut = '\0';
2272 addr = strchrW(pszIn, ':');
2273 if(!addr)
2274 scheme = URL_SCHEME_UNKNOWN;
2275 else
2276 scheme = get_scheme_code(pszIn, addr-pszIn);
2278 ret = URL_ParseUrl(pszIn, &pl);
2280 switch (dwPart) {
2281 case URL_PART_SCHEME:
2282 if (!pl.szScheme) {
2283 *pcchOut = 0;
2284 return S_FALSE;
2286 addr = pl.pScheme;
2287 size = pl.szScheme;
2288 break;
2290 case URL_PART_HOSTNAME:
2291 switch(scheme) {
2292 case URL_SCHEME_FTP:
2293 case URL_SCHEME_HTTP:
2294 case URL_SCHEME_GOPHER:
2295 case URL_SCHEME_TELNET:
2296 case URL_SCHEME_FILE:
2297 case URL_SCHEME_HTTPS:
2298 break;
2299 default:
2300 *pcchOut = 0;
2301 return E_FAIL;
2304 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2305 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2306 *pcchOut = 0;
2307 return S_FALSE;
2310 if (!pl.szHostName) {
2311 *pcchOut = 0;
2312 return S_FALSE;
2314 addr = pl.pHostName;
2315 size = pl.szHostName;
2316 break;
2318 case URL_PART_USERNAME:
2319 if (!pl.szUserName) {
2320 *pcchOut = 0;
2321 return S_FALSE;
2323 addr = pl.pUserName;
2324 size = pl.szUserName;
2325 break;
2327 case URL_PART_PASSWORD:
2328 if (!pl.szPassword) {
2329 *pcchOut = 0;
2330 return S_FALSE;
2332 addr = pl.pPassword;
2333 size = pl.szPassword;
2334 break;
2336 case URL_PART_PORT:
2337 if (!pl.szPort) {
2338 *pcchOut = 0;
2339 return S_FALSE;
2341 addr = pl.pPort;
2342 size = pl.szPort;
2343 break;
2345 case URL_PART_QUERY:
2346 if (!pl.szQuery) {
2347 *pcchOut = 0;
2348 return S_FALSE;
2350 addr = pl.pQuery;
2351 size = pl.szQuery;
2352 break;
2354 default:
2355 *pcchOut = 0;
2356 return E_INVALIDARG;
2359 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2360 if(!pl.pScheme || !pl.szScheme) {
2361 *pcchOut = 0;
2362 return E_FAIL;
2364 schaddr = pl.pScheme;
2365 schsize = pl.szScheme;
2366 if (*pcchOut < schsize + size + 2) {
2367 *pcchOut = schsize + size + 2;
2368 return E_POINTER;
2370 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2371 pszOut[schsize] = ':';
2372 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2373 pszOut[schsize+1+size] = 0;
2374 *pcchOut = schsize + 1 + size;
2376 else {
2377 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2378 memcpy(pszOut, addr, size*sizeof(WCHAR));
2379 pszOut[size] = 0;
2380 *pcchOut = size;
2382 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2384 return ret;
2387 /*************************************************************************
2388 * PathIsURLA [SHLWAPI.@]
2390 * Check if the given path is a Url.
2392 * PARAMS
2393 * lpszPath [I] Path to check.
2395 * RETURNS
2396 * TRUE if lpszPath is a Url.
2397 * FALSE if lpszPath is NULL or not a Url.
2399 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2401 PARSEDURLA base;
2402 HRESULT hres;
2404 TRACE("%s\n", debugstr_a(lpstrPath));
2406 if (!lpstrPath || !*lpstrPath) return FALSE;
2408 /* get protocol */
2409 base.cbSize = sizeof(base);
2410 hres = ParseURLA(lpstrPath, &base);
2411 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2414 /*************************************************************************
2415 * PathIsURLW [SHLWAPI.@]
2417 * See PathIsURLA.
2419 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2421 PARSEDURLW base;
2422 HRESULT hres;
2424 TRACE("%s\n", debugstr_w(lpstrPath));
2426 if (!lpstrPath || !*lpstrPath) return FALSE;
2428 /* get protocol */
2429 base.cbSize = sizeof(base);
2430 hres = ParseURLW(lpstrPath, &base);
2431 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2434 /*************************************************************************
2435 * UrlCreateFromPathA [SHLWAPI.@]
2437 * See UrlCreateFromPathW
2439 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2441 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2442 WCHAR *urlW = bufW;
2443 UNICODE_STRING pathW;
2444 HRESULT ret;
2445 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2447 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2448 return E_INVALIDARG;
2449 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2450 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2451 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2453 if(ret == S_OK || ret == S_FALSE) {
2454 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2455 if(*pcchUrl > lenA) {
2456 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2457 pszUrl[lenA] = 0;
2458 *pcchUrl = lenA;
2459 } else {
2460 *pcchUrl = lenA + 1;
2461 ret = E_POINTER;
2464 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2465 RtlFreeUnicodeString(&pathW);
2466 return ret;
2469 /*************************************************************************
2470 * UrlCreateFromPathW [SHLWAPI.@]
2472 * Create a Url from a file path.
2474 * PARAMS
2475 * pszPath [I] Path to convert
2476 * pszUrl [O] Destination for the converted Url
2477 * pcchUrl [I/O] Length of pszUrl
2478 * dwReserved [I] Reserved, must be 0
2480 * RETURNS
2481 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2482 * Failure: An HRESULT error code.
2484 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2486 HRESULT ret;
2488 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2490 /* Validate arguments */
2491 if (dwReserved != 0)
2492 return E_INVALIDARG;
2493 if (!pszUrl || !pcchUrl)
2494 return E_INVALIDARG;
2496 ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl);
2498 if (S_FALSE == ret)
2499 strcpyW(pszUrl, pszPath);
2501 return ret;
2504 /*************************************************************************
2505 * SHAutoComplete [SHLWAPI.@]
2507 * Enable auto-completion for an edit control.
2509 * PARAMS
2510 * hwndEdit [I] Handle of control to enable auto-completion for
2511 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2513 * RETURNS
2514 * Success: S_OK. Auto-completion is enabled for the control.
2515 * Failure: An HRESULT error code indicating the error.
2517 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2519 FIXME("stub\n");
2520 return S_FALSE;
2523 /*************************************************************************
2524 * MLBuildResURLA [SHLWAPI.405]
2526 * Create a Url pointing to a resource in a module.
2528 * PARAMS
2529 * lpszLibName [I] Name of the module containing the resource
2530 * hMod [I] Callers module handle
2531 * dwFlags [I] Undocumented flags for loading the module
2532 * lpszRes [I] Resource name
2533 * lpszDest [O] Destination for resulting Url
2534 * dwDestLen [I] Length of lpszDest
2536 * RETURNS
2537 * Success: S_OK. lpszDest contains the resource Url.
2538 * Failure: E_INVALIDARG, if any argument is invalid, or
2539 * E_FAIL if dwDestLen is too small.
2541 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2542 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2544 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2545 HRESULT hRet;
2547 if (lpszLibName)
2548 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2550 if (lpszRes)
2551 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2553 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2554 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2556 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2557 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2558 if (SUCCEEDED(hRet) && lpszDest)
2559 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, NULL, NULL);
2561 return hRet;
2564 /*************************************************************************
2565 * MLBuildResURLA [SHLWAPI.406]
2567 * See MLBuildResURLA.
2569 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2570 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2572 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2573 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2574 HRESULT hRet = E_FAIL;
2576 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2577 debugstr_w(lpszRes), lpszDest, dwDestLen);
2579 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2580 !lpszDest || (dwFlags && dwFlags != 2))
2581 return E_INVALIDARG;
2583 if (dwDestLen >= szResLen + 1)
2585 dwDestLen -= (szResLen + 1);
2586 memcpy(lpszDest, szRes, sizeof(szRes));
2588 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2590 if (hMod)
2592 WCHAR szBuff[MAX_PATH];
2593 DWORD len;
2595 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2596 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2598 DWORD dwPathLen = strlenW(szBuff) + 1;
2600 if (dwDestLen >= dwPathLen)
2602 DWORD dwResLen;
2604 dwDestLen -= dwPathLen;
2605 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2607 dwResLen = strlenW(lpszRes) + 1;
2608 if (dwDestLen >= dwResLen + 1)
2610 lpszDest[szResLen + dwPathLen-1] = '/';
2611 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2612 hRet = S_OK;
2616 MLFreeLibrary(hMod);
2619 return hRet;
2622 /***********************************************************************
2623 * UrlFixupW [SHLWAPI.462]
2625 * Checks the scheme part of a URL and attempts to correct misspellings.
2627 * PARAMS
2628 * lpszUrl [I] Pointer to the URL to be corrected
2629 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2630 * dwMaxChars [I] Maximum size of corrected URL
2632 * RETURNS
2633 * success: S_OK if URL corrected or already correct
2634 * failure: S_FALSE if unable to correct / COM error code if other error
2637 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2639 DWORD srcLen;
2641 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2643 if (!url)
2644 return E_FAIL;
2646 srcLen = lstrlenW(url) + 1;
2648 /* For now just copy the URL directly */
2649 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2651 return S_OK;
2654 /*************************************************************************
2655 * IsInternetESCEnabled [SHLWAPI.@]
2657 BOOL WINAPI IsInternetESCEnabled(void)
2659 FIXME(": stub\n");
2660 return FALSE;