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