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