pop b9dc5e3b122188b492ca3b22b2645fadb18a6ce1
[wine/hacks.git] / dlls / shlwapi / url.c
blobd7a36dbec467594e8f41619ba11bafdba788763b
1 /*
2 * Url functions
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #define NO_SHLWAPI_STREAM
35 #include "shlwapi.h"
36 #include "intshcut.h"
37 #include "wine/debug.h"
39 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
40 BOOL WINAPI MLFreeLibrary(HMODULE);
41 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell);
45 /* The following schemes were identified in the native version of
46 * SHLWAPI.DLL version 5.50
48 static const struct {
49 URL_SCHEME scheme_number;
50 WCHAR scheme_name[12];
51 } shlwapi_schemes[] = {
52 {URL_SCHEME_FTP, {'f','t','p',0}},
53 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
54 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
55 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
56 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
57 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
58 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
59 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
60 {URL_SCHEME_FILE, {'f','i','l','e',0}},
61 {URL_SCHEME_MK, {'m','k',0}},
62 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
63 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
64 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
65 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
66 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
67 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
68 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
69 {URL_SCHEME_RES, {'r','e','s',0}},
72 typedef struct {
73 LPCWSTR pScheme; /* [out] start of scheme */
74 DWORD szScheme; /* [out] size of scheme (until colon) */
75 LPCWSTR pUserName; /* [out] start of Username */
76 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
77 LPCWSTR pPassword; /* [out] start of Password */
78 DWORD szPassword; /* [out] size of Password (until "@") */
79 LPCWSTR pHostName; /* [out] start of Hostname */
80 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
81 LPCWSTR pPort; /* [out] start of Port */
82 DWORD szPort; /* [out] size of Port (until "/" or eos) */
83 LPCWSTR pQuery; /* [out] start of Query */
84 DWORD szQuery; /* [out] size of Query (until eos) */
85 } WINE_PARSE_URL;
87 typedef enum {
88 SCHEME,
89 HOST,
90 PORT,
91 USERPASS,
92 } WINE_URL_SCAN_TYPE;
94 static const CHAR hexDigits[] = "0123456789ABCDEF";
96 static const WCHAR fileW[] = {'f','i','l','e','\0'};
98 static const unsigned char HashDataLookup[256] = {
99 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
100 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
101 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
102 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
103 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
104 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
105 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
106 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
107 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
108 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
109 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
110 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
111 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
112 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
113 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
114 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
115 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
116 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
117 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
118 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
120 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
122 unsigned int i;
124 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
125 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
126 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
127 return shlwapi_schemes[i].scheme_number;
130 return URL_SCHEME_UNKNOWN;
133 /*************************************************************************
134 * @ [SHLWAPI.1]
136 * Parse a Url into its constituent parts.
138 * PARAMS
139 * x [I] Url to parse
140 * y [O] Undocumented structure holding the parsed information
142 * RETURNS
143 * Success: S_OK. y contains the parsed Url details.
144 * Failure: An HRESULT error code.
146 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
148 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
149 const char *ptr = x;
150 int len;
152 TRACE("%s %p\n", debugstr_a(x), y);
154 if(y->cbSize != sizeof(*y))
155 return E_INVALIDARG;
157 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
158 ptr++;
160 if (*ptr != ':' || ptr <= x+1) {
161 y->pszProtocol = NULL;
162 return URL_E_INVALID_SYNTAX;
165 y->pszProtocol = x;
166 y->cchProtocol = ptr-x;
167 y->pszSuffix = ptr+1;
168 y->cchSuffix = strlen(y->pszSuffix);
170 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
171 scheme, sizeof(scheme)/sizeof(WCHAR));
172 y->nScheme = get_scheme_code(scheme, len);
174 return S_OK;
177 /*************************************************************************
178 * @ [SHLWAPI.2]
180 * Unicode version of ParseURLA.
182 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
184 const WCHAR *ptr = x;
186 TRACE("%s %p\n", debugstr_w(x), y);
188 if(y->cbSize != sizeof(*y))
189 return E_INVALIDARG;
191 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
192 ptr++;
194 if (*ptr != ':' || ptr <= x+1) {
195 y->pszProtocol = NULL;
196 return URL_E_INVALID_SYNTAX;
199 y->pszProtocol = x;
200 y->cchProtocol = ptr-x;
201 y->pszSuffix = ptr+1;
202 y->cchSuffix = strlenW(y->pszSuffix);
203 y->nScheme = get_scheme_code(x, ptr-x);
205 return S_OK;
208 /*************************************************************************
209 * UrlCanonicalizeA [SHLWAPI.@]
211 * Canonicalize a Url.
213 * PARAMS
214 * pszUrl [I] Url to cCanonicalize
215 * pszCanonicalized [O] Destination for converted Url.
216 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
217 * dwFlags [I] Flags controlling the conversion.
219 * RETURNS
220 * Success: S_OK. The pszCanonicalized contains the converted Url.
221 * Failure: E_POINTER, if *pcchCanonicalized is too small.
223 * MSDN incorrectly describes the flags for this function. They should be:
224 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
225 *| URL_ESCAPE_SPACES_ONLY 0x04000000
226 *| URL_ESCAPE_PERCENT 0x00001000
227 *| URL_ESCAPE_UNSAFE 0x10000000
228 *| URL_UNESCAPE 0x10000000
229 *| URL_DONT_SIMPLIFY 0x08000000
230 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
232 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
233 LPDWORD pcchCanonicalized, DWORD dwFlags)
235 LPWSTR url, canonical;
236 HRESULT ret;
237 DWORD len;
239 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
240 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
242 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
243 return E_INVALIDARG;
245 len = strlen(pszUrl)+1;
246 url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
247 canonical = HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized*sizeof(WCHAR));
248 if(!url || !canonical) {
249 HeapFree(GetProcessHeap(), 0, url);
250 HeapFree(GetProcessHeap(), 0, canonical);
251 return E_OUTOFMEMORY;
254 MultiByteToWideChar(0, 0, pszUrl, -1, url, len);
256 ret = UrlCanonicalizeW(url, canonical, pcchCanonicalized, dwFlags);
257 if(ret == S_OK)
258 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized,
259 *pcchCanonicalized+1, 0, 0);
261 HeapFree(GetProcessHeap(), 0, canonical);
262 return ret;
265 /*************************************************************************
266 * UrlCanonicalizeW [SHLWAPI.@]
268 * See UrlCanonicalizeA.
270 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
271 LPDWORD pcchCanonicalized, DWORD dwFlags)
273 HRESULT hr = S_OK;
274 DWORD EscapeFlags;
275 LPCWSTR wk1, root;
276 LPWSTR lpszUrlCpy, wk2, mp, mp2;
277 INT state;
278 DWORD nByteLen, nLen, nWkLen;
279 WCHAR slash = '/';
281 static const WCHAR wszFile[] = {'f','i','l','e',':'};
282 static const WCHAR wszRes[] = {'r','e','s',':'};
283 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
284 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
286 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
287 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
289 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
290 return E_INVALIDARG;
292 if(!*pszUrl) {
293 *pszCanonicalized = 0;
294 return S_OK;
297 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
298 /* Allocate memory for simplified URL (before escaping) */
299 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
300 nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR));
302 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
303 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
304 slash = '\\';
306 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, pszUrl, sizeof(wszRes))) {
307 dwFlags &= ~URL_FILE_USE_PATHURL;
308 slash = '\0';
312 * state =
313 * 0 initial 1,3
314 * 1 have 2[+] alnum 2,3
315 * 2 have scheme (found :) 4,6,3
316 * 3 failed (no location)
317 * 4 have // 5,3
318 * 5 have 1[+] alnum 6,3
319 * 6 have location (found /) save root location
322 wk1 = pszUrl;
323 wk2 = lpszUrlCpy;
324 state = 0;
326 if(pszUrl[1] == ':') { /* Assume path */
327 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
328 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
329 if (dwFlags & URL_FILE_USE_PATHURL)
331 slash = '\\';
332 --wk2;
334 else
335 dwFlags |= URL_ESCAPE_UNSAFE;
336 state = 5;
339 while (*wk1) {
340 switch (state) {
341 case 0:
342 if (!isalnumW(*wk1)) {state = 3; break;}
343 *wk2++ = *wk1++;
344 if (!isalnumW(*wk1)) {state = 3; break;}
345 *wk2++ = *wk1++;
346 state = 1;
347 break;
348 case 1:
349 *wk2++ = *wk1;
350 if (*wk1++ == ':') state = 2;
351 break;
352 case 2:
353 *wk2++ = *wk1++;
354 if (*wk1 != '/') {state = 6; break;}
355 *wk2++ = *wk1++;
356 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
357 && !strncmpW(wszFile, pszUrl, sizeof(wszFile)/sizeof(WCHAR))
358 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
359 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
360 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
361 wk1++;
363 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
364 wk1++;
365 state = 4;
366 break;
367 case 3:
368 nWkLen = strlenW(wk1);
369 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
370 mp = wk2;
371 wk1 += nWkLen;
372 wk2 += nWkLen;
374 if(slash) {
375 while(mp < wk2) {
376 if(*mp == '/' || *mp == '\\')
377 *mp = slash;
378 mp++;
381 break;
382 case 4:
383 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
384 {state = 3; break;}
385 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
386 *wk2++ = *wk1++;
387 state = 5;
388 if (!*wk1) {
389 if(slash)
390 *wk2++ = slash;
391 else
392 *wk2++ = '/';
394 break;
395 case 5:
396 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
397 while(*wk1 == '/' || *wk1 == '\\') {
398 if(slash)
399 *wk2++ = slash;
400 else
401 *wk2++ = *wk1;
402 wk1++;
404 state = 6;
405 break;
406 case 6:
407 if(dwFlags & URL_DONT_SIMPLIFY) {
408 state = 3;
409 break;
412 /* Now at root location, cannot back up any more. */
413 /* "root" will point at the '/' */
415 root = wk2-1;
416 while (*wk1) {
417 mp = strchrW(wk1, '/');
418 mp2 = strchrW(wk1, '\\');
419 if(mp2 && (!mp || mp2 < mp))
420 mp = mp2;
421 if (!mp) {
422 nWkLen = strlenW(wk1);
423 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
424 wk1 += nWkLen;
425 wk2 += nWkLen;
426 continue;
428 nLen = mp - wk1;
429 if(nLen) {
430 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
431 wk2 += nLen;
432 wk1 += nLen;
434 if(slash)
435 *wk2++ = slash;
436 else
437 *wk2++ = *wk1;
438 wk1++;
440 if (*wk1 == '.') {
441 TRACE("found '/.'\n");
442 if (wk1[1] == '/' || wk1[1] == '\\') {
443 /* case of /./ -> skip the ./ */
444 wk1 += 2;
446 else if (wk1[1] == '.') {
447 /* found /.. look for next / */
448 TRACE("found '/..'\n");
449 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
450 || wk1[2] == '#' || !wk1[2]) {
451 /* case /../ -> need to backup wk2 */
452 TRACE("found '/../'\n");
453 *(wk2-1) = '\0'; /* set end of string */
454 mp = strrchrW(root, '/');
455 mp2 = strrchrW(root, '\\');
456 if(mp2 && (!mp || mp2 < mp))
457 mp = mp2;
458 if (mp && (mp >= root)) {
459 /* found valid backup point */
460 wk2 = mp + 1;
461 if(wk1[2] != '/' && wk1[2] != '\\')
462 wk1 += 2;
463 else
464 wk1 += 3;
466 else {
467 /* did not find point, restore '/' */
468 *(wk2-1) = slash;
474 *wk2 = '\0';
475 break;
476 default:
477 FIXME("how did we get here - state=%d\n", state);
478 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
479 return E_INVALIDARG;
481 *wk2 = '\0';
482 TRACE("Simplified, orig <%s>, simple <%s>\n",
483 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
485 nLen = lstrlenW(lpszUrlCpy);
486 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
487 lpszUrlCpy[--nLen]=0;
489 if((dwFlags & URL_UNESCAPE) ||
490 ((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
491 && !memcmp(wszFile, pszUrl, sizeof(wszFile))))
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 HRESULT ret;
597 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
598 debugstr_w(pszBase),debugstr_w(pszRelative),
599 pcchCombined?*pcchCombined:0,dwFlags);
601 if(!pszBase || !pszRelative || !pcchCombined)
602 return E_INVALIDARG;
604 base.cbSize = sizeof(base);
605 relative.cbSize = sizeof(relative);
607 /* Get space for duplicates of the input and the output */
608 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
609 sizeof(WCHAR));
610 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
611 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
612 *preliminary = '\0';
614 /* Canonicalize the base input prior to looking for the scheme */
615 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
616 len = INTERNET_MAX_URL_LENGTH;
617 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
619 /* Canonicalize the relative input prior to looking for the scheme */
620 len = INTERNET_MAX_URL_LENGTH;
621 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
623 /* See if the base has a scheme */
624 res1 = ParseURLW(mbase, &base);
625 if (res1) {
626 /* if pszBase has no scheme, then return pszRelative */
627 TRACE("no scheme detected in Base\n");
628 process_case = 1;
630 else do {
631 BOOL manual_search = FALSE;
633 /* mk is a special case */
634 if(base.nScheme == URL_SCHEME_MK) {
635 static const WCHAR wsz[] = {':',':',0};
637 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
638 if(ptr) {
639 int delta;
641 ptr += 2;
642 delta = ptr-base.pszSuffix;
643 base.cchProtocol += delta;
644 base.pszSuffix += delta;
645 base.cchSuffix -= delta;
647 }else {
648 /* get size of location field (if it exists) */
649 work = (LPWSTR)base.pszSuffix;
650 sizeloc = 0;
651 if (*work++ == '/') {
652 if (*work++ == '/') {
653 /* At this point have start of location and
654 * it ends at next '/' or end of string.
656 while(*work && (*work != '/')) work++;
657 sizeloc = (DWORD)(work - base.pszSuffix);
662 /* If there is a '#' and the characters immediately preceding it are
663 * ".htm[l]", then begin looking for the last leaf starting from
664 * the '#'. Otherwise the '#' is not meaningful and just start
665 * looking from the end. */
666 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
667 const WCHAR htmlW[] = {'.','h','t','m','l',0};
668 const int len_htmlW = 5;
669 const WCHAR htmW[] = {'.','h','t','m',0};
670 const int len_htmW = 4;
672 if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
673 work -= len_htmW;
674 if (strncmpiW(work, htmW, len_htmW) == 0)
675 manual_search = TRUE;
676 work += len_htmW;
679 if (!manual_search &&
680 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
681 work -= len_htmlW;
682 if (strncmpiW(work, htmlW, len_htmlW) == 0)
683 manual_search = TRUE;
684 work += len_htmlW;
688 if (manual_search) {
689 /* search backwards starting from the current position */
690 while (*work != '/' && work > base.pszSuffix + sizeloc)
691 --work;
692 if (work > base.pszSuffix + sizeloc)
693 base.cchSuffix = work - base.pszSuffix + 1;
694 }else {
695 /* search backwards starting from the end of the string */
696 work = strrchrW((base.pszSuffix+sizeloc), '/');
697 if (work) {
698 len = (DWORD)(work - base.pszSuffix + 1);
699 base.cchSuffix = len;
704 * At this point:
705 * .pszSuffix points to location (starting with '//')
706 * .cchSuffix length of location (above) and rest less the last
707 * leaf (if any)
708 * sizeloc length of location (above) up to but not including
709 * the last '/'
712 res2 = ParseURLW(mrelative, &relative);
713 if (res2) {
714 /* no scheme in pszRelative */
715 TRACE("no scheme detected in Relative\n");
716 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
717 relative.cchSuffix = strlenW(mrelative);
718 if (*pszRelative == ':') {
719 /* case that is either left alone or uses pszBase */
720 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
721 process_case = 5;
722 break;
724 process_case = 1;
725 break;
727 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
728 /* case that becomes "file:///" */
729 strcpyW(preliminary, myfilestr);
730 process_case = 1;
731 break;
733 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
734 /* pszRelative has location and rest */
735 process_case = 3;
736 break;
738 if (*mrelative == '/') {
739 /* case where pszRelative is root to location */
740 process_case = 4;
741 break;
743 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
744 break;
747 /* handle cases where pszRelative has scheme */
748 if ((base.cchProtocol == relative.cchProtocol) &&
749 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
751 /* since the schemes are the same */
752 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
753 /* case where pszRelative replaces location and following */
754 process_case = 3;
755 break;
757 if (*relative.pszSuffix == '/') {
758 /* case where pszRelative is root to location */
759 process_case = 4;
760 break;
762 /* replace either just location if base's location starts with a
763 * slash or otherwise everything */
764 process_case = (*base.pszSuffix == '/') ? 5 : 1;
765 break;
767 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
768 /* case where pszRelative replaces scheme, location,
769 * and following and handles PLUGGABLE
771 process_case = 2;
772 break;
774 process_case = 1;
775 break;
776 } while(FALSE); /* a little trick to allow easy exit from nested if's */
778 ret = S_OK;
779 switch (process_case) {
781 case 1: /*
782 * Return pszRelative appended to what ever is in pszCombined,
783 * (which may the string "file:///"
785 strcatW(preliminary, mrelative);
786 break;
788 case 2: /* case where pszRelative replaces scheme, and location */
789 strcpyW(preliminary, mrelative);
790 break;
792 case 3: /*
793 * Return the pszBase scheme with pszRelative. Basically
794 * keeps the scheme and replaces the domain and following.
796 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
797 work = preliminary + base.cchProtocol + 1;
798 strcpyW(work, relative.pszSuffix);
799 break;
801 case 4: /*
802 * Return the pszBase scheme and location but everything
803 * after the location is pszRelative. (Replace document
804 * from root on.)
806 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
807 work = preliminary + base.cchProtocol + 1 + sizeloc;
808 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
809 *(work++) = '/';
810 strcpyW(work, relative.pszSuffix);
811 break;
813 case 5: /*
814 * Return the pszBase without its document (if any) and
815 * append pszRelative after its scheme.
817 memcpy(preliminary, base.pszProtocol,
818 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
819 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
820 if (*work++ != '/')
821 *(work++) = '/';
822 strcpyW(work, relative.pszSuffix);
823 break;
825 default:
826 FIXME("How did we get here????? process_case=%d\n", process_case);
827 ret = E_INVALIDARG;
830 if (ret == S_OK) {
831 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
832 if(*pcchCombined == 0)
833 *pcchCombined = 1;
834 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
835 if(SUCCEEDED(ret) && pszCombined) {
836 lstrcpyW(pszCombined, mrelative);
838 TRACE("return-%d len=%d, %s\n",
839 process_case, *pcchCombined, debugstr_w(pszCombined));
841 HeapFree(GetProcessHeap(), 0, preliminary);
842 return ret;
845 /*************************************************************************
846 * UrlEscapeA [SHLWAPI.@]
849 HRESULT WINAPI UrlEscapeA(
850 LPCSTR pszUrl,
851 LPSTR pszEscaped,
852 LPDWORD pcchEscaped,
853 DWORD dwFlags)
855 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
856 WCHAR *escapedW = bufW;
857 UNICODE_STRING urlW;
858 HRESULT ret;
859 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
861 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
862 return E_INVALIDARG;
864 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
865 return E_INVALIDARG;
866 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
867 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
868 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
870 if(ret == S_OK) {
871 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
872 if(*pcchEscaped > lenA) {
873 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
874 pszEscaped[lenA] = 0;
875 *pcchEscaped = lenA;
876 } else {
877 *pcchEscaped = lenA + 1;
878 ret = E_POINTER;
881 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
882 RtlFreeUnicodeString(&urlW);
883 return ret;
886 #define WINE_URL_BASH_AS_SLASH 0x01
887 #define WINE_URL_COLLAPSE_SLASHES 0x02
888 #define WINE_URL_ESCAPE_SLASH 0x04
889 #define WINE_URL_ESCAPE_HASH 0x08
890 #define WINE_URL_ESCAPE_QUESTION 0x10
891 #define WINE_URL_STOP_ON_HASH 0x20
892 #define WINE_URL_STOP_ON_QUESTION 0x40
894 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
897 if (isalnumW(ch))
898 return FALSE;
900 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
901 if(ch == ' ')
902 return TRUE;
903 else
904 return FALSE;
907 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
908 return TRUE;
910 if (ch <= 31 || ch >= 127)
911 return TRUE;
913 else {
914 switch (ch) {
915 case ' ':
916 case '<':
917 case '>':
918 case '\"':
919 case '{':
920 case '}':
921 case '|':
922 case '\\':
923 case '^':
924 case ']':
925 case '[':
926 case '`':
927 case '&':
928 return TRUE;
930 case '/':
931 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
932 return FALSE;
934 case '?':
935 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
936 return FALSE;
938 case '#':
939 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
940 return FALSE;
942 default:
943 return FALSE;
949 /*************************************************************************
950 * UrlEscapeW [SHLWAPI.@]
952 * Converts unsafe characters in a Url into escape sequences.
954 * PARAMS
955 * pszUrl [I] Url to modify
956 * pszEscaped [O] Destination for modified Url
957 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
958 * dwFlags [I] URL_ flags from "shlwapi.h"
960 * RETURNS
961 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
962 * contains its length.
963 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
964 * pcchEscaped is set to the required length.
966 * Converts unsafe characters into their escape sequences.
968 * NOTES
969 * - By default this function stops converting at the first '?' or
970 * '#' character.
971 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
972 * converted, but the conversion continues past a '?' or '#'.
973 * - Note that this function did not work well (or at all) in shlwapi version 4.
975 * BUGS
976 * Only the following flags are implemented:
977 *| URL_ESCAPE_SPACES_ONLY
978 *| URL_DONT_ESCAPE_EXTRA_INFO
979 *| URL_ESCAPE_SEGMENT_ONLY
980 *| URL_ESCAPE_PERCENT
982 HRESULT WINAPI UrlEscapeW(
983 LPCWSTR pszUrl,
984 LPWSTR pszEscaped,
985 LPDWORD pcchEscaped,
986 DWORD dwFlags)
988 LPCWSTR src;
989 DWORD needed = 0, ret;
990 BOOL stop_escaping = FALSE;
991 WCHAR next[5], *dst = pszEscaped;
992 INT len;
993 PARSEDURLW parsed_url;
994 DWORD int_flags;
995 DWORD slashes = 0;
996 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
998 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
999 pcchEscaped, dwFlags);
1001 if(!pszUrl || !pcchEscaped)
1002 return E_INVALIDARG;
1004 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1005 URL_ESCAPE_SEGMENT_ONLY |
1006 URL_DONT_ESCAPE_EXTRA_INFO |
1007 URL_ESCAPE_PERCENT))
1008 FIXME("Unimplemented flags: %08x\n", dwFlags);
1010 /* fix up flags */
1011 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1012 /* if SPACES_ONLY specified, reset the other controls */
1013 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1014 URL_ESCAPE_PERCENT |
1015 URL_ESCAPE_SEGMENT_ONLY);
1017 else
1018 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1019 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1022 int_flags = 0;
1023 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1024 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1025 } else {
1026 parsed_url.cbSize = sizeof(parsed_url);
1027 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1028 parsed_url.nScheme = URL_SCHEME_INVALID;
1030 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1032 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1033 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1035 switch(parsed_url.nScheme) {
1036 case URL_SCHEME_FILE:
1037 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1038 int_flags &= ~WINE_URL_STOP_ON_HASH;
1039 break;
1041 case URL_SCHEME_HTTP:
1042 case URL_SCHEME_HTTPS:
1043 int_flags |= WINE_URL_BASH_AS_SLASH;
1044 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1045 int_flags |= WINE_URL_ESCAPE_SLASH;
1046 break;
1048 case URL_SCHEME_MAILTO:
1049 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1050 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1051 break;
1053 case URL_SCHEME_INVALID:
1054 break;
1056 case URL_SCHEME_FTP:
1057 default:
1058 if(parsed_url.pszSuffix[0] != '/')
1059 int_flags |= WINE_URL_ESCAPE_SLASH;
1060 break;
1064 for(src = pszUrl; *src; ) {
1065 WCHAR cur = *src;
1066 len = 0;
1068 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1069 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1070 while(cur == '/' || cur == '\\') {
1071 slashes++;
1072 cur = *++src;
1074 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1075 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1076 src += localhost_len + 1;
1077 slashes = 3;
1080 switch(slashes) {
1081 case 1:
1082 case 3:
1083 next[0] = next[1] = next[2] = '/';
1084 len = 3;
1085 break;
1086 case 0:
1087 len = 0;
1088 break;
1089 default:
1090 next[0] = next[1] = '/';
1091 len = 2;
1092 break;
1095 if(len == 0) {
1097 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1098 stop_escaping = TRUE;
1100 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1101 stop_escaping = TRUE;
1103 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1105 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1106 next[0] = '%';
1107 next[1] = hexDigits[(cur >> 4) & 0xf];
1108 next[2] = hexDigits[cur & 0xf];
1109 len = 3;
1110 } else {
1111 next[0] = cur;
1112 len = 1;
1114 src++;
1117 if(needed + len <= *pcchEscaped) {
1118 memcpy(dst, next, len*sizeof(WCHAR));
1119 dst += len;
1121 needed += len;
1124 if(needed < *pcchEscaped) {
1125 *dst = '\0';
1126 ret = S_OK;
1127 } else {
1128 needed++; /* add one for the '\0' */
1129 ret = E_POINTER;
1131 *pcchEscaped = needed;
1132 return ret;
1136 /*************************************************************************
1137 * UrlUnescapeA [SHLWAPI.@]
1139 * Converts Url escape sequences back to ordinary characters.
1141 * PARAMS
1142 * pszUrl [I/O] Url to convert
1143 * pszUnescaped [O] Destination for converted Url
1144 * pcchUnescaped [I/O] Size of output string
1145 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1147 * RETURNS
1148 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1149 * dwFlags includes URL_ESCAPE_INPLACE.
1150 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1151 * this case pcchUnescaped is set to the size required.
1152 * NOTES
1153 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1154 * the first occurrence of either a '?' or '#' character.
1156 HRESULT WINAPI UrlUnescapeA(
1157 LPSTR pszUrl,
1158 LPSTR pszUnescaped,
1159 LPDWORD pcchUnescaped,
1160 DWORD dwFlags)
1162 char *dst, next;
1163 LPCSTR src;
1164 HRESULT ret;
1165 DWORD needed;
1166 BOOL stop_unescaping = FALSE;
1168 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1169 pcchUnescaped, dwFlags);
1171 if (!pszUrl) return E_INVALIDARG;
1173 if(dwFlags & URL_UNESCAPE_INPLACE)
1174 dst = pszUrl;
1175 else
1177 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1178 dst = pszUnescaped;
1181 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1182 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1183 (*src == '#' || *src == '?')) {
1184 stop_unescaping = TRUE;
1185 next = *src;
1186 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1187 && stop_unescaping == FALSE) {
1188 INT ih;
1189 char buf[3];
1190 memcpy(buf, src + 1, 2);
1191 buf[2] = '\0';
1192 ih = strtol(buf, NULL, 16);
1193 next = (CHAR) ih;
1194 src += 2; /* Advance to end of escape */
1195 } else
1196 next = *src;
1198 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1199 *dst++ = next;
1202 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1203 *dst = '\0';
1204 ret = S_OK;
1205 } else {
1206 needed++; /* add one for the '\0' */
1207 ret = E_POINTER;
1209 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1210 *pcchUnescaped = needed;
1212 if (ret == S_OK) {
1213 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1214 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1217 return ret;
1220 /*************************************************************************
1221 * UrlUnescapeW [SHLWAPI.@]
1223 * See UrlUnescapeA.
1225 HRESULT WINAPI UrlUnescapeW(
1226 LPWSTR pszUrl,
1227 LPWSTR pszUnescaped,
1228 LPDWORD pcchUnescaped,
1229 DWORD dwFlags)
1231 WCHAR *dst, next;
1232 LPCWSTR src;
1233 HRESULT ret;
1234 DWORD needed;
1235 BOOL stop_unescaping = FALSE;
1237 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1238 pcchUnescaped, dwFlags);
1240 if(!pszUrl) return E_INVALIDARG;
1242 if(dwFlags & URL_UNESCAPE_INPLACE)
1243 dst = pszUrl;
1244 else
1246 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1247 dst = pszUnescaped;
1250 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1251 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1252 (*src == '#' || *src == '?')) {
1253 stop_unescaping = TRUE;
1254 next = *src;
1255 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1256 && stop_unescaping == FALSE) {
1257 INT ih;
1258 WCHAR buf[5] = {'0','x',0};
1259 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1260 buf[4] = 0;
1261 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1262 next = (WCHAR) ih;
1263 src += 2; /* Advance to end of escape */
1264 } else
1265 next = *src;
1267 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1268 *dst++ = next;
1271 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1272 *dst = '\0';
1273 ret = S_OK;
1274 } else {
1275 needed++; /* add one for the '\0' */
1276 ret = E_POINTER;
1278 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1279 *pcchUnescaped = needed;
1281 if (ret == S_OK) {
1282 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1283 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1286 return ret;
1289 /*************************************************************************
1290 * UrlGetLocationA [SHLWAPI.@]
1292 * Get the location from a Url.
1294 * PARAMS
1295 * pszUrl [I] Url to get the location from
1297 * RETURNS
1298 * A pointer to the start of the location in pszUrl, or NULL if there is
1299 * no location.
1301 * NOTES
1302 * - MSDN erroneously states that "The location is the segment of the Url
1303 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1304 * stop at '?' and always return a NULL in this case.
1305 * - MSDN also erroneously states that "If a file URL has a query string,
1306 * the returned string is the query string". In all tested cases, if the
1307 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1308 *| Result Url
1309 *| ------ ---
1310 *| NULL file://aa/b/cd#hohoh
1311 *| #hohoh http://aa/b/cd#hohoh
1312 *| NULL fi://aa/b/cd#hohoh
1313 *| #hohoh ff://aa/b/cd#hohoh
1315 LPCSTR WINAPI UrlGetLocationA(
1316 LPCSTR pszUrl)
1318 PARSEDURLA base;
1319 DWORD res1;
1321 base.cbSize = sizeof(base);
1322 res1 = ParseURLA(pszUrl, &base);
1323 if (res1) return NULL; /* invalid scheme */
1325 /* if scheme is file: then never return pointer */
1326 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1328 /* Look for '#' and return its addr */
1329 return strchr(base.pszSuffix, '#');
1332 /*************************************************************************
1333 * UrlGetLocationW [SHLWAPI.@]
1335 * See UrlGetLocationA.
1337 LPCWSTR WINAPI UrlGetLocationW(
1338 LPCWSTR pszUrl)
1340 PARSEDURLW base;
1341 DWORD res1;
1343 base.cbSize = sizeof(base);
1344 res1 = ParseURLW(pszUrl, &base);
1345 if (res1) return NULL; /* invalid scheme */
1347 /* if scheme is file: then never return pointer */
1348 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1350 /* Look for '#' and return its addr */
1351 return strchrW(base.pszSuffix, '#');
1354 /*************************************************************************
1355 * UrlCompareA [SHLWAPI.@]
1357 * Compare two Urls.
1359 * PARAMS
1360 * pszUrl1 [I] First Url to compare
1361 * pszUrl2 [I] Url to compare to pszUrl1
1362 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1364 * RETURNS
1365 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1366 * than, equal to, or less than pszUrl1 respectively.
1368 INT WINAPI UrlCompareA(
1369 LPCSTR pszUrl1,
1370 LPCSTR pszUrl2,
1371 BOOL fIgnoreSlash)
1373 INT ret, len, len1, len2;
1375 if (!fIgnoreSlash)
1376 return strcmp(pszUrl1, pszUrl2);
1377 len1 = strlen(pszUrl1);
1378 if (pszUrl1[len1-1] == '/') len1--;
1379 len2 = strlen(pszUrl2);
1380 if (pszUrl2[len2-1] == '/') len2--;
1381 if (len1 == len2)
1382 return strncmp(pszUrl1, pszUrl2, len1);
1383 len = min(len1, len2);
1384 ret = strncmp(pszUrl1, pszUrl2, len);
1385 if (ret) return ret;
1386 if (len1 > len2) return 1;
1387 return -1;
1390 /*************************************************************************
1391 * UrlCompareW [SHLWAPI.@]
1393 * See UrlCompareA.
1395 INT WINAPI UrlCompareW(
1396 LPCWSTR pszUrl1,
1397 LPCWSTR pszUrl2,
1398 BOOL fIgnoreSlash)
1400 INT ret;
1401 size_t len, len1, len2;
1403 if (!fIgnoreSlash)
1404 return strcmpW(pszUrl1, pszUrl2);
1405 len1 = strlenW(pszUrl1);
1406 if (pszUrl1[len1-1] == '/') len1--;
1407 len2 = strlenW(pszUrl2);
1408 if (pszUrl2[len2-1] == '/') len2--;
1409 if (len1 == len2)
1410 return strncmpW(pszUrl1, pszUrl2, len1);
1411 len = min(len1, len2);
1412 ret = strncmpW(pszUrl1, pszUrl2, len);
1413 if (ret) return ret;
1414 if (len1 > len2) return 1;
1415 return -1;
1418 /*************************************************************************
1419 * HashData [SHLWAPI.@]
1421 * Hash an input block into a variable sized digest.
1423 * PARAMS
1424 * lpSrc [I] Input block
1425 * nSrcLen [I] Length of lpSrc
1426 * lpDest [I] Output for hash digest
1427 * nDestLen [I] Length of lpDest
1429 * RETURNS
1430 * Success: TRUE. lpDest is filled with the computed hash value.
1431 * Failure: FALSE, if any argument is invalid.
1433 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1434 unsigned char *lpDest, DWORD nDestLen)
1436 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1438 if (!lpSrc || !lpDest)
1439 return E_INVALIDARG;
1441 while (destCount >= 0)
1443 lpDest[destCount] = (destCount & 0xff);
1444 destCount--;
1447 while (srcCount >= 0)
1449 destCount = nDestLen - 1;
1450 while (destCount >= 0)
1452 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1453 destCount--;
1455 srcCount--;
1457 return S_OK;
1460 /*************************************************************************
1461 * UrlHashA [SHLWAPI.@]
1463 * Produce a Hash from a Url.
1465 * PARAMS
1466 * pszUrl [I] Url to hash
1467 * lpDest [O] Destinationh for hash
1468 * nDestLen [I] Length of lpDest
1470 * RETURNS
1471 * Success: S_OK. lpDest is filled with the computed hash value.
1472 * Failure: E_INVALIDARG, if any argument is invalid.
1474 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1476 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1477 return E_INVALIDARG;
1479 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1480 return S_OK;
1483 /*************************************************************************
1484 * UrlHashW [SHLWAPI.@]
1486 * See UrlHashA.
1488 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1490 char szUrl[MAX_PATH];
1492 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1494 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1495 return E_INVALIDARG;
1497 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1498 * return the same digests for the same URL.
1500 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1501 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1502 return S_OK;
1505 /*************************************************************************
1506 * UrlApplySchemeA [SHLWAPI.@]
1508 * Apply a scheme to a Url.
1510 * PARAMS
1511 * pszIn [I] Url to apply scheme to
1512 * pszOut [O] Destination for modified Url
1513 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1514 * dwFlags [I] URL_ flags from "shlwapi.h"
1516 * RETURNS
1517 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1518 * Failure: An HRESULT error code describing the error.
1520 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1522 LPWSTR in, out;
1523 HRESULT ret;
1524 DWORD len;
1526 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1527 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1529 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1531 in = HeapAlloc(GetProcessHeap(), 0,
1532 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1533 out = in + INTERNET_MAX_URL_LENGTH;
1535 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1536 len = INTERNET_MAX_URL_LENGTH;
1538 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1539 if (ret != S_OK) {
1540 HeapFree(GetProcessHeap(), 0, in);
1541 return ret;
1544 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1545 if (len > *pcchOut) {
1546 ret = E_POINTER;
1547 goto cleanup;
1550 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1551 len--;
1553 cleanup:
1554 *pcchOut = len;
1555 HeapFree(GetProcessHeap(), 0, in);
1556 return ret;
1559 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1561 HKEY newkey;
1562 BOOL j;
1563 INT index;
1564 DWORD value_len, data_len, dwType, i;
1565 WCHAR reg_path[MAX_PATH];
1566 WCHAR value[MAX_PATH], data[MAX_PATH];
1567 WCHAR Wxx, Wyy;
1569 MultiByteToWideChar(0, 0,
1570 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1571 -1, reg_path, MAX_PATH);
1572 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1573 index = 0;
1574 while(value_len = data_len = MAX_PATH,
1575 RegEnumValueW(newkey, index, value, &value_len,
1576 0, &dwType, (LPVOID)data, &data_len) == 0) {
1577 TRACE("guess %d %s is %s\n",
1578 index, debugstr_w(value), debugstr_w(data));
1580 j = FALSE;
1581 for(i=0; i<value_len; i++) {
1582 Wxx = pszIn[i];
1583 Wyy = value[i];
1584 /* remember that TRUE is not-equal */
1585 j = ChrCmpIW(Wxx, Wyy);
1586 if (j) break;
1588 if ((i == value_len) && !j) {
1589 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1590 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1591 RegCloseKey(newkey);
1592 return E_POINTER;
1594 strcpyW(pszOut, data);
1595 strcatW(pszOut, pszIn);
1596 *pcchOut = strlenW(pszOut);
1597 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1598 RegCloseKey(newkey);
1599 return S_OK;
1601 index++;
1603 RegCloseKey(newkey);
1604 return E_FAIL;
1607 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1609 HKEY newkey;
1610 DWORD data_len, dwType;
1611 WCHAR data[MAX_PATH];
1613 static const WCHAR prefix_keyW[] =
1614 {'S','o','f','t','w','a','r','e',
1615 '\\','M','i','c','r','o','s','o','f','t',
1616 '\\','W','i','n','d','o','w','s',
1617 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1618 '\\','U','R','L',
1619 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1621 /* get and prepend default */
1622 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1623 data_len = sizeof(data);
1624 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1625 RegCloseKey(newkey);
1626 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1627 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1628 return E_POINTER;
1630 strcpyW(pszOut, data);
1631 strcatW(pszOut, pszIn);
1632 *pcchOut = strlenW(pszOut);
1633 TRACE("used default %s\n", debugstr_w(pszOut));
1634 return S_OK;
1637 /*************************************************************************
1638 * UrlApplySchemeW [SHLWAPI.@]
1640 * See UrlApplySchemeA.
1642 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1644 PARSEDURLW in_scheme;
1645 DWORD res1;
1646 HRESULT ret;
1648 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1649 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1651 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1653 if (dwFlags & URL_APPLY_GUESSFILE) {
1654 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1655 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1656 strcpyW(pszOut, pszIn);
1657 *pcchOut = strlenW(pszOut);
1658 return S_FALSE;
1661 in_scheme.cbSize = sizeof(in_scheme);
1662 /* See if the base has a scheme */
1663 res1 = ParseURLW(pszIn, &in_scheme);
1664 if (res1) {
1665 /* no scheme in input, need to see if we need to guess */
1666 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1667 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1668 return ret;
1671 else {
1672 /* we have a scheme, see if valid (known scheme) */
1673 if (in_scheme.nScheme) {
1674 /* have valid scheme, so just copy and exit */
1675 if (strlenW(pszIn) + 1 > *pcchOut) {
1676 *pcchOut = strlenW(pszIn) + 1;
1677 return E_POINTER;
1679 strcpyW(pszOut, pszIn);
1680 *pcchOut = strlenW(pszOut);
1681 TRACE("valid scheme, returning copy\n");
1682 return S_OK;
1686 /* If we are here, then either invalid scheme,
1687 * or no scheme and can't/failed guess.
1689 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1690 ((res1 != 0)) ) &&
1691 (dwFlags & URL_APPLY_DEFAULT)) {
1692 /* find and apply default scheme */
1693 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1696 return S_FALSE;
1699 /*************************************************************************
1700 * UrlIsA [SHLWAPI.@]
1702 * Determine if a Url is of a certain class.
1704 * PARAMS
1705 * pszUrl [I] Url to check
1706 * Urlis [I] URLIS_ constant from "shlwapi.h"
1708 * RETURNS
1709 * TRUE if pszUrl belongs to the class type in Urlis.
1710 * FALSE Otherwise.
1712 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1714 PARSEDURLA base;
1715 DWORD res1;
1716 LPCSTR last;
1718 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1720 if(!pszUrl)
1721 return FALSE;
1723 switch (Urlis) {
1725 case URLIS_OPAQUE:
1726 base.cbSize = sizeof(base);
1727 res1 = ParseURLA(pszUrl, &base);
1728 if (res1) return FALSE; /* invalid scheme */
1729 switch (base.nScheme)
1731 case URL_SCHEME_MAILTO:
1732 case URL_SCHEME_SHELL:
1733 case URL_SCHEME_JAVASCRIPT:
1734 case URL_SCHEME_VBSCRIPT:
1735 case URL_SCHEME_ABOUT:
1736 return TRUE;
1738 return FALSE;
1740 case URLIS_FILEURL:
1741 return !StrCmpNA("file:", pszUrl, 5);
1743 case URLIS_DIRECTORY:
1744 last = pszUrl + strlen(pszUrl) - 1;
1745 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1747 case URLIS_URL:
1748 return PathIsURLA(pszUrl);
1750 case URLIS_NOHISTORY:
1751 case URLIS_APPLIABLE:
1752 case URLIS_HASQUERY:
1753 default:
1754 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1756 return FALSE;
1759 /*************************************************************************
1760 * UrlIsW [SHLWAPI.@]
1762 * See UrlIsA.
1764 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1766 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1767 PARSEDURLW base;
1768 DWORD res1;
1769 LPCWSTR last;
1771 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1773 if(!pszUrl)
1774 return FALSE;
1776 switch (Urlis) {
1778 case URLIS_OPAQUE:
1779 base.cbSize = sizeof(base);
1780 res1 = ParseURLW(pszUrl, &base);
1781 if (res1) return FALSE; /* invalid scheme */
1782 switch (base.nScheme)
1784 case URL_SCHEME_MAILTO:
1785 case URL_SCHEME_SHELL:
1786 case URL_SCHEME_JAVASCRIPT:
1787 case URL_SCHEME_VBSCRIPT:
1788 case URL_SCHEME_ABOUT:
1789 return TRUE;
1791 return FALSE;
1793 case URLIS_FILEURL:
1794 return !strncmpW(stemp, pszUrl, 5);
1796 case URLIS_DIRECTORY:
1797 last = pszUrl + strlenW(pszUrl) - 1;
1798 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1800 case URLIS_URL:
1801 return PathIsURLW(pszUrl);
1803 case URLIS_NOHISTORY:
1804 case URLIS_APPLIABLE:
1805 case URLIS_HASQUERY:
1806 default:
1807 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1809 return FALSE;
1812 /*************************************************************************
1813 * UrlIsNoHistoryA [SHLWAPI.@]
1815 * Determine if a Url should not be stored in the users history list.
1817 * PARAMS
1818 * pszUrl [I] Url to check
1820 * RETURNS
1821 * TRUE, if pszUrl should be excluded from the history list,
1822 * FALSE otherwise.
1824 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1826 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1829 /*************************************************************************
1830 * UrlIsNoHistoryW [SHLWAPI.@]
1832 * See UrlIsNoHistoryA.
1834 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1836 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1839 /*************************************************************************
1840 * UrlIsOpaqueA [SHLWAPI.@]
1842 * Determine if a Url is opaque.
1844 * PARAMS
1845 * pszUrl [I] Url to check
1847 * RETURNS
1848 * TRUE if pszUrl is opaque,
1849 * FALSE Otherwise.
1851 * NOTES
1852 * An opaque Url is one that does not start with "<protocol>://".
1854 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1856 return UrlIsA(pszUrl, URLIS_OPAQUE);
1859 /*************************************************************************
1860 * UrlIsOpaqueW [SHLWAPI.@]
1862 * See UrlIsOpaqueA.
1864 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1866 return UrlIsW(pszUrl, URLIS_OPAQUE);
1869 /*************************************************************************
1870 * Scans for characters of type "type" and when not matching found,
1871 * returns pointer to it and length in size.
1873 * Characters tested based on RFC 1738
1875 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1877 static DWORD alwayszero = 0;
1878 BOOL cont = TRUE;
1880 *size = 0;
1882 switch(type){
1884 case SCHEME:
1885 while (cont) {
1886 if ( (islowerW(*start) && isalphaW(*start)) ||
1887 isdigitW(*start) ||
1888 (*start == '+') ||
1889 (*start == '-') ||
1890 (*start == '.')) {
1891 start++;
1892 (*size)++;
1894 else
1895 cont = FALSE;
1897 break;
1899 case USERPASS:
1900 while (cont) {
1901 if ( isalphaW(*start) ||
1902 isdigitW(*start) ||
1903 /* user/password only characters */
1904 (*start == ';') ||
1905 (*start == '?') ||
1906 (*start == '&') ||
1907 (*start == '=') ||
1908 /* *extra* characters */
1909 (*start == '!') ||
1910 (*start == '*') ||
1911 (*start == '\'') ||
1912 (*start == '(') ||
1913 (*start == ')') ||
1914 (*start == ',') ||
1915 /* *safe* characters */
1916 (*start == '$') ||
1917 (*start == '_') ||
1918 (*start == '+') ||
1919 (*start == '-') ||
1920 (*start == '.') ||
1921 (*start == ' ')) {
1922 start++;
1923 (*size)++;
1924 } else if (*start == '%') {
1925 if (isxdigitW(*(start+1)) &&
1926 isxdigitW(*(start+2))) {
1927 start += 3;
1928 *size += 3;
1929 } else
1930 cont = FALSE;
1931 } else
1932 cont = FALSE;
1934 break;
1936 case PORT:
1937 while (cont) {
1938 if (isdigitW(*start)) {
1939 start++;
1940 (*size)++;
1942 else
1943 cont = FALSE;
1945 break;
1947 case HOST:
1948 while (cont) {
1949 if (isalnumW(*start) ||
1950 (*start == '-') ||
1951 (*start == '.') ||
1952 (*start == ' ') ) {
1953 start++;
1954 (*size)++;
1956 else
1957 cont = FALSE;
1959 break;
1960 default:
1961 FIXME("unknown type %d\n", type);
1962 return (LPWSTR)&alwayszero;
1964 /* TRACE("scanned %d characters next char %p<%c>\n",
1965 *size, start, *start); */
1966 return start;
1969 /*************************************************************************
1970 * Attempt to parse URL into pieces.
1972 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1974 LPCWSTR work;
1976 memset(pl, 0, sizeof(WINE_PARSE_URL));
1977 pl->pScheme = pszUrl;
1978 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1979 if (!*work || (*work != ':')) goto ErrorExit;
1980 work++;
1981 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
1982 pl->pUserName = work + 2;
1983 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1984 if (*work == ':' ) {
1985 /* parse password */
1986 work++;
1987 pl->pPassword = work;
1988 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1989 if (*work != '@') {
1990 /* what we just parsed must be the hostname and port
1991 * so reset pointers and clear then let it parse */
1992 pl->szUserName = pl->szPassword = 0;
1993 work = pl->pUserName - 1;
1994 pl->pUserName = pl->pPassword = 0;
1996 } else if (*work == '@') {
1997 /* no password */
1998 pl->szPassword = 0;
1999 pl->pPassword = 0;
2000 } else if (!*work || (*work == '/') || (*work == '.')) {
2001 /* what was parsed was hostname, so reset pointers and let it parse */
2002 pl->szUserName = pl->szPassword = 0;
2003 work = pl->pUserName - 1;
2004 pl->pUserName = pl->pPassword = 0;
2005 } else goto ErrorExit;
2007 /* now start parsing hostname or hostnumber */
2008 work++;
2009 pl->pHostName = work;
2010 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2011 if (*work == ':') {
2012 /* parse port */
2013 work++;
2014 pl->pPort = work;
2015 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2017 if (*work == '/') {
2018 /* see if query string */
2019 pl->pQuery = strchrW(work, '?');
2020 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2022 SuccessExit:
2023 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2024 pl->pScheme, pl->szScheme,
2025 pl->pUserName, pl->szUserName,
2026 pl->pPassword, pl->szPassword,
2027 pl->pHostName, pl->szHostName,
2028 pl->pPort, pl->szPort,
2029 pl->pQuery, pl->szQuery);
2030 return S_OK;
2031 ErrorExit:
2032 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2033 return E_INVALIDARG;
2036 /*************************************************************************
2037 * UrlGetPartA [SHLWAPI.@]
2039 * Retrieve part of a Url.
2041 * PARAMS
2042 * pszIn [I] Url to parse
2043 * pszOut [O] Destination for part of pszIn requested
2044 * pcchOut [I] Size of pszOut
2045 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2046 * needed size of pszOut INCLUDING '\0'.
2047 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2048 * dwFlags [I] URL_ flags from "shlwapi.h"
2050 * RETURNS
2051 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2052 * Failure: An HRESULT error code describing the error.
2054 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2055 DWORD dwPart, DWORD dwFlags)
2057 LPWSTR in, out;
2058 DWORD ret, len, len2;
2060 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2061 return E_INVALIDARG;
2063 in = HeapAlloc(GetProcessHeap(), 0,
2064 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2065 out = in + INTERNET_MAX_URL_LENGTH;
2067 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2069 len = INTERNET_MAX_URL_LENGTH;
2070 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2072 if (FAILED(ret)) {
2073 HeapFree(GetProcessHeap(), 0, in);
2074 return ret;
2077 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2078 if (len2 > *pcchOut) {
2079 *pcchOut = len2+1;
2080 HeapFree(GetProcessHeap(), 0, in);
2081 return E_POINTER;
2083 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2084 *pcchOut = len2-1;
2085 HeapFree(GetProcessHeap(), 0, in);
2086 return ret;
2089 /*************************************************************************
2090 * UrlGetPartW [SHLWAPI.@]
2092 * See UrlGetPartA.
2094 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2095 DWORD dwPart, DWORD dwFlags)
2097 WINE_PARSE_URL pl;
2098 HRESULT ret;
2099 DWORD scheme, size, schsize;
2100 LPCWSTR addr, schaddr;
2102 TRACE("(%s %p %p(%d) %08x %08x)\n",
2103 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2105 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2106 return E_INVALIDARG;
2108 *pszOut = '\0';
2110 addr = strchrW(pszIn, ':');
2111 if(!addr)
2112 scheme = URL_SCHEME_UNKNOWN;
2113 else
2114 scheme = get_scheme_code(pszIn, addr-pszIn);
2116 ret = URL_ParseUrl(pszIn, &pl);
2118 switch (dwPart) {
2119 case URL_PART_SCHEME:
2120 if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
2121 *pcchOut = 0;
2122 return S_FALSE;
2124 addr = pl.pScheme;
2125 size = pl.szScheme;
2126 break;
2128 case URL_PART_HOSTNAME:
2129 switch(scheme) {
2130 case URL_SCHEME_FTP:
2131 case URL_SCHEME_HTTP:
2132 case URL_SCHEME_GOPHER:
2133 case URL_SCHEME_TELNET:
2134 case URL_SCHEME_FILE:
2135 case URL_SCHEME_HTTPS:
2136 break;
2137 default:
2138 *pcchOut = 0;
2139 return E_FAIL;
2142 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2143 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2144 *pcchOut = 0;
2145 return S_FALSE;
2148 if (!pl.szHostName) {
2149 *pcchOut = 0;
2150 return S_FALSE;
2152 addr = pl.pHostName;
2153 size = pl.szHostName;
2154 break;
2156 case URL_PART_USERNAME:
2157 if (!pl.szUserName) {
2158 *pcchOut = 0;
2159 return S_FALSE;
2161 addr = pl.pUserName;
2162 size = pl.szUserName;
2163 break;
2165 case URL_PART_PASSWORD:
2166 if (!pl.szPassword) {
2167 *pcchOut = 0;
2168 return S_FALSE;
2170 addr = pl.pPassword;
2171 size = pl.szPassword;
2172 break;
2174 case URL_PART_PORT:
2175 if (!pl.szPort) {
2176 *pcchOut = 0;
2177 return S_FALSE;
2179 addr = pl.pPort;
2180 size = pl.szPort;
2181 break;
2183 case URL_PART_QUERY:
2184 if (!pl.szQuery) {
2185 *pcchOut = 0;
2186 return S_FALSE;
2188 addr = pl.pQuery;
2189 size = pl.szQuery;
2190 break;
2192 default:
2193 *pcchOut = 0;
2194 return E_INVALIDARG;
2197 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2198 if(!pl.pScheme || !pl.szScheme) {
2199 *pcchOut = 0;
2200 return E_FAIL;
2202 schaddr = pl.pScheme;
2203 schsize = pl.szScheme;
2204 if (*pcchOut < schsize + size + 2) {
2205 *pcchOut = schsize + size + 2;
2206 return E_POINTER;
2208 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2209 pszOut[schsize] = ':';
2210 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2211 pszOut[schsize+1+size] = 0;
2212 *pcchOut = schsize + 1 + size;
2214 else {
2215 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2216 memcpy(pszOut, addr, size*sizeof(WCHAR));
2217 pszOut[size] = 0;
2218 *pcchOut = size;
2220 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2222 return ret;
2225 /*************************************************************************
2226 * PathIsURLA [SHLWAPI.@]
2228 * Check if the given path is a Url.
2230 * PARAMS
2231 * lpszPath [I] Path to check.
2233 * RETURNS
2234 * TRUE if lpszPath is a Url.
2235 * FALSE if lpszPath is NULL or not a Url.
2237 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2239 PARSEDURLA base;
2240 HRESULT hres;
2242 TRACE("%s\n", debugstr_a(lpstrPath));
2244 if (!lpstrPath || !*lpstrPath) return FALSE;
2246 /* get protocol */
2247 base.cbSize = sizeof(base);
2248 hres = ParseURLA(lpstrPath, &base);
2249 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2252 /*************************************************************************
2253 * PathIsURLW [SHLWAPI.@]
2255 * See PathIsURLA.
2257 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2259 PARSEDURLW base;
2260 HRESULT hres;
2262 TRACE("%s\n", debugstr_w(lpstrPath));
2264 if (!lpstrPath || !*lpstrPath) return FALSE;
2266 /* get protocol */
2267 base.cbSize = sizeof(base);
2268 hres = ParseURLW(lpstrPath, &base);
2269 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2272 /*************************************************************************
2273 * UrlCreateFromPathA [SHLWAPI.@]
2275 * See UrlCreateFromPathW
2277 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2279 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2280 WCHAR *urlW = bufW;
2281 UNICODE_STRING pathW;
2282 HRESULT ret;
2283 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2285 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2286 return E_INVALIDARG;
2287 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2288 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2289 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2291 if(ret == S_OK || ret == S_FALSE) {
2292 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2293 if(*pcchUrl > lenA) {
2294 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2295 pszUrl[lenA] = 0;
2296 *pcchUrl = lenA;
2297 } else {
2298 *pcchUrl = lenA + 1;
2299 ret = E_POINTER;
2302 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2303 RtlFreeUnicodeString(&pathW);
2304 return ret;
2307 /*************************************************************************
2308 * UrlCreateFromPathW [SHLWAPI.@]
2310 * Create a Url from a file path.
2312 * PARAMS
2313 * pszPath [I] Path to convert
2314 * pszUrl [O] Destination for the converted Url
2315 * pcchUrl [I/O] Length of pszUrl
2316 * dwReserved [I] Reserved, must be 0
2318 * RETURNS
2319 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2320 * Failure: An HRESULT error code.
2322 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2324 DWORD needed;
2325 HRESULT ret;
2326 WCHAR *pszNewUrl;
2327 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2328 WCHAR three_slashesW[] = {'/','/','/',0};
2329 PARSEDURLW parsed_url;
2331 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2333 /* Validate arguments */
2334 if (dwReserved != 0)
2335 return E_INVALIDARG;
2336 if (!pszUrl || !pcchUrl)
2337 return E_INVALIDARG;
2340 parsed_url.cbSize = sizeof(parsed_url);
2341 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2342 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2343 needed = strlenW(pszPath);
2344 if (needed >= *pcchUrl) {
2345 *pcchUrl = needed + 1;
2346 return E_POINTER;
2347 } else {
2348 *pcchUrl = needed;
2349 strcpyW(pszUrl, pszPath);
2350 return S_FALSE;
2355 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2356 strcpyW(pszNewUrl, file_colonW);
2357 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2358 strcatW(pszNewUrl, three_slashesW);
2359 strcatW(pszNewUrl, pszPath);
2360 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2362 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2363 return ret;
2366 /*************************************************************************
2367 * SHAutoComplete [SHLWAPI.@]
2369 * Enable auto-completion for an edit control.
2371 * PARAMS
2372 * hwndEdit [I] Handle of control to enable auto-completion for
2373 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2375 * RETURNS
2376 * Success: S_OK. Auto-completion is enabled for the control.
2377 * Failure: An HRESULT error code indicating the error.
2379 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2381 FIXME("stub\n");
2382 return S_FALSE;
2385 /*************************************************************************
2386 * MLBuildResURLA [SHLWAPI.405]
2388 * Create a Url pointing to a resource in a module.
2390 * PARAMS
2391 * lpszLibName [I] Name of the module containing the resource
2392 * hMod [I] Callers module handle
2393 * dwFlags [I] Undocumented flags for loading the module
2394 * lpszRes [I] Resource name
2395 * lpszDest [O] Destination for resulting Url
2396 * dwDestLen [I] Length of lpszDest
2398 * RETURNS
2399 * Success: S_OK. lpszDest contains the resource Url.
2400 * Failure: E_INVALIDARG, if any argument is invalid, or
2401 * E_FAIL if dwDestLen is too small.
2403 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2404 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2406 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2407 HRESULT hRet;
2409 if (lpszLibName)
2410 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2412 if (lpszRes)
2413 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2415 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2416 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2418 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2419 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2420 if (SUCCEEDED(hRet) && lpszDest)
2421 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2423 return hRet;
2426 /*************************************************************************
2427 * MLBuildResURLA [SHLWAPI.406]
2429 * See MLBuildResURLA.
2431 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2432 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2434 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2435 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2436 HRESULT hRet = E_FAIL;
2438 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2439 debugstr_w(lpszRes), lpszDest, dwDestLen);
2441 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2442 !lpszDest || (dwFlags && dwFlags != 2))
2443 return E_INVALIDARG;
2445 if (dwDestLen >= szResLen + 1)
2447 dwDestLen -= (szResLen + 1);
2448 memcpy(lpszDest, szRes, sizeof(szRes));
2450 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2452 if (hMod)
2454 WCHAR szBuff[MAX_PATH];
2455 DWORD len;
2457 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2458 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2460 DWORD dwPathLen = strlenW(szBuff) + 1;
2462 if (dwDestLen >= dwPathLen)
2464 DWORD dwResLen;
2466 dwDestLen -= dwPathLen;
2467 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2469 dwResLen = strlenW(lpszRes) + 1;
2470 if (dwDestLen >= dwResLen + 1)
2472 lpszDest[szResLen + dwPathLen-1] = '/';
2473 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2474 hRet = S_OK;
2478 MLFreeLibrary(hMod);
2481 return hRet;
2484 /***********************************************************************
2485 * UrlFixupW [SHLWAPI.462]
2487 * Checks the scheme part of a URL and attempts to correct misspellings.
2489 * PARAMS
2490 * lpszUrl [I] Pointer to the URL to be corrected
2491 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2492 * dwMaxChars [I] Maximum size of corrected URL
2494 * RETURNS
2495 * success: S_OK if URL corrected or already correct
2496 * failure: S_FALSE if unable to correct / COM error code if other error
2499 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2501 DWORD srcLen;
2503 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2505 if (!url)
2506 return E_FAIL;
2508 srcLen = lstrlenW(url) + 1;
2510 /* For now just copy the URL directly */
2511 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2513 return S_OK;