diff --git a/src/Makefile.am b/src/Makefile.am
index 96e36c2f..21675c2a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,635 +1,636 @@
# Makefile.am - Installer for GnuPG 4 Windows Makefile.
# Copyright (C) 2005, 2006, 2007, 2008, 2009 g10 Code GmbH
#
# This file is part of Gpg4win.
#
# Gpg4win 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.
#
# Gpg4win 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, see .
EXTRA_DIST = pkg-config gpg4win.nsi gpg4win-src.nsi \
config.site config.nsi.in \
libiconv.def libintl.def loreley.mid \
gpg4win-splash.wav exdll.h g4wihelp.c desktopshellrun.cpp \
g4wihelp.nsi slideshow.cpp gpgwrap.c \
inst-sections.nsi installer.nsi installer-finish.nsi \
zlib.pc sha1sum.c \
potomo Memento.nsh \
inst-gpg4win.nsi uninst-gpg4win.nsi \
inst-gettext.nsi uninst-gettext.nsi \
inst-gnupg-w32.nsi uninst-gnupg-w32.nsi \
inst-gpgme.nsi uninst-gpgme.nsi \
inst-paperkey.nsi uninst-paperkey.nsi \
inst-gpgol.nsi uninst-gpgol.nsi \
inst-gpgex.nsi uninst-gpgex.nsi \
inst-libassuan.nsi uninst-libassuan.nsi \
inst-libgpg-error.nsi uninst-libgpg-error.nsi \
inst-libiconv.nsi uninst-libiconv.nsi \
inst-compendium.nsi uninst-compendium.nsi \
inst-man_advanced_de.nsi uninst-man_advanced_de.nsi \
inst-man_advanced_en.nsi uninst-man_advanced_en.nsi \
inst-man_novice_de.nsi uninst-man_novice_de.nsi \
inst-man_novice_en.nsi uninst-man_novice_en.nsi \
inst-pinentry.nsi uninst-pinentry.nsi \
inst-pkgconfig.nsi uninst-pkgconfig.nsi \
inst-zlib.nsi uninst-zlib.nsi \
inst-kconfig.nsi uninst-kconfig.nsi \
inst-kleopatra.nsi uninst-kleopatra.nsi \
inst-bzip2.nsi uninst-bzip2.nsi \
inst-qtbase.nsi uninst-qtbase.nsi \
inst-ki18n.nsi uninst-ki18n.nsi \
inst-kio.nsi uninst-kio.nsi \
inst-kbookmarks.nsi uninst-kbookmarks.nsi \
inst-kservice.nsi uninst-kservice.nsi \
inst-solid.nsi uninst-solid.nsi \
inst-kjobwidgets.nsi uninst-kjobwidgets.nsi \
inst-kwidgetsaddons.nsi uninst-kwidgetsaddons.nsi \
inst-kcompletion.nsi uninst-kcompletion.nsi \
inst-kwindowsystem.nsi uninst-kwindowsystem.nsi \
inst-libkleo.nsi uninst-libkleo.nsi \
inst-kcoreaddons.nsi uninst-kcoreaddons.nsi \
inst-kcodecs.nsi uninst-kcodecs.nsi \
inst-kconfigwidgets.nsi uninst-kconfigwidgets.nsi \
inst-kxmlgui.nsi uninst-kxmlgui.nsi \
inst-kiconthemes.nsi uninst-kiconthemes.nsi \
inst-breeze-icons.nsi uninst-breeze-icons.nsi \
inst-karchive.nsi uninst-karchive.nsi \
inst-kcrash.nsi uninst-kcrash.nsi \
inst-kitemviews.nsi uninst-kitemviews.nsi \
inst-kitemmodels.nsi uninst-kitemmodels.nsi \
inst-kguiaddons.nsi uninst-kguiaddons.nsi \
inst-kmime.nsi uninst-kmime.nsi \
inst-kde-l10n.nsi uninst-kde-l10n.nsi \
inst-qtsvg.nsi uninst-qtsvg.nsi \
inst-qttools.nsi uninst-qttools.nsi \
inst-qttranslations.nsi uninst-qttranslations.nsi \
inst-qtwinextras.nsi uninst-qtwinextras.nsi \
inst-scute.nsi uninst-scute.nsi \
inst-extra-cmake-modules.nsi uninst-extra-cmake-modules.nsi \
inst-gpg4win-tools.nsi uninst-gpg4win-tools.nsi \
inst-gpgme-browser.nsi \
inst-final.nsi index.theme \
slideshow/slides.dat \
slideshow/slide1-gpgol.png \
slideshow/slide2-gpgex.png \
slideshow/slide3-kleopatra.png \
slideshow/slide4-summary.png \
icons/file-ext.ico \
kdeglobals qtlogging.ini \
make-msi.pl make-msi.guids \
build-gpg4win.sh \
WixUI_Gpg4win.wxs icons/shield.ico
if BUILD_APPIMAGE
appimage = appimage
else
appimage =
endif
SUBDIRS = ${appimage}
# These paths must be absolute, as we switch directories pretty often.
root := $(shell pwd)/playground
stampdir := $(shell pwd)/stamps
gpg4win_readme_ll = en de ar es fr ru pt zh
README_files = $(addsuffix .txt,$(addprefix README.,$(gpg4win_readme_ll)))
gpg4win_howto_smime_ll = en de
HOWTO_files = $(addsuffix .txt, \
$(addprefix HOWTO-SMIME.,$(gpg4win_howto_smime_ll)))
foosum_exe = sha1sum.exe md5sum.exe sha256sum.exe
# Need to clean the dll because we bypassed automake.
CLEANFILES = g4wihelp.dll desktopshellrun.o versioninfo.txt $(README_files) \
$(HOWTO_files) NEWS.tmp COMPONENTS-list.tmp \
license.blurb cdversioninfo.tmp slideshow.o \
$(foosum_exe) gpgwrap.exe
# Supported source packages. (see also ../appimage/Makefile.am)
gpg4win_spkgs = glib libffi gdk-pixbuf gtk+ \
libgpg-error gpgme gpgol gpgex libpng \
gpa opencdk \
pinentry libassuan ntbtls pcre \
paperkey regex libiconv gettext scute
gpg4win_qtpkgs = qtbase qttools qtwinextras qtsvg qttranslations
# Source packages following the KDE pattern
gpg4win_kdepkgs = kconfig kwidgetsaddons ki18n extra-cmake-modules \
kcompletion kwindowsystem kcoreaddons libkleo kcodecs \
kmime kconfigwidgets kxmlgui kguiaddons kitemviews \
kiconthemes kleopatra breeze-icons kitemmodels karchive \
gpg4win-tools kcrash kio kbookmarks kservice solid \
kjobwidgets
# Supported source packages to build in an additional architecture
gpg4win_expkgs = libgpg-error libassuan libiconv gettext gpgex gpgol \
gpgme
# Some variables
WINE = wine
WINHOST = win10
WINLIGHT = c:/wix/light.exe
# Extra options to configure for individual packages.
# We can use $(idir) here for the installation prefix.
gpg4win_pkg_libiconv_ex_configure = \
--enable-shared=no --enable-static=yes
gpg4win_pkg_gettext_configure = \
--with-lib-prefix=$(idir) --with-libiconv-prefix=$(idir) \
CPPFLAGS=-I$(idir)/include CFLAGS=-O2 LDFLAGS=-L$(idir)/lib
gpg4win_pkg_scute_configure = \
LDFLAGS=-lws2_32
gpg4win_pkg_gettext_ex_configure = \
--with-lib-prefix=$(ex_idir) --with-libiconv-prefix=$(ex_idir) \
CPPFLAGS=-I$(ex_idir)/include CFLAGS=-O2 LDFLAGS=-L$(ex_idir)/lib
# We only need gettext-runtime and there is sadly no top level
# configure option for this
gpg4win_pkg_gettext_make_dir = gettext-runtime
gpg4win_pkg_gettext_make_dir_inst = gettext-runtime
gpg4win_pkg_gettext_ex_make_dir = gettext-runtime
gpg4win_pkg_gettext_ex_make_dir_inst = gettext-runtime
# Paperkey comes with a man pages which we have to convert.
define gpg4win_pkg_paperkey_post_install
(for i in paperkey ; \
do man -Tlatin1 -l $$$${pkgidir}/share/man/man1/$$$${i}.1 | \
sed `printf "s/_\b//g;s/\b.//g"` | \
sed -e 's/$$$$/\r/' > $$$${pkgidir}/share/man/man1/$$$${i}.man ; \
done)
endef
# Build the reference manual.
define gpg4win_pkg_gpgol_post_install
( cd $$$${pkgbdir}/doc; make pdf )
endef
# We would like to use --with-libiconv-prefix and
# --with-libintl-prefix, but these don't work with the cheesy
# non-"libfoo.la" versions of iconv and gettext that we are using.
#gpg4win_pkg_libgpg_error_configure = \
# --with-libiconv-prefix=$(idir) --with-libintl-prefix=$(idir)
gpg4win_pkg_libgpg_error_configure = --silent \
CPPFLAGS=-I$(idir)/include LDFLAGS=-L$(idir)/lib \
--enable-static --enable-install-gpg-error-config
gpg4win_pkg_libgpg_error_ex_configure = --silent \
--enable-static --enable-install-gpg-error-config
# We convert the PO files to UTF-8. FIXME: This should be done in
# libgpg-error proper.
define gpg4win_pkg_libgpg_error_post_install
(for i in `$(tsdir)/src/potomo --get-linguas $$$${pkgsdir}/po` ; do \
rm $$$${pkgidir}/share/locale/$$$${i}/LC_MESSAGES/libgpg-error.mo \
2>/dev/null|| true; \
$(tsdir)/src/potomo $$$${pkgsdir}/po/$$$${i}.po \
$$$${pkgidir}/share/locale/$$$${i}/LC_MESSAGES/libgpg-error.mo; \
done)
endef
gpg4win_pkg_libassuan_configure = --silent --enable-static
gpg4win_pkg_libassuan_ex_configure = --with-gpg-error-prefix=$(ex_idir) --silent --enable-static
gpg4win_pkg_libpng_configure = HAVE_SOLARIS_LD_FALSE=yes CPPFLAGS=\"-I$(idir)/include -DPNG_BUILD_DLL\" LDFLAGS=\"-L$(idir)/lib\" LIBPNG_DEFINES=\"-DPNG_BUILD_DLL\"
# We would like to use --with-libiconv-prefix and
# --with-libintl-prefix, but these don't work with the cheesy
# non-"libfoo.la" versions of iconv and gettext that we are using.
gpg4win_pkg_pinentry_configure = \
--enable-pinentry-qt \
$(pinentry_enable_disable) \
--disable-pinentry-fltk \
--disable-pinentry-curses \
--disable-fallback-curses \
CPPFLAGS=-I$(idir)/include LDFLAGS=-L$(idir)/lib
# --with-libiconv-prefix=$(idir) --with-libintl-prefix=$(idir)
# The LDFLAGS is needed for -lintl for glib. The QT4 variables work
# around the lack of cross compilation support in qt-dev.
gpg4win_pkg_gpgme_configure = --silent \
--with-gpg-error-prefix=$(idir) --enable-static \
LDFLAGS=-L$(idir)/lib \
--enable-languages=qt,cpp \
--disable-gpg-test \
--disable-gpgsm-test
gpg4win_pkg_gpgme_ex_configure = --silent \
--with-gpg-error-prefix=$(ex_idir) --enable-static \
--with-libassuan-prefix=$(ex_idir) \
LDFLAGS=-L$(ex_idir)/lib \
--enable-languages=cpp \
--disable-gpg-test \
--disable-gpgsm-test
gpg4win_pkg_gpgol_configure = --silent \
--with-gpgme-prefix=$(idir) --with-gpg-error-prefix=$(idir) \
--with-libassuan-prefix=$(idir)
gpg4win_pkg_gpgol_ex_configure = --silent \
--with-gpgme-prefix=$(ex_idir) --with-gpg-error-prefix=$(ex_idir) \
--with-libassuan-prefix=$(ex_idir)
gpg4win_pkg_gpgex_configure = --silent \
--with-gpg-error-prefix=$(idir) \
--with-libassuan-prefix=$(idir)
gpg4win_pkg_gpgex_ex_configure = --silent \
--with-gpg-error-prefix=$(ex_idir) \
--with-libassuan-prefix=$(ex_idir)
gpg4win_pkg_paperkey_configure = --silent
gpg4win_pkg_ki18n_configure = -DBUILD_WITH_QML=OFF
gpg4win_pkg_kconfig_configure = -DKCONFIG_USE_DBUS=OFF -DKCONFIG_USE_QML=OFF
gpg4win_pkg_qtbase_configure = ../$$$${pkg_version}/configure -opensource \
-confirm-license \
-xplatform win32-g++ \
-device-option CROSS_COMPILE=$(host)- \
-device-option PKG_CONFIG='$(host)-pkg-config' \
-no-use-gold-linker \
-release \
-shared \
-prefix $$$${pkgidir} \
-I '$(idir)/include' \
-L '$(idir)/lib' \
-no-icu \
-opengl desktop \
-no-glib \
-accessibility \
-nomake examples \
-nomake tests \
-no-sql-mysql \
-no-sql-sqlite \
-no-sql-odbc \
-no-sql-psql \
-no-sql-tds \
-qt-zlib \
-qt-libpng \
-qt-libjpeg \
-qt-freetype \
-qt-pcre \
-no-openssl \
-no-dbus \
-v
# qtbase
# invoke qmake with removed debug options as a workaround for
# https://bugreports.qt-project.org/browse/QTBUG-30898
gpg4win_pkg_qtbase_make_args = \
QMAKE='$$$${pkgbdir}/bin/qmake'
# Qmake still writes debug names in pkgconfig files.
define gpg4win_pkg_qtbase_post_install
(cd $$$${pkgbdir}; \
make install;)
endef
gpg4win_pkg_qttools_configure = \
"$(idir)/bin/qmake" ../$$$${pkg_version}
define gpg4win_pkg_qttools_post_install
(cd $$$${pkgbdir}; \
mkdir -p $$$${pkgidir}; \
cp -r bin lib include $$$${pkgidir})
endef
# Qmake does not support setting the installation path.
# really https://bugreports.qt.io/browse/QTBUG-12341
gpg4win_pkg_qtwinextras_configure = \
"$(idir)/bin/qmake" ../$$$${pkg_version}
define gpg4win_pkg_qtwinextras_post_install
(cd $$$${pkgbdir}; \
mkdir -p $$$${pkgidir}/bin; \
cp -r lib include $$$${pkgidir}; \
mv $$$${pkgidir}/lib/*.dll $$$${pkgidir}/bin;)
endef
gpg4win_pkg_qtsvg_configure = \
"$(idir)/bin/qmake" ../$$$${pkg_version}
# XXX Adding the qtconf after qtsvg is a weird hack
# because somhow (yay qmake) zlib is not linked if
# the qt conf exists before that. The qt conf is
# needed for qttranslations to find the correct path
# of the translation tools.
define gpg4win_pkg_qtsvg_post_install
(cd $$$${pkgbdir}; \
mkdir -p $$$${pkgidir}; \
cp -r lib include plugins $$$${pkgidir}; \
mkdir -p $$$${pkgidir}/bin; \
mv $$$${pkgidir}/lib/*.dll $$$${pkgidir}/bin; \
echo "[Paths]" > $$$${pkgidir}/bin/qt.conf; \
echo "Prefix = .." >> $$$${pkgidir}/bin/qt.conf)
endef
gpg4win_pkg_qttranslations_configure = \
"$(idir)/bin/qmake" ../$$$${pkg_version}
define gpg4win_pkg_qttranslations_post_install
( rm -fR $$$${pkgidir}/qttranslations; \
mkdir -p $$$${pkgidir}; \
cp -r $$$${pkgbdir}/translations $$$${pkgidir}/qttranslations;)
endef
gpg4win_pkg_kleopatra_configure = \
-DFORCE_DISABLE_KCMUTILS=ON \
-DDISABLE_KWATCHGNUPG=ON \
-DRELEASE_SERVICE_VERSION=Gpg4win-$(VERSION) \
-DKLEOPATRA_DISTRIBUTION_TEXT="$(DISTRIBUTION_TEXT)"
gpg4win_pkg_kxmlgui_configure = \
-DFORCE_DISABLE_KGLOBALACCEL=ON \
-DFORCE_DISABLE_DBUS=ON \
-DXMLGUI_COMPILER_VERSION="i686-w64-mingw32-gcc" \
-DXMLGUI_DISTRIBUTION_TEXT="$(DISTRIBUTION_TEXT)"
gpg4win_pkg_breeze_icons_configure = \
-DBINARY_ICONS_RESOURCE=OFF
gpg4win_pkg_kbookmarks_configure = \
-DANDROID=ON
gpg4win_pkg_kservice_configure = \
-DANDROID=ON
gpg4win_pkg_kjobwidgets_configure = \
-DANDROID=ON
# Supported make-only source packages.
gpg4win_mpkgs = bzip2 zlib
# Extra options for the make invocations for individual packages.
# make_flags is used for building, make_args_inst is used for installing.
gpg4win_pkg_bzip2_make_args = \
CC=$(CC) AR=$(AR) RANLIB=$(RANLIB)
gpg4win_pkg_bzip2_make_args_inst = \
PREFIX=$$$${pkgidir} CC=$(CC) AR=$(AR) RANLIB=$(RANLIB)
gpg4win_pkg_zlib_make_args = \
-fwin32/Makefile.gcc PREFIX=$(host)- IMPLIB=libz.dll.a
gpg4win_pkg_zlib_make_args_inst = -fwin32/Makefile.gcc install \
BINARY_PATH=$$$${pkgidir}/bin INCLUDE_PATH=$$$${pkgidir}/include \
LIBRARY_PATH=$$$${pkgidir}/lib SHARED_MODE=1 IMPLIB=libz.dll.a
# Supported binary packages.
gpg4win_bpkgs = pkgconfig gnupg-w32 kde-l10n
# libiconv needs some special magic to generate a usable import
# library.
# define gpg4win_pkg_libiconv_post_install
# (cp $(srcdir)/libiconv.def $$$${pkgidir}/lib; \
# cd $$$${pkgidir}/lib; \
# $(DLLTOOL) --output-lib libiconv.dll.a --def libiconv.def)
# endef
# libiconv needs some special magic to generate a usable import
# library. Note that we include some internal symbols that are not
# actually part of the DLL. They are needed by the AM_GNU_GETTEXT
# configure test, but not used otherwise.
# define gpg4win_pkg_gettext_post_install
# (cp $(srcdir)/libintl.def $$$${pkgidir}/lib; \
# cd $$$${pkgidir}/lib; \
# $(DLLTOOL) --output-lib libintl.dll.a --def libintl.def)
# endef
# Zlib needs some special magic to generate a libtool file.
# We also install the pc file here.
define gpg4win_pkg_zlib_post_install
(mkdir $$$${pkgidir}/lib/pkgconfig || true; \
cp $(abs_srcdir)/zlib.pc $$$${pkgidir}/lib/pkgconfig/; \
cd $$$${pkgidir}; \
echo "# Generated by libtool" > lib/libz.la \
echo "dlname='../bin/zlib1.dll'" >> lib/libz.la; \
echo "library_names='libz.dll.a'" >> lib/libz.la; \
echo "old_library='libz.a'" >> lib/libz.la; \
echo "dependency_libs=''" >> lib/libz.la; \
echo "current=1" >> lib/libz.la; \
echo "age=2" >> lib/libz.la; \
echo "revision=5" >> lib/libz.la; \
echo "installed=yes" >> lib/libz.la; \
echo "shouldnotlink=no" >> lib/libz.la; \
echo "dlopen=''" >> lib/libz.la; \
echo "dlpreopen=''" >> lib/libz.la; \
echo "libdir=\"$$$${pkgidir}/lib\"" >> lib/libz.la)
endef
# We don't use khelpcenter in kleopatra so remove the help links and
# point to the correct common folder
# TODO Needs to be fixed with current doc
#define gpg4win_pkg_kleopatra_post_install
# (for i in de en ; do \
# (cd $$$${pkgidir}/share/doc/HTML/$$$${i}; \
# perl -pi -e 's/help:\///g' `find . -name \*.html`; \
# perl -pi -e 's@common/@../../common/@g' `find . -name \*.html`); \
# done)
#endef
#define gpg4win_pkg_libiconv_post_install
#(cp $(srcdir)/libiconv.def $$$${pkgidir}/lib; \
#cd $$$${pkgidir}/lib; \
#$(DLLTOOL) --output-lib libiconv.dll.a --def libiconv.def)
#endef
# Supported internal packages. Internal packages do not require any
# special support. Thus, this variable is actually unused, and no
# rules are added in gpg4win.mk.
gpg4win_ipkgs = man_novice_de man_advanced_de man_novice_en man_advanced_en \
compendium
# Default to same version.
VSD_VERSION=$(VERSION)
# Include installer targets for customized packages
-include gnupg-vsd/custom.mk
# Signging host/key setup for codesigning
-include gnupg-vsd/sign.mk
$(stampdir)/stamp-msi-base: icons/shield.ico Makefile.am \
$(top_srcdir)/doc/logo/gpg4win-msi*.bmp \
$(top_srcdir)/po/gpg4win-de.wxl \
$(top_srcdir)/po/gpg4win-en.wxl
ssh $(WINHOST) "mkdir AppData\\Local\\Temp\\gpg4win-$(VERSION)" || true
scp $(top_srcdir)/packages/gnupg-msi-$(gpg4win_pkg_gnupg_msi_version)-bin.wixlib \
$(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION);
scp $(top_srcdir)/src/icons/shield.ico $(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)
scp $(top_srcdir)/doc/logo/gpg4win-msi-header_install-493x58.bmp \
$(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)/header.bmp
scp $(top_srcdir)/doc/logo/gpg4win-msi-wizard_install-493x312.bmp \
$(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)/dialog.bmp
scp $(top_srcdir)/doc/logo/gpg4win-msi-wizard_install-493x312.bmp \
$(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)/dialog.bmp
scp $(top_srcdir)/doc/logo/gpg4win-msi-wizard_install-info-32x32.bmp \
$(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)/info.bmp
scp $(top_srcdir)/doc/logo/gpg4win-msi-wizard_install-exclamation-32x32.bmp \
$(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)/exclamation.bmp
scp $(top_srcdir)/po/gpg4win-en.wxl $(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)
scp $(top_srcdir)/po/gpg4win-de.wxl $(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)
scp WixUI_Gpg4win.wxs $(WINHOST):AppData/Local/Temp/gpg4win-$(VERSION)
touch $(stampdir)/stamp-msi-base
# Now do the bunch of the work. This is a bunch of dirty magic to
# integrate our special makefile into automake, while not actually
# including it (make should include it). This is in turn necessary
# because automake can't deal with the elegant magic in the actual
# Makefile.
define INCLUDE_BY_MAKE
include $(1)
endef
if BUILD_GPG4WIN
$(eval $(call INCLUDE_BY_MAKE,gpg4win.mk))
gpg4win_clean = clean-gpg4win
endif
clean-local: $(gpg4win_clean)
common_nsi = inst-sections.nsi installer.nsi installer-finish.nsi \
$(addsuffix .nsi,$(addprefix inst-,$(gpg4win_build_list))) \
$(addsuffix .nsi,$(addprefix uninst-,$(gpg4win_build_list)))
# Sign additional files as per signing setup
# This is a bit of a hack.
$(stampdir)/stamp-additional-signedfiles: $(stampdir)/stamp-binaries
(set -e;\
cd "$(idir)"; \
for f in $(AUTHENTICODE_FILES); do \
if [ -f "$$f" ]; then \
$(call AUTHENTICODE_sign,"$$f","$$f");\
else \
echo "speedo: WARNING: file '$$f' not available for signing";\
fi;\
done \
)
touch $(stampdir)/stamp-additional-signedfiles
# Prepare the versioninfo file. The pipeline extracts the last
# occurrence of a package with the same name, sorts the entries and
# writes the file with DOS line endings. This helps to avoid
# duplicate entries in case one package has been rebuild (which is
# common when developing a new version).
versioninfo.txt:
$(SHA1SUM) $(pkg_files) versioninfo.tmp
set -e; \
( while read a b; do echo "$$a $$(basename $$b)"; \
done < versioninfo.tmp \
| sort -k2 -sf | tac | uniq -f1 ; \
echo '=========== SHA-1 checksum ============= == package ==' \
) | tac | awk '{printf "%s\r\n", $$0}' > versioninfo.txt
-rm versioninfo.tmp
NEWS.tmp : $(top_srcdir)/NEWS
awk '/^#/ {next} /^\(de\)/ {skip=1;next} /^[^[:space:]]/ {skip=0} \
!skip { sub(/^\(en\)/," *"); print }' \
<$(top_srcdir)/NEWS >NEWS.tmp
NEWS.de : $(top_srcdir)/NEWS
awk '/^#/ {next} /^\(en\)/ {skip=1;next} /^[^[:space:]]/ {skip=0} \
!skip { sub(/^\(de\)/,"-"); print }' \
<$(top_srcdir)/NEWS >NEWS.de; \
sed -i 's/^ / /' NEWS.de
NEWS.en : $(top_srcdir)/NEWS
awk '/^#/ {next} /^\(de\)/ {skip=1;next} /^[^[:space:]]/ {skip=0} \
!skip { sub(/^\(en\)/,"-"); print }' \
<$(top_srcdir)/NEWS >NEWS.en; \
sed -i 's/^ / /' NEWS.en
COMPONENTS-list.tmp : $(top_srcdir)/NEWS
awk '/^~~~~~~~~~~~/ { ok++; next} ok==1 {print " "$$0}' \
< $(top_srcdir)/NEWS > COMPONENTS-list.tmp
# For some nut-crazy reason someone thought it would be a great idea
# if makensis changed to the directory of the source file at startup.
# So we have to pull a couple of strings to correct this.
installers/gpg4win-$(VERSION).exe: gpg4win.nsi $(common_nsi) $(stampdir)/stamp-final \
g4wihelp.dll gpgwrap.exe \
$(foosum_exe) \
$(README_files) $(HOWTO_files) \
license.blurb versioninfo.txt
$(MAKENSIS) -V3 -DBUILD_DIR=`pwd` -DTOP_SRCDIR=$(top_srcdir) \
-DSRCDIR=$(srcdir) $(EXTRA_MAKENSIS_FLAGS) $(srcdir)/gpg4win.nsi && \
mv gpg4win-$(VERSION).exe installers/gpg4win-$(VERSION).exe
$(stampdir)/stamp-dist-self: versioninfo.txt
(set -e; cd ..; make dist-xz)
touch $(stampdir)/stamp-dist-self
installers/gpg4win-src-$(VERSION).exe: gpg4win-src.nsi $(common_nsii) \
$(stampdir)/stamp-final \
$(stampdir)/stamp-dist-self \
license.blurb
$(MAKENSIS) -V3 -DBUILD_DIR=`pwd` -DTOP_SRCDIR=$(top_srcdir) \
-DSRCDIR=$(srcdir) $(EXTRA_MAKENSIS_FLAGS) $(srcdir)/gpg4win-src.nsi && \
mv gpg4win-src-$(VERSION).exe installers/gpg4win-src-$(VERSION).exe
license.blurb: $(top_srcdir)/doc/license-page $(top_srcdir)/doc/GPLv3
cat $(top_srcdir)/doc/license-page $(top_srcdir)/doc/GPLv3 >$@
g4wihelp.dll: slideshow.cpp desktopshellrun.cpp g4wihelp.c exdll.h
- $(CC) -static-libgcc -I. -O2 -c -o desktopshellrun.o $(srcdir)/desktopshellrun.cpp
- $(CC) -static-libgcc -I. -O2 -c -o slideshow.o $(srcdir)/slideshow.cpp
- $(CC) -static-libgcc -I. -shared -O2 -o g4wihelp.dll $(srcdir)/g4wihelp.c \
- desktopshellrun.o slideshow.o -lwinmm -lgdi32 -luserenv -lshell32 \
+ $(CC) -DUNICODE -static-libgcc -I. -O2 -c -o exdll.o $(srcdir)/exdll.c
+ $(CC) -DUNICODE -static-libgcc -I. -O2 -c -o desktopshellrun.o $(srcdir)/desktopshellrun.cpp
+ $(CC) -DUNICODE -static-libgcc -I. -O2 -c -o slideshow.o $(srcdir)/slideshow.cpp
+ $(CC) -DUNICODE -static-libgcc -I. -shared -O2 -o g4wihelp.dll $(srcdir)/g4wihelp.c \
+ desktopshellrun.o slideshow.o exdll.o -lwinmm -lgdi32 -luserenv -lshell32 \
-lole32 -loleaut32 -lshlwapi -lmsimg32
$(STRIP) g4wihelp.dll
gpgwrap.exe: gpgwrap.c
$(CC) -I. -I.. -DHAVE_CONFIG_H -O2 -Wl,-subsystem,windows -o $@ $^
$(STRIP) $@
sha1sum.exe: sha1sum.c
$(CC) -O2 -o $@ $^
$(STRIP) $@
md5sum.exe: sha1sum.c
$(CC) -DBUILD_MD5SUM -O2 -o $@ $^
$(STRIP) $@
sha256sum.exe: sha1sum.c
$(CC) -DBUILD_SHA256SUM -O2 -o $@ $^
$(STRIP) $@
if BUILD_GPG4WIN
all_full = installers/gpg4win-$(VERSION).exe
endif
all-local: $(all_full)
if BUILD_GPG4WIN
@echo "###################################################"
@echo " Gpg4win $(VERSION) successfully build!"
@echo " Installers can be found under src/installers"
@echo "###################################################"
endif
clean-local:
rm -f installers/gpg4win-$(VERSION).exe \
installers/GnuPG-VS-Desktop-$(VERSION).msi
diff --git a/src/desktopshellrun.cpp b/src/desktopshellrun.cpp
index 9af54d21..69b6420b 100644
--- a/src/desktopshellrun.cpp
+++ b/src/desktopshellrun.cpp
@@ -1,296 +1,296 @@
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
* Copyright (C) 2016 Intevation GmbH
* Software engineering by Intevation GmbH
*
* This file is Free Software under the GNU GPL (v>=2)
* and comes with ABSOLUTELY NO WARRANTY!
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "exdll.h"
#ifndef INITGUID
#define INITGUID
#endif
/* Some declarations missing in mingw-w64 3.1.0 taken from msdn */
#if ! defined (__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 5
__CRT_UUID_DECL(IShellWindows, 0x85CB6900, 0x4D95, 0x11CF,
0x96, 0x0C, 0x00, 0x80, 0xC7, 0xF4, 0xEE, 0x85);
DEFINE_GUID(IID_IShellWindows,
0x85CB6900, 0x4D95, 0x11CF,
0x96, 0x0C, 0x00, 0x80, 0xC7, 0xF4, 0xEE, 0x85);
DEFINE_GUID(CLSID_ShellWindows,
0x9BA05972, 0xF6A8, 0x11CF,
0xA4, 0x42, 0x00, 0xA0, 0xC9, 0x0A, 0x8F, 0x39);
__CRT_UUID_DECL(IShellDispatch2, 0xA4C6892C, 0x3BA9, 0x11d2,
0x9D, 0xEA, 0x00, 0xC0, 0x4F, 0xB1, 0x61, 0x62);
__CRT_UUID_DECL(IShellFolderViewDual, 0xe7a1af80, 0x4d96,
0x11cf, 0x96, 0x0c, 0x00, 0x80, 0xc7, 0xf4, 0xee, 0x85);
#endif
#ifndef SWC_DESKTOP /* Will probably be addedd in future mingw */
#define SWC_DESKTOP 0x00000008
/* from http://msdn.microsoft.com/en-us/library/windows/desktop/cc836581%28v=vs.85%29.aspx */
#endif
#define UNUSED(x) (void)(x)
/** @brief the actual execuation call on the shell dispatcher
*
* @param[in] disp The shell dispatcher to use for shell execute.
* @param[in] fName The file that should be exectued.
* @param[in] param Optinal parameters to add.
*
* @returns true on success.
*/
static bool
shellexecute(IShellDispatch2 *disp, wchar_t *fName, wchar_t *param)
{
BSTR bName = NULL,
bParam = NULL,
bDir = NULL,
bOp = NULL;
VARIANT vParams[4];
HRESULT hr;
if (!fName || !disp)
{
ERRORPRINTF ("Invalid call to shellexecute.");
return false;
}
bName = SysAllocString(fName);
bParam = SysAllocString(param ? param : L"");
bDir = SysAllocString(L"");
bOp = SysAllocString(L"");
if (!bName || !bParam || !bDir || !bOp)
{
/* Out of memory */
ERRORPRINTF ("Failed to allocate bstr values ");
return false;
}
vParams[0].vt = VT_BSTR;
vParams[0].bstrVal = bParam;
vParams[1].vt = VT_BSTR;
vParams[1].bstrVal = bDir;
vParams[2].vt = VT_BSTR;
vParams[2].bstrVal = bOp;
vParams[3].vt = VT_INT;
vParams[3].intVal = SW_SHOWNORMAL;
hr = disp->ShellExecute(bName, vParams[0], vParams[1], vParams[2], vParams[3]);
SysFreeString(bName);
SysFreeString(bParam);
SysFreeString(bOp);
SysFreeString(bDir);
if (FAILED(hr))
{
ERRORPRINTF ("Failed to execute.");
return false;
}
return true;
}
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Execute a command with the current running shell.
*
* This function is intended to be called when you want to
* make sure that your application is not executed with higher
* privileges then the normal desktop session.
*
* The code is based on the idea:
* http://blogs.msdn.com/b/oldnewthing/archive/2013/11/18/10468726.aspx
*
* The function signature is explained by NSIS.
*/
void __declspec(dllexport) __cdecl DesktopShellRun(HWND hwndParent,
int string_size,
char *variables,
stack_t **stacktop)
{
UNUSED(hwndParent);
UNUSED(string_size);
HRESULT hr;
wchar_t *wbuf = NULL;
IShellWindows *shellWindows = NULL;
IShellBrowser *shellBrowser = NULL;
IShellView *shellView = NULL;
IShellFolderViewDual *folderView = NULL;
IShellDispatch2 *shellDispatch = NULL;
IServiceProvider *serviceProv = NULL;
HWND hwnd;
IDispatch *disp = NULL,
*bgDisp = NULL,
*sDisp = NULL;
VARIANT vEmpty = {};
if (!stacktop || !*stacktop || !(*stacktop)->text)
{
ERRORPRINTF ("Invalid call to exec :");
return;
}
/* Initialize com ctx */
hr = CoInitialize(NULL);
if(FAILED(hr))
{
ERRORPRINTF ("CoInitializeEx failed. error = 0x%lx.", hr);
return;
}
/* Get the shell interface */
hr = CoCreateInstance(CLSID_ShellWindows,
NULL, CLSCTX_LOCAL_SERVER,
IID_PPV_ARGS(&shellWindows));
if (FAILED(hr))
{
ERRORPRINTF ("Failed to get shell interface.");
goto done;
}
/* Get the desktop shell window */
hr = shellWindows->FindWindowSW(&vEmpty,
&vEmpty,
SWC_DESKTOP,
(long*)&hwnd,
SWFO_NEEDDISPATCH,
&disp);
if (FAILED(hr))
{
ERRORPRINTF ("Failed to find the desktop dispatcher.");
goto done;
}
hr = disp->QueryInterface(IID_PPV_ARGS(&serviceProv));
if (FAILED(hr))
{
ERRORPRINTF ("Failed to get the service provider.");
goto done;
}
/* Get the shell browser */
hr = serviceProv->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shellBrowser));
if (FAILED(hr))
{
ERRORPRINTF ("Failed to find the top level browser.");
goto done;
}
hr = shellBrowser->QueryActiveShellView(&shellView);
if (FAILED(hr))
{
ERRORPRINTF ("Failed to find the active view.");
goto done;
}
hr = shellView->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&bgDisp));
if (FAILED(hr))
{
ERRORPRINTF ("Failed to get the views background.");
goto done;
}
hr = bgDisp->QueryInterface(IID_PPV_ARGS(&folderView));
if (FAILED(hr))
{
ERRORPRINTF ("Failed to get the folder view.");
goto done;
}
hr = folderView->get_Application(&sDisp);
if (FAILED(hr))
{
ERRORPRINTF ("Failed to get the shell dispatch.");
goto done;
}
hr = sDisp->QueryInterface(IID_PPV_ARGS(&shellDispatch));
if (FAILED(hr))
{
ERRORPRINTF ("Failed to get the shell dispatch interface.");
goto done;
}
/* For unicodensis this has to be utf8 to wchar */
- wbuf = acp_to_wchar((*stacktop)->text, strlen((*stacktop)->text));
+ wbuf = wcsdup ((*stacktop)->text);
if (!wbuf)
{
ERRORPRINTF ("Failed to convert argument to wchar. error = 0x%lx.", hr);
goto done;
}
if (!shellexecute(shellDispatch, wbuf, NULL))
{
ERRORPRINTF ("Failed to execute.");
}
if (wbuf)
free(wbuf);
done:
if (folderView)
{
folderView->Release();
}
if (disp)
{
disp->Release();
}
if (shellBrowser)
{
shellBrowser->Release();
}
if (shellWindows)
{
shellWindows->Release();
}
if (shellView)
{
shellView->Release();
}
if (sDisp)
{
sDisp->Release();
}
if (shellDispatch)
{
shellDispatch->Release();
}
if (serviceProv)
{
serviceProv->Release();
}
CoUninitialize();
return;
}
#ifdef __cplusplus
}
#endif
diff --git a/src/exdll.h b/src/exdll.h
index 0c36f28a..91e082f9 100644
--- a/src/exdll.h
+++ b/src/exdll.h
@@ -1,189 +1,162 @@
-/* exdll.h for use with gpg4win
- * Copyright (C) 1999-2005 Nullsoft, Inc.
- *
- * This license applies to everything in the NSIS package, except
- * where otherwise noted.
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any
- * damages arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any
- * purpose, including commercial applications, and to alter it and
- * redistribute it freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must
- * not claim that you wrote the original software. If you use this
- * software in a product, an acknowledgment in the product
- * documentation would be appreciated but is not required.
- *
- * 2. Altered source versions must be plainly marked as such, and must
- * not be misrepresented as being the original software.
- *
- * 3. This notice may not be removed or altered from any source
- * distribution.
- ************************************************************
- * 2005-11-14 wk Applied license text to orginal exdll.h file from
- * NSIS 2.0.4 and did some formatting changes.
- */
-
-#ifndef _EXDLL_H_
-#define _EXDLL_H_
-
-/* only include this file from one place in your DLL. (it is all
- static, if you use it in two places it will fail) */
+#include
+#ifndef ___NSIS_PLUGIN__H___
+#define ___NSIS_PLUGIN__H___
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef NSISCALL
+# define NSISCALL __stdcall
+#endif
+#if !defined(_WIN32) && !defined(LPTSTR)
+# define LPTSTR TCHAR*
+#endif
+
+
+
+#ifndef NSISCALL
+# define NSISCALL WINAPI
+#endif
#define EXDLL_INIT() { \
g_stringsize=string_size; \
g_stacktop=stacktop; \
g_variables=variables; }
-/* For page showing plug-ins */
-#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8)
-#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd)
-#define NOTIFY_BYE_BYE 'x'
-
typedef struct _stack_t {
struct _stack_t *next;
- char text[1]; /* This should be the length of string_size. */
+#ifdef UNICODE
+ WCHAR text[1]; // this should be the length of g_stringsize when allocating
+#else
+ char text[1];
+#endif
} stack_t;
-
-static unsigned int g_stringsize;
-static stack_t **g_stacktop;
-static char *g_variables;
-
-static int __stdcall popstring(char *str, size_t maxlen); /* 0 on success, 1 on empty stack */
-static void __stdcall pushstring(const char *str);
-
-enum
- {
- INST_0, // $0
- INST_1, // $1
- INST_2, // $2
- INST_3, // $3
- INST_4, // $4
- INST_5, // $5
- INST_6, // $6
- INST_7, // $7
- INST_8, // $8
- INST_9, // $9
- INST_R0, // $R0
- INST_R1, // $R1
- INST_R2, // $R2
- INST_R3, // $R3
- INST_R4, // $R4
- INST_R5, // $R5
- INST_R6, // $R6
- INST_R7, // $R7
- INST_R8, // $R8
- INST_R9, // $R9
- INST_CMDLINE, // $CMDLINE
- INST_INSTDIR, // $INSTDIR
- INST_OUTDIR, // $OUTDIR
- INST_EXEDIR, // $EXEDIR
- INST_LANG, // $LANGUAGE
- __INST_LAST
-};
-
typedef struct {
int autoclose;
int all_user_var;
int exec_error;
int abort;
int exec_reboot;
int reboot_called;
int XXX_cur_insttype; /* deprecated */
int XXX_insttype_changed; /* deprecated */
int silent;
int instdir_error;
int rtl;
int errlvl;
} exec_flags_t;
typedef struct {
exec_flags_t *exec_flags;
int (__stdcall *ExecuteCodeSegment)(int, HWND);
} extra_parameters_t;
-/* Utility functions (not required but often useful). */
-static int __stdcall
-popstring(char *str, size_t maxlen)
+enum
{
- stack_t *th;
- if (!g_stacktop || !*g_stacktop)
- return 1;
- th=(*g_stacktop);
- lstrcpyn (str, th->text, maxlen);
- *g_stacktop = th->next;
- GlobalFree((HGLOBAL)th);
- return 0;
-}
+INST_0, // $0
+INST_1, // $1
+INST_2, // $2
+INST_3, // $3
+INST_4, // $4
+INST_5, // $5
+INST_6, // $6
+INST_7, // $7
+INST_8, // $8
+INST_9, // $9
+INST_R0, // $R0
+INST_R1, // $R1
+INST_R2, // $R2
+INST_R3, // $R3
+INST_R4, // $R4
+INST_R5, // $R5
+INST_R6, // $R6
+INST_R7, // $R7
+INST_R8, // $R8
+INST_R9, // $R9
+INST_CMDLINE, // $CMDLINE
+INST_INSTDIR, // $INSTDIR
+INST_OUTDIR, // $OUTDIR
+INST_EXEDIR, // $EXEDIR
+INST_LANG, // $LANGUAGE
+__INST_LAST
+};
-static void __stdcall
-pushstring(const char *str)
-{
- stack_t *th;
- if (!g_stacktop) return;
- th=(stack_t*)GlobalAlloc(GPTR,sizeof(stack_t)+g_stringsize);
- lstrcpyn(th->text,str,g_stringsize);
- th->next=*g_stacktop;
- *g_stacktop=th;
+extern unsigned int g_stringsize;
+extern stack_t **g_stacktop;
+extern LPTSTR g_variables;
+
+void NSISCALL pushstring(LPCTSTR str);
+void NSISCALL pushintptr(INT_PTR value);
+#define pushint(v) pushintptr((INT_PTR)(v))
+int NSISCALL popstring(LPTSTR str); // 0 on success, 1 on empty stack
+int NSISCALL popstringn(LPTSTR str, int maxlen); // with length limit, pass 0 for g_stringsize
+INT_PTR NSISCALL popintptr();
+#define popint() ( (int) popintptr() )
+int NSISCALL popint_or(); // with support for or'ing (2|4|8)
+INT_PTR NSISCALL nsishelper_str_to_ptr(LPCTSTR s);
+#define myatoi(s) ( (int) nsishelper_str_to_ptr(s) ) // converts a string to an integer
+unsigned int NSISCALL myatou(LPCTSTR s); // converts a string to an unsigned integer, decimal only
+int NSISCALL myatoi_or(LPCTSTR s); // with support for or'ing (2|4|8)
+LPTSTR NSISCALL getuservariable(const int varnum);
+void NSISCALL setuservariable(const int varnum, LPCTSTR var);
+
+#ifdef UNICODE
+#define PopStringW(x) popstring(x)
+#define PushStringW(x) pushstring(x)
+#define SetUserVariableW(x,y) setuservariable(x,y)
+
+int NSISCALL PopStringA(LPSTR ansiStr);
+void NSISCALL PushStringA(LPCSTR ansiStr);
+void NSISCALL GetUserVariableW(const int varnum, LPWSTR wideStr);
+void NSISCALL GetUserVariableA(const int varnum, LPSTR ansiStr);
+void NSISCALL SetUserVariableA(const int varnum, LPCSTR ansiStr);
+
+#else
+// ANSI defs
+
+#define PopStringA(x) popstring(x)
+#define PushStringA(x) pushstring(x)
+#define SetUserVariableA(x,y) setuservariable(x,y)
+
+int NSISCALL PopStringW(LPWSTR wideStr);
+void NSISCALL PushStringW(LPWSTR wideStr);
+void NSISCALL GetUserVariableW(const int varnum, LPWSTR wideStr);
+void NSISCALL GetUserVariableA(const int varnum, LPSTR ansiStr);
+void NSISCALL SetUserVariableW(const int varnum, LPCWSTR wideStr);
+
+#endif
+
+#ifdef __cplusplus
}
+#endif
-static char * __stdcall
-getuservariable(const int varnum)
-{
- if (varnum < 0 || varnum >= __INST_LAST) return NULL;
- return g_variables+varnum*g_stringsize;
-}
+#endif//!___NSIS_PLUGIN__H___
-static void __stdcall
-setuservariable(const int varnum, const char *var)
-{
- if (var != NULL && varnum >= 0 && varnum < __INST_LAST)
- lstrcpy(g_variables + varnum*g_stringsize, var);
-}
+#ifndef COUNTOF
+#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
+#endif
+
+// minimal tchar.h emulation
+#ifndef _T
+# define _T TEXT
+#endif
+#if !defined(TCHAR) && !defined(_TCHAR_DEFINED)
+# ifdef UNICODE
+# define TCHAR WCHAR
+# else
+# define TCHAR char
+# endif
+#endif
+
+#define isvalidnsisvarindex(varnum) ( ((unsigned int)(varnum)) < (__INST_LAST) )
#define ERRORPRINTF(fmt, ...) \
{ \
char buf[512]; \
snprintf(buf, 511, "ERROR: " fmt, ##__VA_ARGS__); \
buf[511] = '\0'; \
OutputDebugStringA(buf); \
}
-
-static wchar_t
-*acp_to_wchar (const char *string, size_t len)
-{
- int n, ilen;
- wchar_t *result;
-
- ilen = (int) len;
- if (ilen < 0)
- return NULL;
-
- n = MultiByteToWideChar (CP_ACP, 0, string, ilen, NULL, 0);
- if (n < 0 || n + 1 < 0)
- return NULL;
-
- result = (wchar_t *) malloc ((size_t)(n+1) * sizeof *result);
- if (!result)
- {
- ERRORPRINTF("Out of core");
- exit(1);
- }
- n = MultiByteToWideChar (CP_ACP, 0, string, ilen, result, n);
- if (n < 0)
- {
- if (result)
- free (result);
- return NULL;
- }
- result[n] = 0;
- return result;
-}
-
-
-#endif//_EXDLL_H_
diff --git a/src/g4wihelp.c b/src/g4wihelp.c
index ddb7eb59..2a65142e 100644
--- a/src/g4wihelp.c
+++ b/src/g4wihelp.c
@@ -1,1029 +1,519 @@
/* g4wihelp.c - NSIS Helper DLL used with gpg4win. -*- coding: latin-1; -*-
* Copyright (C) 2005 g10 Code GmbH
* Copyright (C) 2001 Justin Frankel
* Copyright (C) 2016, 2017 Intevation GmbH
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must
* not claim that you wrote the original software. If you use this
* software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must
* not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
************************************************************
* The code for the splash screen has been taken from the Splash
* plugin of the NSIS 2.04 distribution. That code comes without
* explicit copyright notices in tyhe source files or author names, it
* seems that it has been written by Justin Frankel; not sure about
* the year, though. [wk 2005-11-28]
*/
#include
#include
#include
#include
#include "exdll.h"
static HINSTANCE g_hInstance; /* Our Instance. */
static HWND g_hwndParent; /* Handle of parent window or NULL. */
static HBITMAP g_hbm; /* Handle of the splash image. */
static int sleepint; /* Milliseconds to show the spals image. */
void
slide_stop(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop);
/* Standard entry point for DLLs. */
int WINAPI
DllMain (HANDLE hinst, DWORD reason, LPVOID reserved)
{
if (reason == DLL_PROCESS_ATTACH)
g_hInstance = hinst;
else if (reason == DLL_PROCESS_DETACH)
slide_stop(NULL, 0, NULL, NULL);
return TRUE;
}
/* Dummy function for testing. */
void __declspec(dllexport)
dummy (HWND hwndParent, int string_size, char *variables,
stack_t **stacktop, extra_parameters_t *extra)
{
g_hwndParent = hwndParent;
EXDLL_INIT();
// note if you want parameters from the stack, pop them off in order.
// i.e. if you are called via exdll::myFunction file.dat poop.dat
// calling popstring() the first time would give you file.dat,
// and the second time would give you poop.dat.
// you should empty the stack of your parameters, and ONLY your
// parameters.
// do your stuff here
{
char buf[1024];
sprintf(buf,"$R0=%s\r\n$R1=%s\r\n",
getuservariable(INST_R0),
getuservariable(INST_R1));
MessageBox(g_hwndParent,buf,0,MB_OK);
sprintf (buf,
"autoclose =%d\r\n"
"all_user_var =%d\r\n"
"exec_error =%d\r\n"
"abort =%d\r\n"
"exec_reboot =%d\r\n"
"reboot_called=%d\r\n"
"silent =%d\r\n"
"instdir_error=%d\r\n"
"rtl =%d\r\n"
"errlvl =%d\r\n",
extra->exec_flags->autoclose,
extra->exec_flags->all_user_var,
extra->exec_flags->exec_error,
extra->exec_flags->abort,
extra->exec_flags->exec_reboot,
extra->exec_flags->reboot_called,
extra->exec_flags->silent,
extra->exec_flags->instdir_error,
extra->exec_flags->rtl,
extra->exec_flags->errlvl);
MessageBox(g_hwndParent,buf,0,MB_OK);
}
}
void __declspec(dllexport)
runonce (HWND hwndParent, int string_size, char *variables,
stack_t **stacktop, extra_parameters_t *extra)
{
- const char *result;
+ LPCTSTR result;
g_hwndParent = hwndParent;
EXDLL_INIT();
- CreateMutexA (NULL, 0, getuservariable(INST_R0));
- result = GetLastError ()? "1":"0";
+ CreateMutexA (NULL, 0, "gpg4win");
+ result = GetLastError ()? L"1" : L"0";
+ OutputDebugStringA ("Runonce returns:");
+ OutputDebugStringW (result);
setuservariable (INST_R0, result);
}
-void __declspec(dllexport)
-playsound (HWND hwndParent, int string_size, char *variables,
- stack_t **stacktop, extra_parameters_t *extra)
-{
- char fname[MAX_PATH];
-
- g_hwndParent = hwndParent;
- EXDLL_INIT();
-
- if (popstring(fname, sizeof fname))
- return;
- PlaySound (fname, NULL, SND_ASYNC|SND_FILENAME|SND_NODEFAULT);
-}
-
-
-void __declspec(dllexport)
-stopsound (HWND hwndParent, int string_size, char *variables,
- stack_t **stacktop, extra_parameters_t *extra)
-{
- g_hwndParent = hwndParent;
- EXDLL_INIT();
- PlaySound (NULL, NULL, 0);
-}
-
-
-/* Windows procedure to control the splashimage. This one pauses the
- execution until the sleep time is over or the user closes this
- windows. */
-static LRESULT CALLBACK
-splash_wndproc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- LRESULT result = 0;
-
- switch (uMsg)
- {
- case WM_CREATE:
- {
- BITMAP bm;
- RECT vp;
-
- GetObject(g_hbm, sizeof(bm), (LPSTR)&bm);
- SystemParametersInfo(SPI_GETWORKAREA, 0, &vp, 0);
- SetWindowLong(hwnd,GWL_STYLE,0);
- SetWindowPos(hwnd,NULL,
- vp.left+(vp.right-vp.left-bm.bmWidth)/2,
- vp.top+(vp.bottom-vp.top-bm.bmHeight)/2,
- bm.bmWidth,bm.bmHeight,
- SWP_NOZORDER);
- ShowWindow(hwnd,SW_SHOW);
- SetTimer(hwnd,1,sleepint,NULL);
- }
- break;
-
- case WM_PAINT:
- {
- PAINTSTRUCT ps;
- RECT r;
- HDC curdc=BeginPaint(hwnd,&ps);
- HDC hdc=CreateCompatibleDC(curdc);
- HBITMAP oldbm;
- GetClientRect(hwnd,&r);
- oldbm=(HBITMAP)SelectObject(hdc,g_hbm);
- BitBlt(curdc,r.left,r.top,r.right-r.left,r.bottom-r.top,
- hdc,0,0,SRCCOPY);
- SelectObject(hdc,oldbm);
- DeleteDC(hdc);
- EndPaint(hwnd,&ps);
- }
- break;
-
- case WM_CLOSE:
- break;
-
- case WM_TIMER:
- case WM_LBUTTONDOWN:
- DestroyWindow(hwnd);
- /*(fall through)*/
- default:
- result = DefWindowProc (hwnd, uMsg, wParam, lParam);
- }
-
- return result;
-}
-
-
-/* Display a splash screen. Call as
-
- g4wihelp::showsplash SLEEP FNAME
-
- With SLEEP being the time in milliseconds to show the splashscreen
- and FNAME the complete filename of the image. As of now only BMP
- is supported.
-*/
-void __declspec(dllexport)
-showsplash (HWND hwndParent, int string_size, char *variables,
- stack_t **stacktop, extra_parameters_t *extra)
-{
- static WNDCLASS wc;
- char sleepstr[30];
- char fname[MAX_PATH];
- int err = 0;
- char *p;
- char classname[] = "_sp";
-
- g_hwndParent = hwndParent;
- EXDLL_INIT();
- if (popstring(sleepstr, sizeof sleepstr))
- err = 1;
- if (popstring(fname, sizeof fname))
- err = 1;
- if (err)
- return;
-
- if (!*fname)
- return; /* Nothing to do. */
-
- for (sleepint=0, p=sleepstr; *p >= '0' && *p <= '9'; p++)
- {
- sleepint *= 10;
- sleepint += *p - '0';
- }
- if (sleepint <= 0)
- return; /* Nothing to do. */
-
- wc.lpfnWndProc = splash_wndproc;
- wc.hInstance = g_hInstance;
- wc.hCursor = LoadCursor(NULL,IDC_ARROW);
- wc.lpszClassName = classname;
- if (!RegisterClass(&wc))
- return; /* Error. */
-
- g_hbm = LoadImage (NULL, fname, IMAGE_BITMAP,
- 0, 0 , LR_CREATEDIBSECTION|LR_LOADFROMFILE);
- if (g_hbm)
- {
- MSG msg;
- HWND hwnd;
-
- hwnd = CreateWindowEx (WS_EX_TOOLWINDOW, classname, classname,
- 0, 0, 0, 0, 0, (HWND)hwndParent, NULL,
- g_hInstance, NULL);
-
- while (IsWindow(hwnd) && GetMessage ( &msg, hwnd, 0, 0))
- {
- DispatchMessage (&msg);
- }
-
- DeleteObject (g_hbm);
- g_hbm = NULL;
- }
- UnregisterClass (classname, g_hInstance);
-}
-
-
-/* Service Management. */
-
-/* Use this to report unexpected errors. FIXME: This is really not
- very descriptive. */
-void
-service_error (const char *str)
-{
- char buf[1024];
- snprintf (buf, sizeof (buf) - 1, "error: %s: ec=%d\r\n", str,
- GetLastError ());
- MessageBox(g_hwndParent, buf, 0, MB_OK);
-
- setuservariable (INST_R0, "1");
-}
-
-
-void __declspec(dllexport)
-service_create (HWND hwndParent, int string_size, char *variables,
- stack_t **stacktop, extra_parameters_t *extra)
-{
- SC_HANDLE sc;
- SC_HANDLE service;
- const char *result = NULL;
- char service_name[256];
- char display_name[256];
- char program[256];
- int err = 0;
-
- g_hwndParent = hwndParent;
- EXDLL_INIT();
-
- /* The expected stack layout: service_name, display_name, program. */
- if (popstring (service_name, sizeof (service_name)))
- err = 1;
- if (!err && popstring (display_name, sizeof (display_name)))
- err = 1;
- if (!err && popstring (program, sizeof (program)))
- err = 1;
- if (err)
- {
- setuservariable (INST_R0, "1");
- return;
- }
-
- sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
- if (sc == NULL)
- {
- service_error ("OpenSCManager");
- return;
- }
-
- service = CreateService (sc, service_name, display_name,
- SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
- /* Use SERVICE_DEMAND_START for testing.
- FIXME: Currently not configurable by caller. */
- SERVICE_AUTO_START,
- SERVICE_ERROR_NORMAL, program,
- NULL, NULL, NULL,
- /* FIXME: Currently not configurable by caller. */
- /* FIXME: LocalService or NetworkService
- don't work for dirmngr right now. NOTE!
- If you change it here, you also should
- adjust make-msi.pl for the msi
- installer. In the future, this should
- be an argument to the function and then
- the make-msi.pl script can extract it
- from the invocation. */
- NULL /* "NT AUTHORITY\\LocalService" */,
- NULL);
- if (service == NULL)
- {
- service_error ("CreateService");
- CloseServiceHandle (sc);
- return;
- }
- CloseServiceHandle (service);
-
- result = GetLastError () ? "1":"0";
- setuservariable (INST_R0, result);
- return;
-}
-
-
-/* Requires g_hwndParent to be set! */
-SC_HANDLE
-service_lookup (char *service_name)
-{
- SC_HANDLE sc;
- SC_HANDLE service;
-
- sc = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
- if (sc == NULL)
- {
- service_error ("OpenSCManager");
- return NULL;
- }
- service = OpenService (sc, service_name, SC_MANAGER_ALL_ACCESS);
- if (service == NULL)
- {
- /* Fail silently here. */
- CloseServiceHandle (sc);
- return NULL;
- }
- CloseServiceHandle (sc);
- return service;
-}
-
-
-/* Returns status. */
-void __declspec(dllexport)
-service_query (HWND hwndParent, int string_size, char *variables,
- stack_t **stacktop, extra_parameters_t *extra)
-{
- SC_HANDLE service;
- const char *result = NULL;
- char service_name[256];
- int err = 0;
- SERVICE_STATUS status;
-
- g_hwndParent = hwndParent;
- EXDLL_INIT();
-
- /* The expected stack layout: service_name argc [argv]. */
- if (popstring (service_name, sizeof (service_name)))
- err = 1;
- if (err)
- {
- setuservariable (INST_R0, "ERROR");
- return;
- }
-
- service = service_lookup (service_name);
- if (service == NULL)
- if (err == 0)
- {
- setuservariable (INST_R0, "MISSING");
- return;
- }
-
- err = QueryServiceStatus (service, &status);
- if (err == 0)
- {
- setuservariable (INST_R0, "ERROR");
- CloseServiceHandle (service);
- return;
- }
- CloseServiceHandle (service);
-
- switch (status.dwCurrentState)
- {
- case SERVICE_START_PENDING:
- result = "START_PENDING";
- break;
- case SERVICE_RUNNING:
- result = "RUNNING";
- break;
- case SERVICE_PAUSE_PENDING:
- result = "PAUSE_PENDING";
- break;
- case SERVICE_PAUSED:
- result = "PAUSED";
- break;
- case SERVICE_CONTINUE_PENDING:
- result = "CONTINUE_PENDING";
- break;
- case SERVICE_STOP_PENDING:
- result = "STOP_PENDING";
- break;
- case SERVICE_STOPPED:
- result = "STOPPED";
- break;
- default:
- result = "UNKNOWN";
- }
- setuservariable (INST_R0, result);
- return;
-}
-
-
-void __declspec(dllexport)
-service_start (HWND hwndParent, int string_size, char *variables,
- stack_t **stacktop, extra_parameters_t *extra)
-{
- SC_HANDLE service;
- const char *result = NULL;
- char service_name[256];
- char argc_str[256];
-#define NR_ARGS 10
-#define ARG_MAX 256
- char argv_str[NR_ARGS][ARG_MAX];
- char *argv[NR_ARGS + 1];
- int argc;
- int i;
- int err = 0;
-
- g_hwndParent = hwndParent;
- EXDLL_INIT();
-
- /* The expected stack layout: service_name argc [argv]. */
- if (popstring (service_name, sizeof (service_name)))
- err = 1;
- if (!err && popstring (argc_str, sizeof (argc_str)))
- err = 1;
- if (!err)
- {
- argc = atoi (argc_str);
- for (i = 0; i < argc; i++)
- {
- if (popstring (argv_str[i], ARG_MAX))
- {
- err = 1;
- break;
- }
- argv[i] = argv_str[i];
- }
- argv[i] = NULL;
- }
- if (err)
- {
- setuservariable (INST_R0, "1");
- return;
- }
-
- service = service_lookup (service_name);
- if (service == NULL)
- return;
-
- err = StartService (service, argc, argc == 0 ? NULL : argv);
- if (err == 0)
- {
- service_error ("StartService");
- CloseServiceHandle (service);
- return;
- }
- CloseServiceHandle (service);
-
- setuservariable (INST_R0, "0");
- return;
-}
-
-
-void __declspec(dllexport)
-service_stop (HWND hwndParent, int string_size, char *variables,
- stack_t **stacktop, extra_parameters_t *extra)
-{
- SC_HANDLE service;
- const char *result = NULL;
- char service_name[256];
- int err = 0;
- SERVICE_STATUS status;
- DWORD timeout = 10000; /* 10 seconds. */
- DWORD start_time;
-
- g_hwndParent = hwndParent;
- EXDLL_INIT();
-
- /* The expected stack layout: service_name argc [argv]. */
- if (popstring (service_name, sizeof (service_name)))
- err = 1;
- if (err)
- {
- setuservariable (INST_R0, "1");
- return;
- }
-
- service = service_lookup (service_name);
- if (service == NULL)
- return;
-
- err = QueryServiceStatus (service, &status);
- if (err == 0)
- {
- service_error ("QueryService");
- CloseServiceHandle (service);
- return;
- }
-
- if (status.dwCurrentState != SERVICE_STOPPED
- && status.dwCurrentState != SERVICE_STOP_PENDING)
- {
- err = ControlService (service, SERVICE_CONTROL_STOP, &status);
- if (err == 0)
- {
- service_error ("ControlService");
- CloseServiceHandle (service);
- return;
- }
- }
-
- start_time = GetTickCount ();
- while (status.dwCurrentState != SERVICE_STOPPED)
- {
- Sleep (1000); /* One second. */
- if (!QueryServiceStatus (service, &status))
- {
- service_error ("QueryService");
- CloseServiceHandle (service);
- return;
- }
- if (status.dwCurrentState == SERVICE_STOPPED)
- break;
-
- if (GetTickCount () - start_time > timeout)
- {
- char buf[1024];
- snprintf (buf, sizeof (buf) - 1,
- "time out waiting for service %s to stop\r\n",
- service_name);
- MessageBox (g_hwndParent, buf, 0, MB_OK);
- setuservariable (INST_R0, "1");
- return;
- }
- }
- CloseServiceHandle (service);
- setuservariable (INST_R0, "0");
- return;
-}
-
-
-void __declspec(dllexport)
-service_delete (HWND hwndParent, int string_size, char *variables,
- stack_t **stacktop, extra_parameters_t *extra)
-{
- SC_HANDLE service;
- const char *result = NULL;
- char service_name[256];
- int err = 0;
-
- g_hwndParent = hwndParent;
- EXDLL_INIT();
-
- /* The expected stack layout: service_name argc [argv]. */
- if (popstring (service_name, sizeof (service_name)))
- err = 1;
- if (err)
- {
- setuservariable (INST_R0, "1");
- return;
- }
-
- service = service_lookup (service_name);
- if (service == NULL)
- return;
-
- err = DeleteService (service);
- if (err == 0)
- {
- service_error ("DeleteService");
- CloseServiceHandle (service);
- return;
- }
- CloseServiceHandle (service);
-
- setuservariable (INST_R0, "0");
- return;
-}
-
-
#include
/* Extract config file parameters. FIXME: Not particularly robust.
We expect some reasonable formatting. The parser below is very
limited. It expects a command line option /c=FILE or /C=FILE,
where FILE must be enclosed in double-quotes if it contains spaces.
That file should contain a single section [gpg4win] and KEY=VALUE
pairs for each additional configuration file to install. Comments
are supported only on lines by themselves. VALUE can be quoted in
double-quotes, but does not need to be, unless it has whitespace at
the beginning or end. KEY can, for example, be "gpg.conf" (without
the quotes). */
void
config_init (char **keys, char **values, int max)
{
/* First, parse the command line. */
char *cmdline;
char *begin = NULL;
char *end = NULL;
char mark;
char *fname;
char *ptr;
FILE *conf;
*keys = NULL;
*values = NULL;
cmdline = getuservariable (INST_CMDLINE);
mark = (*cmdline == '"') ? (cmdline++, '"') : ' ';
while (*cmdline && *cmdline != mark)
cmdline++;
if (mark == '"' && *cmdline)
cmdline++;
while (*cmdline && *cmdline == ' ')
cmdline++;
while (*cmdline)
{
/* We are at the beginning of a new argument. */
if (cmdline[0] == '/' && (cmdline[1] == 'C' || cmdline[1] == 'c')
&& cmdline[2] == '=')
{
cmdline += 3;
begin = cmdline;
}
while (*cmdline && *cmdline != ' ')
{
/* Skip over quoted parts. */
if (*cmdline == '"')
{
cmdline++;
while (*cmdline && *cmdline != '"')
cmdline++;
if (*cmdline)
cmdline++;
}
else
cmdline++;
}
if (begin && !end)
{
end = cmdline - 1;
break;
}
while (*cmdline && *cmdline == ' ')
cmdline++;
}
if (!begin || begin > end)
return;
/* Strip quotes. */
if (*begin == '"' && *end == '"')
{
begin++;
end--;
}
if (begin > end)
return;
fname = malloc (end - begin + 2);
if (!fname)
return;
ptr = fname;
while (begin <= end)
*(ptr++) = *(begin++);
*ptr = '\0';
conf = fopen (fname, "r");
free (fname);
if (!conf)
return;
while (max - 1 > 0)
{
char line[256];
char *ptr2;
if (fgets (line, sizeof (line), conf) == NULL)
break;
ptr = &line[strlen (line)];
while (ptr > line && (ptr[-1] == '\n' || ptr[-1] == '\r'
|| ptr[-1] == ' ' || ptr[-1] == '\t'))
ptr--;
*ptr = '\0';
ptr = line;
while (*ptr && (*ptr == ' ' || *ptr == '\t'))
ptr++;
/* Ignore comment lines. */
/* FIXME: Ignore section markers. */
if (*ptr == '\0' || *ptr == ';' || *ptr == '[')
continue;
begin = ptr;
while (*ptr && *ptr != '=' && *ptr != ' ' && *ptr != '\t')
ptr++;
end = ptr - 1;
while (*ptr && (*ptr == ' ' || *ptr == '\t'))
ptr++;
if (*ptr != '=')
continue;
ptr++;
if (begin > end)
continue;
/* We found a key. */
*keys = malloc (end - begin + 2);
if (!keys)
return;
ptr2 = *keys;
while (begin <= end)
*(ptr2++) = *(begin++);
*ptr2 = '\0';
*values = NULL;
while (*ptr && (*ptr == ' ' || *ptr == '\t'))
ptr++;
begin = ptr;
/* In this case, end points to the byte after the value, which
is OK because that is '\0'. */
end = &line[strlen (line)];
if (begin > end)
begin = end;
/* Strip quotes. */
if (*begin == '"' && end[-1] == '"')
{
begin++;
end--;
*end = '\0';
}
if (begin > end)
return;
*values = malloc (end - begin + 1);
ptr2 = *values;
while (begin <= end)
*(ptr2++) = *(begin++);
keys++;
values++;
max--;
}
fclose (conf);
*keys = NULL;
*values = NULL;
}
char *
config_lookup (char *key)
{
#define MAX_KEYS 128
static int initialised = 0;
static char *keys[MAX_KEYS];
static char *values[MAX_KEYS];
int i;
if (initialised == 0)
{
initialised = 1;
config_init (keys, values, MAX_KEYS);
#if 0
MessageBox(g_hwndParent, "Configuration File:", 0, MB_OK);
i = 0;
while (keys[i])
{
char buf[256];
sprintf (buf, "%s=%s\r\n", keys[i], values[i]);
MessageBox (g_hwndParent, buf, 0, MB_OK);
i++;
}
#endif
}
i = 0;
while (keys[i])
{
if (!strcmp (keys[i], key))
return values[i];
i++;
}
return NULL;
}
void __declspec(dllexport)
config_fetch (HWND hwndParent, int string_size, char *variables,
stack_t **stacktop, extra_parameters_t *extra)
{
char key[256];
int err = 0;
char *value;
g_hwndParent = hwndParent;
EXDLL_INIT();
/* The expected stack layout: key. */
- if (popstring (key, sizeof (key)))
+ if (popstringn (key, sizeof (key)))
err = 1;
if (err)
{
setuservariable (INST_R0, "");
return;
}
value = config_lookup (key);
setuservariable (INST_R0, value == NULL ? "" : value);
return;
}
void __declspec(dllexport)
config_fetch_bool (HWND hwndParent, int string_size, char *variables,
stack_t **stacktop, extra_parameters_t *extra)
{
char key[256];
int err = 0;
char *value;
int result;
g_hwndParent = hwndParent;
EXDLL_INIT();
/* The expected stack layout: key. */
- if (popstring (key, sizeof (key)))
+ if (popstringn (key, sizeof (key)))
err = 1;
if (err)
{
setuservariable (INST_R0, "");
return;
}
value = config_lookup (key);
if (value == NULL || *value == '\0')
{
setuservariable (INST_R0, "");
return;
}
result = 0;
if (!strcasecmp (value, "true")
|| !strcasecmp (value, "yes")
|| atoi (value) != 0)
result = 1;
setuservariable (INST_R0, result == 0 ? "0" : "1");
return;
}
/* 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 (HKEY root, const char *dir, const char *name)
{
HKEY root_key;
HKEY key_handle;
DWORD n1, nbytes, type;
char *result = NULL;
root_key = root;
if (! root_key)
root_key = HKEY_CURRENT_USER;
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, result, &n1 ) ) {
free(result); result = NULL;
goto leave;
}
result[nbytes] = 0; /* make sure it is really a string */
leave:
RegCloseKey( key_handle );
return result;
}
/** @brief Kill processes with the name name.
*
* This function tries to kill a process using ExitProcess.
*
* If it does not work it does not work. No return values.
* The intention is to make an effort to kill something during
* installation / uninstallation.
*
* The function signature is explained by NSIS.
*/
void __declspec(dllexport) __cdecl KillProc(HWND hwndParent,
int string_size,
char *variables,
stack_t **stacktop)
{
HANDLE h;
PROCESSENTRY32 pe32;
if (!stacktop || !*stacktop || !(*stacktop)->text)
{
ERRORPRINTF ("Invalid call to KillProc.");
return;
}
h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
if (h == INVALID_HANDLE_VALUE)
{
ERRORPRINTF ("Failed to create Toolhelp snapshot");
return;
}
pe32.dwSize = sizeof (PROCESSENTRY32);
if (!Process32First (h, &pe32))
{
ERRORPRINTF ("Failed to get first process");
CloseHandle (h);
return;
}
do
{
if (!strcmp ((*stacktop)->text, pe32.szExeFile))
{
HANDLE hProc = OpenProcess (PROCESS_ALL_ACCESS, FALSE,
pe32.th32ProcessID);
if (!hProc)
{
ERRORPRINTF ("Failed to open process handle.");
continue;
}
if (!TerminateProcess (hProc, 1))
{
ERRORPRINTF ("Failed to terminate process.");
}
CloseHandle (hProc);
}
}
while (Process32Next (h, &pe32));
CloseHandle (h);
}
diff --git a/src/g4wihelp.nsi b/src/g4wihelp.nsi
index f6574087..54313c8f 100644
--- a/src/g4wihelp.nsi
+++ b/src/g4wihelp.nsi
@@ -1,44 +1,44 @@
# g4wihelp.nsi - Access g4wihelp.dll. -*- coding: latin-1; -*-
-# Copyright (C) 2005 g10 Code GmbH
+# Copyright (C) 2005, 2023 g10 Code GmbH
#
# This file is part of GPG4Win.
#
# GPG4Win 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.
#
# GPG4Win 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
LangString T_AlreadyRunning ${LANG_ENGLISH} \
"An instance of this installer is already running."
#Function G4wTest
# Push $R0
# Push $R1
# StrCpy $R0 "mein argument 0"
# StrCpy $R1 "mein argument 1"
# g4wihelp::dummy
# Pop $R1
# Pop $R0
#FunctionEnd
Function G4wRunOnce
Push $R0
StrCpy $R0 "gpg4win"
g4wihelp::runonce
StrCmp $R0 0 +3
MessageBox MB_OK $(T_AlreadyRunning)
Abort
Pop $R0
FunctionEnd
diff --git a/src/installer.nsi b/src/installer.nsi
index 2f43701f..b8732980 100644
--- a/src/installer.nsi
+++ b/src/installer.nsi
@@ -1,784 +1,784 @@
# installer.nsi - Installer for GnuPG 4 Windows. -*- coding: latin-1; -*-
# Copyright (C) 2005, 2007 g10 Code GmbH
# Copyright (C) 2017 Intevation GmbH
#
# This file is part of GPG4Win.
#
# GPG4Win 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.
#
# GPG4Win 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
# No servicable parts beyond this line. Stay clear :)
!ifdef REQUIRE_W32_NSIS
!ifdef PACKAGE_LIBRARY
!include "Library.nsh"
!endif
!else
!macro InstallLib libtype shared install localfile destfile tempbasedir
File "${localfile}"
!macroend
!endif
!include "FileFunc.nsh"
!include "WinMessages.nsh"
!include "WinVer.nsh"
# We use the modern UI 2.
!include "MUI2.nsh"
!ifdef DEBUG
!undef DEBUG
!endif
# Set the package name. Note that this name should not be sufficed
# with the version because this would get displayed in the start menu.
Name "${PRETTY_PACKAGE}"
OutFile "${PACKAGE}-${VERSION}.exe"
BrandingText "${PRETTY_PACKAGE}-${VERSION}"
# Details button conflicts with splashscreen
ShowInstDetails nevershow
# Set the installation directory.
!ifndef INSTALL_DIR
!define INSTALL_DIR "${PACKAGE}"
!endif
InstallDir "$PROGRAMFILES\${INSTALL_DIR}"
# Add version information to the file properties.
VIProductVersion "${PROD_VERSION}"
VIAddVersionKey "ProductName" "${PRETTY_PACKAGE_SHORT} (${VERSION})"
!ifdef LICENSE_GPL
VIAddVersionKey "Comments" \
"${PRETTY_PACKAGE_SHORT} is Free Software; you can redistribute it \
and/or modify it under the terms of the GNU General Public License. \
You should have received a copy of the GNU General Public License \
along with this software; if not, write to the Free Software \
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, \
MA 02110-1301, USA"
!else
VIAddVersionKey "Comments" \
"${PRETTY_PACKAGE_SHORT} is Free Software; you can redistribute it \
and/or modify it under the terms of the GNU Lesser General Public \
License. You should have received a copy of the GNU Lesser General \
Public License along with this software; if not, write to the Free \
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, \
MA 02110-1301, USA"
!endif
VIAddVersionKey "CompanyName" "${COMPANY}"
VIAddVersionKey "LegalTrademarks" ""
VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
VIAddVersionKey "FileDescription" "${DESCRIPTION}"
VIAddVersionKey "FileVersion" "${PROD_VERSION}"
# Set to the name of another GnuPG installation if one has been detected
Var OtherGnuPGDetected
# Interface Settings
# !define MUI_ABORTWARNING
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP \
"${TOP_SRCDIR}/doc/logo/gpg4win-nsis-header-install-150x57.bmp"
!define MUI_HEADERIMAGE_UNBITMAP \
"${TOP_SRCDIR}/doc/logo/gpg4win-nsis-header-uninstall-150x57.bmp"
!define MUI_WELCOMEFINISHPAGE_BITMAP \
"${TOP_SRCDIR}/doc/logo/gpg4win-nsis-wizard-install-164x314.bmp"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP \
"${TOP_SRCDIR}/doc/logo/gpg4win-nsis-wizard-uninstall-164x314.bmp"
!define MUI_ICON "${TOP_SRCDIR}/doc/logo/gpg4win-nsis-install.ico"
!define MUI_UNICON "${TOP_SRCDIR}/doc/logo/gpg4win-nsis-uninstall.ico"
# Remember the installer language
!define MUI_LANGDLL_REGISTRY_ROOT "SHCTX"
!define MUI_LANGDLL_REGISTRY_KEY "Software\${PRETTY_PACKAGE_SHORT}"
!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language"
# No Umlaute, please!
!define MUI_LANGDLL_INFO "Please choose a language for the setup.$\r$\n\
Bitte die Sprache des Installations-Vorgangs angeben."
# The list of wizard pages.
!define MUI_WELCOMEPAGE_TITLE "${WELCOME_TITLE_STR}"
!define MUI_PAGE_CUSTOMFUNCTION_SHOW WelcomeFunction
!define MUI_WELCOMEPAGE_TEXT "${ABOUT_STR}"
!insertmacro MUI_PAGE_WELCOME
Function WelcomeFunction
IfSilent leave
ClearErrors
ReadRegStr $0 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPG4Win" "DisplayVersion"
IfErrors leave 0
StrCmp $0 "" leave
SendMessage $mui.WelcomePage.Text ${WM_SETTEXT} 0 "STR:${ABOUT_STR}$\r$\n$(T_UPDATE_STR) $0"
leave:
FunctionEnd
#!define MUI_LICENSEPAGE_BUTTON "$(^NextBtn)"
#!define MUI_PAGE_HEADER_SUBTEXT "$(T_GPLHeader)"
#!insertmacro MUI_PAGE_LICENSE "license.blurb"
!define MUI_PAGE_CUSTOMFUNCTION_SHOW PrintNonAdminWarning
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE CheckExistingVersion
!insertmacro MUI_PAGE_DIRECTORY
!define MUI_PAGE_CUSTOMFUNCTION_PRE BeforeInstallHooks
!insertmacro MUI_PAGE_INSTFILES
# Finish page
!ifndef SOURCES
!define MUI_PAGE_CUSTOMFUNCTION_PRE ShowFinalWarnings
!define MUI_PAGE_CUSTOMFUNCTION_SHOW FinishFunction
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION RunAsUser
!define MUI_FINISHPAGE_SHOWREADME "share\gpg4win\README.$(T_LangCode).txt"
!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(T_ShowReadme)"
!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
!define MUI_FINISHPAGE_LINK "$(T_MoreInfo)"
!define MUI_FINISHPAGE_LINK_LOCATION "$(T_MoreInfoURL)"
!insertmacro MUI_PAGE_FINISH
Function FinishFunction
IfSilent leave
!insertmacro SectionFlagIsSet ${SEC_kleopatra} \
${SF_SELECTED} have_kleo have_nothing
have_nothing:
ShowWindow $mui.FinishPage.Run ${SW_HIDE}
goto leave
have_kleo:
SendMessage $mui.FinishPage.Run.Text ${WM_SETTEXT} 0 "STR:$(T_RunKleopatra)"
goto leave
leave:
FunctionEnd
Function RunAsUser
!insertmacro SectionFlagIsSet ${SEC_kleopatra} \
${SF_SELECTED} do_kleo leave
do_kleo:
g4wihelp::DesktopShellRun "$INSTDIR\bin\kleopatra.exe"
leave:
FunctionEnd
LangString T_RunKleopatra ${LANG_ENGLISH} \
"Run Kleopatra"
# /SOURCES
!endif
# Uninstaller pages.
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!ifndef SOURCES
!define MUI_PAGE_CUSTOMFUNCTION_PRE un.CloseApps
!endif
!insertmacro MUI_UNPAGE_INSTFILES
#!insertmacro MUI_UNPAGE_FINISH
#Page license
#Page components
#Page directory
#Page instfiles
#UninstPage uninstConfirm
#UninstPage instfiles
# Language support. This has to be done after defining the pages, but
# before defining the translation strings. Confusing.
# Enable this to not filter languages for the current code page. Note
# that languages which are then not filtered out may not be displayed
# correctly in the Windows version the user is using. Not recommended,
# but can be useful for testing.
!ifdef DEBUG
!define MUI_LANGDLL_ALLLANGUAGES
!endif
!insertmacro MUI_LANGUAGE "English"
!define PO_HEADER
!include "../po/catalogs.nsi"
!undef PO_HEADER
!insertmacro MUI_RESERVEFILE_LANGDLL
ReserveFile "${BUILD_DIR}\g4wihelp.dll"
!ifdef SOURCES
ReserveFile "${TOP_SRCDIR}\doc\logo\gpg4win-logo-400px.bmp"
ReserveFile "${TOP_SRCDIR}\src\gpg4win-splash.wav"
!endif
ReserveFile "${TOP_SRCDIR}\COPYING"
ReserveFile "${TOP_SRCDIR}\doc\logo\gpg4win-logo-164x314.bmp"
# Language support
LangString T_LangCode ${LANG_ENGLISH} "en"
# Startup page
LangString T_GPLHeader ${LANG_ENGLISH} \
"This software is licensed under the terms of the GNU General Public \
License (GNU GPL)."
LangString T_GPLShort ${LANG_ENGLISH} \
"In short: You are allowed to run this software for any purpose. \
You may distribute it as long as you give the recipients the same \
rights you have received."
LangString T_MoreInfo ${LANG_ENGLISH} \
"Go to Gpg4win's webpage"
LangString T_MoreInfoURL ${LANG_ENGLISH} "https://www.gpg4win.org"
LangString T_ShowReadme ${LANG_ENGLISH} \
"Show the README file"
LangString T_NoKeyManager ${LANG_ENGLISH} \
"No key manager has been installed, thus we can't run one now."
# Used as subdirectory name in Start Menu.
LangString DESC_Menu_manuals ${LANG_ENGLISH} \
"Documentation"
LangString DESC_Menu_uninstall ${LANG_ENGLISH} \
"Uninstall"
# Used as subdirectory name on Desktop.
LangString DESC_Desktop_manuals ${LANG_ENGLISH} \
"Gpg4win Documentation"
# Functions
# Custom functions and macros for gpg4win.
!include "g4wihelp.nsi"
# Display a warning if this is a Beta version.
#Function PrintBetaWarning
# MessageBox MB_OK "$(T_BetaWarning)"
#FunctionEnd
# Display a warning if GnuPP has been detected and allow the user to abort
# the installation.
Function PrintGnuPPWarning
MessageBox MB_YESNO "$(T_FoundOldGnuPP)" IDYES cont
Abort
cont:
StrCpy $OtherGnuPGDetected "GnuPP"
FunctionEnd
# Display a warning if GnuPT has been detected and allow the user to abort
# the installation.
Function PrintGnuPTWarning
MessageBox MB_YESNO "$(T_FoundOldGnuPT)" IDYES cont
Abort
cont:
StrCpy $OtherGnuPGDetected "GnuPT"
FunctionEnd
# Display a warning if the Sourceforge WinPT has been detected and
# allow the user to abort the installation.
Function PrintWinPTSFWarning
MessageBox MB_YESNO "$(T_FoundOldWinPTSF)" IDYES cont
Abort
cont:
StrCpy $OtherGnuPGDetected "WinPT-SF"
FunctionEnd
# Display a warning if GnuPG Pack has been detected and abort the
# the installation. This one clobbers our own Registry space.
Function PrintGnuPackWarning
MessageBox MB_OK "$(T_FoundOldGnuPack)"
Abort
FunctionEnd
# Check whether one of the other GnuPG systems has already been
# installed. We do this by looking at the registry.
!ifndef SOURCES
Function CheckOtherGnuPGApps
StrCpy $OtherGnuPGDetected ""
ClearErrors
ReadRegStr $0 SHCTX "Software\GNU\GnuPP\Settings" "Path"
IfErrors +2 0
Call PrintGnuPPWarning
EnumRegKey $0 HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Start Menu\Programs\GnuPT" 0
StrCmp $0 "" +2
Call PrintGnuPTWarning
ClearErrors
ReadRegStr $0 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\Windows Privacy Tools" "DisplayVersion"
IfErrors +2 0
Call PrintWinPTSFWarning
ReadRegStr $0 SHCTX "Software\GNU\GnuPG" "Install Directory"
Push $0
Push "GnuPG-Pack"
Call StrStr
Pop $0
StrCmp $0 "" +2
Call PrintGnuPackWarning
FunctionEnd
!endif
# Check whether gpg4win has already been installed. This is called as
# a leave function from the directory page. A call to abort will get
# back to the directory selection.
Function CheckExistingVersion
ClearErrors
FileOpen $0 "$INSTDIR\VERSION" r
IfErrors leave
FileRead $0 $R0
FileRead $0 $R1
FileClose $0
Push $R1
Call TrimNewLines
Pop $R1
# Extract major version.
StrCpy $0 $R1 2
StrCmp $0 "1." 0 leave
MessageBox MB_YESNO "$(T_FoundExistingOldVersion)" IDYES leave
Abort
leave:
FunctionEnd
# PrintNonAdminWarning
# Check whether the current user is in the Administrator group or an
# OS version without the need for an Administrator is in use. Print a
# diagnostic if this is not the case and abort installation.
Function PrintNonAdminWarning
#Call PrintBetaWarning
ClearErrors
UserInfo::GetName
IfErrors leave
Pop $0
UserInfo::GetAccountType
Pop $1
StrCmp $1 "Admin" leave +1
MessageBox MB_YESNO "$(T_AdminWanted)" IDNO exit
goto leave
exit:
Quit
leave:
FunctionEnd
# Check for claws mail installation which was shipped in Gpg4win
# versions < 2.2.6
Function CheckClawsUninstall
IfFileExists $INSTDIR\claws-mail.exe 0 leave
MessageBox MB_YESNO "$(T_FoundOldClaws)" IDYES uninstall IDNO leave
uninstall:
!insertmacro SelectSection ${SecUninstClawsMail}
leave:
FunctionEnd
# Check whether this is a reinstall and popup a message box to explain
# that it is better to close other apps before continuing
Function KillOtherAppsOrWarn
g4wihelp::KillProc "kleopatra.exe"
g4wihelp::KillProc "gpgme-w32spawn.exe"
g4wihelp::KillProc "resolver.exe"
g4wihelp::KillProc "overlayer.exe"
g4wihelp::KillProc "gpg-agent.exe"
g4wihelp::KillProc "gpg.exe"
g4wihelp::KillProc "dirmngr.exe"
g4wihelp::KillProc "gpgsm.exe"
goto leave
# TODO check for running outlook and offer to kill it.
print_warning:
MessageBox MB_OK|MB_ICONEXCLAMATION "$(T_CloseOtherApps)"
leave:
FunctionEnd
Function un.CloseApps
g4wihelp::KillProc "kleopatra.exe"
g4wihelp::KillProc "gpgme-w32spawn.exe"
g4wihelp::KillProc "resolver.exe"
g4wihelp::KillProc "overlayer.exe"
g4wihelp::KillProc "gpg-agent.exe"
g4wihelp::KillProc "gpg.exe"
g4wihelp::KillProc "dirmngr.exe"
g4wihelp::KillProc "gpgsm.exe"
FunctionEnd
# Called right before installation
Function BeforeInstallHooks
Call CheckClawsUninstall
FunctionEnd
# Called right before the final page to show more warnings.
Function ShowFinalWarnings
StrCmp $OtherGnupgDetected "" +2
MessageBox MB_OK "$(T_FoundOldSeeManual)"
leave:
FunctionEnd
#-----------------------------------------------
# Strings pertaining to the install options page
#-----------------------------------------------
# Installation options title
LangString T_InstallOptions ${LANG_ENGLISH} "Install Options"
# Installation options subtitle 1
LangString T_InstallOptLinks ${LANG_ENGLISH} "Start links"
LangString T_InstOptLabelA ${LANG_ENGLISH} \
"Please select where Gpg4win shall install links:"
LangString T_InstOptLabelB ${LANG_ENGLISH} \
"(Only programs will be linked into the quick launch bar.)"
LangString T_InstOptFieldA ${LANG_ENGLISH} \
"Start Menu"
LangString T_InstOptFieldB ${LANG_ENGLISH} \
"Desktop"
LangString T_InstOptFieldC ${LANG_ENGLISH} \
"Quick Launch Bar"
#------------------------------------------------
# String pertaining to the existing version check
#------------------------------------------------
LangString T_FoundExistingVersion ${LANG_ENGLISH} \
"Version $R1 has already been installed. $\r$\n\
Do you want to overwrite it with version ${VERSION}?"
LangString T_FoundExistingOldVersion ${LANG_ENGLISH} \
"An old version $R1 has already been installed. It is \
strongly recommended to deinstall previous versions on \
major upgrades. $\r$\n\
Do you want to continue installing Gpg4win ${VERSION} anyway?"
LangString T_UninstallingOldVersion ${LANG_ENGLISH} \
"Uninstalling Gpg4win-"
#---------------------------------------------
# From the old installation checking functions
#---------------------------------------------
LangString T_FoundOldSeeManual ${LANG_ENGLISH} \
"Please see the Gpg4win user manual to learn how to migrate existing \
keys from other GnuPG based installations to Gpg4win."
#---------
LangString T_FoundOldGnuPP ${LANG_ENGLISH} \
"An old installation of GnuPP (GNU Privacy Project) has been \
detected. That software is not maintained anymore and thus should \
be removed. $\r$\n\
$\r$\n\
Do you want to continue installing Gpg4win and take care of the old \
installation later?"
#---------
LangString T_FoundOldGnuPT ${LANG_ENGLISH} \
"An installation of GnuPT has been detected. This may cause \
problems when used along with Gpg4win. $\r$\n\
$\r$\n\
Do you want to continue installing Gpg4win?"
#---------
LangString T_FoundOldWinPTSF ${LANG_ENGLISH} \
"An old installation of the Sourceforge hosted WinPT has been \
detected. That software is not maintained anymore and should \
be removed. $\r$\n\
$\r$\n\
Do you want to continue installing Gpg4win and take care of the old \
installation later?"
#--------
LangString T_FoundOldGnuPack ${LANG_ENGLISH} \
"An installation of GnuPG-Pack has been detected. You need to \
uninstall it before you can continue with Gpg4win installation. $\r$\n\
$\r$\n\
The installation will be aborted now!"
# From Function PrintBetaWarning
LangString T_BetaWarning ${LANG_ENGLISH} \
"Note: This is a BETA version of Gpg4win. $\r$\n\
$\r$\n\
Beta versions are only intended for testing and \
shall not be used in a production environment."
# From Function PrintNonAdminWarning
LangString T_AdminWanted ${LANG_ENGLISH} \
"Warning: It is recommended to install Gpg4win system-wide with \
administrator rights. \
$\r$\n\
$\r$\n\
Do you want to continue installing Gpg4win without administrator rights?"
# From Function KillOtherAppsOrWarn
LangString T_CloseOtherApps ${LANG_ENGLISH} \
"Please make sure that other applications are not running. \
In particular you should close Outlook and all Explorer \
windows. Gpg4win will try to install anyway but a reboot \
will be required then."
LangString T_ShuttingDownWinPT ${LANG_ENGLISH} \
"Trying to shutdown a possible running instance of WinPT."
# From Function CheckClawsUninstall
LangString T_FoundOldClaws ${LANG_ENGLISH} \
"An old version of Claws Mail was found in your Installation directory. \
$\r$\nPlease note that Claws Mail is no longer bundled with Gpg4win \
and is now available as a standalone package.$\r$\n\
You should uninstall Claws Mail now, and if you wish to \
continue to use it, install an up-to-date version from:$\r$\n\
http://www.claws-mail.org/win32 $\r$\n$\r$\n\
Uninstall Claws Mail from Gpg4win now?"
# From Function CheckClawsUninstall
LangString T_WinisDeprecated ${LANG_ENGLISH} \
"Windows Versions before Windows 7 are no longer maintained by Gpg4win. \
$\r$\nSupport for them may be removed in a future version.\
$\r$\n$\r$\nKleopatra is disabled."
# From Function WelcomeFunction
LangString T_UPDATE_STR ${LANG_ENGLISH} \
"Updating Version"
# FIXME: The GetAfterChar function comes from the NSIS wiki.
Function un.GetAfterChar
Exch $0 ; chop char
Exch
Exch $1 ; input string
Push $2
Push $3
StrCpy $2 0
loop:
IntOp $2 $2 - 1
StrCpy $3 $1 1 $2
StrCmp $3 "" 0 +3
StrCpy $0 ""
Goto exit2
StrCmp $3 $0 exit1
Goto loop
exit1:
IntOp $2 $2 + 1
StrCpy $0 $1 "" $2
exit2:
Pop $3
Pop $2
Pop $1
Exch $0 ; output
FunctionEnd
Function un.SourceDelete
Push "/"
Call un.GetAfterChar
Pop $R0
Delete "$INSTDIR\$R0"
FunctionEnd
# StrStr - taken from the NSIS reference
# input, top of stack = string to search for
# top of stack-1 = string to search in
# output, top of stack (replaces with the portion of the string remaining)
# modifies no other variables.
#
# Usage:
# Push "this is a long ass string"
# Push "ass"
# Call StrStr
# Pop $R0
# ($R0 at this point is "ass string")
#
!macro StrStr un
Function ${un}StrStr
Exch $R1 # st=haystack,old$R1, $R1=needle
Exch # st=old$R1,haystack
Exch $R2 # st=old$R1,old$R2, $R2=haystack
Push $R3
Push $R4
Push $R5
StrLen $R3 $R1
StrCpy $R4 0
# $R1=needle
# $R2=haystack
# $R3=len(needle)
# $R4=cnt
# $R5=tmp
loop:
StrCpy $R5 $R2 $R3 $R4
StrCmp $R5 $R1 done
StrCmp $R5 "" done
IntOp $R4 $R4 + 1
Goto loop
done:
StrCpy $R1 $R2 "" $R4
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Exch $R1
FunctionEnd
!macroend
# TrimNewlines - taken from the NSIS reference
# input, top of stack (e.g. whatever$\r$\n)
# output, top of stack (replaces, with e.g. whatever)
# modifies no other variables.
Function TrimNewlines
Exch $R0
Push $R1
Push $R2
StrCpy $R1 0
loop:
IntOp $R1 $R1 - 1
StrCpy $R2 $R0 1 $R1
StrCmp $R2 "$\r" loop
StrCmp $R2 "$\n" loop
IntOp $R1 $R1 + 1
IntCmp $R1 0 no_trim_needed
StrCpy $R0 $R0 $R1
no_trim_needed:
Pop $R2
Pop $R1
Exch $R0
FunctionEnd
Function .onInit
# Temporay disabled until we have fixed the DLL issue (wk 2023-04-11)
- # Call G4wRunOnce
+ Call G4wRunOnce
SetOutPath $TEMP
!ifdef SOURCES
File /oname=gpgspltmp.bmp "${TOP_SRCDIR}/doc/logo/gpg4win-logo-400px.bmp"
# We play the tune only for the source installer
File /oname=gpgspltmp.wav "${TOP_SRCDIR}/src/gpg4win-splash.wav"
g4wihelp::playsound $TEMP\gpgspltmp.wav
g4wihelp::showsplash 2500 $TEMP\gpgspltmp.bmp
Delete $TEMP\gpgspltmp.bmp
# Note that we delete gpgspltmp.wav in .onInst{Failed,Success}
!else
${GetParameters} $R0
ClearErrors
${GetOptions} $R0 /MINIMAL= $is_minimal
${GetOptions} $R0 /WITH_BROWSER= $with_browser
Var /GLOBAL changed_dir
# Check if the install directory was modified on the command line
StrCmp "$INSTDIR" "$PROGRAMFILES\${INSTALL_DIR}" unmodified 0
# It is modified. Save that value.
StrCpy $changed_dir "$INSTDIR"
# MULITUSER_INIT overwrites directory setting from command line
!insertmacro MULTIUSER_INIT
StrCpy $INSTDIR "$changed_dir"
goto initDone
unmodified:
!insertmacro MULTIUSER_INIT
!endif
initDone:
# Enable this to force a language selection dialog on every run (the
# preferred language is the default). Otherwise, the preferred
# language is stored in the registry, and the installer does not ask
# on upgrades.
!ifdef DEBUG
!define MUI_LANGDLL_ALWAYSSHOW
!endif
!insertmacro MUI_LANGDLL_DISPLAY
${IfNot} ${AtLeastWin7}
MessageBox MB_OK "$(T_WinisDeprecated)"
${Endif}
${MementoSectionRestore}
Call CalcDefaults
Call CalcDepends
!ifndef SOURCES
Call CheckOtherGnuPGApps
!endif
FunctionEnd
Function un.onInit
!ifndef SOURCES
!insertmacro MULTIUSER_UNINIT
!endif
# Remove the language preference.
!insertmacro MUI_UNGETLANGUAGE
!ifndef SOURCES
StrCpy $is_update "0"
${un.GetParameters} $R0
ClearErrors
${un.GetOptions} $R0 /UPDATE= $R1
IfErrors +2
StrCpy $is_update "1"
!endif
FunctionEnd
# This must be in a central place. Urgs.
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!ifdef HAVE_PKG_GNUPG_W32
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_gnupg_w32} $(DESC_SEC_gnupg_w32)
!endif
!ifdef HAVE_PKG_GPGOL
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_gpgol} $(DESC_SEC_gpgol)
!endif
!ifdef HAVE_PKG_GPGEX
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_gpgex} $(DESC_SEC_gpgex)
!endif
!ifdef HAVE_PKG_PAPERKEY
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_paperkey} $(DESC_SEC_paperkey)
!endif
!ifdef HAVE_PKG_KLEOPATRA
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_kleopatra} $(DESC_SEC_kleopatra)
!endif
!ifdef HAVE_PKG_MAN_NOVICE_EN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_man_novice_en} $(DESC_SEC_man_novice_en)
!endif
!ifdef HAVE_PKG_MAN_ADVANCED_EN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_man_advanced_en} $(DESC_SEC_man_advanced_en)
!endif
!ifdef HAVE_PKG_COMPENDIUM
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_compendium} $(DESC_SEC_compendium)
!endif
!ifdef HAVE_PKG_MAN_NOVICE_DE
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_man_novice_de} $(DESC_SEC_man_novice_de)
!endif
!ifdef HAVE_PKG_MAN_ADVANCED_DE
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_man_advanced_de} $(DESC_SEC_man_advanced_de)
!endif
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_gpgme_browser} $(DESC_SEC_gpgme_browser)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
diff --git a/src/slideshow.cpp b/src/slideshow.cpp
index 932097e0..812f1742 100644
--- a/src/slideshow.cpp
+++ b/src/slideshow.cpp
@@ -1,461 +1,461 @@
/* slideshow.cpp - NSIS Helper DLL for a slideshow. -*- coding: latin-1; -*-
* Copyright (C) 2016 Intevation GmbH
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must
* not claim that you wrote the original software. If you use this
* software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must
* not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
************************************************************
* The code is heavily based on the Slideshow
* plugin from http://wiz0u.free.fr/prog/nsisSlideshow
*
* It was slightly modified and adapted by:
* 2016 Andre Heinecke
*
* Version 1.7 was Licensed at the time of copying (28.6.2016) as:
* Copyright (c) 2009-2011 Olivier Marcoux
*
* This software is provided 'as-is', without any express or implied warranty. In no
* event will the authors be held liable for any damages arising from the use of this
* software.
*
* Permission is granted to anyone to use this software for any purpose, including
* commercial applications, and to alter it and redistribute it freely, subject to the
* following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim
* that you wrote the original software. If you use this software in a product, an
* acknowledgment in the product documentation would be appreciated but is not
* required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
************************************************************
*/
#include
#include
#include
#include
#include "exdll.h"
#include
static unsigned int timerid = 0xBEEF;
#ifndef _countof
#define _countof(A) (sizeof(A)/sizeof((A)[0]))
#endif
#ifndef INITGUID
#define INITGUID
#endif
__CRT_UUID_DECL(IImgCtx,
0x3050f3d7, 0x98b5, 0x11cf,
0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b);
DEFINE_GUID(IID_IImgCtx,
0x3050f3d7, 0x98b5, 0x11cf,
0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b);
DEFINE_GUID(CLSID_IImgCtx,
0x3050f3d6, 0x98b5, 0x11cf,
0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b);
#undef INITGUID
/* array of precalculated alpha values for a linear crossfade in 15 steps */
const BYTE SCA_Steps[15] = { 17, 18, 20, 21, 23, 26, 28, 32, 36, 43, 51, 64, 85, 127, 255 };
int g_step = 0;
HWND g_hWnd = NULL;
HDC g_hdcMem = NULL;
HBITMAP g_hbmMem = NULL;
TCHAR g_autoPath[MAX_PATH];
LPTSTR g_autoBuffer = NULL;
LPTSTR g_autoNext = NULL;
int g_autoDelay = 0;
RECT rDest;
int wDest, hDest;
WNDPROC lpPrevWndFunc = NULL;
enum HAlign {
HALIGN_CENTER = 0,
HALIGN_LEFT,
HALIGN_RIGHT
} iHAlign;
enum VAlign {
VALIGN_CENTER = 0,
VALIGN_TOP,
VALIGN_BOTTOM
} iVAlign;
enum Fit {
FIT_STRETCH = 0,
FIT_WIDTH,
FIT_HEIGHT,
FIT_BOTH
} iFit;
COLORREF captionColor;
static LRESULT CALLBACK slide_WndProc(HWND, UINT, WPARAM, LPARAM);
/*****************************************************
* Abort: stops current processing, detach from slide_WndProc and release resources
*****************************************************/
static void
slide_abort(bool stayAuto = false)
{
KillTimer(g_hWnd, timerid);
if (!stayAuto)
{
if (lpPrevWndFunc != NULL && IsWindow(g_hWnd))
{
if ((WNDPROC) GetWindowLong(g_hWnd, GWL_WNDPROC) == slide_WndProc)
SetWindowLongPtr(g_hWnd, GWL_WNDPROC, (long)lpPrevWndFunc);
}
lpPrevWndFunc = NULL;
GlobalFree(g_autoBuffer);
g_autoBuffer = NULL;
g_autoNext = NULL;
g_autoDelay = false;
}
DeleteDC(g_hdcMem);
g_hdcMem = NULL;
DeleteObject(g_hbmMem);
g_hbmMem = NULL;
}
static void
slide_NewImage(LPCTSTR imgPath, LPCTSTR caption, int duration)
{
-#ifdef _UNICODE
+#ifdef UNICODE
LPCWSTR imgPathW = imgPath;
#else
WCHAR imgPathW[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, imgPath, -1, imgPathW, _countof(imgPathW));
#endif
IImgCtx *pImage = NULL;
SIZE imgSize = {0, 0};
if (SUCCEEDED(CoCreateInstance(CLSID_IImgCtx, NULL, CLSCTX_ALL, IID_IImgCtx, (void**)&pImage)))
{
if (SUCCEEDED(pImage->Load(imgPathW, 0)))
{
DWORD dwState;
while (SUCCEEDED(pImage->GetStateInfo(&dwState, NULL, true)) && (dwState & (IMGLOAD_COMPLETE|IMGLOAD_ERROR)) == 0)
Sleep(20);
pImage->GetStateInfo(&dwState, &imgSize, true);
}
if (imgSize.cx == 0 || imgSize.cy == 0) // error path or format (IMGLOAD_ERROR)
{
pImage->Release();
pImage = NULL;
}
}
if (pImage == NULL)
return;
// fit image
wDest = rDest.right - rDest.left;
hDest = rDest.bottom - rDest.top;
if (iFit == FIT_BOTH)
iFit = (wDest*imgSize.cy > imgSize.cx*hDest) ? FIT_HEIGHT : FIT_WIDTH;
if (iFit == FIT_HEIGHT)
wDest = (imgSize.cx * hDest) / imgSize.cy;
else if (iFit == FIT_WIDTH)
hDest = (imgSize.cy * wDest) / imgSize.cx;
// align image
if (iHAlign == HALIGN_CENTER) rDest.left = (rDest.left + rDest.right - wDest) / 2;
else if (iHAlign == HALIGN_RIGHT) rDest.left = rDest.right - wDest;
if (iVAlign == VALIGN_CENTER) rDest.top = (rDest.top + rDest.bottom - hDest) / 2;
else if (iVAlign == VALIGN_BOTTOM) rDest.top = rDest.bottom - hDest;
rDest.right = rDest.left + wDest;
rDest.bottom = rDest.top + hDest;
// create memory DC & Bitmap compatible with window's DC
HDC hWndDC = GetDC(g_hWnd);
g_hdcMem = CreateCompatibleDC(hWndDC);
g_hbmMem = CreateCompatibleBitmap(hWndDC, wDest, hDest);
ReleaseDC(g_hWnd, hWndDC);
SelectObject(g_hdcMem, g_hbmMem);
// paint image in memory DC
RECT bounds = { 0, 0, wDest, hDest };
pImage->Draw(g_hdcMem, &bounds);
pImage->Release(); // we don't need the image anymore
if (caption[0] != '\0')
{
LOGFONT lf;
GetObject((HFONT) ::SendMessage(g_hWnd, WM_GETFONT, 0, 0), sizeof(lf), &lf);
lf.lfHeight += lf.lfHeight/2;
HFONT hFont = CreateFontIndirect(&lf);
HGDIOBJ hOldFont = SelectObject(g_hdcMem, hFont);
SetTextColor(g_hdcMem, captionColor);
SetBkMode(g_hdcMem, TRANSPARENT);
SetTextAlign(g_hdcMem, TA_BOTTOM|TA_CENTER|TA_NOUPDATECP);
TextOut(g_hdcMem, wDest/2, hDest-10, caption, lstrlen(caption));
DeleteObject(SelectObject(g_hdcMem, hOldFont));
}
// replace windows procedure, start time and initiate first step
if (lpPrevWndFunc == NULL)
lpPrevWndFunc = (WNDPROC) SetWindowLongPtr(g_hWnd, GWL_WNDPROC, (long) slide_WndProc);
if (duration == 0)
{
g_step = _countof(SCA_Steps);
InvalidateRect(g_hWnd, NULL, FALSE); // no duration => force a WM_PAINT for immediate draw of picture
if (g_autoNext && g_autoDelay)
SetTimer(g_hWnd, timerid, g_autoDelay, NULL);
}
else
{
g_step = 0;
slide_WndProc(g_hWnd, WM_TIMER, timerid, 0); // first iteration right now
SetTimer(g_hWnd, timerid, duration/_countof(SCA_Steps), NULL);
}
}
static bool
slide_NextAuto()
{
LPTSTR scan;
if (g_autoNext == NULL)
return false;
if (*g_autoNext == '.')
{
g_autoNext = g_autoBuffer;
return false;
}
bool result = false;
if (*g_autoNext == '=')
g_autoNext++;
for (scan = g_autoNext; *scan; scan++)
if (*scan == ',') break;
if (*scan)
{
TCHAR imgPath[MAX_PATH];
*scan = '\0';
PathCombine(imgPath, g_autoPath, g_autoNext);
*scan = ',';
g_autoNext = scan+1;
int duration = StrToInt(g_autoNext);
for (scan = g_autoNext; *scan; scan++)
if (*scan == ',') break;
if (*scan)
{
g_autoNext = scan+1;
g_autoDelay = StrToInt(g_autoNext);
for (scan = g_autoNext; *scan; scan++)
if (*scan == ',') break;
if (*scan && (scan[1] == '"'))
{
g_autoNext = scan+2;
for (scan = g_autoNext; *scan; scan++)
if (*scan == '"') break;
if (*scan)
{
TCHAR caption[MAX_PATH];
lstrcpyn(caption, g_autoNext, scan-g_autoNext+1);
g_autoNext = scan+1;
slide_NewImage(imgPath, caption, duration);
result = true;
}
}
}
}
g_autoNext += lstrlen(g_autoNext);
g_autoNext++;
if (*g_autoNext == '\0')
g_autoNext = g_autoBuffer;
return result;
}
/*****************************************************
* overriden WndProc for NSIS wizard pane
*****************************************************/
static LRESULT CALLBACK slide_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_TIMER:
if (wParam == timerid)
{
if (g_step == _countof(SCA_Steps))
{
slide_abort(true);
if (!slide_NextAuto())
{
slide_abort();
}
return 0;
}
HDC hDC = GetDC(hWnd);
const BLENDFUNCTION ftn = { AC_SRC_OVER, 0, SCA_Steps[g_step++], 0 };
AlphaBlend(hDC, rDest.left, rDest.top, wDest, hDest, g_hdcMem, 0, 0, wDest, hDest, ftn);
ReleaseDC(hWnd, hDC);
if (g_step == _countof(SCA_Steps))
{
if (g_autoNext && g_autoDelay)
SetTimer(hWnd, timerid, g_autoDelay, NULL);
else
KillTimer(hWnd, timerid);
}
return 0;
}
case WM_PAINT:
if (g_hdcMem)
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
CallWindowProc(lpPrevWndFunc, hWnd, uMsg, wParam, lParam);
BitBlt(hDC, rDest.left, rDest.top, wDest, hDest, g_hdcMem, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
}
break;
case WM_CLOSE:
slide_abort();
break;
case WM_COMMAND:
if(LOWORD(wParam) == IDCANCEL || LOWORD(wParam) == IDOK || LOWORD(wParam) == IDABORT)
slide_abort();
break;
default:
break;
}
return CallWindowProc(lpPrevWndFunc, hWnd, uMsg, wParam, lParam);
}
/*****************************************************
* NSIS Plugin "stop" entrypoint
*****************************************************/
#ifdef __cplusplus
extern "C" {
#endif
void __declspec(dllexport)
slide_stop(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop)
{
slide_abort();
}
/*****************************************************
* NSIS Plugin "show" entrypoint
*****************************************************/
void __declspec(dllexport)
slide_show(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop)
{
EXDLL_INIT();
slide_abort();
// argument default values
iHAlign = HALIGN_CENTER;
iVAlign = VALIGN_CENTER;
iFit = FIT_BOTH;
g_hWnd = NULL;
captionColor = RGB(255,255,255);
int duration = 1000; // transition duration in ms (default = 1s)
TCHAR caption[MAX_PATH];
caption[0] = '\0';
// parse arguments
TCHAR arg[MAX_PATH];
LPTSTR argValue;
- while(!popstring(arg, sizeof arg) && *arg == '/' && (argValue = StrChr(arg, '=')) != NULL)
+ while(!popstringn(arg, sizeof arg) && *arg == '/' && (argValue = StrChr(arg, '=')) != NULL)
{
*argValue++ = '\0'; // replace '=' by '\0'
if(lstrcmpi(arg, TEXT("/hwnd")) == 0)
StrToIntEx(argValue, STIF_SUPPORT_HEX, (int*) &g_hWnd);
else if(lstrcmpi(arg, TEXT("/fit")) == 0)
{
if(lstrcmpi(argValue, TEXT("height")) == 0) iFit = FIT_HEIGHT;
else if(lstrcmpi(argValue, TEXT("width")) == 0) iFit = FIT_WIDTH;
else if(lstrcmpi(argValue, TEXT("stretch")) == 0) iFit = FIT_STRETCH;
}
else if(lstrcmpi(arg, TEXT("/halign")) == 0)
{
if(lstrcmpi(argValue, TEXT("left")) == 0) iHAlign = HALIGN_LEFT;
else if(lstrcmpi(argValue, TEXT("right")) == 0) iHAlign = HALIGN_RIGHT;
}
else if(lstrcmpi(arg, TEXT("/valign")) == 0)
{
if(lstrcmpi(argValue, TEXT("top")) == 0) iVAlign = VALIGN_TOP;
else if(lstrcmpi(argValue, TEXT("bottom")) == 0) iVAlign = VALIGN_BOTTOM;
}
else if(lstrcmpi(arg, TEXT("/duration")) == 0)
StrToIntEx(argValue, STIF_SUPPORT_HEX, &duration);
else if(lstrcmpi(arg, TEXT("/caption")) == 0)
lstrcpy(caption, argValue);
else if(lstrcmpi(arg, TEXT("/ccolor")) == 0)
StrToIntEx(argValue, STIF_SUPPORT_HEX, (int*) &captionColor);
else if(lstrcmpi(arg, TEXT("/auto")) == 0)
{
lstrcpy(g_autoPath, argValue);
PathRemoveFileSpec(g_autoPath);
HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, 32767*sizeof(TCHAR));
DWORD count = GetPrivateProfileSection(getuservariable(INST_LANG), LPTSTR(hMem), 32767, argValue);
if (count == 0)
{
count = GetPrivateProfileSection(TEXT("1033"), LPTSTR(hMem), 32767, argValue);
if (count == 0)
count = GetPrivateProfileSection(TEXT("0"), LPTSTR(hMem), 32767, argValue);
}
if (count)
{
g_autoBuffer = LPTSTR(GlobalReAlloc(hMem, (count+1)*sizeof(TCHAR), 0));
g_autoNext = g_autoBuffer;
}
else
GlobalFree(hMem);
}
}
// if target window not defined we'll search for default (the details listview)
if (g_hWnd == NULL)
{
g_hWnd = FindWindowEx(hwndParent, NULL, TEXT("#32770"), NULL);
if (g_hWnd == NULL)
return;
hwndParent = FindWindowEx(hwndParent, g_hWnd, TEXT("#32770"), NULL);
if (hwndParent != NULL && !IsWindowVisible(hwndParent))
g_hWnd = hwndParent;
if (g_hWnd == NULL)
return;
HWND hWnd = GetDlgItem(g_hWnd, 1016);
GetWindowRect(hWnd, &rDest);
ScreenToClient(g_hWnd, (LPPOINT) &rDest.left);
ScreenToClient(g_hWnd, (LPPOINT) &rDest.right);
}
else
GetClientRect(g_hWnd, &rDest);
// load new image
if (arg[0] == '\0')
return; // stop here if no filename
if (g_autoNext != NULL)
slide_NextAuto();
else
slide_NewImage(arg, caption, duration);
}
#ifdef __cplusplus
}
#endif