[SHLWAPI] Sync with Wine Staging 1.7.37. CORE-9246
[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 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
677
678 /* Canonicalize the relative input prior to looking for the scheme */
679 len = INTERNET_MAX_URL_LENGTH;
680 ret = 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((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
950 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
951 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
952 }
953 if(ret == S_OK) {
954 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
955 if(*pcchEscaped > lenA) {
956 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
957 pszEscaped[lenA] = 0;
958 *pcchEscaped = lenA;
959 } else {
960 *pcchEscaped = lenA + 1;
961 ret = E_POINTER;
962 }
963 }
964 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
965 RtlFreeUnicodeString(&urlW);
966 return ret;
967 }
968
969 #define WINE_URL_BASH_AS_SLASH 0x01
970 #define WINE_URL_COLLAPSE_SLASHES 0x02
971 #define WINE_URL_ESCAPE_SLASH 0x04
972 #define WINE_URL_ESCAPE_HASH 0x08
973 #define WINE_URL_ESCAPE_QUESTION 0x10
974 #define WINE_URL_STOP_ON_HASH 0x20
975 #define WINE_URL_STOP_ON_QUESTION 0x40
976
977 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD flags, DWORD int_flags)
978 {
979 if (flags & URL_ESCAPE_SPACES_ONLY)
980 return ch == ' ';
981
982 if ((flags & URL_ESCAPE_PERCENT) && (ch == '%'))
983 return TRUE;
984
985 if (ch <= 31 || (ch >= 127 && ch <= 255) )
986 return TRUE;
987
988 if (isalnumW(ch))
989 return FALSE;
990
991 switch (ch) {
992 case ' ':
993 case '<':
994 case '>':
995 case '\"':
996 case '{':
997 case '}':
998 case '|':
999 case '\\':
1000 case '^':
1001 case ']':
1002 case '[':
1003 case '`':
1004 case '&':
1005 return TRUE;
1006 case '/':
1007 return !!(int_flags & WINE_URL_ESCAPE_SLASH);
1008 case '?':
1009 return !!(int_flags & WINE_URL_ESCAPE_QUESTION);
1010 case '#':
1011 return !!(int_flags & WINE_URL_ESCAPE_HASH);
1012 default:
1013 return FALSE;
1014 }
1015 }
1016
1017
1018 /*************************************************************************
1019 * UrlEscapeW [SHLWAPI.@]
1020 *
1021 * Converts unsafe characters in a Url into escape sequences.
1022 *
1023 * PARAMS
1024 * pszUrl [I] Url to modify
1025 * pszEscaped [O] Destination for modified Url
1026 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1027 * dwFlags [I] URL_ flags from "shlwapi.h"
1028 *
1029 * RETURNS
1030 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1031 * contains its length.
1032 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1033 * pcchEscaped is set to the required length.
1034 *
1035 * Converts unsafe characters into their escape sequences.
1036 *
1037 * NOTES
1038 * - By default this function stops converting at the first '?' or
1039 * '#' character.
1040 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1041 * converted, but the conversion continues past a '?' or '#'.
1042 * - Note that this function did not work well (or at all) in shlwapi version 4.
1043 *
1044 * BUGS
1045 * Only the following flags are implemented:
1046 *| URL_ESCAPE_SPACES_ONLY
1047 *| URL_DONT_ESCAPE_EXTRA_INFO
1048 *| URL_ESCAPE_SEGMENT_ONLY
1049 *| URL_ESCAPE_PERCENT
1050 */
1051 HRESULT WINAPI UrlEscapeW(
1052 LPCWSTR pszUrl,
1053 LPWSTR pszEscaped,
1054 LPDWORD pcchEscaped,
1055 DWORD dwFlags)
1056 {
1057 LPCWSTR src;
1058 DWORD needed = 0, ret;
1059 BOOL stop_escaping = FALSE;
1060 WCHAR next[5], *dst, *dst_ptr;
1061 INT len;
1062 PARSEDURLW parsed_url;
1063 DWORD int_flags;
1064 DWORD slashes = 0;
1065 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1066
1067 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1068 pszEscaped, pcchEscaped, dwFlags);
1069
1070 if(!pszUrl || !pcchEscaped)
1071 return E_INVALIDARG;
1072
1073 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1074 URL_ESCAPE_SEGMENT_ONLY |
1075 URL_DONT_ESCAPE_EXTRA_INFO |
1076 URL_ESCAPE_PERCENT))
1077 FIXME("Unimplemented flags: %08x\n", dwFlags);
1078
1079 dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1080 if(!dst_ptr)
1081 return E_OUTOFMEMORY;
1082
1083 /* fix up flags */
1084 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1085 /* if SPACES_ONLY specified, reset the other controls */
1086 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1087 URL_ESCAPE_PERCENT |
1088 URL_ESCAPE_SEGMENT_ONLY);
1089
1090 else
1091 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1092 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1093
1094
1095 int_flags = 0;
1096 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1097 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1098 } else {
1099 parsed_url.cbSize = sizeof(parsed_url);
1100 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1101 parsed_url.nScheme = URL_SCHEME_INVALID;
1102
1103 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1104
1105 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1106 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1107
1108 switch(parsed_url.nScheme) {
1109 case URL_SCHEME_FILE:
1110 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1111 int_flags &= ~WINE_URL_STOP_ON_HASH;
1112 break;
1113
1114 case URL_SCHEME_HTTP:
1115 case URL_SCHEME_HTTPS:
1116 int_flags |= WINE_URL_BASH_AS_SLASH;
1117 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1118 int_flags |= WINE_URL_ESCAPE_SLASH;
1119 break;
1120
1121 case URL_SCHEME_MAILTO:
1122 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1123 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1124 break;
1125
1126 case URL_SCHEME_INVALID:
1127 break;
1128
1129 case URL_SCHEME_FTP:
1130 default:
1131 if(parsed_url.pszSuffix[0] != '/')
1132 int_flags |= WINE_URL_ESCAPE_SLASH;
1133 break;
1134 }
1135 }
1136
1137 for(src = pszUrl; *src; ) {
1138 WCHAR cur = *src;
1139 len = 0;
1140
1141 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1142 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1143 while(cur == '/' || cur == '\\') {
1144 slashes++;
1145 cur = *++src;
1146 }
1147 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1148 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1149 src += localhost_len + 1;
1150 slashes = 3;
1151 }
1152
1153 switch(slashes) {
1154 case 1:
1155 case 3:
1156 next[0] = next[1] = next[2] = '/';
1157 len = 3;
1158 break;
1159 case 0:
1160 len = 0;
1161 break;
1162 default:
1163 next[0] = next[1] = '/';
1164 len = 2;
1165 break;
1166 }
1167 }
1168 if(len == 0) {
1169
1170 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1171 stop_escaping = TRUE;
1172
1173 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1174 stop_escaping = TRUE;
1175
1176 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1177
1178 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1179 next[0] = '%';
1180 next[1] = hexDigits[(cur >> 4) & 0xf];
1181 next[2] = hexDigits[cur & 0xf];
1182 len = 3;
1183 } else {
1184 next[0] = cur;
1185 len = 1;
1186 }
1187 src++;
1188 }
1189
1190 if(needed + len <= *pcchEscaped) {
1191 memcpy(dst, next, len*sizeof(WCHAR));
1192 dst += len;
1193 }
1194 needed += len;
1195 }
1196
1197 if(needed < *pcchEscaped) {
1198 *dst = '\0';
1199 memcpy(pszEscaped, dst_ptr, (needed+1)*sizeof(WCHAR));
1200
1201 ret = S_OK;
1202 } else {
1203 needed++; /* add one for the '\0' */
1204 ret = E_POINTER;
1205 }
1206 *pcchEscaped = needed;
1207
1208 HeapFree(GetProcessHeap(), 0, dst_ptr);
1209 return ret;
1210 }
1211
1212
1213 /*************************************************************************
1214 * UrlUnescapeA [SHLWAPI.@]
1215 *
1216 * Converts Url escape sequences back to ordinary characters.
1217 *
1218 * PARAMS
1219 * pszUrl [I/O] Url to convert
1220 * pszUnescaped [O] Destination for converted Url
1221 * pcchUnescaped [I/O] Size of output string
1222 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1223 *
1224 * RETURNS
1225 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1226 * dwFlags includes URL_ESCAPE_INPLACE.
1227 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1228 * this case pcchUnescaped is set to the size required.
1229 * NOTES
1230 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1231 * the first occurrence of either a '?' or '#' character.
1232 */
1233 HRESULT WINAPI UrlUnescapeA(
1234 LPSTR pszUrl,
1235 LPSTR pszUnescaped,
1236 LPDWORD pcchUnescaped,
1237 DWORD dwFlags)
1238 {
1239 char *dst, next;
1240 LPCSTR src;
1241 HRESULT ret;
1242 DWORD needed;
1243 BOOL stop_unescaping = FALSE;
1244
1245 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1246 pcchUnescaped, dwFlags);
1247
1248 if (!pszUrl) return E_INVALIDARG;
1249
1250 if(dwFlags & URL_UNESCAPE_INPLACE)
1251 dst = pszUrl;
1252 else
1253 {
1254 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1255 dst = pszUnescaped;
1256 }
1257
1258 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1259 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1260 (*src == '#' || *src == '?')) {
1261 stop_unescaping = TRUE;
1262 next = *src;
1263 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1264 && stop_unescaping == FALSE) {
1265 INT ih;
1266 char buf[3];
1267 memcpy(buf, src + 1, 2);
1268 buf[2] = '\0';
1269 ih = strtol(buf, NULL, 16);
1270 next = (CHAR) ih;
1271 src += 2; /* Advance to end of escape */
1272 } else
1273 next = *src;
1274
1275 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1276 *dst++ = next;
1277 }
1278
1279 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1280 *dst = '\0';
1281 ret = S_OK;
1282 } else {
1283 needed++; /* add one for the '\0' */
1284 ret = E_POINTER;
1285 }
1286 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1287 *pcchUnescaped = needed;
1288
1289 if (ret == S_OK) {
1290 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1291 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1292 }
1293
1294 return ret;
1295 }
1296
1297 /*************************************************************************
1298 * UrlUnescapeW [SHLWAPI.@]
1299 *
1300 * See UrlUnescapeA.
1301 */
1302 HRESULT WINAPI UrlUnescapeW(
1303 LPWSTR pszUrl,
1304 LPWSTR pszUnescaped,
1305 LPDWORD pcchUnescaped,
1306 DWORD dwFlags)
1307 {
1308 WCHAR *dst, next;
1309 LPCWSTR src;
1310 HRESULT ret;
1311 DWORD needed;
1312 BOOL stop_unescaping = FALSE;
1313
1314 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1315 pcchUnescaped, dwFlags);
1316
1317 if(!pszUrl) return E_INVALIDARG;
1318
1319 if(dwFlags & URL_UNESCAPE_INPLACE)
1320 dst = pszUrl;
1321 else
1322 {
1323 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1324 dst = pszUnescaped;
1325 }
1326
1327 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1328 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1329 (*src == '#' || *src == '?')) {
1330 stop_unescaping = TRUE;
1331 next = *src;
1332 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1333 && stop_unescaping == FALSE) {
1334 INT ih;
1335 WCHAR buf[5] = {'0','x',0};
1336 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1337 buf[4] = 0;
1338 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1339 next = (WCHAR) ih;
1340 src += 2; /* Advance to end of escape */
1341 } else
1342 next = *src;
1343
1344 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1345 *dst++ = next;
1346 }
1347
1348 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1349 *dst = '\0';
1350 ret = S_OK;
1351 } else {
1352 needed++; /* add one for the '\0' */
1353 ret = E_POINTER;
1354 }
1355 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1356 *pcchUnescaped = needed;
1357
1358 if (ret == S_OK) {
1359 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1360 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1361 }
1362
1363 return ret;
1364 }
1365
1366 /*************************************************************************
1367 * UrlGetLocationA [SHLWAPI.@]
1368 *
1369 * Get the location from a Url.
1370 *
1371 * PARAMS
1372 * pszUrl [I] Url to get the location from
1373 *
1374 * RETURNS
1375 * A pointer to the start of the location in pszUrl, or NULL if there is
1376 * no location.
1377 *
1378 * NOTES
1379 * - MSDN erroneously states that "The location is the segment of the Url
1380 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1381 * stop at '?' and always return a NULL in this case.
1382 * - MSDN also erroneously states that "If a file URL has a query string,
1383 * the returned string is the query string". In all tested cases, if the
1384 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1385 *| Result Url
1386 *| ------ ---
1387 *| NULL file://aa/b/cd#hohoh
1388 *| #hohoh http://aa/b/cd#hohoh
1389 *| NULL fi://aa/b/cd#hohoh
1390 *| #hohoh ff://aa/b/cd#hohoh
1391 */
1392 LPCSTR WINAPI UrlGetLocationA(
1393 LPCSTR pszUrl)
1394 {
1395 PARSEDURLA base;
1396 DWORD res1;
1397
1398 base.cbSize = sizeof(base);
1399 res1 = ParseURLA(pszUrl, &base);
1400 if (res1) return NULL; /* invalid scheme */
1401
1402 /* if scheme is file: then never return pointer */
1403 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1404
1405 /* Look for '#' and return its addr */
1406 return strchr(base.pszSuffix, '#');
1407 }
1408
1409 /*************************************************************************
1410 * UrlGetLocationW [SHLWAPI.@]
1411 *
1412 * See UrlGetLocationA.
1413 */
1414 LPCWSTR WINAPI UrlGetLocationW(
1415 LPCWSTR pszUrl)
1416 {
1417 PARSEDURLW base;
1418 DWORD res1;
1419
1420 base.cbSize = sizeof(base);
1421 res1 = ParseURLW(pszUrl, &base);
1422 if (res1) return NULL; /* invalid scheme */
1423
1424 /* if scheme is file: then never return pointer */
1425 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1426
1427 /* Look for '#' and return its addr */
1428 return strchrW(base.pszSuffix, '#');
1429 }
1430
1431 /*************************************************************************
1432 * UrlCompareA [SHLWAPI.@]
1433 *
1434 * Compare two Urls.
1435 *
1436 * PARAMS
1437 * pszUrl1 [I] First Url to compare
1438 * pszUrl2 [I] Url to compare to pszUrl1
1439 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1440 *
1441 * RETURNS
1442 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1443 * than, equal to, or less than pszUrl1 respectively.
1444 */
1445 INT WINAPI UrlCompareA(
1446 LPCSTR pszUrl1,
1447 LPCSTR pszUrl2,
1448 BOOL fIgnoreSlash)
1449 {
1450 INT ret, len, len1, len2;
1451
1452 if (!fIgnoreSlash)
1453 return strcmp(pszUrl1, pszUrl2);
1454 len1 = strlen(pszUrl1);
1455 if (pszUrl1[len1-1] == '/') len1--;
1456 len2 = strlen(pszUrl2);
1457 if (pszUrl2[len2-1] == '/') len2--;
1458 if (len1 == len2)
1459 return strncmp(pszUrl1, pszUrl2, len1);
1460 len = min(len1, len2);
1461 ret = strncmp(pszUrl1, pszUrl2, len);
1462 if (ret) return ret;
1463 if (len1 > len2) return 1;
1464 return -1;
1465 }
1466
1467 /*************************************************************************
1468 * UrlCompareW [SHLWAPI.@]
1469 *
1470 * See UrlCompareA.
1471 */
1472 INT WINAPI UrlCompareW(
1473 LPCWSTR pszUrl1,
1474 LPCWSTR pszUrl2,
1475 BOOL fIgnoreSlash)
1476 {
1477 INT ret;
1478 size_t len, len1, len2;
1479
1480 if (!fIgnoreSlash)
1481 return strcmpW(pszUrl1, pszUrl2);
1482 len1 = strlenW(pszUrl1);
1483 if (pszUrl1[len1-1] == '/') len1--;
1484 len2 = strlenW(pszUrl2);
1485 if (pszUrl2[len2-1] == '/') len2--;
1486 if (len1 == len2)
1487 return strncmpW(pszUrl1, pszUrl2, len1);
1488 len = min(len1, len2);
1489 ret = strncmpW(pszUrl1, pszUrl2, len);
1490 if (ret) return ret;
1491 if (len1 > len2) return 1;
1492 return -1;
1493 }
1494
1495 /*************************************************************************
1496 * HashData [SHLWAPI.@]
1497 *
1498 * Hash an input block into a variable sized digest.
1499 *
1500 * PARAMS
1501 * lpSrc [I] Input block
1502 * nSrcLen [I] Length of lpSrc
1503 * lpDest [I] Output for hash digest
1504 * nDestLen [I] Length of lpDest
1505 *
1506 * RETURNS
1507 * Success: TRUE. lpDest is filled with the computed hash value.
1508 * Failure: FALSE, if any argument is invalid.
1509 */
1510 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1511 unsigned char *lpDest, DWORD nDestLen)
1512 {
1513 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1514
1515 if (!lpSrc || !lpDest)
1516 return E_INVALIDARG;
1517
1518 while (destCount >= 0)
1519 {
1520 lpDest[destCount] = (destCount & 0xff);
1521 destCount--;
1522 }
1523
1524 while (srcCount >= 0)
1525 {
1526 destCount = nDestLen - 1;
1527 while (destCount >= 0)
1528 {
1529 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1530 destCount--;
1531 }
1532 srcCount--;
1533 }
1534 return S_OK;
1535 }
1536
1537 /*************************************************************************
1538 * UrlHashA [SHLWAPI.@]
1539 *
1540 * Produce a Hash from a Url.
1541 *
1542 * PARAMS
1543 * pszUrl [I] Url to hash
1544 * lpDest [O] Destinationh for hash
1545 * nDestLen [I] Length of lpDest
1546 *
1547 * RETURNS
1548 * Success: S_OK. lpDest is filled with the computed hash value.
1549 * Failure: E_INVALIDARG, if any argument is invalid.
1550 */
1551 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1552 {
1553 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1554 return E_INVALIDARG;
1555
1556 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1557 return S_OK;
1558 }
1559
1560 /*************************************************************************
1561 * UrlHashW [SHLWAPI.@]
1562 *
1563 * See UrlHashA.
1564 */
1565 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1566 {
1567 char szUrl[MAX_PATH];
1568
1569 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1570
1571 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1572 return E_INVALIDARG;
1573
1574 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1575 * return the same digests for the same URL.
1576 */
1577 WideCharToMultiByte(CP_ACP, 0, pszUrl, -1, szUrl, MAX_PATH, NULL, NULL);
1578 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1579 return S_OK;
1580 }
1581
1582 /*************************************************************************
1583 * UrlApplySchemeA [SHLWAPI.@]
1584 *
1585 * Apply a scheme to a Url.
1586 *
1587 * PARAMS
1588 * pszIn [I] Url to apply scheme to
1589 * pszOut [O] Destination for modified Url
1590 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1591 * dwFlags [I] URL_ flags from "shlwapi.h"
1592 *
1593 * RETURNS
1594 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1595 * Failure: An HRESULT error code describing the error.
1596 */
1597 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1598 {
1599 LPWSTR in, out;
1600 HRESULT ret;
1601 DWORD len;
1602
1603 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1604 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1605
1606 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1607
1608 in = HeapAlloc(GetProcessHeap(), 0,
1609 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1610 out = in + INTERNET_MAX_URL_LENGTH;
1611
1612 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1613 len = INTERNET_MAX_URL_LENGTH;
1614
1615 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1616 if (ret != S_OK) {
1617 HeapFree(GetProcessHeap(), 0, in);
1618 return ret;
1619 }
1620
1621 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1622 if (len > *pcchOut) {
1623 ret = E_POINTER;
1624 goto cleanup;
1625 }
1626
1627 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1628 len--;
1629
1630 cleanup:
1631 *pcchOut = len;
1632 HeapFree(GetProcessHeap(), 0, in);
1633 return ret;
1634 }
1635
1636 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1637 {
1638 HKEY newkey;
1639 BOOL j;
1640 INT index;
1641 DWORD value_len, data_len, dwType, i;
1642 WCHAR reg_path[MAX_PATH];
1643 WCHAR value[MAX_PATH], data[MAX_PATH];
1644 WCHAR Wxx, Wyy;
1645
1646 MultiByteToWideChar(CP_ACP, 0,
1647 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1648 -1, reg_path, MAX_PATH);
1649 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1650 index = 0;
1651 while(value_len = data_len = MAX_PATH,
1652 RegEnumValueW(newkey, index, value, &value_len,
1653 0, &dwType, (LPVOID)data, &data_len) == 0) {
1654 TRACE("guess %d %s is %s\n",
1655 index, debugstr_w(value), debugstr_w(data));
1656
1657 j = FALSE;
1658 for(i=0; i<value_len; i++) {
1659 Wxx = pszIn[i];
1660 Wyy = value[i];
1661 /* remember that TRUE is not-equal */
1662 j = ChrCmpIW(Wxx, Wyy);
1663 if (j) break;
1664 }
1665 if ((i == value_len) && !j) {
1666 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1667 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1668 RegCloseKey(newkey);
1669 return E_POINTER;
1670 }
1671 strcpyW(pszOut, data);
1672 strcatW(pszOut, pszIn);
1673 *pcchOut = strlenW(pszOut);
1674 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1675 RegCloseKey(newkey);
1676 return S_OK;
1677 }
1678 index++;
1679 }
1680 RegCloseKey(newkey);
1681 return E_FAIL;
1682 }
1683
1684 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl)
1685 {
1686 DWORD needed;
1687 HRESULT ret = S_OK;
1688 WCHAR *pszNewUrl;
1689 WCHAR file_colonW[] = {'f','i','l','e',':',0};
1690 WCHAR three_slashesW[] = {'/','/','/',0};
1691 PARSEDURLW parsed_url;
1692
1693 parsed_url.cbSize = sizeof(parsed_url);
1694 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
1695 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
1696 needed = strlenW(pszPath);
1697 if (needed >= *pcchUrl) {
1698 *pcchUrl = needed + 1;
1699 return E_POINTER;
1700 } else {
1701 *pcchUrl = needed;
1702 return S_FALSE;
1703 }
1704 }
1705 }
1706
1707 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
1708 strcpyW(pszNewUrl, file_colonW);
1709 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
1710 strcatW(pszNewUrl, three_slashesW);
1711 strcatW(pszNewUrl, pszPath);
1712 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
1713 HeapFree(GetProcessHeap(), 0, pszNewUrl);
1714 return ret;
1715 }
1716
1717 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1718 {
1719 HKEY newkey;
1720 DWORD data_len, dwType;
1721 WCHAR data[MAX_PATH];
1722
1723 static const WCHAR prefix_keyW[] =
1724 {'S','o','f','t','w','a','r','e',
1725 '\\','M','i','c','r','o','s','o','f','t',
1726 '\\','W','i','n','d','o','w','s',
1727 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1728 '\\','U','R','L',
1729 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1730
1731 /* get and prepend default */
1732 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1733 data_len = sizeof(data);
1734 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1735 RegCloseKey(newkey);
1736 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1737 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1738 return E_POINTER;
1739 }
1740 strcpyW(pszOut, data);
1741 strcatW(pszOut, pszIn);
1742 *pcchOut = strlenW(pszOut);
1743 TRACE("used default %s\n", debugstr_w(pszOut));
1744 return S_OK;
1745 }
1746
1747 /*************************************************************************
1748 * UrlApplySchemeW [SHLWAPI.@]
1749 *
1750 * See UrlApplySchemeA.
1751 */
1752 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1753 {
1754 PARSEDURLW in_scheme;
1755 DWORD res1;
1756 HRESULT ret;
1757
1758 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1759 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1760
1761 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1762
1763 if (dwFlags & URL_APPLY_GUESSFILE) {
1764 if (*pcchOut > 1 && ':' == pszIn[1]) {
1765 res1 = *pcchOut;
1766 ret = URL_CreateFromPath(pszIn, pszOut, &res1);
1767 if (ret == S_OK || ret == E_POINTER){
1768 *pcchOut = res1;
1769 return ret;
1770 }
1771 else if (ret == S_FALSE)
1772 {
1773 return ret;
1774 }
1775 }
1776 }
1777
1778 in_scheme.cbSize = sizeof(in_scheme);
1779 /* See if the base has a scheme */
1780 res1 = ParseURLW(pszIn, &in_scheme);
1781 if (res1) {
1782 /* no scheme in input, need to see if we need to guess */
1783 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1784 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1785 return ret;
1786 }
1787 }
1788
1789 /* If we are here, then either invalid scheme,
1790 * or no scheme and can't/failed guess.
1791 */
1792 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1793 ((res1 != 0)) ) &&
1794 (dwFlags & URL_APPLY_DEFAULT)) {
1795 /* find and apply default scheme */
1796 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1797 }
1798
1799 return S_FALSE;
1800 }
1801
1802 /*************************************************************************
1803 * UrlIsA [SHLWAPI.@]
1804 *
1805 * Determine if a Url is of a certain class.
1806 *
1807 * PARAMS
1808 * pszUrl [I] Url to check
1809 * Urlis [I] URLIS_ constant from "shlwapi.h"
1810 *
1811 * RETURNS
1812 * TRUE if pszUrl belongs to the class type in Urlis.
1813 * FALSE Otherwise.
1814 */
1815 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1816 {
1817 PARSEDURLA base;
1818 DWORD res1;
1819 LPCSTR last;
1820
1821 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1822
1823 if(!pszUrl)
1824 return FALSE;
1825
1826 switch (Urlis) {
1827
1828 case URLIS_OPAQUE:
1829 base.cbSize = sizeof(base);
1830 res1 = ParseURLA(pszUrl, &base);
1831 if (res1) return FALSE; /* invalid scheme */
1832 switch (base.nScheme)
1833 {
1834 case URL_SCHEME_MAILTO:
1835 case URL_SCHEME_SHELL:
1836 case URL_SCHEME_JAVASCRIPT:
1837 case URL_SCHEME_VBSCRIPT:
1838 case URL_SCHEME_ABOUT:
1839 return TRUE;
1840 }
1841 return FALSE;
1842
1843 case URLIS_FILEURL:
1844 return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1845 "file:", 5) == CSTR_EQUAL);
1846
1847 case URLIS_DIRECTORY:
1848 last = pszUrl + strlen(pszUrl) - 1;
1849 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1850
1851 case URLIS_URL:
1852 return PathIsURLA(pszUrl);
1853
1854 case URLIS_NOHISTORY:
1855 case URLIS_APPLIABLE:
1856 case URLIS_HASQUERY:
1857 default:
1858 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1859 }
1860 return FALSE;
1861 }
1862
1863 /*************************************************************************
1864 * UrlIsW [SHLWAPI.@]
1865 *
1866 * See UrlIsA.
1867 */
1868 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1869 {
1870 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
1871 PARSEDURLW base;
1872 DWORD res1;
1873 LPCWSTR last;
1874
1875 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1876
1877 if(!pszUrl)
1878 return FALSE;
1879
1880 switch (Urlis) {
1881
1882 case URLIS_OPAQUE:
1883 base.cbSize = sizeof(base);
1884 res1 = ParseURLW(pszUrl, &base);
1885 if (res1) return FALSE; /* invalid scheme */
1886 switch (base.nScheme)
1887 {
1888 case URL_SCHEME_MAILTO:
1889 case URL_SCHEME_SHELL:
1890 case URL_SCHEME_JAVASCRIPT:
1891 case URL_SCHEME_VBSCRIPT:
1892 case URL_SCHEME_ABOUT:
1893 return TRUE;
1894 }
1895 return FALSE;
1896
1897 case URLIS_FILEURL:
1898 return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1899 file_colon, 5) == CSTR_EQUAL);
1900
1901 case URLIS_DIRECTORY:
1902 last = pszUrl + strlenW(pszUrl) - 1;
1903 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1904
1905 case URLIS_URL:
1906 return PathIsURLW(pszUrl);
1907
1908 case URLIS_NOHISTORY:
1909 case URLIS_APPLIABLE:
1910 case URLIS_HASQUERY:
1911 default:
1912 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1913 }
1914 return FALSE;
1915 }
1916
1917 /*************************************************************************
1918 * UrlIsNoHistoryA [SHLWAPI.@]
1919 *
1920 * Determine if a Url should not be stored in the users history list.
1921 *
1922 * PARAMS
1923 * pszUrl [I] Url to check
1924 *
1925 * RETURNS
1926 * TRUE, if pszUrl should be excluded from the history list,
1927 * FALSE otherwise.
1928 */
1929 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1930 {
1931 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1932 }
1933
1934 /*************************************************************************
1935 * UrlIsNoHistoryW [SHLWAPI.@]
1936 *
1937 * See UrlIsNoHistoryA.
1938 */
1939 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1940 {
1941 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1942 }
1943
1944 /*************************************************************************
1945 * UrlIsOpaqueA [SHLWAPI.@]
1946 *
1947 * Determine if a Url is opaque.
1948 *
1949 * PARAMS
1950 * pszUrl [I] Url to check
1951 *
1952 * RETURNS
1953 * TRUE if pszUrl is opaque,
1954 * FALSE Otherwise.
1955 *
1956 * NOTES
1957 * An opaque Url is one that does not start with "<protocol>://".
1958 */
1959 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1960 {
1961 return UrlIsA(pszUrl, URLIS_OPAQUE);
1962 }
1963
1964 /*************************************************************************
1965 * UrlIsOpaqueW [SHLWAPI.@]
1966 *
1967 * See UrlIsOpaqueA.
1968 */
1969 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1970 {
1971 return UrlIsW(pszUrl, URLIS_OPAQUE);
1972 }
1973
1974 /*************************************************************************
1975 * Scans for characters of type "type" and when not matching found,
1976 * returns pointer to it and length in size.
1977 *
1978 * Characters tested based on RFC 1738
1979 */
1980 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1981 {
1982 static DWORD alwayszero = 0;
1983 BOOL cont = TRUE;
1984
1985 *size = 0;
1986
1987 switch(type){
1988
1989 case SCHEME:
1990 while (cont) {
1991 if ( (islowerW(*start) && isalphaW(*start)) ||
1992 isdigitW(*start) ||
1993 (*start == '+') ||
1994 (*start == '-') ||
1995 (*start == '.')) {
1996 start++;
1997 (*size)++;
1998 }
1999 else
2000 cont = FALSE;
2001 }
2002
2003 if(*start != ':')
2004 *size = 0;
2005
2006 break;
2007
2008 case USERPASS:
2009 while (cont) {
2010 if ( isalphaW(*start) ||
2011 isdigitW(*start) ||
2012 /* user/password only characters */
2013 (*start == ';') ||
2014 (*start == '?') ||
2015 (*start == '&') ||
2016 (*start == '=') ||
2017 /* *extra* characters */
2018 (*start == '!') ||
2019 (*start == '*') ||
2020 (*start == '\'') ||
2021 (*start == '(') ||
2022 (*start == ')') ||
2023 (*start == ',') ||
2024 /* *safe* characters */
2025 (*start == '$') ||
2026 (*start == '_') ||
2027 (*start == '+') ||
2028 (*start == '-') ||
2029 (*start == '.') ||
2030 (*start == ' ')) {
2031 start++;
2032 (*size)++;
2033 } else if (*start == '%') {
2034 if (isxdigitW(*(start+1)) &&
2035 isxdigitW(*(start+2))) {
2036 start += 3;
2037 *size += 3;
2038 } else
2039 cont = FALSE;
2040 } else
2041 cont = FALSE;
2042 }
2043 break;
2044
2045 case PORT:
2046 while (cont) {
2047 if (isdigitW(*start)) {
2048 start++;
2049 (*size)++;
2050 }
2051 else
2052 cont = FALSE;
2053 }
2054 break;
2055
2056 case HOST:
2057 while (cont) {
2058 if (isalnumW(*start) ||
2059 (*start == '-') ||
2060 (*start == '.') ||
2061 (*start == ' ') ||
2062 (*start == '*') ) {
2063 start++;
2064 (*size)++;
2065 }
2066 else
2067 cont = FALSE;
2068 }
2069 break;
2070 default:
2071 FIXME("unknown type %d\n", type);
2072 return (LPWSTR)&alwayszero;
2073 }
2074 /* TRACE("scanned %d characters next char %p<%c>\n",
2075 *size, start, *start); */
2076 return start;
2077 }
2078
2079 /*************************************************************************
2080 * Attempt to parse URL into pieces.
2081 */
2082 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2083 {
2084 LPCWSTR work;
2085
2086 memset(pl, 0, sizeof(WINE_PARSE_URL));
2087 pl->pScheme = pszUrl;
2088 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2089 if (!*work || (*work != ':')) goto ErrorExit;
2090 work++;
2091 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2092 pl->pUserName = work + 2;
2093 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2094 if (*work == ':' ) {
2095 /* parse password */
2096 work++;
2097 pl->pPassword = work;
2098 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2099 if (*work != '@') {
2100 /* what we just parsed must be the hostname and port
2101 * so reset pointers and clear then let it parse */
2102 pl->szUserName = pl->szPassword = 0;
2103 work = pl->pUserName - 1;
2104 pl->pUserName = pl->pPassword = 0;
2105 }
2106 } else if (*work == '@') {
2107 /* no password */
2108 pl->szPassword = 0;
2109 pl->pPassword = 0;
2110 } else if (!*work || (*work == '/') || (*work == '.')) {
2111 /* what was parsed was hostname, so reset pointers and let it parse */
2112 pl->szUserName = pl->szPassword = 0;
2113 work = pl->pUserName - 1;
2114 pl->pUserName = pl->pPassword = 0;
2115 } else goto ErrorExit;
2116
2117 /* now start parsing hostname or hostnumber */
2118 work++;
2119 pl->pHostName = work;
2120 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2121 if (*work == ':') {
2122 /* parse port */
2123 work++;
2124 pl->pPort = work;
2125 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2126 }
2127 if (*work == '/') {
2128 /* see if query string */
2129 pl->pQuery = strchrW(work, '?');
2130 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2131 }
2132 SuccessExit:
2133 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2134 pl->pScheme, pl->szScheme,
2135 pl->pUserName, pl->szUserName,
2136 pl->pPassword, pl->szPassword,
2137 pl->pHostName, pl->szHostName,
2138 pl->pPort, pl->szPort,
2139 pl->pQuery, pl->szQuery);
2140 return S_OK;
2141 ErrorExit:
2142 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2143 return E_INVALIDARG;
2144 }
2145
2146 /*************************************************************************
2147 * UrlGetPartA [SHLWAPI.@]
2148 *
2149 * Retrieve part of a Url.
2150 *
2151 * PARAMS
2152 * pszIn [I] Url to parse
2153 * pszOut [O] Destination for part of pszIn requested
2154 * pcchOut [I] Size of pszOut
2155 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2156 * needed size of pszOut INCLUDING '\0'.
2157 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2158 * dwFlags [I] URL_ flags from "shlwapi.h"
2159 *
2160 * RETURNS
2161 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2162 * Failure: An HRESULT error code describing the error.
2163 */
2164 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2165 DWORD dwPart, DWORD dwFlags)
2166 {
2167 LPWSTR in, out;
2168 DWORD ret, len, len2;
2169
2170 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2171 return E_INVALIDARG;
2172
2173 in = HeapAlloc(GetProcessHeap(), 0,
2174 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2175 out = in + INTERNET_MAX_URL_LENGTH;
2176
2177 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2178
2179 len = INTERNET_MAX_URL_LENGTH;
2180 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2181
2182 if (FAILED(ret)) {
2183 HeapFree(GetProcessHeap(), 0, in);
2184 return ret;
2185 }
2186
2187 len2 = WideCharToMultiByte(CP_ACP, 0, out, len, NULL, 0, NULL, NULL);
2188 if (len2 > *pcchOut) {
2189 *pcchOut = len2+1;
2190 HeapFree(GetProcessHeap(), 0, in);
2191 return E_POINTER;
2192 }
2193 len2 = WideCharToMultiByte(CP_ACP, 0, out, len+1, pszOut, *pcchOut, NULL, NULL);
2194 *pcchOut = len2-1;
2195 HeapFree(GetProcessHeap(), 0, in);
2196 return ret;
2197 }
2198
2199 /*************************************************************************
2200 * UrlGetPartW [SHLWAPI.@]
2201 *
2202 * See UrlGetPartA.
2203 */
2204 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2205 DWORD dwPart, DWORD dwFlags)
2206 {
2207 WINE_PARSE_URL pl;
2208 HRESULT ret;
2209 DWORD scheme, size, schsize;
2210 LPCWSTR addr, schaddr;
2211
2212 TRACE("(%s %p %p(%d) %08x %08x)\n",
2213 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2214
2215 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2216 return E_INVALIDARG;
2217
2218 *pszOut = '\0';
2219
2220 addr = strchrW(pszIn, ':');
2221 if(!addr)
2222 scheme = URL_SCHEME_UNKNOWN;
2223 else
2224 scheme = get_scheme_code(pszIn, addr-pszIn);
2225
2226 ret = URL_ParseUrl(pszIn, &pl);
2227
2228 switch (dwPart) {
2229 case URL_PART_SCHEME:
2230 if (!pl.szScheme) {
2231 *pcchOut = 0;
2232 return S_FALSE;
2233 }
2234 addr = pl.pScheme;
2235 size = pl.szScheme;
2236 break;
2237
2238 case URL_PART_HOSTNAME:
2239 switch(scheme) {
2240 case URL_SCHEME_FTP:
2241 case URL_SCHEME_HTTP:
2242 case URL_SCHEME_GOPHER:
2243 case URL_SCHEME_TELNET:
2244 case URL_SCHEME_FILE:
2245 case URL_SCHEME_HTTPS:
2246 break;
2247 default:
2248 *pcchOut = 0;
2249 return E_FAIL;
2250 }
2251
2252 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2253 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2254 *pcchOut = 0;
2255 return S_FALSE;
2256 }
2257
2258 if (!pl.szHostName) {
2259 *pcchOut = 0;
2260 return S_FALSE;
2261 }
2262 addr = pl.pHostName;
2263 size = pl.szHostName;
2264 break;
2265
2266 case URL_PART_USERNAME:
2267 if (!pl.szUserName) {
2268 *pcchOut = 0;
2269 return S_FALSE;
2270 }
2271 addr = pl.pUserName;
2272 size = pl.szUserName;
2273 break;
2274
2275 case URL_PART_PASSWORD:
2276 if (!pl.szPassword) {
2277 *pcchOut = 0;
2278 return S_FALSE;
2279 }
2280 addr = pl.pPassword;
2281 size = pl.szPassword;
2282 break;
2283
2284 case URL_PART_PORT:
2285 if (!pl.szPort) {
2286 *pcchOut = 0;
2287 return S_FALSE;
2288 }
2289 addr = pl.pPort;
2290 size = pl.szPort;
2291 break;
2292
2293 case URL_PART_QUERY:
2294 if (!pl.szQuery) {
2295 *pcchOut = 0;
2296 return S_FALSE;
2297 }
2298 addr = pl.pQuery;
2299 size = pl.szQuery;
2300 break;
2301
2302 default:
2303 *pcchOut = 0;
2304 return E_INVALIDARG;
2305 }
2306
2307 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2308 if(!pl.pScheme || !pl.szScheme) {
2309 *pcchOut = 0;
2310 return E_FAIL;
2311 }
2312 schaddr = pl.pScheme;
2313 schsize = pl.szScheme;
2314 if (*pcchOut < schsize + size + 2) {
2315 *pcchOut = schsize + size + 2;
2316 return E_POINTER;
2317 }
2318 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2319 pszOut[schsize] = ':';
2320 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2321 pszOut[schsize+1+size] = 0;
2322 *pcchOut = schsize + 1 + size;
2323 }
2324 else {
2325 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2326 memcpy(pszOut, addr, size*sizeof(WCHAR));
2327 pszOut[size] = 0;
2328 *pcchOut = size;
2329 }
2330 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2331
2332 return ret;
2333 }
2334
2335 /*************************************************************************
2336 * PathIsURLA [SHLWAPI.@]
2337 *
2338 * Check if the given path is a Url.
2339 *
2340 * PARAMS
2341 * lpszPath [I] Path to check.
2342 *
2343 * RETURNS
2344 * TRUE if lpszPath is a Url.
2345 * FALSE if lpszPath is NULL or not a Url.
2346 */
2347 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2348 {
2349 PARSEDURLA base;
2350 HRESULT hres;
2351
2352 TRACE("%s\n", debugstr_a(lpstrPath));
2353
2354 if (!lpstrPath || !*lpstrPath) return FALSE;
2355
2356 /* get protocol */
2357 base.cbSize = sizeof(base);
2358 hres = ParseURLA(lpstrPath, &base);
2359 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2360 }
2361
2362 /*************************************************************************
2363 * PathIsURLW [SHLWAPI.@]
2364 *
2365 * See PathIsURLA.
2366 */
2367 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2368 {
2369 PARSEDURLW base;
2370 HRESULT hres;
2371
2372 TRACE("%s\n", debugstr_w(lpstrPath));
2373
2374 if (!lpstrPath || !*lpstrPath) return FALSE;
2375
2376 /* get protocol */
2377 base.cbSize = sizeof(base);
2378 hres = ParseURLW(lpstrPath, &base);
2379 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2380 }
2381
2382 /*************************************************************************
2383 * UrlCreateFromPathA [SHLWAPI.@]
2384 *
2385 * See UrlCreateFromPathW
2386 */
2387 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2388 {
2389 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2390 WCHAR *urlW = bufW;
2391 UNICODE_STRING pathW;
2392 HRESULT ret;
2393 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2394
2395 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2396 return E_INVALIDARG;
2397 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2398 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2399 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2400 }
2401 if(ret == S_OK || ret == S_FALSE) {
2402 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2403 if(*pcchUrl > lenA) {
2404 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2405 pszUrl[lenA] = 0;
2406 *pcchUrl = lenA;
2407 } else {
2408 *pcchUrl = lenA + 1;
2409 ret = E_POINTER;
2410 }
2411 }
2412 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2413 RtlFreeUnicodeString(&pathW);
2414 return ret;
2415 }
2416
2417 /*************************************************************************
2418 * UrlCreateFromPathW [SHLWAPI.@]
2419 *
2420 * Create a Url from a file path.
2421 *
2422 * PARAMS
2423 * pszPath [I] Path to convert
2424 * pszUrl [O] Destination for the converted Url
2425 * pcchUrl [I/O] Length of pszUrl
2426 * dwReserved [I] Reserved, must be 0
2427 *
2428 * RETURNS
2429 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2430 * Failure: An HRESULT error code.
2431 */
2432 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2433 {
2434 HRESULT ret;
2435
2436 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2437
2438 /* Validate arguments */
2439 if (dwReserved != 0)
2440 return E_INVALIDARG;
2441 if (!pszUrl || !pcchUrl)
2442 return E_INVALIDARG;
2443
2444 ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl);
2445
2446 if (S_FALSE == ret)
2447 strcpyW(pszUrl, pszPath);
2448
2449 return ret;
2450 }
2451
2452 /*************************************************************************
2453 * SHAutoComplete [SHLWAPI.@]
2454 *
2455 * Enable auto-completion for an edit control.
2456 *
2457 * PARAMS
2458 * hwndEdit [I] Handle of control to enable auto-completion for
2459 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2460 *
2461 * RETURNS
2462 * Success: S_OK. Auto-completion is enabled for the control.
2463 * Failure: An HRESULT error code indicating the error.
2464 */
2465 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2466 {
2467 FIXME("stub\n");
2468 return S_FALSE;
2469 }
2470
2471 /*************************************************************************
2472 * MLBuildResURLA [SHLWAPI.405]
2473 *
2474 * Create a Url pointing to a resource in a module.
2475 *
2476 * PARAMS
2477 * lpszLibName [I] Name of the module containing the resource
2478 * hMod [I] Callers module handle
2479 * dwFlags [I] Undocumented flags for loading the module
2480 * lpszRes [I] Resource name
2481 * lpszDest [O] Destination for resulting Url
2482 * dwDestLen [I] Length of lpszDest
2483 *
2484 * RETURNS
2485 * Success: S_OK. lpszDest contains the resource Url.
2486 * Failure: E_INVALIDARG, if any argument is invalid, or
2487 * E_FAIL if dwDestLen is too small.
2488 */
2489 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2490 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2491 {
2492 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2493 HRESULT hRet;
2494
2495 if (lpszLibName)
2496 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2497
2498 if (lpszRes)
2499 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2500
2501 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2502 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2503
2504 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2505 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2506 if (SUCCEEDED(hRet) && lpszDest)
2507 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, NULL, NULL);
2508
2509 return hRet;
2510 }
2511
2512 /*************************************************************************
2513 * MLBuildResURLA [SHLWAPI.406]
2514 *
2515 * See MLBuildResURLA.
2516 */
2517 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2518 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2519 {
2520 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2521 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2522 HRESULT hRet = E_FAIL;
2523
2524 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2525 debugstr_w(lpszRes), lpszDest, dwDestLen);
2526
2527 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2528 !lpszDest || (dwFlags && dwFlags != 2))
2529 return E_INVALIDARG;
2530
2531 if (dwDestLen >= szResLen + 1)
2532 {
2533 dwDestLen -= (szResLen + 1);
2534 memcpy(lpszDest, szRes, sizeof(szRes));
2535
2536 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2537
2538 if (hMod)
2539 {
2540 WCHAR szBuff[MAX_PATH];
2541 DWORD len;
2542
2543 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2544 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2545 {
2546 DWORD dwPathLen = strlenW(szBuff) + 1;
2547
2548 if (dwDestLen >= dwPathLen)
2549 {
2550 DWORD dwResLen;
2551
2552 dwDestLen -= dwPathLen;
2553 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2554
2555 dwResLen = strlenW(lpszRes) + 1;
2556 if (dwDestLen >= dwResLen + 1)
2557 {
2558 lpszDest[szResLen + dwPathLen-1] = '/';
2559 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2560 hRet = S_OK;
2561 }
2562 }
2563 }
2564 MLFreeLibrary(hMod);
2565 }
2566 }
2567 return hRet;
2568 }
2569
2570 /***********************************************************************
2571 * UrlFixupW [SHLWAPI.462]
2572 *
2573 * Checks the scheme part of a URL and attempts to correct misspellings.
2574 *
2575 * PARAMS
2576 * lpszUrl [I] Pointer to the URL to be corrected
2577 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2578 * dwMaxChars [I] Maximum size of corrected URL
2579 *
2580 * RETURNS
2581 * success: S_OK if URL corrected or already correct
2582 * failure: S_FALSE if unable to correct / COM error code if other error
2583 *
2584 */
2585 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2586 {
2587 DWORD srcLen;
2588
2589 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2590
2591 if (!url)
2592 return E_FAIL;
2593
2594 srcLen = lstrlenW(url) + 1;
2595
2596 /* For now just copy the URL directly */
2597 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2598
2599 return S_OK;
2600 }
2601
2602 /*************************************************************************
2603 * IsInternetESCEnabled [SHLWAPI.@]
2604 */
2605 BOOL WINAPI IsInternetESCEnabled(void)
2606 {
2607 FIXME(": stub\n");
2608 return FALSE;
2609 }