/* 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 <https://www.gnu.org/licenses/>.
+ **/
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "ath.h"
#include "sema.h"
#include "debug.h"
+#include "sys-util.h"
#ifndef HAVE_W32CE_SYSTEM
# define F_OK 0
#endif
+/* The Registry key used by GNUPG. */
+#ifdef _WIN64
+# define GNUPG_REGKEY_2 "Software\\Wow6432Node\\GNU\\GnuPG"
+#else
+# define GNUPG_REGKEY_2 "Software\\GNU\\GnuPG"
+#endif
+#ifdef _WIN64
+# define GNUPG_REGKEY_3 "Software\\Wow6432Node\\GnuPG"
+#else
+# define GNUPG_REGKEY_3 "Software\\GnuPG"
+#endif
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;
+
+/* These variables store the malloced name of alternative default
+ binaries. The are set only once by gpgme_set_global_flag. */
+static char *default_gpg_name;
+static char *default_gpgconf_name;
+/* If this variable is not NULL the value is assumed to be the
+ installation directory. The variable may only be set once by
+ gpgme_set_global_flag and accessed by _gpgme_get_inst_dir. */
+static char *override_inst_dir;
#ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
dlopen (const char * name, int flag)
{
void * hd = LoadLibrary (name);
+
+ (void)flag;
return 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;
+}
+
+
+/* Replace all forward slashes by backslashes. */
+static void
+replace_slashes (char *string)
+{
+ for (; *string; string++)
+ if (*string == '/')
+ *string = '\\';
+}
+
+
+/* Get the base name of NAME. Returns a pointer into NAME right after
+ the last slash or backslash or to NAME if no slash or backslash
+ exists. */
+static const char *
+get_basename (const char *name)
+{
+ const char *mark, *s;
+
+ for (mark=NULL, s=name; *s; s++)
+ if (*s == '/' || *s == '\\')
+ mark = s;
+
+ return mark? mark+1 : name;
+}
+
+
void
_gpgme_allow_set_foreground_window (pid_t pid)
{
}
-#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;
+
+ if (override_inst_dir)
+ return override_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 = _gpgme_strconcat (dir, "\\", name, NULL);
if (!result)
- {
- free (tmp);
- return NULL;
- }
+ return NULL;
- strcpy (stpcpy (stpcpy (result, tmp), "\\"), name);
- free (tmp);
if (access (result, F_OK))
{
free (result);
char path[MAX_PATH];
char *result = NULL;
- /* See http://wiki.tcl.tk/17492 for details on compatibility. */
+ /* See http://wiki.tcl.tk/17492 for details on compatibility.
+
+ 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))
{
- result = malloc (strlen (path) + 1 + strlen (name) + 1);
- if (result)
+ result = _gpgme_strconcat (path, "\\", name, NULL);
+ if (result && access (result, F_OK))
{
- strcpy (stpcpy (stpcpy (result, path), "\\"), name);
- if (access (result, F_OK))
- {
- free (result);
- result = NULL;
- }
+ free (result);
+ result = NULL;
+ }
+ }
+ if (!result
+ && SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0))
+ {
+ result = _gpgme_strconcat (path, "\\", name, NULL);
+ if (result && access (result, F_OK))
+ {
+ free (result);
+ result = NULL;
}
}
return result;
}
-const char *
-_gpgme_get_gpg_path (void)
+/* Set the default name for the gpg binary. This function may only be
+ called by gpgme_set_global_flag. Returns 0 on success. */
+int
+_gpgme_set_default_gpg_name (const char *name)
{
- static char *gpg_program;
+ if (!default_gpg_name)
+ {
+ default_gpg_name = _gpgme_strconcat (name, ".exe", NULL);
+ if (default_gpg_name)
+ replace_slashes (default_gpg_name);
+ }
+ return !default_gpg_name;
+}
- 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");
- if (!gpg_program)
- gpg_program = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe");
- UNLOCK (get_path_lock);
- return gpg_program;
+/* Set the default name for the gpgconf binary. This function may only be
+ called by gpgme_set_global_flag. Returns 0 on success. */
+int
+_gpgme_set_default_gpgconf_name (const char *name)
+{
+ if (!default_gpgconf_name)
+ {
+ default_gpgconf_name = _gpgme_strconcat (name, ".exe", NULL);
+ if (default_gpgconf_name)
+ replace_slashes (default_gpgconf_name);
+ }
+ return !default_gpgconf_name;
}
-const char *
-_gpgme_get_gpgsm_path (void)
+/* Set the override installation directory. This function may only be
+ called by gpgme_set_global_flag. Returns 0 on success. */
+int
+_gpgme_set_override_inst_dir (const char *dir)
{
- static char *gpgsm_program;
-
- 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");
- if (!gpgsm_program)
- gpgsm_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
- UNLOCK (get_path_lock);
- return gpgsm_program;
+ if (!override_inst_dir)
+ {
+ override_inst_dir = strdup (dir);
+ if (override_inst_dir)
+ {
+ replace_slashes (override_inst_dir);
+ /* Remove a trailing slash. */
+ if (*override_inst_dir
+ && override_inst_dir[strlen (override_inst_dir)-1] == '\\')
+ override_inst_dir[strlen (override_inst_dir)-1] = 0;
+ }
+ }
+ return !override_inst_dir;
}
-const char *
-_gpgme_get_gpgconf_path (void)
+/* Return the full file name of the GPG binary. This function is used
+ iff gpgconf was not found and thus it can be assumed that gpg2 is
+ not installed. This function is only called by get_gpgconf_item
+ and may not be called concurrently. */
+char *
+_gpgme_get_gpg_path (void)
{
- static char *gpgconf_program;
+ char *gpg = NULL;
+ const char *name, *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");
- if (!gpgconf_program)
- gpgconf_program
- = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
- UNLOCK (get_path_lock);
- return gpgconf_program;
-}
+ name = default_gpg_name? get_basename (default_gpg_name) : "gpg.exe";
+ /* 1. Try to find gpg.exe in the installation directory of gpgme. */
+ inst_dir = _gpgme_get_inst_dir ();
+ if (inst_dir)
+ {
+ gpg = find_program_in_dir (inst_dir, name);
+ }
-const char *
-_gpgme_get_g13_path (void)
-{
- static char *g13_program;
+ /* 2. Try to find gpg.exe using that ancient registry key. */
+ if (!gpg)
+ {
+ char *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");
- if (!g13_program)
- g13_program = find_program_at_standard_place ("GNU\\GnuPG\\g13.exe");
- UNLOCK (get_path_lock);
- return g13_program;
+ dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
+ GNUPG_REGKEY_2,
+ "Install Directory");
+ if (dir)
+ {
+ gpg = find_program_in_dir (dir, name);
+ free (dir);
+ }
+ }
+
+ /* 3. Try to find gpg.exe below CSIDL_PROGRAM_FILES. */
+ if (!gpg)
+ {
+ name = default_gpg_name? default_gpg_name : "GNU\\GnuPG\\gpg.exe";
+ gpg = find_program_at_standard_place (name);
+ }
+
+ /* 4. Print a debug message if not found. */
+ if (!gpg)
+ _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpg_path: '%s' not found", name);
+
+ return gpg;
}
-const char *
-_gpgme_get_uiserver_socket_path (void)
+/* This function is only called by get_gpgconf_item and may not be
+ called concurrently. */
+char *
+_gpgme_get_gpgconf_path (void)
{
- static char *socket_path;
- const char *homedir;
- const char name[] = "S.uiserver";
+ char *gpgconf = NULL;
+ const char *inst_dir, *name;
- if (socket_path)
- return socket_path;
+ name = default_gpgconf_name? get_basename(default_gpgconf_name):"gpgconf.exe";
- homedir = _gpgme_get_default_homedir ();
- if (! homedir)
- return NULL;
+ /* 1. Try to find gpgconf.exe in the installation directory of gpgme. */
+ inst_dir = _gpgme_get_inst_dir ();
+ if (inst_dir)
+ {
+ gpgconf = find_program_in_dir (inst_dir, name);
+ }
- socket_path = malloc (strlen (homedir) + 1 + strlen (name) + 1);
- if (! socket_path)
- return NULL;
+ /* 2. Try to find gpgconf.exe from GnuPG >= 2.1 below CSIDL_PROGRAM_FILES. */
+ if (!gpgconf)
+ {
+ const char *name2 = (default_gpgconf_name ? default_gpgconf_name
+ /**/ : "GnuPG\\bin\\gpgconf.exe");
+ gpgconf = find_program_at_standard_place (name2);
+ }
- strcpy (stpcpy (stpcpy (socket_path, homedir), "\\"), name);
- return socket_path;
+ /* 3. Try to find gpgconf.exe using the Windows registry. */
+ if (!gpgconf)
+ {
+ char *dir;
+
+ dir = read_w32_registry_string (NULL,
+ GNUPG_REGKEY_2,
+ "Install Directory");
+ if (!dir)
+ {
+ char *tmp = read_w32_registry_string (NULL,
+ GNUPG_REGKEY_3,
+ "Install Directory");
+ if (tmp)
+ {
+ dir = _gpgme_strconcat (tmp, "\\bin", NULL);
+ free (tmp);
+ if (!dir)
+ return NULL;
+ }
+ }
+ if (dir)
+ {
+ gpgconf = find_program_in_dir (dir, name);
+ free (dir);
+ }
+ }
+
+ /* 4. Try to find gpgconf.exe from Gpg4win below CSIDL_PROGRAM_FILES. */
+ if (!gpgconf)
+ {
+ gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
+ }
+
+ /* 5. Try to find gpgconf.exe relative to us. */
+ if (!gpgconf && inst_dir)
+ {
+ char *dir = _gpgme_strconcat (inst_dir, "\\..\\..\\GnuPG\\bin");
+ gpgconf = find_program_in_dir (dir, name);
+ free (dir);
+ }
+
+ /* 5. Print a debug message if not found. */
+ if (!gpgconf)
+ _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpgconf_path: '%s' not found",name);
+
+ return gpgconf;
}
_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");
- if (!w32spawn_program)
- w32spawn_program
- = find_program_at_standard_place ("GNU\\GnuPG\\gpgme-w32spawn.exe");
+ w32spawn_program = find_program_in_dir (inst_dir, "gpgme-w32spawn.exe");
UNLOCK (get_path_lock);
return w32spawn_program;
}
does not exist at the time of the call to mkstemp. TMPL is
overwritten with the result. */
static int
-mkstemp (char *tmpl)
+my_mkstemp (char *tmpl)
{
int len;
char *XXXXXX;
}
}
- tmpname = malloc (strlen (tmp) + 13 + 1);
+ tmpname = _gpgme_strconcat (tmp, "\\gpgme-XXXXXX", NULL);
if (!tmpname)
return -1;
- strcpy (stpcpy (tmpname, tmp), "\\gpgme-XXXXXX");
- *fd = mkstemp (tmpname);
- if (fd < 0)
- return -1;
+ *fd = my_mkstemp (tmpname);
+ if (*fd < 0)
+ {
+ free (tmpname);
+ return -1;
+ }
*name = tmpname;
return 0;
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*/