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