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
28 #include "wine/unicode.h"
31 #define NO_SHLWAPI_STREAM
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
37 /* The following schemes were identified in the native version of
38 * SHLWAPI.DLL version 5.50
41 URL_SCHEME_INVALID
= -1,
42 URL_SCHEME_UNKNOWN
= 0,
57 URL_SCHEME_JAVASCRIPT
,
65 URL_SCHEME scheme_number
;
69 static const SHL_2_inet_scheme shlwapi_schemes
[] = {
70 {URL_SCHEME_FTP
, "ftp"},
71 {URL_SCHEME_HTTP
, "http"},
72 {URL_SCHEME_GOPHER
, "gopher"},
73 {URL_SCHEME_MAILTO
, "mailto"},
74 {URL_SCHEME_NEWS
, "news"},
75 {URL_SCHEME_NNTP
, "nntp"},
76 {URL_SCHEME_TELNET
, "telnet"},
77 {URL_SCHEME_WAIS
, "wais"},
78 {URL_SCHEME_FILE
, "file"},
79 {URL_SCHEME_MK
, "mk"},
80 {URL_SCHEME_HTTPS
, "https"},
81 {URL_SCHEME_SHELL
, "shell"},
82 {URL_SCHEME_SNEWS
, "snews"},
83 {URL_SCHEME_LOCAL
, "local"},
84 {URL_SCHEME_JAVASCRIPT
, "javascript"},
85 {URL_SCHEME_VBSCRIPT
, "vbscript"},
86 {URL_SCHEME_ABOUT
, "about"},
87 {URL_SCHEME_RES
, "res"},
92 LPCWSTR pScheme
; /* [out] start of scheme */
93 DWORD szScheme
; /* [out] size of scheme (until colon) */
94 LPCWSTR pUserName
; /* [out] start of Username */
95 DWORD szUserName
; /* [out] size of Username (until ":" or "@") */
96 LPCWSTR pPassword
; /* [out] start of Password */
97 DWORD szPassword
; /* [out] size of Password (until "@") */
98 LPCWSTR pHostName
; /* [out] start of Hostname */
99 DWORD szHostName
; /* [out] size of Hostname (until ":" or "/") */
100 LPCWSTR pPort
; /* [out] start of Port */
101 DWORD szPort
; /* [out] size of Port (until "/" or eos) */
102 LPCWSTR pQuery
; /* [out] start of Query */
103 DWORD szQuery
; /* [out] size of Query (until eos) */
111 } WINE_URL_SCAN_TYPE
;
114 INT size
; /* [in] (always 0x18) */
115 LPCSTR ap1
; /* [out] start of scheme */
116 INT sizep1
; /* [out] size of scheme (until colon) */
117 LPCSTR ap2
; /* [out] pointer following first colon */
118 INT sizep2
; /* [out] size of remainder */
119 INT fcncde
; /* [out] function match of p1 (0 if unknown) */
123 INT size
; /* [in] (always 0x18) */
124 LPCWSTR ap1
; /* [out] start of scheme */
125 INT sizep1
; /* [out] size of scheme (until colon) */
126 LPCWSTR ap2
; /* [out] pointer following first colon */
127 INT sizep2
; /* [out] size of remainder */
128 INT fcncde
; /* [out] function match of p1 (0 if unknown) */
131 static const CHAR hexDigits
[] = "0123456789ABCDEF";
133 static const WCHAR fileW
[] = {'f','i','l','e','\0'};
135 static const unsigned char HashDataLookup
[256] = {
136 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
137 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
138 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
139 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
140 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
141 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
142 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
143 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
144 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
145 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
146 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
147 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
148 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
149 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
150 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
151 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
152 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
153 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
154 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
155 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
157 static BOOL
URL_NeedEscapeA(CHAR ch
, DWORD dwFlags
)
163 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
170 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
173 if (ch
<= 31 || ch
>= 127)
195 if (dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) return TRUE
;
202 static BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
)
208 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
215 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== L
'%'))
218 if (ch
<= 31 || ch
>= 127)
240 if (dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) return TRUE
;
247 static BOOL
URL_JustLocation(LPCWSTR str
)
249 while(*str
&& (*str
== L
'/')) str
++;
251 while (*str
&& ((*str
== L
'-') ||
253 isalnumW(*str
))) str
++;
254 if (*str
== L
'/') return FALSE
;
260 /*************************************************************************
263 * Parse a Url into its constituent parts.
267 * y [O] Undocumented structure holding the parsed information
270 * Success: S_OK. y contains the parsed Url details.
271 * Failure: An HRESULT error code.
273 DWORD WINAPI
ParseURLA(LPCSTR x
, UNKNOWN_SHLWAPI_1
*y
)
276 const SHL_2_inet_scheme
*inet_pro
;
278 y
->fcncde
= URL_SCHEME_INVALID
;
279 if (y
->size
!= 0x18) return E_INVALIDARG
;
280 /* FIXME: leading white space generates error of 0x80041001 which
283 if (*x
<= ' ') return 0x80041001;
298 /* check for no scheme in string start */
299 /* (apparently schemes *must* be larger than a single character) */
300 if ((*x
== '\0') || (y
->sizep1
<= 1)) {
305 /* found scheme, set length of remainder */
306 y
->sizep2
= lstrlenA(y
->ap2
);
308 /* see if known scheme and return indicator number */
309 y
->fcncde
= URL_SCHEME_UNKNOWN
;
310 inet_pro
= shlwapi_schemes
;
311 while (inet_pro
->scheme_name
) {
312 if (!strncasecmp(inet_pro
->scheme_name
, y
->ap1
,
313 min(y
->sizep1
, lstrlenA(inet_pro
->scheme_name
)))) {
314 y
->fcncde
= inet_pro
->scheme_number
;
322 /*************************************************************************
325 * Unicode version of ParseURLA.
327 DWORD WINAPI
ParseURLW(LPCWSTR x
, UNKNOWN_SHLWAPI_2
*y
)
330 const SHL_2_inet_scheme
*inet_pro
;
334 y
->fcncde
= URL_SCHEME_INVALID
;
335 if (y
->size
!= 0x18) return E_INVALIDARG
;
336 /* FIXME: leading white space generates error of 0x80041001 which
339 if (*x
<= L
' ') return 0x80041001;
354 /* check for no scheme in string start */
355 /* (apparently schemes *must* be larger than a single character) */
356 if ((*x
== L
'\0') || (y
->sizep1
<= 1)) {
361 /* found scheme, set length of remainder */
362 y
->sizep2
= lstrlenW(y
->ap2
);
364 /* see if known scheme and return indicator number */
365 len
= WideCharToMultiByte(0, 0, y
->ap1
, y
->sizep1
, 0, 0, 0, 0);
366 cmpstr
= (LPSTR
)HeapAlloc(GetProcessHeap(), 0, len
+1);
367 WideCharToMultiByte(0, 0, y
->ap1
, y
->sizep1
, cmpstr
, len
+1, 0, 0);
368 y
->fcncde
= URL_SCHEME_UNKNOWN
;
369 inet_pro
= shlwapi_schemes
;
370 while (inet_pro
->scheme_name
) {
371 if (!strncasecmp(inet_pro
->scheme_name
, cmpstr
,
372 min(len
, lstrlenA(inet_pro
->scheme_name
)))) {
373 y
->fcncde
= inet_pro
->scheme_number
;
378 HeapFree(GetProcessHeap(), 0, cmpstr
);
382 /*************************************************************************
383 * UrlCanonicalizeA [SHLWAPI.@]
385 * Canonicalize a Url.
388 * pszUrl [I] Url to cCanonicalize
389 * pszCanonicalized [O] Destination for converted Url.
390 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
391 * dwFlags [I] Flags controlling the conversion.
394 * Success: S_OK. The pszCanonicalized contains the converted Url.
395 * Failure: E_POINTER, if *pcchCanonicalized is too small.
397 * MSDN is wrong (at 10/30/01 - go figure). This should support the
398 * following flags: GLA
399 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
400 *| URL_ESCAPE_SPACES_ONLY 0x04000000
401 *| URL_ESCAPE_PERCENT 0x00001000
402 *| URL_ESCAPE_UNSAFE 0x10000000
403 *| URL_UNESCAPE 0x10000000
404 *| URL_DONT_SIMPLIFY 0x08000000
405 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
407 HRESULT WINAPI
UrlCanonicalizeA(LPCSTR pszUrl
, LPSTR pszCanonicalized
,
408 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
410 LPWSTR base
, canonical
;
411 DWORD ret
, len
, len2
;
413 TRACE("(%s %p %p 0x%08lx) using W version\n",
414 debugstr_a(pszUrl
), pszCanonicalized
,
415 pcchCanonicalized
, dwFlags
);
417 base
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
418 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
419 canonical
= base
+ INTERNET_MAX_URL_LENGTH
;
421 MultiByteToWideChar(0, 0, pszUrl
, -1, base
, INTERNET_MAX_URL_LENGTH
);
422 len
= INTERNET_MAX_URL_LENGTH
;
424 ret
= UrlCanonicalizeW(base
, canonical
, &len
, dwFlags
);
426 HeapFree(GetProcessHeap(), 0, base
);
430 len2
= WideCharToMultiByte(0, 0, canonical
, len
, 0, 0, 0, 0);
431 if (len2
> *pcchCanonicalized
) {
432 *pcchCanonicalized
= len
;
433 HeapFree(GetProcessHeap(), 0, base
);
436 WideCharToMultiByte(0, 0, canonical
, len
+1, pszCanonicalized
,
437 *pcchCanonicalized
, 0, 0);
438 *pcchCanonicalized
= len2
;
439 HeapFree(GetProcessHeap(), 0, base
);
443 /*************************************************************************
444 * UrlCanonicalizeW [SHLWAPI.@]
446 * See UrlCanonicalizeA.
448 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
449 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
453 LPWSTR lpszUrlCpy
, wk1
, wk2
, mp
, root
;
454 INT nLen
, nByteLen
, state
;
456 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszCanonicalized
,
457 pcchCanonicalized
, dwFlags
);
459 nByteLen
= (lstrlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
460 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
462 if (dwFlags
& URL_DONT_SIMPLIFY
)
463 memcpy(lpszUrlCpy
, pszUrl
, nByteLen
);
469 * 1 have 2[+] alnum 2,3
470 * 2 have scheme (found :) 4,6,3
471 * 3 failed (no location)
473 * 5 have 1[+] alnum 6,3
474 * 6 have location (found /) save root location
477 wk1
= (LPWSTR
)pszUrl
;
483 if (!isalnumW(*wk1
)) {state
= 3; break;}
485 if (!isalnumW(*wk1
)) {state
= 3; break;}
491 if (*wk1
++ == L
':') state
= 2;
494 if (*wk1
!= L
'/') {state
= 3; break;}
496 if (*wk1
!= L
'/') {state
= 6; break;}
506 if (!isalnumW(*wk1
) && (*wk1
!= L
'-')) {state
= 3; break;}
507 while(isalnumW(*wk1
) || (*wk1
== L
'-')) *wk2
++ = *wk1
++;
511 if (*wk1
!= L
'/') {state
= 3; break;}
516 /* Now at root location, cannot back up any more. */
517 /* "root" will point at the '/' */
520 TRACE("wk1=%c\n", (CHAR
)*wk1
);
521 mp
= strchrW(wk1
, L
'/');
529 strncpyW(wk2
, wk1
, nLen
);
533 TRACE("found '/.'\n");
534 if (*(wk1
+1) == L
'/') {
535 /* case of /./ -> skip the ./ */
538 else if (*(wk1
+1) == L
'.') {
539 /* found /.. look for next / */
540 TRACE("found '/..'\n");
541 if (*(wk1
+2) == L
'/') {
542 /* case /../ -> need to backup wk2 */
543 TRACE("found '/../'\n");
544 *(wk2
-1) = L
'\0'; /* set end of string */
545 mp
= strrchrW(root
, L
'/');
546 if (mp
&& (mp
>= root
)) {
547 /* found valid backup point */
552 /* did not find point, restore '/' */
562 FIXME("how did we get here - state=%d\n", state
);
567 TRACE("Simplified, orig <%s>, simple <%s>\n",
568 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
571 if(dwFlags
& URL_UNESCAPE
)
572 UrlUnescapeW(lpszUrlCpy
, NULL
, NULL
, URL_UNESCAPE_INPLACE
);
574 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
575 URL_ESCAPE_SPACES_ONLY
|
577 URL_DONT_ESCAPE_EXTRA_INFO
|
578 URL_ESCAPE_SEGMENT_ONLY
))) {
579 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
580 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
582 } else { /* No escaping needed, just copy the string */
583 nLen
= lstrlenW(lpszUrlCpy
);
584 if(nLen
< *pcchCanonicalized
)
585 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
590 *pcchCanonicalized
= nLen
;
593 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
596 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
601 /*************************************************************************
602 * UrlCombineA [SHLWAPI.@]
604 * Uses the W version to do job.
606 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
607 LPSTR pszCombined
, LPDWORD pcchCombined
,
610 LPWSTR base
, relative
, combined
;
611 DWORD ret
, len
, len2
;
613 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
614 debugstr_a(pszBase
),debugstr_a(pszRelative
),
615 *pcchCombined
,dwFlags
);
617 base
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
618 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
619 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
620 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
622 MultiByteToWideChar(0, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
623 MultiByteToWideChar(0, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
624 len
= INTERNET_MAX_URL_LENGTH
;
626 ret
= UrlCombineW(base
, relative
, combined
, &len
, dwFlags
);
628 HeapFree(GetProcessHeap(), 0, base
);
632 len2
= WideCharToMultiByte(0, 0, combined
, len
, 0, 0, 0, 0);
633 if (len2
> *pcchCombined
) {
634 *pcchCombined
= len2
;
635 HeapFree(GetProcessHeap(), 0, base
);
638 WideCharToMultiByte(0, 0, combined
, len
+1, pszCombined
, *pcchCombined
,
640 *pcchCombined
= len2
;
641 HeapFree(GetProcessHeap(), 0, base
);
645 /*************************************************************************
646 * UrlCombineW [SHLWAPI.@]
648 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
649 LPWSTR pszCombined
, LPDWORD pcchCombined
,
652 UNKNOWN_SHLWAPI_2 base
, relative
;
653 DWORD myflags
, sizeloc
= 0;
654 DWORD len
, res1
, res2
, process_case
= 0;
655 LPWSTR work
, preliminary
, mbase
, mrelative
;
656 WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
657 WCHAR single_slash
[] = {'/','\0'};
660 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
661 debugstr_w(pszBase
),debugstr_w(pszRelative
),
662 *pcchCombined
,dwFlags
);
667 /* Get space for duplicates of the input and the output */
668 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
670 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
671 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
672 *preliminary
= L
'\0';
674 /* Canonicalize the base input prior to looking for the scheme */
675 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
676 len
= INTERNET_MAX_URL_LENGTH
;
677 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
679 /* Canonicalize the relative input prior to looking for the scheme */
680 len
= INTERNET_MAX_URL_LENGTH
;
681 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
683 /* See if the base has a scheme */
684 res1
= ParseURLW(mbase
, &base
);
686 /* if pszBase has no scheme, then return pszRelative */
687 TRACE("no scheme detected in Base\n");
692 /* get size of location field (if it exists) */
693 work
= (LPWSTR
)base
.ap2
;
695 if (*work
++ == L
'/') {
696 if (*work
++ == L
'/') {
697 /* At this point have start of location and
698 * it ends at next '/' or end of string.
700 while(*work
&& (*work
!= L
'/')) work
++;
701 sizeloc
= work
- base
.ap2
;
705 /* Change .sizep2 to not have the last leaf in it,
706 * Note: we need to start after the location (if it exists)
708 work
= strrchrW((base
.ap2
+sizeloc
), L
'/');
710 len
= work
- base
.ap2
+ 1;
715 * .ap2 points to location (starting with '//')
716 * .sizep2 length of location (above) and rest less the last
718 * sizeloc length of location (above) up to but not including
722 res2
= ParseURLW(mrelative
, &relative
);
724 /* no scheme in pszRelative */
725 TRACE("no scheme detected in Relative\n");
726 relative
.ap2
= mrelative
; /* case 3,4,5 depends on this */
727 relative
.sizep2
= strlenW(mrelative
);
728 if (*pszRelative
== L
':') {
729 /* case that is either left alone or uses pszBase */
730 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
737 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == L
':')) {
738 /* case that becomes "file:///" */
739 strcpyW(preliminary
, myfilestr
);
743 if ((*mrelative
== L
'/') && (*(mrelative
+1) == L
'/')) {
744 /* pszRelative has location and rest */
748 if (*mrelative
== L
'/') {
749 /* case where pszRelative is root to location */
753 process_case
= (*base
.ap2
== L
'/') ? 5 : 3;
757 /* handle cases where pszRelative has scheme */
758 if ((base
.sizep1
== relative
.sizep1
) &&
759 (strncmpW(base
.ap1
, relative
.ap1
, base
.sizep1
) == 0)) {
761 /* since the schemes are the same */
762 if ((*relative
.ap2
== L
'/') && (*(relative
.ap2
+1) == L
'/')) {
763 /* case where pszRelative replaces location and following */
767 if (*relative
.ap2
== L
'/') {
768 /* case where pszRelative is root to location */
772 /* case where scheme is followed by document path */
776 if ((*relative
.ap2
== L
'/') && (*(relative
.ap2
+1) == L
'/')) {
777 /* case where pszRelative replaces scheme, location,
778 * and following and handles PLUGGABLE
785 } while(FALSE
); /* a litte trick to allow easy exit from nested if's */
789 switch (process_case
) {
792 * Return pszRelative appended to what ever is in pszCombined,
793 * (which may the string "file:///"
795 len
= strlenW(mrelative
) + strlenW(preliminary
);
796 if (len
+1 > *pcchCombined
) {
801 strcatW(preliminary
, mrelative
);
805 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
806 * and pszRelative starts with "//", then append a "/"
808 len
= strlenW(mrelative
) + 1;
809 if (len
+1 > *pcchCombined
) {
814 strcpyW(preliminary
, mrelative
);
815 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
816 URL_JustLocation(relative
.ap2
))
817 strcatW(preliminary
, single_slash
);
821 * Return the pszBase scheme with pszRelative. Basicly
822 * keeps the scheme and replaces the domain and following.
824 len
= base
.sizep1
+ 1 + relative
.sizep2
+ 1;
825 if (len
+1 > *pcchCombined
) {
830 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+ 1);
831 work
= preliminary
+ base
.sizep1
+ 1;
832 strcpyW(work
, relative
.ap2
);
833 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
834 URL_JustLocation(relative
.ap2
))
835 strcatW(work
, single_slash
);
839 * Return the pszBase scheme and location but everything
840 * after the location is pszRelative. (Replace document
843 len
= base
.sizep1
+ 1 + sizeloc
+ relative
.sizep2
+ 1;
844 if (len
+1 > *pcchCombined
) {
849 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+1+sizeloc
);
850 work
= preliminary
+ base
.sizep1
+ 1 + sizeloc
;
851 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
853 strcpyW(work
, relative
.ap2
);
857 * Return the pszBase without its document (if any) and
858 * append pszRelative after its scheme.
860 len
= base
.sizep1
+ 1 + base
.sizep2
+ relative
.sizep2
;
861 if (len
+1 > *pcchCombined
) {
866 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+1+base
.sizep2
);
867 work
= preliminary
+ base
.sizep1
+1+base
.sizep2
- 1;
870 strcpyW(work
, relative
.ap2
);
874 FIXME("How did we get here????? process_case=%ld\n", process_case
);
880 * Now that the combining is done, process the escape options if
881 * necessary, otherwise just copy the string.
883 myflags
= dwFlags
& (URL_ESCAPE_PERCENT
|
884 URL_ESCAPE_SPACES_ONLY
|
885 URL_DONT_ESCAPE_EXTRA_INFO
|
886 URL_ESCAPE_SEGMENT_ONLY
);
888 ret
= UrlEscapeW(preliminary
, pszCombined
,
889 pcchCombined
, myflags
);
891 len
= (strlenW(preliminary
) + 1) * sizeof(WCHAR
);
892 memcpy(pszCombined
, preliminary
, len
);
893 *pcchCombined
= strlenW(preliminary
);
895 TRACE("return-%ld len=%ld, %s\n",
896 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
898 HeapFree(GetProcessHeap(), 0, preliminary
);
902 /*************************************************************************
903 * UrlEscapeA [SHLWAPI.@]
905 * Converts unsafe characters into their escape sequences.
908 * The converted string is returned in pszEscaped if the buffer size
909 * (which should be supplied in pcchEscaped) is large enough, in this
910 * case the function returns S_OK and pcchEscaped contains the length
911 * of the escaped string. If the buffer is not large enough the
912 * function returns E_POINTER and pcchEscaped contains the required
913 * buffer size (including room for the '\0').
915 * By default the function stops converting at the first '?' or
916 * '#'. [MSDN says differently]. If URL_ESCAPE_SPACES_ONLY flag is set
917 * then only spaces are converted, but the conversion continues past a
921 * Have now implemented the following flags:
922 *| URL_ESCAPE_SPACES_ONLY
923 *| URL_DONT_ESCAPE_EXTRA_INFO
924 *| URL_ESCAPE_SEGMENT_ONLY
925 *| URL_ESCAPE_PERCENT
926 * Initial testing seems to indicate that this is now working like
927 * native shlwapi version 5. Note that these functions did not work
928 * well (or at all) in shlwapi version 4.
931 HRESULT WINAPI
UrlEscapeA(
938 DWORD needed
= 0, ret
;
939 BOOL stop_escaping
= FALSE
;
940 char next
[3], *dst
= pszEscaped
;
943 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl
), pszEscaped
,
944 *pcchEscaped
, dwFlags
);
946 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
947 URL_ESCAPE_SEGMENT_ONLY
|
948 URL_DONT_ESCAPE_EXTRA_INFO
|
950 FIXME("Unimplemented flags: %08lx\n", dwFlags
);
953 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
954 /* if SPACES_ONLY specified, reset the other controls */
955 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
957 URL_ESCAPE_SEGMENT_ONLY
);
960 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
961 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
963 for(src
= pszUrl
; *src
; src
++) {
964 if(!(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) &&
965 (dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
) &&
966 (*src
== '#' || *src
== '?'))
967 stop_escaping
= TRUE
;
969 if(URL_NeedEscapeA(*src
, dwFlags
) && stop_escaping
== FALSE
) {
970 /* TRACE("escaping %c\n", *src); */
972 next
[1] = hexDigits
[(*src
>> 4) & 0xf];
973 next
[2] = hexDigits
[*src
& 0xf];
976 /* TRACE("passing %c\n", *src); */
981 if(needed
+ len
<= *pcchEscaped
) {
982 memcpy(dst
, next
, len
);
988 if(needed
< *pcchEscaped
) {
992 needed
++; /* add one for the '\0' */
995 *pcchEscaped
= needed
;
999 /*************************************************************************
1000 * UrlEscapeW [SHLWAPI.@]
1004 HRESULT WINAPI
UrlEscapeW(
1007 LPDWORD pcchEscaped
,
1011 DWORD needed
= 0, ret
;
1012 BOOL stop_escaping
= FALSE
;
1013 WCHAR next
[5], *dst
= pszEscaped
;
1016 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszEscaped
,
1017 pcchEscaped
, dwFlags
);
1019 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
1020 URL_ESCAPE_SEGMENT_ONLY
|
1021 URL_DONT_ESCAPE_EXTRA_INFO
|
1022 URL_ESCAPE_PERCENT
))
1023 FIXME("Unimplemented flags: %08lx\n", dwFlags
);
1026 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
1027 /* if SPACES_ONLY specified, reset the other controls */
1028 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
1029 URL_ESCAPE_PERCENT
|
1030 URL_ESCAPE_SEGMENT_ONLY
);
1033 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1034 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
1036 for(src
= pszUrl
; *src
; src
++) {
1038 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1039 * (*src == L'#' || *src == L'?'))
1040 * stop_escaping = TRUE;
1042 if(!(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) &&
1043 (dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
) &&
1044 (*src
== L
'#' || *src
== L
'?'))
1045 stop_escaping
= TRUE
;
1047 if(URL_NeedEscapeW(*src
, dwFlags
) && stop_escaping
== FALSE
) {
1048 /* TRACE("escaping %c\n", *src); */
1051 * I would have assumed that the W form would escape
1052 * the character with 4 hex digits (or even 8),
1053 * however, experiments show that native shlwapi escapes
1054 * with only 2 hex digits.
1055 * next[1] = hexDigits[(*src >> 12) & 0xf];
1056 * next[2] = hexDigits[(*src >> 8) & 0xf];
1057 * next[3] = hexDigits[(*src >> 4) & 0xf];
1058 * next[4] = hexDigits[*src & 0xf];
1061 next
[1] = hexDigits
[(*src
>> 4) & 0xf];
1062 next
[2] = hexDigits
[*src
& 0xf];
1065 /* TRACE("passing %c\n", *src); */
1070 if(needed
+ len
<= *pcchEscaped
) {
1071 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1077 if(needed
< *pcchEscaped
) {
1081 needed
++; /* add one for the '\0' */
1084 *pcchEscaped
= needed
;
1089 /*************************************************************************
1090 * UrlUnescapeA [SHLWAPI.@]
1092 * Converts escape sequences back to ordinary characters.
1095 * pszUrl [I/O] Url to convert
1096 * pszUnescaped [O] Destination for converted Url
1097 * pcchUnescaped [I/O] Size of output string
1098 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1101 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1102 * dwFlags includes URL_ESCAPE_INPLACE.
1103 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1104 * this case pcchUnescaped is set to the size required.
1106 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1107 * the first occurrence of either '?' or '#'.
1109 HRESULT WINAPI
UrlUnescapeA(
1112 LPDWORD pcchUnescaped
,
1119 BOOL stop_unescaping
= FALSE
;
1121 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl
), pszUnescaped
,
1122 pcchUnescaped
, dwFlags
);
1124 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1125 dst
= (char*)pszUrl
;
1129 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1130 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1131 (*src
== '#' || *src
== '?')) {
1132 stop_unescaping
= TRUE
;
1134 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1135 && stop_unescaping
== FALSE
) {
1138 memcpy(buf
, src
+ 1, 2);
1140 ih
= strtol(buf
, NULL
, 16);
1142 src
+= 2; /* Advance to end of escape */
1146 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1150 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1154 needed
++; /* add one for the '\0' */
1157 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1158 *pcchUnescaped
= needed
;
1161 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1162 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1168 /*************************************************************************
1169 * UrlUnescapeW [SHLWAPI.@]
1173 HRESULT WINAPI
UrlUnescapeW(
1175 LPWSTR pszUnescaped
,
1176 LPDWORD pcchUnescaped
,
1183 BOOL stop_unescaping
= FALSE
;
1185 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl
), pszUnescaped
,
1186 pcchUnescaped
, dwFlags
);
1188 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1189 dst
= (WCHAR
*)pszUrl
;
1193 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1194 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1195 (*src
== L
'#' || *src
== L
'?')) {
1196 stop_unescaping
= TRUE
;
1198 } else if(*src
== L
'%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1199 && stop_unescaping
== FALSE
) {
1202 memcpy(buf
, src
+ 1, 2*sizeof(WCHAR
));
1204 ih
= StrToIntW(buf
);
1206 src
+= 2; /* Advance to end of escape */
1210 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1214 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1218 needed
++; /* add one for the '\0' */
1221 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1222 *pcchUnescaped
= needed
;
1225 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1226 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1232 /*************************************************************************
1233 * UrlGetLocationA [SHLWAPI.@]
1235 * Get the location from a Url.
1238 * pszUrl [I] Url to get the location from
1241 * A pointer to the start of the location in pszUrl, or NULL if there is
1245 * MSDN (as of 2001-11-01) says that:
1246 * "The location is the segment of the URL starting with a ?
1248 * Neither V4 nor V5 of shlwapi.dll implement the '?' and always return
1251 * MSDN further states that:
1252 * "If a file URL has a query string, the returned string is
1253 * the query string."
1254 * In all test cases if the scheme starts with "fi" then a NULL is
1255 * returned. V5 gives the following results:
1256 *| NULL file://aa/b/cd#hohoh
1257 *| #hohoh http://aa/b/cd#hohoh
1258 *| NULL fi://aa/b/cd#hohoh
1259 *| #hohoh ff://aa/b/cd#hohoh
1261 LPCSTR WINAPI
UrlGetLocationA(
1264 UNKNOWN_SHLWAPI_1 base
;
1268 res1
= ParseURLA(pszUrl
, &base
);
1269 if (res1
) return NULL
; /* invalid scheme */
1271 /* if scheme is file: then never return pointer */
1272 if (strncmp(base
.ap1
, "file", min(4,base
.sizep1
)) == 0) return NULL
;
1274 /* Look for '#' and return its addr */
1275 return strchr(base
.ap2
, '#');
1278 /*************************************************************************
1279 * UrlGetLocationW [SHLWAPI.@]
1281 * See UrlGetLocationA.
1283 LPCWSTR WINAPI
UrlGetLocationW(
1286 UNKNOWN_SHLWAPI_2 base
;
1290 res1
= ParseURLW(pszUrl
, &base
);
1291 if (res1
) return NULL
; /* invalid scheme */
1293 /* if scheme is file: then never return pointer */
1294 if (strncmpW(base
.ap1
, fileW
, min(4,base
.sizep1
)) == 0) return NULL
;
1296 /* Look for '#' and return its addr */
1297 return strchrW(base
.ap2
, L
'#');
1300 /*************************************************************************
1301 * UrlCompareA [SHLWAPI.@]
1303 INT WINAPI
UrlCompareA(
1308 INT ret
, len
, len1
, len2
;
1311 return strcmp(pszUrl1
, pszUrl2
);
1312 len1
= strlen(pszUrl1
);
1313 if (pszUrl1
[len1
-1] == L
'/') len1
--;
1314 len2
= strlen(pszUrl2
);
1315 if (pszUrl2
[len2
-1] == L
'/') len2
--;
1317 return strncmp(pszUrl1
, pszUrl2
, len1
);
1318 len
= min(len1
, len2
);
1319 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1320 if (ret
) return ret
;
1321 if (len1
> len2
) return 1;
1325 /*************************************************************************
1326 * UrlCompareW [SHLWAPI.@]
1328 INT WINAPI
UrlCompareW(
1333 INT ret
, len
, len1
, len2
;
1336 return strcmpW(pszUrl1
, pszUrl2
);
1337 len1
= strlenW(pszUrl1
);
1338 if (pszUrl1
[len1
-1] == L
'/') len1
--;
1339 len2
= strlenW(pszUrl2
);
1340 if (pszUrl2
[len2
-1] == L
'/') len2
--;
1342 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1343 len
= min(len1
, len2
);
1344 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1345 if (ret
) return ret
;
1346 if (len1
> len2
) return 1;
1350 /*************************************************************************
1351 * HashData [SHLWAPI.@]
1353 * Hash an input block into a variable sized digest.
1356 * lpSrc [I] Input block
1357 * nSrcLen [I] Length of lpSrc
1358 * lpDest [I] Output for hash digest
1359 * nDestLen [I] Length of lpDest
1362 * Success: TRUE. lpDest is filled with the computed hash value.
1363 * Failure: FALSE, if any argument is invalid.
1365 BOOL WINAPI
HashData(const unsigned char *lpSrc
, INT nSrcLen
,
1366 unsigned char *lpDest
, INT nDestLen
)
1368 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1370 if (IsBadReadPtr(lpSrc
, nSrcLen
) ||
1371 IsBadWritePtr(lpDest
, nDestLen
))
1374 while (destCount
>= 0)
1376 lpDest
[destCount
] = (destCount
& 0xff);
1380 while (srcCount
>= 0)
1382 destCount
= nDestLen
- 1;
1383 while (destCount
>= 0)
1385 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1393 /*************************************************************************
1394 * UrlHashA [SHLWAPI.@]
1396 * Produce a Hash from a Url.
1399 * pszUrl [I] Url to hash
1400 * lpDest [O] Destinationh for hash
1401 * nDestLen [I] Length of lpDest
1404 * Success: S_OK. lpDest is filled with the computed hash value.
1405 * Failure: E_INVALIDARG, if any argument is invalid.
1407 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, INT nDestLen
)
1409 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1410 return E_INVALIDARG
;
1412 HashData(pszUrl
, strlen(pszUrl
), lpDest
, nDestLen
);
1416 /*************************************************************************
1417 * UrlHashW [SHLWAPI.@]
1421 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, INT nDestLen
)
1423 char szUrl
[MAX_PATH
];
1425 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1427 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1428 return E_INVALIDARG
;
1430 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1431 * return the same digests for the same URL.
1433 WideCharToMultiByte(0, 0, pszUrl
, -1, szUrl
, MAX_PATH
, 0, 0);
1434 HashData(szUrl
, strlen(szUrl
), lpDest
, nDestLen
);
1438 /*************************************************************************
1439 * UrlApplySchemeA [SHLWAPI.@]
1441 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1444 DWORD ret
, len
, len2
;
1446 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1447 debugstr_a(pszIn
), *pcchOut
, dwFlags
);
1449 in
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
1450 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1451 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1453 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1454 len
= INTERNET_MAX_URL_LENGTH
;
1456 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1457 if ((ret
!= S_OK
) && (ret
!= S_FALSE
)) {
1458 HeapFree(GetProcessHeap(), 0, in
);
1462 len2
= WideCharToMultiByte(0, 0, out
, len
+1, 0, 0, 0, 0);
1463 if (len2
> *pcchOut
) {
1465 HeapFree(GetProcessHeap(), 0, in
);
1468 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1470 HeapFree(GetProcessHeap(), 0, in
);
1474 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1479 DWORD value_len
, data_len
, dwType
;
1480 WCHAR reg_path
[MAX_PATH
];
1481 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1484 MultiByteToWideChar(0, 0,
1485 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1486 -1, reg_path
, MAX_PATH
);
1487 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1489 while(value_len
= data_len
= MAX_PATH
,
1490 RegEnumValueW(newkey
, index
, value
, &value_len
,
1491 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1492 TRACE("guess %d %s is %s\n",
1493 index
, debugstr_w(value
), debugstr_w(data
));
1496 for(i
=0; i
<value_len
; i
++) {
1499 /* remember that TRUE is not-equal */
1500 j
= ChrCmpIW(Wxx
, Wyy
);
1503 if ((i
== value_len
) && !j
) {
1504 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1505 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1506 RegCloseKey(newkey
);
1509 strcpyW(pszOut
, data
);
1510 strcatW(pszOut
, pszIn
);
1511 *pcchOut
= strlenW(pszOut
);
1512 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1513 RegCloseKey(newkey
);
1518 RegCloseKey(newkey
);
1522 HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1525 DWORD data_len
, dwType
;
1526 WCHAR reg_path
[MAX_PATH
];
1527 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1529 /* get and prepend default */
1530 MultiByteToWideChar(0, 0,
1531 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1532 -1, reg_path
, MAX_PATH
);
1533 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1534 data_len
= MAX_PATH
;
1537 RegQueryValueExW(newkey
, value
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1538 RegCloseKey(newkey
);
1539 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1540 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1543 strcpyW(pszOut
, data
);
1544 strcatW(pszOut
, pszIn
);
1545 *pcchOut
= strlenW(pszOut
);
1546 TRACE("used default %s\n", debugstr_w(pszOut
));
1550 /*************************************************************************
1551 * UrlApplySchemeW [SHLWAPI.@]
1553 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1555 UNKNOWN_SHLWAPI_2 in_scheme
;
1559 TRACE("(in %s, out size %ld, flags %08lx)\n",
1560 debugstr_w(pszIn
), *pcchOut
, dwFlags
);
1562 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1563 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1564 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwFlags
);
1565 strcpyW(pszOut
, pszIn
);
1566 *pcchOut
= strlenW(pszOut
);
1570 in_scheme
.size
= 24;
1571 /* See if the base has a scheme */
1572 res1
= ParseURLW(pszIn
, &in_scheme
);
1574 /* no scheme in input, need to see if we need to guess */
1575 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1576 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != -1)
1581 /* we have a scheme, see if valid (known scheme) */
1582 if (in_scheme
.fcncde
) {
1583 /* have valid scheme, so just copy and exit */
1584 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1585 *pcchOut
= strlenW(pszIn
) + 1;
1588 strcpyW(pszOut
, pszIn
);
1589 *pcchOut
= strlenW(pszOut
);
1590 TRACE("valid scheme, returing copy\n");
1595 /* If we are here, then either invalid scheme,
1596 * or no scheme and can't/failed guess.
1598 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1600 (dwFlags
& URL_APPLY_DEFAULT
)) {
1601 /* find and apply default scheme */
1602 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1605 /* just copy and give proper return code */
1606 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1607 *pcchOut
= strlenW(pszIn
) + 1;
1610 strcpyW(pszOut
, pszIn
);
1611 *pcchOut
= strlenW(pszOut
);
1612 TRACE("returning copy, left alone\n");
1616 /*************************************************************************
1617 * UrlIsA [SHLWAPI.@]
1619 * Determine if a Url is of a certain class.
1622 * pszUrl [I] Url to check
1623 * Urlis [I] URLIS_ constant from "shlwapi.h"
1626 * TRUE if pszUrl belongs to the class type in Urlis.
1629 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1631 UNKNOWN_SHLWAPI_1 base
;
1638 res1
= ParseURLA(pszUrl
, &base
);
1639 if (res1
) return FALSE
; /* invalid scheme */
1640 if ((*base
.ap2
== '/') && (*(base
.ap2
+1) == '/'))
1641 /* has scheme followed by 2 '/' */
1646 case URLIS_NOHISTORY
:
1648 case URLIS_APPLIABLE
:
1649 case URLIS_DIRECTORY
:
1650 case URLIS_HASQUERY
:
1652 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1657 /*************************************************************************
1658 * UrlIsW [SHLWAPI.@]
1662 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1664 UNKNOWN_SHLWAPI_2 base
;
1671 res1
= ParseURLW(pszUrl
, &base
);
1672 if (res1
) return FALSE
; /* invalid scheme */
1673 if ((*base
.ap2
== L
'/') && (*(base
.ap2
+1) == L
'/'))
1674 /* has scheme followed by 2 '/' */
1679 case URLIS_NOHISTORY
:
1681 case URLIS_APPLIABLE
:
1682 case URLIS_DIRECTORY
:
1683 case URLIS_HASQUERY
:
1685 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1690 /*************************************************************************
1691 * UrlIsNoHistoryA [SHLWAPI.@]
1693 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1695 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1698 /*************************************************************************
1699 * UrlIsNoHistoryW [SHLWAPI.@]
1701 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1703 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1706 /*************************************************************************
1707 * UrlIsOpaqueA [SHLWAPI.@]
1709 * Determine if a Url is opaque.
1712 * pszUrl [I] Url to check
1715 * TRUE if pszUrl is opaque,
1719 * An opaque Url is one that does not start with "<protocol>://".
1721 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1723 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1726 /*************************************************************************
1727 * UrlIsOpaqueW [SHLWAPI.@]
1731 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1733 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1736 /*************************************************************************
1737 * Scans for characters of type "type" and when not matching found,
1738 * returns pointer to it and length in size.
1740 * Characters tested based on RFC 1738
1742 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1744 static DWORD alwayszero
= 0;
1753 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1768 if ( isalphaW(*start
) ||
1770 /* user/password only characters */
1775 /* *extra* characters */
1778 (*start
== L
'\'') ||
1782 /* *safe* characters */
1790 } else if (*start
== L
'%') {
1791 if (isxdigitW(*(start
+1)) &&
1792 isxdigitW(*(start
+2))) {
1804 if (isdigitW(*start
)) {
1815 if (isalnumW(*start
) ||
1817 (*start
== L
'.') ) {
1826 FIXME("unknown type %d\n", type
);
1827 return (LPWSTR
)&alwayszero
;
1829 /* TRACE("scanned %ld characters next char %p<%c>\n",
1830 *size, start, *start); */
1834 /*************************************************************************
1835 * Attempt to parse URL into pieces.
1837 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
1841 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
1842 pl
->pScheme
= pszUrl
;
1843 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
1844 if (!*work
|| (*work
!= L
':')) goto ERROR
;
1846 if ((*work
!= L
'/') || (*(work
+1) != L
'/')) goto ERROR
;
1847 pl
->pUserName
= work
+ 2;
1848 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
1849 if (*work
== L
':' ) {
1850 /* parse password */
1852 pl
->pPassword
= work
;
1853 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
1854 if (*work
!= L
'@') {
1855 /* what we just parsed must be the hostname and port
1856 * so reset pointers and clear then let it parse */
1857 pl
->szUserName
= pl
->szPassword
= 0;
1858 work
= pl
->pUserName
- 1;
1859 pl
->pUserName
= pl
->pPassword
= 0;
1861 } else if (*work
== L
'@') {
1865 } else if (!*work
|| (*work
== L
'/') || (*work
== L
'.')) {
1866 /* what was parsed was hostname, so reset pointers and let it parse */
1867 pl
->szUserName
= pl
->szPassword
= 0;
1868 work
= pl
->pUserName
- 1;
1869 pl
->pUserName
= pl
->pPassword
= 0;
1872 /* now start parsing hostname or hostnumber */
1874 pl
->pHostName
= work
;
1875 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
1876 if (*work
== L
':') {
1880 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
1882 if (*work
== L
'/') {
1883 /* see if query string */
1884 pl
->pQuery
= strchrW(work
, L
'?');
1885 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
1887 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1888 pl
->pScheme
, pl
->szScheme
,
1889 pl
->pUserName
, pl
->szUserName
,
1890 pl
->pPassword
, pl
->szPassword
,
1891 pl
->pHostName
, pl
->szHostName
,
1892 pl
->pPort
, pl
->szPort
,
1893 pl
->pQuery
, pl
->szQuery
);
1896 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
1897 return E_INVALIDARG
;
1900 /*************************************************************************
1901 * UrlGetPartA [SHLWAPI.@]
1903 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
1904 DWORD dwPart
, DWORD dwFlags
)
1907 DWORD ret
, len
, len2
;
1909 in
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
1910 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1911 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1913 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1915 len
= INTERNET_MAX_URL_LENGTH
;
1916 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
1919 HeapFree(GetProcessHeap(), 0, in
);
1923 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
1924 if (len2
> *pcchOut
) {
1926 HeapFree(GetProcessHeap(), 0, in
);
1929 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1931 HeapFree(GetProcessHeap(), 0, in
);
1935 /*************************************************************************
1936 * UrlGetPartW [SHLWAPI.@]
1938 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
1939 DWORD dwPart
, DWORD dwFlags
)
1943 DWORD size
, schsize
;
1944 LPCWSTR addr
, schaddr
;
1947 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
1948 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
1950 ret
= URL_ParseUrl(pszIn
, &pl
);
1952 schaddr
= pl
.pScheme
;
1953 schsize
= pl
.szScheme
;
1956 case URL_PART_SCHEME
:
1957 if (!pl
.szScheme
) return E_INVALIDARG
;
1962 case URL_PART_HOSTNAME
:
1963 if (!pl
.szHostName
) return E_INVALIDARG
;
1964 addr
= pl
.pHostName
;
1965 size
= pl
.szHostName
;
1968 case URL_PART_USERNAME
:
1969 if (!pl
.szUserName
) return E_INVALIDARG
;
1970 addr
= pl
.pUserName
;
1971 size
= pl
.szUserName
;
1974 case URL_PART_PASSWORD
:
1975 if (!pl
.szPassword
) return E_INVALIDARG
;
1976 addr
= pl
.pPassword
;
1977 size
= pl
.szPassword
;
1981 if (!pl
.szPort
) return E_INVALIDARG
;
1986 case URL_PART_QUERY
:
1987 if (!pl
.szQuery
) return E_INVALIDARG
;
1993 return E_INVALIDARG
;
1996 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
1997 if (*pcchOut
< size
+ schsize
+ 2) {
1998 *pcchOut
= size
+ schsize
+ 2;
2001 strncpyW(pszOut
, schaddr
, schsize
);
2002 work
= pszOut
+ schsize
;
2004 strncpyW(work
+1, addr
, size
);
2005 *pcchOut
= size
+ schsize
+ 1;
2010 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2011 strncpyW(pszOut
, addr
, size
);
2013 work
= pszOut
+ size
;
2016 TRACE("len=%ld %s\n", *pcchOut
, debugstr_w(pszOut
));
2021 /*************************************************************************
2022 * PathIsURLA [SHLWAPI.@]
2024 * Check if the given path is a URL.
2027 * lpszPath [I] Path to check.
2030 * TRUE if lpszPath is a URL.
2031 * FALSE if lpszPath is NULL or not a URL.
2033 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2035 UNKNOWN_SHLWAPI_1 base
;
2038 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2041 base
.size
= sizeof(base
);
2042 res1
= ParseURLA(lpstrPath
, &base
);
2043 return (base
.fcncde
> 0);
2046 /*************************************************************************
2047 * PathIsURLW [SHLWAPI.@]
2051 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2053 UNKNOWN_SHLWAPI_2 base
;
2056 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2059 base
.size
= sizeof(base
);
2060 res1
= ParseURLW(lpstrPath
, &base
);
2061 return (base
.fcncde
> 0);
2064 /*************************************************************************
2065 * UrlCreateFromPathA [SHLWAPI.@]
2067 * Create a Url from a file path.
2070 * pszPath [I] Path to convert
2071 * pszUrl [O] Destination for the converted Url
2072 * pcchUrl [I/O] Length of pszUrl
2073 * dwReserved [I] Reserved, must be 0
2076 * Success: S_OK. pszUrl contains the converted path.
2077 * Failure: An HRESULT error code.
2079 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2081 DWORD nCharBeforeColon
= 0;
2083 DWORD dwChRequired
= 0;
2084 LPSTR pszNewUrl
= NULL
;
2085 LPCSTR pszConstPointer
= NULL
;
2086 LPSTR pszPointer
= NULL
;
2090 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2092 /* Validate arguments */
2093 if (dwReserved
!= 0)
2095 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved
);
2096 return E_INVALIDARG
;
2098 if (!pszUrl
|| !pcchUrl
|| !pszUrl
)
2100 ERR("Invalid argument\n");
2101 return E_INVALIDARG
;
2104 for (pszConstPointer
= pszPath
; *pszConstPointer
; pszConstPointer
++)
2106 if (isalpha(*pszConstPointer
) || isdigit(*pszConstPointer
) ||
2107 *pszConstPointer
== '.' || *pszConstPointer
== '-')
2111 if (*pszConstPointer
== ':') /* then already in URL format, so copy */
2113 dwChRequired
= lstrlenA(pszPath
);
2114 if (dwChRequired
> *pcchUrl
)
2116 *pcchUrl
= dwChRequired
;
2121 *pcchUrl
= dwChRequired
;
2122 StrCpyA(pszUrl
, pszPath
);
2126 /* then must need converting to file: format */
2128 /* Strip off leading slashes */
2129 while (*pszPath
== '\\' || *pszPath
== '/')
2135 dwChRequired
= *pcchUrl
; /* UrlEscape will fill this in with the correct amount */
2136 TRACE("pszUrl: %s\n", debugstr_a(pszPath
));
2137 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, dwChRequired
+ 1);
2138 ret
= UrlEscapeA(pszPath
, pszNewUrl
, &dwChRequired
, URL_ESCAPE_PERCENT
);
2139 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret
, debugstr_a(pszNewUrl
));
2140 TRACE("%ld\n", dwChRequired
);
2141 if (ret
!= E_POINTER
&& FAILED(ret
))
2143 dwChRequired
+= 5; /* "file:" */
2144 if ((lstrlenA(pszUrl
) > 1) && isalpha(pszUrl
[0]) && (pszUrl
[1] == ':'))
2146 dwChRequired
+= 3; /* "///" */
2152 case 0: /* no slashes */
2154 case 2: /* two slashes */
2161 default: /* three slashes */
2166 if (dwChRequired
> *pcchUrl
)
2168 *pcchUrl
= dwChRequired
; /* Return number of chars required (not including termination) */
2169 StrCpyA(pszUrl
, "file:");
2170 pszPointer
= pszUrl
+ lstrlenA(pszUrl
);
2171 for (i
=0; i
< nSlashes
; i
++)
2176 StrCpyA(pszPointer
, pszNewUrl
);
2177 TRACE("<- %s\n", debugstr_a(pszUrl
));
2181 /*************************************************************************
2182 * UrlCreateFromPathW [SHLWAPI.@]
2184 * See UrlCreateFromPathA.
2186 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2188 DWORD nCharBeforeColon
= 0;
2190 DWORD dwChRequired
= 0;
2191 LPWSTR pszNewUrl
= NULL
;
2192 LPCWSTR pszConstPointer
= NULL
;
2193 LPWSTR pszPointer
= NULL
;
2197 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2199 /* Validate arguments */
2200 if (dwReserved
!= 0)
2201 return E_INVALIDARG
;
2202 if (!pszUrl
|| !pcchUrl
|| !pszUrl
)
2203 return E_INVALIDARG
;
2205 for (pszConstPointer
= pszPath
; *pszConstPointer
; pszConstPointer
++)
2207 if (isalphaW(*pszConstPointer
) || isdigitW(*pszConstPointer
) ||
2208 *pszConstPointer
== '.' || *pszConstPointer
== '-')
2212 if (*pszConstPointer
== ':') /* then already in URL format, so copy */
2214 dwChRequired
= lstrlenW(pszPath
);
2215 *pcchUrl
= dwChRequired
;
2216 if (dwChRequired
> *pcchUrl
)
2220 StrCpyW(pszUrl
, pszPath
);
2224 /* then must need converting to file: format */
2226 /* Strip off leading slashes */
2227 while (*pszPath
== '\\' || *pszPath
== '/')
2233 dwChRequired
= *pcchUrl
; /* UrlEscape will fill this in with the correct amount */
2234 ret
= UrlEscapeW(pszPath
, pszUrl
, &dwChRequired
, URL_ESCAPE_PERCENT
);
2235 if (ret
!= E_POINTER
&& FAILED(ret
))
2237 dwChRequired
+= 5; /* "file:" */
2238 if ((lstrlenW(pszUrl
) > 1) && isalphaW(pszUrl
[0]) && (pszUrl
[1] == ':'))
2240 dwChRequired
+= 3; /* "///" */
2246 case 0: /* no slashes */
2248 case 2: /* two slashes */
2255 default: /* three slashes */
2260 *pcchUrl
= dwChRequired
; /* Return number of chars required (not including termination) */
2261 if (dwChRequired
> *pcchUrl
)
2263 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (dwChRequired
+ 1) * sizeof(WCHAR
));
2264 StrCpyW(pszNewUrl
, fileW
);
2265 pszPointer
= pszNewUrl
+ 4;
2268 for (i
=0; i
< nSlashes
; i
++)
2273 StrCpyW(pszPointer
, pszPath
);
2274 StrCpyW(pszUrl
, pszNewUrl
);
2278 /*************************************************************************
2279 * SHAutoComplete [SHLWAPI.@]
2281 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2283 FIXME("SHAutoComplete stub\n");