msi: Add support for the Unicode version of the global UI handler.
[wine.git] / dlls / shlwapi / url.c
blob2b7a8cd168636272c2cbaf47fb1eb8584fbe0cd0
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 wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
288 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
289 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
291 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
292 return E_INVALIDARG;
294 if(!*pszUrl) {
295 *pszCanonicalized = 0;
296 return S_OK;
299 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
300 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
301 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
303 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
304 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
305 slash = '\\';
308 * state =
309 * 0 initial 1,3
310 * 1 have 2[+] alnum 2,3
311 * 2 have scheme (found :) 4,6,3
312 * 3 failed (no location)
313 * 4 have // 5,3
314 * 5 have 1[+] alnum 6,3
315 * 6 have location (found /) save root location
318 wk1 = (LPWSTR)pszUrl;
319 wk2 = lpszUrlCpy;
320 state = 0;
322 if(pszUrl[1] == ':') { /* Assume path */
323 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
325 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
326 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
327 if (dwFlags & URL_FILE_USE_PATHURL)
329 slash = '\\';
330 --wk2;
332 else
333 dwFlags |= URL_ESCAPE_UNSAFE;
334 state = 5;
337 while (*wk1) {
338 switch (state) {
339 case 0:
340 if (!isalnumW(*wk1)) {state = 3; break;}
341 *wk2++ = *wk1++;
342 if (!isalnumW(*wk1)) {state = 3; break;}
343 *wk2++ = *wk1++;
344 state = 1;
345 break;
346 case 1:
347 *wk2++ = *wk1;
348 if (*wk1++ == ':') state = 2;
349 break;
350 case 2:
351 *wk2++ = *wk1++;
352 if (*wk1 != '/') {state = 6; break;}
353 *wk2++ = *wk1++;
354 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
355 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
356 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
357 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
358 wk1++;
360 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
361 wk1++;
362 state = 4;
363 break;
364 case 3:
365 nWkLen = strlenW(wk1);
366 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
367 mp = wk2;
368 wk1 += nWkLen;
369 wk2 += nWkLen;
371 while(mp < wk2) {
372 if(*mp == '/' || *mp == '\\')
373 *mp = slash;
374 mp++;
376 break;
377 case 4:
378 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
379 {state = 3; break;}
380 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
381 *wk2++ = *wk1++;
382 state = 5;
383 if (!*wk1)
384 *wk2++ = slash;
385 break;
386 case 5:
387 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
388 while(*wk1 == '/' || *wk1 == '\\') {
389 *wk2++ = slash;
390 wk1++;
392 state = 6;
393 break;
394 case 6:
395 if(dwFlags & URL_DONT_SIMPLIFY) {
396 state = 3;
397 break;
400 /* Now at root location, cannot back up any more. */
401 /* "root" will point at the '/' */
403 root = wk2-1;
404 while (*wk1) {
405 mp = strchrW(wk1, '/');
406 mp2 = strchrW(wk1, '\\');
407 if(mp2 && (!mp || mp2 < mp))
408 mp = mp2;
409 if (!mp) {
410 nWkLen = strlenW(wk1);
411 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
412 wk1 += nWkLen;
413 wk2 += nWkLen;
414 continue;
416 nLen = mp - wk1;
417 if(nLen) {
418 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
419 wk2 += nLen;
420 wk1 += nLen;
422 *wk2++ = slash;
423 wk1++;
425 if (*wk1 == '.') {
426 TRACE("found '/.'\n");
427 if (wk1[1] == '/' || wk1[1] == '\\') {
428 /* case of /./ -> skip the ./ */
429 wk1 += 2;
431 else if (wk1[1] == '.') {
432 /* found /.. look for next / */
433 TRACE("found '/..'\n");
434 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
435 || wk1[2] == '#' || !wk1[2]) {
436 /* case /../ -> need to backup wk2 */
437 TRACE("found '/../'\n");
438 *(wk2-1) = '\0'; /* set end of string */
439 mp = strrchrW(root, slash);
440 if (mp && (mp >= root)) {
441 /* found valid backup point */
442 wk2 = mp + 1;
443 if(wk1[2] != '/' && wk1[2] != '\\')
444 wk1 += 2;
445 else
446 wk1 += 3;
448 else {
449 /* did not find point, restore '/' */
450 *(wk2-1) = slash;
456 *wk2 = '\0';
457 break;
458 default:
459 FIXME("how did we get here - state=%d\n", state);
460 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
461 return E_INVALIDARG;
463 *wk2 = '\0';
464 TRACE("Simplified, orig <%s>, simple <%s>\n",
465 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
467 nLen = lstrlenW(lpszUrlCpy);
468 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
469 lpszUrlCpy[--nLen]=0;
471 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
472 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
474 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
475 URL_ESCAPE_SPACES_ONLY |
476 URL_ESCAPE_PERCENT |
477 URL_DONT_ESCAPE_EXTRA_INFO |
478 URL_ESCAPE_SEGMENT_ONLY ))) {
479 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
480 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
481 EscapeFlags);
482 } else { /* No escaping needed, just copy the string */
483 nLen = lstrlenW(lpszUrlCpy);
484 if(nLen < *pcchCanonicalized)
485 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
486 else {
487 hr = E_POINTER;
488 nLen++;
490 *pcchCanonicalized = nLen;
493 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
495 if (hr == S_OK)
496 TRACE("result %s\n", debugstr_w(pszCanonicalized));
498 return hr;
501 /*************************************************************************
502 * UrlCombineA [SHLWAPI.@]
504 * Combine two Urls.
506 * PARAMS
507 * pszBase [I] Base Url
508 * pszRelative [I] Url to combine with pszBase
509 * pszCombined [O] Destination for combined Url
510 * pcchCombined [O] Destination for length of pszCombined
511 * dwFlags [I] URL_ flags from "shlwapi.h"
513 * RETURNS
514 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
515 * contains its length.
516 * Failure: An HRESULT error code indicating the error.
518 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
519 LPSTR pszCombined, LPDWORD pcchCombined,
520 DWORD dwFlags)
522 LPWSTR base, relative, combined;
523 DWORD ret, len, len2;
525 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
526 debugstr_a(pszBase),debugstr_a(pszRelative),
527 pcchCombined?*pcchCombined:0,dwFlags);
529 if(!pszBase || !pszRelative || !pcchCombined)
530 return E_INVALIDARG;
532 base = HeapAlloc(GetProcessHeap(), 0,
533 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
534 relative = base + INTERNET_MAX_URL_LENGTH;
535 combined = relative + INTERNET_MAX_URL_LENGTH;
537 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
538 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
539 len = *pcchCombined;
541 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
542 if (ret != S_OK) {
543 *pcchCombined = len;
544 HeapFree(GetProcessHeap(), 0, base);
545 return ret;
548 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
549 if (len2 > *pcchCombined) {
550 *pcchCombined = len2;
551 HeapFree(GetProcessHeap(), 0, base);
552 return E_POINTER;
554 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
555 0, 0);
556 *pcchCombined = len2;
557 HeapFree(GetProcessHeap(), 0, base);
558 return S_OK;
561 /*************************************************************************
562 * UrlCombineW [SHLWAPI.@]
564 * See UrlCombineA.
566 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
567 LPWSTR pszCombined, LPDWORD pcchCombined,
568 DWORD dwFlags)
570 PARSEDURLW base, relative;
571 DWORD myflags, sizeloc = 0;
572 DWORD len, res1, res2, process_case = 0;
573 LPWSTR work, preliminary, mbase, mrelative;
574 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
575 HRESULT ret;
577 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
578 debugstr_w(pszBase),debugstr_w(pszRelative),
579 pcchCombined?*pcchCombined:0,dwFlags);
581 if(!pszBase || !pszRelative || !pcchCombined)
582 return E_INVALIDARG;
584 base.cbSize = sizeof(base);
585 relative.cbSize = sizeof(relative);
587 /* Get space for duplicates of the input and the output */
588 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
589 sizeof(WCHAR));
590 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
591 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
592 *preliminary = '\0';
594 /* Canonicalize the base input prior to looking for the scheme */
595 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
596 len = INTERNET_MAX_URL_LENGTH;
597 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
599 /* Canonicalize the relative input prior to looking for the scheme */
600 len = INTERNET_MAX_URL_LENGTH;
601 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
603 /* See if the base has a scheme */
604 res1 = ParseURLW(mbase, &base);
605 if (res1) {
606 /* if pszBase has no scheme, then return pszRelative */
607 TRACE("no scheme detected in Base\n");
608 process_case = 1;
610 else do {
611 /* mk is a special case */
612 if(base.nScheme == URL_SCHEME_MK) {
613 static const WCHAR wsz[] = {':',':',0};
615 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
616 if(ptr) {
617 int delta;
619 ptr += 2;
620 delta = ptr-base.pszSuffix;
621 base.cchProtocol += delta;
622 base.pszSuffix += delta;
623 base.cchSuffix -= delta;
625 }else {
626 /* get size of location field (if it exists) */
627 work = (LPWSTR)base.pszSuffix;
628 sizeloc = 0;
629 if (*work++ == '/') {
630 if (*work++ == '/') {
631 /* At this point have start of location and
632 * it ends at next '/' or end of string.
634 while(*work && (*work != '/')) work++;
635 sizeloc = (DWORD)(work - base.pszSuffix);
640 /* Change .sizep2 to not have the last leaf in it,
641 * Note: we need to start after the location (if it exists)
643 work = strrchrW((base.pszSuffix+sizeloc), '/');
644 if (work) {
645 len = (DWORD)(work - base.pszSuffix + 1);
646 base.cchSuffix = len;
650 * At this point:
651 * .pszSuffix points to location (starting with '//')
652 * .cchSuffix length of location (above) and rest less the last
653 * leaf (if any)
654 * sizeloc length of location (above) up to but not including
655 * the last '/'
658 res2 = ParseURLW(mrelative, &relative);
659 if (res2) {
660 /* no scheme in pszRelative */
661 TRACE("no scheme detected in Relative\n");
662 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
663 relative.cchSuffix = strlenW(mrelative);
664 if (*pszRelative == ':') {
665 /* case that is either left alone or uses pszBase */
666 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
667 process_case = 5;
668 break;
670 process_case = 1;
671 break;
673 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
674 /* case that becomes "file:///" */
675 strcpyW(preliminary, myfilestr);
676 process_case = 1;
677 break;
679 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
680 /* pszRelative has location and rest */
681 process_case = 3;
682 break;
684 if (*mrelative == '/') {
685 /* case where pszRelative is root to location */
686 process_case = 4;
687 break;
689 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
690 break;
693 /* handle cases where pszRelative has scheme */
694 if ((base.cchProtocol == relative.cchProtocol) &&
695 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
697 /* since the schemes are the same */
698 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
699 /* case where pszRelative replaces location and following */
700 process_case = 3;
701 break;
703 if (*relative.pszSuffix == '/') {
704 /* case where pszRelative is root to location */
705 process_case = 4;
706 break;
708 /* replace either just location if base's location starts with a
709 * slash or otherwise everything */
710 process_case = (*base.pszSuffix == '/') ? 5 : 1;
711 break;
713 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
714 /* case where pszRelative replaces scheme, location,
715 * and following and handles PLUGGABLE
717 process_case = 2;
718 break;
720 process_case = 1;
721 break;
722 } while(FALSE); /* a little trick to allow easy exit from nested if's */
724 ret = S_OK;
725 switch (process_case) {
727 case 1: /*
728 * Return pszRelative appended to what ever is in pszCombined,
729 * (which may the string "file:///"
731 strcatW(preliminary, mrelative);
732 break;
734 case 2: /* case where pszRelative replaces scheme, and location */
735 strcpyW(preliminary, mrelative);
736 break;
738 case 3: /*
739 * Return the pszBase scheme with pszRelative. Basically
740 * keeps the scheme and replaces the domain and following.
742 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
743 work = preliminary + base.cchProtocol + 1;
744 strcpyW(work, relative.pszSuffix);
745 break;
747 case 4: /*
748 * Return the pszBase scheme and location but everything
749 * after the location is pszRelative. (Replace document
750 * from root on.)
752 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
753 work = preliminary + base.cchProtocol + 1 + sizeloc;
754 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
755 *(work++) = '/';
756 strcpyW(work, relative.pszSuffix);
757 break;
759 case 5: /*
760 * Return the pszBase without its document (if any) and
761 * append pszRelative after its scheme.
763 memcpy(preliminary, base.pszProtocol,
764 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
765 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
766 if (*work++ != '/')
767 *(work++) = '/';
768 strcpyW(work, relative.pszSuffix);
769 break;
771 default:
772 FIXME("How did we get here????? process_case=%d\n", process_case);
773 ret = E_INVALIDARG;
776 if (ret == S_OK) {
777 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
778 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
779 if(SUCCEEDED(ret) && pszCombined) {
780 lstrcpyW(pszCombined, mrelative);
782 TRACE("return-%d len=%d, %s\n",
783 process_case, *pcchCombined, debugstr_w(pszCombined));
785 HeapFree(GetProcessHeap(), 0, preliminary);
786 return ret;
789 /*************************************************************************
790 * UrlEscapeA [SHLWAPI.@]
793 HRESULT WINAPI UrlEscapeA(
794 LPCSTR pszUrl,
795 LPSTR pszEscaped,
796 LPDWORD pcchEscaped,
797 DWORD dwFlags)
799 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
800 WCHAR *escapedW = bufW;
801 UNICODE_STRING urlW;
802 HRESULT ret;
803 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
805 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
806 return E_INVALIDARG;
808 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
809 return E_INVALIDARG;
810 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
811 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
812 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
814 if(ret == S_OK) {
815 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
816 if(*pcchEscaped > lenA) {
817 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
818 pszEscaped[lenA] = 0;
819 *pcchEscaped = lenA;
820 } else {
821 *pcchEscaped = lenA + 1;
822 ret = E_POINTER;
825 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
826 RtlFreeUnicodeString(&urlW);
827 return ret;
830 #define WINE_URL_BASH_AS_SLASH 0x01
831 #define WINE_URL_COLLAPSE_SLASHES 0x02
832 #define WINE_URL_ESCAPE_SLASH 0x04
833 #define WINE_URL_ESCAPE_HASH 0x08
834 #define WINE_URL_ESCAPE_QUESTION 0x10
835 #define WINE_URL_STOP_ON_HASH 0x20
836 #define WINE_URL_STOP_ON_QUESTION 0x40
838 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
841 if (isalnumW(ch))
842 return FALSE;
844 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
845 if(ch == ' ')
846 return TRUE;
847 else
848 return FALSE;
851 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
852 return TRUE;
854 if (ch <= 31 || ch >= 127)
855 return TRUE;
857 else {
858 switch (ch) {
859 case ' ':
860 case '<':
861 case '>':
862 case '\"':
863 case '{':
864 case '}':
865 case '|':
866 case '\\':
867 case '^':
868 case ']':
869 case '[':
870 case '`':
871 case '&':
872 return TRUE;
874 case '/':
875 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
876 return FALSE;
878 case '?':
879 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
880 return FALSE;
882 case '#':
883 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
884 return FALSE;
886 default:
887 return FALSE;
893 /*************************************************************************
894 * UrlEscapeW [SHLWAPI.@]
896 * Converts unsafe characters in a Url into escape sequences.
898 * PARAMS
899 * pszUrl [I] Url to modify
900 * pszEscaped [O] Destination for modified Url
901 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
902 * dwFlags [I] URL_ flags from "shlwapi.h"
904 * RETURNS
905 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
906 * contains its length.
907 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
908 * pcchEscaped is set to the required length.
910 * Converts unsafe characters into their escape sequences.
912 * NOTES
913 * - By default this function stops converting at the first '?' or
914 * '#' character.
915 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
916 * converted, but the conversion continues past a '?' or '#'.
917 * - Note that this function did not work well (or at all) in shlwapi version 4.
919 * BUGS
920 * Only the following flags are implemented:
921 *| URL_ESCAPE_SPACES_ONLY
922 *| URL_DONT_ESCAPE_EXTRA_INFO
923 *| URL_ESCAPE_SEGMENT_ONLY
924 *| URL_ESCAPE_PERCENT
926 HRESULT WINAPI UrlEscapeW(
927 LPCWSTR pszUrl,
928 LPWSTR pszEscaped,
929 LPDWORD pcchEscaped,
930 DWORD dwFlags)
932 LPCWSTR src;
933 DWORD needed = 0, ret;
934 BOOL stop_escaping = FALSE;
935 WCHAR next[5], *dst = pszEscaped;
936 INT len;
937 PARSEDURLW parsed_url;
938 DWORD int_flags;
939 DWORD slashes = 0;
940 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
942 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
943 pcchEscaped, dwFlags);
945 if(!pszUrl || !pcchEscaped)
946 return E_INVALIDARG;
948 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
949 URL_ESCAPE_SEGMENT_ONLY |
950 URL_DONT_ESCAPE_EXTRA_INFO |
951 URL_ESCAPE_PERCENT))
952 FIXME("Unimplemented flags: %08x\n", dwFlags);
954 /* fix up flags */
955 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
956 /* if SPACES_ONLY specified, reset the other controls */
957 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
958 URL_ESCAPE_PERCENT |
959 URL_ESCAPE_SEGMENT_ONLY);
961 else
962 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
963 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
966 int_flags = 0;
967 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
968 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
969 } else {
970 parsed_url.cbSize = sizeof(parsed_url);
971 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
972 parsed_url.nScheme = URL_SCHEME_INVALID;
974 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
976 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
977 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
979 switch(parsed_url.nScheme) {
980 case URL_SCHEME_FILE:
981 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
982 int_flags &= ~WINE_URL_STOP_ON_HASH;
983 break;
985 case URL_SCHEME_HTTP:
986 case URL_SCHEME_HTTPS:
987 int_flags |= WINE_URL_BASH_AS_SLASH;
988 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
989 int_flags |= WINE_URL_ESCAPE_SLASH;
990 break;
992 case URL_SCHEME_MAILTO:
993 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
994 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
995 break;
997 case URL_SCHEME_INVALID:
998 break;
1000 case URL_SCHEME_FTP:
1001 default:
1002 if(parsed_url.pszSuffix[0] != '/')
1003 int_flags |= WINE_URL_ESCAPE_SLASH;
1004 break;
1008 for(src = pszUrl; *src; ) {
1009 WCHAR cur = *src;
1010 len = 0;
1012 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1013 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1014 while(cur == '/' || cur == '\\') {
1015 slashes++;
1016 cur = *++src;
1018 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1019 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1020 src += localhost_len + 1;
1021 slashes = 3;
1024 switch(slashes) {
1025 case 1:
1026 case 3:
1027 next[0] = next[1] = next[2] = '/';
1028 len = 3;
1029 break;
1030 case 0:
1031 len = 0;
1032 break;
1033 default:
1034 next[0] = next[1] = '/';
1035 len = 2;
1036 break;
1039 if(len == 0) {
1041 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1042 stop_escaping = TRUE;
1044 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1045 stop_escaping = TRUE;
1047 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1049 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1050 next[0] = '%';
1051 next[1] = hexDigits[(cur >> 4) & 0xf];
1052 next[2] = hexDigits[cur & 0xf];
1053 len = 3;
1054 } else {
1055 next[0] = cur;
1056 len = 1;
1058 src++;
1061 if(needed + len <= *pcchEscaped) {
1062 memcpy(dst, next, len*sizeof(WCHAR));
1063 dst += len;
1065 needed += len;
1068 if(needed < *pcchEscaped) {
1069 *dst = '\0';
1070 ret = S_OK;
1071 } else {
1072 needed++; /* add one for the '\0' */
1073 ret = E_POINTER;
1075 *pcchEscaped = needed;
1076 return ret;
1080 /*************************************************************************
1081 * UrlUnescapeA [SHLWAPI.@]
1083 * Converts Url escape sequences back to ordinary characters.
1085 * PARAMS
1086 * pszUrl [I/O] Url to convert
1087 * pszUnescaped [O] Destination for converted Url
1088 * pcchUnescaped [I/O] Size of output string
1089 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1091 * RETURNS
1092 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1093 * dwFlags includes URL_ESCAPE_INPLACE.
1094 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1095 * this case pcchUnescaped is set to the size required.
1096 * NOTES
1097 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1098 * the first occurrence of either a '?' or '#' character.
1100 HRESULT WINAPI UrlUnescapeA(
1101 LPSTR pszUrl,
1102 LPSTR pszUnescaped,
1103 LPDWORD pcchUnescaped,
1104 DWORD dwFlags)
1106 char *dst, next;
1107 LPCSTR src;
1108 HRESULT ret;
1109 DWORD needed;
1110 BOOL stop_unescaping = FALSE;
1112 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1113 pcchUnescaped, dwFlags);
1115 if (!pszUrl) return E_INVALIDARG;
1117 if(dwFlags & URL_UNESCAPE_INPLACE)
1118 dst = pszUrl;
1119 else
1121 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1122 dst = pszUnescaped;
1125 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1126 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1127 (*src == '#' || *src == '?')) {
1128 stop_unescaping = TRUE;
1129 next = *src;
1130 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1131 && stop_unescaping == FALSE) {
1132 INT ih;
1133 char buf[3];
1134 memcpy(buf, src + 1, 2);
1135 buf[2] = '\0';
1136 ih = strtol(buf, NULL, 16);
1137 next = (CHAR) ih;
1138 src += 2; /* Advance to end of escape */
1139 } else
1140 next = *src;
1142 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1143 *dst++ = next;
1146 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1147 *dst = '\0';
1148 ret = S_OK;
1149 } else {
1150 needed++; /* add one for the '\0' */
1151 ret = E_POINTER;
1153 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1154 *pcchUnescaped = needed;
1156 if (ret == S_OK) {
1157 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1158 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1161 return ret;
1164 /*************************************************************************
1165 * UrlUnescapeW [SHLWAPI.@]
1167 * See UrlUnescapeA.
1169 HRESULT WINAPI UrlUnescapeW(
1170 LPWSTR pszUrl,
1171 LPWSTR pszUnescaped,
1172 LPDWORD pcchUnescaped,
1173 DWORD dwFlags)
1175 WCHAR *dst, next;
1176 LPCWSTR src;
1177 HRESULT ret;
1178 DWORD needed;
1179 BOOL stop_unescaping = FALSE;
1181 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1182 pcchUnescaped, dwFlags);
1184 if(!pszUrl) return E_INVALIDARG;
1186 if(dwFlags & URL_UNESCAPE_INPLACE)
1187 dst = pszUrl;
1188 else
1190 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1191 dst = pszUnescaped;
1194 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1195 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1196 (*src == '#' || *src == '?')) {
1197 stop_unescaping = TRUE;
1198 next = *src;
1199 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1200 && stop_unescaping == FALSE) {
1201 INT ih;
1202 WCHAR buf[5] = {'0','x',0};
1203 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1204 buf[4] = 0;
1205 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1206 next = (WCHAR) ih;
1207 src += 2; /* Advance to end of escape */
1208 } else
1209 next = *src;
1211 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1212 *dst++ = next;
1215 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1216 *dst = '\0';
1217 ret = S_OK;
1218 } else {
1219 needed++; /* add one for the '\0' */
1220 ret = E_POINTER;
1222 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1223 *pcchUnescaped = needed;
1225 if (ret == S_OK) {
1226 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1227 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1230 return ret;
1233 /*************************************************************************
1234 * UrlGetLocationA [SHLWAPI.@]
1236 * Get the location from a Url.
1238 * PARAMS
1239 * pszUrl [I] Url to get the location from
1241 * RETURNS
1242 * A pointer to the start of the location in pszUrl, or NULL if there is
1243 * no location.
1245 * NOTES
1246 * - MSDN erroneously states that "The location is the segment of the Url
1247 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1248 * stop at '?' and always return a NULL in this case.
1249 * - MSDN also erroneously states that "If a file URL has a query string,
1250 * the returned string is the query string". In all tested cases, if the
1251 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1252 *| Result Url
1253 *| ------ ---
1254 *| NULL file://aa/b/cd#hohoh
1255 *| #hohoh http://aa/b/cd#hohoh
1256 *| NULL fi://aa/b/cd#hohoh
1257 *| #hohoh ff://aa/b/cd#hohoh
1259 LPCSTR WINAPI UrlGetLocationA(
1260 LPCSTR pszUrl)
1262 PARSEDURLA base;
1263 DWORD res1;
1265 base.cbSize = sizeof(base);
1266 res1 = ParseURLA(pszUrl, &base);
1267 if (res1) return NULL; /* invalid scheme */
1269 /* if scheme is file: then never return pointer */
1270 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1272 /* Look for '#' and return its addr */
1273 return strchr(base.pszSuffix, '#');
1276 /*************************************************************************
1277 * UrlGetLocationW [SHLWAPI.@]
1279 * See UrlGetLocationA.
1281 LPCWSTR WINAPI UrlGetLocationW(
1282 LPCWSTR pszUrl)
1284 PARSEDURLW base;
1285 DWORD res1;
1287 base.cbSize = sizeof(base);
1288 res1 = ParseURLW(pszUrl, &base);
1289 if (res1) return NULL; /* invalid scheme */
1291 /* if scheme is file: then never return pointer */
1292 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1294 /* Look for '#' and return its addr */
1295 return strchrW(base.pszSuffix, '#');
1298 /*************************************************************************
1299 * UrlCompareA [SHLWAPI.@]
1301 * Compare two Urls.
1303 * PARAMS
1304 * pszUrl1 [I] First Url to compare
1305 * pszUrl2 [I] Url to compare to pszUrl1
1306 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1308 * RETURNS
1309 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1310 * than, equal to, or less than pszUrl1 respectively.
1312 INT WINAPI UrlCompareA(
1313 LPCSTR pszUrl1,
1314 LPCSTR pszUrl2,
1315 BOOL fIgnoreSlash)
1317 INT ret, len, len1, len2;
1319 if (!fIgnoreSlash)
1320 return strcmp(pszUrl1, pszUrl2);
1321 len1 = strlen(pszUrl1);
1322 if (pszUrl1[len1-1] == '/') len1--;
1323 len2 = strlen(pszUrl2);
1324 if (pszUrl2[len2-1] == '/') len2--;
1325 if (len1 == len2)
1326 return strncmp(pszUrl1, pszUrl2, len1);
1327 len = min(len1, len2);
1328 ret = strncmp(pszUrl1, pszUrl2, len);
1329 if (ret) return ret;
1330 if (len1 > len2) return 1;
1331 return -1;
1334 /*************************************************************************
1335 * UrlCompareW [SHLWAPI.@]
1337 * See UrlCompareA.
1339 INT WINAPI UrlCompareW(
1340 LPCWSTR pszUrl1,
1341 LPCWSTR pszUrl2,
1342 BOOL fIgnoreSlash)
1344 INT ret;
1345 size_t len, len1, len2;
1347 if (!fIgnoreSlash)
1348 return strcmpW(pszUrl1, pszUrl2);
1349 len1 = strlenW(pszUrl1);
1350 if (pszUrl1[len1-1] == '/') len1--;
1351 len2 = strlenW(pszUrl2);
1352 if (pszUrl2[len2-1] == '/') len2--;
1353 if (len1 == len2)
1354 return strncmpW(pszUrl1, pszUrl2, len1);
1355 len = min(len1, len2);
1356 ret = strncmpW(pszUrl1, pszUrl2, len);
1357 if (ret) return ret;
1358 if (len1 > len2) return 1;
1359 return -1;
1362 /*************************************************************************
1363 * HashData [SHLWAPI.@]
1365 * Hash an input block into a variable sized digest.
1367 * PARAMS
1368 * lpSrc [I] Input block
1369 * nSrcLen [I] Length of lpSrc
1370 * lpDest [I] Output for hash digest
1371 * nDestLen [I] Length of lpDest
1373 * RETURNS
1374 * Success: TRUE. lpDest is filled with the computed hash value.
1375 * Failure: FALSE, if any argument is invalid.
1377 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1378 unsigned char *lpDest, DWORD nDestLen)
1380 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1382 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1383 IsBadWritePtr(lpDest, nDestLen))
1384 return E_INVALIDARG;
1386 while (destCount >= 0)
1388 lpDest[destCount] = (destCount & 0xff);
1389 destCount--;
1392 while (srcCount >= 0)
1394 destCount = nDestLen - 1;
1395 while (destCount >= 0)
1397 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1398 destCount--;
1400 srcCount--;
1402 return S_OK;
1405 /*************************************************************************
1406 * UrlHashA [SHLWAPI.@]
1408 * Produce a Hash from a Url.
1410 * PARAMS
1411 * pszUrl [I] Url to hash
1412 * lpDest [O] Destinationh for hash
1413 * nDestLen [I] Length of lpDest
1415 * RETURNS
1416 * Success: S_OK. lpDest is filled with the computed hash value.
1417 * Failure: E_INVALIDARG, if any argument is invalid.
1419 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1421 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1422 return E_INVALIDARG;
1424 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1425 return S_OK;
1428 /*************************************************************************
1429 * UrlHashW [SHLWAPI.@]
1431 * See UrlHashA.
1433 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1435 char szUrl[MAX_PATH];
1437 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1439 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1440 return E_INVALIDARG;
1442 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1443 * return the same digests for the same URL.
1445 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1446 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1447 return S_OK;
1450 /*************************************************************************
1451 * UrlApplySchemeA [SHLWAPI.@]
1453 * Apply a scheme to a Url.
1455 * PARAMS
1456 * pszIn [I] Url to apply scheme to
1457 * pszOut [O] Destination for modified Url
1458 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1459 * dwFlags [I] URL_ flags from "shlwapi.h"
1461 * RETURNS
1462 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1463 * Failure: An HRESULT error code describing the error.
1465 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1467 LPWSTR in, out;
1468 HRESULT ret;
1469 DWORD len;
1471 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1472 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1474 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1476 in = HeapAlloc(GetProcessHeap(), 0,
1477 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1478 out = in + INTERNET_MAX_URL_LENGTH;
1480 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1481 len = INTERNET_MAX_URL_LENGTH;
1483 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1484 if (ret != S_OK) {
1485 HeapFree(GetProcessHeap(), 0, in);
1486 return ret;
1489 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1490 if (len > *pcchOut) {
1491 ret = E_POINTER;
1492 goto cleanup;
1495 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1496 len--;
1498 cleanup:
1499 *pcchOut = len;
1500 HeapFree(GetProcessHeap(), 0, in);
1501 return ret;
1504 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1506 HKEY newkey;
1507 BOOL j;
1508 INT index;
1509 DWORD value_len, data_len, dwType, i;
1510 WCHAR reg_path[MAX_PATH];
1511 WCHAR value[MAX_PATH], data[MAX_PATH];
1512 WCHAR Wxx, Wyy;
1514 MultiByteToWideChar(0, 0,
1515 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1516 -1, reg_path, MAX_PATH);
1517 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1518 index = 0;
1519 while(value_len = data_len = MAX_PATH,
1520 RegEnumValueW(newkey, index, value, &value_len,
1521 0, &dwType, (LPVOID)data, &data_len) == 0) {
1522 TRACE("guess %d %s is %s\n",
1523 index, debugstr_w(value), debugstr_w(data));
1525 j = FALSE;
1526 for(i=0; i<value_len; i++) {
1527 Wxx = pszIn[i];
1528 Wyy = value[i];
1529 /* remember that TRUE is not-equal */
1530 j = ChrCmpIW(Wxx, Wyy);
1531 if (j) break;
1533 if ((i == value_len) && !j) {
1534 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1535 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1536 RegCloseKey(newkey);
1537 return E_POINTER;
1539 strcpyW(pszOut, data);
1540 strcatW(pszOut, pszIn);
1541 *pcchOut = strlenW(pszOut);
1542 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1543 RegCloseKey(newkey);
1544 return S_OK;
1546 index++;
1548 RegCloseKey(newkey);
1549 return E_FAIL;
1552 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1554 HKEY newkey;
1555 DWORD data_len, dwType;
1556 WCHAR data[MAX_PATH];
1558 static const WCHAR prefix_keyW[] =
1559 {'S','o','f','t','w','a','r','e',
1560 '\\','M','i','c','r','o','s','o','f','t',
1561 '\\','W','i','n','d','o','w','s',
1562 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1563 '\\','U','R','L',
1564 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1566 /* get and prepend default */
1567 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1568 data_len = sizeof(data);
1569 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1570 RegCloseKey(newkey);
1571 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1572 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1573 return E_POINTER;
1575 strcpyW(pszOut, data);
1576 strcatW(pszOut, pszIn);
1577 *pcchOut = strlenW(pszOut);
1578 TRACE("used default %s\n", debugstr_w(pszOut));
1579 return S_OK;
1582 /*************************************************************************
1583 * UrlApplySchemeW [SHLWAPI.@]
1585 * See UrlApplySchemeA.
1587 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1589 PARSEDURLW in_scheme;
1590 DWORD res1;
1591 HRESULT ret;
1593 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1594 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1596 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1598 if (dwFlags & URL_APPLY_GUESSFILE) {
1599 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1600 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1601 strcpyW(pszOut, pszIn);
1602 *pcchOut = strlenW(pszOut);
1603 return S_FALSE;
1606 in_scheme.cbSize = sizeof(in_scheme);
1607 /* See if the base has a scheme */
1608 res1 = ParseURLW(pszIn, &in_scheme);
1609 if (res1) {
1610 /* no scheme in input, need to see if we need to guess */
1611 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1612 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1613 return ret;
1616 else {
1617 /* we have a scheme, see if valid (known scheme) */
1618 if (in_scheme.nScheme) {
1619 /* have valid scheme, so just copy and exit */
1620 if (strlenW(pszIn) + 1 > *pcchOut) {
1621 *pcchOut = strlenW(pszIn) + 1;
1622 return E_POINTER;
1624 strcpyW(pszOut, pszIn);
1625 *pcchOut = strlenW(pszOut);
1626 TRACE("valid scheme, returning copy\n");
1627 return S_OK;
1631 /* If we are here, then either invalid scheme,
1632 * or no scheme and can't/failed guess.
1634 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1635 ((res1 != 0)) ) &&
1636 (dwFlags & URL_APPLY_DEFAULT)) {
1637 /* find and apply default scheme */
1638 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1641 return S_FALSE;
1644 /*************************************************************************
1645 * UrlIsA [SHLWAPI.@]
1647 * Determine if a Url is of a certain class.
1649 * PARAMS
1650 * pszUrl [I] Url to check
1651 * Urlis [I] URLIS_ constant from "shlwapi.h"
1653 * RETURNS
1654 * TRUE if pszUrl belongs to the class type in Urlis.
1655 * FALSE Otherwise.
1657 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1659 PARSEDURLA base;
1660 DWORD res1;
1661 LPCSTR last;
1663 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1665 switch (Urlis) {
1667 case URLIS_OPAQUE:
1668 base.cbSize = sizeof(base);
1669 res1 = ParseURLA(pszUrl, &base);
1670 if (res1) return FALSE; /* invalid scheme */
1671 switch (base.nScheme)
1673 case URL_SCHEME_MAILTO:
1674 case URL_SCHEME_SHELL:
1675 case URL_SCHEME_JAVASCRIPT:
1676 case URL_SCHEME_VBSCRIPT:
1677 case URL_SCHEME_ABOUT:
1678 return TRUE;
1680 return FALSE;
1682 case URLIS_FILEURL:
1683 return !StrCmpNA("file:", pszUrl, 5);
1685 case URLIS_DIRECTORY:
1686 last = pszUrl + strlen(pszUrl) - 1;
1687 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1689 case URLIS_URL:
1690 return PathIsURLA(pszUrl);
1692 case URLIS_NOHISTORY:
1693 case URLIS_APPLIABLE:
1694 case URLIS_HASQUERY:
1695 default:
1696 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1698 return FALSE;
1701 /*************************************************************************
1702 * UrlIsW [SHLWAPI.@]
1704 * See UrlIsA.
1706 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1708 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1709 PARSEDURLW base;
1710 DWORD res1;
1711 LPCWSTR last;
1713 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1715 switch (Urlis) {
1717 case URLIS_OPAQUE:
1718 base.cbSize = sizeof(base);
1719 res1 = ParseURLW(pszUrl, &base);
1720 if (res1) return FALSE; /* invalid scheme */
1721 switch (base.nScheme)
1723 case URL_SCHEME_MAILTO:
1724 case URL_SCHEME_SHELL:
1725 case URL_SCHEME_JAVASCRIPT:
1726 case URL_SCHEME_VBSCRIPT:
1727 case URL_SCHEME_ABOUT:
1728 return TRUE;
1730 return FALSE;
1732 case URLIS_FILEURL:
1733 return !strncmpW(stemp, pszUrl, 5);
1735 case URLIS_DIRECTORY:
1736 last = pszUrl + strlenW(pszUrl) - 1;
1737 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1739 case URLIS_URL:
1740 return PathIsURLW(pszUrl);
1742 case URLIS_NOHISTORY:
1743 case URLIS_APPLIABLE:
1744 case URLIS_HASQUERY:
1745 default:
1746 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1748 return FALSE;
1751 /*************************************************************************
1752 * UrlIsNoHistoryA [SHLWAPI.@]
1754 * Determine if a Url should not be stored in the users history list.
1756 * PARAMS
1757 * pszUrl [I] Url to check
1759 * RETURNS
1760 * TRUE, if pszUrl should be excluded from the history list,
1761 * FALSE otherwise.
1763 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1765 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1768 /*************************************************************************
1769 * UrlIsNoHistoryW [SHLWAPI.@]
1771 * See UrlIsNoHistoryA.
1773 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1775 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1778 /*************************************************************************
1779 * UrlIsOpaqueA [SHLWAPI.@]
1781 * Determine if a Url is opaque.
1783 * PARAMS
1784 * pszUrl [I] Url to check
1786 * RETURNS
1787 * TRUE if pszUrl is opaque,
1788 * FALSE Otherwise.
1790 * NOTES
1791 * An opaque Url is one that does not start with "<protocol>://".
1793 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1795 return UrlIsA(pszUrl, URLIS_OPAQUE);
1798 /*************************************************************************
1799 * UrlIsOpaqueW [SHLWAPI.@]
1801 * See UrlIsOpaqueA.
1803 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1805 return UrlIsW(pszUrl, URLIS_OPAQUE);
1808 /*************************************************************************
1809 * Scans for characters of type "type" and when not matching found,
1810 * returns pointer to it and length in size.
1812 * Characters tested based on RFC 1738
1814 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1816 static DWORD alwayszero = 0;
1817 BOOL cont = TRUE;
1819 *size = 0;
1821 switch(type){
1823 case SCHEME:
1824 while (cont) {
1825 if ( (islowerW(*start) && isalphaW(*start)) ||
1826 isdigitW(*start) ||
1827 (*start == '+') ||
1828 (*start == '-') ||
1829 (*start == '.')) {
1830 start++;
1831 (*size)++;
1833 else
1834 cont = FALSE;
1836 break;
1838 case USERPASS:
1839 while (cont) {
1840 if ( isalphaW(*start) ||
1841 isdigitW(*start) ||
1842 /* user/password only characters */
1843 (*start == ';') ||
1844 (*start == '?') ||
1845 (*start == '&') ||
1846 (*start == '=') ||
1847 /* *extra* characters */
1848 (*start == '!') ||
1849 (*start == '*') ||
1850 (*start == '\'') ||
1851 (*start == '(') ||
1852 (*start == ')') ||
1853 (*start == ',') ||
1854 /* *safe* characters */
1855 (*start == '$') ||
1856 (*start == '_') ||
1857 (*start == '+') ||
1858 (*start == '-') ||
1859 (*start == '.')) {
1860 start++;
1861 (*size)++;
1862 } else if (*start == '%') {
1863 if (isxdigitW(*(start+1)) &&
1864 isxdigitW(*(start+2))) {
1865 start += 3;
1866 *size += 3;
1867 } else
1868 cont = FALSE;
1869 } else
1870 cont = FALSE;
1872 break;
1874 case PORT:
1875 while (cont) {
1876 if (isdigitW(*start)) {
1877 start++;
1878 (*size)++;
1880 else
1881 cont = FALSE;
1883 break;
1885 case HOST:
1886 while (cont) {
1887 if (isalnumW(*start) ||
1888 (*start == '-') ||
1889 (*start == '.') ) {
1890 start++;
1891 (*size)++;
1893 else
1894 cont = FALSE;
1896 break;
1897 default:
1898 FIXME("unknown type %d\n", type);
1899 return (LPWSTR)&alwayszero;
1901 /* TRACE("scanned %d characters next char %p<%c>\n",
1902 *size, start, *start); */
1903 return start;
1906 /*************************************************************************
1907 * Attempt to parse URL into pieces.
1909 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1911 LPCWSTR work;
1913 memset(pl, 0, sizeof(WINE_PARSE_URL));
1914 pl->pScheme = pszUrl;
1915 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1916 if (!*work || (*work != ':')) goto ErrorExit;
1917 work++;
1918 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1919 pl->pUserName = work + 2;
1920 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1921 if (*work == ':' ) {
1922 /* parse password */
1923 work++;
1924 pl->pPassword = work;
1925 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1926 if (*work != '@') {
1927 /* what we just parsed must be the hostname and port
1928 * so reset pointers and clear then let it parse */
1929 pl->szUserName = pl->szPassword = 0;
1930 work = pl->pUserName - 1;
1931 pl->pUserName = pl->pPassword = 0;
1933 } else if (*work == '@') {
1934 /* no password */
1935 pl->szPassword = 0;
1936 pl->pPassword = 0;
1937 } else if (!*work || (*work == '/') || (*work == '.')) {
1938 /* what was parsed was hostname, so reset pointers and let it parse */
1939 pl->szUserName = pl->szPassword = 0;
1940 work = pl->pUserName - 1;
1941 pl->pUserName = pl->pPassword = 0;
1942 } else goto ErrorExit;
1944 /* now start parsing hostname or hostnumber */
1945 work++;
1946 pl->pHostName = work;
1947 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1948 if (*work == ':') {
1949 /* parse port */
1950 work++;
1951 pl->pPort = work;
1952 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1954 if (*work == '/') {
1955 /* see if query string */
1956 pl->pQuery = strchrW(work, '?');
1957 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1959 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1960 pl->pScheme, pl->szScheme,
1961 pl->pUserName, pl->szUserName,
1962 pl->pPassword, pl->szPassword,
1963 pl->pHostName, pl->szHostName,
1964 pl->pPort, pl->szPort,
1965 pl->pQuery, pl->szQuery);
1966 return S_OK;
1967 ErrorExit:
1968 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1969 return E_INVALIDARG;
1972 /*************************************************************************
1973 * UrlGetPartA [SHLWAPI.@]
1975 * Retrieve part of a Url.
1977 * PARAMS
1978 * pszIn [I] Url to parse
1979 * pszOut [O] Destination for part of pszIn requested
1980 * pcchOut [I] Size of pszOut
1981 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
1982 * needed size of pszOut INCLUDING '\0'.
1983 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1984 * dwFlags [I] URL_ flags from "shlwapi.h"
1986 * RETURNS
1987 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1988 * Failure: An HRESULT error code describing the error.
1990 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1991 DWORD dwPart, DWORD dwFlags)
1993 LPWSTR in, out;
1994 DWORD ret, len, len2;
1996 in = HeapAlloc(GetProcessHeap(), 0,
1997 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1998 out = in + INTERNET_MAX_URL_LENGTH;
2000 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2002 len = INTERNET_MAX_URL_LENGTH;
2003 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2005 if (ret != S_OK) {
2006 HeapFree(GetProcessHeap(), 0, in);
2007 return ret;
2010 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2011 if (len2 > *pcchOut) {
2012 *pcchOut = len2;
2013 HeapFree(GetProcessHeap(), 0, in);
2014 return E_POINTER;
2016 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2017 *pcchOut = len2;
2018 HeapFree(GetProcessHeap(), 0, in);
2019 return S_OK;
2022 /*************************************************************************
2023 * UrlGetPartW [SHLWAPI.@]
2025 * See UrlGetPartA.
2027 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2028 DWORD dwPart, DWORD dwFlags)
2030 WINE_PARSE_URL pl;
2031 HRESULT ret;
2032 DWORD size, schsize;
2033 LPCWSTR addr, schaddr;
2035 TRACE("(%s %p %p(%d) %08x %08x)\n",
2036 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2038 ret = URL_ParseUrl(pszIn, &pl);
2039 if (ret == S_OK) {
2040 schaddr = pl.pScheme;
2041 schsize = pl.szScheme;
2043 switch (dwPart) {
2044 case URL_PART_SCHEME:
2045 if (!pl.szScheme) return E_INVALIDARG;
2046 addr = pl.pScheme;
2047 size = pl.szScheme;
2048 break;
2050 case URL_PART_HOSTNAME:
2051 if (!pl.szHostName) return E_INVALIDARG;
2052 addr = pl.pHostName;
2053 size = pl.szHostName;
2054 break;
2056 case URL_PART_USERNAME:
2057 if (!pl.szUserName) return E_INVALIDARG;
2058 addr = pl.pUserName;
2059 size = pl.szUserName;
2060 break;
2062 case URL_PART_PASSWORD:
2063 if (!pl.szPassword) return E_INVALIDARG;
2064 addr = pl.pPassword;
2065 size = pl.szPassword;
2066 break;
2068 case URL_PART_PORT:
2069 if (!pl.szPort) return E_INVALIDARG;
2070 addr = pl.pPort;
2071 size = pl.szPort;
2072 break;
2074 case URL_PART_QUERY:
2075 if (!pl.szQuery) return E_INVALIDARG;
2076 addr = pl.pQuery;
2077 size = pl.szQuery;
2078 break;
2080 default:
2081 return E_INVALIDARG;
2084 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2085 if (*pcchOut < schsize + size + 2) {
2086 *pcchOut = schsize + size + 2;
2087 return E_POINTER;
2089 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2090 pszOut[schsize] = ':';
2091 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2092 pszOut[schsize+1+size] = 0;
2093 *pcchOut = schsize + 1 + size;
2095 else {
2096 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2097 memcpy(pszOut, addr, size*sizeof(WCHAR));
2098 pszOut[size] = 0;
2099 *pcchOut = size;
2101 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2103 return ret;
2106 /*************************************************************************
2107 * PathIsURLA [SHLWAPI.@]
2109 * Check if the given path is a Url.
2111 * PARAMS
2112 * lpszPath [I] Path to check.
2114 * RETURNS
2115 * TRUE if lpszPath is a Url.
2116 * FALSE if lpszPath is NULL or not a Url.
2118 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2120 PARSEDURLA base;
2121 HRESULT hres;
2123 TRACE("%s\n", debugstr_a(lpstrPath));
2125 if (!lpstrPath || !*lpstrPath) return FALSE;
2127 /* get protocol */
2128 base.cbSize = sizeof(base);
2129 hres = ParseURLA(lpstrPath, &base);
2130 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2133 /*************************************************************************
2134 * PathIsURLW [SHLWAPI.@]
2136 * See PathIsURLA.
2138 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2140 PARSEDURLW base;
2141 HRESULT hres;
2143 TRACE("%s\n", debugstr_w(lpstrPath));
2145 if (!lpstrPath || !*lpstrPath) return FALSE;
2147 /* get protocol */
2148 base.cbSize = sizeof(base);
2149 hres = ParseURLW(lpstrPath, &base);
2150 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2153 /*************************************************************************
2154 * UrlCreateFromPathA [SHLWAPI.@]
2156 * See UrlCreateFromPathW
2158 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2160 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2161 WCHAR *urlW = bufW;
2162 UNICODE_STRING pathW;
2163 HRESULT ret;
2164 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2166 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2167 return E_INVALIDARG;
2168 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2169 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2170 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2172 if(ret == S_OK || ret == S_FALSE) {
2173 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2174 if(*pcchUrl > lenA) {
2175 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2176 pszUrl[lenA] = 0;
2177 *pcchUrl = lenA;
2178 } else {
2179 *pcchUrl = lenA + 1;
2180 ret = E_POINTER;
2183 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2184 RtlFreeUnicodeString(&pathW);
2185 return ret;
2188 /*************************************************************************
2189 * UrlCreateFromPathW [SHLWAPI.@]
2191 * Create a Url from a file path.
2193 * PARAMS
2194 * pszPath [I] Path to convert
2195 * pszUrl [O] Destination for the converted Url
2196 * pcchUrl [I/O] Length of pszUrl
2197 * dwReserved [I] Reserved, must be 0
2199 * RETURNS
2200 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2201 * Failure: An HRESULT error code.
2203 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2205 DWORD needed;
2206 HRESULT ret;
2207 WCHAR *pszNewUrl;
2208 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2209 WCHAR three_slashesW[] = {'/','/','/',0};
2210 PARSEDURLW parsed_url;
2212 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2214 /* Validate arguments */
2215 if (dwReserved != 0)
2216 return E_INVALIDARG;
2217 if (!pszUrl || !pcchUrl)
2218 return E_INVALIDARG;
2221 parsed_url.cbSize = sizeof(parsed_url);
2222 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2223 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2224 needed = strlenW(pszPath);
2225 if (needed >= *pcchUrl) {
2226 *pcchUrl = needed + 1;
2227 return E_POINTER;
2228 } else {
2229 *pcchUrl = needed;
2230 strcpyW(pszUrl, pszPath);
2231 return S_FALSE;
2236 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2237 strcpyW(pszNewUrl, file_colonW);
2238 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2239 strcatW(pszNewUrl, three_slashesW);
2240 strcatW(pszNewUrl, pszPath);
2241 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2243 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2244 return ret;
2247 /*************************************************************************
2248 * SHAutoComplete [SHLWAPI.@]
2250 * Enable auto-completion for an edit control.
2252 * PARAMS
2253 * hwndEdit [I] Handle of control to enable auto-completion for
2254 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2256 * RETURNS
2257 * Success: S_OK. Auto-completion is enabled for the control.
2258 * Failure: An HRESULT error code indicating the error.
2260 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2262 FIXME("SHAutoComplete stub\n");
2263 return S_FALSE;
2266 /*************************************************************************
2267 * MLBuildResURLA [SHLWAPI.405]
2269 * Create a Url pointing to a resource in a module.
2271 * PARAMS
2272 * lpszLibName [I] Name of the module containing the resource
2273 * hMod [I] Callers module handle
2274 * dwFlags [I] Undocumented flags for loading the module
2275 * lpszRes [I] Resource name
2276 * lpszDest [O] Destination for resulting Url
2277 * dwDestLen [I] Length of lpszDest
2279 * RETURNS
2280 * Success: S_OK. lpszDest contains the resource Url.
2281 * Failure: E_INVALIDARG, if any argument is invalid, or
2282 * E_FAIL if dwDestLen is too small.
2284 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2285 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2287 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2288 HRESULT hRet;
2290 if (lpszLibName)
2291 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2293 if (lpszRes)
2294 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2296 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2297 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2299 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2300 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2301 if (SUCCEEDED(hRet) && lpszDest)
2302 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2304 return hRet;
2307 /*************************************************************************
2308 * MLBuildResURLA [SHLWAPI.406]
2310 * See MLBuildResURLA.
2312 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2313 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2315 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2316 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2317 HRESULT hRet = E_FAIL;
2319 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2320 debugstr_w(lpszRes), lpszDest, dwDestLen);
2322 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2323 !lpszDest || (dwFlags && dwFlags != 2))
2324 return E_INVALIDARG;
2326 if (dwDestLen >= szResLen + 1)
2328 dwDestLen -= (szResLen + 1);
2329 memcpy(lpszDest, szRes, sizeof(szRes));
2331 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2333 if (hMod)
2335 WCHAR szBuff[MAX_PATH];
2336 DWORD len;
2338 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2339 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2341 DWORD dwPathLen = strlenW(szBuff) + 1;
2343 if (dwDestLen >= dwPathLen)
2345 DWORD dwResLen;
2347 dwDestLen -= dwPathLen;
2348 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2350 dwResLen = strlenW(lpszRes) + 1;
2351 if (dwDestLen >= dwResLen + 1)
2353 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2354 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2355 hRet = S_OK;
2359 MLFreeLibrary(hMod);
2362 return hRet;
2365 /***********************************************************************
2366 * UrlFixupW [SHLWAPI.462]
2368 * Checks the scheme part of a URL and attempts to correct misspellings.
2370 * PARAMS
2371 * lpszUrl [I] Pointer to the URL to be corrected
2372 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2373 * dwMaxChars [I] Maximum size of corrected URL
2375 * RETURNS
2376 * success: S_OK if URL corrected or already correct
2377 * failure: S_FALSE if unable to correct / COM error code if other error
2380 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2382 DWORD srcLen;
2384 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2386 if (!url)
2387 return E_FAIL;
2389 srcLen = lstrlenW(url);
2391 /* For now just copy the URL directly */
2392 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2394 return S_OK;