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