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