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