msvcp90: Return last index in string::find_last_not_of_cstr_substr if input is empty.
[wine.git] / dlls / shlwapi / url.c
blob11589e424dc4d53ba16c2a7a2da3679b5aaacd91
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 dwFlags, DWORD int_flags)
992 if (isalnumW(ch))
993 return FALSE;
995 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
996 if(ch == ' ')
997 return TRUE;
998 else
999 return FALSE;
1002 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
1003 return TRUE;
1005 if (ch <= 31 || ch >= 127)
1006 return TRUE;
1008 else {
1009 switch (ch) {
1010 case ' ':
1011 case '<':
1012 case '>':
1013 case '\"':
1014 case '{':
1015 case '}':
1016 case '|':
1017 case '\\':
1018 case '^':
1019 case ']':
1020 case '[':
1021 case '`':
1022 case '&':
1023 return TRUE;
1025 case '/':
1026 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
1027 return FALSE;
1029 case '?':
1030 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
1031 return FALSE;
1033 case '#':
1034 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
1035 return FALSE;
1037 default:
1038 return FALSE;
1044 /*************************************************************************
1045 * UrlEscapeW [SHLWAPI.@]
1047 * Converts unsafe characters in a Url into escape sequences.
1049 * PARAMS
1050 * pszUrl [I] Url to modify
1051 * pszEscaped [O] Destination for modified Url
1052 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1053 * dwFlags [I] URL_ flags from "shlwapi.h"
1055 * RETURNS
1056 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1057 * contains its length.
1058 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1059 * pcchEscaped is set to the required length.
1061 * Converts unsafe characters into their escape sequences.
1063 * NOTES
1064 * - By default this function stops converting at the first '?' or
1065 * '#' character.
1066 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1067 * converted, but the conversion continues past a '?' or '#'.
1068 * - Note that this function did not work well (or at all) in shlwapi version 4.
1070 * BUGS
1071 * Only the following flags are implemented:
1072 *| URL_ESCAPE_SPACES_ONLY
1073 *| URL_DONT_ESCAPE_EXTRA_INFO
1074 *| URL_ESCAPE_SEGMENT_ONLY
1075 *| URL_ESCAPE_PERCENT
1077 HRESULT WINAPI UrlEscapeW(
1078 LPCWSTR pszUrl,
1079 LPWSTR pszEscaped,
1080 LPDWORD pcchEscaped,
1081 DWORD dwFlags)
1083 LPCWSTR src;
1084 DWORD needed = 0, ret;
1085 BOOL stop_escaping = FALSE;
1086 WCHAR next[5], *dst, *dst_ptr;
1087 INT len;
1088 PARSEDURLW parsed_url;
1089 DWORD int_flags;
1090 DWORD slashes = 0;
1091 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1093 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1094 pszEscaped, pcchEscaped, dwFlags);
1096 if(!pszUrl || !pcchEscaped)
1097 return E_INVALIDARG;
1099 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1100 URL_ESCAPE_SEGMENT_ONLY |
1101 URL_DONT_ESCAPE_EXTRA_INFO |
1102 URL_ESCAPE_PERCENT))
1103 FIXME("Unimplemented flags: %08x\n", dwFlags);
1105 dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1106 if(!dst_ptr)
1107 return E_OUTOFMEMORY;
1109 /* fix up flags */
1110 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1111 /* if SPACES_ONLY specified, reset the other controls */
1112 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1113 URL_ESCAPE_PERCENT |
1114 URL_ESCAPE_SEGMENT_ONLY);
1116 else
1117 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1118 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1121 int_flags = 0;
1122 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1123 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1124 } else {
1125 parsed_url.cbSize = sizeof(parsed_url);
1126 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1127 parsed_url.nScheme = URL_SCHEME_INVALID;
1129 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1131 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1132 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1134 switch(parsed_url.nScheme) {
1135 case URL_SCHEME_FILE:
1136 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1137 int_flags &= ~WINE_URL_STOP_ON_HASH;
1138 break;
1140 case URL_SCHEME_HTTP:
1141 case URL_SCHEME_HTTPS:
1142 int_flags |= WINE_URL_BASH_AS_SLASH;
1143 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1144 int_flags |= WINE_URL_ESCAPE_SLASH;
1145 break;
1147 case URL_SCHEME_MAILTO:
1148 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1149 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1150 break;
1152 case URL_SCHEME_INVALID:
1153 break;
1155 case URL_SCHEME_FTP:
1156 default:
1157 if(parsed_url.pszSuffix[0] != '/')
1158 int_flags |= WINE_URL_ESCAPE_SLASH;
1159 break;
1163 for(src = pszUrl; *src; ) {
1164 WCHAR cur = *src;
1165 len = 0;
1167 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1168 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1169 while(cur == '/' || cur == '\\') {
1170 slashes++;
1171 cur = *++src;
1173 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1174 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1175 src += localhost_len + 1;
1176 slashes = 3;
1179 switch(slashes) {
1180 case 1:
1181 case 3:
1182 next[0] = next[1] = next[2] = '/';
1183 len = 3;
1184 break;
1185 case 0:
1186 len = 0;
1187 break;
1188 default:
1189 next[0] = next[1] = '/';
1190 len = 2;
1191 break;
1194 if(len == 0) {
1196 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1197 stop_escaping = TRUE;
1199 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1200 stop_escaping = TRUE;
1202 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1204 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1205 next[0] = '%';
1206 next[1] = hexDigits[(cur >> 4) & 0xf];
1207 next[2] = hexDigits[cur & 0xf];
1208 len = 3;
1209 } else {
1210 next[0] = cur;
1211 len = 1;
1213 src++;
1216 if(needed + len <= *pcchEscaped) {
1217 memcpy(dst, next, len*sizeof(WCHAR));
1218 dst += len;
1220 needed += len;
1223 if(needed < *pcchEscaped) {
1224 *dst = '\0';
1225 memcpy(pszEscaped, dst_ptr, (needed+1)*sizeof(WCHAR));
1227 ret = S_OK;
1228 } else {
1229 needed++; /* add one for the '\0' */
1230 ret = E_POINTER;
1232 *pcchEscaped = needed;
1234 HeapFree(GetProcessHeap(), 0, dst_ptr);
1235 return ret;
1239 /*************************************************************************
1240 * UrlUnescapeA [SHLWAPI.@]
1242 * Converts Url escape sequences back to ordinary characters.
1244 * PARAMS
1245 * pszUrl [I/O] Url to convert
1246 * pszUnescaped [O] Destination for converted Url
1247 * pcchUnescaped [I/O] Size of output string
1248 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1250 * RETURNS
1251 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1252 * dwFlags includes URL_ESCAPE_INPLACE.
1253 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1254 * this case pcchUnescaped is set to the size required.
1255 * NOTES
1256 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1257 * the first occurrence of either a '?' or '#' character.
1259 HRESULT WINAPI UrlUnescapeA(
1260 LPSTR pszUrl,
1261 LPSTR pszUnescaped,
1262 LPDWORD pcchUnescaped,
1263 DWORD dwFlags)
1265 char *dst, next;
1266 LPCSTR src;
1267 HRESULT ret;
1268 DWORD needed;
1269 BOOL stop_unescaping = FALSE;
1271 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1272 pcchUnescaped, dwFlags);
1274 if (!pszUrl) return E_INVALIDARG;
1276 if(dwFlags & URL_UNESCAPE_INPLACE)
1277 dst = pszUrl;
1278 else
1280 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1281 dst = pszUnescaped;
1284 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1285 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1286 (*src == '#' || *src == '?')) {
1287 stop_unescaping = TRUE;
1288 next = *src;
1289 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1290 && stop_unescaping == FALSE) {
1291 INT ih;
1292 char buf[3];
1293 memcpy(buf, src + 1, 2);
1294 buf[2] = '\0';
1295 ih = strtol(buf, NULL, 16);
1296 next = (CHAR) ih;
1297 src += 2; /* Advance to end of escape */
1298 } else
1299 next = *src;
1301 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1302 *dst++ = next;
1305 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1306 *dst = '\0';
1307 ret = S_OK;
1308 } else {
1309 needed++; /* add one for the '\0' */
1310 ret = E_POINTER;
1312 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1313 *pcchUnescaped = needed;
1315 if (ret == S_OK) {
1316 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1317 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1320 return ret;
1323 /*************************************************************************
1324 * UrlUnescapeW [SHLWAPI.@]
1326 * See UrlUnescapeA.
1328 HRESULT WINAPI UrlUnescapeW(
1329 LPWSTR pszUrl,
1330 LPWSTR pszUnescaped,
1331 LPDWORD pcchUnescaped,
1332 DWORD dwFlags)
1334 WCHAR *dst, next;
1335 LPCWSTR src;
1336 HRESULT ret;
1337 DWORD needed;
1338 BOOL stop_unescaping = FALSE;
1340 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1341 pcchUnescaped, dwFlags);
1343 if(!pszUrl) return E_INVALIDARG;
1345 if(dwFlags & URL_UNESCAPE_INPLACE)
1346 dst = pszUrl;
1347 else
1349 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1350 dst = pszUnescaped;
1353 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1354 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1355 (*src == '#' || *src == '?')) {
1356 stop_unescaping = TRUE;
1357 next = *src;
1358 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1359 && stop_unescaping == FALSE) {
1360 INT ih;
1361 WCHAR buf[5] = {'0','x',0};
1362 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1363 buf[4] = 0;
1364 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1365 next = (WCHAR) ih;
1366 src += 2; /* Advance to end of escape */
1367 } else
1368 next = *src;
1370 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1371 *dst++ = next;
1374 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1375 *dst = '\0';
1376 ret = S_OK;
1377 } else {
1378 needed++; /* add one for the '\0' */
1379 ret = E_POINTER;
1381 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1382 *pcchUnescaped = needed;
1384 if (ret == S_OK) {
1385 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1386 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1389 return ret;
1392 /*************************************************************************
1393 * UrlGetLocationA [SHLWAPI.@]
1395 * Get the location from a Url.
1397 * PARAMS
1398 * pszUrl [I] Url to get the location from
1400 * RETURNS
1401 * A pointer to the start of the location in pszUrl, or NULL if there is
1402 * no location.
1404 * NOTES
1405 * - MSDN erroneously states that "The location is the segment of the Url
1406 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1407 * stop at '?' and always return a NULL in this case.
1408 * - MSDN also erroneously states that "If a file URL has a query string,
1409 * the returned string is the query string". In all tested cases, if the
1410 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1411 *| Result Url
1412 *| ------ ---
1413 *| NULL file://aa/b/cd#hohoh
1414 *| #hohoh http://aa/b/cd#hohoh
1415 *| NULL fi://aa/b/cd#hohoh
1416 *| #hohoh ff://aa/b/cd#hohoh
1418 LPCSTR WINAPI UrlGetLocationA(
1419 LPCSTR pszUrl)
1421 PARSEDURLA base;
1422 DWORD res1;
1424 base.cbSize = sizeof(base);
1425 res1 = ParseURLA(pszUrl, &base);
1426 if (res1) return NULL; /* invalid scheme */
1428 /* if scheme is file: then never return pointer */
1429 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1431 /* Look for '#' and return its addr */
1432 return strchr(base.pszSuffix, '#');
1435 /*************************************************************************
1436 * UrlGetLocationW [SHLWAPI.@]
1438 * See UrlGetLocationA.
1440 LPCWSTR WINAPI UrlGetLocationW(
1441 LPCWSTR pszUrl)
1443 PARSEDURLW base;
1444 DWORD res1;
1446 base.cbSize = sizeof(base);
1447 res1 = ParseURLW(pszUrl, &base);
1448 if (res1) return NULL; /* invalid scheme */
1450 /* if scheme is file: then never return pointer */
1451 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1453 /* Look for '#' and return its addr */
1454 return strchrW(base.pszSuffix, '#');
1457 /*************************************************************************
1458 * UrlCompareA [SHLWAPI.@]
1460 * Compare two Urls.
1462 * PARAMS
1463 * pszUrl1 [I] First Url to compare
1464 * pszUrl2 [I] Url to compare to pszUrl1
1465 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1467 * RETURNS
1468 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1469 * than, equal to, or less than pszUrl1 respectively.
1471 INT WINAPI UrlCompareA(
1472 LPCSTR pszUrl1,
1473 LPCSTR pszUrl2,
1474 BOOL fIgnoreSlash)
1476 INT ret, len, len1, len2;
1478 if (!fIgnoreSlash)
1479 return strcmp(pszUrl1, pszUrl2);
1480 len1 = strlen(pszUrl1);
1481 if (pszUrl1[len1-1] == '/') len1--;
1482 len2 = strlen(pszUrl2);
1483 if (pszUrl2[len2-1] == '/') len2--;
1484 if (len1 == len2)
1485 return strncmp(pszUrl1, pszUrl2, len1);
1486 len = min(len1, len2);
1487 ret = strncmp(pszUrl1, pszUrl2, len);
1488 if (ret) return ret;
1489 if (len1 > len2) return 1;
1490 return -1;
1493 /*************************************************************************
1494 * UrlCompareW [SHLWAPI.@]
1496 * See UrlCompareA.
1498 INT WINAPI UrlCompareW(
1499 LPCWSTR pszUrl1,
1500 LPCWSTR pszUrl2,
1501 BOOL fIgnoreSlash)
1503 INT ret;
1504 size_t len, len1, len2;
1506 if (!fIgnoreSlash)
1507 return strcmpW(pszUrl1, pszUrl2);
1508 len1 = strlenW(pszUrl1);
1509 if (pszUrl1[len1-1] == '/') len1--;
1510 len2 = strlenW(pszUrl2);
1511 if (pszUrl2[len2-1] == '/') len2--;
1512 if (len1 == len2)
1513 return strncmpW(pszUrl1, pszUrl2, len1);
1514 len = min(len1, len2);
1515 ret = strncmpW(pszUrl1, pszUrl2, len);
1516 if (ret) return ret;
1517 if (len1 > len2) return 1;
1518 return -1;
1521 /*************************************************************************
1522 * HashData [SHLWAPI.@]
1524 * Hash an input block into a variable sized digest.
1526 * PARAMS
1527 * lpSrc [I] Input block
1528 * nSrcLen [I] Length of lpSrc
1529 * lpDest [I] Output for hash digest
1530 * nDestLen [I] Length of lpDest
1532 * RETURNS
1533 * Success: TRUE. lpDest is filled with the computed hash value.
1534 * Failure: FALSE, if any argument is invalid.
1536 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1537 unsigned char *lpDest, DWORD nDestLen)
1539 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1541 if (!lpSrc || !lpDest)
1542 return E_INVALIDARG;
1544 while (destCount >= 0)
1546 lpDest[destCount] = (destCount & 0xff);
1547 destCount--;
1550 while (srcCount >= 0)
1552 destCount = nDestLen - 1;
1553 while (destCount >= 0)
1555 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1556 destCount--;
1558 srcCount--;
1560 return S_OK;
1563 /*************************************************************************
1564 * UrlHashA [SHLWAPI.@]
1566 * Produce a Hash from a Url.
1568 * PARAMS
1569 * pszUrl [I] Url to hash
1570 * lpDest [O] Destinationh for hash
1571 * nDestLen [I] Length of lpDest
1573 * RETURNS
1574 * Success: S_OK. lpDest is filled with the computed hash value.
1575 * Failure: E_INVALIDARG, if any argument is invalid.
1577 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1579 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1580 return E_INVALIDARG;
1582 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1583 return S_OK;
1586 /*************************************************************************
1587 * UrlHashW [SHLWAPI.@]
1589 * See UrlHashA.
1591 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1593 char szUrl[MAX_PATH];
1595 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1597 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1598 return E_INVALIDARG;
1600 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1601 * return the same digests for the same URL.
1603 WideCharToMultiByte(CP_ACP, 0, pszUrl, -1, szUrl, MAX_PATH, NULL, NULL);
1604 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1605 return S_OK;
1608 /*************************************************************************
1609 * UrlApplySchemeA [SHLWAPI.@]
1611 * Apply a scheme to a Url.
1613 * PARAMS
1614 * pszIn [I] Url to apply scheme to
1615 * pszOut [O] Destination for modified Url
1616 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1617 * dwFlags [I] URL_ flags from "shlwapi.h"
1619 * RETURNS
1620 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1621 * Failure: An HRESULT error code describing the error.
1623 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1625 LPWSTR in, out;
1626 HRESULT ret;
1627 DWORD len;
1629 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1630 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1632 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1634 in = HeapAlloc(GetProcessHeap(), 0,
1635 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1636 out = in + INTERNET_MAX_URL_LENGTH;
1638 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1639 len = INTERNET_MAX_URL_LENGTH;
1641 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1642 if (ret != S_OK) {
1643 HeapFree(GetProcessHeap(), 0, in);
1644 return ret;
1647 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1648 if (len > *pcchOut) {
1649 ret = E_POINTER;
1650 goto cleanup;
1653 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1654 len--;
1656 cleanup:
1657 *pcchOut = len;
1658 HeapFree(GetProcessHeap(), 0, in);
1659 return ret;
1662 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1664 HKEY newkey;
1665 BOOL j;
1666 INT index;
1667 DWORD value_len, data_len, dwType, i;
1668 WCHAR reg_path[MAX_PATH];
1669 WCHAR value[MAX_PATH], data[MAX_PATH];
1670 WCHAR Wxx, Wyy;
1672 MultiByteToWideChar(CP_ACP, 0,
1673 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1674 -1, reg_path, MAX_PATH);
1675 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1676 index = 0;
1677 while(value_len = data_len = MAX_PATH,
1678 RegEnumValueW(newkey, index, value, &value_len,
1679 0, &dwType, (LPVOID)data, &data_len) == 0) {
1680 TRACE("guess %d %s is %s\n",
1681 index, debugstr_w(value), debugstr_w(data));
1683 j = FALSE;
1684 for(i=0; i<value_len; i++) {
1685 Wxx = pszIn[i];
1686 Wyy = value[i];
1687 /* remember that TRUE is not-equal */
1688 j = ChrCmpIW(Wxx, Wyy);
1689 if (j) break;
1691 if ((i == value_len) && !j) {
1692 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1693 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1694 RegCloseKey(newkey);
1695 return E_POINTER;
1697 strcpyW(pszOut, data);
1698 strcatW(pszOut, pszIn);
1699 *pcchOut = strlenW(pszOut);
1700 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1701 RegCloseKey(newkey);
1702 return S_OK;
1704 index++;
1706 RegCloseKey(newkey);
1707 return E_FAIL;
1710 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl)
1712 DWORD needed;
1713 HRESULT ret = S_OK;
1714 WCHAR *pszNewUrl;
1715 WCHAR file_colonW[] = {'f','i','l','e',':',0};
1716 WCHAR three_slashesW[] = {'/','/','/',0};
1717 PARSEDURLW parsed_url;
1719 parsed_url.cbSize = sizeof(parsed_url);
1720 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
1721 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
1722 needed = strlenW(pszPath);
1723 if (needed >= *pcchUrl) {
1724 *pcchUrl = needed + 1;
1725 return E_POINTER;
1726 } else {
1727 *pcchUrl = needed;
1728 return S_FALSE;
1733 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
1734 strcpyW(pszNewUrl, file_colonW);
1735 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
1736 strcatW(pszNewUrl, three_slashesW);
1737 strcatW(pszNewUrl, pszPath);
1738 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
1739 HeapFree(GetProcessHeap(), 0, pszNewUrl);
1740 return ret;
1743 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1745 HKEY newkey;
1746 DWORD data_len, dwType;
1747 WCHAR data[MAX_PATH];
1749 static const WCHAR prefix_keyW[] =
1750 {'S','o','f','t','w','a','r','e',
1751 '\\','M','i','c','r','o','s','o','f','t',
1752 '\\','W','i','n','d','o','w','s',
1753 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1754 '\\','U','R','L',
1755 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1757 /* get and prepend default */
1758 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1759 data_len = sizeof(data);
1760 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1761 RegCloseKey(newkey);
1762 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1763 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1764 return E_POINTER;
1766 strcpyW(pszOut, data);
1767 strcatW(pszOut, pszIn);
1768 *pcchOut = strlenW(pszOut);
1769 TRACE("used default %s\n", debugstr_w(pszOut));
1770 return S_OK;
1773 /*************************************************************************
1774 * UrlApplySchemeW [SHLWAPI.@]
1776 * See UrlApplySchemeA.
1778 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1780 PARSEDURLW in_scheme;
1781 DWORD res1;
1782 HRESULT ret;
1784 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1785 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1787 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1789 if (dwFlags & URL_APPLY_GUESSFILE) {
1790 if (*pcchOut > 1 && ':' == pszIn[1]) {
1791 res1 = *pcchOut;
1792 ret = URL_CreateFromPath(pszIn, pszOut, &res1);
1793 if (ret == S_OK || ret == E_POINTER){
1794 *pcchOut = res1;
1795 return ret;
1797 else if (ret == S_FALSE)
1799 return ret;
1804 in_scheme.cbSize = sizeof(in_scheme);
1805 /* See if the base has a scheme */
1806 res1 = ParseURLW(pszIn, &in_scheme);
1807 if (res1) {
1808 /* no scheme in input, need to see if we need to guess */
1809 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1810 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1811 return ret;
1815 /* If we are here, then either invalid scheme,
1816 * or no scheme and can't/failed guess.
1818 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1819 ((res1 != 0)) ) &&
1820 (dwFlags & URL_APPLY_DEFAULT)) {
1821 /* find and apply default scheme */
1822 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1825 return S_FALSE;
1828 /*************************************************************************
1829 * UrlIsA [SHLWAPI.@]
1831 * Determine if a Url is of a certain class.
1833 * PARAMS
1834 * pszUrl [I] Url to check
1835 * Urlis [I] URLIS_ constant from "shlwapi.h"
1837 * RETURNS
1838 * TRUE if pszUrl belongs to the class type in Urlis.
1839 * FALSE Otherwise.
1841 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1843 PARSEDURLA base;
1844 DWORD res1;
1845 LPCSTR last;
1847 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1849 if(!pszUrl)
1850 return FALSE;
1852 switch (Urlis) {
1854 case URLIS_OPAQUE:
1855 base.cbSize = sizeof(base);
1856 res1 = ParseURLA(pszUrl, &base);
1857 if (res1) return FALSE; /* invalid scheme */
1858 switch (base.nScheme)
1860 case URL_SCHEME_MAILTO:
1861 case URL_SCHEME_SHELL:
1862 case URL_SCHEME_JAVASCRIPT:
1863 case URL_SCHEME_VBSCRIPT:
1864 case URL_SCHEME_ABOUT:
1865 return TRUE;
1867 return FALSE;
1869 case URLIS_FILEURL:
1870 return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1871 "file:", 5) == CSTR_EQUAL);
1873 case URLIS_DIRECTORY:
1874 last = pszUrl + strlen(pszUrl) - 1;
1875 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1877 case URLIS_URL:
1878 return PathIsURLA(pszUrl);
1880 case URLIS_NOHISTORY:
1881 case URLIS_APPLIABLE:
1882 case URLIS_HASQUERY:
1883 default:
1884 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1886 return FALSE;
1889 /*************************************************************************
1890 * UrlIsW [SHLWAPI.@]
1892 * See UrlIsA.
1894 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1896 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
1897 PARSEDURLW base;
1898 DWORD res1;
1899 LPCWSTR last;
1901 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1903 if(!pszUrl)
1904 return FALSE;
1906 switch (Urlis) {
1908 case URLIS_OPAQUE:
1909 base.cbSize = sizeof(base);
1910 res1 = ParseURLW(pszUrl, &base);
1911 if (res1) return FALSE; /* invalid scheme */
1912 switch (base.nScheme)
1914 case URL_SCHEME_MAILTO:
1915 case URL_SCHEME_SHELL:
1916 case URL_SCHEME_JAVASCRIPT:
1917 case URL_SCHEME_VBSCRIPT:
1918 case URL_SCHEME_ABOUT:
1919 return TRUE;
1921 return FALSE;
1923 case URLIS_FILEURL:
1924 return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1925 file_colon, 5) == CSTR_EQUAL);
1927 case URLIS_DIRECTORY:
1928 last = pszUrl + strlenW(pszUrl) - 1;
1929 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1931 case URLIS_URL:
1932 return PathIsURLW(pszUrl);
1934 case URLIS_NOHISTORY:
1935 case URLIS_APPLIABLE:
1936 case URLIS_HASQUERY:
1937 default:
1938 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1940 return FALSE;
1943 /*************************************************************************
1944 * UrlIsNoHistoryA [SHLWAPI.@]
1946 * Determine if a Url should not be stored in the users history list.
1948 * PARAMS
1949 * pszUrl [I] Url to check
1951 * RETURNS
1952 * TRUE, if pszUrl should be excluded from the history list,
1953 * FALSE otherwise.
1955 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1957 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1960 /*************************************************************************
1961 * UrlIsNoHistoryW [SHLWAPI.@]
1963 * See UrlIsNoHistoryA.
1965 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1967 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1970 /*************************************************************************
1971 * UrlIsOpaqueA [SHLWAPI.@]
1973 * Determine if a Url is opaque.
1975 * PARAMS
1976 * pszUrl [I] Url to check
1978 * RETURNS
1979 * TRUE if pszUrl is opaque,
1980 * FALSE Otherwise.
1982 * NOTES
1983 * An opaque Url is one that does not start with "<protocol>://".
1985 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1987 return UrlIsA(pszUrl, URLIS_OPAQUE);
1990 /*************************************************************************
1991 * UrlIsOpaqueW [SHLWAPI.@]
1993 * See UrlIsOpaqueA.
1995 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1997 return UrlIsW(pszUrl, URLIS_OPAQUE);
2000 /*************************************************************************
2001 * Scans for characters of type "type" and when not matching found,
2002 * returns pointer to it and length in size.
2004 * Characters tested based on RFC 1738
2006 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
2008 static DWORD alwayszero = 0;
2009 BOOL cont = TRUE;
2011 *size = 0;
2013 switch(type){
2015 case SCHEME:
2016 while (cont) {
2017 if ( (islowerW(*start) && isalphaW(*start)) ||
2018 isdigitW(*start) ||
2019 (*start == '+') ||
2020 (*start == '-') ||
2021 (*start == '.')) {
2022 start++;
2023 (*size)++;
2025 else
2026 cont = FALSE;
2029 if(*start != ':')
2030 *size = 0;
2032 break;
2034 case USERPASS:
2035 while (cont) {
2036 if ( isalphaW(*start) ||
2037 isdigitW(*start) ||
2038 /* user/password only characters */
2039 (*start == ';') ||
2040 (*start == '?') ||
2041 (*start == '&') ||
2042 (*start == '=') ||
2043 /* *extra* characters */
2044 (*start == '!') ||
2045 (*start == '*') ||
2046 (*start == '\'') ||
2047 (*start == '(') ||
2048 (*start == ')') ||
2049 (*start == ',') ||
2050 /* *safe* characters */
2051 (*start == '$') ||
2052 (*start == '_') ||
2053 (*start == '+') ||
2054 (*start == '-') ||
2055 (*start == '.') ||
2056 (*start == ' ')) {
2057 start++;
2058 (*size)++;
2059 } else if (*start == '%') {
2060 if (isxdigitW(*(start+1)) &&
2061 isxdigitW(*(start+2))) {
2062 start += 3;
2063 *size += 3;
2064 } else
2065 cont = FALSE;
2066 } else
2067 cont = FALSE;
2069 break;
2071 case PORT:
2072 while (cont) {
2073 if (isdigitW(*start)) {
2074 start++;
2075 (*size)++;
2077 else
2078 cont = FALSE;
2080 break;
2082 case HOST:
2083 while (cont) {
2084 if (isalnumW(*start) ||
2085 (*start == '-') ||
2086 (*start == '.') ||
2087 (*start == ' ') ||
2088 (*start == '*') ) {
2089 start++;
2090 (*size)++;
2092 else
2093 cont = FALSE;
2095 break;
2096 default:
2097 FIXME("unknown type %d\n", type);
2098 return (LPWSTR)&alwayszero;
2100 /* TRACE("scanned %d characters next char %p<%c>\n",
2101 *size, start, *start); */
2102 return start;
2105 /*************************************************************************
2106 * Attempt to parse URL into pieces.
2108 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2110 LPCWSTR work;
2112 memset(pl, 0, sizeof(WINE_PARSE_URL));
2113 pl->pScheme = pszUrl;
2114 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2115 if (!*work || (*work != ':')) goto ErrorExit;
2116 work++;
2117 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2118 pl->pUserName = work + 2;
2119 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2120 if (*work == ':' ) {
2121 /* parse password */
2122 work++;
2123 pl->pPassword = work;
2124 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2125 if (*work != '@') {
2126 /* what we just parsed must be the hostname and port
2127 * so reset pointers and clear then let it parse */
2128 pl->szUserName = pl->szPassword = 0;
2129 work = pl->pUserName - 1;
2130 pl->pUserName = pl->pPassword = 0;
2132 } else if (*work == '@') {
2133 /* no password */
2134 pl->szPassword = 0;
2135 pl->pPassword = 0;
2136 } else if (!*work || (*work == '/') || (*work == '.')) {
2137 /* what was parsed was hostname, so reset pointers and let it parse */
2138 pl->szUserName = pl->szPassword = 0;
2139 work = pl->pUserName - 1;
2140 pl->pUserName = pl->pPassword = 0;
2141 } else goto ErrorExit;
2143 /* now start parsing hostname or hostnumber */
2144 work++;
2145 pl->pHostName = work;
2146 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2147 if (*work == ':') {
2148 /* parse port */
2149 work++;
2150 pl->pPort = work;
2151 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2153 if (*work == '/') {
2154 /* see if query string */
2155 pl->pQuery = strchrW(work, '?');
2156 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2158 SuccessExit:
2159 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2160 pl->pScheme, pl->szScheme,
2161 pl->pUserName, pl->szUserName,
2162 pl->pPassword, pl->szPassword,
2163 pl->pHostName, pl->szHostName,
2164 pl->pPort, pl->szPort,
2165 pl->pQuery, pl->szQuery);
2166 return S_OK;
2167 ErrorExit:
2168 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2169 return E_INVALIDARG;
2172 /*************************************************************************
2173 * UrlGetPartA [SHLWAPI.@]
2175 * Retrieve part of a Url.
2177 * PARAMS
2178 * pszIn [I] Url to parse
2179 * pszOut [O] Destination for part of pszIn requested
2180 * pcchOut [I] Size of pszOut
2181 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2182 * needed size of pszOut INCLUDING '\0'.
2183 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2184 * dwFlags [I] URL_ flags from "shlwapi.h"
2186 * RETURNS
2187 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2188 * Failure: An HRESULT error code describing the error.
2190 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2191 DWORD dwPart, DWORD dwFlags)
2193 LPWSTR in, out;
2194 DWORD ret, len, len2;
2196 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2197 return E_INVALIDARG;
2199 in = HeapAlloc(GetProcessHeap(), 0,
2200 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2201 out = in + INTERNET_MAX_URL_LENGTH;
2203 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2205 len = INTERNET_MAX_URL_LENGTH;
2206 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2208 if (FAILED(ret)) {
2209 HeapFree(GetProcessHeap(), 0, in);
2210 return ret;
2213 len2 = WideCharToMultiByte(CP_ACP, 0, out, len, NULL, 0, NULL, NULL);
2214 if (len2 > *pcchOut) {
2215 *pcchOut = len2+1;
2216 HeapFree(GetProcessHeap(), 0, in);
2217 return E_POINTER;
2219 len2 = WideCharToMultiByte(CP_ACP, 0, out, len+1, pszOut, *pcchOut, NULL, NULL);
2220 *pcchOut = len2-1;
2221 HeapFree(GetProcessHeap(), 0, in);
2222 return ret;
2225 /*************************************************************************
2226 * UrlGetPartW [SHLWAPI.@]
2228 * See UrlGetPartA.
2230 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2231 DWORD dwPart, DWORD dwFlags)
2233 WINE_PARSE_URL pl;
2234 HRESULT ret;
2235 DWORD scheme, size, schsize;
2236 LPCWSTR addr, schaddr;
2238 TRACE("(%s %p %p(%d) %08x %08x)\n",
2239 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2241 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2242 return E_INVALIDARG;
2244 *pszOut = '\0';
2246 addr = strchrW(pszIn, ':');
2247 if(!addr)
2248 scheme = URL_SCHEME_UNKNOWN;
2249 else
2250 scheme = get_scheme_code(pszIn, addr-pszIn);
2252 ret = URL_ParseUrl(pszIn, &pl);
2254 switch (dwPart) {
2255 case URL_PART_SCHEME:
2256 if (!pl.szScheme) {
2257 *pcchOut = 0;
2258 return S_FALSE;
2260 addr = pl.pScheme;
2261 size = pl.szScheme;
2262 break;
2264 case URL_PART_HOSTNAME:
2265 switch(scheme) {
2266 case URL_SCHEME_FTP:
2267 case URL_SCHEME_HTTP:
2268 case URL_SCHEME_GOPHER:
2269 case URL_SCHEME_TELNET:
2270 case URL_SCHEME_FILE:
2271 case URL_SCHEME_HTTPS:
2272 break;
2273 default:
2274 *pcchOut = 0;
2275 return E_FAIL;
2278 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2279 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2280 *pcchOut = 0;
2281 return S_FALSE;
2284 if (!pl.szHostName) {
2285 *pcchOut = 0;
2286 return S_FALSE;
2288 addr = pl.pHostName;
2289 size = pl.szHostName;
2290 break;
2292 case URL_PART_USERNAME:
2293 if (!pl.szUserName) {
2294 *pcchOut = 0;
2295 return S_FALSE;
2297 addr = pl.pUserName;
2298 size = pl.szUserName;
2299 break;
2301 case URL_PART_PASSWORD:
2302 if (!pl.szPassword) {
2303 *pcchOut = 0;
2304 return S_FALSE;
2306 addr = pl.pPassword;
2307 size = pl.szPassword;
2308 break;
2310 case URL_PART_PORT:
2311 if (!pl.szPort) {
2312 *pcchOut = 0;
2313 return S_FALSE;
2315 addr = pl.pPort;
2316 size = pl.szPort;
2317 break;
2319 case URL_PART_QUERY:
2320 if (!pl.szQuery) {
2321 *pcchOut = 0;
2322 return S_FALSE;
2324 addr = pl.pQuery;
2325 size = pl.szQuery;
2326 break;
2328 default:
2329 *pcchOut = 0;
2330 return E_INVALIDARG;
2333 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2334 if(!pl.pScheme || !pl.szScheme) {
2335 *pcchOut = 0;
2336 return E_FAIL;
2338 schaddr = pl.pScheme;
2339 schsize = pl.szScheme;
2340 if (*pcchOut < schsize + size + 2) {
2341 *pcchOut = schsize + size + 2;
2342 return E_POINTER;
2344 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2345 pszOut[schsize] = ':';
2346 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2347 pszOut[schsize+1+size] = 0;
2348 *pcchOut = schsize + 1 + size;
2350 else {
2351 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2352 memcpy(pszOut, addr, size*sizeof(WCHAR));
2353 pszOut[size] = 0;
2354 *pcchOut = size;
2356 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2358 return ret;
2361 /*************************************************************************
2362 * PathIsURLA [SHLWAPI.@]
2364 * Check if the given path is a Url.
2366 * PARAMS
2367 * lpszPath [I] Path to check.
2369 * RETURNS
2370 * TRUE if lpszPath is a Url.
2371 * FALSE if lpszPath is NULL or not a Url.
2373 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2375 PARSEDURLA base;
2376 HRESULT hres;
2378 TRACE("%s\n", debugstr_a(lpstrPath));
2380 if (!lpstrPath || !*lpstrPath) return FALSE;
2382 /* get protocol */
2383 base.cbSize = sizeof(base);
2384 hres = ParseURLA(lpstrPath, &base);
2385 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2388 /*************************************************************************
2389 * PathIsURLW [SHLWAPI.@]
2391 * See PathIsURLA.
2393 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2395 PARSEDURLW base;
2396 HRESULT hres;
2398 TRACE("%s\n", debugstr_w(lpstrPath));
2400 if (!lpstrPath || !*lpstrPath) return FALSE;
2402 /* get protocol */
2403 base.cbSize = sizeof(base);
2404 hres = ParseURLW(lpstrPath, &base);
2405 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2408 /*************************************************************************
2409 * UrlCreateFromPathA [SHLWAPI.@]
2411 * See UrlCreateFromPathW
2413 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2415 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2416 WCHAR *urlW = bufW;
2417 UNICODE_STRING pathW;
2418 HRESULT ret;
2419 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2421 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2422 return E_INVALIDARG;
2423 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2424 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2425 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2427 if(ret == S_OK || ret == S_FALSE) {
2428 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2429 if(*pcchUrl > lenA) {
2430 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2431 pszUrl[lenA] = 0;
2432 *pcchUrl = lenA;
2433 } else {
2434 *pcchUrl = lenA + 1;
2435 ret = E_POINTER;
2438 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2439 RtlFreeUnicodeString(&pathW);
2440 return ret;
2443 /*************************************************************************
2444 * UrlCreateFromPathW [SHLWAPI.@]
2446 * Create a Url from a file path.
2448 * PARAMS
2449 * pszPath [I] Path to convert
2450 * pszUrl [O] Destination for the converted Url
2451 * pcchUrl [I/O] Length of pszUrl
2452 * dwReserved [I] Reserved, must be 0
2454 * RETURNS
2455 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2456 * Failure: An HRESULT error code.
2458 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2460 HRESULT ret;
2462 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2464 /* Validate arguments */
2465 if (dwReserved != 0)
2466 return E_INVALIDARG;
2467 if (!pszUrl || !pcchUrl)
2468 return E_INVALIDARG;
2470 ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl);
2472 if (S_FALSE == ret)
2473 strcpyW(pszUrl, pszPath);
2475 return ret;
2478 /*************************************************************************
2479 * SHAutoComplete [SHLWAPI.@]
2481 * Enable auto-completion for an edit control.
2483 * PARAMS
2484 * hwndEdit [I] Handle of control to enable auto-completion for
2485 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2487 * RETURNS
2488 * Success: S_OK. Auto-completion is enabled for the control.
2489 * Failure: An HRESULT error code indicating the error.
2491 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2493 FIXME("stub\n");
2494 return S_FALSE;
2497 /*************************************************************************
2498 * MLBuildResURLA [SHLWAPI.405]
2500 * Create a Url pointing to a resource in a module.
2502 * PARAMS
2503 * lpszLibName [I] Name of the module containing the resource
2504 * hMod [I] Callers module handle
2505 * dwFlags [I] Undocumented flags for loading the module
2506 * lpszRes [I] Resource name
2507 * lpszDest [O] Destination for resulting Url
2508 * dwDestLen [I] Length of lpszDest
2510 * RETURNS
2511 * Success: S_OK. lpszDest contains the resource Url.
2512 * Failure: E_INVALIDARG, if any argument is invalid, or
2513 * E_FAIL if dwDestLen is too small.
2515 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2516 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2518 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2519 HRESULT hRet;
2521 if (lpszLibName)
2522 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2524 if (lpszRes)
2525 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2527 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2528 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2530 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2531 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2532 if (SUCCEEDED(hRet) && lpszDest)
2533 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, NULL, NULL);
2535 return hRet;
2538 /*************************************************************************
2539 * MLBuildResURLA [SHLWAPI.406]
2541 * See MLBuildResURLA.
2543 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2544 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2546 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2547 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2548 HRESULT hRet = E_FAIL;
2550 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2551 debugstr_w(lpszRes), lpszDest, dwDestLen);
2553 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2554 !lpszDest || (dwFlags && dwFlags != 2))
2555 return E_INVALIDARG;
2557 if (dwDestLen >= szResLen + 1)
2559 dwDestLen -= (szResLen + 1);
2560 memcpy(lpszDest, szRes, sizeof(szRes));
2562 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2564 if (hMod)
2566 WCHAR szBuff[MAX_PATH];
2567 DWORD len;
2569 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2570 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2572 DWORD dwPathLen = strlenW(szBuff) + 1;
2574 if (dwDestLen >= dwPathLen)
2576 DWORD dwResLen;
2578 dwDestLen -= dwPathLen;
2579 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2581 dwResLen = strlenW(lpszRes) + 1;
2582 if (dwDestLen >= dwResLen + 1)
2584 lpszDest[szResLen + dwPathLen-1] = '/';
2585 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2586 hRet = S_OK;
2590 MLFreeLibrary(hMod);
2593 return hRet;
2596 /***********************************************************************
2597 * UrlFixupW [SHLWAPI.462]
2599 * Checks the scheme part of a URL and attempts to correct misspellings.
2601 * PARAMS
2602 * lpszUrl [I] Pointer to the URL to be corrected
2603 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2604 * dwMaxChars [I] Maximum size of corrected URL
2606 * RETURNS
2607 * success: S_OK if URL corrected or already correct
2608 * failure: S_FALSE if unable to correct / COM error code if other error
2611 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2613 DWORD srcLen;
2615 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2617 if (!url)
2618 return E_FAIL;
2620 srcLen = lstrlenW(url) + 1;
2622 /* For now just copy the URL directly */
2623 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2625 return S_OK;
2628 /*************************************************************************
2629 * IsInternetESCEnabled [SHLWAPI.@]
2631 BOOL WINAPI IsInternetESCEnabled(void)
2633 FIXME(": stub\n");
2634 return FALSE;