Tests should not use wine/unicode.h.
[wine/multimedia.git] / dlls / shlwapi / url.c
blob4710e75abd440e49e412c0ea7ab31dbd36c64e2a
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 = dwFlags & URL_FILE_USE_PATHURL ? '\\' : '/';
335 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
336 pcchCanonicalized, dwFlags);
338 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
339 return E_INVALIDARG;
341 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
342 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
345 * state =
346 * 0 initial 1,3
347 * 1 have 2[+] alnum 2,3
348 * 2 have scheme (found :) 4,6,3
349 * 3 failed (no location)
350 * 4 have // 5,3
351 * 5 have 1[+] alnum 6,3
352 * 6 have location (found /) save root location
355 wk1 = (LPWSTR)pszUrl;
356 wk2 = lpszUrlCpy;
357 state = 0;
358 while (*wk1) {
359 switch (state) {
360 case 0:
361 if (!isalnumW(*wk1)) {state = 3; break;}
362 *wk2++ = *wk1++;
363 if (!isalnumW(*wk1)) {state = 3; break;}
364 *wk2++ = *wk1++;
365 state = 1;
366 break;
367 case 1:
368 *wk2++ = *wk1;
369 if (*wk1++ == L':') state = 2;
370 break;
371 case 2:
372 if (*wk1 != L'/') {state = 3; break;}
373 *wk2++ = *wk1++;
374 if (*wk1 != L'/') {state = 6; break;}
375 *wk2++ = *wk1++;
376 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
377 wk1++;
378 state = 4;
379 break;
380 case 3:
381 nWkLen = strlenW(wk1);
382 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
383 mp = wk2;
384 wk1 += nWkLen;
385 wk2 += nWkLen;
387 while(mp < wk2) {
388 if(*mp == '/' || *mp == '\\')
389 *mp = slash;
390 mp++;
392 break;
393 case 4:
394 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.') && (*wk1 != ':'))
395 {state = 3; break;}
396 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.') || (*wk1 == ':'))
397 *wk2++ = *wk1++;
398 state = 5;
399 break;
400 case 5:
401 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
402 while(*wk1 == '/' || *wk1 == '\\') {
403 *wk2++ = slash;
404 wk1++;
406 state = 6;
407 break;
408 case 6:
409 if(dwFlags & URL_DONT_SIMPLIFY) {
410 state = 3;
411 break;
414 /* Now at root location, cannot back up any more. */
415 /* "root" will point at the '/' */
417 root = wk2-1;
418 while (*wk1) {
419 mp = strchrW(wk1, '/');
420 mp2 = strchrW(wk1, '\\');
421 if(mp2 && (!mp || mp2 < mp))
422 mp = mp2;
423 if (!mp) {
424 nWkLen = strlenW(wk1);
425 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
426 wk1 += nWkLen;
427 wk2 += nWkLen;
428 continue;
430 nLen = mp - wk1;
431 if(nLen) {
432 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
433 wk2 += nLen;
434 wk1 += nLen;
436 *wk2++ = slash;
437 wk1++;
439 if (*wk1 == L'.') {
440 TRACE("found '/.'\n");
441 if (wk1[1] == '/' || wk1[1] == '\\') {
442 /* case of /./ -> skip the ./ */
443 wk1 += 2;
445 else if (wk1[1] == '.') {
446 /* found /.. look for next / */
447 TRACE("found '/..'\n");
448 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
449 || wk1[2] == '#' || !wk1[2]) {
450 /* case /../ -> need to backup wk2 */
451 TRACE("found '/../'\n");
452 *(wk2-1) = L'\0'; /* set end of string */
453 mp = strrchrW(root, slash);
454 if (mp && (mp >= root)) {
455 /* found valid backup point */
456 wk2 = mp + 1;
457 if(wk1[2] != '/' && wk1[2] != '\\')
458 wk1 += 2;
459 else
460 wk1 += 3;
462 else {
463 /* did not find point, restore '/' */
464 *(wk2-1) = slash;
470 *wk2 = L'\0';
471 break;
472 default:
473 FIXME("how did we get here - state=%d\n", state);
474 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
475 return E_INVALIDARG;
477 *wk2 = L'\0';
478 TRACE("Simplified, orig <%s>, simple <%s>\n",
479 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
481 nLen = lstrlenW(lpszUrlCpy);
482 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
483 lpszUrlCpy[--nLen]=0;
485 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
486 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
488 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
489 URL_ESCAPE_SPACES_ONLY |
490 URL_ESCAPE_PERCENT |
491 URL_DONT_ESCAPE_EXTRA_INFO |
492 URL_ESCAPE_SEGMENT_ONLY ))) {
493 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
494 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
495 EscapeFlags);
496 } else { /* No escaping needed, just copy the string */
497 nLen = lstrlenW(lpszUrlCpy);
498 if(nLen < *pcchCanonicalized)
499 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
500 else {
501 hr = E_POINTER;
502 nLen++;
504 *pcchCanonicalized = nLen;
507 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
509 if (hr == S_OK)
510 TRACE("result %s\n", debugstr_w(pszCanonicalized));
512 return hr;
515 /*************************************************************************
516 * UrlCombineA [SHLWAPI.@]
518 * Combine two Urls.
520 * PARAMS
521 * pszBase [I] Base Url
522 * pszRelative [I] Url to combine with pszBase
523 * pszCombined [O] Destination for combined Url
524 * pcchCombined [O] Destination for length of pszCombined
525 * dwFlags [I] URL_ flags from "shlwapi.h"
527 * RETURNS
528 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
529 * contains its length.
530 * Failure: An HRESULT error code indicating the error.
532 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
533 LPSTR pszCombined, LPDWORD pcchCombined,
534 DWORD dwFlags)
536 LPWSTR base, relative, combined;
537 DWORD ret, len, len2;
539 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
540 debugstr_a(pszBase),debugstr_a(pszRelative),
541 pcchCombined?*pcchCombined:0,dwFlags);
543 if(!pszBase || !pszRelative || !pcchCombined)
544 return E_INVALIDARG;
546 base = HeapAlloc(GetProcessHeap(), 0,
547 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
548 relative = base + INTERNET_MAX_URL_LENGTH;
549 combined = relative + INTERNET_MAX_URL_LENGTH;
551 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
552 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
553 len = *pcchCombined;
555 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
556 if (ret != S_OK) {
557 *pcchCombined = len;
558 HeapFree(GetProcessHeap(), 0, base);
559 return ret;
562 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
563 if (len2 > *pcchCombined) {
564 *pcchCombined = len2;
565 HeapFree(GetProcessHeap(), 0, base);
566 return E_POINTER;
568 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
569 0, 0);
570 *pcchCombined = len2;
571 HeapFree(GetProcessHeap(), 0, base);
572 return S_OK;
575 /*************************************************************************
576 * UrlCombineW [SHLWAPI.@]
578 * See UrlCombineA.
580 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
581 LPWSTR pszCombined, LPDWORD pcchCombined,
582 DWORD dwFlags)
584 PARSEDURLW base, relative;
585 DWORD myflags, sizeloc = 0;
586 DWORD len, res1, res2, process_case = 0;
587 LPWSTR work, preliminary, mbase, mrelative;
588 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
589 static const WCHAR single_slash[] = {'/','\0'};
590 HRESULT ret;
592 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
593 debugstr_w(pszBase),debugstr_w(pszRelative),
594 pcchCombined?*pcchCombined:0,dwFlags);
596 if(!pszBase || !pszRelative || !pcchCombined)
597 return E_INVALIDARG;
599 base.cbSize = sizeof(base);
600 relative.cbSize = sizeof(relative);
602 /* Get space for duplicates of the input and the output */
603 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
604 sizeof(WCHAR));
605 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
606 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
607 *preliminary = L'\0';
609 /* Canonicalize the base input prior to looking for the scheme */
610 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
611 len = INTERNET_MAX_URL_LENGTH;
612 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
614 /* Canonicalize the relative input prior to looking for the scheme */
615 len = INTERNET_MAX_URL_LENGTH;
616 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
618 /* See if the base has a scheme */
619 res1 = ParseURLW(mbase, &base);
620 if (res1) {
621 /* if pszBase has no scheme, then return pszRelative */
622 TRACE("no scheme detected in Base\n");
623 process_case = 1;
625 else do {
627 /* get size of location field (if it exists) */
628 work = (LPWSTR)base.pszSuffix;
629 sizeloc = 0;
630 if (*work++ == L'/') {
631 if (*work++ == L'/') {
632 /* At this point have start of location and
633 * it ends at next '/' or end of string.
635 while(*work && (*work != L'/')) work++;
636 sizeloc = (DWORD)(work - base.pszSuffix);
640 /* Change .sizep2 to not have the last leaf in it,
641 * Note: we need to start after the location (if it exists)
643 work = strrchrW((base.pszSuffix+sizeloc), L'/');
644 if (work) {
645 len = (DWORD)(work - base.pszSuffix + 1);
646 base.cchSuffix = len;
649 * At this point:
650 * .pszSuffix points to location (starting with '//')
651 * .cchSuffix length of location (above) and rest less the last
652 * leaf (if any)
653 * sizeloc length of location (above) up to but not including
654 * the last '/'
657 res2 = ParseURLW(mrelative, &relative);
658 if (res2) {
659 /* no scheme in pszRelative */
660 TRACE("no scheme detected in Relative\n");
661 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
662 relative.cchSuffix = strlenW(mrelative);
663 if (*pszRelative == L':') {
664 /* case that is either left alone or uses pszBase */
665 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
666 process_case = 5;
667 break;
669 process_case = 1;
670 break;
672 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
673 /* case that becomes "file:///" */
674 strcpyW(preliminary, myfilestr);
675 process_case = 1;
676 break;
678 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
679 /* pszRelative has location and rest */
680 process_case = 3;
681 break;
683 if (*mrelative == L'/') {
684 /* case where pszRelative is root to location */
685 process_case = 4;
686 break;
688 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
689 break;
692 /* handle cases where pszRelative has scheme */
693 if ((base.cchProtocol == relative.cchProtocol) &&
694 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
696 /* since the schemes are the same */
697 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
698 /* case where pszRelative replaces location and following */
699 process_case = 3;
700 break;
702 if (*relative.pszSuffix == L'/') {
703 /* case where pszRelative is root to location */
704 process_case = 4;
705 break;
707 /* case where scheme is followed by document path */
708 process_case = 5;
709 break;
711 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
712 /* case where pszRelative replaces scheme, location,
713 * and following and handles PLUGGABLE
715 process_case = 2;
716 break;
718 process_case = 1;
719 break;
720 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
723 ret = S_OK;
724 switch (process_case) {
726 case 1: /*
727 * Return pszRelative appended to what ever is in pszCombined,
728 * (which may the string "file:///"
730 strcatW(preliminary, mrelative);
731 break;
733 case 2: /*
734 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
735 * and pszRelative starts with "//", then append a "/"
737 strcpyW(preliminary, mrelative);
738 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
739 URL_JustLocation(relative.pszSuffix))
740 strcatW(preliminary, single_slash);
741 break;
743 case 3: /*
744 * Return the pszBase scheme with pszRelative. Basically
745 * keeps the scheme and replaces the domain and following.
747 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
748 work = preliminary + base.cchProtocol + 1;
749 strcpyW(work, relative.pszSuffix);
750 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
751 URL_JustLocation(relative.pszSuffix))
752 strcatW(work, single_slash);
753 break;
755 case 4: /*
756 * Return the pszBase scheme and location but everything
757 * after the location is pszRelative. (Replace document
758 * from root on.)
760 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
761 work = preliminary + base.cchProtocol + 1 + sizeloc;
762 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
763 *(work++) = L'/';
764 strcpyW(work, relative.pszSuffix);
765 break;
767 case 5: /*
768 * Return the pszBase without its document (if any) and
769 * append pszRelative after its scheme.
771 memcpy(preliminary, base.pszProtocol,
772 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
773 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
774 if (*work++ != L'/')
775 *(work++) = L'/';
776 strcpyW(work, relative.pszSuffix);
777 break;
779 default:
780 FIXME("How did we get here????? process_case=%ld\n", process_case);
781 ret = E_INVALIDARG;
784 if (ret == S_OK) {
785 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
786 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
787 if(SUCCEEDED(ret) && pszCombined) {
788 lstrcpyW(pszCombined, mrelative);
790 TRACE("return-%ld len=%ld, %s\n",
791 process_case, *pcchCombined, debugstr_w(pszCombined));
793 HeapFree(GetProcessHeap(), 0, preliminary);
794 return ret;
797 /*************************************************************************
798 * UrlEscapeA [SHLWAPI.@]
801 HRESULT WINAPI UrlEscapeA(
802 LPCSTR pszUrl,
803 LPSTR pszEscaped,
804 LPDWORD pcchEscaped,
805 DWORD dwFlags)
807 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
808 WCHAR *escapedW = bufW;
809 UNICODE_STRING urlW;
810 HRESULT ret;
811 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
813 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
814 return E_INVALIDARG;
815 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
816 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
817 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
819 if(ret == S_OK) {
820 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
821 if(*pcchEscaped > lenA) {
822 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
823 pszEscaped[lenA] = 0;
824 *pcchEscaped = lenA;
825 } else {
826 *pcchEscaped = lenA + 1;
827 ret = E_POINTER;
830 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
831 RtlFreeUnicodeString(&urlW);
832 return ret;
835 #define WINE_URL_BASH_AS_SLASH 0x01
836 #define WINE_URL_COLLAPSE_SLASHES 0x02
837 #define WINE_URL_ESCAPE_SLASH 0x04
838 #define WINE_URL_ESCAPE_HASH 0x08
839 #define WINE_URL_ESCAPE_QUESTION 0x10
840 #define WINE_URL_STOP_ON_HASH 0x20
841 #define WINE_URL_STOP_ON_QUESTION 0x40
843 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
846 if (isalnumW(ch))
847 return FALSE;
849 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
850 if(ch == ' ')
851 return TRUE;
852 else
853 return FALSE;
856 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
857 return TRUE;
859 if (ch <= 31 || ch >= 127)
860 return TRUE;
862 else {
863 switch (ch) {
864 case ' ':
865 case '<':
866 case '>':
867 case '\"':
868 case '{':
869 case '}':
870 case '|':
871 case '\\':
872 case '^':
873 case ']':
874 case '[':
875 case '`':
876 case '&':
877 return TRUE;
879 case '/':
880 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
881 return FALSE;
883 case '?':
884 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
885 return FALSE;
887 case '#':
888 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
889 return FALSE;
891 default:
892 return FALSE;
898 /*************************************************************************
899 * UrlEscapeW [SHLWAPI.@]
901 * Converts unsafe characters in a Url into escape sequences.
903 * PARAMS
904 * pszUrl [I] Url to modify
905 * pszEscaped [O] Destination for modified Url
906 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
907 * dwFlags [I] URL_ flags from "shlwapi.h"
909 * RETURNS
910 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
911 * contains its length.
912 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
913 * pcchEscaped is set to the required length.
915 * Converts unsafe characters into their escape sequences.
917 * NOTES
918 * - By default this function stops converting at the first '?' or
919 * '#' character.
920 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
921 * converted, but the conversion continues past a '?' or '#'.
922 * - Note that this function did not work well (or at all) in shlwapi version 4.
924 * BUGS
925 * Only the following flags are implemented:
926 *| URL_ESCAPE_SPACES_ONLY
927 *| URL_DONT_ESCAPE_EXTRA_INFO
928 *| URL_ESCAPE_SEGMENT_ONLY
929 *| URL_ESCAPE_PERCENT
931 HRESULT WINAPI UrlEscapeW(
932 LPCWSTR pszUrl,
933 LPWSTR pszEscaped,
934 LPDWORD pcchEscaped,
935 DWORD dwFlags)
937 LPCWSTR src;
938 DWORD needed = 0, ret;
939 BOOL stop_escaping = FALSE;
940 WCHAR next[5], *dst = pszEscaped;
941 INT len;
942 PARSEDURLW parsed_url;
943 DWORD int_flags;
944 DWORD slashes = 0;
945 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
947 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
948 pcchEscaped, dwFlags);
950 if(!pszUrl || !pszEscaped || !pcchEscaped)
951 return E_INVALIDARG;
953 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
954 URL_ESCAPE_SEGMENT_ONLY |
955 URL_DONT_ESCAPE_EXTRA_INFO |
956 URL_ESCAPE_PERCENT))
957 FIXME("Unimplemented flags: %08lx\n", dwFlags);
959 /* fix up flags */
960 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
961 /* if SPACES_ONLY specified, reset the other controls */
962 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
963 URL_ESCAPE_PERCENT |
964 URL_ESCAPE_SEGMENT_ONLY);
966 else
967 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
968 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
971 int_flags = 0;
972 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
973 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
974 } else {
975 parsed_url.cbSize = sizeof(parsed_url);
976 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
977 parsed_url.nScheme = URL_SCHEME_INVALID;
979 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
981 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
982 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
984 switch(parsed_url.nScheme) {
985 case URL_SCHEME_FILE:
986 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
987 int_flags &= ~WINE_URL_STOP_ON_HASH;
988 break;
990 case URL_SCHEME_HTTP:
991 case URL_SCHEME_HTTPS:
992 int_flags |= WINE_URL_BASH_AS_SLASH;
993 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
994 int_flags |= WINE_URL_ESCAPE_SLASH;
995 break;
997 case URL_SCHEME_MAILTO:
998 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
999 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1000 break;
1002 case URL_SCHEME_INVALID:
1003 break;
1005 case URL_SCHEME_FTP:
1006 default:
1007 if(parsed_url.pszSuffix[0] != '/')
1008 int_flags |= WINE_URL_ESCAPE_SLASH;
1009 break;
1013 for(src = pszUrl; *src; ) {
1014 WCHAR cur = *src;
1015 len = 0;
1017 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1018 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1019 while(cur == '/' || cur == '\\') {
1020 slashes++;
1021 cur = *++src;
1023 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1024 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1025 src += localhost_len + 1;
1026 slashes = 3;
1029 switch(slashes) {
1030 case 1:
1031 case 3:
1032 next[0] = next[1] = next[2] = '/';
1033 len = 3;
1034 break;
1035 case 0:
1036 len = 0;
1037 break;
1038 default:
1039 next[0] = next[1] = '/';
1040 len = 2;
1041 break;
1044 if(len == 0) {
1046 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1047 stop_escaping = TRUE;
1049 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1050 stop_escaping = TRUE;
1052 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1054 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1055 next[0] = L'%';
1056 next[1] = hexDigits[(cur >> 4) & 0xf];
1057 next[2] = hexDigits[cur & 0xf];
1058 len = 3;
1059 } else {
1060 next[0] = cur;
1061 len = 1;
1063 src++;
1066 if(needed + len <= *pcchEscaped) {
1067 memcpy(dst, next, len*sizeof(WCHAR));
1068 dst += len;
1070 needed += len;
1073 if(needed < *pcchEscaped) {
1074 *dst = '\0';
1075 ret = S_OK;
1076 } else {
1077 needed++; /* add one for the '\0' */
1078 ret = E_POINTER;
1080 *pcchEscaped = needed;
1081 return ret;
1085 /*************************************************************************
1086 * UrlUnescapeA [SHLWAPI.@]
1088 * Converts Url escape sequences back to ordinary characters.
1090 * PARAMS
1091 * pszUrl [I/O] Url to convert
1092 * pszUnescaped [O] Destination for converted Url
1093 * pcchUnescaped [I/O] Size of output string
1094 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1096 * RETURNS
1097 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1098 * dwFlags includes URL_ESCAPE_INPLACE.
1099 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1100 * this case pcchUnescaped is set to the size required.
1101 * NOTES
1102 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1103 * the first occurrence of either a '?' or '#' character.
1105 HRESULT WINAPI UrlUnescapeA(
1106 LPSTR pszUrl,
1107 LPSTR pszUnescaped,
1108 LPDWORD pcchUnescaped,
1109 DWORD dwFlags)
1111 char *dst, next;
1112 LPCSTR src;
1113 HRESULT ret;
1114 DWORD needed;
1115 BOOL stop_unescaping = FALSE;
1117 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1118 pcchUnescaped, dwFlags);
1120 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1121 return E_INVALIDARG;
1123 if(dwFlags & URL_UNESCAPE_INPLACE)
1124 dst = pszUrl;
1125 else
1126 dst = pszUnescaped;
1128 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1129 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1130 (*src == '#' || *src == '?')) {
1131 stop_unescaping = TRUE;
1132 next = *src;
1133 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1134 && stop_unescaping == FALSE) {
1135 INT ih;
1136 char buf[3];
1137 memcpy(buf, src + 1, 2);
1138 buf[2] = '\0';
1139 ih = strtol(buf, NULL, 16);
1140 next = (CHAR) ih;
1141 src += 2; /* Advance to end of escape */
1142 } else
1143 next = *src;
1145 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1146 *dst++ = next;
1149 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1150 *dst = '\0';
1151 ret = S_OK;
1152 } else {
1153 needed++; /* add one for the '\0' */
1154 ret = E_POINTER;
1156 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1157 *pcchUnescaped = needed;
1159 if (ret == S_OK) {
1160 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1161 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1164 return ret;
1167 /*************************************************************************
1168 * UrlUnescapeW [SHLWAPI.@]
1170 * See UrlUnescapeA.
1172 HRESULT WINAPI UrlUnescapeW(
1173 LPWSTR pszUrl,
1174 LPWSTR pszUnescaped,
1175 LPDWORD pcchUnescaped,
1176 DWORD dwFlags)
1178 WCHAR *dst, next;
1179 LPCWSTR src;
1180 HRESULT ret;
1181 DWORD needed;
1182 BOOL stop_unescaping = FALSE;
1184 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1185 pcchUnescaped, dwFlags);
1187 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1188 return E_INVALIDARG;
1190 if(dwFlags & URL_UNESCAPE_INPLACE)
1191 dst = pszUrl;
1192 else
1193 dst = pszUnescaped;
1195 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1196 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1197 (*src == L'#' || *src == L'?')) {
1198 stop_unescaping = TRUE;
1199 next = *src;
1200 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1201 && stop_unescaping == FALSE) {
1202 INT ih;
1203 WCHAR buf[5] = {'0','x',0};
1204 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1205 buf[4] = 0;
1206 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1207 next = (WCHAR) ih;
1208 src += 2; /* Advance to end of escape */
1209 } else
1210 next = *src;
1212 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1213 *dst++ = next;
1216 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1217 *dst = L'\0';
1218 ret = S_OK;
1219 } else {
1220 needed++; /* add one for the '\0' */
1221 ret = E_POINTER;
1223 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1224 *pcchUnescaped = needed;
1226 if (ret == S_OK) {
1227 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1228 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1231 return ret;
1234 /*************************************************************************
1235 * UrlGetLocationA [SHLWAPI.@]
1237 * Get the location from a Url.
1239 * PARAMS
1240 * pszUrl [I] Url to get the location from
1242 * RETURNS
1243 * A pointer to the start of the location in pszUrl, or NULL if there is
1244 * no location.
1246 * NOTES
1247 * - MSDN erroneously states that "The location is the segment of the Url
1248 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1249 * stop at '?' and always return a NULL in this case.
1250 * - MSDN also erroneously states that "If a file URL has a query string,
1251 * the returned string is the query string". In all tested cases, if the
1252 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1253 *| Result Url
1254 *| ------ ---
1255 *| NULL file://aa/b/cd#hohoh
1256 *| #hohoh http://aa/b/cd#hohoh
1257 *| NULL fi://aa/b/cd#hohoh
1258 *| #hohoh ff://aa/b/cd#hohoh
1260 LPCSTR WINAPI UrlGetLocationA(
1261 LPCSTR pszUrl)
1263 PARSEDURLA base;
1264 DWORD res1;
1266 base.cbSize = sizeof(base);
1267 res1 = ParseURLA(pszUrl, &base);
1268 if (res1) return NULL; /* invalid scheme */
1270 /* if scheme is file: then never return pointer */
1271 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1273 /* Look for '#' and return its addr */
1274 return strchr(base.pszSuffix, '#');
1277 /*************************************************************************
1278 * UrlGetLocationW [SHLWAPI.@]
1280 * See UrlGetLocationA.
1282 LPCWSTR WINAPI UrlGetLocationW(
1283 LPCWSTR pszUrl)
1285 PARSEDURLW base;
1286 DWORD res1;
1288 base.cbSize = sizeof(base);
1289 res1 = ParseURLW(pszUrl, &base);
1290 if (res1) return NULL; /* invalid scheme */
1292 /* if scheme is file: then never return pointer */
1293 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1295 /* Look for '#' and return its addr */
1296 return strchrW(base.pszSuffix, L'#');
1299 /*************************************************************************
1300 * UrlCompareA [SHLWAPI.@]
1302 * Compare two Urls.
1304 * PARAMS
1305 * pszUrl1 [I] First Url to compare
1306 * pszUrl2 [I] Url to compare to pszUrl1
1307 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1309 * RETURNS
1310 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1311 * than, equal to, or less than pszUrl1 respectively.
1313 INT WINAPI UrlCompareA(
1314 LPCSTR pszUrl1,
1315 LPCSTR pszUrl2,
1316 BOOL fIgnoreSlash)
1318 INT ret, len, len1, len2;
1320 if (!fIgnoreSlash)
1321 return strcmp(pszUrl1, pszUrl2);
1322 len1 = strlen(pszUrl1);
1323 if (pszUrl1[len1-1] == '/') len1--;
1324 len2 = strlen(pszUrl2);
1325 if (pszUrl2[len2-1] == '/') len2--;
1326 if (len1 == len2)
1327 return strncmp(pszUrl1, pszUrl2, len1);
1328 len = min(len1, len2);
1329 ret = strncmp(pszUrl1, pszUrl2, len);
1330 if (ret) return ret;
1331 if (len1 > len2) return 1;
1332 return -1;
1335 /*************************************************************************
1336 * UrlCompareW [SHLWAPI.@]
1338 * See UrlCompareA.
1340 INT WINAPI UrlCompareW(
1341 LPCWSTR pszUrl1,
1342 LPCWSTR pszUrl2,
1343 BOOL fIgnoreSlash)
1345 INT ret;
1346 size_t len, len1, len2;
1348 if (!fIgnoreSlash)
1349 return strcmpW(pszUrl1, pszUrl2);
1350 len1 = strlenW(pszUrl1);
1351 if (pszUrl1[len1-1] == '/') len1--;
1352 len2 = strlenW(pszUrl2);
1353 if (pszUrl2[len2-1] == '/') len2--;
1354 if (len1 == len2)
1355 return strncmpW(pszUrl1, pszUrl2, len1);
1356 len = min(len1, len2);
1357 ret = strncmpW(pszUrl1, pszUrl2, len);
1358 if (ret) return ret;
1359 if (len1 > len2) return 1;
1360 return -1;
1363 /*************************************************************************
1364 * HashData [SHLWAPI.@]
1366 * Hash an input block into a variable sized digest.
1368 * PARAMS
1369 * lpSrc [I] Input block
1370 * nSrcLen [I] Length of lpSrc
1371 * lpDest [I] Output for hash digest
1372 * nDestLen [I] Length of lpDest
1374 * RETURNS
1375 * Success: TRUE. lpDest is filled with the computed hash value.
1376 * Failure: FALSE, if any argument is invalid.
1378 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1379 unsigned char *lpDest, DWORD nDestLen)
1381 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1383 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1384 IsBadWritePtr(lpDest, nDestLen))
1385 return E_INVALIDARG;
1387 while (destCount >= 0)
1389 lpDest[destCount] = (destCount & 0xff);
1390 destCount--;
1393 while (srcCount >= 0)
1395 destCount = nDestLen - 1;
1396 while (destCount >= 0)
1398 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1399 destCount--;
1401 srcCount--;
1403 return S_OK;
1406 /*************************************************************************
1407 * UrlHashA [SHLWAPI.@]
1409 * Produce a Hash from a Url.
1411 * PARAMS
1412 * pszUrl [I] Url to hash
1413 * lpDest [O] Destinationh for hash
1414 * nDestLen [I] Length of lpDest
1416 * RETURNS
1417 * Success: S_OK. lpDest is filled with the computed hash value.
1418 * Failure: E_INVALIDARG, if any argument is invalid.
1420 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1422 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1423 return E_INVALIDARG;
1425 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1426 return S_OK;
1429 /*************************************************************************
1430 * UrlHashW [SHLWAPI.@]
1432 * See UrlHashA.
1434 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1436 char szUrl[MAX_PATH];
1438 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1440 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1441 return E_INVALIDARG;
1443 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1444 * return the same digests for the same URL.
1446 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1447 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1448 return S_OK;
1451 /*************************************************************************
1452 * UrlApplySchemeA [SHLWAPI.@]
1454 * Apply a scheme to a Url.
1456 * PARAMS
1457 * pszIn [I] Url to apply scheme to
1458 * pszOut [O] Destination for modified Url
1459 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1460 * dwFlags [I] URL_ flags from "shlwapi.h"
1462 * RETURNS
1463 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1464 * Failure: An HRESULT error code describing the error.
1466 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1468 LPWSTR in, out;
1469 DWORD ret, len, len2;
1471 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1472 debugstr_a(pszIn), *pcchOut, dwFlags);
1474 in = HeapAlloc(GetProcessHeap(), 0,
1475 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1476 out = in + INTERNET_MAX_URL_LENGTH;
1478 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1479 len = INTERNET_MAX_URL_LENGTH;
1481 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1482 if ((ret != S_OK) && (ret != S_FALSE)) {
1483 HeapFree(GetProcessHeap(), 0, in);
1484 return ret;
1487 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1488 if (len2 > *pcchOut) {
1489 *pcchOut = len2;
1490 HeapFree(GetProcessHeap(), 0, in);
1491 return E_POINTER;
1493 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1494 *pcchOut = len2;
1495 HeapFree(GetProcessHeap(), 0, in);
1496 return ret;
1499 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1501 HKEY newkey;
1502 BOOL j;
1503 INT index;
1504 DWORD value_len, data_len, dwType, i;
1505 WCHAR reg_path[MAX_PATH];
1506 WCHAR value[MAX_PATH], data[MAX_PATH];
1507 WCHAR Wxx, Wyy;
1509 MultiByteToWideChar(0, 0,
1510 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1511 -1, reg_path, MAX_PATH);
1512 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1513 index = 0;
1514 while(value_len = data_len = MAX_PATH,
1515 RegEnumValueW(newkey, index, value, &value_len,
1516 0, &dwType, (LPVOID)data, &data_len) == 0) {
1517 TRACE("guess %d %s is %s\n",
1518 index, debugstr_w(value), debugstr_w(data));
1520 j = FALSE;
1521 for(i=0; i<value_len; i++) {
1522 Wxx = pszIn[i];
1523 Wyy = value[i];
1524 /* remember that TRUE is not-equal */
1525 j = ChrCmpIW(Wxx, Wyy);
1526 if (j) break;
1528 if ((i == value_len) && !j) {
1529 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1530 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1531 RegCloseKey(newkey);
1532 return E_POINTER;
1534 strcpyW(pszOut, data);
1535 strcatW(pszOut, pszIn);
1536 *pcchOut = strlenW(pszOut);
1537 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1538 RegCloseKey(newkey);
1539 return S_OK;
1541 index++;
1543 RegCloseKey(newkey);
1544 return -1;
1547 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1549 HKEY newkey;
1550 DWORD data_len, dwType;
1551 WCHAR reg_path[MAX_PATH];
1552 WCHAR value[MAX_PATH], data[MAX_PATH];
1554 /* get and prepend default */
1555 MultiByteToWideChar(0, 0,
1556 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1557 -1, reg_path, MAX_PATH);
1558 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1559 data_len = MAX_PATH;
1560 value[0] = L'@';
1561 value[1] = L'\0';
1562 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1563 RegCloseKey(newkey);
1564 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1565 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1566 return E_POINTER;
1568 strcpyW(pszOut, data);
1569 strcatW(pszOut, pszIn);
1570 *pcchOut = strlenW(pszOut);
1571 TRACE("used default %s\n", debugstr_w(pszOut));
1572 return S_OK;
1575 /*************************************************************************
1576 * UrlApplySchemeW [SHLWAPI.@]
1578 * See UrlApplySchemeA.
1580 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1582 PARSEDURLW in_scheme;
1583 DWORD res1;
1584 HRESULT ret;
1586 TRACE("(in %s, out size %ld, flags %08lx)\n",
1587 debugstr_w(pszIn), *pcchOut, dwFlags);
1589 if (dwFlags & URL_APPLY_GUESSFILE) {
1590 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1591 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1592 strcpyW(pszOut, pszIn);
1593 *pcchOut = strlenW(pszOut);
1594 return S_FALSE;
1597 in_scheme.cbSize = sizeof(in_scheme);
1598 /* See if the base has a scheme */
1599 res1 = ParseURLW(pszIn, &in_scheme);
1600 if (res1) {
1601 /* no scheme in input, need to see if we need to guess */
1602 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1603 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1604 return ret;
1607 else {
1608 /* we have a scheme, see if valid (known scheme) */
1609 if (in_scheme.nScheme) {
1610 /* have valid scheme, so just copy and exit */
1611 if (strlenW(pszIn) + 1 > *pcchOut) {
1612 *pcchOut = strlenW(pszIn) + 1;
1613 return E_POINTER;
1615 strcpyW(pszOut, pszIn);
1616 *pcchOut = strlenW(pszOut);
1617 TRACE("valid scheme, returing copy\n");
1618 return S_OK;
1622 /* If we are here, then either invalid scheme,
1623 * or no scheme and can't/failed guess.
1625 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1626 ((res1 != 0)) ) &&
1627 (dwFlags & URL_APPLY_DEFAULT)) {
1628 /* find and apply default scheme */
1629 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1632 /* just copy and give proper return code */
1633 if (strlenW(pszIn) + 1 > *pcchOut) {
1634 *pcchOut = strlenW(pszIn) + 1;
1635 return E_POINTER;
1637 strcpyW(pszOut, pszIn);
1638 *pcchOut = strlenW(pszOut);
1639 TRACE("returning copy, left alone\n");
1640 return S_FALSE;
1643 /*************************************************************************
1644 * UrlIsA [SHLWAPI.@]
1646 * Determine if a Url is of a certain class.
1648 * PARAMS
1649 * pszUrl [I] Url to check
1650 * Urlis [I] URLIS_ constant from "shlwapi.h"
1652 * RETURNS
1653 * TRUE if pszUrl belongs to the class type in Urlis.
1654 * FALSE Otherwise.
1656 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1658 PARSEDURLA base;
1659 DWORD res1;
1660 LPCSTR last;
1662 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1664 switch (Urlis) {
1666 case URLIS_OPAQUE:
1667 base.cbSize = sizeof(base);
1668 res1 = ParseURLA(pszUrl, &base);
1669 if (res1) return FALSE; /* invalid scheme */
1670 switch (base.nScheme)
1672 case URL_SCHEME_MAILTO:
1673 case URL_SCHEME_SHELL:
1674 case URL_SCHEME_JAVASCRIPT:
1675 case URL_SCHEME_VBSCRIPT:
1676 case URL_SCHEME_ABOUT:
1677 return TRUE;
1679 return FALSE;
1681 case URLIS_FILEURL:
1682 return !StrCmpNA("file:", pszUrl, 5);
1684 case URLIS_DIRECTORY:
1685 last = pszUrl + strlen(pszUrl) - 1;
1686 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1688 case URLIS_URL:
1689 return PathIsURLA(pszUrl);
1691 case URLIS_NOHISTORY:
1692 case URLIS_APPLIABLE:
1693 case URLIS_HASQUERY:
1694 default:
1695 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1697 return FALSE;
1700 /*************************************************************************
1701 * UrlIsW [SHLWAPI.@]
1703 * See UrlIsA.
1705 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1707 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1708 PARSEDURLW base;
1709 DWORD res1;
1710 LPCWSTR last;
1712 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1714 switch (Urlis) {
1716 case URLIS_OPAQUE:
1717 base.cbSize = sizeof(base);
1718 res1 = ParseURLW(pszUrl, &base);
1719 if (res1) return FALSE; /* invalid scheme */
1720 switch (base.nScheme)
1722 case URL_SCHEME_MAILTO:
1723 case URL_SCHEME_SHELL:
1724 case URL_SCHEME_JAVASCRIPT:
1725 case URL_SCHEME_VBSCRIPT:
1726 case URL_SCHEME_ABOUT:
1727 return TRUE;
1729 return FALSE;
1731 case URLIS_FILEURL:
1732 return !strncmpW(stemp, pszUrl, 5);
1734 case URLIS_DIRECTORY:
1735 last = pszUrl + strlenW(pszUrl) - 1;
1736 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1738 case URLIS_URL:
1739 return PathIsURLW(pszUrl);
1741 case URLIS_NOHISTORY:
1742 case URLIS_APPLIABLE:
1743 case URLIS_HASQUERY:
1744 default:
1745 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1747 return FALSE;
1750 /*************************************************************************
1751 * UrlIsNoHistoryA [SHLWAPI.@]
1753 * Determine if a Url should not be stored in the users history list.
1755 * PARAMS
1756 * pszUrl [I] Url to check
1758 * RETURNS
1759 * TRUE, if pszUrl should be excluded from the history list,
1760 * FALSE otherwise.
1762 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1764 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1767 /*************************************************************************
1768 * UrlIsNoHistoryW [SHLWAPI.@]
1770 * See UrlIsNoHistoryA.
1772 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1774 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1777 /*************************************************************************
1778 * UrlIsOpaqueA [SHLWAPI.@]
1780 * Determine if a Url is opaque.
1782 * PARAMS
1783 * pszUrl [I] Url to check
1785 * RETURNS
1786 * TRUE if pszUrl is opaque,
1787 * FALSE Otherwise.
1789 * NOTES
1790 * An opaque Url is one that does not start with "<protocol>://".
1792 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1794 return UrlIsA(pszUrl, URLIS_OPAQUE);
1797 /*************************************************************************
1798 * UrlIsOpaqueW [SHLWAPI.@]
1800 * See UrlIsOpaqueA.
1802 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1804 return UrlIsW(pszUrl, URLIS_OPAQUE);
1807 /*************************************************************************
1808 * Scans for characters of type "type" and when not matching found,
1809 * returns pointer to it and length in size.
1811 * Characters tested based on RFC 1738
1813 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1815 static DWORD alwayszero = 0;
1816 BOOL cont = TRUE;
1818 *size = 0;
1820 switch(type){
1822 case SCHEME:
1823 while (cont) {
1824 if ( (islowerW(*start) && isalphaW(*start)) ||
1825 isdigitW(*start) ||
1826 (*start == L'+') ||
1827 (*start == L'-') ||
1828 (*start == L'.')) {
1829 start++;
1830 (*size)++;
1832 else
1833 cont = FALSE;
1835 break;
1837 case USERPASS:
1838 while (cont) {
1839 if ( isalphaW(*start) ||
1840 isdigitW(*start) ||
1841 /* user/password only characters */
1842 (*start == L';') ||
1843 (*start == L'?') ||
1844 (*start == L'&') ||
1845 (*start == L'=') ||
1846 /* *extra* characters */
1847 (*start == L'!') ||
1848 (*start == L'*') ||
1849 (*start == L'\'') ||
1850 (*start == L'(') ||
1851 (*start == L')') ||
1852 (*start == L',') ||
1853 /* *safe* characters */
1854 (*start == L'$') ||
1855 (*start == L'_') ||
1856 (*start == L'+') ||
1857 (*start == L'-') ||
1858 (*start == L'.')) {
1859 start++;
1860 (*size)++;
1861 } else if (*start == L'%') {
1862 if (isxdigitW(*(start+1)) &&
1863 isxdigitW(*(start+2))) {
1864 start += 3;
1865 *size += 3;
1866 } else
1867 cont = FALSE;
1868 } else
1869 cont = FALSE;
1871 break;
1873 case PORT:
1874 while (cont) {
1875 if (isdigitW(*start)) {
1876 start++;
1877 (*size)++;
1879 else
1880 cont = FALSE;
1882 break;
1884 case HOST:
1885 while (cont) {
1886 if (isalnumW(*start) ||
1887 (*start == L'-') ||
1888 (*start == L'.') ) {
1889 start++;
1890 (*size)++;
1892 else
1893 cont = FALSE;
1895 break;
1896 default:
1897 FIXME("unknown type %d\n", type);
1898 return (LPWSTR)&alwayszero;
1900 /* TRACE("scanned %ld characters next char %p<%c>\n",
1901 *size, start, *start); */
1902 return start;
1905 /*************************************************************************
1906 * Attempt to parse URL into pieces.
1908 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1910 LPCWSTR work;
1912 memset(pl, 0, sizeof(WINE_PARSE_URL));
1913 pl->pScheme = pszUrl;
1914 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1915 if (!*work || (*work != L':')) goto ErrorExit;
1916 work++;
1917 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1918 pl->pUserName = work + 2;
1919 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1920 if (*work == L':' ) {
1921 /* parse password */
1922 work++;
1923 pl->pPassword = work;
1924 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1925 if (*work != L'@') {
1926 /* what we just parsed must be the hostname and port
1927 * so reset pointers and clear then let it parse */
1928 pl->szUserName = pl->szPassword = 0;
1929 work = pl->pUserName - 1;
1930 pl->pUserName = pl->pPassword = 0;
1932 } else if (*work == L'@') {
1933 /* no password */
1934 pl->szPassword = 0;
1935 pl->pPassword = 0;
1936 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1937 /* what was parsed was hostname, so reset pointers and let it parse */
1938 pl->szUserName = pl->szPassword = 0;
1939 work = pl->pUserName - 1;
1940 pl->pUserName = pl->pPassword = 0;
1941 } else goto ErrorExit;
1943 /* now start parsing hostname or hostnumber */
1944 work++;
1945 pl->pHostName = work;
1946 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1947 if (*work == L':') {
1948 /* parse port */
1949 work++;
1950 pl->pPort = work;
1951 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1953 if (*work == L'/') {
1954 /* see if query string */
1955 pl->pQuery = strchrW(work, L'?');
1956 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1958 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1959 pl->pScheme, pl->szScheme,
1960 pl->pUserName, pl->szUserName,
1961 pl->pPassword, pl->szPassword,
1962 pl->pHostName, pl->szHostName,
1963 pl->pPort, pl->szPort,
1964 pl->pQuery, pl->szQuery);
1965 return S_OK;
1966 ErrorExit:
1967 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1968 return E_INVALIDARG;
1971 /*************************************************************************
1972 * UrlGetPartA [SHLWAPI.@]
1974 * Retrieve part of a Url.
1976 * PARAMS
1977 * pszIn [I] Url to parse
1978 * pszOut [O] Destination for part of pszIn requested
1979 * pcchOut [I] Size of pszOut
1980 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
1981 * needed size of pszOut INCLUDING '\0'.
1982 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1983 * dwFlags [I] URL_ flags from "shlwapi.h"
1985 * RETURNS
1986 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1987 * Failure: An HRESULT error code describing the error.
1989 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1990 DWORD dwPart, DWORD dwFlags)
1992 LPWSTR in, out;
1993 DWORD ret, len, len2;
1995 in = HeapAlloc(GetProcessHeap(), 0,
1996 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1997 out = in + INTERNET_MAX_URL_LENGTH;
1999 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2001 len = INTERNET_MAX_URL_LENGTH;
2002 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2004 if (ret != S_OK) {
2005 HeapFree(GetProcessHeap(), 0, in);
2006 return ret;
2009 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2010 if (len2 > *pcchOut) {
2011 *pcchOut = len2;
2012 HeapFree(GetProcessHeap(), 0, in);
2013 return E_POINTER;
2015 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2016 *pcchOut = len2;
2017 HeapFree(GetProcessHeap(), 0, in);
2018 return S_OK;
2021 /*************************************************************************
2022 * UrlGetPartW [SHLWAPI.@]
2024 * See UrlGetPartA.
2026 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2027 DWORD dwPart, DWORD dwFlags)
2029 WINE_PARSE_URL pl;
2030 HRESULT ret;
2031 DWORD size, schsize;
2032 LPCWSTR addr, schaddr;
2034 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2035 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2037 ret = URL_ParseUrl(pszIn, &pl);
2038 if (!ret) {
2039 schaddr = pl.pScheme;
2040 schsize = pl.szScheme;
2042 switch (dwPart) {
2043 case URL_PART_SCHEME:
2044 if (!pl.szScheme) return E_INVALIDARG;
2045 addr = pl.pScheme;
2046 size = pl.szScheme;
2047 break;
2049 case URL_PART_HOSTNAME:
2050 if (!pl.szHostName) return E_INVALIDARG;
2051 addr = pl.pHostName;
2052 size = pl.szHostName;
2053 break;
2055 case URL_PART_USERNAME:
2056 if (!pl.szUserName) return E_INVALIDARG;
2057 addr = pl.pUserName;
2058 size = pl.szUserName;
2059 break;
2061 case URL_PART_PASSWORD:
2062 if (!pl.szPassword) return E_INVALIDARG;
2063 addr = pl.pPassword;
2064 size = pl.szPassword;
2065 break;
2067 case URL_PART_PORT:
2068 if (!pl.szPort) return E_INVALIDARG;
2069 addr = pl.pPort;
2070 size = pl.szPort;
2071 break;
2073 case URL_PART_QUERY:
2074 if (!pl.szQuery) return E_INVALIDARG;
2075 addr = pl.pQuery;
2076 size = pl.szQuery;
2077 break;
2079 default:
2080 return E_INVALIDARG;
2083 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2084 if (*pcchOut < schsize + size + 2) {
2085 *pcchOut = schsize + size + 2;
2086 return E_POINTER;
2088 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2089 pszOut[schsize] = ':';
2090 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2091 pszOut[schsize+1+size] = 0;
2092 *pcchOut = schsize + 1 + size;
2094 else {
2095 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2096 memcpy(pszOut, addr, size*sizeof(WCHAR));
2097 pszOut[size] = 0;
2098 *pcchOut = size;
2100 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2102 return ret;
2105 /*************************************************************************
2106 * PathIsURLA [SHLWAPI.@]
2108 * Check if the given path is a Url.
2110 * PARAMS
2111 * lpszPath [I] Path to check.
2113 * RETURNS
2114 * TRUE if lpszPath is a Url.
2115 * FALSE if lpszPath is NULL or not a Url.
2117 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2119 PARSEDURLA base;
2120 DWORD res1;
2122 if (!lpstrPath || !*lpstrPath) return FALSE;
2124 /* get protocol */
2125 base.cbSize = sizeof(base);
2126 res1 = ParseURLA(lpstrPath, &base);
2127 return (base.nScheme != URL_SCHEME_INVALID);
2130 /*************************************************************************
2131 * PathIsURLW [SHLWAPI.@]
2133 * See PathIsURLA.
2135 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2137 PARSEDURLW base;
2138 DWORD res1;
2140 if (!lpstrPath || !*lpstrPath) return FALSE;
2142 /* get protocol */
2143 base.cbSize = sizeof(base);
2144 res1 = ParseURLW(lpstrPath, &base);
2145 return (base.nScheme != URL_SCHEME_INVALID);
2148 /*************************************************************************
2149 * UrlCreateFromPathA [SHLWAPI.@]
2151 * See UrlCreateFromPathW
2153 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2155 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2156 WCHAR *urlW = bufW;
2157 UNICODE_STRING pathW;
2158 HRESULT ret;
2159 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2161 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2162 return E_INVALIDARG;
2163 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2164 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2165 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2167 if(ret == S_OK || ret == S_FALSE) {
2168 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2169 if(*pcchUrl > lenA) {
2170 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2171 pszUrl[lenA] = 0;
2172 *pcchUrl = lenA;
2173 } else {
2174 *pcchUrl = lenA + 1;
2175 ret = E_POINTER;
2178 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2179 RtlFreeUnicodeString(&pathW);
2180 return ret;
2183 /*************************************************************************
2184 * UrlCreateFromPathW [SHLWAPI.@]
2186 * Create a Url from a file path.
2188 * PARAMS
2189 * pszPath [I] Path to convert
2190 * pszUrl [O] Destination for the converted Url
2191 * pcchUrl [I/O] Length of pszUrl
2192 * dwReserved [I] Reserved, must be 0
2194 * RETURNS
2195 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2196 * Failure: An HRESULT error code.
2198 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2200 DWORD needed;
2201 HRESULT ret;
2202 WCHAR *pszNewUrl;
2203 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2204 WCHAR three_slashesW[] = {'/','/','/',0};
2205 PARSEDURLW parsed_url;
2207 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2209 /* Validate arguments */
2210 if (dwReserved != 0)
2211 return E_INVALIDARG;
2212 if (!pszUrl || !pcchUrl)
2213 return E_INVALIDARG;
2216 parsed_url.cbSize = sizeof(parsed_url);
2217 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2218 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2219 needed = strlenW(pszPath);
2220 if (needed >= *pcchUrl) {
2221 *pcchUrl = needed + 1;
2222 return E_POINTER;
2223 } else {
2224 *pcchUrl = needed;
2225 strcpyW(pszUrl, pszPath);
2226 return S_FALSE;
2231 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2232 strcpyW(pszNewUrl, file_colonW);
2233 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2234 strcatW(pszNewUrl, three_slashesW);
2235 strcatW(pszNewUrl, pszPath);
2236 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2238 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2239 return ret;
2242 /*************************************************************************
2243 * SHAutoComplete [SHLWAPI.@]
2245 * Enable auto-completion for an edit control.
2247 * PARAMS
2248 * hwndEdit [I] Handle of control to enable auto-completion for
2249 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2251 * RETURNS
2252 * Success: S_OK. Auto-completion is enabled for the control.
2253 * Failure: An HRESULT error code indicating the error.
2255 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2257 FIXME("SHAutoComplete stub\n");
2258 return S_FALSE;
2261 /*************************************************************************
2262 * MLBuildResURLA [SHLWAPI.405]
2264 * Create a Url pointing to a resource in a module.
2266 * PARAMS
2267 * lpszLibName [I] Name of the module containing the resource
2268 * hMod [I] Callers module handle
2269 * dwFlags [I] Undocumented flags for loading the module
2270 * lpszRes [I] Resource name
2271 * lpszDest [O] Destination for resulting Url
2272 * dwDestLen [I] Length of lpszDest
2274 * RETURNS
2275 * Success: S_OK. lpszDest constains the resource Url.
2276 * Failure: E_INVALIDARG, if any argument is invalid, or
2277 * E_FAIL if dwDestLen is too small.
2279 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2280 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2282 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2283 HRESULT hRet;
2285 if (lpszLibName)
2286 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2288 if (lpszRes)
2289 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2291 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2292 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2294 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2295 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2296 if (SUCCEEDED(hRet) && lpszDest)
2297 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2299 return hRet;
2302 /*************************************************************************
2303 * MLBuildResURLA [SHLWAPI.406]
2305 * See MLBuildResURLA.
2307 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2308 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2310 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2311 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2312 HRESULT hRet = E_FAIL;
2314 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2315 debugstr_w(lpszRes), lpszDest, dwDestLen);
2317 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2318 !lpszDest || (dwFlags && dwFlags != 2))
2319 return E_INVALIDARG;
2321 if (dwDestLen >= szResLen + 1)
2323 dwDestLen -= (szResLen + 1);
2324 memcpy(lpszDest, szRes, sizeof(szRes));
2326 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2328 if (hMod)
2330 WCHAR szBuff[MAX_PATH];
2331 DWORD len;
2333 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2334 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2336 DWORD dwPathLen = strlenW(szBuff) + 1;
2338 if (dwDestLen >= dwPathLen)
2340 DWORD dwResLen;
2342 dwDestLen -= dwPathLen;
2343 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2345 dwResLen = strlenW(lpszRes) + 1;
2346 if (dwDestLen >= dwResLen + 1)
2348 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2349 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2350 hRet = S_OK;
2354 MLFreeLibrary(hMod);
2357 return hRet;