sync rosapps to r44454
[reactos.git] / reactos / dll / win32 / shlwapi / url.c
1 /*
2 * Url functions
3 *
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
5 *
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.
10 *
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.
15 *
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
19 */
20
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"
37
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
41
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
43
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
46 */
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}},
69 };
70
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;
85
86 typedef enum {
87 SCHEME,
88 HOST,
89 PORT,
90 USERPASS,
91 } WINE_URL_SCAN_TYPE;
92
93 static const CHAR hexDigits[] = "0123456789ABCDEF";
94
95 static const WCHAR fileW[] = {'f','i','l','e','\0'};
96
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 };
118
119 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
120 {
121 unsigned int i;
122
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;
127 }
128
129 return URL_SCHEME_UNKNOWN;
130 }
131
132 /*************************************************************************
133 * @ [SHLWAPI.1]
134 *
135 * Parse a Url into its constituent parts.
136 *
137 * PARAMS
138 * x [I] Url to parse
139 * y [O] Undocumented structure holding the parsed information
140 *
141 * RETURNS
142 * Success: S_OK. y contains the parsed Url details.
143 * Failure: An HRESULT error code.
144 */
145 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
146 {
147 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
148 const char *ptr = x;
149 int len;
150
151 TRACE("%s %p\n", debugstr_a(x), y);
152
153 if(y->cbSize != sizeof(*y))
154 return E_INVALIDARG;
155
156 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
157 ptr++;
158
159 if (*ptr != ':' || ptr <= x+1) {
160 y->pszProtocol = NULL;
161 return 0x80041001;
162 }
163
164 y->pszProtocol = x;
165 y->cchProtocol = ptr-x;
166 y->pszSuffix = ptr+1;
167 y->cchSuffix = strlen(y->pszSuffix);
168
169 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
170 scheme, sizeof(scheme)/sizeof(WCHAR));
171 y->nScheme = get_scheme_code(scheme, len);
172
173 return S_OK;
174 }
175
176 /*************************************************************************
177 * @ [SHLWAPI.2]
178 *
179 * Unicode version of ParseURLA.
180 */
181 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
182 {
183 const WCHAR *ptr = x;
184
185 TRACE("%s %p\n", debugstr_w(x), y);
186
187 if(y->cbSize != sizeof(*y))
188 return E_INVALIDARG;
189
190 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
191 ptr++;
192
193 if (*ptr != ':' || ptr <= x+1) {
194 y->pszProtocol = NULL;
195 return 0x80041001;
196 }
197
198 y->pszProtocol = x;
199 y->cchProtocol = ptr-x;
200 y->pszSuffix = ptr+1;
201 y->cchSuffix = strlenW(y->pszSuffix);
202 y->nScheme = get_scheme_code(x, ptr-x);
203
204 return S_OK;
205 }
206
207 /*************************************************************************
208 * UrlCanonicalizeA [SHLWAPI.@]
209 *
210 * Canonicalize a Url.
211 *
212 * PARAMS
213 * pszUrl [I] Url to cCanonicalize
214 * pszCanonicalized [O] Destination for converted Url.
215 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
216 * dwFlags [I] Flags controlling the conversion.
217 *
218 * RETURNS
219 * Success: S_OK. The pszCanonicalized contains the converted Url.
220 * Failure: E_POINTER, if *pcchCanonicalized is too small.
221 *
222 * MSDN incorrectly describes the flags for this function. They should be:
223 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
224 *| URL_ESCAPE_SPACES_ONLY 0x04000000
225 *| URL_ESCAPE_PERCENT 0x00001000
226 *| URL_ESCAPE_UNSAFE 0x10000000
227 *| URL_UNESCAPE 0x10000000
228 *| URL_DONT_SIMPLIFY 0x08000000
229 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
230 */
231 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
232 LPDWORD pcchCanonicalized, DWORD dwFlags)
233 {
234 LPWSTR base, canonical;
235 HRESULT ret;
236 DWORD len, len2;
237
238 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
239 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
240
241 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
242 return E_INVALIDARG;
243
244 base = HeapAlloc(GetProcessHeap(), 0,
245 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
246 canonical = base + INTERNET_MAX_URL_LENGTH;
247
248 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
249 len = INTERNET_MAX_URL_LENGTH;
250
251 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
252 if (ret != S_OK) {
253 *pcchCanonicalized = len * 2;
254 HeapFree(GetProcessHeap(), 0, base);
255 return ret;
256 }
257
258 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
259 if (len2 > *pcchCanonicalized) {
260 *pcchCanonicalized = len2;
261 HeapFree(GetProcessHeap(), 0, base);
262 return E_POINTER;
263 }
264 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
265 *pcchCanonicalized = len;
266 HeapFree(GetProcessHeap(), 0, base);
267 return S_OK;
268 }
269
270 /*************************************************************************
271 * UrlCanonicalizeW [SHLWAPI.@]
272 *
273 * See UrlCanonicalizeA.
274 */
275 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
276 LPDWORD pcchCanonicalized, DWORD dwFlags)
277 {
278 HRESULT hr = S_OK;
279 DWORD EscapeFlags;
280 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
281 INT state;
282 DWORD nByteLen, nLen, nWkLen;
283 WCHAR slash = '/';
284
285 static const WCHAR wszFile[] = {'f','i','l','e',':'};
286 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
287
288 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
289 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
290
291 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
292 return E_INVALIDARG;
293
294 if(!*pszUrl) {
295 *pszCanonicalized = 0;
296 return S_OK;
297 }
298
299 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
300 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
301 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
302
303 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
304 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
305 slash = '\\';
306
307 /*
308 * state =
309 * 0 initial 1,3
310 * 1 have 2[+] alnum 2,3
311 * 2 have scheme (found :) 4,6,3
312 * 3 failed (no location)
313 * 4 have // 5,3
314 * 5 have 1[+] alnum 6,3
315 * 6 have location (found /) save root location
316 */
317
318 wk1 = (LPWSTR)pszUrl;
319 wk2 = lpszUrlCpy;
320 state = 0;
321
322 if(pszUrl[1] == ':') { /* Assume path */
323 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
324
325 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
326 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
327 if (dwFlags & URL_FILE_USE_PATHURL)
328 {
329 slash = '\\';
330 --wk2;
331 }
332 else
333 dwFlags |= URL_ESCAPE_UNSAFE;
334 state = 5;
335 }
336
337 while (*wk1) {
338 switch (state) {
339 case 0:
340 if (!isalnumW(*wk1)) {state = 3; break;}
341 *wk2++ = *wk1++;
342 if (!isalnumW(*wk1)) {state = 3; break;}
343 *wk2++ = *wk1++;
344 state = 1;
345 break;
346 case 1:
347 *wk2++ = *wk1;
348 if (*wk1++ == ':') state = 2;
349 break;
350 case 2:
351 *wk2++ = *wk1++;
352 if (*wk1 != '/') {state = 6; break;}
353 *wk2++ = *wk1++;
354 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
355 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
356 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
357 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
358 wk1++;
359 }
360 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
361 wk1++;
362 state = 4;
363 break;
364 case 3:
365 nWkLen = strlenW(wk1);
366 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
367 mp = wk2;
368 wk1 += nWkLen;
369 wk2 += nWkLen;
370
371 while(mp < wk2) {
372 if(*mp == '/' || *mp == '\\')
373 *mp = slash;
374 mp++;
375 }
376 break;
377 case 4:
378 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
379 {state = 3; break;}
380 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
381 *wk2++ = *wk1++;
382 state = 5;
383 if (!*wk1)
384 *wk2++ = slash;
385 break;
386 case 5:
387 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
388 while(*wk1 == '/' || *wk1 == '\\') {
389 *wk2++ = slash;
390 wk1++;
391 }
392 state = 6;
393 break;
394 case 6:
395 if(dwFlags & URL_DONT_SIMPLIFY) {
396 state = 3;
397 break;
398 }
399
400 /* Now at root location, cannot back up any more. */
401 /* "root" will point at the '/' */
402
403 root = wk2-1;
404 while (*wk1) {
405 mp = strchrW(wk1, '/');
406 mp2 = strchrW(wk1, '\\');
407 if(mp2 && (!mp || mp2 < mp))
408 mp = mp2;
409 if (!mp) {
410 nWkLen = strlenW(wk1);
411 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
412 wk1 += nWkLen;
413 wk2 += nWkLen;
414 continue;
415 }
416 nLen = mp - wk1;
417 if(nLen) {
418 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
419 wk2 += nLen;
420 wk1 += nLen;
421 }
422 *wk2++ = slash;
423 wk1++;
424
425 if (*wk1 == '.') {
426 TRACE("found '/.'\n");
427 if (wk1[1] == '/' || wk1[1] == '\\') {
428 /* case of /./ -> skip the ./ */
429 wk1 += 2;
430 }
431 else if (wk1[1] == '.') {
432 /* found /.. look for next / */
433 TRACE("found '/..'\n");
434 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
435 || wk1[2] == '#' || !wk1[2]) {
436 /* case /../ -> need to backup wk2 */
437 TRACE("found '/../'\n");
438 *(wk2-1) = '\0'; /* set end of string */
439 mp = strrchrW(root, slash);
440 if (mp && (mp >= root)) {
441 /* found valid backup point */
442 wk2 = mp + 1;
443 if(wk1[2] != '/' && wk1[2] != '\\')
444 wk1 += 2;
445 else
446 wk1 += 3;
447 }
448 else {
449 /* did not find point, restore '/' */
450 *(wk2-1) = slash;
451 }
452 }
453 }
454 }
455 }
456 *wk2 = '\0';
457 break;
458 default:
459 FIXME("how did we get here - state=%d\n", state);
460 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
461 return E_INVALIDARG;
462 }
463 *wk2 = '\0';
464 TRACE("Simplified, orig <%s>, simple <%s>\n",
465 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
466 }
467 nLen = lstrlenW(lpszUrlCpy);
468 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
469 lpszUrlCpy[--nLen]=0;
470
471 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
472 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
473
474 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
475 URL_ESCAPE_SPACES_ONLY |
476 URL_ESCAPE_PERCENT |
477 URL_DONT_ESCAPE_EXTRA_INFO |
478 URL_ESCAPE_SEGMENT_ONLY ))) {
479 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
480 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
481 EscapeFlags);
482 } else { /* No escaping needed, just copy the string */
483 nLen = lstrlenW(lpszUrlCpy);
484 if(nLen < *pcchCanonicalized)
485 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
486 else {
487 hr = E_POINTER;
488 nLen++;
489 }
490 *pcchCanonicalized = nLen;
491 }
492
493 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
494
495 if (hr == S_OK)
496 TRACE("result %s\n", debugstr_w(pszCanonicalized));
497
498 return hr;
499 }
500
501 /*************************************************************************
502 * UrlCombineA [SHLWAPI.@]
503 *
504 * Combine two Urls.
505 *
506 * PARAMS
507 * pszBase [I] Base Url
508 * pszRelative [I] Url to combine with pszBase
509 * pszCombined [O] Destination for combined Url
510 * pcchCombined [O] Destination for length of pszCombined
511 * dwFlags [I] URL_ flags from "shlwapi.h"
512 *
513 * RETURNS
514 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
515 * contains its length.
516 * Failure: An HRESULT error code indicating the error.
517 */
518 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
519 LPSTR pszCombined, LPDWORD pcchCombined,
520 DWORD dwFlags)
521 {
522 LPWSTR base, relative, combined;
523 DWORD ret, len, len2;
524
525 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
526 debugstr_a(pszBase),debugstr_a(pszRelative),
527 pcchCombined?*pcchCombined:0,dwFlags);
528
529 if(!pszBase || !pszRelative || !pcchCombined)
530 return E_INVALIDARG;
531
532 base = HeapAlloc(GetProcessHeap(), 0,
533 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
534 relative = base + INTERNET_MAX_URL_LENGTH;
535 combined = relative + INTERNET_MAX_URL_LENGTH;
536
537 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
538 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
539 len = *pcchCombined;
540
541 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
542 if (ret != S_OK) {
543 *pcchCombined = len;
544 HeapFree(GetProcessHeap(), 0, base);
545 return ret;
546 }
547
548 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
549 if (len2 > *pcchCombined) {
550 *pcchCombined = len2;
551 HeapFree(GetProcessHeap(), 0, base);
552 return E_POINTER;
553 }
554 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
555 0, 0);
556 *pcchCombined = len2;
557 HeapFree(GetProcessHeap(), 0, base);
558 return S_OK;
559 }
560
561 /*************************************************************************
562 * UrlCombineW [SHLWAPI.@]
563 *
564 * See UrlCombineA.
565 */
566 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
567 LPWSTR pszCombined, LPDWORD pcchCombined,
568 DWORD dwFlags)
569 {
570 PARSEDURLW base, relative;
571 DWORD myflags, sizeloc = 0;
572 DWORD len, res1, res2, process_case = 0;
573 LPWSTR work, preliminary, mbase, mrelative;
574 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
575 HRESULT ret;
576
577 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
578 debugstr_w(pszBase),debugstr_w(pszRelative),
579 pcchCombined?*pcchCombined:0,dwFlags);
580
581 if(!pszBase || !pszRelative || !pcchCombined)
582 return E_INVALIDARG;
583
584 base.cbSize = sizeof(base);
585 relative.cbSize = sizeof(relative);
586
587 /* Get space for duplicates of the input and the output */
588 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
589 sizeof(WCHAR));
590 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
591 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
592 *preliminary = '\0';
593
594 /* Canonicalize the base input prior to looking for the scheme */
595 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
596 len = INTERNET_MAX_URL_LENGTH;
597 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
598
599 /* Canonicalize the relative input prior to looking for the scheme */
600 len = INTERNET_MAX_URL_LENGTH;
601 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
602
603 /* See if the base has a scheme */
604 res1 = ParseURLW(mbase, &base);
605 if (res1) {
606 /* if pszBase has no scheme, then return pszRelative */
607 TRACE("no scheme detected in Base\n");
608 process_case = 1;
609 }
610 else do {
611 /* mk is a special case */
612 if(base.nScheme == URL_SCHEME_MK) {
613 static const WCHAR wsz[] = {':',':',0};
614
615 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
616 if(ptr) {
617 int delta;
618
619 ptr += 2;
620 delta = ptr-base.pszSuffix;
621 base.cchProtocol += delta;
622 base.pszSuffix += delta;
623 base.cchSuffix -= delta;
624 }
625 }else {
626 /* get size of location field (if it exists) */
627 work = (LPWSTR)base.pszSuffix;
628 sizeloc = 0;
629 if (*work++ == '/') {
630 if (*work++ == '/') {
631 /* At this point have start of location and
632 * it ends at next '/' or end of string.
633 */
634 while(*work && (*work != '/')) work++;
635 sizeloc = (DWORD)(work - base.pszSuffix);
636 }
637 }
638 }
639
640 /* Change .sizep2 to not have the last leaf in it,
641 * Note: we need to start after the location (if it exists)
642 */
643 work = strrchrW((base.pszSuffix+sizeloc), '/');
644 if (work) {
645 len = (DWORD)(work - base.pszSuffix + 1);
646 base.cchSuffix = len;
647 }
648
649 /*
650 * At this point:
651 * .pszSuffix points to location (starting with '//')
652 * .cchSuffix length of location (above) and rest less the last
653 * leaf (if any)
654 * sizeloc length of location (above) up to but not including
655 * the last '/'
656 */
657
658 res2 = ParseURLW(mrelative, &relative);
659 if (res2) {
660 /* no scheme in pszRelative */
661 TRACE("no scheme detected in Relative\n");
662 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
663 relative.cchSuffix = strlenW(mrelative);
664 if (*pszRelative == ':') {
665 /* case that is either left alone or uses pszBase */
666 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
667 process_case = 5;
668 break;
669 }
670 process_case = 1;
671 break;
672 }
673 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
674 /* case that becomes "file:///" */
675 strcpyW(preliminary, myfilestr);
676 process_case = 1;
677 break;
678 }
679 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
680 /* pszRelative has location and rest */
681 process_case = 3;
682 break;
683 }
684 if (*mrelative == '/') {
685 /* case where pszRelative is root to location */
686 process_case = 4;
687 break;
688 }
689 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
690 break;
691 }
692
693 /* handle cases where pszRelative has scheme */
694 if ((base.cchProtocol == relative.cchProtocol) &&
695 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
696
697 /* since the schemes are the same */
698 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
699 /* case where pszRelative replaces location and following */
700 process_case = 3;
701 break;
702 }
703 if (*relative.pszSuffix == '/') {
704 /* case where pszRelative is root to location */
705 process_case = 4;
706 break;
707 }
708 /* replace either just location if base's location starts with a
709 * slash or otherwise everything */
710 process_case = (*base.pszSuffix == '/') ? 5 : 1;
711 break;
712 }
713 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
714 /* case where pszRelative replaces scheme, location,
715 * and following and handles PLUGGABLE
716 */
717 process_case = 2;
718 break;
719 }
720 process_case = 1;
721 break;
722 } while(FALSE); /* a little trick to allow easy exit from nested if's */
723
724 ret = S_OK;
725 switch (process_case) {
726
727 case 1: /*
728 * Return pszRelative appended to what ever is in pszCombined,
729 * (which may the string "file:///"
730 */
731 strcatW(preliminary, mrelative);
732 break;
733
734 case 2: /* case where pszRelative replaces scheme, and location */
735 strcpyW(preliminary, mrelative);
736 break;
737
738 case 3: /*
739 * Return the pszBase scheme with pszRelative. Basically
740 * keeps the scheme and replaces the domain and following.
741 */
742 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
743 work = preliminary + base.cchProtocol + 1;
744 strcpyW(work, relative.pszSuffix);
745 break;
746
747 case 4: /*
748 * Return the pszBase scheme and location but everything
749 * after the location is pszRelative. (Replace document
750 * from root on.)
751 */
752 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
753 work = preliminary + base.cchProtocol + 1 + sizeloc;
754 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
755 *(work++) = '/';
756 strcpyW(work, relative.pszSuffix);
757 break;
758
759 case 5: /*
760 * Return the pszBase without its document (if any) and
761 * append pszRelative after its scheme.
762 */
763 memcpy(preliminary, base.pszProtocol,
764 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
765 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
766 if (*work++ != '/')
767 *(work++) = '/';
768 strcpyW(work, relative.pszSuffix);
769 break;
770
771 default:
772 FIXME("How did we get here????? process_case=%d\n", process_case);
773 ret = E_INVALIDARG;
774 }
775
776 if (ret == S_OK) {
777 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
778 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
779 if(SUCCEEDED(ret) && pszCombined) {
780 lstrcpyW(pszCombined, mrelative);
781 }
782 TRACE("return-%d len=%d, %s\n",
783 process_case, *pcchCombined, debugstr_w(pszCombined));
784 }
785 HeapFree(GetProcessHeap(), 0, preliminary);
786 return ret;
787 }
788
789 /*************************************************************************
790 * UrlEscapeA [SHLWAPI.@]
791 */
792
793 HRESULT WINAPI UrlEscapeA(
794 LPCSTR pszUrl,
795 LPSTR pszEscaped,
796 LPDWORD pcchEscaped,
797 DWORD dwFlags)
798 {
799 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
800 WCHAR *escapedW = bufW;
801 UNICODE_STRING urlW;
802 HRESULT ret;
803 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
804
805 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
806 return E_INVALIDARG;
807
808 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
809 return E_INVALIDARG;
810 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
811 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
812 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
813 }
814 if(ret == S_OK) {
815 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
816 if(*pcchEscaped > lenA) {
817 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
818 pszEscaped[lenA] = 0;
819 *pcchEscaped = lenA;
820 } else {
821 *pcchEscaped = lenA + 1;
822 ret = E_POINTER;
823 }
824 }
825 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
826 RtlFreeUnicodeString(&urlW);
827 return ret;
828 }
829
830 #define WINE_URL_BASH_AS_SLASH 0x01
831 #define WINE_URL_COLLAPSE_SLASHES 0x02
832 #define WINE_URL_ESCAPE_SLASH 0x04
833 #define WINE_URL_ESCAPE_HASH 0x08
834 #define WINE_URL_ESCAPE_QUESTION 0x10
835 #define WINE_URL_STOP_ON_HASH 0x20
836 #define WINE_URL_STOP_ON_QUESTION 0x40
837
838 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
839 {
840
841 if (isalnumW(ch))
842 return FALSE;
843
844 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
845 if(ch == ' ')
846 return TRUE;
847 else
848 return FALSE;
849 }
850
851 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
852 return TRUE;
853
854 if (ch <= 31 || ch >= 127)
855 return TRUE;
856
857 else {
858 switch (ch) {
859 case ' ':
860 case '<':
861 case '>':
862 case '\"':
863 case '{':
864 case '}':
865 case '|':
866 case '\\':
867 case '^':
868 case ']':
869 case '[':
870 case '`':
871 case '&':
872 return TRUE;
873
874 case '/':
875 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
876 return FALSE;
877
878 case '?':
879 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
880 return FALSE;
881
882 case '#':
883 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
884 return FALSE;
885
886 default:
887 return FALSE;
888 }
889 }
890 }
891
892
893 /*************************************************************************
894 * UrlEscapeW [SHLWAPI.@]
895 *
896 * Converts unsafe characters in a Url into escape sequences.
897 *
898 * PARAMS
899 * pszUrl [I] Url to modify
900 * pszEscaped [O] Destination for modified Url
901 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
902 * dwFlags [I] URL_ flags from "shlwapi.h"
903 *
904 * RETURNS
905 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
906 * contains its length.
907 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
908 * pcchEscaped is set to the required length.
909 *
910 * Converts unsafe characters into their escape sequences.
911 *
912 * NOTES
913 * - By default this function stops converting at the first '?' or
914 * '#' character.
915 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
916 * converted, but the conversion continues past a '?' or '#'.
917 * - Note that this function did not work well (or at all) in shlwapi version 4.
918 *
919 * BUGS
920 * Only the following flags are implemented:
921 *| URL_ESCAPE_SPACES_ONLY
922 *| URL_DONT_ESCAPE_EXTRA_INFO
923 *| URL_ESCAPE_SEGMENT_ONLY
924 *| URL_ESCAPE_PERCENT
925 */
926 HRESULT WINAPI UrlEscapeW(
927 LPCWSTR pszUrl,
928 LPWSTR pszEscaped,
929 LPDWORD pcchEscaped,
930 DWORD dwFlags)
931 {
932 LPCWSTR src;
933 DWORD needed = 0, ret;
934 BOOL stop_escaping = FALSE;
935 WCHAR next[5], *dst = pszEscaped;
936 INT len;
937 PARSEDURLW parsed_url;
938 DWORD int_flags;
939 DWORD slashes = 0;
940 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
941
942 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
943 pcchEscaped, dwFlags);
944
945 if(!pszUrl || !pcchEscaped)
946 return E_INVALIDARG;
947
948 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
949 URL_ESCAPE_SEGMENT_ONLY |
950 URL_DONT_ESCAPE_EXTRA_INFO |
951 URL_ESCAPE_PERCENT))
952 FIXME("Unimplemented flags: %08x\n", dwFlags);
953
954 /* fix up flags */
955 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
956 /* if SPACES_ONLY specified, reset the other controls */
957 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
958 URL_ESCAPE_PERCENT |
959 URL_ESCAPE_SEGMENT_ONLY);
960
961 else
962 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
963 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
964
965
966 int_flags = 0;
967 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
968 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
969 } else {
970 parsed_url.cbSize = sizeof(parsed_url);
971 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
972 parsed_url.nScheme = URL_SCHEME_INVALID;
973
974 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
975
976 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
977 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
978
979 switch(parsed_url.nScheme) {
980 case URL_SCHEME_FILE:
981 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
982 int_flags &= ~WINE_URL_STOP_ON_HASH;
983 break;
984
985 case URL_SCHEME_HTTP:
986 case URL_SCHEME_HTTPS:
987 int_flags |= WINE_URL_BASH_AS_SLASH;
988 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
989 int_flags |= WINE_URL_ESCAPE_SLASH;
990 break;
991
992 case URL_SCHEME_MAILTO:
993 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
994 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
995 break;
996
997 case URL_SCHEME_INVALID:
998 break;
999
1000 case URL_SCHEME_FTP:
1001 default:
1002 if(parsed_url.pszSuffix[0] != '/')
1003 int_flags |= WINE_URL_ESCAPE_SLASH;
1004 break;
1005 }
1006 }
1007
1008 for(src = pszUrl; *src; ) {
1009 WCHAR cur = *src;
1010 len = 0;
1011
1012 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1013 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1014 while(cur == '/' || cur == '\\') {
1015 slashes++;
1016 cur = *++src;
1017 }
1018 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1019 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1020 src += localhost_len + 1;
1021 slashes = 3;
1022 }
1023
1024 switch(slashes) {
1025 case 1:
1026 case 3:
1027 next[0] = next[1] = next[2] = '/';
1028 len = 3;
1029 break;
1030 case 0:
1031 len = 0;
1032 break;
1033 default:
1034 next[0] = next[1] = '/';
1035 len = 2;
1036 break;
1037 }
1038 }
1039 if(len == 0) {
1040
1041 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1042 stop_escaping = TRUE;
1043
1044 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1045 stop_escaping = TRUE;
1046
1047 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1048
1049 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1050 next[0] = '%';
1051 next[1] = hexDigits[(cur >> 4) & 0xf];
1052 next[2] = hexDigits[cur & 0xf];
1053 len = 3;
1054 } else {
1055 next[0] = cur;
1056 len = 1;
1057 }
1058 src++;
1059 }
1060
1061 if(needed + len <= *pcchEscaped) {
1062 memcpy(dst, next, len*sizeof(WCHAR));
1063 dst += len;
1064 }
1065 needed += len;
1066 }
1067
1068 if(needed < *pcchEscaped) {
1069 *dst = '\0';
1070 ret = S_OK;
1071 } else {
1072 needed++; /* add one for the '\0' */
1073 ret = E_POINTER;
1074 }
1075 *pcchEscaped = needed;
1076 return ret;
1077 }
1078
1079
1080 /*************************************************************************
1081 * UrlUnescapeA [SHLWAPI.@]
1082 *
1083 * Converts Url escape sequences back to ordinary characters.
1084 *
1085 * PARAMS
1086 * pszUrl [I/O] Url to convert
1087 * pszUnescaped [O] Destination for converted Url
1088 * pcchUnescaped [I/O] Size of output string
1089 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1090 *
1091 * RETURNS
1092 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1093 * dwFlags includes URL_ESCAPE_INPLACE.
1094 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1095 * this case pcchUnescaped is set to the size required.
1096 * NOTES
1097 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1098 * the first occurrence of either a '?' or '#' character.
1099 */
1100 HRESULT WINAPI UrlUnescapeA(
1101 LPSTR pszUrl,
1102 LPSTR pszUnescaped,
1103 LPDWORD pcchUnescaped,
1104 DWORD dwFlags)
1105 {
1106 char *dst, next;
1107 LPCSTR src;
1108 HRESULT ret;
1109 DWORD needed;
1110 BOOL stop_unescaping = FALSE;
1111
1112 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1113 pcchUnescaped, dwFlags);
1114
1115 if (!pszUrl) return E_INVALIDARG;
1116
1117 if(dwFlags & URL_UNESCAPE_INPLACE)
1118 dst = pszUrl;
1119 else
1120 {
1121 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1122 dst = pszUnescaped;
1123 }
1124
1125 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1126 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1127 (*src == '#' || *src == '?')) {
1128 stop_unescaping = TRUE;
1129 next = *src;
1130 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1131 && stop_unescaping == FALSE) {
1132 INT ih;
1133 char buf[3];
1134 memcpy(buf, src + 1, 2);
1135 buf[2] = '\0';
1136 ih = strtol(buf, NULL, 16);
1137 next = (CHAR) ih;
1138 src += 2; /* Advance to end of escape */
1139 } else
1140 next = *src;
1141
1142 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1143 *dst++ = next;
1144 }
1145
1146 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1147 *dst = '\0';
1148 ret = S_OK;
1149 } else {
1150 needed++; /* add one for the '\0' */
1151 ret = E_POINTER;
1152 }
1153 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1154 *pcchUnescaped = needed;
1155
1156 if (ret == S_OK) {
1157 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1158 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1159 }
1160
1161 return ret;
1162 }
1163
1164 /*************************************************************************
1165 * UrlUnescapeW [SHLWAPI.@]
1166 *
1167 * See UrlUnescapeA.
1168 */
1169 HRESULT WINAPI UrlUnescapeW(
1170 LPWSTR pszUrl,
1171 LPWSTR pszUnescaped,
1172 LPDWORD pcchUnescaped,
1173 DWORD dwFlags)
1174 {
1175 WCHAR *dst, next;
1176 LPCWSTR src;
1177 HRESULT ret;
1178 DWORD needed;
1179 BOOL stop_unescaping = FALSE;
1180
1181 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1182 pcchUnescaped, dwFlags);
1183
1184 if(!pszUrl) return E_INVALIDARG;
1185
1186 if(dwFlags & URL_UNESCAPE_INPLACE)
1187 dst = pszUrl;
1188 else
1189 {
1190 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1191 dst = pszUnescaped;
1192 }
1193
1194 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1195 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1196 (*src == '#' || *src == '?')) {
1197 stop_unescaping = TRUE;
1198 next = *src;
1199 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1200 && stop_unescaping == FALSE) {
1201 INT ih;
1202 WCHAR buf[5] = {'0','x',0};
1203 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1204 buf[4] = 0;
1205 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1206 next = (WCHAR) ih;
1207 src += 2; /* Advance to end of escape */
1208 } else
1209 next = *src;
1210
1211 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1212 *dst++ = next;
1213 }
1214
1215 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1216 *dst = '\0';
1217 ret = S_OK;
1218 } else {
1219 needed++; /* add one for the '\0' */
1220 ret = E_POINTER;
1221 }
1222 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1223 *pcchUnescaped = needed;
1224
1225 if (ret == S_OK) {
1226 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1227 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1228 }
1229
1230 return ret;
1231 }
1232
1233 /*************************************************************************
1234 * UrlGetLocationA [SHLWAPI.@]
1235 *
1236 * Get the location from a Url.
1237 *
1238 * PARAMS
1239 * pszUrl [I] Url to get the location from
1240 *
1241 * RETURNS
1242 * A pointer to the start of the location in pszUrl, or NULL if there is
1243 * no location.
1244 *
1245 * NOTES
1246 * - MSDN erroneously states that "The location is the segment of the Url
1247 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1248 * stop at '?' and always return a NULL in this case.
1249 * - MSDN also erroneously states that "If a file URL has a query string,
1250 * the returned string is the query string". In all tested cases, if the
1251 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1252 *| Result Url
1253 *| ------ ---
1254 *| NULL file://aa/b/cd#hohoh
1255 *| #hohoh http://aa/b/cd#hohoh
1256 *| NULL fi://aa/b/cd#hohoh
1257 *| #hohoh ff://aa/b/cd#hohoh
1258 */
1259 LPCSTR WINAPI UrlGetLocationA(
1260 LPCSTR pszUrl)
1261 {
1262 PARSEDURLA base;
1263 DWORD res1;
1264
1265 base.cbSize = sizeof(base);
1266 res1 = ParseURLA(pszUrl, &base);
1267 if (res1) return NULL; /* invalid scheme */
1268
1269 /* if scheme is file: then never return pointer */
1270 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1271
1272 /* Look for '#' and return its addr */
1273 return strchr(base.pszSuffix, '#');
1274 }
1275
1276 /*************************************************************************
1277 * UrlGetLocationW [SHLWAPI.@]
1278 *
1279 * See UrlGetLocationA.
1280 */
1281 LPCWSTR WINAPI UrlGetLocationW(
1282 LPCWSTR pszUrl)
1283 {
1284 PARSEDURLW base;
1285 DWORD res1;
1286
1287 base.cbSize = sizeof(base);
1288 res1 = ParseURLW(pszUrl, &base);
1289 if (res1) return NULL; /* invalid scheme */
1290
1291 /* if scheme is file: then never return pointer */
1292 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1293
1294 /* Look for '#' and return its addr */
1295 return strchrW(base.pszSuffix, '#');
1296 }
1297
1298 /*************************************************************************
1299 * UrlCompareA [SHLWAPI.@]
1300 *
1301 * Compare two Urls.
1302 *
1303 * PARAMS
1304 * pszUrl1 [I] First Url to compare
1305 * pszUrl2 [I] Url to compare to pszUrl1
1306 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1307 *
1308 * RETURNS
1309 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1310 * than, equal to, or less than pszUrl1 respectively.
1311 */
1312 INT WINAPI UrlCompareA(
1313 LPCSTR pszUrl1,
1314 LPCSTR pszUrl2,
1315 BOOL fIgnoreSlash)
1316 {
1317 INT ret, len, len1, len2;
1318
1319 if (!fIgnoreSlash)
1320 return strcmp(pszUrl1, pszUrl2);
1321 len1 = strlen(pszUrl1);
1322 if (pszUrl1[len1-1] == '/') len1--;
1323 len2 = strlen(pszUrl2);
1324 if (pszUrl2[len2-1] == '/') len2--;
1325 if (len1 == len2)
1326 return strncmp(pszUrl1, pszUrl2, len1);
1327 len = min(len1, len2);
1328 ret = strncmp(pszUrl1, pszUrl2, len);
1329 if (ret) return ret;
1330 if (len1 > len2) return 1;
1331 return -1;
1332 }
1333
1334 /*************************************************************************
1335 * UrlCompareW [SHLWAPI.@]
1336 *
1337 * See UrlCompareA.
1338 */
1339 INT WINAPI UrlCompareW(
1340 LPCWSTR pszUrl1,
1341 LPCWSTR pszUrl2,
1342 BOOL fIgnoreSlash)
1343 {
1344 INT ret;
1345 size_t len, len1, len2;
1346
1347 if (!fIgnoreSlash)
1348 return strcmpW(pszUrl1, pszUrl2);
1349 len1 = strlenW(pszUrl1);
1350 if (pszUrl1[len1-1] == '/') len1--;
1351 len2 = strlenW(pszUrl2);
1352 if (pszUrl2[len2-1] == '/') len2--;
1353 if (len1 == len2)
1354 return strncmpW(pszUrl1, pszUrl2, len1);
1355 len = min(len1, len2);
1356 ret = strncmpW(pszUrl1, pszUrl2, len);
1357 if (ret) return ret;
1358 if (len1 > len2) return 1;
1359 return -1;
1360 }
1361
1362 /*************************************************************************
1363 * HashData [SHLWAPI.@]
1364 *
1365 * Hash an input block into a variable sized digest.
1366 *
1367 * PARAMS
1368 * lpSrc [I] Input block
1369 * nSrcLen [I] Length of lpSrc
1370 * lpDest [I] Output for hash digest
1371 * nDestLen [I] Length of lpDest
1372 *
1373 * RETURNS
1374 * Success: TRUE. lpDest is filled with the computed hash value.
1375 * Failure: FALSE, if any argument is invalid.
1376 */
1377 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1378 unsigned char *lpDest, DWORD nDestLen)
1379 {
1380 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1381
1382 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1383 IsBadWritePtr(lpDest, nDestLen))
1384 return E_INVALIDARG;
1385
1386 while (destCount >= 0)
1387 {
1388 lpDest[destCount] = (destCount & 0xff);
1389 destCount--;
1390 }
1391
1392 while (srcCount >= 0)
1393 {
1394 destCount = nDestLen - 1;
1395 while (destCount >= 0)
1396 {
1397 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1398 destCount--;
1399 }
1400 srcCount--;
1401 }
1402 return S_OK;
1403 }
1404
1405 /*************************************************************************
1406 * UrlHashA [SHLWAPI.@]
1407 *
1408 * Produce a Hash from a Url.
1409 *
1410 * PARAMS
1411 * pszUrl [I] Url to hash
1412 * lpDest [O] Destinationh for hash
1413 * nDestLen [I] Length of lpDest
1414 *
1415 * RETURNS
1416 * Success: S_OK. lpDest is filled with the computed hash value.
1417 * Failure: E_INVALIDARG, if any argument is invalid.
1418 */
1419 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1420 {
1421 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1422 return E_INVALIDARG;
1423
1424 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1425 return S_OK;
1426 }
1427
1428 /*************************************************************************
1429 * UrlHashW [SHLWAPI.@]
1430 *
1431 * See UrlHashA.
1432 */
1433 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1434 {
1435 char szUrl[MAX_PATH];
1436
1437 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1438
1439 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1440 return E_INVALIDARG;
1441
1442 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1443 * return the same digests for the same URL.
1444 */
1445 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1446 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1447 return S_OK;
1448 }
1449
1450 /*************************************************************************
1451 * UrlApplySchemeA [SHLWAPI.@]
1452 *
1453 * Apply a scheme to a Url.
1454 *
1455 * PARAMS
1456 * pszIn [I] Url to apply scheme to
1457 * pszOut [O] Destination for modified Url
1458 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1459 * dwFlags [I] URL_ flags from "shlwapi.h"
1460 *
1461 * RETURNS
1462 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1463 * Failure: An HRESULT error code describing the error.
1464 */
1465 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1466 {
1467 LPWSTR in, out;
1468 HRESULT ret;
1469 DWORD len;
1470
1471 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1472 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1473
1474 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1475
1476 in = HeapAlloc(GetProcessHeap(), 0,
1477 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1478 out = in + INTERNET_MAX_URL_LENGTH;
1479
1480 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1481 len = INTERNET_MAX_URL_LENGTH;
1482
1483 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1484 if (ret != S_OK) {
1485 HeapFree(GetProcessHeap(), 0, in);
1486 return ret;
1487 }
1488
1489 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1490 if (len > *pcchOut) {
1491 ret = E_POINTER;
1492 goto cleanup;
1493 }
1494
1495 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1496 len--;
1497
1498 cleanup:
1499 *pcchOut = len;
1500 HeapFree(GetProcessHeap(), 0, in);
1501 return ret;
1502 }
1503
1504 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1505 {
1506 HKEY newkey;
1507 BOOL j;
1508 INT index;
1509 DWORD value_len, data_len, dwType, i;
1510 WCHAR reg_path[MAX_PATH];
1511 WCHAR value[MAX_PATH], data[MAX_PATH];
1512 WCHAR Wxx, Wyy;
1513
1514 MultiByteToWideChar(0, 0,
1515 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1516 -1, reg_path, MAX_PATH);
1517 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1518 index = 0;
1519 while(value_len = data_len = MAX_PATH,
1520 RegEnumValueW(newkey, index, value, &value_len,
1521 0, &dwType, (LPVOID)data, &data_len) == 0) {
1522 TRACE("guess %d %s is %s\n",
1523 index, debugstr_w(value), debugstr_w(data));
1524
1525 j = FALSE;
1526 for(i=0; i<value_len; i++) {
1527 Wxx = pszIn[i];
1528 Wyy = value[i];
1529 /* remember that TRUE is not-equal */
1530 j = ChrCmpIW(Wxx, Wyy);
1531 if (j) break;
1532 }
1533 if ((i == value_len) && !j) {
1534 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1535 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1536 RegCloseKey(newkey);
1537 return E_POINTER;
1538 }
1539 strcpyW(pszOut, data);
1540 strcatW(pszOut, pszIn);
1541 *pcchOut = strlenW(pszOut);
1542 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1543 RegCloseKey(newkey);
1544 return S_OK;
1545 }
1546 index++;
1547 }
1548 RegCloseKey(newkey);
1549 return E_FAIL;
1550 }
1551
1552 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1553 {
1554 HKEY newkey;
1555 DWORD data_len, dwType;
1556 WCHAR data[MAX_PATH];
1557
1558 static const WCHAR prefix_keyW[] =
1559 {'S','o','f','t','w','a','r','e',
1560 '\\','M','i','c','r','o','s','o','f','t',
1561 '\\','W','i','n','d','o','w','s',
1562 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1563 '\\','U','R','L',
1564 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1565
1566 /* get and prepend default */
1567 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1568 data_len = sizeof(data);
1569 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1570 RegCloseKey(newkey);
1571 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1572 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1573 return E_POINTER;
1574 }
1575 strcpyW(pszOut, data);
1576 strcatW(pszOut, pszIn);
1577 *pcchOut = strlenW(pszOut);
1578 TRACE("used default %s\n", debugstr_w(pszOut));
1579 return S_OK;
1580 }
1581
1582 /*************************************************************************
1583 * UrlApplySchemeW [SHLWAPI.@]
1584 *
1585 * See UrlApplySchemeA.
1586 */
1587 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1588 {
1589 PARSEDURLW in_scheme;
1590 DWORD res1;
1591 HRESULT ret;
1592
1593 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1594 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1595
1596 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1597
1598 if (dwFlags & URL_APPLY_GUESSFILE) {
1599 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1600 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1601 strcpyW(pszOut, pszIn);
1602 *pcchOut = strlenW(pszOut);
1603 return S_FALSE;
1604 }
1605
1606 in_scheme.cbSize = sizeof(in_scheme);
1607 /* See if the base has a scheme */
1608 res1 = ParseURLW(pszIn, &in_scheme);
1609 if (res1) {
1610 /* no scheme in input, need to see if we need to guess */
1611 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1612 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1613 return ret;
1614 }
1615 }
1616 else {
1617 /* we have a scheme, see if valid (known scheme) */
1618 if (in_scheme.nScheme) {
1619 /* have valid scheme, so just copy and exit */
1620 if (strlenW(pszIn) + 1 > *pcchOut) {
1621 *pcchOut = strlenW(pszIn) + 1;
1622 return E_POINTER;
1623 }
1624 strcpyW(pszOut, pszIn);
1625 *pcchOut = strlenW(pszOut);
1626 TRACE("valid scheme, returning copy\n");
1627 return S_OK;
1628 }
1629 }
1630
1631 /* If we are here, then either invalid scheme,
1632 * or no scheme and can't/failed guess.
1633 */
1634 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1635 ((res1 != 0)) ) &&
1636 (dwFlags & URL_APPLY_DEFAULT)) {
1637 /* find and apply default scheme */
1638 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1639 }
1640
1641 return S_FALSE;
1642 }
1643
1644 /*************************************************************************
1645 * UrlIsA [SHLWAPI.@]
1646 *
1647 * Determine if a Url is of a certain class.
1648 *
1649 * PARAMS
1650 * pszUrl [I] Url to check
1651 * Urlis [I] URLIS_ constant from "shlwapi.h"
1652 *
1653 * RETURNS
1654 * TRUE if pszUrl belongs to the class type in Urlis.
1655 * FALSE Otherwise.
1656 */
1657 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1658 {
1659 PARSEDURLA base;
1660 DWORD res1;
1661 LPCSTR last;
1662
1663 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1664
1665 switch (Urlis) {
1666
1667 case URLIS_OPAQUE:
1668 base.cbSize = sizeof(base);
1669 res1 = ParseURLA(pszUrl, &base);
1670 if (res1) return FALSE; /* invalid scheme */
1671 switch (base.nScheme)
1672 {
1673 case URL_SCHEME_MAILTO:
1674 case URL_SCHEME_SHELL:
1675 case URL_SCHEME_JAVASCRIPT:
1676 case URL_SCHEME_VBSCRIPT:
1677 case URL_SCHEME_ABOUT:
1678 return TRUE;
1679 }
1680 return FALSE;
1681
1682 case URLIS_FILEURL:
1683 return !StrCmpNA("file:", pszUrl, 5);
1684
1685 case URLIS_DIRECTORY:
1686 last = pszUrl + strlen(pszUrl) - 1;
1687 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1688
1689 case URLIS_URL:
1690 return PathIsURLA(pszUrl);
1691
1692 case URLIS_NOHISTORY:
1693 case URLIS_APPLIABLE:
1694 case URLIS_HASQUERY:
1695 default:
1696 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1697 }
1698 return FALSE;
1699 }
1700
1701 /*************************************************************************
1702 * UrlIsW [SHLWAPI.@]
1703 *
1704 * See UrlIsA.
1705 */
1706 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1707 {
1708 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1709 PARSEDURLW base;
1710 DWORD res1;
1711 LPCWSTR last;
1712
1713 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1714
1715 switch (Urlis) {
1716
1717 case URLIS_OPAQUE:
1718 base.cbSize = sizeof(base);
1719 res1 = ParseURLW(pszUrl, &base);
1720 if (res1) return FALSE; /* invalid scheme */
1721 switch (base.nScheme)
1722 {
1723 case URL_SCHEME_MAILTO:
1724 case URL_SCHEME_SHELL:
1725 case URL_SCHEME_JAVASCRIPT:
1726 case URL_SCHEME_VBSCRIPT:
1727 case URL_SCHEME_ABOUT:
1728 return TRUE;
1729 }
1730 return FALSE;
1731
1732 case URLIS_FILEURL:
1733 return !strncmpW(stemp, pszUrl, 5);
1734
1735 case URLIS_DIRECTORY:
1736 last = pszUrl + strlenW(pszUrl) - 1;
1737 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1738
1739 case URLIS_URL:
1740 return PathIsURLW(pszUrl);
1741
1742 case URLIS_NOHISTORY:
1743 case URLIS_APPLIABLE:
1744 case URLIS_HASQUERY:
1745 default:
1746 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1747 }
1748 return FALSE;
1749 }
1750
1751 /*************************************************************************
1752 * UrlIsNoHistoryA [SHLWAPI.@]
1753 *
1754 * Determine if a Url should not be stored in the users history list.
1755 *
1756 * PARAMS
1757 * pszUrl [I] Url to check
1758 *
1759 * RETURNS
1760 * TRUE, if pszUrl should be excluded from the history list,
1761 * FALSE otherwise.
1762 */
1763 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1764 {
1765 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1766 }
1767
1768 /*************************************************************************
1769 * UrlIsNoHistoryW [SHLWAPI.@]
1770 *
1771 * See UrlIsNoHistoryA.
1772 */
1773 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1774 {
1775 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1776 }
1777
1778 /*************************************************************************
1779 * UrlIsOpaqueA [SHLWAPI.@]
1780 *
1781 * Determine if a Url is opaque.
1782 *
1783 * PARAMS
1784 * pszUrl [I] Url to check
1785 *
1786 * RETURNS
1787 * TRUE if pszUrl is opaque,
1788 * FALSE Otherwise.
1789 *
1790 * NOTES
1791 * An opaque Url is one that does not start with "<protocol>://".
1792 */
1793 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1794 {
1795 return UrlIsA(pszUrl, URLIS_OPAQUE);
1796 }
1797
1798 /*************************************************************************
1799 * UrlIsOpaqueW [SHLWAPI.@]
1800 *
1801 * See UrlIsOpaqueA.
1802 */
1803 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1804 {
1805 return UrlIsW(pszUrl, URLIS_OPAQUE);
1806 }
1807
1808 /*************************************************************************
1809 * Scans for characters of type "type" and when not matching found,
1810 * returns pointer to it and length in size.
1811 *
1812 * Characters tested based on RFC 1738
1813 */
1814 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1815 {
1816 static DWORD alwayszero = 0;
1817 BOOL cont = TRUE;
1818
1819 *size = 0;
1820
1821 switch(type){
1822
1823 case SCHEME:
1824 while (cont) {
1825 if ( (islowerW(*start) && isalphaW(*start)) ||
1826 isdigitW(*start) ||
1827 (*start == '+') ||
1828 (*start == '-') ||
1829 (*start == '.')) {
1830 start++;
1831 (*size)++;
1832 }
1833 else
1834 cont = FALSE;
1835 }
1836 break;
1837
1838 case USERPASS:
1839 while (cont) {
1840 if ( isalphaW(*start) ||
1841 isdigitW(*start) ||
1842 /* user/password only characters */
1843 (*start == ';') ||
1844 (*start == '?') ||
1845 (*start == '&') ||
1846 (*start == '=') ||
1847 /* *extra* characters */
1848 (*start == '!') ||
1849 (*start == '*') ||
1850 (*start == '\'') ||
1851 (*start == '(') ||
1852 (*start == ')') ||
1853 (*start == ',') ||
1854 /* *safe* characters */
1855 (*start == '$') ||
1856 (*start == '_') ||
1857 (*start == '+') ||
1858 (*start == '-') ||
1859 (*start == '.')) {
1860 start++;
1861 (*size)++;
1862 } else if (*start == '%') {
1863 if (isxdigitW(*(start+1)) &&
1864 isxdigitW(*(start+2))) {
1865 start += 3;
1866 *size += 3;
1867 } else
1868 cont = FALSE;
1869 } else
1870 cont = FALSE;
1871 }
1872 break;
1873
1874 case PORT:
1875 while (cont) {
1876 if (isdigitW(*start)) {
1877 start++;
1878 (*size)++;
1879 }
1880 else
1881 cont = FALSE;
1882 }
1883 break;
1884
1885 case HOST:
1886 while (cont) {
1887 if (isalnumW(*start) ||
1888 (*start == '-') ||
1889 (*start == '.') ) {
1890 start++;
1891 (*size)++;
1892 }
1893 else
1894 cont = FALSE;
1895 }
1896 break;
1897 default:
1898 FIXME("unknown type %d\n", type);
1899 return (LPWSTR)&alwayszero;
1900 }
1901 /* TRACE("scanned %d characters next char %p<%c>\n",
1902 *size, start, *start); */
1903 return start;
1904 }
1905
1906 /*************************************************************************
1907 * Attempt to parse URL into pieces.
1908 */
1909 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1910 {
1911 LPCWSTR work;
1912
1913 memset(pl, 0, sizeof(WINE_PARSE_URL));
1914 pl->pScheme = pszUrl;
1915 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1916 if (!*work || (*work != ':')) goto ErrorExit;
1917 work++;
1918 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1919 pl->pUserName = work + 2;
1920 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1921 if (*work == ':' ) {
1922 /* parse password */
1923 work++;
1924 pl->pPassword = work;
1925 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1926 if (*work != '@') {
1927 /* what we just parsed must be the hostname and port
1928 * so reset pointers and clear then let it parse */
1929 pl->szUserName = pl->szPassword = 0;
1930 work = pl->pUserName - 1;
1931 pl->pUserName = pl->pPassword = 0;
1932 }
1933 } else if (*work == '@') {
1934 /* no password */
1935 pl->szPassword = 0;
1936 pl->pPassword = 0;
1937 } else if (!*work || (*work == '/') || (*work == '.')) {
1938 /* what was parsed was hostname, so reset pointers and let it parse */
1939 pl->szUserName = pl->szPassword = 0;
1940 work = pl->pUserName - 1;
1941 pl->pUserName = pl->pPassword = 0;
1942 } else goto ErrorExit;
1943
1944 /* now start parsing hostname or hostnumber */
1945 work++;
1946 pl->pHostName = work;
1947 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1948 if (*work == ':') {
1949 /* parse port */
1950 work++;
1951 pl->pPort = work;
1952 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1953 }
1954 if (*work == '/') {
1955 /* see if query string */
1956 pl->pQuery = strchrW(work, '?');
1957 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1958 }
1959 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1960 pl->pScheme, pl->szScheme,
1961 pl->pUserName, pl->szUserName,
1962 pl->pPassword, pl->szPassword,
1963 pl->pHostName, pl->szHostName,
1964 pl->pPort, pl->szPort,
1965 pl->pQuery, pl->szQuery);
1966 return S_OK;
1967 ErrorExit:
1968 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1969 return E_INVALIDARG;
1970 }
1971
1972 /*************************************************************************
1973 * UrlGetPartA [SHLWAPI.@]
1974 *
1975 * Retrieve part of a Url.
1976 *
1977 * PARAMS
1978 * pszIn [I] Url to parse
1979 * pszOut [O] Destination for part of pszIn requested
1980 * pcchOut [I] Size of pszOut
1981 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
1982 * needed size of pszOut INCLUDING '\0'.
1983 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1984 * dwFlags [I] URL_ flags from "shlwapi.h"
1985 *
1986 * RETURNS
1987 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1988 * Failure: An HRESULT error code describing the error.
1989 */
1990 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1991 DWORD dwPart, DWORD dwFlags)
1992 {
1993 LPWSTR in, out;
1994 DWORD ret, len, len2;
1995
1996 in = HeapAlloc(GetProcessHeap(), 0,
1997 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1998 out = in + INTERNET_MAX_URL_LENGTH;
1999
2000 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2001
2002 len = INTERNET_MAX_URL_LENGTH;
2003 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2004
2005 if (ret != S_OK) {
2006 HeapFree(GetProcessHeap(), 0, in);
2007 return ret;
2008 }
2009
2010 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2011 if (len2 > *pcchOut) {
2012 *pcchOut = len2;
2013 HeapFree(GetProcessHeap(), 0, in);
2014 return E_POINTER;
2015 }
2016 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2017 *pcchOut = len2;
2018 HeapFree(GetProcessHeap(), 0, in);
2019 return S_OK;
2020 }
2021
2022 /*************************************************************************
2023 * UrlGetPartW [SHLWAPI.@]
2024 *
2025 * See UrlGetPartA.
2026 */
2027 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2028 DWORD dwPart, DWORD dwFlags)
2029 {
2030 WINE_PARSE_URL pl;
2031 HRESULT ret;
2032 DWORD size, schsize;
2033 LPCWSTR addr, schaddr;
2034
2035 TRACE("(%s %p %p(%d) %08x %08x)\n",
2036 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2037
2038 ret = URL_ParseUrl(pszIn, &pl);
2039 if (ret == S_OK) {
2040 schaddr = pl.pScheme;
2041 schsize = pl.szScheme;
2042
2043 switch (dwPart) {
2044 case URL_PART_SCHEME:
2045 if (!pl.szScheme) return E_INVALIDARG;
2046 addr = pl.pScheme;
2047 size = pl.szScheme;
2048 break;
2049
2050 case URL_PART_HOSTNAME:
2051 if (!pl.szHostName) return E_INVALIDARG;
2052 addr = pl.pHostName;
2053 size = pl.szHostName;
2054 break;
2055
2056 case URL_PART_USERNAME:
2057 if (!pl.szUserName) return E_INVALIDARG;
2058 addr = pl.pUserName;
2059 size = pl.szUserName;
2060 break;
2061
2062 case URL_PART_PASSWORD:
2063 if (!pl.szPassword) return E_INVALIDARG;
2064 addr = pl.pPassword;
2065 size = pl.szPassword;
2066 break;
2067
2068 case URL_PART_PORT:
2069 if (!pl.szPort) return E_INVALIDARG;
2070 addr = pl.pPort;
2071 size = pl.szPort;
2072 break;
2073
2074 case URL_PART_QUERY:
2075 if (!pl.szQuery) return E_INVALIDARG;
2076 addr = pl.pQuery;
2077 size = pl.szQuery;
2078 break;
2079
2080 default:
2081 return E_INVALIDARG;
2082 }
2083
2084 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2085 if (*pcchOut < schsize + size + 2) {
2086 *pcchOut = schsize + size + 2;
2087 return E_POINTER;
2088 }
2089 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2090 pszOut[schsize] = ':';
2091 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2092 pszOut[schsize+1+size] = 0;
2093 *pcchOut = schsize + 1 + size;
2094 }
2095 else {
2096 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2097 memcpy(pszOut, addr, size*sizeof(WCHAR));
2098 pszOut[size] = 0;
2099 *pcchOut = size;
2100 }
2101 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2102 }
2103 return ret;
2104 }
2105
2106 /*************************************************************************
2107 * PathIsURLA [SHLWAPI.@]
2108 *
2109 * Check if the given path is a Url.
2110 *
2111 * PARAMS
2112 * lpszPath [I] Path to check.
2113 *
2114 * RETURNS
2115 * TRUE if lpszPath is a Url.
2116 * FALSE if lpszPath is NULL or not a Url.
2117 */
2118 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2119 {
2120 PARSEDURLA base;
2121 HRESULT hres;
2122
2123 TRACE("%s\n", debugstr_a(lpstrPath));
2124
2125 if (!lpstrPath || !*lpstrPath) return FALSE;
2126
2127 /* get protocol */
2128 base.cbSize = sizeof(base);
2129 hres = ParseURLA(lpstrPath, &base);
2130 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2131 }
2132
2133 /*************************************************************************
2134 * PathIsURLW [SHLWAPI.@]
2135 *
2136 * See PathIsURLA.
2137 */
2138 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2139 {
2140 PARSEDURLW base;
2141 HRESULT hres;
2142
2143 TRACE("%s\n", debugstr_w(lpstrPath));
2144
2145 if (!lpstrPath || !*lpstrPath) return FALSE;
2146
2147 /* get protocol */
2148 base.cbSize = sizeof(base);
2149 hres = ParseURLW(lpstrPath, &base);
2150 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2151 }
2152
2153 /*************************************************************************
2154 * UrlCreateFromPathA [SHLWAPI.@]
2155 *
2156 * See UrlCreateFromPathW
2157 */
2158 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2159 {
2160 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2161 WCHAR *urlW = bufW;
2162 UNICODE_STRING pathW;
2163 HRESULT ret;
2164 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2165
2166 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2167 return E_INVALIDARG;
2168 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2169 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2170 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2171 }
2172 if(ret == S_OK || ret == S_FALSE) {
2173 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2174 if(*pcchUrl > lenA) {
2175 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2176 pszUrl[lenA] = 0;
2177 *pcchUrl = lenA;
2178 } else {
2179 *pcchUrl = lenA + 1;
2180 ret = E_POINTER;
2181 }
2182 }
2183 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2184 RtlFreeUnicodeString(&pathW);
2185 return ret;
2186 }
2187
2188 /*************************************************************************
2189 * UrlCreateFromPathW [SHLWAPI.@]
2190 *
2191 * Create a Url from a file path.
2192 *
2193 * PARAMS
2194 * pszPath [I] Path to convert
2195 * pszUrl [O] Destination for the converted Url
2196 * pcchUrl [I/O] Length of pszUrl
2197 * dwReserved [I] Reserved, must be 0
2198 *
2199 * RETURNS
2200 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2201 * Failure: An HRESULT error code.
2202 */
2203 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2204 {
2205 DWORD needed;
2206 HRESULT ret;
2207 WCHAR *pszNewUrl;
2208 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2209 WCHAR three_slashesW[] = {'/','/','/',0};
2210 PARSEDURLW parsed_url;
2211
2212 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2213
2214 /* Validate arguments */
2215 if (dwReserved != 0)
2216 return E_INVALIDARG;
2217 if (!pszUrl || !pcchUrl)
2218 return E_INVALIDARG;
2219
2220
2221 parsed_url.cbSize = sizeof(parsed_url);
2222 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2223 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2224 needed = strlenW(pszPath);
2225 if (needed >= *pcchUrl) {
2226 *pcchUrl = needed + 1;
2227 return E_POINTER;
2228 } else {
2229 *pcchUrl = needed;
2230 strcpyW(pszUrl, pszPath);
2231 return S_FALSE;
2232 }
2233 }
2234 }
2235
2236 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2237 strcpyW(pszNewUrl, file_colonW);
2238 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2239 strcatW(pszNewUrl, three_slashesW);
2240 strcatW(pszNewUrl, pszPath);
2241 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2242
2243 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2244 return ret;
2245 }
2246
2247 /*************************************************************************
2248 * SHAutoComplete [SHLWAPI.@]
2249 *
2250 * Enable auto-completion for an edit control.
2251 *
2252 * PARAMS
2253 * hwndEdit [I] Handle of control to enable auto-completion for
2254 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2255 *
2256 * RETURNS
2257 * Success: S_OK. Auto-completion is enabled for the control.
2258 * Failure: An HRESULT error code indicating the error.
2259 */
2260 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2261 {
2262 FIXME("SHAutoComplete stub\n");
2263 return S_FALSE;
2264 }
2265
2266 /*************************************************************************
2267 * MLBuildResURLA [SHLWAPI.405]
2268 *
2269 * Create a Url pointing to a resource in a module.
2270 *
2271 * PARAMS
2272 * lpszLibName [I] Name of the module containing the resource
2273 * hMod [I] Callers module handle
2274 * dwFlags [I] Undocumented flags for loading the module
2275 * lpszRes [I] Resource name
2276 * lpszDest [O] Destination for resulting Url
2277 * dwDestLen [I] Length of lpszDest
2278 *
2279 * RETURNS
2280 * Success: S_OK. lpszDest contains the resource Url.
2281 * Failure: E_INVALIDARG, if any argument is invalid, or
2282 * E_FAIL if dwDestLen is too small.
2283 */
2284 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2285 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2286 {
2287 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2288 HRESULT hRet;
2289
2290 if (lpszLibName)
2291 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2292
2293 if (lpszRes)
2294 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2295
2296 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2297 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2298
2299 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2300 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2301 if (SUCCEEDED(hRet) && lpszDest)
2302 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2303
2304 return hRet;
2305 }
2306
2307 /*************************************************************************
2308 * MLBuildResURLA [SHLWAPI.406]
2309 *
2310 * See MLBuildResURLA.
2311 */
2312 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2313 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2314 {
2315 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2316 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2317 HRESULT hRet = E_FAIL;
2318
2319 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2320 debugstr_w(lpszRes), lpszDest, dwDestLen);
2321
2322 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2323 !lpszDest || (dwFlags && dwFlags != 2))
2324 return E_INVALIDARG;
2325
2326 if (dwDestLen >= szResLen + 1)
2327 {
2328 dwDestLen -= (szResLen + 1);
2329 memcpy(lpszDest, szRes, sizeof(szRes));
2330
2331 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2332
2333 if (hMod)
2334 {
2335 WCHAR szBuff[MAX_PATH];
2336 DWORD len;
2337
2338 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2339 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2340 {
2341 DWORD dwPathLen = strlenW(szBuff) + 1;
2342
2343 if (dwDestLen >= dwPathLen)
2344 {
2345 DWORD dwResLen;
2346
2347 dwDestLen -= dwPathLen;
2348 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2349
2350 dwResLen = strlenW(lpszRes) + 1;
2351 if (dwDestLen >= dwResLen + 1)
2352 {
2353 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2354 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2355 hRet = S_OK;
2356 }
2357 }
2358 }
2359 MLFreeLibrary(hMod);
2360 }
2361 }
2362 return hRet;
2363 }
2364
2365 /***********************************************************************
2366 * UrlFixupW [SHLWAPI.462]
2367 *
2368 * Checks the scheme part of a URL and attempts to correct misspellings.
2369 *
2370 * PARAMS
2371 * lpszUrl [I] Pointer to the URL to be corrected
2372 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2373 * dwMaxChars [I] Maximum size of corrected URL
2374 *
2375 * RETURNS
2376 * success: S_OK if URL corrected or already correct
2377 * failure: S_FALSE if unable to correct / COM error code if other error
2378 *
2379 */
2380 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2381 {
2382 DWORD srcLen;
2383
2384 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2385
2386 if (!url)
2387 return E_FAIL;
2388
2389 srcLen = lstrlenW(url);
2390
2391 /* For now just copy the URL directly */
2392 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2393
2394 return S_OK;
2395 }