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