From 35cd165bec46743351978c47b2221b78e21039f7 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 20 Apr 2018 14:09:53 -0700 Subject: [PATCH] Bug 795134 - Improper handle of unicode character in username Part 1: gnc-filename-utils didn't manage the encoding differences between Windows (UTF16) and GLib (UTF-8). --- gnucash/gnucash-bin.c | 13 ++- libgnucash/core-utils/gnc-filepath-utils.cpp | 106 +++++++++---------- libgnucash/engine/qoflog.cpp | 2 +- 3 files changed, 61 insertions(+), 60 deletions(-) diff --git a/gnucash/gnucash-bin.c b/gnucash/gnucash-bin.c index 51525ecb3b..e9a8277633 100644 --- a/gnucash/gnucash-bin.c +++ b/gnucash/gnucash-bin.c @@ -81,7 +81,11 @@ static int gnucash_show_version = 0; static int debugging = 0; static int extra = 0; static gchar **log_flags = NULL; -static gchar *log_to_filename = NULL; +#ifdef __MINGW64__ +static wchar_t *log_to_filename = NULL; +#else +static char *log_to_filename = NULL; +#endif static int nofile = 0; static const gchar *gsettings_prefix = NULL; static const char *add_quotes_file = NULL; @@ -679,7 +683,12 @@ gnc_log_init() { if (log_to_filename != NULL) { - qof_log_init_filename_special(log_to_filename); +#ifdef __MINGW64__ + char* filename = g_utf16_to_utf8(log_to_filename, -1, NULL, NULL, NULL); +#else + char* filename = log_to_filename; +#endif + qof_log_init_filename_special(filename); } else { diff --git a/libgnucash/core-utils/gnc-filepath-utils.cpp b/libgnucash/core-utils/gnc-filepath-utils.cpp index e259798d3f..6fc3a080c5 100644 --- a/libgnucash/core-utils/gnc-filepath-utils.cpp +++ b/libgnucash/core-utils/gnc-filepath-utils.cpp @@ -66,14 +66,19 @@ extern "C" { #include #include +#include #include - #if PLATFORM(WINDOWS) -#include -#include +using codecvt = std::codecvt_utf8; +using string = std::wstring; +#else +using codecvt = std::codecvt; +using string = std::string; #endif +static codecvt cvt; +static std::locale bfs_locale(std::locale(), new codecvt); namespace bfs = boost::filesystem; namespace bst = boost::system; @@ -366,7 +371,8 @@ gnc_validate_directory (const bfs::path &dirname) * we need to overrule it during build (when guile interferes) * and testing. */ - auto home_dir = bfs::path (g_get_home_dir ()); + bfs::path home_dir(g_get_home_dir(), cvt); + home_dir.imbue(bfs_locale); auto homedir_exists = bfs::exists(home_dir); auto is_descendant = dir_is_descendant (dirname, home_dir); if (!homedir_exists && is_descendant) @@ -432,11 +438,16 @@ copy_recursive(const bfs::path& src, const bfs::path& dest) for(auto direntry = bfs::recursive_directory_iterator(src); direntry != bfs::recursive_directory_iterator(); ++direntry) { - auto cur_str = direntry->path().string(); +#ifdef G_OS_WIN32 + string cur_str = direntry->path().wstring(); +#else + string cur_str = direntry->path().string(); +#endif auto cur_len = cur_str.size(); - auto rel_str = std::string(cur_str, old_len, cur_len - old_len); - auto relpath = bfs::path(rel_str).relative_path(); - auto newpath = bfs::absolute (relpath, dest); + string rel_str(cur_str, old_len, cur_len - old_len); + bfs::path relpath(rel_str, cvt); + auto newpath = bfs::absolute (relpath.relative_path(), dest); + newpath.imbue(bfs_locale); bfs::copy(direntry->path(), newpath); } } @@ -458,31 +469,27 @@ copy_recursive(const bfs::path& src, const bfs::path& dest) * So this function is a copy of glib's internal get_special_folder * and minimally adjusted to fetch CSIDL_APPDATA */ -static char * -win32_get_userdata_home (void) +static bfs::path +get_user_data_dir () { wchar_t path[MAX_PATH+1]; HRESULT hr; LPITEMIDLIST pidl = NULL; BOOL b; - char *retval = NULL; hr = SHGetSpecialFolderLocation (NULL, CSIDL_APPDATA, &pidl); if (hr == S_OK) { b = SHGetPathFromIDListW (pidl, path); - if (b) - { - std::wstring_convert> utf8_conv; - retval = g_strdup(utf8_conv.to_bytes(path).c_str()); - } CoTaskMemFree (pidl); } + bfs::path retval(path, cvt); + retval.imbue(bfs_locale); return retval; } #elif defined MAC_INTEGRATION -static char* -quarz_get_userdata_home(void) +static bfs::path +get_user_data_dir() { char *retval = NULL; NSFileManager*fm = [NSFileManager defaultManager]; @@ -492,9 +499,14 @@ quarz_get_userdata_home(void) { NSURL* dirUrl = [appSupportDir objectAtIndex:0]; NSString* dirPath = [dirUrl path]; - retval = g_strdup([dirPath UTF8String]); } - return retval; + return [dirPath UTF8String]; +} +#else +static bfs::path +get_user_data_dir() +{ + return g_get_user_data_dir(); } #endif @@ -508,22 +520,8 @@ static bfs::path get_userdata_home(void) { auto try_tmp_dir = true; - gchar *data_dir = NULL; - auto userdata_home = bfs::path(); + auto userdata_home = get_user_data_dir(); -#ifdef G_OS_WIN32 - data_dir = win32_get_userdata_home (); -#elif defined MAC_INTEGRATION - data_dir = quarz_get_userdata_home (); -#endif - - if (data_dir) - { - userdata_home = data_dir; - g_free(data_dir); - } - else - userdata_home = g_get_user_data_dir(); /* g_get_user_data_dir doesn't check whether the path exists nor attempts to * create it. So while it may return an actual path we may not be able to use it. @@ -548,7 +546,9 @@ get_userdata_home(void) Hopefully we can always write there. */ if (try_tmp_dir) { - userdata_home = bfs::path (g_get_tmp_dir ()) / g_get_user_name (); + bfs::path newpath(g_get_tmp_dir (), cvt); + newpath.imbue(bfs_locale); + userdata_home = std::move(newpath); } g_assert(!userdata_home.empty()); @@ -564,33 +564,21 @@ get_userdata_home(void) static bfs::path get_userconfig_home(void) { - gchar *config_dir = NULL; - auto userconfig_home = bfs::path(); - -#ifdef G_OS_WIN32 - config_dir = win32_get_userdata_home (); -#elif defined MAC_INTEGRATION - config_dir = quarz_get_userdata_home (); -#endif - /* On Windows and Macs the data directory is used, for Linux $HOME/.config is used */ - if (config_dir) - { - userconfig_home = config_dir; - g_free(config_dir); - } - else - userconfig_home = g_get_user_config_dir(); - - return userconfig_home; +#if defined (G_OS_WIN32) || defined (MAC_INTEGRATION) + return get_user_data_dir(); +#else + return g_get_user_config_dir(); +#endif } static std::string migrate_gnc_datahome() { auto success = false; // Specify location of dictionaries - auto old_dir = bfs::path(g_get_home_dir()) / ".gnucash"; + bfs::path old_dir(g_get_home_dir(), cvt); + old_dir += ".gnucash"; bl::generator gen; gen.add_messages_path(gnc_path_get_datadir()); @@ -760,7 +748,9 @@ gnc_filepath_init (void) * issues when the build environment is not a complete environment (like * it could be missing a valid home directory). */ auto env_build_dir = g_getenv ("GNC_BUILDDIR"); - build_dir = bfs::path(env_build_dir ? env_build_dir : ""); + bfs::path new_dir(env_build_dir ? env_build_dir : "", cvt); + new_dir.imbue(bfs_locale); + build_dir = std::move(new_dir); auto running_uninstalled = (g_getenv ("GNC_UNINSTALLED") != NULL); if (running_uninstalled && !build_dir.empty()) { @@ -787,7 +777,9 @@ gnc_filepath_init (void) auto gnc_userdata_home_env = g_getenv ("GNC_DATA_HOME"); if (gnc_userdata_home_env) { - gnc_userdata_home = bfs::path (gnc_userdata_home_env); + bfs::path newdir(gnc_userdata_home_env, cvt); + newdir.imbue(bfs_locale); + gnc_userdata_home = std::move(newdir); try { gnc_userdata_home_exists = bfs::exists (gnc_userdata_home); diff --git a/libgnucash/engine/qoflog.cpp b/libgnucash/engine/qoflog.cpp index 7d0707b3e6..fc2738dffc 100644 --- a/libgnucash/engine/qoflog.cpp +++ b/libgnucash/engine/qoflog.cpp @@ -166,7 +166,7 @@ qof_log_init_filename(const gchar* log_filename) #if PLATFORM(WINDOWS) /* MSVC compiler: Somehow the OS thinks file descriptor from above * still isn't open. So we open normally with the file name and that's it. */ - fout = fopen(fname, "wb"); + fout = g_fopen(fname, "wb"); #else /* We must not overwrite /dev/null */ g_assert(g_strcmp0(log_filename, "/dev/null") != 0);