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 * Parse a Url into its constituent parts.
266 * y [O] Undocumented structure holding the parsed information
269 * Success: S_OK. y contains the parsed Url details.
270 * Failure: An HRESULT error code.
272 DWORD WINAPI SHLWAPI_1 (LPCSTR x, UNKNOWN_SHLWAPI_1 *y)
275 const SHL_2_inet_scheme *inet_pro;
277 y->fcncde = URL_SCHEME_INVALID;
278 if (y->size != 0x18) return E_INVALIDARG;
279 /* FIXME: leading white space generates error of 0x80041001 which
282 if (*x <= ' ') return 0x80041001;
297 /* check for no scheme in string start */
298 /* (apparently schemes *must* be larger than a single character) */
299 if ((*x == '\0') || (y->sizep1 <= 1)) {
304 /* found scheme, set length of remainder */
305 y->sizep2 = lstrlenA(y->ap2);
307 /* see if known scheme and return indicator number */
308 y->fcncde = URL_SCHEME_UNKNOWN;
309 inet_pro = shlwapi_schemes;
310 while (inet_pro->scheme_name) {
311 if (!strncasecmp(inet_pro->scheme_name, y->ap1,
312 min(y->sizep1, lstrlenA(inet_pro->scheme_name)))) {
313 y->fcncde = inet_pro->scheme_number;
321 /*************************************************************************
324 * Unicode version of SHLWAPI_1.
326 DWORD WINAPI SHLWAPI_2 (LPCWSTR x, UNKNOWN_SHLWAPI_2 *y)
329 const SHL_2_inet_scheme *inet_pro;
333 y->fcncde = URL_SCHEME_INVALID;
334 if (y->size != 0x18) return E_INVALIDARG;
335 /* FIXME: leading white space generates error of 0x80041001 which
338 if (*x <= L' ') return 0x80041001;
353 /* check for no scheme in string start */
354 /* (apparently schemes *must* be larger than a single character) */
355 if ((*x == L'\0') || (y->sizep1 <= 1)) {
360 /* found scheme, set length of remainder */
361 y->sizep2 = lstrlenW(y->ap2);
363 /* see if known scheme and return indicator number */
364 len = WideCharToMultiByte(0, 0, y->ap1, y->sizep1, 0, 0, 0, 0);
365 cmpstr = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len+1);
366 WideCharToMultiByte(0, 0, y->ap1, y->sizep1, cmpstr, len+1, 0, 0);
367 y->fcncde = URL_SCHEME_UNKNOWN;
368 inet_pro = shlwapi_schemes;
369 while (inet_pro->scheme_name) {
370 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
371 min(len, lstrlenA(inet_pro->scheme_name)))) {
372 y->fcncde = inet_pro->scheme_number;
377 HeapFree(GetProcessHeap(), 0, cmpstr);
381 /*************************************************************************
382 * UrlCanonicalizeA [SHLWAPI.@]
384 * Canonicalize a Url.
387 * pszUrl [I] Url to cCanonicalize
388 * pszCanonicalized [O] Destination for converted Url.
389 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
390 * dwFlags [I] Flags controlling the conversion.
393 * Success: S_OK. The pszCanonicalized contains the converted Url.
394 * Failure: E_POINTER, if *pcchCanonicalized is too small.
396 * MSDN is wrong (at 10/30/01 - go figure). This should support the
397 * following flags: GLA
398 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
399 *| URL_ESCAPE_SPACES_ONLY 0x04000000
400 *| URL_ESCAPE_PERCENT 0x00001000
401 *| URL_ESCAPE_UNSAFE 0x10000000
402 *| URL_UNESCAPE 0x10000000
403 *| URL_DONT_SIMPLIFY 0x08000000
404 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
406 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
407 LPDWORD pcchCanonicalized, DWORD dwFlags)
409 LPWSTR base, canonical;
410 DWORD ret, len, len2;
412 TRACE("(%s %p %p 0x%08lx) using W version\n",
413 debugstr_a(pszUrl), pszCanonicalized,
414 pcchCanonicalized, dwFlags);
416 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
417 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
418 canonical = base + INTERNET_MAX_URL_LENGTH;
420 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
421 len = INTERNET_MAX_URL_LENGTH;
423 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
425 HeapFree(GetProcessHeap(), 0, base);
429 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
430 if (len2 > *pcchCanonicalized) {
431 *pcchCanonicalized = len;
432 HeapFree(GetProcessHeap(), 0, base);
435 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
436 *pcchCanonicalized, 0, 0);
437 *pcchCanonicalized = len2;
438 HeapFree(GetProcessHeap(), 0, base);
442 /*************************************************************************
443 * UrlCanonicalizeW [SHLWAPI.@]
445 * See UrlCanonicalizeA.
447 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
448 LPDWORD pcchCanonicalized, DWORD dwFlags)
452 LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
453 INT nLen, nByteLen, state;
455 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
456 pcchCanonicalized, dwFlags);
458 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
459 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
461 if (dwFlags & URL_DONT_SIMPLIFY)
462 memcpy(lpszUrlCpy, pszUrl, nByteLen);
468 * 1 have 2[+] alnum 2,3
469 * 2 have scheme (found :) 4,6,3
470 * 3 failed (no location)
472 * 5 have 1[+] alnum 6,3
473 * 6 have location (found /) save root location
476 wk1 = (LPWSTR)pszUrl;
482 if (!isalnumW(*wk1)) {state = 3; break;}
484 if (!isalnumW(*wk1)) {state = 3; break;}
490 if (*wk1++ == L':') state = 2;
493 if (*wk1 != L'/') {state = 3; break;}
495 if (*wk1 != L'/') {state = 6; break;}
505 if (!isalnumW(*wk1) && (*wk1 != L'-')) {state = 3; break;}
506 while(isalnumW(*wk1) || (*wk1 == L'-')) *wk2++ = *wk1++;
510 if (*wk1 != L'/') {state = 3; break;}
515 /* Now at root location, cannot back up any more. */
516 /* "root" will point at the '/' */
519 TRACE("wk1=%c\n", (CHAR)*wk1);
520 mp = strchrW(wk1, L'/');
528 strncpyW(wk2, wk1, nLen);
532 TRACE("found '/.'\n");
533 if (*(wk1+1) == L'/') {
534 /* case of /./ -> skip the ./ */
537 else if (*(wk1+1) == L'.') {
538 /* found /.. look for next / */
539 TRACE("found '/..'\n");
540 if (*(wk1+2) == L'/') {
541 /* case /../ -> need to backup wk2 */
542 TRACE("found '/../'\n");
543 *(wk2-1) = L'\0'; /* set end of string */
544 mp = strrchrW(root, L'/');
545 if (mp && (mp >= root)) {
546 /* found valid backup point */
551 /* did not find point, restore '/' */
561 FIXME("how did we get here - state=%d\n", state);
566 TRACE("Simplified, orig <%s>, simple <%s>\n",
567 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
570 if(dwFlags & URL_UNESCAPE)
571 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
573 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
574 URL_ESCAPE_SPACES_ONLY |
576 URL_DONT_ESCAPE_EXTRA_INFO |
577 URL_ESCAPE_SEGMENT_ONLY ))) {
578 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
579 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
581 } else { /* No escaping needed, just copy the string */
582 nLen = lstrlenW(lpszUrlCpy);
583 if(nLen < *pcchCanonicalized)
584 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
589 *pcchCanonicalized = nLen;
592 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
595 TRACE("result %s\n", debugstr_w(pszCanonicalized));
600 /*************************************************************************
601 * UrlCombineA [SHLWAPI.@]
603 * Uses the W version to do job.
605 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
606 LPSTR pszCombined, LPDWORD pcchCombined,
609 LPWSTR base, relative, combined;
610 DWORD ret, len, len2;
612 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
613 debugstr_a(pszBase),debugstr_a(pszRelative),
614 *pcchCombined,dwFlags);
616 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
617 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
618 relative = base + INTERNET_MAX_URL_LENGTH;
619 combined = relative + INTERNET_MAX_URL_LENGTH;
621 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
622 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
623 len = INTERNET_MAX_URL_LENGTH;
625 ret = UrlCombineW(base, relative, combined, &len, dwFlags);
627 HeapFree(GetProcessHeap(), 0, base);
631 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
632 if (len2 > *pcchCombined) {
633 *pcchCombined = len2;
634 HeapFree(GetProcessHeap(), 0, base);
637 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, *pcchCombined,
639 *pcchCombined = len2;
640 HeapFree(GetProcessHeap(), 0, base);
644 /*************************************************************************
645 * UrlCombineW [SHLWAPI.@]
647 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
648 LPWSTR pszCombined, LPDWORD pcchCombined,
651 UNKNOWN_SHLWAPI_2 base, relative;
652 DWORD myflags, sizeloc = 0;
653 DWORD len, res1, res2, process_case = 0;
654 LPWSTR work, preliminary, mbase, mrelative;
655 WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
656 WCHAR single_slash[] = {'/','\0'};
659 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
660 debugstr_w(pszBase),debugstr_w(pszRelative),
661 *pcchCombined,dwFlags);
666 /* Get space for duplicates of the input and the output */
667 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
669 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
670 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
671 *preliminary = L'\0';
673 /* Canonicalize the base input prior to looking for the scheme */
674 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
675 len = INTERNET_MAX_URL_LENGTH;
676 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
678 /* Canonicalize the relative input prior to looking for the scheme */
679 len = INTERNET_MAX_URL_LENGTH;
680 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
682 /* See if the base has a scheme */
683 res1 = SHLWAPI_2(mbase, &base);
685 /* if pszBase has no scheme, then return pszRelative */
686 TRACE("no scheme detected in Base\n");
691 /* get size of location field (if it exists) */
692 work = (LPWSTR)base.ap2;
694 if (*work++ == L'/') {
695 if (*work++ == L'/') {
696 /* At this point have start of location and
697 * it ends at next '/' or end of string.
699 while(*work && (*work != L'/')) work++;
700 sizeloc = work - base.ap2;
704 /* Change .sizep2 to not have the last leaf in it,
705 * Note: we need to start after the location (if it exists)
707 work = strrchrW((base.ap2+sizeloc), L'/');
709 len = work - base.ap2 + 1;
714 * .ap2 points to location (starting with '//')
715 * .sizep2 length of location (above) and rest less the last
717 * sizeloc length of location (above) up to but not including
721 res2 = SHLWAPI_2(mrelative, &relative);
723 /* no scheme in pszRelative */
724 TRACE("no scheme detected in Relative\n");
725 relative.ap2 = mrelative; /* case 3,4,5 depends on this */
726 relative.sizep2 = strlenW(mrelative);
727 if (*pszRelative == L':') {
728 /* case that is either left alone or uses pszBase */
729 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
736 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
737 /* case that becomes "file:///" */
738 strcpyW(preliminary, myfilestr);
742 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
743 /* pszRelative has location and rest */
747 if (*mrelative == L'/') {
748 /* case where pszRelative is root to location */
752 process_case = (*base.ap2 == L'/') ? 5 : 3;
756 /* handle cases where pszRelative has scheme */
757 if ((base.sizep1 == relative.sizep1) &&
758 (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) {
760 /* since the schemes are the same */
761 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
762 /* case where pszRelative replaces location and following */
766 if (*relative.ap2 == L'/') {
767 /* case where pszRelative is root to location */
771 /* case where scheme is followed by document path */
775 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
776 /* case where pszRelative replaces scheme, location,
777 * and following and handles PLUGGABLE
784 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
788 switch (process_case) {
791 * Return pszRelative appended to what ever is in pszCombined,
792 * (which may the string "file:///"
794 len = strlenW(mrelative) + strlenW(preliminary);
795 if (len+1 > *pcchCombined) {
800 strcatW(preliminary, mrelative);
804 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
805 * and pszRelative starts with "//", then append a "/"
807 len = strlenW(mrelative) + 1;
808 if (len+1 > *pcchCombined) {
813 strcpyW(preliminary, mrelative);
814 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
815 URL_JustLocation(relative.ap2))
816 strcatW(preliminary, single_slash);
820 * Return the pszBase scheme with pszRelative. Basicly
821 * keeps the scheme and replaces the domain and following.
823 len = base.sizep1 + 1 + relative.sizep2 + 1;
824 if (len+1 > *pcchCombined) {
829 strncpyW(preliminary, base.ap1, base.sizep1 + 1);
830 work = preliminary + base.sizep1 + 1;
831 strcpyW(work, relative.ap2);
832 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
833 URL_JustLocation(relative.ap2))
834 strcatW(work, single_slash);
838 * Return the pszBase scheme and location but everything
839 * after the location is pszRelative. (Replace document
842 len = base.sizep1 + 1 + sizeloc + relative.sizep2 + 1;
843 if (len+1 > *pcchCombined) {
848 strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc);
849 work = preliminary + base.sizep1 + 1 + sizeloc;
850 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
852 strcpyW(work, relative.ap2);
856 * Return the pszBase without its document (if any) and
857 * append pszRelative after its scheme.
859 len = base.sizep1 + 1 + base.sizep2 + relative.sizep2;
860 if (len+1 > *pcchCombined) {
865 strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2);
866 work = preliminary + base.sizep1+1+base.sizep2 - 1;
869 strcpyW(work, relative.ap2);
873 FIXME("How did we get here????? process_case=%ld\n", process_case);
879 * Now that the combining is done, process the escape options if
880 * necessary, otherwise just copy the string.
882 myflags = dwFlags & (URL_ESCAPE_PERCENT |
883 URL_ESCAPE_SPACES_ONLY |
884 URL_DONT_ESCAPE_EXTRA_INFO |
885 URL_ESCAPE_SEGMENT_ONLY);
887 ret = UrlEscapeW(preliminary, pszCombined,
888 pcchCombined, myflags);
890 len = (strlenW(preliminary) + 1) * sizeof(WCHAR);
891 memcpy(pszCombined, preliminary, len);
892 *pcchCombined = strlenW(preliminary);
894 TRACE("return-%ld len=%ld, %s\n",
895 process_case, *pcchCombined, debugstr_w(pszCombined));
897 HeapFree(GetProcessHeap(), 0, preliminary);
901 /*************************************************************************
902 * UrlEscapeA [SHLWAPI.@]
904 * Converts unsafe characters into their escape sequences.
907 * The converted string is returned in pszEscaped if the buffer size
908 * (which should be supplied in pcchEscaped) is large enough, in this
909 * case the function returns S_OK and pcchEscaped contains the length
910 * of the escaped string. If the buffer is not large enough the
911 * function returns E_POINTER and pcchEscaped contains the required
912 * buffer size (including room for the '\0').
914 * By default the function stops converting at the first '?' or
915 * '#'. [MSDN says differently]. If URL_ESCAPE_SPACES_ONLY flag is set
916 * then only spaces are converted, but the conversion continues past a
920 * Have now implemented the following flags:
921 *| URL_ESCAPE_SPACES_ONLY
922 *| URL_DONT_ESCAPE_EXTRA_INFO
923 *| URL_ESCAPE_SEGMENT_ONLY
924 *| URL_ESCAPE_PERCENT
925 * Initial testing seems to indicate that this is now working like
926 * native shlwapi version 5. Note that these functions did not work
927 * well (or at all) in shlwapi version 4.
930 HRESULT WINAPI UrlEscapeA(
937 DWORD needed = 0, ret;
938 BOOL stop_escaping = FALSE;
939 char next[3], *dst = pszEscaped;
942 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
943 *pcchEscaped, dwFlags);
945 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
946 URL_ESCAPE_SEGMENT_ONLY |
947 URL_DONT_ESCAPE_EXTRA_INFO |
949 FIXME("Unimplemented flags: %08lx\n", dwFlags);
952 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
953 /* if SPACES_ONLY specified, reset the other controls */
954 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
956 URL_ESCAPE_SEGMENT_ONLY);
959 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
960 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
962 for(src = pszUrl; *src; src++) {
963 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
964 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
965 (*src == '#' || *src == '?'))
966 stop_escaping = TRUE;
968 if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
969 /* TRACE("escaping %c\n", *src); */
971 next[1] = hexDigits[(*src >> 4) & 0xf];
972 next[2] = hexDigits[*src & 0xf];
975 /* TRACE("passing %c\n", *src); */
980 if(needed + len <= *pcchEscaped) {
981 memcpy(dst, next, len);
987 if(needed < *pcchEscaped) {
991 needed++; /* add one for the '\0' */
994 *pcchEscaped = needed;
998 /*************************************************************************
999 * UrlEscapeW [SHLWAPI.@]
1003 HRESULT WINAPI UrlEscapeW(
1006 LPDWORD pcchEscaped,
1010 DWORD needed = 0, ret;
1011 BOOL stop_escaping = FALSE;
1012 WCHAR next[5], *dst = pszEscaped;
1015 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
1016 pcchEscaped, dwFlags);
1018 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1019 URL_ESCAPE_SEGMENT_ONLY |
1020 URL_DONT_ESCAPE_EXTRA_INFO |
1021 URL_ESCAPE_PERCENT))
1022 FIXME("Unimplemented flags: %08lx\n", dwFlags);
1025 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1026 /* if SPACES_ONLY specified, reset the other controls */
1027 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1028 URL_ESCAPE_PERCENT |
1029 URL_ESCAPE_SEGMENT_ONLY);
1032 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1033 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1035 for(src = pszUrl; *src; src++) {
1037 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1038 * (*src == L'#' || *src == L'?'))
1039 * stop_escaping = TRUE;
1041 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1042 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1043 (*src == L'#' || *src == L'?'))
1044 stop_escaping = TRUE;
1046 if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
1047 /* TRACE("escaping %c\n", *src); */
1050 * I would have assumed that the W form would escape
1051 * the character with 4 hex digits (or even 8),
1052 * however, experiments show that native shlwapi escapes
1053 * with only 2 hex digits.
1054 * next[1] = hexDigits[(*src >> 12) & 0xf];
1055 * next[2] = hexDigits[(*src >> 8) & 0xf];
1056 * next[3] = hexDigits[(*src >> 4) & 0xf];
1057 * next[4] = hexDigits[*src & 0xf];
1060 next[1] = hexDigits[(*src >> 4) & 0xf];
1061 next[2] = hexDigits[*src & 0xf];
1064 /* TRACE("passing %c\n", *src); */
1069 if(needed + len <= *pcchEscaped) {
1070 memcpy(dst, next, len*sizeof(WCHAR));
1076 if(needed < *pcchEscaped) {
1080 needed++; /* add one for the '\0' */
1083 *pcchEscaped = needed;
1088 /*************************************************************************
1089 * UrlUnescapeA [SHLWAPI.@]
1091 * Converts escape sequences back to ordinary characters.
1094 * pszUrl [I/O] Url to convert
1095 * pszUnescaped [O] Destination for converted Url
1096 * pcchUnescaped [I/O] Size of output string
1097 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1100 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1101 * dwFlags includes URL_ESCAPE_INPLACE.
1102 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1103 * this case pcchUnescaped is set to the size required.
1105 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1106 * the first occurrence of either '?' or '#'.
1108 HRESULT WINAPI UrlUnescapeA(
1111 LPDWORD pcchUnescaped,
1118 BOOL stop_unescaping = FALSE;
1120 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1121 pcchUnescaped, dwFlags);
1123 if(dwFlags & URL_UNESCAPE_INPLACE)
1124 dst = (char*)pszUrl;
1128 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1129 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1130 (*src == '#' || *src == '?')) {
1131 stop_unescaping = TRUE;
1133 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1134 && stop_unescaping == FALSE) {
1137 memcpy(buf, src + 1, 2);
1139 ih = strtol(buf, NULL, 16);
1141 src += 2; /* Advance to end of escape */
1145 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1149 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1153 needed++; /* add one for the '\0' */
1156 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1157 *pcchUnescaped = needed;
1160 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1161 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1167 /*************************************************************************
1168 * UrlUnescapeW [SHLWAPI.@]
1172 HRESULT WINAPI UrlUnescapeW(
1174 LPWSTR pszUnescaped,
1175 LPDWORD pcchUnescaped,
1182 BOOL stop_unescaping = FALSE;
1184 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1185 pcchUnescaped, dwFlags);
1187 if(dwFlags & URL_UNESCAPE_INPLACE)
1188 dst = (WCHAR*)pszUrl;
1192 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1193 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1194 (*src == L'#' || *src == L'?')) {
1195 stop_unescaping = TRUE;
1197 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1198 && stop_unescaping == FALSE) {
1201 memcpy(buf, src + 1, 2*sizeof(WCHAR));
1203 ih = StrToIntW(buf);
1205 src += 2; /* Advance to end of escape */
1209 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1213 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1217 needed++; /* add one for the '\0' */
1220 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1221 *pcchUnescaped = needed;
1224 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1225 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1231 /*************************************************************************
1232 * UrlGetLocationA [SHLWAPI.@]
1234 * Get the location from a Url.
1237 * pszUrl [I] Url to get the location from
1240 * A pointer to the start of the location in pszUrl, or NULL if there is
1244 * MSDN (as of 2001-11-01) says that:
1245 * "The location is the segment of the URL starting with a ?
1247 * Neither V4 nor V5 of shlwapi.dll implement the '?' and always return
1250 * MSDN further states that:
1251 * "If a file URL has a query string, the returned string is
1252 * the query string."
1253 * In all test cases if the scheme starts with "fi" then a NULL is
1254 * returned. V5 gives the following results:
1255 *| NULL file://aa/b/cd#hohoh
1256 *| #hohoh http://aa/b/cd#hohoh
1257 *| NULL fi://aa/b/cd#hohoh
1258 *| #hohoh ff://aa/b/cd#hohoh
1260 LPCSTR WINAPI UrlGetLocationA(
1263 UNKNOWN_SHLWAPI_1 base;
1267 res1 = SHLWAPI_1(pszUrl, &base);
1268 if (res1) return NULL; /* invalid scheme */
1270 /* if scheme is file: then never return pointer */
1271 if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL;
1273 /* Look for '#' and return its addr */
1274 return strchr(base.ap2, '#');
1277 /*************************************************************************
1278 * UrlGetLocationW [SHLWAPI.@]
1280 * See UrlGetLocationA.
1282 LPCWSTR WINAPI UrlGetLocationW(
1285 UNKNOWN_SHLWAPI_2 base;
1289 res1 = SHLWAPI_2(pszUrl, &base);
1290 if (res1) return NULL; /* invalid scheme */
1292 /* if scheme is file: then never return pointer */
1293 if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL;
1295 /* Look for '#' and return its addr */
1296 return strchrW(base.ap2, L'#');
1299 /*************************************************************************
1300 * UrlCompareA [SHLWAPI.@]
1302 INT WINAPI UrlCompareA(
1307 INT ret, len, len1, len2;
1310 return strcmp(pszUrl1, pszUrl2);
1311 len1 = strlen(pszUrl1);
1312 if (pszUrl1[len1-1] == L'/') len1--;
1313 len2 = strlen(pszUrl2);
1314 if (pszUrl2[len2-1] == L'/') len2--;
1316 return strncmp(pszUrl1, pszUrl2, len1);
1317 len = min(len1, len2);
1318 ret = strncmp(pszUrl1, pszUrl2, len);
1319 if (ret) return ret;
1320 if (len1 > len2) return 1;
1324 /*************************************************************************
1325 * UrlCompareW [SHLWAPI.@]
1327 INT WINAPI UrlCompareW(
1332 INT ret, len, len1, len2;
1335 return strcmpW(pszUrl1, pszUrl2);
1336 len1 = strlenW(pszUrl1);
1337 if (pszUrl1[len1-1] == L'/') len1--;
1338 len2 = strlenW(pszUrl2);
1339 if (pszUrl2[len2-1] == L'/') len2--;
1341 return strncmpW(pszUrl1, pszUrl2, len1);
1342 len = min(len1, len2);
1343 ret = strncmpW(pszUrl1, pszUrl2, len);
1344 if (ret) return ret;
1345 if (len1 > len2) return 1;
1349 /*************************************************************************
1350 * HashData [SHLWAPI.@]
1352 * Hash an input block into a variable sized digest.
1355 * lpSrc [I] Input block
1356 * nSrcLen [I] Length of lpSrc
1357 * lpDest [I] Output for hash digest
1358 * nDestLen [I] Length of lpDest
1361 * Success: TRUE. lpDest is filled with the computed hash value.
1362 * Failure: FALSE, if any argument is invalid.
1364 BOOL WINAPI HashData(const unsigned char *lpSrc, INT nSrcLen,
1365 unsigned char *lpDest, INT nDestLen)
1367 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1369 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1370 IsBadWritePtr(lpDest, nDestLen))
1373 while (destCount >= 0)
1375 lpDest[destCount] = (destCount & 0xff);
1379 while (srcCount >= 0)
1381 destCount = nDestLen - 1;
1382 while (destCount >= 0)
1384 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1392 /*************************************************************************
1393 * UrlHashA [SHLWAPI.@]
1395 * Produce a Hash from a Url.
1398 * pszUrl [I] Url to hash
1399 * lpDest [O] Destinationh for hash
1400 * nDestLen [I] Length of lpDest
1403 * Success: S_OK. lpDest is filled with the computed hash value.
1404 * Failure: E_INVALIDARG, if any argument is invalid.
1406 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1408 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1409 return E_INVALIDARG;
1411 HashData(pszUrl, strlen(pszUrl), lpDest, nDestLen);
1415 /*************************************************************************
1416 * UrlHashW [SHLWAPI.@]
1420 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1422 char szUrl[MAX_PATH];
1424 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1426 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1427 return E_INVALIDARG;
1429 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1430 * return the same digests for the same URL.
1432 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1433 HashData(szUrl, strlen(szUrl), lpDest, nDestLen);
1437 /*************************************************************************
1438 * UrlApplySchemeA [SHLWAPI.@]
1440 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1443 DWORD ret, len, len2;
1445 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1446 debugstr_a(pszIn), *pcchOut, dwFlags);
1448 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1449 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1450 out = in + INTERNET_MAX_URL_LENGTH;
1452 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1453 len = INTERNET_MAX_URL_LENGTH;
1455 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1456 if ((ret != S_OK) && (ret != S_FALSE)) {
1457 HeapFree(GetProcessHeap(), 0, in);
1461 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1462 if (len2 > *pcchOut) {
1464 HeapFree(GetProcessHeap(), 0, in);
1467 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1469 HeapFree(GetProcessHeap(), 0, in);
1473 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1478 DWORD value_len, data_len, dwType;
1479 WCHAR reg_path[MAX_PATH];
1480 WCHAR value[MAX_PATH], data[MAX_PATH];
1483 MultiByteToWideChar(0, 0,
1484 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1485 -1, reg_path, MAX_PATH);
1486 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1488 while(value_len = data_len = MAX_PATH,
1489 RegEnumValueW(newkey, index, value, &value_len,
1490 0, &dwType, (LPVOID)data, &data_len) == 0) {
1491 TRACE("guess %d %s is %s\n",
1492 index, debugstr_w(value), debugstr_w(data));
1495 for(i=0; i<value_len; i++) {
1498 /* remember that TRUE is not-equal */
1499 j = ChrCmpIW(Wxx, Wyy);
1502 if ((i == value_len) && !j) {
1503 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1504 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1505 RegCloseKey(newkey);
1508 strcpyW(pszOut, data);
1509 strcatW(pszOut, pszIn);
1510 *pcchOut = strlenW(pszOut);
1511 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1512 RegCloseKey(newkey);
1517 RegCloseKey(newkey);
1521 HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1524 DWORD data_len, dwType;
1525 WCHAR reg_path[MAX_PATH];
1526 WCHAR value[MAX_PATH], data[MAX_PATH];
1528 /* get and prepend default */
1529 MultiByteToWideChar(0, 0,
1530 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1531 -1, reg_path, MAX_PATH);
1532 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1533 data_len = MAX_PATH;
1536 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1537 RegCloseKey(newkey);
1538 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1539 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1542 strcpyW(pszOut, data);
1543 strcatW(pszOut, pszIn);
1544 *pcchOut = strlenW(pszOut);
1545 TRACE("used default %s\n", debugstr_w(pszOut));
1549 /*************************************************************************
1550 * UrlApplySchemeW [SHLWAPI.@]
1552 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1554 UNKNOWN_SHLWAPI_2 in_scheme;
1558 TRACE("(in %s, out size %ld, flags %08lx)\n",
1559 debugstr_w(pszIn), *pcchOut, dwFlags);
1561 if (dwFlags & URL_APPLY_GUESSFILE) {
1562 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1563 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1564 strcpyW(pszOut, pszIn);
1565 *pcchOut = strlenW(pszOut);
1569 in_scheme.size = 24;
1570 /* See if the base has a scheme */
1571 res1 = SHLWAPI_2(pszIn, &in_scheme);
1573 /* no scheme in input, need to see if we need to guess */
1574 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1575 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1580 /* we have a scheme, see if valid (known scheme) */
1581 if (in_scheme.fcncde) {
1582 /* have valid scheme, so just copy and exit */
1583 if (strlenW(pszIn) + 1 > *pcchOut) {
1584 *pcchOut = strlenW(pszIn) + 1;
1587 strcpyW(pszOut, pszIn);
1588 *pcchOut = strlenW(pszOut);
1589 TRACE("valid scheme, returing copy\n");
1594 /* If we are here, then either invalid scheme,
1595 * or no scheme and can't/failed guess.
1597 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1599 (dwFlags & URL_APPLY_DEFAULT)) {
1600 /* find and apply default scheme */
1601 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1604 /* just copy and give proper return code */
1605 if (strlenW(pszIn) + 1 > *pcchOut) {
1606 *pcchOut = strlenW(pszIn) + 1;
1609 strcpyW(pszOut, pszIn);
1610 *pcchOut = strlenW(pszOut);
1611 TRACE("returning copy, left alone\n");
1615 /*************************************************************************
1616 * UrlIsA [SHLWAPI.@]
1618 * Determine if a Url is of a certain class.
1621 * pszUrl [I] Url to check
1622 * Urlis [I] URLIS_ constant from "shlwapi.h"
1625 * TRUE if pszUrl belongs to the class type in Urlis.
1628 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1630 UNKNOWN_SHLWAPI_1 base;
1637 res1 = SHLWAPI_1(pszUrl, &base);
1638 if (res1) return FALSE; /* invalid scheme */
1639 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1640 /* has scheme followed by 2 '/' */
1645 case URLIS_NOHISTORY:
1647 case URLIS_APPLIABLE:
1648 case URLIS_DIRECTORY:
1649 case URLIS_HASQUERY:
1651 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1656 /*************************************************************************
1657 * UrlIsW [SHLWAPI.@]
1661 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1663 UNKNOWN_SHLWAPI_2 base;
1670 res1 = SHLWAPI_2(pszUrl, &base);
1671 if (res1) return FALSE; /* invalid scheme */
1672 if ((*base.ap2 == L'/') && (*(base.ap2+1) == L'/'))
1673 /* has scheme followed by 2 '/' */
1678 case URLIS_NOHISTORY:
1680 case URLIS_APPLIABLE:
1681 case URLIS_DIRECTORY:
1682 case URLIS_HASQUERY:
1684 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1689 /*************************************************************************
1690 * UrlIsNoHistoryA [SHLWAPI.@]
1692 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1694 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1697 /*************************************************************************
1698 * UrlIsNoHistoryW [SHLWAPI.@]
1700 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1702 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1705 /*************************************************************************
1706 * UrlIsOpaqueA [SHLWAPI.@]
1708 * Determine if a Url is opaque.
1711 * pszUrl [I] Url to check
1714 * TRUE if pszUrl is opaque,
1718 * An opaque Url is one that does not start with "<protocol>://".
1720 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1722 return UrlIsA(pszUrl, URLIS_OPAQUE);
1725 /*************************************************************************
1726 * UrlIsOpaqueW [SHLWAPI.@]
1730 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1732 return UrlIsW(pszUrl, URLIS_OPAQUE);
1735 /*************************************************************************
1736 * Scans for characters of type "type" and when not matching found,
1737 * returns pointer to it and length in size.
1739 * Characters tested based on RFC 1738
1741 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1743 static DWORD alwayszero = 0;
1752 if ( (islowerW(*start) && isalphaW(*start)) ||
1767 if ( isalphaW(*start) ||
1769 /* user/password only characters */
1774 /* *extra* characters */
1777 (*start == L'\'') ||
1781 /* *safe* characters */
1789 } else if (*start == L'%') {
1790 if (isxdigitW(*(start+1)) &&
1791 isxdigitW(*(start+2))) {
1803 if (isdigitW(*start)) {
1814 if (isalnumW(*start) ||
1816 (*start == L'.') ) {
1825 FIXME("unknown type %d\n", type);
1826 return (LPWSTR)&alwayszero;
1828 /* TRACE("scanned %ld characters next char %p<%c>\n",
1829 *size, start, *start); */
1833 /*************************************************************************
1834 * Attempt to parse URL into pieces.
1836 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1840 memset(pl, 0, sizeof(WINE_PARSE_URL));
1841 pl->pScheme = pszUrl;
1842 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1843 if (!*work || (*work != L':')) goto ERROR;
1845 if ((*work != L'/') || (*(work+1) != L'/')) goto ERROR;
1846 pl->pUserName = work + 2;
1847 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1848 if (*work == L':' ) {
1849 /* parse password */
1851 pl->pPassword = work;
1852 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1853 if (*work != L'@') {
1854 /* what we just parsed must be the hostname and port
1855 * so reset pointers and clear then let it parse */
1856 pl->szUserName = pl->szPassword = 0;
1857 work = pl->pUserName - 1;
1858 pl->pUserName = pl->pPassword = 0;
1860 } else if (*work == L'@') {
1864 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1865 /* what was parsed was hostname, so reset pointers and let it parse */
1866 pl->szUserName = pl->szPassword = 0;
1867 work = pl->pUserName - 1;
1868 pl->pUserName = pl->pPassword = 0;
1871 /* now start parsing hostname or hostnumber */
1873 pl->pHostName = work;
1874 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1875 if (*work == L':') {
1879 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1881 if (*work == L'/') {
1882 /* see if query string */
1883 pl->pQuery = strchrW(work, L'?');
1884 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1886 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1887 pl->pScheme, pl->szScheme,
1888 pl->pUserName, pl->szUserName,
1889 pl->pPassword, pl->szPassword,
1890 pl->pHostName, pl->szHostName,
1891 pl->pPort, pl->szPort,
1892 pl->pQuery, pl->szQuery);
1895 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1896 return E_INVALIDARG;
1899 /*************************************************************************
1900 * UrlGetPartA [SHLWAPI.@]
1902 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1903 DWORD dwPart, DWORD dwFlags)
1906 DWORD ret, len, len2;
1908 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1909 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1910 out = in + INTERNET_MAX_URL_LENGTH;
1912 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1914 len = INTERNET_MAX_URL_LENGTH;
1915 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
1918 HeapFree(GetProcessHeap(), 0, in);
1922 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
1923 if (len2 > *pcchOut) {
1925 HeapFree(GetProcessHeap(), 0, in);
1928 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1930 HeapFree(GetProcessHeap(), 0, in);
1934 /*************************************************************************
1935 * UrlGetPartW [SHLWAPI.@]
1937 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
1938 DWORD dwPart, DWORD dwFlags)
1942 DWORD size, schsize;
1943 LPCWSTR addr, schaddr;
1946 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
1947 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
1949 ret = URL_ParseUrl(pszIn, &pl);
1951 schaddr = pl.pScheme;
1952 schsize = pl.szScheme;
1955 case URL_PART_SCHEME:
1956 if (!pl.szScheme) return E_INVALIDARG;
1961 case URL_PART_HOSTNAME:
1962 if (!pl.szHostName) return E_INVALIDARG;
1963 addr = pl.pHostName;
1964 size = pl.szHostName;
1967 case URL_PART_USERNAME:
1968 if (!pl.szUserName) return E_INVALIDARG;
1969 addr = pl.pUserName;
1970 size = pl.szUserName;
1973 case URL_PART_PASSWORD:
1974 if (!pl.szPassword) return E_INVALIDARG;
1975 addr = pl.pPassword;
1976 size = pl.szPassword;
1980 if (!pl.szPort) return E_INVALIDARG;
1985 case URL_PART_QUERY:
1986 if (!pl.szQuery) return E_INVALIDARG;
1992 return E_INVALIDARG;
1995 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
1996 if (*pcchOut < size + schsize + 2) {
1997 *pcchOut = size + schsize + 2;
2000 strncpyW(pszOut, schaddr, schsize);
2001 work = pszOut + schsize;
2003 strncpyW(work+1, addr, size);
2004 *pcchOut = size + schsize + 1;
2009 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2010 strncpyW(pszOut, addr, size);
2012 work = pszOut + size;
2015 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2020 /*************************************************************************
2021 * PathIsURLA [SHLWAPI.@]
2023 * Check if the given path is a URL.
2026 * lpszPath [I] Path to check.
2029 * TRUE if lpszPath is a URL.
2030 * FALSE if lpszPath is NULL or not a URL.
2032 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2034 UNKNOWN_SHLWAPI_1 base;
2037 if (!lpstrPath || !*lpstrPath) return FALSE;
2040 base.size = sizeof(base);
2041 res1 = SHLWAPI_1(lpstrPath, &base);
2042 return (base.fcncde > 0);
2045 /*************************************************************************
2046 * PathIsURLW [SHLWAPI.@]
2050 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2052 UNKNOWN_SHLWAPI_2 base;
2055 if (!lpstrPath || !*lpstrPath) return FALSE;
2058 base.size = sizeof(base);
2059 res1 = SHLWAPI_2(lpstrPath, &base);
2060 return (base.fcncde > 0);
2063 /*************************************************************************
2064 * UrlCreateFromPathA [SHLWAPI.@]
2066 * Create a Url from a file path.
2069 * pszPath [I] Path to convert
2070 * pszUrl [O] Destination for the converted Url
2071 * pcchUrl [I/O] Length of pszUrl
2072 * dwReserved [I] Reserved, must be 0
2075 * Success: S_OK. pszUrl contains the converted path.
2076 * Failure: An HRESULT error code.
2078 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2080 DWORD nCharBeforeColon = 0;
2082 DWORD dwChRequired = 0;
2083 LPSTR pszNewUrl = NULL;
2084 LPCSTR pszConstPointer = NULL;
2085 LPSTR pszPointer = NULL;
2089 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved);
2091 /* Validate arguments */
2092 if (dwReserved != 0)
2094 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved);
2095 return E_INVALIDARG;
2097 if (!pszUrl || !pcchUrl || !pszUrl)
2099 ERR("Invalid argument\n");
2100 return E_INVALIDARG;
2103 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2105 if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) ||
2106 *pszConstPointer == '.' || *pszConstPointer == '-')
2110 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2112 dwChRequired = lstrlenA(pszPath);
2113 if (dwChRequired > *pcchUrl)
2115 *pcchUrl = dwChRequired;
2120 *pcchUrl = dwChRequired;
2121 StrCpyA(pszUrl, pszPath);
2125 /* then must need converting to file: format */
2127 /* Strip off leading slashes */
2128 while (*pszPath == '\\' || *pszPath == '/')
2134 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2135 TRACE("pszUrl: %s\n", debugstr_a(pszPath));
2136 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1);
2137 ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2138 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl));
2139 TRACE("%ld\n", dwChRequired);
2140 if (ret != E_POINTER && FAILED(ret))
2142 dwChRequired += 5; /* "file:" */
2143 if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':'))
2145 dwChRequired += 3; /* "///" */
2151 case 0: /* no slashes */
2153 case 2: /* two slashes */
2160 default: /* three slashes */
2165 if (dwChRequired > *pcchUrl)
2167 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2168 StrCpyA(pszUrl, "file:");
2169 pszPointer = pszUrl + lstrlenA(pszUrl);
2170 for (i=0; i < nSlashes; i++)
2175 StrCpyA(pszPointer, pszNewUrl);
2176 TRACE("<- %s\n", debugstr_a(pszUrl));
2180 /*************************************************************************
2181 * UrlCreateFromPathW [SHLWAPI.@]
2183 * See UrlCreateFromPathA.
2185 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2187 DWORD nCharBeforeColon = 0;
2189 DWORD dwChRequired = 0;
2190 LPWSTR pszNewUrl = NULL;
2191 LPCWSTR pszConstPointer = NULL;
2192 LPWSTR pszPointer = NULL;
2196 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2198 /* Validate arguments */
2199 if (dwReserved != 0)
2200 return E_INVALIDARG;
2201 if (!pszUrl || !pcchUrl || !pszUrl)
2202 return E_INVALIDARG;
2204 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2206 if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) ||
2207 *pszConstPointer == '.' || *pszConstPointer == '-')
2211 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2213 dwChRequired = lstrlenW(pszPath);
2214 *pcchUrl = dwChRequired;
2215 if (dwChRequired > *pcchUrl)
2219 StrCpyW(pszUrl, pszPath);
2223 /* then must need converting to file: format */
2225 /* Strip off leading slashes */
2226 while (*pszPath == '\\' || *pszPath == '/')
2232 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2233 ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2234 if (ret != E_POINTER && FAILED(ret))
2236 dwChRequired += 5; /* "file:" */
2237 if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':'))
2239 dwChRequired += 3; /* "///" */
2245 case 0: /* no slashes */
2247 case 2: /* two slashes */
2254 default: /* three slashes */
2259 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2260 if (dwChRequired > *pcchUrl)
2262 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR));
2263 StrCpyW(pszNewUrl, fileW);
2264 pszPointer = pszNewUrl + 4;
2267 for (i=0; i < nSlashes; i++)
2272 StrCpyW(pszPointer, pszPath);
2273 StrCpyW(pszUrl, pszNewUrl);
2277 /*************************************************************************
2278 * SHAutoComplete [SHLWAPI.@]
2280 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2282 FIXME("SHAutoComplete stub\n");