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