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