shlwapi: Fix handling mk URLs.
[wine/hacks.git] / dlls / shlwapi / url.c
blob50e608a10150b6ec71d82e2a1300c7ad768d8aee
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));
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 state = 5;
364 while (*wk1) {
365 switch (state) {
366 case 0:
367 if (!isalnumW(*wk1)) {state = 3; break;}
368 *wk2++ = *wk1++;
369 if (!isalnumW(*wk1)) {state = 3; break;}
370 *wk2++ = *wk1++;
371 state = 1;
372 break;
373 case 1:
374 *wk2++ = *wk1;
375 if (*wk1++ == L':') state = 2;
376 break;
377 case 2:
378 if (*wk1 != L'/') {state = 3; break;}
379 *wk2++ = *wk1++;
380 if (*wk1 != L'/') {state = 6; break;}
381 *wk2++ = *wk1++;
382 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
383 wk1++;
384 state = 4;
385 break;
386 case 3:
387 nWkLen = strlenW(wk1);
388 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
389 mp = wk2;
390 wk1 += nWkLen;
391 wk2 += nWkLen;
393 while(mp < wk2) {
394 if(*mp == '/' || *mp == '\\')
395 *mp = slash;
396 mp++;
398 break;
399 case 4:
400 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.') && (*wk1 != ':'))
401 {state = 3; break;}
402 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.') || (*wk1 == ':'))
403 *wk2++ = *wk1++;
404 state = 5;
405 break;
406 case 5:
407 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
408 while(*wk1 == '/' || *wk1 == '\\') {
409 *wk2++ = slash;
410 wk1++;
412 state = 6;
413 break;
414 case 6:
415 if(dwFlags & URL_DONT_SIMPLIFY) {
416 state = 3;
417 break;
420 /* Now at root location, cannot back up any more. */
421 /* "root" will point at the '/' */
423 root = wk2-1;
424 while (*wk1) {
425 mp = strchrW(wk1, '/');
426 mp2 = strchrW(wk1, '\\');
427 if(mp2 && (!mp || mp2 < mp))
428 mp = mp2;
429 if (!mp) {
430 nWkLen = strlenW(wk1);
431 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
432 wk1 += nWkLen;
433 wk2 += nWkLen;
434 continue;
436 nLen = mp - wk1;
437 if(nLen) {
438 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
439 wk2 += nLen;
440 wk1 += nLen;
442 *wk2++ = slash;
443 wk1++;
445 if (*wk1 == L'.') {
446 TRACE("found '/.'\n");
447 if (wk1[1] == '/' || wk1[1] == '\\') {
448 /* case of /./ -> skip the ./ */
449 wk1 += 2;
451 else if (wk1[1] == '.') {
452 /* found /.. look for next / */
453 TRACE("found '/..'\n");
454 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
455 || wk1[2] == '#' || !wk1[2]) {
456 /* case /../ -> need to backup wk2 */
457 TRACE("found '/../'\n");
458 *(wk2-1) = L'\0'; /* set end of string */
459 mp = strrchrW(root, slash);
460 if (mp && (mp >= root)) {
461 /* found valid backup point */
462 wk2 = mp + 1;
463 if(wk1[2] != '/' && wk1[2] != '\\')
464 wk1 += 2;
465 else
466 wk1 += 3;
468 else {
469 /* did not find point, restore '/' */
470 *(wk2-1) = slash;
476 *wk2 = L'\0';
477 break;
478 default:
479 FIXME("how did we get here - state=%d\n", state);
480 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
481 return E_INVALIDARG;
483 *wk2 = L'\0';
484 TRACE("Simplified, orig <%s>, simple <%s>\n",
485 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
487 nLen = lstrlenW(lpszUrlCpy);
488 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
489 lpszUrlCpy[--nLen]=0;
491 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
492 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
494 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
495 URL_ESCAPE_SPACES_ONLY |
496 URL_ESCAPE_PERCENT |
497 URL_DONT_ESCAPE_EXTRA_INFO |
498 URL_ESCAPE_SEGMENT_ONLY ))) {
499 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
500 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
501 EscapeFlags);
502 } else { /* No escaping needed, just copy the string */
503 nLen = lstrlenW(lpszUrlCpy);
504 if(nLen < *pcchCanonicalized)
505 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
506 else {
507 hr = E_POINTER;
508 nLen++;
510 *pcchCanonicalized = nLen;
513 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
515 if (hr == S_OK)
516 TRACE("result %s\n", debugstr_w(pszCanonicalized));
518 return hr;
521 /*************************************************************************
522 * UrlCombineA [SHLWAPI.@]
524 * Combine two Urls.
526 * PARAMS
527 * pszBase [I] Base Url
528 * pszRelative [I] Url to combine with pszBase
529 * pszCombined [O] Destination for combined Url
530 * pcchCombined [O] Destination for length of pszCombined
531 * dwFlags [I] URL_ flags from "shlwapi.h"
533 * RETURNS
534 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
535 * contains its length.
536 * Failure: An HRESULT error code indicating the error.
538 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
539 LPSTR pszCombined, LPDWORD pcchCombined,
540 DWORD dwFlags)
542 LPWSTR base, relative, combined;
543 DWORD ret, len, len2;
545 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
546 debugstr_a(pszBase),debugstr_a(pszRelative),
547 pcchCombined?*pcchCombined:0,dwFlags);
549 if(!pszBase || !pszRelative || !pcchCombined)
550 return E_INVALIDARG;
552 base = HeapAlloc(GetProcessHeap(), 0,
553 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
554 relative = base + INTERNET_MAX_URL_LENGTH;
555 combined = relative + INTERNET_MAX_URL_LENGTH;
557 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
558 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
559 len = *pcchCombined;
561 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
562 if (ret != S_OK) {
563 *pcchCombined = len;
564 HeapFree(GetProcessHeap(), 0, base);
565 return ret;
568 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
569 if (len2 > *pcchCombined) {
570 *pcchCombined = len2;
571 HeapFree(GetProcessHeap(), 0, base);
572 return E_POINTER;
574 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
575 0, 0);
576 *pcchCombined = len2;
577 HeapFree(GetProcessHeap(), 0, base);
578 return S_OK;
581 /*************************************************************************
582 * UrlCombineW [SHLWAPI.@]
584 * See UrlCombineA.
586 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
587 LPWSTR pszCombined, LPDWORD pcchCombined,
588 DWORD dwFlags)
590 PARSEDURLW base, relative;
591 DWORD myflags, sizeloc = 0;
592 DWORD len, res1, res2, process_case = 0;
593 LPWSTR work, preliminary, mbase, mrelative;
594 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
595 static const WCHAR single_slash[] = {'/','\0'};
596 HRESULT ret;
598 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
599 debugstr_w(pszBase),debugstr_w(pszRelative),
600 pcchCombined?*pcchCombined:0,dwFlags);
602 if(!pszBase || !pszRelative || !pcchCombined)
603 return E_INVALIDARG;
605 base.cbSize = sizeof(base);
606 relative.cbSize = sizeof(relative);
608 /* Get space for duplicates of the input and the output */
609 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
610 sizeof(WCHAR));
611 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
612 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
613 *preliminary = L'\0';
615 /* Canonicalize the base input prior to looking for the scheme */
616 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
617 len = INTERNET_MAX_URL_LENGTH;
618 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
620 /* Canonicalize the relative input prior to looking for the scheme */
621 len = INTERNET_MAX_URL_LENGTH;
622 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
624 /* See if the base has a scheme */
625 res1 = ParseURLW(mbase, &base);
626 if (res1) {
627 /* if pszBase has no scheme, then return pszRelative */
628 TRACE("no scheme detected in Base\n");
629 process_case = 1;
631 else do {
632 /* mk is a special case */
633 if(base.nScheme == URL_SCHEME_MK) {
634 static const WCHAR wsz[] = {':',':',0};
636 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
637 if(ptr) {
638 int delta;
640 ptr += 2;
641 delta = ptr-base.pszSuffix;
642 base.cchProtocol += delta;
643 base.pszSuffix += delta;
644 base.cchSuffix -= delta;
648 /* get size of location field (if it exists) */
649 work = (LPWSTR)base.pszSuffix;
650 sizeloc = 0;
651 if (*work++ == L'/') {
652 if (*work++ == L'/') {
653 /* At this point have start of location and
654 * it ends at next '/' or end of string.
656 while(*work && (*work != L'/')) work++;
657 sizeloc = (DWORD)(work - base.pszSuffix);
661 /* Change .sizep2 to not have the last leaf in it,
662 * Note: we need to start after the location (if it exists)
664 work = strrchrW((base.pszSuffix+sizeloc), L'/');
665 if (work) {
666 len = (DWORD)(work - base.pszSuffix + 1);
667 base.cchSuffix = len;
671 * At this point:
672 * .pszSuffix points to location (starting with '//')
673 * .cchSuffix length of location (above) and rest less the last
674 * leaf (if any)
675 * sizeloc length of location (above) up to but not including
676 * the last '/'
679 res2 = ParseURLW(mrelative, &relative);
680 if (res2) {
681 /* no scheme in pszRelative */
682 TRACE("no scheme detected in Relative\n");
683 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
684 relative.cchSuffix = strlenW(mrelative);
685 if (*pszRelative == L':') {
686 /* case that is either left alone or uses pszBase */
687 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
688 process_case = 5;
689 break;
691 process_case = 1;
692 break;
694 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
695 /* case that becomes "file:///" */
696 strcpyW(preliminary, myfilestr);
697 process_case = 1;
698 break;
700 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
701 /* pszRelative has location and rest */
702 process_case = 3;
703 break;
705 if (*mrelative == L'/') {
706 /* case where pszRelative is root to location */
707 process_case = 4;
708 break;
710 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
711 break;
714 /* handle cases where pszRelative has scheme */
715 if ((base.cchProtocol == relative.cchProtocol) &&
716 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
718 /* since the schemes are the same */
719 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
720 /* case where pszRelative replaces location and following */
721 process_case = 3;
722 break;
724 if (*relative.pszSuffix == L'/') {
725 /* case where pszRelative is root to location */
726 process_case = 4;
727 break;
729 /* case where scheme is followed by document path */
730 process_case = 5;
731 break;
733 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
734 /* case where pszRelative replaces scheme, location,
735 * and following and handles PLUGGABLE
737 process_case = 2;
738 break;
740 process_case = 1;
741 break;
742 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
745 ret = S_OK;
746 switch (process_case) {
748 case 1: /*
749 * Return pszRelative appended to what ever is in pszCombined,
750 * (which may the string "file:///"
752 strcatW(preliminary, mrelative);
753 break;
755 case 2: /*
756 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
757 * and pszRelative starts with "//", then append a "/"
759 strcpyW(preliminary, mrelative);
760 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
761 URL_JustLocation(relative.pszSuffix))
762 strcatW(preliminary, single_slash);
763 break;
765 case 3: /*
766 * Return the pszBase scheme with pszRelative. Basically
767 * keeps the scheme and replaces the domain and following.
769 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
770 work = preliminary + base.cchProtocol + 1;
771 strcpyW(work, relative.pszSuffix);
772 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
773 URL_JustLocation(relative.pszSuffix))
774 strcatW(work, single_slash);
775 break;
777 case 4: /*
778 * Return the pszBase scheme and location but everything
779 * after the location is pszRelative. (Replace document
780 * from root on.)
782 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
783 work = preliminary + base.cchProtocol + 1 + sizeloc;
784 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
785 *(work++) = L'/';
786 strcpyW(work, relative.pszSuffix);
787 break;
789 case 5: /*
790 * Return the pszBase without its document (if any) and
791 * append pszRelative after its scheme.
793 memcpy(preliminary, base.pszProtocol,
794 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
795 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
796 if (*work++ != L'/')
797 *(work++) = L'/';
798 strcpyW(work, relative.pszSuffix);
799 break;
801 default:
802 FIXME("How did we get here????? process_case=%d\n", process_case);
803 ret = E_INVALIDARG;
806 if (ret == S_OK) {
807 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
808 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
809 if(SUCCEEDED(ret) && pszCombined) {
810 lstrcpyW(pszCombined, mrelative);
812 TRACE("return-%d len=%d, %s\n",
813 process_case, *pcchCombined, debugstr_w(pszCombined));
815 HeapFree(GetProcessHeap(), 0, preliminary);
816 return ret;
819 /*************************************************************************
820 * UrlEscapeA [SHLWAPI.@]
823 HRESULT WINAPI UrlEscapeA(
824 LPCSTR pszUrl,
825 LPSTR pszEscaped,
826 LPDWORD pcchEscaped,
827 DWORD dwFlags)
829 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
830 WCHAR *escapedW = bufW;
831 UNICODE_STRING urlW;
832 HRESULT ret;
833 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
835 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
836 return E_INVALIDARG;
837 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
838 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
839 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
841 if(ret == S_OK) {
842 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
843 if(pszEscaped && *pcchEscaped > lenA) {
844 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
845 pszEscaped[lenA] = 0;
846 *pcchEscaped = lenA;
847 } else {
848 *pcchEscaped = lenA + 1;
849 ret = E_INVALIDARG;
852 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
853 RtlFreeUnicodeString(&urlW);
854 return ret;
857 #define WINE_URL_BASH_AS_SLASH 0x01
858 #define WINE_URL_COLLAPSE_SLASHES 0x02
859 #define WINE_URL_ESCAPE_SLASH 0x04
860 #define WINE_URL_ESCAPE_HASH 0x08
861 #define WINE_URL_ESCAPE_QUESTION 0x10
862 #define WINE_URL_STOP_ON_HASH 0x20
863 #define WINE_URL_STOP_ON_QUESTION 0x40
865 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
868 if (isalnumW(ch))
869 return FALSE;
871 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
872 if(ch == ' ')
873 return TRUE;
874 else
875 return FALSE;
878 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
879 return TRUE;
881 if (ch <= 31 || ch >= 127)
882 return TRUE;
884 else {
885 switch (ch) {
886 case ' ':
887 case '<':
888 case '>':
889 case '\"':
890 case '{':
891 case '}':
892 case '|':
893 case '\\':
894 case '^':
895 case ']':
896 case '[':
897 case '`':
898 case '&':
899 return TRUE;
901 case '/':
902 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
903 return FALSE;
905 case '?':
906 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
907 return FALSE;
909 case '#':
910 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
911 return FALSE;
913 default:
914 return FALSE;
920 /*************************************************************************
921 * UrlEscapeW [SHLWAPI.@]
923 * Converts unsafe characters in a Url into escape sequences.
925 * PARAMS
926 * pszUrl [I] Url to modify
927 * pszEscaped [O] Destination for modified Url
928 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
929 * dwFlags [I] URL_ flags from "shlwapi.h"
931 * RETURNS
932 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
933 * contains its length.
934 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
935 * pcchEscaped is set to the required length.
937 * Converts unsafe characters into their escape sequences.
939 * NOTES
940 * - By default this function stops converting at the first '?' or
941 * '#' character.
942 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
943 * converted, but the conversion continues past a '?' or '#'.
944 * - Note that this function did not work well (or at all) in shlwapi version 4.
946 * BUGS
947 * Only the following flags are implemented:
948 *| URL_ESCAPE_SPACES_ONLY
949 *| URL_DONT_ESCAPE_EXTRA_INFO
950 *| URL_ESCAPE_SEGMENT_ONLY
951 *| URL_ESCAPE_PERCENT
953 HRESULT WINAPI UrlEscapeW(
954 LPCWSTR pszUrl,
955 LPWSTR pszEscaped,
956 LPDWORD pcchEscaped,
957 DWORD dwFlags)
959 LPCWSTR src;
960 DWORD needed = 0, ret;
961 BOOL stop_escaping = FALSE;
962 WCHAR next[5], *dst = pszEscaped;
963 INT len;
964 PARSEDURLW parsed_url;
965 DWORD int_flags;
966 DWORD slashes = 0;
967 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
969 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
970 pcchEscaped, dwFlags);
972 if(!pszUrl || !pcchEscaped)
973 return E_INVALIDARG;
975 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
976 URL_ESCAPE_SEGMENT_ONLY |
977 URL_DONT_ESCAPE_EXTRA_INFO |
978 URL_ESCAPE_PERCENT))
979 FIXME("Unimplemented flags: %08x\n", dwFlags);
981 /* fix up flags */
982 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
983 /* if SPACES_ONLY specified, reset the other controls */
984 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
985 URL_ESCAPE_PERCENT |
986 URL_ESCAPE_SEGMENT_ONLY);
988 else
989 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
990 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
993 int_flags = 0;
994 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
995 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
996 } else {
997 parsed_url.cbSize = sizeof(parsed_url);
998 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
999 parsed_url.nScheme = URL_SCHEME_INVALID;
1001 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1003 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1004 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1006 switch(parsed_url.nScheme) {
1007 case URL_SCHEME_FILE:
1008 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1009 int_flags &= ~WINE_URL_STOP_ON_HASH;
1010 break;
1012 case URL_SCHEME_HTTP:
1013 case URL_SCHEME_HTTPS:
1014 int_flags |= WINE_URL_BASH_AS_SLASH;
1015 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1016 int_flags |= WINE_URL_ESCAPE_SLASH;
1017 break;
1019 case URL_SCHEME_MAILTO:
1020 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1021 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1022 break;
1024 case URL_SCHEME_INVALID:
1025 break;
1027 case URL_SCHEME_FTP:
1028 default:
1029 if(parsed_url.pszSuffix[0] != '/')
1030 int_flags |= WINE_URL_ESCAPE_SLASH;
1031 break;
1035 for(src = pszUrl; *src; ) {
1036 WCHAR cur = *src;
1037 len = 0;
1039 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1040 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1041 while(cur == '/' || cur == '\\') {
1042 slashes++;
1043 cur = *++src;
1045 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1046 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1047 src += localhost_len + 1;
1048 slashes = 3;
1051 switch(slashes) {
1052 case 1:
1053 case 3:
1054 next[0] = next[1] = next[2] = '/';
1055 len = 3;
1056 break;
1057 case 0:
1058 len = 0;
1059 break;
1060 default:
1061 next[0] = next[1] = '/';
1062 len = 2;
1063 break;
1066 if(len == 0) {
1068 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1069 stop_escaping = TRUE;
1071 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1072 stop_escaping = TRUE;
1074 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1076 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1077 next[0] = L'%';
1078 next[1] = hexDigits[(cur >> 4) & 0xf];
1079 next[2] = hexDigits[cur & 0xf];
1080 len = 3;
1081 } else {
1082 next[0] = cur;
1083 len = 1;
1085 src++;
1088 if(needed + len <= *pcchEscaped) {
1089 memcpy(dst, next, len*sizeof(WCHAR));
1090 dst += len;
1092 needed += len;
1095 if(needed < *pcchEscaped) {
1096 *dst = '\0';
1097 ret = S_OK;
1098 } else {
1099 needed++; /* add one for the '\0' */
1100 ret = E_POINTER;
1102 *pcchEscaped = needed;
1103 return ret;
1107 /*************************************************************************
1108 * UrlUnescapeA [SHLWAPI.@]
1110 * Converts Url escape sequences back to ordinary characters.
1112 * PARAMS
1113 * pszUrl [I/O] Url to convert
1114 * pszUnescaped [O] Destination for converted Url
1115 * pcchUnescaped [I/O] Size of output string
1116 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1118 * RETURNS
1119 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1120 * dwFlags includes URL_ESCAPE_INPLACE.
1121 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1122 * this case pcchUnescaped is set to the size required.
1123 * NOTES
1124 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1125 * the first occurrence of either a '?' or '#' character.
1127 HRESULT WINAPI UrlUnescapeA(
1128 LPSTR pszUrl,
1129 LPSTR pszUnescaped,
1130 LPDWORD pcchUnescaped,
1131 DWORD dwFlags)
1133 char *dst, next;
1134 LPCSTR src;
1135 HRESULT ret;
1136 DWORD needed;
1137 BOOL stop_unescaping = FALSE;
1139 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1140 pcchUnescaped, dwFlags);
1142 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1143 return E_INVALIDARG;
1145 if(dwFlags & URL_UNESCAPE_INPLACE)
1146 dst = pszUrl;
1147 else
1148 dst = pszUnescaped;
1150 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1151 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1152 (*src == '#' || *src == '?')) {
1153 stop_unescaping = TRUE;
1154 next = *src;
1155 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1156 && stop_unescaping == FALSE) {
1157 INT ih;
1158 char buf[3];
1159 memcpy(buf, src + 1, 2);
1160 buf[2] = '\0';
1161 ih = strtol(buf, NULL, 16);
1162 next = (CHAR) ih;
1163 src += 2; /* Advance to end of escape */
1164 } else
1165 next = *src;
1167 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1168 *dst++ = next;
1171 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1172 *dst = '\0';
1173 ret = S_OK;
1174 } else {
1175 needed++; /* add one for the '\0' */
1176 ret = E_POINTER;
1178 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1179 *pcchUnescaped = needed;
1181 if (ret == S_OK) {
1182 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1183 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1186 return ret;
1189 /*************************************************************************
1190 * UrlUnescapeW [SHLWAPI.@]
1192 * See UrlUnescapeA.
1194 HRESULT WINAPI UrlUnescapeW(
1195 LPWSTR pszUrl,
1196 LPWSTR pszUnescaped,
1197 LPDWORD pcchUnescaped,
1198 DWORD dwFlags)
1200 WCHAR *dst, next;
1201 LPCWSTR src;
1202 HRESULT ret;
1203 DWORD needed;
1204 BOOL stop_unescaping = FALSE;
1206 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1207 pcchUnescaped, dwFlags);
1209 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1210 return E_INVALIDARG;
1212 if(dwFlags & URL_UNESCAPE_INPLACE)
1213 dst = pszUrl;
1214 else
1215 dst = pszUnescaped;
1217 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1218 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1219 (*src == L'#' || *src == L'?')) {
1220 stop_unescaping = TRUE;
1221 next = *src;
1222 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1223 && stop_unescaping == FALSE) {
1224 INT ih;
1225 WCHAR buf[5] = {'0','x',0};
1226 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1227 buf[4] = 0;
1228 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1229 next = (WCHAR) ih;
1230 src += 2; /* Advance to end of escape */
1231 } else
1232 next = *src;
1234 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1235 *dst++ = next;
1238 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1239 *dst = L'\0';
1240 ret = S_OK;
1241 } else {
1242 needed++; /* add one for the '\0' */
1243 ret = E_POINTER;
1245 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1246 *pcchUnescaped = needed;
1248 if (ret == S_OK) {
1249 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1250 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1253 return ret;
1256 /*************************************************************************
1257 * UrlGetLocationA [SHLWAPI.@]
1259 * Get the location from a Url.
1261 * PARAMS
1262 * pszUrl [I] Url to get the location from
1264 * RETURNS
1265 * A pointer to the start of the location in pszUrl, or NULL if there is
1266 * no location.
1268 * NOTES
1269 * - MSDN erroneously states that "The location is the segment of the Url
1270 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1271 * stop at '?' and always return a NULL in this case.
1272 * - MSDN also erroneously states that "If a file URL has a query string,
1273 * the returned string is the query string". In all tested cases, if the
1274 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1275 *| Result Url
1276 *| ------ ---
1277 *| NULL file://aa/b/cd#hohoh
1278 *| #hohoh http://aa/b/cd#hohoh
1279 *| NULL fi://aa/b/cd#hohoh
1280 *| #hohoh ff://aa/b/cd#hohoh
1282 LPCSTR WINAPI UrlGetLocationA(
1283 LPCSTR pszUrl)
1285 PARSEDURLA base;
1286 DWORD res1;
1288 base.cbSize = sizeof(base);
1289 res1 = ParseURLA(pszUrl, &base);
1290 if (res1) return NULL; /* invalid scheme */
1292 /* if scheme is file: then never return pointer */
1293 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1295 /* Look for '#' and return its addr */
1296 return strchr(base.pszSuffix, '#');
1299 /*************************************************************************
1300 * UrlGetLocationW [SHLWAPI.@]
1302 * See UrlGetLocationA.
1304 LPCWSTR WINAPI UrlGetLocationW(
1305 LPCWSTR pszUrl)
1307 PARSEDURLW base;
1308 DWORD res1;
1310 base.cbSize = sizeof(base);
1311 res1 = ParseURLW(pszUrl, &base);
1312 if (res1) return NULL; /* invalid scheme */
1314 /* if scheme is file: then never return pointer */
1315 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1317 /* Look for '#' and return its addr */
1318 return strchrW(base.pszSuffix, L'#');
1321 /*************************************************************************
1322 * UrlCompareA [SHLWAPI.@]
1324 * Compare two Urls.
1326 * PARAMS
1327 * pszUrl1 [I] First Url to compare
1328 * pszUrl2 [I] Url to compare to pszUrl1
1329 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1331 * RETURNS
1332 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1333 * than, equal to, or less than pszUrl1 respectively.
1335 INT WINAPI UrlCompareA(
1336 LPCSTR pszUrl1,
1337 LPCSTR pszUrl2,
1338 BOOL fIgnoreSlash)
1340 INT ret, len, len1, len2;
1342 if (!fIgnoreSlash)
1343 return strcmp(pszUrl1, pszUrl2);
1344 len1 = strlen(pszUrl1);
1345 if (pszUrl1[len1-1] == '/') len1--;
1346 len2 = strlen(pszUrl2);
1347 if (pszUrl2[len2-1] == '/') len2--;
1348 if (len1 == len2)
1349 return strncmp(pszUrl1, pszUrl2, len1);
1350 len = min(len1, len2);
1351 ret = strncmp(pszUrl1, pszUrl2, len);
1352 if (ret) return ret;
1353 if (len1 > len2) return 1;
1354 return -1;
1357 /*************************************************************************
1358 * UrlCompareW [SHLWAPI.@]
1360 * See UrlCompareA.
1362 INT WINAPI UrlCompareW(
1363 LPCWSTR pszUrl1,
1364 LPCWSTR pszUrl2,
1365 BOOL fIgnoreSlash)
1367 INT ret;
1368 size_t len, len1, len2;
1370 if (!fIgnoreSlash)
1371 return strcmpW(pszUrl1, pszUrl2);
1372 len1 = strlenW(pszUrl1);
1373 if (pszUrl1[len1-1] == '/') len1--;
1374 len2 = strlenW(pszUrl2);
1375 if (pszUrl2[len2-1] == '/') len2--;
1376 if (len1 == len2)
1377 return strncmpW(pszUrl1, pszUrl2, len1);
1378 len = min(len1, len2);
1379 ret = strncmpW(pszUrl1, pszUrl2, len);
1380 if (ret) return ret;
1381 if (len1 > len2) return 1;
1382 return -1;
1385 /*************************************************************************
1386 * HashData [SHLWAPI.@]
1388 * Hash an input block into a variable sized digest.
1390 * PARAMS
1391 * lpSrc [I] Input block
1392 * nSrcLen [I] Length of lpSrc
1393 * lpDest [I] Output for hash digest
1394 * nDestLen [I] Length of lpDest
1396 * RETURNS
1397 * Success: TRUE. lpDest is filled with the computed hash value.
1398 * Failure: FALSE, if any argument is invalid.
1400 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1401 unsigned char *lpDest, DWORD nDestLen)
1403 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1405 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1406 IsBadWritePtr(lpDest, nDestLen))
1407 return E_INVALIDARG;
1409 while (destCount >= 0)
1411 lpDest[destCount] = (destCount & 0xff);
1412 destCount--;
1415 while (srcCount >= 0)
1417 destCount = nDestLen - 1;
1418 while (destCount >= 0)
1420 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1421 destCount--;
1423 srcCount--;
1425 return S_OK;
1428 /*************************************************************************
1429 * UrlHashA [SHLWAPI.@]
1431 * Produce a Hash from a Url.
1433 * PARAMS
1434 * pszUrl [I] Url to hash
1435 * lpDest [O] Destinationh for hash
1436 * nDestLen [I] Length of lpDest
1438 * RETURNS
1439 * Success: S_OK. lpDest is filled with the computed hash value.
1440 * Failure: E_INVALIDARG, if any argument is invalid.
1442 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1444 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1445 return E_INVALIDARG;
1447 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1448 return S_OK;
1451 /*************************************************************************
1452 * UrlHashW [SHLWAPI.@]
1454 * See UrlHashA.
1456 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1458 char szUrl[MAX_PATH];
1460 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1462 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1463 return E_INVALIDARG;
1465 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1466 * return the same digests for the same URL.
1468 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1469 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1470 return S_OK;
1473 /*************************************************************************
1474 * UrlApplySchemeA [SHLWAPI.@]
1476 * Apply a scheme to a Url.
1478 * PARAMS
1479 * pszIn [I] Url to apply scheme to
1480 * pszOut [O] Destination for modified Url
1481 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1482 * dwFlags [I] URL_ flags from "shlwapi.h"
1484 * RETURNS
1485 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1486 * Failure: An HRESULT error code describing the error.
1488 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1490 LPWSTR in, out;
1491 DWORD ret, len, len2;
1493 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1494 debugstr_a(pszIn), *pcchOut, dwFlags);
1496 in = HeapAlloc(GetProcessHeap(), 0,
1497 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1498 out = in + INTERNET_MAX_URL_LENGTH;
1500 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1501 len = INTERNET_MAX_URL_LENGTH;
1503 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1504 if ((ret != S_OK) && (ret != S_FALSE)) {
1505 HeapFree(GetProcessHeap(), 0, in);
1506 return ret;
1509 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1510 if (len2 > *pcchOut) {
1511 *pcchOut = len2;
1512 HeapFree(GetProcessHeap(), 0, in);
1513 return E_POINTER;
1515 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1516 *pcchOut = len2;
1517 HeapFree(GetProcessHeap(), 0, in);
1518 return ret;
1521 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1523 HKEY newkey;
1524 BOOL j;
1525 INT index;
1526 DWORD value_len, data_len, dwType, i;
1527 WCHAR reg_path[MAX_PATH];
1528 WCHAR value[MAX_PATH], data[MAX_PATH];
1529 WCHAR Wxx, Wyy;
1531 MultiByteToWideChar(0, 0,
1532 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1533 -1, reg_path, MAX_PATH);
1534 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1535 index = 0;
1536 while(value_len = data_len = MAX_PATH,
1537 RegEnumValueW(newkey, index, value, &value_len,
1538 0, &dwType, (LPVOID)data, &data_len) == 0) {
1539 TRACE("guess %d %s is %s\n",
1540 index, debugstr_w(value), debugstr_w(data));
1542 j = FALSE;
1543 for(i=0; i<value_len; i++) {
1544 Wxx = pszIn[i];
1545 Wyy = value[i];
1546 /* remember that TRUE is not-equal */
1547 j = ChrCmpIW(Wxx, Wyy);
1548 if (j) break;
1550 if ((i == value_len) && !j) {
1551 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1552 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1553 RegCloseKey(newkey);
1554 return E_POINTER;
1556 strcpyW(pszOut, data);
1557 strcatW(pszOut, pszIn);
1558 *pcchOut = strlenW(pszOut);
1559 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1560 RegCloseKey(newkey);
1561 return S_OK;
1563 index++;
1565 RegCloseKey(newkey);
1566 return -1;
1569 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1571 HKEY newkey;
1572 DWORD data_len, dwType;
1573 WCHAR reg_path[MAX_PATH];
1574 WCHAR value[MAX_PATH], data[MAX_PATH];
1576 /* get and prepend default */
1577 MultiByteToWideChar(0, 0,
1578 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1579 -1, reg_path, MAX_PATH);
1580 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1581 data_len = MAX_PATH;
1582 value[0] = L'@';
1583 value[1] = L'\0';
1584 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1585 RegCloseKey(newkey);
1586 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1587 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1588 return E_POINTER;
1590 strcpyW(pszOut, data);
1591 strcatW(pszOut, pszIn);
1592 *pcchOut = strlenW(pszOut);
1593 TRACE("used default %s\n", debugstr_w(pszOut));
1594 return S_OK;
1597 /*************************************************************************
1598 * UrlApplySchemeW [SHLWAPI.@]
1600 * See UrlApplySchemeA.
1602 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1604 PARSEDURLW in_scheme;
1605 DWORD res1;
1606 HRESULT ret;
1608 TRACE("(in %s, out size %d, flags %08x)\n",
1609 debugstr_w(pszIn), *pcchOut, dwFlags);
1611 if (dwFlags & URL_APPLY_GUESSFILE) {
1612 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1613 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1614 strcpyW(pszOut, pszIn);
1615 *pcchOut = strlenW(pszOut);
1616 return S_FALSE;
1619 in_scheme.cbSize = sizeof(in_scheme);
1620 /* See if the base has a scheme */
1621 res1 = ParseURLW(pszIn, &in_scheme);
1622 if (res1) {
1623 /* no scheme in input, need to see if we need to guess */
1624 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1625 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1626 return ret;
1629 else {
1630 /* we have a scheme, see if valid (known scheme) */
1631 if (in_scheme.nScheme) {
1632 /* have valid scheme, so just copy and exit */
1633 if (strlenW(pszIn) + 1 > *pcchOut) {
1634 *pcchOut = strlenW(pszIn) + 1;
1635 return E_POINTER;
1637 strcpyW(pszOut, pszIn);
1638 *pcchOut = strlenW(pszOut);
1639 TRACE("valid scheme, returning copy\n");
1640 return S_OK;
1644 /* If we are here, then either invalid scheme,
1645 * or no scheme and can't/failed guess.
1647 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1648 ((res1 != 0)) ) &&
1649 (dwFlags & URL_APPLY_DEFAULT)) {
1650 /* find and apply default scheme */
1651 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1654 /* just copy and give proper return code */
1655 if (strlenW(pszIn) + 1 > *pcchOut) {
1656 *pcchOut = strlenW(pszIn) + 1;
1657 return E_POINTER;
1659 strcpyW(pszOut, pszIn);
1660 *pcchOut = strlenW(pszOut);
1661 TRACE("returning copy, left alone\n");
1662 return S_FALSE;
1665 /*************************************************************************
1666 * UrlIsA [SHLWAPI.@]
1668 * Determine if a Url is of a certain class.
1670 * PARAMS
1671 * pszUrl [I] Url to check
1672 * Urlis [I] URLIS_ constant from "shlwapi.h"
1674 * RETURNS
1675 * TRUE if pszUrl belongs to the class type in Urlis.
1676 * FALSE Otherwise.
1678 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1680 PARSEDURLA base;
1681 DWORD res1;
1682 LPCSTR last;
1684 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1686 switch (Urlis) {
1688 case URLIS_OPAQUE:
1689 base.cbSize = sizeof(base);
1690 res1 = ParseURLA(pszUrl, &base);
1691 if (res1) return FALSE; /* invalid scheme */
1692 switch (base.nScheme)
1694 case URL_SCHEME_MAILTO:
1695 case URL_SCHEME_SHELL:
1696 case URL_SCHEME_JAVASCRIPT:
1697 case URL_SCHEME_VBSCRIPT:
1698 case URL_SCHEME_ABOUT:
1699 return TRUE;
1701 return FALSE;
1703 case URLIS_FILEURL:
1704 return !StrCmpNA("file:", pszUrl, 5);
1706 case URLIS_DIRECTORY:
1707 last = pszUrl + strlen(pszUrl) - 1;
1708 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1710 case URLIS_URL:
1711 return PathIsURLA(pszUrl);
1713 case URLIS_NOHISTORY:
1714 case URLIS_APPLIABLE:
1715 case URLIS_HASQUERY:
1716 default:
1717 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1719 return FALSE;
1722 /*************************************************************************
1723 * UrlIsW [SHLWAPI.@]
1725 * See UrlIsA.
1727 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1729 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1730 PARSEDURLW base;
1731 DWORD res1;
1732 LPCWSTR last;
1734 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1736 switch (Urlis) {
1738 case URLIS_OPAQUE:
1739 base.cbSize = sizeof(base);
1740 res1 = ParseURLW(pszUrl, &base);
1741 if (res1) return FALSE; /* invalid scheme */
1742 switch (base.nScheme)
1744 case URL_SCHEME_MAILTO:
1745 case URL_SCHEME_SHELL:
1746 case URL_SCHEME_JAVASCRIPT:
1747 case URL_SCHEME_VBSCRIPT:
1748 case URL_SCHEME_ABOUT:
1749 return TRUE;
1751 return FALSE;
1753 case URLIS_FILEURL:
1754 return !strncmpW(stemp, pszUrl, 5);
1756 case URLIS_DIRECTORY:
1757 last = pszUrl + strlenW(pszUrl) - 1;
1758 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1760 case URLIS_URL:
1761 return PathIsURLW(pszUrl);
1763 case URLIS_NOHISTORY:
1764 case URLIS_APPLIABLE:
1765 case URLIS_HASQUERY:
1766 default:
1767 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1769 return FALSE;
1772 /*************************************************************************
1773 * UrlIsNoHistoryA [SHLWAPI.@]
1775 * Determine if a Url should not be stored in the users history list.
1777 * PARAMS
1778 * pszUrl [I] Url to check
1780 * RETURNS
1781 * TRUE, if pszUrl should be excluded from the history list,
1782 * FALSE otherwise.
1784 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1786 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1789 /*************************************************************************
1790 * UrlIsNoHistoryW [SHLWAPI.@]
1792 * See UrlIsNoHistoryA.
1794 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1796 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1799 /*************************************************************************
1800 * UrlIsOpaqueA [SHLWAPI.@]
1802 * Determine if a Url is opaque.
1804 * PARAMS
1805 * pszUrl [I] Url to check
1807 * RETURNS
1808 * TRUE if pszUrl is opaque,
1809 * FALSE Otherwise.
1811 * NOTES
1812 * An opaque Url is one that does not start with "<protocol>://".
1814 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1816 return UrlIsA(pszUrl, URLIS_OPAQUE);
1819 /*************************************************************************
1820 * UrlIsOpaqueW [SHLWAPI.@]
1822 * See UrlIsOpaqueA.
1824 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1826 return UrlIsW(pszUrl, URLIS_OPAQUE);
1829 /*************************************************************************
1830 * Scans for characters of type "type" and when not matching found,
1831 * returns pointer to it and length in size.
1833 * Characters tested based on RFC 1738
1835 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1837 static DWORD alwayszero = 0;
1838 BOOL cont = TRUE;
1840 *size = 0;
1842 switch(type){
1844 case SCHEME:
1845 while (cont) {
1846 if ( (islowerW(*start) && isalphaW(*start)) ||
1847 isdigitW(*start) ||
1848 (*start == L'+') ||
1849 (*start == L'-') ||
1850 (*start == L'.')) {
1851 start++;
1852 (*size)++;
1854 else
1855 cont = FALSE;
1857 break;
1859 case USERPASS:
1860 while (cont) {
1861 if ( isalphaW(*start) ||
1862 isdigitW(*start) ||
1863 /* user/password only characters */
1864 (*start == L';') ||
1865 (*start == L'?') ||
1866 (*start == L'&') ||
1867 (*start == L'=') ||
1868 /* *extra* characters */
1869 (*start == L'!') ||
1870 (*start == L'*') ||
1871 (*start == L'\'') ||
1872 (*start == L'(') ||
1873 (*start == L')') ||
1874 (*start == L',') ||
1875 /* *safe* characters */
1876 (*start == L'$') ||
1877 (*start == L'_') ||
1878 (*start == L'+') ||
1879 (*start == L'-') ||
1880 (*start == L'.')) {
1881 start++;
1882 (*size)++;
1883 } else if (*start == L'%') {
1884 if (isxdigitW(*(start+1)) &&
1885 isxdigitW(*(start+2))) {
1886 start += 3;
1887 *size += 3;
1888 } else
1889 cont = FALSE;
1890 } else
1891 cont = FALSE;
1893 break;
1895 case PORT:
1896 while (cont) {
1897 if (isdigitW(*start)) {
1898 start++;
1899 (*size)++;
1901 else
1902 cont = FALSE;
1904 break;
1906 case HOST:
1907 while (cont) {
1908 if (isalnumW(*start) ||
1909 (*start == L'-') ||
1910 (*start == L'.') ) {
1911 start++;
1912 (*size)++;
1914 else
1915 cont = FALSE;
1917 break;
1918 default:
1919 FIXME("unknown type %d\n", type);
1920 return (LPWSTR)&alwayszero;
1922 /* TRACE("scanned %d characters next char %p<%c>\n",
1923 *size, start, *start); */
1924 return start;
1927 /*************************************************************************
1928 * Attempt to parse URL into pieces.
1930 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1932 LPCWSTR work;
1934 memset(pl, 0, sizeof(WINE_PARSE_URL));
1935 pl->pScheme = pszUrl;
1936 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1937 if (!*work || (*work != L':')) goto ErrorExit;
1938 work++;
1939 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1940 pl->pUserName = work + 2;
1941 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1942 if (*work == L':' ) {
1943 /* parse password */
1944 work++;
1945 pl->pPassword = work;
1946 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1947 if (*work != L'@') {
1948 /* what we just parsed must be the hostname and port
1949 * so reset pointers and clear then let it parse */
1950 pl->szUserName = pl->szPassword = 0;
1951 work = pl->pUserName - 1;
1952 pl->pUserName = pl->pPassword = 0;
1954 } else if (*work == L'@') {
1955 /* no password */
1956 pl->szPassword = 0;
1957 pl->pPassword = 0;
1958 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1959 /* what was parsed was hostname, so reset pointers and let it parse */
1960 pl->szUserName = pl->szPassword = 0;
1961 work = pl->pUserName - 1;
1962 pl->pUserName = pl->pPassword = 0;
1963 } else goto ErrorExit;
1965 /* now start parsing hostname or hostnumber */
1966 work++;
1967 pl->pHostName = work;
1968 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1969 if (*work == L':') {
1970 /* parse port */
1971 work++;
1972 pl->pPort = work;
1973 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1975 if (*work == L'/') {
1976 /* see if query string */
1977 pl->pQuery = strchrW(work, L'?');
1978 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1980 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1981 pl->pScheme, pl->szScheme,
1982 pl->pUserName, pl->szUserName,
1983 pl->pPassword, pl->szPassword,
1984 pl->pHostName, pl->szHostName,
1985 pl->pPort, pl->szPort,
1986 pl->pQuery, pl->szQuery);
1987 return S_OK;
1988 ErrorExit:
1989 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1990 return E_INVALIDARG;
1993 /*************************************************************************
1994 * UrlGetPartA [SHLWAPI.@]
1996 * Retrieve part of a Url.
1998 * PARAMS
1999 * pszIn [I] Url to parse
2000 * pszOut [O] Destination for part of pszIn requested
2001 * pcchOut [I] Size of pszOut
2002 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
2003 * needed size of pszOut INCLUDING '\0'.
2004 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2005 * dwFlags [I] URL_ flags from "shlwapi.h"
2007 * RETURNS
2008 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2009 * Failure: An HRESULT error code describing the error.
2011 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2012 DWORD dwPart, DWORD dwFlags)
2014 LPWSTR in, out;
2015 DWORD ret, len, len2;
2017 in = HeapAlloc(GetProcessHeap(), 0,
2018 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2019 out = in + INTERNET_MAX_URL_LENGTH;
2021 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2023 len = INTERNET_MAX_URL_LENGTH;
2024 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2026 if (ret != S_OK) {
2027 HeapFree(GetProcessHeap(), 0, in);
2028 return ret;
2031 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2032 if (len2 > *pcchOut) {
2033 *pcchOut = len2;
2034 HeapFree(GetProcessHeap(), 0, in);
2035 return E_POINTER;
2037 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2038 *pcchOut = len2;
2039 HeapFree(GetProcessHeap(), 0, in);
2040 return S_OK;
2043 /*************************************************************************
2044 * UrlGetPartW [SHLWAPI.@]
2046 * See UrlGetPartA.
2048 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2049 DWORD dwPart, DWORD dwFlags)
2051 WINE_PARSE_URL pl;
2052 HRESULT ret;
2053 DWORD size, schsize;
2054 LPCWSTR addr, schaddr;
2056 TRACE("(%s %p %p(%d) %08x %08x)\n",
2057 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2059 ret = URL_ParseUrl(pszIn, &pl);
2060 if (!ret) {
2061 schaddr = pl.pScheme;
2062 schsize = pl.szScheme;
2064 switch (dwPart) {
2065 case URL_PART_SCHEME:
2066 if (!pl.szScheme) return E_INVALIDARG;
2067 addr = pl.pScheme;
2068 size = pl.szScheme;
2069 break;
2071 case URL_PART_HOSTNAME:
2072 if (!pl.szHostName) return E_INVALIDARG;
2073 addr = pl.pHostName;
2074 size = pl.szHostName;
2075 break;
2077 case URL_PART_USERNAME:
2078 if (!pl.szUserName) return E_INVALIDARG;
2079 addr = pl.pUserName;
2080 size = pl.szUserName;
2081 break;
2083 case URL_PART_PASSWORD:
2084 if (!pl.szPassword) return E_INVALIDARG;
2085 addr = pl.pPassword;
2086 size = pl.szPassword;
2087 break;
2089 case URL_PART_PORT:
2090 if (!pl.szPort) return E_INVALIDARG;
2091 addr = pl.pPort;
2092 size = pl.szPort;
2093 break;
2095 case URL_PART_QUERY:
2096 if (!pl.szQuery) return E_INVALIDARG;
2097 addr = pl.pQuery;
2098 size = pl.szQuery;
2099 break;
2101 default:
2102 return E_INVALIDARG;
2105 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2106 if (*pcchOut < schsize + size + 2) {
2107 *pcchOut = schsize + size + 2;
2108 return E_POINTER;
2110 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2111 pszOut[schsize] = ':';
2112 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2113 pszOut[schsize+1+size] = 0;
2114 *pcchOut = schsize + 1 + size;
2116 else {
2117 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2118 memcpy(pszOut, addr, size*sizeof(WCHAR));
2119 pszOut[size] = 0;
2120 *pcchOut = size;
2122 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2124 return ret;
2127 /*************************************************************************
2128 * PathIsURLA [SHLWAPI.@]
2130 * Check if the given path is a Url.
2132 * PARAMS
2133 * lpszPath [I] Path to check.
2135 * RETURNS
2136 * TRUE if lpszPath is a Url.
2137 * FALSE if lpszPath is NULL or not a Url.
2139 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2141 PARSEDURLA base;
2142 DWORD res1;
2144 if (!lpstrPath || !*lpstrPath) return FALSE;
2146 /* get protocol */
2147 base.cbSize = sizeof(base);
2148 res1 = ParseURLA(lpstrPath, &base);
2149 return (base.nScheme != URL_SCHEME_INVALID);
2152 /*************************************************************************
2153 * PathIsURLW [SHLWAPI.@]
2155 * See PathIsURLA.
2157 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2159 PARSEDURLW base;
2160 DWORD res1;
2162 if (!lpstrPath || !*lpstrPath) return FALSE;
2164 /* get protocol */
2165 base.cbSize = sizeof(base);
2166 res1 = ParseURLW(lpstrPath, &base);
2167 return (base.nScheme != URL_SCHEME_INVALID);
2170 /*************************************************************************
2171 * UrlCreateFromPathA [SHLWAPI.@]
2173 * See UrlCreateFromPathW
2175 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2177 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2178 WCHAR *urlW = bufW;
2179 UNICODE_STRING pathW;
2180 HRESULT ret;
2181 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2183 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2184 return E_INVALIDARG;
2185 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2186 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2187 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2189 if(ret == S_OK || ret == S_FALSE) {
2190 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2191 if(*pcchUrl > lenA) {
2192 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2193 pszUrl[lenA] = 0;
2194 *pcchUrl = lenA;
2195 } else {
2196 *pcchUrl = lenA + 1;
2197 ret = E_POINTER;
2200 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2201 RtlFreeUnicodeString(&pathW);
2202 return ret;
2205 /*************************************************************************
2206 * UrlCreateFromPathW [SHLWAPI.@]
2208 * Create a Url from a file path.
2210 * PARAMS
2211 * pszPath [I] Path to convert
2212 * pszUrl [O] Destination for the converted Url
2213 * pcchUrl [I/O] Length of pszUrl
2214 * dwReserved [I] Reserved, must be 0
2216 * RETURNS
2217 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2218 * Failure: An HRESULT error code.
2220 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2222 DWORD needed;
2223 HRESULT ret;
2224 WCHAR *pszNewUrl;
2225 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2226 WCHAR three_slashesW[] = {'/','/','/',0};
2227 PARSEDURLW parsed_url;
2229 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2231 /* Validate arguments */
2232 if (dwReserved != 0)
2233 return E_INVALIDARG;
2234 if (!pszUrl || !pcchUrl)
2235 return E_INVALIDARG;
2238 parsed_url.cbSize = sizeof(parsed_url);
2239 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2240 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2241 needed = strlenW(pszPath);
2242 if (needed >= *pcchUrl) {
2243 *pcchUrl = needed + 1;
2244 return E_POINTER;
2245 } else {
2246 *pcchUrl = needed;
2247 strcpyW(pszUrl, pszPath);
2248 return S_FALSE;
2253 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2254 strcpyW(pszNewUrl, file_colonW);
2255 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2256 strcatW(pszNewUrl, three_slashesW);
2257 strcatW(pszNewUrl, pszPath);
2258 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2260 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2261 return ret;
2264 /*************************************************************************
2265 * SHAutoComplete [SHLWAPI.@]
2267 * Enable auto-completion for an edit control.
2269 * PARAMS
2270 * hwndEdit [I] Handle of control to enable auto-completion for
2271 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2273 * RETURNS
2274 * Success: S_OK. Auto-completion is enabled for the control.
2275 * Failure: An HRESULT error code indicating the error.
2277 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2279 FIXME("SHAutoComplete stub\n");
2280 return S_FALSE;
2283 /*************************************************************************
2284 * MLBuildResURLA [SHLWAPI.405]
2286 * Create a Url pointing to a resource in a module.
2288 * PARAMS
2289 * lpszLibName [I] Name of the module containing the resource
2290 * hMod [I] Callers module handle
2291 * dwFlags [I] Undocumented flags for loading the module
2292 * lpszRes [I] Resource name
2293 * lpszDest [O] Destination for resulting Url
2294 * dwDestLen [I] Length of lpszDest
2296 * RETURNS
2297 * Success: S_OK. lpszDest constains the resource Url.
2298 * Failure: E_INVALIDARG, if any argument is invalid, or
2299 * E_FAIL if dwDestLen is too small.
2301 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2302 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2304 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2305 HRESULT hRet;
2307 if (lpszLibName)
2308 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2310 if (lpszRes)
2311 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2313 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2314 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2316 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2317 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2318 if (SUCCEEDED(hRet) && lpszDest)
2319 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2321 return hRet;
2324 /*************************************************************************
2325 * MLBuildResURLA [SHLWAPI.406]
2327 * See MLBuildResURLA.
2329 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2330 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2332 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2333 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2334 HRESULT hRet = E_FAIL;
2336 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2337 debugstr_w(lpszRes), lpszDest, dwDestLen);
2339 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2340 !lpszDest || (dwFlags && dwFlags != 2))
2341 return E_INVALIDARG;
2343 if (dwDestLen >= szResLen + 1)
2345 dwDestLen -= (szResLen + 1);
2346 memcpy(lpszDest, szRes, sizeof(szRes));
2348 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2350 if (hMod)
2352 WCHAR szBuff[MAX_PATH];
2353 DWORD len;
2355 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2356 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2358 DWORD dwPathLen = strlenW(szBuff) + 1;
2360 if (dwDestLen >= dwPathLen)
2362 DWORD dwResLen;
2364 dwDestLen -= dwPathLen;
2365 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2367 dwResLen = strlenW(lpszRes) + 1;
2368 if (dwDestLen >= dwResLen + 1)
2370 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2371 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2372 hRet = S_OK;
2376 MLFreeLibrary(hMod);
2379 return hRet;