mmdevapi: Fix small capture bugs.
[wine/multimedia.git] / dlls / shlwapi / url.c
blobcd83b457178b997bfbc0c52cfc3c22733a2bae43
1 /*
2 * Url functions
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #define NO_SHLWAPI_STREAM
35 #include "shlwapi.h"
36 #include "intshcut.h"
37 #include "wine/debug.h"
39 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
40 BOOL WINAPI MLFreeLibrary(HMODULE);
41 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell);
45 /* The following schemes were identified in the native version of
46 * SHLWAPI.DLL version 5.50
48 static const struct {
49 URL_SCHEME scheme_number;
50 WCHAR scheme_name[12];
51 } shlwapi_schemes[] = {
52 {URL_SCHEME_FTP, {'f','t','p',0}},
53 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
54 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
55 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
56 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
57 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
58 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
59 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
60 {URL_SCHEME_FILE, {'f','i','l','e',0}},
61 {URL_SCHEME_MK, {'m','k',0}},
62 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
63 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
64 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
65 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
66 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
67 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
68 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
69 {URL_SCHEME_RES, {'r','e','s',0}},
72 typedef struct {
73 LPCWSTR pScheme; /* [out] start of scheme */
74 DWORD szScheme; /* [out] size of scheme (until colon) */
75 LPCWSTR pUserName; /* [out] start of Username */
76 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
77 LPCWSTR pPassword; /* [out] start of Password */
78 DWORD szPassword; /* [out] size of Password (until "@") */
79 LPCWSTR pHostName; /* [out] start of Hostname */
80 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
81 LPCWSTR pPort; /* [out] start of Port */
82 DWORD szPort; /* [out] size of Port (until "/" or eos) */
83 LPCWSTR pQuery; /* [out] start of Query */
84 DWORD szQuery; /* [out] size of Query (until eos) */
85 } WINE_PARSE_URL;
87 typedef enum {
88 SCHEME,
89 HOST,
90 PORT,
91 USERPASS,
92 } WINE_URL_SCAN_TYPE;
94 static const CHAR hexDigits[] = "0123456789ABCDEF";
96 static const WCHAR fileW[] = {'f','i','l','e','\0'};
98 static const unsigned char HashDataLookup[256] = {
99 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
100 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
101 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
102 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
103 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
104 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
105 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
106 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
107 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
108 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
109 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
110 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
111 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
112 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
113 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
114 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
115 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
116 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
117 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
118 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
120 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
122 unsigned int i;
124 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
125 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
126 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
127 return shlwapi_schemes[i].scheme_number;
130 return URL_SCHEME_UNKNOWN;
133 /*************************************************************************
134 * @ [SHLWAPI.1]
136 * Parse a Url into its constituent parts.
138 * PARAMS
139 * x [I] Url to parse
140 * y [O] Undocumented structure holding the parsed information
142 * RETURNS
143 * Success: S_OK. y contains the parsed Url details.
144 * Failure: An HRESULT error code.
146 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
148 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
149 const char *ptr = x;
150 int len;
152 TRACE("%s %p\n", debugstr_a(x), y);
154 if(y->cbSize != sizeof(*y))
155 return E_INVALIDARG;
157 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
158 ptr++;
160 if (*ptr != ':' || ptr <= x+1) {
161 y->pszProtocol = NULL;
162 return URL_E_INVALID_SYNTAX;
165 y->pszProtocol = x;
166 y->cchProtocol = ptr-x;
167 y->pszSuffix = ptr+1;
168 y->cchSuffix = strlen(y->pszSuffix);
170 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
171 scheme, sizeof(scheme)/sizeof(WCHAR));
172 y->nScheme = get_scheme_code(scheme, len);
174 return S_OK;
177 /*************************************************************************
178 * @ [SHLWAPI.2]
180 * Unicode version of ParseURLA.
182 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
184 const WCHAR *ptr = x;
186 TRACE("%s %p\n", debugstr_w(x), y);
188 if(y->cbSize != sizeof(*y))
189 return E_INVALIDARG;
191 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
192 ptr++;
194 if (*ptr != ':' || ptr <= x+1) {
195 y->pszProtocol = NULL;
196 return URL_E_INVALID_SYNTAX;
199 y->pszProtocol = x;
200 y->cchProtocol = ptr-x;
201 y->pszSuffix = ptr+1;
202 y->cchSuffix = strlenW(y->pszSuffix);
203 y->nScheme = get_scheme_code(x, ptr-x);
205 return S_OK;
208 /*************************************************************************
209 * UrlCanonicalizeA [SHLWAPI.@]
211 * Canonicalize a Url.
213 * PARAMS
214 * pszUrl [I] Url to cCanonicalize
215 * pszCanonicalized [O] Destination for converted Url.
216 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
217 * dwFlags [I] Flags controlling the conversion.
219 * RETURNS
220 * Success: S_OK. The pszCanonicalized contains the converted Url.
221 * Failure: E_POINTER, if *pcchCanonicalized is too small.
223 * MSDN incorrectly describes the flags for this function. They should be:
224 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
225 *| URL_ESCAPE_SPACES_ONLY 0x04000000
226 *| URL_ESCAPE_PERCENT 0x00001000
227 *| URL_ESCAPE_UNSAFE 0x10000000
228 *| URL_UNESCAPE 0x10000000
229 *| URL_DONT_SIMPLIFY 0x08000000
230 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
232 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
233 LPDWORD pcchCanonicalized, DWORD dwFlags)
235 LPWSTR base, canonical;
236 HRESULT ret;
237 DWORD len, len2;
239 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
240 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
242 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
243 return E_INVALIDARG;
245 base = HeapAlloc(GetProcessHeap(), 0,
246 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
247 canonical = base + INTERNET_MAX_URL_LENGTH;
249 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
250 len = INTERNET_MAX_URL_LENGTH;
252 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
253 if (ret != S_OK) {
254 *pcchCanonicalized = len * 2;
255 HeapFree(GetProcessHeap(), 0, base);
256 return ret;
259 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
260 if (len2 > *pcchCanonicalized) {
261 *pcchCanonicalized = len2;
262 HeapFree(GetProcessHeap(), 0, base);
263 return E_POINTER;
265 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
266 *pcchCanonicalized = len;
267 HeapFree(GetProcessHeap(), 0, base);
268 return S_OK;
271 /*************************************************************************
272 * UrlCanonicalizeW [SHLWAPI.@]
274 * See UrlCanonicalizeA.
276 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
277 LPDWORD pcchCanonicalized, DWORD dwFlags)
279 HRESULT hr = S_OK;
280 DWORD EscapeFlags;
281 LPCWSTR wk1, root;
282 LPWSTR lpszUrlCpy, wk2, mp, mp2;
283 INT state;
284 DWORD nByteLen, nLen, nWkLen;
285 WCHAR slash = '/';
287 static const WCHAR wszFile[] = {'f','i','l','e',':'};
288 static const WCHAR wszRes[] = {'r','e','s',':'};
289 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
291 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
292 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
294 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
295 return E_INVALIDARG;
297 if(!*pszUrl) {
298 *pszCanonicalized = 0;
299 return S_OK;
302 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
303 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
304 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
306 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
307 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
308 slash = '\\';
310 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, pszUrl, sizeof(wszRes))) {
311 dwFlags &= ~URL_FILE_USE_PATHURL;
312 slash = '\0';
316 * state =
317 * 0 initial 1,3
318 * 1 have 2[+] alnum 2,3
319 * 2 have scheme (found :) 4,6,3
320 * 3 failed (no location)
321 * 4 have // 5,3
322 * 5 have 1[+] alnum 6,3
323 * 6 have location (found /) save root location
326 wk1 = pszUrl;
327 wk2 = lpszUrlCpy;
328 state = 0;
330 if(pszUrl[1] == ':') { /* Assume path */
331 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
333 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
334 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
335 if (dwFlags & URL_FILE_USE_PATHURL)
337 slash = '\\';
338 --wk2;
340 else
341 dwFlags |= URL_ESCAPE_UNSAFE;
342 state = 5;
345 while (*wk1) {
346 switch (state) {
347 case 0:
348 if (!isalnumW(*wk1)) {state = 3; break;}
349 *wk2++ = *wk1++;
350 if (!isalnumW(*wk1)) {state = 3; break;}
351 *wk2++ = *wk1++;
352 state = 1;
353 break;
354 case 1:
355 *wk2++ = *wk1;
356 if (*wk1++ == ':') state = 2;
357 break;
358 case 2:
359 *wk2++ = *wk1++;
360 if (*wk1 != '/') {state = 6; break;}
361 *wk2++ = *wk1++;
362 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
363 && !strncmpW(wszFile, pszUrl, sizeof(wszFile)/sizeof(WCHAR))
364 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
365 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
366 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
367 wk1++;
369 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
370 wk1++;
371 state = 4;
372 break;
373 case 3:
374 nWkLen = strlenW(wk1);
375 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
376 mp = wk2;
377 wk1 += nWkLen;
378 wk2 += nWkLen;
380 if(slash) {
381 while(mp < wk2) {
382 if(*mp == '/' || *mp == '\\')
383 *mp = slash;
384 mp++;
387 break;
388 case 4:
389 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
390 {state = 3; break;}
391 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
392 *wk2++ = *wk1++;
393 state = 5;
394 if (!*wk1) {
395 if(slash)
396 *wk2++ = slash;
397 else
398 *wk2++ = '/';
400 break;
401 case 5:
402 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
403 while(*wk1 == '/' || *wk1 == '\\') {
404 if(slash)
405 *wk2++ = slash;
406 else
407 *wk2++ = *wk1;
408 wk1++;
410 state = 6;
411 break;
412 case 6:
413 if(dwFlags & URL_DONT_SIMPLIFY) {
414 state = 3;
415 break;
418 /* Now at root location, cannot back up any more. */
419 /* "root" will point at the '/' */
421 root = wk2-1;
422 while (*wk1) {
423 mp = strchrW(wk1, '/');
424 mp2 = strchrW(wk1, '\\');
425 if(mp2 && (!mp || mp2 < mp))
426 mp = mp2;
427 if (!mp) {
428 nWkLen = strlenW(wk1);
429 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
430 wk1 += nWkLen;
431 wk2 += nWkLen;
432 continue;
434 nLen = mp - wk1;
435 if(nLen) {
436 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
437 wk2 += nLen;
438 wk1 += nLen;
440 if(slash)
441 *wk2++ = slash;
442 else
443 *wk2++ = *wk1;
444 wk1++;
446 if (*wk1 == '.') {
447 TRACE("found '/.'\n");
448 if (wk1[1] == '/' || wk1[1] == '\\') {
449 /* case of /./ -> skip the ./ */
450 wk1 += 2;
452 else if (wk1[1] == '.') {
453 /* found /.. look for next / */
454 TRACE("found '/..'\n");
455 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
456 || wk1[2] == '#' || !wk1[2]) {
457 /* case /../ -> need to backup wk2 */
458 TRACE("found '/../'\n");
459 *(wk2-1) = '\0'; /* set end of string */
460 mp = strrchrW(root, '/');
461 mp2 = strrchrW(root, '\\');
462 if(mp2 && (!mp || mp2 < mp))
463 mp = mp2;
464 if (mp && (mp >= root)) {
465 /* found valid backup point */
466 wk2 = mp + 1;
467 if(wk1[2] != '/' && wk1[2] != '\\')
468 wk1 += 2;
469 else
470 wk1 += 3;
472 else {
473 /* did not find point, restore '/' */
474 *(wk2-1) = slash;
480 *wk2 = '\0';
481 break;
482 default:
483 FIXME("how did we get here - state=%d\n", state);
484 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
485 return E_INVALIDARG;
487 *wk2 = '\0';
488 TRACE("Simplified, orig <%s>, simple <%s>\n",
489 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
491 nLen = lstrlenW(lpszUrlCpy);
492 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
493 lpszUrlCpy[--nLen]=0;
495 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
496 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
498 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
499 URL_ESCAPE_SPACES_ONLY |
500 URL_ESCAPE_PERCENT |
501 URL_DONT_ESCAPE_EXTRA_INFO |
502 URL_ESCAPE_SEGMENT_ONLY ))) {
503 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
504 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
505 EscapeFlags);
506 } else { /* No escaping needed, just copy the string */
507 nLen = lstrlenW(lpszUrlCpy);
508 if(nLen < *pcchCanonicalized)
509 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
510 else {
511 hr = E_POINTER;
512 nLen++;
514 *pcchCanonicalized = nLen;
517 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
519 if (hr == S_OK)
520 TRACE("result %s\n", debugstr_w(pszCanonicalized));
522 return hr;
525 /*************************************************************************
526 * UrlCombineA [SHLWAPI.@]
528 * Combine two Urls.
530 * PARAMS
531 * pszBase [I] Base Url
532 * pszRelative [I] Url to combine with pszBase
533 * pszCombined [O] Destination for combined Url
534 * pcchCombined [O] Destination for length of pszCombined
535 * dwFlags [I] URL_ flags from "shlwapi.h"
537 * RETURNS
538 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
539 * contains its length.
540 * Failure: An HRESULT error code indicating the error.
542 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
543 LPSTR pszCombined, LPDWORD pcchCombined,
544 DWORD dwFlags)
546 LPWSTR base, relative, combined;
547 DWORD ret, len, len2;
549 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
550 debugstr_a(pszBase),debugstr_a(pszRelative),
551 pcchCombined?*pcchCombined:0,dwFlags);
553 if(!pszBase || !pszRelative || !pcchCombined)
554 return E_INVALIDARG;
556 base = HeapAlloc(GetProcessHeap(), 0,
557 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
558 relative = base + INTERNET_MAX_URL_LENGTH;
559 combined = relative + INTERNET_MAX_URL_LENGTH;
561 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
562 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
563 len = *pcchCombined;
565 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
566 if (ret != S_OK) {
567 *pcchCombined = len;
568 HeapFree(GetProcessHeap(), 0, base);
569 return ret;
572 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
573 if (len2 > *pcchCombined) {
574 *pcchCombined = len2;
575 HeapFree(GetProcessHeap(), 0, base);
576 return E_POINTER;
578 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
579 0, 0);
580 *pcchCombined = len2;
581 HeapFree(GetProcessHeap(), 0, base);
582 return S_OK;
585 /*************************************************************************
586 * UrlCombineW [SHLWAPI.@]
588 * See UrlCombineA.
590 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
591 LPWSTR pszCombined, LPDWORD pcchCombined,
592 DWORD dwFlags)
594 PARSEDURLW base, relative;
595 DWORD myflags, sizeloc = 0;
596 DWORD len, res1, res2, process_case = 0;
597 LPWSTR work, preliminary, mbase, mrelative;
598 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
599 HRESULT ret;
601 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
602 debugstr_w(pszBase),debugstr_w(pszRelative),
603 pcchCombined?*pcchCombined:0,dwFlags);
605 if(!pszBase || !pszRelative || !pcchCombined)
606 return E_INVALIDARG;
608 base.cbSize = sizeof(base);
609 relative.cbSize = sizeof(relative);
611 /* Get space for duplicates of the input and the output */
612 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
613 sizeof(WCHAR));
614 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
615 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
616 *preliminary = '\0';
618 /* Canonicalize the base input prior to looking for the scheme */
619 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
620 len = INTERNET_MAX_URL_LENGTH;
621 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
623 /* Canonicalize the relative input prior to looking for the scheme */
624 len = INTERNET_MAX_URL_LENGTH;
625 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
627 /* See if the base has a scheme */
628 res1 = ParseURLW(mbase, &base);
629 if (res1) {
630 /* if pszBase has no scheme, then return pszRelative */
631 TRACE("no scheme detected in Base\n");
632 process_case = 1;
634 else do {
635 BOOL manual_search = FALSE;
637 /* mk is a special case */
638 if(base.nScheme == URL_SCHEME_MK) {
639 static const WCHAR wsz[] = {':',':',0};
641 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
642 if(ptr) {
643 int delta;
645 ptr += 2;
646 delta = ptr-base.pszSuffix;
647 base.cchProtocol += delta;
648 base.pszSuffix += delta;
649 base.cchSuffix -= delta;
651 }else {
652 /* get size of location field (if it exists) */
653 work = (LPWSTR)base.pszSuffix;
654 sizeloc = 0;
655 if (*work++ == '/') {
656 if (*work++ == '/') {
657 /* At this point have start of location and
658 * it ends at next '/' or end of string.
660 while(*work && (*work != '/')) work++;
661 sizeloc = (DWORD)(work - base.pszSuffix);
666 /* If there is a '#' and the characters immediately preceding it are
667 * ".htm[l]", then begin looking for the last leaf starting from
668 * the '#'. Otherwise the '#' is not meaningful and just start
669 * looking from the end. */
670 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
671 const WCHAR htmlW[] = {'.','h','t','m','l',0};
672 const int len_htmlW = 5;
673 const WCHAR htmW[] = {'.','h','t','m',0};
674 const int len_htmW = 4;
676 if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
677 work -= len_htmW;
678 if (strncmpiW(work, htmW, len_htmW) == 0)
679 manual_search = TRUE;
680 work += len_htmW;
683 if (!manual_search &&
684 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
685 work -= len_htmlW;
686 if (strncmpiW(work, htmlW, len_htmlW) == 0)
687 manual_search = TRUE;
688 work += len_htmlW;
692 if (manual_search) {
693 /* search backwards starting from the current position */
694 while (*work != '/' && work > base.pszSuffix + sizeloc)
695 --work;
696 if (work > base.pszSuffix + sizeloc)
697 base.cchSuffix = work - base.pszSuffix + 1;
698 }else {
699 /* search backwards starting from the end of the string */
700 work = strrchrW((base.pszSuffix+sizeloc), '/');
701 if (work) {
702 len = (DWORD)(work - base.pszSuffix + 1);
703 base.cchSuffix = len;
708 * At this point:
709 * .pszSuffix points to location (starting with '//')
710 * .cchSuffix length of location (above) and rest less the last
711 * leaf (if any)
712 * sizeloc length of location (above) up to but not including
713 * the last '/'
716 res2 = ParseURLW(mrelative, &relative);
717 if (res2) {
718 /* no scheme in pszRelative */
719 TRACE("no scheme detected in Relative\n");
720 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
721 relative.cchSuffix = strlenW(mrelative);
722 if (*pszRelative == ':') {
723 /* case that is either left alone or uses pszBase */
724 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
725 process_case = 5;
726 break;
728 process_case = 1;
729 break;
731 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
732 /* case that becomes "file:///" */
733 strcpyW(preliminary, myfilestr);
734 process_case = 1;
735 break;
737 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
738 /* pszRelative has location and rest */
739 process_case = 3;
740 break;
742 if (*mrelative == '/') {
743 /* case where pszRelative is root to location */
744 process_case = 4;
745 break;
747 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
748 break;
751 /* handle cases where pszRelative has scheme */
752 if ((base.cchProtocol == relative.cchProtocol) &&
753 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
755 /* since the schemes are the same */
756 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
757 /* case where pszRelative replaces location and following */
758 process_case = 3;
759 break;
761 if (*relative.pszSuffix == '/') {
762 /* case where pszRelative is root to location */
763 process_case = 4;
764 break;
766 /* replace either just location if base's location starts with a
767 * slash or otherwise everything */
768 process_case = (*base.pszSuffix == '/') ? 5 : 1;
769 break;
771 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
772 /* case where pszRelative replaces scheme, location,
773 * and following and handles PLUGGABLE
775 process_case = 2;
776 break;
778 process_case = 1;
779 break;
780 } while(FALSE); /* a little trick to allow easy exit from nested if's */
782 ret = S_OK;
783 switch (process_case) {
785 case 1: /*
786 * Return pszRelative appended to what ever is in pszCombined,
787 * (which may the string "file:///"
789 strcatW(preliminary, mrelative);
790 break;
792 case 2: /* case where pszRelative replaces scheme, and location */
793 strcpyW(preliminary, mrelative);
794 break;
796 case 3: /*
797 * Return the pszBase scheme with pszRelative. Basically
798 * keeps the scheme and replaces the domain and following.
800 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
801 work = preliminary + base.cchProtocol + 1;
802 strcpyW(work, relative.pszSuffix);
803 break;
805 case 4: /*
806 * Return the pszBase scheme and location but everything
807 * after the location is pszRelative. (Replace document
808 * from root on.)
810 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
811 work = preliminary + base.cchProtocol + 1 + sizeloc;
812 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
813 *(work++) = '/';
814 strcpyW(work, relative.pszSuffix);
815 break;
817 case 5: /*
818 * Return the pszBase without its document (if any) and
819 * append pszRelative after its scheme.
821 memcpy(preliminary, base.pszProtocol,
822 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
823 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
824 if (*work++ != '/')
825 *(work++) = '/';
826 strcpyW(work, relative.pszSuffix);
827 break;
829 default:
830 FIXME("How did we get here????? process_case=%d\n", process_case);
831 ret = E_INVALIDARG;
834 if (ret == S_OK) {
835 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
836 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
837 if(SUCCEEDED(ret) && pszCombined) {
838 lstrcpyW(pszCombined, mrelative);
840 TRACE("return-%d len=%d, %s\n",
841 process_case, *pcchCombined, debugstr_w(pszCombined));
843 HeapFree(GetProcessHeap(), 0, preliminary);
844 return ret;
847 /*************************************************************************
848 * UrlEscapeA [SHLWAPI.@]
851 HRESULT WINAPI UrlEscapeA(
852 LPCSTR pszUrl,
853 LPSTR pszEscaped,
854 LPDWORD pcchEscaped,
855 DWORD dwFlags)
857 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
858 WCHAR *escapedW = bufW;
859 UNICODE_STRING urlW;
860 HRESULT ret;
861 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
863 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
864 return E_INVALIDARG;
866 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
867 return E_INVALIDARG;
868 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
869 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
870 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
872 if(ret == S_OK) {
873 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
874 if(*pcchEscaped > lenA) {
875 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
876 pszEscaped[lenA] = 0;
877 *pcchEscaped = lenA;
878 } else {
879 *pcchEscaped = lenA + 1;
880 ret = E_POINTER;
883 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
884 RtlFreeUnicodeString(&urlW);
885 return ret;
888 #define WINE_URL_BASH_AS_SLASH 0x01
889 #define WINE_URL_COLLAPSE_SLASHES 0x02
890 #define WINE_URL_ESCAPE_SLASH 0x04
891 #define WINE_URL_ESCAPE_HASH 0x08
892 #define WINE_URL_ESCAPE_QUESTION 0x10
893 #define WINE_URL_STOP_ON_HASH 0x20
894 #define WINE_URL_STOP_ON_QUESTION 0x40
896 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
899 if (isalnumW(ch))
900 return FALSE;
902 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
903 if(ch == ' ')
904 return TRUE;
905 else
906 return FALSE;
909 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
910 return TRUE;
912 if (ch <= 31 || ch >= 127)
913 return TRUE;
915 else {
916 switch (ch) {
917 case ' ':
918 case '<':
919 case '>':
920 case '\"':
921 case '{':
922 case '}':
923 case '|':
924 case '\\':
925 case '^':
926 case ']':
927 case '[':
928 case '`':
929 case '&':
930 return TRUE;
932 case '/':
933 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
934 return FALSE;
936 case '?':
937 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
938 return FALSE;
940 case '#':
941 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
942 return FALSE;
944 default:
945 return FALSE;
951 /*************************************************************************
952 * UrlEscapeW [SHLWAPI.@]
954 * Converts unsafe characters in a Url into escape sequences.
956 * PARAMS
957 * pszUrl [I] Url to modify
958 * pszEscaped [O] Destination for modified Url
959 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
960 * dwFlags [I] URL_ flags from "shlwapi.h"
962 * RETURNS
963 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
964 * contains its length.
965 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
966 * pcchEscaped is set to the required length.
968 * Converts unsafe characters into their escape sequences.
970 * NOTES
971 * - By default this function stops converting at the first '?' or
972 * '#' character.
973 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
974 * converted, but the conversion continues past a '?' or '#'.
975 * - Note that this function did not work well (or at all) in shlwapi version 4.
977 * BUGS
978 * Only the following flags are implemented:
979 *| URL_ESCAPE_SPACES_ONLY
980 *| URL_DONT_ESCAPE_EXTRA_INFO
981 *| URL_ESCAPE_SEGMENT_ONLY
982 *| URL_ESCAPE_PERCENT
984 HRESULT WINAPI UrlEscapeW(
985 LPCWSTR pszUrl,
986 LPWSTR pszEscaped,
987 LPDWORD pcchEscaped,
988 DWORD dwFlags)
990 LPCWSTR src;
991 DWORD needed = 0, ret;
992 BOOL stop_escaping = FALSE;
993 WCHAR next[5], *dst = pszEscaped;
994 INT len;
995 PARSEDURLW parsed_url;
996 DWORD int_flags;
997 DWORD slashes = 0;
998 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1000 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
1001 pcchEscaped, dwFlags);
1003 if(!pszUrl || !pcchEscaped)
1004 return E_INVALIDARG;
1006 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1007 URL_ESCAPE_SEGMENT_ONLY |
1008 URL_DONT_ESCAPE_EXTRA_INFO |
1009 URL_ESCAPE_PERCENT))
1010 FIXME("Unimplemented flags: %08x\n", dwFlags);
1012 /* fix up flags */
1013 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1014 /* if SPACES_ONLY specified, reset the other controls */
1015 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1016 URL_ESCAPE_PERCENT |
1017 URL_ESCAPE_SEGMENT_ONLY);
1019 else
1020 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1021 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1024 int_flags = 0;
1025 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1026 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1027 } else {
1028 parsed_url.cbSize = sizeof(parsed_url);
1029 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1030 parsed_url.nScheme = URL_SCHEME_INVALID;
1032 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1034 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1035 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1037 switch(parsed_url.nScheme) {
1038 case URL_SCHEME_FILE:
1039 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1040 int_flags &= ~WINE_URL_STOP_ON_HASH;
1041 break;
1043 case URL_SCHEME_HTTP:
1044 case URL_SCHEME_HTTPS:
1045 int_flags |= WINE_URL_BASH_AS_SLASH;
1046 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1047 int_flags |= WINE_URL_ESCAPE_SLASH;
1048 break;
1050 case URL_SCHEME_MAILTO:
1051 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1052 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1053 break;
1055 case URL_SCHEME_INVALID:
1056 break;
1058 case URL_SCHEME_FTP:
1059 default:
1060 if(parsed_url.pszSuffix[0] != '/')
1061 int_flags |= WINE_URL_ESCAPE_SLASH;
1062 break;
1066 for(src = pszUrl; *src; ) {
1067 WCHAR cur = *src;
1068 len = 0;
1070 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1071 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1072 while(cur == '/' || cur == '\\') {
1073 slashes++;
1074 cur = *++src;
1076 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1077 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1078 src += localhost_len + 1;
1079 slashes = 3;
1082 switch(slashes) {
1083 case 1:
1084 case 3:
1085 next[0] = next[1] = next[2] = '/';
1086 len = 3;
1087 break;
1088 case 0:
1089 len = 0;
1090 break;
1091 default:
1092 next[0] = next[1] = '/';
1093 len = 2;
1094 break;
1097 if(len == 0) {
1099 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1100 stop_escaping = TRUE;
1102 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1103 stop_escaping = TRUE;
1105 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1107 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1108 next[0] = '%';
1109 next[1] = hexDigits[(cur >> 4) & 0xf];
1110 next[2] = hexDigits[cur & 0xf];
1111 len = 3;
1112 } else {
1113 next[0] = cur;
1114 len = 1;
1116 src++;
1119 if(needed + len <= *pcchEscaped) {
1120 memcpy(dst, next, len*sizeof(WCHAR));
1121 dst += len;
1123 needed += len;
1126 if(needed < *pcchEscaped) {
1127 *dst = '\0';
1128 ret = S_OK;
1129 } else {
1130 needed++; /* add one for the '\0' */
1131 ret = E_POINTER;
1133 *pcchEscaped = needed;
1134 return ret;
1138 /*************************************************************************
1139 * UrlUnescapeA [SHLWAPI.@]
1141 * Converts Url escape sequences back to ordinary characters.
1143 * PARAMS
1144 * pszUrl [I/O] Url to convert
1145 * pszUnescaped [O] Destination for converted Url
1146 * pcchUnescaped [I/O] Size of output string
1147 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1149 * RETURNS
1150 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1151 * dwFlags includes URL_ESCAPE_INPLACE.
1152 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1153 * this case pcchUnescaped is set to the size required.
1154 * NOTES
1155 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1156 * the first occurrence of either a '?' or '#' character.
1158 HRESULT WINAPI UrlUnescapeA(
1159 LPSTR pszUrl,
1160 LPSTR pszUnescaped,
1161 LPDWORD pcchUnescaped,
1162 DWORD dwFlags)
1164 char *dst, next;
1165 LPCSTR src;
1166 HRESULT ret;
1167 DWORD needed;
1168 BOOL stop_unescaping = FALSE;
1170 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1171 pcchUnescaped, dwFlags);
1173 if (!pszUrl) return E_INVALIDARG;
1175 if(dwFlags & URL_UNESCAPE_INPLACE)
1176 dst = pszUrl;
1177 else
1179 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1180 dst = pszUnescaped;
1183 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1184 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1185 (*src == '#' || *src == '?')) {
1186 stop_unescaping = TRUE;
1187 next = *src;
1188 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1189 && stop_unescaping == FALSE) {
1190 INT ih;
1191 char buf[3];
1192 memcpy(buf, src + 1, 2);
1193 buf[2] = '\0';
1194 ih = strtol(buf, NULL, 16);
1195 next = (CHAR) ih;
1196 src += 2; /* Advance to end of escape */
1197 } else
1198 next = *src;
1200 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1201 *dst++ = next;
1204 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1205 *dst = '\0';
1206 ret = S_OK;
1207 } else {
1208 needed++; /* add one for the '\0' */
1209 ret = E_POINTER;
1211 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1212 *pcchUnescaped = needed;
1214 if (ret == S_OK) {
1215 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1216 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1219 return ret;
1222 /*************************************************************************
1223 * UrlUnescapeW [SHLWAPI.@]
1225 * See UrlUnescapeA.
1227 HRESULT WINAPI UrlUnescapeW(
1228 LPWSTR pszUrl,
1229 LPWSTR pszUnescaped,
1230 LPDWORD pcchUnescaped,
1231 DWORD dwFlags)
1233 WCHAR *dst, next;
1234 LPCWSTR src;
1235 HRESULT ret;
1236 DWORD needed;
1237 BOOL stop_unescaping = FALSE;
1239 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1240 pcchUnescaped, dwFlags);
1242 if(!pszUrl) return E_INVALIDARG;
1244 if(dwFlags & URL_UNESCAPE_INPLACE)
1245 dst = pszUrl;
1246 else
1248 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1249 dst = pszUnescaped;
1252 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1253 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1254 (*src == '#' || *src == '?')) {
1255 stop_unescaping = TRUE;
1256 next = *src;
1257 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1258 && stop_unescaping == FALSE) {
1259 INT ih;
1260 WCHAR buf[5] = {'0','x',0};
1261 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1262 buf[4] = 0;
1263 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1264 next = (WCHAR) ih;
1265 src += 2; /* Advance to end of escape */
1266 } else
1267 next = *src;
1269 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1270 *dst++ = next;
1273 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1274 *dst = '\0';
1275 ret = S_OK;
1276 } else {
1277 needed++; /* add one for the '\0' */
1278 ret = E_POINTER;
1280 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1281 *pcchUnescaped = needed;
1283 if (ret == S_OK) {
1284 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1285 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1288 return ret;
1291 /*************************************************************************
1292 * UrlGetLocationA [SHLWAPI.@]
1294 * Get the location from a Url.
1296 * PARAMS
1297 * pszUrl [I] Url to get the location from
1299 * RETURNS
1300 * A pointer to the start of the location in pszUrl, or NULL if there is
1301 * no location.
1303 * NOTES
1304 * - MSDN erroneously states that "The location is the segment of the Url
1305 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1306 * stop at '?' and always return a NULL in this case.
1307 * - MSDN also erroneously states that "If a file URL has a query string,
1308 * the returned string is the query string". In all tested cases, if the
1309 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1310 *| Result Url
1311 *| ------ ---
1312 *| NULL file://aa/b/cd#hohoh
1313 *| #hohoh http://aa/b/cd#hohoh
1314 *| NULL fi://aa/b/cd#hohoh
1315 *| #hohoh ff://aa/b/cd#hohoh
1317 LPCSTR WINAPI UrlGetLocationA(
1318 LPCSTR pszUrl)
1320 PARSEDURLA base;
1321 DWORD res1;
1323 base.cbSize = sizeof(base);
1324 res1 = ParseURLA(pszUrl, &base);
1325 if (res1) return NULL; /* invalid scheme */
1327 /* if scheme is file: then never return pointer */
1328 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1330 /* Look for '#' and return its addr */
1331 return strchr(base.pszSuffix, '#');
1334 /*************************************************************************
1335 * UrlGetLocationW [SHLWAPI.@]
1337 * See UrlGetLocationA.
1339 LPCWSTR WINAPI UrlGetLocationW(
1340 LPCWSTR pszUrl)
1342 PARSEDURLW base;
1343 DWORD res1;
1345 base.cbSize = sizeof(base);
1346 res1 = ParseURLW(pszUrl, &base);
1347 if (res1) return NULL; /* invalid scheme */
1349 /* if scheme is file: then never return pointer */
1350 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1352 /* Look for '#' and return its addr */
1353 return strchrW(base.pszSuffix, '#');
1356 /*************************************************************************
1357 * UrlCompareA [SHLWAPI.@]
1359 * Compare two Urls.
1361 * PARAMS
1362 * pszUrl1 [I] First Url to compare
1363 * pszUrl2 [I] Url to compare to pszUrl1
1364 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1366 * RETURNS
1367 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1368 * than, equal to, or less than pszUrl1 respectively.
1370 INT WINAPI UrlCompareA(
1371 LPCSTR pszUrl1,
1372 LPCSTR pszUrl2,
1373 BOOL fIgnoreSlash)
1375 INT ret, len, len1, len2;
1377 if (!fIgnoreSlash)
1378 return strcmp(pszUrl1, pszUrl2);
1379 len1 = strlen(pszUrl1);
1380 if (pszUrl1[len1-1] == '/') len1--;
1381 len2 = strlen(pszUrl2);
1382 if (pszUrl2[len2-1] == '/') len2--;
1383 if (len1 == len2)
1384 return strncmp(pszUrl1, pszUrl2, len1);
1385 len = min(len1, len2);
1386 ret = strncmp(pszUrl1, pszUrl2, len);
1387 if (ret) return ret;
1388 if (len1 > len2) return 1;
1389 return -1;
1392 /*************************************************************************
1393 * UrlCompareW [SHLWAPI.@]
1395 * See UrlCompareA.
1397 INT WINAPI UrlCompareW(
1398 LPCWSTR pszUrl1,
1399 LPCWSTR pszUrl2,
1400 BOOL fIgnoreSlash)
1402 INT ret;
1403 size_t len, len1, len2;
1405 if (!fIgnoreSlash)
1406 return strcmpW(pszUrl1, pszUrl2);
1407 len1 = strlenW(pszUrl1);
1408 if (pszUrl1[len1-1] == '/') len1--;
1409 len2 = strlenW(pszUrl2);
1410 if (pszUrl2[len2-1] == '/') len2--;
1411 if (len1 == len2)
1412 return strncmpW(pszUrl1, pszUrl2, len1);
1413 len = min(len1, len2);
1414 ret = strncmpW(pszUrl1, pszUrl2, len);
1415 if (ret) return ret;
1416 if (len1 > len2) return 1;
1417 return -1;
1420 /*************************************************************************
1421 * HashData [SHLWAPI.@]
1423 * Hash an input block into a variable sized digest.
1425 * PARAMS
1426 * lpSrc [I] Input block
1427 * nSrcLen [I] Length of lpSrc
1428 * lpDest [I] Output for hash digest
1429 * nDestLen [I] Length of lpDest
1431 * RETURNS
1432 * Success: TRUE. lpDest is filled with the computed hash value.
1433 * Failure: FALSE, if any argument is invalid.
1435 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1436 unsigned char *lpDest, DWORD nDestLen)
1438 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1440 if (!lpSrc || !lpDest)
1441 return E_INVALIDARG;
1443 while (destCount >= 0)
1445 lpDest[destCount] = (destCount & 0xff);
1446 destCount--;
1449 while (srcCount >= 0)
1451 destCount = nDestLen - 1;
1452 while (destCount >= 0)
1454 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1455 destCount--;
1457 srcCount--;
1459 return S_OK;
1462 /*************************************************************************
1463 * UrlHashA [SHLWAPI.@]
1465 * Produce a Hash from a Url.
1467 * PARAMS
1468 * pszUrl [I] Url to hash
1469 * lpDest [O] Destinationh for hash
1470 * nDestLen [I] Length of lpDest
1472 * RETURNS
1473 * Success: S_OK. lpDest is filled with the computed hash value.
1474 * Failure: E_INVALIDARG, if any argument is invalid.
1476 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1478 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1479 return E_INVALIDARG;
1481 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1482 return S_OK;
1485 /*************************************************************************
1486 * UrlHashW [SHLWAPI.@]
1488 * See UrlHashA.
1490 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1492 char szUrl[MAX_PATH];
1494 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1496 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1497 return E_INVALIDARG;
1499 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1500 * return the same digests for the same URL.
1502 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1503 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1504 return S_OK;
1507 /*************************************************************************
1508 * UrlApplySchemeA [SHLWAPI.@]
1510 * Apply a scheme to a Url.
1512 * PARAMS
1513 * pszIn [I] Url to apply scheme to
1514 * pszOut [O] Destination for modified Url
1515 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1516 * dwFlags [I] URL_ flags from "shlwapi.h"
1518 * RETURNS
1519 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1520 * Failure: An HRESULT error code describing the error.
1522 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1524 LPWSTR in, out;
1525 HRESULT ret;
1526 DWORD len;
1528 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1529 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1531 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1533 in = HeapAlloc(GetProcessHeap(), 0,
1534 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1535 out = in + INTERNET_MAX_URL_LENGTH;
1537 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1538 len = INTERNET_MAX_URL_LENGTH;
1540 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1541 if (ret != S_OK) {
1542 HeapFree(GetProcessHeap(), 0, in);
1543 return ret;
1546 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1547 if (len > *pcchOut) {
1548 ret = E_POINTER;
1549 goto cleanup;
1552 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1553 len--;
1555 cleanup:
1556 *pcchOut = len;
1557 HeapFree(GetProcessHeap(), 0, in);
1558 return ret;
1561 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1563 HKEY newkey;
1564 BOOL j;
1565 INT index;
1566 DWORD value_len, data_len, dwType, i;
1567 WCHAR reg_path[MAX_PATH];
1568 WCHAR value[MAX_PATH], data[MAX_PATH];
1569 WCHAR Wxx, Wyy;
1571 MultiByteToWideChar(0, 0,
1572 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1573 -1, reg_path, MAX_PATH);
1574 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1575 index = 0;
1576 while(value_len = data_len = MAX_PATH,
1577 RegEnumValueW(newkey, index, value, &value_len,
1578 0, &dwType, (LPVOID)data, &data_len) == 0) {
1579 TRACE("guess %d %s is %s\n",
1580 index, debugstr_w(value), debugstr_w(data));
1582 j = FALSE;
1583 for(i=0; i<value_len; i++) {
1584 Wxx = pszIn[i];
1585 Wyy = value[i];
1586 /* remember that TRUE is not-equal */
1587 j = ChrCmpIW(Wxx, Wyy);
1588 if (j) break;
1590 if ((i == value_len) && !j) {
1591 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1592 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1593 RegCloseKey(newkey);
1594 return E_POINTER;
1596 strcpyW(pszOut, data);
1597 strcatW(pszOut, pszIn);
1598 *pcchOut = strlenW(pszOut);
1599 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1600 RegCloseKey(newkey);
1601 return S_OK;
1603 index++;
1605 RegCloseKey(newkey);
1606 return E_FAIL;
1609 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1611 HKEY newkey;
1612 DWORD data_len, dwType;
1613 WCHAR data[MAX_PATH];
1615 static const WCHAR prefix_keyW[] =
1616 {'S','o','f','t','w','a','r','e',
1617 '\\','M','i','c','r','o','s','o','f','t',
1618 '\\','W','i','n','d','o','w','s',
1619 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1620 '\\','U','R','L',
1621 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1623 /* get and prepend default */
1624 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1625 data_len = sizeof(data);
1626 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1627 RegCloseKey(newkey);
1628 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1629 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1630 return E_POINTER;
1632 strcpyW(pszOut, data);
1633 strcatW(pszOut, pszIn);
1634 *pcchOut = strlenW(pszOut);
1635 TRACE("used default %s\n", debugstr_w(pszOut));
1636 return S_OK;
1639 /*************************************************************************
1640 * UrlApplySchemeW [SHLWAPI.@]
1642 * See UrlApplySchemeA.
1644 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1646 PARSEDURLW in_scheme;
1647 DWORD res1;
1648 HRESULT ret;
1650 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1651 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1653 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1655 if (dwFlags & URL_APPLY_GUESSFILE) {
1656 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1657 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1658 strcpyW(pszOut, pszIn);
1659 *pcchOut = strlenW(pszOut);
1660 return S_FALSE;
1663 in_scheme.cbSize = sizeof(in_scheme);
1664 /* See if the base has a scheme */
1665 res1 = ParseURLW(pszIn, &in_scheme);
1666 if (res1) {
1667 /* no scheme in input, need to see if we need to guess */
1668 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1669 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1670 return ret;
1673 else {
1674 /* we have a scheme, see if valid (known scheme) */
1675 if (in_scheme.nScheme) {
1676 /* have valid scheme, so just copy and exit */
1677 if (strlenW(pszIn) + 1 > *pcchOut) {
1678 *pcchOut = strlenW(pszIn) + 1;
1679 return E_POINTER;
1681 strcpyW(pszOut, pszIn);
1682 *pcchOut = strlenW(pszOut);
1683 TRACE("valid scheme, returning copy\n");
1684 return S_OK;
1688 /* If we are here, then either invalid scheme,
1689 * or no scheme and can't/failed guess.
1691 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1692 ((res1 != 0)) ) &&
1693 (dwFlags & URL_APPLY_DEFAULT)) {
1694 /* find and apply default scheme */
1695 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1698 return S_FALSE;
1701 /*************************************************************************
1702 * UrlIsA [SHLWAPI.@]
1704 * Determine if a Url is of a certain class.
1706 * PARAMS
1707 * pszUrl [I] Url to check
1708 * Urlis [I] URLIS_ constant from "shlwapi.h"
1710 * RETURNS
1711 * TRUE if pszUrl belongs to the class type in Urlis.
1712 * FALSE Otherwise.
1714 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1716 PARSEDURLA base;
1717 DWORD res1;
1718 LPCSTR last;
1720 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1722 if(!pszUrl)
1723 return FALSE;
1725 switch (Urlis) {
1727 case URLIS_OPAQUE:
1728 base.cbSize = sizeof(base);
1729 res1 = ParseURLA(pszUrl, &base);
1730 if (res1) return FALSE; /* invalid scheme */
1731 switch (base.nScheme)
1733 case URL_SCHEME_MAILTO:
1734 case URL_SCHEME_SHELL:
1735 case URL_SCHEME_JAVASCRIPT:
1736 case URL_SCHEME_VBSCRIPT:
1737 case URL_SCHEME_ABOUT:
1738 return TRUE;
1740 return FALSE;
1742 case URLIS_FILEURL:
1743 return !StrCmpNA("file:", pszUrl, 5);
1745 case URLIS_DIRECTORY:
1746 last = pszUrl + strlen(pszUrl) - 1;
1747 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1749 case URLIS_URL:
1750 return PathIsURLA(pszUrl);
1752 case URLIS_NOHISTORY:
1753 case URLIS_APPLIABLE:
1754 case URLIS_HASQUERY:
1755 default:
1756 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1758 return FALSE;
1761 /*************************************************************************
1762 * UrlIsW [SHLWAPI.@]
1764 * See UrlIsA.
1766 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1768 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1769 PARSEDURLW base;
1770 DWORD res1;
1771 LPCWSTR last;
1773 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1775 if(!pszUrl)
1776 return FALSE;
1778 switch (Urlis) {
1780 case URLIS_OPAQUE:
1781 base.cbSize = sizeof(base);
1782 res1 = ParseURLW(pszUrl, &base);
1783 if (res1) return FALSE; /* invalid scheme */
1784 switch (base.nScheme)
1786 case URL_SCHEME_MAILTO:
1787 case URL_SCHEME_SHELL:
1788 case URL_SCHEME_JAVASCRIPT:
1789 case URL_SCHEME_VBSCRIPT:
1790 case URL_SCHEME_ABOUT:
1791 return TRUE;
1793 return FALSE;
1795 case URLIS_FILEURL:
1796 return !strncmpW(stemp, pszUrl, 5);
1798 case URLIS_DIRECTORY:
1799 last = pszUrl + strlenW(pszUrl) - 1;
1800 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1802 case URLIS_URL:
1803 return PathIsURLW(pszUrl);
1805 case URLIS_NOHISTORY:
1806 case URLIS_APPLIABLE:
1807 case URLIS_HASQUERY:
1808 default:
1809 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1811 return FALSE;
1814 /*************************************************************************
1815 * UrlIsNoHistoryA [SHLWAPI.@]
1817 * Determine if a Url should not be stored in the users history list.
1819 * PARAMS
1820 * pszUrl [I] Url to check
1822 * RETURNS
1823 * TRUE, if pszUrl should be excluded from the history list,
1824 * FALSE otherwise.
1826 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1828 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1831 /*************************************************************************
1832 * UrlIsNoHistoryW [SHLWAPI.@]
1834 * See UrlIsNoHistoryA.
1836 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1838 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1841 /*************************************************************************
1842 * UrlIsOpaqueA [SHLWAPI.@]
1844 * Determine if a Url is opaque.
1846 * PARAMS
1847 * pszUrl [I] Url to check
1849 * RETURNS
1850 * TRUE if pszUrl is opaque,
1851 * FALSE Otherwise.
1853 * NOTES
1854 * An opaque Url is one that does not start with "<protocol>://".
1856 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1858 return UrlIsA(pszUrl, URLIS_OPAQUE);
1861 /*************************************************************************
1862 * UrlIsOpaqueW [SHLWAPI.@]
1864 * See UrlIsOpaqueA.
1866 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1868 return UrlIsW(pszUrl, URLIS_OPAQUE);
1871 /*************************************************************************
1872 * Scans for characters of type "type" and when not matching found,
1873 * returns pointer to it and length in size.
1875 * Characters tested based on RFC 1738
1877 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1879 static DWORD alwayszero = 0;
1880 BOOL cont = TRUE;
1882 *size = 0;
1884 switch(type){
1886 case SCHEME:
1887 while (cont) {
1888 if ( (islowerW(*start) && isalphaW(*start)) ||
1889 isdigitW(*start) ||
1890 (*start == '+') ||
1891 (*start == '-') ||
1892 (*start == '.')) {
1893 start++;
1894 (*size)++;
1896 else
1897 cont = FALSE;
1899 break;
1901 case USERPASS:
1902 while (cont) {
1903 if ( isalphaW(*start) ||
1904 isdigitW(*start) ||
1905 /* user/password only characters */
1906 (*start == ';') ||
1907 (*start == '?') ||
1908 (*start == '&') ||
1909 (*start == '=') ||
1910 /* *extra* characters */
1911 (*start == '!') ||
1912 (*start == '*') ||
1913 (*start == '\'') ||
1914 (*start == '(') ||
1915 (*start == ')') ||
1916 (*start == ',') ||
1917 /* *safe* characters */
1918 (*start == '$') ||
1919 (*start == '_') ||
1920 (*start == '+') ||
1921 (*start == '-') ||
1922 (*start == '.') ||
1923 (*start == ' ')) {
1924 start++;
1925 (*size)++;
1926 } else if (*start == '%') {
1927 if (isxdigitW(*(start+1)) &&
1928 isxdigitW(*(start+2))) {
1929 start += 3;
1930 *size += 3;
1931 } else
1932 cont = FALSE;
1933 } else
1934 cont = FALSE;
1936 break;
1938 case PORT:
1939 while (cont) {
1940 if (isdigitW(*start)) {
1941 start++;
1942 (*size)++;
1944 else
1945 cont = FALSE;
1947 break;
1949 case HOST:
1950 while (cont) {
1951 if (isalnumW(*start) ||
1952 (*start == '-') ||
1953 (*start == '.') ||
1954 (*start == ' ') ) {
1955 start++;
1956 (*size)++;
1958 else
1959 cont = FALSE;
1961 break;
1962 default:
1963 FIXME("unknown type %d\n", type);
1964 return (LPWSTR)&alwayszero;
1966 /* TRACE("scanned %d characters next char %p<%c>\n",
1967 *size, start, *start); */
1968 return start;
1971 /*************************************************************************
1972 * Attempt to parse URL into pieces.
1974 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1976 LPCWSTR work;
1978 memset(pl, 0, sizeof(WINE_PARSE_URL));
1979 pl->pScheme = pszUrl;
1980 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1981 if (!*work || (*work != ':')) goto ErrorExit;
1982 work++;
1983 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
1984 pl->pUserName = work + 2;
1985 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1986 if (*work == ':' ) {
1987 /* parse password */
1988 work++;
1989 pl->pPassword = work;
1990 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1991 if (*work != '@') {
1992 /* what we just parsed must be the hostname and port
1993 * so reset pointers and clear then let it parse */
1994 pl->szUserName = pl->szPassword = 0;
1995 work = pl->pUserName - 1;
1996 pl->pUserName = pl->pPassword = 0;
1998 } else if (*work == '@') {
1999 /* no password */
2000 pl->szPassword = 0;
2001 pl->pPassword = 0;
2002 } else if (!*work || (*work == '/') || (*work == '.')) {
2003 /* what was parsed was hostname, so reset pointers and let it parse */
2004 pl->szUserName = pl->szPassword = 0;
2005 work = pl->pUserName - 1;
2006 pl->pUserName = pl->pPassword = 0;
2007 } else goto ErrorExit;
2009 /* now start parsing hostname or hostnumber */
2010 work++;
2011 pl->pHostName = work;
2012 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2013 if (*work == ':') {
2014 /* parse port */
2015 work++;
2016 pl->pPort = work;
2017 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2019 if (*work == '/') {
2020 /* see if query string */
2021 pl->pQuery = strchrW(work, '?');
2022 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2024 SuccessExit:
2025 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2026 pl->pScheme, pl->szScheme,
2027 pl->pUserName, pl->szUserName,
2028 pl->pPassword, pl->szPassword,
2029 pl->pHostName, pl->szHostName,
2030 pl->pPort, pl->szPort,
2031 pl->pQuery, pl->szQuery);
2032 return S_OK;
2033 ErrorExit:
2034 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2035 return E_INVALIDARG;
2038 /*************************************************************************
2039 * UrlGetPartA [SHLWAPI.@]
2041 * Retrieve part of a Url.
2043 * PARAMS
2044 * pszIn [I] Url to parse
2045 * pszOut [O] Destination for part of pszIn requested
2046 * pcchOut [I] Size of pszOut
2047 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2048 * needed size of pszOut INCLUDING '\0'.
2049 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2050 * dwFlags [I] URL_ flags from "shlwapi.h"
2052 * RETURNS
2053 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2054 * Failure: An HRESULT error code describing the error.
2056 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2057 DWORD dwPart, DWORD dwFlags)
2059 LPWSTR in, out;
2060 DWORD ret, len, len2;
2062 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2063 return E_INVALIDARG;
2065 in = HeapAlloc(GetProcessHeap(), 0,
2066 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2067 out = in + INTERNET_MAX_URL_LENGTH;
2069 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2071 len = INTERNET_MAX_URL_LENGTH;
2072 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2074 if (FAILED(ret)) {
2075 HeapFree(GetProcessHeap(), 0, in);
2076 return ret;
2079 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2080 if (len2 > *pcchOut) {
2081 *pcchOut = len2+1;
2082 HeapFree(GetProcessHeap(), 0, in);
2083 return E_POINTER;
2085 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2086 *pcchOut = len2-1;
2087 HeapFree(GetProcessHeap(), 0, in);
2088 return ret;
2091 /*************************************************************************
2092 * UrlGetPartW [SHLWAPI.@]
2094 * See UrlGetPartA.
2096 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2097 DWORD dwPart, DWORD dwFlags)
2099 WINE_PARSE_URL pl;
2100 HRESULT ret;
2101 DWORD scheme, size, schsize;
2102 LPCWSTR addr, schaddr;
2104 TRACE("(%s %p %p(%d) %08x %08x)\n",
2105 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2107 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2108 return E_INVALIDARG;
2110 *pszOut = '\0';
2112 addr = strchrW(pszIn, ':');
2113 if(!addr)
2114 scheme = URL_SCHEME_UNKNOWN;
2115 else
2116 scheme = get_scheme_code(pszIn, addr-pszIn);
2118 ret = URL_ParseUrl(pszIn, &pl);
2120 switch (dwPart) {
2121 case URL_PART_SCHEME:
2122 if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
2123 *pcchOut = 0;
2124 return S_FALSE;
2126 addr = pl.pScheme;
2127 size = pl.szScheme;
2128 break;
2130 case URL_PART_HOSTNAME:
2131 switch(scheme) {
2132 case URL_SCHEME_FTP:
2133 case URL_SCHEME_HTTP:
2134 case URL_SCHEME_GOPHER:
2135 case URL_SCHEME_TELNET:
2136 case URL_SCHEME_FILE:
2137 case URL_SCHEME_HTTPS:
2138 break;
2139 default:
2140 *pcchOut = 0;
2141 return E_FAIL;
2144 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2145 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2146 *pcchOut = 0;
2147 return S_FALSE;
2150 if (!pl.szHostName) {
2151 *pcchOut = 0;
2152 return S_FALSE;
2154 addr = pl.pHostName;
2155 size = pl.szHostName;
2156 break;
2158 case URL_PART_USERNAME:
2159 if (!pl.szUserName) {
2160 *pcchOut = 0;
2161 return S_FALSE;
2163 addr = pl.pUserName;
2164 size = pl.szUserName;
2165 break;
2167 case URL_PART_PASSWORD:
2168 if (!pl.szPassword) {
2169 *pcchOut = 0;
2170 return S_FALSE;
2172 addr = pl.pPassword;
2173 size = pl.szPassword;
2174 break;
2176 case URL_PART_PORT:
2177 if (!pl.szPort) {
2178 *pcchOut = 0;
2179 return S_FALSE;
2181 addr = pl.pPort;
2182 size = pl.szPort;
2183 break;
2185 case URL_PART_QUERY:
2186 if (!pl.szQuery) {
2187 *pcchOut = 0;
2188 return S_FALSE;
2190 addr = pl.pQuery;
2191 size = pl.szQuery;
2192 break;
2194 default:
2195 *pcchOut = 0;
2196 return E_INVALIDARG;
2199 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2200 if(!pl.pScheme || !pl.szScheme) {
2201 *pcchOut = 0;
2202 return E_FAIL;
2204 schaddr = pl.pScheme;
2205 schsize = pl.szScheme;
2206 if (*pcchOut < schsize + size + 2) {
2207 *pcchOut = schsize + size + 2;
2208 return E_POINTER;
2210 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2211 pszOut[schsize] = ':';
2212 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2213 pszOut[schsize+1+size] = 0;
2214 *pcchOut = schsize + 1 + size;
2216 else {
2217 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2218 memcpy(pszOut, addr, size*sizeof(WCHAR));
2219 pszOut[size] = 0;
2220 *pcchOut = size;
2222 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2224 return ret;
2227 /*************************************************************************
2228 * PathIsURLA [SHLWAPI.@]
2230 * Check if the given path is a Url.
2232 * PARAMS
2233 * lpszPath [I] Path to check.
2235 * RETURNS
2236 * TRUE if lpszPath is a Url.
2237 * FALSE if lpszPath is NULL or not a Url.
2239 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2241 PARSEDURLA base;
2242 HRESULT hres;
2244 TRACE("%s\n", debugstr_a(lpstrPath));
2246 if (!lpstrPath || !*lpstrPath) return FALSE;
2248 /* get protocol */
2249 base.cbSize = sizeof(base);
2250 hres = ParseURLA(lpstrPath, &base);
2251 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2254 /*************************************************************************
2255 * PathIsURLW [SHLWAPI.@]
2257 * See PathIsURLA.
2259 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2261 PARSEDURLW base;
2262 HRESULT hres;
2264 TRACE("%s\n", debugstr_w(lpstrPath));
2266 if (!lpstrPath || !*lpstrPath) return FALSE;
2268 /* get protocol */
2269 base.cbSize = sizeof(base);
2270 hres = ParseURLW(lpstrPath, &base);
2271 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2274 /*************************************************************************
2275 * UrlCreateFromPathA [SHLWAPI.@]
2277 * See UrlCreateFromPathW
2279 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2281 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2282 WCHAR *urlW = bufW;
2283 UNICODE_STRING pathW;
2284 HRESULT ret;
2285 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2287 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2288 return E_INVALIDARG;
2289 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2290 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2291 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2293 if(ret == S_OK || ret == S_FALSE) {
2294 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2295 if(*pcchUrl > lenA) {
2296 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2297 pszUrl[lenA] = 0;
2298 *pcchUrl = lenA;
2299 } else {
2300 *pcchUrl = lenA + 1;
2301 ret = E_POINTER;
2304 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2305 RtlFreeUnicodeString(&pathW);
2306 return ret;
2309 /*************************************************************************
2310 * UrlCreateFromPathW [SHLWAPI.@]
2312 * Create a Url from a file path.
2314 * PARAMS
2315 * pszPath [I] Path to convert
2316 * pszUrl [O] Destination for the converted Url
2317 * pcchUrl [I/O] Length of pszUrl
2318 * dwReserved [I] Reserved, must be 0
2320 * RETURNS
2321 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2322 * Failure: An HRESULT error code.
2324 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2326 DWORD needed;
2327 HRESULT ret;
2328 WCHAR *pszNewUrl;
2329 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2330 WCHAR three_slashesW[] = {'/','/','/',0};
2331 PARSEDURLW parsed_url;
2333 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2335 /* Validate arguments */
2336 if (dwReserved != 0)
2337 return E_INVALIDARG;
2338 if (!pszUrl || !pcchUrl)
2339 return E_INVALIDARG;
2342 parsed_url.cbSize = sizeof(parsed_url);
2343 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2344 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2345 needed = strlenW(pszPath);
2346 if (needed >= *pcchUrl) {
2347 *pcchUrl = needed + 1;
2348 return E_POINTER;
2349 } else {
2350 *pcchUrl = needed;
2351 strcpyW(pszUrl, pszPath);
2352 return S_FALSE;
2357 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2358 strcpyW(pszNewUrl, file_colonW);
2359 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2360 strcatW(pszNewUrl, three_slashesW);
2361 strcatW(pszNewUrl, pszPath);
2362 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2364 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2365 return ret;
2368 /*************************************************************************
2369 * SHAutoComplete [SHLWAPI.@]
2371 * Enable auto-completion for an edit control.
2373 * PARAMS
2374 * hwndEdit [I] Handle of control to enable auto-completion for
2375 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2377 * RETURNS
2378 * Success: S_OK. Auto-completion is enabled for the control.
2379 * Failure: An HRESULT error code indicating the error.
2381 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2383 FIXME("stub\n");
2384 return S_FALSE;
2387 /*************************************************************************
2388 * MLBuildResURLA [SHLWAPI.405]
2390 * Create a Url pointing to a resource in a module.
2392 * PARAMS
2393 * lpszLibName [I] Name of the module containing the resource
2394 * hMod [I] Callers module handle
2395 * dwFlags [I] Undocumented flags for loading the module
2396 * lpszRes [I] Resource name
2397 * lpszDest [O] Destination for resulting Url
2398 * dwDestLen [I] Length of lpszDest
2400 * RETURNS
2401 * Success: S_OK. lpszDest contains the resource Url.
2402 * Failure: E_INVALIDARG, if any argument is invalid, or
2403 * E_FAIL if dwDestLen is too small.
2405 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2406 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2408 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2409 HRESULT hRet;
2411 if (lpszLibName)
2412 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2414 if (lpszRes)
2415 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2417 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2418 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2420 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2421 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2422 if (SUCCEEDED(hRet) && lpszDest)
2423 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2425 return hRet;
2428 /*************************************************************************
2429 * MLBuildResURLA [SHLWAPI.406]
2431 * See MLBuildResURLA.
2433 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2434 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2436 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2437 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2438 HRESULT hRet = E_FAIL;
2440 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2441 debugstr_w(lpszRes), lpszDest, dwDestLen);
2443 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2444 !lpszDest || (dwFlags && dwFlags != 2))
2445 return E_INVALIDARG;
2447 if (dwDestLen >= szResLen + 1)
2449 dwDestLen -= (szResLen + 1);
2450 memcpy(lpszDest, szRes, sizeof(szRes));
2452 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2454 if (hMod)
2456 WCHAR szBuff[MAX_PATH];
2457 DWORD len;
2459 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2460 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2462 DWORD dwPathLen = strlenW(szBuff) + 1;
2464 if (dwDestLen >= dwPathLen)
2466 DWORD dwResLen;
2468 dwDestLen -= dwPathLen;
2469 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2471 dwResLen = strlenW(lpszRes) + 1;
2472 if (dwDestLen >= dwResLen + 1)
2474 lpszDest[szResLen + dwPathLen-1] = '/';
2475 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2476 hRet = S_OK;
2480 MLFreeLibrary(hMod);
2483 return hRet;
2486 /***********************************************************************
2487 * UrlFixupW [SHLWAPI.462]
2489 * Checks the scheme part of a URL and attempts to correct misspellings.
2491 * PARAMS
2492 * lpszUrl [I] Pointer to the URL to be corrected
2493 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2494 * dwMaxChars [I] Maximum size of corrected URL
2496 * RETURNS
2497 * success: S_OK if URL corrected or already correct
2498 * failure: S_FALSE if unable to correct / COM error code if other error
2501 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2503 DWORD srcLen;
2505 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2507 if (!url)
2508 return E_FAIL;
2510 srcLen = lstrlenW(url) + 1;
2512 /* For now just copy the URL directly */
2513 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2515 return S_OK;