4 * Copyright 2000 Huw D M Davies for CodeWeavers.
12 #include "wine/unicode.h"
16 #include "debugtools.h"
19 DEFAULT_DEBUG_CHANNEL(shell
);
22 LPCWSTR pScheme
; /* [out] start of scheme */
23 DWORD szScheme
; /* [out] size of scheme (until colon) */
24 LPCWSTR pUserName
; /* [out] start of Username */
25 DWORD szUserName
; /* [out] size of Username (until ":" or "@") */
26 LPCWSTR pPassword
; /* [out] start of Password */
27 DWORD szPassword
; /* [out] size of Password (until "@") */
28 LPCWSTR pHostName
; /* [out] start of Hostname */
29 DWORD szHostName
; /* [out] size of Hostname (until ":" or "/") */
30 LPCWSTR pPort
; /* [out] start of Port */
31 DWORD szPort
; /* [out] size of Port (until "/" or eos) */
32 LPCWSTR pQuery
; /* [out] start of Query */
33 DWORD szQuery
; /* [out] size of Query (until eos) */
43 static const WCHAR fileW
[] = {'f','i','l','e','\0'};
45 static const unsigned char HashDataLookup
[256] = {
46 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
47 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
48 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
49 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
50 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
51 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
52 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
53 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
54 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
55 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
56 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
57 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
58 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
59 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
60 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
61 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
62 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
63 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
64 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
65 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
67 static BOOL
URL_NeedEscapeA(CHAR ch
, DWORD dwFlags
)
73 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
80 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
83 if (ch
<= 31 || ch
>= 127)
105 if (dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) return TRUE
;
112 static BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
)
118 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
125 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== L
'%'))
128 if (ch
<= 31 || ch
>= 127)
150 if (dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) return TRUE
;
157 static BOOL
URL_JustLocation(LPCWSTR str
)
159 while(*str
&& (*str
== L
'/')) str
++;
161 while (*str
&& ((*str
== L
'-') ||
163 isalnumW(*str
))) str
++;
164 if (*str
== L
'/') return FALSE
;
170 /*************************************************************************
171 * UrlCanonicalizeA [SHLWAPI.@]
173 * Uses the W version to do job.
175 HRESULT WINAPI
UrlCanonicalizeA(LPCSTR pszUrl
, LPSTR pszCanonicalized
,
176 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
178 LPWSTR base
, canonical
;
179 DWORD ret
, len
, len2
;
181 TRACE("(%s %p %p 0x%08lx) using W version\n",
182 debugstr_a(pszUrl
), pszCanonicalized
,
183 pcchCanonicalized
, dwFlags
);
185 base
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
186 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
187 canonical
= base
+ INTERNET_MAX_URL_LENGTH
;
189 MultiByteToWideChar(0, 0, pszUrl
, -1, base
, INTERNET_MAX_URL_LENGTH
);
190 len
= INTERNET_MAX_URL_LENGTH
;
192 ret
= UrlCanonicalizeW(base
, canonical
, &len
, dwFlags
);
194 HeapFree(GetProcessHeap(), 0, base
);
198 len2
= WideCharToMultiByte(0, 0, canonical
, len
, 0, 0, 0, 0);
199 if (len2
> *pcchCanonicalized
) {
200 *pcchCanonicalized
= len
;
201 HeapFree(GetProcessHeap(), 0, base
);
204 WideCharToMultiByte(0, 0, canonical
, len
+1, pszCanonicalized
,
205 *pcchCanonicalized
, 0, 0);
206 *pcchCanonicalized
= len2
;
207 HeapFree(GetProcessHeap(), 0, base
);
211 /*************************************************************************
212 * UrlCanonicalizeW [SHLWAPI.@]
215 * MSDN is wrong (at 10/30/01 - go figure). This should support the
216 * following flags: GLA
217 * URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
218 * URL_ESCAPE_SPACES_ONLY 0x04000000
219 * URL_ESCAPE_PERCENT 0x00001000
220 * URL_ESCAPE_UNSAFE 0x10000000
221 * URL_UNESCAPE 0x10000000
222 * URL_DONT_SIMPLIFY 0x08000000
223 * URL_ESCAPE_SEGMENT_ONLY 0x00002000
225 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
226 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
230 LPWSTR lpszUrlCpy
, wk1
, wk2
, mp
, root
;
231 INT nLen
, nByteLen
, state
;
233 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszCanonicalized
,
234 pcchCanonicalized
, dwFlags
);
236 nByteLen
= (lstrlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
237 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
239 if (dwFlags
& URL_DONT_SIMPLIFY
)
240 memcpy(lpszUrlCpy
, pszUrl
, nByteLen
);
246 * 1 have 2[+] alnum 2,3
247 * 2 have scheme (found :) 4,6,3
248 * 3 failed (no location)
250 * 5 have 1[+] alnum 6,3
251 * 6 have location (found /) save root location
254 wk1
= (LPWSTR
)pszUrl
;
260 if (!isalnumW(*wk1
)) {state
= 3; break;}
262 if (!isalnumW(*wk1
)) {state
= 3; break;}
268 if (*wk1
++ == L
':') state
= 2;
271 if (*wk1
!= L
'/') {state
= 3; break;}
273 if (*wk1
!= L
'/') {state
= 6; break;}
283 if (!isalnumW(*wk1
) && (*wk1
!= L
'-')) {state
= 3; break;}
284 while(isalnumW(*wk1
) || (*wk1
== L
'-')) *wk2
++ = *wk1
++;
288 if (*wk1
!= L
'/') {state
= 3; break;}
293 /* Now at root location, cannot back up any more. */
294 /* "root" will point at the '/' */
297 TRACE("wk1=%c\n", (CHAR
)*wk1
);
298 mp
= strchrW(wk1
, L
'/');
306 strncpyW(wk2
, wk1
, nLen
);
310 TRACE("found '/.'\n");
311 if (*(wk1
+1) == L
'/') {
312 /* case of /./ -> skip the ./ */
315 else if (*(wk1
+1) == L
'.') {
316 /* found /.. look for next / */
317 TRACE("found '/..'\n");
318 if (*(wk1
+2) == L
'/') {
319 /* case /../ -> need to backup wk2 */
320 TRACE("found '/../'\n");
321 *(wk2
-1) = L
'\0'; /* set end of string */
322 mp
= strrchrW(root
, L
'/');
323 if (mp
&& (mp
>= root
)) {
324 /* found valid backup point */
329 /* did not find point, restore '/' */
339 FIXME("how did we get here - state=%d\n", state
);
344 TRACE("Simplified, orig <%s>, simple <%s>\n",
345 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
348 if(dwFlags
& URL_UNESCAPE
)
349 UrlUnescapeW(lpszUrlCpy
, NULL
, NULL
, URL_UNESCAPE_INPLACE
);
351 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
352 URL_ESCAPE_SPACES_ONLY
|
354 URL_DONT_ESCAPE_EXTRA_INFO
|
355 URL_ESCAPE_SEGMENT_ONLY
))) {
356 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
357 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
359 } else { /* No escaping needed, just copy the string */
360 nLen
= lstrlenW(lpszUrlCpy
);
361 if(nLen
< *pcchCanonicalized
)
362 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
367 *pcchCanonicalized
= nLen
;
370 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
373 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
378 /*************************************************************************
379 * UrlCombineA [SHLWAPI.@]
381 * Uses the W version to do job.
383 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
384 LPSTR pszCombined
, LPDWORD pcchCombined
,
387 LPWSTR base
, relative
, combined
;
388 DWORD ret
, len
, len2
;
390 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
391 debugstr_a(pszBase
),debugstr_a(pszRelative
),
392 *pcchCombined
,dwFlags
);
394 base
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
395 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
396 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
397 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
399 MultiByteToWideChar(0, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
400 MultiByteToWideChar(0, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
401 len
= INTERNET_MAX_URL_LENGTH
;
403 ret
= UrlCombineW(base
, relative
, combined
, &len
, dwFlags
);
405 HeapFree(GetProcessHeap(), 0, base
);
409 len2
= WideCharToMultiByte(0, 0, combined
, len
, 0, 0, 0, 0);
410 if (len2
> *pcchCombined
) {
411 *pcchCombined
= len2
;
412 HeapFree(GetProcessHeap(), 0, base
);
415 WideCharToMultiByte(0, 0, combined
, len
+1, pszCombined
, *pcchCombined
,
417 *pcchCombined
= len2
;
418 HeapFree(GetProcessHeap(), 0, base
);
422 /*************************************************************************
423 * UrlCombineW [SHLWAPI.@]
425 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
426 LPWSTR pszCombined
, LPDWORD pcchCombined
,
429 UNKNOWN_SHLWAPI_2 base
, relative
;
430 DWORD myflags
, sizeloc
= 0;
431 DWORD len
, res1
, res2
, process_case
= 0;
432 LPWSTR work
, preliminary
, mbase
, mrelative
;
433 WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
434 WCHAR single_slash
[] = {'/','\0'};
437 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
438 debugstr_w(pszBase
),debugstr_w(pszRelative
),
439 *pcchCombined
,dwFlags
);
444 /* Get space for duplicates of the input and the output */
445 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
447 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
448 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
449 *preliminary
= L
'\0';
451 /* Canonicalize the base input prior to looking for the scheme */
452 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
453 len
= INTERNET_MAX_URL_LENGTH
;
454 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
456 /* Canonicalize the relative input prior to looking for the scheme */
457 len
= INTERNET_MAX_URL_LENGTH
;
458 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
460 /* See if the base has a scheme */
461 res1
= SHLWAPI_2(mbase
, &base
);
463 /* if pszBase has no scheme, then return pszRelative */
464 TRACE("no scheme detected in Base\n");
469 /* get size of location field (if it exists) */
470 work
= (LPWSTR
)base
.ap2
;
472 if (*work
++ == L
'/') {
473 if (*work
++ == L
'/') {
474 /* At this point have start of location and
475 * it ends at next '/' or end of string.
477 while(*work
&& (*work
!= L
'/')) work
++;
478 sizeloc
= work
- base
.ap2
;
482 /* Change .sizep2 to not have the last leaf in it,
483 * Note: we need to start after the location (if it exists)
485 work
= strrchrW((base
.ap2
+sizeloc
), L
'/');
487 len
= work
- base
.ap2
+ 1;
492 * .ap2 points to location (starting with '//')
493 * .sizep2 length of location (above) and rest less the last
495 * sizeloc length of location (above) up to but not including
499 res2
= SHLWAPI_2(mrelative
, &relative
);
501 /* no scheme in pszRelative */
502 TRACE("no scheme detected in Relative\n");
503 relative
.ap2
= mrelative
; /* case 3,4,5 depends on this */
504 relative
.sizep2
= strlenW(mrelative
);
505 if (*pszRelative
== L
':') {
506 /* case that is either left alone or uses pszBase */
507 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
514 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == L
':')) {
515 /* case that becomes "file:///" */
516 strcpyW(preliminary
, myfilestr
);
520 if ((*mrelative
== L
'/') && (*(mrelative
+1) == L
'/')) {
521 /* pszRelative has location and rest */
525 if (*mrelative
== L
'/') {
526 /* case where pszRelative is root to location */
530 process_case
= (*base
.ap2
== L
'/') ? 5 : 3;
534 /* handle cases where pszRelative has scheme */
535 if ((base
.sizep1
== relative
.sizep1
) &&
536 (strncmpW(base
.ap1
, relative
.ap1
, base
.sizep1
) == 0)) {
538 /* since the schemes are the same */
539 if ((*relative
.ap2
== L
'/') && (*(relative
.ap2
+1) == L
'/')) {
540 /* case where pszRelative replaces location and following */
544 if (*relative
.ap2
== L
'/') {
545 /* case where pszRelative is root to location */
549 /* case where scheme is followed by document path */
553 if ((*relative
.ap2
== L
'/') && (*(relative
.ap2
+1) == L
'/')) {
554 /* case where pszRelative replaces scheme, location,
555 * and following and handles PLUGGABLE
562 } while(FALSE
); /* a litte trick to allow easy exit from nested if's */
566 switch (process_case
) {
569 * Return pszRelative appended to what ever is in pszCombined,
570 * (which may the string "file:///"
572 len
= strlenW(mrelative
) + strlenW(preliminary
);
573 if (len
+1 > *pcchCombined
) {
578 strcatW(preliminary
, mrelative
);
582 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
583 * and pszRelative starts with "//", then append a "/"
585 len
= strlenW(mrelative
) + 1;
586 if (len
+1 > *pcchCombined
) {
591 strcpyW(preliminary
, mrelative
);
592 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
593 URL_JustLocation(relative
.ap2
))
594 strcatW(preliminary
, single_slash
);
598 * Return the pszBase scheme with pszRelative. Basicly
599 * keeps the scheme and replaces the domain and following.
601 len
= base
.sizep1
+ 1 + relative
.sizep2
+ 1;
602 if (len
+1 > *pcchCombined
) {
607 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+ 1);
608 work
= preliminary
+ base
.sizep1
+ 1;
609 strcpyW(work
, relative
.ap2
);
610 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
611 URL_JustLocation(relative
.ap2
))
612 strcatW(work
, single_slash
);
616 * Return the pszBase scheme and location but everything
617 * after the location is pszRelative. (Replace document
620 len
= base
.sizep1
+ 1 + sizeloc
+ relative
.sizep2
+ 1;
621 if (len
+1 > *pcchCombined
) {
626 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+1+sizeloc
);
627 work
= preliminary
+ base
.sizep1
+ 1 + sizeloc
;
628 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
630 strcpyW(work
, relative
.ap2
);
634 * Return the pszBase without its document (if any) and
635 * append pszRelative after its scheme.
637 len
= base
.sizep1
+ 1 + base
.sizep2
+ relative
.sizep2
;
638 if (len
+1 > *pcchCombined
) {
643 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+1+base
.sizep2
);
644 work
= preliminary
+ base
.sizep1
+1+base
.sizep2
- 1;
647 strcpyW(work
, relative
.ap2
);
651 FIXME("How did we get here????? process_case=%ld\n", process_case
);
657 * Now that the combining is done, process the escape options if
658 * necessary, otherwise just copy the string.
660 myflags
= dwFlags
& (URL_ESCAPE_PERCENT
|
661 URL_ESCAPE_SPACES_ONLY
|
662 URL_DONT_ESCAPE_EXTRA_INFO
|
663 URL_ESCAPE_SEGMENT_ONLY
);
665 ret
= UrlEscapeW(preliminary
, pszCombined
,
666 pcchCombined
, myflags
);
668 len
= (strlenW(preliminary
) + 1) * sizeof(WCHAR
);
669 memcpy(pszCombined
, preliminary
, len
);
670 *pcchCombined
= strlenW(preliminary
);
672 TRACE("return-%ld len=%ld, %s\n",
673 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
675 HeapFree(GetProcessHeap(), 0, preliminary
);
679 /*************************************************************************
680 * UrlEscapeA [SHLWAPI.@]
682 * Converts unsafe characters into their escape sequences.
684 * The converted string is returned in pszEscaped if the buffer size
685 * (which should be supplied in pcchEscaped) is large enough, in this
686 * case the function returns S_OK and pcchEscaped contains the length
687 * of the escaped string. If the buffer is not large enough the
688 * function returns E_POINTER and pcchEscaped contains the required
689 * buffer size (including room for the '\0').
691 * By default the function stops converting at the first '?' or
692 * '#'. [MSDN says differently]. If URL_ESCAPE_SPACE_ONLY flag is set
693 * then only spaces are converted, but the conversion continues past a
697 * Have now implemented the following flags:
698 * URL_ESCAPE_SPACES_ONLY
699 * URL_DONT_ESCAPE_EXTRA_INFO
700 * URL_ESCAPE_SEGMENT_ONLY
702 * Initial testing seems to indicate that this is now working like
703 * native shlwapi version 5. Note that these functions did not work
704 * well (or at all) in shlwapi version 4.
707 HRESULT WINAPI
UrlEscapeA(
714 DWORD needed
= 0, ret
;
715 BOOL stop_escaping
= FALSE
;
716 char next
[3], *dst
= pszEscaped
;
717 char hex
[] = "0123456789ABCDEF";
720 TRACE("(%s %p %p 0x%08lx)\n", debugstr_a(pszUrl
), pszEscaped
,
721 pcchEscaped
, dwFlags
);
723 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
724 URL_ESCAPE_SEGMENT_ONLY
|
725 URL_DONT_ESCAPE_EXTRA_INFO
|
727 FIXME("Unimplemented flags: %08lx\n", dwFlags
);
730 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
731 /* if SPACES_ONLY specified, reset the other controls */
732 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
734 URL_ESCAPE_SEGMENT_ONLY
);
737 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
738 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
740 for(src
= pszUrl
; *src
; src
++) {
741 if(!(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) &&
742 (dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
) &&
743 (*src
== '#' || *src
== '?'))
744 stop_escaping
= TRUE
;
746 if(URL_NeedEscapeA(*src
, dwFlags
) && stop_escaping
== FALSE
) {
747 /* TRACE("escaping %c\n", *src); */
749 next
[1] = hex
[(*src
>> 4) & 0xf];
750 next
[2] = hex
[*src
& 0xf];
753 /* TRACE("passing %c\n", *src); */
758 if(needed
+ len
<= *pcchEscaped
) {
759 memcpy(dst
, next
, len
);
765 if(needed
< *pcchEscaped
) {
769 needed
++; /* add one for the '\0' */
772 *pcchEscaped
= needed
;
776 /*************************************************************************
777 * UrlEscapeW [SHLWAPI.@]
779 * See UrlEscapeA for list of assumptions, bugs, and FIXMEs
781 HRESULT WINAPI
UrlEscapeW(
788 DWORD needed
= 0, ret
;
789 BOOL stop_escaping
= FALSE
;
790 WCHAR next
[5], *dst
= pszEscaped
;
791 CHAR hex
[] = "0123456789ABCDEF";
794 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszEscaped
,
795 pcchEscaped
, dwFlags
);
797 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
798 URL_ESCAPE_SEGMENT_ONLY
|
799 URL_DONT_ESCAPE_EXTRA_INFO
|
801 FIXME("Unimplemented flags: %08lx\n", dwFlags
);
804 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
805 /* if SPACES_ONLY specified, reset the other controls */
806 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
808 URL_ESCAPE_SEGMENT_ONLY
);
811 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
812 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
814 for(src
= pszUrl
; *src
; src
++) {
816 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
817 * (*src == L'#' || *src == L'?'))
818 * stop_escaping = TRUE;
820 if(!(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) &&
821 (dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
) &&
822 (*src
== L
'#' || *src
== L
'?'))
823 stop_escaping
= TRUE
;
825 if(URL_NeedEscapeW(*src
, dwFlags
) && stop_escaping
== FALSE
) {
826 /* TRACE("escaping %c\n", *src); */
829 * I would have assumed that the W form would escape
830 * the character with 4 hex digits (or even 8),
831 * however, experiments show that native shlwapi escapes
832 * with only 2 hex digits.
833 * next[1] = hex[(*src >> 12) & 0xf];
834 * next[2] = hex[(*src >> 8) & 0xf];
835 * next[3] = hex[(*src >> 4) & 0xf];
836 * next[4] = hex[*src & 0xf];
839 next
[1] = hex
[(*src
>> 4) & 0xf];
840 next
[2] = hex
[*src
& 0xf];
843 /* TRACE("passing %c\n", *src); */
848 if(needed
+ len
<= *pcchEscaped
) {
849 memcpy(dst
, next
, len
*sizeof(WCHAR
));
855 if(needed
< *pcchEscaped
) {
859 needed
++; /* add one for the '\0' */
862 *pcchEscaped
= needed
;
867 /*************************************************************************
868 * UrlUnescapeA [SHLWAPI.@]
870 * Converts escape sequences back to ordinary characters.
872 * If URL_ESCAPE_INPLACE is set in dwFlags then pszUnescaped and
873 * pcchUnescaped are ignored and the converted string is returned in
874 * pszUrl, otherwise the string is returned in pszUnescaped.
875 * pcchUnescaped should contain the size of pszUnescaped on calling
876 * and will contain the length the the returned string on return if
877 * the buffer is big enough else it will contain the buffer size
878 * required (including room for the '\0'). The function returns S_OK
879 * on success or E_POINTER if the buffer is not large enough. If the
880 * URL_DONT_ESCAPE_EXTRA_INFO flag is set then the conversion stops at
881 * the first occurrence of either '?' or '#'.
884 HRESULT WINAPI
UrlUnescapeA(
887 LPDWORD pcchUnescaped
,
894 BOOL stop_unescaping
= FALSE
;
896 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl
), pszUnescaped
,
897 pcchUnescaped
, dwFlags
);
899 if(dwFlags
& URL_UNESCAPE_INPLACE
)
904 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
905 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
906 (*src
== '#' || *src
== '?')) {
907 stop_unescaping
= TRUE
;
909 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
910 && stop_unescaping
== FALSE
) {
913 memcpy(buf
, src
+ 1, 2);
915 ih
= strtol(buf
, NULL
, 16);
917 src
+= 2; /* Advance to end of escape */
921 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
925 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
929 needed
++; /* add one for the '\0' */
932 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
933 *pcchUnescaped
= needed
;
936 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
937 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
943 /*************************************************************************
944 * UrlUnescapeW [SHLWAPI.@]
946 * See UrlUnescapeA for list of assumptions, bugs, and FIXMEs
948 HRESULT WINAPI
UrlUnescapeW(
951 LPDWORD pcchUnescaped
,
958 BOOL stop_unescaping
= FALSE
;
960 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl
), pszUnescaped
,
961 pcchUnescaped
, dwFlags
);
963 if(dwFlags
& URL_UNESCAPE_INPLACE
)
964 dst
= (WCHAR
*)pszUrl
;
968 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
969 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
970 (*src
== L
'#' || *src
== L
'?')) {
971 stop_unescaping
= TRUE
;
973 } else if(*src
== L
'%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
974 && stop_unescaping
== FALSE
) {
977 memcpy(buf
, src
+ 1, 2*sizeof(WCHAR
));
981 src
+= 2; /* Advance to end of escape */
985 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
989 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
993 needed
++; /* add one for the '\0' */
996 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
997 *pcchUnescaped
= needed
;
1000 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1001 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1007 /*************************************************************************
1008 * UrlGetLocationA [SHLWAPI.@]
1011 * MSDN (as of 2001-11-01) says that:
1012 * "The location is the segment of the URL starting with a ?
1014 * Neither V4 nor V5 of shlwapi.dll implement the '?' and always return
1016 * MSDN further states that:
1017 * "If a file URL has a query string, ther returned string
1018 * the query string."
1019 * In all test cases if the scheme starts with "fi" then a NULL is
1020 * returned. V5 gives the following results:
1021 * NULL file://aa/b/cd#hohoh
1022 * #hohoh http://aa/b/cd#hohoh
1023 * NULL fi://aa/b/cd#hohoh
1024 * #hohoh ff://aa/b/cd#hohoh
1026 LPCSTR WINAPI
UrlGetLocationA(
1029 UNKNOWN_SHLWAPI_1 base
;
1033 res1
= SHLWAPI_1(pszUrl
, &base
);
1034 if (res1
) return NULL
; /* invalid scheme */
1036 /* if scheme is file: then never return pointer */
1037 if (strncmp(base
.ap1
, "file", min(4,base
.sizep1
)) == 0) return NULL
;
1039 /* Look for '#' and return its addr */
1040 return strchr(base
.ap2
, '#');
1043 /*************************************************************************
1044 * UrlGetLocationW [SHLWAPI.@]
1046 * See UrlGetLocationA for list of assumptions, bugs, and FIXMEs
1048 LPCWSTR WINAPI
UrlGetLocationW(
1051 UNKNOWN_SHLWAPI_2 base
;
1055 res1
= SHLWAPI_2(pszUrl
, &base
);
1056 if (res1
) return NULL
; /* invalid scheme */
1058 /* if scheme is file: then never return pointer */
1059 if (strncmpW(base
.ap1
, fileW
, min(4,base
.sizep1
)) == 0) return NULL
;
1061 /* Look for '#' and return its addr */
1062 return strchrW(base
.ap2
, L
'#');
1065 /*************************************************************************
1066 * HashData [SHLWAPI.@]
1068 * Hash an input block into a variable sized digest.
1070 BOOL WINAPI
HashData(const unsigned char *lpSrc
, INT nSrcLen
,
1071 unsigned char *lpDest
, INT nDestLen
)
1073 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1075 if (IsBadReadPtr(lpSrc
, nSrcLen
) ||
1076 IsBadWritePtr(lpDest
, nDestLen
))
1079 while (destCount
>= 0)
1081 lpDest
[destCount
] = (destCount
& 0xff);
1085 while (srcCount
>= 0)
1087 destCount
= nDestLen
- 1;
1088 while (destCount
>= 0)
1090 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1098 /*************************************************************************
1099 * UrlHashA [SHLWAPI.@]
1101 * Hash an ASCII URL.
1103 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, INT nDestLen
)
1105 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1106 return E_INVALIDARG
;
1108 HashData(pszUrl
, strlen(pszUrl
), lpDest
, nDestLen
);
1112 /*************************************************************************
1113 * UrlApplySchemeW [SHLWAPI.@]
1115 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1117 HRESULT err
= NOERROR
;
1118 FIXME("(%s %p %p %08lx): stub !\n", debugstr_w(pszIn
), pszOut
, pcchOut
, dwFlags
);
1119 strcpyW(pszOut
, pszIn
);
1120 *pcchOut
= (err
!= E_POINTER
) ? strlenW(pszOut
) : 0;
1124 /*************************************************************************
1125 * UrlIsA [SHLWAPI.@]
1127 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1129 UNKNOWN_SHLWAPI_1 base
;
1136 res1
= SHLWAPI_1(pszUrl
, &base
);
1137 if (res1
) return FALSE
; /* invalid scheme */
1138 if ((*base
.ap2
== '/') && (*(base
.ap2
+1) == '/'))
1139 /* has scheme followed by 2 '/' */
1144 case URLIS_NOHISTORY
:
1146 case URLIS_APPLIABLE
:
1147 case URLIS_DIRECTORY
:
1148 case URLIS_HASQUERY
:
1150 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1155 /*************************************************************************
1156 * UrlIsW [SHLWAPI.@]
1158 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1160 UNKNOWN_SHLWAPI_2 base
;
1167 res1
= SHLWAPI_2(pszUrl
, &base
);
1168 if (res1
) return FALSE
; /* invalid scheme */
1169 if ((*base
.ap2
== L
'/') && (*(base
.ap2
+1) == L
'/'))
1170 /* has scheme followed by 2 '/' */
1175 case URLIS_NOHISTORY
:
1177 case URLIS_APPLIABLE
:
1178 case URLIS_DIRECTORY
:
1179 case URLIS_HASQUERY
:
1181 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1186 /*************************************************************************
1187 * UrlIsNoHistoryA [SHLWAPI.@]
1189 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1191 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1194 /*************************************************************************
1195 * UrlIsNoHistoryW [SHLWAPI.@]
1197 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1199 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1202 /*************************************************************************
1203 * UrlIsOpaqueA [SHLWAPI.@]
1205 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1207 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1210 /*************************************************************************
1211 * UrlIsOpaqueW [SHLWAPI.@]
1213 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1215 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1218 /*************************************************************************
1219 * Scans for characters of type "type" and when not matching found,
1220 * returns pointer to it and length in size.
1222 * Characters tested based on RFC 1738
1224 LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1226 static DWORD alwayszero
= 0;
1235 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1250 if ( isalphaW(*start
) ||
1252 /* user/password only characters */
1257 /* *extra* characters */
1260 (*start
== L
'\'') ||
1264 /* *safe* characters */
1272 } else if (*start
== L
'%') {
1273 if (isxdigitW(*(start
+1)) &&
1274 isxdigitW(*(start
+2))) {
1286 if (isdigitW(*start
)) {
1297 if (isalnumW(*start
) ||
1299 (*start
== L
'.') ) {
1308 FIXME("unknown type %d\n", type
);
1309 return (LPWSTR
)&alwayszero
;
1311 /* TRACE("scanned %ld characters next char %p<%c>\n",
1312 *size, start, *start); */
1316 /*************************************************************************
1317 * Attempt to parse URL into pieces.
1319 LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
1323 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
1324 pl
->pScheme
= pszUrl
;
1325 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
1326 if (!*work
|| (*work
!= L
':')) goto ERROR
;
1328 if ((*work
!= L
'/') || (*(work
+1) != L
'/')) goto ERROR
;
1329 pl
->pUserName
= work
+ 2;
1330 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
1331 if (*work
== L
':' ) {
1332 /* parse password */
1334 pl
->pPassword
= work
;
1335 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
1336 if (*work
!= L
'@') {
1337 /* what we just parsed must be the hostname and port
1338 * so reset pointers and clear then let it parse */
1339 pl
->szUserName
= pl
->szPassword
= 0;
1340 work
= pl
->pUserName
- 1;
1341 pl
->pUserName
= pl
->pPassword
= 0;
1343 } else if (*work
== L
'@') {
1347 } else if (!*work
|| (*work
== L
'/') || (*work
== L
'.')) {
1348 /* what was parsed was hostname, so reset pointers and let it parse */
1349 pl
->szUserName
= pl
->szPassword
= 0;
1350 work
= pl
->pUserName
- 1;
1351 pl
->pUserName
= pl
->pPassword
= 0;
1354 /* now start parsing hostname or hostnumber */
1356 pl
->pHostName
= work
;
1357 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
1358 if (*work
== L
':') {
1362 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
1364 if (*work
== L
'/') {
1365 /* see if query string */
1366 pl
->pQuery
= strchrW(work
, L
'?');
1367 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
1369 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1370 pl
->pScheme
, pl
->szScheme
,
1371 pl
->pUserName
, pl
->szUserName
,
1372 pl
->pPassword
, pl
->szPassword
,
1373 pl
->pHostName
, pl
->szHostName
,
1374 pl
->pPort
, pl
->szPort
,
1375 pl
->pQuery
, pl
->szQuery
);
1378 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
1379 return E_INVALIDARG
;
1382 /*************************************************************************
1383 * UrlGetPartA [SHLWAPI.@]
1385 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
1386 DWORD dwPart
, DWORD dwFlags
)
1389 DWORD ret
, len
, len2
;
1391 in
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
1392 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1393 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1395 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1397 len
= INTERNET_MAX_URL_LENGTH
;
1398 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
1401 HeapFree(GetProcessHeap(), 0, in
);
1405 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
1406 if (len2
> *pcchOut
) {
1408 HeapFree(GetProcessHeap(), 0, in
);
1411 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1413 HeapFree(GetProcessHeap(), 0, in
);
1417 /*************************************************************************
1418 * UrlGetPartW [SHLWAPI.@]
1420 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
1421 DWORD dwPart
, DWORD dwFlags
)
1425 DWORD size
, schsize
;
1426 LPCWSTR addr
, schaddr
;
1429 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
1430 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
1432 ret
= URL_ParseUrl(pszIn
, &pl
);
1434 schaddr
= pl
.pScheme
;
1435 schsize
= pl
.szScheme
;
1438 case URL_PART_SCHEME
:
1439 if (!pl
.szScheme
) return E_INVALIDARG
;
1444 case URL_PART_HOSTNAME
:
1445 if (!pl
.szHostName
) return E_INVALIDARG
;
1446 addr
= pl
.pHostName
;
1447 size
= pl
.szHostName
;
1450 case URL_PART_USERNAME
:
1451 if (!pl
.szUserName
) return E_INVALIDARG
;
1452 addr
= pl
.pUserName
;
1453 size
= pl
.szUserName
;
1456 case URL_PART_PASSWORD
:
1457 if (!pl
.szPassword
) return E_INVALIDARG
;
1458 addr
= pl
.pPassword
;
1459 size
= pl
.szPassword
;
1463 if (!pl
.szPort
) return E_INVALIDARG
;
1468 case URL_PART_QUERY
:
1469 if (!pl
.szQuery
) return E_INVALIDARG
;
1475 return E_INVALIDARG
;
1478 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
1479 if (*pcchOut
< size
+ schsize
+ 2) {
1480 *pcchOut
= size
+ schsize
+ 2;
1483 strncpyW(pszOut
, schaddr
, schsize
);
1484 work
= pszOut
+ schsize
;
1486 strncpyW(work
+1, addr
, size
);
1487 *pcchOut
= size
+ schsize
+ 1;
1492 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
1493 strncpyW(pszOut
, addr
, size
);
1495 work
= pszOut
+ size
;
1498 TRACE("len=%ld %s\n", *pcchOut
, debugstr_w(pszOut
));