shdocvw: Add Lithuanian translation.
[wine/multimedia.git] / dlls / shlwapi / url.c
blob2f09bc7836da59e5f0901495b5461f5cadc0f5a9
1 /*
2 * Url functions
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #define NO_SHLWAPI_STREAM
35 #include "shlwapi.h"
36 #include "intshcut.h"
37 #include "wine/debug.h"
39 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
40 BOOL WINAPI MLFreeLibrary(HMODULE);
41 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell);
45 /* The following schemes were identified in the native version of
46 * SHLWAPI.DLL version 5.50
48 static const struct {
49 URL_SCHEME scheme_number;
50 WCHAR scheme_name[12];
51 } shlwapi_schemes[] = {
52 {URL_SCHEME_FTP, {'f','t','p',0}},
53 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
54 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
55 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
56 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
57 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
58 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
59 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
60 {URL_SCHEME_FILE, {'f','i','l','e',0}},
61 {URL_SCHEME_MK, {'m','k',0}},
62 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
63 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
64 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
65 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
66 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
67 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
68 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
69 {URL_SCHEME_RES, {'r','e','s',0}},
72 typedef struct {
73 LPCWSTR pScheme; /* [out] start of scheme */
74 DWORD szScheme; /* [out] size of scheme (until colon) */
75 LPCWSTR pUserName; /* [out] start of Username */
76 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
77 LPCWSTR pPassword; /* [out] start of Password */
78 DWORD szPassword; /* [out] size of Password (until "@") */
79 LPCWSTR pHostName; /* [out] start of Hostname */
80 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
81 LPCWSTR pPort; /* [out] start of Port */
82 DWORD szPort; /* [out] size of Port (until "/" or eos) */
83 LPCWSTR pQuery; /* [out] start of Query */
84 DWORD szQuery; /* [out] size of Query (until eos) */
85 } WINE_PARSE_URL;
87 typedef enum {
88 SCHEME,
89 HOST,
90 PORT,
91 USERPASS,
92 } WINE_URL_SCAN_TYPE;
94 static const CHAR hexDigits[] = "0123456789ABCDEF";
96 static const WCHAR fileW[] = {'f','i','l','e','\0'};
98 static const unsigned char HashDataLookup[256] = {
99 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
100 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
101 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
102 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
103 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
104 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
105 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
106 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
107 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
108 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
109 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
110 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
111 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
112 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
113 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
114 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
115 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
116 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
117 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
118 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
120 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
122 unsigned int i;
124 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
125 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
126 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
127 return shlwapi_schemes[i].scheme_number;
130 return URL_SCHEME_UNKNOWN;
133 /*************************************************************************
134 * @ [SHLWAPI.1]
136 * Parse a Url into its constituent parts.
138 * PARAMS
139 * x [I] Url to parse
140 * y [O] Undocumented structure holding the parsed information
142 * RETURNS
143 * Success: S_OK. y contains the parsed Url details.
144 * Failure: An HRESULT error code.
146 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
148 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
149 const char *ptr = x;
150 int len;
152 TRACE("%s %p\n", debugstr_a(x), y);
154 if(y->cbSize != sizeof(*y))
155 return E_INVALIDARG;
157 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
158 ptr++;
160 if (*ptr != ':' || ptr <= x+1) {
161 y->pszProtocol = NULL;
162 return URL_E_INVALID_SYNTAX;
165 y->pszProtocol = x;
166 y->cchProtocol = ptr-x;
167 y->pszSuffix = ptr+1;
168 y->cchSuffix = strlen(y->pszSuffix);
170 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
171 scheme, sizeof(scheme)/sizeof(WCHAR));
172 y->nScheme = get_scheme_code(scheme, len);
174 return S_OK;
177 /*************************************************************************
178 * @ [SHLWAPI.2]
180 * Unicode version of ParseURLA.
182 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
184 const WCHAR *ptr = x;
186 TRACE("%s %p\n", debugstr_w(x), y);
188 if(y->cbSize != sizeof(*y))
189 return E_INVALIDARG;
191 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
192 ptr++;
194 if (*ptr != ':' || ptr <= x+1) {
195 y->pszProtocol = NULL;
196 return URL_E_INVALID_SYNTAX;
199 y->pszProtocol = x;
200 y->cchProtocol = ptr-x;
201 y->pszSuffix = ptr+1;
202 y->cchSuffix = strlenW(y->pszSuffix);
203 y->nScheme = get_scheme_code(x, ptr-x);
205 return S_OK;
208 /*************************************************************************
209 * UrlCanonicalizeA [SHLWAPI.@]
211 * Canonicalize a Url.
213 * PARAMS
214 * pszUrl [I] Url to cCanonicalize
215 * pszCanonicalized [O] Destination for converted Url.
216 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
217 * dwFlags [I] Flags controlling the conversion.
219 * RETURNS
220 * Success: S_OK. The pszCanonicalized contains the converted Url.
221 * Failure: E_POINTER, if *pcchCanonicalized is too small.
223 * MSDN incorrectly describes the flags for this function. They should be:
224 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
225 *| URL_ESCAPE_SPACES_ONLY 0x04000000
226 *| URL_ESCAPE_PERCENT 0x00001000
227 *| URL_ESCAPE_UNSAFE 0x10000000
228 *| URL_UNESCAPE 0x10000000
229 *| URL_DONT_SIMPLIFY 0x08000000
230 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
232 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
233 LPDWORD pcchCanonicalized, DWORD dwFlags)
235 LPWSTR url, canonical;
236 HRESULT ret;
237 DWORD len;
239 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
240 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
242 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
243 return E_INVALIDARG;
245 len = strlen(pszUrl)+1;
246 url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
247 canonical = HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized*sizeof(WCHAR));
248 if(!url || !canonical) {
249 HeapFree(GetProcessHeap(), 0, url);
250 HeapFree(GetProcessHeap(), 0, canonical);
251 return E_OUTOFMEMORY;
254 MultiByteToWideChar(0, 0, pszUrl, -1, url, len);
256 ret = UrlCanonicalizeW(url, canonical, pcchCanonicalized, dwFlags);
257 if(ret == S_OK)
258 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized,
259 *pcchCanonicalized+1, 0, 0);
261 HeapFree(GetProcessHeap(), 0, canonical);
262 return ret;
265 /*************************************************************************
266 * UrlCanonicalizeW [SHLWAPI.@]
268 * See UrlCanonicalizeA.
270 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
271 LPDWORD pcchCanonicalized, DWORD dwFlags)
273 HRESULT hr = S_OK;
274 DWORD EscapeFlags;
275 LPCWSTR wk1, root;
276 LPWSTR lpszUrlCpy, wk2, mp, mp2;
277 INT state;
278 DWORD nByteLen, nLen, nWkLen;
279 WCHAR slash = '\0';
281 static const WCHAR wszFile[] = {'f','i','l','e',':'};
282 static const WCHAR wszRes[] = {'r','e','s',':'};
283 static const WCHAR wszHttp[] = {'h','t','t','p',':'};
284 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
285 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
287 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
288 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
290 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
291 return E_INVALIDARG;
293 if(!*pszUrl) {
294 *pszCanonicalized = 0;
295 return S_OK;
298 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
299 /* Allocate memory for simplified URL (before escaping) */
300 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
301 nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR));
303 if ((nByteLen >= sizeof(wszHttp) &&
304 !memcmp(wszHttp, pszUrl, sizeof(wszHttp))) ||
305 (nByteLen >= sizeof(wszFile) &&
306 !memcmp(wszFile, pszUrl, sizeof(wszFile))))
307 slash = '/';
309 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
310 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
311 slash = '\\';
313 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, pszUrl, sizeof(wszRes))) {
314 dwFlags &= ~URL_FILE_USE_PATHURL;
315 slash = '\0';
319 * state =
320 * 0 initial 1,3
321 * 1 have 2[+] alnum 2,3
322 * 2 have scheme (found :) 4,6,3
323 * 3 failed (no location)
324 * 4 have // 5,3
325 * 5 have 1[+] alnum 6,3
326 * 6 have location (found /) save root location
329 wk1 = pszUrl;
330 wk2 = lpszUrlCpy;
331 state = 0;
333 if(pszUrl[1] == ':') { /* Assume path */
334 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
335 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
336 if (dwFlags & URL_FILE_USE_PATHURL)
338 slash = '\\';
339 --wk2;
341 else
342 dwFlags |= URL_ESCAPE_UNSAFE;
343 state = 5;
346 while (*wk1) {
347 switch (state) {
348 case 0:
349 if (!isalnumW(*wk1)) {state = 3; break;}
350 *wk2++ = *wk1++;
351 if (!isalnumW(*wk1)) {state = 3; break;}
352 *wk2++ = *wk1++;
353 state = 1;
354 break;
355 case 1:
356 *wk2++ = *wk1;
357 if (*wk1++ == ':') state = 2;
358 break;
359 case 2:
360 *wk2++ = *wk1++;
361 if (*wk1 != '/') {state = 6; break;}
362 *wk2++ = *wk1++;
363 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
364 && !strncmpW(wszFile, pszUrl, sizeof(wszFile)/sizeof(WCHAR))
365 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
366 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
367 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
368 wk1++;
370 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
371 wk1++;
372 state = 4;
373 break;
374 case 3:
375 nWkLen = strlenW(wk1);
376 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
377 mp = wk2;
378 wk1 += nWkLen;
379 wk2 += nWkLen;
381 if(slash) {
382 while(mp < wk2) {
383 if(*mp == '/' || *mp == '\\')
384 *mp = slash;
385 mp++;
388 break;
389 case 4:
390 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
391 {state = 3; break;}
392 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
393 *wk2++ = *wk1++;
394 state = 5;
395 if (!*wk1) {
396 if(slash)
397 *wk2++ = slash;
398 else
399 *wk2++ = '/';
401 break;
402 case 5:
403 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
404 while(*wk1 == '/' || *wk1 == '\\') {
405 if(slash)
406 *wk2++ = slash;
407 else
408 *wk2++ = *wk1;
409 wk1++;
411 state = 6;
412 break;
413 case 6:
414 if(dwFlags & URL_DONT_SIMPLIFY) {
415 state = 3;
416 break;
419 /* Now at root location, cannot back up any more. */
420 /* "root" will point at the '/' */
422 root = wk2-1;
423 while (*wk1) {
424 mp = strchrW(wk1, '/');
425 mp2 = strchrW(wk1, '\\');
426 if(mp2 && (!mp || mp2 < mp))
427 mp = mp2;
428 if (!mp) {
429 nWkLen = strlenW(wk1);
430 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
431 wk1 += nWkLen;
432 wk2 += nWkLen;
433 continue;
435 nLen = mp - wk1;
436 if(nLen) {
437 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
438 wk2 += nLen;
439 wk1 += nLen;
441 if(slash)
442 *wk2++ = slash;
443 else
444 *wk2++ = *wk1;
445 wk1++;
447 while (*wk1 == '.') {
448 TRACE("found '/.'\n");
449 if (wk1[1] == '/' || wk1[1] == '\\') {
450 /* case of /./ -> skip the ./ */
451 wk1 += 2;
453 else if (wk1[1] == '.' && (wk1[2] == '/'
454 || wk1[2] == '\\' || wk1[2] == '?'
455 || wk1[2] == '#' || !wk1[2])) {
456 /* case /../ -> need to backup wk2 */
457 TRACE("found '/../'\n");
458 *(wk2-1) = '\0'; /* set end of string */
459 mp = strrchrW(root, '/');
460 mp2 = strrchrW(root, '\\');
461 if(mp2 && (!mp || mp2 < mp))
462 mp = mp2;
463 if (mp && (mp >= root)) {
464 /* found valid backup point */
465 wk2 = mp + 1;
466 if(wk1[2] != '/' && wk1[2] != '\\')
467 wk1 += 2;
468 else
469 wk1 += 3;
471 else {
472 /* did not find point, restore '/' */
473 *(wk2-1) = slash;
474 break;
477 else
478 break;
481 *wk2 = '\0';
482 break;
483 default:
484 FIXME("how did we get here - state=%d\n", state);
485 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
486 return E_INVALIDARG;
488 *wk2 = '\0';
489 TRACE("Simplified, orig <%s>, simple <%s>\n",
490 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
492 nLen = lstrlenW(lpszUrlCpy);
493 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
494 lpszUrlCpy[--nLen]=0;
496 if((dwFlags & URL_UNESCAPE) ||
497 ((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
498 && !memcmp(wszFile, pszUrl, sizeof(wszFile))))
499 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
501 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
502 URL_ESCAPE_SPACES_ONLY |
503 URL_ESCAPE_PERCENT |
504 URL_DONT_ESCAPE_EXTRA_INFO |
505 URL_ESCAPE_SEGMENT_ONLY ))) {
506 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
507 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
508 EscapeFlags);
509 } else { /* No escaping needed, just copy the string */
510 nLen = lstrlenW(lpszUrlCpy);
511 if(nLen < *pcchCanonicalized)
512 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
513 else {
514 hr = E_POINTER;
515 nLen++;
517 *pcchCanonicalized = nLen;
520 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
522 if (hr == S_OK)
523 TRACE("result %s\n", debugstr_w(pszCanonicalized));
525 return hr;
528 /*************************************************************************
529 * UrlCombineA [SHLWAPI.@]
531 * Combine two Urls.
533 * PARAMS
534 * pszBase [I] Base Url
535 * pszRelative [I] Url to combine with pszBase
536 * pszCombined [O] Destination for combined Url
537 * pcchCombined [O] Destination for length of pszCombined
538 * dwFlags [I] URL_ flags from "shlwapi.h"
540 * RETURNS
541 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
542 * contains its length.
543 * Failure: An HRESULT error code indicating the error.
545 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
546 LPSTR pszCombined, LPDWORD pcchCombined,
547 DWORD dwFlags)
549 LPWSTR base, relative, combined;
550 DWORD ret, len, len2;
552 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
553 debugstr_a(pszBase),debugstr_a(pszRelative),
554 pcchCombined?*pcchCombined:0,dwFlags);
556 if(!pszBase || !pszRelative || !pcchCombined)
557 return E_INVALIDARG;
559 base = HeapAlloc(GetProcessHeap(), 0,
560 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
561 relative = base + INTERNET_MAX_URL_LENGTH;
562 combined = relative + INTERNET_MAX_URL_LENGTH;
564 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
565 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
566 len = *pcchCombined;
568 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
569 if (ret != S_OK) {
570 *pcchCombined = len;
571 HeapFree(GetProcessHeap(), 0, base);
572 return ret;
575 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
576 if (len2 > *pcchCombined) {
577 *pcchCombined = len2;
578 HeapFree(GetProcessHeap(), 0, base);
579 return E_POINTER;
581 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
582 0, 0);
583 *pcchCombined = len2;
584 HeapFree(GetProcessHeap(), 0, base);
585 return S_OK;
588 /*************************************************************************
589 * UrlCombineW [SHLWAPI.@]
591 * See UrlCombineA.
593 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
594 LPWSTR pszCombined, LPDWORD pcchCombined,
595 DWORD dwFlags)
597 PARSEDURLW base, relative;
598 DWORD myflags, sizeloc = 0;
599 DWORD len, res1, res2, process_case = 0;
600 LPWSTR work, preliminary, mbase, mrelative;
601 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
602 HRESULT ret;
604 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
605 debugstr_w(pszBase),debugstr_w(pszRelative),
606 pcchCombined?*pcchCombined:0,dwFlags);
608 if(!pszBase || !pszRelative || !pcchCombined)
609 return E_INVALIDARG;
611 base.cbSize = sizeof(base);
612 relative.cbSize = sizeof(relative);
614 /* Get space for duplicates of the input and the output */
615 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
616 sizeof(WCHAR));
617 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
618 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
619 *preliminary = '\0';
621 /* Canonicalize the base input prior to looking for the scheme */
622 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
623 len = INTERNET_MAX_URL_LENGTH;
624 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
626 /* Canonicalize the relative input prior to looking for the scheme */
627 len = INTERNET_MAX_URL_LENGTH;
628 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
630 /* See if the base has a scheme */
631 res1 = ParseURLW(mbase, &base);
632 if (res1) {
633 /* if pszBase has no scheme, then return pszRelative */
634 TRACE("no scheme detected in Base\n");
635 process_case = 1;
637 else do {
638 BOOL manual_search = FALSE;
640 /* mk is a special case */
641 if(base.nScheme == URL_SCHEME_MK) {
642 static const WCHAR wsz[] = {':',':',0};
644 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
645 if(ptr) {
646 int delta;
648 ptr += 2;
649 delta = ptr-base.pszSuffix;
650 base.cchProtocol += delta;
651 base.pszSuffix += delta;
652 base.cchSuffix -= delta;
654 }else {
655 /* get size of location field (if it exists) */
656 work = (LPWSTR)base.pszSuffix;
657 sizeloc = 0;
658 if (*work++ == '/') {
659 if (*work++ == '/') {
660 /* At this point have start of location and
661 * it ends at next '/' or end of string.
663 while(*work && (*work != '/')) work++;
664 sizeloc = (DWORD)(work - base.pszSuffix);
669 /* If there is a '#' and the characters immediately preceding it are
670 * ".htm[l]", then begin looking for the last leaf starting from
671 * the '#'. Otherwise the '#' is not meaningful and just start
672 * looking from the end. */
673 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
674 const WCHAR htmlW[] = {'.','h','t','m','l',0};
675 const int len_htmlW = 5;
676 const WCHAR htmW[] = {'.','h','t','m',0};
677 const int len_htmW = 4;
679 if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
680 work -= len_htmW;
681 if (strncmpiW(work, htmW, len_htmW) == 0)
682 manual_search = TRUE;
683 work += len_htmW;
686 if (!manual_search &&
687 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
688 work -= len_htmlW;
689 if (strncmpiW(work, htmlW, len_htmlW) == 0)
690 manual_search = TRUE;
691 work += len_htmlW;
695 if (manual_search) {
696 /* search backwards starting from the current position */
697 while (*work != '/' && work > base.pszSuffix + sizeloc)
698 --work;
699 if (work > base.pszSuffix + sizeloc)
700 base.cchSuffix = work - base.pszSuffix + 1;
701 }else {
702 /* search backwards starting from the end of the string */
703 work = strrchrW((base.pszSuffix+sizeloc), '/');
704 if (work) {
705 len = (DWORD)(work - base.pszSuffix + 1);
706 base.cchSuffix = len;
711 * At this point:
712 * .pszSuffix points to location (starting with '//')
713 * .cchSuffix length of location (above) and rest less the last
714 * leaf (if any)
715 * sizeloc length of location (above) up to but not including
716 * the last '/'
719 res2 = ParseURLW(mrelative, &relative);
720 if (res2) {
721 /* no scheme in pszRelative */
722 TRACE("no scheme detected in Relative\n");
723 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
724 relative.cchSuffix = strlenW(mrelative);
725 if (*pszRelative == ':') {
726 /* case that is either left alone or uses pszBase */
727 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
728 process_case = 5;
729 break;
731 process_case = 1;
732 break;
734 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
735 /* case that becomes "file:///" */
736 strcpyW(preliminary, myfilestr);
737 process_case = 1;
738 break;
740 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
741 /* pszRelative has location and rest */
742 process_case = 3;
743 break;
745 if (*mrelative == '/') {
746 /* case where pszRelative is root to location */
747 process_case = 4;
748 break;
750 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
751 break;
754 /* handle cases where pszRelative has scheme */
755 if ((base.cchProtocol == relative.cchProtocol) &&
756 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
758 /* since the schemes are the same */
759 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
760 /* case where pszRelative replaces location and following */
761 process_case = 3;
762 break;
764 if (*relative.pszSuffix == '/') {
765 /* case where pszRelative is root to location */
766 process_case = 4;
767 break;
769 /* replace either just location if base's location starts with a
770 * slash or otherwise everything */
771 process_case = (*base.pszSuffix == '/') ? 5 : 1;
772 break;
774 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
775 /* case where pszRelative replaces scheme, location,
776 * and following and handles PLUGGABLE
778 process_case = 2;
779 break;
781 process_case = 1;
782 break;
783 } while(FALSE); /* a little trick to allow easy exit from nested if's */
785 ret = S_OK;
786 switch (process_case) {
788 case 1: /*
789 * Return pszRelative appended to what ever is in pszCombined,
790 * (which may the string "file:///"
792 strcatW(preliminary, mrelative);
793 break;
795 case 2: /* case where pszRelative replaces scheme, and location */
796 strcpyW(preliminary, mrelative);
797 break;
799 case 3: /*
800 * Return the pszBase scheme with pszRelative. Basically
801 * keeps the scheme and replaces the domain and following.
803 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
804 work = preliminary + base.cchProtocol + 1;
805 strcpyW(work, relative.pszSuffix);
806 break;
808 case 4: /*
809 * Return the pszBase scheme and location but everything
810 * after the location is pszRelative. (Replace document
811 * from root on.)
813 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
814 work = preliminary + base.cchProtocol + 1 + sizeloc;
815 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
816 *(work++) = '/';
817 strcpyW(work, relative.pszSuffix);
818 break;
820 case 5: /*
821 * Return the pszBase without its document (if any) and
822 * append pszRelative after its scheme.
824 memcpy(preliminary, base.pszProtocol,
825 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
826 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
827 if (*work++ != '/')
828 *(work++) = '/';
829 strcpyW(work, relative.pszSuffix);
830 break;
832 default:
833 FIXME("How did we get here????? process_case=%d\n", process_case);
834 ret = E_INVALIDARG;
837 if (ret == S_OK) {
838 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
839 if(*pcchCombined == 0)
840 *pcchCombined = 1;
841 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
842 if(SUCCEEDED(ret) && pszCombined) {
843 lstrcpyW(pszCombined, mrelative);
845 TRACE("return-%d len=%d, %s\n",
846 process_case, *pcchCombined, debugstr_w(pszCombined));
848 HeapFree(GetProcessHeap(), 0, preliminary);
849 return ret;
852 /*************************************************************************
853 * UrlEscapeA [SHLWAPI.@]
856 HRESULT WINAPI UrlEscapeA(
857 LPCSTR pszUrl,
858 LPSTR pszEscaped,
859 LPDWORD pcchEscaped,
860 DWORD dwFlags)
862 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
863 WCHAR *escapedW = bufW;
864 UNICODE_STRING urlW;
865 HRESULT ret;
866 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
868 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
869 return E_INVALIDARG;
871 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
872 return E_INVALIDARG;
873 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
874 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
875 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
877 if(ret == S_OK) {
878 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
879 if(*pcchEscaped > lenA) {
880 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
881 pszEscaped[lenA] = 0;
882 *pcchEscaped = lenA;
883 } else {
884 *pcchEscaped = lenA + 1;
885 ret = E_POINTER;
888 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
889 RtlFreeUnicodeString(&urlW);
890 return ret;
893 #define WINE_URL_BASH_AS_SLASH 0x01
894 #define WINE_URL_COLLAPSE_SLASHES 0x02
895 #define WINE_URL_ESCAPE_SLASH 0x04
896 #define WINE_URL_ESCAPE_HASH 0x08
897 #define WINE_URL_ESCAPE_QUESTION 0x10
898 #define WINE_URL_STOP_ON_HASH 0x20
899 #define WINE_URL_STOP_ON_QUESTION 0x40
901 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
904 if (isalnumW(ch))
905 return FALSE;
907 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
908 if(ch == ' ')
909 return TRUE;
910 else
911 return FALSE;
914 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
915 return TRUE;
917 if (ch <= 31 || ch >= 127)
918 return TRUE;
920 else {
921 switch (ch) {
922 case ' ':
923 case '<':
924 case '>':
925 case '\"':
926 case '{':
927 case '}':
928 case '|':
929 case '\\':
930 case '^':
931 case ']':
932 case '[':
933 case '`':
934 case '&':
935 return TRUE;
937 case '/':
938 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
939 return FALSE;
941 case '?':
942 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
943 return FALSE;
945 case '#':
946 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
947 return FALSE;
949 default:
950 return FALSE;
956 /*************************************************************************
957 * UrlEscapeW [SHLWAPI.@]
959 * Converts unsafe characters in a Url into escape sequences.
961 * PARAMS
962 * pszUrl [I] Url to modify
963 * pszEscaped [O] Destination for modified Url
964 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
965 * dwFlags [I] URL_ flags from "shlwapi.h"
967 * RETURNS
968 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
969 * contains its length.
970 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
971 * pcchEscaped is set to the required length.
973 * Converts unsafe characters into their escape sequences.
975 * NOTES
976 * - By default this function stops converting at the first '?' or
977 * '#' character.
978 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
979 * converted, but the conversion continues past a '?' or '#'.
980 * - Note that this function did not work well (or at all) in shlwapi version 4.
982 * BUGS
983 * Only the following flags are implemented:
984 *| URL_ESCAPE_SPACES_ONLY
985 *| URL_DONT_ESCAPE_EXTRA_INFO
986 *| URL_ESCAPE_SEGMENT_ONLY
987 *| URL_ESCAPE_PERCENT
989 HRESULT WINAPI UrlEscapeW(
990 LPCWSTR pszUrl,
991 LPWSTR pszEscaped,
992 LPDWORD pcchEscaped,
993 DWORD dwFlags)
995 LPCWSTR src;
996 DWORD needed = 0, ret;
997 BOOL stop_escaping = FALSE;
998 WCHAR next[5], *dst = pszEscaped;
999 INT len;
1000 PARSEDURLW parsed_url;
1001 DWORD int_flags;
1002 DWORD slashes = 0;
1003 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1005 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
1006 pcchEscaped, dwFlags);
1008 if(!pszUrl || !pcchEscaped)
1009 return E_INVALIDARG;
1011 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1012 URL_ESCAPE_SEGMENT_ONLY |
1013 URL_DONT_ESCAPE_EXTRA_INFO |
1014 URL_ESCAPE_PERCENT))
1015 FIXME("Unimplemented flags: %08x\n", dwFlags);
1017 /* fix up flags */
1018 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1019 /* if SPACES_ONLY specified, reset the other controls */
1020 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1021 URL_ESCAPE_PERCENT |
1022 URL_ESCAPE_SEGMENT_ONLY);
1024 else
1025 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1026 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1029 int_flags = 0;
1030 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1031 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1032 } else {
1033 parsed_url.cbSize = sizeof(parsed_url);
1034 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1035 parsed_url.nScheme = URL_SCHEME_INVALID;
1037 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1039 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1040 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1042 switch(parsed_url.nScheme) {
1043 case URL_SCHEME_FILE:
1044 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1045 int_flags &= ~WINE_URL_STOP_ON_HASH;
1046 break;
1048 case URL_SCHEME_HTTP:
1049 case URL_SCHEME_HTTPS:
1050 int_flags |= WINE_URL_BASH_AS_SLASH;
1051 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1052 int_flags |= WINE_URL_ESCAPE_SLASH;
1053 break;
1055 case URL_SCHEME_MAILTO:
1056 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1057 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1058 break;
1060 case URL_SCHEME_INVALID:
1061 break;
1063 case URL_SCHEME_FTP:
1064 default:
1065 if(parsed_url.pszSuffix[0] != '/')
1066 int_flags |= WINE_URL_ESCAPE_SLASH;
1067 break;
1071 for(src = pszUrl; *src; ) {
1072 WCHAR cur = *src;
1073 len = 0;
1075 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1076 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1077 while(cur == '/' || cur == '\\') {
1078 slashes++;
1079 cur = *++src;
1081 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1082 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1083 src += localhost_len + 1;
1084 slashes = 3;
1087 switch(slashes) {
1088 case 1:
1089 case 3:
1090 next[0] = next[1] = next[2] = '/';
1091 len = 3;
1092 break;
1093 case 0:
1094 len = 0;
1095 break;
1096 default:
1097 next[0] = next[1] = '/';
1098 len = 2;
1099 break;
1102 if(len == 0) {
1104 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1105 stop_escaping = TRUE;
1107 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1108 stop_escaping = TRUE;
1110 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1112 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1113 next[0] = '%';
1114 next[1] = hexDigits[(cur >> 4) & 0xf];
1115 next[2] = hexDigits[cur & 0xf];
1116 len = 3;
1117 } else {
1118 next[0] = cur;
1119 len = 1;
1121 src++;
1124 if(needed + len <= *pcchEscaped) {
1125 memcpy(dst, next, len*sizeof(WCHAR));
1126 dst += len;
1128 needed += len;
1131 if(needed < *pcchEscaped) {
1132 *dst = '\0';
1133 ret = S_OK;
1134 } else {
1135 needed++; /* add one for the '\0' */
1136 ret = E_POINTER;
1138 *pcchEscaped = needed;
1139 return ret;
1143 /*************************************************************************
1144 * UrlUnescapeA [SHLWAPI.@]
1146 * Converts Url escape sequences back to ordinary characters.
1148 * PARAMS
1149 * pszUrl [I/O] Url to convert
1150 * pszUnescaped [O] Destination for converted Url
1151 * pcchUnescaped [I/O] Size of output string
1152 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1154 * RETURNS
1155 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1156 * dwFlags includes URL_ESCAPE_INPLACE.
1157 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1158 * this case pcchUnescaped is set to the size required.
1159 * NOTES
1160 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1161 * the first occurrence of either a '?' or '#' character.
1163 HRESULT WINAPI UrlUnescapeA(
1164 LPSTR pszUrl,
1165 LPSTR pszUnescaped,
1166 LPDWORD pcchUnescaped,
1167 DWORD dwFlags)
1169 char *dst, next;
1170 LPCSTR src;
1171 HRESULT ret;
1172 DWORD needed;
1173 BOOL stop_unescaping = FALSE;
1175 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1176 pcchUnescaped, dwFlags);
1178 if (!pszUrl) return E_INVALIDARG;
1180 if(dwFlags & URL_UNESCAPE_INPLACE)
1181 dst = pszUrl;
1182 else
1184 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1185 dst = pszUnescaped;
1188 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1189 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1190 (*src == '#' || *src == '?')) {
1191 stop_unescaping = TRUE;
1192 next = *src;
1193 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1194 && stop_unescaping == FALSE) {
1195 INT ih;
1196 char buf[3];
1197 memcpy(buf, src + 1, 2);
1198 buf[2] = '\0';
1199 ih = strtol(buf, NULL, 16);
1200 next = (CHAR) ih;
1201 src += 2; /* Advance to end of escape */
1202 } else
1203 next = *src;
1205 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1206 *dst++ = next;
1209 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1210 *dst = '\0';
1211 ret = S_OK;
1212 } else {
1213 needed++; /* add one for the '\0' */
1214 ret = E_POINTER;
1216 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1217 *pcchUnescaped = needed;
1219 if (ret == S_OK) {
1220 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1221 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1224 return ret;
1227 /*************************************************************************
1228 * UrlUnescapeW [SHLWAPI.@]
1230 * See UrlUnescapeA.
1232 HRESULT WINAPI UrlUnescapeW(
1233 LPWSTR pszUrl,
1234 LPWSTR pszUnescaped,
1235 LPDWORD pcchUnescaped,
1236 DWORD dwFlags)
1238 WCHAR *dst, next;
1239 LPCWSTR src;
1240 HRESULT ret;
1241 DWORD needed;
1242 BOOL stop_unescaping = FALSE;
1244 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1245 pcchUnescaped, dwFlags);
1247 if(!pszUrl) return E_INVALIDARG;
1249 if(dwFlags & URL_UNESCAPE_INPLACE)
1250 dst = pszUrl;
1251 else
1253 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1254 dst = pszUnescaped;
1257 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1258 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1259 (*src == '#' || *src == '?')) {
1260 stop_unescaping = TRUE;
1261 next = *src;
1262 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1263 && stop_unescaping == FALSE) {
1264 INT ih;
1265 WCHAR buf[5] = {'0','x',0};
1266 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1267 buf[4] = 0;
1268 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1269 next = (WCHAR) ih;
1270 src += 2; /* Advance to end of escape */
1271 } else
1272 next = *src;
1274 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1275 *dst++ = next;
1278 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1279 *dst = '\0';
1280 ret = S_OK;
1281 } else {
1282 needed++; /* add one for the '\0' */
1283 ret = E_POINTER;
1285 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1286 *pcchUnescaped = needed;
1288 if (ret == S_OK) {
1289 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1290 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1293 return ret;
1296 /*************************************************************************
1297 * UrlGetLocationA [SHLWAPI.@]
1299 * Get the location from a Url.
1301 * PARAMS
1302 * pszUrl [I] Url to get the location from
1304 * RETURNS
1305 * A pointer to the start of the location in pszUrl, or NULL if there is
1306 * no location.
1308 * NOTES
1309 * - MSDN erroneously states that "The location is the segment of the Url
1310 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1311 * stop at '?' and always return a NULL in this case.
1312 * - MSDN also erroneously states that "If a file URL has a query string,
1313 * the returned string is the query string". In all tested cases, if the
1314 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1315 *| Result Url
1316 *| ------ ---
1317 *| NULL file://aa/b/cd#hohoh
1318 *| #hohoh http://aa/b/cd#hohoh
1319 *| NULL fi://aa/b/cd#hohoh
1320 *| #hohoh ff://aa/b/cd#hohoh
1322 LPCSTR WINAPI UrlGetLocationA(
1323 LPCSTR pszUrl)
1325 PARSEDURLA base;
1326 DWORD res1;
1328 base.cbSize = sizeof(base);
1329 res1 = ParseURLA(pszUrl, &base);
1330 if (res1) return NULL; /* invalid scheme */
1332 /* if scheme is file: then never return pointer */
1333 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1335 /* Look for '#' and return its addr */
1336 return strchr(base.pszSuffix, '#');
1339 /*************************************************************************
1340 * UrlGetLocationW [SHLWAPI.@]
1342 * See UrlGetLocationA.
1344 LPCWSTR WINAPI UrlGetLocationW(
1345 LPCWSTR pszUrl)
1347 PARSEDURLW base;
1348 DWORD res1;
1350 base.cbSize = sizeof(base);
1351 res1 = ParseURLW(pszUrl, &base);
1352 if (res1) return NULL; /* invalid scheme */
1354 /* if scheme is file: then never return pointer */
1355 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1357 /* Look for '#' and return its addr */
1358 return strchrW(base.pszSuffix, '#');
1361 /*************************************************************************
1362 * UrlCompareA [SHLWAPI.@]
1364 * Compare two Urls.
1366 * PARAMS
1367 * pszUrl1 [I] First Url to compare
1368 * pszUrl2 [I] Url to compare to pszUrl1
1369 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1371 * RETURNS
1372 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1373 * than, equal to, or less than pszUrl1 respectively.
1375 INT WINAPI UrlCompareA(
1376 LPCSTR pszUrl1,
1377 LPCSTR pszUrl2,
1378 BOOL fIgnoreSlash)
1380 INT ret, len, len1, len2;
1382 if (!fIgnoreSlash)
1383 return strcmp(pszUrl1, pszUrl2);
1384 len1 = strlen(pszUrl1);
1385 if (pszUrl1[len1-1] == '/') len1--;
1386 len2 = strlen(pszUrl2);
1387 if (pszUrl2[len2-1] == '/') len2--;
1388 if (len1 == len2)
1389 return strncmp(pszUrl1, pszUrl2, len1);
1390 len = min(len1, len2);
1391 ret = strncmp(pszUrl1, pszUrl2, len);
1392 if (ret) return ret;
1393 if (len1 > len2) return 1;
1394 return -1;
1397 /*************************************************************************
1398 * UrlCompareW [SHLWAPI.@]
1400 * See UrlCompareA.
1402 INT WINAPI UrlCompareW(
1403 LPCWSTR pszUrl1,
1404 LPCWSTR pszUrl2,
1405 BOOL fIgnoreSlash)
1407 INT ret;
1408 size_t len, len1, len2;
1410 if (!fIgnoreSlash)
1411 return strcmpW(pszUrl1, pszUrl2);
1412 len1 = strlenW(pszUrl1);
1413 if (pszUrl1[len1-1] == '/') len1--;
1414 len2 = strlenW(pszUrl2);
1415 if (pszUrl2[len2-1] == '/') len2--;
1416 if (len1 == len2)
1417 return strncmpW(pszUrl1, pszUrl2, len1);
1418 len = min(len1, len2);
1419 ret = strncmpW(pszUrl1, pszUrl2, len);
1420 if (ret) return ret;
1421 if (len1 > len2) return 1;
1422 return -1;
1425 /*************************************************************************
1426 * HashData [SHLWAPI.@]
1428 * Hash an input block into a variable sized digest.
1430 * PARAMS
1431 * lpSrc [I] Input block
1432 * nSrcLen [I] Length of lpSrc
1433 * lpDest [I] Output for hash digest
1434 * nDestLen [I] Length of lpDest
1436 * RETURNS
1437 * Success: TRUE. lpDest is filled with the computed hash value.
1438 * Failure: FALSE, if any argument is invalid.
1440 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1441 unsigned char *lpDest, DWORD nDestLen)
1443 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1445 if (!lpSrc || !lpDest)
1446 return E_INVALIDARG;
1448 while (destCount >= 0)
1450 lpDest[destCount] = (destCount & 0xff);
1451 destCount--;
1454 while (srcCount >= 0)
1456 destCount = nDestLen - 1;
1457 while (destCount >= 0)
1459 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1460 destCount--;
1462 srcCount--;
1464 return S_OK;
1467 /*************************************************************************
1468 * UrlHashA [SHLWAPI.@]
1470 * Produce a Hash from a Url.
1472 * PARAMS
1473 * pszUrl [I] Url to hash
1474 * lpDest [O] Destinationh for hash
1475 * nDestLen [I] Length of lpDest
1477 * RETURNS
1478 * Success: S_OK. lpDest is filled with the computed hash value.
1479 * Failure: E_INVALIDARG, if any argument is invalid.
1481 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1483 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1484 return E_INVALIDARG;
1486 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1487 return S_OK;
1490 /*************************************************************************
1491 * UrlHashW [SHLWAPI.@]
1493 * See UrlHashA.
1495 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1497 char szUrl[MAX_PATH];
1499 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1501 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1502 return E_INVALIDARG;
1504 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1505 * return the same digests for the same URL.
1507 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1508 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1509 return S_OK;
1512 /*************************************************************************
1513 * UrlApplySchemeA [SHLWAPI.@]
1515 * Apply a scheme to a Url.
1517 * PARAMS
1518 * pszIn [I] Url to apply scheme to
1519 * pszOut [O] Destination for modified Url
1520 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1521 * dwFlags [I] URL_ flags from "shlwapi.h"
1523 * RETURNS
1524 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1525 * Failure: An HRESULT error code describing the error.
1527 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1529 LPWSTR in, out;
1530 HRESULT ret;
1531 DWORD len;
1533 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1534 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1536 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1538 in = HeapAlloc(GetProcessHeap(), 0,
1539 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1540 out = in + INTERNET_MAX_URL_LENGTH;
1542 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1543 len = INTERNET_MAX_URL_LENGTH;
1545 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1546 if (ret != S_OK) {
1547 HeapFree(GetProcessHeap(), 0, in);
1548 return ret;
1551 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1552 if (len > *pcchOut) {
1553 ret = E_POINTER;
1554 goto cleanup;
1557 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1558 len--;
1560 cleanup:
1561 *pcchOut = len;
1562 HeapFree(GetProcessHeap(), 0, in);
1563 return ret;
1566 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1568 HKEY newkey;
1569 BOOL j;
1570 INT index;
1571 DWORD value_len, data_len, dwType, i;
1572 WCHAR reg_path[MAX_PATH];
1573 WCHAR value[MAX_PATH], data[MAX_PATH];
1574 WCHAR Wxx, Wyy;
1576 MultiByteToWideChar(0, 0,
1577 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1578 -1, reg_path, MAX_PATH);
1579 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1580 index = 0;
1581 while(value_len = data_len = MAX_PATH,
1582 RegEnumValueW(newkey, index, value, &value_len,
1583 0, &dwType, (LPVOID)data, &data_len) == 0) {
1584 TRACE("guess %d %s is %s\n",
1585 index, debugstr_w(value), debugstr_w(data));
1587 j = FALSE;
1588 for(i=0; i<value_len; i++) {
1589 Wxx = pszIn[i];
1590 Wyy = value[i];
1591 /* remember that TRUE is not-equal */
1592 j = ChrCmpIW(Wxx, Wyy);
1593 if (j) break;
1595 if ((i == value_len) && !j) {
1596 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1597 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1598 RegCloseKey(newkey);
1599 return E_POINTER;
1601 strcpyW(pszOut, data);
1602 strcatW(pszOut, pszIn);
1603 *pcchOut = strlenW(pszOut);
1604 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1605 RegCloseKey(newkey);
1606 return S_OK;
1608 index++;
1610 RegCloseKey(newkey);
1611 return E_FAIL;
1614 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1616 HKEY newkey;
1617 DWORD data_len, dwType;
1618 WCHAR data[MAX_PATH];
1620 static const WCHAR prefix_keyW[] =
1621 {'S','o','f','t','w','a','r','e',
1622 '\\','M','i','c','r','o','s','o','f','t',
1623 '\\','W','i','n','d','o','w','s',
1624 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1625 '\\','U','R','L',
1626 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1628 /* get and prepend default */
1629 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1630 data_len = sizeof(data);
1631 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1632 RegCloseKey(newkey);
1633 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1634 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1635 return E_POINTER;
1637 strcpyW(pszOut, data);
1638 strcatW(pszOut, pszIn);
1639 *pcchOut = strlenW(pszOut);
1640 TRACE("used default %s\n", debugstr_w(pszOut));
1641 return S_OK;
1644 /*************************************************************************
1645 * UrlApplySchemeW [SHLWAPI.@]
1647 * See UrlApplySchemeA.
1649 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1651 PARSEDURLW in_scheme;
1652 DWORD res1;
1653 HRESULT ret;
1655 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1656 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1658 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1660 if (dwFlags & URL_APPLY_GUESSFILE) {
1661 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1662 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1663 strcpyW(pszOut, pszIn);
1664 *pcchOut = strlenW(pszOut);
1665 return S_FALSE;
1668 in_scheme.cbSize = sizeof(in_scheme);
1669 /* See if the base has a scheme */
1670 res1 = ParseURLW(pszIn, &in_scheme);
1671 if (res1) {
1672 /* no scheme in input, need to see if we need to guess */
1673 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1674 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1675 return ret;
1678 else {
1679 /* we have a scheme, see if valid (known scheme) */
1680 if (in_scheme.nScheme) {
1681 /* have valid scheme, so just copy and exit */
1682 if (strlenW(pszIn) + 1 > *pcchOut) {
1683 *pcchOut = strlenW(pszIn) + 1;
1684 return E_POINTER;
1686 strcpyW(pszOut, pszIn);
1687 *pcchOut = strlenW(pszOut);
1688 TRACE("valid scheme, returning copy\n");
1689 return S_OK;
1693 /* If we are here, then either invalid scheme,
1694 * or no scheme and can't/failed guess.
1696 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1697 ((res1 != 0)) ) &&
1698 (dwFlags & URL_APPLY_DEFAULT)) {
1699 /* find and apply default scheme */
1700 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1703 return S_FALSE;
1706 /*************************************************************************
1707 * UrlIsA [SHLWAPI.@]
1709 * Determine if a Url is of a certain class.
1711 * PARAMS
1712 * pszUrl [I] Url to check
1713 * Urlis [I] URLIS_ constant from "shlwapi.h"
1715 * RETURNS
1716 * TRUE if pszUrl belongs to the class type in Urlis.
1717 * FALSE Otherwise.
1719 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1721 PARSEDURLA base;
1722 DWORD res1;
1723 LPCSTR last;
1725 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1727 if(!pszUrl)
1728 return FALSE;
1730 switch (Urlis) {
1732 case URLIS_OPAQUE:
1733 base.cbSize = sizeof(base);
1734 res1 = ParseURLA(pszUrl, &base);
1735 if (res1) return FALSE; /* invalid scheme */
1736 switch (base.nScheme)
1738 case URL_SCHEME_MAILTO:
1739 case URL_SCHEME_SHELL:
1740 case URL_SCHEME_JAVASCRIPT:
1741 case URL_SCHEME_VBSCRIPT:
1742 case URL_SCHEME_ABOUT:
1743 return TRUE;
1745 return FALSE;
1747 case URLIS_FILEURL:
1748 return !StrCmpNA("file:", pszUrl, 5);
1750 case URLIS_DIRECTORY:
1751 last = pszUrl + strlen(pszUrl) - 1;
1752 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1754 case URLIS_URL:
1755 return PathIsURLA(pszUrl);
1757 case URLIS_NOHISTORY:
1758 case URLIS_APPLIABLE:
1759 case URLIS_HASQUERY:
1760 default:
1761 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1763 return FALSE;
1766 /*************************************************************************
1767 * UrlIsW [SHLWAPI.@]
1769 * See UrlIsA.
1771 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1773 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1774 PARSEDURLW base;
1775 DWORD res1;
1776 LPCWSTR last;
1778 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1780 if(!pszUrl)
1781 return FALSE;
1783 switch (Urlis) {
1785 case URLIS_OPAQUE:
1786 base.cbSize = sizeof(base);
1787 res1 = ParseURLW(pszUrl, &base);
1788 if (res1) return FALSE; /* invalid scheme */
1789 switch (base.nScheme)
1791 case URL_SCHEME_MAILTO:
1792 case URL_SCHEME_SHELL:
1793 case URL_SCHEME_JAVASCRIPT:
1794 case URL_SCHEME_VBSCRIPT:
1795 case URL_SCHEME_ABOUT:
1796 return TRUE;
1798 return FALSE;
1800 case URLIS_FILEURL:
1801 return !strncmpW(stemp, pszUrl, 5);
1803 case URLIS_DIRECTORY:
1804 last = pszUrl + strlenW(pszUrl) - 1;
1805 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1807 case URLIS_URL:
1808 return PathIsURLW(pszUrl);
1810 case URLIS_NOHISTORY:
1811 case URLIS_APPLIABLE:
1812 case URLIS_HASQUERY:
1813 default:
1814 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1816 return FALSE;
1819 /*************************************************************************
1820 * UrlIsNoHistoryA [SHLWAPI.@]
1822 * Determine if a Url should not be stored in the users history list.
1824 * PARAMS
1825 * pszUrl [I] Url to check
1827 * RETURNS
1828 * TRUE, if pszUrl should be excluded from the history list,
1829 * FALSE otherwise.
1831 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1833 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1836 /*************************************************************************
1837 * UrlIsNoHistoryW [SHLWAPI.@]
1839 * See UrlIsNoHistoryA.
1841 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1843 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1846 /*************************************************************************
1847 * UrlIsOpaqueA [SHLWAPI.@]
1849 * Determine if a Url is opaque.
1851 * PARAMS
1852 * pszUrl [I] Url to check
1854 * RETURNS
1855 * TRUE if pszUrl is opaque,
1856 * FALSE Otherwise.
1858 * NOTES
1859 * An opaque Url is one that does not start with "<protocol>://".
1861 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1863 return UrlIsA(pszUrl, URLIS_OPAQUE);
1866 /*************************************************************************
1867 * UrlIsOpaqueW [SHLWAPI.@]
1869 * See UrlIsOpaqueA.
1871 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1873 return UrlIsW(pszUrl, URLIS_OPAQUE);
1876 /*************************************************************************
1877 * Scans for characters of type "type" and when not matching found,
1878 * returns pointer to it and length in size.
1880 * Characters tested based on RFC 1738
1882 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1884 static DWORD alwayszero = 0;
1885 BOOL cont = TRUE;
1887 *size = 0;
1889 switch(type){
1891 case SCHEME:
1892 while (cont) {
1893 if ( (islowerW(*start) && isalphaW(*start)) ||
1894 isdigitW(*start) ||
1895 (*start == '+') ||
1896 (*start == '-') ||
1897 (*start == '.')) {
1898 start++;
1899 (*size)++;
1901 else
1902 cont = FALSE;
1904 break;
1906 case USERPASS:
1907 while (cont) {
1908 if ( isalphaW(*start) ||
1909 isdigitW(*start) ||
1910 /* user/password only characters */
1911 (*start == ';') ||
1912 (*start == '?') ||
1913 (*start == '&') ||
1914 (*start == '=') ||
1915 /* *extra* characters */
1916 (*start == '!') ||
1917 (*start == '*') ||
1918 (*start == '\'') ||
1919 (*start == '(') ||
1920 (*start == ')') ||
1921 (*start == ',') ||
1922 /* *safe* characters */
1923 (*start == '$') ||
1924 (*start == '_') ||
1925 (*start == '+') ||
1926 (*start == '-') ||
1927 (*start == '.') ||
1928 (*start == ' ')) {
1929 start++;
1930 (*size)++;
1931 } else if (*start == '%') {
1932 if (isxdigitW(*(start+1)) &&
1933 isxdigitW(*(start+2))) {
1934 start += 3;
1935 *size += 3;
1936 } else
1937 cont = FALSE;
1938 } else
1939 cont = FALSE;
1941 break;
1943 case PORT:
1944 while (cont) {
1945 if (isdigitW(*start)) {
1946 start++;
1947 (*size)++;
1949 else
1950 cont = FALSE;
1952 break;
1954 case HOST:
1955 while (cont) {
1956 if (isalnumW(*start) ||
1957 (*start == '-') ||
1958 (*start == '.') ||
1959 (*start == ' ') ) {
1960 start++;
1961 (*size)++;
1963 else
1964 cont = FALSE;
1966 break;
1967 default:
1968 FIXME("unknown type %d\n", type);
1969 return (LPWSTR)&alwayszero;
1971 /* TRACE("scanned %d characters next char %p<%c>\n",
1972 *size, start, *start); */
1973 return start;
1976 /*************************************************************************
1977 * Attempt to parse URL into pieces.
1979 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1981 LPCWSTR work;
1983 memset(pl, 0, sizeof(WINE_PARSE_URL));
1984 pl->pScheme = pszUrl;
1985 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1986 if (!*work || (*work != ':')) goto ErrorExit;
1987 work++;
1988 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
1989 pl->pUserName = work + 2;
1990 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1991 if (*work == ':' ) {
1992 /* parse password */
1993 work++;
1994 pl->pPassword = work;
1995 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1996 if (*work != '@') {
1997 /* what we just parsed must be the hostname and port
1998 * so reset pointers and clear then let it parse */
1999 pl->szUserName = pl->szPassword = 0;
2000 work = pl->pUserName - 1;
2001 pl->pUserName = pl->pPassword = 0;
2003 } else if (*work == '@') {
2004 /* no password */
2005 pl->szPassword = 0;
2006 pl->pPassword = 0;
2007 } else if (!*work || (*work == '/') || (*work == '.')) {
2008 /* what was parsed was hostname, so reset pointers and let it parse */
2009 pl->szUserName = pl->szPassword = 0;
2010 work = pl->pUserName - 1;
2011 pl->pUserName = pl->pPassword = 0;
2012 } else goto ErrorExit;
2014 /* now start parsing hostname or hostnumber */
2015 work++;
2016 pl->pHostName = work;
2017 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2018 if (*work == ':') {
2019 /* parse port */
2020 work++;
2021 pl->pPort = work;
2022 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2024 if (*work == '/') {
2025 /* see if query string */
2026 pl->pQuery = strchrW(work, '?');
2027 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2029 SuccessExit:
2030 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2031 pl->pScheme, pl->szScheme,
2032 pl->pUserName, pl->szUserName,
2033 pl->pPassword, pl->szPassword,
2034 pl->pHostName, pl->szHostName,
2035 pl->pPort, pl->szPort,
2036 pl->pQuery, pl->szQuery);
2037 return S_OK;
2038 ErrorExit:
2039 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2040 return E_INVALIDARG;
2043 /*************************************************************************
2044 * UrlGetPartA [SHLWAPI.@]
2046 * Retrieve part of a Url.
2048 * PARAMS
2049 * pszIn [I] Url to parse
2050 * pszOut [O] Destination for part of pszIn requested
2051 * pcchOut [I] Size of pszOut
2052 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2053 * needed size of pszOut INCLUDING '\0'.
2054 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2055 * dwFlags [I] URL_ flags from "shlwapi.h"
2057 * RETURNS
2058 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2059 * Failure: An HRESULT error code describing the error.
2061 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2062 DWORD dwPart, DWORD dwFlags)
2064 LPWSTR in, out;
2065 DWORD ret, len, len2;
2067 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2068 return E_INVALIDARG;
2070 in = HeapAlloc(GetProcessHeap(), 0,
2071 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2072 out = in + INTERNET_MAX_URL_LENGTH;
2074 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2076 len = INTERNET_MAX_URL_LENGTH;
2077 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2079 if (FAILED(ret)) {
2080 HeapFree(GetProcessHeap(), 0, in);
2081 return ret;
2084 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2085 if (len2 > *pcchOut) {
2086 *pcchOut = len2+1;
2087 HeapFree(GetProcessHeap(), 0, in);
2088 return E_POINTER;
2090 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2091 *pcchOut = len2-1;
2092 HeapFree(GetProcessHeap(), 0, in);
2093 return ret;
2096 /*************************************************************************
2097 * UrlGetPartW [SHLWAPI.@]
2099 * See UrlGetPartA.
2101 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2102 DWORD dwPart, DWORD dwFlags)
2104 WINE_PARSE_URL pl;
2105 HRESULT ret;
2106 DWORD scheme, size, schsize;
2107 LPCWSTR addr, schaddr;
2109 TRACE("(%s %p %p(%d) %08x %08x)\n",
2110 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2112 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2113 return E_INVALIDARG;
2115 *pszOut = '\0';
2117 addr = strchrW(pszIn, ':');
2118 if(!addr)
2119 scheme = URL_SCHEME_UNKNOWN;
2120 else
2121 scheme = get_scheme_code(pszIn, addr-pszIn);
2123 ret = URL_ParseUrl(pszIn, &pl);
2125 switch (dwPart) {
2126 case URL_PART_SCHEME:
2127 if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
2128 *pcchOut = 0;
2129 return S_FALSE;
2131 addr = pl.pScheme;
2132 size = pl.szScheme;
2133 break;
2135 case URL_PART_HOSTNAME:
2136 switch(scheme) {
2137 case URL_SCHEME_FTP:
2138 case URL_SCHEME_HTTP:
2139 case URL_SCHEME_GOPHER:
2140 case URL_SCHEME_TELNET:
2141 case URL_SCHEME_FILE:
2142 case URL_SCHEME_HTTPS:
2143 break;
2144 default:
2145 *pcchOut = 0;
2146 return E_FAIL;
2149 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2150 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2151 *pcchOut = 0;
2152 return S_FALSE;
2155 if (!pl.szHostName) {
2156 *pcchOut = 0;
2157 return S_FALSE;
2159 addr = pl.pHostName;
2160 size = pl.szHostName;
2161 break;
2163 case URL_PART_USERNAME:
2164 if (!pl.szUserName) {
2165 *pcchOut = 0;
2166 return S_FALSE;
2168 addr = pl.pUserName;
2169 size = pl.szUserName;
2170 break;
2172 case URL_PART_PASSWORD:
2173 if (!pl.szPassword) {
2174 *pcchOut = 0;
2175 return S_FALSE;
2177 addr = pl.pPassword;
2178 size = pl.szPassword;
2179 break;
2181 case URL_PART_PORT:
2182 if (!pl.szPort) {
2183 *pcchOut = 0;
2184 return S_FALSE;
2186 addr = pl.pPort;
2187 size = pl.szPort;
2188 break;
2190 case URL_PART_QUERY:
2191 if (!pl.szQuery) {
2192 *pcchOut = 0;
2193 return S_FALSE;
2195 addr = pl.pQuery;
2196 size = pl.szQuery;
2197 break;
2199 default:
2200 *pcchOut = 0;
2201 return E_INVALIDARG;
2204 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2205 if(!pl.pScheme || !pl.szScheme) {
2206 *pcchOut = 0;
2207 return E_FAIL;
2209 schaddr = pl.pScheme;
2210 schsize = pl.szScheme;
2211 if (*pcchOut < schsize + size + 2) {
2212 *pcchOut = schsize + size + 2;
2213 return E_POINTER;
2215 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2216 pszOut[schsize] = ':';
2217 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2218 pszOut[schsize+1+size] = 0;
2219 *pcchOut = schsize + 1 + size;
2221 else {
2222 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2223 memcpy(pszOut, addr, size*sizeof(WCHAR));
2224 pszOut[size] = 0;
2225 *pcchOut = size;
2227 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2229 return ret;
2232 /*************************************************************************
2233 * PathIsURLA [SHLWAPI.@]
2235 * Check if the given path is a Url.
2237 * PARAMS
2238 * lpszPath [I] Path to check.
2240 * RETURNS
2241 * TRUE if lpszPath is a Url.
2242 * FALSE if lpszPath is NULL or not a Url.
2244 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2246 PARSEDURLA base;
2247 HRESULT hres;
2249 TRACE("%s\n", debugstr_a(lpstrPath));
2251 if (!lpstrPath || !*lpstrPath) return FALSE;
2253 /* get protocol */
2254 base.cbSize = sizeof(base);
2255 hres = ParseURLA(lpstrPath, &base);
2256 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2259 /*************************************************************************
2260 * PathIsURLW [SHLWAPI.@]
2262 * See PathIsURLA.
2264 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2266 PARSEDURLW base;
2267 HRESULT hres;
2269 TRACE("%s\n", debugstr_w(lpstrPath));
2271 if (!lpstrPath || !*lpstrPath) return FALSE;
2273 /* get protocol */
2274 base.cbSize = sizeof(base);
2275 hres = ParseURLW(lpstrPath, &base);
2276 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2279 /*************************************************************************
2280 * UrlCreateFromPathA [SHLWAPI.@]
2282 * See UrlCreateFromPathW
2284 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2286 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2287 WCHAR *urlW = bufW;
2288 UNICODE_STRING pathW;
2289 HRESULT ret;
2290 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2292 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2293 return E_INVALIDARG;
2294 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2295 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2296 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2298 if(ret == S_OK || ret == S_FALSE) {
2299 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2300 if(*pcchUrl > lenA) {
2301 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2302 pszUrl[lenA] = 0;
2303 *pcchUrl = lenA;
2304 } else {
2305 *pcchUrl = lenA + 1;
2306 ret = E_POINTER;
2309 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2310 RtlFreeUnicodeString(&pathW);
2311 return ret;
2314 /*************************************************************************
2315 * UrlCreateFromPathW [SHLWAPI.@]
2317 * Create a Url from a file path.
2319 * PARAMS
2320 * pszPath [I] Path to convert
2321 * pszUrl [O] Destination for the converted Url
2322 * pcchUrl [I/O] Length of pszUrl
2323 * dwReserved [I] Reserved, must be 0
2325 * RETURNS
2326 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2327 * Failure: An HRESULT error code.
2329 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2331 DWORD needed;
2332 HRESULT ret;
2333 WCHAR *pszNewUrl;
2334 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2335 WCHAR three_slashesW[] = {'/','/','/',0};
2336 PARSEDURLW parsed_url;
2338 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2340 /* Validate arguments */
2341 if (dwReserved != 0)
2342 return E_INVALIDARG;
2343 if (!pszUrl || !pcchUrl)
2344 return E_INVALIDARG;
2347 parsed_url.cbSize = sizeof(parsed_url);
2348 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2349 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2350 needed = strlenW(pszPath);
2351 if (needed >= *pcchUrl) {
2352 *pcchUrl = needed + 1;
2353 return E_POINTER;
2354 } else {
2355 *pcchUrl = needed;
2356 strcpyW(pszUrl, pszPath);
2357 return S_FALSE;
2362 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2363 strcpyW(pszNewUrl, file_colonW);
2364 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2365 strcatW(pszNewUrl, three_slashesW);
2366 strcatW(pszNewUrl, pszPath);
2367 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2369 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2370 return ret;
2373 /*************************************************************************
2374 * SHAutoComplete [SHLWAPI.@]
2376 * Enable auto-completion for an edit control.
2378 * PARAMS
2379 * hwndEdit [I] Handle of control to enable auto-completion for
2380 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2382 * RETURNS
2383 * Success: S_OK. Auto-completion is enabled for the control.
2384 * Failure: An HRESULT error code indicating the error.
2386 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2388 FIXME("stub\n");
2389 return S_FALSE;
2392 /*************************************************************************
2393 * MLBuildResURLA [SHLWAPI.405]
2395 * Create a Url pointing to a resource in a module.
2397 * PARAMS
2398 * lpszLibName [I] Name of the module containing the resource
2399 * hMod [I] Callers module handle
2400 * dwFlags [I] Undocumented flags for loading the module
2401 * lpszRes [I] Resource name
2402 * lpszDest [O] Destination for resulting Url
2403 * dwDestLen [I] Length of lpszDest
2405 * RETURNS
2406 * Success: S_OK. lpszDest contains the resource Url.
2407 * Failure: E_INVALIDARG, if any argument is invalid, or
2408 * E_FAIL if dwDestLen is too small.
2410 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2411 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2413 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2414 HRESULT hRet;
2416 if (lpszLibName)
2417 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2419 if (lpszRes)
2420 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2422 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2423 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2425 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2426 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2427 if (SUCCEEDED(hRet) && lpszDest)
2428 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2430 return hRet;
2433 /*************************************************************************
2434 * MLBuildResURLA [SHLWAPI.406]
2436 * See MLBuildResURLA.
2438 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2439 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2441 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2442 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2443 HRESULT hRet = E_FAIL;
2445 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2446 debugstr_w(lpszRes), lpszDest, dwDestLen);
2448 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2449 !lpszDest || (dwFlags && dwFlags != 2))
2450 return E_INVALIDARG;
2452 if (dwDestLen >= szResLen + 1)
2454 dwDestLen -= (szResLen + 1);
2455 memcpy(lpszDest, szRes, sizeof(szRes));
2457 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2459 if (hMod)
2461 WCHAR szBuff[MAX_PATH];
2462 DWORD len;
2464 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2465 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2467 DWORD dwPathLen = strlenW(szBuff) + 1;
2469 if (dwDestLen >= dwPathLen)
2471 DWORD dwResLen;
2473 dwDestLen -= dwPathLen;
2474 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2476 dwResLen = strlenW(lpszRes) + 1;
2477 if (dwDestLen >= dwResLen + 1)
2479 lpszDest[szResLen + dwPathLen-1] = '/';
2480 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2481 hRet = S_OK;
2485 MLFreeLibrary(hMod);
2488 return hRet;
2491 /***********************************************************************
2492 * UrlFixupW [SHLWAPI.462]
2494 * Checks the scheme part of a URL and attempts to correct misspellings.
2496 * PARAMS
2497 * lpszUrl [I] Pointer to the URL to be corrected
2498 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2499 * dwMaxChars [I] Maximum size of corrected URL
2501 * RETURNS
2502 * success: S_OK if URL corrected or already correct
2503 * failure: S_FALSE if unable to correct / COM error code if other error
2506 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2508 DWORD srcLen;
2510 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2512 if (!url)
2513 return E_FAIL;
2515 srcLen = lstrlenW(url) + 1;
2517 /* For now just copy the URL directly */
2518 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2520 return S_OK;