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