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