ddraw: Avoid fallout of failing tests.
[wine/multimedia.git] / dlls / shlwapi / url.c
blob0991dad42e2a9bd82f52055c70ac33c654d9f5de
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 "wine/debug.h"
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
47 static const struct {
48 URL_SCHEME scheme_number;
49 WCHAR scheme_name[12];
50 } shlwapi_schemes[] = {
51 {URL_SCHEME_FTP, {'f','t','p',0}},
52 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
53 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
54 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
55 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
56 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
57 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
58 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
59 {URL_SCHEME_FILE, {'f','i','l','e',0}},
60 {URL_SCHEME_MK, {'m','k',0}},
61 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
62 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
63 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
64 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
65 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
66 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
67 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
68 {URL_SCHEME_RES, {'r','e','s',0}},
71 typedef struct {
72 LPCWSTR pScheme; /* [out] start of scheme */
73 DWORD szScheme; /* [out] size of scheme (until colon) */
74 LPCWSTR pUserName; /* [out] start of Username */
75 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
76 LPCWSTR pPassword; /* [out] start of Password */
77 DWORD szPassword; /* [out] size of Password (until "@") */
78 LPCWSTR pHostName; /* [out] start of Hostname */
79 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
80 LPCWSTR pPort; /* [out] start of Port */
81 DWORD szPort; /* [out] size of Port (until "/" or eos) */
82 LPCWSTR pQuery; /* [out] start of Query */
83 DWORD szQuery; /* [out] size of Query (until eos) */
84 } WINE_PARSE_URL;
86 typedef enum {
87 SCHEME,
88 HOST,
89 PORT,
90 USERPASS,
91 } WINE_URL_SCAN_TYPE;
93 static const CHAR hexDigits[] = "0123456789ABCDEF";
95 static const WCHAR fileW[] = {'f','i','l','e','\0'};
97 static const unsigned char HashDataLookup[256] = {
98 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
99 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
100 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
101 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
102 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
103 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
104 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
105 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
106 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
107 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
108 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
109 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
110 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
111 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
112 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
113 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
114 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
115 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
116 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
117 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
119 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
121 int i;
123 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
124 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
125 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
126 return shlwapi_schemes[i].scheme_number;
129 return URL_SCHEME_UNKNOWN;
132 static BOOL URL_JustLocation(LPCWSTR str)
134 while(*str && (*str == L'/')) str++;
135 if (*str) {
136 while (*str && ((*str == L'-') ||
137 (*str == L'.') ||
138 isalnumW(*str))) str++;
139 if (*str == L'/') return FALSE;
141 return TRUE;
145 /*************************************************************************
146 * @ [SHLWAPI.1]
148 * Parse a Url into its constituent parts.
150 * PARAMS
151 * x [I] Url to parse
152 * y [O] Undocumented structure holding the parsed information
154 * RETURNS
155 * Success: S_OK. y contains the parsed Url details.
156 * Failure: An HRESULT error code.
158 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
160 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
161 DWORD cnt, len;
163 y->nScheme = URL_SCHEME_INVALID;
164 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
165 /* FIXME: leading white space generates error of 0x80041001 which
166 * is undefined
168 if (*x <= ' ') return 0x80041001;
169 cnt = 0;
170 y->cchProtocol = 0;
171 y->pszProtocol = x;
172 while (*x) {
173 if (*x == ':') {
174 y->cchProtocol = cnt;
175 cnt = -1;
176 y->pszSuffix = x+1;
177 break;
179 x++;
180 cnt++;
183 /* check for no scheme in string start */
184 /* (apparently schemes *must* be larger than a single character) */
185 if ((*x == '\0') || (y->cchProtocol <= 1)) {
186 y->pszProtocol = NULL;
187 return 0x80041001;
190 /* found scheme, set length of remainder */
191 y->cchSuffix = lstrlenA(y->pszSuffix);
193 len = MultiByteToWideChar(CP_ACP, 0, y->pszProtocol, y->cchProtocol,
194 scheme, sizeof(scheme)/sizeof(WCHAR));
195 y->nScheme = get_scheme_code(scheme, len);
197 return S_OK;
200 /*************************************************************************
201 * @ [SHLWAPI.2]
203 * Unicode version of ParseURLA.
205 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
207 DWORD cnt;
209 y->nScheme = URL_SCHEME_INVALID;
210 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
211 /* FIXME: leading white space generates error of 0x80041001 which
212 * is undefined
214 if (*x <= L' ') return 0x80041001;
215 cnt = 0;
216 y->cchProtocol = 0;
217 y->pszProtocol = x;
218 while (*x) {
219 if (*x == L':') {
220 y->cchProtocol = cnt;
221 cnt = -1;
222 y->pszSuffix = x+1;
223 break;
225 x++;
226 cnt++;
229 /* check for no scheme in string start */
230 /* (apparently schemes *must* be larger than a single character) */
231 if ((*x == L'\0') || (y->cchProtocol <= 1)) {
232 y->pszProtocol = NULL;
233 return 0x80041001;
236 /* found scheme, set length of remainder */
237 y->cchSuffix = lstrlenW(y->pszSuffix);
238 y->nScheme = get_scheme_code(y->pszProtocol, y->cchProtocol);
240 return S_OK;
243 /*************************************************************************
244 * UrlCanonicalizeA [SHLWAPI.@]
246 * Canonicalize a Url.
248 * PARAMS
249 * pszUrl [I] Url to cCanonicalize
250 * pszCanonicalized [O] Destination for converted Url.
251 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
252 * dwFlags [I] Flags controlling the conversion.
254 * RETURNS
255 * Success: S_OK. The pszCanonicalized contains the converted Url.
256 * Failure: E_POINTER, if *pcchCanonicalized is too small.
258 * MSDN incorrectly describes the flags for this function. They should be:
259 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
260 *| URL_ESCAPE_SPACES_ONLY 0x04000000
261 *| URL_ESCAPE_PERCENT 0x00001000
262 *| URL_ESCAPE_UNSAFE 0x10000000
263 *| URL_UNESCAPE 0x10000000
264 *| URL_DONT_SIMPLIFY 0x08000000
265 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
267 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
268 LPDWORD pcchCanonicalized, DWORD dwFlags)
270 LPWSTR base, canonical;
271 DWORD ret, len, len2;
273 TRACE("(%s %p %p 0x%08x) using W version\n",
274 debugstr_a(pszUrl), pszCanonicalized,
275 pcchCanonicalized, dwFlags);
277 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
278 return E_INVALIDARG;
280 base = HeapAlloc(GetProcessHeap(), 0,
281 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
282 canonical = base + INTERNET_MAX_URL_LENGTH;
284 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
285 len = INTERNET_MAX_URL_LENGTH;
287 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
288 if (ret != S_OK) {
289 HeapFree(GetProcessHeap(), 0, base);
290 return ret;
293 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
294 if (len2 > *pcchCanonicalized) {
295 *pcchCanonicalized = len;
296 HeapFree(GetProcessHeap(), 0, base);
297 return E_POINTER;
299 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
300 *pcchCanonicalized, 0, 0);
301 *pcchCanonicalized = len2;
302 HeapFree(GetProcessHeap(), 0, base);
303 return S_OK;
306 /*************************************************************************
307 * UrlCanonicalizeW [SHLWAPI.@]
309 * See UrlCanonicalizeA.
311 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
312 LPDWORD pcchCanonicalized, DWORD dwFlags)
314 HRESULT hr = S_OK;
315 DWORD EscapeFlags;
316 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
317 INT nByteLen, state;
318 DWORD nLen, nWkLen;
319 WCHAR slash = '/';
321 static const WCHAR wszFile[] = {'f','i','l','e',':'};
323 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszCanonicalized,
324 pcchCanonicalized, dwFlags);
326 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
327 return E_INVALIDARG;
329 if(!*pszUrl) {
330 *pszCanonicalized = 0;
331 return S_OK;
334 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
335 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, INTERNET_MAX_URL_LENGTH);
337 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
338 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
339 slash = '\\';
342 * state =
343 * 0 initial 1,3
344 * 1 have 2[+] alnum 2,3
345 * 2 have scheme (found :) 4,6,3
346 * 3 failed (no location)
347 * 4 have // 5,3
348 * 5 have 1[+] alnum 6,3
349 * 6 have location (found /) save root location
352 wk1 = (LPWSTR)pszUrl;
353 wk2 = lpszUrlCpy;
354 state = 0;
356 if(pszUrl[1] == ':') { /* Assume path */
357 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
359 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
360 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
361 if (dwFlags & URL_FILE_USE_PATHURL)
363 slash = '\\';
364 --wk2;
366 else
367 dwFlags |= URL_ESCAPE_UNSAFE;
368 state = 5;
371 while (*wk1) {
372 switch (state) {
373 case 0:
374 if (!isalnumW(*wk1)) {state = 3; break;}
375 *wk2++ = *wk1++;
376 if (!isalnumW(*wk1)) {state = 3; break;}
377 *wk2++ = *wk1++;
378 state = 1;
379 break;
380 case 1:
381 *wk2++ = *wk1;
382 if (*wk1++ == L':') state = 2;
383 break;
384 case 2:
385 if (*wk1 != L'/') {state = 3; break;}
386 *wk2++ = *wk1++;
387 if (*wk1 != L'/') {state = 6; break;}
388 *wk2++ = *wk1++;
389 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
390 wk1++;
391 state = 4;
392 break;
393 case 3:
394 nWkLen = strlenW(wk1);
395 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
396 mp = wk2;
397 wk1 += nWkLen;
398 wk2 += nWkLen;
400 while(mp < wk2) {
401 if(*mp == '/' || *mp == '\\')
402 *mp = slash;
403 mp++;
405 break;
406 case 4:
407 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.') && (*wk1 != ':'))
408 {state = 3; break;}
409 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.') || (*wk1 == ':'))
410 *wk2++ = *wk1++;
411 state = 5;
412 break;
413 case 5:
414 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
415 while(*wk1 == '/' || *wk1 == '\\') {
416 *wk2++ = slash;
417 wk1++;
419 state = 6;
420 break;
421 case 6:
422 if(dwFlags & URL_DONT_SIMPLIFY) {
423 state = 3;
424 break;
427 /* Now at root location, cannot back up any more. */
428 /* "root" will point at the '/' */
430 root = wk2-1;
431 while (*wk1) {
432 mp = strchrW(wk1, '/');
433 mp2 = strchrW(wk1, '\\');
434 if(mp2 && (!mp || mp2 < mp))
435 mp = mp2;
436 if (!mp) {
437 nWkLen = strlenW(wk1);
438 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
439 wk1 += nWkLen;
440 wk2 += nWkLen;
441 continue;
443 nLen = mp - wk1;
444 if(nLen) {
445 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
446 wk2 += nLen;
447 wk1 += nLen;
449 *wk2++ = slash;
450 wk1++;
452 if (*wk1 == L'.') {
453 TRACE("found '/.'\n");
454 if (wk1[1] == '/' || wk1[1] == '\\') {
455 /* case of /./ -> skip the ./ */
456 wk1 += 2;
458 else if (wk1[1] == '.') {
459 /* found /.. look for next / */
460 TRACE("found '/..'\n");
461 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
462 || wk1[2] == '#' || !wk1[2]) {
463 /* case /../ -> need to backup wk2 */
464 TRACE("found '/../'\n");
465 *(wk2-1) = L'\0'; /* set end of string */
466 mp = strrchrW(root, slash);
467 if (mp && (mp >= root)) {
468 /* found valid backup point */
469 wk2 = mp + 1;
470 if(wk1[2] != '/' && wk1[2] != '\\')
471 wk1 += 2;
472 else
473 wk1 += 3;
475 else {
476 /* did not find point, restore '/' */
477 *(wk2-1) = slash;
483 *wk2 = L'\0';
484 break;
485 default:
486 FIXME("how did we get here - state=%d\n", state);
487 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
488 return E_INVALIDARG;
490 *wk2 = L'\0';
491 TRACE("Simplified, orig <%s>, simple <%s>\n",
492 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
494 nLen = lstrlenW(lpszUrlCpy);
495 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
496 lpszUrlCpy[--nLen]=0;
498 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
499 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
501 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
502 URL_ESCAPE_SPACES_ONLY |
503 URL_ESCAPE_PERCENT |
504 URL_DONT_ESCAPE_EXTRA_INFO |
505 URL_ESCAPE_SEGMENT_ONLY ))) {
506 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
507 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
508 EscapeFlags);
509 } else { /* No escaping needed, just copy the string */
510 nLen = lstrlenW(lpszUrlCpy);
511 if(nLen < *pcchCanonicalized)
512 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
513 else {
514 hr = E_POINTER;
515 nLen++;
517 *pcchCanonicalized = nLen;
520 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
522 if (hr == S_OK)
523 TRACE("result %s\n", debugstr_w(pszCanonicalized));
525 return hr;
528 /*************************************************************************
529 * UrlCombineA [SHLWAPI.@]
531 * Combine two Urls.
533 * PARAMS
534 * pszBase [I] Base Url
535 * pszRelative [I] Url to combine with pszBase
536 * pszCombined [O] Destination for combined Url
537 * pcchCombined [O] Destination for length of pszCombined
538 * dwFlags [I] URL_ flags from "shlwapi.h"
540 * RETURNS
541 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
542 * contains its length.
543 * Failure: An HRESULT error code indicating the error.
545 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
546 LPSTR pszCombined, LPDWORD pcchCombined,
547 DWORD dwFlags)
549 LPWSTR base, relative, combined;
550 DWORD ret, len, len2;
552 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
553 debugstr_a(pszBase),debugstr_a(pszRelative),
554 pcchCombined?*pcchCombined:0,dwFlags);
556 if(!pszBase || !pszRelative || !pcchCombined)
557 return E_INVALIDARG;
559 base = HeapAlloc(GetProcessHeap(), 0,
560 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
561 relative = base + INTERNET_MAX_URL_LENGTH;
562 combined = relative + INTERNET_MAX_URL_LENGTH;
564 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
565 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
566 len = *pcchCombined;
568 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
569 if (ret != S_OK) {
570 *pcchCombined = len;
571 HeapFree(GetProcessHeap(), 0, base);
572 return ret;
575 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
576 if (len2 > *pcchCombined) {
577 *pcchCombined = len2;
578 HeapFree(GetProcessHeap(), 0, base);
579 return E_POINTER;
581 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
582 0, 0);
583 *pcchCombined = len2;
584 HeapFree(GetProcessHeap(), 0, base);
585 return S_OK;
588 /*************************************************************************
589 * UrlCombineW [SHLWAPI.@]
591 * See UrlCombineA.
593 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
594 LPWSTR pszCombined, LPDWORD pcchCombined,
595 DWORD dwFlags)
597 PARSEDURLW base, relative;
598 DWORD myflags, sizeloc = 0;
599 DWORD len, res1, res2, process_case = 0;
600 LPWSTR work, preliminary, mbase, mrelative;
601 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
602 static const WCHAR single_slash[] = {'/','\0'};
603 HRESULT ret;
605 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
606 debugstr_w(pszBase),debugstr_w(pszRelative),
607 pcchCombined?*pcchCombined:0,dwFlags);
609 if(!pszBase || !pszRelative || !pcchCombined)
610 return E_INVALIDARG;
612 base.cbSize = sizeof(base);
613 relative.cbSize = sizeof(relative);
615 /* Get space for duplicates of the input and the output */
616 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
617 sizeof(WCHAR));
618 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
619 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
620 *preliminary = L'\0';
622 /* Canonicalize the base input prior to looking for the scheme */
623 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
624 len = INTERNET_MAX_URL_LENGTH;
625 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
627 /* Canonicalize the relative input prior to looking for the scheme */
628 len = INTERNET_MAX_URL_LENGTH;
629 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
631 /* See if the base has a scheme */
632 res1 = ParseURLW(mbase, &base);
633 if (res1) {
634 /* if pszBase has no scheme, then return pszRelative */
635 TRACE("no scheme detected in Base\n");
636 process_case = 1;
638 else do {
639 /* mk is a special case */
640 if(base.nScheme == URL_SCHEME_MK) {
641 static const WCHAR wsz[] = {':',':',0};
643 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
644 if(ptr) {
645 int delta;
647 ptr += 2;
648 delta = ptr-base.pszSuffix;
649 base.cchProtocol += delta;
650 base.pszSuffix += delta;
651 base.cchSuffix -= delta;
655 /* get size of location field (if it exists) */
656 work = (LPWSTR)base.pszSuffix;
657 sizeloc = 0;
658 if (*work++ == L'/') {
659 if (*work++ == L'/') {
660 /* At this point have start of location and
661 * it ends at next '/' or end of string.
663 while(*work && (*work != L'/')) work++;
664 sizeloc = (DWORD)(work - base.pszSuffix);
668 /* Change .sizep2 to not have the last leaf in it,
669 * Note: we need to start after the location (if it exists)
671 work = strrchrW((base.pszSuffix+sizeloc), L'/');
672 if (work) {
673 len = (DWORD)(work - base.pszSuffix + 1);
674 base.cchSuffix = len;
678 * At this point:
679 * .pszSuffix points to location (starting with '//')
680 * .cchSuffix length of location (above) and rest less the last
681 * leaf (if any)
682 * sizeloc length of location (above) up to but not including
683 * the last '/'
686 res2 = ParseURLW(mrelative, &relative);
687 if (res2) {
688 /* no scheme in pszRelative */
689 TRACE("no scheme detected in Relative\n");
690 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
691 relative.cchSuffix = strlenW(mrelative);
692 if (*pszRelative == L':') {
693 /* case that is either left alone or uses pszBase */
694 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
695 process_case = 5;
696 break;
698 process_case = 1;
699 break;
701 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
702 /* case that becomes "file:///" */
703 strcpyW(preliminary, myfilestr);
704 process_case = 1;
705 break;
707 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
708 /* pszRelative has location and rest */
709 process_case = 3;
710 break;
712 if (*mrelative == L'/') {
713 /* case where pszRelative is root to location */
714 process_case = 4;
715 break;
717 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
718 break;
721 /* handle cases where pszRelative has scheme */
722 if ((base.cchProtocol == relative.cchProtocol) &&
723 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
725 /* since the schemes are the same */
726 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
727 /* case where pszRelative replaces location and following */
728 process_case = 3;
729 break;
731 if (*relative.pszSuffix == L'/') {
732 /* case where pszRelative is root to location */
733 process_case = 4;
734 break;
736 /* case where scheme is followed by document path */
737 process_case = 5;
738 break;
740 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
741 /* case where pszRelative replaces scheme, location,
742 * and following and handles PLUGGABLE
744 process_case = 2;
745 break;
747 process_case = 1;
748 break;
749 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
752 ret = S_OK;
753 switch (process_case) {
755 case 1: /*
756 * Return pszRelative appended to what ever is in pszCombined,
757 * (which may the string "file:///"
759 strcatW(preliminary, mrelative);
760 break;
762 case 2: /*
763 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
764 * and pszRelative starts with "//", then append a "/"
766 strcpyW(preliminary, mrelative);
767 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
768 URL_JustLocation(relative.pszSuffix))
769 strcatW(preliminary, single_slash);
770 break;
772 case 3: /*
773 * Return the pszBase scheme with pszRelative. Basically
774 * keeps the scheme and replaces the domain and following.
776 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
777 work = preliminary + base.cchProtocol + 1;
778 strcpyW(work, relative.pszSuffix);
779 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
780 URL_JustLocation(relative.pszSuffix))
781 strcatW(work, single_slash);
782 break;
784 case 4: /*
785 * Return the pszBase scheme and location but everything
786 * after the location is pszRelative. (Replace document
787 * from root on.)
789 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
790 work = preliminary + base.cchProtocol + 1 + sizeloc;
791 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
792 *(work++) = L'/';
793 strcpyW(work, relative.pszSuffix);
794 break;
796 case 5: /*
797 * Return the pszBase without its document (if any) and
798 * append pszRelative after its scheme.
800 memcpy(preliminary, base.pszProtocol,
801 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
802 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
803 if (*work++ != L'/')
804 *(work++) = L'/';
805 strcpyW(work, relative.pszSuffix);
806 break;
808 default:
809 FIXME("How did we get here????? process_case=%d\n", process_case);
810 ret = E_INVALIDARG;
813 if (ret == S_OK) {
814 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
815 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
816 if(SUCCEEDED(ret) && pszCombined) {
817 lstrcpyW(pszCombined, mrelative);
819 TRACE("return-%d len=%d, %s\n",
820 process_case, *pcchCombined, debugstr_w(pszCombined));
822 HeapFree(GetProcessHeap(), 0, preliminary);
823 return ret;
826 /*************************************************************************
827 * UrlEscapeA [SHLWAPI.@]
830 HRESULT WINAPI UrlEscapeA(
831 LPCSTR pszUrl,
832 LPSTR pszEscaped,
833 LPDWORD pcchEscaped,
834 DWORD dwFlags)
836 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
837 WCHAR *escapedW = bufW;
838 UNICODE_STRING urlW;
839 HRESULT ret;
840 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
842 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
843 return E_INVALIDARG;
844 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
845 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
846 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
848 if(ret == S_OK) {
849 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
850 if(pszEscaped && *pcchEscaped > lenA) {
851 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
852 pszEscaped[lenA] = 0;
853 *pcchEscaped = lenA;
854 } else {
855 *pcchEscaped = lenA + 1;
856 ret = E_INVALIDARG;
859 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
860 RtlFreeUnicodeString(&urlW);
861 return ret;
864 #define WINE_URL_BASH_AS_SLASH 0x01
865 #define WINE_URL_COLLAPSE_SLASHES 0x02
866 #define WINE_URL_ESCAPE_SLASH 0x04
867 #define WINE_URL_ESCAPE_HASH 0x08
868 #define WINE_URL_ESCAPE_QUESTION 0x10
869 #define WINE_URL_STOP_ON_HASH 0x20
870 #define WINE_URL_STOP_ON_QUESTION 0x40
872 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
875 if (isalnumW(ch))
876 return FALSE;
878 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
879 if(ch == ' ')
880 return TRUE;
881 else
882 return FALSE;
885 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
886 return TRUE;
888 if (ch <= 31 || ch >= 127)
889 return TRUE;
891 else {
892 switch (ch) {
893 case ' ':
894 case '<':
895 case '>':
896 case '\"':
897 case '{':
898 case '}':
899 case '|':
900 case '\\':
901 case '^':
902 case ']':
903 case '[':
904 case '`':
905 case '&':
906 return TRUE;
908 case '/':
909 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
910 return FALSE;
912 case '?':
913 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
914 return FALSE;
916 case '#':
917 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
918 return FALSE;
920 default:
921 return FALSE;
927 /*************************************************************************
928 * UrlEscapeW [SHLWAPI.@]
930 * Converts unsafe characters in a Url into escape sequences.
932 * PARAMS
933 * pszUrl [I] Url to modify
934 * pszEscaped [O] Destination for modified Url
935 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
936 * dwFlags [I] URL_ flags from "shlwapi.h"
938 * RETURNS
939 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
940 * contains its length.
941 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
942 * pcchEscaped is set to the required length.
944 * Converts unsafe characters into their escape sequences.
946 * NOTES
947 * - By default this function stops converting at the first '?' or
948 * '#' character.
949 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
950 * converted, but the conversion continues past a '?' or '#'.
951 * - Note that this function did not work well (or at all) in shlwapi version 4.
953 * BUGS
954 * Only the following flags are implemented:
955 *| URL_ESCAPE_SPACES_ONLY
956 *| URL_DONT_ESCAPE_EXTRA_INFO
957 *| URL_ESCAPE_SEGMENT_ONLY
958 *| URL_ESCAPE_PERCENT
960 HRESULT WINAPI UrlEscapeW(
961 LPCWSTR pszUrl,
962 LPWSTR pszEscaped,
963 LPDWORD pcchEscaped,
964 DWORD dwFlags)
966 LPCWSTR src;
967 DWORD needed = 0, ret;
968 BOOL stop_escaping = FALSE;
969 WCHAR next[5], *dst = pszEscaped;
970 INT len;
971 PARSEDURLW parsed_url;
972 DWORD int_flags;
973 DWORD slashes = 0;
974 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
976 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
977 pcchEscaped, dwFlags);
979 if(!pszUrl || !pcchEscaped)
980 return E_INVALIDARG;
982 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
983 URL_ESCAPE_SEGMENT_ONLY |
984 URL_DONT_ESCAPE_EXTRA_INFO |
985 URL_ESCAPE_PERCENT))
986 FIXME("Unimplemented flags: %08x\n", dwFlags);
988 /* fix up flags */
989 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
990 /* if SPACES_ONLY specified, reset the other controls */
991 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
992 URL_ESCAPE_PERCENT |
993 URL_ESCAPE_SEGMENT_ONLY);
995 else
996 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
997 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1000 int_flags = 0;
1001 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1002 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1003 } else {
1004 parsed_url.cbSize = sizeof(parsed_url);
1005 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1006 parsed_url.nScheme = URL_SCHEME_INVALID;
1008 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1010 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1011 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1013 switch(parsed_url.nScheme) {
1014 case URL_SCHEME_FILE:
1015 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1016 int_flags &= ~WINE_URL_STOP_ON_HASH;
1017 break;
1019 case URL_SCHEME_HTTP:
1020 case URL_SCHEME_HTTPS:
1021 int_flags |= WINE_URL_BASH_AS_SLASH;
1022 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1023 int_flags |= WINE_URL_ESCAPE_SLASH;
1024 break;
1026 case URL_SCHEME_MAILTO:
1027 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1028 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1029 break;
1031 case URL_SCHEME_INVALID:
1032 break;
1034 case URL_SCHEME_FTP:
1035 default:
1036 if(parsed_url.pszSuffix[0] != '/')
1037 int_flags |= WINE_URL_ESCAPE_SLASH;
1038 break;
1042 for(src = pszUrl; *src; ) {
1043 WCHAR cur = *src;
1044 len = 0;
1046 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1047 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1048 while(cur == '/' || cur == '\\') {
1049 slashes++;
1050 cur = *++src;
1052 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1053 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1054 src += localhost_len + 1;
1055 slashes = 3;
1058 switch(slashes) {
1059 case 1:
1060 case 3:
1061 next[0] = next[1] = next[2] = '/';
1062 len = 3;
1063 break;
1064 case 0:
1065 len = 0;
1066 break;
1067 default:
1068 next[0] = next[1] = '/';
1069 len = 2;
1070 break;
1073 if(len == 0) {
1075 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1076 stop_escaping = TRUE;
1078 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1079 stop_escaping = TRUE;
1081 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1083 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1084 next[0] = L'%';
1085 next[1] = hexDigits[(cur >> 4) & 0xf];
1086 next[2] = hexDigits[cur & 0xf];
1087 len = 3;
1088 } else {
1089 next[0] = cur;
1090 len = 1;
1092 src++;
1095 if(needed + len <= *pcchEscaped) {
1096 memcpy(dst, next, len*sizeof(WCHAR));
1097 dst += len;
1099 needed += len;
1102 if(needed < *pcchEscaped) {
1103 *dst = '\0';
1104 ret = S_OK;
1105 } else {
1106 needed++; /* add one for the '\0' */
1107 ret = E_POINTER;
1109 *pcchEscaped = needed;
1110 return ret;
1114 /*************************************************************************
1115 * UrlUnescapeA [SHLWAPI.@]
1117 * Converts Url escape sequences back to ordinary characters.
1119 * PARAMS
1120 * pszUrl [I/O] Url to convert
1121 * pszUnescaped [O] Destination for converted Url
1122 * pcchUnescaped [I/O] Size of output string
1123 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1125 * RETURNS
1126 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1127 * dwFlags includes URL_ESCAPE_INPLACE.
1128 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1129 * this case pcchUnescaped is set to the size required.
1130 * NOTES
1131 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1132 * the first occurrence of either a '?' or '#' character.
1134 HRESULT WINAPI UrlUnescapeA(
1135 LPSTR pszUrl,
1136 LPSTR pszUnescaped,
1137 LPDWORD pcchUnescaped,
1138 DWORD dwFlags)
1140 char *dst, next;
1141 LPCSTR src;
1142 HRESULT ret;
1143 DWORD needed;
1144 BOOL stop_unescaping = FALSE;
1146 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1147 pcchUnescaped, dwFlags);
1149 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1150 return E_INVALIDARG;
1152 if(dwFlags & URL_UNESCAPE_INPLACE)
1153 dst = pszUrl;
1154 else
1155 dst = pszUnescaped;
1157 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1158 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1159 (*src == '#' || *src == '?')) {
1160 stop_unescaping = TRUE;
1161 next = *src;
1162 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1163 && stop_unescaping == FALSE) {
1164 INT ih;
1165 char buf[3];
1166 memcpy(buf, src + 1, 2);
1167 buf[2] = '\0';
1168 ih = strtol(buf, NULL, 16);
1169 next = (CHAR) ih;
1170 src += 2; /* Advance to end of escape */
1171 } else
1172 next = *src;
1174 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1175 *dst++ = next;
1178 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1179 *dst = '\0';
1180 ret = S_OK;
1181 } else {
1182 needed++; /* add one for the '\0' */
1183 ret = E_POINTER;
1185 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1186 *pcchUnescaped = needed;
1188 if (ret == S_OK) {
1189 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1190 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1193 return ret;
1196 /*************************************************************************
1197 * UrlUnescapeW [SHLWAPI.@]
1199 * See UrlUnescapeA.
1201 HRESULT WINAPI UrlUnescapeW(
1202 LPWSTR pszUrl,
1203 LPWSTR pszUnescaped,
1204 LPDWORD pcchUnescaped,
1205 DWORD dwFlags)
1207 WCHAR *dst, next;
1208 LPCWSTR src;
1209 HRESULT ret;
1210 DWORD needed;
1211 BOOL stop_unescaping = FALSE;
1213 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1214 pcchUnescaped, dwFlags);
1216 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1217 return E_INVALIDARG;
1219 if(dwFlags & URL_UNESCAPE_INPLACE)
1220 dst = pszUrl;
1221 else
1222 dst = pszUnescaped;
1224 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1225 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1226 (*src == L'#' || *src == L'?')) {
1227 stop_unescaping = TRUE;
1228 next = *src;
1229 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1230 && stop_unescaping == FALSE) {
1231 INT ih;
1232 WCHAR buf[5] = {'0','x',0};
1233 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1234 buf[4] = 0;
1235 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1236 next = (WCHAR) ih;
1237 src += 2; /* Advance to end of escape */
1238 } else
1239 next = *src;
1241 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1242 *dst++ = next;
1245 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1246 *dst = L'\0';
1247 ret = S_OK;
1248 } else {
1249 needed++; /* add one for the '\0' */
1250 ret = E_POINTER;
1252 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1253 *pcchUnescaped = needed;
1255 if (ret == S_OK) {
1256 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1257 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1260 return ret;
1263 /*************************************************************************
1264 * UrlGetLocationA [SHLWAPI.@]
1266 * Get the location from a Url.
1268 * PARAMS
1269 * pszUrl [I] Url to get the location from
1271 * RETURNS
1272 * A pointer to the start of the location in pszUrl, or NULL if there is
1273 * no location.
1275 * NOTES
1276 * - MSDN erroneously states that "The location is the segment of the Url
1277 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1278 * stop at '?' and always return a NULL in this case.
1279 * - MSDN also erroneously states that "If a file URL has a query string,
1280 * the returned string is the query string". In all tested cases, if the
1281 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1282 *| Result Url
1283 *| ------ ---
1284 *| NULL file://aa/b/cd#hohoh
1285 *| #hohoh http://aa/b/cd#hohoh
1286 *| NULL fi://aa/b/cd#hohoh
1287 *| #hohoh ff://aa/b/cd#hohoh
1289 LPCSTR WINAPI UrlGetLocationA(
1290 LPCSTR pszUrl)
1292 PARSEDURLA base;
1293 DWORD res1;
1295 base.cbSize = sizeof(base);
1296 res1 = ParseURLA(pszUrl, &base);
1297 if (res1) return NULL; /* invalid scheme */
1299 /* if scheme is file: then never return pointer */
1300 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1302 /* Look for '#' and return its addr */
1303 return strchr(base.pszSuffix, '#');
1306 /*************************************************************************
1307 * UrlGetLocationW [SHLWAPI.@]
1309 * See UrlGetLocationA.
1311 LPCWSTR WINAPI UrlGetLocationW(
1312 LPCWSTR pszUrl)
1314 PARSEDURLW base;
1315 DWORD res1;
1317 base.cbSize = sizeof(base);
1318 res1 = ParseURLW(pszUrl, &base);
1319 if (res1) return NULL; /* invalid scheme */
1321 /* if scheme is file: then never return pointer */
1322 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1324 /* Look for '#' and return its addr */
1325 return strchrW(base.pszSuffix, L'#');
1328 /*************************************************************************
1329 * UrlCompareA [SHLWAPI.@]
1331 * Compare two Urls.
1333 * PARAMS
1334 * pszUrl1 [I] First Url to compare
1335 * pszUrl2 [I] Url to compare to pszUrl1
1336 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1338 * RETURNS
1339 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1340 * than, equal to, or less than pszUrl1 respectively.
1342 INT WINAPI UrlCompareA(
1343 LPCSTR pszUrl1,
1344 LPCSTR pszUrl2,
1345 BOOL fIgnoreSlash)
1347 INT ret, len, len1, len2;
1349 if (!fIgnoreSlash)
1350 return strcmp(pszUrl1, pszUrl2);
1351 len1 = strlen(pszUrl1);
1352 if (pszUrl1[len1-1] == '/') len1--;
1353 len2 = strlen(pszUrl2);
1354 if (pszUrl2[len2-1] == '/') len2--;
1355 if (len1 == len2)
1356 return strncmp(pszUrl1, pszUrl2, len1);
1357 len = min(len1, len2);
1358 ret = strncmp(pszUrl1, pszUrl2, len);
1359 if (ret) return ret;
1360 if (len1 > len2) return 1;
1361 return -1;
1364 /*************************************************************************
1365 * UrlCompareW [SHLWAPI.@]
1367 * See UrlCompareA.
1369 INT WINAPI UrlCompareW(
1370 LPCWSTR pszUrl1,
1371 LPCWSTR pszUrl2,
1372 BOOL fIgnoreSlash)
1374 INT ret;
1375 size_t len, len1, len2;
1377 if (!fIgnoreSlash)
1378 return strcmpW(pszUrl1, pszUrl2);
1379 len1 = strlenW(pszUrl1);
1380 if (pszUrl1[len1-1] == '/') len1--;
1381 len2 = strlenW(pszUrl2);
1382 if (pszUrl2[len2-1] == '/') len2--;
1383 if (len1 == len2)
1384 return strncmpW(pszUrl1, pszUrl2, len1);
1385 len = min(len1, len2);
1386 ret = strncmpW(pszUrl1, pszUrl2, len);
1387 if (ret) return ret;
1388 if (len1 > len2) return 1;
1389 return -1;
1392 /*************************************************************************
1393 * HashData [SHLWAPI.@]
1395 * Hash an input block into a variable sized digest.
1397 * PARAMS
1398 * lpSrc [I] Input block
1399 * nSrcLen [I] Length of lpSrc
1400 * lpDest [I] Output for hash digest
1401 * nDestLen [I] Length of lpDest
1403 * RETURNS
1404 * Success: TRUE. lpDest is filled with the computed hash value.
1405 * Failure: FALSE, if any argument is invalid.
1407 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1408 unsigned char *lpDest, DWORD nDestLen)
1410 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1412 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1413 IsBadWritePtr(lpDest, nDestLen))
1414 return E_INVALIDARG;
1416 while (destCount >= 0)
1418 lpDest[destCount] = (destCount & 0xff);
1419 destCount--;
1422 while (srcCount >= 0)
1424 destCount = nDestLen - 1;
1425 while (destCount >= 0)
1427 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1428 destCount--;
1430 srcCount--;
1432 return S_OK;
1435 /*************************************************************************
1436 * UrlHashA [SHLWAPI.@]
1438 * Produce a Hash from a Url.
1440 * PARAMS
1441 * pszUrl [I] Url to hash
1442 * lpDest [O] Destinationh for hash
1443 * nDestLen [I] Length of lpDest
1445 * RETURNS
1446 * Success: S_OK. lpDest is filled with the computed hash value.
1447 * Failure: E_INVALIDARG, if any argument is invalid.
1449 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1451 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1452 return E_INVALIDARG;
1454 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1455 return S_OK;
1458 /*************************************************************************
1459 * UrlHashW [SHLWAPI.@]
1461 * See UrlHashA.
1463 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1465 char szUrl[MAX_PATH];
1467 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1469 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1470 return E_INVALIDARG;
1472 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1473 * return the same digests for the same URL.
1475 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1476 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1477 return S_OK;
1480 /*************************************************************************
1481 * UrlApplySchemeA [SHLWAPI.@]
1483 * Apply a scheme to a Url.
1485 * PARAMS
1486 * pszIn [I] Url to apply scheme to
1487 * pszOut [O] Destination for modified Url
1488 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1489 * dwFlags [I] URL_ flags from "shlwapi.h"
1491 * RETURNS
1492 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1493 * Failure: An HRESULT error code describing the error.
1495 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1497 LPWSTR in, out;
1498 DWORD ret, len, len2;
1500 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1501 debugstr_a(pszIn), *pcchOut, dwFlags);
1503 in = HeapAlloc(GetProcessHeap(), 0,
1504 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1505 out = in + INTERNET_MAX_URL_LENGTH;
1507 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1508 len = INTERNET_MAX_URL_LENGTH;
1510 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1511 if ((ret != S_OK) && (ret != S_FALSE)) {
1512 HeapFree(GetProcessHeap(), 0, in);
1513 return ret;
1516 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1517 if (len2 > *pcchOut) {
1518 *pcchOut = len2;
1519 HeapFree(GetProcessHeap(), 0, in);
1520 return E_POINTER;
1522 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1523 *pcchOut = len2;
1524 HeapFree(GetProcessHeap(), 0, in);
1525 return ret;
1528 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1530 HKEY newkey;
1531 BOOL j;
1532 INT index;
1533 DWORD value_len, data_len, dwType, i;
1534 WCHAR reg_path[MAX_PATH];
1535 WCHAR value[MAX_PATH], data[MAX_PATH];
1536 WCHAR Wxx, Wyy;
1538 MultiByteToWideChar(0, 0,
1539 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1540 -1, reg_path, MAX_PATH);
1541 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1542 index = 0;
1543 while(value_len = data_len = MAX_PATH,
1544 RegEnumValueW(newkey, index, value, &value_len,
1545 0, &dwType, (LPVOID)data, &data_len) == 0) {
1546 TRACE("guess %d %s is %s\n",
1547 index, debugstr_w(value), debugstr_w(data));
1549 j = FALSE;
1550 for(i=0; i<value_len; i++) {
1551 Wxx = pszIn[i];
1552 Wyy = value[i];
1553 /* remember that TRUE is not-equal */
1554 j = ChrCmpIW(Wxx, Wyy);
1555 if (j) break;
1557 if ((i == value_len) && !j) {
1558 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1559 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1560 RegCloseKey(newkey);
1561 return E_POINTER;
1563 strcpyW(pszOut, data);
1564 strcatW(pszOut, pszIn);
1565 *pcchOut = strlenW(pszOut);
1566 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1567 RegCloseKey(newkey);
1568 return S_OK;
1570 index++;
1572 RegCloseKey(newkey);
1573 return -1;
1576 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1578 HKEY newkey;
1579 DWORD data_len, dwType;
1580 WCHAR reg_path[MAX_PATH];
1581 WCHAR value[MAX_PATH], data[MAX_PATH];
1583 /* get and prepend default */
1584 MultiByteToWideChar(0, 0,
1585 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1586 -1, reg_path, MAX_PATH);
1587 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1588 data_len = MAX_PATH;
1589 value[0] = L'@';
1590 value[1] = L'\0';
1591 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1592 RegCloseKey(newkey);
1593 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1594 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1595 return E_POINTER;
1597 strcpyW(pszOut, data);
1598 strcatW(pszOut, pszIn);
1599 *pcchOut = strlenW(pszOut);
1600 TRACE("used default %s\n", debugstr_w(pszOut));
1601 return S_OK;
1604 /*************************************************************************
1605 * UrlApplySchemeW [SHLWAPI.@]
1607 * See UrlApplySchemeA.
1609 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1611 PARSEDURLW in_scheme;
1612 DWORD res1;
1613 HRESULT ret;
1615 TRACE("(in %s, out size %d, flags %08x)\n",
1616 debugstr_w(pszIn), *pcchOut, dwFlags);
1618 if (dwFlags & URL_APPLY_GUESSFILE) {
1619 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1620 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1621 strcpyW(pszOut, pszIn);
1622 *pcchOut = strlenW(pszOut);
1623 return S_FALSE;
1626 in_scheme.cbSize = sizeof(in_scheme);
1627 /* See if the base has a scheme */
1628 res1 = ParseURLW(pszIn, &in_scheme);
1629 if (res1) {
1630 /* no scheme in input, need to see if we need to guess */
1631 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1632 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1633 return ret;
1636 else {
1637 /* we have a scheme, see if valid (known scheme) */
1638 if (in_scheme.nScheme) {
1639 /* have valid scheme, so just copy and exit */
1640 if (strlenW(pszIn) + 1 > *pcchOut) {
1641 *pcchOut = strlenW(pszIn) + 1;
1642 return E_POINTER;
1644 strcpyW(pszOut, pszIn);
1645 *pcchOut = strlenW(pszOut);
1646 TRACE("valid scheme, returning copy\n");
1647 return S_OK;
1651 /* If we are here, then either invalid scheme,
1652 * or no scheme and can't/failed guess.
1654 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1655 ((res1 != 0)) ) &&
1656 (dwFlags & URL_APPLY_DEFAULT)) {
1657 /* find and apply default scheme */
1658 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1661 /* just copy and give proper return code */
1662 if (strlenW(pszIn) + 1 > *pcchOut) {
1663 *pcchOut = strlenW(pszIn) + 1;
1664 return E_POINTER;
1666 strcpyW(pszOut, pszIn);
1667 *pcchOut = strlenW(pszOut);
1668 TRACE("returning copy, left alone\n");
1669 return S_FALSE;
1672 /*************************************************************************
1673 * UrlIsA [SHLWAPI.@]
1675 * Determine if a Url is of a certain class.
1677 * PARAMS
1678 * pszUrl [I] Url to check
1679 * Urlis [I] URLIS_ constant from "shlwapi.h"
1681 * RETURNS
1682 * TRUE if pszUrl belongs to the class type in Urlis.
1683 * FALSE Otherwise.
1685 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1687 PARSEDURLA base;
1688 DWORD res1;
1689 LPCSTR last;
1691 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1693 switch (Urlis) {
1695 case URLIS_OPAQUE:
1696 base.cbSize = sizeof(base);
1697 res1 = ParseURLA(pszUrl, &base);
1698 if (res1) return FALSE; /* invalid scheme */
1699 switch (base.nScheme)
1701 case URL_SCHEME_MAILTO:
1702 case URL_SCHEME_SHELL:
1703 case URL_SCHEME_JAVASCRIPT:
1704 case URL_SCHEME_VBSCRIPT:
1705 case URL_SCHEME_ABOUT:
1706 return TRUE;
1708 return FALSE;
1710 case URLIS_FILEURL:
1711 return !StrCmpNA("file:", pszUrl, 5);
1713 case URLIS_DIRECTORY:
1714 last = pszUrl + strlen(pszUrl) - 1;
1715 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1717 case URLIS_URL:
1718 return PathIsURLA(pszUrl);
1720 case URLIS_NOHISTORY:
1721 case URLIS_APPLIABLE:
1722 case URLIS_HASQUERY:
1723 default:
1724 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1726 return FALSE;
1729 /*************************************************************************
1730 * UrlIsW [SHLWAPI.@]
1732 * See UrlIsA.
1734 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1736 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1737 PARSEDURLW base;
1738 DWORD res1;
1739 LPCWSTR last;
1741 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1743 switch (Urlis) {
1745 case URLIS_OPAQUE:
1746 base.cbSize = sizeof(base);
1747 res1 = ParseURLW(pszUrl, &base);
1748 if (res1) return FALSE; /* invalid scheme */
1749 switch (base.nScheme)
1751 case URL_SCHEME_MAILTO:
1752 case URL_SCHEME_SHELL:
1753 case URL_SCHEME_JAVASCRIPT:
1754 case URL_SCHEME_VBSCRIPT:
1755 case URL_SCHEME_ABOUT:
1756 return TRUE;
1758 return FALSE;
1760 case URLIS_FILEURL:
1761 return !strncmpW(stemp, pszUrl, 5);
1763 case URLIS_DIRECTORY:
1764 last = pszUrl + strlenW(pszUrl) - 1;
1765 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1767 case URLIS_URL:
1768 return PathIsURLW(pszUrl);
1770 case URLIS_NOHISTORY:
1771 case URLIS_APPLIABLE:
1772 case URLIS_HASQUERY:
1773 default:
1774 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1776 return FALSE;
1779 /*************************************************************************
1780 * UrlIsNoHistoryA [SHLWAPI.@]
1782 * Determine if a Url should not be stored in the users history list.
1784 * PARAMS
1785 * pszUrl [I] Url to check
1787 * RETURNS
1788 * TRUE, if pszUrl should be excluded from the history list,
1789 * FALSE otherwise.
1791 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1793 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1796 /*************************************************************************
1797 * UrlIsNoHistoryW [SHLWAPI.@]
1799 * See UrlIsNoHistoryA.
1801 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1803 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1806 /*************************************************************************
1807 * UrlIsOpaqueA [SHLWAPI.@]
1809 * Determine if a Url is opaque.
1811 * PARAMS
1812 * pszUrl [I] Url to check
1814 * RETURNS
1815 * TRUE if pszUrl is opaque,
1816 * FALSE Otherwise.
1818 * NOTES
1819 * An opaque Url is one that does not start with "<protocol>://".
1821 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1823 return UrlIsA(pszUrl, URLIS_OPAQUE);
1826 /*************************************************************************
1827 * UrlIsOpaqueW [SHLWAPI.@]
1829 * See UrlIsOpaqueA.
1831 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1833 return UrlIsW(pszUrl, URLIS_OPAQUE);
1836 /*************************************************************************
1837 * Scans for characters of type "type" and when not matching found,
1838 * returns pointer to it and length in size.
1840 * Characters tested based on RFC 1738
1842 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1844 static DWORD alwayszero = 0;
1845 BOOL cont = TRUE;
1847 *size = 0;
1849 switch(type){
1851 case SCHEME:
1852 while (cont) {
1853 if ( (islowerW(*start) && isalphaW(*start)) ||
1854 isdigitW(*start) ||
1855 (*start == L'+') ||
1856 (*start == L'-') ||
1857 (*start == L'.')) {
1858 start++;
1859 (*size)++;
1861 else
1862 cont = FALSE;
1864 break;
1866 case USERPASS:
1867 while (cont) {
1868 if ( isalphaW(*start) ||
1869 isdigitW(*start) ||
1870 /* user/password only characters */
1871 (*start == L';') ||
1872 (*start == L'?') ||
1873 (*start == L'&') ||
1874 (*start == L'=') ||
1875 /* *extra* characters */
1876 (*start == L'!') ||
1877 (*start == L'*') ||
1878 (*start == L'\'') ||
1879 (*start == L'(') ||
1880 (*start == L')') ||
1881 (*start == L',') ||
1882 /* *safe* characters */
1883 (*start == L'$') ||
1884 (*start == L'_') ||
1885 (*start == L'+') ||
1886 (*start == L'-') ||
1887 (*start == L'.')) {
1888 start++;
1889 (*size)++;
1890 } else if (*start == L'%') {
1891 if (isxdigitW(*(start+1)) &&
1892 isxdigitW(*(start+2))) {
1893 start += 3;
1894 *size += 3;
1895 } else
1896 cont = FALSE;
1897 } else
1898 cont = FALSE;
1900 break;
1902 case PORT:
1903 while (cont) {
1904 if (isdigitW(*start)) {
1905 start++;
1906 (*size)++;
1908 else
1909 cont = FALSE;
1911 break;
1913 case HOST:
1914 while (cont) {
1915 if (isalnumW(*start) ||
1916 (*start == L'-') ||
1917 (*start == L'.') ) {
1918 start++;
1919 (*size)++;
1921 else
1922 cont = FALSE;
1924 break;
1925 default:
1926 FIXME("unknown type %d\n", type);
1927 return (LPWSTR)&alwayszero;
1929 /* TRACE("scanned %d characters next char %p<%c>\n",
1930 *size, start, *start); */
1931 return start;
1934 /*************************************************************************
1935 * Attempt to parse URL into pieces.
1937 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1939 LPCWSTR work;
1941 memset(pl, 0, sizeof(WINE_PARSE_URL));
1942 pl->pScheme = pszUrl;
1943 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1944 if (!*work || (*work != L':')) goto ErrorExit;
1945 work++;
1946 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1947 pl->pUserName = work + 2;
1948 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1949 if (*work == L':' ) {
1950 /* parse password */
1951 work++;
1952 pl->pPassword = work;
1953 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1954 if (*work != L'@') {
1955 /* what we just parsed must be the hostname and port
1956 * so reset pointers and clear then let it parse */
1957 pl->szUserName = pl->szPassword = 0;
1958 work = pl->pUserName - 1;
1959 pl->pUserName = pl->pPassword = 0;
1961 } else if (*work == L'@') {
1962 /* no password */
1963 pl->szPassword = 0;
1964 pl->pPassword = 0;
1965 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1966 /* what was parsed was hostname, so reset pointers and let it parse */
1967 pl->szUserName = pl->szPassword = 0;
1968 work = pl->pUserName - 1;
1969 pl->pUserName = pl->pPassword = 0;
1970 } else goto ErrorExit;
1972 /* now start parsing hostname or hostnumber */
1973 work++;
1974 pl->pHostName = work;
1975 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1976 if (*work == L':') {
1977 /* parse port */
1978 work++;
1979 pl->pPort = work;
1980 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1982 if (*work == L'/') {
1983 /* see if query string */
1984 pl->pQuery = strchrW(work, L'?');
1985 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1987 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1988 pl->pScheme, pl->szScheme,
1989 pl->pUserName, pl->szUserName,
1990 pl->pPassword, pl->szPassword,
1991 pl->pHostName, pl->szHostName,
1992 pl->pPort, pl->szPort,
1993 pl->pQuery, pl->szQuery);
1994 return S_OK;
1995 ErrorExit:
1996 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1997 return E_INVALIDARG;
2000 /*************************************************************************
2001 * UrlGetPartA [SHLWAPI.@]
2003 * Retrieve part of a Url.
2005 * PARAMS
2006 * pszIn [I] Url to parse
2007 * pszOut [O] Destination for part of pszIn requested
2008 * pcchOut [I] Size of pszOut
2009 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
2010 * needed size of pszOut INCLUDING '\0'.
2011 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2012 * dwFlags [I] URL_ flags from "shlwapi.h"
2014 * RETURNS
2015 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2016 * Failure: An HRESULT error code describing the error.
2018 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2019 DWORD dwPart, DWORD dwFlags)
2021 LPWSTR in, out;
2022 DWORD ret, len, len2;
2024 in = HeapAlloc(GetProcessHeap(), 0,
2025 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2026 out = in + INTERNET_MAX_URL_LENGTH;
2028 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2030 len = INTERNET_MAX_URL_LENGTH;
2031 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2033 if (ret != S_OK) {
2034 HeapFree(GetProcessHeap(), 0, in);
2035 return ret;
2038 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2039 if (len2 > *pcchOut) {
2040 *pcchOut = len2;
2041 HeapFree(GetProcessHeap(), 0, in);
2042 return E_POINTER;
2044 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2045 *pcchOut = len2;
2046 HeapFree(GetProcessHeap(), 0, in);
2047 return S_OK;
2050 /*************************************************************************
2051 * UrlGetPartW [SHLWAPI.@]
2053 * See UrlGetPartA.
2055 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2056 DWORD dwPart, DWORD dwFlags)
2058 WINE_PARSE_URL pl;
2059 HRESULT ret;
2060 DWORD size, schsize;
2061 LPCWSTR addr, schaddr;
2063 TRACE("(%s %p %p(%d) %08x %08x)\n",
2064 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2066 ret = URL_ParseUrl(pszIn, &pl);
2067 if (!ret) {
2068 schaddr = pl.pScheme;
2069 schsize = pl.szScheme;
2071 switch (dwPart) {
2072 case URL_PART_SCHEME:
2073 if (!pl.szScheme) return E_INVALIDARG;
2074 addr = pl.pScheme;
2075 size = pl.szScheme;
2076 break;
2078 case URL_PART_HOSTNAME:
2079 if (!pl.szHostName) return E_INVALIDARG;
2080 addr = pl.pHostName;
2081 size = pl.szHostName;
2082 break;
2084 case URL_PART_USERNAME:
2085 if (!pl.szUserName) return E_INVALIDARG;
2086 addr = pl.pUserName;
2087 size = pl.szUserName;
2088 break;
2090 case URL_PART_PASSWORD:
2091 if (!pl.szPassword) return E_INVALIDARG;
2092 addr = pl.pPassword;
2093 size = pl.szPassword;
2094 break;
2096 case URL_PART_PORT:
2097 if (!pl.szPort) return E_INVALIDARG;
2098 addr = pl.pPort;
2099 size = pl.szPort;
2100 break;
2102 case URL_PART_QUERY:
2103 if (!pl.szQuery) return E_INVALIDARG;
2104 addr = pl.pQuery;
2105 size = pl.szQuery;
2106 break;
2108 default:
2109 return E_INVALIDARG;
2112 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2113 if (*pcchOut < schsize + size + 2) {
2114 *pcchOut = schsize + size + 2;
2115 return E_POINTER;
2117 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2118 pszOut[schsize] = ':';
2119 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2120 pszOut[schsize+1+size] = 0;
2121 *pcchOut = schsize + 1 + size;
2123 else {
2124 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2125 memcpy(pszOut, addr, size*sizeof(WCHAR));
2126 pszOut[size] = 0;
2127 *pcchOut = size;
2129 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2131 return ret;
2134 /*************************************************************************
2135 * PathIsURLA [SHLWAPI.@]
2137 * Check if the given path is a Url.
2139 * PARAMS
2140 * lpszPath [I] Path to check.
2142 * RETURNS
2143 * TRUE if lpszPath is a Url.
2144 * FALSE if lpszPath is NULL or not a Url.
2146 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2148 PARSEDURLA base;
2149 DWORD res1;
2151 if (!lpstrPath || !*lpstrPath) return FALSE;
2153 /* get protocol */
2154 base.cbSize = sizeof(base);
2155 res1 = ParseURLA(lpstrPath, &base);
2156 return (base.nScheme != URL_SCHEME_INVALID);
2159 /*************************************************************************
2160 * PathIsURLW [SHLWAPI.@]
2162 * See PathIsURLA.
2164 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2166 PARSEDURLW base;
2167 DWORD res1;
2169 if (!lpstrPath || !*lpstrPath) return FALSE;
2171 /* get protocol */
2172 base.cbSize = sizeof(base);
2173 res1 = ParseURLW(lpstrPath, &base);
2174 return (base.nScheme != URL_SCHEME_INVALID);
2177 /*************************************************************************
2178 * UrlCreateFromPathA [SHLWAPI.@]
2180 * See UrlCreateFromPathW
2182 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2184 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2185 WCHAR *urlW = bufW;
2186 UNICODE_STRING pathW;
2187 HRESULT ret;
2188 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2190 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2191 return E_INVALIDARG;
2192 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2193 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2194 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2196 if(ret == S_OK || ret == S_FALSE) {
2197 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2198 if(*pcchUrl > lenA) {
2199 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2200 pszUrl[lenA] = 0;
2201 *pcchUrl = lenA;
2202 } else {
2203 *pcchUrl = lenA + 1;
2204 ret = E_POINTER;
2207 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2208 RtlFreeUnicodeString(&pathW);
2209 return ret;
2212 /*************************************************************************
2213 * UrlCreateFromPathW [SHLWAPI.@]
2215 * Create a Url from a file path.
2217 * PARAMS
2218 * pszPath [I] Path to convert
2219 * pszUrl [O] Destination for the converted Url
2220 * pcchUrl [I/O] Length of pszUrl
2221 * dwReserved [I] Reserved, must be 0
2223 * RETURNS
2224 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2225 * Failure: An HRESULT error code.
2227 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2229 DWORD needed;
2230 HRESULT ret;
2231 WCHAR *pszNewUrl;
2232 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2233 WCHAR three_slashesW[] = {'/','/','/',0};
2234 PARSEDURLW parsed_url;
2236 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2238 /* Validate arguments */
2239 if (dwReserved != 0)
2240 return E_INVALIDARG;
2241 if (!pszUrl || !pcchUrl)
2242 return E_INVALIDARG;
2245 parsed_url.cbSize = sizeof(parsed_url);
2246 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2247 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2248 needed = strlenW(pszPath);
2249 if (needed >= *pcchUrl) {
2250 *pcchUrl = needed + 1;
2251 return E_POINTER;
2252 } else {
2253 *pcchUrl = needed;
2254 strcpyW(pszUrl, pszPath);
2255 return S_FALSE;
2260 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2261 strcpyW(pszNewUrl, file_colonW);
2262 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2263 strcatW(pszNewUrl, three_slashesW);
2264 strcatW(pszNewUrl, pszPath);
2265 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2267 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2268 return ret;
2271 /*************************************************************************
2272 * SHAutoComplete [SHLWAPI.@]
2274 * Enable auto-completion for an edit control.
2276 * PARAMS
2277 * hwndEdit [I] Handle of control to enable auto-completion for
2278 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2280 * RETURNS
2281 * Success: S_OK. Auto-completion is enabled for the control.
2282 * Failure: An HRESULT error code indicating the error.
2284 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2286 FIXME("SHAutoComplete stub\n");
2287 return S_FALSE;
2290 /*************************************************************************
2291 * MLBuildResURLA [SHLWAPI.405]
2293 * Create a Url pointing to a resource in a module.
2295 * PARAMS
2296 * lpszLibName [I] Name of the module containing the resource
2297 * hMod [I] Callers module handle
2298 * dwFlags [I] Undocumented flags for loading the module
2299 * lpszRes [I] Resource name
2300 * lpszDest [O] Destination for resulting Url
2301 * dwDestLen [I] Length of lpszDest
2303 * RETURNS
2304 * Success: S_OK. lpszDest constains the resource Url.
2305 * Failure: E_INVALIDARG, if any argument is invalid, or
2306 * E_FAIL if dwDestLen is too small.
2308 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2309 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2311 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2312 HRESULT hRet;
2314 if (lpszLibName)
2315 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2317 if (lpszRes)
2318 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2320 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2321 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2323 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2324 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2325 if (SUCCEEDED(hRet) && lpszDest)
2326 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2328 return hRet;
2331 /*************************************************************************
2332 * MLBuildResURLA [SHLWAPI.406]
2334 * See MLBuildResURLA.
2336 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2337 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2339 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2340 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2341 HRESULT hRet = E_FAIL;
2343 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2344 debugstr_w(lpszRes), lpszDest, dwDestLen);
2346 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2347 !lpszDest || (dwFlags && dwFlags != 2))
2348 return E_INVALIDARG;
2350 if (dwDestLen >= szResLen + 1)
2352 dwDestLen -= (szResLen + 1);
2353 memcpy(lpszDest, szRes, sizeof(szRes));
2355 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2357 if (hMod)
2359 WCHAR szBuff[MAX_PATH];
2360 DWORD len;
2362 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2363 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2365 DWORD dwPathLen = strlenW(szBuff) + 1;
2367 if (dwDestLen >= dwPathLen)
2369 DWORD dwResLen;
2371 dwDestLen -= dwPathLen;
2372 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2374 dwResLen = strlenW(lpszRes) + 1;
2375 if (dwDestLen >= dwResLen + 1)
2377 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2378 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2379 hRet = S_OK;
2383 MLFreeLibrary(hMod);
2386 return hRet;