ntdll: move relocations from mapping into loader
[wine/kumbayo.git] / dlls / shlwapi / url.c
blobd1f64fefc1ff29c70d15c3faf20b773d40a71929
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 == '/')) str++;
135 if (*str) {
136 while (*str && ((*str == '-') ||
137 (*str == '.') ||
138 isalnumW(*str))) str++;
139 if (*str == '/') 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 <= ' ') return 0x80041001;
215 cnt = 0;
216 y->cchProtocol = 0;
217 y->pszProtocol = x;
218 while (*x) {
219 if (*x == ':') {
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 == '\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 HRESULT ret;
272 DWORD len, len2;
274 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
275 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
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 *pcchCanonicalized = len * 2;
290 HeapFree(GetProcessHeap(), 0, base);
291 return ret;
294 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
295 if (len2 > *pcchCanonicalized) {
296 *pcchCanonicalized = len2;
297 HeapFree(GetProcessHeap(), 0, base);
298 return E_POINTER;
300 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
301 *pcchCanonicalized = len;
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) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
324 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
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,
336 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
338 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
339 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
340 slash = '\\';
343 * state =
344 * 0 initial 1,3
345 * 1 have 2[+] alnum 2,3
346 * 2 have scheme (found :) 4,6,3
347 * 3 failed (no location)
348 * 4 have // 5,3
349 * 5 have 1[+] alnum 6,3
350 * 6 have location (found /) save root location
353 wk1 = (LPWSTR)pszUrl;
354 wk2 = lpszUrlCpy;
355 state = 0;
357 if(pszUrl[1] == ':') { /* Assume path */
358 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
360 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
361 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
362 if (dwFlags & URL_FILE_USE_PATHURL)
364 slash = '\\';
365 --wk2;
367 else
368 dwFlags |= URL_ESCAPE_UNSAFE;
369 state = 5;
372 while (*wk1) {
373 switch (state) {
374 case 0:
375 if (!isalnumW(*wk1)) {state = 3; break;}
376 *wk2++ = *wk1++;
377 if (!isalnumW(*wk1)) {state = 3; break;}
378 *wk2++ = *wk1++;
379 state = 1;
380 break;
381 case 1:
382 *wk2++ = *wk1;
383 if (*wk1++ == ':') state = 2;
384 break;
385 case 2:
386 if (*wk1 != '/') {state = 3; break;}
387 *wk2++ = *wk1++;
388 if (*wk1 != '/') {state = 6; break;}
389 *wk2++ = *wk1++;
390 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
391 wk1++;
392 state = 4;
393 break;
394 case 3:
395 nWkLen = strlenW(wk1);
396 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
397 mp = wk2;
398 wk1 += nWkLen;
399 wk2 += nWkLen;
401 while(mp < wk2) {
402 if(*mp == '/' || *mp == '\\')
403 *mp = slash;
404 mp++;
406 break;
407 case 4:
408 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
409 {state = 3; break;}
410 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
411 *wk2++ = *wk1++;
412 state = 5;
413 if (!*wk1)
414 *wk2++ = slash;
415 break;
416 case 5:
417 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
418 while(*wk1 == '/' || *wk1 == '\\') {
419 *wk2++ = slash;
420 wk1++;
422 state = 6;
423 break;
424 case 6:
425 if(dwFlags & URL_DONT_SIMPLIFY) {
426 state = 3;
427 break;
430 /* Now at root location, cannot back up any more. */
431 /* "root" will point at the '/' */
433 root = wk2-1;
434 while (*wk1) {
435 mp = strchrW(wk1, '/');
436 mp2 = strchrW(wk1, '\\');
437 if(mp2 && (!mp || mp2 < mp))
438 mp = mp2;
439 if (!mp) {
440 nWkLen = strlenW(wk1);
441 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
442 wk1 += nWkLen;
443 wk2 += nWkLen;
444 continue;
446 nLen = mp - wk1;
447 if(nLen) {
448 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
449 wk2 += nLen;
450 wk1 += nLen;
452 *wk2++ = slash;
453 wk1++;
455 if (*wk1 == '.') {
456 TRACE("found '/.'\n");
457 if (wk1[1] == '/' || wk1[1] == '\\') {
458 /* case of /./ -> skip the ./ */
459 wk1 += 2;
461 else if (wk1[1] == '.') {
462 /* found /.. look for next / */
463 TRACE("found '/..'\n");
464 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
465 || wk1[2] == '#' || !wk1[2]) {
466 /* case /../ -> need to backup wk2 */
467 TRACE("found '/../'\n");
468 *(wk2-1) = '\0'; /* set end of string */
469 mp = strrchrW(root, slash);
470 if (mp && (mp >= root)) {
471 /* found valid backup point */
472 wk2 = mp + 1;
473 if(wk1[2] != '/' && wk1[2] != '\\')
474 wk1 += 2;
475 else
476 wk1 += 3;
478 else {
479 /* did not find point, restore '/' */
480 *(wk2-1) = slash;
486 *wk2 = '\0';
487 break;
488 default:
489 FIXME("how did we get here - state=%d\n", state);
490 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
491 return E_INVALIDARG;
493 *wk2 = '\0';
494 TRACE("Simplified, orig <%s>, simple <%s>\n",
495 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
497 nLen = lstrlenW(lpszUrlCpy);
498 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
499 lpszUrlCpy[--nLen]=0;
501 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
502 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
504 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
505 URL_ESCAPE_SPACES_ONLY |
506 URL_ESCAPE_PERCENT |
507 URL_DONT_ESCAPE_EXTRA_INFO |
508 URL_ESCAPE_SEGMENT_ONLY ))) {
509 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
510 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
511 EscapeFlags);
512 } else { /* No escaping needed, just copy the string */
513 nLen = lstrlenW(lpszUrlCpy);
514 if(nLen < *pcchCanonicalized)
515 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
516 else {
517 hr = E_POINTER;
518 nLen++;
520 *pcchCanonicalized = nLen;
523 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
525 if (hr == S_OK)
526 TRACE("result %s\n", debugstr_w(pszCanonicalized));
528 return hr;
531 /*************************************************************************
532 * UrlCombineA [SHLWAPI.@]
534 * Combine two Urls.
536 * PARAMS
537 * pszBase [I] Base Url
538 * pszRelative [I] Url to combine with pszBase
539 * pszCombined [O] Destination for combined Url
540 * pcchCombined [O] Destination for length of pszCombined
541 * dwFlags [I] URL_ flags from "shlwapi.h"
543 * RETURNS
544 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
545 * contains its length.
546 * Failure: An HRESULT error code indicating the error.
548 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
549 LPSTR pszCombined, LPDWORD pcchCombined,
550 DWORD dwFlags)
552 LPWSTR base, relative, combined;
553 DWORD ret, len, len2;
555 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
556 debugstr_a(pszBase),debugstr_a(pszRelative),
557 pcchCombined?*pcchCombined:0,dwFlags);
559 if(!pszBase || !pszRelative || !pcchCombined)
560 return E_INVALIDARG;
562 base = HeapAlloc(GetProcessHeap(), 0,
563 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
564 relative = base + INTERNET_MAX_URL_LENGTH;
565 combined = relative + INTERNET_MAX_URL_LENGTH;
567 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
568 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
569 len = *pcchCombined;
571 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
572 if (ret != S_OK) {
573 *pcchCombined = len;
574 HeapFree(GetProcessHeap(), 0, base);
575 return ret;
578 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
579 if (len2 > *pcchCombined) {
580 *pcchCombined = len2;
581 HeapFree(GetProcessHeap(), 0, base);
582 return E_POINTER;
584 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
585 0, 0);
586 *pcchCombined = len2;
587 HeapFree(GetProcessHeap(), 0, base);
588 return S_OK;
591 /*************************************************************************
592 * UrlCombineW [SHLWAPI.@]
594 * See UrlCombineA.
596 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
597 LPWSTR pszCombined, LPDWORD pcchCombined,
598 DWORD dwFlags)
600 PARSEDURLW base, relative;
601 DWORD myflags, sizeloc = 0;
602 DWORD len, res1, res2, process_case = 0;
603 LPWSTR work, preliminary, mbase, mrelative;
604 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
605 static const WCHAR single_slash[] = {'/','\0'};
606 HRESULT ret;
608 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
609 debugstr_w(pszBase),debugstr_w(pszRelative),
610 pcchCombined?*pcchCombined:0,dwFlags);
612 if(!pszBase || !pszRelative || !pcchCombined)
613 return E_INVALIDARG;
615 base.cbSize = sizeof(base);
616 relative.cbSize = sizeof(relative);
618 /* Get space for duplicates of the input and the output */
619 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
620 sizeof(WCHAR));
621 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
622 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
623 *preliminary = '\0';
625 /* Canonicalize the base input prior to looking for the scheme */
626 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
627 len = INTERNET_MAX_URL_LENGTH;
628 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
630 /* Canonicalize the relative input prior to looking for the scheme */
631 len = INTERNET_MAX_URL_LENGTH;
632 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
634 /* See if the base has a scheme */
635 res1 = ParseURLW(mbase, &base);
636 if (res1) {
637 /* if pszBase has no scheme, then return pszRelative */
638 TRACE("no scheme detected in Base\n");
639 process_case = 1;
641 else do {
642 /* mk is a special case */
643 if(base.nScheme == URL_SCHEME_MK) {
644 static const WCHAR wsz[] = {':',':',0};
646 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
647 if(ptr) {
648 int delta;
650 ptr += 2;
651 delta = ptr-base.pszSuffix;
652 base.cchProtocol += delta;
653 base.pszSuffix += delta;
654 base.cchSuffix -= delta;
658 /* get size of location field (if it exists) */
659 work = (LPWSTR)base.pszSuffix;
660 sizeloc = 0;
661 if (*work++ == '/') {
662 if (*work++ == '/') {
663 /* At this point have start of location and
664 * it ends at next '/' or end of string.
666 while(*work && (*work != '/')) work++;
667 sizeloc = (DWORD)(work - base.pszSuffix);
671 /* Change .sizep2 to not have the last leaf in it,
672 * Note: we need to start after the location (if it exists)
674 work = strrchrW((base.pszSuffix+sizeloc), '/');
675 if (work) {
676 len = (DWORD)(work - base.pszSuffix + 1);
677 base.cchSuffix = len;
681 * At this point:
682 * .pszSuffix points to location (starting with '//')
683 * .cchSuffix length of location (above) and rest less the last
684 * leaf (if any)
685 * sizeloc length of location (above) up to but not including
686 * the last '/'
689 res2 = ParseURLW(mrelative, &relative);
690 if (res2) {
691 /* no scheme in pszRelative */
692 TRACE("no scheme detected in Relative\n");
693 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
694 relative.cchSuffix = strlenW(mrelative);
695 if (*pszRelative == ':') {
696 /* case that is either left alone or uses pszBase */
697 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
698 process_case = 5;
699 break;
701 process_case = 1;
702 break;
704 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
705 /* case that becomes "file:///" */
706 strcpyW(preliminary, myfilestr);
707 process_case = 1;
708 break;
710 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
711 /* pszRelative has location and rest */
712 process_case = 3;
713 break;
715 if (*mrelative == '/') {
716 /* case where pszRelative is root to location */
717 process_case = 4;
718 break;
720 process_case = (*base.pszSuffix == '/') ? 5 : 3;
721 break;
724 /* handle cases where pszRelative has scheme */
725 if ((base.cchProtocol == relative.cchProtocol) &&
726 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
728 /* since the schemes are the same */
729 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
730 /* case where pszRelative replaces location and following */
731 process_case = 3;
732 break;
734 if (*relative.pszSuffix == '/') {
735 /* case where pszRelative is root to location */
736 process_case = 4;
737 break;
739 /* replace either just location if base's location starts with a
740 * slash or otherwise everything */
741 process_case = (*base.pszSuffix == '/') ? 5 : 1;
742 break;
744 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
745 /* case where pszRelative replaces scheme, location,
746 * and following and handles PLUGGABLE
748 process_case = 2;
749 break;
751 process_case = 1;
752 break;
753 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
756 ret = S_OK;
757 switch (process_case) {
759 case 1: /*
760 * Return pszRelative appended to what ever is in pszCombined,
761 * (which may the string "file:///"
763 strcatW(preliminary, mrelative);
764 break;
766 case 2: /*
767 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
768 * and pszRelative starts with "//", then append a "/"
770 strcpyW(preliminary, mrelative);
771 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
772 URL_JustLocation(relative.pszSuffix))
773 strcatW(preliminary, single_slash);
774 break;
776 case 3: /*
777 * Return the pszBase scheme with pszRelative. Basically
778 * keeps the scheme and replaces the domain and following.
780 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
781 work = preliminary + base.cchProtocol + 1;
782 strcpyW(work, relative.pszSuffix);
783 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
784 URL_JustLocation(relative.pszSuffix))
785 strcatW(work, single_slash);
786 break;
788 case 4: /*
789 * Return the pszBase scheme and location but everything
790 * after the location is pszRelative. (Replace document
791 * from root on.)
793 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
794 work = preliminary + base.cchProtocol + 1 + sizeloc;
795 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
796 *(work++) = '/';
797 strcpyW(work, relative.pszSuffix);
798 break;
800 case 5: /*
801 * Return the pszBase without its document (if any) and
802 * append pszRelative after its scheme.
804 memcpy(preliminary, base.pszProtocol,
805 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
806 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
807 if (*work++ != '/')
808 *(work++) = '/';
809 strcpyW(work, relative.pszSuffix);
810 break;
812 default:
813 FIXME("How did we get here????? process_case=%d\n", process_case);
814 ret = E_INVALIDARG;
817 if (ret == S_OK) {
818 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
819 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
820 if(SUCCEEDED(ret) && pszCombined) {
821 lstrcpyW(pszCombined, mrelative);
823 TRACE("return-%d len=%d, %s\n",
824 process_case, *pcchCombined, debugstr_w(pszCombined));
826 HeapFree(GetProcessHeap(), 0, preliminary);
827 return ret;
830 /*************************************************************************
831 * UrlEscapeA [SHLWAPI.@]
834 HRESULT WINAPI UrlEscapeA(
835 LPCSTR pszUrl,
836 LPSTR pszEscaped,
837 LPDWORD pcchEscaped,
838 DWORD dwFlags)
840 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
841 WCHAR *escapedW = bufW;
842 UNICODE_STRING urlW;
843 HRESULT ret;
844 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
846 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
847 return E_INVALIDARG;
848 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
849 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
850 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
852 if(ret == S_OK) {
853 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
854 if(pszEscaped && *pcchEscaped > lenA) {
855 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
856 pszEscaped[lenA] = 0;
857 *pcchEscaped = lenA;
858 } else {
859 *pcchEscaped = lenA + 1;
860 ret = E_INVALIDARG;
863 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
864 RtlFreeUnicodeString(&urlW);
865 return ret;
868 #define WINE_URL_BASH_AS_SLASH 0x01
869 #define WINE_URL_COLLAPSE_SLASHES 0x02
870 #define WINE_URL_ESCAPE_SLASH 0x04
871 #define WINE_URL_ESCAPE_HASH 0x08
872 #define WINE_URL_ESCAPE_QUESTION 0x10
873 #define WINE_URL_STOP_ON_HASH 0x20
874 #define WINE_URL_STOP_ON_QUESTION 0x40
876 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
879 if (isalnumW(ch))
880 return FALSE;
882 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
883 if(ch == ' ')
884 return TRUE;
885 else
886 return FALSE;
889 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
890 return TRUE;
892 if (ch <= 31 || ch >= 127)
893 return TRUE;
895 else {
896 switch (ch) {
897 case ' ':
898 case '<':
899 case '>':
900 case '\"':
901 case '{':
902 case '}':
903 case '|':
904 case '\\':
905 case '^':
906 case ']':
907 case '[':
908 case '`':
909 case '&':
910 return TRUE;
912 case '/':
913 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
914 return FALSE;
916 case '?':
917 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
918 return FALSE;
920 case '#':
921 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
922 return FALSE;
924 default:
925 return FALSE;
931 /*************************************************************************
932 * UrlEscapeW [SHLWAPI.@]
934 * Converts unsafe characters in a Url into escape sequences.
936 * PARAMS
937 * pszUrl [I] Url to modify
938 * pszEscaped [O] Destination for modified Url
939 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
940 * dwFlags [I] URL_ flags from "shlwapi.h"
942 * RETURNS
943 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
944 * contains its length.
945 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
946 * pcchEscaped is set to the required length.
948 * Converts unsafe characters into their escape sequences.
950 * NOTES
951 * - By default this function stops converting at the first '?' or
952 * '#' character.
953 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
954 * converted, but the conversion continues past a '?' or '#'.
955 * - Note that this function did not work well (or at all) in shlwapi version 4.
957 * BUGS
958 * Only the following flags are implemented:
959 *| URL_ESCAPE_SPACES_ONLY
960 *| URL_DONT_ESCAPE_EXTRA_INFO
961 *| URL_ESCAPE_SEGMENT_ONLY
962 *| URL_ESCAPE_PERCENT
964 HRESULT WINAPI UrlEscapeW(
965 LPCWSTR pszUrl,
966 LPWSTR pszEscaped,
967 LPDWORD pcchEscaped,
968 DWORD dwFlags)
970 LPCWSTR src;
971 DWORD needed = 0, ret;
972 BOOL stop_escaping = FALSE;
973 WCHAR next[5], *dst = pszEscaped;
974 INT len;
975 PARSEDURLW parsed_url;
976 DWORD int_flags;
977 DWORD slashes = 0;
978 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
980 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
981 pcchEscaped, dwFlags);
983 if(!pszUrl || !pcchEscaped)
984 return E_INVALIDARG;
986 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
987 URL_ESCAPE_SEGMENT_ONLY |
988 URL_DONT_ESCAPE_EXTRA_INFO |
989 URL_ESCAPE_PERCENT))
990 FIXME("Unimplemented flags: %08x\n", dwFlags);
992 /* fix up flags */
993 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
994 /* if SPACES_ONLY specified, reset the other controls */
995 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
996 URL_ESCAPE_PERCENT |
997 URL_ESCAPE_SEGMENT_ONLY);
999 else
1000 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1001 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1004 int_flags = 0;
1005 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1006 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1007 } else {
1008 parsed_url.cbSize = sizeof(parsed_url);
1009 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1010 parsed_url.nScheme = URL_SCHEME_INVALID;
1012 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1014 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1015 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1017 switch(parsed_url.nScheme) {
1018 case URL_SCHEME_FILE:
1019 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1020 int_flags &= ~WINE_URL_STOP_ON_HASH;
1021 break;
1023 case URL_SCHEME_HTTP:
1024 case URL_SCHEME_HTTPS:
1025 int_flags |= WINE_URL_BASH_AS_SLASH;
1026 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1027 int_flags |= WINE_URL_ESCAPE_SLASH;
1028 break;
1030 case URL_SCHEME_MAILTO:
1031 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1032 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1033 break;
1035 case URL_SCHEME_INVALID:
1036 break;
1038 case URL_SCHEME_FTP:
1039 default:
1040 if(parsed_url.pszSuffix[0] != '/')
1041 int_flags |= WINE_URL_ESCAPE_SLASH;
1042 break;
1046 for(src = pszUrl; *src; ) {
1047 WCHAR cur = *src;
1048 len = 0;
1050 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1051 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1052 while(cur == '/' || cur == '\\') {
1053 slashes++;
1054 cur = *++src;
1056 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1057 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1058 src += localhost_len + 1;
1059 slashes = 3;
1062 switch(slashes) {
1063 case 1:
1064 case 3:
1065 next[0] = next[1] = next[2] = '/';
1066 len = 3;
1067 break;
1068 case 0:
1069 len = 0;
1070 break;
1071 default:
1072 next[0] = next[1] = '/';
1073 len = 2;
1074 break;
1077 if(len == 0) {
1079 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1080 stop_escaping = TRUE;
1082 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1083 stop_escaping = TRUE;
1085 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1087 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1088 next[0] = '%';
1089 next[1] = hexDigits[(cur >> 4) & 0xf];
1090 next[2] = hexDigits[cur & 0xf];
1091 len = 3;
1092 } else {
1093 next[0] = cur;
1094 len = 1;
1096 src++;
1099 if(needed + len <= *pcchEscaped) {
1100 memcpy(dst, next, len*sizeof(WCHAR));
1101 dst += len;
1103 needed += len;
1106 if(needed < *pcchEscaped) {
1107 *dst = '\0';
1108 ret = S_OK;
1109 } else {
1110 needed++; /* add one for the '\0' */
1111 ret = E_POINTER;
1113 *pcchEscaped = needed;
1114 return ret;
1118 /*************************************************************************
1119 * UrlUnescapeA [SHLWAPI.@]
1121 * Converts Url escape sequences back to ordinary characters.
1123 * PARAMS
1124 * pszUrl [I/O] Url to convert
1125 * pszUnescaped [O] Destination for converted Url
1126 * pcchUnescaped [I/O] Size of output string
1127 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1129 * RETURNS
1130 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1131 * dwFlags includes URL_ESCAPE_INPLACE.
1132 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1133 * this case pcchUnescaped is set to the size required.
1134 * NOTES
1135 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1136 * the first occurrence of either a '?' or '#' character.
1138 HRESULT WINAPI UrlUnescapeA(
1139 LPSTR pszUrl,
1140 LPSTR pszUnescaped,
1141 LPDWORD pcchUnescaped,
1142 DWORD dwFlags)
1144 char *dst, next;
1145 LPCSTR src;
1146 HRESULT ret;
1147 DWORD needed;
1148 BOOL stop_unescaping = FALSE;
1150 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1151 pcchUnescaped, dwFlags);
1153 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1154 return E_INVALIDARG;
1156 if(dwFlags & URL_UNESCAPE_INPLACE)
1157 dst = pszUrl;
1158 else
1159 dst = pszUnescaped;
1161 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1162 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1163 (*src == '#' || *src == '?')) {
1164 stop_unescaping = TRUE;
1165 next = *src;
1166 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1167 && stop_unescaping == FALSE) {
1168 INT ih;
1169 char buf[3];
1170 memcpy(buf, src + 1, 2);
1171 buf[2] = '\0';
1172 ih = strtol(buf, NULL, 16);
1173 next = (CHAR) ih;
1174 src += 2; /* Advance to end of escape */
1175 } else
1176 next = *src;
1178 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1179 *dst++ = next;
1182 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1183 *dst = '\0';
1184 ret = S_OK;
1185 } else {
1186 needed++; /* add one for the '\0' */
1187 ret = E_POINTER;
1189 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1190 *pcchUnescaped = needed;
1192 if (ret == S_OK) {
1193 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1194 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1197 return ret;
1200 /*************************************************************************
1201 * UrlUnescapeW [SHLWAPI.@]
1203 * See UrlUnescapeA.
1205 HRESULT WINAPI UrlUnescapeW(
1206 LPWSTR pszUrl,
1207 LPWSTR pszUnescaped,
1208 LPDWORD pcchUnescaped,
1209 DWORD dwFlags)
1211 WCHAR *dst, next;
1212 LPCWSTR src;
1213 HRESULT ret;
1214 DWORD needed;
1215 BOOL stop_unescaping = FALSE;
1217 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1218 pcchUnescaped, dwFlags);
1220 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1221 return E_INVALIDARG;
1223 if(dwFlags & URL_UNESCAPE_INPLACE)
1224 dst = pszUrl;
1225 else
1226 dst = pszUnescaped;
1228 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1229 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1230 (*src == '#' || *src == '?')) {
1231 stop_unescaping = TRUE;
1232 next = *src;
1233 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1234 && stop_unescaping == FALSE) {
1235 INT ih;
1236 WCHAR buf[5] = {'0','x',0};
1237 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1238 buf[4] = 0;
1239 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1240 next = (WCHAR) ih;
1241 src += 2; /* Advance to end of escape */
1242 } else
1243 next = *src;
1245 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1246 *dst++ = next;
1249 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1250 *dst = '\0';
1251 ret = S_OK;
1252 } else {
1253 needed++; /* add one for the '\0' */
1254 ret = E_POINTER;
1256 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1257 *pcchUnescaped = needed;
1259 if (ret == S_OK) {
1260 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1261 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1264 return ret;
1267 /*************************************************************************
1268 * UrlGetLocationA [SHLWAPI.@]
1270 * Get the location from a Url.
1272 * PARAMS
1273 * pszUrl [I] Url to get the location from
1275 * RETURNS
1276 * A pointer to the start of the location in pszUrl, or NULL if there is
1277 * no location.
1279 * NOTES
1280 * - MSDN erroneously states that "The location is the segment of the Url
1281 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1282 * stop at '?' and always return a NULL in this case.
1283 * - MSDN also erroneously states that "If a file URL has a query string,
1284 * the returned string is the query string". In all tested cases, if the
1285 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1286 *| Result Url
1287 *| ------ ---
1288 *| NULL file://aa/b/cd#hohoh
1289 *| #hohoh http://aa/b/cd#hohoh
1290 *| NULL fi://aa/b/cd#hohoh
1291 *| #hohoh ff://aa/b/cd#hohoh
1293 LPCSTR WINAPI UrlGetLocationA(
1294 LPCSTR pszUrl)
1296 PARSEDURLA base;
1297 DWORD res1;
1299 base.cbSize = sizeof(base);
1300 res1 = ParseURLA(pszUrl, &base);
1301 if (res1) return NULL; /* invalid scheme */
1303 /* if scheme is file: then never return pointer */
1304 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1306 /* Look for '#' and return its addr */
1307 return strchr(base.pszSuffix, '#');
1310 /*************************************************************************
1311 * UrlGetLocationW [SHLWAPI.@]
1313 * See UrlGetLocationA.
1315 LPCWSTR WINAPI UrlGetLocationW(
1316 LPCWSTR pszUrl)
1318 PARSEDURLW base;
1319 DWORD res1;
1321 base.cbSize = sizeof(base);
1322 res1 = ParseURLW(pszUrl, &base);
1323 if (res1) return NULL; /* invalid scheme */
1325 /* if scheme is file: then never return pointer */
1326 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1328 /* Look for '#' and return its addr */
1329 return strchrW(base.pszSuffix, '#');
1332 /*************************************************************************
1333 * UrlCompareA [SHLWAPI.@]
1335 * Compare two Urls.
1337 * PARAMS
1338 * pszUrl1 [I] First Url to compare
1339 * pszUrl2 [I] Url to compare to pszUrl1
1340 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1342 * RETURNS
1343 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1344 * than, equal to, or less than pszUrl1 respectively.
1346 INT WINAPI UrlCompareA(
1347 LPCSTR pszUrl1,
1348 LPCSTR pszUrl2,
1349 BOOL fIgnoreSlash)
1351 INT ret, len, len1, len2;
1353 if (!fIgnoreSlash)
1354 return strcmp(pszUrl1, pszUrl2);
1355 len1 = strlen(pszUrl1);
1356 if (pszUrl1[len1-1] == '/') len1--;
1357 len2 = strlen(pszUrl2);
1358 if (pszUrl2[len2-1] == '/') len2--;
1359 if (len1 == len2)
1360 return strncmp(pszUrl1, pszUrl2, len1);
1361 len = min(len1, len2);
1362 ret = strncmp(pszUrl1, pszUrl2, len);
1363 if (ret) return ret;
1364 if (len1 > len2) return 1;
1365 return -1;
1368 /*************************************************************************
1369 * UrlCompareW [SHLWAPI.@]
1371 * See UrlCompareA.
1373 INT WINAPI UrlCompareW(
1374 LPCWSTR pszUrl1,
1375 LPCWSTR pszUrl2,
1376 BOOL fIgnoreSlash)
1378 INT ret;
1379 size_t len, len1, len2;
1381 if (!fIgnoreSlash)
1382 return strcmpW(pszUrl1, pszUrl2);
1383 len1 = strlenW(pszUrl1);
1384 if (pszUrl1[len1-1] == '/') len1--;
1385 len2 = strlenW(pszUrl2);
1386 if (pszUrl2[len2-1] == '/') len2--;
1387 if (len1 == len2)
1388 return strncmpW(pszUrl1, pszUrl2, len1);
1389 len = min(len1, len2);
1390 ret = strncmpW(pszUrl1, pszUrl2, len);
1391 if (ret) return ret;
1392 if (len1 > len2) return 1;
1393 return -1;
1396 /*************************************************************************
1397 * HashData [SHLWAPI.@]
1399 * Hash an input block into a variable sized digest.
1401 * PARAMS
1402 * lpSrc [I] Input block
1403 * nSrcLen [I] Length of lpSrc
1404 * lpDest [I] Output for hash digest
1405 * nDestLen [I] Length of lpDest
1407 * RETURNS
1408 * Success: TRUE. lpDest is filled with the computed hash value.
1409 * Failure: FALSE, if any argument is invalid.
1411 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1412 unsigned char *lpDest, DWORD nDestLen)
1414 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1416 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1417 IsBadWritePtr(lpDest, nDestLen))
1418 return E_INVALIDARG;
1420 while (destCount >= 0)
1422 lpDest[destCount] = (destCount & 0xff);
1423 destCount--;
1426 while (srcCount >= 0)
1428 destCount = nDestLen - 1;
1429 while (destCount >= 0)
1431 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1432 destCount--;
1434 srcCount--;
1436 return S_OK;
1439 /*************************************************************************
1440 * UrlHashA [SHLWAPI.@]
1442 * Produce a Hash from a Url.
1444 * PARAMS
1445 * pszUrl [I] Url to hash
1446 * lpDest [O] Destinationh for hash
1447 * nDestLen [I] Length of lpDest
1449 * RETURNS
1450 * Success: S_OK. lpDest is filled with the computed hash value.
1451 * Failure: E_INVALIDARG, if any argument is invalid.
1453 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1455 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1456 return E_INVALIDARG;
1458 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1459 return S_OK;
1462 /*************************************************************************
1463 * UrlHashW [SHLWAPI.@]
1465 * See UrlHashA.
1467 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1469 char szUrl[MAX_PATH];
1471 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1473 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1474 return E_INVALIDARG;
1476 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1477 * return the same digests for the same URL.
1479 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1480 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1481 return S_OK;
1484 /*************************************************************************
1485 * UrlApplySchemeA [SHLWAPI.@]
1487 * Apply a scheme to a Url.
1489 * PARAMS
1490 * pszIn [I] Url to apply scheme to
1491 * pszOut [O] Destination for modified Url
1492 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1493 * dwFlags [I] URL_ flags from "shlwapi.h"
1495 * RETURNS
1496 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1497 * Failure: An HRESULT error code describing the error.
1499 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1501 LPWSTR in, out;
1502 DWORD ret, len, len2;
1504 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1505 debugstr_a(pszIn), *pcchOut, dwFlags);
1507 in = HeapAlloc(GetProcessHeap(), 0,
1508 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1509 out = in + INTERNET_MAX_URL_LENGTH;
1511 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1512 len = INTERNET_MAX_URL_LENGTH;
1514 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1515 if ((ret != S_OK) && (ret != S_FALSE)) {
1516 HeapFree(GetProcessHeap(), 0, in);
1517 return ret;
1520 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1521 if (len2 > *pcchOut) {
1522 *pcchOut = len2;
1523 HeapFree(GetProcessHeap(), 0, in);
1524 return E_POINTER;
1526 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1527 *pcchOut = len2;
1528 HeapFree(GetProcessHeap(), 0, in);
1529 return ret;
1532 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1534 HKEY newkey;
1535 BOOL j;
1536 INT index;
1537 DWORD value_len, data_len, dwType, i;
1538 WCHAR reg_path[MAX_PATH];
1539 WCHAR value[MAX_PATH], data[MAX_PATH];
1540 WCHAR Wxx, Wyy;
1542 MultiByteToWideChar(0, 0,
1543 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1544 -1, reg_path, MAX_PATH);
1545 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1546 index = 0;
1547 while(value_len = data_len = MAX_PATH,
1548 RegEnumValueW(newkey, index, value, &value_len,
1549 0, &dwType, (LPVOID)data, &data_len) == 0) {
1550 TRACE("guess %d %s is %s\n",
1551 index, debugstr_w(value), debugstr_w(data));
1553 j = FALSE;
1554 for(i=0; i<value_len; i++) {
1555 Wxx = pszIn[i];
1556 Wyy = value[i];
1557 /* remember that TRUE is not-equal */
1558 j = ChrCmpIW(Wxx, Wyy);
1559 if (j) break;
1561 if ((i == value_len) && !j) {
1562 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1563 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1564 RegCloseKey(newkey);
1565 return E_POINTER;
1567 strcpyW(pszOut, data);
1568 strcatW(pszOut, pszIn);
1569 *pcchOut = strlenW(pszOut);
1570 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1571 RegCloseKey(newkey);
1572 return S_OK;
1574 index++;
1576 RegCloseKey(newkey);
1577 return -1;
1580 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1582 HKEY newkey;
1583 DWORD data_len, dwType;
1584 WCHAR reg_path[MAX_PATH];
1585 WCHAR value[MAX_PATH], data[MAX_PATH];
1587 /* get and prepend default */
1588 MultiByteToWideChar(0, 0,
1589 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1590 -1, reg_path, MAX_PATH);
1591 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1592 data_len = MAX_PATH;
1593 value[0] = '@';
1594 value[1] = '\0';
1595 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1596 RegCloseKey(newkey);
1597 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1598 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1599 return E_POINTER;
1601 strcpyW(pszOut, data);
1602 strcatW(pszOut, pszIn);
1603 *pcchOut = strlenW(pszOut);
1604 TRACE("used default %s\n", debugstr_w(pszOut));
1605 return S_OK;
1608 /*************************************************************************
1609 * UrlApplySchemeW [SHLWAPI.@]
1611 * See UrlApplySchemeA.
1613 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1615 PARSEDURLW in_scheme;
1616 DWORD res1;
1617 HRESULT ret;
1619 TRACE("(in %s, out size %d, flags %08x)\n",
1620 debugstr_w(pszIn), *pcchOut, dwFlags);
1622 if (dwFlags & URL_APPLY_GUESSFILE) {
1623 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1624 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1625 strcpyW(pszOut, pszIn);
1626 *pcchOut = strlenW(pszOut);
1627 return S_FALSE;
1630 in_scheme.cbSize = sizeof(in_scheme);
1631 /* See if the base has a scheme */
1632 res1 = ParseURLW(pszIn, &in_scheme);
1633 if (res1) {
1634 /* no scheme in input, need to see if we need to guess */
1635 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1636 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1637 return ret;
1640 else {
1641 /* we have a scheme, see if valid (known scheme) */
1642 if (in_scheme.nScheme) {
1643 /* have valid scheme, so just copy and exit */
1644 if (strlenW(pszIn) + 1 > *pcchOut) {
1645 *pcchOut = strlenW(pszIn) + 1;
1646 return E_POINTER;
1648 strcpyW(pszOut, pszIn);
1649 *pcchOut = strlenW(pszOut);
1650 TRACE("valid scheme, returning copy\n");
1651 return S_OK;
1655 /* If we are here, then either invalid scheme,
1656 * or no scheme and can't/failed guess.
1658 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1659 ((res1 != 0)) ) &&
1660 (dwFlags & URL_APPLY_DEFAULT)) {
1661 /* find and apply default scheme */
1662 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1665 /* just copy and give proper return code */
1666 if (strlenW(pszIn) + 1 > *pcchOut) {
1667 *pcchOut = strlenW(pszIn) + 1;
1668 return E_POINTER;
1670 strcpyW(pszOut, pszIn);
1671 *pcchOut = strlenW(pszOut);
1672 TRACE("returning copy, left alone\n");
1673 return S_FALSE;
1676 /*************************************************************************
1677 * UrlIsA [SHLWAPI.@]
1679 * Determine if a Url is of a certain class.
1681 * PARAMS
1682 * pszUrl [I] Url to check
1683 * Urlis [I] URLIS_ constant from "shlwapi.h"
1685 * RETURNS
1686 * TRUE if pszUrl belongs to the class type in Urlis.
1687 * FALSE Otherwise.
1689 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1691 PARSEDURLA base;
1692 DWORD res1;
1693 LPCSTR last;
1695 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1697 switch (Urlis) {
1699 case URLIS_OPAQUE:
1700 base.cbSize = sizeof(base);
1701 res1 = ParseURLA(pszUrl, &base);
1702 if (res1) return FALSE; /* invalid scheme */
1703 switch (base.nScheme)
1705 case URL_SCHEME_MAILTO:
1706 case URL_SCHEME_SHELL:
1707 case URL_SCHEME_JAVASCRIPT:
1708 case URL_SCHEME_VBSCRIPT:
1709 case URL_SCHEME_ABOUT:
1710 return TRUE;
1712 return FALSE;
1714 case URLIS_FILEURL:
1715 return !StrCmpNA("file:", pszUrl, 5);
1717 case URLIS_DIRECTORY:
1718 last = pszUrl + strlen(pszUrl) - 1;
1719 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1721 case URLIS_URL:
1722 return PathIsURLA(pszUrl);
1724 case URLIS_NOHISTORY:
1725 case URLIS_APPLIABLE:
1726 case URLIS_HASQUERY:
1727 default:
1728 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1730 return FALSE;
1733 /*************************************************************************
1734 * UrlIsW [SHLWAPI.@]
1736 * See UrlIsA.
1738 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1740 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1741 PARSEDURLW base;
1742 DWORD res1;
1743 LPCWSTR last;
1745 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1747 switch (Urlis) {
1749 case URLIS_OPAQUE:
1750 base.cbSize = sizeof(base);
1751 res1 = ParseURLW(pszUrl, &base);
1752 if (res1) return FALSE; /* invalid scheme */
1753 switch (base.nScheme)
1755 case URL_SCHEME_MAILTO:
1756 case URL_SCHEME_SHELL:
1757 case URL_SCHEME_JAVASCRIPT:
1758 case URL_SCHEME_VBSCRIPT:
1759 case URL_SCHEME_ABOUT:
1760 return TRUE;
1762 return FALSE;
1764 case URLIS_FILEURL:
1765 return !strncmpW(stemp, pszUrl, 5);
1767 case URLIS_DIRECTORY:
1768 last = pszUrl + strlenW(pszUrl) - 1;
1769 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1771 case URLIS_URL:
1772 return PathIsURLW(pszUrl);
1774 case URLIS_NOHISTORY:
1775 case URLIS_APPLIABLE:
1776 case URLIS_HASQUERY:
1777 default:
1778 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1780 return FALSE;
1783 /*************************************************************************
1784 * UrlIsNoHistoryA [SHLWAPI.@]
1786 * Determine if a Url should not be stored in the users history list.
1788 * PARAMS
1789 * pszUrl [I] Url to check
1791 * RETURNS
1792 * TRUE, if pszUrl should be excluded from the history list,
1793 * FALSE otherwise.
1795 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1797 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1800 /*************************************************************************
1801 * UrlIsNoHistoryW [SHLWAPI.@]
1803 * See UrlIsNoHistoryA.
1805 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1807 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1810 /*************************************************************************
1811 * UrlIsOpaqueA [SHLWAPI.@]
1813 * Determine if a Url is opaque.
1815 * PARAMS
1816 * pszUrl [I] Url to check
1818 * RETURNS
1819 * TRUE if pszUrl is opaque,
1820 * FALSE Otherwise.
1822 * NOTES
1823 * An opaque Url is one that does not start with "<protocol>://".
1825 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1827 return UrlIsA(pszUrl, URLIS_OPAQUE);
1830 /*************************************************************************
1831 * UrlIsOpaqueW [SHLWAPI.@]
1833 * See UrlIsOpaqueA.
1835 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1837 return UrlIsW(pszUrl, URLIS_OPAQUE);
1840 /*************************************************************************
1841 * Scans for characters of type "type" and when not matching found,
1842 * returns pointer to it and length in size.
1844 * Characters tested based on RFC 1738
1846 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1848 static DWORD alwayszero = 0;
1849 BOOL cont = TRUE;
1851 *size = 0;
1853 switch(type){
1855 case SCHEME:
1856 while (cont) {
1857 if ( (islowerW(*start) && isalphaW(*start)) ||
1858 isdigitW(*start) ||
1859 (*start == '+') ||
1860 (*start == '-') ||
1861 (*start == '.')) {
1862 start++;
1863 (*size)++;
1865 else
1866 cont = FALSE;
1868 break;
1870 case USERPASS:
1871 while (cont) {
1872 if ( isalphaW(*start) ||
1873 isdigitW(*start) ||
1874 /* user/password only characters */
1875 (*start == ';') ||
1876 (*start == '?') ||
1877 (*start == '&') ||
1878 (*start == '=') ||
1879 /* *extra* characters */
1880 (*start == '!') ||
1881 (*start == '*') ||
1882 (*start == '\'') ||
1883 (*start == '(') ||
1884 (*start == ')') ||
1885 (*start == ',') ||
1886 /* *safe* characters */
1887 (*start == '$') ||
1888 (*start == '_') ||
1889 (*start == '+') ||
1890 (*start == '-') ||
1891 (*start == '.')) {
1892 start++;
1893 (*size)++;
1894 } else if (*start == '%') {
1895 if (isxdigitW(*(start+1)) &&
1896 isxdigitW(*(start+2))) {
1897 start += 3;
1898 *size += 3;
1899 } else
1900 cont = FALSE;
1901 } else
1902 cont = FALSE;
1904 break;
1906 case PORT:
1907 while (cont) {
1908 if (isdigitW(*start)) {
1909 start++;
1910 (*size)++;
1912 else
1913 cont = FALSE;
1915 break;
1917 case HOST:
1918 while (cont) {
1919 if (isalnumW(*start) ||
1920 (*start == '-') ||
1921 (*start == '.') ) {
1922 start++;
1923 (*size)++;
1925 else
1926 cont = FALSE;
1928 break;
1929 default:
1930 FIXME("unknown type %d\n", type);
1931 return (LPWSTR)&alwayszero;
1933 /* TRACE("scanned %d characters next char %p<%c>\n",
1934 *size, start, *start); */
1935 return start;
1938 /*************************************************************************
1939 * Attempt to parse URL into pieces.
1941 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1943 LPCWSTR work;
1945 memset(pl, 0, sizeof(WINE_PARSE_URL));
1946 pl->pScheme = pszUrl;
1947 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1948 if (!*work || (*work != ':')) goto ErrorExit;
1949 work++;
1950 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1951 pl->pUserName = work + 2;
1952 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1953 if (*work == ':' ) {
1954 /* parse password */
1955 work++;
1956 pl->pPassword = work;
1957 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1958 if (*work != '@') {
1959 /* what we just parsed must be the hostname and port
1960 * so reset pointers and clear then let it parse */
1961 pl->szUserName = pl->szPassword = 0;
1962 work = pl->pUserName - 1;
1963 pl->pUserName = pl->pPassword = 0;
1965 } else if (*work == '@') {
1966 /* no password */
1967 pl->szPassword = 0;
1968 pl->pPassword = 0;
1969 } else if (!*work || (*work == '/') || (*work == '.')) {
1970 /* what was parsed was hostname, so reset pointers and let it parse */
1971 pl->szUserName = pl->szPassword = 0;
1972 work = pl->pUserName - 1;
1973 pl->pUserName = pl->pPassword = 0;
1974 } else goto ErrorExit;
1976 /* now start parsing hostname or hostnumber */
1977 work++;
1978 pl->pHostName = work;
1979 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1980 if (*work == ':') {
1981 /* parse port */
1982 work++;
1983 pl->pPort = work;
1984 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1986 if (*work == '/') {
1987 /* see if query string */
1988 pl->pQuery = strchrW(work, '?');
1989 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1991 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1992 pl->pScheme, pl->szScheme,
1993 pl->pUserName, pl->szUserName,
1994 pl->pPassword, pl->szPassword,
1995 pl->pHostName, pl->szHostName,
1996 pl->pPort, pl->szPort,
1997 pl->pQuery, pl->szQuery);
1998 return S_OK;
1999 ErrorExit:
2000 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2001 return E_INVALIDARG;
2004 /*************************************************************************
2005 * UrlGetPartA [SHLWAPI.@]
2007 * Retrieve part of a Url.
2009 * PARAMS
2010 * pszIn [I] Url to parse
2011 * pszOut [O] Destination for part of pszIn requested
2012 * pcchOut [I] Size of pszOut
2013 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
2014 * needed size of pszOut INCLUDING '\0'.
2015 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2016 * dwFlags [I] URL_ flags from "shlwapi.h"
2018 * RETURNS
2019 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2020 * Failure: An HRESULT error code describing the error.
2022 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2023 DWORD dwPart, DWORD dwFlags)
2025 LPWSTR in, out;
2026 DWORD ret, len, len2;
2028 in = HeapAlloc(GetProcessHeap(), 0,
2029 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2030 out = in + INTERNET_MAX_URL_LENGTH;
2032 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2034 len = INTERNET_MAX_URL_LENGTH;
2035 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2037 if (ret != S_OK) {
2038 HeapFree(GetProcessHeap(), 0, in);
2039 return ret;
2042 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2043 if (len2 > *pcchOut) {
2044 *pcchOut = len2;
2045 HeapFree(GetProcessHeap(), 0, in);
2046 return E_POINTER;
2048 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2049 *pcchOut = len2;
2050 HeapFree(GetProcessHeap(), 0, in);
2051 return S_OK;
2054 /*************************************************************************
2055 * UrlGetPartW [SHLWAPI.@]
2057 * See UrlGetPartA.
2059 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2060 DWORD dwPart, DWORD dwFlags)
2062 WINE_PARSE_URL pl;
2063 HRESULT ret;
2064 DWORD size, schsize;
2065 LPCWSTR addr, schaddr;
2067 TRACE("(%s %p %p(%d) %08x %08x)\n",
2068 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2070 ret = URL_ParseUrl(pszIn, &pl);
2071 if (!ret) {
2072 schaddr = pl.pScheme;
2073 schsize = pl.szScheme;
2075 switch (dwPart) {
2076 case URL_PART_SCHEME:
2077 if (!pl.szScheme) return E_INVALIDARG;
2078 addr = pl.pScheme;
2079 size = pl.szScheme;
2080 break;
2082 case URL_PART_HOSTNAME:
2083 if (!pl.szHostName) return E_INVALIDARG;
2084 addr = pl.pHostName;
2085 size = pl.szHostName;
2086 break;
2088 case URL_PART_USERNAME:
2089 if (!pl.szUserName) return E_INVALIDARG;
2090 addr = pl.pUserName;
2091 size = pl.szUserName;
2092 break;
2094 case URL_PART_PASSWORD:
2095 if (!pl.szPassword) return E_INVALIDARG;
2096 addr = pl.pPassword;
2097 size = pl.szPassword;
2098 break;
2100 case URL_PART_PORT:
2101 if (!pl.szPort) return E_INVALIDARG;
2102 addr = pl.pPort;
2103 size = pl.szPort;
2104 break;
2106 case URL_PART_QUERY:
2107 if (!pl.szQuery) return E_INVALIDARG;
2108 addr = pl.pQuery;
2109 size = pl.szQuery;
2110 break;
2112 default:
2113 return E_INVALIDARG;
2116 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2117 if (*pcchOut < schsize + size + 2) {
2118 *pcchOut = schsize + size + 2;
2119 return E_POINTER;
2121 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2122 pszOut[schsize] = ':';
2123 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2124 pszOut[schsize+1+size] = 0;
2125 *pcchOut = schsize + 1 + size;
2127 else {
2128 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2129 memcpy(pszOut, addr, size*sizeof(WCHAR));
2130 pszOut[size] = 0;
2131 *pcchOut = size;
2133 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2135 return ret;
2138 /*************************************************************************
2139 * PathIsURLA [SHLWAPI.@]
2141 * Check if the given path is a Url.
2143 * PARAMS
2144 * lpszPath [I] Path to check.
2146 * RETURNS
2147 * TRUE if lpszPath is a Url.
2148 * FALSE if lpszPath is NULL or not a Url.
2150 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2152 PARSEDURLA base;
2153 DWORD res1;
2155 if (!lpstrPath || !*lpstrPath) return FALSE;
2157 /* get protocol */
2158 base.cbSize = sizeof(base);
2159 res1 = ParseURLA(lpstrPath, &base);
2160 return (base.nScheme != URL_SCHEME_INVALID);
2163 /*************************************************************************
2164 * PathIsURLW [SHLWAPI.@]
2166 * See PathIsURLA.
2168 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2170 PARSEDURLW base;
2171 DWORD res1;
2173 if (!lpstrPath || !*lpstrPath) return FALSE;
2175 /* get protocol */
2176 base.cbSize = sizeof(base);
2177 res1 = ParseURLW(lpstrPath, &base);
2178 return (base.nScheme != URL_SCHEME_INVALID);
2181 /*************************************************************************
2182 * UrlCreateFromPathA [SHLWAPI.@]
2184 * See UrlCreateFromPathW
2186 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2188 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2189 WCHAR *urlW = bufW;
2190 UNICODE_STRING pathW;
2191 HRESULT ret;
2192 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2194 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2195 return E_INVALIDARG;
2196 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2197 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2198 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2200 if(ret == S_OK || ret == S_FALSE) {
2201 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2202 if(*pcchUrl > lenA) {
2203 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2204 pszUrl[lenA] = 0;
2205 *pcchUrl = lenA;
2206 } else {
2207 *pcchUrl = lenA + 1;
2208 ret = E_POINTER;
2211 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2212 RtlFreeUnicodeString(&pathW);
2213 return ret;
2216 /*************************************************************************
2217 * UrlCreateFromPathW [SHLWAPI.@]
2219 * Create a Url from a file path.
2221 * PARAMS
2222 * pszPath [I] Path to convert
2223 * pszUrl [O] Destination for the converted Url
2224 * pcchUrl [I/O] Length of pszUrl
2225 * dwReserved [I] Reserved, must be 0
2227 * RETURNS
2228 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2229 * Failure: An HRESULT error code.
2231 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2233 DWORD needed;
2234 HRESULT ret;
2235 WCHAR *pszNewUrl;
2236 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2237 WCHAR three_slashesW[] = {'/','/','/',0};
2238 PARSEDURLW parsed_url;
2240 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2242 /* Validate arguments */
2243 if (dwReserved != 0)
2244 return E_INVALIDARG;
2245 if (!pszUrl || !pcchUrl)
2246 return E_INVALIDARG;
2249 parsed_url.cbSize = sizeof(parsed_url);
2250 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2251 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2252 needed = strlenW(pszPath);
2253 if (needed >= *pcchUrl) {
2254 *pcchUrl = needed + 1;
2255 return E_POINTER;
2256 } else {
2257 *pcchUrl = needed;
2258 strcpyW(pszUrl, pszPath);
2259 return S_FALSE;
2264 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2265 strcpyW(pszNewUrl, file_colonW);
2266 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2267 strcatW(pszNewUrl, three_slashesW);
2268 strcatW(pszNewUrl, pszPath);
2269 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2271 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2272 return ret;
2275 /*************************************************************************
2276 * SHAutoComplete [SHLWAPI.@]
2278 * Enable auto-completion for an edit control.
2280 * PARAMS
2281 * hwndEdit [I] Handle of control to enable auto-completion for
2282 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2284 * RETURNS
2285 * Success: S_OK. Auto-completion is enabled for the control.
2286 * Failure: An HRESULT error code indicating the error.
2288 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2290 FIXME("SHAutoComplete stub\n");
2291 return S_FALSE;
2294 /*************************************************************************
2295 * MLBuildResURLA [SHLWAPI.405]
2297 * Create a Url pointing to a resource in a module.
2299 * PARAMS
2300 * lpszLibName [I] Name of the module containing the resource
2301 * hMod [I] Callers module handle
2302 * dwFlags [I] Undocumented flags for loading the module
2303 * lpszRes [I] Resource name
2304 * lpszDest [O] Destination for resulting Url
2305 * dwDestLen [I] Length of lpszDest
2307 * RETURNS
2308 * Success: S_OK. lpszDest constains the resource Url.
2309 * Failure: E_INVALIDARG, if any argument is invalid, or
2310 * E_FAIL if dwDestLen is too small.
2312 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2313 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2315 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2316 HRESULT hRet;
2318 if (lpszLibName)
2319 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2321 if (lpszRes)
2322 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2324 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2325 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2327 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2328 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2329 if (SUCCEEDED(hRet) && lpszDest)
2330 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2332 return hRet;
2335 /*************************************************************************
2336 * MLBuildResURLA [SHLWAPI.406]
2338 * See MLBuildResURLA.
2340 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2341 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2343 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2344 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2345 HRESULT hRet = E_FAIL;
2347 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2348 debugstr_w(lpszRes), lpszDest, dwDestLen);
2350 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2351 !lpszDest || (dwFlags && dwFlags != 2))
2352 return E_INVALIDARG;
2354 if (dwDestLen >= szResLen + 1)
2356 dwDestLen -= (szResLen + 1);
2357 memcpy(lpszDest, szRes, sizeof(szRes));
2359 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2361 if (hMod)
2363 WCHAR szBuff[MAX_PATH];
2364 DWORD len;
2366 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2367 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2369 DWORD dwPathLen = strlenW(szBuff) + 1;
2371 if (dwDestLen >= dwPathLen)
2373 DWORD dwResLen;
2375 dwDestLen -= dwPathLen;
2376 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2378 dwResLen = strlenW(lpszRes) + 1;
2379 if (dwDestLen >= dwResLen + 1)
2381 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2382 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2383 hRet = S_OK;
2387 MLFreeLibrary(hMod);
2390 return hRet;