Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34768436
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
91 KB
Subscribers
None
View Options
diff --git a/NEWS b/NEWS
index 3747301..671edad 100644
--- a/NEWS
+++ b/NEWS
@@ -1,150 +1,153 @@
-Noteworthy changes for version 1.0.12 (unreleased)
+Noteworthy changes for version 1.1.0 (unreleased)
-------------------------------------------------
+* Get rid of the Registry entries for finding gpgconf and the
+ UI-Server.
+
Noteworthy changes for version 1.0.11 (2024-07-03)
-------------------------------------------------
* Build system updates
Noteworthy changes for version 1.0.10 (2023-12-01)
-------------------------------------------------
* Removed outdated help entry.
* General build system maintenance.
Noteworthy changes for version 1.0.9 (2022-04-21)
-------------------------------------------------
* Added possibility for registry configuration.
Noteworthy changes for version 1.0.8 (2021-09-22)
-------------------------------------------------
* Take the UI-Server socket directory from information provided by
gpgconf. This matches the changes to the used socket directory
introduced with GnuPG 2.2.30.
Noteworthy changes for version 1.0.7 (2021-01-26)
-------------------------------------------------
* Added dutch translation.
* Moved icon to most important menu item.
Noteworthy changes for version 1.0.6 (2018-04-11)
-------------------------------------------------
* Updated portugese translation.
* Nicer PNG based Icon.
Noteworthy changes for version 1.0.5 (2017-09-12)
-------------------------------------------------
* Fixed localisation for the 64bit version.
* Added c++ hardending flags by default.
Noteworthy changes for version 1.0.4 (2016-03-30)
-------------------------------------------------
* Updated Portuguese translation.
* Improved UI Server startup.
Noteworthy changes for version 1.0.3 (2015-11-23)
-------------------------------------------------
* Added Chinese translation.
* Added French translation.
Noteworthy changes for version 1.0.2 (2014-11-05)
-------------------------------------------------
* Added Polish translation.
* Add configure option --enable-gpa-only
Noteworthy changes for version 1.0.1 (2014-07-30)
-------------------------------------------------
* Fixed segv in case GNUPGHOME is set.
* Fixed possible double starting of the UI-server.
Noteworthy changes for version 1.0.0 (2013-07-30)
-------------------------------------------------
* Support 64 bit Windows.
* Improved help file detection.
* Updated Arabian, German, Portuguese, Russian, and Spanish translations.
Noteworthy changes for version 0.9.7 (2010-07-21)
-------------------------------------------------
* Fix building with latest libgpg-error.
* Add Portuguese translation.
Noteworthy changes for version 0.9.6 (2010-04-21)
-------------------------------------------------
* Update to libassuan 2.0.0.
* Improve German translation.
* Fix default menu item.
Noteworthy changes for version 0.9.5 (2010-01-08)
-------------------------------------------------
* Fixed icon.
Noteworthy changes for version 0.9.4 (2009-12-26)
-------------------------------------------------
* New icon.
Noteworthy changes for version 0.9.3 (2009-03-24)
-------------------------------------------------
* Fixed build problems with newer WINDRES.
Noteworthy changes for version 0.9.2 (2009-01-28)
-------------------------------------------------
* Translation changes.
Noteworthy changes for version 0.9.1 (2008-09-09)
-------------------------------------------------
* Take care of CMS file suffixes.
Noteworthy changes for version 0.9.0 (2008-03-26)
-------------------------------------------------
* Initial release.
diff --git a/README b/README
index 33646bb..786eda3 100644
--- a/README
+++ b/README
@@ -1,67 +1,52 @@
GPG Explorer Extensions
=======================
This package contains GpgEX, the GNU Privacy Guard extensions for the
Windows Explorer shell.
Registry Settings
=================
-The following registry entries are read by GpgEX and affect its
-operation:
+The following registry entries are set by GpgEX at installation time
+to register it as a shell extension:
-HKCU\Software\GNU\GnuPG:UI Server
-HKLM\Software\GNU\GnuPG:UI Server
- The path to the executable binary of the UI server, relative to
- INSTDIR. Absolute paths are currently not supported. It defaults
- to the empty sting, so that the UI server is expected in INSTDIR.
- If the configure option --enable-gpa-only is used these registry
- entries have no effect.
+HKCR\CLSID\CCD955E4-5C16-4A33-AFDA-A8947A94946B
+HKCR\*\ShellEx\ContextMenuHandlers\GpgEX
+HKCR\Directory\ShellEx\ContextMenuHandlers\GpgEX
-HKCU\Software\GNU\GnuPG:Install Directory
-HKLM\Software\GNU\GnuPG:Install Directory
- The directory where GnuPG is installed. This is set by the GnuPG or
- Gpg4win installer and used to locate the gpgconf.exe binary. If
- this is not set that binary is searched starting at the installation
- directory of gpgex.dll in this order:
- .\gpgconf.exe
- ..\bin\gpgconf.exe
- ..\GnuPG\\bin\\gpgconf.exe
- ..\..\GnuPG\bin\gpgconf.exe
+The following registry entries are read by GpgEX and affect its
+operation:
HKLM\Software\GNU\GnuPG:GpgEX Debug File
HKLM\Software\Wow6432Node\GNU\GnuPG:GpgEX Debug File
See below.
-The following registry entries are set by GpgEX at installation time
-to register it as a shell extension:
+HKCU\Software\GNU\GnuPG:UI Server
+HKLM\Software\GNU\GnuPG:UI Server
+HKCU\Software\GNU\GnuPG:Install Directory
+HKLM\Software\GNU\GnuPG:Install Directory
+ These entries are not anymore used.
-HKCR\CLSID\CCD955E4-5C16-4A33-AFDA-A8947A94946B
-HKCR\*\ShellEx\ContextMenuHandlers\GpgEX
-HKCR\Directory\ShellEx\ContextMenuHandlers\GpgEX
Debugging
=========
A debug file path can be specified with the registry value "GpgEX
Debug File" in the HKLM\Software\GNU\GnuPG key. To this file logging
-information is appended. The above key is used for the legacy 32 bit
-version of Windows. For the current 64 bit version the key is
-HKLM\Software\Wow6432Node\GNU\GnuPG - and yes, this is twisted but we
-won't change it anymore.
+information is appended.
To increase the frequency at which extensions are unloaded, create the
following registry key with default value "1".
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\AlwaysUnloadDLL
On NT, you can run the taskbar, the Desktop and each explorer window
in a separate process by creating a DWORD "DesktopProcess" with value
1 under:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer
You can also terminate the explorer process and restart it from a
previously opened console window.
diff --git a/configure.ac b/configure.ac
index 5ba220d..90ff464 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,310 +1,310 @@
# configure.ac - for GpgEX
# Copyright (C) 2005, 2006, 2007, 2013 g10 Code GmbH
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
min_automake_version="1.10"
# To build a release you need to create a tag with the version number
# (git tag -s gnupg-2.n.m) and run "./autogen.sh --force". Please
# bump the version number immediately *after* the release and do
# another commit and push so that the git magic is able to work.
-m4_define([mym4_version], [1.0.12])
+m4_define([mym4_version], [1.1.0])
# Below is m4 magic to extract and compute the git revision number,
# the decimalized short revision number, a beta version string and a
# flag indicating a development version (mym4_isgit). Note that the
# m4 processing is done by autoconf and not during the configure run.
m4_define([mym4_revision],
m4_esyscmd([git rev-parse --short HEAD | tr -d '\n\r']))
m4_define([mym4_revision_dec],
m4_esyscmd_s([echo $((0x$(echo ]mym4_revision[|head -c 4)))]))
m4_define([mym4_betastring],
m4_esyscmd_s([git describe --match 'gpgex-[0-9].[0-9].*[0-9]' --long|\
awk -F- '$3!=0{print"-beta"$3}']))
m4_define([mym4_isgit],m4_if(mym4_betastring,[],[no],[yes]))
m4_define([mym4_full_version],[mym4_version[]mym4_betastring])
AC_INIT([gpgex],[mym4_full_version], [http://bugs.gnupg.org])
-NEED_GPG_ERROR_VERSION=1.12
+NEED_GPG_ERROR_VERSION=1.58
NEED_LIBASSUAN_API=2
NEED_LIBASSUAN_VERSION=2.1.1
PACKAGE=$PACKAGE_NAME
PACKAGE_GT=${PACKAGE_NAME}
VERSION=$PACKAGE_VERSION
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_SRCDIR(src/gpgex.def)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AC_GNU_SOURCE
have_gpg_error=no
have_libassuan=no
AC_SUBST(PACKAGE)
AC_SUBST(PACKAGE_GT)
AC_SUBST(VERSION)
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package])
AC_DEFINE_UNQUOTED(PACKAGE_GT, "$PACKAGE_GT",
[Name of this package for gettext])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package])
AC_DEFINE_UNQUOTED(PACKAGE_BUGREPORT, "$PACKAGE_BUGREPORT",
[Bug report address])
AH_BOTTOM([
/* Some global constants. */
/* Force using of NLS for W32 even if no libintl has been found. This is
okay because we have our own gettext implementation for W32. */
#if defined(HAVE_W32_SYSTEM) && !defined(ENABLE_NLS)
#define ENABLE_NLS 1
#endif
])
AM_MAINTAINER_MODE
AM_SILENT_RULES
# Checks for programs.
AC_PROG_MAKE_SET
AM_SANITY_CHECK
missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AC_PROG_CXX
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_RANLIB
AC_CHECK_TOOL(AR, ar, :)
AC_CHECK_TOOL(DLLTOOL, dlltool, :)
AC_CHECK_TOOL(WINDRES, windres, :)
have_dosish_system=no
have_w32_system=no
have_w64_system=no
case "${host}" in
x86_64-*mingw32*)
have_dosish_system=yes
have_w32_system=yes
have_w64_system=yes
BUILD_CPU_ARCH=amd64
;;
*-mingw32*)
have_dosish_system=yes
have_w32_system=yes
BUILD_CPU_ARCH=x86
;;
*)
AC_MSG_ERROR([[
***
*** This software my only be build for W32 systems. Use
*** ./autogen.sh --build-w32
*** or
*** ./autogen.sh --build-w64
*** to prepare it for such a build.
***]])
;;
esac
# We need the CPU architecture for the manifest file.
AC_SUBST(BUILD_CPU_ARCH)
AC_DEFINE(HAVE_DRIVE_LETTERS,1,
[defined if we must run on a stupid file system])
if test "$have_dosish_system" = yes; then
AC_DEFINE(HAVE_DOSISH_SYSTEM,1,
[Defined if we run on some of the PCDOS like systems
(DOS, Windoze. OS/2) with special properties like
no file modes])
fi
AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes)
if test "$have_w32_system" = yes; then
AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system])
if test "$have_w64_system" = yes; then
AC_DEFINE(HAVE_W64_SYSTEM,1,
[Defined if we run on a 64 bit W32 API based system])
fi
fi
AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes)
AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes)
gpa_only=no
AC_ARG_ENABLE(gpa-only,
AC_HELP_STRING([--enable-gpa-only],
[Try only GPA as UI-server]),
gpa_only=$enableval)
AC_MSG_RESULT($gpa_only)
if test "$gpa_only" = yes ; then
AC_DEFINE(ENABLE_GPA_ONLY, 1,
[Define to use only GPA as UI-server])
fi
#
# Checks for libraries.
#
# Note, that autogen.sh greps for the next line.
AM_GNU_GETTEXT_VERSION([0.12.1])
AM_GNU_GETTEXT([external])
# We always want NLs, despite what the above macro figures out.
USE_NLS=yes
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(string.h unistd.h langinfo.h termio.h locale.h)
AC_CHECK_FUNCS(stpcpy)
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_TYPE_SIZE_T
AC_TYPE_SIGNAL
AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes)
# Add some extra libs here so that previous tests don't fail for
# mysterious reasons - the final link step should bail out.
if test "$have_w32_system" = yes; then
W32LIBS="-lwsock32"
fi
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -mms-bitfields"
CXXFLAGS="$CXXFLAGS -Wall -mms-bitfields"
if test "$USE_MAINTAINER_MODE" = "yes"; then
CFLAGS="$CFLAGS -Wcast-align -Wshadow -Wstrict-prototypes"
CFLAGS="$CFLAGS -Wno-format-y2k -Wformat-security"
CXXFLAGS="$CXXFLAGS -Wcast-align -Wshadow"
CXXFLAGS="$CXXFLAGS -Wno-format-y2k -Wformat-security"
fi
HARDENING="-Wl,--dynamicbase -Wl,--nxcompat -fno-exceptions -D_FORTIFY_SOURCE=2"
CFLAGS="$CFLAGS $HARDENING"
CXXFLAGS="$CXXFLAGS $HARDENING"
fi
AC_SUBST(W32LIBS)
#
# libgpg-error is a library with error codes shared between GnuPG
# related projects.
#
AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION",
have_gpg_error=yes,have_gpg_error=no)
AC_DEFINE(GPG_ERR_SOURCE_DEFAULT, GPG_ERR_SOURCE_USER_1,
[The default error source for GpgEX.])
# We need the declaration for the function GetUserDefaultUILanguage
# and AllowSetForegroundWindow.
AC_DEFINE(WINVER, 0x500, [Version of Windows API])
#
# libassuan is used for IPC
#
AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION",
have_libassuan=yes,have_libassuan=no)
AH_BOTTOM([
/* We don't want the old assuan codes anymore. */
#define _ASSUAN_ONLY_GPG_ERRORS 1
/* We use the gettext code from libgpg-error. */
#define GPG_ERR_ENABLE_GETTEXT_MACROS
])
#
# Provide information about the build.
#
BUILD_REVISION="mym4_revision"
AC_SUBST(BUILD_REVISION)
AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION",
[GIT commit id revision used to build this package])
changequote(,)dnl
BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'`
changequote([,])dnl
BUILD_FILEVERSION="${BUILD_FILEVERSION}mym4_revision_dec"
AC_SUBST(BUILD_FILEVERSION)
BUILD_FILEVERSION_DOT=`echo "$BUILD_FILEVERSION" | tr ',' '.'`
AC_SUBST(BUILD_FILEVERSION_DOT)
BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date`
AC_SUBST(BUILD_TIMESTAMP)
AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP",
[The time this package was configured for a build])
BUILD_HOSTNAME="$ac_hostname"
AC_SUBST(BUILD_HOSTNAME)
#
# Print errors here so that they are visible all
# together and the user can acquire them all together.
#
die=no
if test "$have_gpg_error" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libgpg-error to build this program.
** This library is for example available at
*** ftp://ftp.gnupg.org/gcrypt/libgpg-error
*** (at least version $NEED_GPG_ERROR_VERSION is required.)
***]])
fi
if test "$have_libassuan" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libassuan to build this program.
*** This library is for example available at
*** ftp://ftp.gnupg.org/gcrypt/libassuan/
*** (at least version $NEED_LIBASSUAN_VERSION is required).
***]])
fi
if test "$die" = "yes"; then
AC_MSG_ERROR([[
***
*** Required libraries not found. Please consult the above messages
*** and install them before running configure again.
***]])
fi
AC_CONFIG_FILES([ Makefile
doc/Makefile
src/Makefile
src/versioninfo.rc
src/gpgex.manifest
po/Makefile.in
m4/Makefile
])
AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index 133ceef..416df5a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,75 +1,74 @@
# Makefile.am - main makefile for dialogs part of GpgEX
# Copyright (C) 2005, 2007, 2013 g10 Code GmbH
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = gpgex
EXTRA_DIST = versioninfo.rc.in gpgex.manifest.in \
GNU.GnuPG.Gcc64Support.manifest gnupg.ico \
gpgex_logo.svg standalone.svg
EXEEXT = .dll
AM_CFLAGS = $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) -shared
AM_CXXFLAGS = $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) -shared
ICONS = icon-16.png
nodist_gpgex_SOURCES = versioninfo.rc gpgex.manifest
gpgex_SOURCES = \
gpgex.def \
- registry.h registry.c \
exechelp.h exechelp.c \
gpgex-class.h gpgex-class.cc \
gpgex-factory.h gpgex-factory.cc \
gpgex.h gpgex.cc \
client.h client.cc \
main.h debug.h main.cc \
resource.h \
$(ICONS)
if HAVE_W64_SYSTEM
libgpg-error.a:
if [ -e "$$($(GPG_ERROR_CONFIG) --prefix)/lib64/libgpg-error.a" ]; then \
ln -sf $$($(GPG_ERROR_CONFIG) --prefix)/lib64/libgpg-error.a .; \
else \
ln -sf $$($(GPG_ERROR_CONFIG) --prefix)/lib/libgpg-error.a .; \
fi
else
libgpg-error.a:
ln -sf $$($(GPG_ERROR_CONFIG) --prefix)/lib/libgpg-error.a .
endif
if HAVE_W64_SYSTEM
libassuan.a:
if [ -e "$$($(LIBASSUAN_CONFIG) --prefix)/lib64/libassuan.a" ]; then \
ln -sf $$($(LIBASSUAN_CONFIG) --prefix)/lib64/libassuan.a .; \
else \
ln -sf $$($(LIBASSUAN_CONFIG) --prefix)/lib/libassuan.a .; \
fi
else
libassuan.a:
ln -sf $$($(LIBASSUAN_CONFIG) --prefix)/lib/libassuan.a .
endif
clean-local:
rm -f libgpg-error.a libassuan.a
#gpgex_LDADD = $(srcdir)/gpgex.def \
# -L . -lshell32 -lcomdlg32 -ladvapi32
gpgex_LDFLAGS = -static-libgcc -static-libstdc++
# We need -loleaut32 for start_help() in gpgex.cc.
gpgex_LDADD = $(srcdir)/gpgex.def -L . \
-lshell32 -lgdi32 -lole32 -luuid -lgdiplus \
./libassuan.a ./libgpg-error.a -lws2_32 -loleaut32
.rc.o:
$(WINDRES) -I $(srcdir) -I . `test -f '$<' || echo '$(srcdir)/'`$< $@
diff --git a/src/client.cc b/src/client.cc
index 93e732b..00006dc 100644
--- a/src/client.cc
+++ b/src/client.cc
@@ -1,655 +1,560 @@
/* client.cc - gpgex assuan client implementation
Copyright (C) 2007, 2008, 2013, 2014 g10 Code GmbH
This file is part of GpgEX.
GpgEX 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 of the License, or (at your option) any later version.
GpgEX 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <vector>
#include <string>
#include <stdexcept>
using std::vector;
using std::string;
#include <winsock2.h>
#include <windows.h>
#include <assuan.h>
#include "main.h"
-#include "registry.h"
#include "exechelp.h"
#include "client.h"
static inline char *
_gpgex_stpcpy (char *a, const char *b)
{
while (*b)
*a++ = *b++;
*a = 0;
return a;
}
#define stpcpy(a,b) _gpgex_stpcpy ((a), (b))
+
/* Find the gpgconf binary which is used to return installation
- * properties of the GnuPG system. We can't simply snatch some code
- * from GnuPG because that would also require that we link to
- * Libgcrypt or implement our own SHA-1 function to build the
- * directory name for a non-default GNUPGHOME. It is just better to
- * that info from gpgconf and try hard to find the correct
- * gpgconf. */
+ * properties of the GnuPG system. We avoid linking to gpgme to avoid
+ * its overhead. Instead we call gpgconf direcly. */
static const char *
get_gpgconf_name (void)
{
static int tried;
static char *name;
if (!tried)
{
- const char *dir, **tmp;
- char *instdir, *p;
+ const char **tmp;
const char *possible_names[] =
{
- "gpgconf.exe",
- "..\\bin\\gpgconf.exe",
- "..\\GnuPG\\bin\\gpgconf.exe",
- "..\\..\\GnuPG\\bin\\gpgconf.exe",
+ "../GnuPG/bin/gpgconf.exe", /* GnuPG-[VS-]Desktop */
+ "../../GnuPG/bin/gpgconf.exe", /* Gpg4win. */
+ "../bin/gpgconf.exe", /* Legacy */
NULL
};
tried = 1;
- instdir = read_w32_registry_string (NULL, GPG4WIN_REGKEY_1,
- "Install Directory");
- if (instdir)
- {
- name = (char*)malloc (strlen (instdir) + 16 + 1);
- if (!name)
- return NULL;
- strcpy (stpcpy (name, instdir), "/bin/gpgconf.exe");
- for (p = name; *p; p++)
- if (*p == '/')
- *p = '\\';
- free (instdir);
- }
- if (name && !gpgrt_access (name, F_OK))
- return name; /* Yeah, Installed. */
-
- dir = gpgex_server::root_dir;
- if (!dir)
- return NULL; /* No way. */
-
- /* Try fallbacks */
for (tmp = possible_names; *tmp; tmp++)
{
- if (name)
- free (name);
- name = (char*)malloc (strlen (dir) + 1 + strlen (*tmp) + 1);
+ name = gpgrt_fconcat (0, get_gpg4win_dir (), *tmp, NULL);
if (!name)
return NULL; /* Ooops. */
-
- strcpy (stpcpy (stpcpy (name, dir), "\\"), *tmp);
- for (p = name; *p; p++)
- if (*p == '/')
- *p = '\\';
if (!gpgrt_access (name, F_OK))
- return name; /* Found. */
+ return name; /* Found. */
+ free (name);
+ name = NULL;
}
}
return name;
}
static const char *
default_socket_name (void)
{
static int tried;
static char *name;
if (!tried)
{
const char *gpgconf;
char *dir;
const char sockname[] = "\\S.uiserver";
tried = 1;
gpgconf = get_gpgconf_name ();
if (!gpgconf)
return NULL;
if (gpgex_spawn_get_string (gpgconf, "gpgconf -0 --list-dirs socketdir",
&dir))
return NULL;
+ _gpgex_debug (DEBUG_INIT, " got dir '%s'", dir);
name = (char *)malloc (strlen (dir) + strlen (sockname) + 1);
if (name)
strcpy (stpcpy (name, dir), sockname);
+ _gpgex_debug (DEBUG_INIT, " using socket name '%s'", name? name: "(null)");
free (dir);
}
return name;
}
/* Return the name of the default UI server. This name is used to
auto start an UI server if an initial connect failed. */
static const char *
default_uiserver_name (void)
{
static char *name;
if (!name)
-#if ENABLE_GPA_ONLY
{
- const char gpaserver[] = "bin\\launch-gpa.exe";
- const char *dir;
+ const char **tmp;
char *p;
-
- dir = gpgex_server::root_dir;
- if (!dir)
- return NULL;
-
- name = (char*)malloc (strlen (dir) + strlen (gpaserver) + 9 + 2);
- if (!name)
- return NULL;
- strcpy (stpcpy (stpcpy (name, dir), "\\"), gpaserver);
- for (p = name; *p; p++)
- if (*p == '/')
- *p = '\\';
- strcat (name, " --daemon");
- gpgex_server::ui_server = "GPA";
- }
-#else /*!ENABLE_GPA_ONLY*/
- {
- const char *dir, **tmp;
- char *uiserver, *p;
- int extra_arglen = 9;
- const char * server_names[] = {"kleopatra.exe",
- "bin\\kleopatra.exe",
+ const char *server_names[] = {
+#ifndef ENABLE_GPA_ONLY
+ "bin/kleopatra.exe",
+#endif
+ "bin/launch-gpa.exe",
+ "bin/gpa.exe",
+#ifndef ENABLE_GPA_ONLY
+ "kleopatra.exe",
+#endif
"launch-gpa.exe",
- "bin\\launch-gpa.exe",
"gpa.exe",
- "bin\\gpa.exe",
NULL};
- dir = gpgex_server::root_dir;
- if (!dir)
- return NULL;
-
- uiserver = read_w32_registry_string (NULL, GPG4WIN_REGKEY_2,
- "UI Server");
- if (!uiserver)
- {
- uiserver = read_w32_registry_string (NULL, GPG4WIN_REGKEY_3,
- "UI Server");
- }
- if (!uiserver)
- {
- uiserver = strdup ("kleopatra.exe");
- if (!uiserver)
- return NULL;
- }
- if (uiserver)
- {
- name = (char*) malloc (strlen (dir) + strlen (uiserver) +
- extra_arglen + 2);
- if (!name)
- return NULL;
- strcpy (stpcpy (stpcpy (name, dir), "\\"), uiserver);
- for (p = name; *p; p++)
- if (*p == '/')
- *p = '\\';
- free (uiserver);
- }
- if (name && !gpgrt_access (name, F_OK))
- {
- /* Set through registry or default kleo */
- if (strstr (name, "kleopatra.exe"))
- {
- gpgex_server::ui_server = "Kleopatra";
- }
- else
- {
- gpgex_server::ui_server = "GPA";
- }
- return name;
- }
- /* Fallbacks */
for (tmp = server_names; *tmp; tmp++)
{
- if (name)
- {
- free (name);
- }
- name = (char*) malloc (strlen (dir) + strlen (*tmp) + extra_arglen + 2);
+ free (name);
+ name = gpgrt_fconcat (0, get_gpg4win_dir (), *tmp, NULL);
if (!name)
return NULL;
- strcpy (stpcpy (stpcpy (name, dir), "\\"), *tmp);
- for (p = name; *p; p++)
- if (*p == '/')
- *p = '\\';
if (!gpgrt_access (name, F_OK))
{
/* Found a viable candidate */
/* Set through registry and is accessible */
if (strstr (name, "kleopatra.exe"))
{
gpgex_server::ui_server = "Kleopatra";
}
else
{
gpgex_server::ui_server = "GPA";
}
+ for (p = name; *p; p++)
+ if (*p == '/')
+ *p = '\\';
return name;
}
}
gpgex_server::ui_server = NULL;
}
-#endif /*!ENABLE_GPA_ONLY*/
return name;
}
#define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a'))
/* Percent-escape the string STR by replacing colons with '%3a'. If
EXTRA is not NULL all characters in it are also escaped. */
static char *
percent_escape (const char *str, const char *extra)
{
int i, j;
char *ptr;
if (!str)
return NULL;
for (i=j=0; str[i]; i++)
if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i])))
j++;
ptr = (char *) malloc (i + 2 * j + 1);
i = 0;
while (*str)
{
/* FIXME: Work around a bug in Kleo. */
if (*str == ':')
{
ptr[i++] = '%';
ptr[i++] = '3';
ptr[i++] = 'a';
}
else
if (*str == '%')
{
ptr[i++] = '%';
ptr[i++] = '2';
ptr[i++] = '5';
}
else if (extra && strchr (extra, *str))
{
ptr[i++] = '%';
ptr[i++] = tohex_lower ((*str >> 4) & 15);
ptr[i++] = tohex_lower (*str & 15);
}
else
ptr[i++] = *str;
str++;
}
ptr[i] = '\0';
return ptr;
}
static string
escape (string str)
{
char *arg_esc = percent_escape (str.c_str (), "+= ");
if (arg_esc == NULL)
return std::string();
string res = arg_esc;
free (arg_esc);
return res;
}
/* Send options to the UI server and return the server's PID. */
static gpg_error_t
send_one_option (assuan_context_t ctx, const char *name, const char *value)
{
gpg_error_t err;
char buffer[1024];
if (! value || ! *value)
err = 0; /* Avoid sending empty strings. */
else
{
snprintf (buffer, sizeof (buffer), "OPTION %s=%s", name, value);
err = assuan_transact (ctx, buffer, NULL, NULL, NULL, NULL, NULL, NULL);
}
return err;
}
static gpg_error_t
getinfo_pid_cb (void *opaque, const void *buffer, size_t length)
{
pid_t *pid = (pid_t *) opaque;
*pid = (pid_t) strtoul ((char *) buffer, NULL, 10);
return 0;
}
static gpg_error_t
send_options (assuan_context_t ctx, HWND hwnd, pid_t *r_pid)
{
gpg_error_t rc = 0;
char numbuf[50];
TRACE_BEG (DEBUG_ASSUAN, "client_t::send_options", ctx);
*r_pid = (pid_t) (-1);
rc = assuan_transact (ctx, "GETINFO pid", getinfo_pid_cb, r_pid,
NULL, NULL, NULL, NULL);
if (! rc && *r_pid == (pid_t) (-1))
{
(void) TRACE_LOG ("server did not return a PID");
rc = gpg_error (GPG_ERR_ASSUAN_SERVER_FAULT);
}
if (! rc && *r_pid != (pid_t) (-1)
&& ! AllowSetForegroundWindow (*r_pid))
{
(void) TRACE_LOG ("AllowSetForegroundWindow (%u) failed");
TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
/* Ignore the error, though. */
}
if (! rc && hwnd)
{
/* We hope that HWND is limited to 32 bit. If not a 32 bit
UI-server would not be able to do anything with this
window-id. */
uintptr_t tmp = (uintptr_t)hwnd;
if (!(tmp & ~0xffffffff))
{
/* HWND fits into 32 bit - send it. */
snprintf (numbuf, sizeof (numbuf), "%lx", (unsigned long)tmp);
rc = send_one_option (ctx, "window-id", numbuf);
}
}
return TRACE_GPGERR (rc);
}
static gpg_error_t
uiserver_connect (assuan_context_t *ctx, HWND hwnd)
{
gpg_error_t rc;
const char *socket_name = NULL;
pid_t pid;
lock_spawn_t lock;
TRACE_BEG (DEBUG_ASSUAN, "client_t::uiserver_connect", ctx);
socket_name = default_socket_name ();
if (! socket_name || ! *socket_name)
{
(void) TRACE_LOG ("invalid socket name");
return TRACE_GPGERR (gpg_error (GPG_ERR_INV_ARG));
}
(void) TRACE_LOG1 ("socket name: %s", socket_name);
rc = assuan_new (ctx);
if (rc)
{
(void) TRACE_LOG ("could not allocate context");
return TRACE_GPGERR (rc);
}
rc = assuan_socket_connect (*ctx, socket_name, -1, 0);
if (rc)
{
int count;
(void) TRACE_LOG ("UI server not running, starting it");
const char *cmdline = NULL;
const char *program = default_uiserver_name ();
if (gpgex_server::ui_server &&
!strcmp (gpgex_server::ui_server, "Kleopatra"))
{
cmdline = "--daemon";
}
/* Now try to connect again with the spawn lock taken. */
if (!(rc = gpgex_lock_spawning (&lock))
&& assuan_socket_connect (*ctx, socket_name, -1, 0))
{
rc = gpgex_spawn_detached (program, cmdline);
if (!rc)
{
/* Give it a bit of time to start up and try a couple of
times. */
for (count = 0; count < 10; count++)
{
Sleep (1000);
rc = assuan_socket_connect (*ctx, socket_name, -1, 0);
if (!rc)
break;
}
}
}
gpgex_unlock_spawning (&lock);
}
if (! rc)
{
if (debug_flags & DEBUG_ASSUAN)
assuan_set_log_stream (*ctx, debug_file);
rc = send_options (*ctx, hwnd, &pid);
if (rc)
{
assuan_release (*ctx);
*ctx = NULL;
}
}
return TRACE_GPGERR (rc);
}
typedef struct async_arg
{
const char *cmd;
vector<string> filenames;
HWND wid;
} async_arg_t;
static DWORD WINAPI
call_assuan_async (LPVOID arg)
{
async_arg_t *async_args = (async_arg_t *)arg;
int rc = 0;
int connect_failed = 0;
const char *cmd = async_args->cmd;
const vector<string> filenames = async_args->filenames;
assuan_context_t ctx = NULL;
string msg;
TRACE_BEG2 (DEBUG_ASSUAN, "client_t::call_assuan_async", 0,
"%s on %u files", cmd, filenames.size ());
rc = uiserver_connect (&ctx, async_args->wid);
if (rc)
{
connect_failed = 1;
goto leave;
}
/* Set the input files. We don't specify the output files. */
for (unsigned int i = 0; i < filenames.size (); i++)
{
msg = "FILE " + escape (filenames[i]);
(void) TRACE_LOG1 ("sending cmd: %s", msg.c_str ());
rc = assuan_transact (ctx, msg.c_str (),
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
goto leave;
}
/* Set the --nohup option, so that the operation continues and
completes in the background. */
msg = ((string) cmd) + " --nohup";
(void) TRACE_LOG1 ("sending cmd: %s", msg.c_str ());
rc = assuan_transact (ctx, msg.c_str (),
NULL, NULL, NULL, NULL, NULL, NULL);
/* Fall-through. */
leave:
TRACE_GPGERR (rc);
if (ctx)
assuan_release (ctx);
if (rc)
{
char buf[256];
if (connect_failed)
snprintf (buf, sizeof (buf),
_("Can not connect to the GnuPG user interface%s%s%s:\r\n%s"),
gpgex_server::ui_server? " (":"",
gpgex_server::ui_server? gpgex_server::ui_server:"",
gpgex_server::ui_server? ")":"",
gpg_strerror (rc));
else
snprintf (buf, sizeof (buf),
_("Error returned by the GnuPG user interface%s%s%s:\r\n%s"),
gpgex_server::ui_server? " (":"",
gpgex_server::ui_server? gpgex_server::ui_server:"",
gpgex_server::ui_server? ")":"",
gpg_strerror (rc));
MessageBox (async_args->wid, buf, "GpgEX", MB_ICONINFORMATION);
}
delete async_args;
return 0;
}
void
client_t::call_assuan (const char *cmd, vector<string> &filenames)
{
TRACE_BEG (DEBUG_ASSUAN, "client_t::call_assuan", cmd);
async_arg_t * args = new async_arg_t;
args->cmd = cmd;
args->filenames = filenames;
args->wid = this->window;
/* We move the call in a different thread as the Windows explorer
is blocked until our call finishes. We don't want that.
Additionally Kleopatra / Qt5 SendsMessages to the parent
window provided in wid. Qt does this with blocking calls
so Kleopatra blocks until the explorer processes more
Window Messages and we block the explorer. This is
a deadlock. */
CreateThread (NULL, 0, call_assuan_async, (LPVOID) args, 0,
NULL);
return;
}
void
client_t::decrypt_verify (vector<string> &filenames)
{
this->call_assuan ("DECRYPT_VERIFY_FILES", filenames);
}
void
client_t::verify (vector<string> &filenames)
{
this->call_assuan ("VERIFY_FILES", filenames);
}
void
client_t::decrypt (vector<string> &filenames)
{
this->call_assuan ("DECRYPT_FILES", filenames);
}
void
client_t::sign_encrypt (vector<string> &filenames)
{
this->call_assuan ("ENCRYPT_SIGN_FILES", filenames);
}
void
client_t::encrypt (vector<string> &filenames)
{
this->call_assuan ("ENCRYPT_FILES", filenames);
}
void
client_t::sign (vector<string> &filenames)
{
this->call_assuan ("SIGN_FILES", filenames);
}
void
client_t::import (vector<string> &filenames)
{
this->call_assuan ("IMPORT_FILES", filenames);
}
void
client_t::create_checksums (vector<string> &filenames)
{
this->call_assuan ("CHECKSUM_CREATE_FILES", filenames);
}
void
client_t::verify_checksums (vector<string> &filenames)
{
this->call_assuan ("CHECKSUM_VERIFY_FILES", filenames);
}
diff --git a/src/exechelp.c b/src/exechelp.c
index 6050a58..7be2577 100644
--- a/src/exechelp.c
+++ b/src/exechelp.c
@@ -1,361 +1,361 @@
/* exechelp.c - fork and exec helpers
* Copyright (C) 2004, 2007, 2014 g10 Code GmbH
*
* This file is part of GpgEX.
*
* GpgEX is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GpgEX 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 General Public License for more details.
*
* You should have received a copy of the GNU 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
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <windows.h>
#include <gpg-error.h>
#include "debug.h"
#include "exechelp.h"
/* Define to 1 do enable debugging. */
#define DEBUG_W32_SPAWN 0
struct private_membuf_s
{
size_t len;
size_t size;
char *buf;
int out_of_core;
};
typedef struct private_membuf_s membuf_t;
/* A simple implementation of a dynamic buffer. Use init_membuf() to
create a buffer, put_membuf to append bytes and get_membuf to
release and return the buffer. Allocation errors are detected but
only returned at the final get_membuf(), this helps not to clutter
the code with out of core checks. */
void
init_membuf (membuf_t *mb, int initiallen)
{
mb->len = 0;
mb->size = initiallen;
mb->out_of_core = 0;
mb->buf = malloc (initiallen);
if (!mb->buf)
mb->out_of_core = errno;
}
/* Shift the content of the membuf MB by AMOUNT bytes. The next
operation will then behave as if AMOUNT bytes had not been put into
the buffer. If AMOUNT is greater than the actual accumulated
bytes, the membuf is basically reset to its initial state. */
void
clear_membuf (membuf_t *mb, size_t amount)
{
/* No need to clear if we are already out of core. */
if (mb->out_of_core)
return;
if (amount >= mb->len)
mb->len = 0;
else
{
mb->len -= amount;
memmove (mb->buf, mb->buf+amount, mb->len);
}
}
void
put_membuf (membuf_t *mb, const void *buf, size_t len)
{
if (mb->out_of_core || !len)
return;
if (mb->len + len >= mb->size)
{
char *p;
mb->size += len + 1024;
p = realloc (mb->buf, mb->size);
if (!p)
{
mb->out_of_core = errno ? errno : ENOMEM;
/* /\* Wipe out what we already accumulated. This is required */
/* in case we are storing sensitive data here. The membuf */
/* API does not provide another way to cleanup after an */
/* error. *\/ */
/* wipememory (mb->buf, mb->len); */
return;
}
mb->buf = p;
}
if (buf)
memcpy (mb->buf + mb->len, buf, len);
else
memset (mb->buf + mb->len, 0, len);
mb->len += len;
}
void *
get_membuf (membuf_t *mb, size_t *len)
{
char *p;
if (mb->out_of_core)
{
if (mb->buf)
{
/* wipememory (mb->buf, mb->len); */
free (mb->buf);
mb->buf = NULL;
}
gpg_err_set_errno (mb->out_of_core);
return NULL;
}
p = mb->buf;
if (len)
*len = mb->len;
mb->buf = NULL;
mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
return p;
}
/* Lock a spawning process. The caller needs to provide the address
of a variable to store the lock information and the name or the
process. */
gpg_error_t
gpgex_lock_spawning (lock_spawn_t *lock)
{
int waitrc;
int timeout = 5;
_TRACE (DEBUG_ASSUAN, "gpgex_lock_spawning", lock);
*lock = CreateMutexW (NULL, FALSE, L"spawn_gnupg_uiserver_sentinel");
if (!*lock)
{
TRACE_LOG1 ("failed to create the spawn mutex: rc=%d", GetLastError ());
return gpg_error (GPG_ERR_GENERAL);
}
retry:
waitrc = WaitForSingleObject (*lock, 1000);
if (waitrc == WAIT_OBJECT_0)
return 0;
if (waitrc == WAIT_TIMEOUT && timeout)
{
timeout--;
goto retry;
}
if (waitrc == WAIT_TIMEOUT)
TRACE_LOG ("error waiting for the spawn mutex: timeout");
else
TRACE_LOG2 ("error waiting for the spawn mutex: (code=%d) rc=%d",
waitrc, GetLastError ());
return gpg_error (GPG_ERR_GENERAL);
}
/* Unlock the spawning process. */
void
gpgex_unlock_spawning (lock_spawn_t *lock)
{
if (*lock)
{
_TRACE (DEBUG_ASSUAN, "gpgex_unlock_spawning", lock);
if (!ReleaseMutex (*lock))
TRACE_LOG1 ("failed to release the spawn mutex: rc=%d", GetLastError());
CloseHandle (*lock);
*lock = NULL;
}
}
/* Fork and exec the program with /dev/null as stdin, stdout and
stderr. Returns 0 on success or an error code. */
gpg_error_t
gpgex_spawn_detached (const char *pgmname, const char *cmdline)
{
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
STARTUPINFO si;
int cr_flags;
- TRACE_BEG1 (DEBUG_ASSUAN, "gpgex_spawn_detached", cmdline,
- "cmdline=%s", cmdline);
+ TRACE_BEG2 (DEBUG_ASSUAN, "gpgex_spawn_detached", cmdline,
+ "pgm=%s cmdline=%s", pgmname, cmdline);
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Start the process. Note that we can't run the PREEXEC function
because this would change our own environment. */
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = DEBUG_W32_SPAWN ? SW_SHOW : SW_MINIMIZE;
cr_flags = (CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ())
| CREATE_NEW_PROCESS_GROUP
| DETACHED_PROCESS);
if (!CreateProcess (pgmname, /* pgmname; Program to start. */
(char *) cmdline, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
TRUE, /* Inherit handles. */
cr_flags, /* Creation flags. */
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
))
{
(void) TRACE_LOG1 ("CreateProcess failed: %i\n", GetLastError ());
return gpg_error (GPG_ERR_GENERAL);
}
/* Process has been created suspended; resume it now. */
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
return 0;
}
/* Fork and exec PGMNAME with args in CMDLINE and /dev/null connected
* to stdin and stderr. Read from stdout and return the result as a
* malloced string at R_STRING. Returns 0 on success or an error code. */
gpg_error_t
gpgex_spawn_get_string (const char *pgmname, const char *cmdline,
char **r_string)
{
SECURITY_ATTRIBUTES sec_attr;
HANDLE rh, wh;
PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
STARTUPINFO si;
membuf_t mb;
*r_string = NULL;
- TRACE_BEG1 (DEBUG_ASSUAN, "gpgex_spawn_get_string", cmdline,
- "cmdline=%s", cmdline);
+ TRACE_BEG2 (DEBUG_ASSUAN, "gpgex_spawn_get_string", cmdline,
+ "pgm=%s cmdline=%s", pgmname, cmdline);
/* Set the inherit flag for the pipe into the securit attributes. */
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = TRUE;
/* Create a pipe to read form stdout. */
if (!CreatePipe (&rh, &wh, &sec_attr, 0))
{
TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
return gpg_error (GPG_ERR_GENERAL);
}
/* Set the read end to non-inheritable. */
if (!SetHandleInformation (rh, HANDLE_FLAG_INHERIT, 0))
{
TRACE_LOG1 ("SHI failed: ec=%d", (int) GetLastError ());
CloseHandle (rh);
CloseHandle (wh);
return gpg_error (GPG_ERR_GENERAL);
}
/* Start the process. Note that we can't run the PREEXEC function
because this would change our own environment. */
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = INVALID_HANDLE_VALUE;
si.hStdOutput = wh;
si.hStdError = INVALID_HANDLE_VALUE;
if (!CreateProcess (pgmname, /* pgmname; Program to start. */
(char *)cmdline, /* Command line arguments. */
NULL, /* Process security attributes. */
NULL, /* Thread security attributes. */
TRUE, /* Inherit handles. */
0, /* Creation flags. */
NULL, /* Use current environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
))
{
(void) TRACE_LOG1 ("CreateProcess failed: %i\n", GetLastError ());
CloseHandle (rh);
CloseHandle (wh);
return gpg_error (GPG_ERR_GENERAL);
}
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
CloseHandle (wh); /* This end is used by the child. */
init_membuf (&mb, 1024);
for (;;)
{
char readbuf[1024];
DWORD nread;
if (!ReadFile (rh, readbuf, sizeof readbuf, &nread, NULL) || !nread)
break;
put_membuf (&mb, readbuf, nread);
}
CloseHandle (rh); /* Ready with reading. */
put_membuf (&mb, "", 1); /* Terminate string. */
*r_string = get_membuf (&mb, NULL);
if (!*r_string)
return gpg_error (GPG_ERR_EIO);
return 0;
}
diff --git a/src/gpgex.cc b/src/gpgex.cc
index da8e7f7..bde3314 100644
--- a/src/gpgex.cc
+++ b/src/gpgex.cc
@@ -1,773 +1,770 @@
/* gpgex.cc - gpgex implementation
Copyright (C) 2007, 2013 g10 Code GmbH
This file is part of GpgEX.
GpgEX 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 of the License, or (at your option) any later version.
GpgEX 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <vector>
#include <string>
#include <stdexcept>
#include <map>
using std::vector;
using std::string;
#include <windows.h>
#include <gdiplus.h>
#include <olectl.h>
#include <objidl.h>
#include "main.h"
#include "client.h"
-#include "registry.h"
#include "gpgex.h"
#include "resource.h"
/* For context menus. */
#define ID_CMD_DECRYPT_VERIFY 1
#define ID_CMD_DECRYPT 2
#define ID_CMD_VERIFY 3
#define ID_CMD_SIGN_ENCRYPT 4
#define ID_CMD_ENCRYPT 5
#define ID_CMD_SIGN 6
#define ID_CMD_IMPORT 7
#define ID_CMD_CREATE_CHECKSUMS 8
#define ID_CMD_VERIFY_CHECKSUMS 9
#define ID_CMD_POPUP 10
#define ID_CMD_ABOUT 11
#define ID_CMD_MAX 11
#define ID_CMD_STR_ABOUT _("About GpgEX")
#define ID_CMD_STR_DECRYPT_VERIFY _("Decrypt and verify")
#define ID_CMD_STR_DECRYPT _("Decrypt")
#define ID_CMD_STR_VERIFY _("Verify")
#define ID_CMD_STR_SIGN_ENCRYPT _("Sign and encrypt")
#define ID_CMD_STR_ENCRYPT _("Encrypt")
#define ID_CMD_STR_SIGN _("Sign")
#define ID_CMD_STR_IMPORT _("Import keys")
#define ID_CMD_STR_CREATE_CHECKSUMS _("Create checksums")
#define ID_CMD_STR_VERIFY_CHECKSUMS _("Verify checksums")
/* Returns the string for a command id */
static const char *
getCaptionForId (int id)
{
/* Yeah,.. maps were invented but this works, too */
switch (id)
{
case ID_CMD_DECRYPT_VERIFY:
return ID_CMD_STR_DECRYPT_VERIFY;
case ID_CMD_DECRYPT:
return ID_CMD_STR_DECRYPT;
case ID_CMD_VERIFY:
return ID_CMD_STR_VERIFY;
case ID_CMD_SIGN_ENCRYPT:
return ID_CMD_STR_SIGN_ENCRYPT;
case ID_CMD_ENCRYPT:
return ID_CMD_STR_ENCRYPT;
case ID_CMD_SIGN:
return ID_CMD_STR_SIGN;
case ID_CMD_IMPORT:
return ID_CMD_STR_IMPORT;
case ID_CMD_CREATE_CHECKSUMS:
return ID_CMD_STR_CREATE_CHECKSUMS;
case ID_CMD_VERIFY_CHECKSUMS:
return ID_CMD_STR_VERIFY_CHECKSUMS;
case ID_CMD_ABOUT:
return ID_CMD_STR_ABOUT;
default:
return "Unknown command";
}
}
/* Reset the instance between operations. */
void
gpgex_t::reset (void)
{
this->filenames.clear ();
this->all_files_gpg = TRUE;
}
STDMETHODIMP
gpgex_t::QueryInterface (REFIID riid, void **ppv)
{
#define _TRACE_BEG12(a,b,c,d,e,f) TRACE_BEG12(a,b,c,d,e,f)
_TRACE_BEG12 (DEBUG_INIT, "gpgex_t::QueryInterface", this,
"riid=" GUID_FMT ", ppv=%p", GUID_ARG (riid), ppv);
if (ppv == NULL)
return TRACE_RES (E_INVALIDARG);
/* Be nice to broken software. */
*ppv = NULL;
/* The static casts ensure that the virtual function table layout of
the returned object is correct. We can not cast to IUnknown
because that base class is ambiguous (because it is not virtual),
so we pick one of the derived classes instead. */
if (riid == IID_IUnknown)
*ppv = static_cast<IShellExtInit *> (this);
else if (riid == IID_IShellExtInit)
*ppv = static_cast<IShellExtInit *> (this);
else if (riid == IID_IContextMenu)
*ppv = static_cast<IContextMenu3 *> (this);
#if 0
/* FIXME: Enable this when the functions are actually
implemented. */
else if (riid == IID_IContextMenu2)
*ppv = static_cast<IContextMenu3 *> (this);
else if (riid == IID_IContextMenu3)
*ppv = static_cast<IContextMenu3 *> (this);
#endif
else
return TRACE_RES (E_NOINTERFACE);
/* We have to acquire a reference to the returned object. We lost
the type information, but we know that all object classes inherit
from IUnknown, which is good enough. */
reinterpret_cast<IUnknown *>(*ppv)->AddRef ();
return TRACE_RES (S_OK);
}
STDMETHODIMP_(ULONG)
gpgex_t::AddRef (void)
{
(void) TRACE1 (DEBUG_INIT, "gpgex_t::AddRef", this,
"new_refcount=%i", this->refcount + 1);
return InterlockedIncrement (&this->refcount);
}
STDMETHODIMP_(ULONG)
gpgex_t::Release (void)
{
LONG count;
(void) TRACE1 (DEBUG_INIT, "gpgex_t::Release", this,
"new_refcount=%i", this->refcount - 1);
count = InterlockedDecrement (&this->refcount);
if (count == 0)
delete this;
return count;
}
/* IShellExtInit methods. */
STDMETHODIMP
gpgex_t::Initialize (LPCITEMIDLIST pIDFolder, IDataObject *pDataObj,
HKEY hRegKey)
{
HRESULT err = S_OK;
TRACE_BEG3 (DEBUG_INIT, "gpgex_t::Initialize", this,
"pIDFolder=%p, pDataObj=%p, hRegKey=%p",
pIDFolder, pDataObj, hRegKey);
/* This function is called for the Shortcut (context menu),
Drag-and-Drop, and Property Sheet extensions. */
this->reset ();
if (pDataObj)
{
/* The data object contains a drop item which we extract. */
FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
UINT count;
if (SUCCEEDED (pDataObj->GetData (&fe, &medium)))
{
HDROP drop = (HDROP) GlobalLock (medium.hGlobal);
unsigned int i;
/* Now that we have the drop item, we can extract the
file names. */
count = DragQueryFile (drop, (UINT) -1, NULL, 0);
if (count == 0)
{
err = E_INVALIDARG;
}
if (!err)
{
for (i = 0; i < count; i++)
{
char filename[MAX_PATH];
UINT len;
len = DragQueryFile (drop, i,
filename, sizeof (filename) - 1);
if (len == 0)
{
err = E_INVALIDARG;
break;
}
/* Take a look at the ending. */
char *ending = strrchr (filename, '.');
if (ending)
{
BOOL gpg = false;
ending++;
if (! strcasecmp (ending, "gpg")
|| ! strcasecmp (ending, "pgp")
|| ! strcasecmp (ending, "asc")
|| ! strcasecmp (ending, "sig")
|| ! strcasecmp (ending, "pem")
|| ! strcasecmp (ending, "p7m")
|| ! strcasecmp (ending, "p7s")
)
gpg = true;
if (gpg == false)
this->all_files_gpg = FALSE;
}
else
this->all_files_gpg = FALSE;
this->filenames.push_back (filename);
}
GlobalUnlock (medium.hGlobal);
ReleaseStgMedium (&medium);
}
}
}
if (err != S_OK)
this->reset ();
return TRACE_RES (err);
}
static HBITMAP
getBitmap (int id)
{
TRACE_BEG0 (DEBUG_CONTEXT_MENU, __func__, nullptr, "get bitmap");
PICTDESC pdesc;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::Bitmap* pbitmap;
ULONG_PTR gdiplusToken;
HRSRC hResource;
DWORD imageSize;
const void* pResourceData;
HGLOBAL hBuffer;
memset (&pdesc, 0, sizeof pdesc);
pdesc.cbSizeofstruct = sizeof pdesc;
pdesc.picType = PICTYPE_BITMAP;
/* Initialize GDI */
gdiplusStartupInput.DebugEventCallback = NULL;
gdiplusStartupInput.SuppressBackgroundThread = FALSE;
gdiplusStartupInput.SuppressExternalCodecs = FALSE;
gdiplusStartupInput.GdiplusVersion = 1;
GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL);
/* Get the image from the resource file */
hResource = FindResource (gpgex_server::instance, MAKEINTRESOURCE(id), RT_RCDATA);
if (!hResource)
{
TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "Failed to find id: %i",
id);
return nullptr;
}
imageSize = SizeofResource (gpgex_server::instance, hResource);
if (!imageSize)
{
TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "WTF: %i",
__LINE__);
return nullptr;
}
pResourceData = LockResource (LoadResource (gpgex_server::instance, hResource));
if (!pResourceData)
{
TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "WTF: %i",
__LINE__);
return nullptr;
}
hBuffer = GlobalAlloc (GMEM_MOVEABLE, imageSize);
if (hBuffer)
{
void* pBuffer = GlobalLock (hBuffer);
if (pBuffer)
{
IStream* pStream = NULL;
CopyMemory (pBuffer, pResourceData, imageSize);
if (CreateStreamOnHGlobal (hBuffer, FALSE, &pStream) == S_OK)
{
pbitmap = Gdiplus::Bitmap::FromStream (pStream);
pStream->Release();
if (!pbitmap || pbitmap->GetHBITMAP (0, &pdesc.bmp.hbitmap))
{
TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "WTF: %i",
__LINE__);
return nullptr;
}
}
}
GlobalUnlock (pBuffer);
}
GlobalFree (hBuffer);
Gdiplus::GdiplusShutdown (gdiplusToken);
return pdesc.bmp.hbitmap;
}
static HBITMAP
getBitmapCached (int id)
{
static std::map<int, HBITMAP> s_id_map;
const auto it = s_id_map.find (id);
if (it == s_id_map.end ())
{
const HBITMAP icon = getBitmap (id);
s_id_map.insert (std::make_pair (id, icon));
return icon;
}
return it->second;
}
static bool
setupContextMenuIcon (int id, HMENU hMenu, UINT indexMenu)
{
TRACE_BEG2 (DEBUG_CONTEXT_MENU, __func__, nullptr, "Start. menu: %p index %u",
hMenu, indexMenu);
int width = GetSystemMetrics (SM_CXMENUCHECK);
int height = GetSystemMetrics (SM_CYMENUCHECK);
TRACE2 (DEBUG_CONTEXT_MENU, __func__, nullptr, "width %i height %i",
width, height);
HBITMAP bmp = getBitmapCached (id);
if (!bmp)
{
TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "WTF: %i",
__LINE__);
return false;
}
return SetMenuItemBitmaps (hMenu, indexMenu - 1, MF_BYPOSITION,
bmp, bmp);
}
static int
readDefaultEntry ()
{
TRACE_BEG0 (DEBUG_CONTEXT_MENU, __func__, nullptr, "read default entry");
- char *entry = read_w32_registry_string (NULL,
- GPG4WIN_REGKEY_2,
- "GpgExDefault");
+ char *entry = gpgrt_w32_reg_get_string ("\\Software\\Gpg4win:GpgExDefault");
if (!entry)
{
return -1;
}
long int val = strtol (entry, nullptr, 10);
free (entry);
if (val > ID_CMD_MAX || val < 0)
{
TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "invalid cmd value: %li",
val);
return -1;
}
return static_cast<int> (val);
}
/* IContextMenu methods. */
/* The argument HMENU contains the context menu, and INDEXMENU points
to the first index where we can add items. IDCMDFIRST and
IDCMDLAST is the range of command ID values which we can use. */
STDMETHODIMP
gpgex_t::QueryContextMenu (HMENU hMenu, UINT indexMenu, UINT idCmdFirst,
UINT idCmdLast, UINT uFlags)
{
BOOL res;
TRACE_BEG5 (DEBUG_CONTEXT_MENU, "gpgex_t::QueryContextMenu", this,
"hMenu=%p, indexMenu=%u, idCmdFirst=%u, idCmdLast=%u, uFlags=%x",
hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
/* FIXME: Do something if idCmdLast - idCmdFirst + 1 is not big
enough. */
/* If the flags include CMF_DEFAULTONLY then nothing should be done. */
if (uFlags & CMF_DEFAULTONLY)
return TRACE_RES (MAKE_HRESULT (SEVERITY_SUCCESS, FACILITY_NULL, 0));
res = InsertMenu (hMenu, indexMenu++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
if (! res)
return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
/* First we add the file-specific menus. */
if (this->all_files_gpg)
{
res = InsertMenu (hMenu, indexMenu++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_DECRYPT_VERIFY,
ID_CMD_STR_DECRYPT_VERIFY);
if (! res)
return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
}
else
{
/* Check registry if a different default menu entry is configured */
int def_entry = readDefaultEntry ();
if (def_entry == -1)
{
def_entry = ID_CMD_SIGN_ENCRYPT;
}
res = InsertMenu (hMenu, indexMenu++, MF_BYPOSITION | MF_STRING,
idCmdFirst + def_entry,
getCaptionForId (def_entry));
if (! res)
return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
}
/* Setup the icon */
res = setupContextMenuIcon (IDI_ICON_16, hMenu, indexMenu);
if (! res)
return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
/* Now generate and add the generic command popup menu. */
HMENU popup;
UINT idx = 0;
/* FIXME: Check error. */
popup = CreatePopupMenu ();
if (popup == NULL)
return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
MENUITEMINFO mii = { sizeof (MENUITEMINFO) };
mii.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
mii.wID = idCmdFirst + ID_CMD_POPUP;
mii.hSubMenu = popup;
mii.dwTypeData = (CHAR *) _("More GpgEX options");
res = InsertMenuItem (hMenu, indexMenu++, TRUE, &mii);
if (!res)
{
DWORD last_error = GetLastError ();
DestroyMenu (popup);
return TRACE_RES (HRESULT_FROM_WIN32 (last_error));
}
res = InsertMenu (hMenu, indexMenu++, MF_BYPOSITION | MF_SEPARATOR,
0, NULL);
if (! res)
return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_DECRYPT,
ID_CMD_STR_DECRYPT);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_VERIFY,
ID_CMD_STR_VERIFY);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_DECRYPT_VERIFY,
ID_CMD_STR_DECRYPT_VERIFY);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_ENCRYPT,
ID_CMD_STR_ENCRYPT);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_SIGN,
ID_CMD_STR_SIGN);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_SIGN_ENCRYPT,
ID_CMD_STR_SIGN_ENCRYPT);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_IMPORT, ID_CMD_STR_IMPORT);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_CREATE_CHECKSUMS, ID_CMD_STR_CREATE_CHECKSUMS);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_VERIFY_CHECKSUMS, ID_CMD_STR_VERIFY_CHECKSUMS);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
if (res)
res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING,
idCmdFirst + ID_CMD_ABOUT, ID_CMD_STR_ABOUT);
if (! res)
return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
/* We should return a HRESULT that indicates success and the offset
to the next free command ID after the last one we used, relative
to idCmdFirst. In other words: max_used - idCmdFirst + 1. */
return TRACE_RES (MAKE_HRESULT (SEVERITY_SUCCESS, FACILITY_NULL,
ID_CMD_MAX + 1));
}
/* Get a verb or help text for the command IDCOMMAND (which is the
offset to IDCMDFIRST of QueryContextMenu, ie zero based). UFLAGS
has GCS_HELPTEXT set if the help-text is requested (otherwise a
verb is requested). If UFLAGS has the GCS_UNICODE bit set, we need
to return a wide character string. */
STDMETHODIMP
gpgex_t::GetCommandString (UINT_PTR idCommand, UINT uFlags, LPUINT lpReserved,
LPSTR pszName, UINT uMaxNameLen)
{
const char *txt;
TRACE_BEG5 (DEBUG_CONTEXT_MENU, "gpgex_t::GetCommandString", this,
"idCommand=%u, uFlags=%x, lpReserved=%lu, pszName=%p, "
"uMaxNameLen=%u",
(unsigned int)(idCommand & 0xffffffff),
uFlags, lpReserved, pszName, uMaxNameLen);
if (! (uFlags & GCS_HELPTEXT))
return TRACE_RES (E_INVALIDARG);
if (idCommand > ID_CMD_MAX)
return TRACE_RES (E_INVALIDARG);
switch (idCommand)
{
case ID_CMD_ABOUT:
txt = _("Show the version of GpgEX.");
break;
case ID_CMD_DECRYPT_VERIFY:
txt = _("Decrypt and verify the marked files.");
break;
case ID_CMD_DECRYPT:
txt = _("Decrypt the marked files.");
break;
case ID_CMD_VERIFY:
txt = _("Verify the marked files.");
break;
case ID_CMD_SIGN_ENCRYPT:
txt = _("Sign and encrypt the marked files.");
break;
case ID_CMD_ENCRYPT:
txt = _("Encrypt the marked files.");
break;
case ID_CMD_SIGN:
txt = _("Sign the marked files.");
break;
case ID_CMD_IMPORT:
txt = _("Import the marked files.");
break;
case ID_CMD_CREATE_CHECKSUMS:
txt = _("Create checksums.");
break;
case ID_CMD_VERIFY_CHECKSUMS:
txt = _("Verify checksums.");
break;
case ID_CMD_POPUP:
txt = _("Show more GpgEX options.");
break;
default:
return TRACE_RES (E_INVALIDARG);
}
if (uFlags & GCS_UNICODE)
{
/* FIXME: Convert to unicode. */
lstrcpynW ((LPWSTR) pszName, L"(Unicode help not available yet)",
uMaxNameLen);
}
else
lstrcpynA (pszName, txt, uMaxNameLen);
return TRACE_RES (S_OK);
}
/* Show the version informatione etc. */
static void
show_about (HWND hwnd)
{
const char cpynotice[] = "Copyright (C) 2022 g10 Code GmbH";
const char en_notice[] =
"GpgEX is an Explorer plugin for data encryption and signing\n"
"It uses the GnuPG software (http://www.gnupg.org).\n"
"\n"
"GpgEX is free software; you can redistribute it and/or\n"
"modify it under the terms of the GNU Lesser General Public\n"
"License as published by the Free Software Foundation; either\n"
"version 2.1 of the License, or (at your option) any later version.\n"
"\n"
"GpgEX is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU Lesser General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU Lesser General Public "
"License\n"
"along with this program; if not, see <http://www.gnu.org/licenses/>.";
/* TRANSLATORS: See the source for the full english text. */
const char notice_key[] = N_("-#GpgEXFullHelpText#-");
const char *notice;
char header[300];
char *buffer;
size_t nbuffer;
snprintf (header, sizeof header, _("This is GpgEX version %s (%s)"),
PACKAGE_VERSION,
#ifdef HAVE_W64_SYSTEM
"64 bit"
#else
"32 bit"
#endif
);
notice = _(notice_key);
if (!strcmp (notice, notice_key))
notice = en_notice;
nbuffer = strlen (header) + strlen (cpynotice) + strlen (notice) + 20;
buffer = (char*)malloc (nbuffer);
if (buffer)
{
snprintf (buffer, nbuffer, "%s\n%s\n\n%s\n",
header, cpynotice, notice);
MessageBox (hwnd, buffer, "GpgEx", MB_OK);
free (buffer);
}
else
MessageBox (hwnd, header, "GpgEx", MB_OK);
}
STDMETHODIMP
gpgex_t::InvokeCommand (LPCMINVOKECOMMANDINFO lpcmi)
{
TRACE_BEG1 (DEBUG_CONTEXT_MENU, "gpgex_t::InvokeCommand", this,
"lpcmi=%p", lpcmi);
/* If lpVerb really points to a string, ignore this function call
and bail out. */
if (HIWORD (lpcmi->lpVerb) != 0)
return TRACE_RES (E_INVALIDARG);
client_t client (lpcmi->hwnd);
/* Get the command index, which is the offset to IDCMDFIRST of
QueryContextMenu, ie zero based). */
switch (LOWORD (lpcmi->lpVerb))
{
case ID_CMD_ABOUT:
show_about (lpcmi->hwnd);
break;
case ID_CMD_DECRYPT_VERIFY:
client.decrypt_verify (this->filenames);
break;
case ID_CMD_DECRYPT:
client.decrypt (this->filenames);
break;
case ID_CMD_VERIFY:
client.verify (this->filenames);
break;
case ID_CMD_SIGN_ENCRYPT:
client.sign_encrypt (this->filenames);
break;
case ID_CMD_ENCRYPT:
client.encrypt (this->filenames);
break;
case ID_CMD_SIGN:
client.sign (this->filenames);
break;
case ID_CMD_IMPORT:
client.import (this->filenames);
break;
case ID_CMD_CREATE_CHECKSUMS:
client.create_checksums (this->filenames);
break;
case ID_CMD_VERIFY_CHECKSUMS:
client.verify_checksums (this->filenames);
break;
default:
return TRACE_RES (E_INVALIDARG);
break;
}
return TRACE_RES (S_OK);
}
/* IContextMenu2 methods. */
STDMETHODIMP
gpgex_t::HandleMenuMsg (UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* FIXME */
return S_OK;
}
/* IContextMenu3 methods. */
STDMETHODIMP
gpgex_t::HandleMenuMsg2 (UINT uMsg, WPARAM wParam, LPARAM lParam,
LRESULT *plResult)
{
/* FIXME */
return S_OK;
}
diff --git a/src/main.cc b/src/main.cc
index 661b9ad..4b4fac5 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -1,405 +1,348 @@
/* main.cc - DLL entry point
Copyright (C) 2007, 2010, 2013 g10 Code GmbH
This file is part of GpgEX.
GpgEX 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.
GpgEX 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
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/>.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
#include <shlobj.h>
#include <gpg-error.h>
#include <assuan.h>
-#include "registry.h"
#include "gpgex-class.h"
#include "gpgex-factory.h"
#include "main.h"
/* This is the main part of the COM server component. The component
is an in-process server DLL. */
/* The instance of this DLL. */
HINSTANCE gpgex_server::instance;
/* The number of references to this component. */
LONG gpgex_server::refcount;
-/* The root of our installation. */
-const char *gpgex_server::root_dir;
-
/* The name of the UI-server or NULL if not known. */
const char *gpgex_server::ui_server;
-static char *
-get_locale_dir (void)
+/** Get the Gpg4win Install directory.
+ *
+ * This function returns the root of the install directory of Gpg4win
+ * or GnuPG-[VS-]Desktop. Noet that the fucntion returns only
+ * standard slashes.
+ *
+ * @returns NULL if no dir could be found - this indicates a bad
+ * installation. Otherwise a malloced string with the utf-8 name.
+ */
+const char *
+get_gpg4win_dir (void)
{
- static wchar_t moddir[MAX_PATH+5];
- char *result, *p;
- int nbytes;
+ static int initialized;
+ static char *mydir;
- if (!GetModuleFileNameW (gpgex_server::instance, moddir, MAX_PATH))
- *moddir = 0;
-
-#define SLDIR "\\share\\locale"
- if (*moddir)
+ if (!initialized)
{
- nbytes = WideCharToMultiByte (CP_UTF8, 0, moddir, -1, NULL, 0, NULL, NULL);
- if (nbytes < 0)
- return NULL;
-
- result = (char*)malloc (nbytes + strlen (SLDIR) + 1);
- if (result)
+ wchar_t *wmodulename;
+ char *modulename = NULL;
+ char *p;
+
+ wmodulename = (wchar_t*)calloc (MAX_PATH+5, sizeof *wmodulename);
+ if (!GetModuleFileNameW (gpgex_server::instance, wmodulename, MAX_PATH))
+ _gpgex_debug (DEBUG_INIT, "GetModuleFileName failed");
+ else
{
- nbytes = WideCharToMultiByte (CP_UTF8, 0, moddir, -1,
- result, nbytes, NULL, NULL);
- if (nbytes < 0)
- {
- free (result);
- result = NULL;
- }
- else
+ modulename = gpgrt_wchar_to_utf8 (wmodulename);
+ _gpgex_debug (DEBUG_INIT, "GetModuleFileName: '%s'\n", modulename);
+ p = strrchr (modulename, '\\');
+ if (p)
{
- p = strrchr (result, '\\');
+ *p = 0;
+ /* MODULENAME is now the actual directory from where we
+ * were executed. In the most cases this is either the
+ * bin or the bin_64 sub directory. */
+ p = strrchr (modulename, '\\');
if (p)
- *p = 0;
- /* If we are installed below "bin" strip that part and
- use the top directory instead.
-
- Background: Under Windows we don't install GnuPG
- below bin/ but in the top directory with only share/,
- lib/, and etc/ below it. One of the reasons is to
- keep the the length of the filenames at bay so not to
- increase the limited length of the PATH envvar.
- Another and more important reason, however, is that
- the very first GPG versions on W32 were installed
- into a flat directory structure and for best
- compatibility with these versions we didn't changed
- that later. For WindowsCE we can right away install
- it under bin, though. The hack with detection of the
- bin directory part allows us to eventually migrate to
- such a directory layout under plain Windows without
- the need to change libgpg-error. */
- p = strrchr (result, '\\');
- if (p && (!strcmp (p+1, "bin") || !strcmp (p+1, "bin_64")))
- *p = 0;
-
- gpgex_server::root_dir = strdup (result);
-
- /* Append the static part. */
- strcat (result, SLDIR);
+ {
+ *p = 0;
+ for (p=modulename; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+ mydir = modulename; /* Not really threadsafe, but okay. */
+ modulename = NULL;
+ initialized = 1;
+ _gpgex_debug (DEBUG_INIT, "Using install dir: '%s'\n", mydir);
+ }
}
}
+ free (modulename);
+ free (wmodulename);
}
- else /* Use the old default value. */
- {
- result = (char*)malloc (10 + strlen (SLDIR) + 1);
- if (result)
- {
- strcpy (result, "c:\\gnupg");
- strcat (result, SLDIR);
- }
- }
- if (!gpgex_server::root_dir)
- gpgex_server::root_dir = "c:";
+ return mydir;
+}
+
- _gpgex_debug (1, "root dir is '%s'", gpgex_server::root_dir);
-#undef SLDIR
- return result;
+static char *
+get_locale_dir (void)
+{
+ return gpgrt_fconcat (0, get_gpg4win_dir (), "share/locale", NULL);
}
static void
drop_locale_dir (char *locale_dir)
{
free (locale_dir);
}
static void
i18n_init (void)
{
char *locale_dir;
locale_dir = get_locale_dir ();
if (locale_dir)
{
bindtextdomain (PACKAGE_GT, locale_dir);
drop_locale_dir (locale_dir);
}
textdomain (PACKAGE_GT);
}
static CRITICAL_SECTION debug_lock;
/* No flags on means no debugging. */
unsigned int debug_flags = 0;
/* Debug log file. */
FILE *debug_file;
/* Get the filename of the debug file, if any. */
static char *
get_debug_file (void)
{
- char *name = read_w32_registry_string (NULL,
- GPG4WIN_REGKEY_3,
- "GpgEX Debug File");
- if (!name)
- {
- name = read_w32_registry_string (NULL,
- GPG4WIN_REGKEY_2,
- "GpgEX Debug File");
- }
- return name;
+ return gpgrt_w32_reg_get_string ("\\Software\\Gpg4win:GpgEX Debug File");
}
static void
debug_init (void)
{
char *filename;
/* Sanity check. */
if (debug_file)
return;
InitializeCriticalSection (&debug_lock);
filename = get_debug_file ();
if (!filename)
return;
debug_file = fopen (filename, "a");
free (filename);
if (!debug_file)
return;
/* FIXME: Make this configurable eventually. */
debug_flags = DEBUG_INIT | DEBUG_CONTEXT_MENU | DEBUG_ASSUAN;
}
static void
debug_deinit (void)
{
if (debug_file)
{
fclose (debug_file);
debug_file = NULL;
}
}
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
/* Log the formatted string FORMAT at debug level LEVEL or higher. */
extern
void
_gpgex_debug (unsigned int flags, const char *format, ...)
{
va_list arg_ptr;
int saved_errno;
saved_errno = errno;
if (! (debug_flags & flags))
return;
va_start (arg_ptr, format);
EnterCriticalSection (&debug_lock);
vfprintf (debug_file, format, arg_ptr);
va_end (arg_ptr);
if (format && *format && format[strlen (format) - 1] != '\n')
putc ('\n', debug_file);
LeaveCriticalSection (&debug_lock);
fflush (debug_file);
errno = saved_errno;
}
-/* 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.
- The result of calling this function with STRING set to NULL is not
- defined. */
-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;
-}
-
-
#ifdef __cplusplus
#if 0
{
#endif
}
#endif
/* Entry point called by DLL loader. */
STDAPI
DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
{
if (reason == DLL_PROCESS_ATTACH)
{
gpgex_server::instance = hinst;
/* Early initializations of our subsystems. */
gpg_err_init ();
debug_init ();
i18n_init ();
if (debug_flags & DEBUG_ASSUAN)
{
assuan_set_assuan_log_stream (debug_file);
assuan_set_assuan_log_prefix ("gpgex:assuan");
}
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
(void) TRACE0 (DEBUG_INIT, "DllMain", hinst,
"reason=DLL_PROCESS_ATTACH");
{
WSADATA wsadat;
WSAStartup (0x202, &wsadat);
}
}
else if (reason == DLL_PROCESS_DETACH)
{
WSACleanup ();
(void) TRACE0 (DEBUG_INIT, "DllMain", hinst,
"reason=DLL_PROCESS_DETACH");
debug_deinit ();
/* We are linking statically to libgpg-error which means there
is no DllMain in libgpg-error. Thus we call the deinit
function to cleanly deinitialize libgpg-error. */
gpg_err_deinit (0);
}
return TRUE;
}
/* Check if the server component, the DLL, can be unloaded. This is
called by the client of this in-process server (for example through
CoFreeUnusedLibrary) whenever it is considered to unload this
server component. */
STDAPI
DllCanUnloadNow (void)
{
(void) TRACE (DEBUG_INIT, "DllCanUnloadNow", gpgex_server::refcount);
return (gpgex_server::refcount == 0 ? S_OK : S_FALSE);
}
/* Registry handling. The DLL is registered with regsvr32.exe. */
/* Register the DLL. */
STDAPI
DllRegisterServer (void)
{
gpgex_class::init ();
/* Notify the shell about the change. */
SHChangeNotify (SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
return S_OK;
}
/* Unregister the DLL. */
STDAPI
DllUnregisterServer (void)
{
gpgex_class::deinit ();
/* Notify the shell about the change. */
SHChangeNotify (SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
return S_OK;
}
/* Acquire a reference for the class factory of object class RCLSID
with the interface RIID (typically IID_IClassFactory) and return it
in PPVOUT. Typically called by CoGetClassObject for in-process
server components. */
STDAPI
DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
/* We have to evaluate the arguments first. */
#define _TRACE_BEG22(a,b,c,d,e,f) TRACE_BEG22(a,b,c,d,e,f)
_TRACE_BEG22 (DEBUG_INIT, "DllGetClassObject", ppv,
"rclsid=" GUID_FMT ", riid=" GUID_FMT,
GUID_ARG (rclsid), GUID_ARG (riid));
if (rclsid == CLSID_gpgex)
{
HRESULT err = gpgex_factory.QueryInterface (riid, ppv);
return TRACE_RES (err);
}
/* Be nice to broken software. */
*ppv = NULL;
return TRACE_RES (CLASS_E_CLASSNOTAVAILABLE);
}
diff --git a/src/main.h b/src/main.h
index 641799e..b1895a1 100644
--- a/src/main.h
+++ b/src/main.h
@@ -1,91 +1,87 @@
/* main.h - main prototypes
Copyright (C) 2007 g10 Code GmbH
This file is part of GpgEX.
GpgEX 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 of the License, or (at your option) any later version.
GpgEX 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA. */
#ifndef MAIN_H
#define MAIN_H 1
#include <windows.h>
#include "debug.h"
#include <gpg-error.h> /* For gettext */
#define _(a) gettext (a)
#define N_(a) gettext_noop (a)
/* A pseudo function call that serves as a marker for the automated
extraction of messages, but does not call gettext(). The run-time
translation is done at a different place in the code.
The argument, String, should be a literal string. Concatenated strings
and other string expressions won't work.
The macro's expansion is not parenthesized, so that it is suitable as
initializer for static 'char[]' or 'const char[]' variables. */
#define gettext_noop(String) String
/* We use a class just for namespace cleanliness. */
class gpgex_server
{
public:
/* The instance of this DLL. */
static HINSTANCE instance;
/* Global reference counting for the server component. This is
increased by acquiring references to any COM objects as well as
when locking the server component, and needed to implement
DllCanUnloadNow. */
/* The number of references to this component. */
static LONG refcount;
- /* The root of our installation. */
- static const char *root_dir;
-
static const char *ui_server;
/* Acquire a reference to the server component. */
static inline ULONG
add_ref (void)
{
return InterlockedIncrement (&refcount);
}
/* Release a reference to the server component. */
static inline ULONG
release (void)
{
return InterlockedDecrement (&refcount);
}
};
#define GUID_FMT "{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}"
#define GUID_ARG(x) (x).Data1, (x).Data2, (x).Data3, (x).Data4[0], \
(x).Data4[1], (x).Data4[2], (x).Data4[3], (x).Data4[4], \
(x).Data4[5], (x).Data4[6], (x).Data4[7]
#endif
-
-extern "C" wchar_t *utf8_to_wchar (const char *string);
+extern "C" const char *get_gpg4win_dir (void);
diff --git a/src/registry.c b/src/registry.c
deleted file mode 100644
index 7410e46..0000000
--- a/src/registry.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/* registry.c - Registry routines
- Copyright (C) 2005, 2007 g10 Code GmbH
-
- This file is part of GpgEX.
-
- GpgEX 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.
-
- GpgEX 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
- 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., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA. */
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <unistd.h>
-#include <assert.h>
-#include <windows.h>
-
-#include <shlobj.h>
-#ifndef CSIDL_APPDATA
-#define CSIDL_APPDATA 0x001a
-#endif
-#ifndef CSIDL_LOCAL_APPDATA
-#define CSIDL_LOCAL_APPDATA 0x001c
-#endif
-#ifndef CSIDL_FLAG_CREATE
-#define CSIDL_FLAG_CREATE 0x8000
-#endif
-
-#include "registry.h"
-
-
-/* This is a helper function to load a Windows function from either of
- one DLLs. */
-HRESULT
-w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
-{
- static int initialized;
- static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
-
- if (!initialized)
- {
- static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
- void *handle;
- int i;
-
- initialized = 1;
-
- for (i=0, handle = NULL; !handle && dllnames[i]; i++)
- {
- handle = LoadLibrary (dllnames[i]);
- if (handle)
- {
- func = (HRESULT (WINAPI *)(HWND,int,HANDLE,DWORD,LPSTR))
- GetProcAddress (handle, "SHGetFolderPathA");
- if (!func)
- {
- FreeLibrary (handle);
- handle = NULL;
- }
- }
- }
- }
-
- if (func)
- return func (a,b,c,d,e);
- else
- return -1;
-}
-
-
-/* Helper for read_w32_registry_string(). */
-static HKEY
-get_root_key(const char *root)
-{
- HKEY root_key;
-
- if( !root )
- root_key = HKEY_CURRENT_USER;
- else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
- root_key = HKEY_CLASSES_ROOT;
- else if( !strcmp( root, "HKEY_CURRENT_USER" ) )
- root_key = HKEY_CURRENT_USER;
- else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
- root_key = HKEY_LOCAL_MACHINE;
- else if( !strcmp( root, "HKEY_USERS" ) )
- root_key = HKEY_USERS;
- else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
- root_key = HKEY_PERFORMANCE_DATA;
- else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
- root_key = HKEY_CURRENT_CONFIG;
- else
- return NULL;
- return root_key;
-}
-
-
-/* Return a string from the Win32 Registry or NULL in case of error.
- Caller must release the return value. A NULL for root is an alias
- for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
-char *
-read_w32_registry_string (const char *root, const char *dir, const char *name)
-{
- HKEY root_key, key_handle;
- DWORD n1, nbytes, type;
- char *result = NULL;
-
- if ( !(root_key = get_root_key(root) ) )
- return NULL;
-
- if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
- {
- if (root)
- return NULL; /* no need for a RegClose, so return direct */
- /* It seems to be common practise to fall back to HKLM. */
- if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
- return NULL; /* still no need for a RegClose, so return direct */
- }
-
- nbytes = 1;
- if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) {
- if (root)
- goto leave;
- /* Try to fallback to HKLM also vor a missing value. */
- RegCloseKey (key_handle);
- if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
- return NULL; /* Nope. */
- if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes))
- goto leave;
- }
- result = malloc( (n1=nbytes+1) );
- if( !result )
- goto leave;
- if( RegQueryValueEx( key_handle, name, 0, &type, (LPBYTE)result, &n1 ) ) {
- free(result); result = NULL;
- goto leave;
- }
- result[nbytes] = 0; /* make sure it is really a string */
- if (type == REG_EXPAND_SZ && strchr (result, '%')) {
- char *tmp;
-
- n1 += 1000;
- tmp = malloc (n1+1);
- if (!tmp)
- goto leave;
- nbytes = ExpandEnvironmentStrings (result, tmp, n1);
- if (nbytes && nbytes > n1) {
- free (tmp);
- n1 = nbytes;
- tmp = malloc (n1 + 1);
- if (!tmp)
- goto leave;
- nbytes = ExpandEnvironmentStrings (result, tmp, n1);
- if (nbytes && nbytes > n1) {
- free (tmp); /* oops - truncated, better don't expand at all */
- goto leave;
- }
- tmp[nbytes] = 0;
- free (result);
- result = tmp;
- }
- else if (nbytes) { /* okay, reduce the length */
- tmp[nbytes] = 0;
- free (result);
- result = malloc (strlen (tmp)+1);
- if (!result)
- result = tmp;
- else {
- strcpy (result, tmp);
- free (tmp);
- }
- }
- else { /* error - don't expand */
- free (tmp);
- }
- }
-
- leave:
- RegCloseKey( key_handle );
- return result;
-}
diff --git a/src/registry.h b/src/registry.h
deleted file mode 100644
index 05e1c05..0000000
--- a/src/registry.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* registry.h - registry prototypes
- Copyright (C) 2006, 2007 g10 Code GmbH
-
- This file is part of GpgEX.
-
- GpgEX 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 of the License, or (at your option) any later version.
-
- GpgEX 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., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA. */
-
-#ifndef REGISTRY_H
-#define REGISTRY_H
-
-#include <windows.h>
-
-#ifdef __cplusplus
-extern "C" {
-#if 0
-}
-#endif
-#endif
-
-/* This is a helper function to load a Windows function from either of
- one DLLs. */
-HRESULT w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e);
-
-/* Return a string from the Win32 Registry or NULL in case of error.
- Caller must release the return value. A NULL for root is an alias
- for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
-char *read_w32_registry_string (const char *root, const char *dir,
- const char *name);
-
-/* The Registry keys used by Gpg4win and GnUPG. */
-#ifdef WIN64
-# define GPG4WIN_REGKEY_1 "Software\\Wow6432Node\\GnuPG"
-# define GPG4WIN_REGKEY_2 "Software\\Wow6432Node\\GNU\\GnuPG"
-# define GPG4WIN_REGKEY_3 "Software\\Wow6432Node\\Gpg4win"
-#else
-# define GPG4WIN_REGKEY_1 "Software\\GnuPG"
-# define GPG4WIN_REGKEY_2 "Software\\GNU\\GnuPG"
-# define GPG4WIN_REGKEY_3 "Software\\Gpg4win"
-#endif
-
-#ifdef __cplusplus
-#if 0
-{
-#endif
-}
-#endif
-
-#endif /* ! REGISTRY_H */
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 25, 7:19 AM (1 d, 20 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b9/cf/87b2b347d07e0794d799146c6f76
Attached To
rX GpgEX
Event Timeline
Log In to Comment