core,w32: Improve handling of Unicode paths
authorAndre Heinecke <aheinecke@gnupg.org>
Tue, 9 Apr 2019 11:42:58 +0000 (13:42 +0200)
committerAndre Heinecke <aheinecke@gnupg.org>
Tue, 9 Apr 2019 11:42:58 +0000 (13:42 +0200)
* 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
src/posix-util.c
src/sys-util.h
src/w32-io.c
src/w32-util.c

index d481199..5db6109 100644 (file)
@@ -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);
index 881856c..cddb31f 100644 (file)
@@ -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);
+}
index 6cb2224..e537613 100644 (file)
@@ -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 */
index 67f93ba..c5c21f5 100644 (file)
@@ -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);
index 9802d9c..eced139 100644 (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.  */
 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;
+}
 \f
 /* Entry point called by the DLL loader.  */
 #ifdef DLL_EXPORT