advapi32: Make rpcrt4 a delayed import to work around circular dependencies with...
[wine/testsucceed.git] / dlls / shlwapi / url.c
blobad7d989a4bbd3bd869774ad28ef09c09517975f8
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 | URL_FILE_USE_PATHURL))
490 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
492 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
493 URL_ESCAPE_SPACES_ONLY |
494 URL_ESCAPE_PERCENT |
495 URL_DONT_ESCAPE_EXTRA_INFO |
496 URL_ESCAPE_SEGMENT_ONLY ))) {
497 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
498 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
499 EscapeFlags);
500 } else { /* No escaping needed, just copy the string */
501 nLen = lstrlenW(lpszUrlCpy);
502 if(nLen < *pcchCanonicalized)
503 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
504 else {
505 hr = E_POINTER;
506 nLen++;
508 *pcchCanonicalized = nLen;
511 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
513 if (hr == S_OK)
514 TRACE("result %s\n", debugstr_w(pszCanonicalized));
516 return hr;
519 /*************************************************************************
520 * UrlCombineA [SHLWAPI.@]
522 * Combine two Urls.
524 * PARAMS
525 * pszBase [I] Base Url
526 * pszRelative [I] Url to combine with pszBase
527 * pszCombined [O] Destination for combined Url
528 * pcchCombined [O] Destination for length of pszCombined
529 * dwFlags [I] URL_ flags from "shlwapi.h"
531 * RETURNS
532 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
533 * contains its length.
534 * Failure: An HRESULT error code indicating the error.
536 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
537 LPSTR pszCombined, LPDWORD pcchCombined,
538 DWORD dwFlags)
540 LPWSTR base, relative, combined;
541 DWORD ret, len, len2;
543 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
544 debugstr_a(pszBase),debugstr_a(pszRelative),
545 pcchCombined?*pcchCombined:0,dwFlags);
547 if(!pszBase || !pszRelative || !pcchCombined)
548 return E_INVALIDARG;
550 base = HeapAlloc(GetProcessHeap(), 0,
551 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
552 relative = base + INTERNET_MAX_URL_LENGTH;
553 combined = relative + INTERNET_MAX_URL_LENGTH;
555 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
556 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
557 len = *pcchCombined;
559 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
560 if (ret != S_OK) {
561 *pcchCombined = len;
562 HeapFree(GetProcessHeap(), 0, base);
563 return ret;
566 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
567 if (len2 > *pcchCombined) {
568 *pcchCombined = len2;
569 HeapFree(GetProcessHeap(), 0, base);
570 return E_POINTER;
572 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
573 0, 0);
574 *pcchCombined = len2;
575 HeapFree(GetProcessHeap(), 0, base);
576 return S_OK;
579 /*************************************************************************
580 * UrlCombineW [SHLWAPI.@]
582 * See UrlCombineA.
584 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
585 LPWSTR pszCombined, LPDWORD pcchCombined,
586 DWORD dwFlags)
588 PARSEDURLW base, relative;
589 DWORD myflags, sizeloc = 0;
590 DWORD len, res1, res2, process_case = 0;
591 LPWSTR work, preliminary, mbase, mrelative;
592 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
593 HRESULT ret;
595 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
596 debugstr_w(pszBase),debugstr_w(pszRelative),
597 pcchCombined?*pcchCombined:0,dwFlags);
599 if(!pszBase || !pszRelative || !pcchCombined)
600 return E_INVALIDARG;
602 base.cbSize = sizeof(base);
603 relative.cbSize = sizeof(relative);
605 /* Get space for duplicates of the input and the output */
606 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
607 sizeof(WCHAR));
608 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
609 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
610 *preliminary = '\0';
612 /* Canonicalize the base input prior to looking for the scheme */
613 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
614 len = INTERNET_MAX_URL_LENGTH;
615 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
617 /* Canonicalize the relative input prior to looking for the scheme */
618 len = INTERNET_MAX_URL_LENGTH;
619 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
621 /* See if the base has a scheme */
622 res1 = ParseURLW(mbase, &base);
623 if (res1) {
624 /* if pszBase has no scheme, then return pszRelative */
625 TRACE("no scheme detected in Base\n");
626 process_case = 1;
628 else do {
629 BOOL manual_search = FALSE;
631 /* mk is a special case */
632 if(base.nScheme == URL_SCHEME_MK) {
633 static const WCHAR wsz[] = {':',':',0};
635 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
636 if(ptr) {
637 int delta;
639 ptr += 2;
640 delta = ptr-base.pszSuffix;
641 base.cchProtocol += delta;
642 base.pszSuffix += delta;
643 base.cchSuffix -= delta;
645 }else {
646 /* get size of location field (if it exists) */
647 work = (LPWSTR)base.pszSuffix;
648 sizeloc = 0;
649 if (*work++ == '/') {
650 if (*work++ == '/') {
651 /* At this point have start of location and
652 * it ends at next '/' or end of string.
654 while(*work && (*work != '/')) work++;
655 sizeloc = (DWORD)(work - base.pszSuffix);
660 /* If there is a '#' and the characters immediately preceding it are
661 * ".htm[l]", then begin looking for the last leaf starting from
662 * the '#'. Otherwise the '#' is not meaningful and just start
663 * looking from the end. */
664 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
665 const WCHAR htmlW[] = {'.','h','t','m','l',0};
666 const int len_htmlW = 5;
667 const WCHAR htmW[] = {'.','h','t','m',0};
668 const int len_htmW = 4;
670 if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
671 work -= len_htmW;
672 if (strncmpiW(work, htmW, len_htmW) == 0)
673 manual_search = TRUE;
674 work += len_htmW;
677 if (!manual_search &&
678 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
679 work -= len_htmlW;
680 if (strncmpiW(work, htmlW, len_htmlW) == 0)
681 manual_search = TRUE;
682 work += len_htmlW;
686 if (manual_search) {
687 /* search backwards starting from the current position */
688 while (*work != '/' && work > base.pszSuffix + sizeloc)
689 --work;
690 if (work > base.pszSuffix + sizeloc)
691 base.cchSuffix = work - base.pszSuffix + 1;
692 }else {
693 /* search backwards starting from the end of the string */
694 work = strrchrW((base.pszSuffix+sizeloc), '/');
695 if (work) {
696 len = (DWORD)(work - base.pszSuffix + 1);
697 base.cchSuffix = len;
702 * At this point:
703 * .pszSuffix points to location (starting with '//')
704 * .cchSuffix length of location (above) and rest less the last
705 * leaf (if any)
706 * sizeloc length of location (above) up to but not including
707 * the last '/'
710 res2 = ParseURLW(mrelative, &relative);
711 if (res2) {
712 /* no scheme in pszRelative */
713 TRACE("no scheme detected in Relative\n");
714 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
715 relative.cchSuffix = strlenW(mrelative);
716 if (*pszRelative == ':') {
717 /* case that is either left alone or uses pszBase */
718 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
719 process_case = 5;
720 break;
722 process_case = 1;
723 break;
725 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
726 /* case that becomes "file:///" */
727 strcpyW(preliminary, myfilestr);
728 process_case = 1;
729 break;
731 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
732 /* pszRelative has location and rest */
733 process_case = 3;
734 break;
736 if (*mrelative == '/') {
737 /* case where pszRelative is root to location */
738 process_case = 4;
739 break;
741 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
742 break;
745 /* handle cases where pszRelative has scheme */
746 if ((base.cchProtocol == relative.cchProtocol) &&
747 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
749 /* since the schemes are the same */
750 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
751 /* case where pszRelative replaces location and following */
752 process_case = 3;
753 break;
755 if (*relative.pszSuffix == '/') {
756 /* case where pszRelative is root to location */
757 process_case = 4;
758 break;
760 /* replace either just location if base's location starts with a
761 * slash or otherwise everything */
762 process_case = (*base.pszSuffix == '/') ? 5 : 1;
763 break;
765 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
766 /* case where pszRelative replaces scheme, location,
767 * and following and handles PLUGGABLE
769 process_case = 2;
770 break;
772 process_case = 1;
773 break;
774 } while(FALSE); /* a little trick to allow easy exit from nested if's */
776 ret = S_OK;
777 switch (process_case) {
779 case 1: /*
780 * Return pszRelative appended to what ever is in pszCombined,
781 * (which may the string "file:///"
783 strcatW(preliminary, mrelative);
784 break;
786 case 2: /* case where pszRelative replaces scheme, and location */
787 strcpyW(preliminary, mrelative);
788 break;
790 case 3: /*
791 * Return the pszBase scheme with pszRelative. Basically
792 * keeps the scheme and replaces the domain and following.
794 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
795 work = preliminary + base.cchProtocol + 1;
796 strcpyW(work, relative.pszSuffix);
797 break;
799 case 4: /*
800 * Return the pszBase scheme and location but everything
801 * after the location is pszRelative. (Replace document
802 * from root on.)
804 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
805 work = preliminary + base.cchProtocol + 1 + sizeloc;
806 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
807 *(work++) = '/';
808 strcpyW(work, relative.pszSuffix);
809 break;
811 case 5: /*
812 * Return the pszBase without its document (if any) and
813 * append pszRelative after its scheme.
815 memcpy(preliminary, base.pszProtocol,
816 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
817 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
818 if (*work++ != '/')
819 *(work++) = '/';
820 strcpyW(work, relative.pszSuffix);
821 break;
823 default:
824 FIXME("How did we get here????? process_case=%d\n", process_case);
825 ret = E_INVALIDARG;
828 if (ret == S_OK) {
829 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
830 if(*pcchCombined == 0)
831 *pcchCombined = 1;
832 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
833 if(SUCCEEDED(ret) && pszCombined) {
834 lstrcpyW(pszCombined, mrelative);
836 TRACE("return-%d len=%d, %s\n",
837 process_case, *pcchCombined, debugstr_w(pszCombined));
839 HeapFree(GetProcessHeap(), 0, preliminary);
840 return ret;
843 /*************************************************************************
844 * UrlEscapeA [SHLWAPI.@]
847 HRESULT WINAPI UrlEscapeA(
848 LPCSTR pszUrl,
849 LPSTR pszEscaped,
850 LPDWORD pcchEscaped,
851 DWORD dwFlags)
853 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
854 WCHAR *escapedW = bufW;
855 UNICODE_STRING urlW;
856 HRESULT ret;
857 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
859 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
860 return E_INVALIDARG;
862 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
863 return E_INVALIDARG;
864 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
865 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
866 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
868 if(ret == S_OK) {
869 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
870 if(*pcchEscaped > lenA) {
871 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
872 pszEscaped[lenA] = 0;
873 *pcchEscaped = lenA;
874 } else {
875 *pcchEscaped = lenA + 1;
876 ret = E_POINTER;
879 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
880 RtlFreeUnicodeString(&urlW);
881 return ret;
884 #define WINE_URL_BASH_AS_SLASH 0x01
885 #define WINE_URL_COLLAPSE_SLASHES 0x02
886 #define WINE_URL_ESCAPE_SLASH 0x04
887 #define WINE_URL_ESCAPE_HASH 0x08
888 #define WINE_URL_ESCAPE_QUESTION 0x10
889 #define WINE_URL_STOP_ON_HASH 0x20
890 #define WINE_URL_STOP_ON_QUESTION 0x40
892 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
895 if (isalnumW(ch))
896 return FALSE;
898 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
899 if(ch == ' ')
900 return TRUE;
901 else
902 return FALSE;
905 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
906 return TRUE;
908 if (ch <= 31 || ch >= 127)
909 return TRUE;
911 else {
912 switch (ch) {
913 case ' ':
914 case '<':
915 case '>':
916 case '\"':
917 case '{':
918 case '}':
919 case '|':
920 case '\\':
921 case '^':
922 case ']':
923 case '[':
924 case '`':
925 case '&':
926 return TRUE;
928 case '/':
929 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
930 return FALSE;
932 case '?':
933 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
934 return FALSE;
936 case '#':
937 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
938 return FALSE;
940 default:
941 return FALSE;
947 /*************************************************************************
948 * UrlEscapeW [SHLWAPI.@]
950 * Converts unsafe characters in a Url into escape sequences.
952 * PARAMS
953 * pszUrl [I] Url to modify
954 * pszEscaped [O] Destination for modified Url
955 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
956 * dwFlags [I] URL_ flags from "shlwapi.h"
958 * RETURNS
959 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
960 * contains its length.
961 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
962 * pcchEscaped is set to the required length.
964 * Converts unsafe characters into their escape sequences.
966 * NOTES
967 * - By default this function stops converting at the first '?' or
968 * '#' character.
969 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
970 * converted, but the conversion continues past a '?' or '#'.
971 * - Note that this function did not work well (or at all) in shlwapi version 4.
973 * BUGS
974 * Only the following flags are implemented:
975 *| URL_ESCAPE_SPACES_ONLY
976 *| URL_DONT_ESCAPE_EXTRA_INFO
977 *| URL_ESCAPE_SEGMENT_ONLY
978 *| URL_ESCAPE_PERCENT
980 HRESULT WINAPI UrlEscapeW(
981 LPCWSTR pszUrl,
982 LPWSTR pszEscaped,
983 LPDWORD pcchEscaped,
984 DWORD dwFlags)
986 LPCWSTR src;
987 DWORD needed = 0, ret;
988 BOOL stop_escaping = FALSE;
989 WCHAR next[5], *dst = pszEscaped;
990 INT len;
991 PARSEDURLW parsed_url;
992 DWORD int_flags;
993 DWORD slashes = 0;
994 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
996 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
997 pcchEscaped, dwFlags);
999 if(!pszUrl || !pcchEscaped)
1000 return E_INVALIDARG;
1002 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1003 URL_ESCAPE_SEGMENT_ONLY |
1004 URL_DONT_ESCAPE_EXTRA_INFO |
1005 URL_ESCAPE_PERCENT))
1006 FIXME("Unimplemented flags: %08x\n", dwFlags);
1008 /* fix up flags */
1009 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1010 /* if SPACES_ONLY specified, reset the other controls */
1011 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1012 URL_ESCAPE_PERCENT |
1013 URL_ESCAPE_SEGMENT_ONLY);
1015 else
1016 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1017 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1020 int_flags = 0;
1021 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1022 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1023 } else {
1024 parsed_url.cbSize = sizeof(parsed_url);
1025 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1026 parsed_url.nScheme = URL_SCHEME_INVALID;
1028 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1030 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1031 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1033 switch(parsed_url.nScheme) {
1034 case URL_SCHEME_FILE:
1035 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1036 int_flags &= ~WINE_URL_STOP_ON_HASH;
1037 break;
1039 case URL_SCHEME_HTTP:
1040 case URL_SCHEME_HTTPS:
1041 int_flags |= WINE_URL_BASH_AS_SLASH;
1042 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1043 int_flags |= WINE_URL_ESCAPE_SLASH;
1044 break;
1046 case URL_SCHEME_MAILTO:
1047 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1048 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1049 break;
1051 case URL_SCHEME_INVALID:
1052 break;
1054 case URL_SCHEME_FTP:
1055 default:
1056 if(parsed_url.pszSuffix[0] != '/')
1057 int_flags |= WINE_URL_ESCAPE_SLASH;
1058 break;
1062 for(src = pszUrl; *src; ) {
1063 WCHAR cur = *src;
1064 len = 0;
1066 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1067 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1068 while(cur == '/' || cur == '\\') {
1069 slashes++;
1070 cur = *++src;
1072 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1073 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1074 src += localhost_len + 1;
1075 slashes = 3;
1078 switch(slashes) {
1079 case 1:
1080 case 3:
1081 next[0] = next[1] = next[2] = '/';
1082 len = 3;
1083 break;
1084 case 0:
1085 len = 0;
1086 break;
1087 default:
1088 next[0] = next[1] = '/';
1089 len = 2;
1090 break;
1093 if(len == 0) {
1095 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1096 stop_escaping = TRUE;
1098 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1099 stop_escaping = TRUE;
1101 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1103 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1104 next[0] = '%';
1105 next[1] = hexDigits[(cur >> 4) & 0xf];
1106 next[2] = hexDigits[cur & 0xf];
1107 len = 3;
1108 } else {
1109 next[0] = cur;
1110 len = 1;
1112 src++;
1115 if(needed + len <= *pcchEscaped) {
1116 memcpy(dst, next, len*sizeof(WCHAR));
1117 dst += len;
1119 needed += len;
1122 if(needed < *pcchEscaped) {
1123 *dst = '\0';
1124 ret = S_OK;
1125 } else {
1126 needed++; /* add one for the '\0' */
1127 ret = E_POINTER;
1129 *pcchEscaped = needed;
1130 return ret;
1134 /*************************************************************************
1135 * UrlUnescapeA [SHLWAPI.@]
1137 * Converts Url escape sequences back to ordinary characters.
1139 * PARAMS
1140 * pszUrl [I/O] Url to convert
1141 * pszUnescaped [O] Destination for converted Url
1142 * pcchUnescaped [I/O] Size of output string
1143 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1145 * RETURNS
1146 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1147 * dwFlags includes URL_ESCAPE_INPLACE.
1148 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1149 * this case pcchUnescaped is set to the size required.
1150 * NOTES
1151 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1152 * the first occurrence of either a '?' or '#' character.
1154 HRESULT WINAPI UrlUnescapeA(
1155 LPSTR pszUrl,
1156 LPSTR pszUnescaped,
1157 LPDWORD pcchUnescaped,
1158 DWORD dwFlags)
1160 char *dst, next;
1161 LPCSTR src;
1162 HRESULT ret;
1163 DWORD needed;
1164 BOOL stop_unescaping = FALSE;
1166 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1167 pcchUnescaped, dwFlags);
1169 if (!pszUrl) return E_INVALIDARG;
1171 if(dwFlags & URL_UNESCAPE_INPLACE)
1172 dst = pszUrl;
1173 else
1175 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1176 dst = pszUnescaped;
1179 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1180 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1181 (*src == '#' || *src == '?')) {
1182 stop_unescaping = TRUE;
1183 next = *src;
1184 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1185 && stop_unescaping == FALSE) {
1186 INT ih;
1187 char buf[3];
1188 memcpy(buf, src + 1, 2);
1189 buf[2] = '\0';
1190 ih = strtol(buf, NULL, 16);
1191 next = (CHAR) ih;
1192 src += 2; /* Advance to end of escape */
1193 } else
1194 next = *src;
1196 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1197 *dst++ = next;
1200 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1201 *dst = '\0';
1202 ret = S_OK;
1203 } else {
1204 needed++; /* add one for the '\0' */
1205 ret = E_POINTER;
1207 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1208 *pcchUnescaped = needed;
1210 if (ret == S_OK) {
1211 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1212 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1215 return ret;
1218 /*************************************************************************
1219 * UrlUnescapeW [SHLWAPI.@]
1221 * See UrlUnescapeA.
1223 HRESULT WINAPI UrlUnescapeW(
1224 LPWSTR pszUrl,
1225 LPWSTR pszUnescaped,
1226 LPDWORD pcchUnescaped,
1227 DWORD dwFlags)
1229 WCHAR *dst, next;
1230 LPCWSTR src;
1231 HRESULT ret;
1232 DWORD needed;
1233 BOOL stop_unescaping = FALSE;
1235 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1236 pcchUnescaped, dwFlags);
1238 if(!pszUrl) return E_INVALIDARG;
1240 if(dwFlags & URL_UNESCAPE_INPLACE)
1241 dst = pszUrl;
1242 else
1244 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1245 dst = pszUnescaped;
1248 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1249 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1250 (*src == '#' || *src == '?')) {
1251 stop_unescaping = TRUE;
1252 next = *src;
1253 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1254 && stop_unescaping == FALSE) {
1255 INT ih;
1256 WCHAR buf[5] = {'0','x',0};
1257 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1258 buf[4] = 0;
1259 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1260 next = (WCHAR) ih;
1261 src += 2; /* Advance to end of escape */
1262 } else
1263 next = *src;
1265 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1266 *dst++ = next;
1269 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1270 *dst = '\0';
1271 ret = S_OK;
1272 } else {
1273 needed++; /* add one for the '\0' */
1274 ret = E_POINTER;
1276 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1277 *pcchUnescaped = needed;
1279 if (ret == S_OK) {
1280 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1281 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1284 return ret;
1287 /*************************************************************************
1288 * UrlGetLocationA [SHLWAPI.@]
1290 * Get the location from a Url.
1292 * PARAMS
1293 * pszUrl [I] Url to get the location from
1295 * RETURNS
1296 * A pointer to the start of the location in pszUrl, or NULL if there is
1297 * no location.
1299 * NOTES
1300 * - MSDN erroneously states that "The location is the segment of the Url
1301 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1302 * stop at '?' and always return a NULL in this case.
1303 * - MSDN also erroneously states that "If a file URL has a query string,
1304 * the returned string is the query string". In all tested cases, if the
1305 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1306 *| Result Url
1307 *| ------ ---
1308 *| NULL file://aa/b/cd#hohoh
1309 *| #hohoh http://aa/b/cd#hohoh
1310 *| NULL fi://aa/b/cd#hohoh
1311 *| #hohoh ff://aa/b/cd#hohoh
1313 LPCSTR WINAPI UrlGetLocationA(
1314 LPCSTR pszUrl)
1316 PARSEDURLA base;
1317 DWORD res1;
1319 base.cbSize = sizeof(base);
1320 res1 = ParseURLA(pszUrl, &base);
1321 if (res1) return NULL; /* invalid scheme */
1323 /* if scheme is file: then never return pointer */
1324 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1326 /* Look for '#' and return its addr */
1327 return strchr(base.pszSuffix, '#');
1330 /*************************************************************************
1331 * UrlGetLocationW [SHLWAPI.@]
1333 * See UrlGetLocationA.
1335 LPCWSTR WINAPI UrlGetLocationW(
1336 LPCWSTR pszUrl)
1338 PARSEDURLW base;
1339 DWORD res1;
1341 base.cbSize = sizeof(base);
1342 res1 = ParseURLW(pszUrl, &base);
1343 if (res1) return NULL; /* invalid scheme */
1345 /* if scheme is file: then never return pointer */
1346 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1348 /* Look for '#' and return its addr */
1349 return strchrW(base.pszSuffix, '#');
1352 /*************************************************************************
1353 * UrlCompareA [SHLWAPI.@]
1355 * Compare two Urls.
1357 * PARAMS
1358 * pszUrl1 [I] First Url to compare
1359 * pszUrl2 [I] Url to compare to pszUrl1
1360 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1362 * RETURNS
1363 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1364 * than, equal to, or less than pszUrl1 respectively.
1366 INT WINAPI UrlCompareA(
1367 LPCSTR pszUrl1,
1368 LPCSTR pszUrl2,
1369 BOOL fIgnoreSlash)
1371 INT ret, len, len1, len2;
1373 if (!fIgnoreSlash)
1374 return strcmp(pszUrl1, pszUrl2);
1375 len1 = strlen(pszUrl1);
1376 if (pszUrl1[len1-1] == '/') len1--;
1377 len2 = strlen(pszUrl2);
1378 if (pszUrl2[len2-1] == '/') len2--;
1379 if (len1 == len2)
1380 return strncmp(pszUrl1, pszUrl2, len1);
1381 len = min(len1, len2);
1382 ret = strncmp(pszUrl1, pszUrl2, len);
1383 if (ret) return ret;
1384 if (len1 > len2) return 1;
1385 return -1;
1388 /*************************************************************************
1389 * UrlCompareW [SHLWAPI.@]
1391 * See UrlCompareA.
1393 INT WINAPI UrlCompareW(
1394 LPCWSTR pszUrl1,
1395 LPCWSTR pszUrl2,
1396 BOOL fIgnoreSlash)
1398 INT ret;
1399 size_t len, len1, len2;
1401 if (!fIgnoreSlash)
1402 return strcmpW(pszUrl1, pszUrl2);
1403 len1 = strlenW(pszUrl1);
1404 if (pszUrl1[len1-1] == '/') len1--;
1405 len2 = strlenW(pszUrl2);
1406 if (pszUrl2[len2-1] == '/') len2--;
1407 if (len1 == len2)
1408 return strncmpW(pszUrl1, pszUrl2, len1);
1409 len = min(len1, len2);
1410 ret = strncmpW(pszUrl1, pszUrl2, len);
1411 if (ret) return ret;
1412 if (len1 > len2) return 1;
1413 return -1;
1416 /*************************************************************************
1417 * HashData [SHLWAPI.@]
1419 * Hash an input block into a variable sized digest.
1421 * PARAMS
1422 * lpSrc [I] Input block
1423 * nSrcLen [I] Length of lpSrc
1424 * lpDest [I] Output for hash digest
1425 * nDestLen [I] Length of lpDest
1427 * RETURNS
1428 * Success: TRUE. lpDest is filled with the computed hash value.
1429 * Failure: FALSE, if any argument is invalid.
1431 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1432 unsigned char *lpDest, DWORD nDestLen)
1434 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1436 if (!lpSrc || !lpDest)
1437 return E_INVALIDARG;
1439 while (destCount >= 0)
1441 lpDest[destCount] = (destCount & 0xff);
1442 destCount--;
1445 while (srcCount >= 0)
1447 destCount = nDestLen - 1;
1448 while (destCount >= 0)
1450 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1451 destCount--;
1453 srcCount--;
1455 return S_OK;
1458 /*************************************************************************
1459 * UrlHashA [SHLWAPI.@]
1461 * Produce a Hash from a Url.
1463 * PARAMS
1464 * pszUrl [I] Url to hash
1465 * lpDest [O] Destinationh for hash
1466 * nDestLen [I] Length of lpDest
1468 * RETURNS
1469 * Success: S_OK. lpDest is filled with the computed hash value.
1470 * Failure: E_INVALIDARG, if any argument is invalid.
1472 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1474 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1475 return E_INVALIDARG;
1477 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1478 return S_OK;
1481 /*************************************************************************
1482 * UrlHashW [SHLWAPI.@]
1484 * See UrlHashA.
1486 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1488 char szUrl[MAX_PATH];
1490 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1492 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1493 return E_INVALIDARG;
1495 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1496 * return the same digests for the same URL.
1498 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1499 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1500 return S_OK;
1503 /*************************************************************************
1504 * UrlApplySchemeA [SHLWAPI.@]
1506 * Apply a scheme to a Url.
1508 * PARAMS
1509 * pszIn [I] Url to apply scheme to
1510 * pszOut [O] Destination for modified Url
1511 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1512 * dwFlags [I] URL_ flags from "shlwapi.h"
1514 * RETURNS
1515 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1516 * Failure: An HRESULT error code describing the error.
1518 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1520 LPWSTR in, out;
1521 HRESULT ret;
1522 DWORD len;
1524 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1525 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1527 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1529 in = HeapAlloc(GetProcessHeap(), 0,
1530 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1531 out = in + INTERNET_MAX_URL_LENGTH;
1533 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1534 len = INTERNET_MAX_URL_LENGTH;
1536 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1537 if (ret != S_OK) {
1538 HeapFree(GetProcessHeap(), 0, in);
1539 return ret;
1542 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1543 if (len > *pcchOut) {
1544 ret = E_POINTER;
1545 goto cleanup;
1548 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1549 len--;
1551 cleanup:
1552 *pcchOut = len;
1553 HeapFree(GetProcessHeap(), 0, in);
1554 return ret;
1557 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1559 HKEY newkey;
1560 BOOL j;
1561 INT index;
1562 DWORD value_len, data_len, dwType, i;
1563 WCHAR reg_path[MAX_PATH];
1564 WCHAR value[MAX_PATH], data[MAX_PATH];
1565 WCHAR Wxx, Wyy;
1567 MultiByteToWideChar(0, 0,
1568 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1569 -1, reg_path, MAX_PATH);
1570 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1571 index = 0;
1572 while(value_len = data_len = MAX_PATH,
1573 RegEnumValueW(newkey, index, value, &value_len,
1574 0, &dwType, (LPVOID)data, &data_len) == 0) {
1575 TRACE("guess %d %s is %s\n",
1576 index, debugstr_w(value), debugstr_w(data));
1578 j = FALSE;
1579 for(i=0; i<value_len; i++) {
1580 Wxx = pszIn[i];
1581 Wyy = value[i];
1582 /* remember that TRUE is not-equal */
1583 j = ChrCmpIW(Wxx, Wyy);
1584 if (j) break;
1586 if ((i == value_len) && !j) {
1587 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1588 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1589 RegCloseKey(newkey);
1590 return E_POINTER;
1592 strcpyW(pszOut, data);
1593 strcatW(pszOut, pszIn);
1594 *pcchOut = strlenW(pszOut);
1595 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1596 RegCloseKey(newkey);
1597 return S_OK;
1599 index++;
1601 RegCloseKey(newkey);
1602 return E_FAIL;
1605 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1607 HKEY newkey;
1608 DWORD data_len, dwType;
1609 WCHAR data[MAX_PATH];
1611 static const WCHAR prefix_keyW[] =
1612 {'S','o','f','t','w','a','r','e',
1613 '\\','M','i','c','r','o','s','o','f','t',
1614 '\\','W','i','n','d','o','w','s',
1615 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1616 '\\','U','R','L',
1617 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1619 /* get and prepend default */
1620 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1621 data_len = sizeof(data);
1622 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1623 RegCloseKey(newkey);
1624 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1625 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1626 return E_POINTER;
1628 strcpyW(pszOut, data);
1629 strcatW(pszOut, pszIn);
1630 *pcchOut = strlenW(pszOut);
1631 TRACE("used default %s\n", debugstr_w(pszOut));
1632 return S_OK;
1635 /*************************************************************************
1636 * UrlApplySchemeW [SHLWAPI.@]
1638 * See UrlApplySchemeA.
1640 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1642 PARSEDURLW in_scheme;
1643 DWORD res1;
1644 HRESULT ret;
1646 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1647 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1649 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1651 if (dwFlags & URL_APPLY_GUESSFILE) {
1652 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1653 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1654 strcpyW(pszOut, pszIn);
1655 *pcchOut = strlenW(pszOut);
1656 return S_FALSE;
1659 in_scheme.cbSize = sizeof(in_scheme);
1660 /* See if the base has a scheme */
1661 res1 = ParseURLW(pszIn, &in_scheme);
1662 if (res1) {
1663 /* no scheme in input, need to see if we need to guess */
1664 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1665 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1666 return ret;
1669 else {
1670 /* we have a scheme, see if valid (known scheme) */
1671 if (in_scheme.nScheme) {
1672 /* have valid scheme, so just copy and exit */
1673 if (strlenW(pszIn) + 1 > *pcchOut) {
1674 *pcchOut = strlenW(pszIn) + 1;
1675 return E_POINTER;
1677 strcpyW(pszOut, pszIn);
1678 *pcchOut = strlenW(pszOut);
1679 TRACE("valid scheme, returning copy\n");
1680 return S_OK;
1684 /* If we are here, then either invalid scheme,
1685 * or no scheme and can't/failed guess.
1687 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1688 ((res1 != 0)) ) &&
1689 (dwFlags & URL_APPLY_DEFAULT)) {
1690 /* find and apply default scheme */
1691 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1694 return S_FALSE;
1697 /*************************************************************************
1698 * UrlIsA [SHLWAPI.@]
1700 * Determine if a Url is of a certain class.
1702 * PARAMS
1703 * pszUrl [I] Url to check
1704 * Urlis [I] URLIS_ constant from "shlwapi.h"
1706 * RETURNS
1707 * TRUE if pszUrl belongs to the class type in Urlis.
1708 * FALSE Otherwise.
1710 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1712 PARSEDURLA base;
1713 DWORD res1;
1714 LPCSTR last;
1716 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1718 if(!pszUrl)
1719 return FALSE;
1721 switch (Urlis) {
1723 case URLIS_OPAQUE:
1724 base.cbSize = sizeof(base);
1725 res1 = ParseURLA(pszUrl, &base);
1726 if (res1) return FALSE; /* invalid scheme */
1727 switch (base.nScheme)
1729 case URL_SCHEME_MAILTO:
1730 case URL_SCHEME_SHELL:
1731 case URL_SCHEME_JAVASCRIPT:
1732 case URL_SCHEME_VBSCRIPT:
1733 case URL_SCHEME_ABOUT:
1734 return TRUE;
1736 return FALSE;
1738 case URLIS_FILEURL:
1739 return !StrCmpNA("file:", pszUrl, 5);
1741 case URLIS_DIRECTORY:
1742 last = pszUrl + strlen(pszUrl) - 1;
1743 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1745 case URLIS_URL:
1746 return PathIsURLA(pszUrl);
1748 case URLIS_NOHISTORY:
1749 case URLIS_APPLIABLE:
1750 case URLIS_HASQUERY:
1751 default:
1752 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1754 return FALSE;
1757 /*************************************************************************
1758 * UrlIsW [SHLWAPI.@]
1760 * See UrlIsA.
1762 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1764 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1765 PARSEDURLW base;
1766 DWORD res1;
1767 LPCWSTR last;
1769 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1771 if(!pszUrl)
1772 return FALSE;
1774 switch (Urlis) {
1776 case URLIS_OPAQUE:
1777 base.cbSize = sizeof(base);
1778 res1 = ParseURLW(pszUrl, &base);
1779 if (res1) return FALSE; /* invalid scheme */
1780 switch (base.nScheme)
1782 case URL_SCHEME_MAILTO:
1783 case URL_SCHEME_SHELL:
1784 case URL_SCHEME_JAVASCRIPT:
1785 case URL_SCHEME_VBSCRIPT:
1786 case URL_SCHEME_ABOUT:
1787 return TRUE;
1789 return FALSE;
1791 case URLIS_FILEURL:
1792 return !strncmpW(stemp, pszUrl, 5);
1794 case URLIS_DIRECTORY:
1795 last = pszUrl + strlenW(pszUrl) - 1;
1796 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1798 case URLIS_URL:
1799 return PathIsURLW(pszUrl);
1801 case URLIS_NOHISTORY:
1802 case URLIS_APPLIABLE:
1803 case URLIS_HASQUERY:
1804 default:
1805 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1807 return FALSE;
1810 /*************************************************************************
1811 * UrlIsNoHistoryA [SHLWAPI.@]
1813 * Determine if a Url should not be stored in the users history list.
1815 * PARAMS
1816 * pszUrl [I] Url to check
1818 * RETURNS
1819 * TRUE, if pszUrl should be excluded from the history list,
1820 * FALSE otherwise.
1822 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1824 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1827 /*************************************************************************
1828 * UrlIsNoHistoryW [SHLWAPI.@]
1830 * See UrlIsNoHistoryA.
1832 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1834 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1837 /*************************************************************************
1838 * UrlIsOpaqueA [SHLWAPI.@]
1840 * Determine if a Url is opaque.
1842 * PARAMS
1843 * pszUrl [I] Url to check
1845 * RETURNS
1846 * TRUE if pszUrl is opaque,
1847 * FALSE Otherwise.
1849 * NOTES
1850 * An opaque Url is one that does not start with "<protocol>://".
1852 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1854 return UrlIsA(pszUrl, URLIS_OPAQUE);
1857 /*************************************************************************
1858 * UrlIsOpaqueW [SHLWAPI.@]
1860 * See UrlIsOpaqueA.
1862 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1864 return UrlIsW(pszUrl, URLIS_OPAQUE);
1867 /*************************************************************************
1868 * Scans for characters of type "type" and when not matching found,
1869 * returns pointer to it and length in size.
1871 * Characters tested based on RFC 1738
1873 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1875 static DWORD alwayszero = 0;
1876 BOOL cont = TRUE;
1878 *size = 0;
1880 switch(type){
1882 case SCHEME:
1883 while (cont) {
1884 if ( (islowerW(*start) && isalphaW(*start)) ||
1885 isdigitW(*start) ||
1886 (*start == '+') ||
1887 (*start == '-') ||
1888 (*start == '.')) {
1889 start++;
1890 (*size)++;
1892 else
1893 cont = FALSE;
1895 break;
1897 case USERPASS:
1898 while (cont) {
1899 if ( isalphaW(*start) ||
1900 isdigitW(*start) ||
1901 /* user/password only characters */
1902 (*start == ';') ||
1903 (*start == '?') ||
1904 (*start == '&') ||
1905 (*start == '=') ||
1906 /* *extra* characters */
1907 (*start == '!') ||
1908 (*start == '*') ||
1909 (*start == '\'') ||
1910 (*start == '(') ||
1911 (*start == ')') ||
1912 (*start == ',') ||
1913 /* *safe* characters */
1914 (*start == '$') ||
1915 (*start == '_') ||
1916 (*start == '+') ||
1917 (*start == '-') ||
1918 (*start == '.') ||
1919 (*start == ' ')) {
1920 start++;
1921 (*size)++;
1922 } else if (*start == '%') {
1923 if (isxdigitW(*(start+1)) &&
1924 isxdigitW(*(start+2))) {
1925 start += 3;
1926 *size += 3;
1927 } else
1928 cont = FALSE;
1929 } else
1930 cont = FALSE;
1932 break;
1934 case PORT:
1935 while (cont) {
1936 if (isdigitW(*start)) {
1937 start++;
1938 (*size)++;
1940 else
1941 cont = FALSE;
1943 break;
1945 case HOST:
1946 while (cont) {
1947 if (isalnumW(*start) ||
1948 (*start == '-') ||
1949 (*start == '.') ||
1950 (*start == ' ') ) {
1951 start++;
1952 (*size)++;
1954 else
1955 cont = FALSE;
1957 break;
1958 default:
1959 FIXME("unknown type %d\n", type);
1960 return (LPWSTR)&alwayszero;
1962 /* TRACE("scanned %d characters next char %p<%c>\n",
1963 *size, start, *start); */
1964 return start;
1967 /*************************************************************************
1968 * Attempt to parse URL into pieces.
1970 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1972 LPCWSTR work;
1974 memset(pl, 0, sizeof(WINE_PARSE_URL));
1975 pl->pScheme = pszUrl;
1976 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1977 if (!*work || (*work != ':')) goto ErrorExit;
1978 work++;
1979 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
1980 pl->pUserName = work + 2;
1981 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1982 if (*work == ':' ) {
1983 /* parse password */
1984 work++;
1985 pl->pPassword = work;
1986 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1987 if (*work != '@') {
1988 /* what we just parsed must be the hostname and port
1989 * so reset pointers and clear then let it parse */
1990 pl->szUserName = pl->szPassword = 0;
1991 work = pl->pUserName - 1;
1992 pl->pUserName = pl->pPassword = 0;
1994 } else if (*work == '@') {
1995 /* no password */
1996 pl->szPassword = 0;
1997 pl->pPassword = 0;
1998 } else if (!*work || (*work == '/') || (*work == '.')) {
1999 /* what was parsed was hostname, so reset pointers and let it parse */
2000 pl->szUserName = pl->szPassword = 0;
2001 work = pl->pUserName - 1;
2002 pl->pUserName = pl->pPassword = 0;
2003 } else goto ErrorExit;
2005 /* now start parsing hostname or hostnumber */
2006 work++;
2007 pl->pHostName = work;
2008 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2009 if (*work == ':') {
2010 /* parse port */
2011 work++;
2012 pl->pPort = work;
2013 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2015 if (*work == '/') {
2016 /* see if query string */
2017 pl->pQuery = strchrW(work, '?');
2018 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2020 SuccessExit:
2021 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2022 pl->pScheme, pl->szScheme,
2023 pl->pUserName, pl->szUserName,
2024 pl->pPassword, pl->szPassword,
2025 pl->pHostName, pl->szHostName,
2026 pl->pPort, pl->szPort,
2027 pl->pQuery, pl->szQuery);
2028 return S_OK;
2029 ErrorExit:
2030 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2031 return E_INVALIDARG;
2034 /*************************************************************************
2035 * UrlGetPartA [SHLWAPI.@]
2037 * Retrieve part of a Url.
2039 * PARAMS
2040 * pszIn [I] Url to parse
2041 * pszOut [O] Destination for part of pszIn requested
2042 * pcchOut [I] Size of pszOut
2043 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2044 * needed size of pszOut INCLUDING '\0'.
2045 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2046 * dwFlags [I] URL_ flags from "shlwapi.h"
2048 * RETURNS
2049 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2050 * Failure: An HRESULT error code describing the error.
2052 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2053 DWORD dwPart, DWORD dwFlags)
2055 LPWSTR in, out;
2056 DWORD ret, len, len2;
2058 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2059 return E_INVALIDARG;
2061 in = HeapAlloc(GetProcessHeap(), 0,
2062 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2063 out = in + INTERNET_MAX_URL_LENGTH;
2065 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2067 len = INTERNET_MAX_URL_LENGTH;
2068 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2070 if (FAILED(ret)) {
2071 HeapFree(GetProcessHeap(), 0, in);
2072 return ret;
2075 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2076 if (len2 > *pcchOut) {
2077 *pcchOut = len2+1;
2078 HeapFree(GetProcessHeap(), 0, in);
2079 return E_POINTER;
2081 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2082 *pcchOut = len2-1;
2083 HeapFree(GetProcessHeap(), 0, in);
2084 return ret;
2087 /*************************************************************************
2088 * UrlGetPartW [SHLWAPI.@]
2090 * See UrlGetPartA.
2092 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2093 DWORD dwPart, DWORD dwFlags)
2095 WINE_PARSE_URL pl;
2096 HRESULT ret;
2097 DWORD scheme, size, schsize;
2098 LPCWSTR addr, schaddr;
2100 TRACE("(%s %p %p(%d) %08x %08x)\n",
2101 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2103 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2104 return E_INVALIDARG;
2106 *pszOut = '\0';
2108 addr = strchrW(pszIn, ':');
2109 if(!addr)
2110 scheme = URL_SCHEME_UNKNOWN;
2111 else
2112 scheme = get_scheme_code(pszIn, addr-pszIn);
2114 ret = URL_ParseUrl(pszIn, &pl);
2116 switch (dwPart) {
2117 case URL_PART_SCHEME:
2118 if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
2119 *pcchOut = 0;
2120 return S_FALSE;
2122 addr = pl.pScheme;
2123 size = pl.szScheme;
2124 break;
2126 case URL_PART_HOSTNAME:
2127 switch(scheme) {
2128 case URL_SCHEME_FTP:
2129 case URL_SCHEME_HTTP:
2130 case URL_SCHEME_GOPHER:
2131 case URL_SCHEME_TELNET:
2132 case URL_SCHEME_FILE:
2133 case URL_SCHEME_HTTPS:
2134 break;
2135 default:
2136 *pcchOut = 0;
2137 return E_FAIL;
2140 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2141 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2142 *pcchOut = 0;
2143 return S_FALSE;
2146 if (!pl.szHostName) {
2147 *pcchOut = 0;
2148 return S_FALSE;
2150 addr = pl.pHostName;
2151 size = pl.szHostName;
2152 break;
2154 case URL_PART_USERNAME:
2155 if (!pl.szUserName) {
2156 *pcchOut = 0;
2157 return S_FALSE;
2159 addr = pl.pUserName;
2160 size = pl.szUserName;
2161 break;
2163 case URL_PART_PASSWORD:
2164 if (!pl.szPassword) {
2165 *pcchOut = 0;
2166 return S_FALSE;
2168 addr = pl.pPassword;
2169 size = pl.szPassword;
2170 break;
2172 case URL_PART_PORT:
2173 if (!pl.szPort) {
2174 *pcchOut = 0;
2175 return S_FALSE;
2177 addr = pl.pPort;
2178 size = pl.szPort;
2179 break;
2181 case URL_PART_QUERY:
2182 if (!pl.szQuery) {
2183 *pcchOut = 0;
2184 return S_FALSE;
2186 addr = pl.pQuery;
2187 size = pl.szQuery;
2188 break;
2190 default:
2191 *pcchOut = 0;
2192 return E_INVALIDARG;
2195 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2196 if(!pl.pScheme || !pl.szScheme) {
2197 *pcchOut = 0;
2198 return E_FAIL;
2200 schaddr = pl.pScheme;
2201 schsize = pl.szScheme;
2202 if (*pcchOut < schsize + size + 2) {
2203 *pcchOut = schsize + size + 2;
2204 return E_POINTER;
2206 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2207 pszOut[schsize] = ':';
2208 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2209 pszOut[schsize+1+size] = 0;
2210 *pcchOut = schsize + 1 + size;
2212 else {
2213 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2214 memcpy(pszOut, addr, size*sizeof(WCHAR));
2215 pszOut[size] = 0;
2216 *pcchOut = size;
2218 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2220 return ret;
2223 /*************************************************************************
2224 * PathIsURLA [SHLWAPI.@]
2226 * Check if the given path is a Url.
2228 * PARAMS
2229 * lpszPath [I] Path to check.
2231 * RETURNS
2232 * TRUE if lpszPath is a Url.
2233 * FALSE if lpszPath is NULL or not a Url.
2235 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2237 PARSEDURLA base;
2238 HRESULT hres;
2240 TRACE("%s\n", debugstr_a(lpstrPath));
2242 if (!lpstrPath || !*lpstrPath) return FALSE;
2244 /* get protocol */
2245 base.cbSize = sizeof(base);
2246 hres = ParseURLA(lpstrPath, &base);
2247 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2250 /*************************************************************************
2251 * PathIsURLW [SHLWAPI.@]
2253 * See PathIsURLA.
2255 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2257 PARSEDURLW base;
2258 HRESULT hres;
2260 TRACE("%s\n", debugstr_w(lpstrPath));
2262 if (!lpstrPath || !*lpstrPath) return FALSE;
2264 /* get protocol */
2265 base.cbSize = sizeof(base);
2266 hres = ParseURLW(lpstrPath, &base);
2267 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2270 /*************************************************************************
2271 * UrlCreateFromPathA [SHLWAPI.@]
2273 * See UrlCreateFromPathW
2275 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2277 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2278 WCHAR *urlW = bufW;
2279 UNICODE_STRING pathW;
2280 HRESULT ret;
2281 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2283 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2284 return E_INVALIDARG;
2285 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2286 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2287 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2289 if(ret == S_OK || ret == S_FALSE) {
2290 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2291 if(*pcchUrl > lenA) {
2292 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2293 pszUrl[lenA] = 0;
2294 *pcchUrl = lenA;
2295 } else {
2296 *pcchUrl = lenA + 1;
2297 ret = E_POINTER;
2300 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2301 RtlFreeUnicodeString(&pathW);
2302 return ret;
2305 /*************************************************************************
2306 * UrlCreateFromPathW [SHLWAPI.@]
2308 * Create a Url from a file path.
2310 * PARAMS
2311 * pszPath [I] Path to convert
2312 * pszUrl [O] Destination for the converted Url
2313 * pcchUrl [I/O] Length of pszUrl
2314 * dwReserved [I] Reserved, must be 0
2316 * RETURNS
2317 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2318 * Failure: An HRESULT error code.
2320 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2322 DWORD needed;
2323 HRESULT ret;
2324 WCHAR *pszNewUrl;
2325 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2326 WCHAR three_slashesW[] = {'/','/','/',0};
2327 PARSEDURLW parsed_url;
2329 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2331 /* Validate arguments */
2332 if (dwReserved != 0)
2333 return E_INVALIDARG;
2334 if (!pszUrl || !pcchUrl)
2335 return E_INVALIDARG;
2338 parsed_url.cbSize = sizeof(parsed_url);
2339 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2340 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2341 needed = strlenW(pszPath);
2342 if (needed >= *pcchUrl) {
2343 *pcchUrl = needed + 1;
2344 return E_POINTER;
2345 } else {
2346 *pcchUrl = needed;
2347 strcpyW(pszUrl, pszPath);
2348 return S_FALSE;
2353 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2354 strcpyW(pszNewUrl, file_colonW);
2355 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2356 strcatW(pszNewUrl, three_slashesW);
2357 strcatW(pszNewUrl, pszPath);
2358 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2360 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2361 return ret;
2364 /*************************************************************************
2365 * SHAutoComplete [SHLWAPI.@]
2367 * Enable auto-completion for an edit control.
2369 * PARAMS
2370 * hwndEdit [I] Handle of control to enable auto-completion for
2371 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2373 * RETURNS
2374 * Success: S_OK. Auto-completion is enabled for the control.
2375 * Failure: An HRESULT error code indicating the error.
2377 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2379 FIXME("stub\n");
2380 return S_FALSE;
2383 /*************************************************************************
2384 * MLBuildResURLA [SHLWAPI.405]
2386 * Create a Url pointing to a resource in a module.
2388 * PARAMS
2389 * lpszLibName [I] Name of the module containing the resource
2390 * hMod [I] Callers module handle
2391 * dwFlags [I] Undocumented flags for loading the module
2392 * lpszRes [I] Resource name
2393 * lpszDest [O] Destination for resulting Url
2394 * dwDestLen [I] Length of lpszDest
2396 * RETURNS
2397 * Success: S_OK. lpszDest contains the resource Url.
2398 * Failure: E_INVALIDARG, if any argument is invalid, or
2399 * E_FAIL if dwDestLen is too small.
2401 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2402 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2404 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2405 HRESULT hRet;
2407 if (lpszLibName)
2408 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2410 if (lpszRes)
2411 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2413 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2414 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2416 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2417 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2418 if (SUCCEEDED(hRet) && lpszDest)
2419 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2421 return hRet;
2424 /*************************************************************************
2425 * MLBuildResURLA [SHLWAPI.406]
2427 * See MLBuildResURLA.
2429 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2430 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2432 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2433 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2434 HRESULT hRet = E_FAIL;
2436 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2437 debugstr_w(lpszRes), lpszDest, dwDestLen);
2439 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2440 !lpszDest || (dwFlags && dwFlags != 2))
2441 return E_INVALIDARG;
2443 if (dwDestLen >= szResLen + 1)
2445 dwDestLen -= (szResLen + 1);
2446 memcpy(lpszDest, szRes, sizeof(szRes));
2448 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2450 if (hMod)
2452 WCHAR szBuff[MAX_PATH];
2453 DWORD len;
2455 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2456 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2458 DWORD dwPathLen = strlenW(szBuff) + 1;
2460 if (dwDestLen >= dwPathLen)
2462 DWORD dwResLen;
2464 dwDestLen -= dwPathLen;
2465 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2467 dwResLen = strlenW(lpszRes) + 1;
2468 if (dwDestLen >= dwResLen + 1)
2470 lpszDest[szResLen + dwPathLen-1] = '/';
2471 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2472 hRet = S_OK;
2476 MLFreeLibrary(hMod);
2479 return hRet;
2482 /***********************************************************************
2483 * UrlFixupW [SHLWAPI.462]
2485 * Checks the scheme part of a URL and attempts to correct misspellings.
2487 * PARAMS
2488 * lpszUrl [I] Pointer to the URL to be corrected
2489 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2490 * dwMaxChars [I] Maximum size of corrected URL
2492 * RETURNS
2493 * success: S_OK if URL corrected or already correct
2494 * failure: S_FALSE if unable to correct / COM error code if other error
2497 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2499 DWORD srcLen;
2501 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2503 if (!url)
2504 return E_FAIL;
2506 srcLen = lstrlenW(url) + 1;
2508 /* For now just copy the URL directly */
2509 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2511 return S_OK;