gdiplus: Add a stub for GdipSetLineLinearBlend.
[wine/multimedia.git] / dlls / shlwapi / url.c
blobba7cdb6ee92c6e4b35505d7bd9020dd77c707d47
1 /*
2 * Url functions
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #define NO_SHLWAPI_STREAM
35 #include "shlwapi.h"
36 #include "wine/debug.h"
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
47 static const struct {
48 URL_SCHEME scheme_number;
49 WCHAR scheme_name[12];
50 } shlwapi_schemes[] = {
51 {URL_SCHEME_FTP, {'f','t','p',0}},
52 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
53 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
54 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
55 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
56 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
57 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
58 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
59 {URL_SCHEME_FILE, {'f','i','l','e',0}},
60 {URL_SCHEME_MK, {'m','k',0}},
61 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
62 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
63 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
64 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
65 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
66 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
67 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
68 {URL_SCHEME_RES, {'r','e','s',0}},
71 typedef struct {
72 LPCWSTR pScheme; /* [out] start of scheme */
73 DWORD szScheme; /* [out] size of scheme (until colon) */
74 LPCWSTR pUserName; /* [out] start of Username */
75 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
76 LPCWSTR pPassword; /* [out] start of Password */
77 DWORD szPassword; /* [out] size of Password (until "@") */
78 LPCWSTR pHostName; /* [out] start of Hostname */
79 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
80 LPCWSTR pPort; /* [out] start of Port */
81 DWORD szPort; /* [out] size of Port (until "/" or eos) */
82 LPCWSTR pQuery; /* [out] start of Query */
83 DWORD szQuery; /* [out] size of Query (until eos) */
84 } WINE_PARSE_URL;
86 typedef enum {
87 SCHEME,
88 HOST,
89 PORT,
90 USERPASS,
91 } WINE_URL_SCAN_TYPE;
93 static const CHAR hexDigits[] = "0123456789ABCDEF";
95 static const WCHAR fileW[] = {'f','i','l','e','\0'};
97 static const unsigned char HashDataLookup[256] = {
98 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
99 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
100 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
101 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
102 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
103 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
104 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
105 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
106 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
107 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
108 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
109 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
110 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
111 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
112 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
113 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
114 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
115 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
116 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
117 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
119 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
121 int i;
123 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
124 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
125 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
126 return shlwapi_schemes[i].scheme_number;
129 return URL_SCHEME_UNKNOWN;
132 static BOOL URL_JustLocation(LPCWSTR str)
134 while(*str && (*str == '/')) str++;
135 if (*str) {
136 while (*str && ((*str == '-') ||
137 (*str == '.') ||
138 isalnumW(*str))) str++;
139 if (*str == '/') return FALSE;
141 return TRUE;
145 /*************************************************************************
146 * @ [SHLWAPI.1]
148 * Parse a Url into its constituent parts.
150 * PARAMS
151 * x [I] Url to parse
152 * y [O] Undocumented structure holding the parsed information
154 * RETURNS
155 * Success: S_OK. y contains the parsed Url details.
156 * Failure: An HRESULT error code.
158 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
160 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
161 DWORD cnt, len;
163 y->nScheme = URL_SCHEME_INVALID;
164 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
165 /* FIXME: leading white space generates error of 0x80041001 which
166 * is undefined
168 if (*x <= ' ') return 0x80041001;
169 cnt = 0;
170 y->cchProtocol = 0;
171 y->pszProtocol = x;
172 while (*x) {
173 if (*x == ':') {
174 y->cchProtocol = cnt;
175 cnt = -1;
176 y->pszSuffix = x+1;
177 break;
179 x++;
180 cnt++;
183 /* check for no scheme in string start */
184 /* (apparently schemes *must* be larger than a single character) */
185 if ((*x == '\0') || (y->cchProtocol <= 1)) {
186 y->pszProtocol = NULL;
187 return 0x80041001;
190 /* found scheme, set length of remainder */
191 y->cchSuffix = lstrlenA(y->pszSuffix);
193 len = MultiByteToWideChar(CP_ACP, 0, y->pszProtocol, y->cchProtocol,
194 scheme, sizeof(scheme)/sizeof(WCHAR));
195 y->nScheme = get_scheme_code(scheme, len);
197 return S_OK;
200 /*************************************************************************
201 * @ [SHLWAPI.2]
203 * Unicode version of ParseURLA.
205 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
207 DWORD cnt;
209 y->nScheme = URL_SCHEME_INVALID;
210 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
211 /* FIXME: leading white space generates error of 0x80041001 which
212 * is undefined
214 if (*x <= ' ') return 0x80041001;
215 cnt = 0;
216 y->cchProtocol = 0;
217 y->pszProtocol = x;
218 while (*x) {
219 if (*x == ':') {
220 y->cchProtocol = cnt;
221 cnt = -1;
222 y->pszSuffix = x+1;
223 break;
225 x++;
226 cnt++;
229 /* check for no scheme in string start */
230 /* (apparently schemes *must* be larger than a single character) */
231 if ((*x == '\0') || (y->cchProtocol <= 1)) {
232 y->pszProtocol = NULL;
233 return 0x80041001;
236 /* found scheme, set length of remainder */
237 y->cchSuffix = lstrlenW(y->pszSuffix);
238 y->nScheme = get_scheme_code(y->pszProtocol, y->cchProtocol);
240 return S_OK;
243 /*************************************************************************
244 * UrlCanonicalizeA [SHLWAPI.@]
246 * Canonicalize a Url.
248 * PARAMS
249 * pszUrl [I] Url to cCanonicalize
250 * pszCanonicalized [O] Destination for converted Url.
251 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
252 * dwFlags [I] Flags controlling the conversion.
254 * RETURNS
255 * Success: S_OK. The pszCanonicalized contains the converted Url.
256 * Failure: E_POINTER, if *pcchCanonicalized is too small.
258 * MSDN incorrectly describes the flags for this function. They should be:
259 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
260 *| URL_ESCAPE_SPACES_ONLY 0x04000000
261 *| URL_ESCAPE_PERCENT 0x00001000
262 *| URL_ESCAPE_UNSAFE 0x10000000
263 *| URL_UNESCAPE 0x10000000
264 *| URL_DONT_SIMPLIFY 0x08000000
265 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
267 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
268 LPDWORD pcchCanonicalized, DWORD dwFlags)
270 LPWSTR base, canonical;
271 HRESULT ret;
272 DWORD len, len2;
274 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
275 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
277 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
278 return E_INVALIDARG;
280 base = HeapAlloc(GetProcessHeap(), 0,
281 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
282 canonical = base + INTERNET_MAX_URL_LENGTH;
284 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
285 len = INTERNET_MAX_URL_LENGTH;
287 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
288 if (ret != S_OK) {
289 *pcchCanonicalized = len * 2;
290 HeapFree(GetProcessHeap(), 0, base);
291 return ret;
294 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
295 if (len2 > *pcchCanonicalized) {
296 *pcchCanonicalized = len2;
297 HeapFree(GetProcessHeap(), 0, base);
298 return E_POINTER;
300 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
301 *pcchCanonicalized = len;
302 HeapFree(GetProcessHeap(), 0, base);
303 return S_OK;
306 /*************************************************************************
307 * UrlCanonicalizeW [SHLWAPI.@]
309 * See UrlCanonicalizeA.
311 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
312 LPDWORD pcchCanonicalized, DWORD dwFlags)
314 HRESULT hr = S_OK;
315 DWORD EscapeFlags;
316 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
317 INT nByteLen, state;
318 DWORD nLen, nWkLen;
319 WCHAR slash = '/';
321 static const WCHAR wszFile[] = {'f','i','l','e',':'};
323 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
324 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
326 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
327 return E_INVALIDARG;
329 if(!*pszUrl) {
330 *pszCanonicalized = 0;
331 return S_OK;
334 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
335 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
336 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
338 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
339 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
340 slash = '\\';
343 * state =
344 * 0 initial 1,3
345 * 1 have 2[+] alnum 2,3
346 * 2 have scheme (found :) 4,6,3
347 * 3 failed (no location)
348 * 4 have // 5,3
349 * 5 have 1[+] alnum 6,3
350 * 6 have location (found /) save root location
353 wk1 = (LPWSTR)pszUrl;
354 wk2 = lpszUrlCpy;
355 state = 0;
357 if(pszUrl[1] == ':') { /* Assume path */
358 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
360 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
361 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
362 if (dwFlags & URL_FILE_USE_PATHURL)
364 slash = '\\';
365 --wk2;
367 else
368 dwFlags |= URL_ESCAPE_UNSAFE;
369 state = 5;
372 while (*wk1) {
373 switch (state) {
374 case 0:
375 if (!isalnumW(*wk1)) {state = 3; break;}
376 *wk2++ = *wk1++;
377 if (!isalnumW(*wk1)) {state = 3; break;}
378 *wk2++ = *wk1++;
379 state = 1;
380 break;
381 case 1:
382 *wk2++ = *wk1;
383 if (*wk1++ == ':') state = 2;
384 break;
385 case 2:
386 if (*wk1 != '/') {state = 3; break;}
387 *wk2++ = *wk1++;
388 if (*wk1 != '/') {state = 6; break;}
389 *wk2++ = *wk1++;
390 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
391 wk1++;
392 state = 4;
393 break;
394 case 3:
395 nWkLen = strlenW(wk1);
396 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
397 mp = wk2;
398 wk1 += nWkLen;
399 wk2 += nWkLen;
401 while(mp < wk2) {
402 if(*mp == '/' || *mp == '\\')
403 *mp = slash;
404 mp++;
406 break;
407 case 4:
408 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
409 {state = 3; break;}
410 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
411 *wk2++ = *wk1++;
412 state = 5;
413 if (!*wk1)
414 *wk2++ = slash;
415 break;
416 case 5:
417 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
418 while(*wk1 == '/' || *wk1 == '\\') {
419 *wk2++ = slash;
420 wk1++;
422 state = 6;
423 break;
424 case 6:
425 if(dwFlags & URL_DONT_SIMPLIFY) {
426 state = 3;
427 break;
430 /* Now at root location, cannot back up any more. */
431 /* "root" will point at the '/' */
433 root = wk2-1;
434 while (*wk1) {
435 mp = strchrW(wk1, '/');
436 mp2 = strchrW(wk1, '\\');
437 if(mp2 && (!mp || mp2 < mp))
438 mp = mp2;
439 if (!mp) {
440 nWkLen = strlenW(wk1);
441 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
442 wk1 += nWkLen;
443 wk2 += nWkLen;
444 continue;
446 nLen = mp - wk1;
447 if(nLen) {
448 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
449 wk2 += nLen;
450 wk1 += nLen;
452 *wk2++ = slash;
453 wk1++;
455 if (*wk1 == '.') {
456 TRACE("found '/.'\n");
457 if (wk1[1] == '/' || wk1[1] == '\\') {
458 /* case of /./ -> skip the ./ */
459 wk1 += 2;
461 else if (wk1[1] == '.') {
462 /* found /.. look for next / */
463 TRACE("found '/..'\n");
464 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
465 || wk1[2] == '#' || !wk1[2]) {
466 /* case /../ -> need to backup wk2 */
467 TRACE("found '/../'\n");
468 *(wk2-1) = '\0'; /* set end of string */
469 mp = strrchrW(root, slash);
470 if (mp && (mp >= root)) {
471 /* found valid backup point */
472 wk2 = mp + 1;
473 if(wk1[2] != '/' && wk1[2] != '\\')
474 wk1 += 2;
475 else
476 wk1 += 3;
478 else {
479 /* did not find point, restore '/' */
480 *(wk2-1) = slash;
486 *wk2 = '\0';
487 break;
488 default:
489 FIXME("how did we get here - state=%d\n", state);
490 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
491 return E_INVALIDARG;
493 *wk2 = '\0';
494 TRACE("Simplified, orig <%s>, simple <%s>\n",
495 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
497 nLen = lstrlenW(lpszUrlCpy);
498 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
499 lpszUrlCpy[--nLen]=0;
501 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
502 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
504 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
505 URL_ESCAPE_SPACES_ONLY |
506 URL_ESCAPE_PERCENT |
507 URL_DONT_ESCAPE_EXTRA_INFO |
508 URL_ESCAPE_SEGMENT_ONLY ))) {
509 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
510 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
511 EscapeFlags);
512 } else { /* No escaping needed, just copy the string */
513 nLen = lstrlenW(lpszUrlCpy);
514 if(nLen < *pcchCanonicalized)
515 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
516 else {
517 hr = E_POINTER;
518 nLen++;
520 *pcchCanonicalized = nLen;
523 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
525 if (hr == S_OK)
526 TRACE("result %s\n", debugstr_w(pszCanonicalized));
528 return hr;
531 /*************************************************************************
532 * UrlCombineA [SHLWAPI.@]
534 * Combine two Urls.
536 * PARAMS
537 * pszBase [I] Base Url
538 * pszRelative [I] Url to combine with pszBase
539 * pszCombined [O] Destination for combined Url
540 * pcchCombined [O] Destination for length of pszCombined
541 * dwFlags [I] URL_ flags from "shlwapi.h"
543 * RETURNS
544 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
545 * contains its length.
546 * Failure: An HRESULT error code indicating the error.
548 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
549 LPSTR pszCombined, LPDWORD pcchCombined,
550 DWORD dwFlags)
552 LPWSTR base, relative, combined;
553 DWORD ret, len, len2;
555 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
556 debugstr_a(pszBase),debugstr_a(pszRelative),
557 pcchCombined?*pcchCombined:0,dwFlags);
559 if(!pszBase || !pszRelative || !pcchCombined)
560 return E_INVALIDARG;
562 base = HeapAlloc(GetProcessHeap(), 0,
563 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
564 relative = base + INTERNET_MAX_URL_LENGTH;
565 combined = relative + INTERNET_MAX_URL_LENGTH;
567 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
568 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
569 len = *pcchCombined;
571 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
572 if (ret != S_OK) {
573 *pcchCombined = len;
574 HeapFree(GetProcessHeap(), 0, base);
575 return ret;
578 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
579 if (len2 > *pcchCombined) {
580 *pcchCombined = len2;
581 HeapFree(GetProcessHeap(), 0, base);
582 return E_POINTER;
584 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
585 0, 0);
586 *pcchCombined = len2;
587 HeapFree(GetProcessHeap(), 0, base);
588 return S_OK;
591 /*************************************************************************
592 * UrlCombineW [SHLWAPI.@]
594 * See UrlCombineA.
596 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
597 LPWSTR pszCombined, LPDWORD pcchCombined,
598 DWORD dwFlags)
600 PARSEDURLW base, relative;
601 DWORD myflags, sizeloc = 0;
602 DWORD len, res1, res2, process_case = 0;
603 LPWSTR work, preliminary, mbase, mrelative;
604 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
605 static const WCHAR single_slash[] = {'/','\0'};
606 HRESULT ret;
608 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
609 debugstr_w(pszBase),debugstr_w(pszRelative),
610 pcchCombined?*pcchCombined:0,dwFlags);
612 if(!pszBase || !pszRelative || !pcchCombined)
613 return E_INVALIDARG;
615 base.cbSize = sizeof(base);
616 relative.cbSize = sizeof(relative);
618 /* Get space for duplicates of the input and the output */
619 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
620 sizeof(WCHAR));
621 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
622 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
623 *preliminary = '\0';
625 /* Canonicalize the base input prior to looking for the scheme */
626 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
627 len = INTERNET_MAX_URL_LENGTH;
628 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
630 /* Canonicalize the relative input prior to looking for the scheme */
631 len = INTERNET_MAX_URL_LENGTH;
632 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
634 /* See if the base has a scheme */
635 res1 = ParseURLW(mbase, &base);
636 if (res1) {
637 /* if pszBase has no scheme, then return pszRelative */
638 TRACE("no scheme detected in Base\n");
639 process_case = 1;
641 else do {
642 /* mk is a special case */
643 if(base.nScheme == URL_SCHEME_MK) {
644 static const WCHAR wsz[] = {':',':',0};
646 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
647 if(ptr) {
648 int delta;
650 ptr += 2;
651 delta = ptr-base.pszSuffix;
652 base.cchProtocol += delta;
653 base.pszSuffix += delta;
654 base.cchSuffix -= delta;
658 /* get size of location field (if it exists) */
659 work = (LPWSTR)base.pszSuffix;
660 sizeloc = 0;
661 if (*work++ == '/') {
662 if (*work++ == '/') {
663 /* At this point have start of location and
664 * it ends at next '/' or end of string.
666 while(*work && (*work != '/')) work++;
667 sizeloc = (DWORD)(work - base.pszSuffix);
671 /* Change .sizep2 to not have the last leaf in it,
672 * Note: we need to start after the location (if it exists)
674 work = strrchrW((base.pszSuffix+sizeloc), '/');
675 if (work) {
676 len = (DWORD)(work - base.pszSuffix + 1);
677 base.cchSuffix = len;
681 * At this point:
682 * .pszSuffix points to location (starting with '//')
683 * .cchSuffix length of location (above) and rest less the last
684 * leaf (if any)
685 * sizeloc length of location (above) up to but not including
686 * the last '/'
689 res2 = ParseURLW(mrelative, &relative);
690 if (res2) {
691 /* no scheme in pszRelative */
692 TRACE("no scheme detected in Relative\n");
693 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
694 relative.cchSuffix = strlenW(mrelative);
695 if (*pszRelative == ':') {
696 /* case that is either left alone or uses pszBase */
697 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
698 process_case = 5;
699 break;
701 process_case = 1;
702 break;
704 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
705 /* case that becomes "file:///" */
706 strcpyW(preliminary, myfilestr);
707 process_case = 1;
708 break;
710 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
711 /* pszRelative has location and rest */
712 process_case = 3;
713 break;
715 if (*mrelative == '/') {
716 /* case where pszRelative is root to location */
717 process_case = 4;
718 break;
720 process_case = (*base.pszSuffix == '/') ? 5 : 3;
721 break;
724 /* handle cases where pszRelative has scheme */
725 if ((base.cchProtocol == relative.cchProtocol) &&
726 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
728 /* since the schemes are the same */
729 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
730 /* case where pszRelative replaces location and following */
731 process_case = 3;
732 break;
734 if (*relative.pszSuffix == '/') {
735 /* case where pszRelative is root to location */
736 process_case = 4;
737 break;
739 /* replace either just location if base's location starts with a
740 * slash or otherwise everything */
741 process_case = (*base.pszSuffix == '/') ? 5 : 1;
742 break;
744 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
745 /* case where pszRelative replaces scheme, location,
746 * and following and handles PLUGGABLE
748 process_case = 2;
749 break;
751 process_case = 1;
752 break;
753 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
755 ret = S_OK;
756 switch (process_case) {
758 case 1: /*
759 * Return pszRelative appended to what ever is in pszCombined,
760 * (which may the string "file:///"
762 strcatW(preliminary, mrelative);
763 break;
765 case 2: /*
766 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
767 * and pszRelative starts with "//", then append a "/"
769 strcpyW(preliminary, mrelative);
770 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
771 URL_JustLocation(relative.pszSuffix))
772 strcatW(preliminary, single_slash);
773 break;
775 case 3: /*
776 * Return the pszBase scheme with pszRelative. Basically
777 * keeps the scheme and replaces the domain and following.
779 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
780 work = preliminary + base.cchProtocol + 1;
781 strcpyW(work, relative.pszSuffix);
782 break;
784 case 4: /*
785 * Return the pszBase scheme and location but everything
786 * after the location is pszRelative. (Replace document
787 * from root on.)
789 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
790 work = preliminary + base.cchProtocol + 1 + sizeloc;
791 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
792 *(work++) = '/';
793 strcpyW(work, relative.pszSuffix);
794 break;
796 case 5: /*
797 * Return the pszBase without its document (if any) and
798 * append pszRelative after its scheme.
800 memcpy(preliminary, base.pszProtocol,
801 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
802 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
803 if (*work++ != '/')
804 *(work++) = '/';
805 strcpyW(work, relative.pszSuffix);
806 break;
808 default:
809 FIXME("How did we get here????? process_case=%d\n", process_case);
810 ret = E_INVALIDARG;
813 if (ret == S_OK) {
814 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
815 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
816 if(SUCCEEDED(ret) && pszCombined) {
817 lstrcpyW(pszCombined, mrelative);
819 TRACE("return-%d len=%d, %s\n",
820 process_case, *pcchCombined, debugstr_w(pszCombined));
822 HeapFree(GetProcessHeap(), 0, preliminary);
823 return ret;
826 /*************************************************************************
827 * UrlEscapeA [SHLWAPI.@]
830 HRESULT WINAPI UrlEscapeA(
831 LPCSTR pszUrl,
832 LPSTR pszEscaped,
833 LPDWORD pcchEscaped,
834 DWORD dwFlags)
836 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
837 WCHAR *escapedW = bufW;
838 UNICODE_STRING urlW;
839 HRESULT ret;
840 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
842 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
843 return E_INVALIDARG;
845 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
846 return E_INVALIDARG;
847 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
848 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
849 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
851 if(ret == S_OK) {
852 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
853 if(*pcchEscaped > lenA) {
854 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
855 pszEscaped[lenA] = 0;
856 *pcchEscaped = lenA;
857 } else {
858 *pcchEscaped = lenA + 1;
859 ret = E_POINTER;
862 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
863 RtlFreeUnicodeString(&urlW);
864 return ret;
867 #define WINE_URL_BASH_AS_SLASH 0x01
868 #define WINE_URL_COLLAPSE_SLASHES 0x02
869 #define WINE_URL_ESCAPE_SLASH 0x04
870 #define WINE_URL_ESCAPE_HASH 0x08
871 #define WINE_URL_ESCAPE_QUESTION 0x10
872 #define WINE_URL_STOP_ON_HASH 0x20
873 #define WINE_URL_STOP_ON_QUESTION 0x40
875 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
878 if (isalnumW(ch))
879 return FALSE;
881 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
882 if(ch == ' ')
883 return TRUE;
884 else
885 return FALSE;
888 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
889 return TRUE;
891 if (ch <= 31 || ch >= 127)
892 return TRUE;
894 else {
895 switch (ch) {
896 case ' ':
897 case '<':
898 case '>':
899 case '\"':
900 case '{':
901 case '}':
902 case '|':
903 case '\\':
904 case '^':
905 case ']':
906 case '[':
907 case '`':
908 case '&':
909 return TRUE;
911 case '/':
912 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
913 return FALSE;
915 case '?':
916 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
917 return FALSE;
919 case '#':
920 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
921 return FALSE;
923 default:
924 return FALSE;
930 /*************************************************************************
931 * UrlEscapeW [SHLWAPI.@]
933 * Converts unsafe characters in a Url into escape sequences.
935 * PARAMS
936 * pszUrl [I] Url to modify
937 * pszEscaped [O] Destination for modified Url
938 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
939 * dwFlags [I] URL_ flags from "shlwapi.h"
941 * RETURNS
942 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
943 * contains its length.
944 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
945 * pcchEscaped is set to the required length.
947 * Converts unsafe characters into their escape sequences.
949 * NOTES
950 * - By default this function stops converting at the first '?' or
951 * '#' character.
952 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
953 * converted, but the conversion continues past a '?' or '#'.
954 * - Note that this function did not work well (or at all) in shlwapi version 4.
956 * BUGS
957 * Only the following flags are implemented:
958 *| URL_ESCAPE_SPACES_ONLY
959 *| URL_DONT_ESCAPE_EXTRA_INFO
960 *| URL_ESCAPE_SEGMENT_ONLY
961 *| URL_ESCAPE_PERCENT
963 HRESULT WINAPI UrlEscapeW(
964 LPCWSTR pszUrl,
965 LPWSTR pszEscaped,
966 LPDWORD pcchEscaped,
967 DWORD dwFlags)
969 LPCWSTR src;
970 DWORD needed = 0, ret;
971 BOOL stop_escaping = FALSE;
972 WCHAR next[5], *dst = pszEscaped;
973 INT len;
974 PARSEDURLW parsed_url;
975 DWORD int_flags;
976 DWORD slashes = 0;
977 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
979 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
980 pcchEscaped, dwFlags);
982 if(!pszUrl || !pcchEscaped)
983 return E_INVALIDARG;
985 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
986 URL_ESCAPE_SEGMENT_ONLY |
987 URL_DONT_ESCAPE_EXTRA_INFO |
988 URL_ESCAPE_PERCENT))
989 FIXME("Unimplemented flags: %08x\n", dwFlags);
991 /* fix up flags */
992 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
993 /* if SPACES_ONLY specified, reset the other controls */
994 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
995 URL_ESCAPE_PERCENT |
996 URL_ESCAPE_SEGMENT_ONLY);
998 else
999 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1000 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1003 int_flags = 0;
1004 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1005 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1006 } else {
1007 parsed_url.cbSize = sizeof(parsed_url);
1008 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1009 parsed_url.nScheme = URL_SCHEME_INVALID;
1011 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1013 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1014 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1016 switch(parsed_url.nScheme) {
1017 case URL_SCHEME_FILE:
1018 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1019 int_flags &= ~WINE_URL_STOP_ON_HASH;
1020 break;
1022 case URL_SCHEME_HTTP:
1023 case URL_SCHEME_HTTPS:
1024 int_flags |= WINE_URL_BASH_AS_SLASH;
1025 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1026 int_flags |= WINE_URL_ESCAPE_SLASH;
1027 break;
1029 case URL_SCHEME_MAILTO:
1030 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1031 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1032 break;
1034 case URL_SCHEME_INVALID:
1035 break;
1037 case URL_SCHEME_FTP:
1038 default:
1039 if(parsed_url.pszSuffix[0] != '/')
1040 int_flags |= WINE_URL_ESCAPE_SLASH;
1041 break;
1045 for(src = pszUrl; *src; ) {
1046 WCHAR cur = *src;
1047 len = 0;
1049 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1050 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1051 while(cur == '/' || cur == '\\') {
1052 slashes++;
1053 cur = *++src;
1055 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1056 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1057 src += localhost_len + 1;
1058 slashes = 3;
1061 switch(slashes) {
1062 case 1:
1063 case 3:
1064 next[0] = next[1] = next[2] = '/';
1065 len = 3;
1066 break;
1067 case 0:
1068 len = 0;
1069 break;
1070 default:
1071 next[0] = next[1] = '/';
1072 len = 2;
1073 break;
1076 if(len == 0) {
1078 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1079 stop_escaping = TRUE;
1081 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1082 stop_escaping = TRUE;
1084 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1086 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1087 next[0] = '%';
1088 next[1] = hexDigits[(cur >> 4) & 0xf];
1089 next[2] = hexDigits[cur & 0xf];
1090 len = 3;
1091 } else {
1092 next[0] = cur;
1093 len = 1;
1095 src++;
1098 if(needed + len <= *pcchEscaped) {
1099 memcpy(dst, next, len*sizeof(WCHAR));
1100 dst += len;
1102 needed += len;
1105 if(needed < *pcchEscaped) {
1106 *dst = '\0';
1107 ret = S_OK;
1108 } else {
1109 needed++; /* add one for the '\0' */
1110 ret = E_POINTER;
1112 *pcchEscaped = needed;
1113 return ret;
1117 /*************************************************************************
1118 * UrlUnescapeA [SHLWAPI.@]
1120 * Converts Url escape sequences back to ordinary characters.
1122 * PARAMS
1123 * pszUrl [I/O] Url to convert
1124 * pszUnescaped [O] Destination for converted Url
1125 * pcchUnescaped [I/O] Size of output string
1126 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1128 * RETURNS
1129 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1130 * dwFlags includes URL_ESCAPE_INPLACE.
1131 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1132 * this case pcchUnescaped is set to the size required.
1133 * NOTES
1134 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1135 * the first occurrence of either a '?' or '#' character.
1137 HRESULT WINAPI UrlUnescapeA(
1138 LPSTR pszUrl,
1139 LPSTR pszUnescaped,
1140 LPDWORD pcchUnescaped,
1141 DWORD dwFlags)
1143 char *dst, next;
1144 LPCSTR src;
1145 HRESULT ret;
1146 DWORD needed;
1147 BOOL stop_unescaping = FALSE;
1149 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1150 pcchUnescaped, dwFlags);
1152 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1153 return E_INVALIDARG;
1155 if(dwFlags & URL_UNESCAPE_INPLACE)
1156 dst = pszUrl;
1157 else
1158 dst = pszUnescaped;
1160 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1161 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1162 (*src == '#' || *src == '?')) {
1163 stop_unescaping = TRUE;
1164 next = *src;
1165 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1166 && stop_unescaping == FALSE) {
1167 INT ih;
1168 char buf[3];
1169 memcpy(buf, src + 1, 2);
1170 buf[2] = '\0';
1171 ih = strtol(buf, NULL, 16);
1172 next = (CHAR) ih;
1173 src += 2; /* Advance to end of escape */
1174 } else
1175 next = *src;
1177 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1178 *dst++ = next;
1181 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1182 *dst = '\0';
1183 ret = S_OK;
1184 } else {
1185 needed++; /* add one for the '\0' */
1186 ret = E_POINTER;
1188 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1189 *pcchUnescaped = needed;
1191 if (ret == S_OK) {
1192 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1193 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1196 return ret;
1199 /*************************************************************************
1200 * UrlUnescapeW [SHLWAPI.@]
1202 * See UrlUnescapeA.
1204 HRESULT WINAPI UrlUnescapeW(
1205 LPWSTR pszUrl,
1206 LPWSTR pszUnescaped,
1207 LPDWORD pcchUnescaped,
1208 DWORD dwFlags)
1210 WCHAR *dst, next;
1211 LPCWSTR src;
1212 HRESULT ret;
1213 DWORD needed;
1214 BOOL stop_unescaping = FALSE;
1216 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1217 pcchUnescaped, dwFlags);
1219 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1220 return E_INVALIDARG;
1222 if(dwFlags & URL_UNESCAPE_INPLACE)
1223 dst = pszUrl;
1224 else
1225 dst = pszUnescaped;
1227 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1228 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1229 (*src == '#' || *src == '?')) {
1230 stop_unescaping = TRUE;
1231 next = *src;
1232 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1233 && stop_unescaping == FALSE) {
1234 INT ih;
1235 WCHAR buf[5] = {'0','x',0};
1236 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1237 buf[4] = 0;
1238 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1239 next = (WCHAR) ih;
1240 src += 2; /* Advance to end of escape */
1241 } else
1242 next = *src;
1244 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1245 *dst++ = next;
1248 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1249 *dst = '\0';
1250 ret = S_OK;
1251 } else {
1252 needed++; /* add one for the '\0' */
1253 ret = E_POINTER;
1255 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1256 *pcchUnescaped = needed;
1258 if (ret == S_OK) {
1259 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1260 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1263 return ret;
1266 /*************************************************************************
1267 * UrlGetLocationA [SHLWAPI.@]
1269 * Get the location from a Url.
1271 * PARAMS
1272 * pszUrl [I] Url to get the location from
1274 * RETURNS
1275 * A pointer to the start of the location in pszUrl, or NULL if there is
1276 * no location.
1278 * NOTES
1279 * - MSDN erroneously states that "The location is the segment of the Url
1280 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1281 * stop at '?' and always return a NULL in this case.
1282 * - MSDN also erroneously states that "If a file URL has a query string,
1283 * the returned string is the query string". In all tested cases, if the
1284 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1285 *| Result Url
1286 *| ------ ---
1287 *| NULL file://aa/b/cd#hohoh
1288 *| #hohoh http://aa/b/cd#hohoh
1289 *| NULL fi://aa/b/cd#hohoh
1290 *| #hohoh ff://aa/b/cd#hohoh
1292 LPCSTR WINAPI UrlGetLocationA(
1293 LPCSTR pszUrl)
1295 PARSEDURLA base;
1296 DWORD res1;
1298 base.cbSize = sizeof(base);
1299 res1 = ParseURLA(pszUrl, &base);
1300 if (res1) return NULL; /* invalid scheme */
1302 /* if scheme is file: then never return pointer */
1303 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1305 /* Look for '#' and return its addr */
1306 return strchr(base.pszSuffix, '#');
1309 /*************************************************************************
1310 * UrlGetLocationW [SHLWAPI.@]
1312 * See UrlGetLocationA.
1314 LPCWSTR WINAPI UrlGetLocationW(
1315 LPCWSTR pszUrl)
1317 PARSEDURLW base;
1318 DWORD res1;
1320 base.cbSize = sizeof(base);
1321 res1 = ParseURLW(pszUrl, &base);
1322 if (res1) return NULL; /* invalid scheme */
1324 /* if scheme is file: then never return pointer */
1325 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1327 /* Look for '#' and return its addr */
1328 return strchrW(base.pszSuffix, '#');
1331 /*************************************************************************
1332 * UrlCompareA [SHLWAPI.@]
1334 * Compare two Urls.
1336 * PARAMS
1337 * pszUrl1 [I] First Url to compare
1338 * pszUrl2 [I] Url to compare to pszUrl1
1339 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1341 * RETURNS
1342 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1343 * than, equal to, or less than pszUrl1 respectively.
1345 INT WINAPI UrlCompareA(
1346 LPCSTR pszUrl1,
1347 LPCSTR pszUrl2,
1348 BOOL fIgnoreSlash)
1350 INT ret, len, len1, len2;
1352 if (!fIgnoreSlash)
1353 return strcmp(pszUrl1, pszUrl2);
1354 len1 = strlen(pszUrl1);
1355 if (pszUrl1[len1-1] == '/') len1--;
1356 len2 = strlen(pszUrl2);
1357 if (pszUrl2[len2-1] == '/') len2--;
1358 if (len1 == len2)
1359 return strncmp(pszUrl1, pszUrl2, len1);
1360 len = min(len1, len2);
1361 ret = strncmp(pszUrl1, pszUrl2, len);
1362 if (ret) return ret;
1363 if (len1 > len2) return 1;
1364 return -1;
1367 /*************************************************************************
1368 * UrlCompareW [SHLWAPI.@]
1370 * See UrlCompareA.
1372 INT WINAPI UrlCompareW(
1373 LPCWSTR pszUrl1,
1374 LPCWSTR pszUrl2,
1375 BOOL fIgnoreSlash)
1377 INT ret;
1378 size_t len, len1, len2;
1380 if (!fIgnoreSlash)
1381 return strcmpW(pszUrl1, pszUrl2);
1382 len1 = strlenW(pszUrl1);
1383 if (pszUrl1[len1-1] == '/') len1--;
1384 len2 = strlenW(pszUrl2);
1385 if (pszUrl2[len2-1] == '/') len2--;
1386 if (len1 == len2)
1387 return strncmpW(pszUrl1, pszUrl2, len1);
1388 len = min(len1, len2);
1389 ret = strncmpW(pszUrl1, pszUrl2, len);
1390 if (ret) return ret;
1391 if (len1 > len2) return 1;
1392 return -1;
1395 /*************************************************************************
1396 * HashData [SHLWAPI.@]
1398 * Hash an input block into a variable sized digest.
1400 * PARAMS
1401 * lpSrc [I] Input block
1402 * nSrcLen [I] Length of lpSrc
1403 * lpDest [I] Output for hash digest
1404 * nDestLen [I] Length of lpDest
1406 * RETURNS
1407 * Success: TRUE. lpDest is filled with the computed hash value.
1408 * Failure: FALSE, if any argument is invalid.
1410 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1411 unsigned char *lpDest, DWORD nDestLen)
1413 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1415 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1416 IsBadWritePtr(lpDest, nDestLen))
1417 return E_INVALIDARG;
1419 while (destCount >= 0)
1421 lpDest[destCount] = (destCount & 0xff);
1422 destCount--;
1425 while (srcCount >= 0)
1427 destCount = nDestLen - 1;
1428 while (destCount >= 0)
1430 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1431 destCount--;
1433 srcCount--;
1435 return S_OK;
1438 /*************************************************************************
1439 * UrlHashA [SHLWAPI.@]
1441 * Produce a Hash from a Url.
1443 * PARAMS
1444 * pszUrl [I] Url to hash
1445 * lpDest [O] Destinationh for hash
1446 * nDestLen [I] Length of lpDest
1448 * RETURNS
1449 * Success: S_OK. lpDest is filled with the computed hash value.
1450 * Failure: E_INVALIDARG, if any argument is invalid.
1452 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1454 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1455 return E_INVALIDARG;
1457 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1458 return S_OK;
1461 /*************************************************************************
1462 * UrlHashW [SHLWAPI.@]
1464 * See UrlHashA.
1466 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1468 char szUrl[MAX_PATH];
1470 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1472 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1473 return E_INVALIDARG;
1475 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1476 * return the same digests for the same URL.
1478 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1479 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1480 return S_OK;
1483 /*************************************************************************
1484 * UrlApplySchemeA [SHLWAPI.@]
1486 * Apply a scheme to a Url.
1488 * PARAMS
1489 * pszIn [I] Url to apply scheme to
1490 * pszOut [O] Destination for modified Url
1491 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1492 * dwFlags [I] URL_ flags from "shlwapi.h"
1494 * RETURNS
1495 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1496 * Failure: An HRESULT error code describing the error.
1498 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1500 LPWSTR in, out;
1501 DWORD ret, len, len2;
1503 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1504 debugstr_a(pszIn), *pcchOut, dwFlags);
1506 in = HeapAlloc(GetProcessHeap(), 0,
1507 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1508 out = in + INTERNET_MAX_URL_LENGTH;
1510 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1511 len = INTERNET_MAX_URL_LENGTH;
1513 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1514 if ((ret != S_OK) && (ret != S_FALSE)) {
1515 HeapFree(GetProcessHeap(), 0, in);
1516 return ret;
1519 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1520 if (len2 > *pcchOut) {
1521 *pcchOut = len2;
1522 HeapFree(GetProcessHeap(), 0, in);
1523 return E_POINTER;
1525 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1526 *pcchOut = len2;
1527 HeapFree(GetProcessHeap(), 0, in);
1528 return ret;
1531 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1533 HKEY newkey;
1534 BOOL j;
1535 INT index;
1536 DWORD value_len, data_len, dwType, i;
1537 WCHAR reg_path[MAX_PATH];
1538 WCHAR value[MAX_PATH], data[MAX_PATH];
1539 WCHAR Wxx, Wyy;
1541 MultiByteToWideChar(0, 0,
1542 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1543 -1, reg_path, MAX_PATH);
1544 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1545 index = 0;
1546 while(value_len = data_len = MAX_PATH,
1547 RegEnumValueW(newkey, index, value, &value_len,
1548 0, &dwType, (LPVOID)data, &data_len) == 0) {
1549 TRACE("guess %d %s is %s\n",
1550 index, debugstr_w(value), debugstr_w(data));
1552 j = FALSE;
1553 for(i=0; i<value_len; i++) {
1554 Wxx = pszIn[i];
1555 Wyy = value[i];
1556 /* remember that TRUE is not-equal */
1557 j = ChrCmpIW(Wxx, Wyy);
1558 if (j) break;
1560 if ((i == value_len) && !j) {
1561 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1562 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1563 RegCloseKey(newkey);
1564 return E_POINTER;
1566 strcpyW(pszOut, data);
1567 strcatW(pszOut, pszIn);
1568 *pcchOut = strlenW(pszOut);
1569 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1570 RegCloseKey(newkey);
1571 return S_OK;
1573 index++;
1575 RegCloseKey(newkey);
1576 return -1;
1579 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1581 HKEY newkey;
1582 DWORD data_len, dwType;
1583 WCHAR value[MAX_PATH], data[MAX_PATH];
1585 static const WCHAR prefix_keyW[] =
1586 {'S','o','f','t','w','a','r','e',
1587 '\\','M','i','c','r','o','s','o','f','t',
1588 '\\','W','i','n','d','o','w','s',
1589 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1590 '\\','U','R','L',
1591 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1593 /* get and prepend default */
1594 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1595 data_len = MAX_PATH;
1596 value[0] = '@';
1597 value[1] = '\0';
1598 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1599 RegCloseKey(newkey);
1600 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1601 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1602 return E_POINTER;
1604 strcpyW(pszOut, data);
1605 strcatW(pszOut, pszIn);
1606 *pcchOut = strlenW(pszOut);
1607 TRACE("used default %s\n", debugstr_w(pszOut));
1608 return S_OK;
1611 /*************************************************************************
1612 * UrlApplySchemeW [SHLWAPI.@]
1614 * See UrlApplySchemeA.
1616 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1618 PARSEDURLW in_scheme;
1619 DWORD res1;
1620 HRESULT ret;
1622 TRACE("(in %s, out size %d, flags %08x)\n",
1623 debugstr_w(pszIn), *pcchOut, dwFlags);
1625 if (dwFlags & URL_APPLY_GUESSFILE) {
1626 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1627 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1628 strcpyW(pszOut, pszIn);
1629 *pcchOut = strlenW(pszOut);
1630 return S_FALSE;
1633 in_scheme.cbSize = sizeof(in_scheme);
1634 /* See if the base has a scheme */
1635 res1 = ParseURLW(pszIn, &in_scheme);
1636 if (res1) {
1637 /* no scheme in input, need to see if we need to guess */
1638 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1639 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1640 return ret;
1643 else {
1644 /* we have a scheme, see if valid (known scheme) */
1645 if (in_scheme.nScheme) {
1646 /* have valid scheme, so just copy and exit */
1647 if (strlenW(pszIn) + 1 > *pcchOut) {
1648 *pcchOut = strlenW(pszIn) + 1;
1649 return E_POINTER;
1651 strcpyW(pszOut, pszIn);
1652 *pcchOut = strlenW(pszOut);
1653 TRACE("valid scheme, returning copy\n");
1654 return S_OK;
1658 /* If we are here, then either invalid scheme,
1659 * or no scheme and can't/failed guess.
1661 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1662 ((res1 != 0)) ) &&
1663 (dwFlags & URL_APPLY_DEFAULT)) {
1664 /* find and apply default scheme */
1665 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1668 /* just copy and give proper return code */
1669 if (strlenW(pszIn) + 1 > *pcchOut) {
1670 *pcchOut = strlenW(pszIn) + 1;
1671 return E_POINTER;
1673 strcpyW(pszOut, pszIn);
1674 *pcchOut = strlenW(pszOut);
1675 TRACE("returning copy, left alone\n");
1676 return S_FALSE;
1679 /*************************************************************************
1680 * UrlIsA [SHLWAPI.@]
1682 * Determine if a Url is of a certain class.
1684 * PARAMS
1685 * pszUrl [I] Url to check
1686 * Urlis [I] URLIS_ constant from "shlwapi.h"
1688 * RETURNS
1689 * TRUE if pszUrl belongs to the class type in Urlis.
1690 * FALSE Otherwise.
1692 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1694 PARSEDURLA base;
1695 DWORD res1;
1696 LPCSTR last;
1698 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1700 switch (Urlis) {
1702 case URLIS_OPAQUE:
1703 base.cbSize = sizeof(base);
1704 res1 = ParseURLA(pszUrl, &base);
1705 if (res1) return FALSE; /* invalid scheme */
1706 switch (base.nScheme)
1708 case URL_SCHEME_MAILTO:
1709 case URL_SCHEME_SHELL:
1710 case URL_SCHEME_JAVASCRIPT:
1711 case URL_SCHEME_VBSCRIPT:
1712 case URL_SCHEME_ABOUT:
1713 return TRUE;
1715 return FALSE;
1717 case URLIS_FILEURL:
1718 return !StrCmpNA("file:", pszUrl, 5);
1720 case URLIS_DIRECTORY:
1721 last = pszUrl + strlen(pszUrl) - 1;
1722 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1724 case URLIS_URL:
1725 return PathIsURLA(pszUrl);
1727 case URLIS_NOHISTORY:
1728 case URLIS_APPLIABLE:
1729 case URLIS_HASQUERY:
1730 default:
1731 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1733 return FALSE;
1736 /*************************************************************************
1737 * UrlIsW [SHLWAPI.@]
1739 * See UrlIsA.
1741 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1743 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1744 PARSEDURLW base;
1745 DWORD res1;
1746 LPCWSTR last;
1748 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1750 switch (Urlis) {
1752 case URLIS_OPAQUE:
1753 base.cbSize = sizeof(base);
1754 res1 = ParseURLW(pszUrl, &base);
1755 if (res1) return FALSE; /* invalid scheme */
1756 switch (base.nScheme)
1758 case URL_SCHEME_MAILTO:
1759 case URL_SCHEME_SHELL:
1760 case URL_SCHEME_JAVASCRIPT:
1761 case URL_SCHEME_VBSCRIPT:
1762 case URL_SCHEME_ABOUT:
1763 return TRUE;
1765 return FALSE;
1767 case URLIS_FILEURL:
1768 return !strncmpW(stemp, pszUrl, 5);
1770 case URLIS_DIRECTORY:
1771 last = pszUrl + strlenW(pszUrl) - 1;
1772 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1774 case URLIS_URL:
1775 return PathIsURLW(pszUrl);
1777 case URLIS_NOHISTORY:
1778 case URLIS_APPLIABLE:
1779 case URLIS_HASQUERY:
1780 default:
1781 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1783 return FALSE;
1786 /*************************************************************************
1787 * UrlIsNoHistoryA [SHLWAPI.@]
1789 * Determine if a Url should not be stored in the users history list.
1791 * PARAMS
1792 * pszUrl [I] Url to check
1794 * RETURNS
1795 * TRUE, if pszUrl should be excluded from the history list,
1796 * FALSE otherwise.
1798 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1800 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1803 /*************************************************************************
1804 * UrlIsNoHistoryW [SHLWAPI.@]
1806 * See UrlIsNoHistoryA.
1808 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1810 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1813 /*************************************************************************
1814 * UrlIsOpaqueA [SHLWAPI.@]
1816 * Determine if a Url is opaque.
1818 * PARAMS
1819 * pszUrl [I] Url to check
1821 * RETURNS
1822 * TRUE if pszUrl is opaque,
1823 * FALSE Otherwise.
1825 * NOTES
1826 * An opaque Url is one that does not start with "<protocol>://".
1828 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1830 return UrlIsA(pszUrl, URLIS_OPAQUE);
1833 /*************************************************************************
1834 * UrlIsOpaqueW [SHLWAPI.@]
1836 * See UrlIsOpaqueA.
1838 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1840 return UrlIsW(pszUrl, URLIS_OPAQUE);
1843 /*************************************************************************
1844 * Scans for characters of type "type" and when not matching found,
1845 * returns pointer to it and length in size.
1847 * Characters tested based on RFC 1738
1849 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1851 static DWORD alwayszero = 0;
1852 BOOL cont = TRUE;
1854 *size = 0;
1856 switch(type){
1858 case SCHEME:
1859 while (cont) {
1860 if ( (islowerW(*start) && isalphaW(*start)) ||
1861 isdigitW(*start) ||
1862 (*start == '+') ||
1863 (*start == '-') ||
1864 (*start == '.')) {
1865 start++;
1866 (*size)++;
1868 else
1869 cont = FALSE;
1871 break;
1873 case USERPASS:
1874 while (cont) {
1875 if ( isalphaW(*start) ||
1876 isdigitW(*start) ||
1877 /* user/password only characters */
1878 (*start == ';') ||
1879 (*start == '?') ||
1880 (*start == '&') ||
1881 (*start == '=') ||
1882 /* *extra* characters */
1883 (*start == '!') ||
1884 (*start == '*') ||
1885 (*start == '\'') ||
1886 (*start == '(') ||
1887 (*start == ')') ||
1888 (*start == ',') ||
1889 /* *safe* characters */
1890 (*start == '$') ||
1891 (*start == '_') ||
1892 (*start == '+') ||
1893 (*start == '-') ||
1894 (*start == '.')) {
1895 start++;
1896 (*size)++;
1897 } else if (*start == '%') {
1898 if (isxdigitW(*(start+1)) &&
1899 isxdigitW(*(start+2))) {
1900 start += 3;
1901 *size += 3;
1902 } else
1903 cont = FALSE;
1904 } else
1905 cont = FALSE;
1907 break;
1909 case PORT:
1910 while (cont) {
1911 if (isdigitW(*start)) {
1912 start++;
1913 (*size)++;
1915 else
1916 cont = FALSE;
1918 break;
1920 case HOST:
1921 while (cont) {
1922 if (isalnumW(*start) ||
1923 (*start == '-') ||
1924 (*start == '.') ) {
1925 start++;
1926 (*size)++;
1928 else
1929 cont = FALSE;
1931 break;
1932 default:
1933 FIXME("unknown type %d\n", type);
1934 return (LPWSTR)&alwayszero;
1936 /* TRACE("scanned %d characters next char %p<%c>\n",
1937 *size, start, *start); */
1938 return start;
1941 /*************************************************************************
1942 * Attempt to parse URL into pieces.
1944 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1946 LPCWSTR work;
1948 memset(pl, 0, sizeof(WINE_PARSE_URL));
1949 pl->pScheme = pszUrl;
1950 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1951 if (!*work || (*work != ':')) goto ErrorExit;
1952 work++;
1953 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1954 pl->pUserName = work + 2;
1955 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1956 if (*work == ':' ) {
1957 /* parse password */
1958 work++;
1959 pl->pPassword = work;
1960 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1961 if (*work != '@') {
1962 /* what we just parsed must be the hostname and port
1963 * so reset pointers and clear then let it parse */
1964 pl->szUserName = pl->szPassword = 0;
1965 work = pl->pUserName - 1;
1966 pl->pUserName = pl->pPassword = 0;
1968 } else if (*work == '@') {
1969 /* no password */
1970 pl->szPassword = 0;
1971 pl->pPassword = 0;
1972 } else if (!*work || (*work == '/') || (*work == '.')) {
1973 /* what was parsed was hostname, so reset pointers and let it parse */
1974 pl->szUserName = pl->szPassword = 0;
1975 work = pl->pUserName - 1;
1976 pl->pUserName = pl->pPassword = 0;
1977 } else goto ErrorExit;
1979 /* now start parsing hostname or hostnumber */
1980 work++;
1981 pl->pHostName = work;
1982 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1983 if (*work == ':') {
1984 /* parse port */
1985 work++;
1986 pl->pPort = work;
1987 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1989 if (*work == '/') {
1990 /* see if query string */
1991 pl->pQuery = strchrW(work, '?');
1992 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1994 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1995 pl->pScheme, pl->szScheme,
1996 pl->pUserName, pl->szUserName,
1997 pl->pPassword, pl->szPassword,
1998 pl->pHostName, pl->szHostName,
1999 pl->pPort, pl->szPort,
2000 pl->pQuery, pl->szQuery);
2001 return S_OK;
2002 ErrorExit:
2003 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2004 return E_INVALIDARG;
2007 /*************************************************************************
2008 * UrlGetPartA [SHLWAPI.@]
2010 * Retrieve part of a Url.
2012 * PARAMS
2013 * pszIn [I] Url to parse
2014 * pszOut [O] Destination for part of pszIn requested
2015 * pcchOut [I] Size of pszOut
2016 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2017 * needed size of pszOut INCLUDING '\0'.
2018 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2019 * dwFlags [I] URL_ flags from "shlwapi.h"
2021 * RETURNS
2022 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2023 * Failure: An HRESULT error code describing the error.
2025 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2026 DWORD dwPart, DWORD dwFlags)
2028 LPWSTR in, out;
2029 DWORD ret, len, len2;
2031 in = HeapAlloc(GetProcessHeap(), 0,
2032 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2033 out = in + INTERNET_MAX_URL_LENGTH;
2035 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2037 len = INTERNET_MAX_URL_LENGTH;
2038 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2040 if (ret != S_OK) {
2041 HeapFree(GetProcessHeap(), 0, in);
2042 return ret;
2045 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2046 if (len2 > *pcchOut) {
2047 *pcchOut = len2;
2048 HeapFree(GetProcessHeap(), 0, in);
2049 return E_POINTER;
2051 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2052 *pcchOut = len2;
2053 HeapFree(GetProcessHeap(), 0, in);
2054 return S_OK;
2057 /*************************************************************************
2058 * UrlGetPartW [SHLWAPI.@]
2060 * See UrlGetPartA.
2062 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2063 DWORD dwPart, DWORD dwFlags)
2065 WINE_PARSE_URL pl;
2066 HRESULT ret;
2067 DWORD size, schsize;
2068 LPCWSTR addr, schaddr;
2070 TRACE("(%s %p %p(%d) %08x %08x)\n",
2071 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2073 ret = URL_ParseUrl(pszIn, &pl);
2074 if (!ret) {
2075 schaddr = pl.pScheme;
2076 schsize = pl.szScheme;
2078 switch (dwPart) {
2079 case URL_PART_SCHEME:
2080 if (!pl.szScheme) return E_INVALIDARG;
2081 addr = pl.pScheme;
2082 size = pl.szScheme;
2083 break;
2085 case URL_PART_HOSTNAME:
2086 if (!pl.szHostName) return E_INVALIDARG;
2087 addr = pl.pHostName;
2088 size = pl.szHostName;
2089 break;
2091 case URL_PART_USERNAME:
2092 if (!pl.szUserName) return E_INVALIDARG;
2093 addr = pl.pUserName;
2094 size = pl.szUserName;
2095 break;
2097 case URL_PART_PASSWORD:
2098 if (!pl.szPassword) return E_INVALIDARG;
2099 addr = pl.pPassword;
2100 size = pl.szPassword;
2101 break;
2103 case URL_PART_PORT:
2104 if (!pl.szPort) return E_INVALIDARG;
2105 addr = pl.pPort;
2106 size = pl.szPort;
2107 break;
2109 case URL_PART_QUERY:
2110 if (!pl.szQuery) return E_INVALIDARG;
2111 addr = pl.pQuery;
2112 size = pl.szQuery;
2113 break;
2115 default:
2116 return E_INVALIDARG;
2119 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2120 if (*pcchOut < schsize + size + 2) {
2121 *pcchOut = schsize + size + 2;
2122 return E_POINTER;
2124 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2125 pszOut[schsize] = ':';
2126 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2127 pszOut[schsize+1+size] = 0;
2128 *pcchOut = schsize + 1 + size;
2130 else {
2131 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2132 memcpy(pszOut, addr, size*sizeof(WCHAR));
2133 pszOut[size] = 0;
2134 *pcchOut = size;
2136 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2138 return ret;
2141 /*************************************************************************
2142 * PathIsURLA [SHLWAPI.@]
2144 * Check if the given path is a Url.
2146 * PARAMS
2147 * lpszPath [I] Path to check.
2149 * RETURNS
2150 * TRUE if lpszPath is a Url.
2151 * FALSE if lpszPath is NULL or not a Url.
2153 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2155 PARSEDURLA base;
2156 DWORD res1;
2158 TRACE("%s\n", debugstr_a(lpstrPath));
2160 if (!lpstrPath || !*lpstrPath) return FALSE;
2162 /* get protocol */
2163 base.cbSize = sizeof(base);
2164 res1 = ParseURLA(lpstrPath, &base);
2165 return (base.nScheme != URL_SCHEME_INVALID);
2168 /*************************************************************************
2169 * PathIsURLW [SHLWAPI.@]
2171 * See PathIsURLA.
2173 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2175 PARSEDURLW base;
2176 DWORD res1;
2178 TRACE("%s\n", debugstr_w(lpstrPath));
2180 if (!lpstrPath || !*lpstrPath) return FALSE;
2182 /* get protocol */
2183 base.cbSize = sizeof(base);
2184 res1 = ParseURLW(lpstrPath, &base);
2185 return (base.nScheme != URL_SCHEME_INVALID);
2188 /*************************************************************************
2189 * UrlCreateFromPathA [SHLWAPI.@]
2191 * See UrlCreateFromPathW
2193 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2195 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2196 WCHAR *urlW = bufW;
2197 UNICODE_STRING pathW;
2198 HRESULT ret;
2199 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2201 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2202 return E_INVALIDARG;
2203 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2204 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2205 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2207 if(ret == S_OK || ret == S_FALSE) {
2208 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2209 if(*pcchUrl > lenA) {
2210 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2211 pszUrl[lenA] = 0;
2212 *pcchUrl = lenA;
2213 } else {
2214 *pcchUrl = lenA + 1;
2215 ret = E_POINTER;
2218 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2219 RtlFreeUnicodeString(&pathW);
2220 return ret;
2223 /*************************************************************************
2224 * UrlCreateFromPathW [SHLWAPI.@]
2226 * Create a Url from a file path.
2228 * PARAMS
2229 * pszPath [I] Path to convert
2230 * pszUrl [O] Destination for the converted Url
2231 * pcchUrl [I/O] Length of pszUrl
2232 * dwReserved [I] Reserved, must be 0
2234 * RETURNS
2235 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2236 * Failure: An HRESULT error code.
2238 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2240 DWORD needed;
2241 HRESULT ret;
2242 WCHAR *pszNewUrl;
2243 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2244 WCHAR three_slashesW[] = {'/','/','/',0};
2245 PARSEDURLW parsed_url;
2247 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2249 /* Validate arguments */
2250 if (dwReserved != 0)
2251 return E_INVALIDARG;
2252 if (!pszUrl || !pcchUrl)
2253 return E_INVALIDARG;
2256 parsed_url.cbSize = sizeof(parsed_url);
2257 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2258 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2259 needed = strlenW(pszPath);
2260 if (needed >= *pcchUrl) {
2261 *pcchUrl = needed + 1;
2262 return E_POINTER;
2263 } else {
2264 *pcchUrl = needed;
2265 strcpyW(pszUrl, pszPath);
2266 return S_FALSE;
2271 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2272 strcpyW(pszNewUrl, file_colonW);
2273 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2274 strcatW(pszNewUrl, three_slashesW);
2275 strcatW(pszNewUrl, pszPath);
2276 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2278 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2279 return ret;
2282 /*************************************************************************
2283 * SHAutoComplete [SHLWAPI.@]
2285 * Enable auto-completion for an edit control.
2287 * PARAMS
2288 * hwndEdit [I] Handle of control to enable auto-completion for
2289 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2291 * RETURNS
2292 * Success: S_OK. Auto-completion is enabled for the control.
2293 * Failure: An HRESULT error code indicating the error.
2295 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2297 FIXME("SHAutoComplete stub\n");
2298 return S_FALSE;
2301 /*************************************************************************
2302 * MLBuildResURLA [SHLWAPI.405]
2304 * Create a Url pointing to a resource in a module.
2306 * PARAMS
2307 * lpszLibName [I] Name of the module containing the resource
2308 * hMod [I] Callers module handle
2309 * dwFlags [I] Undocumented flags for loading the module
2310 * lpszRes [I] Resource name
2311 * lpszDest [O] Destination for resulting Url
2312 * dwDestLen [I] Length of lpszDest
2314 * RETURNS
2315 * Success: S_OK. lpszDest constains the resource Url.
2316 * Failure: E_INVALIDARG, if any argument is invalid, or
2317 * E_FAIL if dwDestLen is too small.
2319 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2320 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2322 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2323 HRESULT hRet;
2325 if (lpszLibName)
2326 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2328 if (lpszRes)
2329 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2331 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2332 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2334 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2335 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2336 if (SUCCEEDED(hRet) && lpszDest)
2337 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2339 return hRet;
2342 /*************************************************************************
2343 * MLBuildResURLA [SHLWAPI.406]
2345 * See MLBuildResURLA.
2347 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2348 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2350 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2351 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2352 HRESULT hRet = E_FAIL;
2354 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2355 debugstr_w(lpszRes), lpszDest, dwDestLen);
2357 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2358 !lpszDest || (dwFlags && dwFlags != 2))
2359 return E_INVALIDARG;
2361 if (dwDestLen >= szResLen + 1)
2363 dwDestLen -= (szResLen + 1);
2364 memcpy(lpszDest, szRes, sizeof(szRes));
2366 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2368 if (hMod)
2370 WCHAR szBuff[MAX_PATH];
2371 DWORD len;
2373 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2374 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2376 DWORD dwPathLen = strlenW(szBuff) + 1;
2378 if (dwDestLen >= dwPathLen)
2380 DWORD dwResLen;
2382 dwDestLen -= dwPathLen;
2383 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2385 dwResLen = strlenW(lpszRes) + 1;
2386 if (dwDestLen >= dwResLen + 1)
2388 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2389 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2390 hRet = S_OK;
2394 MLFreeLibrary(hMod);
2397 return hRet;