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