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