Signed-off-by: Nikolay Sivov nsivov@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; +}
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/kernelbase/kernelbase.spec | 8 +- dlls/kernelbase/path.c | 137 ++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 4 deletions(-)
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index e2bee49112..844cff17fd 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1097,8 +1097,8 @@ # @ stub PathMatchSpecExA # @ stub PathMatchSpecExW @ stdcall PathMatchSpecW(wstr wstr) -@ stdcall PathParseIconLocationA(str) shlwapi.PathParseIconLocationA -@ stdcall PathParseIconLocationW(wstr) shlwapi.PathParseIconLocationW +@ stdcall PathParseIconLocationA(str) +@ stdcall PathParseIconLocationW(wstr) @ stdcall PathQuoteSpacesA(str) @ stdcall PathQuoteSpacesW(wstr) @ stdcall PathRelativePathToA(ptr str long str long) @@ -1121,8 +1121,8 @@ @ stdcall PathStripPathW(wstr) @ stdcall PathStripToRootA(str) @ stdcall PathStripToRootW(wstr) -@ stdcall PathUnExpandEnvStringsA(str ptr long) shlwapi.PathUnExpandEnvStringsA -@ stdcall PathUnExpandEnvStringsW(wstr ptr long) shlwapi.PathUnExpandEnvStringsW +@ stdcall PathUnExpandEnvStringsA(str ptr long) +@ stdcall PathUnExpandEnvStringsW(wstr ptr long) @ stdcall PathUnquoteSpacesA(str) @ stdcall PathUnquoteSpacesW(wstr) # @ stub PcwAddQueryItem diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index 1a88f20e70..ad2ae84f98 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -2627,6 +2627,143 @@ BOOL WINAPI PathFileExistsW(const WCHAR *path) return attrs != INVALID_FILE_ATTRIBUTES; }
+int WINAPI PathParseIconLocationA(char *path) +{ + int ret = 0; + char *comma; + + TRACE("%s\n", debugstr_a(path)); + + if (!path) + return 0; + + if ((comma = strchr(path, ','))) + { + *comma++ = '\0'; + ret = StrToIntA(comma); + } + PathUnquoteSpacesA(path); + PathRemoveBlanksA(path); + + return ret; +} + +int WINAPI PathParseIconLocationW(WCHAR *path) +{ + WCHAR *comma; + int ret = 0; + + TRACE("%s\n", debugstr_w(path)); + + if (!path) + return 0; + + if ((comma = StrChrW(path, ','))) + { + *comma++ = '\0'; + ret = StrToIntW(comma); + } + PathUnquoteSpacesW(path); + PathRemoveBlanksW(path); + + return ret; +} + +BOOL WINAPI PathUnExpandEnvStringsA(const char *path, char *buffer, UINT buf_len) +{ + WCHAR bufferW[MAX_PATH], *pathW; + DWORD len; + BOOL ret; + + TRACE("%s, %p, %d\n", debugstr_a(path), buffer, buf_len); + + pathW = heap_strdupAtoW(path); + if (!pathW) return FALSE; + + ret = PathUnExpandEnvStringsW(pathW, bufferW, MAX_PATH); + HeapFree(GetProcessHeap(), 0, pathW); + if (!ret) return FALSE; + + len = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); + if (buf_len < len + 1) return FALSE; + + WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buf_len, NULL, NULL); + return TRUE; +} + +static const WCHAR allusersprofileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%',0}; +static const WCHAR appdataW[] = {'%','A','P','P','D','A','T','A','%',0}; +static const WCHAR programfilesW[] = {'%','P','r','o','g','r','a','m','F','i','l','e','s','%',0}; +static const WCHAR systemrootW[] = {'%','S','y','s','t','e','m','R','o','o','t','%',0}; +static const WCHAR systemdriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%',0}; +static const WCHAR userprofileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%',0}; + +struct envvars_map +{ + const WCHAR *var; + UINT varlen; + WCHAR path[MAX_PATH]; + DWORD len; +}; + +static void init_envvars_map(struct envvars_map *map) +{ + while (map->var) + { + map->len = ExpandEnvironmentStringsW(map->var, map->path, ARRAY_SIZE(map->path)); + /* exclude null from length */ + if (map->len) map->len--; + map++; + } +} + +BOOL WINAPI PathUnExpandEnvStringsW(const WCHAR *path, WCHAR *buffer, UINT buf_len) +{ + static struct envvars_map null_var = {NULL, 0, {0}, 0}; + struct envvars_map *match = &null_var, *cur; + struct envvars_map envvars[] = + { + { allusersprofileW, ARRAY_SIZE(allusersprofileW) }, + { appdataW, ARRAY_SIZE(appdataW) }, + { programfilesW, ARRAY_SIZE(programfilesW) }, + { systemrootW, ARRAY_SIZE(systemrootW) }, + { systemdriveW, ARRAY_SIZE(systemdriveW) }, + { userprofileW, ARRAY_SIZE(userprofileW) }, + { NULL } + }; + DWORD pathlen; + UINT needed; + + TRACE("%s, %p, %d\n", debugstr_w(path), buffer, buf_len); + + pathlen = strlenW(path); + init_envvars_map(envvars); + cur = envvars; + while (cur->var) + { + /* path can't contain expanded value or value wasn't retrieved */ + if (cur->len == 0 || cur->len > pathlen || strncmpiW(cur->path, path, cur->len)) + { + cur++; + continue; + } + + if (cur->len > match->len) + match = cur; + cur++; + } + + /* 'varlen' includes NULL termination char */ + needed = match->varlen + pathlen - match->len; + if (match->len == 0 || needed > buf_len) return FALSE; + + strcpyW(buffer, match->var); + strcatW(buffer, &path[match->len]); + TRACE("ret %s\n", debugstr_w(buffer)); + + return TRUE; +} + static const struct { URL_SCHEME scheme_number;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/kernelbase/kernelbase.spec | 4 +- dlls/kernelbase/main.c | 122 ++++++++++++++++++++++++++++++++ include/shlwapi.h | 8 +++ 3 files changed, 132 insertions(+), 2 deletions(-)
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 844cff17fd..193af89b43 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -403,8 +403,8 @@ @ stdcall GenerateConsoleCtrlEvent(long long) kernel32.GenerateConsoleCtrlEvent # @ stub GenerateGPNotificationInternal @ stdcall GetACP() kernel32.GetACP -@ stdcall GetAcceptLanguagesA(ptr ptr) shlwapi.GetAcceptLanguagesA -@ stdcall GetAcceptLanguagesW(ptr ptr) shlwapi.GetAcceptLanguagesW +@ stdcall GetAcceptLanguagesA(ptr ptr) +@ stdcall GetAcceptLanguagesW(ptr ptr) @ stdcall GetAce(ptr long ptr) advapi32.GetAce @ stdcall GetAclInformation(ptr ptr long long) advapi32.GetAclInformation # @ stub GetAdjustObjectAttributesForPrivateNamespaceRoutine diff --git a/dlls/kernelbase/main.c b/dlls/kernelbase/main.c index 3fce23657b..58f3c33f8e 100644 --- a/dlls/kernelbase/main.c +++ b/dlls/kernelbase/main.c @@ -26,6 +26,7 @@ #include "shlwapi.h"
#include "wine/debug.h" +#include "wine/heap.h" #include "winternl.h"
WINE_DEFAULT_DEBUG_CHANNEL(kernelbase); @@ -165,3 +166,124 @@ HRESULT WINAPI QISearch(void *base, const QITAB *table, REFIID riid, void **obj) *obj = NULL; return E_NOINTERFACE; } + +HRESULT WINAPI GetAcceptLanguagesA(LPSTR langbuf, DWORD *buflen) +{ + DWORD buflenW, convlen; + WCHAR *langbufW; + HRESULT hr; + + TRACE("%p, %p, *%p: %d\n", langbuf, buflen, buflen, buflen ? *buflen : -1); + + if (!langbuf || !buflen || !*buflen) + return E_FAIL; + + buflenW = *buflen; + langbufW = heap_alloc(sizeof(WCHAR) * buflenW); + hr = GetAcceptLanguagesW(langbufW, &buflenW); + + if (hr == S_OK) + { + convlen = WideCharToMultiByte(CP_ACP, 0, langbufW, -1, langbuf, *buflen, NULL, NULL); + convlen--; /* do not count the terminating 0 */ + } + else /* copy partial string anyway */ + { + convlen = WideCharToMultiByte(CP_ACP, 0, langbufW, *buflen, langbuf, *buflen, NULL, NULL); + if (convlen < *buflen) + { + langbuf[convlen] = 0; + convlen--; /* do not count the terminating 0 */ + } + else + { + convlen = *buflen; + } + } + *buflen = buflenW ? convlen : 0; + + heap_free(langbufW); + return hr; +} + +static HRESULT lcid_to_rfc1766(LCID lcid, WCHAR *rfc1766, INT len) +{ + WCHAR buffer[6 /* MAX_RFC1766_NAME */]; + INT n = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME, buffer, ARRAY_SIZE(buffer)); + INT i; + + if (n) + { + i = PRIMARYLANGID(lcid); + if ((((i == LANG_ENGLISH) || (i == LANG_CHINESE) || (i == LANG_ARABIC)) && + (SUBLANGID(lcid) == SUBLANG_DEFAULT)) || + (SUBLANGID(lcid) > SUBLANG_DEFAULT)) { + + buffer[n - 1] = '-'; + i = GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME, buffer + n, ARRAY_SIZE(buffer) - n); + if (!i) + buffer[n - 1] = '\0'; + } + else + i = 0; + + LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, buffer, n + i, rfc1766, len); + return ((n + i) > len) ? E_INVALIDARG : S_OK; + } + return E_FAIL; +} + +HRESULT WINAPI GetAcceptLanguagesW(WCHAR *langbuf, DWORD *buflen) +{ + static const WCHAR keyW[] = { + 'S','o','f','t','w','a','r','e','\', + 'M','i','c','r','o','s','o','f','t','\', + 'I','n','t','e','r','n','e','t',' ','E','x','p','l','o','r','e','r','\', + 'I','n','t','e','r','n','a','t','i','o','n','a','l',0}; + static const WCHAR valueW[] = {'A','c','c','e','p','t','L','a','n','g','u','a','g','e',0}; + DWORD mystrlen, mytype; + WCHAR *mystr; + LCID mylcid; + HKEY mykey; + LONG lres; + DWORD len; + + TRACE("%p, %p, *%p: %d\n", langbuf, buflen, buflen, buflen ? *buflen : -1); + + if (!langbuf || !buflen || !*buflen) + return E_FAIL; + + mystrlen = (*buflen > 20) ? *buflen : 20 ; + len = mystrlen * sizeof(WCHAR); + mystr = heap_alloc(len); + mystr[0] = 0; + RegOpenKeyW(HKEY_CURRENT_USER, keyW, &mykey); + lres = RegQueryValueExW(mykey, valueW, 0, &mytype, (PBYTE)mystr, &len); + RegCloseKey(mykey); + len = lstrlenW(mystr); + + if (!lres && (*buflen > len)) + { + lstrcpyW(langbuf, mystr); + *buflen = len; + heap_free(mystr); + return S_OK; + } + + /* Did not find a value in the registry or the user buffer is too small */ + mylcid = GetUserDefaultLCID(); + lcid_to_rfc1766(mylcid, mystr, mystrlen); + len = lstrlenW(mystr); + + memcpy(langbuf, mystr, min(*buflen, len + 1)*sizeof(WCHAR)); + heap_free(mystr); + + if (*buflen > len) + { + *buflen = len; + return S_OK; + } + + *buflen = 0; + return E_NOT_SUFFICIENT_BUFFER; +} diff --git a/include/shlwapi.h b/include/shlwapi.h index 138fcd9ee9..a1cbc576e8 100644 --- a/include/shlwapi.h +++ b/include/shlwapi.h @@ -31,6 +31,14 @@ extern "C" {
#include <pshpack8.h>
+#ifndef NO_SHLWAPI_HTTP + +HRESULT WINAPI GetAcceptLanguagesA(char *buffer, DWORD *buff_len); +HRESULT WINAPI GetAcceptLanguagesW(WCHAR *buffer, DWORD *buff_len); +#define GetAcceptLanguages WINELING_NAME_AW(GetAcceptLanguages) + +#endif /* NO_SHLWAPI_HTTP */ + #ifndef NO_SHLWAPI_REG
/* Registry functions */