From a82e3a0ae57a48ba173e282a050680751006c074 Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Tue, 9 Apr 2019 13:42:58 +0200 Subject: [PATCH] core,w32: Improve handling of Unicode paths * src/dirinfo.c (get_gpgconf_item): Use _gpgme_access. * src/posix-util.c (_gpgme_access): Add forward to normal access. * src/sys-util.h (_gpgme_access): New for posix and w32. * src/w32-io.c (_gpgme_io_spawn): Use _gpgme_crate_process_utf8. * src/w32-util.c (utf8_to_wchar, utf8_to_wchar0): The usual w32 conv. (find_program_in_dir): Use _gpgme_access. (find_program_at_standard_place): Use wchar API and convert to UTF-8. (_gpgme_access): Convert UTF-8 to wchar and use wchar API. (_gpgme_create_process_utf8): Convert UTF-8 to wchar and use wchar API. -- While we should not say that we have full support for unicode path installations of GnuPG, this ensures that GPGME works if GPGME itself is installed in a unicode path. e.g.: Libreoffice supports this. GnuPG-Bug-Id: T4453 Based on a patch provided by Egor Pugin. Thanks. --- src/dirinfo.c | 2 +- src/posix-util.c | 7 ++++ src/sys-util.h | 13 ++++++ src/w32-io.c | 20 ++++----- src/w32-util.c | 105 +++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 128 insertions(+), 19 deletions(-) diff --git a/src/dirinfo.c b/src/dirinfo.c index d4811990..5db61093 100644 --- a/src/dirinfo.c +++ b/src/dirinfo.c @@ -260,7 +260,7 @@ get_gpgconf_item (int what) char *pgmname; pgmname = dirinfo.disable_gpgconf? NULL : _gpgme_get_gpgconf_path (); - if (pgmname && access (pgmname, F_OK)) + if (pgmname && _gpgme_access (pgmname, F_OK)) { _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL, "gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname); diff --git a/src/posix-util.c b/src/posix-util.c index 881856ca..cddb31f4 100644 --- a/src/posix-util.c +++ b/src/posix-util.c @@ -157,3 +157,10 @@ _gpgme_allow_set_foreground_window (pid_t pid) (void)pid; /* Not needed. */ } + +/* See w32-util.c */ +int +_gpgme_access (const char *path, int mode) +{ + return access (path, mode); +} diff --git a/src/sys-util.h b/src/sys-util.h index 6cb22245..e5376133 100644 --- a/src/sys-util.h +++ b/src/sys-util.h @@ -28,9 +28,22 @@ int _gpgme_set_override_inst_dir (const char *dir); char *_gpgme_get_gpg_path (void); char *_gpgme_get_gpgconf_path (void); +int _gpgme_access (const char *path_utf8, int mode); + #ifdef HAVE_W32_SYSTEM const char *_gpgme_get_inst_dir (void); void _gpgme_w32_cancel_synchronous_io (HANDLE thread); +/* See CreateProcessA returns true on success */ +int _gpgme_create_process_utf8 (const char *application_name_utf8, + char *command_line_utf8, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + void *lpEnvironment, + char *working_directory_utf8, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); #endif #endif /* SYS_UTIL_H */ diff --git a/src/w32-io.c b/src/w32-io.c index 67f93baa..c5c21f59 100644 --- a/src/w32-io.c +++ b/src/w32-io.c @@ -1481,16 +1481,16 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, free (tmp_name); return TRACE_SYSRES (-1); } - if (!CreateProcessA (spawnhelper, - arg_string, - &sec_attr, /* process security attributes */ - &sec_attr, /* thread security attributes */ - FALSE, /* inherit handles */ - cr_flags, /* creation flags */ - NULL, /* environment */ - NULL, /* use current drive/directory */ - &si, /* startup information */ - &pi)) /* returns process information */ + if (!_gpgme_create_process_utf8 (spawnhelper, + arg_string, + &sec_attr, /* process security attributes */ + &sec_attr, /* thread security attributes */ + FALSE, /* inherit handles */ + cr_flags, /* creation flags */ + NULL, /* environment */ + NULL, /* use current drive/directory */ + &si, /* startup information */ + &pi)) /* returns process information */ { int lasterr = (int)GetLastError (); TRACE_LOG ("CreateProcess failed: ec=%d", lasterr); diff --git a/src/w32-util.c b/src/w32-util.c index 9802d9cc..eced1396 100644 --- a/src/w32-util.c +++ b/src/w32-util.c @@ -168,6 +168,48 @@ wchar_to_utf8 (const wchar_t *string) } +/* Return a malloced wide char string from an UTF-8 encoded input + string STRING. Caller must free this value. On failure returns + NULL; caller may use GetLastError to get the actual error number. + Calling this function with STRING set to NULL is not defined. */ +static wchar_t * +utf8_to_wchar (const char *string) +{ + int n; + wchar_t *result; + + + n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); + if (n < 0) + return NULL; + + result = (wchar_t *) malloc ((n+1) * sizeof *result); + if (!result) + return NULL; + + n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); + if (n < 0) + { + free (result); + return NULL; + } + return result; +} + + +/* Same as utf8_to_wchar but calling it with NULL returns + NULL. So a return value of NULL only indicates failure + if STRING is not set to NULL. */ +static wchar_t * +utf8_to_wchar0 (const char *string) +{ + if (!string) + return NULL; + + return utf8_to_wchar (string); +} + + /* Replace all forward slashes by backslashes. */ static void replace_slashes (char *string) @@ -395,7 +437,7 @@ find_program_in_dir (const char *dir, const char *name) if (!result) return NULL; - if (access (result, F_OK)) + if (_gpgme_access (result, F_OK)) { free (result); return NULL; @@ -408,7 +450,7 @@ find_program_in_dir (const char *dir, const char *name) static char * find_program_at_standard_place (const char *name) { - char path[MAX_PATH]; + wchar_t path[MAX_PATH]; char *result = NULL; /* See https://wiki.tcl-lang.org/page/Getting+Windows+%22special+folders%22+with+Ffidl for details on compatibility. @@ -416,20 +458,24 @@ find_program_at_standard_place (const char *name) We First try the generic place and then fallback to the x86 (i.e. 32 bit) place. This will prefer a 64 bit of the program over a 32 bit version on 64 bit Windows if installed. */ - if (SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILES, 0)) + if (SHGetSpecialFolderPathW (NULL, path, CSIDL_PROGRAM_FILES, 0)) { - result = _gpgme_strconcat (path, "\\", name, NULL); - if (result && access (result, F_OK)) + char *utf8_path = wchar_to_utf8 (path); + result = _gpgme_strconcat (utf8_path, "\\", name, NULL); + free (utf8_path); + if (result && _gpgme_access (result, F_OK)) { free (result); result = NULL; } } if (!result - && SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0)) + && SHGetSpecialFolderPathW (NULL, path, CSIDL_PROGRAM_FILESX86, 0)) { - result = _gpgme_strconcat (path, "\\", name, NULL); - if (result && access (result, F_OK)) + char *utf8_path = wchar_to_utf8 (path); + result = _gpgme_strconcat (utf8_path, "\\", name, NULL); + free (utf8_path); + if (result && _gpgme_access (result, F_OK)) { free (result); result = NULL; @@ -781,6 +827,49 @@ _gpgme_mkstemp (int *fd, char **name) } +/* Like access but using windows _waccess */ +int +_gpgme_access (const char *path, int mode) +{ + wchar_t *u16 = utf8_to_wchar0 (path); + int r = _waccess (u16, F_OK); + + free(u16); + return r; +} + +/* Like CreateProcessA but mapping the arguments to wchar API */ +int _gpgme_create_process_utf8 (const char *application_name_utf8, + char *command_line_utf8, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + void *lpEnvironment, + char *working_directory_utf8, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) +{ + BOOL ret; + wchar_t *application_name = utf8_to_wchar0 (application_name_utf8); + wchar_t *command_line = utf8_to_wchar0 (command_line_utf8); + wchar_t *working_directory = utf8_to_wchar0 (working_directory_utf8); + + ret = CreateProcessW (application_name, + command_line, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags, + lpEnvironment, + working_directory, + lpStartupInfo, + lpProcessInformation); + free (application_name); + free (command_line); + free (working_directory); + return ret; +} /* Entry point called by the DLL loader. */ #ifdef DLL_EXPORT