summaryrefslogtreecommitdiff
path: root/lang
diff options
context:
space:
mode:
Diffstat (limited to 'lang')
-rw-r--r--lang/Makefile.in4
-rw-r--r--lang/cl/Makefile.in4
-rw-r--r--lang/cl/gpgme.asd2
-rw-r--r--lang/cpp/Makefile.in4
-rw-r--r--lang/cpp/src/GpgmeppConfig.cmake.in.in4
-rw-r--r--lang/cpp/src/Makefile.am11
-rw-r--r--lang/cpp/src/Makefile.in16
-rw-r--r--lang/cpp/src/context.cpp70
-rw-r--r--lang/cpp/src/context.h28
-rw-r--r--lang/cpp/src/context_p.h1
-rw-r--r--lang/cpp/src/data.cpp24
-rw-r--r--lang/cpp/src/data.h5
-rw-r--r--lang/cpp/src/editinteractor.cpp2
-rw-r--r--lang/cpp/src/gpggencardkeyinteractor.cpp332
-rw-r--r--lang/cpp/src/gpggencardkeyinteractor.h71
-rw-r--r--lang/cpp/src/key.cpp59
-rw-r--r--lang/cpp/src/key.h22
-rw-r--r--lang/cpp/src/verificationresult.cpp3
-rw-r--r--lang/python/Makefile.am59
-rw-r--r--lang/python/Makefile.in64
-rw-r--r--lang/python/gpg/constants/__init__.py8
-rw-r--r--lang/python/gpg/constants/create.py25
-rw-r--r--lang/python/gpg/constants/keysign.py25
-rw-r--r--lang/python/gpg/constants/tofu/__init__.py24
-rw-r--r--lang/python/gpg/constants/tofu/policy.py25
-rw-r--r--lang/python/gpg/core.py350
-rw-r--r--lang/python/gpg/gpgme.py126
-rw-r--r--lang/python/gpg/results.py6
-rw-r--r--lang/python/gpg/version.py2
-rw-r--r--lang/python/gpgme.i138
-rw-r--r--lang/python/helpers.c22
-rwxr-xr-xlang/python/setup.py.in16
-rw-r--r--lang/python/tests/Makefile.am12
-rw-r--r--lang/python/tests/Makefile.in16
-rwxr-xr-xlang/python/tests/initial.py3
-rw-r--r--lang/python/tests/run-tests.py34
-rw-r--r--lang/python/tests/support.py65
-rwxr-xr-xlang/python/tests/t-callbacks.py2
-rwxr-xr-xlang/python/tests/t-decrypt-verify.py1
-rwxr-xr-xlang/python/tests/t-decrypt.py1
-rwxr-xr-xlang/python/tests/t-edit.py4
-rwxr-xr-xlang/python/tests/t-encrypt-large.py1
-rwxr-xr-xlang/python/tests/t-encrypt-sign.py1
-rwxr-xr-xlang/python/tests/t-encrypt-sym.py2
-rwxr-xr-xlang/python/tests/t-encrypt.py16
-rwxr-xr-xlang/python/tests/t-export.py1
-rwxr-xr-xlang/python/tests/t-file-name.py1
-rwxr-xr-xlang/python/tests/t-idiomatic.py2
-rwxr-xr-xlang/python/tests/t-import.py1
-rwxr-xr-xlang/python/tests/t-keylist-from-data.py213
-rwxr-xr-xlang/python/tests/t-keylist.py13
-rwxr-xr-xlang/python/tests/t-protocol-assuan.py9
-rwxr-xr-xlang/python/tests/t-quick-key-creation.py140
-rwxr-xr-xlang/python/tests/t-quick-key-manipulation.py125
-rwxr-xr-xlang/python/tests/t-quick-key-signing.py121
-rwxr-xr-xlang/python/tests/t-quick-subkey-creation.py112
-rwxr-xr-xlang/python/tests/t-sig-notation.py2
-rwxr-xr-xlang/python/tests/t-sign.py2
-rwxr-xr-xlang/python/tests/t-signers.py2
-rwxr-xr-xlang/python/tests/t-trustlist.py1
-rwxr-xr-xlang/python/tests/t-verify.py2
-rwxr-xr-xlang/python/tests/t-wait.py1
-rw-r--r--lang/qt/Makefile.in4
-rw-r--r--lang/qt/doc/Makefile.in4
-rw-r--r--lang/qt/src/Makefile.am9
-rw-r--r--lang/qt/src/Makefile.in13
-rw-r--r--lang/qt/src/QGpgmeConfig.cmake.in.in2
-rw-r--r--lang/qt/src/cryptoconfig.cpp44
-rw-r--r--lang/qt/src/cryptoconfig.h14
-rw-r--r--lang/qt/src/defaultkeygenerationjob.cpp11
-rw-r--r--lang/qt/src/dn.cpp4
-rw-r--r--lang/qt/src/qgpgmenewcryptoconfig.cpp26
-rw-r--r--lang/qt/src/qgpgmenewcryptoconfig.h2
-rw-r--r--lang/qt/src/threadedjobmixin.h1
-rw-r--r--lang/qt/tests/Makefile.am11
-rw-r--r--lang/qt/tests/Makefile.in51
-rw-r--r--lang/qt/tests/t-config.cpp94
-rw-r--r--lang/qt/tests/t-encrypt.cpp125
-rw-r--r--lang/qt/tests/t-keylist.cpp18
-rw-r--r--lang/qt/tests/t-keylocate.cpp26
-rw-r--r--lang/qt/tests/t-ownertrust.cpp18
-rw-r--r--lang/qt/tests/t-support.cpp2
-rw-r--r--lang/qt/tests/t-support.h10
-rw-r--r--lang/qt/tests/t-tofuinfo.cpp291
-rw-r--r--lang/qt/tests/t-various.cpp167
-rw-r--r--lang/qt/tests/t-verify.cpp8
-rw-r--r--lang/qt/tests/t-wkspublish.cpp52
87 files changed, 2898 insertions, 567 deletions
diff --git a/lang/Makefile.in b/lang/Makefile.in
index 647734a..5ebbf14 100644
--- a/lang/Makefile.in
+++ b/lang/Makefile.in
@@ -109,8 +109,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
diff --git a/lang/cl/Makefile.in b/lang/cl/Makefile.in
index 31d9325..4737b80 100644
--- a/lang/cl/Makefile.in
+++ b/lang/cl/Makefile.in
@@ -112,8 +112,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
diff --git a/lang/cl/gpgme.asd b/lang/cl/gpgme.asd
index 5410fad..50046a6 100644
--- a/lang/cl/gpgme.asd
+++ b/lang/cl/gpgme.asd
@@ -27,7 +27,7 @@
(defsystem gpgme
:description "GnuPG Made Easy."
:author "g10 Code GmbH"
- :version "1.8.0"
+ :version "1.9.0"
:licence "GPL"
:depends-on ("cffi" "gpg-error")
:components ((:file "gpgme-package")
diff --git a/lang/cpp/Makefile.in b/lang/cpp/Makefile.in
index 30aef31..f49d1d0 100644
--- a/lang/cpp/Makefile.in
+++ b/lang/cpp/Makefile.in
@@ -110,8 +110,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
diff --git a/lang/cpp/src/GpgmeppConfig.cmake.in.in b/lang/cpp/src/GpgmeppConfig.cmake.in.in
index 928d19f..7f42f31 100644
--- a/lang/cpp/src/GpgmeppConfig.cmake.in.in
+++ b/lang/cpp/src/GpgmeppConfig.cmake.in.in
@@ -63,8 +63,8 @@ add_library(Gpgmepp SHARED IMPORTED)
set_target_properties(Gpgmepp PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "@resolved_includedir@/gpgme++;@resolved_includedir@"
- INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@;@LIBASSUAN_LIBS@"
- IMPORTED_LOCATION "@resolved_libdir@/libgpgmepp.so"
+ INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme.so;@LIBASSUAN_LIBS@"
+ IMPORTED_LOCATION "@resolved_libdir@/libgpgmepp@libsuffix@"
)
if(CMAKE_VERSION VERSION_LESS 2.8.12)
diff --git a/lang/cpp/src/Makefile.am b/lang/cpp/src/Makefile.am
index 92ed784..4028b3d 100644
--- a/lang/cpp/src/Makefile.am
+++ b/lang/cpp/src/Makefile.am
@@ -31,7 +31,8 @@ main_sources = \
signingresult.cpp encryptionresult.cpp \
engineinfo.cpp gpgsetexpirytimeeditinteractor.cpp \
gpgsetownertrusteditinteractor.cpp gpgsignkeyeditinteractor.cpp \
- gpgadduserideditinteractor.cpp defaultassuantransaction.cpp \
+ gpgadduserideditinteractor.cpp gpggencardkeyinteractor.cpp \
+ defaultassuantransaction.cpp \
scdgetinfoassuantransaction.cpp gpgagentgetinfoassuantransaction.cpp \
vfsmountresult.cpp configuration.cpp tofuinfo.cpp swdbresult.cpp
@@ -42,6 +43,7 @@ gpgmepp_headers = \
gpgadduserideditinteractor.h gpgagentgetinfoassuantransaction.h \
gpgmefw.h gpgsetexpirytimeeditinteractor.h \
gpgsetownertrusteditinteractor.h gpgsignkeyeditinteractor.h \
+ gpggencardkeyinteractor.h \
importresult.h keygenerationresult.h key.h keylistresult.h \
notation.h result.h scdgetinfoassuantransaction.h signingresult.h \
trustitem.h verificationresult.h vfsmountresult.h gpgmepp_export.h \
@@ -69,6 +71,12 @@ libgpgmepp_la_LIBADD = ../../../src/libgpgme.la @LIBASSUAN_LIBS@
libgpgmepp_la_LDFLAGS = -no-undefined -version-info \
@LIBGPGMEPP_LT_CURRENT@:@LIBGPGMEPP_LT_REVISION@:@LIBGPGMEPP_LT_AGE@
+if HAVE_MACOS_SYSTEM
+libsuffix=.dylib
+else
+libsuffix=.so
+endif
+
if HAVE_W32_SYSTEM
GpgmeppConfig.cmake: GpgmeppConfig-w32.cmake.in
sed -e 's|[@]resolved_bindir@|$(bindir)|g' < "$<" | \
@@ -77,6 +85,7 @@ GpgmeppConfig.cmake: GpgmeppConfig-w32.cmake.in
else
GpgmeppConfig.cmake: GpgmeppConfig.cmake.in
sed -e 's|[@]resolved_libdir@|$(libdir)|g' < "$<" | \
+ sed -e 's|[@]libsuffix@|$(libsuffix)|g' | \
sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@
endif
install-cmake-files: GpgmeppConfig.cmake GpgmeppConfigVersion.cmake
diff --git a/lang/cpp/src/Makefile.in b/lang/cpp/src/Makefile.in
index 45f6219..27bf982 100644
--- a/lang/cpp/src/Makefile.in
+++ b/lang/cpp/src/Makefile.in
@@ -117,8 +117,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
@@ -166,8 +166,8 @@ am__objects_1 = exception.lo context.lo key.lo trustitem.lo data.lo \
encryptionresult.lo engineinfo.lo \
gpgsetexpirytimeeditinteractor.lo \
gpgsetownertrusteditinteractor.lo gpgsignkeyeditinteractor.lo \
- gpgadduserideditinteractor.lo defaultassuantransaction.lo \
- scdgetinfoassuantransaction.lo \
+ gpgadduserideditinteractor.lo gpggencardkeyinteractor.lo \
+ defaultassuantransaction.lo scdgetinfoassuantransaction.lo \
gpgagentgetinfoassuantransaction.lo vfsmountresult.lo \
configuration.lo tofuinfo.lo swdbresult.lo
am__objects_2 =
@@ -470,7 +470,8 @@ main_sources = \
signingresult.cpp encryptionresult.cpp \
engineinfo.cpp gpgsetexpirytimeeditinteractor.cpp \
gpgsetownertrusteditinteractor.cpp gpgsignkeyeditinteractor.cpp \
- gpgadduserideditinteractor.cpp defaultassuantransaction.cpp \
+ gpgadduserideditinteractor.cpp gpggencardkeyinteractor.cpp \
+ defaultassuantransaction.cpp \
scdgetinfoassuantransaction.cpp gpgagentgetinfoassuantransaction.cpp \
vfsmountresult.cpp configuration.cpp tofuinfo.cpp swdbresult.cpp
@@ -481,6 +482,7 @@ gpgmepp_headers = \
gpgadduserideditinteractor.h gpgagentgetinfoassuantransaction.h \
gpgmefw.h gpgsetexpirytimeeditinteractor.h \
gpgsetownertrusteditinteractor.h gpgsignkeyeditinteractor.h \
+ gpggencardkeyinteractor.h \
importresult.h keygenerationresult.h key.h keylistresult.h \
notation.h result.h scdgetinfoassuantransaction.h signingresult.h \
trustitem.h verificationresult.h vfsmountresult.h gpgmepp_export.h \
@@ -507,6 +509,8 @@ libgpgmepp_la_LIBADD = ../../../src/libgpgme.la @LIBASSUAN_LIBS@
libgpgmepp_la_LDFLAGS = -no-undefined -version-info \
@LIBGPGMEPP_LT_CURRENT@:@LIBGPGMEPP_LT_REVISION@:@LIBGPGMEPP_LT_AGE@
+@HAVE_MACOS_SYSTEM_FALSE@libsuffix = .so
+@HAVE_MACOS_SYSTEM_TRUE@libsuffix = .dylib
CLEANFILES = GpgmeppConfig.cmake GpgmeppConfigVersion.cmake \
gpgmepp_version.h GpgmeppConfig.cmake.in
@@ -611,6 +615,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exception.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgadduserideditinteractor.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgagentgetinfoassuantransaction.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpggencardkeyinteractor.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgsetexpirytimeeditinteractor.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgsetownertrusteditinteractor.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgsignkeyeditinteractor.Plo@am__quote@
@@ -943,6 +948,7 @@ uninstall-am: uninstall-gpgmeppincludeHEADERS uninstall-libLTLIBRARIES \
@HAVE_W32_SYSTEM_TRUE@ sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@
@HAVE_W32_SYSTEM_FALSE@GpgmeppConfig.cmake: GpgmeppConfig.cmake.in
@HAVE_W32_SYSTEM_FALSE@ sed -e 's|[@]resolved_libdir@|$(libdir)|g' < "$<" | \
+@HAVE_W32_SYSTEM_FALSE@ sed -e 's|[@]libsuffix@|$(libsuffix)|g' | \
@HAVE_W32_SYSTEM_FALSE@ sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@
install-cmake-files: GpgmeppConfig.cmake GpgmeppConfigVersion.cmake
-$(INSTALL) -d $(DESTDIR)$(libdir)/cmake/Gpgmepp
diff --git a/lang/cpp/src/context.cpp b/lang/cpp/src/context.cpp
index ada7bea..77962d8 100644
--- a/lang/cpp/src/context.cpp
+++ b/lang/cpp/src/context.cpp
@@ -280,6 +280,11 @@ std::unique_ptr<Context> Context::createForEngine(Engine eng, Error *error)
return std::unique_ptr<Context>(new Context(ctx));
}
+void Context::setDecryptionFlags(DecryptionFlags flags)
+{
+ d->decryptFlags = flags;
+}
+
//
//
// Context::Private
@@ -294,7 +299,8 @@ Context::Private::Private(gpgme_ctx_t c)
lastAssuanInquireData(Data::null),
lastAssuanTransaction(),
lastEditInteractor(),
- lastCardEditInteractor()
+ lastCardEditInteractor(),
+ decryptFlags(DecryptNone)
{
}
@@ -904,21 +910,32 @@ std::unique_ptr<AssuanTransaction> Context::takeLastAssuanTransaction()
return std::move(d->lastAssuanTransaction);
}
-DecryptionResult Context::decrypt(const Data &cipherText, Data &plainText)
+DecryptionResult Context::decrypt(const Data &cipherText, Data &plainText, const DecryptionFlags flags)
{
d->lastop = Private::Decrypt;
const Data::Private *const cdp = cipherText.impl();
Data::Private *const pdp = plainText.impl();
- d->lasterr = gpgme_op_decrypt(d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0);
+ d->lasterr = gpgme_op_decrypt_ext(d->ctx, static_cast<gpgme_decrypt_flags_t> (d->decryptFlags | flags), cdp ? cdp->data : 0, pdp ? pdp->data : 0);
return DecryptionResult(d->ctx, Error(d->lasterr));
}
-Error Context::startDecryption(const Data &cipherText, Data &plainText)
+DecryptionResult Context::decrypt(const Data &cipherText, Data &plainText)
+{
+ return decrypt(cipherText, plainText, DecryptNone);
+}
+
+Error Context::startDecryption(const Data &cipherText, Data &plainText, const DecryptionFlags flags)
{
d->lastop = Private::Decrypt;
const Data::Private *const cdp = cipherText.impl();
Data::Private *const pdp = plainText.impl();
- return Error(d->lasterr = gpgme_op_decrypt_start(d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0));
+ return Error(d->lasterr = gpgme_op_decrypt_ext_start(d->ctx, static_cast<gpgme_decrypt_flags_t> (d->decryptFlags | flags),
+ cdp ? cdp->data : 0, pdp ? pdp->data : 0));
+}
+
+Error Context::startDecryption(const Data &cipherText, Data &plainText)
+{
+ return startDecryption(cipherText, plainText, DecryptNone);
}
DecryptionResult Context::decryptionResult() const
@@ -973,22 +990,33 @@ VerificationResult Context::verificationResult() const
}
}
-std::pair<DecryptionResult, VerificationResult> Context::decryptAndVerify(const Data &cipherText, Data &plainText)
+std::pair<DecryptionResult, VerificationResult> Context::decryptAndVerify(const Data &cipherText, Data &plainText, DecryptionFlags flags)
{
d->lastop = Private::DecryptAndVerify;
const Data::Private *const cdp = cipherText.impl();
Data::Private *const pdp = plainText.impl();
- d->lasterr = gpgme_op_decrypt_verify(d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0);
+ d->lasterr = gpgme_op_decrypt_ext(d->ctx, static_cast<gpgme_decrypt_flags_t> (d->decryptFlags | flags | DecryptVerify),
+ cdp ? cdp->data : 0, pdp ? pdp->data : 0);
return std::make_pair(DecryptionResult(d->ctx, Error(d->lasterr)),
VerificationResult(d->ctx, Error(d->lasterr)));
}
-Error Context::startCombinedDecryptionAndVerification(const Data &cipherText, Data &plainText)
+std::pair<DecryptionResult, VerificationResult> Context::decryptAndVerify(const Data &cipherText, Data &plainText)
+{
+ return decryptAndVerify(cipherText, plainText, DecryptNone);
+}
+
+Error Context::startCombinedDecryptionAndVerification(const Data &cipherText, Data &plainText, DecryptionFlags flags)
{
d->lastop = Private::DecryptAndVerify;
const Data::Private *const cdp = cipherText.impl();
Data::Private *const pdp = plainText.impl();
- return Error(d->lasterr = gpgme_op_decrypt_verify_start(d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0));
+ return Error(d->lasterr = gpgme_op_decrypt_ext_start(d->ctx, static_cast<gpgme_decrypt_flags_t> (d->decryptFlags | flags | DecryptVerify), cdp ? cdp->data : 0, pdp ? pdp->data : 0));
+}
+
+Error Context::startCombinedDecryptionAndVerification(const Data &cipherText, Data &plainText)
+{
+ return startCombinedDecryptionAndVerification(cipherText, plainText, DecryptNone);
}
unsigned int to_auditlog_flags(unsigned int flags)
@@ -1376,6 +1404,30 @@ Error Context::setTofuPolicyStart(const Key &k, unsigned int policy)
k.impl(), to_tofu_policy_t(policy)));
}
+Error Context::addUid(const Key &k, const char *userid)
+{
+ return Error(d->lasterr = gpgme_op_adduid(d->ctx,
+ k.impl(), userid, 0));
+}
+
+Error Context::startAddUid(const Key &k, const char *userid)
+{
+ return Error(d->lasterr = gpgme_op_adduid_start(d->ctx,
+ k.impl(), userid, 0));
+}
+
+Error Context::revUid(const Key &k, const char *userid)
+{
+ return Error(d->lasterr = gpgme_op_revuid(d->ctx,
+ k.impl(), userid, 0));
+}
+
+Error Context::startRevUid(const Key &k, const char *userid)
+{
+ return Error(d->lasterr = gpgme_op_revuid_start(d->ctx,
+ k.impl(), userid, 0));
+}
+
// Engine Spawn stuff
Error Context::spawn(const char *file, const char *argv[],
Data &input, Data &output, Data &err,
diff --git a/lang/cpp/src/context.h b/lang/cpp/src/context.h
index 2c205b0..bec4e39 100644
--- a/lang/cpp/src/context.h
+++ b/lang/cpp/src/context.h
@@ -214,6 +214,12 @@ public:
GpgME::Error edit(const Key &key, std::unique_ptr<EditInteractor> function, Data &out);
GpgME::Error startEditing(const Key &key, std::unique_ptr<EditInteractor> function, Data &out);
+ Error addUid(const Key &key, const char *userid);
+ Error startAddUid(const Key &key, const char *userid);
+
+ Error revUid(const Key &key, const char *userid);
+ Error startRevUid(const Key &key, const char *userid);
+
// using TofuInfo::Policy
Error setTofuPolicy(const Key &k, unsigned int policy);
Error setTofuPolicyStart(const Key &k, unsigned int policy);
@@ -255,14 +261,28 @@ public:
//
// Crypto Operations
//
- //
+
+ enum DecryptionFlags {
+ // Keep in line with core's flags
+ DecryptNone = 0,
+ DecryptVerify = 1,
+ DecryptUnwrap = 128,
+ DecryptMaxValue = 0x80000000
+ };
//
// Decryption
//
+ // Alternative way to set decryption flags as they were added only in
+ // 1.9.0 and so other API can still be used but with 1.9.0 additionally
+ // flags can be set.
+ void setDecryptionFlags (const DecryptionFlags flags);
+
DecryptionResult decrypt(const Data &cipherText, Data &plainText);
GpgME::Error startDecryption(const Data &cipherText, Data &plainText);
+ DecryptionResult decrypt(const Data &cipherText, Data &plainText, const DecryptionFlags flags);
+ GpgME::Error startDecryption(const Data &cipherText, Data &plainText, const DecryptionFlags flags);
DecryptionResult decryptionResult() const;
//
@@ -280,7 +300,9 @@ public:
//
std::pair<DecryptionResult, VerificationResult> decryptAndVerify(const Data &cipherText, Data &plainText);
+ std::pair<DecryptionResult, VerificationResult> decryptAndVerify(const Data &cipherText, Data &plainText, const DecryptionFlags flags);
GpgME::Error startCombinedDecryptionAndVerification(const Data &cipherText, Data &plainText);
+ GpgME::Error startCombinedDecryptionAndVerification(const Data &cipherText, Data &plainText, const DecryptionFlags flags);
// use verificationResult() and decryptionResult() to retrieve the result objects...
//
@@ -319,7 +341,9 @@ public:
Prepare = 4,
ExpectSign = 8,
NoCompress = 16,
- Symmetric = 32
+ Symmetric = 32,
+ ThrowKeyIds = 64,
+ EncryptWrap = 128
};
EncryptionResult encrypt(const std::vector<Key> &recipients, const Data &plainText, Data &cipherText, EncryptionFlags flags);
GpgME::Error encryptSymmetrically(const Data &plainText, Data &cipherText);
diff --git a/lang/cpp/src/context_p.h b/lang/cpp/src/context_p.h
index be34783..d53da0a 100644
--- a/lang/cpp/src/context_p.h
+++ b/lang/cpp/src/context_p.h
@@ -77,6 +77,7 @@ public:
Data lastAssuanInquireData;
std::unique_ptr<AssuanTransaction> lastAssuanTransaction;
std::unique_ptr<EditInteractor> lastEditInteractor, lastCardEditInteractor;
+ DecryptionFlags decryptFlags;
};
} // namespace GpgME
diff --git a/lang/cpp/src/data.cpp b/lang/cpp/src/data.cpp
index 2cb4fa8..32ca561 100644
--- a/lang/cpp/src/data.cpp
+++ b/lang/cpp/src/data.cpp
@@ -25,6 +25,7 @@
#endif
#include "data_p.h"
+#include "context_p.h"
#include <error.h>
#include <interfaces/dataprovider.h>
@@ -230,3 +231,26 @@ off_t GpgME::Data::seek(off_t offset, int whence)
{
return gpgme_data_seek(d->data, offset, whence);
}
+
+std::vector<GpgME::Key> GpgME::Data::toKeys(Protocol proto) const
+{
+ std::vector<GpgME::Key> ret;
+ if (isNull()) {
+ return ret;
+ }
+ auto ctx = GpgME::Context::createForProtocol(proto);
+ if (!ctx) {
+ return ret;
+ }
+
+ if (gpgme_op_keylist_from_data_start (ctx->impl()->ctx, d->data, 0)) {
+ return ret;
+ }
+
+ gpgme_key_t key;
+ while (!gpgme_op_keylist_next (ctx->impl()->ctx, &key)) {
+ ret.push_back(GpgME::Key(key, false));
+ }
+ delete ctx;
+ return ret;
+}
diff --git a/lang/cpp/src/data.h b/lang/cpp/src/data.h
index 50bdf62..cc7906f 100644
--- a/lang/cpp/src/data.h
+++ b/lang/cpp/src/data.h
@@ -24,6 +24,7 @@
#define __GPGMEPP_DATA_H__
#include "global.h"
+#include "key.h"
#include <sys/types.h> // for size_t, off_t
#include <cstdio> // FILE
@@ -109,6 +110,10 @@ public:
ssize_t write(const void *buffer, size_t length);
off_t seek(off_t offset, int whence);
+ /** Try to parse the data to a key object using the
+ * Protocol proto. Returns an empty list on error.*/
+ std::vector<Key> toKeys(const Protocol proto = Protocol::OpenPGP) const;
+
class Private;
Private *impl()
{
diff --git a/lang/cpp/src/editinteractor.cpp b/lang/cpp/src/editinteractor.cpp
index 31591fa..b652bda 100644
--- a/lang/cpp/src/editinteractor.cpp
+++ b/lang/cpp/src/editinteractor.cpp
@@ -212,6 +212,8 @@ bool EditInteractor::needsNoResponse(unsigned int status) const
case GPGME_STATUS_KEY_CREATED:
case GPGME_STATUS_NEED_PASSPHRASE_SYM:
case GPGME_STATUS_SC_OP_FAILURE:
+ case GPGME_STATUS_CARDCTRL:
+ case GPGME_STATUS_BACKUP_KEY_CREATED:
return false;
default:
return true;
diff --git a/lang/cpp/src/gpggencardkeyinteractor.cpp b/lang/cpp/src/gpggencardkeyinteractor.cpp
new file mode 100644
index 0000000..90329e2
--- /dev/null
+++ b/lang/cpp/src/gpggencardkeyinteractor.cpp
@@ -0,0 +1,332 @@
+/*
+ gpggencardkeyinteractor.cpp - Edit Interactor to generate a key on a card
+ Copyright (C) 2017 Intevation GmbH
+
+ This file is part of GPGME++.
+
+ GPGME++ is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ GPGME++ is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with GPGME++; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "gpggencardkeyinteractor.h"
+
+#include "error.h"
+
+#include <gpgme.h>
+
+using namespace GpgME;
+
+class GpgGenCardKeyInteractor::Private
+{
+public:
+ Private() : keysize(2048), backup(false)
+ {
+
+ }
+ std::string name, email, backupFileName, expiry, serial;
+ int keysize;
+ bool backup;
+};
+
+GpgGenCardKeyInteractor::~GpgGenCardKeyInteractor() {}
+
+GpgGenCardKeyInteractor::GpgGenCardKeyInteractor(const std::string &serial):
+ d(new Private)
+{
+ d->serial = serial;
+}
+
+void GpgGenCardKeyInteractor::setNameUtf8(const std::string &name)
+{
+ d->name = name;
+}
+
+void GpgGenCardKeyInteractor::setEmailUtf8(const std::string &email)
+{
+ d->email = email;
+}
+
+void GpgGenCardKeyInteractor::setDoBackup(bool value)
+{
+ d->backup = value;
+}
+
+void GpgGenCardKeyInteractor::setKeySize(int value)
+{
+ d->keysize = value;
+}
+
+void GpgGenCardKeyInteractor::setExpiry(const std::string &timeStr)
+{
+ d->expiry = timeStr;
+}
+
+std::string GpgGenCardKeyInteractor::backupFileName() const
+{
+ return d->backupFileName;
+}
+
+namespace GpgGenCardKeyInteractor_Private
+{
+enum {
+ START = EditInteractor::StartState,
+ DO_ADMIN,
+ EXPIRE,
+
+ GOT_SERIAL,
+ COMMAND,
+ NAME,
+ EMAIL,
+ COMMENT,
+ BACKUP,
+ REPLACE,
+ SIZE,
+ SIZE2,
+ SIZE3,
+ BACKUP_KEY_CREATED,
+ KEY_CREATED,
+ QUIT,
+ SAVE,
+
+ ERROR = EditInteractor::ErrorState
+};
+}
+
+const char *GpgGenCardKeyInteractor::action(Error &err) const
+{
+
+ using namespace GpgGenCardKeyInteractor_Private;
+
+ switch (state()) {
+ case DO_ADMIN:
+ return "admin";
+ case COMMAND:
+ return "generate";
+ case NAME:
+ return d->name.c_str();
+ case EMAIL:
+ return d->email.c_str();
+ case EXPIRE:
+ return d->expiry.c_str();
+ case BACKUP:
+ return d->backup ? "Y" : "N";
+ case REPLACE:
+ return "Y";
+ case SIZE:
+ case SIZE2:
+ case SIZE3:
+ return std::to_string(d->keysize).c_str();
+ case COMMENT:
+ return "";
+ case SAVE:
+ return "Y";
+ case QUIT:
+ return "quit";
+ case KEY_CREATED:
+ case START:
+ case GOT_SERIAL:
+ case BACKUP_KEY_CREATED:
+ case ERROR:
+ return 0;
+ default:
+ err = Error::fromCode(GPG_ERR_GENERAL);
+ return 0;
+ }
+}
+
+unsigned int GpgGenCardKeyInteractor::nextState(unsigned int status, const char *args, Error &err) const
+{
+
+ static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL);
+ static const Error INV_NAME_ERROR = Error::fromCode(GPG_ERR_INV_NAME);
+ static const Error INV_EMAIL_ERROR = Error::fromCode(GPG_ERR_INV_USER_ID);
+ static const Error INV_COMMENT_ERROR = Error::fromCode(GPG_ERR_INV_USER_ID);
+
+ if (needsNoResponse(status)) {
+ return state();
+ }
+
+ using namespace GpgGenCardKeyInteractor_Private;
+
+ switch (state()) {
+ case START:
+ if (status == GPGME_STATUS_CARDCTRL &&
+ !d->serial.empty()) {
+ const std::string sArgs = args;
+ if (sArgs.find(d->serial) == std::string::npos) {
+ // Wrong smartcard
+ err = Error::fromCode(GPG_ERR_WRONG_CARD);
+ return ERROR;
+ } else {
+ printf("EditInteractor: Confirmed S/N: %s %s\n",
+ d->serial.c_str(), sArgs.c_str());
+ }
+ return GOT_SERIAL;
+ } else if (d->serial.empty()) {
+ return GOT_SERIAL;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case GOT_SERIAL:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "cardedit.prompt") == 0) {
+ return DO_ADMIN;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case DO_ADMIN:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "cardedit.prompt") == 0) {
+ return COMMAND;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case COMMAND:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "cardedit.genkeys.backup_enc") == 0) {
+ return BACKUP;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case BACKUP:
+ if (status == GPGME_STATUS_GET_BOOL &&
+ strcmp(args, "cardedit.genkeys.replace_keys") == 0) {
+ return REPLACE;
+ }
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "cardedit.genkeys.size") == 0) {
+ return SIZE;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case REPLACE:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "cardedit.genkeys.size") == 0) {
+ printf("Moving to SIZE\n");
+ return SIZE;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case SIZE:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "cardedit.genkeys.size") == 0) {
+ return SIZE2;
+ }
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.valid") == 0) {
+ return EXPIRE;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case SIZE2:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "cardedit.genkeys.size") == 0) {
+ return SIZE3;
+ }
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.valid") == 0) {
+ return EXPIRE;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case SIZE3:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.valid") == 0) {
+ return EXPIRE;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case EXPIRE:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.name") == 0) {
+ return NAME;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case NAME:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.email") == 0) {
+ return EMAIL;
+ }
+ err = GENERAL_ERROR;
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.name") == 0) {
+ err = INV_NAME_ERROR;
+ }
+ return ERROR;
+ case EMAIL:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.comment") == 0) {
+ return COMMENT;
+ }
+ err = GENERAL_ERROR;
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.email") == 0) {
+ err = INV_EMAIL_ERROR;
+ }
+ return ERROR;
+ case COMMENT:
+ if (status == GPGME_STATUS_BACKUP_KEY_CREATED) {
+ std::string sArgs = args;
+ const auto pos = sArgs.rfind(" ");
+ if (pos != std::string::npos) {
+ d->backupFileName = sArgs.substr(pos + 1);
+ return BACKUP_KEY_CREATED;
+ }
+ }
+ if (status == GPGME_STATUS_KEY_CREATED) {
+ return KEY_CREATED;
+ }
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keyedit.prompt") == 0) {
+ return QUIT;
+ }
+ err = GENERAL_ERROR;
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keygen.comment") == 0) {
+ err = INV_COMMENT_ERROR;
+ }
+ return ERROR;
+ case BACKUP_KEY_CREATED:
+ if (status == GPGME_STATUS_KEY_CREATED) {
+ return KEY_CREATED;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case KEY_CREATED:
+ return QUIT;
+ case QUIT:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "cardedit.prompt") == 0) {
+ return QUIT;
+ }
+ err = GENERAL_ERROR;
+ return ERROR;
+ case ERROR:
+ if (status == GPGME_STATUS_GET_LINE &&
+ strcmp(args, "keyedit.prompt") == 0) {
+ return QUIT;
+ }
+ err = lastError();
+ return ERROR;
+ default:
+ err = GENERAL_ERROR;
+ return ERROR;
+ }
+}
diff --git a/lang/cpp/src/gpggencardkeyinteractor.h b/lang/cpp/src/gpggencardkeyinteractor.h
new file mode 100644
index 0000000..c6b17d1
--- /dev/null
+++ b/lang/cpp/src/gpggencardkeyinteractor.h
@@ -0,0 +1,71 @@
+/*
+ gpggencardkeyinteractor.h - Edit Interactor to generate a key on a card
+ Copyright (C) 2017 Intevation GmbH
+
+ This file is part of GPGME++.
+
+ GPGME++ is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ GPGME++ is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with GPGME++; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __GPGMEPP_GPGGENCARDKEYEDITINTERACTOR_H__
+#define __GPGMEPP_GPGGENCARDKEYEDITINTERACTOR_H__
+
+#include <editinteractor.h>
+
+#include <string>
+#include <memory>
+
+namespace GpgME
+{
+
+class GPGMEPP_EXPORT GpgGenCardKeyInteractor: public EditInteractor
+{
+public:
+ /** Edit interactor to generate a key on a smartcard.
+ *
+ * The \a serialnumber argument is intended to safeguard
+ * against accidentally working on the wrong smartcard.
+ *
+ * The edit interactor will fail if the card did not match.
+ *
+ * @param serialnumber: Serialnumber of the intended card.
+ **/
+ explicit GpgGenCardKeyInteractor(const std::string &serialnumber);
+ ~GpgGenCardKeyInteractor();
+
+ /** Set the key sizes for the subkeys (default 2048) */
+ void setKeySize(int size);
+
+ void setNameUtf8(const std::string &name);
+ void setEmailUtf8(const std::string &email);
+
+ void setDoBackup(bool value);
+ void setExpiry(const std::string &timeString);
+
+ std::string backupFileName() const;
+
+private:
+ /* reimp */ const char *action(Error &err) const;
+ /* reimp */ unsigned int nextState(unsigned int statusCode, const char *args, Error &err) const;
+
+private:
+ class Private;
+ std::shared_ptr<Private> d;
+};
+
+} // namespace GpgME
+
+#endif // __GPGMEPP_GPGGENCARDKEYEDITINTERACTOR_H__
diff --git a/lang/cpp/src/key.cpp b/lang/cpp/src/key.cpp
index 235a3c8..31e59e1 100644
--- a/lang/cpp/src/key.cpp
+++ b/lang/cpp/src/key.cpp
@@ -234,6 +234,11 @@ bool Key::isQualified() const
return key && key->is_qualified;
}
+bool Key::isDeVs() const
+{
+ return key && key->subkeys && key->subkeys->is_de_vs;
+}
+
const char *Key::issuerSerial() const
{
return key ? key->issuer_serial : 0 ;
@@ -341,7 +346,12 @@ void Key::update()
KeyListMode::Validate |
KeyListMode::WithTofu);
Error err;
- auto newKey = ctx->key(primaryFingerprint(), err, hasSecret());
+ auto newKey = ctx->key(primaryFingerprint(), err, true);
+ // Not secret so we get the information from the pubring.
+ if (newKey.isNull())
+ {
+ newKey = ctx->key(primaryFingerprint(), err, false);
+ }
delete ctx;
if (err) {
return;
@@ -464,6 +474,11 @@ bool Subkey::isQualified() const
return subkey && subkey->is_qualified;
}
+bool Subkey::isDeVs() const
+{
+ return subkey && subkey->is_de_vs;
+}
+
bool Subkey::isCardKey() const
{
return subkey && subkey->is_cardkey;
@@ -471,7 +486,12 @@ bool Subkey::isCardKey() const
const char *Subkey::cardSerialNumber() const
{
- return subkey ? subkey->card_number : 0 ;
+ return subkey ? subkey->card_number : nullptr;
+}
+
+const char *Subkey::keyGrip() const
+{
+ return subkey ? subkey->keygrip : nullptr;
}
bool Subkey::isSecret() const
@@ -894,7 +914,39 @@ std::string UserID::addrSpecFromString(const char *userid)
std::string UserID::addrSpec() const
{
- return addrSpecFromString(email());
+ if (!uid || !uid->address) {
+ return std::string();
+ }
+
+ return uid->address;
+}
+
+Error UserID::revoke()
+{
+ if (isNull()) {
+ return Error::fromCode(GPG_ERR_GENERAL);
+ }
+ auto ctx = Context::createForProtocol(parent().protocol());
+ if (!ctx) {
+ return Error::fromCode(GPG_ERR_INV_ENGINE);
+ }
+ Error ret = ctx->revUid(key, id());
+ delete ctx;
+ return ret;
+}
+
+Error Key::addUid(const char *uid)
+{
+ if (isNull()) {
+ return Error::fromCode(GPG_ERR_GENERAL);
+ }
+ auto ctx = Context::createForProtocol(protocol());
+ if (!ctx) {
+ return Error::fromCode(GPG_ERR_INV_ENGINE);
+ }
+ Error ret = ctx->addUid(key, uid);
+ delete ctx;
+ return ret;
}
std::ostream &operator<<(std::ostream &os, const UserID &uid)
@@ -903,6 +955,7 @@ std::ostream &operator<<(std::ostream &os, const UserID &uid)
if (!uid.isNull()) {
os << "\n name: " << protect(uid.name())
<< "\n email: " << protect(uid.email())
+ << "\n mbox: " << uid.addrSpec()
<< "\n comment: " << protect(uid.comment())
<< "\n validity: " << uid.validityAsString()
<< "\n revoked: " << uid.isRevoked()
diff --git a/lang/cpp/src/key.h b/lang/cpp/src/key.h
index 3f596a8..829bd26 100644
--- a/lang/cpp/src/key.h
+++ b/lang/cpp/src/key.h
@@ -112,6 +112,7 @@ public:
bool canCertify() const;
bool canAuthenticate() const;
bool isQualified() const;
+ bool isDeVs() const;
bool hasSecret() const;
GPGMEPP_DEPRECATED bool isSecret() const
@@ -152,6 +153,17 @@ public:
* how long the keylisting takes.*/
void update();
+ /**
+ * @brief Add a user id to this key.
+ *
+ * Needs gnupg 2.1.13 and the key needs to be updated
+ * afterwards to see the new uid.
+ *
+ * @param uid should be fully formated and UTF-8 encoded.
+ *
+ * @returns a possible error.
+ **/
+ Error addUid(const char *uid);
private:
gpgme_key_t impl() const
{
@@ -208,6 +220,7 @@ public:
bool canCertify() const;
bool canAuthenticate() const;
bool isQualified() const;
+ bool isDeVs() const;
bool isCardKey() const;
bool isSecret() const;
@@ -259,6 +272,8 @@ public:
const char *cardSerialNumber() const;
+ const char *keyGrip() const;
+
private:
shared_gpgme_key_t key;
gpgme_sub_key_t subkey;
@@ -335,6 +350,13 @@ public:
* @returns a normalized mail address for this userid
* or an empty string. */
std::string addrSpec() const;
+
+ /*! Revoke the user id.
+ *
+ * Key needs update afterwards.
+ *
+ * @returns an error on error.*/
+ Error revoke();
private:
shared_gpgme_key_t key;
gpgme_user_id_t uid;
diff --git a/lang/cpp/src/verificationresult.cpp b/lang/cpp/src/verificationresult.cpp
index 23c458e..42e483c 100644
--- a/lang/cpp/src/verificationresult.cpp
+++ b/lang/cpp/src/verificationresult.cpp
@@ -413,7 +413,8 @@ GpgME::Key GpgME::Signature::key(bool search, bool update) const
}
}
if (update) {
- ret.update();
+ d->keys[idx].update();
+ ret = d->keys[idx];
}
return ret;
}
diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
index e32fd12..d91ead9 100644
--- a/lang/python/Makefile.am
+++ b/lang/python/Makefile.am
@@ -46,42 +46,47 @@ COPY_FILES_GPG = \
# For VPATH builds we need to copy some files because Python's
# distutils are not VPATH-aware.
-copystamp: $(COPY_FILES) $(COPY_FILES_GPG) data.h config.h
- if test "$(srcdir)" != "$(builddir)" ; then \
- cp -R $(COPY_FILES) . ; \
- cp -R $(COPY_FILES_GPG) gpg ; \
- fi
+copystamp: $(COPY_FILES) $(COPY_FILES_GPG)
+ set -e ; for VERSION in $(PYTHON_VERSIONS); do \
+ $(MKDIR_P) python$${VERSION}-gpg/gpg ; \
+ cp -R $(COPY_FILES) python$${VERSION}-gpg ; \
+ cp setup.py python$${VERSION}-gpg ; \
+ cp gpg/version.py python$${VERSION}-gpg/gpg ; \
+ ln -sf "$(abs_top_srcdir)/src/data.h" python$${VERSION}-gpg ; \
+ ln -sf "$(abs_top_builddir)/config.h" python$${VERSION}-gpg ; \
+ cp -R $(COPY_FILES_GPG) python$${VERSION}-gpg/gpg ; \
+ done
touch $@
-data.h:
- ln -s "$(top_srcdir)/src/data.h" .
-
-config.h:
- ln -s "$(top_builddir)/config.h" .
-
all-local: copystamp
- for PYTHON in $(PYTHONS); do \
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+ cd python$${VERSION}-gpg && \
CFLAGS="$(CFLAGS)" \
$$PYTHON setup.py build --verbose ; \
+ cd .. ; \
done
-dist/gpg-$(VERSION).tar.gz dist/gpg-$(VERSION).tar.gz.asc: copystamp
+python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp
+ cd python$(PYTHON_VERSION)-gpg && \
CFLAGS="$(CFLAGS)" \
$(PYTHON) setup.py sdist --verbose
- gpg2 --detach-sign --armor dist/gpg-$(VERSION).tar.gz
+ gpg2 --detach-sign --armor python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz
.PHONY: prepare
prepare: copystamp
.PHONY: sdist
-sdist: dist/gpg-$(VERSION).tar.gz dist/gpg-$(VERSION).tar.gz.asc
+sdist: python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc
.PHONY: upload
-upload: dist/gpg-$(VERSION).tar.gz dist/gpg-$(VERSION).tar.gz.asc
+upload: python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc
twine upload $^
-CLEANFILES = gpgme.h errors.i gpgme_wrap.c gpg/gpgme.py \
- data.h config.h copystamp
+CLEANFILES = copystamp
# Remove the rest.
#
@@ -89,23 +94,23 @@ CLEANFILES = gpgme.h errors.i gpgme_wrap.c gpg/gpgme.py \
# permissions.
clean-local:
rm -rf -- build
- if test "$(srcdir)" != "$(builddir)" ; then \
- find . -type d ! -perm -200 -exec chmod u+w {} ';' ; \
- for F in $(COPY_FILES); do rm -rf -- `basename $$F` ; done ; \
- for F in $(COPY_FILES_GPG); do \
- rm -rf -- gpg/`basename $$F` ; \
- done ; \
- fi
+ for VERSION in $(PYTHON_VERSIONS); do \
+ find python$${VERSION}-gpg -type d ! -perm -200 -exec chmod u+w {} ';' ; \
+ rm -rf -- python$${VERSION}-gpg ; \
+ done
install-exec-local:
rm -f install_files.txt
- for PYTHON in $(PYTHONS); do \
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+ cd python$${VERSION}-gpg ; \
$$PYTHON setup.py install \
--prefix $(DESTDIR)$(prefix) \
--record files.txt \
--verbose ; \
- cat files.txt >> install_files.txt ; \
+ cat files.txt >> ../install_files.txt ; \
rm files.txt ; \
+ cd .. ; \
done
$(MKDIR_P) $(DESTDIR)$(pythondir)/gpg
mv install_files.txt $(DESTDIR)$(pythondir)/gpg
diff --git a/lang/python/Makefile.in b/lang/python/Makefile.in
index 4168708..6c96f84 100644
--- a/lang/python/Makefile.in
+++ b/lang/python/Makefile.in
@@ -109,8 +109,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
@@ -420,9 +420,7 @@ COPY_FILES_GPG = \
$(srcdir)/gpg/results.py \
$(srcdir)/gpg/util.py
-CLEANFILES = gpgme.h errors.i gpgme_wrap.c gpg/gpgme.py \
- data.h config.h copystamp
-
+CLEANFILES = copystamp
all: all-recursive
.SUFFIXES:
@@ -740,38 +738,44 @@ uninstall-am: uninstall-local
# For VPATH builds we need to copy some files because Python's
# distutils are not VPATH-aware.
-copystamp: $(COPY_FILES) $(COPY_FILES_GPG) data.h config.h
- if test "$(srcdir)" != "$(builddir)" ; then \
- cp -R $(COPY_FILES) . ; \
- cp -R $(COPY_FILES_GPG) gpg ; \
- fi
+copystamp: $(COPY_FILES) $(COPY_FILES_GPG)
+ set -e ; for VERSION in $(PYTHON_VERSIONS); do \
+ $(MKDIR_P) python$${VERSION}-gpg/gpg ; \
+ cp -R $(COPY_FILES) python$${VERSION}-gpg ; \
+ cp setup.py python$${VERSION}-gpg ; \
+ cp gpg/version.py python$${VERSION}-gpg/gpg ; \
+ ln -sf "$(abs_top_srcdir)/src/data.h" python$${VERSION}-gpg ; \
+ ln -sf "$(abs_top_builddir)/config.h" python$${VERSION}-gpg ; \
+ cp -R $(COPY_FILES_GPG) python$${VERSION}-gpg/gpg ; \
+ done
touch $@
-data.h:
- ln -s "$(top_srcdir)/src/data.h" .
-
-config.h:
- ln -s "$(top_builddir)/config.h" .
-
all-local: copystamp
- for PYTHON in $(PYTHONS); do \
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+ cd python$${VERSION}-gpg && \
CFLAGS="$(CFLAGS)" \
$$PYTHON setup.py build --verbose ; \
+ cd .. ; \
done
-dist/gpg-$(VERSION).tar.gz dist/gpg-$(VERSION).tar.gz.asc: copystamp
+python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp
+ cd python$(PYTHON_VERSION)-gpg && \
CFLAGS="$(CFLAGS)" \
$(PYTHON) setup.py sdist --verbose
- gpg2 --detach-sign --armor dist/gpg-$(VERSION).tar.gz
+ gpg2 --detach-sign --armor python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz
.PHONY: prepare
prepare: copystamp
.PHONY: sdist
-sdist: dist/gpg-$(VERSION).tar.gz dist/gpg-$(VERSION).tar.gz.asc
+sdist: python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc
.PHONY: upload
-upload: dist/gpg-$(VERSION).tar.gz dist/gpg-$(VERSION).tar.gz.asc
+upload: python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc
twine upload $^
# Remove the rest.
@@ -780,23 +784,23 @@ upload: dist/gpg-$(VERSION).tar.gz dist/gpg-$(VERSION).tar.gz.asc
# permissions.
clean-local:
rm -rf -- build
- if test "$(srcdir)" != "$(builddir)" ; then \
- find . -type d ! -perm -200 -exec chmod u+w {} ';' ; \
- for F in $(COPY_FILES); do rm -rf -- `basename $$F` ; done ; \
- for F in $(COPY_FILES_GPG); do \
- rm -rf -- gpg/`basename $$F` ; \
- done ; \
- fi
+ for VERSION in $(PYTHON_VERSIONS); do \
+ find python$${VERSION}-gpg -type d ! -perm -200 -exec chmod u+w {} ';' ; \
+ rm -rf -- python$${VERSION}-gpg ; \
+ done
install-exec-local:
rm -f install_files.txt
- for PYTHON in $(PYTHONS); do \
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+ cd python$${VERSION}-gpg ; \
$$PYTHON setup.py install \
--prefix $(DESTDIR)$(prefix) \
--record files.txt \
--verbose ; \
- cat files.txt >> install_files.txt ; \
+ cat files.txt >> ../install_files.txt ; \
rm files.txt ; \
+ cd .. ; \
done
$(MKDIR_P) $(DESTDIR)$(pythondir)/gpg
mv install_files.txt $(DESTDIR)$(pythondir)/gpg
diff --git a/lang/python/gpg/constants/__init__.py b/lang/python/gpg/constants/__init__.py
index 4fb3d6f..484ffd2 100644
--- a/lang/python/gpg/constants/__init__.py
+++ b/lang/python/gpg/constants/__init__.py
@@ -25,16 +25,16 @@ util.process_constants('GPGME_', globals())
del util
# For convenience, we import the modules here.
-from . import data, event, keylist, md, pk
-from . import protocol, sig, sigsum, status, validity
+from . import data, keylist, sig, tofu # The subdirs.
+from . import create, event, keysign, md, pk, protocol, sigsum, status, validity
# A complication arises because 'import' is a reserved keyword.
# Import it as 'Import' instead.
globals()['Import'] = getattr(__import__('', globals(), locals(),
[str('import')], 1), "import")
-__all__ = ['data', 'event', 'import', 'keylist', 'md', 'pk',
- 'protocol', 'sig', 'sigsum', 'status', 'validity']
+__all__ = ['data', 'event', 'import', 'keysign', 'keylist', 'md', 'pk',
+ 'protocol', 'sig', 'sigsum', 'status', 'tofu', 'validity', 'create']
# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
# implement gpg.Context.op_edit using gpgme_op_interact, so the
diff --git a/lang/python/gpg/constants/create.py b/lang/python/gpg/constants/create.py
new file mode 100644
index 0000000..132e96d
--- /dev/null
+++ b/lang/python/gpg/constants/create.py
@@ -0,0 +1,25 @@
+# Flags for key creation
+#
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_CREATE_', globals())
+del util
diff --git a/lang/python/gpg/constants/keysign.py b/lang/python/gpg/constants/keysign.py
new file mode 100644
index 0000000..fccdbc4
--- /dev/null
+++ b/lang/python/gpg/constants/keysign.py
@@ -0,0 +1,25 @@
+# Flags for key signing
+#
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_KEYSIGN_', globals())
+del util
diff --git a/lang/python/gpg/constants/tofu/__init__.py b/lang/python/gpg/constants/tofu/__init__.py
new file mode 100644
index 0000000..819a58b
--- /dev/null
+++ b/lang/python/gpg/constants/tofu/__init__.py
@@ -0,0 +1,24 @@
+# TOFU
+#
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from . import policy
+__all__ = ['policy']
diff --git a/lang/python/gpg/constants/tofu/policy.py b/lang/python/gpg/constants/tofu/policy.py
new file mode 100644
index 0000000..5a61f06
--- /dev/null
+++ b/lang/python/gpg/constants/tofu/policy.py
@@ -0,0 +1,25 @@
+# TOFU policies
+#
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from gpg import util
+util.process_constants('GPGME_TOFU_POLICY_', globals())
+del util
diff --git a/lang/python/gpg/core.py b/lang/python/gpg/core.py
index 748bcbb..632f4ca 100644
--- a/lang/python/gpg/core.py
+++ b/lang/python/gpg/core.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2016-2017 g10 Code GmbH
# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
#
@@ -176,7 +176,7 @@ class Context(GpgmeWrapper):
def __init__(self, armor=False, textmode=False, offline=False,
signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
protocol=constants.PROTOCOL_OpenPGP,
- wrapped=None):
+ wrapped=None, home_dir=None):
"""Construct a context object
Keyword arguments:
@@ -186,6 +186,7 @@ class Context(GpgmeWrapper):
signers -- list of keys used for signing (default [])
pinentry_mode -- pinentry mode (default PINENTRY_MODE_DEFAULT)
protocol -- protocol to use (default PROTOCOL_OpenPGP)
+ home_dir -- state directory (default is the engine default)
"""
if wrapped:
@@ -203,6 +204,15 @@ class Context(GpgmeWrapper):
self.signers = signers
self.pinentry_mode = pinentry_mode
self.protocol = protocol
+ self.home_dir = home_dir
+
+ def __repr__(self):
+ return (
+ "Context(armor={0.armor}, "
+ "textmode={0.textmode}, offline={0.offline}, "
+ "signers={0.signers}, pinentry_mode={0.pinentry_mode}, "
+ "protocol={0.protocol}, home_dir={0.home_dir}"
+ ")").format(self)
def encrypt(self, plaintext, recipients=[], sign=True, sink=None,
passphrase=None, always_trust=False, add_encrypt_to=False,
@@ -473,12 +483,17 @@ class Context(GpgmeWrapper):
plainbytes = data.read()
return plainbytes, result
- def keylist(self, pattern=None, secret=False):
+ def keylist(self, pattern=None, secret=False,
+ mode=constants.keylist.mode.LOCAL,
+ source=None):
"""List keys
Keyword arguments:
pattern -- return keys matching pattern (default: all keys)
- secret -- return only secret keys
+ secret -- return only secret keys (default: False)
+ mode -- keylist mode (default: list local keys)
+ source -- read keys from source instead from the keyring
+ (all other options are ignored in this case)
Returns:
-- an iterator returning key objects
@@ -486,7 +501,249 @@ class Context(GpgmeWrapper):
Raises:
GPGMEError -- as signaled by the underlying library
"""
- return self.op_keylist_all(pattern, secret)
+ if not source:
+ self.set_keylist_mode(mode)
+ self.op_keylist_start(pattern, secret)
+ else:
+ # Automatic wrapping of SOURCE is not possible here,
+ # because the object must not be deallocated until the
+ # iteration over the results ends.
+ if not isinstance(source, Data):
+ source = Data(file=source)
+ self.op_keylist_from_data_start(source, 0)
+
+ key = self.op_keylist_next()
+ while key:
+ yield key
+ key = self.op_keylist_next()
+ self.op_keylist_end()
+
+ def create_key(self, userid, algorithm=None, expires_in=0, expires=True,
+ sign=False, encrypt=False, certify=False, authenticate=False,
+ passphrase=None, force=False):
+ """Create a primary key
+
+ Create a primary key for the user id USERID.
+
+ ALGORITHM may be used to specify the public key encryption
+ algorithm for the new key. By default, a reasonable default
+ is chosen. You may use "future-default" to select an
+ algorithm that will be the default in a future implementation
+ of the engine. ALGORITHM may be a string like "rsa", or
+ "rsa2048" to explicitly request an algorithm and a key size.
+
+ EXPIRES_IN specifies the expiration time of the key in number
+ of seconds since the keys creation. By default, a reasonable
+ expiration time is chosen. If you want to create a key that
+ does not expire, use the keyword argument EXPIRES.
+
+ SIGN, ENCRYPT, CERTIFY, and AUTHENTICATE can be used to
+ request the capabilities of the new key. If you don't request
+ any, a reasonable set of capabilities is selected, and in case
+ of OpenPGP, a subkey with a reasonable set of capabilities is
+ created.
+
+ If PASSPHRASE is None (the default), then the key will not be
+ protected with a passphrase. If PASSPHRASE is a string, it
+ will be used to protect the key. If PASSPHRASE is True, the
+ passphrase must be supplied using a passphrase callback or
+ out-of-band with a pinentry.
+
+ Keyword arguments:
+ algorithm -- public key algorithm, see above (default: reasonable)
+ expires_in -- expiration time in seconds (default: reasonable)
+ expires -- whether or not the key should expire (default: True)
+ sign -- request the signing capability (see above)
+ encrypt -- request the encryption capability (see above)
+ certify -- request the certification capability (see above)
+ authenticate -- request the authentication capability (see above)
+ passphrase -- protect the key with a passphrase (default: no passphrase)
+ force -- force key creation even if a key with the same userid exists
+ (default: False)
+
+ Returns:
+ -- an object describing the result of the key creation
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ if util.is_a_string(passphrase):
+ old_pinentry_mode = self.pinentry_mode
+ old_passphrase_cb = getattr(self, '_passphrase_cb', None)
+ self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
+ def passphrase_cb(hint, desc, prev_bad, hook=None):
+ return passphrase
+ self.set_passphrase_cb(passphrase_cb)
+
+ try:
+ self.op_createkey(userid, algorithm,
+ 0, # reserved
+ expires_in,
+ None, # extrakey
+ ((constants.create.SIGN if sign else 0)
+ | (constants.create.ENCR if encrypt else 0)
+ | (constants.create.CERT if certify else 0)
+ | (constants.create.AUTH if authenticate else 0)
+ | (constants.create.NOPASSWD if passphrase == None else 0)
+ | (0 if expires else constants.create.NOEXPIRE)
+ | (constants.create.FORCE if force else 0)))
+ finally:
+ if util.is_a_string(passphrase):
+ self.pinentry_mode = old_pinentry_mode
+ if old_passphrase_cb:
+ self.set_passphrase_cb(*old_passphrase_cb[1:])
+
+ return self.op_genkey_result()
+
+ def create_subkey(self, key, algorithm=None, expires_in=0, expires=True,
+ sign=False, encrypt=False, authenticate=False, passphrase=None):
+ """Create a subkey
+
+ Create a subkey for the given KEY. As subkeys are a concept
+ of OpenPGP, calling this is only valid for the OpenPGP
+ protocol.
+
+ ALGORITHM may be used to specify the public key encryption
+ algorithm for the new subkey. By default, a reasonable
+ default is chosen. You may use "future-default" to select an
+ algorithm that will be the default in a future implementation
+ of the engine. ALGORITHM may be a string like "rsa", or
+ "rsa2048" to explicitly request an algorithm and a key size.
+
+ EXPIRES_IN specifies the expiration time of the subkey in
+ number of seconds since the subkeys creation. By default, a
+ reasonable expiration time is chosen. If you want to create a
+ subkey that does not expire, use the keyword argument EXPIRES.
+
+ SIGN, ENCRYPT, and AUTHENTICATE can be used to request the
+ capabilities of the new subkey. If you don't request any, an
+ encryption subkey is generated.
+
+ If PASSPHRASE is None (the default), then the subkey will not
+ be protected with a passphrase. If PASSPHRASE is a string, it
+ will be used to protect the subkey. If PASSPHRASE is True,
+ the passphrase must be supplied using a passphrase callback or
+ out-of-band with a pinentry.
+
+ Keyword arguments:
+ algorithm -- public key algorithm, see above (default: reasonable)
+ expires_in -- expiration time in seconds (default: reasonable)
+ expires -- whether or not the subkey should expire (default: True)
+ sign -- request the signing capability (see above)
+ encrypt -- request the encryption capability (see above)
+ authenticate -- request the authentication capability (see above)
+ passphrase -- protect the subkey with a passphrase (default: no passphrase)
+
+ Returns:
+ -- an object describing the result of the subkey creation
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ if util.is_a_string(passphrase):
+ old_pinentry_mode = self.pinentry_mode
+ old_passphrase_cb = getattr(self, '_passphrase_cb', None)
+ self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
+ def passphrase_cb(hint, desc, prev_bad, hook=None):
+ return passphrase
+ self.set_passphrase_cb(passphrase_cb)
+
+ try:
+ self.op_createsubkey(key, algorithm,
+ 0, # reserved
+ expires_in,
+ ((constants.create.SIGN if sign else 0)
+ | (constants.create.ENCR if encrypt else 0)
+ | (constants.create.AUTH if authenticate else 0)
+ | (constants.create.NOPASSWD
+ if passphrase == None else 0)
+ | (0 if expires else constants.create.NOEXPIRE)))
+ finally:
+ if util.is_a_string(passphrase):
+ self.pinentry_mode = old_pinentry_mode
+ if old_passphrase_cb:
+ self.set_passphrase_cb(*old_passphrase_cb[1:])
+
+ return self.op_genkey_result()
+
+ def key_add_uid(self, key, uid):
+ """Add a UID
+
+ Add the uid UID to the given KEY. Calling this function is
+ only valid for the OpenPGP protocol.
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ self.op_adduid(key, uid, 0)
+
+ def key_revoke_uid(self, key, uid):
+ """Revoke a UID
+
+ Revoke the uid UID from the given KEY. Calling this function
+ is only valid for the OpenPGP protocol.
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ self.op_revuid(key, uid, 0)
+
+ def key_sign(self, key, uids=None, expires_in=False, local=False):
+ """Sign a key
+
+ Sign a key with the current set of signing keys. Calling this
+ function is only valid for the OpenPGP protocol.
+
+ If UIDS is None (the default), then all UIDs are signed. If
+ it is a string, then only the matching UID is signed. If it
+ is a list of strings, then all matching UIDs are signed. Note
+ that a case-sensitive exact string comparison is done.
+
+ EXPIRES_IN specifies the expiration time of the signature in
+ seconds. If EXPIRES_IN is False, the signature does not
+ expire.
+
+ Keyword arguments:
+ uids -- user ids to sign, see above (default: sign all)
+ expires_in -- validity period of the signature in seconds
+ (default: do not expire)
+ local -- create a local, non-exportable signature
+ (default: False)
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ flags = 0
+ if uids == None or util.is_a_string(uids):
+ pass#through unchanged
+ else:
+ flags |= constants.keysign.LFSEP
+ uids = "\n".join(uids)
+
+ if not expires_in:
+ flags |= constants.keysign.NOEXPIRE
+
+ if local:
+ flags |= constants.keysign.LOCAL
+
+ self.op_keysign(key, uids, expires_in, flags)
+
+ def key_tofu_policy(self, key, policy):
+ """Set a keys' TOFU policy
+
+ Set the TOFU policy associated with KEY to POLICY. Calling
+ this function is only valid for the OpenPGP protocol.
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ self.op_tofu_policy(key, policy)
def assuan_transact(self, command,
data_cb=None, inquire_cb=None, status_cb=None):
@@ -512,7 +769,7 @@ class Context(GpgmeWrapper):
"""
- if isinstance(command, (str, bytes)):
+ if util.is_a_string(command) or isinstance(command, bytes):
cmd = command
else:
cmd = " ".join(util.percent_escape(f) for f in command)
@@ -602,27 +859,40 @@ class Context(GpgmeWrapper):
errorcheck(gpgme.gpgme_engine_check_version(value))
self.set_protocol(value)
+ @property
+ def home_dir(self):
+ """Engine's home directory"""
+ return self.engine_info.home_dir
+ @home_dir.setter
+ def home_dir(self, value):
+ self.set_engine_info(self.protocol, home_dir=value)
+
_ctype = 'gpgme_ctx_t'
_cprefix = 'gpgme_'
def _errorcheck(self, name):
"""This function should list all functions returning gpgme_error_t"""
+ # The list of functions is created using:
+ #
+ # $ grep '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \
+ # | grep -v _op_ | awk "/\(gpgme_ctx/ { printf (\"'%s',\\n\", \$2) } "
return ((name.startswith('gpgme_op_')
and not name.endswith('_result'))
or name in {
+ 'gpgme_new',
'gpgme_set_ctx_flag',
'gpgme_set_protocol',
'gpgme_set_sub_protocol',
'gpgme_set_keylist_mode',
'gpgme_set_pinentry_mode',
'gpgme_set_locale',
- 'gpgme_set_engine_info',
+ 'gpgme_ctx_set_engine_info',
'gpgme_signers_add',
- 'gpgme_get_sig_key',
'gpgme_sig_notation_add',
+ 'gpgme_set_sender',
'gpgme_cancel',
'gpgme_cancel_async',
- 'gpgme_cancel_get_key',
+ 'gpgme_get_key',
})
_boolean_properties = {'armor', 'textmode', 'offline'}
@@ -829,8 +1099,7 @@ class Context(GpgmeWrapper):
home_dir -- configuration directory (unchanged if None)
"""
- errorcheck(gpgme.gpgme_ctx_set_engine_info(
- self.wrapped, proto, file_name, home_dir))
+ self.ctx_set_engine_info(proto, file_name, home_dir)
def wait(self, hang):
"""Wait for asynchronous call to finish. Wait forever if hang is True.
@@ -884,11 +1153,19 @@ class Data(GpgmeWrapper):
def _errorcheck(self, name):
"""This function should list all functions returning gpgme_error_t"""
+ # This list is compiled using
+ #
+ # $ grep -v '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \
+ # | awk "/\(gpgme_data_t/ { printf (\"'%s',\\n\", \$2) } " | sed "s/'\\*/'/"
return name not in {
+ 'gpgme_data_read',
+ 'gpgme_data_write',
+ 'gpgme_data_seek',
+ 'gpgme_data_release',
'gpgme_data_release_and_get_mem',
'gpgme_data_get_encoding',
- 'gpgme_data_seek',
'gpgme_data_get_file_name',
+ 'gpgme_data_identify',
}
def __init__(self, string=None, file=None, offset=None,
@@ -1097,15 +1374,64 @@ class Data(GpgmeWrapper):
chunks.append(result)
return b''.join(chunks)
+def pubkey_algo_string(subkey):
+ """Return short algorithm string
+
+ Return a public key algorithm string (e.g. "rsa2048") for a given
+ SUBKEY.
+
+ Returns:
+ algo - a string
+
+ """
+ return gpgme.gpgme_pubkey_algo_string(subkey)
+
def pubkey_algo_name(algo):
+ """Return name of public key algorithm
+
+ Return the name of the public key algorithm for a given numeric
+ algorithm id ALGO (cf. RFC4880).
+
+ Returns:
+ algo - a string
+
+ """
return gpgme.gpgme_pubkey_algo_name(algo)
def hash_algo_name(algo):
+ """Return name of hash algorithm
+
+ Return the name of the hash algorithm for a given numeric
+ algorithm id ALGO (cf. RFC4880).
+
+ Returns:
+ algo - a string
+
+ """
return gpgme.gpgme_hash_algo_name(algo)
def get_protocol_name(proto):
+ """Get protocol description
+
+ Get the string describing protocol PROTO.
+
+ Returns:
+ proto - a string
+
+ """
return gpgme.gpgme_get_protocol_name(proto)
+def addrspec_from_uid(uid):
+ """Return the address spec
+
+ Return the addr-spec (cf. RFC2822 section 4.3) from a user id UID.
+
+ Returns:
+ addr_spec - a string
+
+ """
+ return gpgme.gpgme_addrspec_from_uid(uid)
+
def check_version(version=None):
return gpgme.gpgme_check_version(version)
diff --git a/lang/python/gpg/gpgme.py b/lang/python/gpg/gpgme.py
deleted file mode 100644
index 238359d..0000000
--- a/lang/python/gpg/gpgme.py
+++ /dev/null
@@ -1,126 +0,0 @@
-# This file was automatically generated by SWIG (http://www.swig.org).
-# Version 3.0.7
-#
-# Do not make changes to this file unless you know what you are doing--modify
-# the SWIG interface file instead.
-
-
-
-
-
-from sys import version_info
-if version_info >= (2, 6, 0):
- def swig_import_helper():
- from os.path import dirname
- import imp
- fp = None
- try:
- fp, pathname, description = imp.find_module('_gpgme', [dirname(__file__)])
- except ImportError:
- import _gpgme
- return _gpgme
- if fp is not None:
- try:
- _mod = imp.load_module('_gpgme', fp, pathname, description)
- finally:
- fp.close()
- return _mod
- _gpgme = swig_import_helper()
- del swig_import_helper
-else:
- import _gpgme
-del version_info
-from _gpgme import *
-try:
- _swig_property = property
-except NameError:
- pass # Python < 2.2 doesn't have 'property'.
-
-
-def _swig_setattr_nondynamic(self, class_type, name, value, static=1):
- if (name == "thisown"):
- return self.this.own(value)
- if (name == "this"):
- if type(value).__name__ == 'SwigPyObject':
- self.__dict__[name] = value
- return
- method = class_type.__swig_setmethods__.get(name, None)
- if method:
- return method(self, value)
- if (not static):
- if _newclass:
- object.__setattr__(self, name, value)
- else:
- self.__dict__[name] = value
- else:
- raise AttributeError("You cannot add attributes to %s" % self)
-
-
-def _swig_setattr(self, class_type, name, value):
- return _swig_setattr_nondynamic(self, class_type, name, value, 0)
-
-
-def _swig_getattr_nondynamic(self, class_type, name, static=1):
- if (name == "thisown"):
- return self.this.own()
- method = class_type.__swig_getmethods__.get(name, None)
- if method:
- return method(self)
- if (not static):
- return object.__getattr__(self, name)
- else:
- raise AttributeError(name)
-
-def _swig_getattr(self, class_type, name):
- return _swig_getattr_nondynamic(self, class_type, name, 0)
-
-
-def _swig_repr(self):
- try:
- strthis = "proxy of " + self.this.__repr__()
- except:
- strthis = ""
- return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
-
-try:
- _object = object
- _newclass = 1
-except AttributeError:
- class _object:
- pass
- _newclass = 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-# This file is compatible with both classic and new-style classes.
-
-
diff --git a/lang/python/gpg/results.py b/lang/python/gpg/results.py
index 3383896..46ebeec 100644
--- a/lang/python/gpg/results.py
+++ b/lang/python/gpg/results.py
@@ -64,10 +64,10 @@ class Result(object):
setattr(self, key, getattr(fragile, key))
- def __str__(self):
- return '<{} {}>'.format(
+ def __repr__(self):
+ return '{}({})'.format(
self.__class__.__name__,
- ', '.join('{}: {}'.format(k, getattr(self, k))
+ ', '.join('{}={!r}'.format(k, getattr(self, k))
for k in dir(self) if not k.startswith('_')))
class InvalidKey(Result):
diff --git a/lang/python/gpg/version.py b/lang/python/gpg/version.py
index 9ec657d..ff4cd71 100644
--- a/lang/python/gpg/version.py
+++ b/lang/python/gpg/version.py
@@ -22,7 +22,7 @@ del absolute_import, print_function
from . import gpgme
productname = 'gpg'
-versionstr = "1.8.0"
+versionstr = "1.9.0"
gpgme_versionstr = gpgme.GPGME_VERSION
in_tree_build = bool(gpgme.cvar.gpg_in_tree_build)
diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i
index 783531f..610b3d9 100644
--- a/lang/python/gpgme.i
+++ b/lang/python/gpgme.i
@@ -111,8 +111,10 @@
}
/* Release returned buffers as necessary. */
-%typemap(newfree) char * "free($1);";
+%typemap(newfree) char * "gpgme_free($1);";
%newobject gpgme_data_release_and_get_mem;
+%newobject gpgme_pubkey_algo_string;
+%newobject gpgme_addrspec_from_uid;
%typemap(arginit) gpgme_key_t [] {
$1 = NULL;
@@ -135,7 +137,12 @@
/* Following code is from swig's python.swg. */
if ((SWIG_ConvertPtr(pypointer,(void **) &$1[i], $*1_descriptor,SWIG_POINTER_EXCEPTION | $disown )) == -1) {
- Py_DECREF(pypointer);
+ Py_DECREF(pypointer);
+ PyErr_Format(PyExc_TypeError,
+ "arg %d: list must contain only gpgme_key_ts, got %s "
+ "at position %d",
+ $argnum, pypointer->ob_type->tp_name, i);
+ free($1);
return NULL;
}
Py_DECREF(pypointer);
@@ -287,7 +294,7 @@
gpgme_data_t sig, gpgme_data_t signed_text,
gpgme_data_t plaintext, gpgme_data_t keydata,
gpgme_data_t pubkey, gpgme_data_t seckey,
- gpgme_data_t out};
+ gpgme_data_t out, gpgme_data_t data};
/* SWIG has problems interpreting ssize_t, off_t or gpgme_error_t in
gpgme.h. */
@@ -424,69 +431,24 @@
/* Wrap the fragile result objects into robust Python ones. */
-%typemap(out) gpgme_encrypt_result_t {
- PyObject *fragile;
- fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
- %newpointer_flags);
- $result = _gpg_wrap_result(fragile, "EncryptResult");
- Py_DECREF(fragile);
-}
-
-%typemap(out) gpgme_decrypt_result_t {
- PyObject *fragile;
- fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
- %newpointer_flags);
- $result = _gpg_wrap_result(fragile, "DecryptResult");
- Py_DECREF(fragile);
-}
-
-%typemap(out) gpgme_sign_result_t {
- PyObject *fragile;
- fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
- %newpointer_flags);
- $result = _gpg_wrap_result(fragile, "SignResult");
- Py_DECREF(fragile);
-}
-
-%typemap(out) gpgme_verify_result_t {
- PyObject *fragile;
- fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
- %newpointer_flags);
- $result = _gpg_wrap_result(fragile, "VerifyResult");
- Py_DECREF(fragile);
-}
-
-%typemap(out) gpgme_import_result_t {
- PyObject *fragile;
- fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
- %newpointer_flags);
- $result = _gpg_wrap_result(fragile, "ImportResult");
- Py_DECREF(fragile);
-}
-
-%typemap(out) gpgme_genkey_result_t {
+%define wrapresult(cls, name)
+%typemap(out) cls {
PyObject *fragile;
fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
%newpointer_flags);
- $result = _gpg_wrap_result(fragile, "GenkeyResult");
+ $result = _gpg_wrap_result(fragile, name);
Py_DECREF(fragile);
}
+%enddef
-%typemap(out) gpgme_keylist_result_t {
- PyObject *fragile;
- fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
- %newpointer_flags);
- $result = _gpg_wrap_result(fragile, "KeylistResult");
- Py_DECREF(fragile);
-}
-
-%typemap(out) gpgme_vfs_mount_result_t {
- PyObject *fragile;
- fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
- %newpointer_flags);
- $result = _gpg_wrap_result(fragile, "VFSMountResult");
- Py_DECREF(fragile);
-}
+wrapresult(gpgme_encrypt_result_t, "EncryptResult")
+wrapresult(gpgme_decrypt_result_t, "DecryptResult")
+wrapresult(gpgme_sign_result_t, "SignResult")
+wrapresult(gpgme_verify_result_t, "VerifyResult")
+wrapresult(gpgme_import_result_t, "ImportResult")
+wrapresult(gpgme_genkey_result_t, "GenkeyResult")
+wrapresult(gpgme_keylist_result_t, "KeylistResult")
+wrapresult(gpgme_vfs_mount_result_t, "VFSMountResult")
%typemap(out) gpgme_engine_info_t {
int i;
@@ -586,6 +548,15 @@
}
}
+
+/* With SWIG, you can define default arguments for parameters.
+ * While it's legal in C++ it is not in C, so we cannot change the
+ * already existing gpgme.h. We need, however, to declare the function
+ * *before* SWIG loads it from gpgme.h. Hence, we define it here. */
+gpgme_error_t gpgme_op_keylist_start (gpgme_ctx_t ctx,
+ const char *pattern="",
+ int secret_only=0);
+
/* Include the unmodified <gpgme.h> for cc, and the cleaned-up local
version for SWIG. We do, however, want to hide certain fields on
some structs, which we provide prior to including the version for
@@ -656,7 +627,17 @@ FILE *fdopen(int fildes, const char *mode);
PyObject *
_gpg_wrap_gpgme_data_t(gpgme_data_t data)
{
- return SWIG_Python_NewPointerObj(NULL, data, SWIGTYPE_p_gpgme_data, 0);
+ /*
+ * If SWIG is invoked without -builtin, the macro SWIG_NewPointerObj
+ * expects a variable named "self".
+ *
+ * XXX: It is not quite clear why passing NULL as self is okay, but
+ * it works with -builtin, and it seems to work just fine without
+ * it too.
+ */
+ PyObject* self = NULL;
+ (void) self;
+ return SWIG_NewPointerObj(data, SWIGTYPE_p_gpgme_data, 0);
}
gpgme_ctx_t
@@ -675,3 +656,38 @@ _gpg_unwrap_gpgme_ctx_t(PyObject *wrapped)
/* ... but only the public definitions here. They will be exposed to
the Python world, so let's be careful. */
%include "helpers.h"
+
+
+%define genericrepr(cls)
+%pythoncode %{
+ def __repr__(self):
+ names = [name for name in dir(self)
+ if not name.startswith("_") and name != "this"]
+ props = ", ".join(("{}={!r}".format(name, getattr(self, name))
+ for name in names)
+ )
+ return "cls({})".format(props)
+%}
+
+%enddef
+
+%extend _gpgme_key {
+ genericrepr(Key)
+};
+
+
+%extend _gpgme_subkey {
+ genericrepr(SubKey)
+};
+
+%extend _gpgme_key_sig {
+ genericrepr(KeySig)
+};
+
+%extend _gpgme_user_id {
+ genericrepr(UID)
+};
+
+%extend _gpgme_tofu_info {
+ genericrepr(TofuInfo)
+};
diff --git a/lang/python/helpers.c b/lang/python/helpers.c
index 8f71a30..947819d 100644
--- a/lang/python/helpers.c
+++ b/lang/python/helpers.c
@@ -293,8 +293,10 @@ _gpg_obj2gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper,
return _gpg_obj2gpgme_t(data, "gpgme_data_t", argnum);
return PyErr_Format(PyExc_TypeError,
- "arg %d: expected gpg.Data, file, or an object "
- "implementing the buffer protocol, got %s",
+ "arg %d: expected gpg.Data, file, "
+ "bytes (not string!), or an object "
+ "implementing the buffer protocol. Got: %s. "
+ "If you provided a string, try to encode() it.",
argnum, data->ob_type->tp_name);
}
@@ -375,7 +377,21 @@ static gpgme_error_t pyPassphraseCb(void *hook,
goto leave;
}
- PyTuple_SetItem(args, 1, PyBytes_FromString(passphrase_info));
+ if (passphrase_info == NULL)
+ {
+ Py_INCREF(Py_None);
+ PyTuple_SetItem(args, 1, Py_None);
+ }
+ else
+ PyTuple_SetItem(args, 1, PyUnicode_DecodeUTF8(passphrase_info,
+ strlen (passphrase_info),
+ "strict"));
+ if (PyErr_Occurred()) {
+ Py_DECREF(args);
+ err_status = gpg_error(GPG_ERR_GENERAL);
+ goto leave;
+ }
+
PyTuple_SetItem(args, 2, PyBool_FromLong((long)prev_was_bad));
if (dataarg) {
Py_INCREF(dataarg); /* Because GetItem doesn't give a ref but SetItem taketh away */
diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
index 9669c28..bf4efa3 100755
--- a/lang/python/setup.py.in
+++ b/lang/python/setup.py.in
@@ -34,12 +34,12 @@ in_tree = False
extra_swig_opts = []
extra_macros = dict()
-if os.path.exists("../../src/gpgme-config"):
+if os.path.exists("../../../src/gpgme-config"):
# In-tree build.
in_tree = True
- gpgme_config = ["../../src/gpgme-config"] + gpgme_config_flags
- gpgme_h = "../../src/gpgme.h"
- library_dirs = ["../../src/.libs"] # XXX uses libtool internals
+ gpgme_config = ["../../../src/gpgme-config"] + gpgme_config_flags
+ gpgme_h = "../../../src/gpgme.h"
+ library_dirs = ["../../../src/.libs"] # XXX uses libtool internals
extra_macros.update(
HAVE_CONFIG_H=1,
HAVE_DATA_H=1,
@@ -152,9 +152,10 @@ class BuildExtFirstHack(build):
self.run_command('build_ext')
build.run(self)
+py3 = [] if sys.version_info.major < 3 else ['-py3']
swige = Extension("gpg._gpgme", ["gpgme.i", "helpers.c"],
- swig_opts = ['-py3', '-builtin', '-threads',
- '-outdir', 'gpg'] + extra_swig_opts,
+ swig_opts = ['-threads',
+ '-outdir', 'gpg'] + py3 + extra_swig_opts,
include_dirs = include_dirs,
define_macros = define_macros,
library_dirs = library_dirs,
@@ -171,7 +172,8 @@ setup(name="gpg",
url='https://www.gnupg.org',
ext_modules=[swige],
packages = ['gpg', 'gpg.constants', 'gpg.constants.data',
- 'gpg.constants.keylist', 'gpg.constants.sig'],
+ 'gpg.constants.keylist', 'gpg.constants.sig',
+ 'gpg.constants.tofu'],
license="LGPL2.1+ (the library), GPL2+ (tests and examples)",
classifiers=[
'Development Status :: 4 - Beta',
diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am
index 39f532c..9c19a13 100644
--- a/lang/python/tests/Makefile.am
+++ b/lang/python/tests/Makefile.am
@@ -46,11 +46,16 @@ py_tests = t-wrapper.py \
t-trustlist.py \
t-edit.py \
t-keylist.py \
+ t-keylist-from-data.py \
t-wait.py \
t-encrypt-large.py \
t-file-name.py \
t-idiomatic.py \
- t-protocol-assuan.py
+ t-protocol-assuan.py \
+ t-quick-key-creation.py \
+ t-quick-subkey-creation.py \
+ t-quick-key-manipulation.py \
+ t-quick-key-signing.py
XTESTS = initial.py $(py_tests) final.py
EXTRA_DIST = support.py $(XTESTS) encrypt-only.asc sign-only.asc \
@@ -73,7 +78,7 @@ xcheck: ./pubring-stamp
CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
gpg-agent.conf pubring.kbx~ gpg.conf pubring.gpg~ \
- random_seed .gpg-v21-migrated \
+ random_seed .gpg-v21-migrated tofu.db \
pubring-stamp private-keys-v1.d/gpg-sample.stamp
private_keys = \
@@ -107,8 +112,9 @@ clean-local:
./gpg.conf:
# This is required for t-sig-notations.
echo no-force-v3-sigs > ./gpg.conf
+ echo ignore-invalid-option agent-program >> ./gpg.conf
+ echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpg.conf
./gpg-agent.conf:
# This is required for gpg2, which does not support command fd.
echo pinentry-program $(abs_top_srcdir)/tests/gpg/pinentry >$@
- echo allow-loopback-pinentry >>$@
diff --git a/lang/python/tests/Makefile.in b/lang/python/tests/Makefile.in
index ce60cd5..4940a8e 100644
--- a/lang/python/tests/Makefile.in
+++ b/lang/python/tests/Makefile.in
@@ -108,8 +108,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
@@ -358,11 +358,16 @@ py_tests = t-wrapper.py \
t-trustlist.py \
t-edit.py \
t-keylist.py \
+ t-keylist-from-data.py \
t-wait.py \
t-encrypt-large.py \
t-file-name.py \
t-idiomatic.py \
- t-protocol-assuan.py
+ t-protocol-assuan.py \
+ t-quick-key-creation.py \
+ t-quick-subkey-creation.py \
+ t-quick-key-manipulation.py \
+ t-quick-key-signing.py
XTESTS = initial.py $(py_tests) final.py
EXTRA_DIST = support.py $(XTESTS) encrypt-only.asc sign-only.asc \
@@ -370,7 +375,7 @@ EXTRA_DIST = support.py $(XTESTS) encrypt-only.asc sign-only.asc \
CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
gpg-agent.conf pubring.kbx~ gpg.conf pubring.gpg~ \
- random_seed .gpg-v21-migrated \
+ random_seed .gpg-v21-migrated tofu.db \
pubring-stamp private-keys-v1.d/gpg-sample.stamp
private_keys = \
@@ -612,11 +617,12 @@ clean-local:
./gpg.conf:
# This is required for t-sig-notations.
echo no-force-v3-sigs > ./gpg.conf
+ echo ignore-invalid-option agent-program >> ./gpg.conf
+ echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpg.conf
./gpg-agent.conf:
# This is required for gpg2, which does not support command fd.
echo pinentry-program $(abs_top_srcdir)/tests/gpg/pinentry >$@
- echo allow-loopback-pinentry >>$@
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/lang/python/tests/initial.py b/lang/python/tests/initial.py
index ebe7f8a..49e4f82 100755
--- a/lang/python/tests/initial.py
+++ b/lang/python/tests/initial.py
@@ -24,7 +24,8 @@ import os
import subprocess
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
+
+print("Using gpg module from {0!r}.".format(os.path.dirname(gpg.__file__)))
subprocess.check_call([os.path.join(os.getenv('top_srcdir'),
"tests", "start-stop-agent"), "--start"])
diff --git a/lang/python/tests/run-tests.py b/lang/python/tests/run-tests.py
index 55d3f11..c4af526 100644
--- a/lang/python/tests/run-tests.py
+++ b/lang/python/tests/run-tests.py
@@ -39,6 +39,8 @@ parser.add_argument('tests', metavar='TEST', type=str, nargs='+',
help='A test to run')
parser.add_argument('-v', '--verbose', action="store_true", default=False,
help='Be verbose.')
+parser.add_argument('-q', '--quiet', action="store_true", default=False,
+ help='Be quiet.')
parser.add_argument('--interpreters', metavar='PYTHON', type=str,
default=[], action=SplitAndAccumulate,
help='Use these interpreters to run the tests, ' +
@@ -49,6 +51,8 @@ parser.add_argument('--srcdir', type=str,
parser.add_argument('--builddir', type=str,
default=os.environ.get("abs_builddir", ""),
help='Location of the tests.')
+parser.add_argument('--parallel', action="store_true", default=False,
+ help='Ignored. For compatibility with run-tests.scm.')
args = parser.parse_args()
if not args.interpreters:
@@ -65,19 +69,29 @@ for interpreter in args.interpreters:
version = subprocess.check_output(
[interpreter, "-c", "import sys; print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))"]).strip().decode()
- builddirs = glob.glob(os.path.join(args.builddir, "..", "build",
- "lib*"+version))
- assert len(builddirs) == 1, \
- "Expected one build directory, got {0}".format(builddirs)
+ pattern = os.path.join(args.builddir, "..",
+ "python{0}-gpg".format(version),
+ "build",
+ "lib*"+version)
+ builddirs = glob.glob(pattern)
+ if len(builddirs) == 0:
+ sys.exit("Build directory matching {0!r} not found.".format(pattern))
+ elif len(builddirs) > 1:
+ sys.exit("Multiple build directories matching {0!r} found: {1}".format(
+ pattern, builddirs))
+
env = dict(os.environ)
env["PYTHONPATH"] = builddirs[0]
- print("Running tests using {0} ({1})...".format(interpreter, version))
+ if not args.quiet:
+ print("Running tests using {0} ({1})...".format(interpreter, version))
+
for test in args.tests:
status = subprocess.call(
[interpreter, os.path.join(args.srcdir, test)],
env=env, stdout=out, stderr=err)
- print("{0}: {1}".format(status_to_str(status), test))
+ if not args.quiet:
+ print("{0}: {1}".format(status_to_str(status), test))
results.append(status)
def count(status):
@@ -85,6 +99,8 @@ def count(status):
def failed():
return len(list(filter(lambda x: x not in (0, 77, 99), results)))
-print("{0} tests run, {1} succeeded, {2} failed, {3} skipped.".format(
- len(results), count(0), failed(), count(77)))
-sys.exit(len(results) - count(0))
+if not args.quiet:
+ print("{0} tests run, {1} succeeded, {2} failed, {3} skipped.".format(
+ len(results), count(0), failed(), count(77)))
+ sys.exit(len(results) - count(0))
+sys.exit(results[0])
diff --git a/lang/python/tests/support.py b/lang/python/tests/support.py
index f991c6d..fabd818 100644
--- a/lang/python/tests/support.py
+++ b/lang/python/tests/support.py
@@ -18,10 +18,28 @@
from __future__ import absolute_import, print_function, unicode_literals
del absolute_import, print_function, unicode_literals
+import contextlib
+import shutil
import sys
import os
+import re
+import tempfile
+import time
import gpg
+def assert_gpg_version(version=(2, 1, 0)):
+ with gpg.Context() as c:
+ clean_version = re.match(r'\d+\.\d+\.\d+', c.engine_info.version).group(0)
+ if tuple(map(int, clean_version.split('.'))) < version:
+ print("GnuPG too old: have {0}, need {1}.".format(
+ c.engine_info.version, '.'.join(map(str, version))))
+ sys.exit(77)
+
+# Skip the Python tests for GnuPG < 2.1.12. Prior versions do not
+# understand the command line flags that we assume exist. C.f. issue
+# 3008.
+assert_gpg_version((2, 1, 12))
+
# known keys
alpha = "A0FF4590BB6122EDEF6E3C542D727CC768697734"
bob = "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2"
@@ -35,9 +53,6 @@ def make_filename(name):
def in_srcdir(name):
return os.path.join(os.environ['srcdir'], name)
-def init_gpgme(proto):
- gpg.core.engine_check_version(proto)
-
verbose = int(os.environ.get('verbose', 0)) > 1
def print_data(data):
if verbose:
@@ -48,7 +63,11 @@ def print_data(data):
except:
# Hope for the best.
pass
- sys.stdout.buffer.write(data)
+
+ if hasattr(sys.stdout, "buffer"):
+ sys.stdout.buffer.write(data)
+ else:
+ sys.stdout.write(data)
def mark_key_trusted(ctx, key):
class Editor(object):
@@ -68,3 +87,41 @@ def mark_key_trusted(ctx, key):
return result
with gpg.Data() as sink:
ctx.op_edit(key, Editor().edit, sink, sink)
+
+
+# Python3.2 and up has tempfile.TemporaryDirectory, but we cannot use
+# that, because there shutil.rmtree is used without
+# ignore_errors=True, and that races against gpg-agent deleting its
+# sockets.
+class TemporaryDirectory(object):
+ def __enter__(self):
+ self.path = tempfile.mkdtemp()
+ return self.path
+ def __exit__(self, *args):
+ shutil.rmtree(self.path, ignore_errors=True)
+
+@contextlib.contextmanager
+def EphemeralContext():
+ with TemporaryDirectory() as tmp:
+ home = os.environ['GNUPGHOME']
+ shutil.copy(os.path.join(home, "gpg.conf"), tmp)
+ shutil.copy(os.path.join(home, "gpg-agent.conf"), tmp)
+
+ with gpg.Context(home_dir=tmp) as ctx:
+ yield ctx
+
+ # Ask the agent to quit.
+ agent_socket = os.path.join(tmp, "S.gpg-agent")
+ ctx.protocol = gpg.constants.protocol.ASSUAN
+ ctx.set_engine_info(ctx.protocol, file_name=agent_socket)
+ try:
+ ctx.assuan_transact(["KILLAGENT"])
+ except gpg.errors.GPGMEError as e:
+ if e.getcode() == gpg.errors.ASS_CONNECT_FAILED:
+ pass # the agent was not running
+ else:
+ raise
+
+ # Block until it is really gone.
+ while os.path.exists(agent_socket):
+ time.sleep(.01)
diff --git a/lang/python/tests/t-callbacks.py b/lang/python/tests/t-callbacks.py
index eed50bc..94cf11e 100755
--- a/lang/python/tests/t-callbacks.py
+++ b/lang/python/tests/t-callbacks.py
@@ -24,7 +24,7 @@ import os
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
+support.assert_gpg_version()
c = gpg.Context()
c.set_pinentry_mode(gpg.constants.PINENTRY_MODE_LOOPBACK)
diff --git a/lang/python/tests/t-decrypt-verify.py b/lang/python/tests/t-decrypt-verify.py
index 6243167..03bbc4b 100755
--- a/lang/python/tests/t-decrypt-verify.py
+++ b/lang/python/tests/t-decrypt-verify.py
@@ -34,7 +34,6 @@ def check_verify_result(result, summary, fpr, status):
assert sig.validity == gpg.constants.validity.FULL
assert gpg.errors.GPGMEError(sig.validity_reason).getcode() == gpg.errors.NO_ERROR
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
source = gpg.Data(file=support.make_filename("cipher-2.asc"))
diff --git a/lang/python/tests/t-decrypt.py b/lang/python/tests/t-decrypt.py
index 1af0562..05b6d8b 100755
--- a/lang/python/tests/t-decrypt.py
+++ b/lang/python/tests/t-decrypt.py
@@ -23,7 +23,6 @@ del absolute_import, print_function, unicode_literals
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
source = gpg.Data(file=support.make_filename("cipher-1.asc"))
diff --git a/lang/python/tests/t-edit.py b/lang/python/tests/t-edit.py
index bd70e7e..ffc3296 100755
--- a/lang/python/tests/t-edit.py
+++ b/lang/python/tests/t-edit.py
@@ -26,6 +26,8 @@ import os
import gpg
import support
+support.assert_gpg_version()
+
class KeyEditor(object):
def __init__(self):
self.steps = ["fpr", "expire", "1", "primary", "quit"]
@@ -51,8 +53,6 @@ class KeyEditor(object):
return result
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
-
c = gpg.Context()
c.set_pinentry_mode(gpg.constants.PINENTRY_MODE_LOOPBACK)
c.set_passphrase_cb(lambda *args: "abc")
diff --git a/lang/python/tests/t-encrypt-large.py b/lang/python/tests/t-encrypt-large.py
index cdb4a32..5646085 100755
--- a/lang/python/tests/t-encrypt-large.py
+++ b/lang/python/tests/t-encrypt-large.py
@@ -30,7 +30,6 @@ if len(sys.argv) == 2:
else:
nbytes = 100000
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
ntoread = nbytes
diff --git a/lang/python/tests/t-encrypt-sign.py b/lang/python/tests/t-encrypt-sign.py
index 094a2b0..f04783f 100755
--- a/lang/python/tests/t-encrypt-sign.py
+++ b/lang/python/tests/t-encrypt-sign.py
@@ -24,7 +24,6 @@ import sys
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.set_armor(True)
diff --git a/lang/python/tests/t-encrypt-sym.py b/lang/python/tests/t-encrypt-sym.py
index 07e6b62..8ee9cd6 100755
--- a/lang/python/tests/t-encrypt-sym.py
+++ b/lang/python/tests/t-encrypt-sym.py
@@ -24,7 +24,7 @@ import os
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
+support.assert_gpg_version()
for passphrase in ("abc", b"abc"):
c = gpg.Context()
diff --git a/lang/python/tests/t-encrypt.py b/lang/python/tests/t-encrypt.py
index 0c0ca35..921502a 100755
--- a/lang/python/tests/t-encrypt.py
+++ b/lang/python/tests/t-encrypt.py
@@ -23,7 +23,6 @@ del absolute_import, print_function, unicode_literals
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.set_armor(True)
@@ -62,3 +61,18 @@ with gpg.Context(armor=True) as c:
assert support.sign_only.endswith(e.recipients[0].fpr)
else:
assert False, "Expected an InvalidRecipients error, got none"
+
+
+
+ try:
+ # People might be tempted to provide strings.
+ # We should raise something useful.
+ ciphertext, _, _ = c.encrypt("Hallo Leute\n",
+ recipients=keys,
+ sign=False,
+ always_trust=True)
+ except TypeError as e:
+ # This test is a bit fragile, because the message
+ # may very well change. So if the behaviour will change
+ # this test can easily be deleted.
+ assert "encode" in str(e)
diff --git a/lang/python/tests/t-export.py b/lang/python/tests/t-export.py
index 4927beb..b9d5204 100755
--- a/lang/python/tests/t-export.py
+++ b/lang/python/tests/t-export.py
@@ -23,7 +23,6 @@ del absolute_import, print_function, unicode_literals
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.set_armor(True)
diff --git a/lang/python/tests/t-file-name.py b/lang/python/tests/t-file-name.py
index d12afb8..aab5680 100755
--- a/lang/python/tests/t-file-name.py
+++ b/lang/python/tests/t-file-name.py
@@ -26,7 +26,6 @@ import support
testname = "abcde12345"
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.set_armor(True)
diff --git a/lang/python/tests/t-idiomatic.py b/lang/python/tests/t-idiomatic.py
index 485f048..826bc23 100755
--- a/lang/python/tests/t-idiomatic.py
+++ b/lang/python/tests/t-idiomatic.py
@@ -27,8 +27,6 @@ import tempfile
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
-
# Both Context and Data can be used as context manager:
with gpg.Context() as c, gpg.Data() as d:
c.get_engine_info()
diff --git a/lang/python/tests/t-import.py b/lang/python/tests/t-import.py
index 5b0576f..e2edf5a 100755
--- a/lang/python/tests/t-import.py
+++ b/lang/python/tests/t-import.py
@@ -67,7 +67,6 @@ def check_result(result, fpr, secret):
assert len(result.imports) == 1 or fpr == result.imports[1].fpr
assert result.imports[0].result == 0
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.op_import(gpg.Data(file=support.make_filename("pubkey-1.asc")))
diff --git a/lang/python/tests/t-keylist-from-data.py b/lang/python/tests/t-keylist-from-data.py
new file mode 100755
index 0000000..6a26267
--- /dev/null
+++ b/lang/python/tests/t-keylist-from-data.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2016 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import os
+import sys
+import gpg
+import support
+
+support.assert_gpg_version((2, 1, 14))
+
+# Check expration of keys. This test assumes three subkeys of which
+# 2 are expired; it is used with the "Whisky" test key. It has
+# already been checked that these 3 subkeys are available.
+def check_whisky(name, key):
+ sub1 = key.subkeys[2]
+ sub2 = key.subkeys[3]
+
+ assert sub1.expired and sub2.expired, \
+ "Subkey of `{}' not flagged as expired".format(name)
+ assert sub1.expires == 1129636886 and sub2.expires == 1129636939, \
+ "Subkey of `{}' has wrong expiration date".format(name)
+
+keys = [
+ [ "A0FF4590BB6122EDEF6E3C542D727CC768697734", "6AE6D7EE46A871F8",
+ [ [ "Alfa Test", "demo key", "alfa@example.net" ],
+ [ "Alpha Test", "demo key", "alpha@example.net" ],
+ [ "Alice", "demo key", "" ] ], 1 ],
+ [ "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", "5381EA4EE29BA37F",
+ [ [ "Bob", "demo key", "" ],
+ [ "Bravo Test", "demo key", "bravo@example.net" ] ], 1 ],
+ [ "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", "E71E72ACBC43DA60",
+ [ [ "Charlie Test", "demo key", "charlie@example.net" ] ], 1 ],
+ [ "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", "06F22880B0C45424",
+ [ [ "Delta Test", "demo key", "delta@example.net" ] ], 1 ],
+ [ "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", "B5C79E1A7272144D",
+ [ [ "Echelon", "demo key", "" ],
+ [ "Echo Test", "demo key", "echo@example.net" ],
+ [ "Eve", "demo key", "" ] ], 1 ],
+ [ "56D33268F7FE693FBB594762D4BF57F37372E243", "0A32EE79EE45198E",
+ [ [ "Foxtrot Test", "demo key", "foxtrot@example.net" ] ], 1 ],
+ [ "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", "247491CC9DCAD354",
+ [ [ "Golf Test", "demo key", "golf@example.net" ] ], 1 ],
+ [ "9E91CBB11E4D4135583EF90513DB965534C6E3F1", "76E26537D622AD0A",
+ [ [ "Hotel Test", "demo key", "hotel@example.net" ] ], 1 ],
+ [ "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", "C1C8EFDE61F76C73",
+ [ [ "India Test", "demo key", "india@example.net" ] ], 1 ],
+ [ "F8F1EDC73995AB739AD54B380C820C71D2699313", "BD0B108735F8F136",
+ [ [ "Juliet Test", "demo key", "juliet@example.net" ] ], 1 ],
+ [ "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", "86CBB34A9AF64D02",
+ [ [ "Kilo Test", "demo key", "kilo@example.net" ] ], 1 ],
+ [ "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", "0363B449FE56350C",
+ [ [ "Lima Test", "demo key", "lima@example.net" ] ], 1 ],
+ [ "2686AA191A278013992C72EBBE794852BE5CF886", "5F600A834F31EAE8",
+ [ [ "Mallory", "demo key", "" ],
+ [ "Mike Test", "demo key", "mike@example.net" ] ], 1 ],
+ [ "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", "4C1D63308B70E472",
+ [ [ "November Test", "demo key", "november@example.net" ] ], 1 ],
+ [ "43929E89F8F79381678CAE515F6356BA6D9732AC", "FF0785712681619F",
+ [ [ "Oscar Test", "demo key", "oscar@example.net" ] ], 1 ],
+ [ "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", "2764E18263330D9C",
+ [ [ "Papa test", "demo key", "papa@example.net" ] ], 1 ],
+ [ "A7969DA1C3297AA96D49843F1C67EC133C661C84", "6CDCFC44A029ACF4",
+ [ [ "Quebec Test", "demo key", "quebec@example.net" ] ], 1 ],
+ [ "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", "9FAB805A11D102EA",
+ [ [ "Romeo Test", "demo key", "romeo@example.net" ] ], 1 ],
+ [ "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", "93B88B0F0F1B50B4",
+ [ [ "Sierra Test", "demo key", "sierra@example.net" ] ], 1 ],
+ [ "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", "97B60E01101C0402",
+ [ [ "Tango Test", "demo key", "tango@example.net" ] ], 1 ],
+ [ "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", "93079B915522BDB9",
+ [ [ "Uniform Test", "demo key", "uniform@example.net" ] ], 1 ],
+ [ "E8143C489C8D41124DC40D0B47AF4B6961F04784", "04071FB807287134",
+ [ [ "Victor Test", "demo key", "victor@example.org" ] ], 1 ],
+ [ "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", "D7FBB421FD6E27F6",
+ [ [ "Whisky Test", "demo key", "whisky@example.net" ] ], 3,
+ check_whisky ],
+ [ "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", "5CC6F87F41E408BE",
+ [ [ "XRay Test", "demo key", "xray@example.net" ] ], 1 ],
+ [ "ED9B316F78644A58D042655A9EEF34CD4B11B25F", "5ADFD255F7B080AD",
+ [ [ "Yankee Test", "demo key", "yankee@example.net" ] ], 1 ],
+ [ "23FD347A419429BACCD5E72D6BC4778054ACD246", "EF9DC276A172C881",
+ [ [ "Zulu Test", "demo key", "zulu@example.net" ] ], 1 ],
+]
+
+def check_global(key, uids, n_subkeys):
+ assert not key.revoked, "Key unexpectedly revoked"
+ assert not key.expired, "Key unexpectedly expired"
+ assert not key.disabled, "Key unexpectedly disabled"
+ assert not key.invalid, "Key unexpectedly invalid"
+ assert key.can_sign, "Key unexpectedly unusable for signing"
+ assert key.can_certify, "Key unexpectedly unusable for certifications"
+ assert not key.secret, "Key unexpectedly secret"
+ assert not key.protocol != gpg.constants.protocol.OpenPGP, \
+ "Key has unexpected protocol: {}".format(key.protocol)
+ assert not key.issuer_serial, \
+ "Key unexpectedly carries issuer serial: {}".format(key.issuer_serial)
+ assert not key.issuer_name, \
+ "Key unexpectedly carries issuer name: {}".format(key.issuer_name)
+ assert not key.chain_id, \
+ "Key unexpectedly carries chain ID: {}".format(key.chain_id)
+ assert key.owner_trust == gpg.constants.validity.UNKNOWN, \
+ "Key has unexpected owner trust: {}".format(key.owner_trust)
+ assert len(key.subkeys) - 1 == n_subkeys, \
+ "Key `{}' has unexpected number of subkeys".format(uids[0][0])
+
+
+def check_subkey(fpr, which, subkey):
+ assert not subkey.revoked, which + " key unexpectedly revoked"
+ assert not subkey.expired, which + " key unexpectedly expired"
+ assert not subkey.disabled, which + " key unexpectedly disabled"
+ assert not subkey.invalid, which + " key unexpectedly invalid"
+
+ if which == "Primary":
+ assert not subkey.can_encrypt, \
+ which + " key unexpectedly usable for encryption"
+ assert subkey.can_sign, \
+ which + " key unexpectedly unusable for signing"
+ assert subkey.can_certify, \
+ which + " key unexpectedly unusable for certifications"
+ else:
+ assert subkey.can_encrypt, \
+ which + " key unexpectedly unusable for encryption"
+ assert not subkey.can_sign, \
+ which + " key unexpectedly usable for signing"
+ assert not subkey.can_certify, \
+ which + " key unexpectedly usable for certifications"
+
+ assert not subkey.secret, which + " key unexpectedly secret"
+ assert not subkey.is_cardkey, "Public key marked as card key"
+ assert not subkey.card_number, "Public key with card number set"
+ assert not subkey.pubkey_algo != (gpg.constants.pk.DSA if which == "Primary"
+ else gpg.constants.pk.ELG_E), \
+ which + " key has unexpected public key algo: {}".\
+ format(subkey.pubkey_algo)
+ assert subkey.length == 1024, \
+ which + " key has unexpected length: {}".format(subkey.length)
+ assert fpr.endswith(subkey.keyid), \
+ which + " key has unexpected key ID: {}".format(subkey.keyid)
+ assert which == "Secondary" or subkey.fpr == fpr, \
+ which + " key has unexpected fingerprint: {}".format(subkey.fpr)
+ assert not subkey.expires, \
+ which + " key unexpectedly expires: {}".format(subkey.expires)
+
+def check_uid(which, ref, uid):
+ assert not uid.revoked, which + " user ID unexpectedly revoked"
+ assert not uid.invalid, which + " user ID unexpectedly invalid"
+ assert uid.validity == gpg.constants.validity.UNKNOWN, \
+ which + " user ID has unexpected validity: {}".format(uid.validity)
+ assert not uid.signatures, which + " user ID unexpectedly signed"
+ assert uid.name == ref[0], \
+ "Unexpected name in {} user ID: {!r}".format(which.lower(), uid.name)
+ assert uid.comment == ref[1], \
+ "Unexpected comment in {} user ID: {!r}".format(which.lower(),
+ uid.comment)
+ assert uid.email == ref[2], \
+ "Unexpected email in {} user ID: {!r}".format(which.lower(), uid.email)
+
+# Export all the data from our keyring...
+key_data = gpg.Data()
+with gpg.Context() as c:
+ c.op_export_keys([c.get_key(k[0]) for k in keys], 0, key_data)
+
+# ... rewind the tape...
+key_data.rewind()
+
+# ... and feed it into a keylist in an empty context.
+with support.EphemeralContext() as c:
+ for i, key in enumerate(c.keylist(source=key_data)):
+ try:
+ if len(keys[i]) == 4:
+ fpr, sec_keyid, uids, n_subkeys = keys[i]
+ misc_check = None
+ else:
+ fpr, sec_keyid, uids, n_subkeys, misc_check = keys[i]
+ except IndexError:
+ # There are more keys. We don't check for that.
+ break
+
+ # Global key flags.
+ check_global(key, uids, n_subkeys)
+ check_subkey(fpr, "Primary", key.subkeys[0])
+ check_subkey(sec_keyid, "Secondary", key.subkeys[1])
+
+ assert len(key.uids) == len(uids)
+ check_uid("First", uids[0], key.uids[0])
+ if len(key.uids) > 1:
+ check_uid("Second", uids[1], key.uids[1])
+ if len(key.uids) > 2:
+ check_uid("Third", uids[2], key.uids[2])
+
+ if misc_check:
+ misc_check (uids[0][0], key)
+
+ assert len(list(c.keylist())) == 0, "Keys were imported"
diff --git a/lang/python/tests/t-keylist.py b/lang/python/tests/t-keylist.py
index ea2a724..76c793e 100755
--- a/lang/python/tests/t-keylist.py
+++ b/lang/python/tests/t-keylist.py
@@ -23,7 +23,6 @@ del absolute_import, print_function, unicode_literals
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
# Check expration of keys. This test assumes three subkeys of which
@@ -219,6 +218,18 @@ result = c.op_keylist_result()
assert not result.truncated, "Key listing unexpectedly truncated"
+# We test for a parameter-less keylist
+keyring_length = len(list(c.op_keylist_all()))
+assert keyring_length > 1,\
+ "Expected to find some keys, but got %r" % keyring_length
+
+# Then we do want to call with a pattern, only
+# i.e. without giving secret=0
+alpha_keys = list(c.op_keylist_all(b"Alpha"))
+assert len(alpha_keys) == 1, "Expected only one key for 'Alpha', got %r" % len(alpha_keys)
+
+
+
for i, key in enumerate(c.keylist()):
try:
if len(keys[i]) == 4:
diff --git a/lang/python/tests/t-protocol-assuan.py b/lang/python/tests/t-protocol-assuan.py
index 0084a6b..27b28c7 100755
--- a/lang/python/tests/t-protocol-assuan.py
+++ b/lang/python/tests/t-protocol-assuan.py
@@ -24,9 +24,12 @@ import gpg
with gpg.Context(protocol=gpg.constants.protocol.ASSUAN) as c:
# Do nothing.
- c.assuan_transact('nop')
- c.assuan_transact('NOP')
- c.assuan_transact(['NOP'])
+ err = c.assuan_transact('nop')
+ assert err == None
+ err = c.assuan_transact(b'NOP')
+ assert err == None
+ err = c.assuan_transact(['NOP'])
+ assert err == None
err = c.assuan_transact('idontexist')
assert err.getsource() == gpg.errors.SOURCE_GPGAGENT
diff --git a/lang/python/tests/t-quick-key-creation.py b/lang/python/tests/t-quick-key-creation.py
new file mode 100755
index 0000000..8b7372e
--- /dev/null
+++ b/lang/python/tests/t-quick-key-creation.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import gpg
+import itertools
+import time
+
+import support
+support.assert_gpg_version((2, 1, 2))
+
+alpha = "Alpha <alpha@invalid.example.net>"
+
+with support.EphemeralContext() as ctx:
+ res = ctx.create_key(alpha)
+
+ keys = list(ctx.keylist())
+ assert len(keys) == 1, "Weird number of keys created"
+
+ key = keys[0]
+ assert key.fpr == res.fpr
+ assert len(key.subkeys) == 2, "Expected one primary key and one subkey"
+ assert key.subkeys[0].expires > 0, "Expected primary key to expire"
+
+ # Try to create a key with the same UID
+ try:
+ ctx.create_key(alpha)
+ assert False, "Expected an error but got none"
+ except gpg.errors.GpgError as e:
+ pass
+
+ # Try to create a key with the same UID, now with force!
+ res2 = ctx.create_key(alpha, force=True)
+ assert res.fpr != res2.fpr
+
+
+# From here on, we use one context, and create unique UIDs
+uid_counter = 0
+def make_uid():
+ global uid_counter
+ uid_counter += 1
+ return "user{0}@invalid.example.org".format(uid_counter)
+
+with support.EphemeralContext() as ctx:
+ # Check gpg.constants.create.NOEXPIRE...
+ res = ctx.create_key(make_uid(), expires=False)
+ key = ctx.get_key(res.fpr, secret=True)
+ assert key.fpr == res.fpr
+ assert len(key.subkeys) == 2, "Expected one primary key and one subkey"
+ assert key.subkeys[0].expires == 0, "Expected primary key not to expire"
+
+ t = 2 * 24 * 60 * 60
+ slack = 5 * 60
+ res = ctx.create_key(make_uid(), expires_in=t)
+ key = ctx.get_key(res.fpr, secret=True)
+ assert key.fpr == res.fpr
+ assert len(key.subkeys) == 2, "Expected one primary key and one subkey"
+ assert abs(time.time() + t - key.subkeys[0].expires) < slack, \
+ "Primary keys expiration time is off"
+
+ # Check capabilities
+ for sign, encrypt, certify, authenticate in itertools.product([False, True],
+ [False, True],
+ [False, True],
+ [False, True]):
+ # Filter some out
+ if not (sign or encrypt or certify or authenticate):
+ # This triggers the default capabilities tested before.
+ continue
+ if (sign or encrypt or authenticate) and not certify:
+ # The primary key always certifies.
+ continue
+
+ res = ctx.create_key(make_uid(), algorithm="rsa",
+ sign=sign, encrypt=encrypt, certify=certify,
+ authenticate=authenticate)
+ key = ctx.get_key(res.fpr, secret=True)
+ assert key.fpr == res.fpr
+ assert len(key.subkeys) == 1, \
+ "Expected no subkey for non-default capabilities"
+
+ p = key.subkeys[0]
+ assert sign == p.can_sign
+ assert encrypt == p.can_encrypt
+ assert certify == p.can_certify
+ assert authenticate == p.can_authenticate
+
+ # Check algorithm
+ res = ctx.create_key(make_uid(), algorithm="rsa")
+ key = ctx.get_key(res.fpr, secret=True)
+ assert key.fpr == res.fpr
+ for k in key.subkeys:
+ assert k.pubkey_algo == 1
+
+ # Check algorithm with size
+ res = ctx.create_key(make_uid(), algorithm="rsa1024")
+ key = ctx.get_key(res.fpr, secret=True)
+ assert key.fpr == res.fpr
+ for k in key.subkeys:
+ assert k.pubkey_algo == 1
+ assert k.length == 1024
+
+ # Check algorithm future-default
+ ctx.create_key(make_uid(), algorithm="future-default")
+
+ # Check passphrase protection
+ recipient = make_uid()
+ passphrase = "streng geheim"
+ res = ctx.create_key(recipient, passphrase=passphrase)
+ ciphertext, _, _ = ctx.encrypt(b"hello there", recipients=[ctx.get_key(res.fpr)])
+
+ cb_called = False
+ def cb(*args):
+ global cb_called
+ cb_called = True
+ return passphrase
+ ctx.pinentry_mode = gpg.constants.PINENTRY_MODE_LOOPBACK
+ ctx.set_passphrase_cb(cb)
+
+ plaintext, _, _ = ctx.decrypt(ciphertext)
+ assert plaintext == b"hello there"
+ assert cb_called
diff --git a/lang/python/tests/t-quick-key-manipulation.py b/lang/python/tests/t-quick-key-manipulation.py
new file mode 100755
index 0000000..0f47006
--- /dev/null
+++ b/lang/python/tests/t-quick-key-manipulation.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import os
+import gpg
+
+import support
+support.assert_gpg_version((2, 1, 14))
+
+alpha = "Alpha <alpha@invalid.example.net>"
+bravo = "Bravo <bravo@invalid.example.net>"
+
+with support.EphemeralContext() as ctx:
+ res = ctx.create_key(alpha, certify=True)
+ key = ctx.get_key(res.fpr)
+ assert len(key.subkeys) == 1, "Expected one primary key and no subkeys"
+ assert len(key.uids) == 1, "Expected exactly one UID"
+
+ def get_uid(uid):
+ key = ctx.get_key(res.fpr)
+ for u in key.uids:
+ if u.uid == uid:
+ return u
+ return None
+
+ # sanity check
+ uid = get_uid(alpha)
+ assert uid, "UID alpha not found"
+ assert uid.revoked == 0
+
+ # add bravo
+ ctx.key_add_uid(key, bravo)
+ uid = get_uid(bravo)
+ assert uid, "UID bravo not found"
+ assert uid.revoked == 0
+
+ # revoke alpha
+ ctx.key_revoke_uid(key, alpha)
+ uid = get_uid(alpha)
+ assert uid, "UID alpha not found"
+ assert uid.revoked == 1
+ uid = get_uid(bravo)
+ assert uid, "UID bravo not found"
+ assert uid.revoked == 0
+
+ # try to revoke the last UID
+ try:
+ ctx.key_revoke_uid(key, alpha)
+ # IMHO this should fail. issue2961.
+ # assert False, "Expected an error but got none"
+ except gpg.errors.GpgError:
+ pass
+
+ # Everything should be the same
+ uid = get_uid(alpha)
+ assert uid, "UID alpha not found"
+ assert uid.revoked == 1
+ uid = get_uid(bravo)
+ assert uid, "UID bravo not found"
+ assert uid.revoked == 0
+
+ # try to revoke a non-existent UID
+ try:
+ ctx.key_revoke_uid(key, "i dont exist")
+ # IMHO this should fail. issue2963.
+ # assert False, "Expected an error but got none"
+ except gpg.errors.GpgError:
+ pass
+
+ # try to add an pre-existent UID
+ try:
+ ctx.key_add_uid(key, bravo)
+ assert False, "Expected an error but got none"
+ except gpg.errors.GpgError:
+ pass
+
+ # Check setting the TOFU policy.
+ with open(os.path.join(ctx.home_dir, "gpg.conf"), "a") as handle:
+ handle.write("trust-model tofu+pgp\n")
+
+ for name, policy in [(name, getattr(gpg.constants.tofu.policy, name))
+ for name in filter(lambda x: not x.startswith('__'),
+ dir(gpg.constants.tofu.policy))]:
+ if policy == gpg.constants.tofu.policy.NONE:
+ # We must not set the policy to NONE.
+ continue
+
+ ctx.key_tofu_policy(key, policy)
+
+ keys = list(ctx.keylist(key.uids[0].uid,
+ mode=(gpg.constants.keylist.mode.LOCAL
+ |gpg.constants.keylist.mode.WITH_TOFU)))
+ assert len(keys) == 1
+
+ if policy == gpg.constants.tofu.policy.AUTO:
+ # We cannot check that it is set to AUTO.
+ continue
+
+ for uid in keys[0].uids:
+ if uid.uid == alpha:
+ # TOFU information of revoked UIDs is not updated.
+ # XXX: Is that expected?
+ continue
+ assert uid.tofu[0].policy == policy, \
+ "Expected policy {0} ({1}), got {2}".format(policy, name,
+ uid.tofu[0].policy)
diff --git a/lang/python/tests/t-quick-key-signing.py b/lang/python/tests/t-quick-key-signing.py
new file mode 100755
index 0000000..3d648c5
--- /dev/null
+++ b/lang/python/tests/t-quick-key-signing.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import gpg
+import itertools
+import time
+
+import support
+support.assert_gpg_version((2, 1, 1))
+
+with support.EphemeralContext() as ctx:
+ uid_counter = 0
+ def make_uid():
+ global uid_counter
+ uid_counter += 1
+ return "user{0}@invalid.example.org".format(uid_counter)
+
+ def make_key():
+ uids = [make_uid() for i in range(3)]
+ res = ctx.create_key(uids[0], certify=True)
+ key = ctx.get_key(res.fpr)
+ for u in uids[1:]:
+ ctx.key_add_uid(key, u)
+ return key, uids
+
+ def check_sigs(key, expected_sigs):
+ keys = list(ctx.keylist(key.fpr, mode=(gpg.constants.keylist.mode.LOCAL
+ |gpg.constants.keylist.mode.SIGS)))
+ assert len(keys) == 1
+ key_uids = {uid.uid: [s for s in uid.signatures] for uid in keys[0].uids}
+ expected = list(expected_sigs)
+
+ while key_uids and expected:
+ uid, signing_key, func = expected[0]
+ match = False
+ for i, s in enumerate(key_uids[uid]):
+ if signing_key.fpr.endswith(s.keyid):
+ if func:
+ func(s)
+ match = True
+ break
+ if match:
+ expected.pop(0)
+ key_uids[uid].pop(i)
+ if not key_uids[uid]:
+ del key_uids[uid]
+
+ assert not key_uids, "Superfluous signatures: {0}".format(key_uids)
+ assert not expected, "Missing signatures: {0}".format(expected)
+
+ # Simplest case. Sign without any options.
+ key_a, uids_a = make_key()
+ key_b, uids_b = make_key()
+ ctx.signers = [key_a]
+
+ def exportable_non_expiring(s):
+ assert s.exportable
+ assert s.expires == 0
+
+ check_sigs(key_b, itertools.product(uids_b, [key_b], [exportable_non_expiring]))
+ ctx.key_sign(key_b)
+ check_sigs(key_b, itertools.product(uids_b, [key_b, key_a], [exportable_non_expiring]))
+
+ # Create a non-exportable signature, and explicitly name all uids.
+ key_c, uids_c = make_key()
+ ctx.signers = [key_a, key_b]
+
+ def non_exportable_non_expiring(s):
+ assert s.exportable == 0
+ assert s.expires == 0
+
+ ctx.key_sign(key_c, local=True, uids=uids_c)
+ check_sigs(key_c,
+ list(itertools.product(uids_c, [key_c],
+ [exportable_non_expiring]))
+ + list(itertools.product(uids_c, [key_b, key_a],
+ [non_exportable_non_expiring])))
+
+ # Create a non-exportable, expiring signature for a single uid.
+ key_d, uids_d = make_key()
+ ctx.signers = [key_c]
+ expires_in = 600
+ slack = 10
+
+ def non_exportable_expiring(s):
+ assert s.exportable == 0
+ assert abs(time.time() + expires_in - s.expires) < slack
+
+ ctx.key_sign(key_d, local=True, expires_in=expires_in, uids=uids_d[0])
+ check_sigs(key_d,
+ list(itertools.product(uids_d, [key_d],
+ [exportable_non_expiring]))
+ + list(itertools.product(uids_d[:1], [key_c],
+ [non_exportable_expiring])))
+
+ # Now sign the second in the same fashion, but use a singleton list.
+ ctx.key_sign(key_d, local=True, expires_in=expires_in, uids=uids_d[1:2])
+ check_sigs(key_d,
+ list(itertools.product(uids_d, [key_d],
+ [exportable_non_expiring]))
+ + list(itertools.product(uids_d[:2], [key_c],
+ [non_exportable_expiring])))
diff --git a/lang/python/tests/t-quick-subkey-creation.py b/lang/python/tests/t-quick-subkey-creation.py
new file mode 100755
index 0000000..ad4f35c
--- /dev/null
+++ b/lang/python/tests/t-quick-subkey-creation.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import gpg
+import itertools
+import time
+
+import support
+
+alpha = "Alpha <alpha@invalid.example.net>"
+bravo = "Bravo <bravo@invalid.example.net>"
+
+with support.EphemeralContext() as ctx:
+ res = ctx.create_key(alpha, certify=True)
+ keys = list(ctx.keylist())
+ assert len(keys) == 1, "Weird number of keys created"
+ key = keys[0]
+ assert key.fpr == res.fpr
+ assert len(key.subkeys) == 1, "Expected one primary key and no subkeys"
+
+ def get_subkey(fpr):
+ k = ctx.get_key(fpr)
+ for sk in k.subkeys:
+ if sk.fpr == fpr:
+ return sk
+ return None
+
+ # Check gpg.constants.create.NOEXPIRE...
+ res = ctx.create_subkey(key, expires=False)
+ subkey = get_subkey(res.fpr)
+ assert subkey.expires == 0, "Expected subkey not to expire"
+ assert subkey.can_encrypt, \
+ "Default subkey capabilities do not include encryption"
+
+ t = 2 * 24 * 60 * 60
+ slack = 5 * 60
+ res = ctx.create_subkey(key, expires_in=t)
+ subkey = get_subkey(res.fpr)
+ assert abs(time.time() + t - subkey.expires) < slack, \
+ "subkeys expiration time is off"
+
+ # Check capabilities
+ for sign, encrypt, authenticate in itertools.product([False, True],
+ [False, True],
+ [False, True]):
+ # Filter some out
+ if not (sign or encrypt or authenticate):
+ # This triggers the default capabilities tested before.
+ continue
+
+ res = ctx.create_subkey(key, sign=sign, encrypt=encrypt,
+ authenticate=authenticate)
+ subkey = get_subkey(res.fpr)
+ assert sign == subkey.can_sign
+ assert encrypt == subkey.can_encrypt
+ assert authenticate == subkey.can_authenticate
+
+ # Check algorithm
+ res = ctx.create_subkey(key, algorithm="rsa")
+ subkey = get_subkey(res.fpr)
+ assert subkey.pubkey_algo == 1
+
+ # Check algorithm with size
+ res = ctx.create_subkey(key, algorithm="rsa1024")
+ subkey = get_subkey(res.fpr)
+ assert subkey.pubkey_algo == 1
+ assert subkey.length == 1024
+
+ # Check algorithm future-default
+ ctx.create_subkey(key, algorithm="future-default")
+
+ # Check passphrase protection. For this we create a new key
+ # so that we have a key with just one encryption subkey.
+ bravo_res = ctx.create_key(bravo, certify=True)
+ bravo_key = ctx.get_key(bravo_res.fpr)
+ assert len(bravo_key.subkeys) == 1, "Expected one primary key and no subkeys"
+
+ passphrase = "streng geheim"
+ res = ctx.create_subkey(bravo_key, passphrase=passphrase)
+ ciphertext, _, _ = ctx.encrypt(b"hello there",
+ recipients=[ctx.get_key(bravo_res.fpr)])
+
+ cb_called = False
+ def cb(*args):
+ global cb_called
+ cb_called = True
+ return passphrase
+ ctx.pinentry_mode = gpg.constants.PINENTRY_MODE_LOOPBACK
+ ctx.set_passphrase_cb(cb)
+
+ plaintext, _, _ = ctx.decrypt(ciphertext)
+ assert plaintext == b"hello there"
+ assert cb_called
diff --git a/lang/python/tests/t-sig-notation.py b/lang/python/tests/t-sig-notation.py
index f1342b1..2277497 100755
--- a/lang/python/tests/t-sig-notation.py
+++ b/lang/python/tests/t-sig-notation.py
@@ -62,8 +62,6 @@ def check_result(result):
assert len(expected_notations) == 0
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
-
source = gpg.Data("Hallo Leute\n")
signed = gpg.Data()
diff --git a/lang/python/tests/t-sign.py b/lang/python/tests/t-sign.py
index 9418ed8..d375729 100755
--- a/lang/python/tests/t-sign.py
+++ b/lang/python/tests/t-sign.py
@@ -53,8 +53,6 @@ def check_result(r, typ):
if signature.fpr != "A0FF4590BB6122EDEF6E3C542D727CC768697734":
fail("Wrong fingerprint reported: {}".format(signature.fpr))
-
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.set_textmode(True)
c.set_armor(True)
diff --git a/lang/python/tests/t-signers.py b/lang/python/tests/t-signers.py
index 80e797c..5864ee5 100755
--- a/lang/python/tests/t-signers.py
+++ b/lang/python/tests/t-signers.py
@@ -53,8 +53,6 @@ def check_result(r, typ):
"23FD347A419429BACCD5E72D6BC4778054ACD246"):
fail("Wrong fingerprint reported: {}".format(signature.fpr))
-
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.set_textmode(True)
c.set_armor(True)
diff --git a/lang/python/tests/t-trustlist.py b/lang/python/tests/t-trustlist.py
index 8c5e214..8586596 100755
--- a/lang/python/tests/t-trustlist.py
+++ b/lang/python/tests/t-trustlist.py
@@ -23,7 +23,6 @@ del absolute_import, print_function, unicode_literals
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
def dump_item(item):
diff --git a/lang/python/tests/t-verify.py b/lang/python/tests/t-verify.py
index f18e1dd..0347638 100755
--- a/lang/python/tests/t-verify.py
+++ b/lang/python/tests/t-verify.py
@@ -97,8 +97,6 @@ def check_result(result, summary, validity, fpr, status, notation):
sig.validity, validity)
assert gpg.errors.GPGMEError(sig.validity_reason).getcode() == gpg.errors.NO_ERROR
-
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.set_armor(True)
diff --git a/lang/python/tests/t-wait.py b/lang/python/tests/t-wait.py
index b1f2043..0c403fa 100755
--- a/lang/python/tests/t-wait.py
+++ b/lang/python/tests/t-wait.py
@@ -24,7 +24,6 @@ import time
import gpg
import support
-support.init_gpgme(gpg.constants.protocol.OpenPGP)
c = gpg.Context()
c.set_armor(True)
diff --git a/lang/qt/Makefile.in b/lang/qt/Makefile.in
index 5c32620..e054e25 100644
--- a/lang/qt/Makefile.in
+++ b/lang/qt/Makefile.in
@@ -110,8 +110,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
diff --git a/lang/qt/doc/Makefile.in b/lang/qt/doc/Makefile.in
index c4aff43..7e86941 100644
--- a/lang/qt/doc/Makefile.in
+++ b/lang/qt/doc/Makefile.in
@@ -108,8 +108,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am
index 87e2ec2..c81461e 100644
--- a/lang/qt/src/Makefile.am
+++ b/lang/qt/src/Makefile.am
@@ -37,7 +37,7 @@ qgpgme_sources = \
qgpgmekeyformailboxjob.cpp gpgme_backend_debug.cpp \
qgpgmetofupolicyjob.cpp \
defaultkeygenerationjob.cpp qgpgmewkspublishjob.cpp \
- dn.cpp
+ dn.cpp cryptoconfig.cpp
# If you add one here make sure that you also add one in camelcase
qgpgme_headers= \
@@ -220,6 +220,12 @@ libqgpgme_la_LIBADD = ../../cpp/src/libgpgmepp.la ../../../src/libgpgme.la \
libqgpgme_la_LDFLAGS = -no-undefined -version-info \
@LIBQGPGME_LT_CURRENT@:@LIBQGPGME_LT_REVISION@:@LIBQGPGME_LT_AGE@
+if HAVE_MACOS_SYSTEM
+libsuffix=.dylib
+else
+libsuffix=.so
+endif
+
if HAVE_W32_SYSTEM
QGpgmeConfig.cmake: QGpgmeConfig-w32.cmake.in
sed -e 's|[@]resolved_bindir@|$(bindir)|g' < "$<" | \
@@ -228,6 +234,7 @@ QGpgmeConfig.cmake: QGpgmeConfig-w32.cmake.in
else
QGpgmeConfig.cmake: QGpgmeConfig.cmake.in
sed -e 's|[@]resolved_libdir@|$(libdir)|g' < "$<" | \
+ sed -e 's|[@]libsuffix@|$(libsuffix)|g' | \
sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@
endif
diff --git a/lang/qt/src/Makefile.in b/lang/qt/src/Makefile.in
index 5b87f6e..635aaaa 100644
--- a/lang/qt/src/Makefile.in
+++ b/lang/qt/src/Makefile.in
@@ -97,8 +97,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
@@ -154,7 +154,8 @@ am__objects_1 = dataprovider.lo job.lo multideletejob.lo \
qgpgmeverifydetachedjob.lo qgpgmeverifyopaquejob.lo \
threadedjobmixin.lo qgpgmekeyformailboxjob.lo \
gpgme_backend_debug.lo qgpgmetofupolicyjob.lo \
- defaultkeygenerationjob.lo qgpgmewkspublishjob.lo dn.lo
+ defaultkeygenerationjob.lo qgpgmewkspublishjob.lo dn.lo \
+ cryptoconfig.lo
am__objects_2 =
am_libqgpgme_la_OBJECTS = $(am__objects_1) $(am__objects_2) \
$(am__objects_2)
@@ -484,7 +485,7 @@ qgpgme_sources = \
qgpgmekeyformailboxjob.cpp gpgme_backend_debug.cpp \
qgpgmetofupolicyjob.cpp \
defaultkeygenerationjob.cpp qgpgmewkspublishjob.cpp \
- dn.cpp
+ dn.cpp cryptoconfig.cpp
# If you add one here make sure that you also add one in camelcase
@@ -667,6 +668,8 @@ libqgpgme_la_LIBADD = ../../cpp/src/libgpgmepp.la ../../../src/libgpgme.la \
libqgpgme_la_LDFLAGS = -no-undefined -version-info \
@LIBQGPGME_LT_CURRENT@:@LIBQGPGME_LT_REVISION@:@LIBQGPGME_LT_AGE@
+@HAVE_MACOS_SYSTEM_FALSE@libsuffix = .so
+@HAVE_MACOS_SYSTEM_TRUE@libsuffix = .dylib
BUILT_SOURCES = $(qgpgme_moc_sources) $(camelcase_headers)
CLEANFILES = $(qgpgme_moc_sources) $(camelcase_headers) QGpgmeConfig.cmake \
qgpgme_version.h QGpgmeConfig.cmake.in \
@@ -761,6 +764,7 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cryptoconfig.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dataprovider.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defaultkeygenerationjob.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dn.Plo@am__quote@
@@ -1113,6 +1117,7 @@ uninstall-am: uninstall-camelcaseincludeHEADERS \
@HAVE_W32_SYSTEM_TRUE@ sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@
@HAVE_W32_SYSTEM_FALSE@QGpgmeConfig.cmake: QGpgmeConfig.cmake.in
@HAVE_W32_SYSTEM_FALSE@ sed -e 's|[@]resolved_libdir@|$(libdir)|g' < "$<" | \
+@HAVE_W32_SYSTEM_FALSE@ sed -e 's|[@]libsuffix@|$(libsuffix)|g' | \
@HAVE_W32_SYSTEM_FALSE@ sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@
$(camelcase_headers): Makefile.am
diff --git a/lang/qt/src/QGpgmeConfig.cmake.in.in b/lang/qt/src/QGpgmeConfig.cmake.in.in
index 88ed242..a17a19f 100644
--- a/lang/qt/src/QGpgmeConfig.cmake.in.in
+++ b/lang/qt/src/QGpgmeConfig.cmake.in.in
@@ -64,7 +64,7 @@ add_library(QGpgme SHARED IMPORTED)
set_target_properties(QGpgme PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "@resolved_includedir@/qgpgme;@resolved_includedir@"
INTERFACE_LINK_LIBRARIES "Gpgmepp;Qt5::Core"
- IMPORTED_LOCATION "@resolved_libdir@/libqgpgme.so"
+ IMPORTED_LOCATION "@resolved_libdir@/libqgpgme@libsuffix@"
)
if(CMAKE_VERSION VERSION_LESS 2.8.12)
diff --git a/lang/qt/src/cryptoconfig.cpp b/lang/qt/src/cryptoconfig.cpp
new file mode 100644
index 0000000..be265d8
--- /dev/null
+++ b/lang/qt/src/cryptoconfig.cpp
@@ -0,0 +1,44 @@
+/*
+ cryptoconfig.cpp
+
+ This file is part of qgpgme, the Qt API binding for gpgme
+ Copyright (c) 2017 Intevation GmbH
+
+ QGpgME 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.
+
+ QGpgME 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ Qt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+#include "cryptoconfig.h"
+#include "qgpgmenewcryptoconfig.h"
+
+using namespace QGpgME;
+
+QStringList CryptoConfigEntry::stringValueList() const
+{
+ const QGpgMENewCryptoConfigEntry *entry = dynamic_cast <const QGpgMENewCryptoConfigEntry*> (this);
+ if (!entry) {
+ return QStringList();
+ }
+ return entry->stringValueList();
+}
diff --git a/lang/qt/src/cryptoconfig.h b/lang/qt/src/cryptoconfig.h
index c3f0c7e..c4de22d 100644
--- a/lang/qt/src/cryptoconfig.h
+++ b/lang/qt/src/cryptoconfig.h
@@ -248,6 +248,15 @@ public:
* @return true if the value was changed
*/
virtual bool isDirty() const = 0;
+
+ // Design change from here on we are closely bound to one implementation
+ // of cryptoconfig. To avoid ABI breaks with every new function we
+ // add real functions from now on.
+
+ /**
+ * @return a stringValueList.
+ */
+ QStringList stringValueList() const;
};
/**
@@ -379,9 +388,8 @@ public:
/**
* Write back changes
*
- * @param runtime If this option is set, the changes will take effect at run-time, as
- * far as this is possible. Otherwise, they will take effect at the next
- * start of the respective backend programs.
+ * @param runtime this parameter is ignored. Changes will always
+ * be made with --runtime set.
*/
virtual void sync(bool runtime) = 0;
diff --git a/lang/qt/src/defaultkeygenerationjob.cpp b/lang/qt/src/defaultkeygenerationjob.cpp
index 020f4d2..f589384 100644
--- a/lang/qt/src/defaultkeygenerationjob.cpp
+++ b/lang/qt/src/defaultkeygenerationjob.cpp
@@ -91,6 +91,11 @@ void DefaultKeyGenerationJob::slotCancel()
GpgME::Error DefaultKeyGenerationJob::start(const QString &email, const QString &name)
{
+ const QString namePart = name.isEmpty() ? QString() :
+ QStringLiteral("name-real: %1\n").arg(name);
+ const QString mailPart = email.isEmpty() ? QString() :
+ QStringLiteral("name-email: %1\n").arg(email);
+
const QString args = QStringLiteral("<GnupgKeyParms format=\"internal\">\n"
"%ask-passphrase\n"
"key-type: RSA\n"
@@ -99,9 +104,9 @@ GpgME::Error DefaultKeyGenerationJob::start(const QString &email, const QString
"subkey-type: RSA\n"
"subkey-length: 2048\n"
"subkey-usage: encrypt\n"
- "name-email: %1\n"
- "name-real: %2\n"
- "</GnupgKeyParms>").arg(email, name);
+ "%1"
+ "%2"
+ "</GnupgKeyParms>").arg(mailPart, namePart);
d->job = openpgp()->keyGenerationJob();
d->job->installEventFilter(this);
diff --git a/lang/qt/src/dn.cpp b/lang/qt/src/dn.cpp
index 0f81a4c..f9fb2f6 100644
--- a/lang/qt/src/dn.cpp
+++ b/lang/qt/src/dn.cpp
@@ -37,6 +37,8 @@
#include "dn.h"
+#include <gpg-error.h>
+
static const struct {
const char *name;
const char *oid;
@@ -165,7 +167,7 @@ parse_dn_part(DnPair *array, const unsigned char *string)
for (unsigned int i = 0; i < numOidMaps; ++i)
if (!strcasecmp((char *)p, oidmap[i].oid)) {
free(p);
- p = strdup(oidmap[i].name);
+ gpgrt_asprintf(&p, oidmap[i].name);
break;
}
array->key = p;
diff --git a/lang/qt/src/qgpgmenewcryptoconfig.cpp b/lang/qt/src/qgpgmenewcryptoconfig.cpp
index eb3af56..6901eef 100644
--- a/lang/qt/src/qgpgmenewcryptoconfig.cpp
+++ b/lang/qt/src/qgpgmenewcryptoconfig.cpp
@@ -49,6 +49,7 @@
#include <sstream>
#include <string>
#include <cassert>
+#include <functional>
using namespace QGpgME;
using namespace GpgME;
@@ -216,17 +217,12 @@ QGpgMENewCryptoConfigGroup *QGpgMENewCryptoConfigComponent::group(const QString
void QGpgMENewCryptoConfigComponent::sync(bool runtime)
{
- Q_UNUSED(runtime)
- // ### how to pass --runtime to gpgconf? -> marcus: not yet supported (2010-11-20)
+ Q_UNUSED(runtime) // runtime is always set by engine_gpgconf
if (const Error err = m_component.save()) {
-#if 0
- TODO port
- const QString wmsg = i18n("Error from gpgconf while saving configuration: %1", QString::fromLocal8Bit(err.asString()));
- qCWarning(GPGPME_BACKEND_LOG) << ":" << wmsg;
- KMessageBox::error(0, wmsg);
-#endif
+ qCWarning(GPGPME_BACKEND_LOG) << ":"
+ << "Error from gpgconf while saving configuration: %1"
+ << QString::fromLocal8Bit(err.asString());
}
- // ### unset dirty state again
}
////
@@ -551,6 +547,18 @@ std::vector<unsigned int> QGpgMENewCryptoConfigEntry::uintValueList() const
return m_option.currentValue().uintValues();
}
+QStringList QGpgMENewCryptoConfigEntry::stringValueList() const
+{
+ Q_ASSERT(isList());
+ const Argument arg = m_option.currentValue();
+ const std::vector<const char *> values = arg.stringValues();
+ QStringList ret;
+ for(const char *value: values) {
+ ret << QString::fromUtf8(value);
+ }
+ return ret;
+}
+
QList<QUrl> QGpgMENewCryptoConfigEntry::urlValueList() const
{
const Type type = m_option.type();
diff --git a/lang/qt/src/qgpgmenewcryptoconfig.h b/lang/qt/src/qgpgmenewcryptoconfig.h
index 81b4cb4..7100e70 100644
--- a/lang/qt/src/qgpgmenewcryptoconfig.h
+++ b/lang/qt/src/qgpgmenewcryptoconfig.h
@@ -93,6 +93,8 @@ public:
void setURLValueList(const QList<QUrl> &) Q_DECL_OVERRIDE;
bool isDirty() const Q_DECL_OVERRIDE;
+ QStringList stringValueList() const;
+
#if 0
void setDirty(bool b);
QString outputString() const;
diff --git a/lang/qt/src/threadedjobmixin.h b/lang/qt/src/threadedjobmixin.h
index 32b23db..5ad2737 100644
--- a/lang/qt/src/threadedjobmixin.h
+++ b/lang/qt/src/threadedjobmixin.h
@@ -51,6 +51,7 @@
#include "job.h"
#include <cassert>
+#include <functional>
namespace QGpgME
{
diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am
index ad08ad4..93dce07 100644
--- a/lang/qt/tests/Makefile.am
+++ b/lang/qt/tests/Makefile.am
@@ -25,10 +25,11 @@ TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir)
EXTRA_DIST = initial.test
TESTS = initial.test t-keylist t-keylocate t-ownertrust t-tofuinfo \
- t-encrypt t-verify
+ t-encrypt t-verify t-various t-config
moc_files = t-keylist.moc t-keylocate.moc t-ownertrust.moc t-tofuinfo.moc \
- t-encrypt.moc t-support.hmoc t-wkspublish.moc t-verify.moc
+ t-encrypt.moc t-support.hmoc t-wkspublish.moc t-verify.moc \
+ t-various.moc t-config.moc
AM_LDFLAGS = -no-install
@@ -57,6 +58,8 @@ t_tofuinfo_SOURCES = t-tofuinfo.cpp $(support_src)
t_encrypt_SOURCES = t-encrypt.cpp $(support_src)
t_wkspublish_SOURCES = t-wkspublish.cpp $(support_src)
t_verify_SOURCES = t-verify.cpp $(support_src)
+t_various_SOURCES = t-various.cpp $(support_src)
+t_config_SOURCES = t-config.cpp $(support_src)
run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp
nodist_t_keylist_SOURCES = $(moc_files)
@@ -64,12 +67,12 @@ nodist_t_keylist_SOURCES = $(moc_files)
BUILT_SOURCES = $(moc_files)
noinst_PROGRAMS = t-keylist t-keylocate t-ownertrust t-tofuinfo t-encrypt \
- run-keyformailboxjob t-wkspublish t-verify
+ run-keyformailboxjob t-wkspublish t-verify t-various t-config
CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \
random_seed S.gpg-agent .gpg-v21-migrated pubring-stamp $(moc_files) \
- gpg.conf
+ gpg.conf tofu.db
clean-local:
-rm -fR private-keys-v1.d crls.d
diff --git a/lang/qt/tests/Makefile.in b/lang/qt/tests/Makefile.in
index e0ac22e..f370058 100644
--- a/lang/qt/tests/Makefile.in
+++ b/lang/qt/tests/Makefile.in
@@ -98,11 +98,11 @@ build_triplet = @build@
host_triplet = @host@
TESTS = initial.test t-keylist$(EXEEXT) t-keylocate$(EXEEXT) \
t-ownertrust$(EXEEXT) t-tofuinfo$(EXEEXT) t-encrypt$(EXEEXT) \
- t-verify$(EXEEXT)
+ t-verify$(EXEEXT) t-various$(EXEEXT) t-config$(EXEEXT)
noinst_PROGRAMS = t-keylist$(EXEEXT) t-keylocate$(EXEEXT) \
t-ownertrust$(EXEEXT) t-tofuinfo$(EXEEXT) t-encrypt$(EXEEXT) \
run-keyformailboxjob$(EXEEXT) t-wkspublish$(EXEEXT) \
- t-verify$(EXEEXT)
+ t-verify$(EXEEXT) t-various$(EXEEXT) t-config$(EXEEXT)
subdir = lang/qt/tests
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
$(top_srcdir)/build-aux/mkinstalldirs \
@@ -117,8 +117,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
- $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
- $(top_srcdir)/configure.ac
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
@@ -136,6 +136,11 @@ am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
am__objects_1 = t-support.$(OBJEXT)
+am_t_config_OBJECTS = t-config.$(OBJEXT) $(am__objects_1)
+t_config_OBJECTS = $(am_t_config_OBJECTS)
+t_config_LDADD = $(LDADD)
+t_config_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \
+ ../src/libqgpgme.la ../../../src/libgpgme.la
am_t_encrypt_OBJECTS = t-encrypt.$(OBJEXT) $(am__objects_1)
t_encrypt_OBJECTS = $(am_t_encrypt_OBJECTS)
t_encrypt_LDADD = $(LDADD)
@@ -164,6 +169,11 @@ t_tofuinfo_OBJECTS = $(am_t_tofuinfo_OBJECTS)
t_tofuinfo_LDADD = $(LDADD)
t_tofuinfo_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \
../src/libqgpgme.la ../../../src/libgpgme.la
+am_t_various_OBJECTS = t-various.$(OBJEXT) $(am__objects_1)
+t_various_OBJECTS = $(am_t_various_OBJECTS)
+t_various_LDADD = $(LDADD)
+t_various_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \
+ ../src/libqgpgme.la ../../../src/libgpgme.la
am_t_verify_OBJECTS = t-verify.$(OBJEXT) $(am__objects_1)
t_verify_OBJECTS = $(am_t_verify_OBJECTS)
t_verify_LDADD = $(LDADD)
@@ -226,15 +236,17 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
-SOURCES = $(run_keyformailboxjob_SOURCES) $(t_encrypt_SOURCES) \
- $(t_keylist_SOURCES) $(nodist_t_keylist_SOURCES) \
+SOURCES = $(run_keyformailboxjob_SOURCES) $(t_config_SOURCES) \
+ $(t_encrypt_SOURCES) $(t_keylist_SOURCES) \
+ $(nodist_t_keylist_SOURCES) $(t_keylocate_SOURCES) \
+ $(t_ownertrust_SOURCES) $(t_tofuinfo_SOURCES) \
+ $(t_various_SOURCES) $(t_verify_SOURCES) \
+ $(t_wkspublish_SOURCES)
+DIST_SOURCES = $(run_keyformailboxjob_SOURCES) $(t_config_SOURCES) \
+ $(t_encrypt_SOURCES) $(t_keylist_SOURCES) \
$(t_keylocate_SOURCES) $(t_ownertrust_SOURCES) \
- $(t_tofuinfo_SOURCES) $(t_verify_SOURCES) \
+ $(t_tofuinfo_SOURCES) $(t_various_SOURCES) $(t_verify_SOURCES) \
$(t_wkspublish_SOURCES)
-DIST_SOURCES = $(run_keyformailboxjob_SOURCES) $(t_encrypt_SOURCES) \
- $(t_keylist_SOURCES) $(t_keylocate_SOURCES) \
- $(t_ownertrust_SOURCES) $(t_tofuinfo_SOURCES) \
- $(t_verify_SOURCES) $(t_wkspublish_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
@@ -481,7 +493,8 @@ GPG = gpg
TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir)
EXTRA_DIST = initial.test
moc_files = t-keylist.moc t-keylocate.moc t-ownertrust.moc t-tofuinfo.moc \
- t-encrypt.moc t-support.hmoc t-wkspublish.moc t-verify.moc
+ t-encrypt.moc t-support.hmoc t-wkspublish.moc t-verify.moc \
+ t-various.moc t-config.moc
AM_LDFLAGS = -no-install
LDADD = ../../cpp/src/libgpgmepp.la ../src/libqgpgme.la \
@@ -502,13 +515,15 @@ t_tofuinfo_SOURCES = t-tofuinfo.cpp $(support_src)
t_encrypt_SOURCES = t-encrypt.cpp $(support_src)
t_wkspublish_SOURCES = t-wkspublish.cpp $(support_src)
t_verify_SOURCES = t-verify.cpp $(support_src)
+t_various_SOURCES = t-various.cpp $(support_src)
+t_config_SOURCES = t-config.cpp $(support_src)
run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp
nodist_t_keylist_SOURCES = $(moc_files)
BUILT_SOURCES = $(moc_files)
CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \
random_seed S.gpg-agent .gpg-v21-migrated pubring-stamp $(moc_files) \
- gpg.conf
+ gpg.conf tofu.db
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -559,6 +574,10 @@ run-keyformailboxjob$(EXEEXT): $(run_keyformailboxjob_OBJECTS) $(run_keyformailb
@rm -f run-keyformailboxjob$(EXEEXT)
$(AM_V_CXXLD)$(CXXLINK) $(run_keyformailboxjob_OBJECTS) $(run_keyformailboxjob_LDADD) $(LIBS)
+t-config$(EXEEXT): $(t_config_OBJECTS) $(t_config_DEPENDENCIES) $(EXTRA_t_config_DEPENDENCIES)
+ @rm -f t-config$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(t_config_OBJECTS) $(t_config_LDADD) $(LIBS)
+
t-encrypt$(EXEEXT): $(t_encrypt_OBJECTS) $(t_encrypt_DEPENDENCIES) $(EXTRA_t_encrypt_DEPENDENCIES)
@rm -f t-encrypt$(EXEEXT)
$(AM_V_CXXLD)$(CXXLINK) $(t_encrypt_OBJECTS) $(t_encrypt_LDADD) $(LIBS)
@@ -579,6 +598,10 @@ t-tofuinfo$(EXEEXT): $(t_tofuinfo_OBJECTS) $(t_tofuinfo_DEPENDENCIES) $(EXTRA_t_
@rm -f t-tofuinfo$(EXEEXT)
$(AM_V_CXXLD)$(CXXLINK) $(t_tofuinfo_OBJECTS) $(t_tofuinfo_LDADD) $(LIBS)
+t-various$(EXEEXT): $(t_various_OBJECTS) $(t_various_DEPENDENCIES) $(EXTRA_t_various_DEPENDENCIES)
+ @rm -f t-various$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(t_various_OBJECTS) $(t_various_LDADD) $(LIBS)
+
t-verify$(EXEEXT): $(t_verify_OBJECTS) $(t_verify_DEPENDENCIES) $(EXTRA_t_verify_DEPENDENCIES)
@rm -f t-verify$(EXEEXT)
$(AM_V_CXXLD)$(CXXLINK) $(t_verify_OBJECTS) $(t_verify_LDADD) $(LIBS)
@@ -594,12 +617,14 @@ distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run-keyformailboxjob.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-config.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-encrypt.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-keylist.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-keylocate.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-ownertrust.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-support.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-tofuinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-various.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-verify.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-wkspublish.Po@am__quote@
diff --git a/lang/qt/tests/t-config.cpp b/lang/qt/tests/t-config.cpp
new file mode 100644
index 0000000..0a7df22
--- /dev/null
+++ b/lang/qt/tests/t-config.cpp
@@ -0,0 +1,94 @@
+/* t-config.cpp
+
+ This file is part of qgpgme, the Qt API binding for gpgme
+ Copyright (c) 2016 Intevation GmbH
+
+ QGpgME 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.
+
+ QGpgME 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ Qt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include <QDebug>
+#include <QTest>
+#include <QTemporaryDir>
+#include "t-support.h"
+#include "protocol.h"
+#include "cryptoconfig.h"
+#include <unistd.h>
+
+using namespace QGpgME;
+
+class CryptoConfigTest: public QGpgMETest
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testKeyserver()
+ {
+ // Repeatedly set a config value and clear it
+ // this war broken at some point so it gets a
+ // unit test.
+ for (int i = 0; i < 10; i++) {
+ auto conf = cryptoConfig();
+ QVERIFY(conf);
+ auto entry = conf->entry(QStringLiteral("gpg"),
+ QStringLiteral("Keyserver"),
+ QStringLiteral("keyserver"));
+ QVERIFY(entry);
+ const QString url(QStringLiteral("hkp://foo.bar.baz"));
+ entry->setStringValue(url);
+ conf->sync(false);
+ conf->clear();
+ entry = conf->entry(QStringLiteral("gpg"),
+ QStringLiteral("Keyserver"),
+ QStringLiteral("keyserver"));
+ QCOMPARE (entry->stringValue(), url);
+ entry->setStringValue(QString());
+ conf->sync(false);
+ conf->clear();
+ entry = conf->entry(QStringLiteral("gpg"),
+ QStringLiteral("Keyserver"),
+ QStringLiteral("keyserver"));
+ QCOMPARE (entry->stringValue(), QString());
+ }
+ }
+
+ void initTestCase()
+ {
+ QGpgMETest::initTestCase();
+ const QString gpgHome = qgetenv("GNUPGHOME");
+ qputenv("GNUPGHOME", mDir.path().toUtf8());
+ QVERIFY(mDir.isValid());
+ }
+private:
+ QTemporaryDir mDir;
+
+};
+
+QTEST_MAIN(CryptoConfigTest)
+
+#include "t-config.moc"
diff --git a/lang/qt/tests/t-encrypt.cpp b/lang/qt/tests/t-encrypt.cpp
index 4d65dc7..a2d8dc4 100644
--- a/lang/qt/tests/t-encrypt.cpp
+++ b/lang/qt/tests/t-encrypt.cpp
@@ -39,6 +39,8 @@
#include <QBuffer>
#include "keylistjob.h"
#include "encryptjob.h"
+#include "signencryptjob.h"
+#include "signingresult.h"
#include "qgpgmeencryptjob.h"
#include "encryptionresult.h"
#include "decryptionresult.h"
@@ -46,6 +48,7 @@
#include "qgpgmebackend.h"
#include "keylistresult.h"
#include "engineinfo.h"
+#include "verifyopaquejob.h"
#include "t-support.h"
#define PROGRESS_TEST_SIZE 1 * 1024 * 1024
@@ -85,18 +88,18 @@ private Q_SLOTS:
std::vector<Key> keys;
auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
false, keys);
- Q_ASSERT(!keylistresult.error());
- Q_ASSERT(keys.size() == 1);
+ QVERIFY(!keylistresult.error());
+ QVERIFY(keys.size() == 1);
delete listjob;
auto job = openpgp()->encryptJob(/*ASCII Armor */true, /* Textmode */ true);
- Q_ASSERT(job);
+ QVERIFY(job);
QByteArray cipherText;
auto result = job->exec(keys, QStringLiteral("Hello World").toUtf8(), Context::AlwaysTrust, cipherText);
delete job;
- Q_ASSERT(!result.error());
+ QVERIFY(!result.error());
const auto cipherString = QString::fromUtf8(cipherText);
- Q_ASSERT(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
+ QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
/* Now decrypt */
if (!decryptSupported()) {
@@ -109,8 +112,8 @@ private Q_SLOTS:
auto decJob = new QGpgMEDecryptJob(ctx);
QByteArray plainText;
auto decResult = decJob->exec(cipherText, plainText);
- Q_ASSERT(!result.error());
- Q_ASSERT(QString::fromUtf8(plainText) == QStringLiteral("Hello World"));
+ QVERIFY(!decResult.error());
+ QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello World"));
delete decJob;
}
@@ -125,12 +128,12 @@ private Q_SLOTS:
std::vector<Key> keys;
auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
false, keys);
- Q_ASSERT(!keylistresult.error());
- Q_ASSERT(keys.size() == 1);
+ QVERIFY(!keylistresult.error());
+ QVERIFY(keys.size() == 1);
delete listjob;
auto job = openpgp()->encryptJob(/*ASCII Armor */false, /* Textmode */ false);
- Q_ASSERT(job);
+ QVERIFY(job);
QByteArray plainBa;
plainBa.fill('X', PROGRESS_TEST_SIZE);
QByteArray cipherText;
@@ -140,21 +143,21 @@ private Q_SLOTS:
connect(job, &Job::progress, this, [this, &initSeen, &finishSeen] (const QString&, int current, int total) {
// We only check for progress 0 and max progress as the other progress
// lines depend on the system speed and are as such unreliable to test.
- Q_ASSERT(total == PROGRESS_TEST_SIZE);
+ QVERIFY(total == PROGRESS_TEST_SIZE);
if (current == 0) {
initSeen = true;
}
if (current == total) {
finishSeen = true;
}
- Q_ASSERT(current >= 0 && current <= total);
+ QVERIFY(current >= 0 && current <= total);
});
connect(job, &EncryptJob::result, this, [this, &initSeen, &finishSeen] (const GpgME::EncryptionResult &,
const QByteArray &,
const QString,
const GpgME::Error) {
- Q_ASSERT(initSeen);
- Q_ASSERT(finishSeen);
+ QVERIFY(initSeen);
+ QVERIFY(finishSeen);
Q_EMIT asyncDone();
});
@@ -165,7 +168,7 @@ private Q_SLOTS:
job->start(keys, inptr, outptr, Context::AlwaysTrust);
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
}
void testSymmetricEncryptDecrypt()
@@ -183,9 +186,9 @@ private Q_SLOTS:
QByteArray cipherText;
auto result = job->exec(std::vector<Key>(), QStringLiteral("Hello symmetric World").toUtf8(), Context::AlwaysTrust, cipherText);
delete job;
- Q_ASSERT(!result.error());
+ QVERIFY(!result.error());
const auto cipherString = QString::fromUtf8(cipherText);
- Q_ASSERT(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
+ QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
killAgent(mDir.path());
@@ -195,12 +198,76 @@ private Q_SLOTS:
auto decJob = new QGpgMEDecryptJob(ctx2);
QByteArray plainText;
auto decResult = decJob->exec(cipherText, plainText);
- Q_ASSERT(!result.error());
- Q_ASSERT(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
+ QVERIFY(!result.error());
+ QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
delete decJob;
}
private:
+ /* This apparently does not work under ASAN currently. TODO fix and reeanble */
+ void testEncryptDecryptNowrap()
+ {
+ /* Now decrypt */
+ if (!decryptSupported()) {
+ return;
+ }
+ auto listjob = openpgp()->keyListJob(false, false, false);
+ std::vector<Key> keys;
+ auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
+ false, keys);
+ QVERIFY(!keylistresult.error());
+ QVERIFY(keys.size() == 1);
+ delete listjob;
+
+ auto job = openpgp()->signEncryptJob(/*ASCII Armor */true, /* Textmode */ true);
+
+ auto encSignCtx = Job::context(job);
+ TestPassphraseProvider provider1;
+ encSignCtx->setPassphraseProvider(&provider1);
+ encSignCtx->setPinentryMode(Context::PinentryLoopback);
+
+ QVERIFY(job);
+ QByteArray cipherText;
+ auto result = job->exec(keys, keys, QStringLiteral("Hello World").toUtf8(), Context::AlwaysTrust, cipherText);
+ delete job;
+ QVERIFY(!result.first.error());
+ QVERIFY(!result.second.error());
+ const auto cipherString = QString::fromUtf8(cipherText);
+ QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
+
+ /* Now decrypt */
+ if (!decryptSupported()) {
+ return;
+ }
+ auto ctx = Context::createForProtocol(OpenPGP);
+ TestPassphraseProvider provider;
+ ctx->setPassphraseProvider(&provider);
+ ctx->setPinentryMode(Context::PinentryLoopback);
+ ctx->setDecryptionFlags(Context::DecryptUnwrap);
+
+ auto decJob = new QGpgMEDecryptJob(ctx);
+ QByteArray plainText;
+ auto decResult = decJob->exec(cipherText, plainText);
+
+ QVERIFY(!decResult.error());
+
+ delete decJob;
+
+ // Now verify the unwrapeped data.
+ auto verifyJob = openpgp()->verifyOpaqueJob(true);
+ QByteArray verified;
+
+ auto verResult = verifyJob->exec(plainText, verified);
+ QVERIFY(!verResult.error());
+ delete verifyJob;
+
+ QVERIFY(verResult.numSignatures() == 1);
+ auto sig = verResult.signatures()[0];
+
+ QVERIFY(verified == QStringLiteral("Hello World"));
+ }
+
+private:
/* Loopback and passphrase provider don't work for mixed encryption.
* So this test is disabled until gnupg(?) is fixed for this. */
void testMixedEncryptDecrypt()
@@ -212,8 +279,8 @@ private:
std::vector<Key> keys;
auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
false, keys);
- Q_ASSERT(!keylistresult.error());
- Q_ASSERT(keys.size() == 1);
+ QVERIFY(!keylistresult.error());
+ QVERIFY(keys.size() == 1);
delete listjob;
auto ctx = Context::createForProtocol(OpenPGP);
@@ -229,10 +296,10 @@ private:
cipherText);
printf("After exec\n");
delete job;
- Q_ASSERT(!result.error());
+ QVERIFY(!result.error());
printf("Cipher:\n%s\n", cipherText.constData());
const auto cipherString = QString::fromUtf8(cipherText);
- Q_ASSERT(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
+ QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
killAgent(mDir.path());
@@ -240,7 +307,7 @@ private:
QTemporaryDir tmp;
qputenv("GNUPGHOME", tmp.path().toUtf8());
QFile agentConf(tmp.path() + QStringLiteral("/gpg-agent.conf"));
- Q_ASSERT(agentConf.open(QIODevice::WriteOnly));
+ QVERIFY(agentConf.open(QIODevice::WriteOnly));
agentConf.write("allow-loopback-pinentry");
agentConf.close();
@@ -251,9 +318,9 @@ private:
auto decJob = new QGpgMEDecryptJob(ctx2);
QByteArray plainText;
auto decResult = decJob->exec(cipherText, plainText);
- Q_ASSERT(!decResult.error());
+ QVERIFY(!decResult.error());
qDebug() << "Plain: " << plainText;
- Q_ASSERT(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
+ QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
delete decJob;
killAgent(tmp.path());
@@ -267,12 +334,12 @@ public Q_SLOT:
QGpgMETest::initTestCase();
const QString gpgHome = qgetenv("GNUPGHOME");
qputenv("GNUPGHOME", mDir.path().toUtf8());
- Q_ASSERT(mDir.isValid());
+ QVERIFY(mDir.isValid());
QFile agentConf(mDir.path() + QStringLiteral("/gpg-agent.conf"));
- Q_ASSERT(agentConf.open(QIODevice::WriteOnly));
+ QVERIFY(agentConf.open(QIODevice::WriteOnly));
agentConf.write("allow-loopback-pinentry");
agentConf.close();
- Q_ASSERT(copyKeyrings(gpgHome, mDir.path()));
+ QVERIFY(copyKeyrings(gpgHome, mDir.path()));
}
private:
diff --git a/lang/qt/tests/t-keylist.cpp b/lang/qt/tests/t-keylist.cpp
index 2578576..a140236 100644
--- a/lang/qt/tests/t-keylist.cpp
+++ b/lang/qt/tests/t-keylist.cpp
@@ -61,14 +61,14 @@ private Q_SLOTS:
GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("alfa@example.net"),
false, keys);
delete job;
- Q_ASSERT (!result.error());
- Q_ASSERT (keys.size() == 1);
+ QVERIFY (!result.error());
+ QVERIFY (keys.size() == 1);
const QString kId = QLatin1String(keys.front().keyID());
- Q_ASSERT (kId == QStringLiteral("2D727CC768697734"));
+ QVERIFY (kId == QStringLiteral("2D727CC768697734"));
- Q_ASSERT (keys[0].subkeys().size() == 2);
- Q_ASSERT (keys[0].subkeys()[0].publicKeyAlgorithm() == Subkey::AlgoDSA);
- Q_ASSERT (keys[0].subkeys()[1].publicKeyAlgorithm() == Subkey::AlgoELG_E);
+ QVERIFY (keys[0].subkeys().size() == 2);
+ QVERIFY (keys[0].subkeys()[0].publicKeyAlgorithm() == Subkey::AlgoDSA);
+ QVERIFY (keys[0].subkeys()[1].publicKeyAlgorithm() == Subkey::AlgoELG_E);
}
void testPubkeyAlgoAsString()
@@ -87,7 +87,7 @@ private Q_SLOTS:
{ Subkey::AlgoUnknown, QString() }
};
Q_FOREACH (Subkey::PubkeyAlgo algo, expected.keys()) {
- Q_ASSERT(QString::fromUtf8(Subkey::publicKeyAlgorithmAsString(algo)) ==
+ QVERIFY(QString::fromUtf8(Subkey::publicKeyAlgorithmAsString(algo)) ==
expected.value(algo));
}
}
@@ -97,12 +97,12 @@ private Q_SLOTS:
KeyListJob *job = openpgp()->keyListJob();
connect(job, &KeyListJob::result, job, [this, job](KeyListResult, std::vector<Key> keys, QString, Error)
{
- Q_ASSERT(keys.size() == 1);
+ QVERIFY(keys.size() == 1);
Q_EMIT asyncDone();
});
job->start(QStringList() << "alfa@example.net");
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
}
};
diff --git a/lang/qt/tests/t-keylocate.cpp b/lang/qt/tests/t-keylocate.cpp
index 63cb836..8c99c8b 100644
--- a/lang/qt/tests/t-keylocate.cpp
+++ b/lang/qt/tests/t-keylocate.cpp
@@ -63,7 +63,7 @@ private Q_SLOTS:
qputenv("GNUPGHOME", dir.path().toUtf8());
/* Could do this with gpgconf but this is not a gpgconf test ;-) */
QFile conf(dir.path() + QStringLiteral("/gpg.conf"));
- Q_ASSERT(conf.open(QIODevice::WriteOnly));
+ QVERIFY(conf.open(QIODevice::WriteOnly));
conf.write("auto-key-locate dane");
conf.close();
@@ -71,11 +71,11 @@ private Q_SLOTS:
mTestpattern = QStringLiteral("wk@gnupg.org");
connect(job, &KeyListJob::result, job, [this, job](KeyListResult result, std::vector<Key> keys, QString, Error)
{
- Q_ASSERT(!result.error());
- Q_ASSERT(keys.size() == 1);
+ QVERIFY(!result.error());
+ QVERIFY(keys.size() == 1);
Key k = keys.front();
- Q_ASSERT(k.numUserIDs());
+ QVERIFY(k.numUserIDs());
bool found = false;
Q_FOREACH (const UserID uid, k.userIDs()) {
const QString mailBox = QString::fromUtf8(uid.email());
@@ -83,12 +83,12 @@ private Q_SLOTS:
found = true;
}
}
- Q_ASSERT(found);
+ QVERIFY(found);
Q_EMIT asyncDone();
});
job->start(QStringList() << mTestpattern);
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
qputenv("GNUPGHOME", oldHome.toUtf8());
}
#endif
@@ -103,13 +103,13 @@ private Q_SLOTS:
connect(job, &KeyListJob::result, job, [this, job](KeyListResult result, std::vector<Key> keys, QString, Error)
{
- Q_ASSERT(!result.isNull());
- Q_ASSERT(!result.isTruncated());
- Q_ASSERT(!result.error());
- Q_ASSERT(keys.size() == 1);
+ QVERIFY(!result.isNull());
+ QVERIFY(!result.isTruncated());
+ QVERIFY(!result.error());
+ QVERIFY(keys.size() == 1);
Key k = keys.front();
- Q_ASSERT(k.numUserIDs());
+ QVERIFY(k.numUserIDs());
bool found = false;
Q_FOREACH (const UserID uid, k.userIDs()) {
const QString mailBox = QString::fromUtf8(uid.email());
@@ -117,12 +117,12 @@ private Q_SLOTS:
found = true;
}
}
- Q_ASSERT(found);
+ QVERIFY(found);
Q_EMIT asyncDone();
});
job->start(QStringList() << mTestpattern);
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
}
private:
diff --git a/lang/qt/tests/t-ownertrust.cpp b/lang/qt/tests/t-ownertrust.cpp
index db863b2..e9a4378 100644
--- a/lang/qt/tests/t-ownertrust.cpp
+++ b/lang/qt/tests/t-ownertrust.cpp
@@ -62,10 +62,10 @@ private Q_SLOTS:
GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("alfa@example.net"),
false, keys);
delete job;
- Q_ASSERT (!result.error());
- Q_ASSERT (keys.size() == 1);
+ QVERIFY (!result.error());
+ QVERIFY (keys.size() == 1);
Key key = keys.front();
- Q_ASSERT (key.ownerTrust() == Key::Unknown);
+ QVERIFY (key.ownerTrust() == Key::Unknown);
ChangeOwnerTrustJob *job2 = openpgp()->changeOwnerTrustJob();
connect(job2, &ChangeOwnerTrustJob::result, this, [this](Error e)
@@ -73,28 +73,28 @@ private Q_SLOTS:
if (e) {
qDebug() << "Error in result: " << e.asString();
}
- Q_ASSERT(!e);
+ QVERIFY(!e);
Q_EMIT asyncDone();
});
job2->start(key, Key::Ultimate);
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
job = openpgp()->keyListJob(false, true, true);
result = job->exec(QStringList() << QStringLiteral("alfa@example.net"),
false, keys);
delete job;
key = keys.front();
- Q_ASSERT (key.ownerTrust() == Key::Ultimate);
+ QVERIFY (key.ownerTrust() == Key::Ultimate);
ChangeOwnerTrustJob *job3 = openpgp()->changeOwnerTrustJob();
connect(job3, &ChangeOwnerTrustJob::result, this, [this](Error e)
{
- Q_ASSERT(!e);
+ QVERIFY(!e);
Q_EMIT asyncDone();
});
job3->start(key, Key::Unknown);
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
job = openpgp()->keyListJob(false, true, true);
result = job->exec(QStringList() << QStringLiteral("alfa@example.net"),
@@ -102,7 +102,7 @@ private Q_SLOTS:
delete job;
key = keys.front();
- Q_ASSERT (key.ownerTrust() == Key::Unknown);
+ QVERIFY (key.ownerTrust() == Key::Unknown);
}
};
diff --git a/lang/qt/tests/t-support.cpp b/lang/qt/tests/t-support.cpp
index 857d0a3..b3a7a70 100644
--- a/lang/qt/tests/t-support.cpp
+++ b/lang/qt/tests/t-support.cpp
@@ -34,6 +34,7 @@
#endif
#include "t-support.h"
+#include "context.h"
#include <QTest>
@@ -44,6 +45,7 @@
void QGpgMETest::initTestCase()
{
+ GpgME::initializeLibrary();
const QString gpgHome = qgetenv("GNUPGHOME");
QVERIFY2(!gpgHome.isEmpty(), "GNUPGHOME environment variable is not set.");
}
diff --git a/lang/qt/tests/t-support.h b/lang/qt/tests/t-support.h
index 704fab4..b03b05d 100644
--- a/lang/qt/tests/t-support.h
+++ b/lang/qt/tests/t-support.h
@@ -34,6 +34,8 @@
#include "interfaces/passphraseprovider.h"
#include <QObject>
+#include <gpg-error.h>
+
namespace GpgME
{
class TestPassphraseProvider : public PassphraseProvider
@@ -42,7 +44,9 @@ public:
char *getPassphrase(const char * /*useridHint*/, const char * /*description*/,
bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE
{
- return strdup("abc");
+ char *ret;
+ gpgrt_asprintf(&ret, "abc");
+ return ret;
}
};
} // namespace GpgME
@@ -60,4 +64,8 @@ public Q_SLOTS:
void cleanupTestCase();
};
+/* Timeout, in milliseconds, for use with QSignalSpy to wait on
+ signals. */
+#define QSIGNALSPY_TIMEOUT 60000
+
#endif // T_SUPPORT_H
diff --git a/lang/qt/tests/t-tofuinfo.cpp b/lang/qt/tests/t-tofuinfo.cpp
index f89e1c2..e16b1fd 100644
--- a/lang/qt/tests/t-tofuinfo.cpp
+++ b/lang/qt/tests/t-tofuinfo.cpp
@@ -35,12 +35,16 @@
#include <QDebug>
#include <QTest>
#include <QTemporaryDir>
+#include <QSignalSpy>
+
#include "protocol.h"
#include "tofuinfo.h"
#include "tofupolicyjob.h"
#include "verifyopaquejob.h"
#include "verificationresult.h"
#include "signingresult.h"
+#include "importjob.h"
+#include "importresult.h"
#include "keylistjob.h"
#include "keylistresult.h"
#include "qgpgmesignjob.h"
@@ -61,10 +65,57 @@ static const char testMsg1[] =
"=Crq6\n"
"-----END PGP MESSAGE-----\n";
+static const char conflictKey1[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+"\n"
+"mDMEWG+w/hYJKwYBBAHaRw8BAQdAiq1oStvDYg8ZfFs5DgisYJo8dJxD+C/AA21O\n"
+"K/aif0O0GXRvZnVfY29uZmxpY3RAZXhhbXBsZS5jb22IlgQTFggAPhYhBHoJBLaV\n"
+"DamYAgoa1L5BwMOl/x88BQJYb7D+AhsDBQkDwmcABQsJCAcCBhUICQoLAgQWAgMB\n"
+"Ah4BAheAAAoJEL5BwMOl/x88GvwA/0SxkbLyAcshGm2PRrPsFQsSVAfwaSYFVmS2\n"
+"cMVIw1PfAQDclRH1Z4MpufK07ju4qI33o4s0UFpVRBuSxt7A4P2ZD7g4BFhvsP4S\n"
+"CisGAQQBl1UBBQEBB0AmVrgaDNJ7K2BSalsRo2EkRJjHGqnp5bBB0tapnF81CQMB\n"
+"CAeIeAQYFggAIBYhBHoJBLaVDamYAgoa1L5BwMOl/x88BQJYb7D+AhsMAAoJEL5B\n"
+"wMOl/x88OR0BAMq4/vmJUORRTmzjHcv/DDrQB030DSq666rlckGIKTShAPoDXM9N\n"
+"0gZK+YzvrinSKZXHmn0aSwmC1/hyPybJPEljBw==\n"
+"=p2Oj\n"
+"-----END PGP PUBLIC KEY BLOCK-----\n";
+
+static const char conflictKey2[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+"\n"
+"mDMEWG+xShYJKwYBBAHaRw8BAQdA567gPEPJRpqKnZjlFJMRNUqruRviYMyygfF6\n"
+"6Ok+ygu0GXRvZnVfY29uZmxpY3RAZXhhbXBsZS5jb22IlgQTFggAPhYhBJ5kRh7E\n"
+"I98w8kgUcmkAfYFvqqHsBQJYb7FKAhsDBQkDwmcABQsJCAcCBhUICQoLAgQWAgMB\n"
+"Ah4BAheAAAoJEGkAfYFvqqHsYR0BAOz8JjYB4VvGkt6noLS3F5TLfsedGwQkBCw5\n"
+"znw/vGZsAQD9DSX+ekwdrN56mNO8ISt5uVS7B1ZQtouNBF+nzcwbDbg4BFhvsUoS\n"
+"CisGAQQBl1UBBQEBB0BFupW8+Xc1ikab8TJqANjQhvFVh6uLsgcK4g9lZgbGXAMB\n"
+"CAeIeAQYFggAIBYhBJ5kRh7EI98w8kgUcmkAfYFvqqHsBQJYb7FKAhsMAAoJEGkA\n"
+"fYFvqqHs15ABALdN3uiV/07cJ3RkNb3WPcijGsto+lECDS11dKEwTMFeAQDx+V36\n"
+"ocbYC/xEuwi3w45oNqGieazzcD/GBbt8OBk3BA==\n"
+"=45IR\n"
+"-----END PGP PUBLIC KEY BLOCK-----\n";
+
+static const char conflictMsg1[] = "-----BEGIN PGP MESSAGE-----\n"
+"\n"
+"owGbwMvMwCG2z/HA4aX/5W0YT3MlMUTkb2xPSizi6ihlYRDjYJAVU2Sp4mTZNpV3\n"
+"5QwmLqkrMLWsTCCFDFycAjCR1vcMf4U0Qrs6qzqfHJ9puGOFduLN2nVmhsumxjBE\n"
+"mdw4lr1ehIWR4QdLuNBpe86PGx1PtNXfVAzm/hu+vfjCp5BVNjPTM9L0eAA=\n"
+"=MfBD\n"
+"-----END PGP MESSAGE-----\n";
+
+static const char conflictMsg2[] = "-----BEGIN PGP MESSAGE-----\n"
+"\n"
+"owGbwMvMwCGWyVDbmL9q4RvG01xJDBH5GyvS8vO5OkpZGMQ4GGTFFFnmpbjJHVG+\n"
+"b/DJQ6QIppaVCaSQgYtTACaySZHhr/SOPrdFJ89KrcwKY5i1XnflXYf2PK76SafK\n"
+"tkxXuXzvJAvDX4kCybuqFk3HXCexz2+IrnZ+5X5EqOnuo3ens2cte+uzlhMA\n"
+"=BIAi\n"
+"-----END PGP MESSAGE-----\n";
+
class TofuInfoTest: public QGpgMETest
{
Q_OBJECT
+Q_SIGNALS:
+ void asyncDone();
+private:
bool testSupported()
{
return !(GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.16");
@@ -72,12 +123,12 @@ class TofuInfoTest: public QGpgMETest
void testTofuCopy(TofuInfo other, const TofuInfo &orig)
{
- Q_ASSERT(!orig.isNull());
- Q_ASSERT(!other.isNull());
- Q_ASSERT(orig.signLast() == other.signLast());
- Q_ASSERT(orig.signCount() == other.signCount());
- Q_ASSERT(orig.validity() == other.validity());
- Q_ASSERT(orig.policy() == other.policy());
+ QVERIFY(!orig.isNull());
+ QVERIFY(!other.isNull());
+ QVERIFY(orig.signLast() == other.signLast());
+ QVERIFY(orig.signCount() == other.signCount());
+ QVERIFY(orig.validity() == other.validity());
+ QVERIFY(orig.policy() == other.policy());
}
void signAndVerify(const QString &what, const GpgME::Key &key, int expected)
@@ -94,10 +145,10 @@ class TofuInfoTest: public QGpgMETest
auto sigResult = job->exec(keys, what.toUtf8(), NormalSignatureMode, signedData);
delete job;
- Q_ASSERT(!sigResult.error());
+ QVERIFY(!sigResult.error());
foreach (const auto uid, keys[0].userIDs()) {
auto info = uid.tofuInfo();
- Q_ASSERT(info.signCount() == expected - 1);
+ QVERIFY(info.signCount() == expected - 1);
}
auto verifyJob = openpgp()->verifyOpaqueJob();
@@ -106,25 +157,25 @@ class TofuInfoTest: public QGpgMETest
auto result = verifyJob->exec(signedData, verified);
delete verifyJob;
- Q_ASSERT(!result.error());
- Q_ASSERT(verified == what.toUtf8());
+ QVERIFY(!result.error());
+ QVERIFY(verified == what.toUtf8());
- Q_ASSERT(result.numSignatures() == 1);
+ QVERIFY(result.numSignatures() == 1);
auto sig = result.signatures()[0];
auto key2 = sig.key();
- Q_ASSERT(!key.isNull());
- Q_ASSERT(!strcmp (key2.primaryFingerprint(), key.primaryFingerprint()));
- Q_ASSERT(!strcmp (key.primaryFingerprint(), sig.fingerprint()));
+ QVERIFY(!key.isNull());
+ QVERIFY(!strcmp (key2.primaryFingerprint(), key.primaryFingerprint()));
+ QVERIFY(!strcmp (key.primaryFingerprint(), sig.fingerprint()));
auto stats = key2.userID(0).tofuInfo();
- Q_ASSERT(!stats.isNull());
+ QVERIFY(!stats.isNull());
if (stats.signCount() != expected) {
std::cout << "################ Key before verify: "
<< key
<< "################ Key after verify: "
<< key2;
}
- Q_ASSERT(stats.signCount() == expected);
+ QVERIFY(stats.signCount() == expected);
}
private Q_SLOTS:
@@ -134,13 +185,13 @@ private Q_SLOTS:
return;
}
TofuInfo tofu;
- Q_ASSERT(tofu.isNull());
- Q_ASSERT(!tofu.description());
- Q_ASSERT(!tofu.signCount());
- Q_ASSERT(!tofu.signLast());
- Q_ASSERT(!tofu.signFirst());
- Q_ASSERT(tofu.validity() == TofuInfo::ValidityUnknown);
- Q_ASSERT(tofu.policy() == TofuInfo::PolicyUnknown);
+ QVERIFY(tofu.isNull());
+ QVERIFY(!tofu.description());
+ QVERIFY(!tofu.signCount());
+ QVERIFY(!tofu.signLast());
+ QVERIFY(!tofu.signFirst());
+ QVERIFY(tofu.validity() == TofuInfo::ValidityUnknown);
+ QVERIFY(tofu.policy() == TofuInfo::PolicyUnknown);
}
void testTofuInfo()
@@ -153,30 +204,30 @@ private Q_SLOTS:
QByteArray plaintext;
auto ctx = Job::context(job);
- Q_ASSERT(ctx);
+ QVERIFY(ctx);
ctx->setSender("alfa@example.net");
auto result = job->exec(data1, plaintext);
delete job;
- Q_ASSERT(!result.isNull());
- Q_ASSERT(!result.error());
- Q_ASSERT(!strcmp(plaintext.constData(), "Just GNU it!\n"));
+ QVERIFY(!result.isNull());
+ QVERIFY(!result.error());
+ QVERIFY(!strcmp(plaintext.constData(), "Just GNU it!\n"));
- Q_ASSERT(result.numSignatures() == 1);
+ QVERIFY(result.numSignatures() == 1);
Signature sig = result.signatures()[0];
/* TOFU is always marginal */
- Q_ASSERT(sig.validity() == Signature::Marginal);
+ QVERIFY(sig.validity() == Signature::Marginal);
auto stats = sig.key().userID(0).tofuInfo();
- Q_ASSERT(!stats.isNull());
- Q_ASSERT(sig.key().primaryFingerprint());
- Q_ASSERT(sig.fingerprint());
- Q_ASSERT(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint()));
- Q_ASSERT(stats.signFirst() == stats.signLast());
- Q_ASSERT(stats.signCount() == 1);
- Q_ASSERT(stats.policy() == TofuInfo::PolicyAuto);
- Q_ASSERT(stats.validity() == TofuInfo::LittleHistory);
+ QVERIFY(!stats.isNull());
+ QVERIFY(sig.key().primaryFingerprint());
+ QVERIFY(sig.fingerprint());
+ QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint()));
+ QVERIFY(stats.signFirst() == stats.signLast());
+ QVERIFY(stats.signCount() == 1);
+ QVERIFY(stats.policy() == TofuInfo::PolicyAuto);
+ QVERIFY(stats.validity() == TofuInfo::LittleHistory);
testTofuCopy(stats, stats);
@@ -186,42 +237,42 @@ private Q_SLOTS:
result = job->exec(data1, plaintext);
delete job;
- Q_ASSERT(!result.isNull());
- Q_ASSERT(!result.error());
+ QVERIFY(!result.isNull());
+ QVERIFY(!result.error());
- Q_ASSERT(result.numSignatures() == 1);
+ QVERIFY(result.numSignatures() == 1);
sig = result.signatures()[0];
/* TOFU is always marginal */
- Q_ASSERT(sig.validity() == Signature::Marginal);
+ QVERIFY(sig.validity() == Signature::Marginal);
stats = sig.key().userID(0).tofuInfo();
- Q_ASSERT(!stats.isNull());
- Q_ASSERT(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint()));
- Q_ASSERT(stats.signFirst() == stats.signLast());
- Q_ASSERT(stats.signCount() == 1);
- Q_ASSERT(stats.policy() == TofuInfo::PolicyAuto);
- Q_ASSERT(stats.validity() == TofuInfo::LittleHistory);
+ QVERIFY(!stats.isNull());
+ QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint()));
+ QVERIFY(stats.signFirst() == stats.signLast());
+ QVERIFY(stats.signCount() == 1);
+ QVERIFY(stats.policy() == TofuInfo::PolicyAuto);
+ QVERIFY(stats.validity() == TofuInfo::LittleHistory);
/* Verify that another call yields the same result */
job = openpgp()->verifyOpaqueJob(true);
result = job->exec(data1, plaintext);
delete job;
- Q_ASSERT(!result.isNull());
- Q_ASSERT(!result.error());
+ QVERIFY(!result.isNull());
+ QVERIFY(!result.error());
- Q_ASSERT(result.numSignatures() == 1);
+ QVERIFY(result.numSignatures() == 1);
sig = result.signatures()[0];
/* TOFU is always marginal */
- Q_ASSERT(sig.validity() == Signature::Marginal);
+ QVERIFY(sig.validity() == Signature::Marginal);
stats = sig.key().userID(0).tofuInfo();
- Q_ASSERT(!stats.isNull());
- Q_ASSERT(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint()));
- Q_ASSERT(stats.signFirst() == stats.signLast());
- Q_ASSERT(stats.signCount() == 1);
- Q_ASSERT(stats.policy() == TofuInfo::PolicyAuto);
- Q_ASSERT(stats.validity() == TofuInfo::LittleHistory);
+ QVERIFY(!stats.isNull());
+ QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint()));
+ QVERIFY(stats.signFirst() == stats.signLast());
+ QVERIFY(stats.signCount() == 1);
+ QVERIFY(stats.policy() == TofuInfo::PolicyAuto);
+ QVERIFY(stats.validity() == TofuInfo::LittleHistory);
}
void testTofuSignCount()
@@ -235,9 +286,9 @@ private Q_SLOTS:
GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("zulu@example.net"),
true, keys);
delete job;
- Q_ASSERT(!keys.empty());
+ QVERIFY(!keys.empty());
Key key = keys[0];
- Q_ASSERT(!key.isNull());
+ QVERIFY(!key.isNull());
/* As we sign & verify quickly here we need different
* messages to avoid having them treated as the same
@@ -266,10 +317,10 @@ private Q_SLOTS:
auto result = job->exec(QStringList() << QStringLiteral("zulu@example.net"),
true, keys);
delete job;
- Q_ASSERT(!keys.empty());
+ QVERIFY(!keys.empty());
auto key = keys[0];
- Q_ASSERT(!key.isNull());
- Q_ASSERT(key.userID(0).tofuInfo().isNull());
+ QVERIFY(!key.isNull());
+ QVERIFY(key.userID(0).tofuInfo().isNull());
auto keyCopy = key;
keyCopy.update();
auto sigCnt = keyCopy.userID(0).tofuInfo().signCount();
@@ -285,13 +336,13 @@ private Q_SLOTS:
result = job->exec(QStringList() << QStringLiteral("zulu@example.net"),
true, keys);
delete job;
- Q_ASSERT(!result.error());
- Q_ASSERT(!keys.empty());
+ QVERIFY(!result.error());
+ QVERIFY(!keys.empty());
auto key2 = keys[0];
- Q_ASSERT(!key2.isNull());
+ QVERIFY(!key2.isNull());
auto info = key2.userID(0).tofuInfo();
- Q_ASSERT(!info.isNull());
- Q_ASSERT(info.signCount());
+ QVERIFY(!info.isNull());
+ QVERIFY(info.signCount());
}
void testTofuPolicy()
@@ -326,44 +377,126 @@ private Q_SLOTS:
<< ">\n fpr: " << key.primaryFingerprint();
}
}
- Q_ASSERT(!result.error());
- Q_ASSERT(!keys.empty());
+ QVERIFY(!result.error());
+ QVERIFY(!keys.empty());
auto key = keys[0];
- Q_ASSERT(!key.isNull());
- Q_ASSERT(key.userID(0).tofuInfo().policy() != TofuInfo::PolicyBad);
+ QVERIFY(!key.isNull());
+ QVERIFY(key.userID(0).tofuInfo().policy() != TofuInfo::PolicyBad);
auto *tofuJob = openpgp()->tofuPolicyJob();
auto err = tofuJob->exec(key, TofuInfo::PolicyBad);
- Q_ASSERT(!err);
+ QVERIFY(!err);
result = job->exec(QStringList() << QStringLiteral("bravo@example.net"),
false, keys);
- Q_ASSERT(!keys.empty());
+ QVERIFY(!keys.empty());
key = keys[0];
- Q_ASSERT(key.userID(0).tofuInfo().policy() == TofuInfo::PolicyBad);
+ QVERIFY(key.userID(0).tofuInfo().policy() == TofuInfo::PolicyBad);
err = tofuJob->exec(key, TofuInfo::PolicyGood);
result = job->exec(QStringList() << QStringLiteral("bravo@example.net"),
false, keys);
key = keys[0];
- Q_ASSERT(key.userID(0).tofuInfo().policy() == TofuInfo::PolicyGood);
+ QVERIFY(key.userID(0).tofuInfo().policy() == TofuInfo::PolicyGood);
delete tofuJob;
delete job;
}
+ void testTofuConflict()
+ {
+ if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.19") {
+ return;
+ }
+
+ // Import key 1
+ auto importjob = openpgp()->importJob();
+ connect(importjob, &ImportJob::result, this,
+ [this](ImportResult result, QString, Error)
+ {
+ QVERIFY(!result.error());
+ QVERIFY(!result.imports().empty());
+ QVERIFY(result.numImported());
+ Q_EMIT asyncDone();
+ });
+ importjob->start(QByteArray(conflictKey1));
+ QSignalSpy spy (this, SIGNAL(asyncDone()));
+ QVERIFY(spy.wait());
+
+ // Verify Message 1
+ const QByteArray signedData(conflictMsg1);
+ auto verifyJob = openpgp()->verifyOpaqueJob(true);
+ QByteArray verified;
+ auto result = verifyJob->exec(signedData, verified);
+ delete verifyJob;
+
+ QVERIFY(!result.isNull());
+ QVERIFY(!result.error());
+
+ QVERIFY(result.numSignatures() == 1);
+ auto sig = result.signatures()[0];
+ QVERIFY(sig.validity() == Signature::Marginal);
+
+ auto stats = sig.key().userID(0).tofuInfo();
+ QVERIFY(!stats.isNull());
+ QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint()));
+ QVERIFY(stats.signFirst() == stats.signLast());
+ QVERIFY(stats.signCount() == 1);
+ QVERIFY(stats.policy() == TofuInfo::PolicyAuto);
+ QVERIFY(stats.validity() == TofuInfo::LittleHistory);
+
+ // Import key 2
+ importjob = openpgp()->importJob();
+ connect(importjob, &ImportJob::result, this,
+ [this](ImportResult result, QString, Error)
+ {
+ QVERIFY(!result.error());
+ QVERIFY(!result.imports().empty());
+ QVERIFY(result.numImported());
+ Q_EMIT asyncDone();
+ });
+ importjob->start(QByteArray(conflictKey2));
+ QSignalSpy spy2 (this, SIGNAL(asyncDone()));
+ QVERIFY(spy2.wait());
+
+ // Verify Message 2
+ const QByteArray signedData2(conflictMsg2);
+ QByteArray verified2;
+ verifyJob = openpgp()->verifyOpaqueJob(true);
+ result = verifyJob->exec(signedData2, verified2);
+ delete verifyJob;
+
+ QVERIFY(!result.isNull());
+ QVERIFY(!result.error());
+
+ QVERIFY(result.numSignatures() == 1);
+ sig = result.signatures()[0];
+ QVERIFY(sig.validity() == Signature::Unknown);
+ // TODO activate when implemented
+ // QVERIFY(sig.summary() == Signature::TofuConflict);
+
+ stats = sig.key().userID(0).tofuInfo();
+ QVERIFY(!stats.isNull());
+ QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint()));
+ QVERIFY(stats.signFirst() == stats.signLast());
+ QVERIFY(stats.signCount() == 1);
+ QVERIFY(stats.policy() == TofuInfo::PolicyAsk);
+ QVERIFY(stats.validity() == TofuInfo::Conflict);
+ }
+
+
void initTestCase()
{
QGpgMETest::initTestCase();
const QString gpgHome = qgetenv("GNUPGHOME");
qputenv("GNUPGHOME", mDir.path().toUtf8());
- Q_ASSERT(mDir.isValid());
+ QVERIFY(mDir.isValid());
QFile conf(mDir.path() + QStringLiteral("/gpg.conf"));
- Q_ASSERT(conf.open(QIODevice::WriteOnly));
+ QVERIFY(conf.open(QIODevice::WriteOnly));
conf.write("trust-model tofu+pgp");
conf.close();
QFile agentConf(mDir.path() + QStringLiteral("/gpg-agent.conf"));
- Q_ASSERT(agentConf.open(QIODevice::WriteOnly));
+ QVERIFY(agentConf.open(QIODevice::WriteOnly));
agentConf.write("allow-loopback-pinentry");
agentConf.close();
- Q_ASSERT(copyKeyrings(gpgHome, mDir.path()));
+ QVERIFY(copyKeyrings(gpgHome, mDir.path()));
}
private:
QTemporaryDir mDir;
diff --git a/lang/qt/tests/t-various.cpp b/lang/qt/tests/t-various.cpp
new file mode 100644
index 0000000..35d8da9
--- /dev/null
+++ b/lang/qt/tests/t-various.cpp
@@ -0,0 +1,167 @@
+/* t-various.cpp
+
+ This file is part of qgpgme, the Qt API binding for gpgme
+ Copyright (c) 2017 Intevation GmbH
+
+ QGpgME 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.
+
+ QGpgME 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ Qt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include <QDebug>
+#include <QTest>
+#include <QSignalSpy>
+#include <QTemporaryDir>
+#include "keylistjob.h"
+#include "protocol.h"
+#include "keylistresult.h"
+#include "context.h"
+#include "engineinfo.h"
+#include "dn.h"
+#include "data.h"
+#include "dataprovider.h"
+
+#include "t-support.h"
+
+using namespace QGpgME;
+using namespace GpgME;
+
+static const char aKey[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+"\n"
+"mDMEWG+w/hYJKwYBBAHaRw8BAQdAiq1oStvDYg8ZfFs5DgisYJo8dJxD+C/AA21O\n"
+"K/aif0O0GXRvZnVfY29uZmxpY3RAZXhhbXBsZS5jb22IlgQTFggAPhYhBHoJBLaV\n"
+"DamYAgoa1L5BwMOl/x88BQJYb7D+AhsDBQkDwmcABQsJCAcCBhUICQoLAgQWAgMB\n"
+"Ah4BAheAAAoJEL5BwMOl/x88GvwA/0SxkbLyAcshGm2PRrPsFQsSVAfwaSYFVmS2\n"
+"cMVIw1PfAQDclRH1Z4MpufK07ju4qI33o4s0UFpVRBuSxt7A4P2ZD7g4BFhvsP4S\n"
+"CisGAQQBl1UBBQEBB0AmVrgaDNJ7K2BSalsRo2EkRJjHGqnp5bBB0tapnF81CQMB\n"
+"CAeIeAQYFggAIBYhBHoJBLaVDamYAgoa1L5BwMOl/x88BQJYb7D+AhsMAAoJEL5B\n"
+"wMOl/x88OR0BAMq4/vmJUORRTmzjHcv/DDrQB030DSq666rlckGIKTShAPoDXM9N\n"
+"0gZK+YzvrinSKZXHmn0aSwmC1/hyPybJPEljBw==\n"
+"=p2Oj\n"
+"-----END PGP PUBLIC KEY BLOCK-----\n";
+
+class TestVarious: public QGpgMETest
+{
+ Q_OBJECT
+
+Q_SIGNALS:
+ void asyncDone();
+
+private Q_SLOTS:
+ void testDN()
+ {
+ DN dn(QStringLiteral("CN=Before\\0DAfter,OU=Test,DC=North America,DC=Fabrikam,DC=COM"));
+ QVERIFY(dn.dn() == QStringLiteral("CN=Before\rAfter,OU=Test,DC=North America,DC=Fabrikam,DC=COM"));
+ QStringList attrOrder;
+ attrOrder << QStringLiteral("DC") << QStringLiteral("OU") << QStringLiteral("CN");
+ dn.setAttributeOrder(attrOrder);
+ QVERIFY(dn.prettyDN() == QStringLiteral("DC=North America,DC=Fabrikam,DC=COM,OU=Test,CN=Before\rAfter"));
+ }
+
+ void testKeyFromFile()
+ {
+ if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.14") {
+ return;
+ }
+ QGpgME::QByteArrayDataProvider dp(aKey);
+ Data data(&dp);
+ const auto keys = data.toKeys();
+ QVERIFY(keys.size() == 1);
+ const auto key = keys[0];
+ QVERIFY(!key.isNull());
+ QVERIFY(key.primaryFingerprint() == QStringLiteral("7A0904B6950DA998020A1AD4BE41C0C3A5FF1F3C"));
+ }
+
+ void testQuickUid()
+ {
+ if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.13") {
+ return;
+ }
+ KeyListJob *job = openpgp()->keyListJob(false, true, true);
+ std::vector<GpgME::Key> keys;
+ GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("alfa@example.net"),
+ false, keys);
+ delete job;
+ QVERIFY (!result.error());
+ QVERIFY (keys.size() == 1);
+ Key key = keys.front();
+
+ QVERIFY (key.numUserIDs() == 3);
+ const char uid[] = "Foo Bar (with comment) <foo@bar.baz>";
+
+ auto ctx = Context::createForProtocol(key.protocol());
+ QVERIFY (ctx);
+ TestPassphraseProvider provider;
+ ctx->setPassphraseProvider(&provider);
+ ctx->setPinentryMode(Context::PinentryLoopback);
+
+ QVERIFY(!ctx->addUid(key, uid));
+ delete ctx;
+ key.update();
+
+ QVERIFY (key.numUserIDs() == 4);
+ bool id_found = false;;
+ for (const auto &u: key.userIDs()) {
+ if (!strcmp (u.id(), uid)) {
+ QVERIFY (!u.isRevoked());
+ id_found = true;
+ break;
+ }
+ }
+ QVERIFY (id_found);
+
+ ctx = Context::createForProtocol(key.protocol());
+ QVERIFY (!ctx->revUid(key, uid));
+ delete ctx;
+ key.update();
+
+ bool id_revoked = false;;
+ for (const auto &u: key.userIDs()) {
+ if (!strcmp (u.id(), uid)) {
+ id_revoked = true;
+ break;
+ }
+ }
+ QVERIFY(id_revoked);
+ }
+
+ void initTestCase()
+ {
+ QGpgMETest::initTestCase();
+ const QString gpgHome = qgetenv("GNUPGHOME");
+ QVERIFY(copyKeyrings(gpgHome, mDir.path()));
+ qputenv("GNUPGHOME", mDir.path().toUtf8());
+ }
+
+private:
+ QTemporaryDir mDir;
+};
+
+QTEST_MAIN(TestVarious)
+
+#include "t-various.moc"
diff --git a/lang/qt/tests/t-verify.cpp b/lang/qt/tests/t-verify.cpp
index aedfc19..7caed28 100644
--- a/lang/qt/tests/t-verify.cpp
+++ b/lang/qt/tests/t-verify.cpp
@@ -70,14 +70,14 @@ private Q_SLOTS:
QByteArray verified;
auto result = verifyJob->exec(signedData, verified);
- Q_ASSERT(!result.error());
+ QVERIFY(!result.error());
delete verifyJob;
- Q_ASSERT(result.numSignatures() == 1);
+ QVERIFY(result.numSignatures() == 1);
auto sig = result.signatures()[0];
const auto key = sig.key(true, false);
- Q_ASSERT(!key.isNull());
+ QVERIFY(!key.isNull());
bool found = false;
for (const auto subkey: key.subkeys()) {
@@ -85,7 +85,7 @@ private Q_SLOTS:
found = true;
}
}
- Q_ASSERT(found);
+ QVERIFY(found);
}
};
diff --git a/lang/qt/tests/t-wkspublish.cpp b/lang/qt/tests/t-wkspublish.cpp
index 326ecaa..c51e8f9 100644
--- a/lang/qt/tests/t-wkspublish.cpp
+++ b/lang/qt/tests/t-wkspublish.cpp
@@ -127,12 +127,12 @@ private Q_SLOTS:
auto job = openpgp()->wksPublishJob();
connect(job, &WKSPublishJob::result, this,
[this] (Error err, QByteArray, QByteArray, QString, Error) {
- Q_ASSERT(err);
+ QVERIFY(err);
Q_EMIT asyncDone();
});
job->startCheck ("testuser1@localhost");
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
}
#ifdef DO_ONLINE_TESTS
private Q_SLOTS:
@@ -147,15 +147,15 @@ private:
[this] (Error err, QByteArray, QByteArray, QString, Error) {
if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.0.16") {
std::cout << err;
- Q_ASSERT(err);
+ QVERIFY(err);
} else {
- Q_ASSERT(!err);
+ QVERIFY(!err);
}
Q_EMIT asyncDone();
});
job->startCheck ("testuser1@test.gnupg.org");
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
}
void testWKSPublishErrors() {
@@ -166,13 +166,13 @@ private:
auto job = openpgp()->wksPublishJob();
connect(job, &WKSPublishJob::result, this,
[this] (Error err, QByteArray, QByteArray, QString, Error) {
- Q_ASSERT(err);
+ QVERIFY(err);
Q_EMIT asyncDone();
});
job->startCreate("AB874F24E98EBB8487EE7B170F8E3D97FE7011B7",
QStringLiteral("Foo@bar.baz"));
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
}
void testWKSPublishCreate() {
@@ -199,31 +199,31 @@ private:
connect(keygenjob, &KeyGenerationJob::result, this,
[this, &fpr](KeyGenerationResult result, QByteArray, QString, Error)
{
- Q_ASSERT(!result.error());
+ QVERIFY(!result.error());
fpr = QByteArray(result.fingerprint());
- Q_ASSERT(!fpr.isEmpty());
+ QVERIFY(!fpr.isEmpty());
Q_EMIT asyncDone();
});
keygenjob->start(args);
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
/* Then try to create a request. */
auto job = openpgp()->wksPublishJob();
connect(job, &WKSPublishJob::result, this,
[this] (Error err, QByteArray out, QByteArray, QString, Error) {
- Q_ASSERT(!err);
+ QVERIFY(!err);
Q_EMIT asyncDone();
const QString outstr = QString(out);
- Q_ASSERT(outstr.contains(
+ QVERIFY(outstr.contains(
QStringLiteral("-----BEGIN PGP PUBLIC KEY BLOCK-----")));
- Q_ASSERT(outstr.contains(
+ QVERIFY(outstr.contains(
QStringLiteral("Content-Type: application/pgp-keys")));
- Q_ASSERT(outstr.contains(
+ QVERIFY(outstr.contains(
QStringLiteral("From: " TEST_ADDRESS)));
});
job->startCreate(fpr.constData(), QLatin1String(TEST_ADDRESS));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
}
void testWKSPublishReceive() {
@@ -235,31 +235,31 @@ private:
connect(importjob, &ImportJob::result, this,
[this](ImportResult result, QString, Error)
{
- Q_ASSERT(!result.error());
- Q_ASSERT(!result.imports().empty());
- Q_ASSERT(result.numSecretKeysImported());
+ QVERIFY(!result.error());
+ QVERIFY(!result.imports().empty());
+ QVERIFY(result.numSecretKeysImported());
Q_EMIT asyncDone();
});
importjob->start(QByteArray(testSecKey));
QSignalSpy spy (this, SIGNAL(asyncDone()));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
/* Get a response. */
auto job = openpgp()->wksPublishJob();
connect(job, &WKSPublishJob::result, this,
[this] (Error err, QByteArray out, QByteArray, QString, Error) {
- Q_ASSERT(!err);
+ QVERIFY(!err);
Q_EMIT asyncDone();
const QString outstr = QString(out);
- Q_ASSERT(outstr.contains(
+ QVERIFY(outstr.contains(
QStringLiteral("-----BEGIN PGP MESSAGE-----")));
- Q_ASSERT(outstr.contains(
+ QVERIFY(outstr.contains(
QStringLiteral("Content-Type: multipart/encrypted;")));
- Q_ASSERT(outstr.contains(
+ QVERIFY(outstr.contains(
QStringLiteral("From: " TEST_ADDRESS)));
});
job->startReceive(QByteArray(testResponse));
- Q_ASSERT(spy.wait());
+ QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
}
void initTestCase()
@@ -267,9 +267,9 @@ private:
QGpgMETest::initTestCase();
const QString gpgHome = qgetenv("GNUPGHOME");
qputenv("GNUPGHOME", mDir.path().toUtf8());
- Q_ASSERT(mDir.isValid());
+ QVERIFY(mDir.isValid());
QFile agentConf(mDir.path() + QStringLiteral("/gpg-agent.conf"));
- Q_ASSERT(agentConf.open(QIODevice::WriteOnly));
+ QVERIFY(agentConf.open(QIODevice::WriteOnly));
agentConf.write("allow-loopback-pinentry");
agentConf.close();
}