programs/wcmd: Rename to programs/cmd.
[wine.git] / dlls / shlwapi / url.c
blob3c4f28bb8d19afd631907be8e52eb622e2baad1d
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 typedef struct {
48 URL_SCHEME scheme_number;
49 LPCSTR scheme_name;
50 } SHL_2_inet_scheme;
52 static const SHL_2_inet_scheme shlwapi_schemes[] = {
53 {URL_SCHEME_FTP, "ftp"},
54 {URL_SCHEME_HTTP, "http"},
55 {URL_SCHEME_GOPHER, "gopher"},
56 {URL_SCHEME_MAILTO, "mailto"},
57 {URL_SCHEME_NEWS, "news"},
58 {URL_SCHEME_NNTP, "nntp"},
59 {URL_SCHEME_TELNET, "telnet"},
60 {URL_SCHEME_WAIS, "wais"},
61 {URL_SCHEME_FILE, "file"},
62 {URL_SCHEME_MK, "mk"},
63 {URL_SCHEME_HTTPS, "https"},
64 {URL_SCHEME_SHELL, "shell"},
65 {URL_SCHEME_SNEWS, "snews"},
66 {URL_SCHEME_LOCAL, "local"},
67 {URL_SCHEME_JAVASCRIPT, "javascript"},
68 {URL_SCHEME_VBSCRIPT, "vbscript"},
69 {URL_SCHEME_ABOUT, "about"},
70 {URL_SCHEME_RES, "res"},
71 {0, 0}
74 typedef struct {
75 LPCWSTR pScheme; /* [out] start of scheme */
76 DWORD szScheme; /* [out] size of scheme (until colon) */
77 LPCWSTR pUserName; /* [out] start of Username */
78 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
79 LPCWSTR pPassword; /* [out] start of Password */
80 DWORD szPassword; /* [out] size of Password (until "@") */
81 LPCWSTR pHostName; /* [out] start of Hostname */
82 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
83 LPCWSTR pPort; /* [out] start of Port */
84 DWORD szPort; /* [out] size of Port (until "/" or eos) */
85 LPCWSTR pQuery; /* [out] start of Query */
86 DWORD szQuery; /* [out] size of Query (until eos) */
87 } WINE_PARSE_URL;
89 typedef enum {
90 SCHEME,
91 HOST,
92 PORT,
93 USERPASS,
94 } WINE_URL_SCAN_TYPE;
96 static const CHAR hexDigits[] = "0123456789ABCDEF";
98 static const WCHAR fileW[] = {'f','i','l','e','\0'};
100 static const unsigned char HashDataLookup[256] = {
101 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
102 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
103 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
104 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
105 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
106 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
107 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
108 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
109 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
110 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
111 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
112 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
113 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
114 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
115 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
116 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
117 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
118 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
119 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
120 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
122 static BOOL URL_JustLocation(LPCWSTR str)
124 while(*str && (*str == L'/')) str++;
125 if (*str) {
126 while (*str && ((*str == L'-') ||
127 (*str == L'.') ||
128 isalnumW(*str))) str++;
129 if (*str == L'/') return FALSE;
131 return TRUE;
135 /*************************************************************************
136 * @ [SHLWAPI.1]
138 * Parse a Url into its constituent parts.
140 * PARAMS
141 * x [I] Url to parse
142 * y [O] Undocumented structure holding the parsed information
144 * RETURNS
145 * Success: S_OK. y contains the parsed Url details.
146 * Failure: An HRESULT error code.
148 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
150 DWORD cnt;
151 const SHL_2_inet_scheme *inet_pro;
153 y->nScheme = URL_SCHEME_INVALID;
154 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
155 /* FIXME: leading white space generates error of 0x80041001 which
156 * is undefined
158 if (*x <= ' ') return 0x80041001;
159 cnt = 0;
160 y->cchProtocol = 0;
161 y->pszProtocol = x;
162 while (*x) {
163 if (*x == ':') {
164 y->cchProtocol = cnt;
165 cnt = -1;
166 y->pszSuffix = x+1;
167 break;
169 x++;
170 cnt++;
173 /* check for no scheme in string start */
174 /* (apparently schemes *must* be larger than a single character) */
175 if ((*x == '\0') || (y->cchProtocol <= 1)) {
176 y->pszProtocol = NULL;
177 return 0x80041001;
180 /* found scheme, set length of remainder */
181 y->cchSuffix = lstrlenA(y->pszSuffix);
183 /* see if known scheme and return indicator number */
184 y->nScheme = URL_SCHEME_UNKNOWN;
185 inet_pro = shlwapi_schemes;
186 while (inet_pro->scheme_name) {
187 if (!strncasecmp(inet_pro->scheme_name, y->pszProtocol,
188 min(y->cchProtocol, lstrlenA(inet_pro->scheme_name)))) {
189 y->nScheme = inet_pro->scheme_number;
190 break;
192 inet_pro++;
194 return S_OK;
197 /*************************************************************************
198 * @ [SHLWAPI.2]
200 * Unicode version of ParseURLA.
202 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
204 DWORD cnt;
205 const SHL_2_inet_scheme *inet_pro;
206 LPSTR cmpstr;
207 INT len;
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 <= L' ') return 0x80041001;
215 cnt = 0;
216 y->cchProtocol = 0;
217 y->pszProtocol = x;
218 while (*x) {
219 if (*x == L':') {
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 == L'\0') || (y->cchProtocol <= 1)) {
232 y->pszProtocol = NULL;
233 return 0x80041001;
236 /* found scheme, set length of remainder */
237 y->cchSuffix = lstrlenW(y->pszSuffix);
239 /* see if known scheme and return indicator number */
240 len = WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, 0, 0, 0, 0);
241 cmpstr = HeapAlloc(GetProcessHeap(), 0, len);
242 WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, cmpstr, len, 0, 0);
243 y->nScheme = URL_SCHEME_UNKNOWN;
244 inet_pro = shlwapi_schemes;
245 while (inet_pro->scheme_name) {
246 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
247 min(len, lstrlenA(inet_pro->scheme_name)))) {
248 y->nScheme = inet_pro->scheme_number;
249 break;
251 inet_pro++;
253 HeapFree(GetProcessHeap(), 0, cmpstr);
254 return S_OK;
257 /*************************************************************************
258 * UrlCanonicalizeA [SHLWAPI.@]
260 * Canonicalize a Url.
262 * PARAMS
263 * pszUrl [I] Url to cCanonicalize
264 * pszCanonicalized [O] Destination for converted Url.
265 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
266 * dwFlags [I] Flags controlling the conversion.
268 * RETURNS
269 * Success: S_OK. The pszCanonicalized contains the converted Url.
270 * Failure: E_POINTER, if *pcchCanonicalized is too small.
272 * MSDN incorrectly describes the flags for this function. They should be:
273 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
274 *| URL_ESCAPE_SPACES_ONLY 0x04000000
275 *| URL_ESCAPE_PERCENT 0x00001000
276 *| URL_ESCAPE_UNSAFE 0x10000000
277 *| URL_UNESCAPE 0x10000000
278 *| URL_DONT_SIMPLIFY 0x08000000
279 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
281 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
282 LPDWORD pcchCanonicalized, DWORD dwFlags)
284 LPWSTR base, canonical;
285 DWORD ret, len, len2;
287 TRACE("(%s %p %p 0x%08lx) using W version\n",
288 debugstr_a(pszUrl), pszCanonicalized,
289 pcchCanonicalized, dwFlags);
291 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
292 return E_INVALIDARG;
294 base = HeapAlloc(GetProcessHeap(), 0,
295 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
296 canonical = base + INTERNET_MAX_URL_LENGTH;
298 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
299 len = INTERNET_MAX_URL_LENGTH;
301 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
302 if (ret != S_OK) {
303 HeapFree(GetProcessHeap(), 0, base);
304 return ret;
307 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
308 if (len2 > *pcchCanonicalized) {
309 *pcchCanonicalized = len;
310 HeapFree(GetProcessHeap(), 0, base);
311 return E_POINTER;
313 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
314 *pcchCanonicalized, 0, 0);
315 *pcchCanonicalized = len2;
316 HeapFree(GetProcessHeap(), 0, base);
317 return S_OK;
320 /*************************************************************************
321 * UrlCanonicalizeW [SHLWAPI.@]
323 * See UrlCanonicalizeA.
325 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
326 LPDWORD pcchCanonicalized, DWORD dwFlags)
328 HRESULT hr = S_OK;
329 DWORD EscapeFlags;
330 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
331 INT nByteLen, state;
332 DWORD nLen, nWkLen;
333 WCHAR slash = '/';
335 static const WCHAR wszFile[] = {'f','i','l','e',':'};
337 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
338 pcchCanonicalized, dwFlags);
340 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
341 return E_INVALIDARG;
343 if(!*pszUrl) {
344 *pszCanonicalized = 0;
345 return S_OK;
348 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
349 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, INTERNET_MAX_URL_LENGTH);
351 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
352 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
353 slash = '\\';
356 * state =
357 * 0 initial 1,3
358 * 1 have 2[+] alnum 2,3
359 * 2 have scheme (found :) 4,6,3
360 * 3 failed (no location)
361 * 4 have // 5,3
362 * 5 have 1[+] alnum 6,3
363 * 6 have location (found /) save root location
366 wk1 = (LPWSTR)pszUrl;
367 wk2 = lpszUrlCpy;
368 state = 0;
370 if(pszUrl[1] == ':') { /* Assume path */
371 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
373 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
374 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
375 state = 5;
378 while (*wk1) {
379 switch (state) {
380 case 0:
381 if (!isalnumW(*wk1)) {state = 3; break;}
382 *wk2++ = *wk1++;
383 if (!isalnumW(*wk1)) {state = 3; break;}
384 *wk2++ = *wk1++;
385 state = 1;
386 break;
387 case 1:
388 *wk2++ = *wk1;
389 if (*wk1++ == L':') state = 2;
390 break;
391 case 2:
392 if (*wk1 != L'/') {state = 3; break;}
393 *wk2++ = *wk1++;
394 if (*wk1 != L'/') {state = 6; break;}
395 *wk2++ = *wk1++;
396 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
397 wk1++;
398 state = 4;
399 break;
400 case 3:
401 nWkLen = strlenW(wk1);
402 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
403 mp = wk2;
404 wk1 += nWkLen;
405 wk2 += nWkLen;
407 while(mp < wk2) {
408 if(*mp == '/' || *mp == '\\')
409 *mp = slash;
410 mp++;
412 break;
413 case 4:
414 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.') && (*wk1 != ':'))
415 {state = 3; break;}
416 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.') || (*wk1 == ':'))
417 *wk2++ = *wk1++;
418 state = 5;
419 break;
420 case 5:
421 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
422 while(*wk1 == '/' || *wk1 == '\\') {
423 *wk2++ = slash;
424 wk1++;
426 state = 6;
427 break;
428 case 6:
429 if(dwFlags & URL_DONT_SIMPLIFY) {
430 state = 3;
431 break;
434 /* Now at root location, cannot back up any more. */
435 /* "root" will point at the '/' */
437 root = wk2-1;
438 while (*wk1) {
439 mp = strchrW(wk1, '/');
440 mp2 = strchrW(wk1, '\\');
441 if(mp2 && (!mp || mp2 < mp))
442 mp = mp2;
443 if (!mp) {
444 nWkLen = strlenW(wk1);
445 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
446 wk1 += nWkLen;
447 wk2 += nWkLen;
448 continue;
450 nLen = mp - wk1;
451 if(nLen) {
452 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
453 wk2 += nLen;
454 wk1 += nLen;
456 *wk2++ = slash;
457 wk1++;
459 if (*wk1 == L'.') {
460 TRACE("found '/.'\n");
461 if (wk1[1] == '/' || wk1[1] == '\\') {
462 /* case of /./ -> skip the ./ */
463 wk1 += 2;
465 else if (wk1[1] == '.') {
466 /* found /.. look for next / */
467 TRACE("found '/..'\n");
468 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
469 || wk1[2] == '#' || !wk1[2]) {
470 /* case /../ -> need to backup wk2 */
471 TRACE("found '/../'\n");
472 *(wk2-1) = L'\0'; /* set end of string */
473 mp = strrchrW(root, slash);
474 if (mp && (mp >= root)) {
475 /* found valid backup point */
476 wk2 = mp + 1;
477 if(wk1[2] != '/' && wk1[2] != '\\')
478 wk1 += 2;
479 else
480 wk1 += 3;
482 else {
483 /* did not find point, restore '/' */
484 *(wk2-1) = slash;
490 *wk2 = L'\0';
491 break;
492 default:
493 FIXME("how did we get here - state=%d\n", state);
494 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
495 return E_INVALIDARG;
497 *wk2 = L'\0';
498 TRACE("Simplified, orig <%s>, simple <%s>\n",
499 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
501 nLen = lstrlenW(lpszUrlCpy);
502 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
503 lpszUrlCpy[--nLen]=0;
505 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
506 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
508 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
509 URL_ESCAPE_SPACES_ONLY |
510 URL_ESCAPE_PERCENT |
511 URL_DONT_ESCAPE_EXTRA_INFO |
512 URL_ESCAPE_SEGMENT_ONLY ))) {
513 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
514 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
515 EscapeFlags);
516 } else { /* No escaping needed, just copy the string */
517 nLen = lstrlenW(lpszUrlCpy);
518 if(nLen < *pcchCanonicalized)
519 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
520 else {
521 hr = E_POINTER;
522 nLen++;
524 *pcchCanonicalized = nLen;
527 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
529 if (hr == S_OK)
530 TRACE("result %s\n", debugstr_w(pszCanonicalized));
532 return hr;
535 /*************************************************************************
536 * UrlCombineA [SHLWAPI.@]
538 * Combine two Urls.
540 * PARAMS
541 * pszBase [I] Base Url
542 * pszRelative [I] Url to combine with pszBase
543 * pszCombined [O] Destination for combined Url
544 * pcchCombined [O] Destination for length of pszCombined
545 * dwFlags [I] URL_ flags from "shlwapi.h"
547 * RETURNS
548 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
549 * contains its length.
550 * Failure: An HRESULT error code indicating the error.
552 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
553 LPSTR pszCombined, LPDWORD pcchCombined,
554 DWORD dwFlags)
556 LPWSTR base, relative, combined;
557 DWORD ret, len, len2;
559 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
560 debugstr_a(pszBase),debugstr_a(pszRelative),
561 pcchCombined?*pcchCombined:0,dwFlags);
563 if(!pszBase || !pszRelative || !pcchCombined)
564 return E_INVALIDARG;
566 base = HeapAlloc(GetProcessHeap(), 0,
567 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
568 relative = base + INTERNET_MAX_URL_LENGTH;
569 combined = relative + INTERNET_MAX_URL_LENGTH;
571 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
572 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
573 len = *pcchCombined;
575 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
576 if (ret != S_OK) {
577 *pcchCombined = len;
578 HeapFree(GetProcessHeap(), 0, base);
579 return ret;
582 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
583 if (len2 > *pcchCombined) {
584 *pcchCombined = len2;
585 HeapFree(GetProcessHeap(), 0, base);
586 return E_POINTER;
588 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
589 0, 0);
590 *pcchCombined = len2;
591 HeapFree(GetProcessHeap(), 0, base);
592 return S_OK;
595 /*************************************************************************
596 * UrlCombineW [SHLWAPI.@]
598 * See UrlCombineA.
600 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
601 LPWSTR pszCombined, LPDWORD pcchCombined,
602 DWORD dwFlags)
604 PARSEDURLW base, relative;
605 DWORD myflags, sizeloc = 0;
606 DWORD len, res1, res2, process_case = 0;
607 LPWSTR work, preliminary, mbase, mrelative;
608 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
609 static const WCHAR single_slash[] = {'/','\0'};
610 HRESULT ret;
612 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
613 debugstr_w(pszBase),debugstr_w(pszRelative),
614 pcchCombined?*pcchCombined:0,dwFlags);
616 if(!pszBase || !pszRelative || !pcchCombined)
617 return E_INVALIDARG;
619 base.cbSize = sizeof(base);
620 relative.cbSize = sizeof(relative);
622 /* Get space for duplicates of the input and the output */
623 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
624 sizeof(WCHAR));
625 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
626 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
627 *preliminary = L'\0';
629 /* Canonicalize the base input prior to looking for the scheme */
630 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
631 len = INTERNET_MAX_URL_LENGTH;
632 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
634 /* Canonicalize the relative input prior to looking for the scheme */
635 len = INTERNET_MAX_URL_LENGTH;
636 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
638 /* See if the base has a scheme */
639 res1 = ParseURLW(mbase, &base);
640 if (res1) {
641 /* if pszBase has no scheme, then return pszRelative */
642 TRACE("no scheme detected in Base\n");
643 process_case = 1;
645 else do {
647 /* get size of location field (if it exists) */
648 work = (LPWSTR)base.pszSuffix;
649 sizeloc = 0;
650 if (*work++ == L'/') {
651 if (*work++ == L'/') {
652 /* At this point have start of location and
653 * it ends at next '/' or end of string.
655 while(*work && (*work != L'/')) work++;
656 sizeloc = (DWORD)(work - base.pszSuffix);
660 /* Change .sizep2 to not have the last leaf in it,
661 * Note: we need to start after the location (if it exists)
663 work = strrchrW((base.pszSuffix+sizeloc), L'/');
664 if (work) {
665 len = (DWORD)(work - base.pszSuffix + 1);
666 base.cchSuffix = len;
669 * At this point:
670 * .pszSuffix points to location (starting with '//')
671 * .cchSuffix length of location (above) and rest less the last
672 * leaf (if any)
673 * sizeloc length of location (above) up to but not including
674 * the last '/'
677 res2 = ParseURLW(mrelative, &relative);
678 if (res2) {
679 /* no scheme in pszRelative */
680 TRACE("no scheme detected in Relative\n");
681 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
682 relative.cchSuffix = strlenW(mrelative);
683 if (*pszRelative == L':') {
684 /* case that is either left alone or uses pszBase */
685 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
686 process_case = 5;
687 break;
689 process_case = 1;
690 break;
692 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
693 /* case that becomes "file:///" */
694 strcpyW(preliminary, myfilestr);
695 process_case = 1;
696 break;
698 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
699 /* pszRelative has location and rest */
700 process_case = 3;
701 break;
703 if (*mrelative == L'/') {
704 /* case where pszRelative is root to location */
705 process_case = 4;
706 break;
708 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
709 break;
712 /* handle cases where pszRelative has scheme */
713 if ((base.cchProtocol == relative.cchProtocol) &&
714 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
716 /* since the schemes are the same */
717 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
718 /* case where pszRelative replaces location and following */
719 process_case = 3;
720 break;
722 if (*relative.pszSuffix == L'/') {
723 /* case where pszRelative is root to location */
724 process_case = 4;
725 break;
727 /* case where scheme is followed by document path */
728 process_case = 5;
729 break;
731 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
732 /* case where pszRelative replaces scheme, location,
733 * and following and handles PLUGGABLE
735 process_case = 2;
736 break;
738 process_case = 1;
739 break;
740 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
743 ret = S_OK;
744 switch (process_case) {
746 case 1: /*
747 * Return pszRelative appended to what ever is in pszCombined,
748 * (which may the string "file:///"
750 strcatW(preliminary, mrelative);
751 break;
753 case 2: /*
754 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
755 * and pszRelative starts with "//", then append a "/"
757 strcpyW(preliminary, mrelative);
758 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
759 URL_JustLocation(relative.pszSuffix))
760 strcatW(preliminary, single_slash);
761 break;
763 case 3: /*
764 * Return the pszBase scheme with pszRelative. Basically
765 * keeps the scheme and replaces the domain and following.
767 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
768 work = preliminary + base.cchProtocol + 1;
769 strcpyW(work, relative.pszSuffix);
770 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
771 URL_JustLocation(relative.pszSuffix))
772 strcatW(work, single_slash);
773 break;
775 case 4: /*
776 * Return the pszBase scheme and location but everything
777 * after the location is pszRelative. (Replace document
778 * from root on.)
780 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
781 work = preliminary + base.cchProtocol + 1 + sizeloc;
782 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
783 *(work++) = L'/';
784 strcpyW(work, relative.pszSuffix);
785 break;
787 case 5: /*
788 * Return the pszBase without its document (if any) and
789 * append pszRelative after its scheme.
791 memcpy(preliminary, base.pszProtocol,
792 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
793 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
794 if (*work++ != L'/')
795 *(work++) = L'/';
796 strcpyW(work, relative.pszSuffix);
797 break;
799 default:
800 FIXME("How did we get here????? process_case=%ld\n", process_case);
801 ret = E_INVALIDARG;
804 if (ret == S_OK) {
805 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
806 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
807 if(SUCCEEDED(ret) && pszCombined) {
808 lstrcpyW(pszCombined, mrelative);
810 TRACE("return-%ld len=%ld, %s\n",
811 process_case, *pcchCombined, debugstr_w(pszCombined));
813 HeapFree(GetProcessHeap(), 0, preliminary);
814 return ret;
817 /*************************************************************************
818 * UrlEscapeA [SHLWAPI.@]
821 HRESULT WINAPI UrlEscapeA(
822 LPCSTR pszUrl,
823 LPSTR pszEscaped,
824 LPDWORD pcchEscaped,
825 DWORD dwFlags)
827 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
828 WCHAR *escapedW = bufW;
829 UNICODE_STRING urlW;
830 HRESULT ret;
831 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
833 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
834 return E_INVALIDARG;
835 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
836 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
837 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
839 if(ret == S_OK) {
840 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
841 if(*pcchEscaped > lenA) {
842 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
843 pszEscaped[lenA] = 0;
844 *pcchEscaped = lenA;
845 } else {
846 *pcchEscaped = lenA + 1;
847 ret = E_POINTER;
850 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
851 RtlFreeUnicodeString(&urlW);
852 return ret;
855 #define WINE_URL_BASH_AS_SLASH 0x01
856 #define WINE_URL_COLLAPSE_SLASHES 0x02
857 #define WINE_URL_ESCAPE_SLASH 0x04
858 #define WINE_URL_ESCAPE_HASH 0x08
859 #define WINE_URL_ESCAPE_QUESTION 0x10
860 #define WINE_URL_STOP_ON_HASH 0x20
861 #define WINE_URL_STOP_ON_QUESTION 0x40
863 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
866 if (isalnumW(ch))
867 return FALSE;
869 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
870 if(ch == ' ')
871 return TRUE;
872 else
873 return FALSE;
876 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
877 return TRUE;
879 if (ch <= 31 || ch >= 127)
880 return TRUE;
882 else {
883 switch (ch) {
884 case ' ':
885 case '<':
886 case '>':
887 case '\"':
888 case '{':
889 case '}':
890 case '|':
891 case '\\':
892 case '^':
893 case ']':
894 case '[':
895 case '`':
896 case '&':
897 return TRUE;
899 case '/':
900 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
901 return FALSE;
903 case '?':
904 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
905 return FALSE;
907 case '#':
908 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
909 return FALSE;
911 default:
912 return FALSE;
918 /*************************************************************************
919 * UrlEscapeW [SHLWAPI.@]
921 * Converts unsafe characters in a Url into escape sequences.
923 * PARAMS
924 * pszUrl [I] Url to modify
925 * pszEscaped [O] Destination for modified Url
926 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
927 * dwFlags [I] URL_ flags from "shlwapi.h"
929 * RETURNS
930 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
931 * contains its length.
932 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
933 * pcchEscaped is set to the required length.
935 * Converts unsafe characters into their escape sequences.
937 * NOTES
938 * - By default this function stops converting at the first '?' or
939 * '#' character.
940 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
941 * converted, but the conversion continues past a '?' or '#'.
942 * - Note that this function did not work well (or at all) in shlwapi version 4.
944 * BUGS
945 * Only the following flags are implemented:
946 *| URL_ESCAPE_SPACES_ONLY
947 *| URL_DONT_ESCAPE_EXTRA_INFO
948 *| URL_ESCAPE_SEGMENT_ONLY
949 *| URL_ESCAPE_PERCENT
951 HRESULT WINAPI UrlEscapeW(
952 LPCWSTR pszUrl,
953 LPWSTR pszEscaped,
954 LPDWORD pcchEscaped,
955 DWORD dwFlags)
957 LPCWSTR src;
958 DWORD needed = 0, ret;
959 BOOL stop_escaping = FALSE;
960 WCHAR next[5], *dst = pszEscaped;
961 INT len;
962 PARSEDURLW parsed_url;
963 DWORD int_flags;
964 DWORD slashes = 0;
965 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
967 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
968 pcchEscaped, dwFlags);
970 if(!pszUrl || !pszEscaped || !pcchEscaped)
971 return E_INVALIDARG;
973 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
974 URL_ESCAPE_SEGMENT_ONLY |
975 URL_DONT_ESCAPE_EXTRA_INFO |
976 URL_ESCAPE_PERCENT))
977 FIXME("Unimplemented flags: %08lx\n", dwFlags);
979 /* fix up flags */
980 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
981 /* if SPACES_ONLY specified, reset the other controls */
982 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
983 URL_ESCAPE_PERCENT |
984 URL_ESCAPE_SEGMENT_ONLY);
986 else
987 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
988 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
991 int_flags = 0;
992 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
993 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
994 } else {
995 parsed_url.cbSize = sizeof(parsed_url);
996 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
997 parsed_url.nScheme = URL_SCHEME_INVALID;
999 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1001 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1002 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1004 switch(parsed_url.nScheme) {
1005 case URL_SCHEME_FILE:
1006 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1007 int_flags &= ~WINE_URL_STOP_ON_HASH;
1008 break;
1010 case URL_SCHEME_HTTP:
1011 case URL_SCHEME_HTTPS:
1012 int_flags |= WINE_URL_BASH_AS_SLASH;
1013 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1014 int_flags |= WINE_URL_ESCAPE_SLASH;
1015 break;
1017 case URL_SCHEME_MAILTO:
1018 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1019 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1020 break;
1022 case URL_SCHEME_INVALID:
1023 break;
1025 case URL_SCHEME_FTP:
1026 default:
1027 if(parsed_url.pszSuffix[0] != '/')
1028 int_flags |= WINE_URL_ESCAPE_SLASH;
1029 break;
1033 for(src = pszUrl; *src; ) {
1034 WCHAR cur = *src;
1035 len = 0;
1037 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1038 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1039 while(cur == '/' || cur == '\\') {
1040 slashes++;
1041 cur = *++src;
1043 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1044 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1045 src += localhost_len + 1;
1046 slashes = 3;
1049 switch(slashes) {
1050 case 1:
1051 case 3:
1052 next[0] = next[1] = next[2] = '/';
1053 len = 3;
1054 break;
1055 case 0:
1056 len = 0;
1057 break;
1058 default:
1059 next[0] = next[1] = '/';
1060 len = 2;
1061 break;
1064 if(len == 0) {
1066 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1067 stop_escaping = TRUE;
1069 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1070 stop_escaping = TRUE;
1072 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1074 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1075 next[0] = L'%';
1076 next[1] = hexDigits[(cur >> 4) & 0xf];
1077 next[2] = hexDigits[cur & 0xf];
1078 len = 3;
1079 } else {
1080 next[0] = cur;
1081 len = 1;
1083 src++;
1086 if(needed + len <= *pcchEscaped) {
1087 memcpy(dst, next, len*sizeof(WCHAR));
1088 dst += len;
1090 needed += len;
1093 if(needed < *pcchEscaped) {
1094 *dst = '\0';
1095 ret = S_OK;
1096 } else {
1097 needed++; /* add one for the '\0' */
1098 ret = E_POINTER;
1100 *pcchEscaped = needed;
1101 return ret;
1105 /*************************************************************************
1106 * UrlUnescapeA [SHLWAPI.@]
1108 * Converts Url escape sequences back to ordinary characters.
1110 * PARAMS
1111 * pszUrl [I/O] Url to convert
1112 * pszUnescaped [O] Destination for converted Url
1113 * pcchUnescaped [I/O] Size of output string
1114 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1116 * RETURNS
1117 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1118 * dwFlags includes URL_ESCAPE_INPLACE.
1119 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1120 * this case pcchUnescaped is set to the size required.
1121 * NOTES
1122 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1123 * the first occurrence of either a '?' or '#' character.
1125 HRESULT WINAPI UrlUnescapeA(
1126 LPSTR pszUrl,
1127 LPSTR pszUnescaped,
1128 LPDWORD pcchUnescaped,
1129 DWORD dwFlags)
1131 char *dst, next;
1132 LPCSTR src;
1133 HRESULT ret;
1134 DWORD needed;
1135 BOOL stop_unescaping = FALSE;
1137 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1138 pcchUnescaped, dwFlags);
1140 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1141 return E_INVALIDARG;
1143 if(dwFlags & URL_UNESCAPE_INPLACE)
1144 dst = pszUrl;
1145 else
1146 dst = pszUnescaped;
1148 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1149 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1150 (*src == '#' || *src == '?')) {
1151 stop_unescaping = TRUE;
1152 next = *src;
1153 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1154 && stop_unescaping == FALSE) {
1155 INT ih;
1156 char buf[3];
1157 memcpy(buf, src + 1, 2);
1158 buf[2] = '\0';
1159 ih = strtol(buf, NULL, 16);
1160 next = (CHAR) ih;
1161 src += 2; /* Advance to end of escape */
1162 } else
1163 next = *src;
1165 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1166 *dst++ = next;
1169 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1170 *dst = '\0';
1171 ret = S_OK;
1172 } else {
1173 needed++; /* add one for the '\0' */
1174 ret = E_POINTER;
1176 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1177 *pcchUnescaped = needed;
1179 if (ret == S_OK) {
1180 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1181 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1184 return ret;
1187 /*************************************************************************
1188 * UrlUnescapeW [SHLWAPI.@]
1190 * See UrlUnescapeA.
1192 HRESULT WINAPI UrlUnescapeW(
1193 LPWSTR pszUrl,
1194 LPWSTR pszUnescaped,
1195 LPDWORD pcchUnescaped,
1196 DWORD dwFlags)
1198 WCHAR *dst, next;
1199 LPCWSTR src;
1200 HRESULT ret;
1201 DWORD needed;
1202 BOOL stop_unescaping = FALSE;
1204 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1205 pcchUnescaped, dwFlags);
1207 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1208 return E_INVALIDARG;
1210 if(dwFlags & URL_UNESCAPE_INPLACE)
1211 dst = pszUrl;
1212 else
1213 dst = pszUnescaped;
1215 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1216 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1217 (*src == L'#' || *src == L'?')) {
1218 stop_unescaping = TRUE;
1219 next = *src;
1220 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1221 && stop_unescaping == FALSE) {
1222 INT ih;
1223 WCHAR buf[5] = {'0','x',0};
1224 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1225 buf[4] = 0;
1226 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1227 next = (WCHAR) ih;
1228 src += 2; /* Advance to end of escape */
1229 } else
1230 next = *src;
1232 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1233 *dst++ = next;
1236 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1237 *dst = L'\0';
1238 ret = S_OK;
1239 } else {
1240 needed++; /* add one for the '\0' */
1241 ret = E_POINTER;
1243 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1244 *pcchUnescaped = needed;
1246 if (ret == S_OK) {
1247 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1248 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1251 return ret;
1254 /*************************************************************************
1255 * UrlGetLocationA [SHLWAPI.@]
1257 * Get the location from a Url.
1259 * PARAMS
1260 * pszUrl [I] Url to get the location from
1262 * RETURNS
1263 * A pointer to the start of the location in pszUrl, or NULL if there is
1264 * no location.
1266 * NOTES
1267 * - MSDN erroneously states that "The location is the segment of the Url
1268 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1269 * stop at '?' and always return a NULL in this case.
1270 * - MSDN also erroneously states that "If a file URL has a query string,
1271 * the returned string is the query string". In all tested cases, if the
1272 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1273 *| Result Url
1274 *| ------ ---
1275 *| NULL file://aa/b/cd#hohoh
1276 *| #hohoh http://aa/b/cd#hohoh
1277 *| NULL fi://aa/b/cd#hohoh
1278 *| #hohoh ff://aa/b/cd#hohoh
1280 LPCSTR WINAPI UrlGetLocationA(
1281 LPCSTR pszUrl)
1283 PARSEDURLA base;
1284 DWORD res1;
1286 base.cbSize = sizeof(base);
1287 res1 = ParseURLA(pszUrl, &base);
1288 if (res1) return NULL; /* invalid scheme */
1290 /* if scheme is file: then never return pointer */
1291 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1293 /* Look for '#' and return its addr */
1294 return strchr(base.pszSuffix, '#');
1297 /*************************************************************************
1298 * UrlGetLocationW [SHLWAPI.@]
1300 * See UrlGetLocationA.
1302 LPCWSTR WINAPI UrlGetLocationW(
1303 LPCWSTR pszUrl)
1305 PARSEDURLW base;
1306 DWORD res1;
1308 base.cbSize = sizeof(base);
1309 res1 = ParseURLW(pszUrl, &base);
1310 if (res1) return NULL; /* invalid scheme */
1312 /* if scheme is file: then never return pointer */
1313 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1315 /* Look for '#' and return its addr */
1316 return strchrW(base.pszSuffix, L'#');
1319 /*************************************************************************
1320 * UrlCompareA [SHLWAPI.@]
1322 * Compare two Urls.
1324 * PARAMS
1325 * pszUrl1 [I] First Url to compare
1326 * pszUrl2 [I] Url to compare to pszUrl1
1327 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1329 * RETURNS
1330 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1331 * than, equal to, or less than pszUrl1 respectively.
1333 INT WINAPI UrlCompareA(
1334 LPCSTR pszUrl1,
1335 LPCSTR pszUrl2,
1336 BOOL fIgnoreSlash)
1338 INT ret, len, len1, len2;
1340 if (!fIgnoreSlash)
1341 return strcmp(pszUrl1, pszUrl2);
1342 len1 = strlen(pszUrl1);
1343 if (pszUrl1[len1-1] == '/') len1--;
1344 len2 = strlen(pszUrl2);
1345 if (pszUrl2[len2-1] == '/') len2--;
1346 if (len1 == len2)
1347 return strncmp(pszUrl1, pszUrl2, len1);
1348 len = min(len1, len2);
1349 ret = strncmp(pszUrl1, pszUrl2, len);
1350 if (ret) return ret;
1351 if (len1 > len2) return 1;
1352 return -1;
1355 /*************************************************************************
1356 * UrlCompareW [SHLWAPI.@]
1358 * See UrlCompareA.
1360 INT WINAPI UrlCompareW(
1361 LPCWSTR pszUrl1,
1362 LPCWSTR pszUrl2,
1363 BOOL fIgnoreSlash)
1365 INT ret;
1366 size_t len, len1, len2;
1368 if (!fIgnoreSlash)
1369 return strcmpW(pszUrl1, pszUrl2);
1370 len1 = strlenW(pszUrl1);
1371 if (pszUrl1[len1-1] == '/') len1--;
1372 len2 = strlenW(pszUrl2);
1373 if (pszUrl2[len2-1] == '/') len2--;
1374 if (len1 == len2)
1375 return strncmpW(pszUrl1, pszUrl2, len1);
1376 len = min(len1, len2);
1377 ret = strncmpW(pszUrl1, pszUrl2, len);
1378 if (ret) return ret;
1379 if (len1 > len2) return 1;
1380 return -1;
1383 /*************************************************************************
1384 * HashData [SHLWAPI.@]
1386 * Hash an input block into a variable sized digest.
1388 * PARAMS
1389 * lpSrc [I] Input block
1390 * nSrcLen [I] Length of lpSrc
1391 * lpDest [I] Output for hash digest
1392 * nDestLen [I] Length of lpDest
1394 * RETURNS
1395 * Success: TRUE. lpDest is filled with the computed hash value.
1396 * Failure: FALSE, if any argument is invalid.
1398 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1399 unsigned char *lpDest, DWORD nDestLen)
1401 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1403 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1404 IsBadWritePtr(lpDest, nDestLen))
1405 return E_INVALIDARG;
1407 while (destCount >= 0)
1409 lpDest[destCount] = (destCount & 0xff);
1410 destCount--;
1413 while (srcCount >= 0)
1415 destCount = nDestLen - 1;
1416 while (destCount >= 0)
1418 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1419 destCount--;
1421 srcCount--;
1423 return S_OK;
1426 /*************************************************************************
1427 * UrlHashA [SHLWAPI.@]
1429 * Produce a Hash from a Url.
1431 * PARAMS
1432 * pszUrl [I] Url to hash
1433 * lpDest [O] Destinationh for hash
1434 * nDestLen [I] Length of lpDest
1436 * RETURNS
1437 * Success: S_OK. lpDest is filled with the computed hash value.
1438 * Failure: E_INVALIDARG, if any argument is invalid.
1440 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1442 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1443 return E_INVALIDARG;
1445 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1446 return S_OK;
1449 /*************************************************************************
1450 * UrlHashW [SHLWAPI.@]
1452 * See UrlHashA.
1454 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1456 char szUrl[MAX_PATH];
1458 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1460 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1461 return E_INVALIDARG;
1463 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1464 * return the same digests for the same URL.
1466 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1467 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1468 return S_OK;
1471 /*************************************************************************
1472 * UrlApplySchemeA [SHLWAPI.@]
1474 * Apply a scheme to a Url.
1476 * PARAMS
1477 * pszIn [I] Url to apply scheme to
1478 * pszOut [O] Destination for modified Url
1479 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1480 * dwFlags [I] URL_ flags from "shlwapi.h"
1482 * RETURNS
1483 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1484 * Failure: An HRESULT error code describing the error.
1486 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1488 LPWSTR in, out;
1489 DWORD ret, len, len2;
1491 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1492 debugstr_a(pszIn), *pcchOut, dwFlags);
1494 in = HeapAlloc(GetProcessHeap(), 0,
1495 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1496 out = in + INTERNET_MAX_URL_LENGTH;
1498 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1499 len = INTERNET_MAX_URL_LENGTH;
1501 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1502 if ((ret != S_OK) && (ret != S_FALSE)) {
1503 HeapFree(GetProcessHeap(), 0, in);
1504 return ret;
1507 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1508 if (len2 > *pcchOut) {
1509 *pcchOut = len2;
1510 HeapFree(GetProcessHeap(), 0, in);
1511 return E_POINTER;
1513 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1514 *pcchOut = len2;
1515 HeapFree(GetProcessHeap(), 0, in);
1516 return ret;
1519 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1521 HKEY newkey;
1522 BOOL j;
1523 INT index;
1524 DWORD value_len, data_len, dwType, i;
1525 WCHAR reg_path[MAX_PATH];
1526 WCHAR value[MAX_PATH], data[MAX_PATH];
1527 WCHAR Wxx, Wyy;
1529 MultiByteToWideChar(0, 0,
1530 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1531 -1, reg_path, MAX_PATH);
1532 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1533 index = 0;
1534 while(value_len = data_len = MAX_PATH,
1535 RegEnumValueW(newkey, index, value, &value_len,
1536 0, &dwType, (LPVOID)data, &data_len) == 0) {
1537 TRACE("guess %d %s is %s\n",
1538 index, debugstr_w(value), debugstr_w(data));
1540 j = FALSE;
1541 for(i=0; i<value_len; i++) {
1542 Wxx = pszIn[i];
1543 Wyy = value[i];
1544 /* remember that TRUE is not-equal */
1545 j = ChrCmpIW(Wxx, Wyy);
1546 if (j) break;
1548 if ((i == value_len) && !j) {
1549 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1550 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1551 RegCloseKey(newkey);
1552 return E_POINTER;
1554 strcpyW(pszOut, data);
1555 strcatW(pszOut, pszIn);
1556 *pcchOut = strlenW(pszOut);
1557 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1558 RegCloseKey(newkey);
1559 return S_OK;
1561 index++;
1563 RegCloseKey(newkey);
1564 return -1;
1567 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1569 HKEY newkey;
1570 DWORD data_len, dwType;
1571 WCHAR reg_path[MAX_PATH];
1572 WCHAR value[MAX_PATH], data[MAX_PATH];
1574 /* get and prepend default */
1575 MultiByteToWideChar(0, 0,
1576 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1577 -1, reg_path, MAX_PATH);
1578 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1579 data_len = MAX_PATH;
1580 value[0] = L'@';
1581 value[1] = L'\0';
1582 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1583 RegCloseKey(newkey);
1584 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1585 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1586 return E_POINTER;
1588 strcpyW(pszOut, data);
1589 strcatW(pszOut, pszIn);
1590 *pcchOut = strlenW(pszOut);
1591 TRACE("used default %s\n", debugstr_w(pszOut));
1592 return S_OK;
1595 /*************************************************************************
1596 * UrlApplySchemeW [SHLWAPI.@]
1598 * See UrlApplySchemeA.
1600 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1602 PARSEDURLW in_scheme;
1603 DWORD res1;
1604 HRESULT ret;
1606 TRACE("(in %s, out size %ld, flags %08lx)\n",
1607 debugstr_w(pszIn), *pcchOut, dwFlags);
1609 if (dwFlags & URL_APPLY_GUESSFILE) {
1610 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1611 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1612 strcpyW(pszOut, pszIn);
1613 *pcchOut = strlenW(pszOut);
1614 return S_FALSE;
1617 in_scheme.cbSize = sizeof(in_scheme);
1618 /* See if the base has a scheme */
1619 res1 = ParseURLW(pszIn, &in_scheme);
1620 if (res1) {
1621 /* no scheme in input, need to see if we need to guess */
1622 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1623 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1624 return ret;
1627 else {
1628 /* we have a scheme, see if valid (known scheme) */
1629 if (in_scheme.nScheme) {
1630 /* have valid scheme, so just copy and exit */
1631 if (strlenW(pszIn) + 1 > *pcchOut) {
1632 *pcchOut = strlenW(pszIn) + 1;
1633 return E_POINTER;
1635 strcpyW(pszOut, pszIn);
1636 *pcchOut = strlenW(pszOut);
1637 TRACE("valid scheme, returing copy\n");
1638 return S_OK;
1642 /* If we are here, then either invalid scheme,
1643 * or no scheme and can't/failed guess.
1645 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1646 ((res1 != 0)) ) &&
1647 (dwFlags & URL_APPLY_DEFAULT)) {
1648 /* find and apply default scheme */
1649 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1652 /* just copy and give proper return code */
1653 if (strlenW(pszIn) + 1 > *pcchOut) {
1654 *pcchOut = strlenW(pszIn) + 1;
1655 return E_POINTER;
1657 strcpyW(pszOut, pszIn);
1658 *pcchOut = strlenW(pszOut);
1659 TRACE("returning copy, left alone\n");
1660 return S_FALSE;
1663 /*************************************************************************
1664 * UrlIsA [SHLWAPI.@]
1666 * Determine if a Url is of a certain class.
1668 * PARAMS
1669 * pszUrl [I] Url to check
1670 * Urlis [I] URLIS_ constant from "shlwapi.h"
1672 * RETURNS
1673 * TRUE if pszUrl belongs to the class type in Urlis.
1674 * FALSE Otherwise.
1676 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1678 PARSEDURLA base;
1679 DWORD res1;
1680 LPCSTR last;
1682 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1684 switch (Urlis) {
1686 case URLIS_OPAQUE:
1687 base.cbSize = sizeof(base);
1688 res1 = ParseURLA(pszUrl, &base);
1689 if (res1) return FALSE; /* invalid scheme */
1690 switch (base.nScheme)
1692 case URL_SCHEME_MAILTO:
1693 case URL_SCHEME_SHELL:
1694 case URL_SCHEME_JAVASCRIPT:
1695 case URL_SCHEME_VBSCRIPT:
1696 case URL_SCHEME_ABOUT:
1697 return TRUE;
1699 return FALSE;
1701 case URLIS_FILEURL:
1702 return !StrCmpNA("file:", pszUrl, 5);
1704 case URLIS_DIRECTORY:
1705 last = pszUrl + strlen(pszUrl) - 1;
1706 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1708 case URLIS_URL:
1709 return PathIsURLA(pszUrl);
1711 case URLIS_NOHISTORY:
1712 case URLIS_APPLIABLE:
1713 case URLIS_HASQUERY:
1714 default:
1715 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1717 return FALSE;
1720 /*************************************************************************
1721 * UrlIsW [SHLWAPI.@]
1723 * See UrlIsA.
1725 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1727 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1728 PARSEDURLW base;
1729 DWORD res1;
1730 LPCWSTR last;
1732 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1734 switch (Urlis) {
1736 case URLIS_OPAQUE:
1737 base.cbSize = sizeof(base);
1738 res1 = ParseURLW(pszUrl, &base);
1739 if (res1) return FALSE; /* invalid scheme */
1740 switch (base.nScheme)
1742 case URL_SCHEME_MAILTO:
1743 case URL_SCHEME_SHELL:
1744 case URL_SCHEME_JAVASCRIPT:
1745 case URL_SCHEME_VBSCRIPT:
1746 case URL_SCHEME_ABOUT:
1747 return TRUE;
1749 return FALSE;
1751 case URLIS_FILEURL:
1752 return !strncmpW(stemp, pszUrl, 5);
1754 case URLIS_DIRECTORY:
1755 last = pszUrl + strlenW(pszUrl) - 1;
1756 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1758 case URLIS_URL:
1759 return PathIsURLW(pszUrl);
1761 case URLIS_NOHISTORY:
1762 case URLIS_APPLIABLE:
1763 case URLIS_HASQUERY:
1764 default:
1765 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1767 return FALSE;
1770 /*************************************************************************
1771 * UrlIsNoHistoryA [SHLWAPI.@]
1773 * Determine if a Url should not be stored in the users history list.
1775 * PARAMS
1776 * pszUrl [I] Url to check
1778 * RETURNS
1779 * TRUE, if pszUrl should be excluded from the history list,
1780 * FALSE otherwise.
1782 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1784 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1787 /*************************************************************************
1788 * UrlIsNoHistoryW [SHLWAPI.@]
1790 * See UrlIsNoHistoryA.
1792 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1794 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1797 /*************************************************************************
1798 * UrlIsOpaqueA [SHLWAPI.@]
1800 * Determine if a Url is opaque.
1802 * PARAMS
1803 * pszUrl [I] Url to check
1805 * RETURNS
1806 * TRUE if pszUrl is opaque,
1807 * FALSE Otherwise.
1809 * NOTES
1810 * An opaque Url is one that does not start with "<protocol>://".
1812 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1814 return UrlIsA(pszUrl, URLIS_OPAQUE);
1817 /*************************************************************************
1818 * UrlIsOpaqueW [SHLWAPI.@]
1820 * See UrlIsOpaqueA.
1822 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1824 return UrlIsW(pszUrl, URLIS_OPAQUE);
1827 /*************************************************************************
1828 * Scans for characters of type "type" and when not matching found,
1829 * returns pointer to it and length in size.
1831 * Characters tested based on RFC 1738
1833 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1835 static DWORD alwayszero = 0;
1836 BOOL cont = TRUE;
1838 *size = 0;
1840 switch(type){
1842 case SCHEME:
1843 while (cont) {
1844 if ( (islowerW(*start) && isalphaW(*start)) ||
1845 isdigitW(*start) ||
1846 (*start == L'+') ||
1847 (*start == L'-') ||
1848 (*start == L'.')) {
1849 start++;
1850 (*size)++;
1852 else
1853 cont = FALSE;
1855 break;
1857 case USERPASS:
1858 while (cont) {
1859 if ( isalphaW(*start) ||
1860 isdigitW(*start) ||
1861 /* user/password only characters */
1862 (*start == L';') ||
1863 (*start == L'?') ||
1864 (*start == L'&') ||
1865 (*start == L'=') ||
1866 /* *extra* characters */
1867 (*start == L'!') ||
1868 (*start == L'*') ||
1869 (*start == L'\'') ||
1870 (*start == L'(') ||
1871 (*start == L')') ||
1872 (*start == L',') ||
1873 /* *safe* characters */
1874 (*start == L'$') ||
1875 (*start == L'_') ||
1876 (*start == L'+') ||
1877 (*start == L'-') ||
1878 (*start == L'.')) {
1879 start++;
1880 (*size)++;
1881 } else if (*start == L'%') {
1882 if (isxdigitW(*(start+1)) &&
1883 isxdigitW(*(start+2))) {
1884 start += 3;
1885 *size += 3;
1886 } else
1887 cont = FALSE;
1888 } else
1889 cont = FALSE;
1891 break;
1893 case PORT:
1894 while (cont) {
1895 if (isdigitW(*start)) {
1896 start++;
1897 (*size)++;
1899 else
1900 cont = FALSE;
1902 break;
1904 case HOST:
1905 while (cont) {
1906 if (isalnumW(*start) ||
1907 (*start == L'-') ||
1908 (*start == L'.') ) {
1909 start++;
1910 (*size)++;
1912 else
1913 cont = FALSE;
1915 break;
1916 default:
1917 FIXME("unknown type %d\n", type);
1918 return (LPWSTR)&alwayszero;
1920 /* TRACE("scanned %ld characters next char %p<%c>\n",
1921 *size, start, *start); */
1922 return start;
1925 /*************************************************************************
1926 * Attempt to parse URL into pieces.
1928 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1930 LPCWSTR work;
1932 memset(pl, 0, sizeof(WINE_PARSE_URL));
1933 pl->pScheme = pszUrl;
1934 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1935 if (!*work || (*work != L':')) goto ErrorExit;
1936 work++;
1937 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1938 pl->pUserName = work + 2;
1939 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1940 if (*work == L':' ) {
1941 /* parse password */
1942 work++;
1943 pl->pPassword = work;
1944 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1945 if (*work != L'@') {
1946 /* what we just parsed must be the hostname and port
1947 * so reset pointers and clear then let it parse */
1948 pl->szUserName = pl->szPassword = 0;
1949 work = pl->pUserName - 1;
1950 pl->pUserName = pl->pPassword = 0;
1952 } else if (*work == L'@') {
1953 /* no password */
1954 pl->szPassword = 0;
1955 pl->pPassword = 0;
1956 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1957 /* what was parsed was hostname, so reset pointers and let it parse */
1958 pl->szUserName = pl->szPassword = 0;
1959 work = pl->pUserName - 1;
1960 pl->pUserName = pl->pPassword = 0;
1961 } else goto ErrorExit;
1963 /* now start parsing hostname or hostnumber */
1964 work++;
1965 pl->pHostName = work;
1966 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1967 if (*work == L':') {
1968 /* parse port */
1969 work++;
1970 pl->pPort = work;
1971 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1973 if (*work == L'/') {
1974 /* see if query string */
1975 pl->pQuery = strchrW(work, L'?');
1976 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1978 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1979 pl->pScheme, pl->szScheme,
1980 pl->pUserName, pl->szUserName,
1981 pl->pPassword, pl->szPassword,
1982 pl->pHostName, pl->szHostName,
1983 pl->pPort, pl->szPort,
1984 pl->pQuery, pl->szQuery);
1985 return S_OK;
1986 ErrorExit:
1987 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1988 return E_INVALIDARG;
1991 /*************************************************************************
1992 * UrlGetPartA [SHLWAPI.@]
1994 * Retrieve part of a Url.
1996 * PARAMS
1997 * pszIn [I] Url to parse
1998 * pszOut [O] Destination for part of pszIn requested
1999 * pcchOut [I] Size of pszOut
2000 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
2001 * needed size of pszOut INCLUDING '\0'.
2002 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2003 * dwFlags [I] URL_ flags from "shlwapi.h"
2005 * RETURNS
2006 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2007 * Failure: An HRESULT error code describing the error.
2009 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2010 DWORD dwPart, DWORD dwFlags)
2012 LPWSTR in, out;
2013 DWORD ret, len, len2;
2015 in = HeapAlloc(GetProcessHeap(), 0,
2016 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2017 out = in + INTERNET_MAX_URL_LENGTH;
2019 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2021 len = INTERNET_MAX_URL_LENGTH;
2022 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2024 if (ret != S_OK) {
2025 HeapFree(GetProcessHeap(), 0, in);
2026 return ret;
2029 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2030 if (len2 > *pcchOut) {
2031 *pcchOut = len2;
2032 HeapFree(GetProcessHeap(), 0, in);
2033 return E_POINTER;
2035 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2036 *pcchOut = len2;
2037 HeapFree(GetProcessHeap(), 0, in);
2038 return S_OK;
2041 /*************************************************************************
2042 * UrlGetPartW [SHLWAPI.@]
2044 * See UrlGetPartA.
2046 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2047 DWORD dwPart, DWORD dwFlags)
2049 WINE_PARSE_URL pl;
2050 HRESULT ret;
2051 DWORD size, schsize;
2052 LPCWSTR addr, schaddr;
2054 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2055 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2057 ret = URL_ParseUrl(pszIn, &pl);
2058 if (!ret) {
2059 schaddr = pl.pScheme;
2060 schsize = pl.szScheme;
2062 switch (dwPart) {
2063 case URL_PART_SCHEME:
2064 if (!pl.szScheme) return E_INVALIDARG;
2065 addr = pl.pScheme;
2066 size = pl.szScheme;
2067 break;
2069 case URL_PART_HOSTNAME:
2070 if (!pl.szHostName) return E_INVALIDARG;
2071 addr = pl.pHostName;
2072 size = pl.szHostName;
2073 break;
2075 case URL_PART_USERNAME:
2076 if (!pl.szUserName) return E_INVALIDARG;
2077 addr = pl.pUserName;
2078 size = pl.szUserName;
2079 break;
2081 case URL_PART_PASSWORD:
2082 if (!pl.szPassword) return E_INVALIDARG;
2083 addr = pl.pPassword;
2084 size = pl.szPassword;
2085 break;
2087 case URL_PART_PORT:
2088 if (!pl.szPort) return E_INVALIDARG;
2089 addr = pl.pPort;
2090 size = pl.szPort;
2091 break;
2093 case URL_PART_QUERY:
2094 if (!pl.szQuery) return E_INVALIDARG;
2095 addr = pl.pQuery;
2096 size = pl.szQuery;
2097 break;
2099 default:
2100 return E_INVALIDARG;
2103 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2104 if (*pcchOut < schsize + size + 2) {
2105 *pcchOut = schsize + size + 2;
2106 return E_POINTER;
2108 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2109 pszOut[schsize] = ':';
2110 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2111 pszOut[schsize+1+size] = 0;
2112 *pcchOut = schsize + 1 + size;
2114 else {
2115 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2116 memcpy(pszOut, addr, size*sizeof(WCHAR));
2117 pszOut[size] = 0;
2118 *pcchOut = size;
2120 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2122 return ret;
2125 /*************************************************************************
2126 * PathIsURLA [SHLWAPI.@]
2128 * Check if the given path is a Url.
2130 * PARAMS
2131 * lpszPath [I] Path to check.
2133 * RETURNS
2134 * TRUE if lpszPath is a Url.
2135 * FALSE if lpszPath is NULL or not a Url.
2137 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2139 PARSEDURLA base;
2140 DWORD res1;
2142 if (!lpstrPath || !*lpstrPath) return FALSE;
2144 /* get protocol */
2145 base.cbSize = sizeof(base);
2146 res1 = ParseURLA(lpstrPath, &base);
2147 return (base.nScheme != URL_SCHEME_INVALID);
2150 /*************************************************************************
2151 * PathIsURLW [SHLWAPI.@]
2153 * See PathIsURLA.
2155 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2157 PARSEDURLW base;
2158 DWORD res1;
2160 if (!lpstrPath || !*lpstrPath) return FALSE;
2162 /* get protocol */
2163 base.cbSize = sizeof(base);
2164 res1 = ParseURLW(lpstrPath, &base);
2165 return (base.nScheme != URL_SCHEME_INVALID);
2168 /*************************************************************************
2169 * UrlCreateFromPathA [SHLWAPI.@]
2171 * See UrlCreateFromPathW
2173 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2175 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2176 WCHAR *urlW = bufW;
2177 UNICODE_STRING pathW;
2178 HRESULT ret;
2179 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2181 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2182 return E_INVALIDARG;
2183 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2184 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2185 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2187 if(ret == S_OK || ret == S_FALSE) {
2188 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2189 if(*pcchUrl > lenA) {
2190 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2191 pszUrl[lenA] = 0;
2192 *pcchUrl = lenA;
2193 } else {
2194 *pcchUrl = lenA + 1;
2195 ret = E_POINTER;
2198 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2199 RtlFreeUnicodeString(&pathW);
2200 return ret;
2203 /*************************************************************************
2204 * UrlCreateFromPathW [SHLWAPI.@]
2206 * Create a Url from a file path.
2208 * PARAMS
2209 * pszPath [I] Path to convert
2210 * pszUrl [O] Destination for the converted Url
2211 * pcchUrl [I/O] Length of pszUrl
2212 * dwReserved [I] Reserved, must be 0
2214 * RETURNS
2215 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2216 * Failure: An HRESULT error code.
2218 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2220 DWORD needed;
2221 HRESULT ret;
2222 WCHAR *pszNewUrl;
2223 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2224 WCHAR three_slashesW[] = {'/','/','/',0};
2225 PARSEDURLW parsed_url;
2227 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2229 /* Validate arguments */
2230 if (dwReserved != 0)
2231 return E_INVALIDARG;
2232 if (!pszUrl || !pcchUrl)
2233 return E_INVALIDARG;
2236 parsed_url.cbSize = sizeof(parsed_url);
2237 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2238 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2239 needed = strlenW(pszPath);
2240 if (needed >= *pcchUrl) {
2241 *pcchUrl = needed + 1;
2242 return E_POINTER;
2243 } else {
2244 *pcchUrl = needed;
2245 strcpyW(pszUrl, pszPath);
2246 return S_FALSE;
2251 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2252 strcpyW(pszNewUrl, file_colonW);
2253 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2254 strcatW(pszNewUrl, three_slashesW);
2255 strcatW(pszNewUrl, pszPath);
2256 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2258 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2259 return ret;
2262 /*************************************************************************
2263 * SHAutoComplete [SHLWAPI.@]
2265 * Enable auto-completion for an edit control.
2267 * PARAMS
2268 * hwndEdit [I] Handle of control to enable auto-completion for
2269 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2271 * RETURNS
2272 * Success: S_OK. Auto-completion is enabled for the control.
2273 * Failure: An HRESULT error code indicating the error.
2275 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2277 FIXME("SHAutoComplete stub\n");
2278 return S_FALSE;
2281 /*************************************************************************
2282 * MLBuildResURLA [SHLWAPI.405]
2284 * Create a Url pointing to a resource in a module.
2286 * PARAMS
2287 * lpszLibName [I] Name of the module containing the resource
2288 * hMod [I] Callers module handle
2289 * dwFlags [I] Undocumented flags for loading the module
2290 * lpszRes [I] Resource name
2291 * lpszDest [O] Destination for resulting Url
2292 * dwDestLen [I] Length of lpszDest
2294 * RETURNS
2295 * Success: S_OK. lpszDest constains the resource Url.
2296 * Failure: E_INVALIDARG, if any argument is invalid, or
2297 * E_FAIL if dwDestLen is too small.
2299 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2300 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2302 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2303 HRESULT hRet;
2305 if (lpszLibName)
2306 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2308 if (lpszRes)
2309 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2311 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2312 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2314 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2315 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2316 if (SUCCEEDED(hRet) && lpszDest)
2317 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2319 return hRet;
2322 /*************************************************************************
2323 * MLBuildResURLA [SHLWAPI.406]
2325 * See MLBuildResURLA.
2327 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2328 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2330 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2331 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2332 HRESULT hRet = E_FAIL;
2334 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2335 debugstr_w(lpszRes), lpszDest, dwDestLen);
2337 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2338 !lpszDest || (dwFlags && dwFlags != 2))
2339 return E_INVALIDARG;
2341 if (dwDestLen >= szResLen + 1)
2343 dwDestLen -= (szResLen + 1);
2344 memcpy(lpszDest, szRes, sizeof(szRes));
2346 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2348 if (hMod)
2350 WCHAR szBuff[MAX_PATH];
2351 DWORD len;
2353 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2354 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2356 DWORD dwPathLen = strlenW(szBuff) + 1;
2358 if (dwDestLen >= dwPathLen)
2360 DWORD dwResLen;
2362 dwDestLen -= dwPathLen;
2363 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2365 dwResLen = strlenW(lpszRes) + 1;
2366 if (dwDestLen >= dwResLen + 1)
2368 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2369 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2370 hRet = S_OK;
2374 MLFreeLibrary(hMod);
2377 return hRet;