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