w32: Try to locate gpg in the gpgme installation dir.
authorWerner Koch <wk@gnupg.org>
Fri, 2 Aug 2013 10:48:30 +0000 (12:48 +0200)
committerWerner Koch <wk@gnupg.org>
Fri, 2 Aug 2013 10:48:30 +0000 (12:48 +0200)
* src/w32-util.c (my_hmodule): New.
(wchar_to_utf8): New.
(DllMain): New.
(_gpgme_get_inst_dir): New.
(find_program_in_dir): New.
(find_program_in_inst_dir): Add arg INST_DIR.
(_gpgme_get_gpg_path): Get inst_dir before acquiring the lock.
(_gpgme_get_gpgconf_path): Ditto.
(_gpgme_get_g13_path): Ditto.
(_gpgme_get_w32spawn_path): Ditto.

src/w32-util.c

index a90f405..27dc5bc 100644 (file)
@@ -1,24 +1,23 @@
 /* w32-util.c - Utility functions for the W32 API
-   Copyright (C) 1999 Free Software Foundation, Inc
-   Copyright (C) 2001 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
-
-   This file is part of GPGME.
-
-   GPGME is free software; you can redistribute it and/or modify it
-   under the terms of the GNU Lesser General Public License as
-   published by the Free Software Foundation; either version 2.1 of
-   the License, or (at your option) any later version.
-
-   GPGME is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+ * Copyright (C) 1999 Free Software Foundation, Inc
+ * Copyright (C) 2001 Werner Koch (dd9jn)
+ * Copyright (C) 2001, 2002, 2003, 2004, 2007, 2013 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ **/
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 
 DEFINE_STATIC_LOCK (get_path_lock);
 
+/* The module handle of this DLL.  If we are linked statically,
+   dllmain does not exists and thus the value of my_hmodule will be
+   NULL.  The effect is that a GetModuleFileName always returns the
+   file name of the DLL or executable which contains the gpgme code.  */
+static HMODULE my_hmodule;
+
 
 #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
 
@@ -112,6 +117,39 @@ dlclose (void * hd)
 }
 #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
 
+
+/* Return a malloced string encoded in UTF-8 from the wide char input
+   string STRING.  Caller must free this value.  Returns NULL and sets
+   ERRNO on failure.  Calling this function with STRING set to NULL is
+   not defined.  */
+static char *
+wchar_to_utf8 (const wchar_t *string)
+{
+  int n;
+  char *result;
+
+  n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
+  if (n < 0)
+    {
+      gpg_err_set_errno (EINVAL);
+      return NULL;
+    }
+
+  result = malloc (n+1);
+  if (!result)
+    return NULL;
+
+  n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL);
+  if (n < 0)
+    {
+      free (result);
+      gpg_err_set_errno (EINVAL);
+      result = NULL;
+    }
+  return result;
+}
+
+
 void
 _gpgme_allow_set_foreground_window (pid_t pid)
 {
@@ -270,51 +308,53 @@ read_w32_registry_string (const char *root, const char *dir, const char *name)
 }
 
 
-#if 0
-static char *
-find_program_in_registry (const char *name)
+/* Return the name of the directory with the gpgme DLL or the EXE (if
+   statically linked).  May return NULL on severe errors. */
+const char *
+_gpgme_get_inst_dir (void)
 {
-  char *program = NULL;
+  static char *inst_dir;
 
-  program = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", name);
-  if (program)
+  LOCK (get_path_lock);
+  if (!inst_dir)
     {
-      int i;
+      wchar_t *moddir;
 
-      TRACE2 (DEBUG_CTX, "gpgme:find_program_in_registry", 0,
-             "found %s in registry: `%s'", name, program);
-      for (i = 0; program[i]; i++)
-       {
-         if (program[i] == '/')
-           program[i] = '\\';
-       }
+      moddir = malloc ((MAX_PATH+5) * sizeof *moddir);
+      if (moddir)
+        {
+          if (!GetModuleFileNameW (my_hmodule, moddir, MAX_PATH))
+            *moddir = 0;
+          if (!*moddir)
+            gpg_err_set_errno (ENOENT);
+          else
+            {
+              inst_dir = wchar_to_utf8 (moddir);
+              if (inst_dir)
+                {
+                  char *p = strrchr (inst_dir, '\\');
+                  if (p)
+                    *p = 0;
+                }
+            }
+          free (moddir);
+        }
     }
-  return program;
+  UNLOCK (get_path_lock);
+  return inst_dir;
 }
-#endif
 
 
 static char *
-find_program_in_inst_dir (const char *name)
+find_program_in_dir (const char *dir, const char *name)
 {
-  char *result = NULL;
-  char *tmp;
+  char *result;
 
-  tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
-                                 "Software\\GNU\\GnuPG",
-                                 "Install Directory");
-  if (!tmp)
-    return NULL;
-
-  result = malloc (strlen (tmp) + 1 + strlen (name) + 1);
+  result = malloc (strlen (dir) + 1 + strlen (name) + 1);
   if (!result)
-    {
-      free (tmp);
-      return NULL;
-    }
+    return NULL;
 
