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