shlwapi: Make ParseURL case-insensitive.
[wine.git] / dlls / shlwapi / url.c
blob879a7342cd3a1eed3426f5db9acc4aa595765534
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 && !memicmpW(scheme, shlwapi_schemes[i].scheme_name, scheme_len))
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 UrlCanonicalizeW(pszBase, mbase, &len, myflags);
693 /* Canonicalize the relative input prior to looking for the scheme */
694 len = INTERNET_MAX_URL_LENGTH;
695 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 it's 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(dwFlags & URL_ESCAPE_AS_UTF8) {
962 RtlFreeUnicodeString(&urlW);
963 return E_NOTIMPL;
965 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
966 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
967 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
969 if(ret == S_OK) {
970 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
971 if(*pcchEscaped > lenA) {
972 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
973 pszEscaped[lenA] = 0;
974 *pcchEscaped = lenA;
975 } else {
976 *pcchEscaped = lenA + 1;
977 ret = E_POINTER;
980 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
981 RtlFreeUnicodeString(&urlW);
982 return ret;
985 #define WINE_URL_BASH_AS_SLASH 0x01
986 #define WINE_URL_COLLAPSE_SLASHES 0x02
987 #define WINE_URL_ESCAPE_SLASH 0x04
988 #define WINE_URL_ESCAPE_HASH 0x08
989 #define WINE_URL_ESCAPE_QUESTION 0x10
990 #define WINE_URL_STOP_ON_HASH 0x20
991 #define WINE_URL_STOP_ON_QUESTION 0x40
993 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD flags, DWORD int_flags)
995 if (flags & URL_ESCAPE_SPACES_ONLY)
996 return ch == ' ';
998 if ((flags & URL_ESCAPE_PERCENT) && (ch == '%'))
999 return TRUE;
1001 if ((flags & URL_ESCAPE_AS_UTF8) && (ch >= 0x80))
1002 return TRUE;
1004 if (ch <= 31 || (ch >= 127 && ch <= 255) )
1005 return TRUE;
1007 if (isalnumW(ch))
1008 return FALSE;
1010 switch (ch) {
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 case '&':
1024 return TRUE;
1025 case '/':
1026 return !!(int_flags & WINE_URL_ESCAPE_SLASH);
1027 case '?':
1028 return !!(int_flags & WINE_URL_ESCAPE_QUESTION);
1029 case '#':
1030 return !!(int_flags & WINE_URL_ESCAPE_HASH);
1031 default:
1032 return FALSE;
1037 /*************************************************************************
1038 * UrlEscapeW [SHLWAPI.@]
1040 * Converts unsafe characters in a Url into escape sequences.
1042 * PARAMS
1043 * pszUrl [I] Url to modify
1044 * pszEscaped [O] Destination for modified Url
1045 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1046 * dwFlags [I] URL_ flags from "shlwapi.h"
1048 * RETURNS
1049 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1050 * contains its length.
1051 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1052 * pcchEscaped is set to the required length.
1054 * Converts unsafe characters into their escape sequences.
1056 * NOTES
1057 * - By default this function stops converting at the first '?' or
1058 * '#' character.
1059 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1060 * converted, but the conversion continues past a '?' or '#'.
1061 * - Note that this function did not work well (or at all) in shlwapi version 4.
1063 * BUGS
1064 * Only the following flags are implemented:
1065 *| URL_ESCAPE_SPACES_ONLY
1066 *| URL_DONT_ESCAPE_EXTRA_INFO
1067 *| URL_ESCAPE_SEGMENT_ONLY
1068 *| URL_ESCAPE_PERCENT
1070 HRESULT WINAPI UrlEscapeW(
1071 LPCWSTR pszUrl,
1072 LPWSTR pszEscaped,
1073 LPDWORD pcchEscaped,
1074 DWORD dwFlags)
1076 LPCWSTR src;
1077 DWORD needed = 0, ret;
1078 BOOL stop_escaping = FALSE;
1079 WCHAR next[12], *dst, *dst_ptr;
1080 INT i, len;
1081 PARSEDURLW parsed_url;
1082 DWORD int_flags;
1083 DWORD slashes = 0;
1084 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1086 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1087 pszEscaped, pcchEscaped, dwFlags);
1089 if(!pszUrl || !pcchEscaped)
1090 return E_INVALIDARG;
1092 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1093 URL_ESCAPE_SEGMENT_ONLY |
1094 URL_DONT_ESCAPE_EXTRA_INFO |
1095 URL_ESCAPE_PERCENT |
1096 URL_ESCAPE_AS_UTF8))
1097 FIXME("Unimplemented flags: %08x\n", dwFlags);
1099 dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1100 if(!dst_ptr)
1101 return E_OUTOFMEMORY;
1103 /* fix up flags */
1104 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1105 /* if SPACES_ONLY specified, reset the other controls */
1106 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1107 URL_ESCAPE_PERCENT |
1108 URL_ESCAPE_SEGMENT_ONLY);
1110 else
1111 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1112 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1115 int_flags = 0;
1116 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1117 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1118 } else {
1119 parsed_url.cbSize = sizeof(parsed_url);
1120 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1121 parsed_url.nScheme = URL_SCHEME_INVALID;
1123 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1125 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1126 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1128 switch(parsed_url.nScheme) {
1129 case URL_SCHEME_FILE:
1130 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1131 int_flags &= ~WINE_URL_STOP_ON_HASH;
1132 break;
1134 case URL_SCHEME_HTTP:
1135 case URL_SCHEME_HTTPS:
1136 int_flags |= WINE_URL_BASH_AS_SLASH;
1137 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1138 int_flags |= WINE_URL_ESCAPE_SLASH;
1139 break;
1141 case URL_SCHEME_MAILTO:
1142 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1143 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1144 break;
1146 case URL_SCHEME_INVALID:
1147 break;
1149 case URL_SCHEME_FTP:
1150 default:
1151 if(parsed_url.pszSuffix[0] != '/')
1152 int_flags |= WINE_URL_ESCAPE_SLASH;
1153 break;
1157 for(src = pszUrl; *src; ) {
1158 WCHAR cur = *src;
1159 len = 0;
1161 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1162 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1163 while(cur == '/' || cur == '\\') {
1164 slashes++;
1165 cur = *++src;
1167 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1168 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1169 src += localhost_len + 1;
1170 slashes = 3;
1173 switch(slashes) {
1174 case 1:
1175 case 3:
1176 next[0] = next[1] = next[2] = '/';
1177 len = 3;
1178 break;
1179 case 0:
1180 len = 0;
1181 break;
1182 default:
1183 next[0] = next[1] = '/';
1184 len = 2;
1185 break;
1188 if(len == 0) {
1190 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1191 stop_escaping = TRUE;
1193 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1194 stop_escaping = TRUE;
1196 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1198 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1199 if(dwFlags & URL_ESCAPE_AS_UTF8) {
1200 char utf[16];
1202 if ((cur >= 0xd800 && cur <= 0xdfff) &&
1203 (src[1] >= 0xdc00 && src[1] <= 0xdfff))
1205 len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, src, 2,
1206 utf, sizeof(utf), NULL, NULL );
1207 src++;
1209 else
1210 len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, &cur, 1,
1211 utf, sizeof(utf), NULL, NULL );
1213 if (!len) {
1214 utf[0] = 0xef;
1215 utf[1] = 0xbf;
1216 utf[2] = 0xbd;
1217 len = 3;
1220 for(i = 0; i < len; i++) {
1221 next[i*3+0] = '%';
1222 next[i*3+1] = hexDigits[(utf[i] >> 4) & 0xf];
1223 next[i*3+2] = hexDigits[utf[i] & 0xf];
1225 len *= 3;
1226 } else {
1227 next[0] = '%';
1228 next[1] = hexDigits[(cur >> 4) & 0xf];
1229 next[2] = hexDigits[cur & 0xf];
1230 len = 3;
1232 } else {
1233 next[0] = cur;
1234 len = 1;
1236 src++;
1239 if(needed + len <= *pcchEscaped) {
1240 memcpy(dst, next, len*sizeof(WCHAR));
1241 dst += len;
1243 needed += len;
1246 if(needed < *pcchEscaped) {
1247 *dst = '\0';
1248 memcpy(pszEscaped, dst_ptr, (needed+1)*sizeof(WCHAR));
1250 ret = S_OK;
1251 } else {
1252 needed++; /* add one for the '\0' */
1253 ret = E_POINTER;
1255 *pcchEscaped = needed;
1257 HeapFree(GetProcessHeap(), 0, dst_ptr);
1258 return ret;
1262 /*************************************************************************
1263 * UrlUnescapeA [SHLWAPI.@]
1265 * Converts Url escape sequences back to ordinary characters.
1267 * PARAMS
1268 * pszUrl [I/O] Url to convert
1269 * pszUnescaped [O] Destination for converted Url
1270 * pcchUnescaped [I/O] Size of output string
1271 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1273 * RETURNS
1274 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1275 * dwFlags includes URL_ESCAPE_INPLACE.
1276 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1277 * this case pcchUnescaped is set to the size required.
1278 * NOTES
1279 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1280 * the first occurrence of either a '?' or '#' character.
1282 HRESULT WINAPI UrlUnescapeA(
1283 LPSTR pszUrl,
1284 LPSTR pszUnescaped,
1285 LPDWORD pcchUnescaped,
1286 DWORD dwFlags)
1288 char *dst, next;
1289 LPCSTR src;
1290 HRESULT ret;
1291 DWORD needed;
1292 BOOL stop_unescaping = FALSE;
1294 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1295 pcchUnescaped, dwFlags);
1297 if (!pszUrl) return E_INVALIDARG;
1299 if(dwFlags & URL_UNESCAPE_INPLACE)
1300 dst = pszUrl;
1301 else
1303 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1304 dst = pszUnescaped;
1307 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1308 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1309 (*src == '#' || *src == '?')) {
1310 stop_unescaping = TRUE;
1311 next = *src;
1312 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1313 && stop_unescaping == FALSE) {
1314 INT ih;
1315 char buf[3];
1316 memcpy(buf, src + 1, 2);
1317 buf[2] = '\0';
1318 ih = strtol(buf, NULL, 16);
1319 next = (CHAR) ih;
1320 src += 2; /* Advance to end of escape */
1321 } else
1322 next = *src;
1324 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1325 *dst++ = next;
1328 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1329 *dst = '\0';
1330 ret = S_OK;
1331 } else {
1332 needed++; /* add one for the '\0' */
1333 ret = E_POINTER;
1335 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1336 *pcchUnescaped = needed;
1338 if (ret == S_OK) {
1339 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1340 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1343 return ret;
1346 /*************************************************************************
1347 * UrlUnescapeW [SHLWAPI.@]
1349 * See UrlUnescapeA.
1351 HRESULT WINAPI UrlUnescapeW(
1352 LPWSTR pszUrl,
1353 LPWSTR pszUnescaped,
1354 LPDWORD pcchUnescaped,
1355 DWORD dwFlags)
1357 WCHAR *dst, next;
1358 LPCWSTR src;
1359 HRESULT ret;
1360 DWORD needed;
1361 BOOL stop_unescaping = FALSE;
1363 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1364 pcchUnescaped, dwFlags);
1366 if(!pszUrl) return E_INVALIDARG;
1368 if(dwFlags & URL_UNESCAPE_INPLACE)
1369 dst = pszUrl;
1370 else
1372 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1373 dst = pszUnescaped;
1376 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1377 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1378 (*src == '#' || *src == '?')) {
1379 stop_unescaping = TRUE;
1380 next = *src;
1381 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1382 && stop_unescaping == FALSE) {
1383 INT ih;
1384 WCHAR buf[5] = {'0','x',0};
1385 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1386 buf[4] = 0;
1387 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1388 next = (WCHAR) ih;
1389 src += 2; /* Advance to end of escape */
1390 } else
1391 next = *src;
1393 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1394 *dst++ = next;
1397 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1398 *dst = '\0';
1399 ret = S_OK;
1400 } else {
1401 needed++; /* add one for the '\0' */
1402 ret = E_POINTER;
1404 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1405 *pcchUnescaped = needed;
1407 if (ret == S_OK) {
1408 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1409 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1412 return ret;
1415 /*************************************************************************
1416 * UrlGetLocationA [SHLWAPI.@]
1418 * Get the location from a Url.
1420 * PARAMS
1421 * pszUrl [I] Url to get the location from
1423 * RETURNS
1424 * A pointer to the start of the location in pszUrl, or NULL if there is
1425 * no location.
1427 * NOTES
1428 * - MSDN erroneously states that "The location is the segment of the Url
1429 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1430 * stop at '?' and always return a NULL in this case.
1431 * - MSDN also erroneously states that "If a file URL has a query string,
1432 * the returned string is the query string". In all tested cases, if the
1433 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1434 *| Result Url
1435 *| ------ ---
1436 *| NULL file://aa/b/cd#hohoh
1437 *| #hohoh http://aa/b/cd#hohoh
1438 *| NULL fi://aa/b/cd#hohoh
1439 *| #hohoh ff://aa/b/cd#hohoh
1441 LPCSTR WINAPI UrlGetLocationA(
1442 LPCSTR pszUrl)
1444 PARSEDURLA base;
1445 DWORD res1;
1447 base.cbSize = sizeof(base);
1448 res1 = ParseURLA(pszUrl, &base);
1449 if (res1) return NULL; /* invalid scheme */
1451 /* if scheme is file: then never return pointer */
1452 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1454 /* Look for '#' and return its addr */
1455 return strchr(base.pszSuffix, '#');
1458 /*************************************************************************
1459 * UrlGetLocationW [SHLWAPI.@]
1461 * See UrlGetLocationA.
1463 LPCWSTR WINAPI UrlGetLocationW(
1464 LPCWSTR pszUrl)
1466 PARSEDURLW base;
1467 DWORD res1;
1469 base.cbSize = sizeof(base);
1470 res1 = ParseURLW(pszUrl, &base);
1471 if (res1) return NULL; /* invalid scheme */
1473 /* if scheme is file: then never return pointer */
1474 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1476 /* Look for '#' and return its addr */
1477 return strchrW(base.pszSuffix, '#');
1480 /*************************************************************************
1481 * UrlCompareA [SHLWAPI.@]
1483 * Compare two Urls.
1485 * PARAMS
1486 * pszUrl1 [I] First Url to compare
1487 * pszUrl2 [I] Url to compare to pszUrl1
1488 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1490 * RETURNS
1491 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1492 * than, equal to, or less than pszUrl1 respectively.
1494 INT WINAPI UrlCompareA(
1495 LPCSTR pszUrl1,
1496 LPCSTR pszUrl2,
1497 BOOL fIgnoreSlash)
1499 INT ret, len, len1, len2;
1501 if (!fIgnoreSlash)
1502 return strcmp(pszUrl1, pszUrl2);
1503 len1 = strlen(pszUrl1);
1504 if (pszUrl1[len1-1] == '/') len1--;
1505 len2 = strlen(pszUrl2);
1506 if (pszUrl2[len2-1] == '/') len2--;
1507 if (len1 == len2)
1508 return strncmp(pszUrl1, pszUrl2, len1);
1509 len = min(len1, len2);
1510 ret = strncmp(pszUrl1, pszUrl2, len);
1511 if (ret) return ret;
1512 if (len1 > len2) return 1;
1513 return -1;
1516 /*************************************************************************
1517 * UrlCompareW [SHLWAPI.@]
1519 * See UrlCompareA.
1521 INT WINAPI UrlCompareW(
1522 LPCWSTR pszUrl1,
1523 LPCWSTR pszUrl2,
1524 BOOL fIgnoreSlash)
1526 INT ret;
1527 size_t len, len1, len2;
1529 if (!fIgnoreSlash)
1530 return strcmpW(pszUrl1, pszUrl2);
1531 len1 = strlenW(pszUrl1);
1532 if (pszUrl1[len1-1] == '/') len1--;
1533 len2 = strlenW(pszUrl2);
1534 if (pszUrl2[len2-1] == '/') len2--;
1535 if (len1 == len2)
1536 return strncmpW(pszUrl1, pszUrl2, len1);
1537 len = min(len1, len2);
1538 ret = strncmpW(pszUrl1, pszUrl2, len);
1539 if (ret) return ret;
1540 if (len1 > len2) return 1;
1541 return -1;
1544 /*************************************************************************
1545 * HashData [SHLWAPI.@]
1547 * Hash an input block into a variable sized digest.
1549 * PARAMS
1550 * lpSrc [I] Input block
1551 * nSrcLen [I] Length of lpSrc
1552 * lpDest [I] Output for hash digest
1553 * nDestLen [I] Length of lpDest
1555 * RETURNS
1556 * Success: TRUE. lpDest is filled with the computed hash value.
1557 * Failure: FALSE, if any argument is invalid.
1559 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1560 unsigned char *lpDest, DWORD nDestLen)
1562 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1564 if (!lpSrc || !lpDest)
1565 return E_INVALIDARG;
1567 while (destCount >= 0)
1569 lpDest[destCount] = (destCount & 0xff);
1570 destCount--;
1573 while (srcCount >= 0)
1575 destCount = nDestLen - 1;
1576 while (destCount >= 0)
1578 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1579 destCount--;
1581 srcCount--;
1583 return S_OK;
1586 /*************************************************************************
1587 * UrlHashA [SHLWAPI.@]
1589 * Produce a Hash from a Url.
1591 * PARAMS
1592 * pszUrl [I] Url to hash
1593 * lpDest [O] Destinationh for hash
1594 * nDestLen [I] Length of lpDest
1596 * RETURNS
1597 * Success: S_OK. lpDest is filled with the computed hash value.
1598 * Failure: E_INVALIDARG, if any argument is invalid.
1600 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1602 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1603 return E_INVALIDARG;
1605 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1606 return S_OK;
1609 /*************************************************************************
1610 * UrlHashW [SHLWAPI.@]
1612 * See UrlHashA.
1614 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1616 char szUrl[MAX_PATH];
1618 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1620 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1621 return E_INVALIDARG;
1623 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1624 * return the same digests for the same URL.
1626 WideCharToMultiByte(CP_ACP, 0, pszUrl, -1, szUrl, MAX_PATH, NULL, NULL);
1627 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1628 return S_OK;
1631 /*************************************************************************
1632 * UrlApplySchemeA [SHLWAPI.@]
1634 * Apply a scheme to a Url.
1636 * PARAMS
1637 * pszIn [I] Url to apply scheme to
1638 * pszOut [O] Destination for modified Url
1639 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1640 * dwFlags [I] URL_ flags from "shlwapi.h"
1642 * RETURNS
1643 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1644 * Failure: An HRESULT error code describing the error.
1646 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1648 LPWSTR in, out;
1649 HRESULT ret;
1650 DWORD len;
1652 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1653 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1655 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1657 in = HeapAlloc(GetProcessHeap(), 0,
1658 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1659 out = in + INTERNET_MAX_URL_LENGTH;
1661 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1662 len = INTERNET_MAX_URL_LENGTH;
1664 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1665 if (ret != S_OK) {
1666 HeapFree(GetProcessHeap(), 0, in);
1667 return ret;
1670 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1671 if (len > *pcchOut) {
1672 ret = E_POINTER;
1673 goto cleanup;
1676 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1677 len--;
1679 cleanup:
1680 *pcchOut = len;
1681 HeapFree(GetProcessHeap(), 0, in);
1682 return ret;
1685 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1687 HKEY newkey;
1688 BOOL j;
1689 INT index;
1690 DWORD value_len, data_len, dwType, i;
1691 WCHAR reg_path[MAX_PATH];
1692 WCHAR value[MAX_PATH], data[MAX_PATH];
1693 WCHAR Wxx, Wyy;
1695 MultiByteToWideChar(CP_ACP, 0,
1696 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1697 -1, reg_path, MAX_PATH);
1698 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1699 index = 0;
1700 while(value_len = data_len = MAX_PATH,
1701 RegEnumValueW(newkey, index, value, &value_len,
1702 0, &dwType, (LPVOID)data, &data_len) == 0) {
1703 TRACE("guess %d %s is %s\n",
1704 index, debugstr_w(value), debugstr_w(data));
1706 j = FALSE;
1707 for(i=0; i<value_len; i++) {
1708 Wxx = pszIn[i];
1709 Wyy = value[i];
1710 /* remember that TRUE is not-equal */
1711 j = ChrCmpIW(Wxx, Wyy);
1712 if (j) break;
1714 if ((i == value_len) && !j) {
1715 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1716 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1717 RegCloseKey(newkey);
1718 return E_POINTER;
1720 strcpyW(pszOut, data);
1721 strcatW(pszOut, pszIn);
1722 *pcchOut = strlenW(pszOut);
1723 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1724 RegCloseKey(newkey);
1725 return S_OK;
1727 index++;
1729 RegCloseKey(newkey);
1730 return E_FAIL;
1733 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl)
1735 DWORD needed;
1736 HRESULT ret = S_OK;
1737 WCHAR *pszNewUrl;
1738 WCHAR file_colonW[] = {'f','i','l','e',':',0};
1739 WCHAR three_slashesW[] = {'/','/','/',0};
1740 PARSEDURLW parsed_url;
1742 parsed_url.cbSize = sizeof(parsed_url);
1743 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
1744 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
1745 needed = strlenW(pszPath);
1746 if (needed >= *pcchUrl) {
1747 *pcchUrl = needed + 1;
1748 return E_POINTER;
1749 } else {
1750 *pcchUrl = needed;
1751 return S_FALSE;
1756 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
1757 strcpyW(pszNewUrl, file_colonW);
1758 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
1759 strcatW(pszNewUrl, three_slashesW);
1760 strcatW(pszNewUrl, pszPath);
1761 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
1762 HeapFree(GetProcessHeap(), 0, pszNewUrl);
1763 return ret;
1766 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1768 HKEY newkey;
1769 DWORD data_len, dwType;
1770 WCHAR data[MAX_PATH];
1772 static const WCHAR prefix_keyW[] =
1773 {'S','o','f','t','w','a','r','e',
1774 '\\','M','i','c','r','o','s','o','f','t',
1775 '\\','W','i','n','d','o','w','s',
1776 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1777 '\\','U','R','L',
1778 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1780 /* get and prepend default */
1781 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1782 data_len = sizeof(data);
1783 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1784 RegCloseKey(newkey);
1785 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1786 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1787 return E_POINTER;
1789 strcpyW(pszOut, data);
1790 strcatW(pszOut, pszIn);
1791 *pcchOut = strlenW(pszOut);
1792 TRACE("used default %s\n", debugstr_w(pszOut));
1793 return S_OK;
1796 /*************************************************************************
1797 * UrlApplySchemeW [SHLWAPI.@]
1799 * See UrlApplySchemeA.
1801 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1803 PARSEDURLW in_scheme;
1804 DWORD res1;
1805 HRESULT ret;
1807 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1808 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1810 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1812 if (dwFlags & URL_APPLY_GUESSFILE) {
1813 if (*pcchOut > 1 && ':' == pszIn[1]) {
1814 res1 = *pcchOut;
1815 ret = URL_CreateFromPath(pszIn, pszOut, &res1);
1816 if (ret == S_OK || ret == E_POINTER){
1817 *pcchOut = res1;
1818 return ret;
1820 else if (ret == S_FALSE)
1822 return ret;
1827 in_scheme.cbSize = sizeof(in_scheme);
1828 /* See if the base has a scheme */
1829 res1 = ParseURLW(pszIn, &in_scheme);
1830 if (res1) {
1831 /* no scheme in input, need to see if we need to guess */
1832 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1833 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1834 return ret;
1838 /* If we are here, then either invalid scheme,
1839 * or no scheme and can't/failed guess.
1841 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1842 ((res1 != 0)) ) &&
1843 (dwFlags & URL_APPLY_DEFAULT)) {
1844 /* find and apply default scheme */
1845 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1848 return S_FALSE;
1851 /*************************************************************************
1852 * UrlIsA [SHLWAPI.@]
1854 * Determine if a Url is of a certain class.
1856 * PARAMS
1857 * pszUrl [I] Url to check
1858 * Urlis [I] URLIS_ constant from "shlwapi.h"
1860 * RETURNS
1861 * TRUE if pszUrl belongs to the class type in Urlis.
1862 * FALSE Otherwise.
1864 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1866 PARSEDURLA base;
1867 DWORD res1;
1868 LPCSTR last;
1870 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1872 if(!pszUrl)
1873 return FALSE;
1875 switch (Urlis) {
1877 case URLIS_OPAQUE:
1878 base.cbSize = sizeof(base);
1879 res1 = ParseURLA(pszUrl, &base);
1880 if (res1) return FALSE; /* invalid scheme */
1881 switch (base.nScheme)
1883 case URL_SCHEME_MAILTO:
1884 case URL_SCHEME_SHELL:
1885 case URL_SCHEME_JAVASCRIPT:
1886 case URL_SCHEME_VBSCRIPT:
1887 case URL_SCHEME_ABOUT:
1888 return TRUE;
1890 return FALSE;
1892 case URLIS_FILEURL:
1893 return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1894 "file:", 5) == CSTR_EQUAL);
1896 case URLIS_DIRECTORY:
1897 last = pszUrl + strlen(pszUrl) - 1;
1898 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1900 case URLIS_URL:
1901 return PathIsURLA(pszUrl);
1903 case URLIS_NOHISTORY:
1904 case URLIS_APPLIABLE:
1905 case URLIS_HASQUERY:
1906 default:
1907 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1909 return FALSE;
1912 /*************************************************************************
1913 * UrlIsW [SHLWAPI.@]
1915 * See UrlIsA.
1917 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1919 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
1920 PARSEDURLW base;
1921 DWORD res1;
1922 LPCWSTR last;
1924 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1926 if(!pszUrl)
1927 return FALSE;
1929 switch (Urlis) {
1931 case URLIS_OPAQUE:
1932 base.cbSize = sizeof(base);
1933 res1 = ParseURLW(pszUrl, &base);
1934 if (res1) return FALSE; /* invalid scheme */
1935 switch (base.nScheme)
1937 case URL_SCHEME_MAILTO:
1938 case URL_SCHEME_SHELL:
1939 case URL_SCHEME_JAVASCRIPT:
1940 case URL_SCHEME_VBSCRIPT:
1941 case URL_SCHEME_ABOUT:
1942 return TRUE;
1944 return FALSE;
1946 case URLIS_FILEURL:
1947 return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1948 file_colon, 5) == CSTR_EQUAL);
1950 case URLIS_DIRECTORY:
1951 last = pszUrl + strlenW(pszUrl) - 1;
1952 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1954 case URLIS_URL:
1955 return PathIsURLW(pszUrl);
1957 case URLIS_NOHISTORY:
1958 case URLIS_APPLIABLE:
1959 case URLIS_HASQUERY:
1960 default:
1961 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1963 return FALSE;
1966 /*************************************************************************
1967 * UrlIsNoHistoryA [SHLWAPI.@]
1969 * Determine if a Url should not be stored in the users history list.
1971 * PARAMS
1972 * pszUrl [I] Url to check
1974 * RETURNS
1975 * TRUE, if pszUrl should be excluded from the history list,
1976 * FALSE otherwise.
1978 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1980 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1983 /*************************************************************************
1984 * UrlIsNoHistoryW [SHLWAPI.@]
1986 * See UrlIsNoHistoryA.
1988 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1990 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1993 /*************************************************************************
1994 * UrlIsOpaqueA [SHLWAPI.@]
1996 * Determine if a Url is opaque.
1998 * PARAMS
1999 * pszUrl [I] Url to check
2001 * RETURNS
2002 * TRUE if pszUrl is opaque,
2003 * FALSE Otherwise.
2005 * NOTES
2006 * An opaque Url is one that does not start with "<protocol>://".
2008 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
2010 return UrlIsA(pszUrl, URLIS_OPAQUE);
2013 /*************************************************************************
2014 * UrlIsOpaqueW [SHLWAPI.@]
2016 * See UrlIsOpaqueA.
2018 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
2020 return UrlIsW(pszUrl, URLIS_OPAQUE);
2023 /*************************************************************************
2024 * Scans for characters of type "type" and when not matching found,
2025 * returns pointer to it and length in size.
2027 * Characters tested based on RFC 1738
2029 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
2031 static DWORD alwayszero = 0;
2032 BOOL cont = TRUE;
2034 *size = 0;
2036 switch(type){
2038 case SCHEME:
2039 while (cont) {
2040 if ( (islowerW(*start) && isalphaW(*start)) ||
2041 isdigitW(*start) ||
2042 (*start == '+') ||
2043 (*start == '-') ||
2044 (*start == '.')) {
2045 start++;
2046 (*size)++;
2048 else
2049 cont = FALSE;
2052 if(*start != ':')
2053 *size = 0;
2055 break;
2057 case USERPASS:
2058 while (cont) {
2059 if ( isalphaW(*start) ||
2060 isdigitW(*start) ||
2061 /* user/password only characters */
2062 (*start == ';') ||
2063 (*start == '?') ||
2064 (*start == '&') ||
2065 (*start == '=') ||
2066 /* *extra* characters */
2067 (*start == '!') ||
2068 (*start == '*') ||
2069 (*start == '\'') ||
2070 (*start == '(') ||
2071 (*start == ')') ||
2072 (*start == ',') ||
2073 /* *safe* characters */
2074 (*start == '$') ||
2075 (*start == '_') ||
2076 (*start == '+') ||
2077 (*start == '-') ||
2078 (*start == '.') ||
2079 (*start == ' ')) {
2080 start++;
2081 (*size)++;
2082 } else if (*start == '%') {
2083 if (isxdigitW(*(start+1)) &&
2084 isxdigitW(*(start+2))) {
2085 start += 3;
2086 *size += 3;
2087 } else
2088 cont = FALSE;
2089 } else
2090 cont = FALSE;
2092 break;
2094 case PORT:
2095 while (cont) {
2096 if (isdigitW(*start)) {
2097 start++;
2098 (*size)++;
2100 else
2101 cont = FALSE;
2103 break;
2105 case HOST:
2106 while (cont) {
2107 if (isalnumW(*start) ||
2108 (*start == '-') ||
2109 (*start == '.') ||
2110 (*start == ' ') ||
2111 (*start == '*') ) {
2112 start++;
2113 (*size)++;
2115 else
2116 cont = FALSE;
2118 break;
2119 default:
2120 FIXME("unknown type %d\n", type);
2121 return (LPWSTR)&alwayszero;
2123 /* TRACE("scanned %d characters next char %p<%c>\n",
2124 *size, start, *start); */
2125 return start;
2128 /*************************************************************************
2129 * Attempt to parse URL into pieces.
2131 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2133 LPCWSTR work;
2135 memset(pl, 0, sizeof(WINE_PARSE_URL));
2136 pl->pScheme = pszUrl;
2137 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2138 if (!*work || (*work != ':')) goto ErrorExit;
2139 work++;
2140 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2141 pl->pUserName = work + 2;
2142 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2143 if (*work == ':' ) {
2144 /* parse password */
2145 work++;
2146 pl->pPassword = work;
2147 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2148 if (*work != '@') {
2149 /* what we just parsed must be the hostname and port
2150 * so reset pointers and clear then let it parse */
2151 pl->szUserName = pl->szPassword = 0;
2152 work = pl->pUserName - 1;
2153 pl->pUserName = pl->pPassword = 0;
2155 } else if (*work == '@') {
2156 /* no password */
2157 pl->szPassword = 0;
2158 pl->pPassword = 0;
2159 } else if (!*work || (*work == '/') || (*work == '.')) {
2160 /* what was parsed was hostname, so reset pointers and let it parse */
2161 pl->szUserName = pl->szPassword = 0;
2162 work = pl->pUserName - 1;
2163 pl->pUserName = pl->pPassword = 0;
2164 } else goto ErrorExit;
2166 /* now start parsing hostname or hostnumber */
2167 work++;
2168 pl->pHostName = work;
2169 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2170 if (*work == ':') {
2171 /* parse port */
2172 work++;
2173 pl->pPort = work;
2174 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2176 if (*work == '/') {
2177 /* see if query string */
2178 pl->pQuery = strchrW(work, '?');
2179 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2181 SuccessExit:
2182 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2183 pl->pScheme, pl->szScheme,
2184 pl->pUserName, pl->szUserName,
2185 pl->pPassword, pl->szPassword,
2186 pl->pHostName, pl->szHostName,
2187 pl->pPort, pl->szPort,
2188 pl->pQuery, pl->szQuery);
2189 return S_OK;
2190 ErrorExit:
2191 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2192 return E_INVALIDARG;
2195 /*************************************************************************
2196 * UrlGetPartA [SHLWAPI.@]
2198 * Retrieve part of a Url.
2200 * PARAMS
2201 * pszIn [I] Url to parse
2202 * pszOut [O] Destination for part of pszIn requested
2203 * pcchOut [I] Size of pszOut
2204 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2205 * needed size of pszOut INCLUDING '\0'.
2206 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2207 * dwFlags [I] URL_ flags from "shlwapi.h"
2209 * RETURNS
2210 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2211 * Failure: An HRESULT error code describing the error.
2213 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2214 DWORD dwPart, DWORD dwFlags)
2216 LPWSTR in, out;
2217 DWORD ret, len, len2;
2219 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2220 return E_INVALIDARG;
2222 in = HeapAlloc(GetProcessHeap(), 0,
2223 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2224 out = in + INTERNET_MAX_URL_LENGTH;
2226 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2228 len = INTERNET_MAX_URL_LENGTH;
2229 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2231 if (FAILED(ret)) {
2232 HeapFree(GetProcessHeap(), 0, in);
2233 return ret;
2236 len2 = WideCharToMultiByte(CP_ACP, 0, out, len, NULL, 0, NULL, NULL);
2237 if (len2 > *pcchOut) {
2238 *pcchOut = len2+1;
2239 HeapFree(GetProcessHeap(), 0, in);
2240 return E_POINTER;
2242 len2 = WideCharToMultiByte(CP_ACP, 0, out, len+1, pszOut, *pcchOut, NULL, NULL);
2243 *pcchOut = len2-1;
2244 HeapFree(GetProcessHeap(), 0, in);
2245 return ret;
2248 /*************************************************************************
2249 * UrlGetPartW [SHLWAPI.@]
2251 * See UrlGetPartA.
2253 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2254 DWORD dwPart, DWORD dwFlags)
2256 WINE_PARSE_URL pl;
2257 HRESULT ret;
2258 DWORD scheme, size, schsize;
2259 LPCWSTR addr, schaddr;
2261 TRACE("(%s %p %p(%d) %08x %08x)\n",
2262 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2264 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2265 return E_INVALIDARG;
2267 *pszOut = '\0';
2269 addr = strchrW(pszIn, ':');
2270 if(!addr)
2271 scheme = URL_SCHEME_UNKNOWN;
2272 else
2273 scheme = get_scheme_code(pszIn, addr-pszIn);
2275 ret = URL_ParseUrl(pszIn, &pl);
2277 switch (dwPart) {
2278 case URL_PART_SCHEME:
2279 if (!pl.szScheme) {
2280 *pcchOut = 0;
2281 return S_FALSE;
2283 addr = pl.pScheme;
2284 size = pl.szScheme;
2285 break;
2287 case URL_PART_HOSTNAME:
2288 switch(scheme) {
2289 case URL_SCHEME_FTP:
2290 case URL_SCHEME_HTTP:
2291 case URL_SCHEME_GOPHER:
2292 case URL_SCHEME_TELNET:
2293 case URL_SCHEME_FILE:
2294 case URL_SCHEME_HTTPS:
2295 break;
2296 default:
2297 *pcchOut = 0;
2298 return E_FAIL;
2301 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2302 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2303 *pcchOut = 0;
2304 return S_FALSE;
2307 if (!pl.szHostName) {
2308 *pcchOut = 0;
2309 return S_FALSE;
2311 addr = pl.pHostName;
2312 size = pl.szHostName;
2313 break;
2315 case URL_PART_USERNAME:
2316 if (!pl.szUserName) {
2317 *pcchOut = 0;
2318 return S_FALSE;
2320 addr = pl.pUserName;
2321 size = pl.szUserName;
2322 break;
2324 case URL_PART_PASSWORD:
2325 if (!pl.szPassword) {
2326 *pcchOut = 0;
2327 return S_FALSE;
2329 addr = pl.pPassword;
2330 size = pl.szPassword;
2331 break;
2333 case URL_PART_PORT:
2334 if (!pl.szPort) {
2335 *pcchOut = 0;
2336 return S_FALSE;
2338 addr = pl.pPort;
2339 size = pl.szPort;
2340 break;
2342 case URL_PART_QUERY:
2343 if (!pl.szQuery) {
2344 *pcchOut = 0;
2345 return S_FALSE;
2347 addr = pl.pQuery;
2348 size = pl.szQuery;
2349 break;
2351 default:
2352 *pcchOut = 0;
2353 return E_INVALIDARG;
2356 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2357 if(!pl.pScheme || !pl.szScheme) {
2358 *pcchOut = 0;
2359 return E_FAIL;
2361 schaddr = pl.pScheme;
2362 schsize = pl.szScheme;
2363 if (*pcchOut < schsize + size + 2) {
2364 *pcchOut = schsize + size + 2;
2365 return E_POINTER;
2367 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2368 pszOut[schsize] = ':';
2369 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2370 pszOut[schsize+1+size] = 0;
2371 *pcchOut = schsize + 1 + size;
2373 else {
2374 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2375 memcpy(pszOut, addr, size*sizeof(WCHAR));
2376 pszOut[size] = 0;
2377 *pcchOut = size;
2379 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2381 return ret;
2384 /*************************************************************************
2385 * PathIsURLA [SHLWAPI.@]
2387 * Check if the given path is a Url.
2389 * PARAMS
2390 * lpszPath [I] Path to check.
2392 * RETURNS
2393 * TRUE if lpszPath is a Url.
2394 * FALSE if lpszPath is NULL or not a Url.
2396 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2398 PARSEDURLA base;
2399 HRESULT hres;
2401 TRACE("%s\n", debugstr_a(lpstrPath));
2403 if (!lpstrPath || !*lpstrPath) return FALSE;
2405 /* get protocol */
2406 base.cbSize = sizeof(base);
2407 hres = ParseURLA(lpstrPath, &base);
2408 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2411 /*************************************************************************
2412 * PathIsURLW [SHLWAPI.@]
2414 * See PathIsURLA.
2416 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2418 PARSEDURLW base;
2419 HRESULT hres;
2421 TRACE("%s\n", debugstr_w(lpstrPath));
2423 if (!lpstrPath || !*lpstrPath) return FALSE;
2425 /* get protocol */
2426 base.cbSize = sizeof(base);
2427 hres = ParseURLW(lpstrPath, &base);
2428 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2431 /*************************************************************************
2432 * UrlCreateFromPathA [SHLWAPI.@]
2434 * See UrlCreateFromPathW
2436 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2438 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2439 WCHAR *urlW = bufW;
2440 UNICODE_STRING pathW;
2441 HRESULT ret;
2442 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2444 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2445 return E_INVALIDARG;
2446 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2447 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2448 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2450 if(ret == S_OK || ret == S_FALSE) {
2451 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2452 if(*pcchUrl > lenA) {
2453 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2454 pszUrl[lenA] = 0;
2455 *pcchUrl = lenA;
2456 } else {
2457 *pcchUrl = lenA + 1;
2458 ret = E_POINTER;
2461 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2462 RtlFreeUnicodeString(&pathW);
2463 return ret;
2466 /*************************************************************************
2467 * UrlCreateFromPathW [SHLWAPI.@]
2469 * Create a Url from a file path.
2471 * PARAMS
2472 * pszPath [I] Path to convert
2473 * pszUrl [O] Destination for the converted Url
2474 * pcchUrl [I/O] Length of pszUrl
2475 * dwReserved [I] Reserved, must be 0
2477 * RETURNS
2478 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2479 * Failure: An HRESULT error code.
2481 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2483 HRESULT ret;
2485 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2487 /* Validate arguments */
2488 if (dwReserved != 0)
2489 return E_INVALIDARG;
2490 if (!pszUrl || !pcchUrl)
2491 return E_INVALIDARG;
2493 ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl);
2495 if (S_FALSE == ret)
2496 strcpyW(pszUrl, pszPath);
2498 return ret;
2501 /*************************************************************************
2502 * SHAutoComplete [SHLWAPI.@]
2504 * Enable auto-completion for an edit control.
2506 * PARAMS
2507 * hwndEdit [I] Handle of control to enable auto-completion for
2508 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2510 * RETURNS
2511 * Success: S_OK. Auto-completion is enabled for the control.
2512 * Failure: An HRESULT error code indicating the error.
2514 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2516 FIXME("stub\n");
2517 return S_FALSE;
2520 /*************************************************************************
2521 * MLBuildResURLA [SHLWAPI.405]
2523 * Create a Url pointing to a resource in a module.
2525 * PARAMS
2526 * lpszLibName [I] Name of the module containing the resource
2527 * hMod [I] Callers module handle
2528 * dwFlags [I] Undocumented flags for loading the module
2529 * lpszRes [I] Resource name
2530 * lpszDest [O] Destination for resulting Url
2531 * dwDestLen [I] Length of lpszDest
2533 * RETURNS
2534 * Success: S_OK. lpszDest contains the resource Url.
2535 * Failure: E_INVALIDARG, if any argument is invalid, or
2536 * E_FAIL if dwDestLen is too small.
2538 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2539 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2541 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2542 HRESULT hRet;
2544 if (lpszLibName)
2545 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2547 if (lpszRes)
2548 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2550 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2551 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2553 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2554 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2555 if (SUCCEEDED(hRet) && lpszDest)
2556 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, NULL, NULL);
2558 return hRet;
2561 /*************************************************************************
2562 * MLBuildResURLA [SHLWAPI.406]
2564 * See MLBuildResURLA.
2566 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2567 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2569 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2570 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2571 HRESULT hRet = E_FAIL;
2573 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2574 debugstr_w(lpszRes), lpszDest, dwDestLen);
2576 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2577 !lpszDest || (dwFlags && dwFlags != 2))
2578 return E_INVALIDARG;
2580 if (dwDestLen >= szResLen + 1)
2582 dwDestLen -= (szResLen + 1);
2583 memcpy(lpszDest, szRes, sizeof(szRes));
2585 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2587 if (hMod)
2589 WCHAR szBuff[MAX_PATH];
2590 DWORD len;
2592 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2593 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2595 DWORD dwPathLen = strlenW(szBuff) + 1;
2597 if (dwDestLen >= dwPathLen)
2599 DWORD dwResLen;
2601 dwDestLen -= dwPathLen;
2602 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2604 dwResLen = strlenW(lpszRes) + 1;
2605 if (dwDestLen >= dwResLen + 1)
2607 lpszDest[szResLen + dwPathLen-1] = '/';
2608 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2609 hRet = S_OK;
2613 MLFreeLibrary(hMod);
2616 return hRet;
2619 /***********************************************************************
2620 * UrlFixupW [SHLWAPI.462]
2622 * Checks the scheme part of a URL and attempts to correct misspellings.
2624 * PARAMS
2625 * lpszUrl [I] Pointer to the URL to be corrected
2626 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2627 * dwMaxChars [I] Maximum size of corrected URL
2629 * RETURNS
2630 * success: S_OK if URL corrected or already correct
2631 * failure: S_FALSE if unable to correct / COM error code if other error
2634 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2636 DWORD srcLen;
2638 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2640 if (!url)
2641 return E_FAIL;
2643 srcLen = lstrlenW(url) + 1;
2645 /* For now just copy the URL directly */
2646 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2648 return S_OK;
2651 /*************************************************************************
2652 * IsInternetESCEnabled [SHLWAPI.@]
2654 BOOL WINAPI IsInternetESCEnabled(void)
2656 FIXME(": stub\n");
2657 return FALSE;