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