hidclass.sys: Correct input vs output parameters for IOCTL_HID_SET_FEATURE.
[wine.git] / dlls / shlwapi / url.c
blob85fa3c147cc52bfd750f0bdb84ae13ef85fd99b5
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 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 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(dwFlags & URL_ESCAPE_AS_UTF8)
962 return E_NOTIMPL;
963 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
964 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
965 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
967 if(ret == S_OK) {
968 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
969 if(*pcchEscaped > lenA) {
970 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
971 pszEscaped[lenA] = 0;
972 *pcchEscaped = lenA;
973 } else {
974 *pcchEscaped = lenA + 1;
975 ret = E_POINTER;
978 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
979 RtlFreeUnicodeString(&urlW);
980 return ret;
983 #define WINE_URL_BASH_AS_SLASH 0x01
984 #define WINE_URL_COLLAPSE_SLASHES 0x02
985 #define WINE_URL_ESCAPE_SLASH 0x04
986 #define WINE_URL_ESCAPE_HASH 0x08
987 #define WINE_URL_ESCAPE_QUESTION 0x10
988 #define WINE_URL_STOP_ON_HASH 0x20
989 #define WINE_URL_STOP_ON_QUESTION 0x40
991 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD flags, DWORD int_flags)
993 if (flags & URL_ESCAPE_SPACES_ONLY)
994 return ch == ' ';
996 if ((flags & URL_ESCAPE_PERCENT) && (ch == '%'))
997 return TRUE;
999 if ((flags & URL_ESCAPE_AS_UTF8) && (ch >= 0x80))
1000 return TRUE;
1002 if (ch <= 31 || (ch >= 127 && ch <= 255) )
1003 return TRUE;
1005 if (isalnumW(ch))
1006 return FALSE;
1008 switch (ch) {
1009 case ' ':
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 return TRUE;
1023 case '/':
1024 return !!(int_flags & WINE_URL_ESCAPE_SLASH);
1025 case '?':
1026 return !!(int_flags & WINE_URL_ESCAPE_QUESTION);
1027 case '#':
1028 return !!(int_flags & WINE_URL_ESCAPE_HASH);
1029 default:
1030 return FALSE;
1035 /*************************************************************************
1036 * UrlEscapeW [SHLWAPI.@]
1038 * Converts unsafe characters in a Url into escape sequences.
1040 * PARAMS
1041 * pszUrl [I] Url to modify
1042 * pszEscaped [O] Destination for modified Url
1043 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1044 * dwFlags [I] URL_ flags from "shlwapi.h"
1046 * RETURNS
1047 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1048 * contains its length.
1049 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
1050 * pcchEscaped is set to the required length.
1052 * Converts unsafe characters into their escape sequences.
1054 * NOTES
1055 * - By default this function stops converting at the first '?' or
1056 * '#' character.
1057 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1058 * converted, but the conversion continues past a '?' or '#'.
1059 * - Note that this function did not work well (or at all) in shlwapi version 4.
1061 * BUGS
1062 * Only the following flags are implemented:
1063 *| URL_ESCAPE_SPACES_ONLY
1064 *| URL_DONT_ESCAPE_EXTRA_INFO
1065 *| URL_ESCAPE_SEGMENT_ONLY
1066 *| URL_ESCAPE_PERCENT
1068 HRESULT WINAPI UrlEscapeW(
1069 LPCWSTR pszUrl,
1070 LPWSTR pszEscaped,
1071 LPDWORD pcchEscaped,
1072 DWORD dwFlags)
1074 LPCWSTR src;
1075 DWORD needed = 0, ret;
1076 BOOL stop_escaping = FALSE;
1077 WCHAR next[12], *dst, *dst_ptr;
1078 INT i, len;
1079 PARSEDURLW parsed_url;
1080 DWORD int_flags;
1081 DWORD slashes = 0;
1082 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1084 TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1085 pszEscaped, pcchEscaped, dwFlags);
1087 if(!pszUrl || !pcchEscaped)
1088 return E_INVALIDARG;
1090 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1091 URL_ESCAPE_SEGMENT_ONLY |
1092 URL_DONT_ESCAPE_EXTRA_INFO |
1093 URL_ESCAPE_PERCENT |
1094 URL_ESCAPE_AS_UTF8))
1095 FIXME("Unimplemented flags: %08x\n", dwFlags);
1097 dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1098 if(!dst_ptr)
1099 return E_OUTOFMEMORY;
1101 /* fix up flags */
1102 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1103 /* if SPACES_ONLY specified, reset the other controls */
1104 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1105 URL_ESCAPE_PERCENT |
1106 URL_ESCAPE_SEGMENT_ONLY);
1108 else
1109 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1110 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1113 int_flags = 0;
1114 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1115 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1116 } else {
1117 parsed_url.cbSize = sizeof(parsed_url);
1118 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1119 parsed_url.nScheme = URL_SCHEME_INVALID;
1121 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1123 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1124 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1126 switch(parsed_url.nScheme) {
1127 case URL_SCHEME_FILE:
1128 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1129 int_flags &= ~WINE_URL_STOP_ON_HASH;
1130 break;
1132 case URL_SCHEME_HTTP:
1133 case URL_SCHEME_HTTPS:
1134 int_flags |= WINE_URL_BASH_AS_SLASH;
1135 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1136 int_flags |= WINE_URL_ESCAPE_SLASH;
1137 break;
1139 case URL_SCHEME_MAILTO:
1140 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1141 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1142 break;
1144 case URL_SCHEME_INVALID:
1145 break;
1147 case URL_SCHEME_FTP:
1148 default:
1149 if(parsed_url.pszSuffix[0] != '/')
1150 int_flags |= WINE_URL_ESCAPE_SLASH;
1151 break;
1155 for(src = pszUrl; *src; ) {
1156 WCHAR cur = *src;
1157 len = 0;
1159 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1160 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1161 while(cur == '/' || cur == '\\') {
1162 slashes++;
1163 cur = *++src;
1165 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1166 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1167 src += localhost_len + 1;
1168 slashes = 3;
1171 switch(slashes) {
1172 case 1:
1173 case 3:
1174 next[0] = next[1] = next[2] = '/';
1175 len = 3;
1176 break;
1177 case 0:
1178 len = 0;
1179 break;
1180 default:
1181 next[0] = next[1] = '/';
1182 len = 2;
1183 break;
1186 if(len == 0) {
1188 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1189 stop_escaping = TRUE;
1191 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1192 stop_escaping = TRUE;
1194 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1196 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1197 if(dwFlags & URL_ESCAPE_AS_UTF8) {
1198 char utf[16];
1200 if ((cur >= 0xd800 && cur <= 0xdfff) &&
1201 (src[1] >= 0xdc00 && src[1] <= 0xdfff))
1203 len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, src, 2,
1204 utf, sizeof(utf), NULL, NULL );
1205 src++;
1207 else
1208 len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, &cur, 1,
1209 utf, sizeof(utf), NULL, NULL );
1211 if (!len) {
1212 utf[0] = 0xef;
1213 utf[1] = 0xbf;
1214 utf[2] = 0xbd;
1215 len = 3;
1218 for(i = 0; i < len; i++) {
1219 next[i*3+0] = '%';
1220 next[i*3+1] = hexDigits[(utf[i] >> 4) & 0xf];
1221 next[i*3+2] = hexDigits[utf[i] & 0xf];
1223 len *= 3;
1224 } else {
1225 next[0] = '%';
1226 next[1] = hexDigits[(cur >> 4) & 0xf];
1227 next[2] = hexDigits[cur & 0xf];
1228 len = 3;
1230 } else {
1231 next[0] = cur;
1232 len = 1;
1234 src++;
1237 if(needed + len <= *pcchEscaped) {
1238 memcpy(dst, next, len*sizeof(WCHAR));
1239 dst += len;
1241 needed += len;
1244 if(needed < *pcchEscaped) {
1245 *dst = '\0';
1246 memcpy(pszEscaped, dst_ptr, (needed+1)*sizeof(WCHAR));
1248 ret = S_OK;
1249 } else {
1250 needed++; /* add one for the '\0' */
1251 ret = E_POINTER;
1253 *pcchEscaped = needed;
1255 HeapFree(GetProcessHeap(), 0, dst_ptr);
1256 return ret;
1260 /*************************************************************************
1261 * UrlUnescapeA [SHLWAPI.@]
1263 * Converts Url escape sequences back to ordinary characters.
1265 * PARAMS
1266 * pszUrl [I/O] Url to convert
1267 * pszUnescaped [O] Destination for converted Url
1268 * pcchUnescaped [I/O] Size of output string
1269 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1271 * RETURNS
1272 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1273 * dwFlags includes URL_ESCAPE_INPLACE.
1274 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1275 * this case pcchUnescaped is set to the size required.
1276 * NOTES
1277 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1278 * the first occurrence of either a '?' or '#' character.
1280 HRESULT WINAPI UrlUnescapeA(
1281 LPSTR pszUrl,
1282 LPSTR pszUnescaped,
1283 LPDWORD pcchUnescaped,
1284 DWORD dwFlags)
1286 char *dst, next;
1287 LPCSTR src;
1288 HRESULT ret;
1289 DWORD needed;
1290 BOOL stop_unescaping = FALSE;
1292 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1293 pcchUnescaped, dwFlags);
1295 if (!pszUrl) return E_INVALIDARG;
1297 if(dwFlags & URL_UNESCAPE_INPLACE)
1298 dst = pszUrl;
1299 else
1301 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1302 dst = pszUnescaped;
1305 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1306 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1307 (*src == '#' || *src == '?')) {
1308 stop_unescaping = TRUE;
1309 next = *src;
1310 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1311 && stop_unescaping == FALSE) {
1312 INT ih;
1313 char buf[3];
1314 memcpy(buf, src + 1, 2);
1315 buf[2] = '\0';
1316 ih = strtol(buf, NULL, 16);
1317 next = (CHAR) ih;
1318 src += 2; /* Advance to end of escape */
1319 } else
1320 next = *src;
1322 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1323 *dst++ = next;
1326 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1327 *dst = '\0';
1328 ret = S_OK;
1329 } else {
1330 needed++; /* add one for the '\0' */
1331 ret = E_POINTER;
1333 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1334 *pcchUnescaped = needed;
1336 if (ret == S_OK) {
1337 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1338 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1341 return ret;
1344 /*************************************************************************
1345 * UrlUnescapeW [SHLWAPI.@]
1347 * See UrlUnescapeA.
1349 HRESULT WINAPI UrlUnescapeW(
1350 LPWSTR pszUrl,
1351 LPWSTR pszUnescaped,
1352 LPDWORD pcchUnescaped,
1353 DWORD dwFlags)
1355 WCHAR *dst, next;
1356 LPCWSTR src;
1357 HRESULT ret;
1358 DWORD needed;
1359 BOOL stop_unescaping = FALSE;
1361 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1362 pcchUnescaped, dwFlags);
1364 if(!pszUrl) return E_INVALIDARG;
1366 if(dwFlags & URL_UNESCAPE_INPLACE)
1367 dst = pszUrl;
1368 else
1370 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1371 dst = pszUnescaped;
1374 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1375 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1376 (*src == '#' || *src == '?')) {
1377 stop_unescaping = TRUE;
1378 next = *src;
1379 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1380 && stop_unescaping == FALSE) {
1381 INT ih;
1382 WCHAR buf[5] = {'0','x',0};
1383 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1384 buf[4] = 0;
1385 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1386 next = (WCHAR) ih;
1387 src += 2; /* Advance to end of escape */
1388 } else
1389 next = *src;
1391 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1392 *dst++ = next;
1395 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1396 *dst = '\0';
1397 ret = S_OK;
1398 } else {
1399 needed++; /* add one for the '\0' */
1400 ret = E_POINTER;
1402 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1403 *pcchUnescaped = needed;
1405 if (ret == S_OK) {
1406 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1407 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1410 return ret;
1413 /*************************************************************************
1414 * UrlGetLocationA [SHLWAPI.@]
1416 * Get the location from a Url.
1418 * PARAMS
1419 * pszUrl [I] Url to get the location from
1421 * RETURNS
1422 * A pointer to the start of the location in pszUrl, or NULL if there is
1423 * no location.
1425 * NOTES
1426 * - MSDN erroneously states that "The location is the segment of the Url
1427 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1428 * stop at '?' and always return a NULL in this case.
1429 * - MSDN also erroneously states that "If a file URL has a query string,
1430 * the returned string is the query string". In all tested cases, if the
1431 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1432 *| Result Url
1433 *| ------ ---
1434 *| NULL file://aa/b/cd#hohoh
1435 *| #hohoh http://aa/b/cd#hohoh
1436 *| NULL fi://aa/b/cd#hohoh
1437 *| #hohoh ff://aa/b/cd#hohoh
1439 LPCSTR WINAPI UrlGetLocationA(
1440 LPCSTR pszUrl)
1442 PARSEDURLA base;
1443 DWORD res1;
1445 base.cbSize = sizeof(base);
1446 res1 = ParseURLA(pszUrl, &base);
1447 if (res1) return NULL; /* invalid scheme */
1449 /* if scheme is file: then never return pointer */
1450 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1452 /* Look for '#' and return its addr */
1453 return strchr(base.pszSuffix, '#');
1456 /*************************************************************************
1457 * UrlGetLocationW [SHLWAPI.@]
1459 * See UrlGetLocationA.
1461 LPCWSTR WINAPI UrlGetLocationW(
1462 LPCWSTR pszUrl)
1464 PARSEDURLW base;
1465 DWORD res1;
1467 base.cbSize = sizeof(base);
1468 res1 = ParseURLW(pszUrl, &base);
1469 if (res1) return NULL; /* invalid scheme */
1471 /* if scheme is file: then never return pointer */
1472 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1474 /* Look for '#' and return its addr */
1475 return strchrW(base.pszSuffix, '#');
1478 /*************************************************************************
1479 * UrlCompareA [SHLWAPI.@]
1481 * Compare two Urls.
1483 * PARAMS
1484 * pszUrl1 [I] First Url to compare
1485 * pszUrl2 [I] Url to compare to pszUrl1
1486 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1488 * RETURNS
1489 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1490 * than, equal to, or less than pszUrl1 respectively.
1492 INT WINAPI UrlCompareA(
1493 LPCSTR pszUrl1,
1494 LPCSTR pszUrl2,
1495 BOOL fIgnoreSlash)
1497 INT ret, len, len1, len2;
1499 if (!fIgnoreSlash)
1500 return strcmp(pszUrl1, pszUrl2);
1501 len1 = strlen(pszUrl1);
1502 if (pszUrl1[len1-1] == '/') len1--;
1503 len2 = strlen(pszUrl2);
1504 if (pszUrl2[len2-1] == '/') len2--;
1505 if (len1 == len2)
1506 return strncmp(pszUrl1, pszUrl2, len1);
1507 len = min(len1, len2);
1508 ret = strncmp(pszUrl1, pszUrl2, len);
1509 if (ret) return ret;
1510 if (len1 > len2) return 1;
1511 return -1;
1514 /*************************************************************************
1515 * UrlCompareW [SHLWAPI.@]
1517 * See UrlCompareA.
1519 INT WINAPI UrlCompareW(
1520 LPCWSTR pszUrl1,
1521 LPCWSTR pszUrl2,
1522 BOOL fIgnoreSlash)
1524 INT ret;
1525 size_t len, len1, len2;
1527 if (!fIgnoreSlash)
1528 return strcmpW(pszUrl1, pszUrl2);
1529 len1 = strlenW(pszUrl1);
1530 if (pszUrl1[len1-1] == '/') len1--;
1531 len2 = strlenW(pszUrl2);
1532 if (pszUrl2[len2-1] == '/') len2--;
1533 if (len1 == len2)
1534 return strncmpW(pszUrl1, pszUrl2, len1);
1535 len = min(len1, len2);
1536 ret = strncmpW(pszUrl1, pszUrl2, len);
1537 if (ret) return ret;
1538 if (len1 > len2) return 1;
1539 return -1;
1542 /*************************************************************************
1543 * HashData [SHLWAPI.@]
1545 * Hash an input block into a variable sized digest.
1547 * PARAMS
1548 * lpSrc [I] Input block
1549 * nSrcLen [I] Length of lpSrc
1550 * lpDest [I] Output for hash digest
1551 * nDestLen [I] Length of lpDest
1553 * RETURNS
1554 * Success: TRUE. lpDest is filled with the computed hash value.
1555 * Failure: FALSE, if any argument is invalid.
1557 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1558 unsigned char *lpDest, DWORD nDestLen)
1560 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1562 if (!lpSrc || !lpDest)
1563 return E_INVALIDARG;
1565 while (destCount >= 0)
1567 lpDest[destCount] = (destCount & 0xff);
1568 destCount--;
1571 while (srcCount >= 0)
1573 destCount = nDestLen - 1;
1574 while (destCount >= 0)
1576 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1577 destCount--;
1579 srcCount--;
1581 return S_OK;
1584 /*************************************************************************
1585 * UrlHashA [SHLWAPI.@]
1587 * Produce a Hash from a Url.
1589 * PARAMS
1590 * pszUrl [I] Url to hash
1591 * lpDest [O] Destinationh for hash
1592 * nDestLen [I] Length of lpDest
1594 * RETURNS
1595 * Success: S_OK. lpDest is filled with the computed hash value.
1596 * Failure: E_INVALIDARG, if any argument is invalid.
1598 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1600 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1601 return E_INVALIDARG;
1603 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1604 return S_OK;
1607 /*************************************************************************
1608 * UrlHashW [SHLWAPI.@]
1610 * See UrlHashA.
1612 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1614 char szUrl[MAX_PATH];
1616 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1618 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1619 return E_INVALIDARG;
1621 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1622 * return the same digests for the same URL.
1624 WideCharToMultiByte(CP_ACP, 0, pszUrl, -1, szUrl, MAX_PATH, NULL, NULL);
1625 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1626 return S_OK;
1629 /*************************************************************************
1630 * UrlApplySchemeA [SHLWAPI.@]
1632 * Apply a scheme to a Url.
1634 * PARAMS
1635 * pszIn [I] Url to apply scheme to
1636 * pszOut [O] Destination for modified Url
1637 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1638 * dwFlags [I] URL_ flags from "shlwapi.h"
1640 * RETURNS
1641 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1642 * Failure: An HRESULT error code describing the error.
1644 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1646 LPWSTR in, out;
1647 HRESULT ret;
1648 DWORD len;
1650 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1651 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1653 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1655 in = HeapAlloc(GetProcessHeap(), 0,
1656 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1657 out = in + INTERNET_MAX_URL_LENGTH;
1659 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1660 len = INTERNET_MAX_URL_LENGTH;
1662 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1663 if (ret != S_OK) {
1664 HeapFree(GetProcessHeap(), 0, in);
1665 return ret;
1668 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1669 if (len > *pcchOut) {
1670 ret = E_POINTER;
1671 goto cleanup;
1674 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1675 len--;
1677 cleanup:
1678 *pcchOut = len;
1679 HeapFree(GetProcessHeap(), 0, in);
1680 return ret;
1683 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1685 HKEY newkey;
1686 BOOL j;
1687 INT index;
1688 DWORD value_len, data_len, dwType, i;
1689 WCHAR reg_path[MAX_PATH];
1690 WCHAR value[MAX_PATH], data[MAX_PATH];
1691 WCHAR Wxx, Wyy;
1693 MultiByteToWideChar(CP_ACP, 0,
1694 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1695 -1, reg_path, MAX_PATH);
1696 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1697 index = 0;
1698 while(value_len = data_len = MAX_PATH,
1699 RegEnumValueW(newkey, index, value, &value_len,
1700 0, &dwType, (LPVOID)data, &data_len) == 0) {
1701 TRACE("guess %d %s is %s\n",
1702 index, debugstr_w(value), debugstr_w(data));
1704 j = FALSE;
1705 for(i=0; i<value_len; i++) {
1706 Wxx = pszIn[i];
1707 Wyy = value[i];
1708 /* remember that TRUE is not-equal */
1709 j = ChrCmpIW(Wxx, Wyy);
1710 if (j) break;
1712 if ((i == value_len) && !j) {
1713 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1714 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1715 RegCloseKey(newkey);
1716 return E_POINTER;
1718 strcpyW(pszOut, data);
1719 strcatW(pszOut, pszIn);
1720 *pcchOut = strlenW(pszOut);
1721 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1722 RegCloseKey(newkey);
1723 return S_OK;
1725 index++;
1727 RegCloseKey(newkey);
1728 return E_FAIL;
1731 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl)
1733 DWORD needed;
1734 HRESULT ret = S_OK;
1735 WCHAR *pszNewUrl;
1736 WCHAR file_colonW[] = {'f','i','l','e',':',0};
1737 WCHAR three_slashesW[] = {'/','/','/',0};
1738 PARSEDURLW parsed_url;
1740 parsed_url.cbSize = sizeof(parsed_url);
1741 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
1742 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
1743 needed = strlenW(pszPath);
1744 if (needed >= *pcchUrl) {
1745 *pcchUrl = needed + 1;
1746 return E_POINTER;
1747 } else {
1748 *pcchUrl = needed;
1749 return S_FALSE;
1754 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
1755 strcpyW(pszNewUrl, file_colonW);
1756 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
1757 strcatW(pszNewUrl, three_slashesW);
1758 strcatW(pszNewUrl, pszPath);
1759 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
1760 HeapFree(GetProcessHeap(), 0, pszNewUrl);
1761 return ret;
1764 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1766 HKEY newkey;
1767 DWORD data_len, dwType;
1768 WCHAR data[MAX_PATH];
1770 static const WCHAR prefix_keyW[] =
1771 {'S','o','f','t','w','a','r','e',
1772 '\\','M','i','c','r','o','s','o','f','t',
1773 '\\','W','i','n','d','o','w','s',
1774 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1775 '\\','U','R','L',
1776 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1778 /* get and prepend default */
1779 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1780 data_len = sizeof(data);
1781 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1782 RegCloseKey(newkey);
1783 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1784 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1785 return E_POINTER;
1787 strcpyW(pszOut, data);
1788 strcatW(pszOut, pszIn);
1789 *pcchOut = strlenW(pszOut);
1790 TRACE("used default %s\n", debugstr_w(pszOut));
1791 return S_OK;
1794 /*************************************************************************
1795 * UrlApplySchemeW [SHLWAPI.@]
1797 * See UrlApplySchemeA.
1799 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1801 PARSEDURLW in_scheme;
1802 DWORD res1;
1803 HRESULT ret;
1805 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1806 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1808 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1810 if (dwFlags & URL_APPLY_GUESSFILE) {
1811 if (*pcchOut > 1 && ':' == pszIn[1]) {
1812 res1 = *pcchOut;
1813 ret = URL_CreateFromPath(pszIn, pszOut, &res1);
1814 if (ret == S_OK || ret == E_POINTER){
1815 *pcchOut = res1;
1816 return ret;
1818 else if (ret == S_FALSE)
1820 return ret;
1825 in_scheme.cbSize = sizeof(in_scheme);
1826 /* See if the base has a scheme */
1827 res1 = ParseURLW(pszIn, &in_scheme);
1828 if (res1) {
1829 /* no scheme in input, need to see if we need to guess */
1830 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1831 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1832 return ret;
1836 /* If we are here, then either invalid scheme,
1837 * or no scheme and can't/failed guess.
1839 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1840 ((res1 != 0)) ) &&
1841 (dwFlags & URL_APPLY_DEFAULT)) {
1842 /* find and apply default scheme */
1843 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1846 return S_FALSE;
1849 /*************************************************************************
1850 * UrlIsA [SHLWAPI.@]
1852 * Determine if a Url is of a certain class.
1854 * PARAMS
1855 * pszUrl [I] Url to check
1856 * Urlis [I] URLIS_ constant from "shlwapi.h"
1858 * RETURNS
1859 * TRUE if pszUrl belongs to the class type in Urlis.
1860 * FALSE Otherwise.
1862 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1864 PARSEDURLA base;
1865 DWORD res1;
1866 LPCSTR last;
1868 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1870 if(!pszUrl)
1871 return FALSE;
1873 switch (Urlis) {
1875 case URLIS_OPAQUE:
1876 base.cbSize = sizeof(base);
1877 res1 = ParseURLA(pszUrl, &base);
1878 if (res1) return FALSE; /* invalid scheme */
1879 switch (base.nScheme)
1881 case URL_SCHEME_MAILTO:
1882 case URL_SCHEME_SHELL:
1883 case URL_SCHEME_JAVASCRIPT:
1884 case URL_SCHEME_VBSCRIPT:
1885 case URL_SCHEME_ABOUT:
1886 return TRUE;
1888 return FALSE;
1890 case URLIS_FILEURL:
1891 return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1892 "file:", 5) == CSTR_EQUAL);
1894 case URLIS_DIRECTORY:
1895 last = pszUrl + strlen(pszUrl) - 1;
1896 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1898 case URLIS_URL:
1899 return PathIsURLA(pszUrl);
1901 case URLIS_NOHISTORY:
1902 case URLIS_APPLIABLE:
1903 case URLIS_HASQUERY:
1904 default:
1905 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1907 return FALSE;
1910 /*************************************************************************
1911 * UrlIsW [SHLWAPI.@]
1913 * See UrlIsA.
1915 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1917 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
1918 PARSEDURLW base;
1919 DWORD res1;
1920 LPCWSTR last;
1922 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1924 if(!pszUrl)
1925 return FALSE;
1927 switch (Urlis) {
1929 case URLIS_OPAQUE:
1930 base.cbSize = sizeof(base);
1931 res1 = ParseURLW(pszUrl, &base);
1932 if (res1) return FALSE; /* invalid scheme */
1933 switch (base.nScheme)
1935 case URL_SCHEME_MAILTO:
1936 case URL_SCHEME_SHELL:
1937 case URL_SCHEME_JAVASCRIPT:
1938 case URL_SCHEME_VBSCRIPT:
1939 case URL_SCHEME_ABOUT:
1940 return TRUE;
1942 return FALSE;
1944 case URLIS_FILEURL:
1945 return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1946 file_colon, 5) == CSTR_EQUAL);
1948 case URLIS_DIRECTORY:
1949 last = pszUrl + strlenW(pszUrl) - 1;
1950 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1952 case URLIS_URL:
1953 return PathIsURLW(pszUrl);
1955 case URLIS_NOHISTORY:
1956 case URLIS_APPLIABLE:
1957 case URLIS_HASQUERY:
1958 default:
1959 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1961 return FALSE;
1964 /*************************************************************************
1965 * UrlIsNoHistoryA [SHLWAPI.@]
1967 * Determine if a Url should not be stored in the users history list.
1969 * PARAMS
1970 * pszUrl [I] Url to check
1972 * RETURNS
1973 * TRUE, if pszUrl should be excluded from the history list,
1974 * FALSE otherwise.
1976 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1978 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1981 /*************************************************************************
1982 * UrlIsNoHistoryW [SHLWAPI.@]
1984 * See UrlIsNoHistoryA.
1986 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1988 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1991 /*************************************************************************
1992 * UrlIsOpaqueA [SHLWAPI.@]
1994 * Determine if a Url is opaque.
1996 * PARAMS
1997 * pszUrl [I] Url to check
1999 * RETURNS
2000 * TRUE if pszUrl is opaque,
2001 * FALSE Otherwise.
2003 * NOTES
2004 * An opaque Url is one that does not start with "<protocol>://".
2006 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
2008 return UrlIsA(pszUrl, URLIS_OPAQUE);
2011 /*************************************************************************
2012 * UrlIsOpaqueW [SHLWAPI.@]
2014 * See UrlIsOpaqueA.
2016 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
2018 return UrlIsW(pszUrl, URLIS_OPAQUE);
2021 /*************************************************************************
2022 * Scans for characters of type "type" and when not matching found,
2023 * returns pointer to it and length in size.
2025 * Characters tested based on RFC 1738
2027 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
2029 static DWORD alwayszero = 0;
2030 BOOL cont = TRUE;
2032 *size = 0;
2034 switch(type){
2036 case SCHEME:
2037 while (cont) {
2038 if ( (islowerW(*start) && isalphaW(*start)) ||
2039 isdigitW(*start) ||
2040 (*start == '+') ||
2041 (*start == '-') ||
2042 (*start == '.')) {
2043 start++;
2044 (*size)++;
2046 else
2047 cont = FALSE;
2050 if(*start != ':')
2051 *size = 0;
2053 break;
2055 case USERPASS:
2056 while (cont) {
2057 if ( isalphaW(*start) ||
2058 isdigitW(*start) ||
2059 /* user/password only characters */
2060 (*start == ';') ||
2061 (*start == '?') ||
2062 (*start == '&') ||
2063 (*start == '=') ||
2064 /* *extra* characters */
2065 (*start == '!') ||
2066 (*start == '*') ||
2067 (*start == '\'') ||
2068 (*start == '(') ||
2069 (*start == ')') ||
2070 (*start == ',') ||
2071 /* *safe* characters */
2072 (*start == '$') ||
2073 (*start == '_') ||
2074 (*start == '+') ||
2075 (*start == '-') ||
2076 (*start == '.') ||
2077 (*start == ' ')) {
2078 start++;
2079 (*size)++;
2080 } else if (*start == '%') {
2081 if (isxdigitW(*(start+1)) &&
2082 isxdigitW(*(start+2))) {
2083 start += 3;
2084 *size += 3;
2085 } else
2086 cont = FALSE;
2087 } else
2088 cont = FALSE;
2090 break;
2092 case PORT:
2093 while (cont) {
2094 if (isdigitW(*start)) {
2095 start++;
2096 (*size)++;
2098 else
2099 cont = FALSE;
2101 break;
2103 case HOST:
2104 while (cont) {
2105 if (isalnumW(*start) ||
2106 (*start == '-') ||
2107 (*start == '.') ||
2108 (*start == ' ') ||
2109 (*start == '*') ) {
2110 start++;
2111 (*size)++;
2113 else
2114 cont = FALSE;
2116 break;
2117 default:
2118 FIXME("unknown type %d\n", type);
2119 return (LPWSTR)&alwayszero;
2121 /* TRACE("scanned %d characters next char %p<%c>\n",
2122 *size, start, *start); */
2123 return start;
2126 /*************************************************************************
2127 * Attempt to parse URL into pieces.
2129 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2131 LPCWSTR work;
2133 memset(pl, 0, sizeof(WINE_PARSE_URL));
2134 pl->pScheme = pszUrl;
2135 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2136 if (!*work || (*work != ':')) goto ErrorExit;
2137 work++;
2138 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2139 pl->pUserName = work + 2;
2140 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2141 if (*work == ':' ) {
2142 /* parse password */
2143 work++;
2144 pl->pPassword = work;
2145 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2146 if (*work != '@') {
2147 /* what we just parsed must be the hostname and port
2148 * so reset pointers and clear then let it parse */
2149 pl->szUserName = pl->szPassword = 0;
2150 work = pl->pUserName - 1;
2151 pl->pUserName = pl->pPassword = 0;
2153 } else if (*work == '@') {
2154 /* no password */
2155 pl->szPassword = 0;
2156 pl->pPassword = 0;
2157 } else if (!*work || (*work == '/') || (*work == '.')) {
2158 /* what was parsed was hostname, so reset pointers and let it parse */
2159 pl->szUserName = pl->szPassword = 0;
2160 work = pl->pUserName - 1;
2161 pl->pUserName = pl->pPassword = 0;
2162 } else goto ErrorExit;
2164 /* now start parsing hostname or hostnumber */
2165 work++;
2166 pl->pHostName = work;
2167 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2168 if (*work == ':') {
2169 /* parse port */
2170 work++;
2171 pl->pPort = work;
2172 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2174 if (*work == '/') {
2175 /* see if query string */
2176 pl->pQuery = strchrW(work, '?');
2177 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2179 SuccessExit:
2180 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2181 pl->pScheme, pl->szScheme,
2182 pl->pUserName, pl->szUserName,
2183 pl->pPassword, pl->szPassword,
2184 pl->pHostName, pl->szHostName,
2185 pl->pPort, pl->szPort,
2186 pl->pQuery, pl->szQuery);
2187 return S_OK;
2188 ErrorExit:
2189 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2190 return E_INVALIDARG;
2193 /*************************************************************************
2194 * UrlGetPartA [SHLWAPI.@]
2196 * Retrieve part of a Url.
2198 * PARAMS
2199 * pszIn [I] Url to parse
2200 * pszOut [O] Destination for part of pszIn requested
2201 * pcchOut [I] Size of pszOut
2202 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2203 * needed size of pszOut INCLUDING '\0'.
2204 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2205 * dwFlags [I] URL_ flags from "shlwapi.h"
2207 * RETURNS
2208 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2209 * Failure: An HRESULT error code describing the error.
2211 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2212 DWORD dwPart, DWORD dwFlags)
2214 LPWSTR in, out;
2215 DWORD ret, len, len2;
2217 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2218 return E_INVALIDARG;
2220 in = HeapAlloc(GetProcessHeap(), 0,
2221 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2222 out = in + INTERNET_MAX_URL_LENGTH;
2224 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2226 len = INTERNET_MAX_URL_LENGTH;
2227 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2229 if (FAILED(ret)) {
2230 HeapFree(GetProcessHeap(), 0, in);
2231 return ret;
2234 len2 = WideCharToMultiByte(CP_ACP, 0, out, len, NULL, 0, NULL, NULL);
2235 if (len2 > *pcchOut) {
2236 *pcchOut = len2+1;
2237 HeapFree(GetProcessHeap(), 0, in);
2238 return E_POINTER;
2240 len2 = WideCharToMultiByte(CP_ACP, 0, out, len+1, pszOut, *pcchOut, NULL, NULL);
2241 *pcchOut = len2-1;
2242 HeapFree(GetProcessHeap(), 0, in);
2243 return ret;
2246 /*************************************************************************
2247 * UrlGetPartW [SHLWAPI.@]
2249 * See UrlGetPartA.
2251 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2252 DWORD dwPart, DWORD dwFlags)
2254 WINE_PARSE_URL pl;
2255 HRESULT ret;
2256 DWORD scheme, size, schsize;
2257 LPCWSTR addr, schaddr;
2259 TRACE("(%s %p %p(%d) %08x %08x)\n",
2260 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2262 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2263 return E_INVALIDARG;
2265 *pszOut = '\0';
2267 addr = strchrW(pszIn, ':');
2268 if(!addr)
2269 scheme = URL_SCHEME_UNKNOWN;
2270 else
2271 scheme = get_scheme_code(pszIn, addr-pszIn);
2273 ret = URL_ParseUrl(pszIn, &pl);
2275 switch (dwPart) {
2276 case URL_PART_SCHEME:
2277 if (!pl.szScheme) {
2278 *pcchOut = 0;
2279 return S_FALSE;
2281 addr = pl.pScheme;
2282 size = pl.szScheme;
2283 break;
2285 case URL_PART_HOSTNAME:
2286 switch(scheme) {
2287 case URL_SCHEME_FTP:
2288 case URL_SCHEME_HTTP:
2289 case URL_SCHEME_GOPHER:
2290 case URL_SCHEME_TELNET:
2291 case URL_SCHEME_FILE:
2292 case URL_SCHEME_HTTPS:
2293 break;
2294 default:
2295 *pcchOut = 0;
2296 return E_FAIL;
2299 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2300 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2301 *pcchOut = 0;
2302 return S_FALSE;
2305 if (!pl.szHostName) {
2306 *pcchOut = 0;
2307 return S_FALSE;
2309 addr = pl.pHostName;
2310 size = pl.szHostName;
2311 break;
2313 case URL_PART_USERNAME:
2314 if (!pl.szUserName) {
2315 *pcchOut = 0;
2316 return S_FALSE;
2318 addr = pl.pUserName;
2319 size = pl.szUserName;
2320 break;
2322 case URL_PART_PASSWORD:
2323 if (!pl.szPassword) {
2324 *pcchOut = 0;
2325 return S_FALSE;
2327 addr = pl.pPassword;
2328 size = pl.szPassword;
2329 break;
2331 case URL_PART_PORT:
2332 if (!pl.szPort) {
2333 *pcchOut = 0;
2334 return S_FALSE;
2336 addr = pl.pPort;
2337 size = pl.szPort;
2338 break;
2340 case URL_PART_QUERY:
2341 if (!pl.szQuery) {
2342 *pcchOut = 0;
2343 return S_FALSE;
2345 addr = pl.pQuery;
2346 size = pl.szQuery;
2347 break;
2349 default:
2350 *pcchOut = 0;
2351 return E_INVALIDARG;
2354 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2355 if(!pl.pScheme || !pl.szScheme) {
2356 *pcchOut = 0;
2357 return E_FAIL;
2359 schaddr = pl.pScheme;
2360 schsize = pl.szScheme;
2361 if (*pcchOut < schsize + size + 2) {
2362 *pcchOut = schsize + size + 2;
2363 return E_POINTER;
2365 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2366 pszOut[schsize] = ':';
2367 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2368 pszOut[schsize+1+size] = 0;
2369 *pcchOut = schsize + 1 + size;
2371 else {
2372 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2373 memcpy(pszOut, addr, size*sizeof(WCHAR));
2374 pszOut[size] = 0;
2375 *pcchOut = size;
2377 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2379 return ret;
2382 /*************************************************************************
2383 * PathIsURLA [SHLWAPI.@]
2385 * Check if the given path is a Url.
2387 * PARAMS
2388 * lpszPath [I] Path to check.
2390 * RETURNS
2391 * TRUE if lpszPath is a Url.
2392 * FALSE if lpszPath is NULL or not a Url.
2394 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2396 PARSEDURLA base;
2397 HRESULT hres;
2399 TRACE("%s\n", debugstr_a(lpstrPath));
2401 if (!lpstrPath || !*lpstrPath) return FALSE;
2403 /* get protocol */
2404 base.cbSize = sizeof(base);
2405 hres = ParseURLA(lpstrPath, &base);
2406 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2409 /*************************************************************************
2410 * PathIsURLW [SHLWAPI.@]
2412 * See PathIsURLA.
2414 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2416 PARSEDURLW base;
2417 HRESULT hres;
2419 TRACE("%s\n", debugstr_w(lpstrPath));
2421 if (!lpstrPath || !*lpstrPath) return FALSE;
2423 /* get protocol */
2424 base.cbSize = sizeof(base);
2425 hres = ParseURLW(lpstrPath, &base);
2426 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2429 /*************************************************************************
2430 * UrlCreateFromPathA [SHLWAPI.@]
2432 * See UrlCreateFromPathW
2434 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2436 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2437 WCHAR *urlW = bufW;
2438 UNICODE_STRING pathW;
2439 HRESULT ret;
2440 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2442 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2443 return E_INVALIDARG;
2444 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2445 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2446 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2448 if(ret == S_OK || ret == S_FALSE) {
2449 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2450 if(*pcchUrl > lenA) {
2451 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2452 pszUrl[lenA] = 0;
2453 *pcchUrl = lenA;
2454 } else {
2455 *pcchUrl = lenA + 1;
2456 ret = E_POINTER;
2459 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2460 RtlFreeUnicodeString(&pathW);
2461 return ret;
2464 /*************************************************************************
2465 * UrlCreateFromPathW [SHLWAPI.@]
2467 * Create a Url from a file path.
2469 * PARAMS
2470 * pszPath [I] Path to convert
2471 * pszUrl [O] Destination for the converted Url
2472 * pcchUrl [I/O] Length of pszUrl
2473 * dwReserved [I] Reserved, must be 0
2475 * RETURNS
2476 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2477 * Failure: An HRESULT error code.
2479 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2481 HRESULT ret;
2483 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2485 /* Validate arguments */
2486 if (dwReserved != 0)
2487 return E_INVALIDARG;
2488 if (!pszUrl || !pcchUrl)
2489 return E_INVALIDARG;
2491 ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl);
2493 if (S_FALSE == ret)
2494 strcpyW(pszUrl, pszPath);
2496 return ret;
2499 /*************************************************************************
2500 * SHAutoComplete [SHLWAPI.@]
2502 * Enable auto-completion for an edit control.
2504 * PARAMS
2505 * hwndEdit [I] Handle of control to enable auto-completion for
2506 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2508 * RETURNS
2509 * Success: S_OK. Auto-completion is enabled for the control.
2510 * Failure: An HRESULT error code indicating the error.
2512 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2514 FIXME("stub\n");
2515 return S_FALSE;
2518 /*************************************************************************
2519 * MLBuildResURLA [SHLWAPI.405]
2521 * Create a Url pointing to a resource in a module.
2523 * PARAMS
2524 * lpszLibName [I] Name of the module containing the resource
2525 * hMod [I] Callers module handle
2526 * dwFlags [I] Undocumented flags for loading the module
2527 * lpszRes [I] Resource name
2528 * lpszDest [O] Destination for resulting Url
2529 * dwDestLen [I] Length of lpszDest
2531 * RETURNS
2532 * Success: S_OK. lpszDest contains the resource Url.
2533 * Failure: E_INVALIDARG, if any argument is invalid, or
2534 * E_FAIL if dwDestLen is too small.
2536 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2537 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2539 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2540 HRESULT hRet;
2542 if (lpszLibName)
2543 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2545 if (lpszRes)
2546 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2548 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2549 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2551 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2552 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2553 if (SUCCEEDED(hRet) && lpszDest)
2554 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, NULL, NULL);
2556 return hRet;
2559 /*************************************************************************
2560 * MLBuildResURLA [SHLWAPI.406]
2562 * See MLBuildResURLA.
2564 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2565 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2567 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2568 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2569 HRESULT hRet = E_FAIL;
2571 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2572 debugstr_w(lpszRes), lpszDest, dwDestLen);
2574 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2575 !lpszDest || (dwFlags && dwFlags != 2))
2576 return E_INVALIDARG;
2578 if (dwDestLen >= szResLen + 1)
2580 dwDestLen -= (szResLen + 1);
2581 memcpy(lpszDest, szRes, sizeof(szRes));
2583 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2585 if (hMod)
2587 WCHAR szBuff[MAX_PATH];
2588 DWORD len;
2590 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2591 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2593 DWORD dwPathLen = strlenW(szBuff) + 1;
2595 if (dwDestLen >= dwPathLen)
2597 DWORD dwResLen;
2599 dwDestLen -= dwPathLen;
2600 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2602 dwResLen = strlenW(lpszRes) + 1;
2603 if (dwDestLen >= dwResLen + 1)
2605 lpszDest[szResLen + dwPathLen-1] = '/';
2606 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2607 hRet = S_OK;
2611 MLFreeLibrary(hMod);
2614 return hRet;
2617 /***********************************************************************
2618 * UrlFixupW [SHLWAPI.462]
2620 * Checks the scheme part of a URL and attempts to correct misspellings.
2622 * PARAMS
2623 * lpszUrl [I] Pointer to the URL to be corrected
2624 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2625 * dwMaxChars [I] Maximum size of corrected URL
2627 * RETURNS
2628 * success: S_OK if URL corrected or already correct
2629 * failure: S_FALSE if unable to correct / COM error code if other error
2632 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2634 DWORD srcLen;
2636 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2638 if (!url)
2639 return E_FAIL;
2641 srcLen = lstrlenW(url) + 1;
2643 /* For now just copy the URL directly */
2644 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2646 return S_OK;
2649 /*************************************************************************
2650 * IsInternetESCEnabled [SHLWAPI.@]
2652 BOOL WINAPI IsInternetESCEnabled(void)
2654 FIXME(": stub\n");
2655 return FALSE;