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.
This commit is contained in:
Andre Heinecke 2019-04-09 13:42:58 +02:00
parent 937adfdcbb
commit a82e3a0ae5
No known key found for this signature in database
GPG Key ID: 2978E9D40CBABA5C
5 changed files with 128 additions and 19 deletions

View File

@ -260,7 +260,7 @@ get_gpgconf_item (int what)
char *pgmname; char *pgmname;
pgmname = dirinfo.disable_gpgconf? NULL : _gpgme_get_gpgconf_path (); 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_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname); "gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname);

View File

@ -157,3 +157,10 @@ _gpgme_allow_set_foreground_window (pid_t pid)
(void)pid; (void)pid;
/* Not needed. */ /* Not needed. */
} }
/* See w32-util.c */
int
_gpgme_access (const char *path, int mode)
{
return access (path, mode);
}

View File

@ -28,9 +28,22 @@ int _gpgme_set_override_inst_dir (const char *dir);
char *_gpgme_get_gpg_path (void); char *_gpgme_get_gpg_path (void);
char *_gpgme_get_gpgconf_path (void); char *_gpgme_get_gpgconf_path (void);
int _gpgme_access (const char *path_utf8, int mode);
#ifdef HAVE_W32_SYSTEM #ifdef HAVE_W32_SYSTEM
const char *_gpgme_get_inst_dir (void); const char *_gpgme_get_inst_dir (void);
void _gpgme_w32_cancel_synchronous_io (HANDLE thread); 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
#endif /* SYS_UTIL_H */ #endif /* SYS_UTIL_H */

View File

@ -1481,16 +1481,16 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
free (tmp_name); free (tmp_name);
return TRACE_SYSRES (-1); return TRACE_SYSRES (-1);
} }
if (!CreateProcessA (spawnhelper, if (!_gpgme_create_process_utf8 (spawnhelper,
arg_string, arg_string,
&sec_attr, /* process security attributes */ &sec_attr, /* process security attributes */
&sec_attr, /* thread security attributes */ &sec_attr, /* thread security attributes */
FALSE, /* inherit handles */ FALSE, /* inherit handles */
cr_flags, /* creation flags */ cr_flags, /* creation flags */
NULL, /* environment */ NULL, /* environment */
NULL, /* use current drive/directory */ NULL, /* use current drive/directory */
&si, /* startup information */ &si, /* startup information */
&pi)) /* returns process information */ &pi)) /* returns process information */
{ {
int lasterr = (int)GetLastError (); int lasterr = (int)GetLastError ();
TRACE_LOG ("CreateProcess failed: ec=%d", lasterr); TRACE_LOG ("CreateProcess failed: ec=%d", lasterr);

View File

@ -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. */ /* Replace all forward slashes by backslashes. */
static void static void
replace_slashes (char *string) replace_slashes (char *string)
@ -395,7 +437,7 @@ find_program_in_dir (const char *dir, const char *name)
if (!result) if (!result)
return NULL; return NULL;
if (access (result, F_OK)) if (_gpgme_access (result, F_OK))
{ {
free (result); free (result);
return NULL; return NULL;
@ -408,7 +450,7 @@ find_program_in_dir (const char *dir, const char *name)
static char * static char *
find_program_at_standard_place (const char *name) find_program_at_standard_place (const char *name)
{ {
char path[MAX_PATH]; wchar_t path[MAX_PATH];
char *result = NULL; char *result = NULL;
/* See https://wiki.tcl-lang.org/page/Getting+Windows+%22special+folders%22+with+Ffidl for details on compatibility. /* 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 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 (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. */ 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); char *utf8_path = wchar_to_utf8 (path);
if (result && access (result, F_OK)) result = _gpgme_strconcat (utf8_path, "\\", name, NULL);
free (utf8_path);
if (result && _gpgme_access (result, F_OK))
{ {
free (result); free (result);
result = NULL; result = NULL;
} }
} }
if (!result if (!result
&& SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0)) && SHGetSpecialFolderPathW (NULL, path, CSIDL_PROGRAM_FILESX86, 0))
{ {
result = _gpgme_strconcat (path, "\\", name, NULL); char *utf8_path = wchar_to_utf8 (path);
if (result && access (result, F_OK)) result = _gpgme_strconcat (utf8_path, "\\", name, NULL);
free (utf8_path);
if (result && _gpgme_access (result, F_OK))
{ {
free (result); free (result);
result = NULL; 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. */ /* Entry point called by the DLL loader. */
#ifdef DLL_EXPORT #ifdef DLL_EXPORT