Release 1.1.37.
[wine/gsoc-2012-control.git] / dlls / shlwapi / url.c
blobd1b6e260992100ccbb07d3592008b2a34e625b9c
1 /*
2 * Url functions
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #define NO_SHLWAPI_STREAM
35 #include "shlwapi.h"
36 #include "wine/debug.h"
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
47 static const struct {
48 URL_SCHEME scheme_number;
49 WCHAR scheme_name[12];
50 } shlwapi_schemes[] = {
51 {URL_SCHEME_FTP, {'f','t','p',0}},
52 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
53 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
54 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
55 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
56 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
57 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
58 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
59 {URL_SCHEME_FILE, {'f','i','l','e',0}},
60 {URL_SCHEME_MK, {'m','k',0}},
61 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
62 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
63 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
64 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
65 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
66 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
67 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
68 {URL_SCHEME_RES, {'r','e','s',0}},
71 typedef struct {
72 LPCWSTR pScheme; /* [out] start of scheme */
73 DWORD szScheme; /* [out] size of scheme (until colon) */
74 LPCWSTR pUserName; /* [out] start of Username */
75 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
76 LPCWSTR pPassword; /* [out] start of Password */
77 DWORD szPassword; /* [out] size of Password (until "@") */
78 LPCWSTR pHostName; /* [out] start of Hostname */
79 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
80 LPCWSTR pPort; /* [out] start of Port */
81 DWORD szPort; /* [out] size of Port (until "/" or eos) */
82 LPCWSTR pQuery; /* [out] start of Query */
83 DWORD szQuery; /* [out] size of Query (until eos) */
84 } WINE_PARSE_URL;
86 typedef enum {
87 SCHEME,
88 HOST,
89 PORT,
90 USERPASS,
91 } WINE_URL_SCAN_TYPE;
93 static const CHAR hexDigits[] = "0123456789ABCDEF";
95 static const WCHAR fileW[] = {'f','i','l','e','\0'};
97 static const unsigned char HashDataLookup[256] = {
98 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
99 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
100 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
101 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
102 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
103 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
104 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
105 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
106 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
107 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
108 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
109 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
110 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
111 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
112 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
113 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
114 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
115 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
116 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
117 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
119 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
121 unsigned int i;
123 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
124 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
125 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
126 return shlwapi_schemes[i].scheme_number;
129 return URL_SCHEME_UNKNOWN;
132 /*************************************************************************
133 * @ [SHLWAPI.1]
135 * Parse a Url into its constituent parts.
137 * PARAMS
138 * x [I] Url to parse
139 * y [O] Undocumented structure holding the parsed information
141 * RETURNS
142 * Success: S_OK. y contains the parsed Url details.
143 * Failure: An HRESULT error code.
145 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
147 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
148 const char *ptr = x;
149 int len;
151 TRACE("%s %p\n", debugstr_a(x), y);
153 if(y->cbSize != sizeof(*y))
154 return E_INVALIDARG;
156 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
157 ptr++;
159 if (*ptr != ':' || ptr <= x+1) {
160 y->pszProtocol = NULL;
161 return 0x80041001;
164 y->pszProtocol = x;
165 y->cchProtocol = ptr-x;
166 y->pszSuffix = ptr+1;
167 y->cchSuffix = strlen(y->pszSuffix);
169 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
170 scheme, sizeof(scheme)/sizeof(WCHAR));
171 y->nScheme = get_scheme_code(scheme, len);
173 return S_OK;
176 /*************************************************************************
177 * @ [SHLWAPI.2]
179 * Unicode version of ParseURLA.
181 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
183 const WCHAR *ptr = x;
185 TRACE("%s %p\n", debugstr_w(x), y);
187 if(y->cbSize != sizeof(*y))
188 return E_INVALIDARG;
190 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
191 ptr++;
193 if (*ptr != ':' || ptr <= x+1) {
194 y->pszProtocol = NULL;
195 return 0x80041001;
198 y->pszProtocol = x;
199 y->cchProtocol = ptr-x;
200 y->pszSuffix = ptr+1;
201 y->cchSuffix = strlenW(y->pszSuffix);
202 y->nScheme = get_scheme_code(x, ptr-x);
204 return S_OK;
207 /*************************************************************************
208 * UrlCanonicalizeA [SHLWAPI.@]
210 * Canonicalize a Url.
212 * PARAMS
213 * pszUrl [I] Url to cCanonicalize
214 * pszCanonicalized [O] Destination for converted Url.
215 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
216 * dwFlags [I] Flags controlling the conversion.
218 * RETURNS
219 * Success: S_OK. The pszCanonicalized contains the converted Url.
220 * Failure: E_POINTER, if *pcchCanonicalized is too small.
222 * MSDN incorrectly describes the flags for this function. They should be:
223 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
224 *| URL_ESCAPE_SPACES_ONLY 0x04000000
225 *| URL_ESCAPE_PERCENT 0x00001000
226 *| URL_ESCAPE_UNSAFE 0x10000000
227 *| URL_UNESCAPE 0x10000000
228 *| URL_DONT_SIMPLIFY 0x08000000
229 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
231 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
232 LPDWORD pcchCanonicalized, DWORD dwFlags)
234 LPWSTR base, canonical;
235 HRESULT ret;
236 DWORD len, len2;
238 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
239 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
241 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
242 return E_INVALIDARG;
244 base = HeapAlloc(GetProcessHeap(), 0,
245 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
246 canonical = base + INTERNET_MAX_URL_LENGTH;
248 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
249 len = INTERNET_MAX_URL_LENGTH;
251 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
252 if (ret != S_OK) {
253 *pcchCanonicalized = len * 2;
254 HeapFree(GetProcessHeap(), 0, base);
255 return ret;
258 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
259 if (len2 > *pcchCanonicalized) {
260 *pcchCanonicalized = len2;
261 HeapFree(GetProcessHeap(), 0, base);
262 return E_POINTER;
264 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
265 *pcchCanonicalized = len;
266 HeapFree(GetProcessHeap(), 0, base);
267 return S_OK;
270 /*************************************************************************
271 * UrlCanonicalizeW [SHLWAPI.@]
273 * See UrlCanonicalizeA.
275 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
276 LPDWORD pcchCanonicalized, DWORD dwFlags)
278 HRESULT hr = S_OK;
279 DWORD EscapeFlags;
280 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
281 INT state;
282 DWORD nByteLen, nLen, nWkLen;
283 WCHAR slash = '/';
285 static const WCHAR wszFile[] = {'f','i','l','e',':'};
286 static const WCHAR wszRes[] = {'r','e','s',':'};
287 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
289 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
290 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
292 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
293 return E_INVALIDARG;
295 if(!*pszUrl) {
296 *pszCanonicalized = 0;
297 return S_OK;
300 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
301 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
302 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
304 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
305 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
306 slash = '\\';
308 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, pszUrl, sizeof(wszRes))) {
309 dwFlags &= ~URL_FILE_USE_PATHURL;
310 slash = '\0';
314 * state =
315 * 0 initial 1,3
316 * 1 have 2[+] alnum 2,3
317 * 2 have scheme (found :) 4,6,3
318 * 3 failed (no location)
319 * 4 have // 5,3
320 * 5 have 1[+] alnum 6,3
321 * 6 have location (found /) save root location
324 wk1 = (LPWSTR)pszUrl;
325 wk2 = lpszUrlCpy;
326 state = 0;
328 if(pszUrl[1] == ':') { /* Assume path */
329 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
331 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
332 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
333 if (dwFlags & URL_FILE_USE_PATHURL)
335 slash = '\\';
336 --wk2;
338 else
339 dwFlags |= URL_ESCAPE_UNSAFE;
340 state = 5;
343 while (*wk1) {
344 switch (state) {
345 case 0:
346 if (!isalnumW(*wk1)) {state = 3; break;}
347 *wk2++ = *wk1++;
348 if (!isalnumW(*wk1)) {state = 3; break;}
349 *wk2++ = *wk1++;
350 state = 1;
351 break;
352 case 1:
353 *wk2++ = *wk1;
354 if (*wk1++ == ':') state = 2;
355 break;
356 case 2:
357 *wk2++ = *wk1++;
358 if (*wk1 != '/') {state = 6; break;}
359 *wk2++ = *wk1++;
360 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
361 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
362 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
363 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
364 wk1++;
366 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
367 wk1++;
368 state = 4;
369 break;
370 case 3:
371 nWkLen = strlenW(wk1);
372 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
373 mp = wk2;
374 wk1 += nWkLen;
375 wk2 += nWkLen;
377 if(slash) {
378 while(mp < wk2) {
379 if(*mp == '/' || *mp == '\\')
380 *mp = slash;
381 mp++;
384 break;
385 case 4:
386 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
387 {state = 3; break;}
388 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
389 *wk2++ = *wk1++;
390 state = 5;
391 if (!*wk1) {
392 if(slash)
393 *wk2++ = slash;
394 else
395 *wk2++ = '/';
397 break;
398 case 5:
399 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
400 while(*wk1 == '/' || *wk1 == '\\') {
401 if(slash)
402 *wk2++ = slash;
403 else
404 *wk2++ = *wk1;
405 wk1++;
407 state = 6;
408 break;
409 case 6:
410 if(dwFlags & URL_DONT_SIMPLIFY) {
411 state = 3;
412 break;
415 /* Now at root location, cannot back up any more. */
416 /* "root" will point at the '/' */
418 root = wk2-1;
419 while (*wk1) {
420 mp = strchrW(wk1, '/');
421 mp2 = strchrW(wk1, '\\');
422 if(mp2 && (!mp || mp2 < mp))
423 mp = mp2;
424 if (!mp) {
425 nWkLen = strlenW(wk1);
426 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
427 wk1 += nWkLen;
428 wk2 += nWkLen;
429 continue;
431 nLen = mp - wk1;
432 if(nLen) {
433 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
434 wk2 += nLen;
435 wk1 += nLen;
437 if(slash)
438 *wk2++ = slash;
439 else
440 *wk2++ = *wk1;
441 wk1++;
443 if (*wk1 == '.') {
444 TRACE("found '/.'\n");
445 if (wk1[1] == '/' || wk1[1] == '\\') {
446 /* case of /./ -> skip the ./ */
447 wk1 += 2;
449 else if (wk1[1] == '.') {
450 /* found /.. look for next / */
451 TRACE("found '/..'\n");
452 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
453 || wk1[2] == '#' || !wk1[2]) {
454 /* case /../ -> need to backup wk2 */
455 TRACE("found '/../'\n");
456 *(wk2-1) = '\0'; /* set end of string */
457 mp = strrchrW(root, '/');
458 mp2 = strrchrW(root, '\\');
459 if(mp2 && (!mp || mp2 < mp))
460 mp = mp2;
461 if (mp && (mp >= root)) {
462 /* found valid backup point */
463 wk2 = mp + 1;
464 if(wk1[2] != '/' && wk1[2] != '\\')
465 wk1 += 2;
466 else
467 wk1 += 3;
469 else {
470 /* did not find point, restore '/' */
471 *(wk2-1) = slash;
477 *wk2 = '\0';
478 break;
479 default:
480 FIXME("how did we get here - state=%d\n", state);
481 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
482 return E_INVALIDARG;
484 *wk2 = '\0';
485 TRACE("Simplified, orig <%s>, simple <%s>\n",
486 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
488 nLen = lstrlenW(lpszUrlCpy);
489 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
490 lpszUrlCpy[--nLen]=0;
492 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
493 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
495 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
496 URL_ESCAPE_SPACES_ONLY |
497 URL_ESCAPE_PERCENT |
498 URL_DONT_ESCAPE_EXTRA_INFO |
499 URL_ESCAPE_SEGMENT_ONLY ))) {
500 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
501 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
502 EscapeFlags);
503 } else { /* No escaping needed, just copy the string */
504 nLen = lstrlenW(lpszUrlCpy);
505 if(nLen < *pcchCanonicalized)
506 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
507 else {
508 hr = E_POINTER;
509 nLen++;
511 *pcchCanonicalized = nLen;
514 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
516 if (hr == S_OK)
517 TRACE("result %s\n", debugstr_w(pszCanonicalized));
519 return hr;
522 /*************************************************************************
523 * UrlCombineA [SHLWAPI.@]
525 * Combine two Urls.
527 * PARAMS
528 * pszBase [I] Base Url
529 * pszRelative [I] Url to combine with pszBase
530 * pszCombined [O] Destination for combined Url
531 * pcchCombined [O] Destination for length of pszCombined
532 * dwFlags [I] URL_ flags from "shlwapi.h"
534 * RETURNS
535 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
536 * contains its length.
537 * Failure: An HRESULT error code indicating the error.
539 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
540 LPSTR pszCombined, LPDWORD pcchCombined,
541 DWORD dwFlags)
543 LPWSTR base, relative, combined;
544 DWORD ret, len, len2;
546 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
547 debugstr_a(pszBase),debugstr_a(pszRelative),
548 pcchCombined?*pcchCombined:0,dwFlags);
550 if(!pszBase || !pszRelative || !pcchCombined)
551 return E_INVALIDARG;
553 base = HeapAlloc(GetProcessHeap(), 0,
554 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
555 relative = base + INTERNET_MAX_URL_LENGTH;
556 combined = relative + INTERNET_MAX_URL_LENGTH;
558 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
559 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
560 len = *pcchCombined;
562 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
563 if (ret != S_OK) {
564 *pcchCombined = len;
565 HeapFree(GetProcessHeap(), 0, base);
566 return ret;
569 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
570 if (len2 > *pcchCombined) {
571 *pcchCombined = len2;
572 HeapFree(GetProcessHeap(), 0, base);
573 return E_POINTER;
575 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
576 0, 0);
577 *pcchCombined = len2;
578 HeapFree(GetProcessHeap(), 0, base);
579 return S_OK;
582 /*************************************************************************
583 * UrlCombineW [SHLWAPI.@]
585 * See UrlCombineA.
587 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
588 LPWSTR pszCombined, LPDWORD pcchCombined,
589 DWORD dwFlags)
591 PARSEDURLW base, relative;
592 DWORD myflags, sizeloc = 0;
593 DWORD len, res1, res2, process_case = 0;
594 LPWSTR work, preliminary, mbase, mrelative;
595 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
596 HRESULT ret;
598 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
599 debugstr_w(pszBase),debugstr_w(pszRelative),
600 pcchCombined?*pcchCombined:0,dwFlags);
602 if(!pszBase || !pszRelative || !pcchCombined)
603 return E_INVALIDARG;
605 base.cbSize = sizeof(base);
606 relative.cbSize = sizeof(relative);
608 /* Get space for duplicates of the input and the output */
609 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
610 sizeof(WCHAR));
611 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
612 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
613 *preliminary = '\0';
615 /* Canonicalize the base input prior to looking for the scheme */
616 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
617 len = INTERNET_MAX_URL_LENGTH;
618 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
620 /* Canonicalize the relative input prior to looking for the scheme */
621 len = INTERNET_MAX_URL_LENGTH;
622 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
624 /* See if the base has a scheme */
625 res1 = ParseURLW(mbase, &base);
626 if (res1) {
627 /* if pszBase has no scheme, then return pszRelative */
628 TRACE("no scheme detected in Base\n");
629 process_case = 1;
631 else do {
632 /* mk is a special case */
633 if(base.nScheme == URL_SCHEME_MK) {
634 static const WCHAR wsz[] = {':',':',0};
636 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
637 if(ptr) {
638 int delta;
640 ptr += 2;
641 delta = ptr-base.pszSuffix;
642 base.cchProtocol += delta;
643 base.pszSuffix += delta;
644 base.cchSuffix -= delta;
646 }else {
647 /* get size of location field (if it exists) */
648 work = (LPWSTR)base.pszSuffix;
649 sizeloc = 0;
650 if (*work++ == '/') {
651 if (*work++ == '/') {
652 /* At this point have start of location and
653 * it ends at next '/' or end of string.
655 while(*work && (*work != '/')) work++;
656 sizeloc = (DWORD)(work - base.pszSuffix);
661 /* Change .sizep2 to not have the last leaf in it,
662 * Note: we need to start after the location (if it exists)
664 work = strrchrW((base.pszSuffix+sizeloc), '/');
665 if (work) {
666 len = (DWORD)(work - base.pszSuffix + 1);
667 base.cchSuffix = len;
671 * At this point:
672 * .pszSuffix points to location (starting with '//')
673 * .cchSuffix length of location (above) and rest less the last
674 * leaf (if any)
675 * sizeloc length of location (above) up to but not including
676 * the last '/'
679 res2 = ParseURLW(mrelative, &relative);
680 if (res2) {
681 /* no scheme in pszRelative */
682 TRACE("no scheme detected in Relative\n");
683 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
684 relative.cchSuffix = strlenW(mrelative);
685 if (*pszRelative == ':') {
686 /* case that is either left alone or uses pszBase */
687 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
688 process_case = 5;
689 break;
691 process_case = 1;
692 break;
694 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
695 /* case that becomes "file:///" */
696 strcpyW(preliminary, myfilestr);
697 process_case = 1;
698 break;
700 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
701 /* pszRelative has location and rest */
702 process_case = 3;
703 break;
705 if (*mrelative == '/') {
706 /* case where pszRelative is root to location */
707 process_case = 4;
708 break;
710 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
711 break;
714 /* handle cases where pszRelative has scheme */
715 if ((base.cchProtocol == relative.cchProtocol) &&
716 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
718 /* since the schemes are the same */
719 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
720 /* case where pszRelative replaces location and following */
721 process_case = 3;
722 break;
724 if (*relative.pszSuffix == '/') {
725 /* case where pszRelative is root to location */
726 process_case = 4;
727 break;
729 /* replace either just location if base's location starts with a
730 * slash or otherwise everything */
731 process_case = (*base.pszSuffix == '/') ? 5 : 1;
732 break;
734 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
735 /* case where pszRelative replaces scheme, location,
736 * and following and handles PLUGGABLE
738 process_case = 2;
739 break;
741 process_case = 1;
742 break;
743 } while(FALSE); /* a little trick to allow easy exit from nested if's */
745 ret = S_OK;
746 switch (process_case) {
748 case 1: /*
749 * Return pszRelative appended to what ever is in pszCombined,
750 * (which may the string "file:///"
752 strcatW(preliminary, mrelative);
753 break;
755 case 2: /* case where pszRelative replaces scheme, and location */
756 strcpyW(preliminary, mrelative);
757 break;
759 case 3: /*
760 * Return the pszBase scheme with pszRelative. Basically
761 * keeps the scheme and replaces the domain and following.
763 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
764 work = preliminary + base.cchProtocol + 1;
765 strcpyW(work, relative.pszSuffix);
766 break;
768 case 4: /*
769 * Return the pszBase scheme and location but everything
770 * after the location is pszRelative. (Replace document
771 * from root on.)
773 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
774 work = preliminary + base.cchProtocol + 1 + sizeloc;
775 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
776 *(work++) = '/';
777 strcpyW(work, relative.pszSuffix);
778 break;
780 case 5: /*
781 * Return the pszBase without its document (if any) and
782 * append pszRelative after its scheme.
784 memcpy(preliminary, base.pszProtocol,
785 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
786 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
787 if (*work++ != '/')
788 *(work++) = '/';
789 strcpyW(work, relative.pszSuffix);
790 break;
792 default:
793 FIXME("How did we get here????? process_case=%d\n", process_case);
794 ret = E_INVALIDARG;
797 if (ret == S_OK) {
798 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
799 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
800 if(SUCCEEDED(ret) && pszCombined) {
801 lstrcpyW(pszCombined, mrelative);
803 TRACE("return-%d len=%d, %s\n",
804 process_case, *pcchCombined, debugstr_w(pszCombined));
806 HeapFree(GetProcessHeap(), 0, preliminary);
807 return ret;
810 /*************************************************************************
811 * UrlEscapeA [SHLWAPI.@]
814 HRESULT WINAPI UrlEscapeA(
815 LPCSTR pszUrl,
816 LPSTR pszEscaped,
817 LPDWORD pcchEscaped,
818 DWORD dwFlags)
820 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
821 WCHAR *escapedW = bufW;
822 UNICODE_STRING urlW;
823 HRESULT ret;
824 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
826 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
827 return E_INVALIDARG;
829 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
830 return E_INVALIDARG;
831 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
832 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
833 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
835 if(ret == S_OK) {
836 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
837 if(*pcchEscaped > lenA) {
838 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
839 pszEscaped[lenA] = 0;
840 *pcchEscaped = lenA;
841 } else {
842 *pcchEscaped = lenA + 1;
843 ret = E_POINTER;
846 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
847 RtlFreeUnicodeString(&urlW);
848 return ret;
851 #define WINE_URL_BASH_AS_SLASH 0x01
852 #define WINE_URL_COLLAPSE_SLASHES 0x02
853 #define WINE_URL_ESCAPE_SLASH 0x04
854 #define WINE_URL_ESCAPE_HASH 0x08
855 #define WINE_URL_ESCAPE_QUESTION 0x10
856 #define WINE_URL_STOP_ON_HASH 0x20
857 #define WINE_URL_STOP_ON_QUESTION 0x40
859 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
862 if (isalnumW(ch))
863 return FALSE;
865 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
866 if(ch == ' ')
867 return TRUE;
868 else
869 return FALSE;
872 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
873 return TRUE;
875 if (ch <= 31 || ch >= 127)
876 return TRUE;
878 else {
879 switch (ch) {
880 case ' ':
881 case '<':
882 case '>':
883 case '\"':
884 case '{':
885 case '}':
886 case '|':
887 case '\\':
888 case '^':
889 case ']':
890 case '[':
891 case '`':
892 case '&':
893 return TRUE;
895 case '/':
896 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
897 return FALSE;
899 case '?':
900 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
901 return FALSE;
903 case '#':
904 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
905 return FALSE;
907 default:
908 return FALSE;
914 /*************************************************************************
915 * UrlEscapeW [SHLWAPI.@]
917 * Converts unsafe characters in a Url into escape sequences.
919 * PARAMS
920 * pszUrl [I] Url to modify
921 * pszEscaped [O] Destination for modified Url
922 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
923 * dwFlags [I] URL_ flags from "shlwapi.h"
925 * RETURNS
926 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
927 * contains its length.
928 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
929 * pcchEscaped is set to the required length.
931 * Converts unsafe characters into their escape sequences.
933 * NOTES
934 * - By default this function stops converting at the first '?' or
935 * '#' character.
936 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
937 * converted, but the conversion continues past a '?' or '#'.
938 * - Note that this function did not work well (or at all) in shlwapi version 4.
940 * BUGS
941 * Only the following flags are implemented:
942 *| URL_ESCAPE_SPACES_ONLY
943 *| URL_DONT_ESCAPE_EXTRA_INFO
944 *| URL_ESCAPE_SEGMENT_ONLY
945 *| URL_ESCAPE_PERCENT
947 HRESULT WINAPI UrlEscapeW(
948 LPCWSTR pszUrl,
949 LPWSTR pszEscaped,
950 LPDWORD pcchEscaped,
951 DWORD dwFlags)
953 LPCWSTR src;
954 DWORD needed = 0, ret;
955 BOOL stop_escaping = FALSE;
956 WCHAR next[5], *dst = pszEscaped;
957 INT len;
958 PARSEDURLW parsed_url;
959 DWORD int_flags;
960 DWORD slashes = 0;
961 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
963 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
964 pcchEscaped, dwFlags);
966 if(!pszUrl || !pcchEscaped)
967 return E_INVALIDARG;
969 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
970 URL_ESCAPE_SEGMENT_ONLY |
971 URL_DONT_ESCAPE_EXTRA_INFO |
972 URL_ESCAPE_PERCENT))
973 FIXME("Unimplemented flags: %08x\n", dwFlags);
975 /* fix up flags */
976 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
977 /* if SPACES_ONLY specified, reset the other controls */
978 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
979 URL_ESCAPE_PERCENT |
980 URL_ESCAPE_SEGMENT_ONLY);
982 else
983 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
984 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
987 int_flags = 0;
988 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
989 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
990 } else {
991 parsed_url.cbSize = sizeof(parsed_url);
992 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
993 parsed_url.nScheme = URL_SCHEME_INVALID;
995 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
997 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
998 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1000 switch(parsed_url.nScheme) {
1001 case URL_SCHEME_FILE:
1002 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1003 int_flags &= ~WINE_URL_STOP_ON_HASH;
1004 break;
1006 case URL_SCHEME_HTTP:
1007 case URL_SCHEME_HTTPS:
1008 int_flags |= WINE_URL_BASH_AS_SLASH;
1009 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1010 int_flags |= WINE_URL_ESCAPE_SLASH;
1011 break;
1013 case URL_SCHEME_MAILTO:
1014 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1015 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1016 break;
1018 case URL_SCHEME_INVALID:
1019 break;
1021 case URL_SCHEME_FTP:
1022 default:
1023 if(parsed_url.pszSuffix[0] != '/')
1024 int_flags |= WINE_URL_ESCAPE_SLASH;
1025 break;
1029 for(src = pszUrl; *src; ) {
1030 WCHAR cur = *src;
1031 len = 0;
1033 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1034 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1035 while(cur == '/' || cur == '\\') {
1036 slashes++;
1037 cur = *++src;
1039 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1040 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1041 src += localhost_len + 1;
1042 slashes = 3;
1045 switch(slashes) {
1046 case 1:
1047 case 3:
1048 next[0] = next[1] = next[2] = '/';
1049 len = 3;
1050 break;
1051 case 0:
1052 len = 0;
1053 break;
1054 default:
1055 next[0] = next[1] = '/';
1056 len = 2;
1057 break;
1060 if(len == 0) {
1062 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1063 stop_escaping = TRUE;
1065 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1066 stop_escaping = TRUE;
1068 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1070 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1071 next[0] = '%';
1072 next[1] = hexDigits[(cur >> 4) & 0xf];
1073 next[2] = hexDigits[cur & 0xf];
1074 len = 3;
1075 } else {
1076 next[0] = cur;
1077 len = 1;
1079 src++;
1082 if(needed + len <= *pcchEscaped) {
1083 memcpy(dst, next, len*sizeof(WCHAR));
1084 dst += len;
1086 needed += len;
1089 if(needed < *pcchEscaped) {
1090 *dst = '\0';
1091 ret = S_OK;
1092 } else {
1093 needed++; /* add one for the '\0' */
1094 ret = E_POINTER;
1096 *pcchEscaped = needed;
1097 return ret;
1101 /*************************************************************************
1102 * UrlUnescapeA [SHLWAPI.@]
1104 * Converts Url escape sequences back to ordinary characters.
1106 * PARAMS
1107 * pszUrl [I/O] Url to convert
1108 * pszUnescaped [O] Destination for converted Url
1109 * pcchUnescaped [I/O] Size of output string
1110 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1112 * RETURNS
1113 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1114 * dwFlags includes URL_ESCAPE_INPLACE.
1115 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1116 * this case pcchUnescaped is set to the size required.
1117 * NOTES
1118 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1119 * the first occurrence of either a '?' or '#' character.
1121 HRESULT WINAPI UrlUnescapeA(
1122 LPSTR pszUrl,
1123 LPSTR pszUnescaped,
1124 LPDWORD pcchUnescaped,
1125 DWORD dwFlags)
1127 char *dst, next;
1128 LPCSTR src;
1129 HRESULT ret;
1130 DWORD needed;
1131 BOOL stop_unescaping = FALSE;
1133 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1134 pcchUnescaped, dwFlags);
1136 if (!pszUrl) return E_INVALIDARG;
1138 if(dwFlags & URL_UNESCAPE_INPLACE)
1139 dst = pszUrl;
1140 else
1142 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1143 dst = pszUnescaped;
1146 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1147 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1148 (*src == '#' || *src == '?')) {
1149 stop_unescaping = TRUE;
1150 next = *src;
1151 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1152 && stop_unescaping == FALSE) {
1153 INT ih;
1154 char buf[3];
1155 memcpy(buf, src + 1, 2);
1156 buf[2] = '\0';
1157 ih = strtol(buf, NULL, 16);
1158 next = (CHAR) ih;
1159 src += 2; /* Advance to end of escape */
1160 } else
1161 next = *src;
1163 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1164 *dst++ = next;
1167 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1168 *dst = '\0';
1169 ret = S_OK;
1170 } else {
1171 needed++; /* add one for the '\0' */
1172 ret = E_POINTER;
1174 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1175 *pcchUnescaped = needed;
1177 if (ret == S_OK) {
1178 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1179 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1182 return ret;
1185 /*************************************************************************
1186 * UrlUnescapeW [SHLWAPI.@]
1188 * See UrlUnescapeA.
1190 HRESULT WINAPI UrlUnescapeW(
1191 LPWSTR pszUrl,
1192 LPWSTR pszUnescaped,
1193 LPDWORD pcchUnescaped,
1194 DWORD dwFlags)
1196 WCHAR *dst, next;
1197 LPCWSTR src;
1198 HRESULT ret;
1199 DWORD needed;
1200 BOOL stop_unescaping = FALSE;
1202 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1203 pcchUnescaped, dwFlags);
1205 if(!pszUrl) return E_INVALIDARG;
1207 if(dwFlags & URL_UNESCAPE_INPLACE)
1208 dst = pszUrl;
1209 else
1211 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1212 dst = pszUnescaped;
1215 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1216 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1217 (*src == '#' || *src == '?')) {
1218 stop_unescaping = TRUE;
1219 next = *src;
1220 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1221 && stop_unescaping == FALSE) {
1222 INT ih;
1223 WCHAR buf[5] = {'0','x',0};
1224 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1225 buf[4] = 0;
1226 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1227 next = (WCHAR) ih;
1228 src += 2; /* Advance to end of escape */
1229 } else
1230 next = *src;
1232 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1233 *dst++ = next;
1236 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1237 *dst = '\0';
1238 ret = S_OK;
1239 } else {
1240 needed++; /* add one for the '\0' */
1241 ret = E_POINTER;
1243 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1244 *pcchUnescaped = needed;
1246 if (ret == S_OK) {
1247 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1248 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1251 return ret;
1254 /*************************************************************************
1255 * UrlGetLocationA [SHLWAPI.@]
1257 * Get the location from a Url.
1259 * PARAMS
1260 * pszUrl [I] Url to get the location from
1262 * RETURNS
1263 * A pointer to the start of the location in pszUrl, or NULL if there is
1264 * no location.
1266 * NOTES
1267 * - MSDN erroneously states that "The location is the segment of the Url
1268 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1269 * stop at '?' and always return a NULL in this case.
1270 * - MSDN also erroneously states that "If a file URL has a query string,
1271 * the returned string is the query string". In all tested cases, if the
1272 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1273 *| Result Url
1274 *| ------ ---
1275 *| NULL file://aa/b/cd#hohoh
1276 *| #hohoh http://aa/b/cd#hohoh
1277 *| NULL fi://aa/b/cd#hohoh
1278 *| #hohoh ff://aa/b/cd#hohoh
1280 LPCSTR WINAPI UrlGetLocationA(
1281 LPCSTR pszUrl)
1283 PARSEDURLA base;
1284 DWORD res1;
1286 base.cbSize = sizeof(base);
1287 res1 = ParseURLA(pszUrl, &base);
1288 if (res1) return NULL; /* invalid scheme */
1290 /* if scheme is file: then never return pointer */
1291 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1293 /* Look for '#' and return its addr */
1294 return strchr(base.pszSuffix, '#');
1297 /*************************************************************************
1298 * UrlGetLocationW [SHLWAPI.@]
1300 * See UrlGetLocationA.
1302 LPCWSTR WINAPI UrlGetLocationW(
1303 LPCWSTR pszUrl)
1305 PARSEDURLW base;
1306 DWORD res1;
1308 base.cbSize = sizeof(base);
1309 res1 = ParseURLW(pszUrl, &base);
1310 if (res1) return NULL; /* invalid scheme */
1312 /* if scheme is file: then never return pointer */
1313 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1315 /* Look for '#' and return its addr */
1316 return strchrW(base.pszSuffix, '#');
1319 /*************************************************************************
1320 * UrlCompareA [SHLWAPI.@]
1322 * Compare two Urls.
1324 * PARAMS
1325 * pszUrl1 [I] First Url to compare
1326 * pszUrl2 [I] Url to compare to pszUrl1
1327 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1329 * RETURNS
1330 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1331 * than, equal to, or less than pszUrl1 respectively.
1333 INT WINAPI UrlCompareA(
1334 LPCSTR pszUrl1,
1335 LPCSTR pszUrl2,
1336 BOOL fIgnoreSlash)
1338 INT ret, len, len1, len2;
1340 if (!fIgnoreSlash)
1341 return strcmp(pszUrl1, pszUrl2);
1342 len1 = strlen(pszUrl1);
1343 if (pszUrl1[len1-1] == '/') len1--;
1344 len2 = strlen(pszUrl2);
1345 if (pszUrl2[len2-1] == '/') len2--;
1346 if (len1 == len2)
1347 return strncmp(pszUrl1, pszUrl2, len1);
1348 len = min(len1, len2);
1349 ret = strncmp(pszUrl1, pszUrl2, len);
1350 if (ret) return ret;
1351 if (len1 > len2) return 1;
1352 return -1;
1355 /*************************************************************************
1356 * UrlCompareW [SHLWAPI.@]
1358 * See UrlCompareA.
1360 INT WINAPI UrlCompareW(
1361 LPCWSTR pszUrl1,
1362 LPCWSTR pszUrl2,
1363 BOOL fIgnoreSlash)
1365 INT ret;
1366 size_t len, len1, len2;
1368 if (!fIgnoreSlash)
1369 return strcmpW(pszUrl1, pszUrl2);
1370 len1 = strlenW(pszUrl1);
1371 if (pszUrl1[len1-1] == '/') len1--;
1372 len2 = strlenW(pszUrl2);
1373 if (pszUrl2[len2-1] == '/') len2--;
1374 if (len1 == len2)
1375 return strncmpW(pszUrl1, pszUrl2, len1);
1376 len = min(len1, len2);
1377 ret = strncmpW(pszUrl1, pszUrl2, len);
1378 if (ret) return ret;
1379 if (len1 > len2) return 1;
1380 return -1;
1383 /*************************************************************************
1384 * HashData [SHLWAPI.@]
1386 * Hash an input block into a variable sized digest.
1388 * PARAMS
1389 * lpSrc [I] Input block
1390 * nSrcLen [I] Length of lpSrc
1391 * lpDest [I] Output for hash digest
1392 * nDestLen [I] Length of lpDest
1394 * RETURNS
1395 * Success: TRUE. lpDest is filled with the computed hash value.
1396 * Failure: FALSE, if any argument is invalid.
1398 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1399 unsigned char *lpDest, DWORD nDestLen)
1401 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1403 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1404 IsBadWritePtr(lpDest, nDestLen))
1405 return E_INVALIDARG;
1407 while (destCount >= 0)
1409 lpDest[destCount] = (destCount & 0xff);
1410 destCount--;
1413 while (srcCount >= 0)
1415 destCount = nDestLen - 1;
1416 while (destCount >= 0)
1418 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1419 destCount--;
1421 srcCount--;
1423 return S_OK;
1426 /*************************************************************************
1427 * UrlHashA [SHLWAPI.@]
1429 * Produce a Hash from a Url.
1431 * PARAMS
1432 * pszUrl [I] Url to hash
1433 * lpDest [O] Destinationh for hash
1434 * nDestLen [I] Length of lpDest
1436 * RETURNS
1437 * Success: S_OK. lpDest is filled with the computed hash value.
1438 * Failure: E_INVALIDARG, if any argument is invalid.
1440 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1442 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1443 return E_INVALIDARG;
1445 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1446 return S_OK;
1449 /*************************************************************************
1450 * UrlHashW [SHLWAPI.@]
1452 * See UrlHashA.
1454 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1456 char szUrl[MAX_PATH];
1458 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1460 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1461 return E_INVALIDARG;
1463 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1464 * return the same digests for the same URL.
1466 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1467 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1468 return S_OK;
1471 /*************************************************************************
1472 * UrlApplySchemeA [SHLWAPI.@]
1474 * Apply a scheme to a Url.
1476 * PARAMS
1477 * pszIn [I] Url to apply scheme to
1478 * pszOut [O] Destination for modified Url
1479 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1480 * dwFlags [I] URL_ flags from "shlwapi.h"
1482 * RETURNS
1483 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1484 * Failure: An HRESULT error code describing the error.
1486 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1488 LPWSTR in, out;
1489 HRESULT ret;
1490 DWORD len;
1492 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1493 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1495 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1497 in = HeapAlloc(GetProcessHeap(), 0,
1498 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1499 out = in + INTERNET_MAX_URL_LENGTH;
1501 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1502 len = INTERNET_MAX_URL_LENGTH;
1504 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1505 if (ret != S_OK) {
1506 HeapFree(GetProcessHeap(), 0, in);
1507 return ret;
1510 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1511 if (len > *pcchOut) {
1512 ret = E_POINTER;
1513 goto cleanup;
1516 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1517 len--;
1519 cleanup:
1520 *pcchOut = len;
1521 HeapFree(GetProcessHeap(), 0, in);
1522 return ret;
1525 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1527 HKEY newkey;
1528 BOOL j;
1529 INT index;
1530 DWORD value_len, data_len, dwType, i;
1531 WCHAR reg_path[MAX_PATH];
1532 WCHAR value[MAX_PATH], data[MAX_PATH];
1533 WCHAR Wxx, Wyy;
1535 MultiByteToWideChar(0, 0,
1536 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1537 -1, reg_path, MAX_PATH);
1538 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1539 index = 0;
1540 while(value_len = data_len = MAX_PATH,
1541 RegEnumValueW(newkey, index, value, &value_len,
1542 0, &dwType, (LPVOID)data, &data_len) == 0) {
1543 TRACE("guess %d %s is %s\n",
1544 index, debugstr_w(value), debugstr_w(data));
1546 j = FALSE;
1547 for(i=0; i<value_len; i++) {
1548 Wxx = pszIn[i];
1549 Wyy = value[i];
1550 /* remember that TRUE is not-equal */
1551 j = ChrCmpIW(Wxx, Wyy);
1552 if (j) break;
1554 if ((i == value_len) && !j) {
1555 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1556 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1557 RegCloseKey(newkey);
1558 return E_POINTER;
1560 strcpyW(pszOut, data);
1561 strcatW(pszOut, pszIn);
1562 *pcchOut = strlenW(pszOut);
1563 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1564 RegCloseKey(newkey);
1565 return S_OK;
1567 index++;
1569 RegCloseKey(newkey);
1570 return E_FAIL;
1573 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1575 HKEY newkey;
1576 DWORD data_len, dwType;
1577 WCHAR data[MAX_PATH];
1579 static const WCHAR prefix_keyW[] =
1580 {'S','o','f','t','w','a','r','e',
1581 '\\','M','i','c','r','o','s','o','f','t',
1582 '\\','W','i','n','d','o','w','s',
1583 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1584 '\\','U','R','L',
1585 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1587 /* get and prepend default */
1588 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1589 data_len = sizeof(data);
1590 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1591 RegCloseKey(newkey);
1592 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1593 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1594 return E_POINTER;
1596 strcpyW(pszOut, data);
1597 strcatW(pszOut, pszIn);
1598 *pcchOut = strlenW(pszOut);
1599 TRACE("used default %s\n", debugstr_w(pszOut));
1600 return S_OK;
1603 /*************************************************************************
1604 * UrlApplySchemeW [SHLWAPI.@]
1606 * See UrlApplySchemeA.
1608 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1610 PARSEDURLW in_scheme;
1611 DWORD res1;
1612 HRESULT ret;
1614 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1615 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1617 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1619 if (dwFlags & URL_APPLY_GUESSFILE) {
1620 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1621 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1622 strcpyW(pszOut, pszIn);
1623 *pcchOut = strlenW(pszOut);
1624 return S_FALSE;
1627 in_scheme.cbSize = sizeof(in_scheme);
1628 /* See if the base has a scheme */
1629 res1 = ParseURLW(pszIn, &in_scheme);
1630 if (res1) {
1631 /* no scheme in input, need to see if we need to guess */
1632 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1633 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1634 return ret;
1637 else {
1638 /* we have a scheme, see if valid (known scheme) */
1639 if (in_scheme.nScheme) {
1640 /* have valid scheme, so just copy and exit */
1641 if (strlenW(pszIn) + 1 > *pcchOut) {
1642 *pcchOut = strlenW(pszIn) + 1;
1643 return E_POINTER;
1645 strcpyW(pszOut, pszIn);
1646 *pcchOut = strlenW(pszOut);
1647 TRACE("valid scheme, returning copy\n");
1648 return S_OK;
1652 /* If we are here, then either invalid scheme,
1653 * or no scheme and can't/failed guess.
1655 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1656 ((res1 != 0)) ) &&
1657 (dwFlags & URL_APPLY_DEFAULT)) {
1658 /* find and apply default scheme */
1659 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1662 return S_FALSE;
1665 /*************************************************************************
1666 * UrlIsA [SHLWAPI.@]
1668 * Determine if a Url is of a certain class.
1670 * PARAMS
1671 * pszUrl [I] Url to check
1672 * Urlis [I] URLIS_ constant from "shlwapi.h"
1674 * RETURNS
1675 * TRUE if pszUrl belongs to the class type in Urlis.
1676 * FALSE Otherwise.
1678 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1680 PARSEDURLA base;
1681 DWORD res1;
1682 LPCSTR last;
1684 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1686 switch (Urlis) {
1688 case URLIS_OPAQUE:
1689 base.cbSize = sizeof(base);
1690 res1 = ParseURLA(pszUrl, &base);
1691 if (res1) return FALSE; /* invalid scheme */
1692 switch (base.nScheme)
1694 case URL_SCHEME_MAILTO:
1695 case URL_SCHEME_SHELL:
1696 case URL_SCHEME_JAVASCRIPT:
1697 case URL_SCHEME_VBSCRIPT:
1698 case URL_SCHEME_ABOUT:
1699 return TRUE;
1701 return FALSE;
1703 case URLIS_FILEURL:
1704 return !StrCmpNA("file:", pszUrl, 5);
1706 case URLIS_DIRECTORY:
1707 last = pszUrl + strlen(pszUrl) - 1;
1708 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1710 case URLIS_URL:
1711 return PathIsURLA(pszUrl);
1713 case URLIS_NOHISTORY:
1714 case URLIS_APPLIABLE:
1715 case URLIS_HASQUERY:
1716 default:
1717 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1719 return FALSE;
1722 /*************************************************************************
1723 * UrlIsW [SHLWAPI.@]
1725 * See UrlIsA.
1727 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1729 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1730 PARSEDURLW base;
1731 DWORD res1;
1732 LPCWSTR last;
1734 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1736 switch (Urlis) {
1738 case URLIS_OPAQUE:
1739 base.cbSize = sizeof(base);
1740 res1 = ParseURLW(pszUrl, &base);
1741 if (res1) return FALSE; /* invalid scheme */
1742 switch (base.nScheme)
1744 case URL_SCHEME_MAILTO:
1745 case URL_SCHEME_SHELL:
1746 case URL_SCHEME_JAVASCRIPT:
1747 case URL_SCHEME_VBSCRIPT:
1748 case URL_SCHEME_ABOUT:
1749 return TRUE;
1751 return FALSE;
1753 case URLIS_FILEURL:
1754 return !strncmpW(stemp, pszUrl, 5);
1756 case URLIS_DIRECTORY:
1757 last = pszUrl + strlenW(pszUrl) - 1;
1758 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1760 case URLIS_URL:
1761 return PathIsURLW(pszUrl);
1763 case URLIS_NOHISTORY:
1764 case URLIS_APPLIABLE:
1765 case URLIS_HASQUERY:
1766 default:
1767 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1769 return FALSE;
1772 /*************************************************************************
1773 * UrlIsNoHistoryA [SHLWAPI.@]
1775 * Determine if a Url should not be stored in the users history list.
1777 * PARAMS
1778 * pszUrl [I] Url to check
1780 * RETURNS
1781 * TRUE, if pszUrl should be excluded from the history list,
1782 * FALSE otherwise.
1784 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1786 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1789 /*************************************************************************
1790 * UrlIsNoHistoryW [SHLWAPI.@]
1792 * See UrlIsNoHistoryA.
1794 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1796 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1799 /*************************************************************************
1800 * UrlIsOpaqueA [SHLWAPI.@]
1802 * Determine if a Url is opaque.
1804 * PARAMS
1805 * pszUrl [I] Url to check
1807 * RETURNS
1808 * TRUE if pszUrl is opaque,
1809 * FALSE Otherwise.
1811 * NOTES
1812 * An opaque Url is one that does not start with "<protocol>://".
1814 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1816 return UrlIsA(pszUrl, URLIS_OPAQUE);
1819 /*************************************************************************
1820 * UrlIsOpaqueW [SHLWAPI.@]
1822 * See UrlIsOpaqueA.
1824 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1826 return UrlIsW(pszUrl, URLIS_OPAQUE);
1829 /*************************************************************************
1830 * Scans for characters of type "type" and when not matching found,
1831 * returns pointer to it and length in size.
1833 * Characters tested based on RFC 1738
1835 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1837 static DWORD alwayszero = 0;
1838 BOOL cont = TRUE;
1840 *size = 0;
1842 switch(type){
1844 case SCHEME:
1845 while (cont) {
1846 if ( (islowerW(*start) && isalphaW(*start)) ||
1847 isdigitW(*start) ||
1848 (*start == '+') ||
1849 (*start == '-') ||
1850 (*start == '.')) {
1851 start++;
1852 (*size)++;
1854 else
1855 cont = FALSE;
1857 break;
1859 case USERPASS:
1860 while (cont) {
1861 if ( isalphaW(*start) ||
1862 isdigitW(*start) ||
1863 /* user/password only characters */
1864 (*start == ';') ||
1865 (*start == '?') ||
1866 (*start == '&') ||
1867 (*start == '=') ||
1868 /* *extra* characters */
1869 (*start == '!') ||
1870 (*start == '*') ||
1871 (*start == '\'') ||
1872 (*start == '(') ||
1873 (*start == ')') ||
1874 (*start == ',') ||
1875 /* *safe* characters */
1876 (*start == '$') ||
1877 (*start == '_') ||
1878 (*start == '+') ||
1879 (*start == '-') ||
1880 (*start == '.') ||
1881 (*start == ' ')) {
1882 start++;
1883 (*size)++;
1884 } else if (*start == '%') {
1885 if (isxdigitW(*(start+1)) &&
1886 isxdigitW(*(start+2))) {
1887 start += 3;
1888 *size += 3;
1889 } else
1890 cont = FALSE;
1891 } else
1892 cont = FALSE;
1894 break;
1896 case PORT:
1897 while (cont) {
1898 if (isdigitW(*start)) {
1899 start++;
1900 (*size)++;
1902 else
1903 cont = FALSE;
1905 break;
1907 case HOST:
1908 while (cont) {
1909 if (isalnumW(*start) ||
1910 (*start == '-') ||
1911 (*start == '.') ||
1912 (*start == ' ') ) {
1913 start++;
1914 (*size)++;
1916 else
1917 cont = FALSE;
1919 break;
1920 default:
1921 FIXME("unknown type %d\n", type);
1922 return (LPWSTR)&alwayszero;
1924 /* TRACE("scanned %d characters next char %p<%c>\n",
1925 *size, start, *start); */
1926 return start;
1929 /*************************************************************************
1930 * Attempt to parse URL into pieces.
1932 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1934 LPCWSTR work;
1936 memset(pl, 0, sizeof(WINE_PARSE_URL));
1937 pl->pScheme = pszUrl;
1938 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1939 if (!*work || (*work != ':')) goto ErrorExit;
1940 work++;
1941 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1942 pl->pUserName = work + 2;
1943 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1944 if (*work == ':' ) {
1945 /* parse password */
1946 work++;
1947 pl->pPassword = work;
1948 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1949 if (*work != '@') {
1950 /* what we just parsed must be the hostname and port
1951 * so reset pointers and clear then let it parse */
1952 pl->szUserName = pl->szPassword = 0;
1953 work = pl->pUserName - 1;
1954 pl->pUserName = pl->pPassword = 0;
1956 } else if (*work == '@') {
1957 /* no password */
1958 pl->szPassword = 0;
1959 pl->pPassword = 0;
1960 } else if (!*work || (*work == '/') || (*work == '.')) {
1961 /* what was parsed was hostname, so reset pointers and let it parse */
1962 pl->szUserName = pl->szPassword = 0;
1963 work = pl->pUserName - 1;
1964 pl->pUserName = pl->pPassword = 0;
1965 } else goto ErrorExit;
1967 /* now start parsing hostname or hostnumber */
1968 work++;
1969 pl->pHostName = work;
1970 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1971 if (*work == ':') {
1972 /* parse port */
1973 work++;
1974 pl->pPort = work;
1975 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1977 if (*work == '/') {
1978 /* see if query string */
1979 pl->pQuery = strchrW(work, '?');
1980 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1982 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1983 pl->pScheme, pl->szScheme,
1984 pl->pUserName, pl->szUserName,
1985 pl->pPassword, pl->szPassword,
1986 pl->pHostName, pl->szHostName,
1987 pl->pPort, pl->szPort,
1988 pl->pQuery, pl->szQuery);
1989 return S_OK;
1990 ErrorExit:
1991 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1992 return E_INVALIDARG;
1995 /*************************************************************************
1996 * UrlGetPartA [SHLWAPI.@]
1998 * Retrieve part of a Url.
2000 * PARAMS
2001 * pszIn [I] Url to parse
2002 * pszOut [O] Destination for part of pszIn requested
2003 * pcchOut [I] Size of pszOut
2004 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2005 * needed size of pszOut INCLUDING '\0'.
2006 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2007 * dwFlags [I] URL_ flags from "shlwapi.h"
2009 * RETURNS
2010 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2011 * Failure: An HRESULT error code describing the error.
2013 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2014 DWORD dwPart, DWORD dwFlags)
2016 LPWSTR in, out;
2017 DWORD ret, len, len2;
2019 in = HeapAlloc(GetProcessHeap(), 0,
2020 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2021 out = in + INTERNET_MAX_URL_LENGTH;
2023 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2025 len = INTERNET_MAX_URL_LENGTH;
2026 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2028 if (FAILED(ret)) {
2029 HeapFree(GetProcessHeap(), 0, in);
2030 return ret;
2033 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2034 if (len2 > *pcchOut) {
2035 *pcchOut = len2;
2036 HeapFree(GetProcessHeap(), 0, in);
2037 return E_POINTER;
2039 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2040 *pcchOut = len2-1;
2041 HeapFree(GetProcessHeap(), 0, in);
2042 return ret;
2045 /*************************************************************************
2046 * UrlGetPartW [SHLWAPI.@]
2048 * See UrlGetPartA.
2050 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2051 DWORD dwPart, DWORD dwFlags)
2053 WINE_PARSE_URL pl;
2054 HRESULT ret;
2055 DWORD scheme, size, schsize;
2056 LPCWSTR addr, schaddr;
2058 TRACE("(%s %p %p(%d) %08x %08x)\n",
2059 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2061 addr = strchrW(pszIn, ':');
2062 if(!addr)
2063 return E_FAIL;
2065 scheme = get_scheme_code(pszIn, addr-pszIn);
2067 ret = URL_ParseUrl(pszIn, &pl);
2068 if (ret == S_OK) {
2069 schaddr = pl.pScheme;
2070 schsize = pl.szScheme;
2072 switch (dwPart) {
2073 case URL_PART_SCHEME:
2074 if (!pl.szScheme) return E_INVALIDARG;
2075 addr = pl.pScheme;
2076 size = pl.szScheme;
2077 break;
2079 case URL_PART_HOSTNAME:
2080 switch(scheme) {
2081 case URL_SCHEME_FTP:
2082 case URL_SCHEME_HTTP:
2083 case URL_SCHEME_GOPHER:
2084 case URL_SCHEME_TELNET:
2085 case URL_SCHEME_FILE:
2086 case URL_SCHEME_HTTPS:
2087 break;
2088 default:
2089 return E_FAIL;
2092 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2093 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2094 if(pcchOut)
2095 *pszOut = '\0';
2096 *pcchOut = 0;
2097 return S_FALSE;
2100 if (!pl.szHostName) return E_INVALIDARG;
2101 addr = pl.pHostName;
2102 size = pl.szHostName;
2103 break;
2105 case URL_PART_USERNAME:
2106 if (!pl.szUserName) return E_INVALIDARG;
2107 addr = pl.pUserName;
2108 size = pl.szUserName;
2109 break;
2111 case URL_PART_PASSWORD:
2112 if (!pl.szPassword) return E_INVALIDARG;
2113 addr = pl.pPassword;
2114 size = pl.szPassword;
2115 break;
2117 case URL_PART_PORT:
2118 if (!pl.szPort) return E_INVALIDARG;
2119 addr = pl.pPort;
2120 size = pl.szPort;
2121 break;
2123 case URL_PART_QUERY:
2124 if (!pl.szQuery) return E_INVALIDARG;
2125 addr = pl.pQuery;
2126 size = pl.szQuery;
2127 break;
2129 default:
2130 return E_INVALIDARG;
2133 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2134 if (*pcchOut < schsize + size + 2) {
2135 *pcchOut = schsize + size + 2;
2136 return E_POINTER;
2138 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2139 pszOut[schsize] = ':';
2140 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2141 pszOut[schsize+1+size] = 0;
2142 *pcchOut = schsize + 1 + size;
2144 else {
2145 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2146 memcpy(pszOut, addr, size*sizeof(WCHAR));
2147 pszOut[size] = 0;
2148 *pcchOut = size;
2150 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2151 }else if(dwPart==URL_PART_HOSTNAME && scheme==URL_SCHEME_FILE) {
2152 if(*pcchOut)
2153 *pszOut = '\0';
2154 *pcchOut = 0;
2155 return S_FALSE;
2158 return ret;
2161 /*************************************************************************
2162 * PathIsURLA [SHLWAPI.@]
2164 * Check if the given path is a Url.
2166 * PARAMS
2167 * lpszPath [I] Path to check.
2169 * RETURNS
2170 * TRUE if lpszPath is a Url.
2171 * FALSE if lpszPath is NULL or not a Url.
2173 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2175 PARSEDURLA base;
2176 HRESULT hres;
2178 TRACE("%s\n", debugstr_a(lpstrPath));
2180 if (!lpstrPath || !*lpstrPath) return FALSE;
2182 /* get protocol */
2183 base.cbSize = sizeof(base);
2184 hres = ParseURLA(lpstrPath, &base);
2185 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2188 /*************************************************************************
2189 * PathIsURLW [SHLWAPI.@]
2191 * See PathIsURLA.
2193 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2195 PARSEDURLW base;
2196 HRESULT hres;
2198 TRACE("%s\n", debugstr_w(lpstrPath));
2200 if (!lpstrPath || !*lpstrPath) return FALSE;
2202 /* get protocol */
2203 base.cbSize = sizeof(base);
2204 hres = ParseURLW(lpstrPath, &base);
2205 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2208 /*************************************************************************
2209 * UrlCreateFromPathA [SHLWAPI.@]
2211 * See UrlCreateFromPathW
2213 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2215 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2216 WCHAR *urlW = bufW;
2217 UNICODE_STRING pathW;
2218 HRESULT ret;
2219 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2221 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2222 return E_INVALIDARG;
2223 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2224 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2225 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2227 if(ret == S_OK || ret == S_FALSE) {
2228 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2229 if(*pcchUrl > lenA) {
2230 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2231 pszUrl[lenA] = 0;
2232 *pcchUrl = lenA;
2233 } else {
2234 *pcchUrl = lenA + 1;
2235 ret = E_POINTER;
2238 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2239 RtlFreeUnicodeString(&pathW);
2240 return ret;
2243 /*************************************************************************
2244 * UrlCreateFromPathW [SHLWAPI.@]
2246 * Create a Url from a file path.
2248 * PARAMS
2249 * pszPath [I] Path to convert
2250 * pszUrl [O] Destination for the converted Url
2251 * pcchUrl [I/O] Length of pszUrl
2252 * dwReserved [I] Reserved, must be 0
2254 * RETURNS
2255 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2256 * Failure: An HRESULT error code.
2258 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2260 DWORD needed;
2261 HRESULT ret;
2262 WCHAR *pszNewUrl;
2263 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2264 WCHAR three_slashesW[] = {'/','/','/',0};
2265 PARSEDURLW parsed_url;
2267 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2269 /* Validate arguments */
2270 if (dwReserved != 0)
2271 return E_INVALIDARG;
2272 if (!pszUrl || !pcchUrl)
2273 return E_INVALIDARG;
2276 parsed_url.cbSize = sizeof(parsed_url);
2277 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2278 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2279 needed = strlenW(pszPath);
2280 if (needed >= *pcchUrl) {
2281 *pcchUrl = needed + 1;
2282 return E_POINTER;
2283 } else {
2284 *pcchUrl = needed;
2285 strcpyW(pszUrl, pszPath);
2286 return S_FALSE;
2291 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2292 strcpyW(pszNewUrl, file_colonW);
2293 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2294 strcatW(pszNewUrl, three_slashesW);
2295 strcatW(pszNewUrl, pszPath);
2296 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2298 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2299 return ret;
2302 /*************************************************************************
2303 * SHAutoComplete [SHLWAPI.@]
2305 * Enable auto-completion for an edit control.
2307 * PARAMS
2308 * hwndEdit [I] Handle of control to enable auto-completion for
2309 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2311 * RETURNS
2312 * Success: S_OK. Auto-completion is enabled for the control.
2313 * Failure: An HRESULT error code indicating the error.
2315 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2317 FIXME("SHAutoComplete stub\n");
2318 return S_FALSE;
2321 /*************************************************************************
2322 * MLBuildResURLA [SHLWAPI.405]
2324 * Create a Url pointing to a resource in a module.
2326 * PARAMS
2327 * lpszLibName [I] Name of the module containing the resource
2328 * hMod [I] Callers module handle
2329 * dwFlags [I] Undocumented flags for loading the module
2330 * lpszRes [I] Resource name
2331 * lpszDest [O] Destination for resulting Url
2332 * dwDestLen [I] Length of lpszDest
2334 * RETURNS
2335 * Success: S_OK. lpszDest contains the resource Url.
2336 * Failure: E_INVALIDARG, if any argument is invalid, or
2337 * E_FAIL if dwDestLen is too small.
2339 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2340 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2342 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2343 HRESULT hRet;
2345 if (lpszLibName)
2346 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2348 if (lpszRes)
2349 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2351 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2352 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2354 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2355 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2356 if (SUCCEEDED(hRet) && lpszDest)
2357 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2359 return hRet;
2362 /*************************************************************************
2363 * MLBuildResURLA [SHLWAPI.406]
2365 * See MLBuildResURLA.
2367 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2368 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2370 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2371 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2372 HRESULT hRet = E_FAIL;
2374 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2375 debugstr_w(lpszRes), lpszDest, dwDestLen);
2377 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2378 !lpszDest || (dwFlags && dwFlags != 2))
2379 return E_INVALIDARG;
2381 if (dwDestLen >= szResLen + 1)
2383 dwDestLen -= (szResLen + 1);
2384 memcpy(lpszDest, szRes, sizeof(szRes));
2386 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2388 if (hMod)
2390 WCHAR szBuff[MAX_PATH];
2391 DWORD len;
2393 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2394 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2396 DWORD dwPathLen = strlenW(szBuff) + 1;
2398 if (dwDestLen >= dwPathLen)
2400 DWORD dwResLen;
2402 dwDestLen -= dwPathLen;
2403 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2405 dwResLen = strlenW(lpszRes) + 1;
2406 if (dwDestLen >= dwResLen + 1)
2408 lpszDest[szResLen + dwPathLen-1] = '/';
2409 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2410 hRet = S_OK;
2414 MLFreeLibrary(hMod);
2417 return hRet;
2420 /***********************************************************************
2421 * UrlFixupW [SHLWAPI.462]
2423 * Checks the scheme part of a URL and attempts to correct misspellings.
2425 * PARAMS
2426 * lpszUrl [I] Pointer to the URL to be corrected
2427 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2428 * dwMaxChars [I] Maximum size of corrected URL
2430 * RETURNS
2431 * success: S_OK if URL corrected or already correct
2432 * failure: S_FALSE if unable to correct / COM error code if other error
2435 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2437 DWORD srcLen;
2439 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2441 if (!url)
2442 return E_FAIL;
2444 srcLen = lstrlenW(url);
2446 /* For now just copy the URL directly */
2447 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2449 return S_OK;