1 /* 2 * Url functions 3 * 4 * Copyright 2000 Huw D M Davies for CodeWeavers. 5 * 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. 10 * 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. 15 * 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 19 */ 20 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" 38 39 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD); 40 BOOL WINAPI MLFreeLibrary(HMODULE); 41 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD); 42 43 WINE_DEFAULT_DEBUG_CHANNEL(shell); 44 45 static inline WCHAR *heap_strdupAtoW(const char *str) 46 { 47 LPWSTR ret = NULL; 48 49 if(str) { 50 DWORD len; 51 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); 55 } 56 57 return ret; 58 } 59 60 /* The following schemes were identified in the native version of 61 * SHLWAPI.DLL version 5.50 62 */ 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}}, 85 }; 86 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; 101 102 typedef enum { 103 SCHEME, 104 HOST, 105 PORT, 106 USERPASS, 107 } WINE_URL_SCAN_TYPE; 108 109 static const CHAR hexDigits[] = "0123456789ABCDEF"; 110 111 static const WCHAR fileW[] = {'f','i','l','e','\0'}; 112 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 }; 134 135 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len) 136 { 137 unsigned int i; 138 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; 143 } 144 145 return URL_SCHEME_UNKNOWN; 146 } 147 148 /************************************************************************* 149 * @ [SHLWAPI.1] 150 * 151 * Parse a Url into its constituent parts. 152 * 153 * PARAMS 154 * x [I] Url to parse 155 * y [O] Undocumented structure holding the parsed information 156 * 157 * RETURNS 158 * Success: S_OK. y contains the parsed Url details. 159 * Failure: An HRESULT error code. 160 */ 161 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y) 162 { 163 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH]; 164 const char *ptr = x; 165 int len; 166 167 TRACE("%s %p\n", debugstr_a(x), y); 168 169 if(y->cbSize != sizeof(*y)) 170 return E_INVALIDARG; 171 172 while(*ptr && (isalnum(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.')) 173 ptr++; 174 175 if (*ptr != ':' || ptr <= x+1) { 176 y->pszProtocol = NULL; 177 return URL_E_INVALID_SYNTAX; 178 } 179 180 y->pszProtocol = x; 181 y->cchProtocol = ptr-x; 182 y->pszSuffix = ptr+1; 183 y->cchSuffix = strlen(y->pszSuffix); 184 185 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x, 186 scheme, sizeof(scheme)/sizeof(WCHAR)); 187 y->nScheme = get_scheme_code(scheme, len); 188 189 return S_OK; 190 } 191 192 /************************************************************************* 193 * @ [SHLWAPI.2] 194 * 195 * Unicode version of ParseURLA. 196 */ 197 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y) 198 { 199 const WCHAR *ptr = x; 200 201 TRACE("%s %p\n", debugstr_w(x), y); 202 203 if(y->cbSize != sizeof(*y)) 204 return E_INVALIDARG; 205 206 while(*ptr && (isalnumW(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.')) 207 ptr++; 208 209 if (*ptr != ':' || ptr <= x+1) { 210 y->pszProtocol = NULL; 211 return URL_E_INVALID_SYNTAX; 212 } 213 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); 219 220 return S_OK; 221 } 222 223 /************************************************************************* 224 * UrlCanonicalizeA [SHLWAPI.@] 225 * 226 * Canonicalize a Url. 227 * 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. 233 * 234 * RETURNS 235 * Success: S_OK. The pszCanonicalized contains the converted Url. 236 * Failure: E_POINTER, if *pcchCanonicalized is too small. 237 * 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 246 */ 247 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized, 248 LPDWORD pcchCanonicalized, DWORD dwFlags) 249 { 250 LPWSTR url, canonical; 251 HRESULT ret; 252 253 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized, 254 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1); 255 256 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized) 257 return E_INVALIDARG; 258 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; 265 } 266 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); 271 272 HeapFree(GetProcessHeap(), 0, url); 273 HeapFree(GetProcessHeap(), 0, canonical); 274 return ret; 275 } 276 277 /************************************************************************* 278 * UrlCanonicalizeW [SHLWAPI.@] 279 * 280 * See UrlCanonicalizeA. 281 */ 282 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized, 283 LPDWORD pcchCanonicalized, DWORD dwFlags) 284 { 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'; 293 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',':','/','/','/'}; 299 300 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized, 301 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1); 302 303 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized) 304 return E_INVALIDARG; 305 306 if(!*pszUrl) { 307 *pszCanonicalized = 0; 308 return S_OK; 309 } 310 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; 316 317 wk1 = pszUrl; 318 wk2 = url; 319 do { 320 while(*wk1 == '\t') 321 wk1++; 322 *wk2++ = *wk1; 323 } while(*wk1++); 324 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; 332 } 333 334 is_file_url = !strncmpW(wszFile, url, sizeof(wszFile)/sizeof(WCHAR)); 335 336 if ((nByteLen >= sizeof(wszHttp) && 337 !memcmp(wszHttp, url, sizeof(wszHttp))) || is_file_url) 338 slash = '/'; 339 340 if((dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY)) && is_file_url) 341 slash = '\\'; 342 343 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, url, sizeof(wszRes))) { 344 dwFlags &= ~URL_FILE_USE_PATHURL; 345 slash = '\0'; 346 } 347 348 /* 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 357 */ 358 359 wk1 = url; 360 wk2 = lpszUrlCpy; 361 state = 0; 362 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)) 367 { 368 slash = '\\'; 369 --wk2; 370 } 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; 378 } 379 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++; 403 } 404 405 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL)){ 406 wk1++; 407 }else if(is_file_url){ 408 const WCHAR *body = wk1; 409 410 while(*body == '/') 411 ++body; 412 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++ = '/'; 419 } 420 }else{ 421 if(dwFlags & URL_WININET_COMPATIBILITY){ 422 if(*wk1 == '/' && *(wk1+1) != '/'){ 423 *wk2++ = '\\'; 424 }else{ 425 *wk2++ = '\\'; 426 *wk2++ = '\\'; 427 } 428 }else{ 429 if(*wk1 == '/' && *(wk1+1) != '/'){ 430 if(slash) 431 *wk2++ = slash; 432 else 433 *wk2++ = '/'; 434 } 435 } 436 } 437 wk1 = body; 438 } 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; 447 448 if(slash) { 449 while(mp < wk2) { 450 if(*mp == '/' || *mp == '\\') 451 *mp = slash; 452 mp++; 453 } 454 } 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++ = '/'; 467 } 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++; 477 } 478 state = 6; 479 break; 480 case 6: 481 if(dwFlags & URL_DONT_SIMPLIFY) { 482 state = 3; 483 break; 484 } 485 486 /* Now at root location, cannot back up any more. */ 487 /* "root" will point at the '/' */ 488 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; 501 } 502 nLen = mp - wk1; 503 if(nLen) { 504 memcpy(wk2, wk1, nLen * sizeof(WCHAR)); 505 wk2 += nLen; 506 wk1 += nLen; 507 } 508 if(slash) 509 *wk2++ = slash; 510 else 511 *wk2++ = *wk1; 512 wk1++; 513 514 while (*wk1 == '.') { 515 TRACE("found '/.'\n"); 516 if (wk1[1] == '/' || wk1[1] == '\\') { 517 /* case of /./ -> skip the ./ */ 518 wk1 += 2; 519 } 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; 537 } 538 else { 539 /* did not find point, restore '/' */ 540 *(wk2-1) = slash; 541 break; 542 } 543 } 544 else 545 break; 546 } 547 } 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; 555 } 556 *wk2 = '\0'; 557 TRACE("Simplified, orig <%s>, simple <%s>\n", 558 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy)); 559 } 560 nLen = lstrlenW(lpszUrlCpy); 561 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' '))) 562 lpszUrlCpy[--nLen]=0; 563 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); 568 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++; 584 } 585 *pcchCanonicalized = nLen; 586 } 587 588 HeapFree(GetProcessHeap(), 0, lpszUrlCpy); 589 HeapFree(GetProcessHeap(), 0, url); 590 591 if (hr == S_OK) 592 TRACE("result %s\n", debugstr_w(pszCanonicalized)); 593 594 return hr; 595 } 596 597 /************************************************************************* 598 * UrlCombineA [SHLWAPI.@] 599 * 600 * Combine two Urls. 601 * 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" 608 * 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. 613 */ 614 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative, 615 LPSTR pszCombined, LPDWORD pcchCombined, 616 DWORD dwFlags) 617 { 618 LPWSTR base, relative, combined; 619 DWORD ret, len, len2; 620 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); 624 625 if(!pszBase || !pszRelative || !pcchCombined) 626 return E_INVALIDARG; 627 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; 632 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; 636 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; 642 } 643 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; 649 } 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; 655 } 656 657 /************************************************************************* 658 * UrlCombineW [SHLWAPI.@] 659 * 660 * See UrlCombineA. 661 */ 662 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative, 663 LPWSTR pszCombined, LPDWORD pcchCombined, 664 DWORD dwFlags) 665 { 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; 673 674 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n", 675 debugstr_w(pszBase),debugstr_w(pszRelative), 676 pcchCombined?*pcchCombined:0,dwFlags); 677 678 if(!pszBase || !pszRelative || !pcchCombined) 679 return E_INVALIDARG; 680 681 base.cbSize = sizeof(base); 682 relative.cbSize = sizeof(relative); 683 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'; 690 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); 695 696 /* Canonicalize the relative input prior to looking for the scheme */ 697 len = INTERNET_MAX_URL_LENGTH; 698 UrlCanonicalizeW(pszRelative, mrelative, &len, myflags); 699 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; 706 } 707 else do { 708 BOOL manual_search = FALSE; 709 710 work = (LPWSTR)base.pszProtocol; 711 for(i=0; i<base.cchProtocol; i++) 712 work[i] = tolowerW(work[i]); 713 714 /* mk is a special case */ 715 if(base.nScheme == URL_SCHEME_MK) { 716 static const WCHAR wsz[] = {':',':',0}; 717 718 WCHAR *ptr = strstrW(base.pszSuffix, wsz); 719 if(ptr) { 720 int delta; 721 722 ptr += 2; 723 delta = ptr-base.pszSuffix; 724 base.cchProtocol += delta; 725 base.pszSuffix += delta; 726 base.cchSuffix -= delta; 727 } 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. 736 */ 737 while(*work && (*work != '/')) work++; 738 sizeloc = (DWORD)(work - base.pszSuffix); 739 } 740 } 741 } 742 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; 754 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; 762 } 763 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; 770 } 771 } 772 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; 786 } 787 788 /* 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 '/' 795 */ 796 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; 808 } 809 process_case = 1; 810 break; 811 } 812 if (isalnumW(*mrelative) && (*(mrelative + 1) == ':')) { 813 /* case that becomes "file:///" */ 814 strcpyW(preliminary, myfilestr); 815 process_case = 1; 816 break; 817 } 818 if ((*mrelative == '/') && (*(mrelative+1) == '/')) { 819 /* pszRelative has location and rest */ 820 process_case = 3; 821 break; 822 } 823 if (*mrelative == '/') { 824 /* case where pszRelative is root to location */ 825 process_case = 4; 826 break; 827 } 828 if (*mrelative == '#') { 829 if(!(work = strchrW(base.pszSuffix+base.cchSuffix, '#'))) 830 work = (LPWSTR)base.pszSuffix + strlenW(base.pszSuffix); 831 832 memcpy(preliminary, base.pszProtocol, (work-base.pszProtocol)*sizeof(WCHAR)); 833 preliminary[work-base.pszProtocol] = '\0'; 834 process_case = 1; 835 break; 836 } 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]); 843 } 844 845 /* handle cases where pszRelative has scheme */ 846 if ((base.cchProtocol == relative.cchProtocol) && 847 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) { 848 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; 854 } 855 if (*relative.pszSuffix == '/') { 856 /* case where pszRelative is root to location */ 857 process_case = 4; 858 break; 859 } 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; 864 } 865 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) { 866 /* case where pszRelative replaces scheme, location, 867 * and following and handles PLUGGABLE 868 */ 869 process_case = 2; 870 break; 871 } 872 process_case = 1; 873 break; 874 } while(FALSE); /* a little trick to allow easy exit from nested if's */ 875 876 ret = S_OK; 877 switch (process_case) { 878 879 case 1: /* 880 * Return pszRelative appended to what ever is in pszCombined, 881 * (which may the string "file:///" 882 */ 883 strcatW(preliminary, mrelative); 884 break; 885 886 case 2: /* case where pszRelative replaces scheme, and location */ 887 strcpyW(preliminary, mrelative); 888 break; 889 890 case 3: /* 891 * Return the pszBase scheme with pszRelative. Basically 892 * keeps the scheme and replaces the domain and following. 893 */ 894 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR)); 895 work = preliminary + base.cchProtocol + 1; 896 strcpyW(work, relative.pszSuffix); 897 break; 898 899 case 4: /* 900 * Return the pszBase scheme and location but everything 901 * after the location is pszRelative. (Replace document 902 * from root on.) 903 */ 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; 910 911 case 5: /* 912 * Return the pszBase without its document (if any) and 913 * append pszRelative after its scheme. 914 */ 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 if (relative.pszSuffix[0] == '.' && relative.pszSuffix[1] == 0) 921 *work = 0; 922 else 923 strcpyW(work, relative.pszSuffix); 924 break; 925 926 default: 927 FIXME("How did we get here????? process_case=%d\n", process_case); 928 ret = E_INVALIDARG; 929 } 930 931 if (ret == S_OK) { 932 /* Reuse mrelative as temp storage as it's already allocated and not needed anymore */ 933 if(*pcchCombined == 0) 934 *pcchCombined = 1; 935 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL)); 936 if(SUCCEEDED(ret) && pszCombined) { 937 lstrcpyW(pszCombined, mrelative); 938 } 939 TRACE("return-%d len=%d, %s\n", 940 process_case, *pcchCombined, debugstr_w(pszCombined)); 941 } 942 HeapFree(GetProcessHeap(), 0, preliminary); 943 return ret; 944 } 945 946 /************************************************************************* 947 * UrlEscapeA [SHLWAPI.@] 948 */ 949 950 HRESULT WINAPI UrlEscapeA( 951 LPCSTR pszUrl, 952 LPSTR pszEscaped, 953 LPDWORD pcchEscaped, 954 DWORD dwFlags) 955 { 956 WCHAR bufW[INTERNET_MAX_URL_LENGTH]; 957 WCHAR *escapedW = bufW; 958 UNICODE_STRING urlW; 959 HRESULT ret; 960 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA; 961 962 if (!pszEscaped || !pcchEscaped || !*pcchEscaped) 963 return E_INVALIDARG; 964 965 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl)) 966 return E_INVALIDARG; 967 if(dwFlags & URL_ESCAPE_AS_UTF8) { 968 RtlFreeUnicodeString(&urlW); 969 return E_NOTIMPL; 970 } 971 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) { 972 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); 973 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags); 974 } 975 if(ret == S_OK) { 976 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR)); 977 if(*pcchEscaped > lenA) { 978 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR)); 979 pszEscaped[lenA] = 0; 980 *pcchEscaped = lenA; 981 } else { 982 *pcchEscaped = lenA + 1; 983 ret = E_POINTER; 984 } 985 } 986 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW); 987 RtlFreeUnicodeString(&urlW); 988 return ret; 989 } 990 991 #define WINE_URL_BASH_AS_SLASH 0x01 992 #define WINE_URL_COLLAPSE_SLASHES 0x02 993 #define WINE_URL_ESCAPE_SLASH 0x04 994 #define WINE_URL_ESCAPE_HASH 0x08 995 #define WINE_URL_ESCAPE_QUESTION 0x10 996 #define WINE_URL_STOP_ON_HASH 0x20 997 #define WINE_URL_STOP_ON_QUESTION 0x40 998 999 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD flags, DWORD int_flags) 1000 { 1001 if (flags & URL_ESCAPE_SPACES_ONLY) 1002 return ch == ' '; 1003 1004 if ((flags & URL_ESCAPE_PERCENT) && (ch == '%')) 1005 return TRUE; 1006 1007 if ((flags & URL_ESCAPE_AS_UTF8) && (ch >= 0x80)) 1008 return TRUE; 1009 1010 if (ch <= 31 || (ch >= 127 && ch <= 255) ) 1011 return TRUE; 1012 1013 if (isalnumW(ch)) 1014 return FALSE; 1015 1016 switch (ch) { 1017 case ' ': 1018 case '<': 1019 case '>': 1020 case '\"': 1021 case '{': 1022 case '}': 1023 case '|': 1024 case '\\': 1025 case '^': 1026 case ']': 1027 case '[': 1028 case '`': 1029 case '&': 1030 return TRUE; 1031 case '/': 1032 return !!(int_flags & WINE_URL_ESCAPE_SLASH); 1033 case '?': 1034 return !!(int_flags & WINE_URL_ESCAPE_QUESTION); 1035 case '#': 1036 return !!(int_flags & WINE_URL_ESCAPE_HASH); 1037 default: 1038 return FALSE; 1039 } 1040 } 1041 1042 1043 /************************************************************************* 1044 * UrlEscapeW [SHLWAPI.@] 1045 * 1046 * Converts unsafe characters in a Url into escape sequences. 1047 * 1048 * PARAMS 1049 * pszUrl [I] Url to modify 1050 * pszEscaped [O] Destination for modified Url 1051 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped 1052 * dwFlags [I] URL_ flags from "shlwapi.h" 1053 * 1054 * RETURNS 1055 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped 1056 * contains its length. 1057 * Failure: E_POINTER, if pszEscaped is not large enough. In this case 1058 * pcchEscaped is set to the required length. 1059 * 1060 * Converts unsafe characters into their escape sequences. 1061 * 1062 * NOTES 1063 * - By default this function stops converting at the first '?' or 1064 * '#' character. 1065 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are 1066 * converted, but the conversion continues past a '?' or '#'. 1067 * - Note that this function did not work well (or at all) in shlwapi version 4. 1068 * 1069 * BUGS 1070 * Only the following flags are implemented: 1071 *| URL_ESCAPE_SPACES_ONLY 1072 *| URL_DONT_ESCAPE_EXTRA_INFO 1073 *| URL_ESCAPE_SEGMENT_ONLY 1074 *| URL_ESCAPE_PERCENT 1075 */ 1076 HRESULT WINAPI UrlEscapeW( 1077 LPCWSTR pszUrl, 1078 LPWSTR pszEscaped, 1079 LPDWORD pcchEscaped, 1080 DWORD dwFlags) 1081 { 1082 LPCWSTR src; 1083 DWORD needed = 0, ret; 1084 BOOL stop_escaping = FALSE; 1085 WCHAR next[12], *dst, *dst_ptr; 1086 INT i, len; 1087 PARSEDURLW parsed_url; 1088 DWORD int_flags; 1089 DWORD slashes = 0; 1090 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0}; 1091 1092 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl), 1093 pszEscaped, pcchEscaped, dwFlags); 1094 1095 if(!pszUrl || !pcchEscaped || !pszEscaped || *pcchEscaped == 0) 1096 return E_INVALIDARG; 1097 1098 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY | 1099 URL_ESCAPE_SEGMENT_ONLY | 1100 URL_DONT_ESCAPE_EXTRA_INFO | 1101 URL_ESCAPE_PERCENT | 1102 URL_ESCAPE_AS_UTF8)) 1103 FIXME("Unimplemented flags: %08x\n", dwFlags); 1104 1105 dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR)); 1106 if(!dst_ptr) 1107 return E_OUTOFMEMORY; 1108 1109 /* fix up flags */ 1110 if (dwFlags & URL_ESCAPE_SPACES_ONLY) 1111 /* if SPACES_ONLY specified, reset the other controls */ 1112 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO | 1113 URL_ESCAPE_PERCENT | 1114 URL_ESCAPE_SEGMENT_ONLY); 1115 1116 else 1117 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */ 1118 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO; 1119 1120 1121 int_flags = 0; 1122 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) { 1123 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH; 1124 } else { 1125 parsed_url.cbSize = sizeof(parsed_url); 1126 if(ParseURLW(pszUrl, &parsed_url) != S_OK) 1127 parsed_url.nScheme = URL_SCHEME_INVALID; 1128 1129 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol)); 1130 1131 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) 1132 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION; 1133 1134 switch(parsed_url.nScheme) { 1135 case URL_SCHEME_FILE: 1136 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH; 1137 int_flags &= ~WINE_URL_STOP_ON_HASH; 1138 break; 1139 1140 case URL_SCHEME_HTTP: 1141 case URL_SCHEME_HTTPS: 1142 int_flags |= WINE_URL_BASH_AS_SLASH; 1143 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\') 1144 int_flags |= WINE_URL_ESCAPE_SLASH; 1145 break; 1146 1147 case URL_SCHEME_MAILTO: 1148 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH; 1149 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH); 1150 break; 1151 1152 case URL_SCHEME_INVALID: 1153 break; 1154 1155 case URL_SCHEME_FTP: 1156 default: 1157 if(parsed_url.pszSuffix[0] != '/') 1158 int_flags |= WINE_URL_ESCAPE_SLASH; 1159 break; 1160 } 1161 } 1162 1163 for(src = pszUrl; *src; ) { 1164 WCHAR cur = *src; 1165 len = 0; 1166 1167 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) { 1168 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1; 1169 while(cur == '/' || cur == '\\') { 1170 slashes++; 1171 cur = *++src; 1172 } 1173 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */ 1174 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\') 1175 src += localhost_len + 1; 1176 slashes = 3; 1177 } 1178 1179 switch(slashes) { 1180 case 1: 1181 case 3: 1182 next[0] = next[1] = next[2] = '/'; 1183 len = 3; 1184 break; 1185 case 0: 1186 len = 0; 1187 break; 1188 default: 1189 next[0] = next[1] = '/'; 1190 len = 2; 1191 break; 1192 } 1193 } 1194 if(len == 0) { 1195 1196 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH)) 1197 stop_escaping = TRUE; 1198 1199 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION)) 1200 stop_escaping = TRUE; 1201 1202 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/'; 1203 1204 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) { 1205 if(dwFlags & URL_ESCAPE_AS_UTF8) { 1206 char utf[16]; 1207 1208 if ((cur >= 0xd800 && cur <= 0xdfff) && 1209 (src[1] >= 0xdc00 && src[1] <= 0xdfff)) 1210 { 1211 #ifdef __REACTOS__ 1212 len = WideCharToMultiByte( CP_UTF8, 0, src, 2, 1213 utf, sizeof(utf), NULL, NULL ); 1214 #else 1215 len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, src, 2, 1216 utf, sizeof(utf), NULL, NULL ); 1217 #endif 1218 src++; 1219 } 1220 else 1221 #ifdef __REACTOS__ 1222 len = WideCharToMultiByte( CP_UTF8, 0, &cur, 1, 1223 utf, sizeof(utf), NULL, NULL ); 1224 #else 1225 len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, &cur, 1, 1226 utf, sizeof(utf), NULL, NULL ); 1227 #endif 1228 1229 if (!len) { 1230 utf[0] = 0xef; 1231 utf[1] = 0xbf; 1232 utf[2] = 0xbd; 1233 len = 3; 1234 } 1235 1236 for(i = 0; i < len; i++) { 1237 next[i*3+0] = '%'; 1238 next[i*3+1] = hexDigits[(utf[i] >> 4) & 0xf]; 1239 next[i*3+2] = hexDigits[utf[i] & 0xf]; 1240 } 1241 len *= 3; 1242 } else { 1243 next[0] = '%'; 1244 next[1] = hexDigits[(cur >> 4) & 0xf]; 1245 next[2] = hexDigits[cur & 0xf]; 1246 len = 3; 1247 } 1248 } else { 1249 next[0] = cur; 1250 len = 1; 1251 } 1252 src++; 1253 } 1254 1255 if(needed + len <= *pcchEscaped) { 1256 memcpy(dst, next, len*sizeof(WCHAR)); 1257 dst += len; 1258 } 1259 needed += len; 1260 } 1261 1262 if(needed < *pcchEscaped) { 1263 *dst = '\0'; 1264 memcpy(pszEscaped, dst_ptr, (needed+1)*sizeof(WCHAR)); 1265 1266 ret = S_OK; 1267 } else { 1268 needed++; /* add one for the '\0' */ 1269 ret = E_POINTER; 1270 } 1271 *pcchEscaped = needed; 1272 1273 HeapFree(GetProcessHeap(), 0, dst_ptr); 1274 return ret; 1275 } 1276 1277 1278 /************************************************************************* 1279 * UrlUnescapeA [SHLWAPI.@] 1280 * 1281 * Converts Url escape sequences back to ordinary characters. 1282 * 1283 * PARAMS 1284 * pszUrl [I/O] Url to convert 1285 * pszUnescaped [O] Destination for converted Url 1286 * pcchUnescaped [I/O] Size of output string 1287 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h" 1288 * 1289 * RETURNS 1290 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if 1291 * dwFlags includes URL_ESCAPE_INPLACE. 1292 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In 1293 * this case pcchUnescaped is set to the size required. 1294 * NOTES 1295 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at 1296 * the first occurrence of either a '?' or '#' character. 1297 */ 1298 HRESULT WINAPI UrlUnescapeA( 1299 LPSTR pszUrl, 1300 LPSTR pszUnescaped, 1301 LPDWORD pcchUnescaped, 1302 DWORD dwFlags) 1303 { 1304 char *dst, next; 1305 LPCSTR src; 1306 HRESULT ret; 1307 DWORD needed; 1308 BOOL stop_unescaping = FALSE; 1309 1310 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped, 1311 pcchUnescaped, dwFlags); 1312 1313 if (!pszUrl) return E_INVALIDARG; 1314 1315 if(dwFlags & URL_UNESCAPE_INPLACE) 1316 dst = pszUrl; 1317 else 1318 { 1319 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG; 1320 dst = pszUnescaped; 1321 } 1322 1323 for(src = pszUrl, needed = 0; *src; src++, needed++) { 1324 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO && 1325 (*src == '#' || *src == '?')) { 1326 stop_unescaping = TRUE; 1327 next = *src; 1328 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2)) 1329 && stop_unescaping == FALSE) { 1330 INT ih; 1331 char buf[3]; 1332 memcpy(buf, src + 1, 2); 1333 buf[2] = '\0'; 1334 ih = strtol(buf, NULL, 16); 1335 next = (CHAR) ih; 1336 src += 2; /* Advance to end of escape */ 1337 } else 1338 next = *src; 1339 1340 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) 1341 *dst++ = next; 1342 } 1343 1344 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) { 1345 *dst = '\0'; 1346 ret = S_OK; 1347 } else { 1348 needed++; /* add one for the '\0' */ 1349 ret = E_POINTER; 1350 } 1351 if(!(dwFlags & URL_UNESCAPE_INPLACE)) 1352 *pcchUnescaped = needed; 1353 1354 if (ret == S_OK) { 1355 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ? 1356 debugstr_a(pszUrl) : debugstr_a(pszUnescaped)); 1357 } 1358 1359 return ret; 1360 } 1361 1362 /************************************************************************* 1363 * UrlUnescapeW [SHLWAPI.@] 1364 * 1365 * See UrlUnescapeA. 1366 */ 1367 HRESULT WINAPI UrlUnescapeW( 1368 LPWSTR pszUrl, 1369 LPWSTR pszUnescaped, 1370 LPDWORD pcchUnescaped, 1371 DWORD dwFlags) 1372 { 1373 WCHAR *dst, next; 1374 LPCWSTR src; 1375 HRESULT ret; 1376 DWORD needed; 1377 BOOL stop_unescaping = FALSE; 1378 1379 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped, 1380 pcchUnescaped, dwFlags); 1381 1382 if(!pszUrl) return E_INVALIDARG; 1383 1384 if(dwFlags & URL_UNESCAPE_INPLACE) 1385 dst = pszUrl; 1386 else 1387 { 1388 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG; 1389 dst = pszUnescaped; 1390 } 1391 1392 for(src = pszUrl, needed = 0; *src; src++, needed++) { 1393 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO && 1394 (*src == '#' || *src == '?')) { 1395 stop_unescaping = TRUE; 1396 next = *src; 1397 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2)) 1398 && stop_unescaping == FALSE) { 1399 INT ih; 1400 WCHAR buf[5] = {'0','x',0}; 1401 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR)); 1402 buf[4] = 0; 1403 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih); 1404 next = (WCHAR) ih; 1405 src += 2; /* Advance to end of escape */ 1406 } else 1407 next = *src; 1408 1409 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) 1410 *dst++ = next; 1411 } 1412 1413 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) { 1414 *dst = '\0'; 1415 ret = S_OK; 1416 } else { 1417 needed++; /* add one for the '\0' */ 1418 ret = E_POINTER; 1419 } 1420 if(!(dwFlags & URL_UNESCAPE_INPLACE)) 1421 *pcchUnescaped = needed; 1422 1423 if (ret == S_OK) { 1424 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ? 1425 debugstr_w(pszUrl) : debugstr_w(pszUnescaped)); 1426 } 1427 1428 return ret; 1429 } 1430 1431 /************************************************************************* 1432 * UrlGetLocationA [SHLWAPI.@] 1433 * 1434 * Get the location from a Url. 1435 * 1436 * PARAMS 1437 * pszUrl [I] Url to get the location from 1438 * 1439 * RETURNS 1440 * A pointer to the start of the location in pszUrl, or NULL if there is 1441 * no location. 1442 * 1443 * NOTES 1444 * - MSDN erroneously states that "The location is the segment of the Url 1445 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll 1446 * stop at '?' and always return a NULL in this case. 1447 * - MSDN also erroneously states that "If a file URL has a query string, 1448 * the returned string is the query string". In all tested cases, if the 1449 * Url starts with "fi" then a NULL is returned. V5 gives the following results: 1450 *| Result Url 1451 *| ------ --- 1452 *| NULL file://aa/b/cd#hohoh 1453 *| #hohoh http://aa/b/cd#hohoh 1454 *| NULL fi://aa/b/cd#hohoh 1455 *| #hohoh ff://aa/b/cd#hohoh 1456 */ 1457 LPCSTR WINAPI UrlGetLocationA( 1458 LPCSTR pszUrl) 1459 { 1460 PARSEDURLA base; 1461 DWORD res1; 1462 1463 base.cbSize = sizeof(base); 1464 res1 = ParseURLA(pszUrl, &base); 1465 if (res1) return NULL; /* invalid scheme */ 1466 1467 /* if scheme is file: then never return pointer */ 1468 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL; 1469 1470 /* Look for '#' and return its addr */ 1471 return strchr(base.pszSuffix, '#'); 1472 } 1473 1474 /************************************************************************* 1475 * UrlGetLocationW [SHLWAPI.@] 1476 * 1477 * See UrlGetLocationA. 1478 */ 1479 LPCWSTR WINAPI UrlGetLocationW( 1480 LPCWSTR pszUrl) 1481 { 1482 PARSEDURLW base; 1483 DWORD res1; 1484 1485 base.cbSize = sizeof(base); 1486 res1 = ParseURLW(pszUrl, &base); 1487 if (res1) return NULL; /* invalid scheme */ 1488 1489 /* if scheme is file: then never return pointer */ 1490 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL; 1491 1492 /* Look for '#' and return its addr */ 1493 return strchrW(base.pszSuffix, '#'); 1494 } 1495 1496 /************************************************************************* 1497 * UrlCompareA [SHLWAPI.@] 1498 * 1499 * Compare two Urls. 1500 * 1501 * PARAMS 1502 * pszUrl1 [I] First Url to compare 1503 * pszUrl2 [I] Url to compare to pszUrl1 1504 * fIgnoreSlash [I] TRUE = compare only up to a final slash 1505 * 1506 * RETURNS 1507 * less than zero, zero, or greater than zero indicating pszUrl2 is greater 1508 * than, equal to, or less than pszUrl1 respectively. 1509 */ 1510 INT WINAPI UrlCompareA( 1511 LPCSTR pszUrl1, 1512 LPCSTR pszUrl2, 1513 BOOL fIgnoreSlash) 1514 { 1515 INT ret, len, len1, len2; 1516 1517 if (!fIgnoreSlash) 1518 return strcmp(pszUrl1, pszUrl2); 1519 len1 = strlen(pszUrl1); 1520 if (pszUrl1[len1-1] == '/') len1--; 1521 len2 = strlen(pszUrl2); 1522 if (pszUrl2[len2-1] == '/') len2--; 1523 if (len1 == len2) 1524 return strncmp(pszUrl1, pszUrl2, len1); 1525 len = min(len1, len2); 1526 ret = strncmp(pszUrl1, pszUrl2, len); 1527 if (ret) return ret; 1528 if (len1 > len2) return 1; 1529 return -1; 1530 } 1531 1532 /************************************************************************* 1533 * UrlCompareW [SHLWAPI.@] 1534 * 1535 * See UrlCompareA. 1536 */ 1537 INT WINAPI UrlCompareW( 1538 LPCWSTR pszUrl1, 1539 LPCWSTR pszUrl2, 1540 BOOL fIgnoreSlash) 1541 { 1542 INT ret; 1543 size_t len, len1, len2; 1544 1545 if (!fIgnoreSlash) 1546 return strcmpW(pszUrl1, pszUrl2); 1547 len1 = strlenW(pszUrl1); 1548 if (pszUrl1[len1-1] == '/') len1--; 1549 len2 = strlenW(pszUrl2); 1550 if (pszUrl2[len2-1] == '/') len2--; 1551 if (len1 == len2) 1552 return strncmpW(pszUrl1, pszUrl2, len1); 1553 len = min(len1, len2); 1554 ret = strncmpW(pszUrl1, pszUrl2, len); 1555 if (ret) return ret; 1556 if (len1 > len2) return 1; 1557 return -1; 1558 } 1559 1560 /************************************************************************* 1561 * HashData [SHLWAPI.@] 1562 * 1563 * Hash an input block into a variable sized digest. 1564 * 1565 * PARAMS 1566 * lpSrc [I] Input block 1567 * nSrcLen [I] Length of lpSrc 1568 * lpDest [I] Output for hash digest 1569 * nDestLen [I] Length of lpDest 1570 * 1571 * RETURNS 1572 * Success: TRUE. lpDest is filled with the computed hash value. 1573 * Failure: FALSE, if any argument is invalid. 1574 */ 1575 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen, 1576 unsigned char *lpDest, DWORD nDestLen) 1577 { 1578 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1; 1579 1580 if (!lpSrc || !lpDest) 1581 return E_INVALIDARG; 1582 1583 while (destCount >= 0) 1584 { 1585 lpDest[destCount] = (destCount & 0xff); 1586 destCount--; 1587 } 1588 1589 while (srcCount >= 0) 1590 { 1591 destCount = nDestLen - 1; 1592 while (destCount >= 0) 1593 { 1594 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]]; 1595 destCount--; 1596 } 1597 srcCount--; 1598 } 1599 return S_OK; 1600 } 1601 1602 /************************************************************************* 1603 * UrlHashA [SHLWAPI.@] 1604 * 1605 * Produce a Hash from a Url. 1606 * 1607 * PARAMS 1608 * pszUrl [I] Url to hash 1609 * lpDest [O] Destinationh for hash 1610 * nDestLen [I] Length of lpDest 1611 * 1612 * RETURNS 1613 * Success: S_OK. lpDest is filled with the computed hash value. 1614 * Failure: E_INVALIDARG, if any argument is invalid. 1615 */ 1616 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen) 1617 { 1618 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen)) 1619 return E_INVALIDARG; 1620 1621 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen); 1622 return S_OK; 1623 } 1624 1625 /************************************************************************* 1626 * UrlHashW [SHLWAPI.@] 1627 * 1628 * See UrlHashA. 1629 */ 1630 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen) 1631 { 1632 char szUrl[MAX_PATH]; 1633 1634 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen); 1635 1636 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen)) 1637 return E_INVALIDARG; 1638 1639 /* Win32 hashes the data as an ASCII string, presumably so that both A+W 1640 * return the same digests for the same URL. 1641 */ 1642 WideCharToMultiByte(CP_ACP, 0, pszUrl, -1, szUrl, MAX_PATH, NULL, NULL); 1643 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen); 1644 return S_OK; 1645 } 1646 1647 /************************************************************************* 1648 * UrlApplySchemeA [SHLWAPI.@] 1649 * 1650 * Apply a scheme to a Url. 1651 * 1652 * PARAMS 1653 * pszIn [I] Url to apply scheme to 1654 * pszOut [O] Destination for modified Url 1655 * pcchOut [I/O] Length of pszOut/destination for length of pszOut 1656 * dwFlags [I] URL_ flags from "shlwapi.h" 1657 * 1658 * RETURNS 1659 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length. 1660 * Failure: An HRESULT error code describing the error. 1661 */ 1662 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags) 1663 { 1664 LPWSTR in, out; 1665 HRESULT ret; 1666 DWORD len; 1667 1668 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn), 1669 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags); 1670 1671 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG; 1672 1673 in = HeapAlloc(GetProcessHeap(), 0, 1674 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR)); 1675 out = in + INTERNET_MAX_URL_LENGTH; 1676 1677 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH); 1678 len = INTERNET_MAX_URL_LENGTH; 1679 1680 ret = UrlApplySchemeW(in, out, &len, dwFlags); 1681 if (ret != S_OK) { 1682 HeapFree(GetProcessHeap(), 0, in); 1683 return ret; 1684 } 1685 1686 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL); 1687 if (len > *pcchOut) { 1688 ret = E_POINTER; 1689 goto cleanup; 1690 } 1691 1692 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL); 1693 len--; 1694 1695 cleanup: 1696 *pcchOut = len; 1697 HeapFree(GetProcessHeap(), 0, in); 1698 return ret; 1699 } 1700 1701 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut) 1702 { 1703 HKEY newkey; 1704 BOOL j; 1705 INT index; 1706 DWORD value_len, data_len, dwType, i; 1707 WCHAR reg_path[MAX_PATH]; 1708 WCHAR value[MAX_PATH], data[MAX_PATH]; 1709 WCHAR Wxx, Wyy; 1710 1711 MultiByteToWideChar(CP_ACP, 0, 1712 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes", 1713 -1, reg_path, MAX_PATH); 1714 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey); 1715 index = 0; 1716 while(value_len = data_len = MAX_PATH, 1717 RegEnumValueW(newkey, index, value, &value_len, 1718 0, &dwType, (LPVOID)data, &data_len) == 0) { 1719 TRACE("guess %d %s is %s\n", 1720 index, debugstr_w(value), debugstr_w(data)); 1721 1722 j = FALSE; 1723 for(i=0; i<value_len; i++) { 1724 Wxx = pszIn[i]; 1725 Wyy = value[i]; 1726 /* remember that TRUE is not-equal */ 1727 j = ChrCmpIW(Wxx, Wyy); 1728 if (j) break; 1729 } 1730 if ((i == value_len) && !j) { 1731 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) { 1732 *pcchOut = strlenW(data) + strlenW(pszIn) + 1; 1733 RegCloseKey(newkey); 1734 return E_POINTER; 1735 } 1736 strcpyW(pszOut, data); 1737 strcatW(pszOut, pszIn); 1738 *pcchOut = strlenW(pszOut); 1739 TRACE("matched and set to %s\n", debugstr_w(pszOut)); 1740 RegCloseKey(newkey); 1741 return S_OK; 1742 } 1743 index++; 1744 } 1745 RegCloseKey(newkey); 1746 return E_FAIL; 1747 } 1748 1749 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl) 1750 { 1751 DWORD needed; 1752 HRESULT ret = S_OK; 1753 WCHAR *pszNewUrl; 1754 WCHAR file_colonW[] = {'f','i','l','e',':',0}; 1755 WCHAR three_slashesW[] = {'/','/','/',0}; 1756 PARSEDURLW parsed_url; 1757 1758 parsed_url.cbSize = sizeof(parsed_url); 1759 if(ParseURLW(pszPath, &parsed_url) == S_OK) { 1760 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) { 1761 needed = strlenW(pszPath); 1762 if (needed >= *pcchUrl) { 1763 *pcchUrl = needed + 1; 1764 return E_POINTER; 1765 } else { 1766 *pcchUrl = needed; 1767 return S_FALSE; 1768 } 1769 } 1770 } 1771 1772 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */ 1773 strcpyW(pszNewUrl, file_colonW); 1774 if(isalphaW(pszPath[0]) && pszPath[1] == ':') 1775 strcatW(pszNewUrl, three_slashesW); 1776 strcatW(pszNewUrl, pszPath); 1777 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT); 1778 HeapFree(GetProcessHeap(), 0, pszNewUrl); 1779 return ret; 1780 } 1781 1782 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut) 1783 { 1784 HKEY newkey; 1785 DWORD data_len, dwType; 1786 WCHAR data[MAX_PATH]; 1787 1788 static const WCHAR prefix_keyW[] = 1789 {'S','o','f','t','w','a','r','e', 1790 '\\','M','i','c','r','o','s','o','f','t', 1791 '\\','W','i','n','d','o','w','s', 1792 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n', 1793 '\\','U','R','L', 1794 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0}; 1795 1796 /* get and prepend default */ 1797 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey); 1798 data_len = sizeof(data); 1799 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len); 1800 RegCloseKey(newkey); 1801 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) { 1802 *pcchOut = strlenW(data) + strlenW(pszIn) + 1; 1803 return E_POINTER; 1804 } 1805 strcpyW(pszOut, data); 1806 strcatW(pszOut, pszIn); 1807 *pcchOut = strlenW(pszOut); 1808 TRACE("used default %s\n", debugstr_w(pszOut)); 1809 return S_OK; 1810 } 1811 1812 /************************************************************************* 1813 * UrlApplySchemeW [SHLWAPI.@] 1814 * 1815 * See UrlApplySchemeA. 1816 */ 1817 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags) 1818 { 1819 PARSEDURLW in_scheme; 1820 DWORD res1; 1821 HRESULT ret; 1822 1823 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn), 1824 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags); 1825 1826 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG; 1827 1828 if (dwFlags & URL_APPLY_GUESSFILE) { 1829 if (*pcchOut > 1 && ':' == pszIn[1]) { 1830 res1 = *pcchOut; 1831 ret = URL_CreateFromPath(pszIn, pszOut, &res1); 1832 if (ret == S_OK || ret == E_POINTER){ 1833 *pcchOut = res1; 1834 return ret; 1835 } 1836 else if (ret == S_FALSE) 1837 { 1838 return ret; 1839 } 1840 } 1841 } 1842 1843 in_scheme.cbSize = sizeof(in_scheme); 1844 /* See if the base has a scheme */ 1845 res1 = ParseURLW(pszIn, &in_scheme); 1846 if (res1) { 1847 /* no scheme in input, need to see if we need to guess */ 1848 if (dwFlags & URL_APPLY_GUESSSCHEME) { 1849 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL) 1850 return ret; 1851 } 1852 } 1853 1854 /* If we are here, then either invalid scheme, 1855 * or no scheme and can't/failed guess. 1856 */ 1857 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) || 1858 ((res1 != 0)) ) && 1859 (dwFlags & URL_APPLY_DEFAULT)) { 1860 /* find and apply default scheme */ 1861 return URL_ApplyDefault(pszIn, pszOut, pcchOut); 1862 } 1863 1864 return S_FALSE; 1865 } 1866 1867 /************************************************************************* 1868 * UrlIsA [SHLWAPI.@] 1869 * 1870 * Determine if a Url is of a certain class. 1871 * 1872 * PARAMS 1873 * pszUrl [I] Url to check 1874 * Urlis [I] URLIS_ constant from "shlwapi.h" 1875 * 1876 * RETURNS 1877 * TRUE if pszUrl belongs to the class type in Urlis. 1878 * FALSE Otherwise. 1879 */ 1880 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis) 1881 { 1882 PARSEDURLA base; 1883 DWORD res1; 1884 LPCSTR last; 1885 1886 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis); 1887 1888 if(!pszUrl) 1889 return FALSE; 1890 1891 switch (Urlis) { 1892 1893 case URLIS_OPAQUE: 1894 base.cbSize = sizeof(base); 1895 res1 = ParseURLA(pszUrl, &base); 1896 if (res1) return FALSE; /* invalid scheme */ 1897 switch (base.nScheme) 1898 { 1899 case URL_SCHEME_MAILTO: 1900 case URL_SCHEME_SHELL: 1901 case URL_SCHEME_JAVASCRIPT: 1902 case URL_SCHEME_VBSCRIPT: 1903 case URL_SCHEME_ABOUT: 1904 return TRUE; 1905 } 1906 return FALSE; 1907 1908 case URLIS_FILEURL: 1909 return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5, 1910 "file:", 5) == CSTR_EQUAL); 1911 1912 case URLIS_DIRECTORY: 1913 last = pszUrl + strlen(pszUrl) - 1; 1914 return (last >= pszUrl && (*last == '/' || *last == '\\' )); 1915 1916 case URLIS_URL: 1917 return PathIsURLA(pszUrl); 1918 1919 case URLIS_NOHISTORY: 1920 case URLIS_APPLIABLE: 1921 case URLIS_HASQUERY: 1922 default: 1923 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis); 1924 } 1925 return FALSE; 1926 } 1927 1928 /************************************************************************* 1929 * UrlIsW [SHLWAPI.@] 1930 * 1931 * See UrlIsA. 1932 */ 1933 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis) 1934 { 1935 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 }; 1936 PARSEDURLW base; 1937 DWORD res1; 1938 LPCWSTR last; 1939 1940 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis); 1941 1942 if(!pszUrl) 1943 return FALSE; 1944 1945 switch (Urlis) { 1946 1947 case URLIS_OPAQUE: 1948 base.cbSize = sizeof(base); 1949 res1 = ParseURLW(pszUrl, &base); 1950 if (res1) return FALSE; /* invalid scheme */ 1951 switch (base.nScheme) 1952 { 1953 case URL_SCHEME_MAILTO: 1954 case URL_SCHEME_SHELL: 1955 case URL_SCHEME_JAVASCRIPT: 1956 case URL_SCHEME_VBSCRIPT: 1957 case URL_SCHEME_ABOUT: 1958 return TRUE; 1959 } 1960 return FALSE; 1961 1962 case URLIS_FILEURL: 1963 return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5, 1964 file_colon, 5) == CSTR_EQUAL); 1965 1966 case URLIS_DIRECTORY: 1967 last = pszUrl + strlenW(pszUrl) - 1; 1968 return (last >= pszUrl && (*last == '/' || *last == '\\')); 1969 1970 case URLIS_URL: 1971 return PathIsURLW(pszUrl); 1972 1973 case URLIS_NOHISTORY: 1974 case URLIS_APPLIABLE: 1975 case URLIS_HASQUERY: 1976 default: 1977 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis); 1978 } 1979 return FALSE; 1980 } 1981 1982 /************************************************************************* 1983 * UrlIsNoHistoryA [SHLWAPI.@] 1984 * 1985 * Determine if a Url should not be stored in the users history list. 1986 * 1987 * PARAMS 1988 * pszUrl [I] Url to check 1989 * 1990 * RETURNS 1991 * TRUE, if pszUrl should be excluded from the history list, 1992 * FALSE otherwise. 1993 */ 1994 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl) 1995 { 1996 return UrlIsA(pszUrl, URLIS_NOHISTORY); 1997 } 1998 1999 /************************************************************************* 2000 * UrlIsNoHistoryW [SHLWAPI.@] 2001 * 2002 * See UrlIsNoHistoryA. 2003 */ 2004 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl) 2005 { 2006 return UrlIsW(pszUrl, URLIS_NOHISTORY); 2007 } 2008 2009 /************************************************************************* 2010 * UrlIsOpaqueA [SHLWAPI.@] 2011 * 2012 * Determine if a Url is opaque. 2013 * 2014 * PARAMS 2015 * pszUrl [I] Url to check 2016 * 2017 * RETURNS 2018 * TRUE if pszUrl is opaque, 2019 * FALSE Otherwise. 2020 * 2021 * NOTES 2022 * An opaque Url is one that does not start with "<protocol>://". 2023 */ 2024 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl) 2025 { 2026 return UrlIsA(pszUrl, URLIS_OPAQUE); 2027 } 2028 2029 /************************************************************************* 2030 * UrlIsOpaqueW [SHLWAPI.@] 2031 * 2032 * See UrlIsOpaqueA. 2033 */ 2034 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl) 2035 { 2036 return UrlIsW(pszUrl, URLIS_OPAQUE); 2037 } 2038 2039 /************************************************************************* 2040 * Scans for characters of type "type" and when not matching found, 2041 * returns pointer to it and length in size. 2042 * 2043 * Characters tested based on RFC 1738 2044 */ 2045 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type) 2046 { 2047 static DWORD alwayszero = 0; 2048 BOOL cont = TRUE; 2049 2050 *size = 0; 2051 2052 switch(type){ 2053 2054 case SCHEME: 2055 while (cont) { 2056 if ( (islowerW(*start) && isalphaW(*start)) || 2057 isdigitW(*start) || 2058 (*start == '+') || 2059 (*start == '-') || 2060 (*start == '.')) { 2061 start++; 2062 (*size)++; 2063 } 2064 else 2065 cont = FALSE; 2066 } 2067 2068 if(*start != ':') 2069 *size = 0; 2070 2071 break; 2072 2073 case USERPASS: 2074 while (cont) { 2075 if ( isalphaW(*start) || 2076 isdigitW(*start) || 2077 /* user/password only characters */ 2078 (*start == ';') || 2079 (*start == '?') || 2080 (*start == '&') || 2081 (*start == '=') || 2082 /* *extra* characters */ 2083 (*start == '!') || 2084 (*start == '*') || 2085 (*start == '\'') || 2086 (*start == '(') || 2087 (*start == ')') || 2088 (*start == ',') || 2089 /* *safe* characters */ 2090 (*start == '$') || 2091 (*start == '_') || 2092 (*start == '+') || 2093 (*start == '-') || 2094 (*start == '.') || 2095 (*start == ' ')) { 2096 start++; 2097 (*size)++; 2098 } else if (*start == '%') { 2099 if (isxdigitW(*(start+1)) && 2100 isxdigitW(*(start+2))) { 2101 start += 3; 2102 *size += 3; 2103 } else 2104 cont = FALSE; 2105 } else 2106 cont = FALSE; 2107 } 2108 break; 2109 2110 case PORT: 2111 while (cont) { 2112 if (isdigitW(*start)) { 2113 start++; 2114 (*size)++; 2115 } 2116 else 2117 cont = FALSE; 2118 } 2119 break; 2120 2121 case HOST: 2122 while (cont) { 2123 if (isalnumW(*start) || 2124 (*start == '-') || 2125 (*start == '.') || 2126 (*start == ' ') || 2127 (*start == '*') ) { 2128 start++; 2129 (*size)++; 2130 } 2131 else 2132 cont = FALSE; 2133 } 2134 break; 2135 default: 2136 FIXME("unknown type %d\n", type); 2137 return (LPWSTR)&alwayszero; 2138 } 2139 /* TRACE("scanned %d characters next char %p<%c>\n", 2140 *size, start, *start); */ 2141 return start; 2142 } 2143 2144 /************************************************************************* 2145 * Attempt to parse URL into pieces. 2146 */ 2147 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl) 2148 { 2149 LPCWSTR work; 2150 2151 memset(pl, 0, sizeof(WINE_PARSE_URL)); 2152 pl->pScheme = pszUrl; 2153 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME); 2154 if (!*work || (*work != ':')) goto ErrorExit; 2155 work++; 2156 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit; 2157 pl->pUserName = work + 2; 2158 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS); 2159 if (*work == ':' ) { 2160 /* parse password */ 2161 work++; 2162 pl->pPassword = work; 2163 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS); 2164 if (*work != '@') { 2165 /* what we just parsed must be the hostname and port 2166 * so reset pointers and clear then let it parse */ 2167 pl->szUserName = pl->szPassword = 0; 2168 work = pl->pUserName - 1; 2169 pl->pUserName = pl->pPassword = 0; 2170 } 2171 } else if (*work == '@') { 2172 /* no password */ 2173 pl->szPassword = 0; 2174 pl->pPassword = 0; 2175 } else if (!*work || (*work == '/') || (*work == '.')) { 2176 /* what was parsed was hostname, so reset pointers and let it parse */ 2177 pl->szUserName = pl->szPassword = 0; 2178 work = pl->pUserName - 1; 2179 pl->pUserName = pl->pPassword = 0; 2180 } else goto ErrorExit; 2181 2182 /* now start parsing hostname or hostnumber */ 2183 work++; 2184 pl->pHostName = work; 2185 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST); 2186 if (*work == ':') { 2187 /* parse port */ 2188 work++; 2189 pl->pPort = work; 2190 work = URL_ScanID(pl->pPort, &pl->szPort, PORT); 2191 } 2192 if (*work == '/') { 2193 /* see if query string */ 2194 pl->pQuery = strchrW(work, '?'); 2195 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery); 2196 } 2197 SuccessExit: 2198 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n", 2199 pl->pScheme, pl->szScheme, 2200 pl->pUserName, pl->szUserName, 2201 pl->pPassword, pl->szPassword, 2202 pl->pHostName, pl->szHostName, 2203 pl->pPort, pl->szPort, 2204 pl->pQuery, pl->szQuery); 2205 return S_OK; 2206 ErrorExit: 2207 FIXME("failed to parse %s\n", debugstr_w(pszUrl)); 2208 return E_INVALIDARG; 2209 } 2210 2211 /************************************************************************* 2212 * UrlGetPartA [SHLWAPI.@] 2213 * 2214 * Retrieve part of a Url. 2215 * 2216 * PARAMS 2217 * pszIn [I] Url to parse 2218 * pszOut [O] Destination for part of pszIn requested 2219 * pcchOut [I] Size of pszOut 2220 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise 2221 * needed size of pszOut INCLUDING '\0'. 2222 * dwPart [I] URL_PART_ enum from "shlwapi.h" 2223 * dwFlags [I] URL_ flags from "shlwapi.h" 2224 * 2225 * RETURNS 2226 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length. 2227 * Failure: An HRESULT error code describing the error. 2228 */ 2229 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, 2230 DWORD dwPart, DWORD dwFlags) 2231 { 2232 LPWSTR in, out; 2233 DWORD ret, len, len2; 2234 2235 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0) 2236 return E_INVALIDARG; 2237 2238 in = HeapAlloc(GetProcessHeap(), 0, 2239 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR)); 2240 out = in + INTERNET_MAX_URL_LENGTH; 2241 2242 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH); 2243 2244 len = INTERNET_MAX_URL_LENGTH; 2245 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags); 2246 2247 if (FAILED(ret)) { 2248 HeapFree(GetProcessHeap(), 0, in); 2249 return ret; 2250 } 2251 2252 len2 = WideCharToMultiByte(CP_ACP, 0, out, len, NULL, 0, NULL, NULL); 2253 if (len2 > *pcchOut) { 2254 *pcchOut = len2+1; 2255 HeapFree(GetProcessHeap(), 0, in); 2256 return E_POINTER; 2257 } 2258 len2 = WideCharToMultiByte(CP_ACP, 0, out, len+1, pszOut, *pcchOut, NULL, NULL); 2259 *pcchOut = len2-1; 2260 HeapFree(GetProcessHeap(), 0, in); 2261 return ret; 2262 } 2263 2264 /************************************************************************* 2265 * UrlGetPartW [SHLWAPI.@] 2266 * 2267 * See UrlGetPartA. 2268 */ 2269 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, 2270 DWORD dwPart, DWORD dwFlags) 2271 { 2272 WINE_PARSE_URL pl; 2273 HRESULT ret; 2274 DWORD scheme, size, schsize; 2275 LPCWSTR addr, schaddr; 2276 2277 TRACE("(%s %p %p(%d) %08x %08x)\n", 2278 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags); 2279 2280 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0) 2281 return E_INVALIDARG; 2282 2283 *pszOut = '\0'; 2284 2285 addr = strchrW(pszIn, ':'); 2286 if(!addr) 2287 scheme = URL_SCHEME_UNKNOWN; 2288 else 2289 scheme = get_scheme_code(pszIn, addr-pszIn); 2290 2291 ret = URL_ParseUrl(pszIn, &pl); 2292 2293 switch (dwPart) { 2294 case URL_PART_SCHEME: 2295 if (!pl.szScheme) { 2296 *pcchOut = 0; 2297 return S_FALSE; 2298 } 2299 addr = pl.pScheme; 2300 size = pl.szScheme; 2301 break; 2302 2303 case URL_PART_HOSTNAME: 2304 switch(scheme) { 2305 case URL_SCHEME_FTP: 2306 case URL_SCHEME_HTTP: 2307 case URL_SCHEME_GOPHER: 2308 case URL_SCHEME_TELNET: 2309 case URL_SCHEME_FILE: 2310 case URL_SCHEME_HTTPS: 2311 break; 2312 default: 2313 *pcchOut = 0; 2314 return E_FAIL; 2315 } 2316 2317 if(scheme==URL_SCHEME_FILE && (!pl.szHostName || 2318 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) { 2319 *pcchOut = 0; 2320 return S_FALSE; 2321 } 2322 2323 if (!pl.szHostName) { 2324 *pcchOut = 0; 2325 return S_FALSE; 2326 } 2327 addr = pl.pHostName; 2328 size = pl.szHostName; 2329 break; 2330 2331 case URL_PART_USERNAME: 2332 if (!pl.szUserName) { 2333 *pcchOut = 0; 2334 return S_FALSE; 2335 } 2336 addr = pl.pUserName; 2337 size = pl.szUserName; 2338 break; 2339 2340 case URL_PART_PASSWORD: 2341 if (!pl.szPassword) { 2342 *pcchOut = 0; 2343 return S_FALSE; 2344 } 2345 addr = pl.pPassword; 2346 size = pl.szPassword; 2347 break; 2348 2349 case URL_PART_PORT: 2350 if (!pl.szPort) { 2351 *pcchOut = 0; 2352 return S_FALSE; 2353 } 2354 addr = pl.pPort; 2355 size = pl.szPort; 2356 break; 2357 2358 case URL_PART_QUERY: 2359 if (!pl.szQuery) { 2360 *pcchOut = 0; 2361 return S_FALSE; 2362 } 2363 addr = pl.pQuery; 2364 size = pl.szQuery; 2365 break; 2366 2367 default: 2368 *pcchOut = 0; 2369 return E_INVALIDARG; 2370 } 2371 2372 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) { 2373 if(!pl.pScheme || !pl.szScheme) { 2374 *pcchOut = 0; 2375 return E_FAIL; 2376 } 2377 schaddr = pl.pScheme; 2378 schsize = pl.szScheme; 2379 if (*pcchOut < schsize + size + 2) { 2380 *pcchOut = schsize + size + 2; 2381 return E_POINTER; 2382 } 2383 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR)); 2384 pszOut[schsize] = ':'; 2385 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR)); 2386 pszOut[schsize+1+size] = 0; 2387 *pcchOut = schsize + 1 + size; 2388 } 2389 else { 2390 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;} 2391 memcpy(pszOut, addr, size*sizeof(WCHAR)); 2392 pszOut[size] = 0; 2393 *pcchOut = size; 2394 } 2395 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut)); 2396 2397 return ret; 2398 } 2399 2400 /************************************************************************* 2401 * PathIsURLA [SHLWAPI.@] 2402 * 2403 * Check if the given path is a Url. 2404 * 2405 * PARAMS 2406 * lpszPath [I] Path to check. 2407 * 2408 * RETURNS 2409 * TRUE if lpszPath is a Url. 2410 * FALSE if lpszPath is NULL or not a Url. 2411 */ 2412 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath) 2413 { 2414 PARSEDURLA base; 2415 HRESULT hres; 2416 2417 TRACE("%s\n", debugstr_a(lpstrPath)); 2418 2419 if (!lpstrPath || !*lpstrPath) return FALSE; 2420 2421 /* get protocol */ 2422 base.cbSize = sizeof(base); 2423 hres = ParseURLA(lpstrPath, &base); 2424 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID); 2425 } 2426 2427 /************************************************************************* 2428 * PathIsURLW [SHLWAPI.@] 2429 * 2430 * See PathIsURLA. 2431 */ 2432 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath) 2433 { 2434 PARSEDURLW base; 2435 HRESULT hres; 2436 2437 TRACE("%s\n", debugstr_w(lpstrPath)); 2438 2439 if (!lpstrPath || !*lpstrPath) return FALSE; 2440 2441 /* get protocol */ 2442 base.cbSize = sizeof(base); 2443 hres = ParseURLW(lpstrPath, &base); 2444 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID); 2445 } 2446 2447 /************************************************************************* 2448 * UrlCreateFromPathA [SHLWAPI.@] 2449 * 2450 * See UrlCreateFromPathW 2451 */ 2452 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved) 2453 { 2454 WCHAR bufW[INTERNET_MAX_URL_LENGTH]; 2455 WCHAR *urlW = bufW; 2456 UNICODE_STRING pathW; 2457 HRESULT ret; 2458 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA; 2459 2460 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath)) 2461 return E_INVALIDARG; 2462 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) { 2463 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); 2464 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved); 2465 } 2466 if(ret == S_OK || ret == S_FALSE) { 2467 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR)); 2468 if(*pcchUrl > lenA) { 2469 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR)); 2470 pszUrl[lenA] = 0; 2471 *pcchUrl = lenA; 2472 } else { 2473 *pcchUrl = lenA + 1; 2474 ret = E_POINTER; 2475 } 2476 } 2477 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW); 2478 RtlFreeUnicodeString(&pathW); 2479 return ret; 2480 } 2481 2482 /************************************************************************* 2483 * UrlCreateFromPathW [SHLWAPI.@] 2484 * 2485 * Create a Url from a file path. 2486 * 2487 * PARAMS 2488 * pszPath [I] Path to convert 2489 * pszUrl [O] Destination for the converted Url 2490 * pcchUrl [I/O] Length of pszUrl 2491 * dwReserved [I] Reserved, must be 0 2492 * 2493 * RETURNS 2494 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url 2495 * Failure: An HRESULT error code. 2496 */ 2497 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved) 2498 { 2499 HRESULT ret; 2500 2501 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved); 2502 2503 /* Validate arguments */ 2504 if (dwReserved != 0) 2505 return E_INVALIDARG; 2506 if (!pszUrl || !pcchUrl) 2507 return E_INVALIDARG; 2508 2509 ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl); 2510 2511 if (S_FALSE == ret) 2512 strcpyW(pszUrl, pszPath); 2513 2514 return ret; 2515 } 2516 2517 #ifndef __REACTOS__ 2518 /************************************************************************* 2519 * SHAutoComplete [SHLWAPI.@] 2520 * 2521 * Enable auto-completion for an edit control. 2522 * 2523 * PARAMS 2524 * hwndEdit [I] Handle of control to enable auto-completion for 2525 * dwFlags [I] SHACF_ flags from "shlwapi.h" 2526 * 2527 * RETURNS 2528 * Success: S_OK. Auto-completion is enabled for the control. 2529 * Failure: An HRESULT error code indicating the error. 2530 */ 2531 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags) 2532 { 2533 FIXME("stub\n"); 2534 return S_FALSE; 2535 } 2536 #endif 2537 2538 /************************************************************************* 2539 * MLBuildResURLA [SHLWAPI.405] 2540 * 2541 * Create a Url pointing to a resource in a module. 2542 * 2543 * PARAMS 2544 * lpszLibName [I] Name of the module containing the resource 2545 * hMod [I] Callers module handle 2546 * dwFlags [I] Undocumented flags for loading the module 2547 * lpszRes [I] Resource name 2548 * lpszDest [O] Destination for resulting Url 2549 * dwDestLen [I] Length of lpszDest 2550 * 2551 * RETURNS 2552 * Success: S_OK. lpszDest contains the resource Url. 2553 * Failure: E_INVALIDARG, if any argument is invalid, or 2554 * E_FAIL if dwDestLen is too small. 2555 */ 2556 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags, 2557 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen) 2558 { 2559 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH]; 2560 HRESULT hRet; 2561 2562 if (lpszLibName) 2563 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR)); 2564 2565 if (lpszRes) 2566 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR)); 2567 2568 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR)) 2569 dwDestLen = sizeof(szLibName)/sizeof(WCHAR); 2570 2571 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags, 2572 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen); 2573 if (SUCCEEDED(hRet) && lpszDest) 2574 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, NULL, NULL); 2575 2576 return hRet; 2577 } 2578 2579 /************************************************************************* 2580 * MLBuildResURLA [SHLWAPI.406] 2581 * 2582 * See MLBuildResURLA. 2583 */ 2584 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags, 2585 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen) 2586 { 2587 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' }; 2588 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR)) 2589 HRESULT hRet = E_FAIL; 2590 2591 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags, 2592 debugstr_w(lpszRes), lpszDest, dwDestLen); 2593 2594 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes || 2595 !lpszDest || (dwFlags && dwFlags != 2)) 2596 return E_INVALIDARG; 2597 2598 if (dwDestLen >= szResLen + 1) 2599 { 2600 dwDestLen -= (szResLen + 1); 2601 memcpy(lpszDest, szRes, sizeof(szRes)); 2602 2603 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags); 2604 2605 if (hMod) 2606 { 2607 WCHAR szBuff[MAX_PATH]; 2608 DWORD len; 2609 2610 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR)); 2611 if (len && len < sizeof(szBuff)/sizeof(WCHAR)) 2612 { 2613 DWORD dwPathLen = strlenW(szBuff) + 1; 2614 2615 if (dwDestLen >= dwPathLen) 2616 { 2617 DWORD dwResLen; 2618 2619 dwDestLen -= dwPathLen; 2620 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR)); 2621 2622 dwResLen = strlenW(lpszRes) + 1; 2623 if (dwDestLen >= dwResLen + 1) 2624 { 2625 lpszDest[szResLen + dwPathLen-1] = '/'; 2626 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR)); 2627 hRet = S_OK; 2628 } 2629 } 2630 } 2631 MLFreeLibrary(hMod); 2632 } 2633 } 2634 return hRet; 2635 } 2636 2637 /*********************************************************************** 2638 * UrlFixupW [SHLWAPI.462] 2639 * 2640 * Checks the scheme part of a URL and attempts to correct misspellings. 2641 * 2642 * PARAMS 2643 * lpszUrl [I] Pointer to the URL to be corrected 2644 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL 2645 * dwMaxChars [I] Maximum size of corrected URL 2646 * 2647 * RETURNS 2648 * success: S_OK if URL corrected or already correct 2649 * failure: S_FALSE if unable to correct / COM error code if other error 2650 * 2651 */ 2652 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars) 2653 { 2654 DWORD srcLen; 2655 2656 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars); 2657 2658 if (!url) 2659 return E_FAIL; 2660 2661 srcLen = lstrlenW(url) + 1; 2662 2663 /* For now just copy the URL directly */ 2664 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen); 2665 2666 return S_OK; 2667 } 2668 2669 /************************************************************************* 2670 * IsInternetESCEnabled [SHLWAPI.@] 2671 */ 2672 BOOL WINAPI IsInternetESCEnabled(void) 2673 { 2674 FIXME(": stub\n"); 2675 return FALSE; 2676 } 2677