Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
---
dlls/kernelbase/Makefile.in | 2 +-
dlls/kernelbase/kernelbase.spec | 34 +-
dlls/kernelbase/path.c | 838 ++++++++++++++++++++++++++++++++
3 files changed, 856 insertions(+), 18 deletions(-)
diff --git a/dlls/kernelbase/Makefile.in b/dlls/kernelbase/Makefile.in
index dcb03b4fdc..373f79d293 100644
--- a/dlls/kernelbase/Makefile.in
+++ b/dlls/kernelbase/Makefile.in
@@ -1,5 +1,5 @@
MODULE = kernelbase.dll
-IMPORTS = uuid
+IMPORTS = uuid advapi32
C_SRCS = \
main.c \
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index f9f9f2487b..e2bee49112 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -1628,31 +1628,31 @@
# @ stub UpdatePackageStatus
# @ stub UpdatePackageStatusForUser
@ 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 UrlApplySchemeA(str ptr ptr long)
+@ stdcall UrlApplySchemeW(wstr ptr ptr long)
@ 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 UrlCompareA(str str long)
+@ stdcall UrlCompareW(wstr wstr long)
+@ stdcall UrlCreateFromPathA(str ptr ptr long)
+@ stdcall UrlCreateFromPathW(wstr ptr ptr long)
@ 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
-@ stdcall UrlGetPartA(str ptr ptr long long) shlwapi.UrlGetPartA
-@ stdcall UrlGetPartW(wstr ptr ptr long long) shlwapi.UrlGetPartW
+@ stdcall UrlFixupW(wstr wstr long)
+@ stdcall UrlGetLocationA(str)
+@ stdcall UrlGetLocationW(wstr)
+@ stdcall UrlGetPartA(str ptr ptr long long)
+@ stdcall UrlGetPartW(wstr ptr ptr long long)
@ stdcall UrlHashA(str ptr long) shlwapi.UrlHashA
@ stdcall UrlHashW(wstr ptr long) shlwapi.UrlHashW
-@ stdcall UrlIsA(str long) shlwapi.UrlIsA
-@ stdcall UrlIsNoHistoryA(str) shlwapi.UrlIsNoHistoryA
-@ stdcall UrlIsNoHistoryW(wstr) shlwapi.UrlIsNoHistoryW
-@ stdcall UrlIsOpaqueA(str) shlwapi.UrlIsOpaqueA
-@ stdcall UrlIsOpaqueW(wstr) shlwapi.UrlIsOpaqueW
-@ stdcall UrlIsW(wstr long) shlwapi.UrlIsW
+@ stdcall UrlIsA(str long)
+@ stdcall UrlIsNoHistoryA(str)
+@ stdcall UrlIsNoHistoryW(wstr)
+@ stdcall UrlIsOpaqueA(str)
+@ stdcall UrlIsOpaqueW(wstr)
+@ stdcall UrlIsW(wstr long)
@ stdcall UrlUnescapeA(str ptr ptr long)
@ stdcall UrlUnescapeW(wstr ptr ptr long)
@ stdcall VerFindFileA(long str str str ptr ptr ptr ptr) version.VerFindFileA
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c
index b81e0bad8c..1a88f20e70 100644
--- a/dlls/kernelbase/path.c
+++ b/dlls/kernelbase/path.c
@@ -36,6 +36,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(path);
static const char hexDigits[] = "0123456789ABCDEF";
+struct parsed_url
+{
+ const WCHAR *scheme; /* [out] start of scheme */
+ DWORD scheme_len; /* [out] size of scheme (until colon) */
+ const WCHAR *username; /* [out] start of Username */
+ DWORD username_len; /* [out] size of Username (until ":" or "@") */
+ const WCHAR *password; /* [out] start of Password */
+ DWORD password_len; /* [out] size of Password (until "@") */
+ const WCHAR *hostname; /* [out] start of Hostname */
+ DWORD hostname_len; /* [out] size of Hostname (until ":" or "/") */
+ const WCHAR *port; /* [out] start of Port */
+ DWORD port_len; /* [out] size of Port (until "/" or eos) */
+ const WCHAR *query; /* [out] start of Query */
+ DWORD query_len; /* [out] size of Query (until eos) */
+};
+
+enum url_scan_type
+{
+ SCHEME,
+ HOST,
+ PORT,
+ USERPASS,
+};
+
static WCHAR *heap_strdupAtoW(const char *str)
{
WCHAR *ret = NULL;
@@ -3724,3 +3748,817 @@ HRESULT WINAPI UrlCanonicalizeW(const WCHAR *src_url, WCHAR *canonicalized, DWOR
return hr;
}
+
+HRESULT WINAPI UrlApplySchemeA(const char *url, char *out, DWORD *out_len, DWORD flags)
+{
+ LPWSTR inW, outW;
+ HRESULT hr;
+ DWORD len;
+
+ TRACE("%s, %p, %p:out size %d, %#x\n", wine_dbgstr_a(url), out, out_len, out_len ? *out_len : 0, flags);
+
+ if (!url || !out || !out_len)
+ return E_INVALIDARG;
+
+ inW = heap_alloc(2 * INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
+ outW = inW + INTERNET_MAX_URL_LENGTH;
+
+ MultiByteToWideChar(CP_ACP, 0, url, -1, inW, INTERNET_MAX_URL_LENGTH);
+ len = INTERNET_MAX_URL_LENGTH;
+
+ hr = UrlApplySchemeW(inW, outW, &len, flags);
+ if (hr != S_OK)
+ {
+ heap_free(inW);
+ return hr;
+ }
+
+ len = WideCharToMultiByte(CP_ACP, 0, outW, -1, NULL, 0, NULL, NULL);
+ if (len > *out_len)
+ {
+ hr = E_POINTER;
+ goto cleanup;
+ }
+
+ WideCharToMultiByte(CP_ACP, 0, outW, -1, out, *out_len, NULL, NULL);
+ len--;
+
+cleanup:
+ *out_len = len;
+ heap_free(inW);
+ return hr;
+}
+
+static HRESULT url_guess_scheme(const WCHAR *url, WCHAR *out, DWORD *out_len)
+{
+ WCHAR reg_path[MAX_PATH], value[MAX_PATH], data[MAX_PATH];
+ DWORD value_len, data_len, dwType, i;
+ WCHAR Wxx, Wyy;
+ HKEY newkey;
+ INT index;
+ BOOL j;
+
+ MultiByteToWideChar(CP_ACP, 0,
+ "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes", 1, reg_path, MAX_PATH);
+ RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
+ index = 0;
+ while (value_len = data_len = MAX_PATH,
+ RegEnumValueW(newkey, index, value, &value_len, 0, &dwType, (LPVOID)data, &data_len) == 0)
+ {
+ TRACE("guess %d %s is %s\n", index, wine_dbgstr_w(value), wine_dbgstr_w(data));
+
+ j = FALSE;
+ for (i = 0; i < value_len; ++i)
+ {
+ Wxx = url[i];
+ Wyy = value[i];
+ /* remember that TRUE is not-equal */
+ j = ChrCmpIW(Wxx, Wyy);
+ if (j) break;
+ }
+ if ((i == value_len) && !j)
+ {
+ if (strlenW(data) + strlenW(url) + 1 > *out_len)
+ {
+ *out_len = strlenW(data) + strlenW(url) + 1;
+ RegCloseKey(newkey);
+ return E_POINTER;
+ }
+ strcpyW(out, data);
+ strcatW(out, url);
+ *out_len = strlenW(out);
+ TRACE("matched and set to %s\n", wine_dbgstr_w(out));
+ RegCloseKey(newkey);
+ return S_OK;
+ }
+ index++;
+ }
+ RegCloseKey(newkey);
+ return E_FAIL;
+}
+
+static HRESULT url_create_from_path(const WCHAR *path, WCHAR *url, DWORD *url_len)
+{
+ static const WCHAR file_colonW[] = {'f','i','l','e',':',0};
+ static const WCHAR three_slashesW[] = {'/','/','/',0};
+ PARSEDURLW parsed_url;
+ WCHAR *new_url;
+ DWORD needed;
+ HRESULT hr;
+
+ parsed_url.cbSize = sizeof(parsed_url);
+ if (ParseURLW(path, &parsed_url) == S_OK)
+ {
+ if (parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1)
+ {
+ needed = strlenW(path);
+ if (needed >= *url_len)
+ {
+ *url_len = needed + 1;
+ return E_POINTER;
+ }
+ else
+ {
+ *url_len = needed;
+ return S_FALSE;
+ }
+ }
+ }
+
+ new_url = heap_alloc((strlenW(path) + 9) * sizeof(WCHAR)); /* "file:///" + path length + 1 */
+ strcpyW(new_url, file_colonW);
+ if (isalphaW(path[0]) && path[1] == ':')
+ strcatW(new_url, three_slashesW);
+ strcatW(new_url, path);
+ hr = UrlEscapeW(new_url, url, url_len, URL_ESCAPE_PERCENT);
+ heap_free(new_url);
+ return hr;
+}
+
+static HRESULT url_apply_default_scheme(const WCHAR *url, WCHAR *out, DWORD *length)
+{
+ static const WCHAR prefix_keyW[] =
+ {'S','o','f','t','w','a','r','e',
+ '\\','M','i','c','r','o','s','o','f','t',
+ '\\','W','i','n','d','o','w','s',
+ '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
+ '\\','U','R','L',
+ '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
+ DWORD data_len, dwType;
+ WCHAR data[MAX_PATH];
+ HKEY newkey;
+
+ /* get and prepend default */
+ RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
+ data_len = sizeof(data);
+ RegQueryValueExW(newkey, NULL, 0, &dwType, (BYTE *)data, &data_len);
+ RegCloseKey(newkey);
+ if (strlenW(data) + strlenW(url) + 1 > *length)
+ {
+ *length = strlenW(data) + strlenW(url) + 1;
+ return E_POINTER;
+ }
+ strcpyW(out, data);
+ strcatW(out, url);
+ *length = strlenW(out);
+ TRACE("used default %s\n", wine_dbgstr_w(out));
+ return S_OK;
+}
+
+HRESULT WINAPI UrlApplySchemeW(const WCHAR *url, WCHAR *out, DWORD *length, DWORD flags)
+{
+ PARSEDURLW in_scheme;
+ DWORD res1;
+ HRESULT hr;
+
+ TRACE("%s, %p, %p:out size %d, %#x\n", wine_dbgstr_w(url), out, length, length ? *length : 0, flags);
+
+ if (!url || !out || !length)
+ return E_INVALIDARG;
+
+ if (flags & URL_APPLY_GUESSFILE)
+ {
+ if (*length > 1 && ':' == url[1])
+ {
+ res1 = *length;
+ hr = url_create_from_path(url, out, &res1);
+ if (hr == S_OK || hr == E_POINTER)
+ {
+ *length = res1;
+ return hr;
+ }
+ else if (hr == S_FALSE)
+ {
+ return hr;
+ }
+ }
+ }
+
+ in_scheme.cbSize = sizeof(in_scheme);
+ /* See if the base has a scheme */
+ res1 = ParseURLW(url, &in_scheme);
+ if (res1)
+ {
+ /* no scheme in input, need to see if we need to guess */
+ if (flags & URL_APPLY_GUESSSCHEME)
+ {
+ if ((hr = url_guess_scheme(url, out, length)) != E_FAIL)
+ return hr;
+ }
+ }
+
+ /* If we are here, then either invalid scheme,
+ * or no scheme and can't/failed guess.
+ */
+ if ((((res1 == 0) && (flags & URL_APPLY_FORCEAPPLY)) || ((res1 != 0)) ) && (flags & URL_APPLY_DEFAULT))
+ return url_apply_default_scheme(url, out, length);
+
+ return S_FALSE;
+}
+
+INT WINAPI UrlCompareA(const char *url1, const char *url2, BOOL ignore_slash)
+{
+ INT ret, len, len1, len2;
+
+ if (!ignore_slash)
+ return strcmp(url1, url2);
+ len1 = strlen(url1);
+ if (url1[len1-1] == '/') len1--;
+ len2 = strlen(url2);
+ if (url2[len2-1] == '/') len2--;
+ if (len1 == len2)
+ return strncmp(url1, url2, len1);
+ len = min(len1, len2);
+ ret = strncmp(url1, url2, len);
+ if (ret) return ret;
+ if (len1 > len2) return 1;
+ return -1;
+}
+
+INT WINAPI UrlCompareW(const WCHAR *url1, const WCHAR *url2, BOOL ignore_slash)
+{
+ size_t len, len1, len2;
+ INT ret;
+
+ if (!ignore_slash)
+ return strcmpW(url1, url2);
+ len1 = strlenW(url1);
+ if (url1[len1-1] == '/') len1--;
+ len2 = strlenW(url2);
+ if (url2[len2-1] == '/') len2--;
+ if (len1 == len2)
+ return strncmpW(url1, url2, len1);
+ len = min(len1, len2);
+ ret = strncmpW(url1, url2, len);
+ if (ret) return ret;
+ if (len1 > len2) return 1;
+ return -1;
+}
+
+HRESULT WINAPI UrlFixupW(const WCHAR *url, WCHAR *translatedUrl, DWORD maxChars)
+{
+ DWORD srcLen;
+
+ FIXME("%s, %p, %d stub\n", wine_dbgstr_w(url), translatedUrl, maxChars);
+
+ if (!url)
+ return E_FAIL;
+
+ srcLen = lstrlenW(url) + 1;
+
+ /* For now just copy the URL directly */
+ lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
+
+ return S_OK;
+}
+
+const char * WINAPI UrlGetLocationA(const char *url)
+{
+ PARSEDURLA base;
+
+ base.cbSize = sizeof(base);
+ if (ParseURLA(url, &base) != S_OK) return NULL; /* invalid scheme */
+
+ /* if scheme is file: then never return pointer */
+ if (!strncmp(base.pszProtocol, "file", min(4, base.cchProtocol)) == 0)
+ return NULL;
+
+ /* Look for '#' and return its addr */
+ return strchr(base.pszSuffix, '#');
+}
+
+const WCHAR * WINAPI UrlGetLocationW(const WCHAR *url)
+{
+ static const WCHAR fileW[] = {'f','i','l','e','\0'};
+ PARSEDURLW base;
+
+ base.cbSize = sizeof(base);
+ if (ParseURLW(url, &base) != S_OK) return NULL; /* invalid scheme */
+
+ /* if scheme is file: then never return pointer */
+ if (!strncmpW(base.pszProtocol, fileW, min(4, base.cchProtocol)))
+ return NULL;
+
+ /* Look for '#' and return its addr */
+ return strchrW(base.pszSuffix, '#');
+}
+
+HRESULT WINAPI UrlGetPartA(const char *url, char *out, DWORD *out_len, DWORD part, DWORD flags)
+{
+ LPWSTR inW, outW;
+ DWORD len, len2;
+ HRESULT hr;
+
+ if (!url || !out || !out_len || !*out_len)
+ return E_INVALIDARG;
+
+ inW = heap_alloc(2 * INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
+ outW = inW + INTERNET_MAX_URL_LENGTH;
+
+ MultiByteToWideChar(CP_ACP, 0, url, -1, inW, INTERNET_MAX_URL_LENGTH);
+
+ len = INTERNET_MAX_URL_LENGTH;
+ hr = UrlGetPartW(inW, outW, &len, part, flags);
+ if (FAILED(hr))
+ {
+ heap_free(inW);
+ return hr;
+ }
+
+ len2 = WideCharToMultiByte(CP_ACP, 0, outW, len, NULL, 0, NULL, NULL);
+ if (len2 > *out_len)
+ {
+ *out_len = len2 + 1;
+ heap_free(inW);
+ return E_POINTER;
+ }
+ len2 = WideCharToMultiByte(CP_ACP, 0, outW, len + 1, out, *out_len, NULL, NULL);
+ *out_len = len2 - 1;
+ heap_free(inW);
+ return hr;
+}
+
+static const WCHAR * scan_url(const WCHAR *start, DWORD *size, enum url_scan_type type)
+{
+ static DWORD alwayszero = 0;
+ BOOL cont = TRUE;
+
+ *size = 0;
+
+ switch (type)
+ {
+ case SCHEME:
+ while (cont)
+ {
+ if ((islowerW(*start) && isalphaW(*start)) ||
+ isdigitW(*start) || *start == '+' || *start == '-' || *start == '.')
+ {
+ start++;
+ (*size)++;
+ }
+ else
+ cont = FALSE;
+ }
+ if (*start != ':')
+ *size = 0;
+ break;
+
+ case USERPASS:
+ while (cont)
+ {
+ if (isalphaW(*start) ||
+ isdigitW(*start) ||
+ /* user/password only characters */
+ (*start == ';') ||
+ (*start == '?') ||
+ (*start == '&') ||
+ (*start == '=') ||
+ /* *extra* characters */
+ (*start == '!') ||
+ (*start == '*') ||
+ (*start == '\'') ||
+ (*start == '(') ||
+ (*start == ')') ||
+ (*start == ',') ||
+ /* *safe* characters */
+ (*start == '$') ||
+ (*start == '_') ||
+ (*start == '+') ||
+ (*start == '-') ||
+ (*start == '.') ||
+ (*start == ' '))
+ {
+ start++;
+ (*size)++;
+ }
+ else if (*start == '%')
+ {
+ if (isxdigitW(*(start + 1)) && isxdigitW(*(start + 2)))
+ {
+ start += 3;
+ *size += 3;
+ }
+ else
+ cont = FALSE;
+ } else
+ cont = FALSE;
+ }
+ break;
+
+ case PORT:
+ while (cont)
+ {
+ if (isdigitW(*start))
+ {
+ start++;
+ (*size)++;
+ }
+ else
+ cont = FALSE;
+ }
+ break;
+
+ case HOST:
+ while (cont)
+ {
+ if (isalnumW(*start) || *start == '-' || *start == '.' || *start == ' ' || *start == '*')
+ {
+ start++;
+ (*size)++;
+ }
+ else
+ cont = FALSE;
+ }
+ break;
+
+ default:
+ FIXME("unknown type %d\n", type);
+ return (LPWSTR)&alwayszero;
+ }
+
+ return start;
+}
+
+static LONG parse_url(const WCHAR *url, struct parsed_url *pl)
+{
+ const WCHAR *work;
+
+ memset(pl, 0, sizeof(*pl));
+ pl->scheme = url;
+ work = scan_url(pl->scheme, &pl->scheme_len, SCHEME);
+ if (!*work || (*work != ':')) goto ErrorExit;
+ work++;
+ if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
+
+ pl->username = work + 2;
+ work = scan_url(pl->username, &pl->username_len, USERPASS);
+ if (*work == ':' )
+ {
+ /* parse password */
+ work++;
+ pl->password = work;
+ work = scan_url(pl->password, &pl->password_len, USERPASS);
+ if (*work != '@')
+ {
+ /* what we just parsed must be the hostname and port
+ * so reset pointers and clear then let it parse */
+ pl->username_len = pl->password_len = 0;
+ work = pl->username - 1;
+ pl->username = pl->password = 0;
+ }
+ }
+ else if (*work == '@')
+ {
+ /* no password */
+ pl->password_len = 0;
+ pl->password = 0;
+ }
+ else if (!*work || *work == '/' || *work == '.')
+ {
+ /* what was parsed was hostname, so reset pointers and let it parse */
+ pl->username_len = pl->password_len = 0;
+ work = pl->username - 1;
+ pl->username = pl->password = 0;
+ }
+ else goto ErrorExit;
+
+ /* now start parsing hostname or hostnumber */
+ work++;
+ pl->hostname = work;
+ work = scan_url(pl->hostname, &pl->hostname_len, HOST);
+ if (*work == ':')
+ {
+ /* parse port */
+ work++;
+ pl->port = work;
+ work = scan_url(pl->port, &pl->port_len, PORT);
+ }
+ if (*work == '/')
+ {
+ /* see if query string */
+ pl->query = strchrW(work, '?');
+ if (pl->query) pl->query_len = strlenW(pl->query);
+ }
+ SuccessExit:
+ TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
+ pl->scheme, pl->scheme_len, pl->username, pl->username_len, pl->password, pl->password_len, pl->hostname,
+ pl->hostname_len, pl->port, pl->port_len, pl->query, pl->query_len);
+
+ return S_OK;
+
+ ErrorExit:
+ FIXME("failed to parse %s\n", debugstr_w(url));
+ return E_INVALIDARG;
+}
+
+HRESULT WINAPI UrlGetPartW(const WCHAR *url, WCHAR *out, DWORD *out_len, DWORD part, DWORD flags)
+{
+ DWORD scheme, size, schsize;
+ LPCWSTR addr, schaddr;
+ struct parsed_url pl;
+ HRESULT hr;
+
+ TRACE("%s, %p, %p(%d), %#x, %#x\n", wine_dbgstr_w(url), out, out_len, *out_len, part, flags);
+
+ if (!url || !out || !out_len || !*out_len)
+ return E_INVALIDARG;
+
+ *out = '\0';
+
+ addr = strchrW(url, ':');
+ if (!addr)
+ scheme = URL_SCHEME_UNKNOWN;
+ else
+ scheme = get_scheme_code(url, addr - url);
+
+ hr = parse_url(url, &pl);
+
+ switch (part)
+ {
+ case URL_PART_SCHEME:
+ if (!pl.scheme_len)
+ {
+ *out_len = 0;
+ return S_FALSE;
+ }
+ addr = pl.scheme;
+ size = pl.scheme_len;
+ break;
+
+ case URL_PART_HOSTNAME:
+ switch (scheme)
+ {
+ case URL_SCHEME_FTP:
+ case URL_SCHEME_HTTP:
+ case URL_SCHEME_GOPHER:
+ case URL_SCHEME_TELNET:
+ case URL_SCHEME_FILE:
+ case URL_SCHEME_HTTPS:
+ break;
+ default:
+ *out_len = 0;
+ return E_FAIL;
+ }
+
+ if (scheme == URL_SCHEME_FILE && (!pl.hostname_len || (pl.hostname_len == 1 && *(pl.hostname + 1) == ':')))
+ {
+ *out_len = 0;
+ return S_FALSE;
+ }
+
+ if (!pl.hostname_len)
+ {
+ *out_len = 0;
+ return S_FALSE;
+ }
+ addr = pl.hostname;
+ size = pl.hostname_len;
+ break;
+
+ case URL_PART_USERNAME:
+ if (!pl.username_len)
+ {
+ *out_len = 0;
+ return S_FALSE;
+ }
+ addr = pl.username;
+ size = pl.username_len;
+ break;
+
+ case URL_PART_PASSWORD:
+ if (!pl.password_len)
+ {
+ *out_len = 0;
+ return S_FALSE;
+ }
+ addr = pl.password;
+ size = pl.password_len;
+ break;
+
+ case URL_PART_PORT:
+ if (!pl.port_len)
+ {
+ *out_len = 0;
+ return S_FALSE;
+ }
+ addr = pl.port;
+ size = pl.port_len;
+ break;
+
+ case URL_PART_QUERY:
+ if (!pl.query_len)
+ {
+ *out_len = 0;
+ return S_FALSE;
+ }
+ addr = pl.query;
+ size = pl.query_len;
+ break;
+
+ default:
+ *out_len = 0;
+ return E_INVALIDARG;
+ }
+
+ if (flags == URL_PARTFLAG_KEEPSCHEME)
+ {
+ if (!pl.scheme || !pl.scheme_len)
+ {
+ *out_len = 0;
+ return E_FAIL;
+ }
+ schaddr = pl.scheme;
+ schsize = pl.scheme_len;
+ if (*out_len < schsize + size + 2)
+ {
+ *out_len = schsize + size + 2;
+ return E_POINTER;
+ }
+ memcpy(out, schaddr, schsize*sizeof(WCHAR));
+ out[schsize] = ':';
+ memcpy(out + schsize+1, addr, size*sizeof(WCHAR));
+ out[schsize+1+size] = 0;
+ *out_len = schsize + 1 + size;
+ }
+ else
+ {
+ if (*out_len < size + 1)
+ {
+ *out_len = size + 1;
+ return E_POINTER;
+ }
+ memcpy(out, addr, size*sizeof(WCHAR));
+ out[size] = 0;
+ *out_len = size;
+ }
+ TRACE("len=%d %s\n", *out_len, wine_dbgstr_w(out));
+
+ return hr;
+}
+
+BOOL WINAPI UrlIsA(const char *url, URLIS Urlis)
+{
+ const char *last;
+ PARSEDURLA base;
+
+ TRACE("%s, %d\n", debugstr_a(url), Urlis);
+
+ if (!url)
+ return FALSE;
+
+ switch (Urlis) {
+
+ case URLIS_OPAQUE:
+ base.cbSize = sizeof(base);
+ if (ParseURLA(url, &base) != S_OK) return FALSE; /* invalid scheme */
+ switch (base.nScheme)
+ {
+ case URL_SCHEME_MAILTO:
+ case URL_SCHEME_SHELL:
+ case URL_SCHEME_JAVASCRIPT:
+ case URL_SCHEME_VBSCRIPT:
+ case URL_SCHEME_ABOUT:
+ return TRUE;
+ }
+ return FALSE;
+
+ case URLIS_FILEURL:
+ return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, url, 5, "file:", 5) == CSTR_EQUAL);
+
+ case URLIS_DIRECTORY:
+ last = url + strlen(url) - 1;
+ return (last >= url && (*last == '/' || *last == '\\' ));
+
+ case URLIS_URL:
+ return PathIsURLA(url);
+
+ case URLIS_NOHISTORY:
+ case URLIS_APPLIABLE:
+ case URLIS_HASQUERY:
+ default:
+ FIXME("(%s %d): stub\n", debugstr_a(url), Urlis);
+ }
+
+ return FALSE;
+}
+
+BOOL WINAPI UrlIsW(const WCHAR *url, URLIS Urlis)
+{
+ static const WCHAR file_colon[] = {'f','i','l','e',':',0};
+ const WCHAR *last;
+ PARSEDURLW base;
+
+ TRACE("%s, %d\n", debugstr_w(url), Urlis);
+
+ if (!url)
+ return FALSE;
+
+ switch (Urlis)
+ {
+ case URLIS_OPAQUE:
+ base.cbSize = sizeof(base);
+ if (ParseURLW(url, &base) != S_OK) return FALSE; /* invalid scheme */
+ switch (base.nScheme)
+ {
+ case URL_SCHEME_MAILTO:
+ case URL_SCHEME_SHELL:
+ case URL_SCHEME_JAVASCRIPT:
+ case URL_SCHEME_VBSCRIPT:
+ case URL_SCHEME_ABOUT:
+ return TRUE;
+ }
+ return FALSE;
+
+ case URLIS_FILEURL:
+ return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, url, 5, file_colon, 5) == CSTR_EQUAL);
+
+ case URLIS_DIRECTORY:
+ last = url + strlenW(url) - 1;
+ return (last >= url && (*last == '/' || *last == '\\'));
+
+ case URLIS_URL:
+ return PathIsURLW(url);
+
+ case URLIS_NOHISTORY:
+ case URLIS_APPLIABLE:
+ case URLIS_HASQUERY:
+ default:
+ FIXME("(%s %d): stub\n", debugstr_w(url), Urlis);
+ }
+
+ return FALSE;
+}
+
+BOOL WINAPI UrlIsOpaqueA(const char *url)
+{
+ return UrlIsA(url, URLIS_OPAQUE);
+}
+
+BOOL WINAPI UrlIsOpaqueW(const WCHAR *url)
+{
+ return UrlIsW(url, URLIS_OPAQUE);
+}
+
+BOOL WINAPI UrlIsNoHistoryA(const char *url)
+{
+ return UrlIsA(url, URLIS_NOHISTORY);
+}
+
+BOOL WINAPI UrlIsNoHistoryW(const WCHAR *url)
+{
+ return UrlIsW(url, URLIS_NOHISTORY);
+}
+
+HRESULT WINAPI UrlCreateFromPathA(const char *path, char *url, DWORD *url_len, DWORD reserved)
+{
+ WCHAR bufW[INTERNET_MAX_URL_LENGTH];
+ DWORD lenW = ARRAY_SIZE(bufW), lenA;
+ UNICODE_STRING pathW;
+ WCHAR *urlW = bufW;
+ HRESULT hr;
+
+ if (!RtlCreateUnicodeStringFromAsciiz(&pathW, path))
+ return E_INVALIDARG;
+
+ if ((hr = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, reserved)) == E_POINTER)
+ {
+ urlW = heap_alloc(lenW * sizeof(WCHAR));
+ hr = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, reserved);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
+ if (*url_len > lenA)
+ {
+ RtlUnicodeToMultiByteN(url, *url_len - 1, &lenA, urlW, lenW * sizeof(WCHAR));
+ url[lenA] = 0;
+ *url_len = lenA;
+ }
+ else
+ {
+ *url_len = lenA + 1;
+ hr = E_POINTER;
+ }
+ }
+ if (urlW != bufW)
+ heap_free(urlW);
+ RtlFreeUnicodeString(&pathW);
+ return hr;
+}
+
+HRESULT WINAPI UrlCreateFromPathW(const WCHAR *path, WCHAR *url, DWORD *url_len, DWORD reserved)
+{
+ HRESULT hr;
+
+ TRACE("%s, %p, %p, %#x\n", debugstr_w(path), url, url_len, reserved);
+
+ if (reserved || !url || !url_len)
+ return E_INVALIDARG;
+
+ hr = url_create_from_path(path, url, url_len);
+ if (hr == S_FALSE)
+ strcpyW(url, path);
+
+ return hr;
+}
--
2.20.1