[PATCH 1/2] kernelbase: Add some URL API functions from shlwapi.
Nikolay Sivov
nsivov at codeweavers.com
Wed May 22 04:27:35 CDT 2019
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
dlls/kernelbase/kernelbase.spec | 26 +-
dlls/kernelbase/path.c | 1144 +++++++++++++++++++++++++++++++
2 files changed, 1157 insertions(+), 13 deletions(-)
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index 874db20c9c..f9f9f2487b 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -1018,8 +1018,8 @@
# @ stub PackageSidFromFamilyName
# @ stub PackageSidFromProductId
# @ stub ParseApplicationUserModelId
-@ stdcall ParseURLA(str ptr) shlwapi.ParseURLA
-@ stdcall ParseURLW(wstr ptr) shlwapi.ParseURLW
+@ stdcall ParseURLA(str ptr)
+@ stdcall ParseURLW(wstr ptr)
@ stdcall PathAddBackslashA(str)
@ stdcall PathAddBackslashW(wstr)
@ stdcall PathAddExtensionA(str str)
@@ -1053,9 +1053,9 @@
@ stdcall PathCombineW(ptr wstr wstr)
@ stdcall PathCommonPrefixA(str str ptr)
@ stdcall PathCommonPrefixW(wstr wstr ptr)
-@ stdcall PathCreateFromUrlA(str ptr ptr long) shlwapi.PathCreateFromUrlA
-@ stdcall PathCreateFromUrlAlloc(wstr ptr long) shlwapi.PathCreateFromUrlAlloc
-@ stdcall PathCreateFromUrlW(wstr ptr ptr long) shlwapi.PathCreateFromUrlW
+@ stdcall PathCreateFromUrlA(str ptr ptr long)
+@ stdcall PathCreateFromUrlAlloc(wstr ptr long)
+@ stdcall PathCreateFromUrlW(wstr ptr ptr long)
@ stdcall PathFileExistsA(str)
@ stdcall PathFileExistsW(wstr)
@ stdcall PathFindExtensionA(str)
@@ -1089,8 +1089,8 @@
@ stdcall PathIsUNCServerShareW(wstr)
@ stdcall PathIsUNCServerW(wstr)
@ stdcall PathIsUNCW(wstr)
-@ stdcall PathIsURLA(str) shlwapi.PathIsURLA
-@ stdcall PathIsURLW(wstr) shlwapi.PathIsURLW
+@ stdcall PathIsURLA(str)
+@ stdcall PathIsURLW(wstr)
@ stdcall PathIsValidCharA(long long)
@ stdcall PathIsValidCharW(long long)
@ stdcall PathMatchSpecA(str str)
@@ -1630,16 +1630,16 @@
@ stdcall UpdateProcThreadAttribute(ptr long long ptr long ptr ptr) kernel32.UpdateProcThreadAttribute
@ stdcall UrlApplySchemeA(str ptr ptr long) shlwapi.UrlApplySchemeA
@ stdcall UrlApplySchemeW(wstr ptr ptr long) shlwapi.UrlApplySchemeW
-@ stdcall UrlCanonicalizeA(str ptr ptr long) shlwapi.UrlCanonicalizeA
-@ stdcall UrlCanonicalizeW(wstr ptr ptr long) shlwapi.UrlCanonicalizeW
+@ stdcall UrlCanonicalizeA(str ptr ptr long)
+@ stdcall UrlCanonicalizeW(wstr ptr ptr long)
@ stdcall UrlCombineA(str str ptr ptr long) shlwapi.UrlCombineA
@ stdcall UrlCombineW(wstr wstr ptr ptr long) shlwapi.UrlCombineW
@ stdcall UrlCompareA(str str long) shlwapi.UrlCompareA
@ stdcall UrlCompareW(wstr wstr long) shlwapi.UrlCompareW
@ stdcall UrlCreateFromPathA(str ptr ptr long) shlwapi.UrlCreateFromPathA
@ stdcall UrlCreateFromPathW(wstr ptr ptr long) shlwapi.UrlCreateFromPathW
-@ stdcall UrlEscapeA(str ptr ptr long) shlwapi.UrlEscapeA
-@ stdcall UrlEscapeW(wstr ptr ptr long) shlwapi.UrlEscapeW
+@ stdcall UrlEscapeA(str ptr ptr long)
+@ stdcall UrlEscapeW(wstr ptr ptr long)
@ stdcall UrlFixupW(wstr wstr long) shlwapi.UrlFixupW
@ stdcall UrlGetLocationA(str) shlwapi.UrlGetLocationA
@ stdcall UrlGetLocationW(wstr) shlwapi.UrlGetLocationW
@@ -1653,8 +1653,8 @@
@ stdcall UrlIsOpaqueA(str) shlwapi.UrlIsOpaqueA
@ stdcall UrlIsOpaqueW(wstr) shlwapi.UrlIsOpaqueW
@ stdcall UrlIsW(wstr long) shlwapi.UrlIsW
-@ stdcall UrlUnescapeA(str ptr ptr long) shlwapi.UrlUnescapeA
-@ stdcall UrlUnescapeW(wstr ptr ptr long) shlwapi.UrlUnescapeW
+@ stdcall UrlUnescapeA(str ptr ptr long)
+@ stdcall UrlUnescapeW(wstr ptr ptr long)
@ stdcall VerFindFileA(long str str str ptr ptr ptr ptr) version.VerFindFileA
@ stdcall VerFindFileW(long wstr wstr wstr ptr ptr ptr ptr) version.VerFindFileW
@ stdcall VerLanguageNameA(long str long) kernel32.VerLanguageNameA
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c
index ca951cfd3f..44d58ea73b 100644
--- a/dlls/kernelbase/path.c
+++ b/dlls/kernelbase/path.c
@@ -24,12 +24,34 @@
#include "pathcch.h"
#include "strsafe.h"
#include "shlwapi.h"
+#include "wininet.h"
+#include "intshcut.h"
+#include "winternl.h"
#include "wine/debug.h"
+#include "wine/heap.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(path);
+static const char hexDigits[] = "0123456789ABCDEF";
+
+static WCHAR *heap_strdupAtoW(const char *str)
+{
+ WCHAR *ret = NULL;
+
+ if (str)
+ {
+ DWORD len;
+
+ len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
+ ret = heap_alloc(len * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
+ }
+
+ return ret;
+}
+
static char *char_next(const char *ptr)
{
if (!*ptr) return (LPSTR)ptr;
@@ -2598,3 +2620,1125 @@ BOOL WINAPI PathFileExistsW(const WCHAR *path)
SetErrorMode(prev_mode);
return attrs != INVALID_FILE_ATTRIBUTES;
}
+
+static const struct
+{
+ URL_SCHEME scheme_number;
+ WCHAR scheme_name[12];
+}
+url_schemes[] =
+{
+ { URL_SCHEME_FTP, {'f','t','p',0}},
+ { URL_SCHEME_HTTP, {'h','t','t','p',0}},
+ { URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
+ { URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
+ { URL_SCHEME_NEWS, {'n','e','w','s',0}},
+ { URL_SCHEME_NNTP, {'n','n','t','p',0}},
+ { URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
+ { URL_SCHEME_WAIS, {'w','a','i','s',0}},
+ { URL_SCHEME_FILE, {'f','i','l','e',0}},
+ { URL_SCHEME_MK, {'m','k',0}},
+ { URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
+ { URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
+ { URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
+ { URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
+ { URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
+ { URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
+ { URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
+ { URL_SCHEME_RES, {'r','e','s',0}},
+};
+
+static DWORD get_scheme_code(const WCHAR *scheme, DWORD scheme_len)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(url_schemes); ++i)
+ {
+ if (scheme_len == strlenW(url_schemes[i].scheme_name)
+ && !strncmpiW(scheme, url_schemes[i].scheme_name, scheme_len))
+ return url_schemes[i].scheme_number;
+ }
+
+ return URL_SCHEME_UNKNOWN;
+}
+
+HRESULT WINAPI ParseURLA(const char *url, PARSEDURLA *result)
+{
+ WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
+ const char *ptr = url;
+ int len;
+
+ TRACE("%s, %p\n", wine_dbgstr_a(url), result);
+
+ if (result->cbSize != sizeof(*result))
+ return E_INVALIDARG;
+
+ while (*ptr && (isalnum(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.'))
+ ptr++;
+
+ if (*ptr != ':' || ptr <= url + 1)
+ {
+ result->pszProtocol = NULL;
+ return URL_E_INVALID_SYNTAX;
+ }
+
+ result->pszProtocol = url;
+ result->cchProtocol = ptr - url;
+ result->pszSuffix = ptr + 1;
+ result->cchSuffix = strlen(result->pszSuffix);
+
+ len = MultiByteToWideChar(CP_ACP, 0, url, ptr - url, scheme, ARRAY_SIZE(scheme));
+ result->nScheme = get_scheme_code(scheme, len);
+
+ return S_OK;
+}
+
+HRESULT WINAPI ParseURLW(const WCHAR *url, PARSEDURLW *result)
+{
+ const WCHAR *ptr = url;
+
+ TRACE("%s, %p\n", wine_dbgstr_w(url), result);
+
+ if (result->cbSize != sizeof(*result))
+ return E_INVALIDARG;
+
+ while (*ptr && (isalnumW(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.'))
+ ptr++;
+
+ if (*ptr != ':' || ptr <= url + 1)
+ {
+ result->pszProtocol = NULL;
+ return URL_E_INVALID_SYNTAX;
+ }
+
+ result->pszProtocol = url;
+ result->cchProtocol = ptr - url;
+ result->pszSuffix = ptr + 1;
+ result->cchSuffix = strlenW(result->pszSuffix);
+ result->nScheme = get_scheme_code(url, ptr - url);
+
+ return S_OK;
+}
+
+HRESULT WINAPI UrlUnescapeA(char *url, char *unescaped, DWORD *unescaped_len, DWORD flags)
+{
+ BOOL stop_unescaping = FALSE;
+ const char *src;
+ char *dst, next;
+ DWORD needed;
+ HRESULT hr;
+
+ TRACE("%s, %p, %p, %#x\n", wine_dbgstr_a(url), unescaped, unescaped_len, flags);
+
+ if (!url)
+ return E_INVALIDARG;
+
+ if (flags & URL_UNESCAPE_INPLACE)
+ dst = url;
+ else
+ {
+ if (!unescaped || !unescaped_len) return E_INVALIDARG;
+ dst = unescaped;
+ }
+
+ for (src = url, needed = 0; *src; src++, needed++)
+ {
+ if (flags & URL_DONT_UNESCAPE_EXTRA_INFO && (*src == '#' || *src == '?'))
+ {
+ stop_unescaping = TRUE;
+ next = *src;
+ }
+ else if (*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2)) && !stop_unescaping)
+ {
+ INT ih;
+ char buf[3];
+ memcpy(buf, src + 1, 2);
+ buf[2] = '\0';
+ ih = strtol(buf, NULL, 16);
+ next = (CHAR) ih;
+ src += 2; /* Advance to end of escape */
+ }
+ else
+ next = *src;
+
+ if (flags & URL_UNESCAPE_INPLACE || needed < *unescaped_len)
+ *dst++ = next;
+ }
+
+ if (flags & URL_UNESCAPE_INPLACE || needed < *unescaped_len)
+ {
+ *dst = '\0';
+ hr = S_OK;
+ }
+ else
+ {
+ needed++; /* add one for the '\0' */
+ hr = E_POINTER;
+ }
+
+ if (!(flags & URL_UNESCAPE_INPLACE))
+ *unescaped_len = needed;
+
+ if (hr == S_OK)
+ TRACE("result %s\n", flags & URL_UNESCAPE_INPLACE ? wine_dbgstr_a(url) : wine_dbgstr_a(unescaped));
+
+ return hr;
+}
+
+HRESULT WINAPI UrlUnescapeW(WCHAR *url, WCHAR *unescaped, DWORD *unescaped_len, DWORD flags)
+{
+ BOOL stop_unescaping = FALSE;
+ const WCHAR *src;
+ WCHAR *dst, next;
+ DWORD needed;
+ HRESULT hr;
+
+ TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(url), unescaped, unescaped_len, flags);
+
+ if (!url)
+ return E_INVALIDARG;
+
+ if (flags & URL_UNESCAPE_INPLACE)
+ dst = url;
+ else
+ {
+ if (!unescaped || !unescaped_len) return E_INVALIDARG;
+ dst = unescaped;
+ }
+
+ for (src = url, needed = 0; *src; src++, needed++)
+ {
+ if (flags & URL_DONT_UNESCAPE_EXTRA_INFO && (*src == '#' || *src == '?'))
+ {
+ stop_unescaping = TRUE;
+ next = *src;
+ }
+ else if (*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2)) && !stop_unescaping)
+ {
+ INT ih;
+ WCHAR buf[5] = {'0','x',0};
+ memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
+ buf[4] = 0;
+ StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
+ next = (WCHAR) ih;
+ src += 2; /* Advance to end of escape */
+ }
+ else
+ next = *src;
+
+ if (flags & URL_UNESCAPE_INPLACE || needed < *unescaped_len)
+ *dst++ = next;
+ }
+
+ if (flags & URL_UNESCAPE_INPLACE || needed < *unescaped_len)
+ {
+ *dst = '\0';
+ hr = S_OK;
+ }
+ else
+ {
+ needed++; /* add one for the '\0' */
+ hr = E_POINTER;
+ }
+
+ if (!(flags & URL_UNESCAPE_INPLACE))
+ *unescaped_len = needed;
+
+ if (hr == S_OK)
+ TRACE("result %s\n", flags & URL_UNESCAPE_INPLACE ? wine_dbgstr_w(url) : wine_dbgstr_w(unescaped));
+
+ return hr;
+}
+
+HRESULT WINAPI PathCreateFromUrlA(const char *pszUrl, char *pszPath, DWORD *pcchPath, DWORD dwReserved)
+{
+ WCHAR bufW[MAX_PATH];
+ WCHAR *pathW = bufW;
+ UNICODE_STRING urlW;
+ HRESULT ret;
+ DWORD lenW = ARRAY_SIZE(bufW), lenA;
+
+ if (!pszUrl || !pszPath || !pcchPath || !*pcchPath)
+ return E_INVALIDARG;
+
+ if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
+ return E_INVALIDARG;
+ if((ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved)) == E_POINTER) {
+ pathW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
+ ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved);
+ }
+ if(ret == S_OK) {
+ RtlUnicodeToMultiByteSize(&lenA, pathW, lenW * sizeof(WCHAR));
+ if(*pcchPath > lenA) {
+ RtlUnicodeToMultiByteN(pszPath, *pcchPath - 1, &lenA, pathW, lenW * sizeof(WCHAR));
+ pszPath[lenA] = 0;
+ *pcchPath = lenA;
+ } else {
+ *pcchPath = lenA + 1;
+ ret = E_POINTER;
+ }
+ }
+ if(pathW != bufW) HeapFree(GetProcessHeap(), 0, pathW);
+ RtlFreeUnicodeString(&urlW);
+ return ret;
+}
+
+HRESULT WINAPI PathCreateFromUrlW(const WCHAR *url, WCHAR *path, DWORD *pcchPath, DWORD dwReserved)
+{
+ static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
+ static const WCHAR localhost[] = { 'l','o','c','a','l','h','o','s','t',0 };
+ DWORD nslashes, unescape, len;
+ const WCHAR *src;
+ WCHAR *tpath, *dst;
+ HRESULT hr = S_OK;
+
+ TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(url), path, pcchPath, dwReserved);
+
+ if (!url || !path || !pcchPath || !*pcchPath)
+ return E_INVALIDARG;
+
+ if (lstrlenW(url) < 5)
+ return E_INVALIDARG;
+
+ if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, url, 5, file_colon, 5) != CSTR_EQUAL)
+ return E_INVALIDARG;
+
+ url += 5;
+
+ src = url;
+ nslashes = 0;
+ while (*src == '/' || *src == '\\')
+ {
+ nslashes++;
+ src++;
+ }
+
+ /* We need a temporary buffer so we can compute what size to ask for.
+ * We know that the final string won't be longer than the current pszUrl
+ * plus at most two backslashes. All the other transformations make it
+ * shorter.
+ */
+ len = 2 + lstrlenW(url) + 1;
+ if (*pcchPath < len)
+ tpath = heap_alloc(len * sizeof(WCHAR));
+ else
+ tpath = path;
+
+ len = 0;
+ dst = tpath;
+ unescape = 1;
+ switch (nslashes)
+ {
+ case 0:
+ /* 'file:' + escaped DOS path */
+ break;
+ case 1:
+ /* 'file:/' + escaped DOS path */
+ /* fall through */
+ case 3:
+ /* 'file:///' (implied localhost) + escaped DOS path */
+ if (!isalphaW(*src) || (src[1] != ':' && src[1] != '|'))
+ src -= 1;
+ break;
+ case 2:
+ if (lstrlenW(src) >= 10 && CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE,
+ src, 9, localhost, 9) == CSTR_EQUAL && (src[9] == '/' || src[9] == '\\'))
+ {
+ /* 'file://localhost/' + escaped DOS path */
+ src += 10;
+ }
+ else if (isalphaW(*src) && (src[1] == ':' || src[1] == '|'))
+ {
+ /* 'file://' + unescaped DOS path */
+ unescape = 0;
+ }
+ else
+ {
+ /* 'file://hostname:port/path' (where path is escaped)
+ * or 'file:' + escaped UNC path (\\server\share\path)
+ * The second form is clearly specific to Windows and it might
+ * even be doing a network lookup to try to figure it out.
+ */
+ while (*src && *src != '/' && *src != '\\')
+ src++;
+ len = src - url;
+ StrCpyNW(dst, url, len + 1);
+ dst += len;
+ if (*src && isalphaW(src[1]) && (src[2] == ':' || src[2] == '|'))
+ {
+ /* 'Forget' to add a trailing '/', just like Windows */
+ src++;
+ }
+ }
+ break;
+ case 4:
+ /* 'file://' + unescaped UNC path (\\server\share\path) */
+ unescape = 0;
+ if (isalphaW(*src) && (src[1] == ':' || src[1] == '|'))
+ break;
+ /* fall through */
+ default:
+ /* 'file:/...' + escaped UNC path (\\server\share\path) */
+ src -= 2;
+ }
+
+ /* Copy the remainder of the path */
+ len += lstrlenW(src);
+ strcpyW(dst, src);
+
+ /* First do the Windows-specific path conversions */
+ for (dst = tpath; *dst; dst++)
+ if (*dst == '/') *dst = '\\';
+ if (isalphaW(*tpath) && tpath[1] == '|')
+ tpath[1] = ':'; /* c| -> c: */
+
+ /* And only then unescape the path (i.e. escaped slashes are left as is) */
+ if (unescape)
+ {
+ hr = UrlUnescapeW(tpath, NULL, &len, URL_UNESCAPE_INPLACE);
+ if (hr == S_OK)
+ {
+ /* When working in-place UrlUnescapeW() does not set len */
+ len = lstrlenW(tpath);
+ }
+ }
+
+ if (*pcchPath < len + 1)
+ {
+ hr = E_POINTER;
+ *pcchPath = len + 1;
+ }
+ else
+ {
+ *pcchPath = len;
+ if (tpath != path)
+ strcpyW(path, tpath);
+ }
+ if (tpath != path)
+ heap_free(tpath);
+
+ TRACE("Returning (%u) %s\n", *pcchPath, wine_dbgstr_w(path));
+ return hr;
+}
+
+HRESULT WINAPI PathCreateFromUrlAlloc(const WCHAR *url, WCHAR **path, DWORD reserved)
+{
+ WCHAR pathW[MAX_PATH];
+ DWORD size;
+ HRESULT hr;
+
+ size = MAX_PATH;
+ hr = PathCreateFromUrlW(url, pathW, &size, reserved);
+ if (SUCCEEDED(hr))
+ {
+ /* Yes, this is supposed to crash if 'path' is NULL */
+ *path = StrDupW(pathW);
+ }
+
+ return hr;
+}
+
+BOOL WINAPI PathIsURLA(const char *path)
+{
+ PARSEDURLA base;
+ HRESULT hr;
+
+ TRACE("%s\n", wine_dbgstr_a(path));
+
+ if (!path || !*path)
+ return FALSE;
+
+ /* get protocol */
+ base.cbSize = sizeof(base);
+ hr = ParseURLA(path, &base);
+ return hr == S_OK && (base.nScheme != URL_SCHEME_INVALID);
+}
+
+BOOL WINAPI PathIsURLW(const WCHAR *path)
+{
+ PARSEDURLW base;
+ HRESULT hr;
+
+ TRACE("%s\n", wine_dbgstr_w(path));
+
+ if (!path || !*path)
+ return FALSE;
+
+ /* get protocol */
+ base.cbSize = sizeof(base);
+ hr = ParseURLW(path, &base);
+ return hr == S_OK && (base.nScheme != URL_SCHEME_INVALID);
+}
+
+#define WINE_URL_BASH_AS_SLASH 0x01
+#define WINE_URL_COLLAPSE_SLASHES 0x02
+#define WINE_URL_ESCAPE_SLASH 0x04
+#define WINE_URL_ESCAPE_HASH 0x08
+#define WINE_URL_ESCAPE_QUESTION 0x10
+#define WINE_URL_STOP_ON_HASH 0x20
+#define WINE_URL_STOP_ON_QUESTION 0x40
+
+static BOOL url_needs_escape(WCHAR ch, DWORD flags, DWORD int_flags)
+{
+ if (flags & URL_ESCAPE_SPACES_ONLY)
+ return ch == ' ';
+
+ if ((flags & URL_ESCAPE_PERCENT) && (ch == '%'))
+ return TRUE;
+
+ if ((flags & URL_ESCAPE_AS_UTF8) && (ch >= 0x80))
+ return TRUE;
+
+ if (ch <= 31 || (ch >= 127 && ch <= 255) )
+ return TRUE;
+
+ if (isalnumW(ch))
+ return FALSE;
+
+ switch (ch) {
+ case ' ':
+ case '<':
+ case '>':
+ case '\"':
+ case '{':
+ case '}':
+ case '|':
+ case '\\':
+ case '^':
+ case ']':
+ case '[':
+ case '`':
+ case '&':
+ return TRUE;
+ case '/':
+ return !!(int_flags & WINE_URL_ESCAPE_SLASH);
+ case '?':
+ return !!(int_flags & WINE_URL_ESCAPE_QUESTION);
+ case '#':
+ return !!(int_flags & WINE_URL_ESCAPE_HASH);
+ default:
+ return FALSE;
+ }
+}
+
+HRESULT WINAPI UrlEscapeA(const char *url, char *escaped, DWORD *escaped_len, DWORD flags)
+{
+ WCHAR bufW[INTERNET_MAX_URL_LENGTH];
+ WCHAR *escapedW = bufW;
+ UNICODE_STRING urlW;
+ HRESULT hr;
+ DWORD lenW = ARRAY_SIZE(bufW), lenA;
+
+ if (!escaped || !escaped_len || !*escaped_len)
+ return E_INVALIDARG;
+
+ if (!RtlCreateUnicodeStringFromAsciiz(&urlW, url))
+ return E_INVALIDARG;
+
+ if (flags & URL_ESCAPE_AS_UTF8)
+ {
+ RtlFreeUnicodeString(&urlW);
+ return E_NOTIMPL;
+ }
+
+ if ((hr = UrlEscapeW(urlW.Buffer, escapedW, &lenW, flags)) == E_POINTER)
+ {
+ escapedW = heap_alloc(lenW * sizeof(WCHAR));
+ hr = UrlEscapeW(urlW.Buffer, escapedW, &lenW, flags);
+ }
+
+ if (hr == S_OK)
+ {
+ RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
+ if (*escaped_len > lenA)
+ {
+ RtlUnicodeToMultiByteN(escaped, *escaped_len - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
+ escaped[lenA] = 0;
+ *escaped_len = lenA;
+ }
+ else
+ {
+ *escaped_len = lenA + 1;
+ hr = E_POINTER;
+ }
+ }
+ if (escapedW != bufW)
+ heap_free(escapedW);
+ RtlFreeUnicodeString(&urlW);
+ return hr;
+}
+
+HRESULT WINAPI UrlEscapeW(const WCHAR *url, WCHAR *escaped, DWORD *escaped_len, DWORD flags)
+{
+ static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
+ DWORD needed = 0, slashes = 0, int_flags;
+ WCHAR next[12], *dst, *dst_ptr;
+ BOOL stop_escaping = FALSE;
+ PARSEDURLW parsed_url;
+ const WCHAR *src;
+ INT i, len;
+ HRESULT hr;
+
+ TRACE("%p, %s, %p, %p, %#x\n", url, wine_dbgstr_w(url), escaped, escaped_len, flags);
+
+ if (!url || !escaped_len || !escaped || *escaped_len == 0)
+ return E_INVALIDARG;
+
+ if (flags & ~(URL_ESCAPE_SPACES_ONLY | URL_ESCAPE_SEGMENT_ONLY | URL_DONT_ESCAPE_EXTRA_INFO |
+ URL_ESCAPE_PERCENT | URL_ESCAPE_AS_UTF8))
+ {
+ FIXME("Unimplemented flags: %08x\n", flags);
+ }
+
+ dst_ptr = dst = heap_alloc(*escaped_len * sizeof(WCHAR));
+ if (!dst_ptr)
+ return E_OUTOFMEMORY;
+
+ /* fix up flags */
+ if (flags & URL_ESCAPE_SPACES_ONLY)
+ /* if SPACES_ONLY specified, reset the other controls */
+ flags &= ~(URL_DONT_ESCAPE_EXTRA_INFO | URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY);
+ else
+ /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
+ flags |= URL_DONT_ESCAPE_EXTRA_INFO;
+
+ int_flags = 0;
+ if (flags & URL_ESCAPE_SEGMENT_ONLY)
+ int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
+ else
+ {
+ parsed_url.cbSize = sizeof(parsed_url);
+ if (ParseURLW(url, &parsed_url) != S_OK)
+ parsed_url.nScheme = URL_SCHEME_INVALID;
+
+ TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
+
+ if (flags & URL_DONT_ESCAPE_EXTRA_INFO)
+ int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
+
+ switch(parsed_url.nScheme) {
+ case URL_SCHEME_FILE:
+ int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
+ int_flags &= ~WINE_URL_STOP_ON_HASH;
+ break;
+
+ case URL_SCHEME_HTTP:
+ case URL_SCHEME_HTTPS:
+ int_flags |= WINE_URL_BASH_AS_SLASH;
+ if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
+ int_flags |= WINE_URL_ESCAPE_SLASH;
+ break;
+
+ case URL_SCHEME_MAILTO:
+ int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
+ int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
+ break;
+
+ case URL_SCHEME_INVALID:
+ break;
+
+ case URL_SCHEME_FTP:
+ default:
+ if(parsed_url.pszSuffix[0] != '/')
+ int_flags |= WINE_URL_ESCAPE_SLASH;
+ break;
+ }
+ }
+
+ for (src = url; *src; )
+ {
+ WCHAR cur = *src;
+ len = 0;
+
+ if ((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == url + parsed_url.cchProtocol + 1)
+ {
+ int localhost_len = ARRAY_SIZE(localhost) - 1;
+ while (cur == '/' || cur == '\\')
+ {
+ slashes++;
+ cur = *++src;
+ }
+ if (slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
+ if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
+ src += localhost_len + 1;
+ slashes = 3;
+ }
+
+ switch (slashes)
+ {
+ case 1:
+ case 3:
+ next[0] = next[1] = next[2] = '/';
+ len = 3;
+ break;
+ case 0:
+ len = 0;
+ break;
+ default:
+ next[0] = next[1] = '/';
+ len = 2;
+ break;
+ }
+ }
+ if (len == 0)
+ {
+ if (cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
+ stop_escaping = TRUE;
+
+ if (cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
+ stop_escaping = TRUE;
+
+ if (cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
+
+ if (url_needs_escape(cur, flags, int_flags) && !stop_escaping)
+ {
+ if (flags & URL_ESCAPE_AS_UTF8)
+ {
+ char utf[16];
+
+ if ((cur >= 0xd800 && cur <= 0xdfff) && (src[1] >= 0xdc00 && src[1] <= 0xdfff))
+ {
+ len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, 2, utf, sizeof(utf), NULL, NULL);
+ src++;
+ }
+ else
+ len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &cur, 1, utf, sizeof(utf), NULL, NULL);
+
+ if (!len)
+ {
+ utf[0] = 0xef;
+ utf[1] = 0xbf;
+ utf[2] = 0xbd;
+ len = 3;
+ }
+
+ for (i = 0; i < len; ++i)
+ {
+ next[i*3+0] = '%';
+ next[i*3+1] = hexDigits[(utf[i] >> 4) & 0xf];
+ next[i*3+2] = hexDigits[utf[i] & 0xf];
+ }
+ len *= 3;
+ }
+ else
+ {
+ next[0] = '%';
+ next[1] = hexDigits[(cur >> 4) & 0xf];
+ next[2] = hexDigits[cur & 0xf];
+ len = 3;
+ }
+ }
+ else
+ {
+ next[0] = cur;
+ len = 1;
+ }
+ src++;
+ }
+
+ if (needed + len <= *escaped_len)
+ {
+ memcpy(dst, next, len*sizeof(WCHAR));
+ dst += len;
+ }
+ needed += len;
+ }
+
+ if (needed < *escaped_len)
+ {
+ *dst = '\0';
+ memcpy(escaped, dst_ptr, (needed+1)*sizeof(WCHAR));
+ hr = S_OK;
+ }
+ else
+ {
+ needed++; /* add one for the '\0' */
+ hr = E_POINTER;
+ }
+ *escaped_len = needed;
+
+ heap_free(dst_ptr);
+ return hr;
+}
+
+HRESULT WINAPI UrlCanonicalizeA(const char *src_url, char *canonicalized, DWORD *canonicalized_len, DWORD flags)
+{
+ LPWSTR url, canonical;
+ HRESULT hr;
+
+ TRACE("%s, %p, %p, %#x\n", wine_dbgstr_a(src_url), canonicalized, canonicalized_len, flags);
+
+ if (!src_url || !canonicalized || !canonicalized_len || !*canonicalized_len)
+ return E_INVALIDARG;
+
+ url = heap_strdupAtoW(src_url);
+ canonical = heap_alloc(*canonicalized_len * sizeof(WCHAR));
+ if (!url || !canonical)
+ {
+ heap_free(url);
+ heap_free(canonical);
+ return E_OUTOFMEMORY;
+ }
+
+ hr = UrlCanonicalizeW(url, canonical, canonicalized_len, flags);
+ if (hr == S_OK)
+ WideCharToMultiByte(CP_ACP, 0, canonical, -1, canonicalized, *canonicalized_len + 1, NULL, NULL);
+
+ heap_free(url);
+ heap_free(canonical);
+ return hr;
+}
+
+HRESULT WINAPI UrlCanonicalizeW(const WCHAR *src_url, WCHAR *canonicalized, DWORD *canonicalized_len, DWORD flags)
+{
+ static const WCHAR wszFile[] = {'f','i','l','e',':'};
+ static const WCHAR wszRes[] = {'r','e','s',':'};
+ static const WCHAR wszHttp[] = {'h','t','t','p',':'};
+ static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
+ static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
+ WCHAR *url_copy, *url, *wk2, *mp, *mp2;
+ DWORD nByteLen, nLen, nWkLen;
+ const WCHAR *wk1, *root;
+ DWORD escape_flags;
+ WCHAR slash = '\0';
+ HRESULT hr = S_OK;
+ BOOL is_file_url;
+ INT state;
+
+ TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(src_url), canonicalized, canonicalized_len, flags);
+
+ if (!src_url || !canonicalized || !canonicalized || !*canonicalized_len)
+ return E_INVALIDARG;
+
+ if (!*src_url)
+ {
+ *canonicalized = 0;
+ return S_OK;
+ }
+
+ /* Remove '\t' characters from URL */
+ nByteLen = (strlenW(src_url) + 1) * sizeof(WCHAR); /* length in bytes */
+ url = HeapAlloc(GetProcessHeap(), 0, nByteLen);
+ if(!url)
+ return E_OUTOFMEMORY;
+
+ wk1 = src_url;
+ wk2 = url;
+ do
+ {
+ while(*wk1 == '\t')
+ wk1++;
+ *wk2++ = *wk1;
+ } while (*wk1++);
+
+ /* Allocate memory for simplified URL (before escaping) */
+ nByteLen = (wk2-url)*sizeof(WCHAR);
+ url_copy = heap_alloc(nByteLen + sizeof(wszFilePrefix) + sizeof(WCHAR));
+ if (!url_copy)
+ {
+ heap_free(url);
+ return E_OUTOFMEMORY;
+ }
+
+ is_file_url = !strncmpW(wszFile, url, ARRAY_SIZE(wszFile));
+
+ if ((nByteLen >= sizeof(wszHttp) && !memcmp(wszHttp, url, sizeof(wszHttp))) || is_file_url)
+ slash = '/';
+
+ if ((flags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY)) && is_file_url)
+ slash = '\\';
+
+ if (nByteLen >= sizeof(wszRes) && !memcmp(wszRes, url, sizeof(wszRes)))
+ {
+ flags &= ~URL_FILE_USE_PATHURL;
+ slash = '\0';
+ }
+
+ /*
+ * state =
+ * 0 initial 1,3
+ * 1 have 2[+] alnum 2,3
+ * 2 have scheme (found :) 4,6,3
+ * 3 failed (no location)
+ * 4 have // 5,3
+ * 5 have 1[+] alnum 6,3
+ * 6 have location (found /) save root location
+ */
+
+ wk1 = url;
+ wk2 = url_copy;
+ state = 0;
+
+ /* Assume path */
+ if (url[1] == ':')
+ {
+ memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
+ wk2 += ARRAY_SIZE(wszFilePrefix);
+ if (flags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY))
+ {
+ slash = '\\';
+ --wk2;
+ }
+ else
+ flags |= URL_ESCAPE_UNSAFE;
+ state = 5;
+ is_file_url = TRUE;
+ }
+ else if (url[0] == '/')
+ {
+ state = 5;
+ is_file_url = TRUE;
+ }
+
+ while (*wk1)
+ {
+ switch (state)
+ {
+ case 0:
+ if (!isalnumW(*wk1)) {state = 3; break;}
+ *wk2++ = *wk1++;
+ if (!isalnumW(*wk1)) {state = 3; break;}
+ *wk2++ = *wk1++;
+ state = 1;
+ break;
+ case 1:
+ *wk2++ = *wk1;
+ if (*wk1++ == ':') state = 2;
+ break;
+ case 2:
+ *wk2++ = *wk1++;
+ if (*wk1 != '/') {state = 6; break;}
+ *wk2++ = *wk1++;
+ if ((flags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost) && is_file_url
+ && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost)))
+ {
+ wk1 += ARRAY_SIZE(wszLocalhost);
+ while (*wk1 == '\\' && (flags & URL_FILE_USE_PATHURL))
+ wk1++;
+ }
+
+ if (*wk1 == '/' && (flags & URL_FILE_USE_PATHURL))
+ wk1++;
+ else if (is_file_url)
+ {
+ const WCHAR *body = wk1;
+
+ while (*body == '/')
+ ++body;
+
+ if (isalnumW(*body) && *(body+1) == ':')
+ {
+ if (!(flags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL)))
+ {
+ if (slash)
+ *wk2++ = slash;
+ else
+ *wk2++ = '/';
+ }
+ }
+ else
+ {
+ if (flags & URL_WININET_COMPATIBILITY)
+ {
+ if (*wk1 == '/' && *(wk1 + 1) != '/')
+ {
+ *wk2++ = '\\';
+ }
+ else
+ {
+ *wk2++ = '\\';
+ *wk2++ = '\\';
+ }
+ }
+ else
+ {
+ if (*wk1 == '/' && *(wk1+1) != '/')
+ {
+ if (slash)
+ *wk2++ = slash;
+ else
+ *wk2++ = '/';
+ }
+ }
+ }
+ wk1 = body;
+ }
+ state = 4;
+ break;
+ case 3:
+ nWkLen = strlenW(wk1);
+ memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
+ mp = wk2;
+ wk1 += nWkLen;
+ wk2 += nWkLen;
+
+ if (slash)
+ {
+ while (mp < wk2)
+ {
+ if (*mp == '/' || *mp == '\\')
+ *mp = slash;
+ mp++;
+ }
+ }
+ break;
+ case 4:
+ if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
+ {
+ state = 3;
+ break;
+ }
+ while (isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
+ *wk2++ = *wk1++;
+ state = 5;
+ if (!*wk1)
+ {
+ if (slash)
+ *wk2++ = slash;
+ else
+ *wk2++ = '/';
+ }
+ break;
+ case 5:
+ if (*wk1 != '/' && *wk1 != '\\')
+ {
+ state = 3;
+ break;
+ }
+ while (*wk1 == '/' || *wk1 == '\\')
+ {
+ if (slash)
+ *wk2++ = slash;
+ else
+ *wk2++ = *wk1;
+ wk1++;
+ }
+ state = 6;
+ break;
+ case 6:
+ if (flags & URL_DONT_SIMPLIFY)
+ {
+ state = 3;
+ break;
+ }
+
+ /* Now at root location, cannot back up any more. */
+ /* "root" will point at the '/' */
+
+ root = wk2-1;
+ while (*wk1)
+ {
+ mp = strchrW(wk1, '/');
+ mp2 = strchrW(wk1, '\\');
+ if (mp2 && (!mp || mp2 < mp))
+ mp = mp2;
+ if (!mp)
+ {
+ nWkLen = strlenW(wk1);
+ memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
+ wk1 += nWkLen;
+ wk2 += nWkLen;
+ continue;
+ }
+ nLen = mp - wk1;
+ if (nLen)
+ {
+ memcpy(wk2, wk1, nLen * sizeof(WCHAR));
+ wk2 += nLen;
+ wk1 += nLen;
+ }
+ if (slash)
+ *wk2++ = slash;
+ else
+ *wk2++ = *wk1;
+ wk1++;
+
+ while (*wk1 == '.')
+ {
+ TRACE("found '/.'\n");
+ if (wk1[1] == '/' || wk1[1] == '\\')
+ {
+ /* case of /./ -> skip the ./ */
+ wk1 += 2;
+ }
+ else if (wk1[1] == '.' && (wk1[2] == '/' || wk1[2] == '\\' || wk1[2] == '?'
+ || wk1[2] == '#' || !wk1[2]))
+ {
+ /* case /../ -> need to backup wk2 */
+ TRACE("found '/../'\n");
+ *(wk2-1) = '\0'; /* set end of string */
+ mp = strrchrW(root, '/');
+ mp2 = strrchrW(root, '\\');
+ if (mp2 && (!mp || mp2 < mp))
+ mp = mp2;
+ if (mp && (mp >= root))
+ {
+ /* found valid backup point */
+ wk2 = mp + 1;
+ if(wk1[2] != '/' && wk1[2] != '\\')
+ wk1 += 2;
+ else
+ wk1 += 3;
+ }
+ else
+ {
+ /* did not find point, restore '/' */
+ *(wk2-1) = slash;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+ *wk2 = '\0';
+ break;
+ default:
+ FIXME("how did we get here - state=%d\n", state);
+ heap_free(url_copy);
+ heap_free(url);
+ return E_INVALIDARG;
+ }
+ *wk2 = '\0';
+ TRACE("Simplified, orig <%s>, simple <%s>\n", wine_dbgstr_w(src_url), wine_dbgstr_w(url_copy));
+ }
+ nLen = lstrlenW(url_copy);
+ while ((nLen > 0) && ((url_copy[nLen-1] <= ' ')))
+ url_copy[--nLen]=0;
+
+ if ((flags & URL_UNESCAPE) || ((flags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
+ && !memcmp(wszFile, url, sizeof(wszFile))))
+ {
+ UrlUnescapeW(url_copy, NULL, &nLen, URL_UNESCAPE_INPLACE);
+ }
+
+ escape_flags = flags & (URL_ESCAPE_UNSAFE | URL_ESCAPE_SPACES_ONLY | URL_ESCAPE_PERCENT |
+ URL_DONT_ESCAPE_EXTRA_INFO | URL_ESCAPE_SEGMENT_ONLY);
+
+ if (escape_flags)
+ {
+ escape_flags &= ~URL_ESCAPE_UNSAFE;
+ hr = UrlEscapeW(url_copy, canonicalized, canonicalized_len, escape_flags);
+ }
+ else
+ {
+ /* No escaping needed, just copy the string */
+ nLen = lstrlenW(url_copy);
+ if (nLen < *canonicalized_len)
+ memcpy(canonicalized, url_copy, (nLen + 1)*sizeof(WCHAR));
+ else
+ {
+ hr = E_POINTER;
+ nLen++;
+ }
+ *canonicalized_len = nLen;
+ }
+
+ heap_free(url_copy);
+ heap_free(url);
+
+ if (hr == S_OK)
+ TRACE("result %s\n", wine_dbgstr_w(canonicalized));
+
+ return hr;
+}
--
2.20.1
More information about the wine-devel
mailing list