dplayx: Tests for checking the behaviour of groups in a p2p session.
[wine/gsoc_dplay.git] / dlls / shlwapi / url.c
blob18ebc41d928ace40421e5d7b03c2195a81675573
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',':'};
322 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
324 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
325 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
327 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
328 return E_INVALIDARG;
330 if(!*pszUrl) {
331 *pszCanonicalized = 0;
332 return S_OK;
335 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
336 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
337 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
339 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
340 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
341 slash = '\\';
344 * state =
345 * 0 initial 1,3
346 * 1 have 2[+] alnum 2,3
347 * 2 have scheme (found :) 4,6,3
348 * 3 failed (no location)
349 * 4 have // 5,3
350 * 5 have 1[+] alnum 6,3
351 * 6 have location (found /) save root location
354 wk1 = (LPWSTR)pszUrl;
355 wk2 = lpszUrlCpy;
356 state = 0;
358 if(pszUrl[1] == ':') { /* Assume path */
359 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
361 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
362 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
363 if (dwFlags & URL_FILE_USE_PATHURL)
365 slash = '\\';
366 --wk2;
368 else
369 dwFlags |= URL_ESCAPE_UNSAFE;
370 state = 5;
373 while (*wk1) {
374 switch (state) {
375 case 0:
376 if (!isalnumW(*wk1)) {state = 3; break;}
377 *wk2++ = *wk1++;
378 if (!isalnumW(*wk1)) {state = 3; break;}
379 *wk2++ = *wk1++;
380 state = 1;
381 break;
382 case 1:
383 *wk2++ = *wk1;
384 if (*wk1++ == ':') state = 2;
385 break;
386 case 2:
387 *wk2++ = *wk1++;
388 if (*wk1 != '/') {state = 6; break;}
389 *wk2++ = *wk1++;
390 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
391 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
392 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
393 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
394 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 != '-') && (*wk1 != '.') && (*wk1 != ':'))
415 {state = 3; break;}
416 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
417 *wk2++ = *wk1++;
418 state = 5;
419 if (!*wk1)
420 *wk2++ = slash;
421 break;
422 case 5:
423 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
424 while(*wk1 == '/' || *wk1 == '\\') {
425 *wk2++ = slash;
426 wk1++;
428 state = 6;
429 break;
430 case 6:
431 if(dwFlags & URL_DONT_SIMPLIFY) {
432 state = 3;
433 break;
436 /* Now at root location, cannot back up any more. */
437 /* "root" will point at the '/' */
439 root = wk2-1;
440 while (*wk1) {
441 mp = strchrW(wk1, '/');
442 mp2 = strchrW(wk1, '\\');
443 if(mp2 && (!mp || mp2 < mp))
444 mp = mp2;
445 if (!mp) {
446 nWkLen = strlenW(wk1);
447 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
448 wk1 += nWkLen;
449 wk2 += nWkLen;
450 continue;
452 nLen = mp - wk1;
453 if(nLen) {
454 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
455 wk2 += nLen;
456 wk1 += nLen;
458 *wk2++ = slash;
459 wk1++;
461 if (*wk1 == '.') {
462 TRACE("found '/.'\n");
463 if (wk1[1] == '/' || wk1[1] == '\\') {
464 /* case of /./ -> skip the ./ */
465 wk1 += 2;
467 else if (wk1[1] == '.') {
468 /* found /.. look for next / */
469 TRACE("found '/..'\n");
470 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
471 || wk1[2] == '#' || !wk1[2]) {
472 /* case /../ -> need to backup wk2 */
473 TRACE("found '/../'\n");
474 *(wk2-1) = '\0'; /* set end of string */
475 mp = strrchrW(root, slash);
476 if (mp && (mp >= root)) {
477 /* found valid backup point */
478 wk2 = mp + 1;
479 if(wk1[2] != '/' && wk1[2] != '\\')
480 wk1 += 2;
481 else
482 wk1 += 3;
484 else {
485 /* did not find point, restore '/' */
486 *(wk2-1) = slash;
492 *wk2 = '\0';
493 break;
494 default:
495 FIXME("how did we get here - state=%d\n", state);
496 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
497 return E_INVALIDARG;
499 *wk2 = '\0';
500 TRACE("Simplified, orig <%s>, simple <%s>\n",
501 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
503 nLen = lstrlenW(lpszUrlCpy);
504 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
505 lpszUrlCpy[--nLen]=0;
507 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
508 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
510 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
511 URL_ESCAPE_SPACES_ONLY |
512 URL_ESCAPE_PERCENT |
513 URL_DONT_ESCAPE_EXTRA_INFO |
514 URL_ESCAPE_SEGMENT_ONLY ))) {
515 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
516 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
517 EscapeFlags);
518 } else { /* No escaping needed, just copy the string */
519 nLen = lstrlenW(lpszUrlCpy);
520 if(nLen < *pcchCanonicalized)
521 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
522 else {
523 hr = E_POINTER;
524 nLen++;
526 *pcchCanonicalized = nLen;
529 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
531 if (hr == S_OK)
532 TRACE("result %s\n", debugstr_w(pszCanonicalized));
534 return hr;
537 /*************************************************************************
538 * UrlCombineA [SHLWAPI.@]
540 * Combine two Urls.
542 * PARAMS
543 * pszBase [I] Base Url
544 * pszRelative [I] Url to combine with pszBase
545 * pszCombined [O] Destination for combined Url
546 * pcchCombined [O] Destination for length of pszCombined
547 * dwFlags [I] URL_ flags from "shlwapi.h"
549 * RETURNS
550 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
551 * contains its length.
552 * Failure: An HRESULT error code indicating the error.
554 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
555 LPSTR pszCombined, LPDWORD pcchCombined,
556 DWORD dwFlags)
558 LPWSTR base, relative, combined;
559 DWORD ret, len, len2;
561 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
562 debugstr_a(pszBase),debugstr_a(pszRelative),
563 pcchCombined?*pcchCombined:0,dwFlags);
565 if(!pszBase || !pszRelative || !pcchCombined)
566 return E_INVALIDARG;
568 base = HeapAlloc(GetProcessHeap(), 0,
569 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
570 relative = base + INTERNET_MAX_URL_LENGTH;
571 combined = relative + INTERNET_MAX_URL_LENGTH;
573 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
574 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
575 len = *pcchCombined;
577 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
578 if (ret != S_OK) {
579 *pcchCombined = len;
580 HeapFree(GetProcessHeap(), 0, base);
581 return ret;
584 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
585 if (len2 > *pcchCombined) {
586 *pcchCombined = len2;
587 HeapFree(GetProcessHeap(), 0, base);
588 return E_POINTER;
590 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
591 0, 0);
592 *pcchCombined = len2;
593 HeapFree(GetProcessHeap(), 0, base);
594 return S_OK;
597 /*************************************************************************
598 * UrlCombineW [SHLWAPI.@]
600 * See UrlCombineA.
602 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
603 LPWSTR pszCombined, LPDWORD pcchCombined,
604 DWORD dwFlags)
606 PARSEDURLW base, relative;
607 DWORD myflags, sizeloc = 0;
608 DWORD len, res1, res2, process_case = 0;
609 LPWSTR work, preliminary, mbase, mrelative;
610 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
611 static const WCHAR single_slash[] = {'/','\0'};
612 HRESULT ret;
614 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
615 debugstr_w(pszBase),debugstr_w(pszRelative),
616 pcchCombined?*pcchCombined:0,dwFlags);
618 if(!pszBase || !pszRelative || !pcchCombined)
619 return E_INVALIDARG;
621 base.cbSize = sizeof(base);
622 relative.cbSize = sizeof(relative);
624 /* Get space for duplicates of the input and the output */
625 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
626 sizeof(WCHAR));
627 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
628 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
629 *preliminary = '\0';
631 /* Canonicalize the base input prior to looking for the scheme */
632 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
633 len = INTERNET_MAX_URL_LENGTH;
634 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
636 /* Canonicalize the relative input prior to looking for the scheme */
637 len = INTERNET_MAX_URL_LENGTH;
638 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
640 /* See if the base has a scheme */
641 res1 = ParseURLW(mbase, &base);
642 if (res1) {
643 /* if pszBase has no scheme, then return pszRelative */
644 TRACE("no scheme detected in Base\n");
645 process_case = 1;
647 else do {
648 /* mk is a special case */
649 if(base.nScheme == URL_SCHEME_MK) {
650 static const WCHAR wsz[] = {':',':',0};
652 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
653 if(ptr) {
654 int delta;
656 ptr += 2;
657 delta = ptr-base.pszSuffix;
658 base.cchProtocol += delta;
659 base.pszSuffix += delta;
660 base.cchSuffix -= delta;
662 }else {
663 /* get size of location field (if it exists) */
664 work = (LPWSTR)base.pszSuffix;
665 sizeloc = 0;
666 if (*work++ == '/') {
667 if (*work++ == '/') {
668 /* At this point have start of location and
669 * it ends at next '/' or end of string.
671 while(*work && (*work != '/')) work++;
672 sizeloc = (DWORD)(work - base.pszSuffix);
677 /* Change .sizep2 to not have the last leaf in it,
678 * Note: we need to start after the location (if it exists)
680 work = strrchrW((base.pszSuffix+sizeloc), '/');
681 if (work) {
682 len = (DWORD)(work - base.pszSuffix + 1);
683 base.cchSuffix = len;
687 * At this point:
688 * .pszSuffix points to location (starting with '//')
689 * .cchSuffix length of location (above) and rest less the last
690 * leaf (if any)
691 * sizeloc length of location (above) up to but not including
692 * the last '/'
695 res2 = ParseURLW(mrelative, &relative);
696 if (res2) {
697 /* no scheme in pszRelative */
698 TRACE("no scheme detected in Relative\n");
699 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
700 relative.cchSuffix = strlenW(mrelative);
701 if (*pszRelative == ':') {
702 /* case that is either left alone or uses pszBase */
703 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
704 process_case = 5;
705 break;
707 process_case = 1;
708 break;
710 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
711 /* case that becomes "file:///" */
712 strcpyW(preliminary, myfilestr);
713 process_case = 1;
714 break;
716 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
717 /* pszRelative has location and rest */
718 process_case = 3;
719 break;
721 if (*mrelative == '/') {
722 /* case where pszRelative is root to location */
723 process_case = 4;
724 break;
726 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
727 break;
730 /* handle cases where pszRelative has scheme */
731 if ((base.cchProtocol == relative.cchProtocol) &&
732 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
734 /* since the schemes are the same */
735 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
736 /* case where pszRelative replaces location and following */
737 process_case = 3;
738 break;
740 if (*relative.pszSuffix == '/') {
741 /* case where pszRelative is root to location */
742 process_case = 4;
743 break;
745 /* replace either just location if base's location starts with a
746 * slash or otherwise everything */
747 process_case = (*base.pszSuffix == '/') ? 5 : 1;
748 break;
750 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
751 /* case where pszRelative replaces scheme, location,
752 * and following and handles PLUGGABLE
754 process_case = 2;
755 break;
757 process_case = 1;
758 break;
759 } while(FALSE); /* a little trick to allow easy exit from nested if's */
761 ret = S_OK;
762 switch (process_case) {
764 case 1: /*
765 * Return pszRelative appended to what ever is in pszCombined,
766 * (which may the string "file:///"
768 strcatW(preliminary, mrelative);
769 break;
771 case 2: /*
772 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
773 * and pszRelative starts with "//", then append a "/"
775 strcpyW(preliminary, mrelative);
776 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
777 URL_JustLocation(relative.pszSuffix))
778 strcatW(preliminary, single_slash);
779 break;
781 case 3: /*
782 * Return the pszBase scheme with pszRelative. Basically
783 * keeps the scheme and replaces the domain and following.
785 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
786 work = preliminary + base.cchProtocol + 1;
787 strcpyW(work, relative.pszSuffix);
788 break;
790 case 4: /*
791 * Return the pszBase scheme and location but everything
792 * after the location is pszRelative. (Replace document
793 * from root on.)
795 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
796 work = preliminary + base.cchProtocol + 1 + sizeloc;
797 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
798 *(work++) = '/';
799 strcpyW(work, relative.pszSuffix);
800 break;
802 case 5: /*
803 * Return the pszBase without its document (if any) and
804 * append pszRelative after its scheme.
806 memcpy(preliminary, base.pszProtocol,
807 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
808 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
809 if (*work++ != '/')
810 *(work++) = '/';
811 strcpyW(work, relative.pszSuffix);
812 break;
814 default:
815 FIXME("How did we get here????? process_case=%d\n", process_case);
816 ret = E_INVALIDARG;
819 if (ret == S_OK) {
820 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
821 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
822 if(SUCCEEDED(ret) && pszCombined) {
823 lstrcpyW(pszCombined, mrelative);
825 TRACE("return-%d len=%d, %s\n",
826 process_case, *pcchCombined, debugstr_w(pszCombined));
828 HeapFree(GetProcessHeap(), 0, preliminary);
829 return ret;
832 /*************************************************************************
833 * UrlEscapeA [SHLWAPI.@]
836 HRESULT WINAPI UrlEscapeA(
837 LPCSTR pszUrl,
838 LPSTR pszEscaped,
839 LPDWORD pcchEscaped,
840 DWORD dwFlags)
842 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
843 WCHAR *escapedW = bufW;
844 UNICODE_STRING urlW;
845 HRESULT ret;
846 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
848 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
849 return E_INVALIDARG;
851 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
852 return E_INVALIDARG;
853 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
854 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
855 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
857 if(ret == S_OK) {
858 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
859 if(*pcchEscaped > lenA) {
860 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
861 pszEscaped[lenA] = 0;
862 *pcchEscaped = lenA;
863 } else {
864 *pcchEscaped = lenA + 1;
865 ret = E_POINTER;
868 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
869 RtlFreeUnicodeString(&urlW);
870 return ret;
873 #define WINE_URL_BASH_AS_SLASH 0x01
874 #define WINE_URL_COLLAPSE_SLASHES 0x02
875 #define WINE_URL_ESCAPE_SLASH 0x04
876 #define WINE_URL_ESCAPE_HASH 0x08
877 #define WINE_URL_ESCAPE_QUESTION 0x10
878 #define WINE_URL_STOP_ON_HASH 0x20
879 #define WINE_URL_STOP_ON_QUESTION 0x40
881 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
884 if (isalnumW(ch))
885 return FALSE;
887 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
888 if(ch == ' ')
889 return TRUE;
890 else
891 return FALSE;
894 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
895 return TRUE;
897 if (ch <= 31 || ch >= 127)
898 return TRUE;
900 else {
901 switch (ch) {
902 case ' ':
903 case '<':
904 case '>':
905 case '\"':
906 case '{':
907 case '}':
908 case '|':
909 case '\\':
910 case '^':
911 case ']':
912 case '[':
913 case '`':
914 case '&':
915 return TRUE;
917 case '/':
918 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
919 return FALSE;
921 case '?':
922 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
923 return FALSE;
925 case '#':
926 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
927 return FALSE;
929 default:
930 return FALSE;
936 /*************************************************************************
937 * UrlEscapeW [SHLWAPI.@]
939 * Converts unsafe characters in a Url into escape sequences.
941 * PARAMS
942 * pszUrl [I] Url to modify
943 * pszEscaped [O] Destination for modified Url
944 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
945 * dwFlags [I] URL_ flags from "shlwapi.h"
947 * RETURNS
948 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
949 * contains its length.
950 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
951 * pcchEscaped is set to the required length.
953 * Converts unsafe characters into their escape sequences.
955 * NOTES
956 * - By default this function stops converting at the first '?' or
957 * '#' character.
958 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
959 * converted, but the conversion continues past a '?' or '#'.
960 * - Note that this function did not work well (or at all) in shlwapi version 4.
962 * BUGS
963 * Only the following flags are implemented:
964 *| URL_ESCAPE_SPACES_ONLY
965 *| URL_DONT_ESCAPE_EXTRA_INFO
966 *| URL_ESCAPE_SEGMENT_ONLY
967 *| URL_ESCAPE_PERCENT
969 HRESULT WINAPI UrlEscapeW(
970 LPCWSTR pszUrl,
971 LPWSTR pszEscaped,
972 LPDWORD pcchEscaped,
973 DWORD dwFlags)
975 LPCWSTR src;
976 DWORD needed = 0, ret;
977 BOOL stop_escaping = FALSE;
978 WCHAR next[5], *dst = pszEscaped;
979 INT len;
980 PARSEDURLW parsed_url;
981 DWORD int_flags;
982 DWORD slashes = 0;
983 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
985 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
986 pcchEscaped, dwFlags);
988 if(!pszUrl || !pcchEscaped)
989 return E_INVALIDARG;
991 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
992 URL_ESCAPE_SEGMENT_ONLY |
993 URL_DONT_ESCAPE_EXTRA_INFO |
994 URL_ESCAPE_PERCENT))
995 FIXME("Unimplemented flags: %08x\n", dwFlags);
997 /* fix up flags */
998 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
999 /* if SPACES_ONLY specified, reset the other controls */
1000 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1001 URL_ESCAPE_PERCENT |
1002 URL_ESCAPE_SEGMENT_ONLY);
1004 else
1005 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1006 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1009 int_flags = 0;
1010 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1011 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1012 } else {
1013 parsed_url.cbSize = sizeof(parsed_url);
1014 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1015 parsed_url.nScheme = URL_SCHEME_INVALID;
1017 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1019 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1020 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1022 switch(parsed_url.nScheme) {
1023 case URL_SCHEME_FILE:
1024 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1025 int_flags &= ~WINE_URL_STOP_ON_HASH;
1026 break;
1028 case URL_SCHEME_HTTP:
1029 case URL_SCHEME_HTTPS:
1030 int_flags |= WINE_URL_BASH_AS_SLASH;
1031 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1032 int_flags |= WINE_URL_ESCAPE_SLASH;
1033 break;
1035 case URL_SCHEME_MAILTO:
1036 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1037 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1038 break;
1040 case URL_SCHEME_INVALID:
1041 break;
1043 case URL_SCHEME_FTP:
1044 default:
1045 if(parsed_url.pszSuffix[0] != '/')
1046 int_flags |= WINE_URL_ESCAPE_SLASH;
1047 break;
1051 for(src = pszUrl; *src; ) {
1052 WCHAR cur = *src;
1053 len = 0;
1055 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1056 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1057 while(cur == '/' || cur == '\\') {
1058 slashes++;
1059 cur = *++src;
1061 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1062 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1063 src += localhost_len + 1;
1064 slashes = 3;
1067 switch(slashes) {
1068 case 1:
1069 case 3:
1070 next[0] = next[1] = next[2] = '/';
1071 len = 3;
1072 break;
1073 case 0:
1074 len = 0;
1075 break;
1076 default:
1077 next[0] = next[1] = '/';
1078 len = 2;
1079 break;
1082 if(len == 0) {
1084 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1085 stop_escaping = TRUE;
1087 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1088 stop_escaping = TRUE;
1090 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1092 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1093 next[0] = '%';
1094 next[1] = hexDigits[(cur >> 4) & 0xf];
1095 next[2] = hexDigits[cur & 0xf];
1096 len = 3;
1097 } else {
1098 next[0] = cur;
1099 len = 1;
1101 src++;
1104 if(needed + len <= *pcchEscaped) {
1105 memcpy(dst, next, len*sizeof(WCHAR));
1106 dst += len;
1108 needed += len;
1111 if(needed < *pcchEscaped) {
1112 *dst = '\0';
1113 ret = S_OK;
1114 } else {
1115 needed++; /* add one for the '\0' */
1116 ret = E_POINTER;
1118 *pcchEscaped = needed;
1119 return ret;
1123 /*************************************************************************
1124 * UrlUnescapeA [SHLWAPI.@]
1126 * Converts Url escape sequences back to ordinary characters.
1128 * PARAMS
1129 * pszUrl [I/O] Url to convert
1130 * pszUnescaped [O] Destination for converted Url
1131 * pcchUnescaped [I/O] Size of output string
1132 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1134 * RETURNS
1135 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1136 * dwFlags includes URL_ESCAPE_INPLACE.
1137 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1138 * this case pcchUnescaped is set to the size required.
1139 * NOTES
1140 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1141 * the first occurrence of either a '?' or '#' character.
1143 HRESULT WINAPI UrlUnescapeA(
1144 LPSTR pszUrl,
1145 LPSTR pszUnescaped,
1146 LPDWORD pcchUnescaped,
1147 DWORD dwFlags)
1149 char *dst, next;
1150 LPCSTR src;
1151 HRESULT ret;
1152 DWORD needed;
1153 BOOL stop_unescaping = FALSE;
1155 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1156 pcchUnescaped, dwFlags);
1158 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1159 return E_INVALIDARG;
1161 if(dwFlags & URL_UNESCAPE_INPLACE)
1162 dst = pszUrl;
1163 else
1164 dst = pszUnescaped;
1166 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1167 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1168 (*src == '#' || *src == '?')) {
1169 stop_unescaping = TRUE;
1170 next = *src;
1171 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1172 && stop_unescaping == FALSE) {
1173 INT ih;
1174 char buf[3];
1175 memcpy(buf, src + 1, 2);
1176 buf[2] = '\0';
1177 ih = strtol(buf, NULL, 16);
1178 next = (CHAR) ih;
1179 src += 2; /* Advance to end of escape */
1180 } else
1181 next = *src;
1183 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1184 *dst++ = next;
1187 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1188 *dst = '\0';
1189 ret = S_OK;
1190 } else {
1191 needed++; /* add one for the '\0' */
1192 ret = E_POINTER;
1194 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1195 *pcchUnescaped = needed;
1197 if (ret == S_OK) {
1198 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1199 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1202 return ret;
1205 /*************************************************************************
1206 * UrlUnescapeW [SHLWAPI.@]
1208 * See UrlUnescapeA.
1210 HRESULT WINAPI UrlUnescapeW(
1211 LPWSTR pszUrl,
1212 LPWSTR pszUnescaped,
1213 LPDWORD pcchUnescaped,
1214 DWORD dwFlags)
1216 WCHAR *dst, next;
1217 LPCWSTR src;
1218 HRESULT ret;
1219 DWORD needed;
1220 BOOL stop_unescaping = FALSE;
1222 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1223 pcchUnescaped, dwFlags);
1225 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1226 return E_INVALIDARG;
1228 if(dwFlags & URL_UNESCAPE_INPLACE)
1229 dst = pszUrl;
1230 else
1231 dst = pszUnescaped;
1233 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1234 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1235 (*src == '#' || *src == '?')) {
1236 stop_unescaping = TRUE;
1237 next = *src;
1238 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1239 && stop_unescaping == FALSE) {
1240 INT ih;
1241 WCHAR buf[5] = {'0','x',0};
1242 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1243 buf[4] = 0;
1244 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1245 next = (WCHAR) ih;
1246 src += 2; /* Advance to end of escape */
1247 } else
1248 next = *src;
1250 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1251 *dst++ = next;
1254 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1255 *dst = '\0';
1256 ret = S_OK;
1257 } else {
1258 needed++; /* add one for the '\0' */
1259 ret = E_POINTER;
1261 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1262 *pcchUnescaped = needed;
1264 if (ret == S_OK) {
1265 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1266 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1269 return ret;
1272 /*************************************************************************
1273 * UrlGetLocationA [SHLWAPI.@]
1275 * Get the location from a Url.
1277 * PARAMS
1278 * pszUrl [I] Url to get the location from
1280 * RETURNS
1281 * A pointer to the start of the location in pszUrl, or NULL if there is
1282 * no location.
1284 * NOTES
1285 * - MSDN erroneously states that "The location is the segment of the Url
1286 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1287 * stop at '?' and always return a NULL in this case.
1288 * - MSDN also erroneously states that "If a file URL has a query string,
1289 * the returned string is the query string". In all tested cases, if the
1290 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1291 *| Result Url
1292 *| ------ ---
1293 *| NULL file://aa/b/cd#hohoh
1294 *| #hohoh http://aa/b/cd#hohoh
1295 *| NULL fi://aa/b/cd#hohoh
1296 *| #hohoh ff://aa/b/cd#hohoh
1298 LPCSTR WINAPI UrlGetLocationA(
1299 LPCSTR pszUrl)
1301 PARSEDURLA base;
1302 DWORD res1;
1304 base.cbSize = sizeof(base);
1305 res1 = ParseURLA(pszUrl, &base);
1306 if (res1) return NULL; /* invalid scheme */
1308 /* if scheme is file: then never return pointer */
1309 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1311 /* Look for '#' and return its addr */
1312 return strchr(base.pszSuffix, '#');
1315 /*************************************************************************
1316 * UrlGetLocationW [SHLWAPI.@]
1318 * See UrlGetLocationA.
1320 LPCWSTR WINAPI UrlGetLocationW(
1321 LPCWSTR pszUrl)
1323 PARSEDURLW base;
1324 DWORD res1;
1326 base.cbSize = sizeof(base);
1327 res1 = ParseURLW(pszUrl, &base);
1328 if (res1) return NULL; /* invalid scheme */
1330 /* if scheme is file: then never return pointer */
1331 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1333 /* Look for '#' and return its addr */
1334 return strchrW(base.pszSuffix, '#');
1337 /*************************************************************************
1338 * UrlCompareA [SHLWAPI.@]
1340 * Compare two Urls.
1342 * PARAMS
1343 * pszUrl1 [I] First Url to compare
1344 * pszUrl2 [I] Url to compare to pszUrl1
1345 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1347 * RETURNS
1348 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1349 * than, equal to, or less than pszUrl1 respectively.
1351 INT WINAPI UrlCompareA(
1352 LPCSTR pszUrl1,
1353 LPCSTR pszUrl2,
1354 BOOL fIgnoreSlash)
1356 INT ret, len, len1, len2;
1358 if (!fIgnoreSlash)
1359 return strcmp(pszUrl1, pszUrl2);
1360 len1 = strlen(pszUrl1);
1361 if (pszUrl1[len1-1] == '/') len1--;
1362 len2 = strlen(pszUrl2);
1363 if (pszUrl2[len2-1] == '/') len2--;
1364 if (len1 == len2)
1365 return strncmp(pszUrl1, pszUrl2, len1);
1366 len = min(len1, len2);
1367 ret = strncmp(pszUrl1, pszUrl2, len);
1368 if (ret) return ret;
1369 if (len1 > len2) return 1;
1370 return -1;
1373 /*************************************************************************
1374 * UrlCompareW [SHLWAPI.@]
1376 * See UrlCompareA.
1378 INT WINAPI UrlCompareW(
1379 LPCWSTR pszUrl1,
1380 LPCWSTR pszUrl2,
1381 BOOL fIgnoreSlash)
1383 INT ret;
1384 size_t len, len1, len2;
1386 if (!fIgnoreSlash)
1387 return strcmpW(pszUrl1, pszUrl2);
1388 len1 = strlenW(pszUrl1);
1389 if (pszUrl1[len1-1] == '/') len1--;
1390 len2 = strlenW(pszUrl2);
1391 if (pszUrl2[len2-1] == '/') len2--;
1392 if (len1 == len2)
1393 return strncmpW(pszUrl1, pszUrl2, len1);
1394 len = min(len1, len2);
1395 ret = strncmpW(pszUrl1, pszUrl2, len);
1396 if (ret) return ret;
1397 if (len1 > len2) return 1;
1398 return -1;
1401 /*************************************************************************
1402 * HashData [SHLWAPI.@]
1404 * Hash an input block into a variable sized digest.
1406 * PARAMS
1407 * lpSrc [I] Input block
1408 * nSrcLen [I] Length of lpSrc
1409 * lpDest [I] Output for hash digest
1410 * nDestLen [I] Length of lpDest
1412 * RETURNS
1413 * Success: TRUE. lpDest is filled with the computed hash value.
1414 * Failure: FALSE, if any argument is invalid.
1416 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1417 unsigned char *lpDest, DWORD nDestLen)
1419 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1421 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1422 IsBadWritePtr(lpDest, nDestLen))
1423 return E_INVALIDARG;
1425 while (destCount >= 0)
1427 lpDest[destCount] = (destCount & 0xff);
1428 destCount--;
1431 while (srcCount >= 0)
1433 destCount = nDestLen - 1;
1434 while (destCount >= 0)
1436 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1437 destCount--;
1439 srcCount--;
1441 return S_OK;
1444 /*************************************************************************
1445 * UrlHashA [SHLWAPI.@]
1447 * Produce a Hash from a Url.
1449 * PARAMS
1450 * pszUrl [I] Url to hash
1451 * lpDest [O] Destinationh for hash
1452 * nDestLen [I] Length of lpDest
1454 * RETURNS
1455 * Success: S_OK. lpDest is filled with the computed hash value.
1456 * Failure: E_INVALIDARG, if any argument is invalid.
1458 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1460 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1461 return E_INVALIDARG;
1463 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1464 return S_OK;
1467 /*************************************************************************
1468 * UrlHashW [SHLWAPI.@]
1470 * See UrlHashA.
1472 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1474 char szUrl[MAX_PATH];
1476 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1478 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1479 return E_INVALIDARG;
1481 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1482 * return the same digests for the same URL.
1484 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1485 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1486 return S_OK;
1489 /*************************************************************************
1490 * UrlApplySchemeA [SHLWAPI.@]
1492 * Apply a scheme to a Url.
1494 * PARAMS
1495 * pszIn [I] Url to apply scheme to
1496 * pszOut [O] Destination for modified Url
1497 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1498 * dwFlags [I] URL_ flags from "shlwapi.h"
1500 * RETURNS
1501 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1502 * Failure: An HRESULT error code describing the error.
1504 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1506 LPWSTR in, out;
1507 DWORD ret, len, len2;
1509 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1510 debugstr_a(pszIn), *pcchOut, dwFlags);
1512 in = HeapAlloc(GetProcessHeap(), 0,
1513 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1514 out = in + INTERNET_MAX_URL_LENGTH;
1516 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1517 len = INTERNET_MAX_URL_LENGTH;
1519 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1520 if ((ret != S_OK) && (ret != S_FALSE)) {
1521 HeapFree(GetProcessHeap(), 0, in);
1522 return ret;
1525 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1526 if (len2 > *pcchOut) {
1527 *pcchOut = len2;
1528 HeapFree(GetProcessHeap(), 0, in);
1529 return E_POINTER;
1531 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1532 *pcchOut = len2;
1533 HeapFree(GetProcessHeap(), 0, in);
1534 return ret;
1537 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1539 HKEY newkey;
1540 BOOL j;
1541 INT index;
1542 DWORD value_len, data_len, dwType, i;
1543 WCHAR reg_path[MAX_PATH];
1544 WCHAR value[MAX_PATH], data[MAX_PATH];
1545 WCHAR Wxx, Wyy;
1547 MultiByteToWideChar(0, 0,
1548 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1549 -1, reg_path, MAX_PATH);
1550 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1551 index = 0;
1552 while(value_len = data_len = MAX_PATH,
1553 RegEnumValueW(newkey, index, value, &value_len,
1554 0, &dwType, (LPVOID)data, &data_len) == 0) {
1555 TRACE("guess %d %s is %s\n",
1556 index, debugstr_w(value), debugstr_w(data));
1558 j = FALSE;
1559 for(i=0; i<value_len; i++) {
1560 Wxx = pszIn[i];
1561 Wyy = value[i];
1562 /* remember that TRUE is not-equal */
1563 j = ChrCmpIW(Wxx, Wyy);
1564 if (j) break;
1566 if ((i == value_len) && !j) {
1567 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1568 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1569 RegCloseKey(newkey);
1570 return E_POINTER;
1572 strcpyW(pszOut, data);
1573 strcatW(pszOut, pszIn);
1574 *pcchOut = strlenW(pszOut);
1575 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1576 RegCloseKey(newkey);
1577 return S_OK;
1579 index++;
1581 RegCloseKey(newkey);
1582 return -1;
1585 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1587 HKEY newkey;
1588 DWORD data_len, dwType;
1589 WCHAR value[MAX_PATH], data[MAX_PATH];
1591 static const WCHAR prefix_keyW[] =
1592 {'S','o','f','t','w','a','r','e',
1593 '\\','M','i','c','r','o','s','o','f','t',
1594 '\\','W','i','n','d','o','w','s',
1595 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1596 '\\','U','R','L',
1597 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1599 /* get and prepend default */
1600 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1601 data_len = MAX_PATH;
1602 value[0] = '@';
1603 value[1] = '\0';
1604 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1605 RegCloseKey(newkey);
1606 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1607 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1608 return E_POINTER;
1610 strcpyW(pszOut, data);
1611 strcatW(pszOut, pszIn);
1612 *pcchOut = strlenW(pszOut);
1613 TRACE("used default %s\n", debugstr_w(pszOut));
1614 return S_OK;
1617 /*************************************************************************
1618 * UrlApplySchemeW [SHLWAPI.@]
1620 * See UrlApplySchemeA.
1622 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1624 PARSEDURLW in_scheme;
1625 DWORD res1;
1626 HRESULT ret;
1628 TRACE("(in %s, out size %d, flags %08x)\n",
1629 debugstr_w(pszIn), *pcchOut, dwFlags);
1631 if (dwFlags & URL_APPLY_GUESSFILE) {
1632 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1633 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1634 strcpyW(pszOut, pszIn);
1635 *pcchOut = strlenW(pszOut);
1636 return S_FALSE;
1639 in_scheme.cbSize = sizeof(in_scheme);
1640 /* See if the base has a scheme */
1641 res1 = ParseURLW(pszIn, &in_scheme);
1642 if (res1) {
1643 /* no scheme in input, need to see if we need to guess */
1644 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1645 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1646 return ret;
1649 else {
1650 /* we have a scheme, see if valid (known scheme) */
1651 if (in_scheme.nScheme) {
1652 /* have valid scheme, so just copy and exit */
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("valid scheme, returning copy\n");
1660 return S_OK;
1664 /* If we are here, then either invalid scheme,
1665 * or no scheme and can't/failed guess.
1667 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1668 ((res1 != 0)) ) &&
1669 (dwFlags & URL_APPLY_DEFAULT)) {
1670 /* find and apply default scheme */
1671 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1674 /* just copy and give proper return code */
1675 if (strlenW(pszIn) + 1 > *pcchOut) {
1676 *pcchOut = strlenW(pszIn) + 1;
1677 return E_POINTER;
1679 strcpyW(pszOut, pszIn);
1680 *pcchOut = strlenW(pszOut);
1681 TRACE("returning copy, left alone\n");
1682 return S_FALSE;
1685 /*************************************************************************
1686 * UrlIsA [SHLWAPI.@]
1688 * Determine if a Url is of a certain class.
1690 * PARAMS
1691 * pszUrl [I] Url to check
1692 * Urlis [I] URLIS_ constant from "shlwapi.h"
1694 * RETURNS
1695 * TRUE if pszUrl belongs to the class type in Urlis.
1696 * FALSE Otherwise.
1698 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1700 PARSEDURLA base;
1701 DWORD res1;
1702 LPCSTR last;
1704 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1706 switch (Urlis) {
1708 case URLIS_OPAQUE:
1709 base.cbSize = sizeof(base);
1710 res1 = ParseURLA(pszUrl, &base);
1711 if (res1) return FALSE; /* invalid scheme */
1712 switch (base.nScheme)
1714 case URL_SCHEME_MAILTO:
1715 case URL_SCHEME_SHELL:
1716 case URL_SCHEME_JAVASCRIPT:
1717 case URL_SCHEME_VBSCRIPT:
1718 case URL_SCHEME_ABOUT:
1719 return TRUE;
1721 return FALSE;
1723 case URLIS_FILEURL:
1724 return !StrCmpNA("file:", pszUrl, 5);
1726 case URLIS_DIRECTORY:
1727 last = pszUrl + strlen(pszUrl) - 1;
1728 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1730 case URLIS_URL:
1731 return PathIsURLA(pszUrl);
1733 case URLIS_NOHISTORY:
1734 case URLIS_APPLIABLE:
1735 case URLIS_HASQUERY:
1736 default:
1737 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1739 return FALSE;
1742 /*************************************************************************
1743 * UrlIsW [SHLWAPI.@]
1745 * See UrlIsA.
1747 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1749 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1750 PARSEDURLW base;
1751 DWORD res1;
1752 LPCWSTR last;
1754 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1756 switch (Urlis) {
1758 case URLIS_OPAQUE:
1759 base.cbSize = sizeof(base);
1760 res1 = ParseURLW(pszUrl, &base);
1761 if (res1) return FALSE; /* invalid scheme */
1762 switch (base.nScheme)
1764 case URL_SCHEME_MAILTO:
1765 case URL_SCHEME_SHELL:
1766 case URL_SCHEME_JAVASCRIPT:
1767 case URL_SCHEME_VBSCRIPT:
1768 case URL_SCHEME_ABOUT:
1769 return TRUE;
1771 return FALSE;
1773 case URLIS_FILEURL:
1774 return !strncmpW(stemp, pszUrl, 5);
1776 case URLIS_DIRECTORY:
1777 last = pszUrl + strlenW(pszUrl) - 1;
1778 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1780 case URLIS_URL:
1781 return PathIsURLW(pszUrl);
1783 case URLIS_NOHISTORY:
1784 case URLIS_APPLIABLE:
1785 case URLIS_HASQUERY:
1786 default:
1787 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1789 return FALSE;
1792 /*************************************************************************
1793 * UrlIsNoHistoryA [SHLWAPI.@]
1795 * Determine if a Url should not be stored in the users history list.
1797 * PARAMS
1798 * pszUrl [I] Url to check
1800 * RETURNS
1801 * TRUE, if pszUrl should be excluded from the history list,
1802 * FALSE otherwise.
1804 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1806 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1809 /*************************************************************************
1810 * UrlIsNoHistoryW [SHLWAPI.@]
1812 * See UrlIsNoHistoryA.
1814 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1816 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1819 /*************************************************************************
1820 * UrlIsOpaqueA [SHLWAPI.@]
1822 * Determine if a Url is opaque.
1824 * PARAMS
1825 * pszUrl [I] Url to check
1827 * RETURNS
1828 * TRUE if pszUrl is opaque,
1829 * FALSE Otherwise.
1831 * NOTES
1832 * An opaque Url is one that does not start with "<protocol>://".
1834 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1836 return UrlIsA(pszUrl, URLIS_OPAQUE);
1839 /*************************************************************************
1840 * UrlIsOpaqueW [SHLWAPI.@]
1842 * See UrlIsOpaqueA.
1844 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1846 return UrlIsW(pszUrl, URLIS_OPAQUE);
1849 /*************************************************************************
1850 * Scans for characters of type "type" and when not matching found,
1851 * returns pointer to it and length in size.
1853 * Characters tested based on RFC 1738
1855 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1857 static DWORD alwayszero = 0;
1858 BOOL cont = TRUE;
1860 *size = 0;
1862 switch(type){
1864 case SCHEME:
1865 while (cont) {
1866 if ( (islowerW(*start) && isalphaW(*start)) ||
1867 isdigitW(*start) ||
1868 (*start == '+') ||
1869 (*start == '-') ||
1870 (*start == '.')) {
1871 start++;
1872 (*size)++;
1874 else
1875 cont = FALSE;
1877 break;
1879 case USERPASS:
1880 while (cont) {
1881 if ( isalphaW(*start) ||
1882 isdigitW(*start) ||
1883 /* user/password only characters */
1884 (*start == ';') ||
1885 (*start == '?') ||
1886 (*start == '&') ||
1887 (*start == '=') ||
1888 /* *extra* characters */
1889 (*start == '!') ||
1890 (*start == '*') ||
1891 (*start == '\'') ||
1892 (*start == '(') ||
1893 (*start == ')') ||
1894 (*start == ',') ||
1895 /* *safe* characters */
1896 (*start == '$') ||
1897 (*start == '_') ||
1898 (*start == '+') ||
1899 (*start == '-') ||
1900 (*start == '.')) {
1901 start++;
1902 (*size)++;
1903 } else if (*start == '%') {
1904 if (isxdigitW(*(start+1)) &&
1905 isxdigitW(*(start+2))) {
1906 start += 3;
1907 *size += 3;
1908 } else
1909 cont = FALSE;
1910 } else
1911 cont = FALSE;
1913 break;
1915 case PORT:
1916 while (cont) {
1917 if (isdigitW(*start)) {
1918 start++;
1919 (*size)++;
1921 else
1922 cont = FALSE;
1924 break;
1926 case HOST:
1927 while (cont) {
1928 if (isalnumW(*start) ||
1929 (*start == '-') ||
1930 (*start == '.') ) {
1931 start++;
1932 (*size)++;
1934 else
1935 cont = FALSE;
1937 break;
1938 default:
1939 FIXME("unknown type %d\n", type);
1940 return (LPWSTR)&alwayszero;
1942 /* TRACE("scanned %d characters next char %p<%c>\n",
1943 *size, start, *start); */
1944 return start;
1947 /*************************************************************************
1948 * Attempt to parse URL into pieces.
1950 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1952 LPCWSTR work;
1954 memset(pl, 0, sizeof(WINE_PARSE_URL));
1955 pl->pScheme = pszUrl;
1956 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1957 if (!*work || (*work != ':')) goto ErrorExit;
1958 work++;
1959 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1960 pl->pUserName = work + 2;
1961 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1962 if (*work == ':' ) {
1963 /* parse password */
1964 work++;
1965 pl->pPassword = work;
1966 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1967 if (*work != '@') {
1968 /* what we just parsed must be the hostname and port
1969 * so reset pointers and clear then let it parse */
1970 pl->szUserName = pl->szPassword = 0;
1971 work = pl->pUserName - 1;
1972 pl->pUserName = pl->pPassword = 0;
1974 } else if (*work == '@') {
1975 /* no password */
1976 pl->szPassword = 0;
1977 pl->pPassword = 0;
1978 } else if (!*work || (*work == '/') || (*work == '.')) {
1979 /* what was parsed was hostname, so reset pointers and let it parse */
1980 pl->szUserName = pl->szPassword = 0;
1981 work = pl->pUserName - 1;
1982 pl->pUserName = pl->pPassword = 0;
1983 } else goto ErrorExit;
1985 /* now start parsing hostname or hostnumber */
1986 work++;
1987 pl->pHostName = work;
1988 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1989 if (*work == ':') {
1990 /* parse port */
1991 work++;
1992 pl->pPort = work;
1993 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1995 if (*work == '/') {
1996 /* see if query string */
1997 pl->pQuery = strchrW(work, '?');
1998 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2000 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2001 pl->pScheme, pl->szScheme,
2002 pl->pUserName, pl->szUserName,
2003 pl->pPassword, pl->szPassword,
2004 pl->pHostName, pl->szHostName,
2005 pl->pPort, pl->szPort,
2006 pl->pQuery, pl->szQuery);
2007 return S_OK;
2008 ErrorExit:
2009 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2010 return E_INVALIDARG;
2013 /*************************************************************************
2014 * UrlGetPartA [SHLWAPI.@]
2016 * Retrieve part of a Url.
2018 * PARAMS
2019 * pszIn [I] Url to parse
2020 * pszOut [O] Destination for part of pszIn requested
2021 * pcchOut [I] Size of pszOut
2022 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2023 * needed size of pszOut INCLUDING '\0'.
2024 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2025 * dwFlags [I] URL_ flags from "shlwapi.h"
2027 * RETURNS
2028 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2029 * Failure: An HRESULT error code describing the error.
2031 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2032 DWORD dwPart, DWORD dwFlags)
2034 LPWSTR in, out;
2035 DWORD ret, len, len2;
2037 in = HeapAlloc(GetProcessHeap(), 0,
2038 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2039 out = in + INTERNET_MAX_URL_LENGTH;
2041 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2043 len = INTERNET_MAX_URL_LENGTH;
2044 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2046 if (ret != S_OK) {
2047 HeapFree(GetProcessHeap(), 0, in);
2048 return ret;
2051 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2052 if (len2 > *pcchOut) {
2053 *pcchOut = len2;
2054 HeapFree(GetProcessHeap(), 0, in);
2055 return E_POINTER;
2057 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2058 *pcchOut = len2;
2059 HeapFree(GetProcessHeap(), 0, in);
2060 return S_OK;
2063 /*************************************************************************
2064 * UrlGetPartW [SHLWAPI.@]
2066 * See UrlGetPartA.
2068 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2069 DWORD dwPart, DWORD dwFlags)
2071 WINE_PARSE_URL pl;
2072 HRESULT ret;
2073 DWORD size, schsize;
2074 LPCWSTR addr, schaddr;
2076 TRACE("(%s %p %p(%d) %08x %08x)\n",
2077 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2079 ret = URL_ParseUrl(pszIn, &pl);
2080 if (ret == S_OK) {
2081 schaddr = pl.pScheme;
2082 schsize = pl.szScheme;
2084 switch (dwPart) {
2085 case URL_PART_SCHEME:
2086 if (!pl.szScheme) return E_INVALIDARG;
2087 addr = pl.pScheme;
2088 size = pl.szScheme;
2089 break;
2091 case URL_PART_HOSTNAME:
2092 if (!pl.szHostName) return E_INVALIDARG;
2093 addr = pl.pHostName;
2094 size = pl.szHostName;
2095 break;
2097 case URL_PART_USERNAME:
2098 if (!pl.szUserName) return E_INVALIDARG;
2099 addr = pl.pUserName;
2100 size = pl.szUserName;
2101 break;
2103 case URL_PART_PASSWORD:
2104 if (!pl.szPassword) return E_INVALIDARG;
2105 addr = pl.pPassword;
2106 size = pl.szPassword;
2107 break;
2109 case URL_PART_PORT:
2110 if (!pl.szPort) return E_INVALIDARG;
2111 addr = pl.pPort;
2112 size = pl.szPort;
2113 break;
2115 case URL_PART_QUERY:
2116 if (!pl.szQuery) return E_INVALIDARG;
2117 addr = pl.pQuery;
2118 size = pl.szQuery;
2119 break;
2121 default:
2122 return E_INVALIDARG;
2125 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2126 if (*pcchOut < schsize + size + 2) {
2127 *pcchOut = schsize + size + 2;
2128 return E_POINTER;
2130 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2131 pszOut[schsize] = ':';
2132 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2133 pszOut[schsize+1+size] = 0;
2134 *pcchOut = schsize + 1 + size;
2136 else {
2137 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2138 memcpy(pszOut, addr, size*sizeof(WCHAR));
2139 pszOut[size] = 0;
2140 *pcchOut = size;
2142 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2144 return ret;
2147 /*************************************************************************
2148 * PathIsURLA [SHLWAPI.@]
2150 * Check if the given path is a Url.
2152 * PARAMS
2153 * lpszPath [I] Path to check.
2155 * RETURNS
2156 * TRUE if lpszPath is a Url.
2157 * FALSE if lpszPath is NULL or not a Url.
2159 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2161 PARSEDURLA base;
2163 TRACE("%s\n", debugstr_a(lpstrPath));
2165 if (!lpstrPath || !*lpstrPath) return FALSE;
2167 /* get protocol */
2168 base.cbSize = sizeof(base);
2169 ParseURLA(lpstrPath, &base);
2170 return (base.nScheme != URL_SCHEME_INVALID);
2173 /*************************************************************************
2174 * PathIsURLW [SHLWAPI.@]
2176 * See PathIsURLA.
2178 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2180 PARSEDURLW base;
2182 TRACE("%s\n", debugstr_w(lpstrPath));
2184 if (!lpstrPath || !*lpstrPath) return FALSE;
2186 /* get protocol */
2187 base.cbSize = sizeof(base);
2188 ParseURLW(lpstrPath, &base);
2189 return (base.nScheme != URL_SCHEME_INVALID);
2192 /*************************************************************************
2193 * UrlCreateFromPathA [SHLWAPI.@]
2195 * See UrlCreateFromPathW
2197 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2199 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2200 WCHAR *urlW = bufW;
2201 UNICODE_STRING pathW;
2202 HRESULT ret;
2203 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2205 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2206 return E_INVALIDARG;
2207 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2208 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2209 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2211 if(ret == S_OK || ret == S_FALSE) {
2212 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2213 if(*pcchUrl > lenA) {
2214 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2215 pszUrl[lenA] = 0;
2216 *pcchUrl = lenA;
2217 } else {
2218 *pcchUrl = lenA + 1;
2219 ret = E_POINTER;
2222 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2223 RtlFreeUnicodeString(&pathW);
2224 return ret;
2227 /*************************************************************************
2228 * UrlCreateFromPathW [SHLWAPI.@]
2230 * Create a Url from a file path.
2232 * PARAMS
2233 * pszPath [I] Path to convert
2234 * pszUrl [O] Destination for the converted Url
2235 * pcchUrl [I/O] Length of pszUrl
2236 * dwReserved [I] Reserved, must be 0
2238 * RETURNS
2239 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2240 * Failure: An HRESULT error code.
2242 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2244 DWORD needed;
2245 HRESULT ret;
2246 WCHAR *pszNewUrl;
2247 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2248 WCHAR three_slashesW[] = {'/','/','/',0};
2249 PARSEDURLW parsed_url;
2251 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2253 /* Validate arguments */
2254 if (dwReserved != 0)
2255 return E_INVALIDARG;
2256 if (!pszUrl || !pcchUrl)
2257 return E_INVALIDARG;
2260 parsed_url.cbSize = sizeof(parsed_url);
2261 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2262 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2263 needed = strlenW(pszPath);
2264 if (needed >= *pcchUrl) {
2265 *pcchUrl = needed + 1;
2266 return E_POINTER;
2267 } else {
2268 *pcchUrl = needed;
2269 strcpyW(pszUrl, pszPath);
2270 return S_FALSE;
2275 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2276 strcpyW(pszNewUrl, file_colonW);
2277 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2278 strcatW(pszNewUrl, three_slashesW);
2279 strcatW(pszNewUrl, pszPath);
2280 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2282 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2283 return ret;
2286 /*************************************************************************
2287 * SHAutoComplete [SHLWAPI.@]
2289 * Enable auto-completion for an edit control.
2291 * PARAMS
2292 * hwndEdit [I] Handle of control to enable auto-completion for
2293 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2295 * RETURNS
2296 * Success: S_OK. Auto-completion is enabled for the control.
2297 * Failure: An HRESULT error code indicating the error.
2299 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2301 FIXME("SHAutoComplete stub\n");
2302 return S_FALSE;
2305 /*************************************************************************
2306 * MLBuildResURLA [SHLWAPI.405]
2308 * Create a Url pointing to a resource in a module.
2310 * PARAMS
2311 * lpszLibName [I] Name of the module containing the resource
2312 * hMod [I] Callers module handle
2313 * dwFlags [I] Undocumented flags for loading the module
2314 * lpszRes [I] Resource name
2315 * lpszDest [O] Destination for resulting Url
2316 * dwDestLen [I] Length of lpszDest
2318 * RETURNS
2319 * Success: S_OK. lpszDest contains the resource Url.
2320 * Failure: E_INVALIDARG, if any argument is invalid, or
2321 * E_FAIL if dwDestLen is too small.
2323 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2324 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2326 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2327 HRESULT hRet;
2329 if (lpszLibName)
2330 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2332 if (lpszRes)
2333 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2335 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2336 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2338 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2339 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2340 if (SUCCEEDED(hRet) && lpszDest)
2341 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2343 return hRet;
2346 /*************************************************************************
2347 * MLBuildResURLA [SHLWAPI.406]
2349 * See MLBuildResURLA.
2351 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2352 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2354 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2355 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2356 HRESULT hRet = E_FAIL;
2358 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2359 debugstr_w(lpszRes), lpszDest, dwDestLen);
2361 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2362 !lpszDest || (dwFlags && dwFlags != 2))
2363 return E_INVALIDARG;
2365 if (dwDestLen >= szResLen + 1)
2367 dwDestLen -= (szResLen + 1);
2368 memcpy(lpszDest, szRes, sizeof(szRes));
2370 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2372 if (hMod)
2374 WCHAR szBuff[MAX_PATH];
2375 DWORD len;
2377 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2378 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2380 DWORD dwPathLen = strlenW(szBuff) + 1;
2382 if (dwDestLen >= dwPathLen)
2384 DWORD dwResLen;
2386 dwDestLen -= dwPathLen;
2387 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2389 dwResLen = strlenW(lpszRes) + 1;
2390 if (dwDestLen >= dwResLen + 1)
2392 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2393 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2394 hRet = S_OK;
2398 MLFreeLibrary(hMod);
2401 return hRet;