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