From 6cddf1d720780195ab8f1cb6c0cf03c42e7c6925 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Tue, 6 Oct 2015 13:10:16 +0300 Subject: windows.c: sanitize the backend for path retrieval The old backend was less safe because, it didn't: - support UTF-16 paths! - use GetUserNameW() The following changes were made: - system_default_path_append() can be used to retrieve a path in the user Roaming path. - system_default_directory() - this is equal to system_default_path_append(NULL) - system_default_filename() also uses the helper system_default_path_append() Signed-off-by: Lubomir I. Ivanov Signed-off-by: Dirk Hohndel --- windows.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/windows.c b/windows.c index 652a95a26..a2386fd83 100644 --- a/windows.c +++ b/windows.c @@ -12,6 +12,7 @@ #include #include #include +#include const char non_standard_system_divelist_default_font[] = "Calibri"; const char current_system_divelist_default_font[] = "Segoe UI"; @@ -38,23 +39,81 @@ bool subsurface_ignore_font(const char *font) return false; } +/* this function returns the Win32 Roaming path for the current user as UTF-8. + * it never returns NULL but fallsback to .\ instead! + * the append argument will append a wchar_t string to the end of the path. + */ +static const char *system_default_path_append(const wchar_t *append) +{ + wchar_t wpath[MAX_PATH] = { 0 }; + const char *fname = "system_default_path_append()"; + + /* obtain the user path via SHGetFolderPathW. + * this API is deprecated but still supported on modern Win32. + * fallback to .\ if it fails. + */ + if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, wpath))) { + fprintf(stderr, "%s: cannot obtain path!\n", fname); + wpath[0] = L'.'; + wpath[1] = L'\0'; + } + + wcscat(wpath, L"\\Subsurface"); + if (append) { + wcscat(wpath, L"\\"); + wcscat(wpath, append); + } + + /* attempt to convert the UTF-16 string to UTF-8. + * resize the buffer and fallback to .\Subsurface if it fails. + */ + const int wsz = wcslen(wpath); + const int sz = WideCharToMultiByte(CP_UTF8, 0, wpath, wsz, NULL, 0, NULL, NULL); + char *path = (char *)malloc(sz + 1); + if (!sz) + goto fallback; + if (WideCharToMultiByte(CP_UTF8, 0, wpath, wsz, path, sz, NULL, NULL)) { + path[sz] = '\0'; + return path; + } + +fallback: + fprintf(stderr, "%s: cannot obtain path as UTF-8!\n", fname); + const char *local = ".\\Subsurface"; + const int len = strlen(local) + 1; + path = (char *)realloc(path, len); + memset(path, 0, len); + strcat(path, local); + return path; +} + +/* by passing NULL to system_default_path_append() we obtain the pure path. + * '\' not included at the end. + */ +const char *system_default_directory(void) +{ + static const char *path = NULL; + if (!path) + path = system_default_path_append(NULL); + return path; +} + +/* obtain the Roaming path and append "\\.xml" to it. + */ const char *system_default_filename(void) { - char datapath[MAX_PATH]; - const char *user; - char *buffer; - int len; - - /* I don't think this works on Windows */ - user = getenv("USERNAME"); - if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, datapath))) { - datapath[0] = '.'; - datapath[1] = '\0'; + static wchar_t filename[UNLEN + 5] = { 0 }; + if (!*filename) { + wchar_t username[UNLEN + 1] = { 0 }; + DWORD username_len = UNLEN + 1; + GetUserNameW(username, &username_len); + wcscat(filename, username); + wcscat(filename, L".xml"); } - len = strlen(datapath) + strlen(user) + 17; - buffer = malloc(len); - snprintf(buffer, len, "%s\\Subsurface\\%s.xml", datapath, user); - return buffer; + static const char *path = NULL; + if (!path) + path = system_default_path_append(filename); + return path; } int enumerate_devices(device_callback_t callback, void *userdata, int dc_type) -- cgit v1.2.3-70-g09d2