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
27 #include "wine/unicode.h"
30 #define NO_SHLWAPI_STREAM
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(shell);
36 /* The following schemes were identified in the native version of
37 * SHLWAPI.DLL version 5.50
40 URL_SCHEME_INVALID = -1,
41 URL_SCHEME_UNKNOWN = 0,
56 URL_SCHEME_JAVASCRIPT,
64 URL_SCHEME scheme_number;
68 static const SHL_2_inet_scheme shlwapi_schemes[] = {
69 {URL_SCHEME_FTP, "ftp"},
70 {URL_SCHEME_HTTP, "http"},
71 {URL_SCHEME_GOPHER, "gopher"},
72 {URL_SCHEME_MAILTO, "mailto"},
73 {URL_SCHEME_NEWS, "news"},
74 {URL_SCHEME_NNTP, "nntp"},
75 {URL_SCHEME_TELNET, "telnet"},
76 {URL_SCHEME_WAIS, "wais"},
77 {URL_SCHEME_FILE, "file"},
78 {URL_SCHEME_MK, "mk"},
79 {URL_SCHEME_HTTPS, "https"},
80 {URL_SCHEME_SHELL, "shell"},
81 {URL_SCHEME_SNEWS, "snews"},
82 {URL_SCHEME_LOCAL, "local"},
83 {URL_SCHEME_JAVASCRIPT, "javascript"},
84 {URL_SCHEME_VBSCRIPT, "vbscript"},
85 {URL_SCHEME_ABOUT, "about"},
86 {URL_SCHEME_RES, "res"},
91 LPCWSTR pScheme; /* [out] start of scheme */
92 DWORD szScheme; /* [out] size of scheme (until colon) */
93 LPCWSTR pUserName; /* [out] start of Username */
94 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
95 LPCWSTR pPassword; /* [out] start of Password */
96 DWORD szPassword; /* [out] size of Password (until "@") */
97 LPCWSTR pHostName; /* [out] start of Hostname */
98 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
99 LPCWSTR pPort; /* [out] start of Port */
100 DWORD szPort; /* [out] size of Port (until "/" or eos) */
101 LPCWSTR pQuery; /* [out] start of Query */
102 DWORD szQuery; /* [out] size of Query (until eos) */
110 } WINE_URL_SCAN_TYPE;
113 INT size; /* [in] (always 0x18) */
114 LPCSTR ap1; /* [out] start of scheme */
115 INT sizep1; /* [out] size of scheme (until colon) */
116 LPCSTR ap2; /* [out] pointer following first colon */
117 INT sizep2; /* [out] size of remainder */
118 INT fcncde; /* [out] function match of p1 (0 if unknown) */
122 INT size; /* [in] (always 0x18) */
123 LPCWSTR ap1; /* [out] start of scheme */
124 INT sizep1; /* [out] size of scheme (until colon) */
125 LPCWSTR ap2; /* [out] pointer following first colon */
126 INT sizep2; /* [out] size of remainder */
127 INT fcncde; /* [out] function match of p1 (0 if unknown) */
130 static const CHAR hexDigits[] = "0123456789ABCDEF";
132 static const WCHAR fileW[] = {'f','i','l','e','\0'};
134 static const unsigned char HashDataLookup[256] = {
135 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
136 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
137 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
138 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
139 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
140 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
141 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
142 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
143 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
144 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
145 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
146 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
147 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
148 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
149 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
150 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
151 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
152 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
153 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
154 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
156 static BOOL URL_NeedEscapeA(CHAR ch, DWORD dwFlags)
162 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
169 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
172 if (ch <= 31 || ch >= 127)
194 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
201 static BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags)
207 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
214 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == L'%'))
217 if (ch <= 31 || ch >= 127)
239 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
246 static BOOL URL_JustLocation(LPCWSTR str)
248 while(*str && (*str == L'/')) str++;
250 while (*str && ((*str == L'-') ||
252 isalnumW(*str))) str++;
253 if (*str == L'/') return FALSE;
259 /*************************************************************************
262 * Identifies the Internet "scheme" in the passed string. ASCII based.
263 * Also determines start and length of item after the ':'
265 DWORD WINAPI SHLWAPI_1 (LPCSTR x, UNKNOWN_SHLWAPI_1 *y)
268 const SHL_2_inet_scheme *inet_pro;
270 y->fcncde = URL_SCHEME_INVALID;
271 if (y->size != 0x18) return E_INVALIDARG;
272 /* FIXME: leading white space generates error of 0x80041001 which
275 if (*x <= ' ') return 0x80041001;
290 /* check for no scheme in string start */
291 /* (apparently schemes *must* be larger than a single character) */
292 if ((*x == '\0') || (y->sizep1 <= 1)) {
297 /* found scheme, set length of remainder */
298 y->sizep2 = lstrlenA(y->ap2);
300 /* see if known scheme and return indicator number */
301 y->fcncde = URL_SCHEME_UNKNOWN;
302 inet_pro = shlwapi_schemes;
303 while (inet_pro->scheme_name) {
304 if (!strncasecmp(inet_pro->scheme_name, y->ap1,
305 min(y->sizep1, lstrlenA(inet_pro->scheme_name)))) {
306 y->fcncde = inet_pro->scheme_number;
314 /*************************************************************************
317 * Identifies the Internet "scheme" in the passed string. UNICODE based.
318 * Also determines start and length of item after the ':'
320 DWORD WINAPI SHLWAPI_2 (LPCWSTR x, UNKNOWN_SHLWAPI_2 *y)
323 const SHL_2_inet_scheme *inet_pro;
327 y->fcncde = URL_SCHEME_INVALID;
328 if (y->size != 0x18) return E_INVALIDARG;
329 /* FIXME: leading white space generates error of 0x80041001 which
332 if (*x <= L' ') return 0x80041001;
347 /* check for no scheme in string start */
348 /* (apparently schemes *must* be larger than a single character) */
349 if ((*x == L'\0') || (y->sizep1 <= 1)) {
354 /* found scheme, set length of remainder */
355 y->sizep2 = lstrlenW(y->ap2);
357 /* see if known scheme and return indicator number */
358 len = WideCharToMultiByte(0, 0, y->ap1, y->sizep1, 0, 0, 0, 0);
359 cmpstr = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len+1);
360 WideCharToMultiByte(0, 0, y->ap1, y->sizep1, cmpstr, len+1, 0, 0);
361 y->fcncde = URL_SCHEME_UNKNOWN;
362 inet_pro = shlwapi_schemes;
363 while (inet_pro->scheme_name) {
364 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
365 min(len, lstrlenA(inet_pro->scheme_name)))) {
366 y->fcncde = inet_pro->scheme_number;
371 HeapFree(GetProcessHeap(), 0, cmpstr);
375 /*************************************************************************
376 * UrlCanonicalizeA [SHLWAPI.@]
378 * Uses the W version to do job.
380 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
381 LPDWORD pcchCanonicalized, DWORD dwFlags)
383 LPWSTR base, canonical;
384 DWORD ret, len, len2;
386 TRACE("(%s %p %p 0x%08lx) using W version\n",
387 debugstr_a(pszUrl), pszCanonicalized,
388 pcchCanonicalized, dwFlags);
390 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
391 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
392 canonical = base + INTERNET_MAX_URL_LENGTH;
394 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
395 len = INTERNET_MAX_URL_LENGTH;
397 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
399 HeapFree(GetProcessHeap(), 0, base);
403 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
404 if (len2 > *pcchCanonicalized) {
405 *pcchCanonicalized = len;
406 HeapFree(GetProcessHeap(), 0, base);
409 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
410 *pcchCanonicalized, 0, 0);
411 *pcchCanonicalized = len2;
412 HeapFree(GetProcessHeap(), 0, base);
416 /*************************************************************************
417 * UrlCanonicalizeW [SHLWAPI.@]
420 * MSDN is wrong (at 10/30/01 - go figure). This should support the
421 * following flags: GLA
422 * URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
423 * URL_ESCAPE_SPACES_ONLY 0x04000000
424 * URL_ESCAPE_PERCENT 0x00001000
425 * URL_ESCAPE_UNSAFE 0x10000000
426 * URL_UNESCAPE 0x10000000
427 * URL_DONT_SIMPLIFY 0x08000000
428 * URL_ESCAPE_SEGMENT_ONLY 0x00002000
430 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
431 LPDWORD pcchCanonicalized, DWORD dwFlags)
435 LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
436 INT nLen, nByteLen, state;
438 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
439 pcchCanonicalized, dwFlags);
441 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
442 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
444 if (dwFlags & URL_DONT_SIMPLIFY)
445 memcpy(lpszUrlCpy, pszUrl, nByteLen);
451 * 1 have 2[+] alnum 2,3
452 * 2 have scheme (found :) 4,6,3
453 * 3 failed (no location)
455 * 5 have 1[+] alnum 6,3
456 * 6 have location (found /) save root location
459 wk1 = (LPWSTR)pszUrl;
465 if (!isalnumW(*wk1)) {state = 3; break;}
467 if (!isalnumW(*wk1)) {state = 3; break;}
473 if (*wk1++ == L':') state = 2;
476 if (*wk1 != L'/') {state = 3; break;}
478 if (*wk1 != L'/') {state = 6; break;}
488 if (!isalnumW(*wk1) && (*wk1 != L'-')) {state = 3; break;}
489 while(isalnumW(*wk1) || (*wk1 == L'-')) *wk2++ = *wk1++;
493 if (*wk1 != L'/') {state = 3; break;}
498 /* Now at root location, cannot back up any more. */
499 /* "root" will point at the '/' */
502 TRACE("wk1=%c\n", (CHAR)*wk1);
503 mp = strchrW(wk1, L'/');
511 strncpyW(wk2, wk1, nLen);
515 TRACE("found '/.'\n");
516 if (*(wk1+1) == L'/') {
517 /* case of /./ -> skip the ./ */
520 else if (*(wk1+1) == L'.') {
521 /* found /.. look for next / */
522 TRACE("found '/..'\n");
523 if (*(wk1+2) == L'/') {
524 /* case /../ -> need to backup wk2 */
525 TRACE("found '/../'\n");
526 *(wk2-1) = L'\0'; /* set end of string */
527 mp = strrchrW(root, L'/');
528 if (mp && (mp >= root)) {
529 /* found valid backup point */
534 /* did not find point, restore '/' */
544 FIXME("how did we get here - state=%d\n", state);
549 TRACE("Simplified, orig <%s>, simple <%s>\n",
550 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
553 if(dwFlags & URL_UNESCAPE)
554 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
556 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
557 URL_ESCAPE_SPACES_ONLY |
559 URL_DONT_ESCAPE_EXTRA_INFO |
560 URL_ESCAPE_SEGMENT_ONLY ))) {
561 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
562 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
564 } else { /* No escaping needed, just copy the string */
565 nLen = lstrlenW(lpszUrlCpy);
566 if(nLen < *pcchCanonicalized)
567 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
572 *pcchCanonicalized = nLen;
575 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
578 TRACE("result %s\n", debugstr_w(pszCanonicalized));
583 /*************************************************************************
584 * UrlCombineA [SHLWAPI.@]
586 * Uses the W version to do job.
588 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
589 LPSTR pszCombined, LPDWORD pcchCombined,
592 LPWSTR base, relative, combined;
593 DWORD ret, len, len2;
595 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
596 debugstr_a(pszBase),debugstr_a(pszRelative),
597 *pcchCombined,dwFlags);
599 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
600 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
601 relative = base + INTERNET_MAX_URL_LENGTH;
602 combined = relative + INTERNET_MAX_URL_LENGTH;
604 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
605 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
606 len = INTERNET_MAX_URL_LENGTH;
608 ret = UrlCombineW(base, relative, combined, &len, dwFlags);
610 HeapFree(GetProcessHeap(), 0, base);
614 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
615 if (len2 > *pcchCombined) {
616 *pcchCombined = len2;
617 HeapFree(GetProcessHeap(), 0, base);
620 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, *pcchCombined,
622 *pcchCombined = len2;
623 HeapFree(GetProcessHeap(), 0, base);
627 /*************************************************************************
628 * UrlCombineW [SHLWAPI.@]
630 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
631 LPWSTR pszCombined, LPDWORD pcchCombined,
634 UNKNOWN_SHLWAPI_2 base, relative;
635 DWORD myflags, sizeloc = 0;
636 DWORD len, res1, res2, process_case = 0;
637 LPWSTR work, preliminary, mbase, mrelative;
638 WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
639 WCHAR single_slash[] = {'/','\0'};
642 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
643 debugstr_w(pszBase),debugstr_w(pszRelative),
644 *pcchCombined,dwFlags);
649 /* Get space for duplicates of the input and the output */
650 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
652 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
653 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
654 *preliminary = L'\0';
656 /* Canonicalize the base input prior to looking for the scheme */
657 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
658 len = INTERNET_MAX_URL_LENGTH;
659 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
661 /* Canonicalize the relative input prior to looking for the scheme */
662 len = INTERNET_MAX_URL_LENGTH;
663 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
665 /* See if the base has a scheme */
666 res1 = SHLWAPI_2(mbase, &base);
668 /* if pszBase has no scheme, then return pszRelative */
669 TRACE("no scheme detected in Base\n");
674 /* get size of location field (if it exists) */
675 work = (LPWSTR)base.ap2;
677 if (*work++ == L'/') {
678 if (*work++ == L'/') {
679 /* At this point have start of location and
680 * it ends at next '/' or end of string.
682 while(*work && (*work != L'/')) work++;
683 sizeloc = work - base.ap2;
687 /* Change .sizep2 to not have the last leaf in it,
688 * Note: we need to start after the location (if it exists)
690 work = strrchrW((base.ap2+sizeloc), L'/');
692 len = work - base.ap2 + 1;
697 * .ap2 points to location (starting with '//')
698 * .sizep2 length of location (above) and rest less the last
700 * sizeloc length of location (above) up to but not including
704 res2 = SHLWAPI_2(mrelative, &relative);
706 /* no scheme in pszRelative */
707 TRACE("no scheme detected in Relative\n");
708 relative.ap2 = mrelative; /* case 3,4,5 depends on this */
709 relative.sizep2 = strlenW(mrelative);
710 if (*pszRelative == L':') {
711 /* case that is either left alone or uses pszBase */
712 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
719 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
720 /* case that becomes "file:///" */
721 strcpyW(preliminary, myfilestr);
725 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
726 /* pszRelative has location and rest */
730 if (*mrelative == L'/') {
731 /* case where pszRelative is root to location */
735 process_case = (*base.ap2 == L'/') ? 5 : 3;
739 /* handle cases where pszRelative has scheme */
740 if ((base.sizep1 == relative.sizep1) &&
741 (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) {
743 /* since the schemes are the same */
744 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
745 /* case where pszRelative replaces location and following */
749 if (*relative.ap2 == L'/') {
750 /* case where pszRelative is root to location */
754 /* case where scheme is followed by document path */
758 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
759 /* case where pszRelative replaces scheme, location,
760 * and following and handles PLUGGABLE
767 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
771 switch (process_case) {
774 * Return pszRelative appended to what ever is in pszCombined,
775 * (which may the string "file:///"
777 len = strlenW(mrelative) + strlenW(preliminary);
778 if (len+1 > *pcchCombined) {
783 strcatW(preliminary, mrelative);
787 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
788 * and pszRelative starts with "//", then append a "/"
790 len = strlenW(mrelative) + 1;
791 if (len+1 > *pcchCombined) {
796 strcpyW(preliminary, mrelative);
797 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
798 URL_JustLocation(relative.ap2))
799 strcatW(preliminary, single_slash);
803 * Return the pszBase scheme with pszRelative. Basicly
804 * keeps the scheme and replaces the domain and following.
806 len = base.sizep1 + 1 + relative.sizep2 + 1;
807 if (len+1 > *pcchCombined) {
812 strncpyW(preliminary, base.ap1, base.sizep1 + 1);
813 work = preliminary + base.sizep1 + 1;
814 strcpyW(work, relative.ap2);
815 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
816 URL_JustLocation(relative.ap2))
817 strcatW(work, single_slash);
821 * Return the pszBase scheme and location but everything
822 * after the location is pszRelative. (Replace document
825 len = base.sizep1 + 1 + sizeloc + relative.sizep2 + 1;
826 if (len+1 > *pcchCombined) {
831 strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc);
832 work = preliminary + base.sizep1 + 1 + sizeloc;
833 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
835 strcpyW(work, relative.ap2);
839 * Return the pszBase without its document (if any) and
840 * append pszRelative after its scheme.
842 len = base.sizep1 + 1 + base.sizep2 + relative.sizep2;
843 if (len+1 > *pcchCombined) {
848 strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2);
849 work = preliminary + base.sizep1+1+base.sizep2 - 1;
852 strcpyW(work, relative.ap2);
856 FIXME("How did we get here????? process_case=%ld\n", process_case);
862 * Now that the combining is done, process the escape options if
863 * necessary, otherwise just copy the string.
865 myflags = dwFlags & (URL_ESCAPE_PERCENT |
866 URL_ESCAPE_SPACES_ONLY |
867 URL_DONT_ESCAPE_EXTRA_INFO |
868 URL_ESCAPE_SEGMENT_ONLY);
870 ret = UrlEscapeW(preliminary, pszCombined,
871 pcchCombined, myflags);
873 len = (strlenW(preliminary) + 1) * sizeof(WCHAR);
874 memcpy(pszCombined, preliminary, len);
875 *pcchCombined = strlenW(preliminary);
877 TRACE("return-%ld len=%ld, %s\n",
878 process_case, *pcchCombined, debugstr_w(pszCombined));
880 HeapFree(GetProcessHeap(), 0, preliminary);
884 /*************************************************************************
885 * UrlEscapeA [SHLWAPI.@]
887 * Converts unsafe characters into their escape sequences.
889 * The converted string is returned in pszEscaped if the buffer size
890 * (which should be supplied in pcchEscaped) is large enough, in this
891 * case the function returns S_OK and pcchEscaped contains the length
892 * of the escaped string. If the buffer is not large enough the
893 * function returns E_POINTER and pcchEscaped contains the required
894 * buffer size (including room for the '\0').
896 * By default the function stops converting at the first '?' or
897 * '#'. [MSDN says differently]. If URL_ESCAPE_SPACES_ONLY flag is set
898 * then only spaces are converted, but the conversion continues past a
902 * Have now implemented the following flags:
903 * URL_ESCAPE_SPACES_ONLY
904 * URL_DONT_ESCAPE_EXTRA_INFO
905 * URL_ESCAPE_SEGMENT_ONLY
907 * Initial testing seems to indicate that this is now working like
908 * native shlwapi version 5. Note that these functions did not work
909 * well (or at all) in shlwapi version 4.
912 HRESULT WINAPI UrlEscapeA(
919 DWORD needed = 0, ret;
920 BOOL stop_escaping = FALSE;
921 char next[3], *dst = pszEscaped;
924 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
925 *pcchEscaped, dwFlags);
927 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
928 URL_ESCAPE_SEGMENT_ONLY |
929 URL_DONT_ESCAPE_EXTRA_INFO |
931 FIXME("Unimplemented flags: %08lx\n", dwFlags);
934 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
935 /* if SPACES_ONLY specified, reset the other controls */
936 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
938 URL_ESCAPE_SEGMENT_ONLY);
941 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
942 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
944 for(src = pszUrl; *src; src++) {
945 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
946 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
947 (*src == '#' || *src == '?'))
948 stop_escaping = TRUE;
950 if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
951 /* TRACE("escaping %c\n", *src); */
953 next[1] = hexDigits[(*src >> 4) & 0xf];
954 next[2] = hexDigits[*src & 0xf];
957 /* TRACE("passing %c\n", *src); */
962 if(needed + len <= *pcchEscaped) {
963 memcpy(dst, next, len);
969 if(needed < *pcchEscaped) {
973 needed++; /* add one for the '\0' */
976 *pcchEscaped = needed;
980 /*************************************************************************
981 * UrlEscapeW [SHLWAPI.@]
983 * See UrlEscapeA for list of assumptions, bugs, and FIXMEs
985 HRESULT WINAPI UrlEscapeW(
992 DWORD needed = 0, ret;
993 BOOL stop_escaping = FALSE;
994 WCHAR next[5], *dst = pszEscaped;
997 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
998 pcchEscaped, dwFlags);
1000 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1001 URL_ESCAPE_SEGMENT_ONLY |
1002 URL_DONT_ESCAPE_EXTRA_INFO |
1003 URL_ESCAPE_PERCENT))
1004 FIXME("Unimplemented flags: %08lx\n", dwFlags);
1007 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1008 /* if SPACES_ONLY specified, reset the other controls */
1009 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1010 URL_ESCAPE_PERCENT |
1011 URL_ESCAPE_SEGMENT_ONLY);
1014 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1015 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1017 for(src = pszUrl; *src; src++) {
1019 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1020 * (*src == L'#' || *src == L'?'))
1021 * stop_escaping = TRUE;
1023 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1024 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1025 (*src == L'#' || *src == L'?'))
1026 stop_escaping = TRUE;
1028 if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
1029 /* TRACE("escaping %c\n", *src); */
1032 * I would have assumed that the W form would escape
1033 * the character with 4 hex digits (or even 8),
1034 * however, experiments show that native shlwapi escapes
1035 * with only 2 hex digits.
1036 * next[1] = hexDigits[(*src >> 12) & 0xf];
1037 * next[2] = hexDigits[(*src >> 8) & 0xf];
1038 * next[3] = hexDigits[(*src >> 4) & 0xf];
1039 * next[4] = hexDigits[*src & 0xf];
1042 next[1] = hexDigits[(*src >> 4) & 0xf];
1043 next[2] = hexDigits[*src & 0xf];
1046 /* TRACE("passing %c\n", *src); */
1051 if(needed + len <= *pcchEscaped) {
1052 memcpy(dst, next, len*sizeof(WCHAR));
1058 if(needed < *pcchEscaped) {
1062 needed++; /* add one for the '\0' */
1065 *pcchEscaped = needed;
1070 /*************************************************************************
1071 * UrlUnescapeA [SHLWAPI.@]
1073 * Converts escape sequences back to ordinary characters.
1075 * If URL_ESCAPE_INPLACE is set in dwFlags then pszUnescaped and
1076 * pcchUnescaped are ignored and the converted string is returned in
1077 * pszUrl, otherwise the string is returned in pszUnescaped.
1078 * pcchUnescaped should contain the size of pszUnescaped on calling
1079 * and will contain the length the the returned string on return if
1080 * the buffer is big enough else it will contain the buffer size
1081 * required (including room for the '\0'). The function returns S_OK
1082 * on success or E_POINTER if the buffer is not large enough. If the
1083 * URL_DONT_ESCAPE_EXTRA_INFO flag is set then the conversion stops at
1084 * the first occurrence of either '?' or '#'.
1087 HRESULT WINAPI UrlUnescapeA(
1090 LPDWORD pcchUnescaped,
1097 BOOL stop_unescaping = FALSE;
1099 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1100 pcchUnescaped, dwFlags);
1102 if(dwFlags & URL_UNESCAPE_INPLACE)
1103 dst = (char*)pszUrl;
1107 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1108 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1109 (*src == '#' || *src == '?')) {
1110 stop_unescaping = TRUE;
1112 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1113 && stop_unescaping == FALSE) {
1116 memcpy(buf, src + 1, 2);
1118 ih = strtol(buf, NULL, 16);
1120 src += 2; /* Advance to end of escape */
1124 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1128 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1132 needed++; /* add one for the '\0' */
1135 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1136 *pcchUnescaped = needed;
1139 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1140 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1146 /*************************************************************************
1147 * UrlUnescapeW [SHLWAPI.@]
1149 * See UrlUnescapeA for list of assumptions, bugs, and FIXMEs
1151 HRESULT WINAPI UrlUnescapeW(
1153 LPWSTR pszUnescaped,
1154 LPDWORD pcchUnescaped,
1161 BOOL stop_unescaping = FALSE;
1163 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1164 pcchUnescaped, dwFlags);
1166 if(dwFlags & URL_UNESCAPE_INPLACE)
1167 dst = (WCHAR*)pszUrl;
1171 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1172 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1173 (*src == L'#' || *src == L'?')) {
1174 stop_unescaping = TRUE;
1176 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1177 && stop_unescaping == FALSE) {
1180 memcpy(buf, src + 1, 2*sizeof(WCHAR));
1182 ih = StrToIntW(buf);
1184 src += 2; /* Advance to end of escape */
1188 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1192 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1196 needed++; /* add one for the '\0' */
1199 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1200 *pcchUnescaped = needed;
1203 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1204 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1210 /*************************************************************************
1211 * UrlGetLocationA [SHLWAPI.@]
1214 * MSDN (as of 2001-11-01) says that:
1215 * "The location is the segment of the URL starting with a ?
1217 * Neither V4 nor V5 of shlwapi.dll implement the '?' and always return
1219 * MSDN further states that:
1220 * "If a file URL has a query string, the returned string is
1221 * the query string."
1222 * In all test cases if the scheme starts with "fi" then a NULL is
1223 * returned. V5 gives the following results:
1224 * NULL file://aa/b/cd#hohoh
1225 * #hohoh http://aa/b/cd#hohoh
1226 * NULL fi://aa/b/cd#hohoh
1227 * #hohoh ff://aa/b/cd#hohoh
1229 LPCSTR WINAPI UrlGetLocationA(
1232 UNKNOWN_SHLWAPI_1 base;
1236 res1 = SHLWAPI_1(pszUrl, &base);
1237 if (res1) return NULL; /* invalid scheme */
1239 /* if scheme is file: then never return pointer */
1240 if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL;
1242 /* Look for '#' and return its addr */
1243 return strchr(base.ap2, '#');
1246 /*************************************************************************
1247 * UrlGetLocationW [SHLWAPI.@]
1249 * See UrlGetLocationA for list of assumptions, bugs, and FIXMEs
1251 LPCWSTR WINAPI UrlGetLocationW(
1254 UNKNOWN_SHLWAPI_2 base;
1258 res1 = SHLWAPI_2(pszUrl, &base);
1259 if (res1) return NULL; /* invalid scheme */
1261 /* if scheme is file: then never return pointer */
1262 if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL;
1264 /* Look for '#' and return its addr */
1265 return strchrW(base.ap2, L'#');
1268 /*************************************************************************
1269 * UrlCompareA [SHLWAPI.@]
1271 INT WINAPI UrlCompareA(
1276 INT ret, len, len1, len2;
1279 return strcmp(pszUrl1, pszUrl2);
1280 len1 = strlen(pszUrl1);
1281 if (pszUrl1[len1-1] == L'/') len1--;
1282 len2 = strlen(pszUrl2);
1283 if (pszUrl2[len2-1] == L'/') len2--;
1285 return strncmp(pszUrl1, pszUrl2, len1);
1286 len = min(len1, len2);
1287 ret = strncmp(pszUrl1, pszUrl2, len);
1288 if (ret) return ret;
1289 if (len1 > len2) return 1;
1293 /*************************************************************************
1294 * UrlCompareW [SHLWAPI.@]
1296 INT WINAPI UrlCompareW(
1301 INT ret, len, len1, len2;
1304 return strcmpW(pszUrl1, pszUrl2);
1305 len1 = strlenW(pszUrl1);
1306 if (pszUrl1[len1-1] == L'/') len1--;
1307 len2 = strlenW(pszUrl2);
1308 if (pszUrl2[len2-1] == L'/') len2--;
1310 return strncmpW(pszUrl1, pszUrl2, len1);
1311 len = min(len1, len2);
1312 ret = strncmpW(pszUrl1, pszUrl2, len);
1313 if (ret) return ret;
1314 if (len1 > len2) return 1;
1318 /*************************************************************************
1319 * HashData [SHLWAPI.@]
1321 * Hash an input block into a variable sized digest.
1323 BOOL WINAPI HashData(const unsigned char *lpSrc, INT nSrcLen,
1324 unsigned char *lpDest, INT nDestLen)
1326 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1328 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1329 IsBadWritePtr(lpDest, nDestLen))
1332 while (destCount >= 0)
1334 lpDest[destCount] = (destCount & 0xff);
1338 while (srcCount >= 0)
1340 destCount = nDestLen - 1;
1341 while (destCount >= 0)
1343 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1351 /*************************************************************************
1352 * UrlHashA [SHLWAPI.@]
1354 * Hash an ASCII URL.
1356 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1358 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1359 return E_INVALIDARG;
1361 HashData(pszUrl, strlen(pszUrl), lpDest, nDestLen);
1365 /*************************************************************************
1366 * UrlHashW [SHLWAPI.@]
1370 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1372 char szUrl[MAX_PATH];
1374 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1376 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1377 return E_INVALIDARG;
1379 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1380 * return the same digests for the same URL.
1382 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1383 HashData(szUrl, strlen(szUrl), lpDest, nDestLen);
1387 /*************************************************************************
1388 * UrlApplySchemeA [SHLWAPI.@]
1390 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1393 DWORD ret, len, len2;
1395 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1396 debugstr_a(pszIn), *pcchOut, dwFlags);
1398 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1399 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1400 out = in + INTERNET_MAX_URL_LENGTH;
1402 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1403 len = INTERNET_MAX_URL_LENGTH;
1405 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1406 if ((ret != S_OK) && (ret != S_FALSE)) {
1407 HeapFree(GetProcessHeap(), 0, in);
1411 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1412 if (len2 > *pcchOut) {
1414 HeapFree(GetProcessHeap(), 0, in);
1417 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1419 HeapFree(GetProcessHeap(), 0, in);
1423 HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1428 DWORD value_len, data_len, dwType;
1429 WCHAR reg_path[MAX_PATH];
1430 WCHAR value[MAX_PATH], data[MAX_PATH];
1433 MultiByteToWideChar(0, 0,
1434 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1435 -1, reg_path, MAX_PATH);
1436 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1438 while(value_len = data_len = MAX_PATH,
1439 RegEnumValueW(newkey, index, value, &value_len,
1440 0, &dwType, (LPVOID)data, &data_len) == 0) {
1441 TRACE("guess %d %s is %s\n",
1442 index, debugstr_w(value), debugstr_w(data));
1445 for(i=0; i<value_len; i++) {
1448 /* remember that TRUE is not-equal */
1449 j = ChrCmpIW(Wxx, Wyy);
1452 if ((i == value_len) && !j) {
1453 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1454 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1455 RegCloseKey(newkey);
1458 strcpyW(pszOut, data);
1459 strcatW(pszOut, pszIn);
1460 *pcchOut = strlenW(pszOut);
1461 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1462 RegCloseKey(newkey);
1467 RegCloseKey(newkey);
1471 HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1474 DWORD data_len, dwType;
1475 WCHAR reg_path[MAX_PATH];
1476 WCHAR value[MAX_PATH], data[MAX_PATH];
1478 /* get and prepend default */
1479 MultiByteToWideChar(0, 0,
1480 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1481 -1, reg_path, MAX_PATH);
1482 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1483 data_len = MAX_PATH;
1486 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1487 RegCloseKey(newkey);
1488 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1489 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1492 strcpyW(pszOut, data);
1493 strcatW(pszOut, pszIn);
1494 *pcchOut = strlenW(pszOut);
1495 TRACE("used default %s\n", debugstr_w(pszOut));
1499 /*************************************************************************
1500 * UrlApplySchemeW [SHLWAPI.@]
1502 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1504 UNKNOWN_SHLWAPI_2 in_scheme;
1508 TRACE("(in %s, out size %ld, flags %08lx)\n",
1509 debugstr_w(pszIn), *pcchOut, dwFlags);
1511 if (dwFlags & URL_APPLY_GUESSFILE) {
1512 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1513 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1514 strcpyW(pszOut, pszIn);
1515 *pcchOut = strlenW(pszOut);
1519 in_scheme.size = 24;
1520 /* See if the base has a scheme */
1521 res1 = SHLWAPI_2(pszIn, &in_scheme);
1523 /* no scheme in input, need to see if we need to guess */
1524 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1525 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1530 /* we have a scheme, see if valid (known scheme) */
1531 if (in_scheme.fcncde) {
1532 /* have valid scheme, so just copy and exit */
1533 if (strlenW(pszIn) + 1 > *pcchOut) {
1534 *pcchOut = strlenW(pszIn) + 1;
1537 strcpyW(pszOut, pszIn);
1538 *pcchOut = strlenW(pszOut);
1539 TRACE("valid scheme, returing copy\n");
1544 /* If we are here, then either invalid scheme,
1545 * or no scheme and can't/failed guess.
1547 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1549 (dwFlags & URL_APPLY_DEFAULT)) {
1550 /* find and apply default scheme */
1551 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1554 /* just copy and give proper return code */
1555 if (strlenW(pszIn) + 1 > *pcchOut) {
1556 *pcchOut = strlenW(pszIn) + 1;
1559 strcpyW(pszOut, pszIn);
1560 *pcchOut = strlenW(pszOut);
1561 TRACE("returning copy, left alone\n");
1565 /*************************************************************************
1566 * UrlIsA [SHLWAPI.@]
1568 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1570 UNKNOWN_SHLWAPI_1 base;
1577 res1 = SHLWAPI_1(pszUrl, &base);
1578 if (res1) return FALSE; /* invalid scheme */
1579 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1580 /* has scheme followed by 2 '/' */
1585 case URLIS_NOHISTORY:
1587 case URLIS_APPLIABLE:
1588 case URLIS_DIRECTORY:
1589 case URLIS_HASQUERY:
1591 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1596 /*************************************************************************
1597 * UrlIsW [SHLWAPI.@]
1599 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1601 UNKNOWN_SHLWAPI_2 base;
1608 res1 = SHLWAPI_2(pszUrl, &base);
1609 if (res1) return FALSE; /* invalid scheme */
1610 if ((*base.ap2 == L'/') && (*(base.ap2+1) == L'/'))
1611 /* has scheme followed by 2 '/' */
1616 case URLIS_NOHISTORY:
1618 case URLIS_APPLIABLE:
1619 case URLIS_DIRECTORY:
1620 case URLIS_HASQUERY:
1622 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1627 /*************************************************************************
1628 * UrlIsNoHistoryA [SHLWAPI.@]
1630 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1632 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1635 /*************************************************************************
1636 * UrlIsNoHistoryW [SHLWAPI.@]
1638 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1640 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1643 /*************************************************************************
1644 * UrlIsOpaqueA [SHLWAPI.@]
1646 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1648 return UrlIsA(pszUrl, URLIS_OPAQUE);
1651 /*************************************************************************
1652 * UrlIsOpaqueW [SHLWAPI.@]
1654 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1656 return UrlIsW(pszUrl, URLIS_OPAQUE);
1659 /*************************************************************************
1660 * Scans for characters of type "type" and when not matching found,
1661 * returns pointer to it and length in size.
1663 * Characters tested based on RFC 1738
1665 LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1667 static DWORD alwayszero = 0;
1676 if ( (islowerW(*start) && isalphaW(*start)) ||
1691 if ( isalphaW(*start) ||
1693 /* user/password only characters */
1698 /* *extra* characters */
1701 (*start == L'\'') ||
1705 /* *safe* characters */
1713 } else if (*start == L'%') {
1714 if (isxdigitW(*(start+1)) &&
1715 isxdigitW(*(start+2))) {
1727 if (isdigitW(*start)) {
1738 if (isalnumW(*start) ||
1740 (*start == L'.') ) {
1749 FIXME("unknown type %d\n", type);
1750 return (LPWSTR)&alwayszero;
1752 /* TRACE("scanned %ld characters next char %p<%c>\n",
1753 *size, start, *start); */
1757 /*************************************************************************
1758 * Attempt to parse URL into pieces.
1760 LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1764 memset(pl, 0, sizeof(WINE_PARSE_URL));
1765 pl->pScheme = pszUrl;
1766 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1767 if (!*work || (*work != L':')) goto ERROR;
1769 if ((*work != L'/') || (*(work+1) != L'/')) goto ERROR;
1770 pl->pUserName = work + 2;
1771 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1772 if (*work == L':' ) {
1773 /* parse password */
1775 pl->pPassword = work;
1776 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1777 if (*work != L'@') {
1778 /* what we just parsed must be the hostname and port
1779 * so reset pointers and clear then let it parse */
1780 pl->szUserName = pl->szPassword = 0;
1781 work = pl->pUserName - 1;
1782 pl->pUserName = pl->pPassword = 0;
1784 } else if (*work == L'@') {
1788 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1789 /* what was parsed was hostname, so reset pointers and let it parse */
1790 pl->szUserName = pl->szPassword = 0;
1791 work = pl->pUserName - 1;
1792 pl->pUserName = pl->pPassword = 0;
1795 /* now start parsing hostname or hostnumber */
1797 pl->pHostName = work;
1798 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1799 if (*work == L':') {
1803 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1805 if (*work == L'/') {
1806 /* see if query string */
1807 pl->pQuery = strchrW(work, L'?');
1808 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1810 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1811 pl->pScheme, pl->szScheme,
1812 pl->pUserName, pl->szUserName,
1813 pl->pPassword, pl->szPassword,
1814 pl->pHostName, pl->szHostName,
1815 pl->pPort, pl->szPort,
1816 pl->pQuery, pl->szQuery);
1819 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1820 return E_INVALIDARG;
1823 /*************************************************************************
1824 * UrlGetPartA [SHLWAPI.@]
1826 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1827 DWORD dwPart, DWORD dwFlags)
1830 DWORD ret, len, len2;
1832 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1833 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1834 out = in + INTERNET_MAX_URL_LENGTH;
1836 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1838 len = INTERNET_MAX_URL_LENGTH;
1839 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
1842 HeapFree(GetProcessHeap(), 0, in);
1846 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
1847 if (len2 > *pcchOut) {
1849 HeapFree(GetProcessHeap(), 0, in);
1852 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1854 HeapFree(GetProcessHeap(), 0, in);
1858 /*************************************************************************
1859 * UrlGetPartW [SHLWAPI.@]
1861 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
1862 DWORD dwPart, DWORD dwFlags)
1866 DWORD size, schsize;
1867 LPCWSTR addr, schaddr;
1870 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
1871 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
1873 ret = URL_ParseUrl(pszIn, &pl);
1875 schaddr = pl.pScheme;
1876 schsize = pl.szScheme;
1879 case URL_PART_SCHEME:
1880 if (!pl.szScheme) return E_INVALIDARG;
1885 case URL_PART_HOSTNAME:
1886 if (!pl.szHostName) return E_INVALIDARG;
1887 addr = pl.pHostName;
1888 size = pl.szHostName;
1891 case URL_PART_USERNAME:
1892 if (!pl.szUserName) return E_INVALIDARG;
1893 addr = pl.pUserName;
1894 size = pl.szUserName;
1897 case URL_PART_PASSWORD:
1898 if (!pl.szPassword) return E_INVALIDARG;
1899 addr = pl.pPassword;
1900 size = pl.szPassword;
1904 if (!pl.szPort) return E_INVALIDARG;
1909 case URL_PART_QUERY:
1910 if (!pl.szQuery) return E_INVALIDARG;
1916 return E_INVALIDARG;
1919 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
1920 if (*pcchOut < size + schsize + 2) {
1921 *pcchOut = size + schsize + 2;
1924 strncpyW(pszOut, schaddr, schsize);
1925 work = pszOut + schsize;
1927 strncpyW(work+1, addr, size);
1928 *pcchOut = size + schsize + 1;
1933 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
1934 strncpyW(pszOut, addr, size);
1936 work = pszOut + size;
1939 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
1944 /*************************************************************************
1945 * PathIsURLA [SHLWAPI.@]
1947 * Check if the given path is a URL.
1950 * lpszPath [I] Path to check.
1953 * TRUE if lpszPath is a URL.
1954 * FALSE if lpszPath is NULL or not a URL.
1956 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
1958 UNKNOWN_SHLWAPI_1 base;
1961 if (!lpstrPath || !*lpstrPath) return FALSE;
1964 base.size = sizeof(base);
1965 res1 = SHLWAPI_1(lpstrPath, &base);
1966 return (base.fcncde > 0);
1969 /*************************************************************************
1970 * PathIsURLW [SHLWAPI.@]
1972 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
1974 UNKNOWN_SHLWAPI_2 base;
1977 if (!lpstrPath || !*lpstrPath) return FALSE;
1980 base.size = sizeof(base);
1981 res1 = SHLWAPI_2(lpstrPath, &base);
1982 return (base.fcncde > 0);
1985 /*************************************************************************
1986 * UrlCreateFromPathA [SHLWAPI.@]
1988 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
1990 DWORD nCharBeforeColon = 0;
1992 DWORD dwChRequired = 0;
1993 LPSTR pszNewUrl = NULL;
1994 LPCSTR pszConstPointer = NULL;
1995 LPSTR pszPointer = NULL;
1999 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved);
2001 /* Validate arguments */
2002 if (dwReserved != 0)
2004 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved);
2005 return E_INVALIDARG;
2007 if (!pszUrl || !pcchUrl || !pszUrl)
2009 ERR("Invalid argument\n");
2010 return E_INVALIDARG;
2013 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2015 if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) ||
2016 *pszConstPointer == '.' || *pszConstPointer == '-')
2020 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2022 dwChRequired = lstrlenA(pszPath);
2023 if (dwChRequired > *pcchUrl)
2025 *pcchUrl = dwChRequired;
2030 *pcchUrl = dwChRequired;
2031 StrCpyA(pszUrl, pszPath);
2035 /* then must need converting to file: format */
2037 /* Strip off leading slashes */
2038 while (*pszPath == '\\' || *pszPath == '/')
2044 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2045 TRACE("pszUrl: %s\n", debugstr_a(pszPath));
2046 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1);
2047 ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2048 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl));
2049 TRACE("%ld\n", dwChRequired);
2050 if (ret != E_POINTER && FAILED(ret))
2052 dwChRequired += 5; /* "file:" */
2053 if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':'))
2055 dwChRequired += 3; /* "///" */
2061 case 0: /* no slashes */
2063 case 2: /* two slashes */
2070 default: /* three slashes */
2075 if (dwChRequired > *pcchUrl)
2077 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2078 StrCpyA(pszUrl, "file:");
2079 pszPointer = pszUrl + lstrlenA(pszUrl);
2080 for (i=0; i < nSlashes; i++)
2085 StrCpyA(pszPointer, pszNewUrl);
2086 TRACE("<- %s\n", debugstr_a(pszUrl));
2090 /*************************************************************************
2091 * UrlCreateFromPathA [SHLWAPI.@]
2093 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2095 DWORD nCharBeforeColon = 0;
2097 DWORD dwChRequired = 0;
2098 LPWSTR pszNewUrl = NULL;
2099 LPCWSTR pszConstPointer = NULL;
2100 LPWSTR pszPointer = NULL;
2104 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2106 /* Validate arguments */
2107 if (dwReserved != 0)
2108 return E_INVALIDARG;
2109 if (!pszUrl || !pcchUrl || !pszUrl)
2110 return E_INVALIDARG;
2112 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2114 if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) ||
2115 *pszConstPointer == '.' || *pszConstPointer == '-')
2119 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2121 dwChRequired = lstrlenW(pszPath);
2122 *pcchUrl = dwChRequired;
2123 if (dwChRequired > *pcchUrl)
2127 StrCpyW(pszUrl, pszPath);
2131 /* then must need converting to file: format */
2133 /* Strip off leading slashes */
2134 while (*pszPath == '\\' || *pszPath == '/')
2140 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2141 ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2142 if (ret != E_POINTER && FAILED(ret))
2144 dwChRequired += 5; /* "file:" */
2145 if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':'))
2147 dwChRequired += 3; /* "///" */
2153 case 0: /* no slashes */
2155 case 2: /* two slashes */
2162 default: /* three slashes */
2167 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2168 if (dwChRequired > *pcchUrl)
2170 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR));
2171 StrCpyW(pszNewUrl, fileW);
2172 pszPointer = pszNewUrl + 4;
2175 for (i=0; i < nSlashes; i++)
2180 StrCpyW(pszPointer, pszPath);
2181 StrCpyW(pszUrl, pszNewUrl);