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