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