-  strcpy (stpcpy (stpcpy (result, tmp), "\\"), name);
-  free (tmp);
+  strcpy (stpcpy (stpcpy (result, dir), "\\"), name);
   if (access (result, F_OK))
     {
       free (result);
@@ -326,6 +366,40 @@ find_program_in_inst_dir (const char *name)
 
 
 static char *
+find_program_in_inst_dir (const char *inst_dir, const char *name)
+{
+  char *result;
+  char *dir;
+
+  /* If an installation directory has been passed, this overrides a
+     location given bu the registry.  The idea here is that we prefer
+     a a program installed alongside with gpgme.  We don't want the
+     registry to override this to have a better isolation of an gpgme
+     aware applications for other effects.  Note that the "Install
+     Directory" registry item has been used for ages in Gpg4win and
+     earlier GnuPG windows installers.  It is technically not anymore
+     required.  */
+  if (inst_dir)
+    {
+      result = find_program_in_dir (inst_dir, name);
+      if (result)
+        return result;
+    }
+
+  dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
+                                 "Software\\GNU\\GnuPG",
+                                 "Install Directory");
+  if (dir)
+    {
+      result = find_program_in_dir (dir, name);
+      free (dir);
+      return result;
+    }
+  return NULL;
+}
+
+
+static char *
 find_program_at_standard_place (const char *name)
 {
   char path[MAX_PATH];
@@ -353,14 +427,12 @@ const char *
 _gpgme_get_gpg_path (void)
 {
   static char *gpg_program;
+  const char *inst_dir;
 
+  inst_dir = _gpgme_get_inst_dir ();
   LOCK (get_path_lock);
-#if 0
   if (!gpg_program)
-    gpg_program = find_program_in_registry ("gpgProgram");
-#endif
-  if (!gpg_program)
-    gpg_program = find_program_in_inst_dir ("gpg.exe");
+    gpg_program = find_program_in_inst_dir (inst_dir, "gpg.exe");
   if (!gpg_program)
     gpg_program = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe");
   UNLOCK (get_path_lock);
@@ -372,14 +444,12 @@ const char *
 _gpgme_get_gpgsm_path (void)
 {
   static char *gpgsm_program;
+  const char *inst_dir;
 
+  inst_dir = _gpgme_get_inst_dir ();
   LOCK (get_path_lock);
-#if 0
   if (!gpgsm_program)
-    gpgsm_program = find_program_in_registry ("gpgsmProgram");
-#endif
-  if (!gpgsm_program)
-    gpgsm_program = find_program_in_inst_dir ("gpgsm.exe");
+    gpgsm_program = find_program_in_inst_dir (inst_dir, "gpgsm.exe");
   if (!gpgsm_program)
     gpgsm_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
   UNLOCK (get_path_lock);
@@ -391,14 +461,12 @@ const char *
 _gpgme_get_gpgconf_path (void)
 {
   static char *gpgconf_program;
+  const char *inst_dir;
 
+  inst_dir = _gpgme_get_inst_dir ();
   LOCK (get_path_lock);
-#if 0
   if (!gpgconf_program)
-    gpgconf_program = find_program_in_registry ("gpgconfProgram");
-#endif
-  if (!gpgconf_program)
-    gpgconf_program = find_program_in_inst_dir ("gpgconf.exe");
+    gpgconf_program = find_program_in_inst_dir (inst_dir, "gpgconf.exe");
   if (!gpgconf_program)
     gpgconf_program
       = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
@@ -411,14 +479,12 @@ const char *
 _gpgme_get_g13_path (void)
 {
   static char *g13_program;
+  const char *inst_dir;
 
+  inst_dir = _gpgme_get_inst_dir ();
   LOCK (get_path_lock);
-#if 0
-  if (!g13_program)
-    g13_program = find_program_in_registry ("g13Program");
-#endif
   if (!g13_program)
-    g13_program = find_program_in_inst_dir ("g13.exe");
+    g13_program = find_program_in_inst_dir (inst_dir, "g13.exe");
   if (!g13_program)
     g13_program = find_program_at_standard_place ("GNU\\GnuPG\\g13.exe");
   UNLOCK (get_path_lock);
@@ -453,10 +519,12 @@ const char *
 _gpgme_get_w32spawn_path (void)
 {
   static char *w32spawn_program;
+  const char *inst_dir;
 
+  inst_dir = _gpgme_get_inst_dir ();
   LOCK (get_path_lock);
   if (!w32spawn_program)
-    w32spawn_program = find_program_in_inst_dir ("gpgme-w32spawn.exe");
+    w32spawn_program = find_program_in_inst_dir (inst_dir,"gpgme-w32spawn.exe");
   if (!w32spawn_program)
     w32spawn_program
       = find_program_at_standard_place ("GNU\\GnuPG\\gpgme-w32spawn.exe");
@@ -641,3 +709,18 @@ _gpgme_w32ce_get_debug_envvar (void)
   return tmp;
 }
 #endif /*HAVE_W32CE_SYSTEM*/
+
+
+/* Entry point called by the DLL loader.  */
+#ifdef DLL_EXPORT
+int WINAPI
+DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
+{
+  (void)reserved;
+
+  if (reason == DLL_PROCESS_ATTACH)
+    my_hmodule = hinst;
+
+  return TRUE;
+}
+#endif /*DLL_EXPORT*/