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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
34 #define NO_SHLWAPI_STREAM
36 #include "wine/debug.h"
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
48 URL_SCHEME scheme_number;
52 static const SHL_2_inet_scheme shlwapi_schemes[] = {
53 {URL_SCHEME_FTP, "ftp"},
54 {URL_SCHEME_HTTP, "http"},
55 {URL_SCHEME_GOPHER, "gopher"},
56 {URL_SCHEME_MAILTO, "mailto"},
57 {URL_SCHEME_NEWS, "news"},
58 {URL_SCHEME_NNTP, "nntp"},
59 {URL_SCHEME_TELNET, "telnet"},
60 {URL_SCHEME_WAIS, "wais"},
61 {URL_SCHEME_FILE, "file"},
62 {URL_SCHEME_MK, "mk"},
63 {URL_SCHEME_HTTPS, "https"},
64 {URL_SCHEME_SHELL, "shell"},
65 {URL_SCHEME_SNEWS, "snews"},
66 {URL_SCHEME_LOCAL, "local"},
67 {URL_SCHEME_JAVASCRIPT, "javascript"},
68 {URL_SCHEME_VBSCRIPT, "vbscript"},
69 {URL_SCHEME_ABOUT, "about"},
70 {URL_SCHEME_RES, "res"},
75 LPCWSTR pScheme; /* [out] start of scheme */
76 DWORD szScheme; /* [out] size of scheme (until colon) */
77 LPCWSTR pUserName; /* [out] start of Username */
78 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
79 LPCWSTR pPassword; /* [out] start of Password */
80 DWORD szPassword; /* [out] size of Password (until "@") */
81 LPCWSTR pHostName; /* [out] start of Hostname */
82 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
83 LPCWSTR pPort; /* [out] start of Port */
84 DWORD szPort; /* [out] size of Port (until "/" or eos) */
85 LPCWSTR pQuery; /* [out] start of Query */
86 DWORD szQuery; /* [out] size of Query (until eos) */
96 static const CHAR hexDigits[] = "0123456789ABCDEF";
98 static const WCHAR fileW[] = {'f','i','l','e','\0'};
100 static const unsigned char HashDataLookup[256] = {
101 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
102 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
103 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
104 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
105 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
106 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
107 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
108 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
109 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
110 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
111 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
112 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
113 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
114 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
115 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
116 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
117 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
118 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
119 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
120 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
122 static BOOL URL_JustLocation(LPCWSTR str)
124 while(*str && (*str == L'/')) str++;
126 while (*str && ((*str == L'-') ||
128 isalnumW(*str))) str++;
129 if (*str == L'/') return FALSE;
135 /*************************************************************************
138 * Parse a Url into its constituent parts.
142 * y [O] Undocumented structure holding the parsed information
145 * Success: S_OK. y contains the parsed Url details.
146 * Failure: An HRESULT error code.
148 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
151 const SHL_2_inet_scheme *inet_pro;
153 y->nScheme = URL_SCHEME_INVALID;
154 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
155 /* FIXME: leading white space generates error of 0x80041001 which
158 if (*x <= ' ') return 0x80041001;
164 y->cchProtocol = cnt;
173 /* check for no scheme in string start */
174 /* (apparently schemes *must* be larger than a single character) */
175 if ((*x == '\0') || (y->cchProtocol <= 1)) {
176 y->pszProtocol = NULL;
180 /* found scheme, set length of remainder */
181 y->cchSuffix = lstrlenA(y->pszSuffix);
183 /* see if known scheme and return indicator number */
184 y->nScheme = URL_SCHEME_UNKNOWN;
185 inet_pro = shlwapi_schemes;
186 while (inet_pro->scheme_name) {
187 if (!strncasecmp(inet_pro->scheme_name, y->pszProtocol,
188 min(y->cchProtocol, lstrlenA(inet_pro->scheme_name)))) {
189 y->nScheme = inet_pro->scheme_number;
197 /*************************************************************************
200 * Unicode version of ParseURLA.
202 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
205 const SHL_2_inet_scheme *inet_pro;
209 y->nScheme = URL_SCHEME_INVALID;
210 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
211 /* FIXME: leading white space generates error of 0x80041001 which
214 if (*x <= L' ') return 0x80041001;
220 y->cchProtocol = cnt;
229 /* check for no scheme in string start */
230 /* (apparently schemes *must* be larger than a single character) */
231 if ((*x == L'\0') || (y->cchProtocol <= 1)) {
232 y->pszProtocol = NULL;
236 /* found scheme, set length of remainder */
237 y->cchSuffix = lstrlenW(y->pszSuffix);
239 /* see if known scheme and return indicator number */
240 len = WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, 0, 0, 0, 0);
241 cmpstr = HeapAlloc(GetProcessHeap(), 0, len);
242 WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, cmpstr, len, 0, 0);
243 y->nScheme = URL_SCHEME_UNKNOWN;
244 inet_pro = shlwapi_schemes;
245 while (inet_pro->scheme_name) {
246 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
247 min(len, lstrlenA(inet_pro->scheme_name)))) {
248 y->nScheme = inet_pro->scheme_number;
253 HeapFree(GetProcessHeap(), 0, cmpstr);
257 /*************************************************************************
258 * UrlCanonicalizeA [SHLWAPI.@]
260 * Canonicalize a Url.
263 * pszUrl [I] Url to cCanonicalize
264 * pszCanonicalized [O] Destination for converted Url.
265 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
266 * dwFlags [I] Flags controlling the conversion.
269 * Success: S_OK. The pszCanonicalized contains the converted Url.
270 * Failure: E_POINTER, if *pcchCanonicalized is too small.
272 * MSDN incorrectly describes the flags for this function. They should be:
273 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
274 *| URL_ESCAPE_SPACES_ONLY 0x04000000
275 *| URL_ESCAPE_PERCENT 0x00001000
276 *| URL_ESCAPE_UNSAFE 0x10000000
277 *| URL_UNESCAPE 0x10000000
278 *| URL_DONT_SIMPLIFY 0x08000000
279 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
281 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
282 LPDWORD pcchCanonicalized, DWORD dwFlags)
284 LPWSTR base, canonical;
285 DWORD ret, len, len2;
287 TRACE("(%s %p %p 0x%08lx) using W version\n",
288 debugstr_a(pszUrl), pszCanonicalized,
289 pcchCanonicalized, dwFlags);
291 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
294 base = HeapAlloc(GetProcessHeap(), 0,
295 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
296 canonical = base + INTERNET_MAX_URL_LENGTH;
298 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
299 len = INTERNET_MAX_URL_LENGTH;
301 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
303 HeapFree(GetProcessHeap(), 0, base);
307 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
308 if (len2 > *pcchCanonicalized) {
309 *pcchCanonicalized = len;
310 HeapFree(GetProcessHeap(), 0, base);
313 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
314 *pcchCanonicalized, 0, 0);
315 *pcchCanonicalized = len2;
316 HeapFree(GetProcessHeap(), 0, base);
320 /*************************************************************************
321 * UrlCanonicalizeW [SHLWAPI.@]
323 * See UrlCanonicalizeA.
325 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
326 LPDWORD pcchCanonicalized, DWORD dwFlags)
330 LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
334 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
335 pcchCanonicalized, dwFlags);
337 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
340 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
341 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
343 if (dwFlags & URL_DONT_SIMPLIFY)
344 memcpy(lpszUrlCpy, pszUrl, nByteLen);
350 * 1 have 2[+] alnum 2,3
351 * 2 have scheme (found :) 4,6,3
352 * 3 failed (no location)
354 * 5 have 1[+] alnum 6,3
355 * 6 have location (found /) save root location
358 wk1 = (LPWSTR)pszUrl;
364 if (!isalnumW(*wk1)) {state = 3; break;}
366 if (!isalnumW(*wk1)) {state = 3; break;}
372 if (*wk1++ == L':') state = 2;
375 if (*wk1 != L'/') {state = 3; break;}
377 if (*wk1 != L'/') {state = 6; break;}
382 nWkLen = strlenW(wk1);
383 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
388 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.')) {state = 3; break;}
389 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.')) *wk2++ = *wk1++;
393 if (*wk1 != L'/') {state = 3; break;}
398 /* Now at root location, cannot back up any more. */
399 /* "root" will point at the '/' */
402 TRACE("wk1=%c\n", (CHAR)*wk1);
403 mp = strchrW(wk1, L'/');
405 nWkLen = strlenW(wk1);
406 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
412 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
416 TRACE("found '/.'\n");
417 if (*(wk1+1) == L'/') {
418 /* case of /./ -> skip the ./ */
421 else if (*(wk1+1) == L'.') {
422 /* found /.. look for next / */
423 TRACE("found '/..'\n");
424 if (*(wk1+2) == L'/' || *(wk1+2) == L'?' || *(wk1+2) == L'#' || *(wk1+2) == 0) {
425 /* case /../ -> need to backup wk2 */
426 TRACE("found '/../'\n");
427 *(wk2-1) = L'\0'; /* set end of string */
428 mp = strrchrW(root, L'/');
429 if (mp && (mp >= root)) {
430 /* found valid backup point */
438 /* did not find point, restore '/' */
448 FIXME("how did we get here - state=%d\n", state);
449 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
454 TRACE("Simplified, orig <%s>, simple <%s>\n",
455 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
457 nLen = lstrlenW(lpszUrlCpy);
458 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
459 lpszUrlCpy[--nLen]=0;
461 if(dwFlags & URL_UNESCAPE)
462 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
464 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
465 URL_ESCAPE_SPACES_ONLY |
467 URL_DONT_ESCAPE_EXTRA_INFO |
468 URL_ESCAPE_SEGMENT_ONLY ))) {
469 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
470 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
472 } else { /* No escaping needed, just copy the string */
473 nLen = lstrlenW(lpszUrlCpy);
474 if(nLen < *pcchCanonicalized)
475 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
480 *pcchCanonicalized = nLen;
483 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
486 TRACE("result %s\n", debugstr_w(pszCanonicalized));
491 /*************************************************************************
492 * UrlCombineA [SHLWAPI.@]
497 * pszBase [I] Base Url
498 * pszRelative [I] Url to combine with pszBase
499 * pszCombined [O] Destination for combined Url
500 * pcchCombined [O] Destination for length of pszCombined
501 * dwFlags [I] URL_ flags from "shlwapi.h"
504 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
505 * contains its length.
506 * Failure: An HRESULT error code indicating the error.
508 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
509 LPSTR pszCombined, LPDWORD pcchCombined,
512 LPWSTR base, relative, combined;
513 DWORD ret, len, len2;
515 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
516 debugstr_a(pszBase),debugstr_a(pszRelative),
517 pcchCombined?*pcchCombined:0,dwFlags);
519 if(!pszBase || !pszRelative || !pcchCombined)
522 base = HeapAlloc(GetProcessHeap(), 0,
523 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
524 relative = base + INTERNET_MAX_URL_LENGTH;
525 combined = relative + INTERNET_MAX_URL_LENGTH;
527 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
528 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
531 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
534 HeapFree(GetProcessHeap(), 0, base);
538 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
539 if (len2 > *pcchCombined) {
540 *pcchCombined = len2;
541 HeapFree(GetProcessHeap(), 0, base);
544 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
546 *pcchCombined = len2;
547 HeapFree(GetProcessHeap(), 0, base);
551 /*************************************************************************
552 * UrlCombineW [SHLWAPI.@]
556 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
557 LPWSTR pszCombined, LPDWORD pcchCombined,
560 PARSEDURLW base, relative;
561 DWORD myflags, sizeloc = 0;
562 DWORD len, res1, res2, process_case = 0;
563 LPWSTR work, preliminary, mbase, mrelative;
564 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
565 static const WCHAR single_slash[] = {'/','\0'};
568 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
569 debugstr_w(pszBase),debugstr_w(pszRelative),
570 pcchCombined?*pcchCombined:0,dwFlags);
572 if(!pszBase || !pszRelative || !pcchCombined)
575 base.cbSize = sizeof(base);
576 relative.cbSize = sizeof(relative);
578 /* Get space for duplicates of the input and the output */
579 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
581 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
582 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
583 *preliminary = L'\0';
585 /* Canonicalize the base input prior to looking for the scheme */
586 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
587 len = INTERNET_MAX_URL_LENGTH;
588 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
590 /* Canonicalize the relative input prior to looking for the scheme */
591 len = INTERNET_MAX_URL_LENGTH;
592 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
594 /* See if the base has a scheme */
595 res1 = ParseURLW(mbase, &base);
597 /* if pszBase has no scheme, then return pszRelative */
598 TRACE("no scheme detected in Base\n");
603 /* get size of location field (if it exists) */
604 work = (LPWSTR)base.pszSuffix;
606 if (*work++ == L'/') {
607 if (*work++ == L'/') {
608 /* At this point have start of location and
609 * it ends at next '/' or end of string.
611 while(*work && (*work != L'/')) work++;
612 sizeloc = (DWORD)(work - base.pszSuffix);
616 /* Change .sizep2 to not have the last leaf in it,
617 * Note: we need to start after the location (if it exists)
619 work = strrchrW((base.pszSuffix+sizeloc), L'/');
621 len = (DWORD)(work - base.pszSuffix + 1);
622 base.cchSuffix = len;
626 * .pszSuffix points to location (starting with '//')
627 * .cchSuffix length of location (above) and rest less the last
629 * sizeloc length of location (above) up to but not including
633 res2 = ParseURLW(mrelative, &relative);
635 /* no scheme in pszRelative */
636 TRACE("no scheme detected in Relative\n");
637 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
638 relative.cchSuffix = strlenW(mrelative);
639 if (*pszRelative == L':') {
640 /* case that is either left alone or uses pszBase */
641 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
648 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
649 /* case that becomes "file:///" */
650 strcpyW(preliminary, myfilestr);
654 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
655 /* pszRelative has location and rest */
659 if (*mrelative == L'/') {
660 /* case where pszRelative is root to location */
664 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
668 /* handle cases where pszRelative has scheme */
669 if ((base.cchProtocol == relative.cchProtocol) &&
670 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
672 /* since the schemes are the same */
673 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
674 /* case where pszRelative replaces location and following */
678 if (*relative.pszSuffix == L'/') {
679 /* case where pszRelative is root to location */
683 /* case where scheme is followed by document path */
687 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
688 /* case where pszRelative replaces scheme, location,
689 * and following and handles PLUGGABLE
696 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
700 switch (process_case) {
703 * Return pszRelative appended to what ever is in pszCombined,
704 * (which may the string "file:///"
706 strcatW(preliminary, mrelative);
710 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
711 * and pszRelative starts with "//", then append a "/"
713 strcpyW(preliminary, mrelative);
714 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
715 URL_JustLocation(relative.pszSuffix))
716 strcatW(preliminary, single_slash);
720 * Return the pszBase scheme with pszRelative. Basically
721 * keeps the scheme and replaces the domain and following.
723 strncpyW(preliminary, base.pszProtocol, base.cchProtocol + 1);
724 work = preliminary + base.cchProtocol + 1;
725 strcpyW(work, relative.pszSuffix);
726 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
727 URL_JustLocation(relative.pszSuffix))
728 strcatW(work, single_slash);
732 * Return the pszBase scheme and location but everything
733 * after the location is pszRelative. (Replace document
736 strncpyW(preliminary, base.pszProtocol, base.cchProtocol+1+sizeloc);
737 work = preliminary + base.cchProtocol + 1 + sizeloc;
738 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
740 strcpyW(work, relative.pszSuffix);
744 * Return the pszBase without its document (if any) and
745 * append pszRelative after its scheme.
747 strncpyW(preliminary, base.pszProtocol, base.cchProtocol+1+base.cchSuffix);
748 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
751 strcpyW(work, relative.pszSuffix);
755 FIXME("How did we get here????? process_case=%ld\n", process_case);
760 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
761 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
762 if(SUCCEEDED(ret) && pszCombined) {
763 lstrcpyW(pszCombined, mrelative);
765 TRACE("return-%ld len=%ld, %s\n",
766 process_case, *pcchCombined, debugstr_w(pszCombined));
768 HeapFree(GetProcessHeap(), 0, preliminary);
772 /*************************************************************************
773 * UrlEscapeA [SHLWAPI.@]
776 HRESULT WINAPI UrlEscapeA(
782 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
783 WCHAR *escapedW = bufW;
786 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
788 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
790 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
791 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
792 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
795 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
796 if(*pcchEscaped > lenA) {
797 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
798 pszEscaped[lenA] = 0;
801 *pcchEscaped = lenA + 1;
805 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
806 RtlFreeUnicodeString(&urlW);
810 #define WINE_URL_BASH_AS_SLASH 0x01
811 #define WINE_URL_COLLAPSE_SLASHES 0x02
812 #define WINE_URL_ESCAPE_SLASH 0x04
813 #define WINE_URL_ESCAPE_HASH 0x08
814 #define WINE_URL_ESCAPE_QUESTION 0x10
815 #define WINE_URL_STOP_ON_HASH 0x20
816 #define WINE_URL_STOP_ON_QUESTION 0x40
818 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
824 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
831 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
834 if (ch <= 31 || ch >= 127)
855 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
859 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
863 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
873 /*************************************************************************
874 * UrlEscapeW [SHLWAPI.@]
876 * Converts unsafe characters in a Url into escape sequences.
879 * pszUrl [I] Url to modify
880 * pszEscaped [O] Destination for modified Url
881 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
882 * dwFlags [I] URL_ flags from "shlwapi.h"
885 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
886 * contains its length.
887 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
888 * pcchEscaped is set to the required length.
890 * Converts unsafe characters into their escape sequences.
893 * - By default this function stops converting at the first '?' or
895 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
896 * converted, but the conversion continues past a '?' or '#'.
897 * - Note that this function did not work well (or at all) in shlwapi version 4.
900 * Only the following flags are implemented:
901 *| URL_ESCAPE_SPACES_ONLY
902 *| URL_DONT_ESCAPE_EXTRA_INFO
903 *| URL_ESCAPE_SEGMENT_ONLY
904 *| URL_ESCAPE_PERCENT
906 HRESULT WINAPI UrlEscapeW(
913 DWORD needed = 0, ret;
914 BOOL stop_escaping = FALSE;
915 WCHAR next[5], *dst = pszEscaped;
917 PARSEDURLW parsed_url;
920 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
922 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
923 pcchEscaped, dwFlags);
925 if(!pszUrl || !pszEscaped || !pcchEscaped)
928 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
929 URL_ESCAPE_SEGMENT_ONLY |
930 URL_DONT_ESCAPE_EXTRA_INFO |
932 FIXME("Unimplemented flags: %08lx\n", dwFlags);
935 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
936 /* if SPACES_ONLY specified, reset the other controls */
937 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
939 URL_ESCAPE_SEGMENT_ONLY);
942 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
943 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
947 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
948 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
950 parsed_url.cbSize = sizeof(parsed_url);
951 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
952 parsed_url.nScheme = URL_SCHEME_INVALID;
954 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
956 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
957 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
959 switch(parsed_url.nScheme) {
960 case URL_SCHEME_FILE:
961 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
962 int_flags &= ~WINE_URL_STOP_ON_HASH;
965 case URL_SCHEME_HTTP:
966 case URL_SCHEME_HTTPS:
967 int_flags |= WINE_URL_BASH_AS_SLASH;
968 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
969 int_flags |= WINE_URL_ESCAPE_SLASH;
972 case URL_SCHEME_MAILTO:
973 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
974 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
977 case URL_SCHEME_INVALID:
982 if(parsed_url.pszSuffix[0] != '/')
983 int_flags |= WINE_URL_ESCAPE_SLASH;
988 for(src = pszUrl; *src; ) {
992 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
993 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
994 while(cur == '/' || cur == '\\') {
998 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
999 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1000 src += localhost_len + 1;
1007 next[0] = next[1] = next[2] = '/';
1014 next[0] = next[1] = '/';
1021 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1022 stop_escaping = TRUE;
1024 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1025 stop_escaping = TRUE;
1027 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1029 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1031 next[1] = hexDigits[(cur >> 4) & 0xf];
1032 next[2] = hexDigits[cur & 0xf];
1041 if(needed + len <= *pcchEscaped) {
1042 memcpy(dst, next, len*sizeof(WCHAR));
1048 if(needed < *pcchEscaped) {
1052 needed++; /* add one for the '\0' */
1055 *pcchEscaped = needed;
1060 /*************************************************************************
1061 * UrlUnescapeA [SHLWAPI.@]
1063 * Converts Url escape sequences back to ordinary characters.
1066 * pszUrl [I/O] Url to convert
1067 * pszUnescaped [O] Destination for converted Url
1068 * pcchUnescaped [I/O] Size of output string
1069 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1072 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1073 * dwFlags includes URL_ESCAPE_INPLACE.
1074 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1075 * this case pcchUnescaped is set to the size required.
1077 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1078 * the first occurrence of either a '?' or '#' character.
1080 HRESULT WINAPI UrlUnescapeA(
1083 LPDWORD pcchUnescaped,
1090 BOOL stop_unescaping = FALSE;
1092 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1093 pcchUnescaped, dwFlags);
1095 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1096 return E_INVALIDARG;
1098 if(dwFlags & URL_UNESCAPE_INPLACE)
1103 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1104 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1105 (*src == '#' || *src == '?')) {
1106 stop_unescaping = TRUE;
1108 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1109 && stop_unescaping == FALSE) {
1112 memcpy(buf, src + 1, 2);
1114 ih = strtol(buf, NULL, 16);
1116 src += 2; /* Advance to end of escape */
1120 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1124 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1128 needed++; /* add one for the '\0' */
1131 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1132 *pcchUnescaped = needed;
1135 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1136 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1142 /*************************************************************************
1143 * UrlUnescapeW [SHLWAPI.@]
1147 HRESULT WINAPI UrlUnescapeW(
1149 LPWSTR pszUnescaped,
1150 LPDWORD pcchUnescaped,
1157 BOOL stop_unescaping = FALSE;
1159 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1160 pcchUnescaped, dwFlags);
1162 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1163 return E_INVALIDARG;
1165 if(dwFlags & URL_UNESCAPE_INPLACE)
1170 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1171 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1172 (*src == L'#' || *src == L'?')) {
1173 stop_unescaping = TRUE;
1175 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1176 && stop_unescaping == FALSE) {
1178 WCHAR buf[5] = {'0','x',0};
1179 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1181 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1183 src += 2; /* Advance to end of escape */
1187 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1191 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1195 needed++; /* add one for the '\0' */
1198 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1199 *pcchUnescaped = needed;
1202 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1203 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1209 /*************************************************************************
1210 * UrlGetLocationA [SHLWAPI.@]
1212 * Get the location from a Url.
1215 * pszUrl [I] Url to get the location from
1218 * A pointer to the start of the location in pszUrl, or NULL if there is
1222 * - MSDN erroneously states that "The location is the segment of the Url
1223 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1224 * stop at '?' and always return a NULL in this case.
1225 * - MSDN also erroneously states that "If a file URL has a query string,
1226 * the returned string is the query string". In all tested cases, if the
1227 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1230 *| NULL file://aa/b/cd#hohoh
1231 *| #hohoh http://aa/b/cd#hohoh
1232 *| NULL fi://aa/b/cd#hohoh
1233 *| #hohoh ff://aa/b/cd#hohoh
1235 LPCSTR WINAPI UrlGetLocationA(
1241 base.cbSize = sizeof(base);
1242 res1 = ParseURLA(pszUrl, &base);
1243 if (res1) return NULL; /* invalid scheme */
1245 /* if scheme is file: then never return pointer */
1246 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1248 /* Look for '#' and return its addr */
1249 return strchr(base.pszSuffix, '#');
1252 /*************************************************************************
1253 * UrlGetLocationW [SHLWAPI.@]
1255 * See UrlGetLocationA.
1257 LPCWSTR WINAPI UrlGetLocationW(
1263 base.cbSize = sizeof(base);
1264 res1 = ParseURLW(pszUrl, &base);
1265 if (res1) return NULL; /* invalid scheme */
1267 /* if scheme is file: then never return pointer */
1268 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1270 /* Look for '#' and return its addr */
1271 return strchrW(base.pszSuffix, L'#');
1274 /*************************************************************************
1275 * UrlCompareA [SHLWAPI.@]
1280 * pszUrl1 [I] First Url to compare
1281 * pszUrl2 [I] Url to compare to pszUrl1
1282 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1285 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1286 * than, equal to, or less than pszUrl1 respectively.
1288 INT WINAPI UrlCompareA(
1293 INT ret, len, len1, len2;
1296 return strcmp(pszUrl1, pszUrl2);
1297 len1 = strlen(pszUrl1);
1298 if (pszUrl1[len1-1] == '/') len1--;
1299 len2 = strlen(pszUrl2);
1300 if (pszUrl2[len2-1] == '/') len2--;
1302 return strncmp(pszUrl1, pszUrl2, len1);
1303 len = min(len1, len2);
1304 ret = strncmp(pszUrl1, pszUrl2, len);
1305 if (ret) return ret;
1306 if (len1 > len2) return 1;
1310 /*************************************************************************
1311 * UrlCompareW [SHLWAPI.@]
1315 INT WINAPI UrlCompareW(
1321 size_t len, len1, len2;
1324 return strcmpW(pszUrl1, pszUrl2);
1325 len1 = strlenW(pszUrl1);
1326 if (pszUrl1[len1-1] == '/') len1--;
1327 len2 = strlenW(pszUrl2);
1328 if (pszUrl2[len2-1] == '/') len2--;
1330 return strncmpW(pszUrl1, pszUrl2, len1);
1331 len = min(len1, len2);
1332 ret = strncmpW(pszUrl1, pszUrl2, len);
1333 if (ret) return ret;
1334 if (len1 > len2) return 1;
1338 /*************************************************************************
1339 * HashData [SHLWAPI.@]
1341 * Hash an input block into a variable sized digest.
1344 * lpSrc [I] Input block
1345 * nSrcLen [I] Length of lpSrc
1346 * lpDest [I] Output for hash digest
1347 * nDestLen [I] Length of lpDest
1350 * Success: TRUE. lpDest is filled with the computed hash value.
1351 * Failure: FALSE, if any argument is invalid.
1353 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1354 unsigned char *lpDest, DWORD nDestLen)
1356 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1358 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1359 IsBadWritePtr(lpDest, nDestLen))
1360 return E_INVALIDARG;
1362 while (destCount >= 0)
1364 lpDest[destCount] = (destCount & 0xff);
1368 while (srcCount >= 0)
1370 destCount = nDestLen - 1;
1371 while (destCount >= 0)
1373 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1381 /*************************************************************************
1382 * UrlHashA [SHLWAPI.@]
1384 * Produce a Hash from a Url.
1387 * pszUrl [I] Url to hash
1388 * lpDest [O] Destinationh for hash
1389 * nDestLen [I] Length of lpDest
1392 * Success: S_OK. lpDest is filled with the computed hash value.
1393 * Failure: E_INVALIDARG, if any argument is invalid.
1395 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1397 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1398 return E_INVALIDARG;
1400 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1404 /*************************************************************************
1405 * UrlHashW [SHLWAPI.@]
1409 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1411 char szUrl[MAX_PATH];
1413 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1415 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1416 return E_INVALIDARG;
1418 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1419 * return the same digests for the same URL.
1421 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1422 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1426 /*************************************************************************
1427 * UrlApplySchemeA [SHLWAPI.@]
1429 * Apply a scheme to a Url.
1432 * pszIn [I] Url to apply scheme to
1433 * pszOut [O] Destination for modified Url
1434 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1435 * dwFlags [I] URL_ flags from "shlwapi.h"
1438 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1439 * Failure: An HRESULT error code describing the error.
1441 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1444 DWORD ret, len, len2;
1446 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1447 debugstr_a(pszIn), *pcchOut, dwFlags);
1449 in = HeapAlloc(GetProcessHeap(), 0,
1450 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1451 out = in + INTERNET_MAX_URL_LENGTH;
1453 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1454 len = INTERNET_MAX_URL_LENGTH;
1456 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1457 if ((ret != S_OK) && (ret != S_FALSE)) {
1458 HeapFree(GetProcessHeap(), 0, in);
1462 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1463 if (len2 > *pcchOut) {
1465 HeapFree(GetProcessHeap(), 0, in);
1468 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1470 HeapFree(GetProcessHeap(), 0, in);
1474 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1479 DWORD value_len, data_len, dwType, i;
1480 WCHAR reg_path[MAX_PATH];
1481 WCHAR value[MAX_PATH], data[MAX_PATH];
1484 MultiByteToWideChar(0, 0,
1485 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1486 -1, reg_path, MAX_PATH);
1487 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1489 while(value_len = data_len = MAX_PATH,
1490 RegEnumValueW(newkey, index, value, &value_len,
1491 0, &dwType, (LPVOID)data, &data_len) == 0) {
1492 TRACE("guess %d %s is %s\n",
1493 index, debugstr_w(value), debugstr_w(data));
1496 for(i=0; i<value_len; i++) {
1499 /* remember that TRUE is not-equal */
1500 j = ChrCmpIW(Wxx, Wyy);
1503 if ((i == value_len) && !j) {
1504 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1505 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1506 RegCloseKey(newkey);
1509 strcpyW(pszOut, data);
1510 strcatW(pszOut, pszIn);
1511 *pcchOut = strlenW(pszOut);
1512 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1513 RegCloseKey(newkey);
1518 RegCloseKey(newkey);
1522 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1525 DWORD data_len, dwType;
1526 WCHAR reg_path[MAX_PATH];
1527 WCHAR value[MAX_PATH], data[MAX_PATH];
1529 /* get and prepend default */
1530 MultiByteToWideChar(0, 0,
1531 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1532 -1, reg_path, MAX_PATH);
1533 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1534 data_len = MAX_PATH;
1537 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1538 RegCloseKey(newkey);
1539 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1540 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1543 strcpyW(pszOut, data);
1544 strcatW(pszOut, pszIn);
1545 *pcchOut = strlenW(pszOut);
1546 TRACE("used default %s\n", debugstr_w(pszOut));
1550 /*************************************************************************
1551 * UrlApplySchemeW [SHLWAPI.@]
1553 * See UrlApplySchemeA.
1555 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1557 PARSEDURLW in_scheme;
1561 TRACE("(in %s, out size %ld, flags %08lx)\n",
1562 debugstr_w(pszIn), *pcchOut, dwFlags);
1564 if (dwFlags & URL_APPLY_GUESSFILE) {
1565 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1566 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1567 strcpyW(pszOut, pszIn);
1568 *pcchOut = strlenW(pszOut);
1572 in_scheme.cbSize = sizeof(in_scheme);
1573 /* See if the base has a scheme */
1574 res1 = ParseURLW(pszIn, &in_scheme);
1576 /* no scheme in input, need to see if we need to guess */
1577 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1578 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1583 /* we have a scheme, see if valid (known scheme) */
1584 if (in_scheme.nScheme) {
1585 /* have valid scheme, so just copy and exit */
1586 if (strlenW(pszIn) + 1 > *pcchOut) {
1587 *pcchOut = strlenW(pszIn) + 1;
1590 strcpyW(pszOut, pszIn);
1591 *pcchOut = strlenW(pszOut);
1592 TRACE("valid scheme, returing copy\n");
1597 /* If we are here, then either invalid scheme,
1598 * or no scheme and can't/failed guess.
1600 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1602 (dwFlags & URL_APPLY_DEFAULT)) {
1603 /* find and apply default scheme */
1604 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1607 /* just copy and give proper return code */
1608 if (strlenW(pszIn) + 1 > *pcchOut) {
1609 *pcchOut = strlenW(pszIn) + 1;
1612 strcpyW(pszOut, pszIn);
1613 *pcchOut = strlenW(pszOut);
1614 TRACE("returning copy, left alone\n");
1618 /*************************************************************************
1619 * UrlIsA [SHLWAPI.@]
1621 * Determine if a Url is of a certain class.
1624 * pszUrl [I] Url to check
1625 * Urlis [I] URLIS_ constant from "shlwapi.h"
1628 * TRUE if pszUrl belongs to the class type in Urlis.
1631 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1637 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1642 base.cbSize = sizeof(base);
1643 res1 = ParseURLA(pszUrl, &base);
1644 if (res1) return FALSE; /* invalid scheme */
1645 switch (base.nScheme)
1647 case URL_SCHEME_MAILTO:
1648 case URL_SCHEME_SHELL:
1649 case URL_SCHEME_JAVASCRIPT:
1650 case URL_SCHEME_VBSCRIPT:
1651 case URL_SCHEME_ABOUT:
1657 return !StrCmpNA("file:", pszUrl, 5);
1659 case URLIS_DIRECTORY:
1660 last = pszUrl + strlen(pszUrl) - 1;
1661 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1664 return PathIsURLA(pszUrl);
1666 case URLIS_NOHISTORY:
1667 case URLIS_APPLIABLE:
1668 case URLIS_HASQUERY:
1670 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1675 /*************************************************************************
1676 * UrlIsW [SHLWAPI.@]
1680 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1682 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1687 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1692 base.cbSize = sizeof(base);
1693 res1 = ParseURLW(pszUrl, &base);
1694 if (res1) return FALSE; /* invalid scheme */
1695 switch (base.nScheme)
1697 case URL_SCHEME_MAILTO:
1698 case URL_SCHEME_SHELL:
1699 case URL_SCHEME_JAVASCRIPT:
1700 case URL_SCHEME_VBSCRIPT:
1701 case URL_SCHEME_ABOUT:
1707 return !strncmpW(stemp, pszUrl, 5);
1709 case URLIS_DIRECTORY:
1710 last = pszUrl + strlenW(pszUrl) - 1;
1711 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1714 return PathIsURLW(pszUrl);
1716 case URLIS_NOHISTORY:
1717 case URLIS_APPLIABLE:
1718 case URLIS_HASQUERY:
1720 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1725 /*************************************************************************
1726 * UrlIsNoHistoryA [SHLWAPI.@]
1728 * Determine if a Url should not be stored in the users history list.
1731 * pszUrl [I] Url to check
1734 * TRUE, if pszUrl should be excluded from the history list,
1737 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1739 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1742 /*************************************************************************
1743 * UrlIsNoHistoryW [SHLWAPI.@]
1745 * See UrlIsNoHistoryA.
1747 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1749 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1752 /*************************************************************************
1753 * UrlIsOpaqueA [SHLWAPI.@]
1755 * Determine if a Url is opaque.
1758 * pszUrl [I] Url to check
1761 * TRUE if pszUrl is opaque,
1765 * An opaque Url is one that does not start with "<protocol>://".
1767 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1769 return UrlIsA(pszUrl, URLIS_OPAQUE);
1772 /*************************************************************************
1773 * UrlIsOpaqueW [SHLWAPI.@]
1777 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1779 return UrlIsW(pszUrl, URLIS_OPAQUE);
1782 /*************************************************************************
1783 * Scans for characters of type "type" and when not matching found,
1784 * returns pointer to it and length in size.
1786 * Characters tested based on RFC 1738
1788 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1790 static DWORD alwayszero = 0;
1799 if ( (islowerW(*start) && isalphaW(*start)) ||
1814 if ( isalphaW(*start) ||
1816 /* user/password only characters */
1821 /* *extra* characters */
1824 (*start == L'\'') ||
1828 /* *safe* characters */
1836 } else if (*start == L'%') {
1837 if (isxdigitW(*(start+1)) &&
1838 isxdigitW(*(start+2))) {
1850 if (isdigitW(*start)) {
1861 if (isalnumW(*start) ||
1863 (*start == L'.') ) {
1872 FIXME("unknown type %d\n", type);
1873 return (LPWSTR)&alwayszero;
1875 /* TRACE("scanned %ld characters next char %p<%c>\n",
1876 *size, start, *start); */
1880 /*************************************************************************
1881 * Attempt to parse URL into pieces.
1883 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1887 memset(pl, 0, sizeof(WINE_PARSE_URL));
1888 pl->pScheme = pszUrl;
1889 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1890 if (!*work || (*work != L':')) goto ErrorExit;
1892 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1893 pl->pUserName = work + 2;
1894 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1895 if (*work == L':' ) {
1896 /* parse password */
1898 pl->pPassword = work;
1899 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1900 if (*work != L'@') {
1901 /* what we just parsed must be the hostname and port
1902 * so reset pointers and clear then let it parse */
1903 pl->szUserName = pl->szPassword = 0;
1904 work = pl->pUserName - 1;
1905 pl->pUserName = pl->pPassword = 0;
1907 } else if (*work == L'@') {
1911 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1912 /* what was parsed was hostname, so reset pointers and let it parse */
1913 pl->szUserName = pl->szPassword = 0;
1914 work = pl->pUserName - 1;
1915 pl->pUserName = pl->pPassword = 0;
1916 } else goto ErrorExit;
1918 /* now start parsing hostname or hostnumber */
1920 pl->pHostName = work;
1921 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1922 if (*work == L':') {
1926 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1928 if (*work == L'/') {
1929 /* see if query string */
1930 pl->pQuery = strchrW(work, L'?');
1931 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1933 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1934 pl->pScheme, pl->szScheme,
1935 pl->pUserName, pl->szUserName,
1936 pl->pPassword, pl->szPassword,
1937 pl->pHostName, pl->szHostName,
1938 pl->pPort, pl->szPort,
1939 pl->pQuery, pl->szQuery);
1942 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1943 return E_INVALIDARG;
1946 /*************************************************************************
1947 * UrlGetPartA [SHLWAPI.@]
1949 * Retrieve part of a Url.
1952 * pszIn [I] Url to parse
1953 * pszOut [O] Destination for part of pszIn requested
1954 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1955 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1956 * dwFlags [I] URL_ flags from "shlwapi.h"
1959 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1960 * Failure: An HRESULT error code describing the error.
1962 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1963 DWORD dwPart, DWORD dwFlags)
1966 DWORD ret, len, len2;
1968 in = HeapAlloc(GetProcessHeap(), 0,
1969 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1970 out = in + INTERNET_MAX_URL_LENGTH;
1972 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1974 len = INTERNET_MAX_URL_LENGTH;
1975 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
1978 HeapFree(GetProcessHeap(), 0, in);
1982 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
1983 if (len2 > *pcchOut) {
1985 HeapFree(GetProcessHeap(), 0, in);
1988 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1990 HeapFree(GetProcessHeap(), 0, in);
1994 /*************************************************************************
1995 * UrlGetPartW [SHLWAPI.@]
1999 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2000 DWORD dwPart, DWORD dwFlags)
2004 DWORD size, schsize;
2005 LPCWSTR addr, schaddr;
2008 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2009 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2011 ret = URL_ParseUrl(pszIn, &pl);
2013 schaddr = pl.pScheme;
2014 schsize = pl.szScheme;
2017 case URL_PART_SCHEME:
2018 if (!pl.szScheme) return E_INVALIDARG;
2023 case URL_PART_HOSTNAME:
2024 if (!pl.szHostName) return E_INVALIDARG;
2025 addr = pl.pHostName;
2026 size = pl.szHostName;
2029 case URL_PART_USERNAME:
2030 if (!pl.szUserName) return E_INVALIDARG;
2031 addr = pl.pUserName;
2032 size = pl.szUserName;
2035 case URL_PART_PASSWORD:
2036 if (!pl.szPassword) return E_INVALIDARG;
2037 addr = pl.pPassword;
2038 size = pl.szPassword;
2042 if (!pl.szPort) return E_INVALIDARG;
2047 case URL_PART_QUERY:
2048 if (!pl.szQuery) return E_INVALIDARG;
2054 return E_INVALIDARG;
2057 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2058 if (*pcchOut < size + schsize + 2) {
2059 *pcchOut = size + schsize + 2;
2062 strncpyW(pszOut, schaddr, schsize);
2063 work = pszOut + schsize;
2065 strncpyW(work+1, addr, size);
2066 *pcchOut = size + schsize + 1;
2071 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2072 strncpyW(pszOut, addr, size);
2074 work = pszOut + size;
2077 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2082 /*************************************************************************
2083 * PathIsURLA [SHLWAPI.@]
2085 * Check if the given path is a Url.
2088 * lpszPath [I] Path to check.
2091 * TRUE if lpszPath is a Url.
2092 * FALSE if lpszPath is NULL or not a Url.
2094 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2099 if (!lpstrPath || !*lpstrPath) return FALSE;
2102 base.cbSize = sizeof(base);
2103 res1 = ParseURLA(lpstrPath, &base);
2104 return (base.nScheme != URL_SCHEME_INVALID);
2107 /*************************************************************************
2108 * PathIsURLW [SHLWAPI.@]
2112 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2117 if (!lpstrPath || !*lpstrPath) return FALSE;
2120 base.cbSize = sizeof(base);
2121 res1 = ParseURLW(lpstrPath, &base);
2122 return (base.nScheme != URL_SCHEME_INVALID);
2125 /*************************************************************************
2126 * UrlCreateFromPathA [SHLWAPI.@]
2128 * See UrlCreateFromPathW
2130 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2132 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2134 UNICODE_STRING pathW;
2136 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2138 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2139 return E_INVALIDARG;
2140 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2141 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2142 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2144 if(ret == S_OK || ret == S_FALSE) {
2145 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2146 if(*pcchUrl > lenA) {
2147 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2151 *pcchUrl = lenA + 1;
2155 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2156 RtlFreeUnicodeString(&pathW);
2160 /*************************************************************************
2161 * UrlCreateFromPathW [SHLWAPI.@]
2163 * Create a Url from a file path.
2166 * pszPath [I] Path to convert
2167 * pszUrl [O] Destination for the converted Url
2168 * pcchUrl [I/O] Length of pszUrl
2169 * dwReserved [I] Reserved, must be 0
2172 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2173 * Failure: An HRESULT error code.
2175 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2180 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2181 WCHAR three_slashesW[] = {'/','/','/',0};
2182 PARSEDURLW parsed_url;
2184 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2186 /* Validate arguments */
2187 if (dwReserved != 0)
2188 return E_INVALIDARG;
2189 if (!pszUrl || !pcchUrl)
2190 return E_INVALIDARG;
2193 parsed_url.cbSize = sizeof(parsed_url);
2194 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2195 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2196 needed = strlenW(pszPath);
2197 if (needed >= *pcchUrl) {
2198 *pcchUrl = needed + 1;
2202 strcpyW(pszUrl, pszPath);
2208 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2209 strcpyW(pszNewUrl, file_colonW);
2210 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2211 strcatW(pszNewUrl, three_slashesW);
2212 strcatW(pszNewUrl, pszPath);
2213 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2215 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2219 /*************************************************************************
2220 * SHAutoComplete [SHLWAPI.@]
2222 * Enable auto-completion for an edit control.
2225 * hwndEdit [I] Handle of control to enable auto-completion for
2226 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2229 * Success: S_OK. Auto-completion is enabled for the control.
2230 * Failure: An HRESULT error code indicating the error.
2232 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2234 FIXME("SHAutoComplete stub\n");
2238 /*************************************************************************
2239 * MLBuildResURLA [SHLWAPI.405]
2241 * Create a Url pointing to a resource in a module.
2244 * lpszLibName [I] Name of the module containing the resource
2245 * hMod [I] Callers module handle
2246 * dwFlags [I] Undocumented flags for loading the module
2247 * lpszRes [I] Resource name
2248 * lpszDest [O] Destination for resulting Url
2249 * dwDestLen [I] Length of lpszDest
2252 * Success: S_OK. lpszDest constains the resource Url.
2253 * Failure: E_INVALIDARG, if any argument is invalid, or
2254 * E_FAIL if dwDestLen is too small.
2256 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2257 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2259 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2263 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2266 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2268 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2269 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2271 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2272 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2273 if (SUCCEEDED(hRet) && lpszDest)
2274 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2279 /*************************************************************************
2280 * MLBuildResURLA [SHLWAPI.406]
2282 * See MLBuildResURLA.
2284 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2285 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2287 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2288 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2289 HRESULT hRet = E_FAIL;
2291 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2292 debugstr_w(lpszRes), lpszDest, dwDestLen);
2294 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2295 !lpszDest || (dwFlags && dwFlags != 2))
2296 return E_INVALIDARG;
2298 if (dwDestLen >= szResLen + 1)
2300 dwDestLen -= (szResLen + 1);
2301 memcpy(lpszDest, szRes, sizeof(szRes));
2303 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2307 WCHAR szBuff[MAX_PATH];
2310 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2311 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2313 DWORD dwPathLen = strlenW(szBuff) + 1;
2315 if (dwDestLen >= dwPathLen)
2319 dwDestLen -= dwPathLen;
2320 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2322 dwResLen = strlenW(lpszRes) + 1;
2323 if (dwDestLen >= dwResLen + 1)
2325 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2326 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2331 MLFreeLibrary(hMod);