shlwapi: Make UrlCanonicalizeW working with long urls (>INTERNET_MAX_URL_LENGTH).
[wine/hacks.git] / dlls / shlwapi / url.c
blob28ea8188a38b694db6d98b49a9e7efa77cd1e25d
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 /* The following schemes were identified in the native version of
46 * SHLWAPI.DLL version 5.50
48 static const struct {
49 URL_SCHEME scheme_number;
50 WCHAR scheme_name[12];
51 } shlwapi_schemes[] = {
52 {URL_SCHEME_FTP, {'f','t','p',0}},
53 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
54 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
55 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
56 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
57 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
58 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
59 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
60 {URL_SCHEME_FILE, {'f','i','l','e',0}},
61 {URL_SCHEME_MK, {'m','k',0}},
62 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
63 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
64 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
65 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
66 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
67 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
68 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
69 {URL_SCHEME_RES, {'r','e','s',0}},
72 typedef struct {
73 LPCWSTR pScheme; /* [out] start of scheme */
74 DWORD szScheme; /* [out] size of scheme (until colon) */
75 LPCWSTR pUserName; /* [out] start of Username */
76 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
77 LPCWSTR pPassword; /* [out] start of Password */
78 DWORD szPassword; /* [out] size of Password (until "@") */
79 LPCWSTR pHostName; /* [out] start of Hostname */
80 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
81 LPCWSTR pPort; /* [out] start of Port */
82 DWORD szPort; /* [out] size of Port (until "/" or eos) */
83 LPCWSTR pQuery; /* [out] start of Query */
84 DWORD szQuery; /* [out] size of Query (until eos) */
85 } WINE_PARSE_URL;
87 typedef enum {
88 SCHEME,
89 HOST,
90 PORT,
91 USERPASS,
92 } WINE_URL_SCAN_TYPE;
94 static const CHAR hexDigits[] = "0123456789ABCDEF";
96 static const WCHAR fileW[] = {'f','i','l','e','\0'};
98 static const unsigned char HashDataLookup[256] = {
99 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
100 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
101 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
102 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
103 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
104 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
105 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
106 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
107 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
108 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
109 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
110 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
111 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
112 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
113 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
114 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
115 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
116 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
117 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
118 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
120 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
122 unsigned int i;
124 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
125 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
126 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
127 return shlwapi_schemes[i].scheme_number;
130 return URL_SCHEME_UNKNOWN;
133 /*************************************************************************
134 * @ [SHLWAPI.1]
136 * Parse a Url into its constituent parts.
138 * PARAMS
139 * x [I] Url to parse
140 * y [O] Undocumented structure holding the parsed information
142 * RETURNS
143 * Success: S_OK. y contains the parsed Url details.
144 * Failure: An HRESULT error code.
146 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
148 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
149 const char *ptr = x;
150 int len;
152 TRACE("%s %p\n", debugstr_a(x), y);
154 if(y->cbSize != sizeof(*y))
155 return E_INVALIDARG;
157 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
158 ptr++;
160 if (*ptr != ':' || ptr <= x+1) {
161 y->pszProtocol = NULL;
162 return URL_E_INVALID_SYNTAX;
165 y->pszProtocol = x;
166 y->cchProtocol = ptr-x;
167 y->pszSuffix = ptr+1;
168 y->cchSuffix = strlen(y->pszSuffix);
170 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
171 scheme, sizeof(scheme)/sizeof(WCHAR));
172 y->nScheme = get_scheme_code(scheme, len);
174 return S_OK;
177 /*************************************************************************
178 * @ [SHLWAPI.2]
180 * Unicode version of ParseURLA.
182 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
184 const WCHAR *ptr = x;
186 TRACE("%s %p\n", debugstr_w(x), y);
188 if(y->cbSize != sizeof(*y))
189 return E_INVALIDARG;
191 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
192 ptr++;
194 if (*ptr != ':' || ptr <= x+1) {
195 y->pszProtocol = NULL;
196 return URL_E_INVALID_SYNTAX;
199 y->pszProtocol = x;
200 y->cchProtocol = ptr-x;
201 y->pszSuffix = ptr+1;
202 y->cchSuffix = strlenW(y->pszSuffix);
203 y->nScheme = get_scheme_code(x, ptr-x);
205 return S_OK;
208 /*************************************************************************
209 * UrlCanonicalizeA [SHLWAPI.@]
211 * Canonicalize a Url.
213 * PARAMS
214 * pszUrl [I] Url to cCanonicalize
215 * pszCanonicalized [O] Destination for converted Url.
216 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
217 * dwFlags [I] Flags controlling the conversion.
219 * RETURNS
220 * Success: S_OK. The pszCanonicalized contains the converted Url.
221 * Failure: E_POINTER, if *pcchCanonicalized is too small.
223 * MSDN incorrectly describes the flags for this function. They should be:
224 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
225 *| URL_ESCAPE_SPACES_ONLY 0x04000000
226 *| URL_ESCAPE_PERCENT 0x00001000
227 *| URL_ESCAPE_UNSAFE 0x10000000
228 *| URL_UNESCAPE 0x10000000
229 *| URL_DONT_SIMPLIFY 0x08000000
230 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
232 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
233 LPDWORD pcchCanonicalized, DWORD dwFlags)
235 LPWSTR base, canonical;
236 HRESULT ret;
237 DWORD len, len2;
239 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
240 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
242 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
243 return E_INVALIDARG;
245 base = HeapAlloc(GetProcessHeap(), 0,
246 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
247 canonical = base + INTERNET_MAX_URL_LENGTH;
249 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
250 len = INTERNET_MAX_URL_LENGTH;
252 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
253 if (ret != S_OK) {
254 *pcchCanonicalized = len * 2;
255 HeapFree(GetProcessHeap(), 0, base);
256 return ret;
259 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
260 if (len2 > *pcchCanonicalized) {
261 *pcchCanonicalized = len2;
262 HeapFree(GetProcessHeap(), 0, base);
263 return E_POINTER;
265 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
266 *pcchCanonicalized = len;
267 HeapFree(GetProcessHeap(), 0, base);
268 return S_OK;
271 /*************************************************************************
272 * UrlCanonicalizeW [SHLWAPI.@]
274 * See UrlCanonicalizeA.
276 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
277 LPDWORD pcchCanonicalized, DWORD dwFlags)
279 HRESULT hr = S_OK;
280 DWORD EscapeFlags;
281 LPCWSTR wk1, root;
282 LPWSTR lpszUrlCpy, wk2, mp, mp2;
283 INT state;
284 DWORD nByteLen, nLen, nWkLen;
285 WCHAR slash = '/';
287 static const WCHAR wszFile[] = {'f','i','l','e',':'};
288 static const WCHAR wszRes[] = {'r','e','s',':'};
289 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
290 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
292 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
293 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
295 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
296 return E_INVALIDARG;
298 if(!*pszUrl) {
299 *pszCanonicalized = 0;
300 return S_OK;
303 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
304 /* Allocate memory for simplified URL (before escaping) */
305 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
306 nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR));
308 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
309 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
310 slash = '\\';
312 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, pszUrl, sizeof(wszRes))) {
313 dwFlags &= ~URL_FILE_USE_PATHURL;
314 slash = '\0';
318 * state =
319 * 0 initial 1,3
320 * 1 have 2[+] alnum 2,3
321 * 2 have scheme (found :) 4,6,3
322 * 3 failed (no location)
323 * 4 have // 5,3
324 * 5 have 1[+] alnum 6,3
325 * 6 have location (found /) save root location
328 wk1 = pszUrl;
329 wk2 = lpszUrlCpy;
330 state = 0;
332 if(pszUrl[1] == ':') { /* Assume path */
333 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
334 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
335 if (dwFlags & URL_FILE_USE_PATHURL)
337 slash = '\\';
338 --wk2;
340 else
341 dwFlags |= URL_ESCAPE_UNSAFE;
342 state = 5;
345 while (*wk1) {
346 switch (state) {
347 case 0:
348 if (!isalnumW(*wk1)) {state = 3; break;}
349 *wk2++ = *wk1++;
350 if (!isalnumW(*wk1)) {state = 3; break;}
351 *wk2++ = *wk1++;
352 state = 1;
353 break;
354 case 1:
355 *wk2++ = *wk1;
356 if (*wk1++ == ':') state = 2;
357 break;
358 case 2:
359 *wk2++ = *wk1++;
360 if (*wk1 != '/') {state = 6; break;}
361 *wk2++ = *wk1++;
362 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
363 && !strncmpW(wszFile, pszUrl, sizeof(wszFile)/sizeof(WCHAR))
364 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
365 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
366 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
367 wk1++;
369 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
370 wk1++;
371 state = 4;
372 break;
373 case 3:
374 nWkLen = strlenW(wk1);
375 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
376 mp = wk2;
377 wk1 += nWkLen;
378 wk2 += nWkLen;
380 if(slash) {
381 while(mp < wk2) {
382 if(*mp == '/' || *mp == '\\')
383 *mp = slash;
384 mp++;
387 break;
388 case 4:
389 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
390 {state = 3; break;}
391 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
392 *wk2++ = *wk1++;
393 state = 5;
394 if (!*wk1) {
395 if(slash)
396 *wk2++ = slash;
397 else
398 *wk2++ = '/';
400 break;
401 case 5:
402 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
403 while(*wk1 == '/' || *wk1 == '\\') {
404 if(slash)
405 *wk2++ = slash;
406 else
407 *wk2++ = *wk1;
408 wk1++;
410 state = 6;
411 break;
412 case 6:
413 if(dwFlags & URL_DONT_SIMPLIFY) {
414 state = 3;
415 break;
418 /* Now at root location, cannot back up any more. */
419 /* "root" will point at the '/' */
421 root = wk2-1;
422 while (*wk1) {
423 mp = strchrW(wk1, '/');
424 mp2 = strchrW(wk1, '\\');
425 if(mp2 && (!mp || mp2 < mp))
426 mp = mp2;
427 if (!mp) {
428 nWkLen = strlenW(wk1);
429 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
430 wk1 += nWkLen;
431 wk2 += nWkLen;
432 continue;
434 nLen = mp - wk1;
435 if(nLen) {
436 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
437 wk2 += nLen;
438 wk1 += nLen;
440 if(slash)
441 *wk2++ = slash;
442 else
443 *wk2++ = *wk1;
444 wk1++;
446 if (*wk1 == '.') {
447 TRACE("found '/.'\n");
448 if (wk1[1] == '/' || wk1[1] == '\\') {
449 /* case of /./ -> skip the ./ */
450 wk1 += 2;
452 else if (wk1[1] == '.') {
453 /* found /.. look for next / */
454 TRACE("found '/..'\n");
455 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
456 || wk1[2] == '#' || !wk1[2]) {
457 /* case /../ -> need to backup wk2 */
458 TRACE("found '/../'\n");
459 *(wk2-1) = '\0'; /* set end of string */
460 mp = strrchrW(root, '/');
461 mp2 = strrchrW(root, '\\');
462 if(mp2 && (!mp || mp2 < mp))
463 mp = mp2;
464 if (mp && (mp >= root)) {
465 /* found valid backup point */
466 wk2 = mp + 1;
467 if(wk1[2] != '/' && wk1[2] != '\\')
468 wk1 += 2;
469 else
470 wk1 += 3;
472 else {
473 /* did not find point, restore '/' */
474 *(wk2-1) = slash;
480 *wk2 = '\0';
481 break;
482 default:
483 FIXME("how did we get here - state=%d\n", state);
484 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
485 return E_INVALIDARG;
487 *wk2 = '\0';
488 TRACE("Simplified, orig <%s>, simple <%s>\n",
489 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
491 nLen = lstrlenW(lpszUrlCpy);
492 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
493 lpszUrlCpy[--nLen]=0;
495 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
496 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
498 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
499 URL_ESCAPE_SPACES_ONLY |
500 URL_ESCAPE_PERCENT |
501 URL_DONT_ESCAPE_EXTRA_INFO |
502 URL_ESCAPE_SEGMENT_ONLY ))) {
503 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
504 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
505 EscapeFlags);
506 } else { /* No escaping needed, just copy the string */
507 nLen = lstrlenW(lpszUrlCpy);
508 if(nLen < *pcchCanonicalized)
509 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
510 else {
511 hr = E_POINTER;
512 nLen++;
514 *pcchCanonicalized = nLen;
517 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
519 if (hr == S_OK)
520 TRACE("result %s\n", debugstr_w(pszCanonicalized));
522 return hr;
525 /*************************************************************************
526 * UrlCombineA [SHLWAPI.@]
528 * Combine two Urls.
530 * PARAMS
531 * pszBase [I] Base Url
532 * pszRelative [I] Url to combine with pszBase
533 * pszCombined [O] Destination for combined Url
534 * pcchCombined [O] Destination for length of pszCombined
535 * dwFlags [I] URL_ flags from "shlwapi.h"
537 * RETURNS
538 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
539 * contains its length.
540 * Failure: An HRESULT error code indicating the error.
542 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
543 LPSTR pszCombined, LPDWORD pcchCombined,
544 DWORD dwFlags)
546 LPWSTR base, relative, combined;
547 DWORD ret, len, len2;
549 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
550 debugstr_a(pszBase),debugstr_a(pszRelative),
551 pcchCombined?*pcchCombined:0,dwFlags);
553 if(!pszBase || !pszRelative || !pcchCombined)
554 return E_INVALIDARG;
556 base = HeapAlloc(GetProcessHeap(), 0,
557 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
558 relative = base + INTERNET_MAX_URL_LENGTH;
559 combined = relative + INTERNET_MAX_URL_LENGTH;
561 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
562 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
563 len = *pcchCombined;
565 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
566 if (ret != S_OK) {
567 *pcchCombined = len;
568 HeapFree(GetProcessHeap(), 0, base);
569 return ret;
572 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
573 if (len2 > *pcchCombined) {
574 *pcchCombined = len2;
575 HeapFree(GetProcessHeap(), 0, base);
576 return E_POINTER;
578 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
579 0, 0);
580 *pcchCombined = len2;
581 HeapFree(GetProcessHeap(), 0, base);
582 return S_OK;
585 /*************************************************************************
586 * UrlCombineW [SHLWAPI.@]
588 * See UrlCombineA.
590 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
591 LPWSTR pszCombined, LPDWORD pcchCombined,
592 DWORD dwFlags)
594 PARSEDURLW base, relative;
595 DWORD myflags, sizeloc = 0;
596 DWORD len, res1, res2, process_case = 0;
597 LPWSTR work, preliminary, mbase, mrelative;
598 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
599 HRESULT ret;
601 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
602 debugstr_w(pszBase),debugstr_w(pszRelative),
603 pcchCombined?*pcchCombined:0,dwFlags);
605 if(!pszBase || !pszRelative || !pcchCombined)
606 return E_INVALIDARG;
608 base.cbSize = sizeof(base);
609 relative.cbSize = sizeof(relative);
611 /* Get space for duplicates of the input and the output */
612 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
613 sizeof(WCHAR));
614 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
615 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
616 *preliminary = '\0';
618 /* Canonicalize the base input prior to looking for the scheme */
619 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
620 len = INTERNET_MAX_URL_LENGTH;
621 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
623 /* Canonicalize the relative input prior to looking for the scheme */
624 len = INTERNET_MAX_URL_LENGTH;
625 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
627 /* See if the base has a scheme */
628 res1 = ParseURLW(mbase, &base);
629 if (res1) {
630 /* if pszBase has no scheme, then return pszRelative */
631 TRACE("no scheme detected in Base\n");
632 process_case = 1;
634 else do {
635 BOOL manual_search = FALSE;
637 /* mk is a special case */
638 if(base.nScheme == URL_SCHEME_MK) {
639 static const WCHAR wsz[] = {':',':',0};
641 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
642 if(ptr) {
643 int delta;
645 ptr += 2;
646 delta = ptr-base.pszSuffix;
647 base.cchProtocol += delta;
648 base.pszSuffix += delta;
649 base.cchSuffix -= delta;
651 }else {
652 /* get size of location field (if it exists) */
653 work = (LPWSTR)base.pszSuffix;
654 sizeloc = 0;
655 if (*work++ == '/') {
656 if (*work++ == '/') {
657 /* At this point have start of location and
658 * it ends at next '/' or end of string.
660 while(*work && (*work != '/')) work++;
661 sizeloc = (DWORD)(work - base.pszSuffix);
666 /* If there is a '#' and the characters immediately preceding it are
667 * ".htm[l]", then begin looking for the last leaf starting from
668 * the '#'. Otherwise the '#' is not meaningful and just start
669 * looking from the end. */
670 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
671 const WCHAR htmlW[] = {'.','h','t','m','l',0};
672 const int len_htmlW = 5;
673 const WCHAR htmW[] = {'.','h','t','m',0};
674 const int len_htmW = 4;
676 if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
677 work -= len_htmW;
678 if (strncmpiW(work, htmW, len_htmW) == 0)
679 manual_search = TRUE;
680 work += len_htmW;
683 if (!manual_search &&
684 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
685 work -= len_htmlW;
686 if (strncmpiW(work, htmlW, len_htmlW) == 0)
687 manual_search = TRUE;
688 work += len_htmlW;
692 if (manual_search) {
693 /* search backwards starting from the current position */
694 while (*work != '/' && work > base.pszSuffix + sizeloc)
695 --work;
696 if (work > base.pszSuffix + sizeloc)
697 base.cchSuffix = work - base.pszSuffix + 1;
698 }else {
699 /* search backwards starting from the end of the string */
700 work = strrchrW((base.pszSuffix+sizeloc), '/');
701 if (work) {
702 len = (DWORD)(work - base.pszSuffix + 1);
703 base.cchSuffix = len;
708 * At this point:
709 * .pszSuffix points to location (starting with '//')
710 * .cchSuffix length of location (above) and rest less the last
711 * leaf (if any)
712 * sizeloc length of location (above) up to but not including
713 * the last '/'
716 res2 = ParseURLW(mrelative, &relative);
717 if (res2) {
718 /* no scheme in pszRelative */
719 TRACE("no scheme detected in Relative\n");
720 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
721 relative.cchSuffix = strlenW(mrelative);
722 if (*pszRelative == ':') {
723 /* case that is either left alone or uses pszBase */
724 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
725 process_case = 5;
726 break;
728 process_case = 1;
729 break;
731 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
732 /* case that becomes "file:///" */
733 strcpyW(preliminary, myfilestr);
734 process_case = 1;
735 break;
737 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
738 /* pszRelative has location and rest */
739 process_case = 3;
740 break;
742 if (*mrelative == '/') {
743 /* case where pszRelative is root to location */
744 process_case = 4;
745 break;
747 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
748 break;
751 /* handle cases where pszRelative has scheme */
752 if ((base.cchProtocol == relative.cchProtocol) &&
753 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
755 /* since the schemes are the same */
756 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
757 /* case where pszRelative replaces location and following */
758 process_case = 3;
759 break;
761 if (*relative.pszSuffix == '/') {
762 /* case where pszRelative is root to location */
763 process_case = 4;
764 break;
766 /* replace either just location if base's location starts with a
767 * slash or otherwise everything */
768 process_case = (*base.pszSuffix == '/') ? 5 : 1;
769 break;
771 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
772 /* case where pszRelative replaces scheme, location,
773 * and following and handles PLUGGABLE
775 process_case = 2;
776 break;
778 process_case = 1;
779 break;
780 } while(FALSE); /* a little trick to allow easy exit from nested if's */
782 ret = S_OK;
783 switch (process_case) {
785 case 1: /*
786 * Return pszRelative appended to what ever is in pszCombined,
787 * (which may the string "file:///"
789 strcatW(preliminary, mrelative);
790 break;
792 case 2: /* case where pszRelative replaces scheme, and location */
793 strcpyW(preliminary, mrelative);
794 break;
796 case 3: /*
797 * Return the pszBase scheme with pszRelative. Basically
798 * keeps the scheme and replaces the domain and following.
800 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
801 work = preliminary + base.cchProtocol + 1;
802 strcpyW(work, relative.pszSuffix);
803 break;
805 case 4: /*
806 * Return the pszBase scheme and location but everything
807 * after the location is pszRelative. (Replace document
808 * from root on.)
810 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
811 work = preliminary + base.cchProtocol + 1 + sizeloc;
812 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
813 *(work++) = '/';
814 strcpyW(work, relative.pszSuffix);
815 break;
817 case 5: /*
818 * Return the pszBase without its document (if any) and
819 * append pszRelative after its scheme.
821 memcpy(preliminary, base.pszProtocol,
822 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
823 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
824 if (*work++ != '/')
825 *(work++) = '/';
826 strcpyW(work, relative.pszSuffix);
827 break;
829 default:
830 FIXME("How did we get here????? process_case=%d\n", process_case);
831 ret = E_INVALIDARG;
834 if (ret == S_OK) {
835 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
836 if(*pcchCombined == 0)
837 *pcchCombined = 1;
838 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
839 if(SUCCEEDED(ret) && pszCombined) {
840 lstrcpyW(pszCombined, mrelative);
842 TRACE("return-%d len=%d, %s\n",
843 process_case, *pcchCombined, debugstr_w(pszCombined));
845 HeapFree(GetProcessHeap(), 0, preliminary);
846 return ret;
849 /*************************************************************************
850 * UrlEscapeA [SHLWAPI.@]
853 HRESULT WINAPI UrlEscapeA(
854 LPCSTR pszUrl,
855 LPSTR pszEscaped,
856 LPDWORD pcchEscaped,
857 DWORD dwFlags)
859 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
860 WCHAR *escapedW = bufW;
861 UNICODE_STRING urlW;
862 HRESULT ret;
863 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
865 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
866 return E_INVALIDARG;
868 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
869 return E_INVALIDARG;
870 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
871 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
872 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
874 if(ret == S_OK) {
875 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
876 if(*pcchEscaped > lenA) {
877 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
878 pszEscaped[lenA] = 0;
879 *pcchEscaped = lenA;
880 } else {
881 *pcchEscaped = lenA + 1;
882 ret = E_POINTER;
885 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
886 RtlFreeUnicodeString(&urlW);
887 return ret;
890 #define WINE_URL_BASH_AS_SLASH 0x01
891 #define WINE_URL_COLLAPSE_SLASHES 0x02
892 #define WINE_URL_ESCAPE_SLASH 0x04
893 #define WINE_URL_ESCAPE_HASH 0x08
894 #define WINE_URL_ESCAPE_QUESTION 0x10
895 #define WINE_URL_STOP_ON_HASH 0x20
896 #define WINE_URL_STOP_ON_QUESTION 0x40
898 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
901 if (isalnumW(ch))
902 return FALSE;
904 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
905 if(ch == ' ')
906 return TRUE;
907 else
908 return FALSE;
911 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
912 return TRUE;
914 if (ch <= 31 || ch >= 127)
915 return TRUE;
917 else {
918 switch (ch) {
919 case ' ':
920 case '<':
921 case '>':
922 case '\"':
923 case '{':
924 case '}':
925 case '|':
926 case '\\':
927 case '^':
928 case ']':
929 case '[':
930 case '`':
931 case '&':
932 return TRUE;
934 case '/':
935 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
936 return FALSE;
938 case '?':
939 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
940 return FALSE;
942 case '#':
943 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
944 return FALSE;
946 default:
947 return FALSE;
953 /*************************************************************************
954 * UrlEscapeW [SHLWAPI.@]
956 * Converts unsafe characters in a Url into escape sequences.
958 * PARAMS
959 * pszUrl [I] Url to modify
960 * pszEscaped [O] Destination for modified Url
961 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
962 * dwFlags [I] URL_ flags from "shlwapi.h"
964 * RETURNS
965 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
966 * contains its length.
967 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
968 * pcchEscaped is set to the required length.
970 * Converts unsafe characters into their escape sequences.
972 * NOTES
973 * - By default this function stops converting at the first '?' or
974 * '#' character.
975 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
976 * converted, but the conversion continues past a '?' or '#'.
977 * - Note that this function did not work well (or at all) in shlwapi version 4.
979 * BUGS
980 * Only the following flags are implemented:
981 *| URL_ESCAPE_SPACES_ONLY
982 *| URL_DONT_ESCAPE_EXTRA_INFO
983 *| URL_ESCAPE_SEGMENT_ONLY
984 *| URL_ESCAPE_PERCENT
986 HRESULT WINAPI UrlEscapeW(
987 LPCWSTR pszUrl,
988 LPWSTR pszEscaped,
989 LPDWORD pcchEscaped,
990 DWORD dwFlags)
992 LPCWSTR src;
993 DWORD needed = 0, ret;
994 BOOL stop_escaping = FALSE;
995 WCHAR next[5], *dst = pszEscaped;
996 INT len;
997 PARSEDURLW parsed_url;
998 DWORD int_flags;
999 DWORD slashes = 0;
1000 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1002 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
1003 pcchEscaped, dwFlags);
1005 if(!pszUrl || !pcchEscaped)
1006 return E_INVALIDARG;
1008 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1009 URL_ESCAPE_SEGMENT_ONLY |
1010 URL_DONT_ESCAPE_EXTRA_INFO |
1011 URL_ESCAPE_PERCENT))
1012 FIXME("Unimplemented flags: %08x\n", dwFlags);
1014 /* fix up flags */
1015 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1016 /* if SPACES_ONLY specified, reset the other controls */
1017 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1018 URL_ESCAPE_PERCENT |
1019 URL_ESCAPE_SEGMENT_ONLY);
1021 else
1022 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1023 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1026 int_flags = 0;
1027 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1028 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1029 } else {
1030 parsed_url.cbSize = sizeof(parsed_url);
1031 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1032 parsed_url.nScheme = URL_SCHEME_INVALID;
1034 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1036 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1037 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1039 switch(parsed_url.nScheme) {
1040 case URL_SCHEME_FILE:
1041 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1042 int_flags &= ~WINE_URL_STOP_ON_HASH;
1043 break;
1045 case URL_SCHEME_HTTP:
1046 case URL_SCHEME_HTTPS:
1047 int_flags |= WINE_URL_BASH_AS_SLASH;
1048 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1049 int_flags |= WINE_URL_ESCAPE_SLASH;
1050 break;
1052 case URL_SCHEME_MAILTO:
1053 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1054 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1055 break;
1057 case URL_SCHEME_INVALID:
1058 break;
1060 case URL_SCHEME_FTP:
1061 default:
1062 if(parsed_url.pszSuffix[0] != '/')
1063 int_flags |= WINE_URL_ESCAPE_SLASH;
1064 break;
1068 for(src = pszUrl; *src; ) {
1069 WCHAR cur = *src;
1070 len = 0;
1072 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1073 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1074 while(cur == '/' || cur == '\\') {
1075 slashes++;
1076 cur = *++src;
1078 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1079 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1080 src += localhost_len + 1;
1081 slashes = 3;
1084 switch(slashes) {
1085 case 1:
1086 case 3:
1087 next[0] = next[1] = next[2] = '/';
1088 len = 3;
1089 break;
1090 case 0:
1091 len = 0;
1092 break;
1093 default:
1094 next[0] = next[1] = '/';
1095 len = 2;
1096 break;
1099 if(len == 0) {
1101 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1102 stop_escaping = TRUE;
1104 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1105 stop_escaping = TRUE;
1107 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1109 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1110 next[0] = '%';
1111 next[1] = hexDigits[(cur >> 4) & 0xf];
1112 next[2] = hexDigits[cur & 0xf];
1113 len = 3;
1114 } else {
1115 next[0] = cur;
1116 len = 1;
1118 src++;
1121 if(needed + len <= *pcchEscaped) {
1122 memcpy(dst, next, len*sizeof(WCHAR));
1123 dst += len;
1125 needed += len;
1128 if(needed < *pcchEscaped) {
1129 *dst = '\0';
1130 ret = S_OK;
1131 } else {
1132 needed++; /* add one for the '\0' */
1133 ret = E_POINTER;
1135 *pcchEscaped = needed;
1136 return ret;
1140 /*************************************************************************
1141 * UrlUnescapeA [SHLWAPI.@]
1143 * Converts Url escape sequences back to ordinary characters.
1145 * PARAMS
1146 * pszUrl [I/O] Url to convert
1147 * pszUnescaped [O] Destination for converted Url
1148 * pcchUnescaped [I/O] Size of output string
1149 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1151 * RETURNS
1152 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1153 * dwFlags includes URL_ESCAPE_INPLACE.
1154 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1155 * this case pcchUnescaped is set to the size required.
1156 * NOTES
1157 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1158 * the first occurrence of either a '?' or '#' character.
1160 HRESULT WINAPI UrlUnescapeA(
1161 LPSTR pszUrl,
1162 LPSTR pszUnescaped,
1163 LPDWORD pcchUnescaped,
1164 DWORD dwFlags)
1166 char *dst, next;
1167 LPCSTR src;
1168 HRESULT ret;
1169 DWORD needed;
1170 BOOL stop_unescaping = FALSE;
1172 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1173 pcchUnescaped, dwFlags);
1175 if (!pszUrl) return E_INVALIDARG;
1177 if(dwFlags & URL_UNESCAPE_INPLACE)
1178 dst = pszUrl;
1179 else
1181 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1182 dst = pszUnescaped;
1185 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1186 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1187 (*src == '#' || *src == '?')) {
1188 stop_unescaping = TRUE;
1189 next = *src;
1190 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1191 && stop_unescaping == FALSE) {
1192 INT ih;
1193 char buf[3];
1194 memcpy(buf, src + 1, 2);
1195 buf[2] = '\0';
1196 ih = strtol(buf, NULL, 16);
1197 next = (CHAR) ih;
1198 src += 2; /* Advance to end of escape */
1199 } else
1200 next = *src;
1202 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1203 *dst++ = next;
1206 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1207 *dst = '\0';
1208 ret = S_OK;
1209 } else {
1210 needed++; /* add one for the '\0' */
1211 ret = E_POINTER;
1213 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1214 *pcchUnescaped = needed;
1216 if (ret == S_OK) {
1217 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1218 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1221 return ret;
1224 /*************************************************************************
1225 * UrlUnescapeW [SHLWAPI.@]
1227 * See UrlUnescapeA.
1229 HRESULT WINAPI UrlUnescapeW(
1230 LPWSTR pszUrl,
1231 LPWSTR pszUnescaped,
1232 LPDWORD pcchUnescaped,
1233 DWORD dwFlags)
1235 WCHAR *dst, next;
1236 LPCWSTR src;
1237 HRESULT ret;
1238 DWORD needed;
1239 BOOL stop_unescaping = FALSE;
1241 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1242 pcchUnescaped, dwFlags);
1244 if(!pszUrl) return E_INVALIDARG;
1246 if(dwFlags & URL_UNESCAPE_INPLACE)
1247 dst = pszUrl;
1248 else
1250 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1251 dst = pszUnescaped;
1254 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1255 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1256 (*src == '#' || *src == '?')) {
1257 stop_unescaping = TRUE;
1258 next = *src;
1259 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1260 && stop_unescaping == FALSE) {
1261 INT ih;
1262 WCHAR buf[5] = {'0','x',0};
1263 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1264 buf[4] = 0;
1265 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1266 next = (WCHAR) ih;
1267 src += 2; /* Advance to end of escape */
1268 } else
1269 next = *src;
1271 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1272 *dst++ = next;
1275 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1276 *dst = '\0';
1277 ret = S_OK;
1278 } else {
1279 needed++; /* add one for the '\0' */
1280 ret = E_POINTER;
1282 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1283 *pcchUnescaped = needed;
1285 if (ret == S_OK) {
1286 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1287 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1290 return ret;
1293 /*************************************************************************
1294 * UrlGetLocationA [SHLWAPI.@]
1296 * Get the location from a Url.
1298 * PARAMS
1299 * pszUrl [I] Url to get the location from
1301 * RETURNS
1302 * A pointer to the start of the location in pszUrl, or NULL if there is
1303 * no location.
1305 * NOTES
1306 * - MSDN erroneously states that "The location is the segment of the Url
1307 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1308 * stop at '?' and always return a NULL in this case.
1309 * - MSDN also erroneously states that "If a file URL has a query string,
1310 * the returned string is the query string". In all tested cases, if the
1311 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1312 *| Result Url
1313 *| ------ ---
1314 *| NULL file://aa/b/cd#hohoh
1315 *| #hohoh http://aa/b/cd#hohoh
1316 *| NULL fi://aa/b/cd#hohoh
1317 *| #hohoh ff://aa/b/cd#hohoh
1319 LPCSTR WINAPI UrlGetLocationA(
1320 LPCSTR pszUrl)
1322 PARSEDURLA base;
1323 DWORD res1;
1325 base.cbSize = sizeof(base);
1326 res1 = ParseURLA(pszUrl, &base);
1327 if (res1) return NULL; /* invalid scheme */
1329 /* if scheme is file: then never return pointer */
1330 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1332 /* Look for '#' and return its addr */
1333 return strchr(base.pszSuffix, '#');
1336 /*************************************************************************
1337 * UrlGetLocationW [SHLWAPI.@]
1339 * See UrlGetLocationA.
1341 LPCWSTR WINAPI UrlGetLocationW(
1342 LPCWSTR pszUrl)
1344 PARSEDURLW base;
1345 DWORD res1;
1347 base.cbSize = sizeof(base);
1348 res1 = ParseURLW(pszUrl, &base);
1349 if (res1) return NULL; /* invalid scheme */
1351 /* if scheme is file: then never return pointer */
1352 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1354 /* Look for '#' and return its addr */
1355 return strchrW(base.pszSuffix, '#');
1358 /*************************************************************************
1359 * UrlCompareA [SHLWAPI.@]
1361 * Compare two Urls.
1363 * PARAMS
1364 * pszUrl1 [I] First Url to compare
1365 * pszUrl2 [I] Url to compare to pszUrl1
1366 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1368 * RETURNS
1369 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1370 * than, equal to, or less than pszUrl1 respectively.
1372 INT WINAPI UrlCompareA(
1373 LPCSTR pszUrl1,
1374 LPCSTR pszUrl2,
1375 BOOL fIgnoreSlash)
1377 INT ret, len, len1, len2;
1379 if (!fIgnoreSlash)
1380 return strcmp(pszUrl1, pszUrl2);
1381 len1 = strlen(pszUrl1);
1382 if (pszUrl1[len1-1] == '/') len1--;
1383 len2 = strlen(pszUrl2);
1384 if (pszUrl2[len2-1] == '/') len2--;
1385 if (len1 == len2)
1386 return strncmp(pszUrl1, pszUrl2, len1);
1387 len = min(len1, len2);
1388 ret = strncmp(pszUrl1, pszUrl2, len);
1389 if (ret) return ret;
1390 if (len1 > len2) return 1;
1391 return -1;
1394 /*************************************************************************
1395 * UrlCompareW [SHLWAPI.@]
1397 * See UrlCompareA.
1399 INT WINAPI UrlCompareW(
1400 LPCWSTR pszUrl1,
1401 LPCWSTR pszUrl2,
1402 BOOL fIgnoreSlash)
1404 INT ret;
1405 size_t len, len1, len2;
1407 if (!fIgnoreSlash)
1408 return strcmpW(pszUrl1, pszUrl2);
1409 len1 = strlenW(pszUrl1);
1410 if (pszUrl1[len1-1] == '/') len1--;
1411 len2 = strlenW(pszUrl2);
1412 if (pszUrl2[len2-1] == '/') len2--;
1413 if (len1 == len2)
1414 return strncmpW(pszUrl1, pszUrl2, len1);
1415 len = min(len1, len2);
1416 ret = strncmpW(pszUrl1, pszUrl2, len);
1417 if (ret) return ret;
1418 if (len1 > len2) return 1;
1419 return -1;
1422 /*************************************************************************
1423 * HashData [SHLWAPI.@]
1425 * Hash an input block into a variable sized digest.
1427 * PARAMS
1428 * lpSrc [I] Input block
1429 * nSrcLen [I] Length of lpSrc
1430 * lpDest [I] Output for hash digest
1431 * nDestLen [I] Length of lpDest
1433 * RETURNS
1434 * Success: TRUE. lpDest is filled with the computed hash value.
1435 * Failure: FALSE, if any argument is invalid.
1437 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1438 unsigned char *lpDest, DWORD nDestLen)
1440 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1442 if (!lpSrc || !lpDest)
1443 return E_INVALIDARG;
1445 while (destCount >= 0)
1447 lpDest[destCount] = (destCount & 0xff);
1448 destCount--;
1451 while (srcCount >= 0)
1453 destCount = nDestLen - 1;
1454 while (destCount >= 0)
1456 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1457 destCount--;
1459 srcCount--;
1461 return S_OK;
1464 /*************************************************************************
1465 * UrlHashA [SHLWAPI.@]
1467 * Produce a Hash from a Url.
1469 * PARAMS
1470 * pszUrl [I] Url to hash
1471 * lpDest [O] Destinationh for hash
1472 * nDestLen [I] Length of lpDest
1474 * RETURNS
1475 * Success: S_OK. lpDest is filled with the computed hash value.
1476 * Failure: E_INVALIDARG, if any argument is invalid.
1478 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1480 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1481 return E_INVALIDARG;
1483 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1484 return S_OK;
1487 /*************************************************************************
1488 * UrlHashW [SHLWAPI.@]
1490 * See UrlHashA.
1492 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1494 char szUrl[MAX_PATH];
1496 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1498 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1499 return E_INVALIDARG;
1501 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1502 * return the same digests for the same URL.
1504 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1505 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1506 return S_OK;
1509 /*************************************************************************
1510 * UrlApplySchemeA [SHLWAPI.@]
1512 * Apply a scheme to a Url.
1514 * PARAMS
1515 * pszIn [I] Url to apply scheme to
1516 * pszOut [O] Destination for modified Url
1517 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1518 * dwFlags [I] URL_ flags from "shlwapi.h"
1520 * RETURNS
1521 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1522 * Failure: An HRESULT error code describing the error.
1524 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1526 LPWSTR in, out;
1527 HRESULT ret;
1528 DWORD len;
1530 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1531 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1533 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1535 in = HeapAlloc(GetProcessHeap(), 0,
1536 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1537 out = in + INTERNET_MAX_URL_LENGTH;
1539 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1540 len = INTERNET_MAX_URL_LENGTH;
1542 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1543 if (ret != S_OK) {
1544 HeapFree(GetProcessHeap(), 0, in);
1545 return ret;
1548 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1549 if (len > *pcchOut) {
1550 ret = E_POINTER;
1551 goto cleanup;
1554 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1555 len--;
1557 cleanup:
1558 *pcchOut = len;
1559 HeapFree(GetProcessHeap(), 0, in);
1560 return ret;
1563 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1565 HKEY newkey;
1566 BOOL j;
1567 INT index;
1568 DWORD value_len, data_len, dwType, i;
1569 WCHAR reg_path[MAX_PATH];
1570 WCHAR value[MAX_PATH], data[MAX_PATH];
1571 WCHAR Wxx, Wyy;
1573 MultiByteToWideChar(0, 0,
1574 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1575 -1, reg_path, MAX_PATH);
1576 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1577 index = 0;
1578 while(value_len = data_len = MAX_PATH,
1579 RegEnumValueW(newkey, index, value, &value_len,
1580 0, &dwType, (LPVOID)data, &data_len) == 0) {
1581 TRACE("guess %d %s is %s\n",
1582 index, debugstr_w(value), debugstr_w(data));
1584 j = FALSE;
1585 for(i=0; i<value_len; i++) {
1586 Wxx = pszIn[i];
1587 Wyy = value[i];
1588 /* remember that TRUE is not-equal */
1589 j = ChrCmpIW(Wxx, Wyy);
1590 if (j) break;
1592 if ((i == value_len) && !j) {
1593 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1594 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1595 RegCloseKey(newkey);
1596 return E_POINTER;
1598 strcpyW(pszOut, data);
1599 strcatW(pszOut, pszIn);
1600 *pcchOut = strlenW(pszOut);
1601 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1602 RegCloseKey(newkey);
1603 return S_OK;
1605 index++;
1607 RegCloseKey(newkey);
1608 return E_FAIL;
1611 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1613 HKEY newkey;
1614 DWORD data_len, dwType;
1615 WCHAR data[MAX_PATH];
1617 static const WCHAR prefix_keyW[] =
1618 {'S','o','f','t','w','a','r','e',
1619 '\\','M','i','c','r','o','s','o','f','t',
1620 '\\','W','i','n','d','o','w','s',
1621 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1622 '\\','U','R','L',
1623 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1625 /* get and prepend default */
1626 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1627 data_len = sizeof(data);
1628 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1629 RegCloseKey(newkey);
1630 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1631 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1632 return E_POINTER;
1634 strcpyW(pszOut, data);
1635 strcatW(pszOut, pszIn);
1636 *pcchOut = strlenW(pszOut);
1637 TRACE("used default %s\n", debugstr_w(pszOut));
1638 return S_OK;
1641 /*************************************************************************
1642 * UrlApplySchemeW [SHLWAPI.@]
1644 * See UrlApplySchemeA.
1646 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1648 PARSEDURLW in_scheme;
1649 DWORD res1;
1650 HRESULT ret;
1652 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1653 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1655 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1657 if (dwFlags & URL_APPLY_GUESSFILE) {
1658 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1659 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1660 strcpyW(pszOut, pszIn);
1661 *pcchOut = strlenW(pszOut);
1662 return S_FALSE;
1665 in_scheme.cbSize = sizeof(in_scheme);
1666 /* See if the base has a scheme */
1667 res1 = ParseURLW(pszIn, &in_scheme);
1668 if (res1) {
1669 /* no scheme in input, need to see if we need to guess */
1670 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1671 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1672 return ret;
1675 else {
1676 /* we have a scheme, see if valid (known scheme) */
1677 if (in_scheme.nScheme) {
1678 /* have valid scheme, so just copy and exit */
1679 if (strlenW(pszIn) + 1 > *pcchOut) {
1680 *pcchOut = strlenW(pszIn) + 1;
1681 return E_POINTER;
1683 strcpyW(pszOut, pszIn);
1684 *pcchOut = strlenW(pszOut);
1685 TRACE("valid scheme, returning copy\n");
1686 return S_OK;
1690 /* If we are here, then either invalid scheme,
1691 * or no scheme and can't/failed guess.
1693 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1694 ((res1 != 0)) ) &&
1695 (dwFlags & URL_APPLY_DEFAULT)) {
1696 /* find and apply default scheme */
1697 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1700 return S_FALSE;
1703 /*************************************************************************
1704 * UrlIsA [SHLWAPI.@]
1706 * Determine if a Url is of a certain class.
1708 * PARAMS
1709 * pszUrl [I] Url to check
1710 * Urlis [I] URLIS_ constant from "shlwapi.h"
1712 * RETURNS
1713 * TRUE if pszUrl belongs to the class type in Urlis.
1714 * FALSE Otherwise.
1716 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1718 PARSEDURLA base;
1719 DWORD res1;
1720 LPCSTR last;
1722 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1724 if(!pszUrl)
1725 return FALSE;
1727 switch (Urlis) {
1729 case URLIS_OPAQUE:
1730 base.cbSize = sizeof(base);
1731 res1 = ParseURLA(pszUrl, &base);
1732 if (res1) return FALSE; /* invalid scheme */
1733 switch (base.nScheme)
1735 case URL_SCHEME_MAILTO:
1736 case URL_SCHEME_SHELL:
1737 case URL_SCHEME_JAVASCRIPT:
1738 case URL_SCHEME_VBSCRIPT:
1739 case URL_SCHEME_ABOUT:
1740 return TRUE;
1742 return FALSE;
1744 case URLIS_FILEURL:
1745 return !StrCmpNA("file:", pszUrl, 5);
1747 case URLIS_DIRECTORY:
1748 last = pszUrl + strlen(pszUrl) - 1;
1749 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1751 case URLIS_URL:
1752 return PathIsURLA(pszUrl);
1754 case URLIS_NOHISTORY:
1755 case URLIS_APPLIABLE:
1756 case URLIS_HASQUERY:
1757 default:
1758 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1760 return FALSE;
1763 /*************************************************************************
1764 * UrlIsW [SHLWAPI.@]
1766 * See UrlIsA.
1768 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1770 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1771 PARSEDURLW base;
1772 DWORD res1;
1773 LPCWSTR last;
1775 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1777 if(!pszUrl)
1778 return FALSE;
1780 switch (Urlis) {
1782 case URLIS_OPAQUE:
1783 base.cbSize = sizeof(base);
1784 res1 = ParseURLW(pszUrl, &base);
1785 if (res1) return FALSE; /* invalid scheme */
1786 switch (base.nScheme)
1788 case URL_SCHEME_MAILTO:
1789 case URL_SCHEME_SHELL:
1790 case URL_SCHEME_JAVASCRIPT:
1791 case URL_SCHEME_VBSCRIPT:
1792 case URL_SCHEME_ABOUT:
1793 return TRUE;
1795 return FALSE;
1797 case URLIS_FILEURL:
1798 return !strncmpW(stemp, pszUrl, 5);
1800 case URLIS_DIRECTORY:
1801 last = pszUrl + strlenW(pszUrl) - 1;
1802 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1804 case URLIS_URL:
1805 return PathIsURLW(pszUrl);
1807 case URLIS_NOHISTORY:
1808 case URLIS_APPLIABLE:
1809 case URLIS_HASQUERY:
1810 default:
1811 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1813 return FALSE;
1816 /*************************************************************************
1817 * UrlIsNoHistoryA [SHLWAPI.@]
1819 * Determine if a Url should not be stored in the users history list.
1821 * PARAMS
1822 * pszUrl [I] Url to check
1824 * RETURNS
1825 * TRUE, if pszUrl should be excluded from the history list,
1826 * FALSE otherwise.
1828 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1830 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1833 /*************************************************************************
1834 * UrlIsNoHistoryW [SHLWAPI.@]
1836 * See UrlIsNoHistoryA.
1838 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1840 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1843 /*************************************************************************
1844 * UrlIsOpaqueA [SHLWAPI.@]
1846 * Determine if a Url is opaque.
1848 * PARAMS
1849 * pszUrl [I] Url to check
1851 * RETURNS
1852 * TRUE if pszUrl is opaque,
1853 * FALSE Otherwise.
1855 * NOTES
1856 * An opaque Url is one that does not start with "<protocol>://".
1858 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1860 return UrlIsA(pszUrl, URLIS_OPAQUE);
1863 /*************************************************************************
1864 * UrlIsOpaqueW [SHLWAPI.@]
1866 * See UrlIsOpaqueA.
1868 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1870 return UrlIsW(pszUrl, URLIS_OPAQUE);
1873 /*************************************************************************
1874 * Scans for characters of type "type" and when not matching found,
1875 * returns pointer to it and length in size.
1877 * Characters tested based on RFC 1738
1879 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1881 static DWORD alwayszero = 0;
1882 BOOL cont = TRUE;
1884 *size = 0;
1886 switch(type){
1888 case SCHEME:
1889 while (cont) {
1890 if ( (islowerW(*start) && isalphaW(*start)) ||
1891 isdigitW(*start) ||
1892 (*start == '+') ||
1893 (*start == '-') ||
1894 (*start == '.')) {
1895 start++;
1896 (*size)++;
1898 else
1899 cont = FALSE;
1901 break;
1903 case USERPASS:
1904 while (cont) {
1905 if ( isalphaW(*start) ||
1906 isdigitW(*start) ||
1907 /* user/password only characters */
1908 (*start == ';') ||
1909 (*start == '?') ||
1910 (*start == '&') ||
1911 (*start == '=') ||
1912 /* *extra* characters */
1913 (*start == '!') ||
1914 (*start == '*') ||
1915 (*start == '\'') ||
1916 (*start == '(') ||
1917 (*start == ')') ||
1918 (*start == ',') ||
1919 /* *safe* characters */
1920 (*start == '$') ||
1921 (*start == '_') ||
1922 (*start == '+') ||
1923 (*start == '-') ||
1924 (*start == '.') ||
1925 (*start == ' ')) {
1926 start++;
1927 (*size)++;
1928 } else if (*start == '%') {
1929 if (isxdigitW(*(start+1)) &&
1930 isxdigitW(*(start+2))) {
1931 start += 3;
1932 *size += 3;
1933 } else
1934 cont = FALSE;
1935 } else
1936 cont = FALSE;
1938 break;
1940 case PORT:
1941 while (cont) {
1942 if (isdigitW(*start)) {
1943 start++;
1944 (*size)++;
1946 else
1947 cont = FALSE;
1949 break;
1951 case HOST:
1952 while (cont) {
1953 if (isalnumW(*start) ||
1954 (*start == '-') ||
1955 (*start == '.') ||
1956 (*start == ' ') ) {
1957 start++;
1958 (*size)++;
1960 else
1961 cont = FALSE;
1963 break;
1964 default:
1965 FIXME("unknown type %d\n", type);
1966 return (LPWSTR)&alwayszero;
1968 /* TRACE("scanned %d characters next char %p<%c>\n",
1969 *size, start, *start); */
1970 return start;
1973 /*************************************************************************
1974 * Attempt to parse URL into pieces.
1976 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1978 LPCWSTR work;
1980 memset(pl, 0, sizeof(WINE_PARSE_URL));
1981 pl->pScheme = pszUrl;
1982 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1983 if (!*work || (*work != ':')) goto ErrorExit;
1984 work++;
1985 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
1986 pl->pUserName = work + 2;
1987 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1988 if (*work == ':' ) {
1989 /* parse password */
1990 work++;
1991 pl->pPassword = work;
1992 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1993 if (*work != '@') {
1994 /* what we just parsed must be the hostname and port
1995 * so reset pointers and clear then let it parse */
1996 pl->szUserName = pl->szPassword = 0;
1997 work = pl->pUserName - 1;
1998 pl->pUserName = pl->pPassword = 0;
2000 } else if (*work == '@') {
2001 /* no password */
2002 pl->szPassword = 0;
2003 pl->pPassword = 0;
2004 } else if (!*work || (*work == '/') || (*work == '.')) {
2005 /* what was parsed was hostname, so reset pointers and let it parse */
2006 pl->szUserName = pl->szPassword = 0;
2007 work = pl->pUserName - 1;
2008 pl->pUserName = pl->pPassword = 0;
2009 } else goto ErrorExit;
2011 /* now start parsing hostname or hostnumber */
2012 work++;
2013 pl->pHostName = work;
2014 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2015 if (*work == ':') {
2016 /* parse port */
2017 work++;
2018 pl->pPort = work;
2019 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2021 if (*work == '/') {
2022 /* see if query string */
2023 pl->pQuery = strchrW(work, '?');
2024 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2026 SuccessExit:
2027 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2028 pl->pScheme, pl->szScheme,
2029 pl->pUserName, pl->szUserName,
2030 pl->pPassword, pl->szPassword,
2031 pl->pHostName, pl->szHostName,
2032 pl->pPort, pl->szPort,
2033 pl->pQuery, pl->szQuery);
2034 return S_OK;
2035 ErrorExit:
2036 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2037 return E_INVALIDARG;
2040 /*************************************************************************
2041 * UrlGetPartA [SHLWAPI.@]
2043 * Retrieve part of a Url.
2045 * PARAMS
2046 * pszIn [I] Url to parse
2047 * pszOut [O] Destination for part of pszIn requested
2048 * pcchOut [I] Size of pszOut
2049 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2050 * needed size of pszOut INCLUDING '\0'.
2051 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2052 * dwFlags [I] URL_ flags from "shlwapi.h"
2054 * RETURNS
2055 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2056 * Failure: An HRESULT error code describing the error.
2058 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2059 DWORD dwPart, DWORD dwFlags)
2061 LPWSTR in, out;
2062 DWORD ret, len, len2;
2064 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2065 return E_INVALIDARG;
2067 in = HeapAlloc(GetProcessHeap(), 0,
2068 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2069 out = in + INTERNET_MAX_URL_LENGTH;
2071 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2073 len = INTERNET_MAX_URL_LENGTH;
2074 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2076 if (FAILED(ret)) {
2077 HeapFree(GetProcessHeap(), 0, in);
2078 return ret;
2081 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2082 if (len2 > *pcchOut) {
2083 *pcchOut = len2+1;
2084 HeapFree(GetProcessHeap(), 0, in);
2085 return E_POINTER;
2087 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2088 *pcchOut = len2-1;
2089 HeapFree(GetProcessHeap(), 0, in);
2090 return ret;
2093 /*************************************************************************
2094 * UrlGetPartW [SHLWAPI.@]
2096 * See UrlGetPartA.
2098 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2099 DWORD dwPart, DWORD dwFlags)
2101 WINE_PARSE_URL pl;
2102 HRESULT ret;
2103 DWORD scheme, size, schsize;
2104 LPCWSTR addr, schaddr;
2106 TRACE("(%s %p %p(%d) %08x %08x)\n",
2107 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2109 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2110 return E_INVALIDARG;
2112 *pszOut = '\0';
2114 addr = strchrW(pszIn, ':');
2115 if(!addr)
2116 scheme = URL_SCHEME_UNKNOWN;
2117 else
2118 scheme = get_scheme_code(pszIn, addr-pszIn);
2120 ret = URL_ParseUrl(pszIn, &pl);
2122 switch (dwPart) {
2123 case URL_PART_SCHEME:
2124 if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
2125 *pcchOut = 0;
2126 return S_FALSE;
2128 addr = pl.pScheme;
2129 size = pl.szScheme;
2130 break;
2132 case URL_PART_HOSTNAME:
2133 switch(scheme) {
2134 case URL_SCHEME_FTP:
2135 case URL_SCHEME_HTTP:
2136 case URL_SCHEME_GOPHER:
2137 case URL_SCHEME_TELNET:
2138 case URL_SCHEME_FILE:
2139 case URL_SCHEME_HTTPS:
2140 break;
2141 default:
2142 *pcchOut = 0;
2143 return E_FAIL;
2146 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2147 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2148 *pcchOut = 0;
2149 return S_FALSE;
2152 if (!pl.szHostName) {
2153 *pcchOut = 0;
2154 return S_FALSE;
2156 addr = pl.pHostName;
2157 size = pl.szHostName;
2158 break;
2160 case URL_PART_USERNAME:
2161 if (!pl.szUserName) {
2162 *pcchOut = 0;
2163 return S_FALSE;
2165 addr = pl.pUserName;
2166 size = pl.szUserName;
2167 break;
2169 case URL_PART_PASSWORD:
2170 if (!pl.szPassword) {
2171 *pcchOut = 0;
2172 return S_FALSE;
2174 addr = pl.pPassword;
2175 size = pl.szPassword;
2176 break;
2178 case URL_PART_PORT:
2179 if (!pl.szPort) {
2180 *pcchOut = 0;
2181 return S_FALSE;
2183 addr = pl.pPort;
2184 size = pl.szPort;
2185 break;
2187 case URL_PART_QUERY:
2188 if (!pl.szQuery) {
2189 *pcchOut = 0;
2190 return S_FALSE;
2192 addr = pl.pQuery;
2193 size = pl.szQuery;
2194 break;
2196 default:
2197 *pcchOut = 0;
2198 return E_INVALIDARG;
2201 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2202 if(!pl.pScheme || !pl.szScheme) {
2203 *pcchOut = 0;
2204 return E_FAIL;
2206 schaddr = pl.pScheme;
2207 schsize = pl.szScheme;
2208 if (*pcchOut < schsize + size + 2) {
2209 *pcchOut = schsize + size + 2;
2210 return E_POINTER;
2212 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2213 pszOut[schsize] = ':';
2214 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2215 pszOut[schsize+1+size] = 0;
2216 *pcchOut = schsize + 1 + size;
2218 else {
2219 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2220 memcpy(pszOut, addr, size*sizeof(WCHAR));
2221 pszOut[size] = 0;
2222 *pcchOut = size;
2224 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2226 return ret;
2229 /*************************************************************************
2230 * PathIsURLA [SHLWAPI.@]
2232 * Check if the given path is a Url.
2234 * PARAMS
2235 * lpszPath [I] Path to check.
2237 * RETURNS
2238 * TRUE if lpszPath is a Url.
2239 * FALSE if lpszPath is NULL or not a Url.
2241 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2243 PARSEDURLA base;
2244 HRESULT hres;
2246 TRACE("%s\n", debugstr_a(lpstrPath));
2248 if (!lpstrPath || !*lpstrPath) return FALSE;
2250 /* get protocol */
2251 base.cbSize = sizeof(base);
2252 hres = ParseURLA(lpstrPath, &base);
2253 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2256 /*************************************************************************
2257 * PathIsURLW [SHLWAPI.@]
2259 * See PathIsURLA.
2261 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2263 PARSEDURLW base;
2264 HRESULT hres;
2266 TRACE("%s\n", debugstr_w(lpstrPath));
2268 if (!lpstrPath || !*lpstrPath) return FALSE;
2270 /* get protocol */
2271 base.cbSize = sizeof(base);
2272 hres = ParseURLW(lpstrPath, &base);
2273 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2276 /*************************************************************************
2277 * UrlCreateFromPathA [SHLWAPI.@]
2279 * See UrlCreateFromPathW
2281 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2283 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2284 WCHAR *urlW = bufW;
2285 UNICODE_STRING pathW;
2286 HRESULT ret;
2287 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2289 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2290 return E_INVALIDARG;
2291 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2292 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2293 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2295 if(ret == S_OK || ret == S_FALSE) {
2296 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2297 if(*pcchUrl > lenA) {
2298 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2299 pszUrl[lenA] = 0;
2300 *pcchUrl = lenA;
2301 } else {
2302 *pcchUrl = lenA + 1;
2303 ret = E_POINTER;
2306 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2307 RtlFreeUnicodeString(&pathW);
2308 return ret;
2311 /*************************************************************************
2312 * UrlCreateFromPathW [SHLWAPI.@]
2314 * Create a Url from a file path.
2316 * PARAMS
2317 * pszPath [I] Path to convert
2318 * pszUrl [O] Destination for the converted Url
2319 * pcchUrl [I/O] Length of pszUrl
2320 * dwReserved [I] Reserved, must be 0
2322 * RETURNS
2323 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2324 * Failure: An HRESULT error code.
2326 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2328 DWORD needed;
2329 HRESULT ret;
2330 WCHAR *pszNewUrl;
2331 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2332 WCHAR three_slashesW[] = {'/','/','/',0};
2333 PARSEDURLW parsed_url;
2335 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2337 /* Validate arguments */
2338 if (dwReserved != 0)
2339 return E_INVALIDARG;
2340 if (!pszUrl || !pcchUrl)
2341 return E_INVALIDARG;
2344 parsed_url.cbSize = sizeof(parsed_url);
2345 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2346 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2347 needed = strlenW(pszPath);
2348 if (needed >= *pcchUrl) {
2349 *pcchUrl = needed + 1;
2350 return E_POINTER;
2351 } else {
2352 *pcchUrl = needed;
2353 strcpyW(pszUrl, pszPath);
2354 return S_FALSE;
2359 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2360 strcpyW(pszNewUrl, file_colonW);
2361 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2362 strcatW(pszNewUrl, three_slashesW);
2363 strcatW(pszNewUrl, pszPath);
2364 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2366 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2367 return ret;
2370 /*************************************************************************
2371 * SHAutoComplete [SHLWAPI.@]
2373 * Enable auto-completion for an edit control.
2375 * PARAMS
2376 * hwndEdit [I] Handle of control to enable auto-completion for
2377 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2379 * RETURNS
2380 * Success: S_OK. Auto-completion is enabled for the control.
2381 * Failure: An HRESULT error code indicating the error.
2383 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2385 FIXME("stub\n");
2386 return S_FALSE;
2389 /*************************************************************************
2390 * MLBuildResURLA [SHLWAPI.405]
2392 * Create a Url pointing to a resource in a module.
2394 * PARAMS
2395 * lpszLibName [I] Name of the module containing the resource
2396 * hMod [I] Callers module handle
2397 * dwFlags [I] Undocumented flags for loading the module
2398 * lpszRes [I] Resource name
2399 * lpszDest [O] Destination for resulting Url
2400 * dwDestLen [I] Length of lpszDest
2402 * RETURNS
2403 * Success: S_OK. lpszDest contains the resource Url.
2404 * Failure: E_INVALIDARG, if any argument is invalid, or
2405 * E_FAIL if dwDestLen is too small.
2407 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2408 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2410 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2411 HRESULT hRet;
2413 if (lpszLibName)
2414 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2416 if (lpszRes)
2417 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2419 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2420 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2422 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2423 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2424 if (SUCCEEDED(hRet) && lpszDest)
2425 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2427 return hRet;
2430 /*************************************************************************
2431 * MLBuildResURLA [SHLWAPI.406]
2433 * See MLBuildResURLA.
2435 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2436 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2438 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2439 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2440 HRESULT hRet = E_FAIL;
2442 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2443 debugstr_w(lpszRes), lpszDest, dwDestLen);
2445 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2446 !lpszDest || (dwFlags && dwFlags != 2))
2447 return E_INVALIDARG;
2449 if (dwDestLen >= szResLen + 1)
2451 dwDestLen -= (szResLen + 1);
2452 memcpy(lpszDest, szRes, sizeof(szRes));
2454 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2456 if (hMod)
2458 WCHAR szBuff[MAX_PATH];
2459 DWORD len;
2461 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2462 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2464 DWORD dwPathLen = strlenW(szBuff) + 1;
2466 if (dwDestLen >= dwPathLen)
2468 DWORD dwResLen;
2470 dwDestLen -= dwPathLen;
2471 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2473 dwResLen = strlenW(lpszRes) + 1;
2474 if (dwDestLen >= dwResLen + 1)
2476 lpszDest[szResLen + dwPathLen-1] = '/';
2477 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2478 hRet = S_OK;
2482 MLFreeLibrary(hMod);
2485 return hRet;
2488 /***********************************************************************
2489 * UrlFixupW [SHLWAPI.462]
2491 * Checks the scheme part of a URL and attempts to correct misspellings.
2493 * PARAMS
2494 * lpszUrl [I] Pointer to the URL to be corrected
2495 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2496 * dwMaxChars [I] Maximum size of corrected URL
2498 * RETURNS
2499 * success: S_OK if URL corrected or already correct
2500 * failure: S_FALSE if unable to correct / COM error code if other error
2503 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2505 DWORD srcLen;
2507 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2509 if (!url)
2510 return E_FAIL;
2512 srcLen = lstrlenW(url) + 1;
2514 /* For now just copy the URL directly */
2515 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2517 return S_OK;