diff options
Diffstat (limited to 'lang')
83 files changed, 2273 insertions, 211 deletions
diff --git a/lang/Makefile.in b/lang/Makefile.in index c080aa4..0be4fc8 100644 --- a/lang/Makefile.in +++ b/lang/Makefile.in @@ -109,6 +109,7 @@ host_triplet = @host@ subdir = lang ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -257,6 +258,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/cl/Makefile.in b/lang/cl/Makefile.in index d9639d4..9891ca7 100644 --- a/lang/cl/Makefile.in +++ b/lang/cl/Makefile.in @@ -110,6 +110,7 @@ host_triplet = @host@ subdir = lang/cl ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -229,6 +230,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/cl/gpgme.asd b/lang/cl/gpgme.asd index facdc8e..72564c4 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.17.1" + :version "1.18.0" :licence "GPL" :defsystem-depends-on ("cffi-grovel") :depends-on ("cffi" "gpg-error" "trivial-garbage") diff --git a/lang/cpp/Makefile.in b/lang/cpp/Makefile.in index 14315ac..7c835ea 100644 --- a/lang/cpp/Makefile.in +++ b/lang/cpp/Makefile.in @@ -110,6 +110,7 @@ host_triplet = @host@ subdir = lang/cpp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -259,6 +260,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/cpp/src/GpgmeppConfig-w32.cmake.in.in b/lang/cpp/src/GpgmeppConfig-w32.cmake.in.in index 1282676..51ada3b 100644 --- a/lang/cpp/src/GpgmeppConfig-w32.cmake.in.in +++ b/lang/cpp/src/GpgmeppConfig-w32.cmake.in.in @@ -97,7 +97,3 @@ unset(_IMPORT_CHECK_TARGETS) # Commands beyond this point should not need to know the version. set(CMAKE_IMPORT_FILE_VERSION) cmake_policy(POP) - -get_filename_component(QGpgme_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -# Pull in QGpgme for compatibility with KF5 variant. -find_package(QGpgme CONFIG) diff --git a/lang/cpp/src/GpgmeppConfig.cmake.in.in b/lang/cpp/src/GpgmeppConfig.cmake.in.in index 73f5eaa..8777623 100644 --- a/lang/cpp/src/GpgmeppConfig.cmake.in.in +++ b/lang/cpp/src/GpgmeppConfig.cmake.in.in @@ -93,7 +93,3 @@ unset(_IMPORT_CHECK_TARGETS) # Commands beyond this point should not need to know the version. set(CMAKE_IMPORT_FILE_VERSION) cmake_policy(POP) - -get_filename_component(QGpgme_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -# Pull in QGpgme for compatibility with KF5 variant. -find_package(QGpgme CONFIG) diff --git a/lang/cpp/src/Makefile.am b/lang/cpp/src/Makefile.am index ba8a7fb..fbec70b 100644 --- a/lang/cpp/src/Makefile.am +++ b/lang/cpp/src/Makefile.am @@ -34,6 +34,7 @@ main_sources = \ gpgsetownertrusteditinteractor.cpp gpgsignkeyeditinteractor.cpp \ gpgadduserideditinteractor.cpp gpggencardkeyinteractor.cpp \ gpgaddexistingsubkeyeditinteractor.cpp \ + gpgrevokekeyeditinteractor.cpp \ defaultassuantransaction.cpp \ scdgetinfoassuantransaction.cpp gpgagentgetinfoassuantransaction.cpp \ statusconsumerassuantransaction.cpp \ @@ -49,6 +50,7 @@ gpgmepp_headers = \ gpgsetownertrusteditinteractor.h gpgsignkeyeditinteractor.h \ gpggencardkeyinteractor.h \ gpgaddexistingsubkeyeditinteractor.h \ + gpgrevokekeyeditinteractor.h \ importresult.h keygenerationresult.h key.h keylistresult.h \ notation.h result.h scdgetinfoassuantransaction.h signingresult.h \ statusconsumerassuantransaction.h \ @@ -71,7 +73,8 @@ nodist_gpgmeppinclude_HEADERS = gpgmepp_version.h libgpgmepp_la_SOURCES = $(main_sources) $(gpgmepp_headers) context_vanilla.cpp \ $(interface_headers) $(private_gpgmepp_headers) -AM_CPPFLAGS = -I$(top_builddir)/src @GPG_ERROR_CFLAGS@ @LIBASSUAN_CFLAGS@ \ +AM_CPPFLAGS = -I$(top_builddir)/src \ + @GPGME_CPP_CFLAGS@ @GPG_ERROR_CFLAGS@ @LIBASSUAN_CFLAGS@ \ -DBUILDING_GPGMEPP -Wsuggest-override \ -Wzero-as-null-pointer-constant diff --git a/lang/cpp/src/Makefile.in b/lang/cpp/src/Makefile.in index f5e859a..0518307 100644 --- a/lang/cpp/src/Makefile.in +++ b/lang/cpp/src/Makefile.in @@ -113,6 +113,7 @@ host_triplet = @host@ subdir = lang/cpp/src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -174,7 +175,8 @@ am__objects_1 = exception.lo context.lo key.lo trustitem.lo data.lo \ gpgsetownertrusteditinteractor.lo gpgsignkeyeditinteractor.lo \ gpgadduserideditinteractor.lo gpggencardkeyinteractor.lo \ gpgaddexistingsubkeyeditinteractor.lo \ - defaultassuantransaction.lo scdgetinfoassuantransaction.lo \ + gpgrevokekeyeditinteractor.lo defaultassuantransaction.lo \ + scdgetinfoassuantransaction.lo \ gpgagentgetinfoassuantransaction.lo \ statusconsumerassuantransaction.lo vfsmountresult.lo \ configuration.lo tofuinfo.lo swdbresult.lo util.lo @@ -217,6 +219,7 @@ am__depfiles_remade = ./$(DEPDIR)/callbacks.Plo \ ./$(DEPDIR)/gpgadduserideditinteractor.Plo \ ./$(DEPDIR)/gpgagentgetinfoassuantransaction.Plo \ ./$(DEPDIR)/gpggencardkeyinteractor.Plo \ + ./$(DEPDIR)/gpgrevokekeyeditinteractor.Plo \ ./$(DEPDIR)/gpgsetexpirytimeeditinteractor.Plo \ ./$(DEPDIR)/gpgsetownertrusteditinteractor.Plo \ ./$(DEPDIR)/gpgsignkeyeditinteractor.Plo \ @@ -350,6 +353,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ @@ -513,6 +517,7 @@ main_sources = \ gpgsetownertrusteditinteractor.cpp gpgsignkeyeditinteractor.cpp \ gpgadduserideditinteractor.cpp gpggencardkeyinteractor.cpp \ gpgaddexistingsubkeyeditinteractor.cpp \ + gpgrevokekeyeditinteractor.cpp \ defaultassuantransaction.cpp \ scdgetinfoassuantransaction.cpp gpgagentgetinfoassuantransaction.cpp \ statusconsumerassuantransaction.cpp \ @@ -528,6 +533,7 @@ gpgmepp_headers = \ gpgsetownertrusteditinteractor.h gpgsignkeyeditinteractor.h \ gpggencardkeyinteractor.h \ gpgaddexistingsubkeyeditinteractor.h \ + gpgrevokekeyeditinteractor.h \ importresult.h keygenerationresult.h key.h keylistresult.h \ notation.h result.h scdgetinfoassuantransaction.h signingresult.h \ statusconsumerassuantransaction.h \ @@ -549,7 +555,8 @@ nodist_gpgmeppinclude_HEADERS = gpgmepp_version.h libgpgmepp_la_SOURCES = $(main_sources) $(gpgmepp_headers) context_vanilla.cpp \ $(interface_headers) $(private_gpgmepp_headers) -AM_CPPFLAGS = -I$(top_builddir)/src @GPG_ERROR_CFLAGS@ @LIBASSUAN_CFLAGS@ \ +AM_CPPFLAGS = -I$(top_builddir)/src \ + @GPGME_CPP_CFLAGS@ @GPG_ERROR_CFLAGS@ @LIBASSUAN_CFLAGS@ \ -DBUILDING_GPGMEPP -Wsuggest-override \ -Wzero-as-null-pointer-constant @@ -664,6 +671,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgadduserideditinteractor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgagentgetinfoassuantransaction.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpggencardkeyinteractor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgrevokekeyeditinteractor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgsetexpirytimeeditinteractor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgsetownertrusteditinteractor.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgsignkeyeditinteractor.Plo@am__quote@ # am--include-marker @@ -925,6 +933,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/gpgadduserideditinteractor.Plo -rm -f ./$(DEPDIR)/gpgagentgetinfoassuantransaction.Plo -rm -f ./$(DEPDIR)/gpggencardkeyinteractor.Plo + -rm -f ./$(DEPDIR)/gpgrevokekeyeditinteractor.Plo -rm -f ./$(DEPDIR)/gpgsetexpirytimeeditinteractor.Plo -rm -f ./$(DEPDIR)/gpgsetownertrusteditinteractor.Plo -rm -f ./$(DEPDIR)/gpgsignkeyeditinteractor.Plo @@ -1004,6 +1013,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/gpgadduserideditinteractor.Plo -rm -f ./$(DEPDIR)/gpgagentgetinfoassuantransaction.Plo -rm -f ./$(DEPDIR)/gpggencardkeyinteractor.Plo + -rm -f ./$(DEPDIR)/gpgrevokekeyeditinteractor.Plo -rm -f ./$(DEPDIR)/gpgsetexpirytimeeditinteractor.Plo -rm -f ./$(DEPDIR)/gpgsetownertrusteditinteractor.Plo -rm -f ./$(DEPDIR)/gpgsignkeyeditinteractor.Plo diff --git a/lang/cpp/src/context.cpp b/lang/cpp/src/context.cpp index 5072681..dba958c 100644 --- a/lang/cpp/src/context.cpp +++ b/lang/cpp/src/context.cpp @@ -195,6 +195,19 @@ std::ostream &operator<<(std::ostream &os, const Error &err) return os << "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))"; } +Context::KeyListModeSaver::KeyListModeSaver(Context *ctx) + : mCtx{ctx} + , mKeyListMode{ctx ? ctx->keyListMode() : 0} +{ +} + +Context::KeyListModeSaver::~KeyListModeSaver() +{ + if (mCtx) { + mCtx->setKeyListMode(mKeyListMode); + } +} + Context::Context(gpgme_ctx_t ctx) : d(new Private(ctx)) { } @@ -522,19 +535,25 @@ const char *Context::getSender () Error Context::startKeyListing(const char *pattern, bool secretOnly) { - d->lastop = Private::KeyList; + d->lastop = (((keyListMode() & GpgME::Locate) == GpgME::Locate) + ? Private::KeyListWithImport + : Private::KeyList); return Error(d->lasterr = gpgme_op_keylist_start(d->ctx, pattern, int(secretOnly))); } Error Context::startKeyListing(const char *patterns[], bool secretOnly) { - d->lastop = Private::KeyList; + d->lastop = (((keyListMode() & GpgME::Locate) == GpgME::Locate) + ? Private::KeyListWithImport + : Private::KeyList); return Error(d->lasterr = gpgme_op_keylist_ext_start(d->ctx, patterns, int(secretOnly), 0)); } Key Context::nextKey(GpgME::Error &e) { - d->lastop = Private::KeyList; + d->lastop = (((keyListMode() & GpgME::Locate) == GpgME::Locate) + ? Private::KeyListWithImport + : Private::KeyList); gpgme_key_t key = nullptr; e = Error(d->lasterr = gpgme_op_keylist_next(d->ctx, &key)); return Key(key, false); @@ -1154,6 +1173,7 @@ Error Context::startCombinedDecryptionAndVerification(const Data &cipherText, Da return startCombinedDecryptionAndVerification(cipherText, plainText, DecryptNone); } +namespace { unsigned int to_auditlog_flags(unsigned int flags) { unsigned int result = 0; @@ -1168,6 +1188,7 @@ unsigned int to_auditlog_flags(unsigned int flags) } return result; } +} Error Context::startGetAuditLog(Data &output, unsigned int flags) { @@ -1620,6 +1641,16 @@ Error Context::startRevUid(const Key &k, const char *userid) k.impl(), userid, 0)); } +Error Context::setPrimaryUid(const Key &k, const char *userid) +{ + return Error(d->lasterr = gpgme_op_set_uid_flag(d->ctx, k.impl(), userid, "primary", nullptr)); +} + +Error Context::startSetPrimaryUid(const Key &k, const char *userid) +{ + return Error(d->lasterr = gpgme_op_set_uid_flag_start(d->ctx, k.impl(), userid, "primary", nullptr)); +} + Error Context::createSubkey(const Key &k, const char *algo, unsigned long reserved, unsigned long expires, @@ -1847,6 +1878,7 @@ std::ostream &operator<<(std::ostream &os, KeyListMode mode) CHECK(WithTofu); CHECK(WithKeygrip); CHECK(WithSecret); + CHECK(ForceExtern); #undef CHECK return os << ')'; } diff --git a/lang/cpp/src/context.h b/lang/cpp/src/context.h index 9c2b2a5..7bd1b03 100644 --- a/lang/cpp/src/context.h +++ b/lang/cpp/src/context.h @@ -64,6 +64,17 @@ class GPGMEPP_EXPORT Context public: //using GpgME::Protocol; + /// RAII-style class for saving/restoring the key list mode. + class GPGMEPP_EXPORT KeyListModeSaver + { + public: + explicit KeyListModeSaver(Context *ctx); + ~KeyListModeSaver(); + private: + Context *mCtx; + unsigned int mKeyListMode; + }; + // // Creation and destruction: // @@ -284,6 +295,9 @@ public: Error revUid(const Key &key, const char *userid); Error startRevUid(const Key &key, const char *userid); + Error setPrimaryUid(const Key &key, const char *userid); + Error startSetPrimaryUid(const Key &key, const char *userid); + Error createSubkey(const Key &key, const char *algo, unsigned long reserved = 0, unsigned long expires = 0, diff --git a/lang/cpp/src/context_p.h b/lang/cpp/src/context_p.h index 491e7f7..8782609 100644 --- a/lang/cpp/src/context_p.h +++ b/lang/cpp/src/context_p.h @@ -53,6 +53,7 @@ public: KeyGen = 0x080, KeyList = 0x100, + KeyListWithImport = KeyList | Import, // gpgme_keylist_result_t and gpgme_import_result_t TrustList = 0x200, // no gpgme_trustlist_result_t, but nevertheless... Edit = 0x400, // no gpgme_edit_result_t, but nevertheless... diff --git a/lang/cpp/src/editinteractor.cpp b/lang/cpp/src/editinteractor.cpp index e411ada..08cb1bc 100644 --- a/lang/cpp/src/editinteractor.cpp +++ b/lang/cpp/src/editinteractor.cpp @@ -29,6 +29,7 @@ #include "editinteractor.h" #include "callbacks.h" #include "error.h" +#include "util.h" #include <gpgme.h> @@ -100,7 +101,7 @@ public: std::fprintf(ei->debug, "EditInteractor: %u -> nextState( %s, %s ) -> %u\n", oldState, status_to_string(status), args ? args : "<null>", ei->state); } - if (err) { + if (err || err.isCanceled()) { ei->state = oldState; goto error; } @@ -153,7 +154,7 @@ public: } error: - if (err) { + if (err || err.isCanceled()) { ei->error = err; ei->state = EditInteractor::ErrorState; } @@ -256,6 +257,20 @@ void EditInteractor::setDebugChannel(std::FILE *debug) d->debug = debug; } +GpgME::Error EditInteractor::parseStatusError(const char *args) +{ + Error err; + + const auto fields = split(args, ' '); + if (fields.size() >= 2) { + err = Error{static_cast<unsigned int>(std::stoul(fields[1]))}; + } else { + err = Error::fromCode(GPG_ERR_GENERAL); + } + + return err; +} + static const char *const status_strings[] = { "EOF", /* mkstatus processing starts here */ diff --git a/lang/cpp/src/editinteractor.h b/lang/cpp/src/editinteractor.h index 247bf8c..2505b02 100644 --- a/lang/cpp/src/editinteractor.h +++ b/lang/cpp/src/editinteractor.h @@ -60,6 +60,9 @@ public: void setDebugChannel(std::FILE *file); +protected: + Error parseStatusError(const char *args); + private: class Private; Private *const d; diff --git a/lang/cpp/src/global.h b/lang/cpp/src/global.h index a25b46c..1336142 100644 --- a/lang/cpp/src/global.h +++ b/lang/cpp/src/global.h @@ -60,18 +60,29 @@ enum Engine { GpgEngine, GpgSMEngine, GpgConfEngine, UnknownEngine, AssuanEngine enum KeyListMode { Local = 0x1, Extern = 0x2, - Locate = 0x3, + Locate = Local|Extern, Signatures = 0x4, SignatureNotations = 0x8, Validate = 0x10, Ephemeral = 0x20, WithTofu = 0x40, WithKeygrip = 0x80, - WithSecret = 0x100 + WithSecret = 0x100, + ForceExtern = 0x200, + LocateExternal = Locate|ForceExtern, + + KeyListModeMask = 0x3ff }; enum SignatureMode { NormalSignatureMode, Detached, Clearsigned }; +enum class RevocationReason { + Unspecified = 0, + Compromised = 1, + Superseded = 2, + NoLongerUsed = 3 +}; + GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, Protocol proto); GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, Engine eng); GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, KeyListMode mode); diff --git a/lang/cpp/src/gpgrevokekeyeditinteractor.cpp b/lang/cpp/src/gpgrevokekeyeditinteractor.cpp new file mode 100644 index 0000000..86b3c3c --- /dev/null +++ b/lang/cpp/src/gpgrevokekeyeditinteractor.cpp @@ -0,0 +1,216 @@ +/* + gpgrevokekeyeditinteractor.cpp - Edit Interactor to revoke own OpenPGP keys + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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 "gpgrevokekeyeditinteractor.h" + +#include "error.h" + +#include <gpgme.h> + +#include <sstream> +#include <vector> + +// avoid conflict (msvc) +#ifdef ERROR +# undef ERROR +#endif + +using namespace GpgME; + +class GpgRevokeKeyEditInteractor::Private +{ + enum { + START = EditInteractor::StartState, + COMMAND, + CONFIRM_REVOKING_ENTIRE_KEY, + REASON_CODE, + REASON_TEXT, + // all these free slots belong to REASON_TEXT, too; we increase state() + // by one for each line of text, so that action() is called + REASON_TEXT_DONE = REASON_TEXT + 1000, + CONFIRM_REASON, + QUIT, + CONFIRM_SAVE, + + ERROR = EditInteractor::ErrorState + }; + + GpgRevokeKeyEditInteractor *const q = nullptr; + +public: + Private(GpgRevokeKeyEditInteractor *q) + : q{q} + , reasonCode{"0"} + { + } + + const char *action(Error &err) const; + unsigned int nextState(unsigned int statusCode, const char *args, Error &err); + + std::string reasonCode; + std::vector<std::string> reasonLines; + int nextLine = -1; +}; + +const char *GpgRevokeKeyEditInteractor::Private::action(Error &err) const +{ + switch (const auto state = q->state()) { + case COMMAND: + return "revkey"; + case CONFIRM_REVOKING_ENTIRE_KEY: + return "Y"; + case REASON_CODE: + return reasonCode.c_str(); + case REASON_TEXT_DONE: + return ""; + case CONFIRM_REASON: + return "Y"; + case QUIT: + return "quit"; + case CONFIRM_SAVE: + return "Y"; + case START: + return nullptr; + default: + if (state >= REASON_TEXT && state < REASON_TEXT_DONE) { + return reasonLines[nextLine].c_str(); + } + // fall through + case ERROR: + err = Error::fromCode(GPG_ERR_GENERAL); + return nullptr; + } +} + +unsigned int GpgRevokeKeyEditInteractor::Private::nextState(unsigned int status, const char *args, Error &err) +{ + using std::strcmp; + + static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL); + + if (q->needsNoResponse(status)) { + return q->state(); + } + + if (status == GPGME_STATUS_ERROR) { + err = q->parseStatusError(args); + return ERROR; + } + switch (const auto state = q->state()) { + case START: + if (status == GPGME_STATUS_GET_LINE && + strcmp(args, "keyedit.prompt") == 0) { + return COMMAND; + } + err = GENERAL_ERROR; + return ERROR; + case COMMAND: + if (status == GPGME_STATUS_GET_BOOL && + strcmp(args, "keyedit.revoke.subkey.okay") == 0) { + return CONFIRM_REVOKING_ENTIRE_KEY; + } + err = GENERAL_ERROR; + return ERROR; + case CONFIRM_REVOKING_ENTIRE_KEY: + if (status == GPGME_STATUS_GET_LINE && + strcmp(args, "ask_revocation_reason.code") == 0) { + return REASON_CODE; + } + err = GENERAL_ERROR; + return ERROR; + case REASON_CODE: + if (status == GPGME_STATUS_GET_LINE && + strcmp(args, "ask_revocation_reason.text") == 0) { + nextLine++; + return nextLine < reasonLines.size() ? REASON_TEXT : REASON_TEXT_DONE; + } + err = GENERAL_ERROR; + return ERROR; + default: + if (state >= REASON_TEXT && state < REASON_TEXT_DONE) { + if (status == GPGME_STATUS_GET_LINE && + strcmp(args, "ask_revocation_reason.text") == 0) { + nextLine++; + return nextLine < reasonLines.size() ? state + 1 : REASON_TEXT_DONE; + } + } + err = GENERAL_ERROR; + return ERROR; + case REASON_TEXT_DONE: + if (status == GPGME_STATUS_GET_BOOL && + strcmp(args, "ask_revocation_reason.okay") == 0) { + return CONFIRM_REASON; + } + err = GENERAL_ERROR; + return ERROR; + case CONFIRM_REASON: + if (status == GPGME_STATUS_GET_LINE && + strcmp(args, "keyedit.prompt") == 0) { + return QUIT; + } + err = GENERAL_ERROR; + return ERROR; + case QUIT: + if (status == GPGME_STATUS_GET_BOOL && + strcmp(args, "keyedit.save.okay") == 0) { + return CONFIRM_SAVE; + } + err = GENERAL_ERROR; + return ERROR; + case ERROR: + if (status == GPGME_STATUS_GET_LINE && + strcmp(args, "keyedit.prompt") == 0) { + return QUIT; + } + err = q->lastError(); + return ERROR; + } +} + +GpgRevokeKeyEditInteractor::GpgRevokeKeyEditInteractor() + : EditInteractor{} + , d{new Private{this}} +{ +} + +GpgRevokeKeyEditInteractor::~GpgRevokeKeyEditInteractor() = default; + +void GpgRevokeKeyEditInteractor::setReason(RevocationReason reason, const std::vector<std::string> &description) +{ + d->reasonCode = std::to_string(static_cast<int>(reason)); + d->reasonLines = description; +} + +const char *GpgRevokeKeyEditInteractor::action(Error &err) const +{ + return d->action(err); +} + +unsigned int GpgRevokeKeyEditInteractor::nextState(unsigned int status, const char *args, Error &err) const +{ + return d->nextState(status, args, err); +} diff --git a/lang/cpp/src/gpgrevokekeyeditinteractor.h b/lang/cpp/src/gpgrevokekeyeditinteractor.h new file mode 100644 index 0000000..c33a71b --- /dev/null +++ b/lang/cpp/src/gpgrevokekeyeditinteractor.h @@ -0,0 +1,62 @@ +/* + gpgrevokekeyeditinteractor.h - Edit Interactor to revoke own OpenPGP keys + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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_GPGREVOKEKEYEDITINTERACTOR_H__ +#define __GPGMEPP_GPGREVOKEKEYEDITINTERACTOR_H__ + +#include "editinteractor.h" +#include "global.h" + +#include <memory> +#include <vector> + +namespace GpgME +{ + +/** Edit interactor to revoke the key a key edit operation is working on. + * Supports revocation of own keys only. */ +class GPGMEPP_EXPORT GpgRevokeKeyEditInteractor : public EditInteractor +{ +public: + GpgRevokeKeyEditInteractor(); + ~GpgRevokeKeyEditInteractor() override; + + /** Sets the reason for the revocation. The reason defaults to \c Unspecified. + * \a description can be used for adding a comment for the revocation. The + * individual elements of \a description must be non-empty strings and they + * must not contain any endline characters. + */ + void setReason(RevocationReason reason, const std::vector<std::string> &description = {}); + +private: + const char *action(Error &err) const override; + unsigned int nextState(unsigned int statusCode, const char *args, Error &err) const override; + +private: + class GPGMEPP_NO_EXPORT Private; + const std::unique_ptr<Private> d; +}; + +} // namespace GpgME + +#endif // __GPGMEPP_GPGREVOKEKEYEDITINTERACTOR_H__ diff --git a/lang/cpp/src/importresult.cpp b/lang/cpp/src/importresult.cpp index 803c34d..0625872 100644 --- a/lang/cpp/src/importresult.cpp +++ b/lang/cpp/src/importresult.cpp @@ -94,6 +94,131 @@ void GpgME::ImportResult::init(gpgme_ctx_t ctx) make_standard_stuff(ImportResult) +void GpgME::ImportResult::mergeWith(const ImportResult &other) +{ + if (other.isNull()) { + return; + } + if (isNull()) { // just assign + operator=(other); + return; + } + + // Add the numbers of considered keys; the number will be corrected when + // merging the imports to account for duplicates + d->res.considered += other.d->res.considered; + // Add the numbers of keys without user ID; may count duplicates + d->res.no_user_id += other.d->res.no_user_id; + // Add the numbers of imported keys + d->res.imported += other.d->res.imported; + // Add the numbers of imported RSA keys + d->res.imported_rsa += other.d->res.imported_rsa; + // Add the numbers of unchanged keys; the number will be corrected when + // merging the imports to account for keys changed by this import + d->res.unchanged += other.d->res.unchanged; + // Add the numbers of new user IDs + d->res.new_user_ids += other.d->res.new_user_ids; + // Add the numbers of new subkeys + d->res.new_sub_keys += other.d->res.new_sub_keys; + // Add the numbers of new signatures + d->res.new_signatures += other.d->res.new_signatures; + // Add the numbers of new revocations + d->res.new_revocations += other.d->res.new_revocations; + + // Add the numbers of considered secret keys; the number will be corrected when + // merging the imports to account for duplicates + d->res.secret_read += other.d->res.secret_read; + // Add the numbers of imported secret keys + d->res.secret_imported += other.d->res.secret_imported; + // Add the numbers of unchanged secret keys; the number will be corrected when + // merging the imports to account for keys changed by this import + d->res.secret_unchanged += other.d->res.secret_unchanged; + + // Add the numbers of new keys that were skipped; may count duplicates + d->res.skipped_new_keys += other.d->res.skipped_new_keys; + // Add the numbers of keys that were not imported; may count duplicates + d->res.not_imported += other.d->res.not_imported; + // Add the numbers of v3 keys that were skipped; may count duplicates + d->res.skipped_v3_keys += other.d->res.skipped_v3_keys; + + // Look at the list of keys for which an import was attempted during the + // other import to correct some of the consolidated numbers + for (auto it = std::begin(other.d->imports), end = std::end(other.d->imports); it != end; ++it) { + const char *fpr = (*it)->fpr; + if (!fpr || !*fpr) { + // we cannot derive any useful information about an import if the + // fingerprint is null or empty + continue; + } + // was this key also considered during the first import + const auto consideredInFirstImports = + std::any_of(std::begin(d->imports), std::end(d->imports), [fpr](const auto i) { + return i->fpr && !strcmp(i->fpr, fpr); + }); + // did we see this key already in the list of keys of the other import + const auto consideredInPreviousOtherImports = + std::any_of(std::begin(other.d->imports), it, [fpr](const auto i) { + return i->fpr && !strcmp(i->fpr, fpr); + }); + // was anything added to this key during the other import + const auto changedInOtherImports = + std::any_of(std::begin(other.d->imports), std::end(other.d->imports), [fpr](const auto i) { + return i->fpr && !strcmp(i->fpr, fpr) && (i->status != 0); + }); + if (consideredInFirstImports && !consideredInPreviousOtherImports) { + // key was also considered during first import, but not before in the list of other imports + d->res.considered -= 1; + if (!changedInOtherImports) { + // key was (most likely) counted as unchanged in the second import; + // this needs to be corrected (regardless of whether it was changed in the first import) + d->res.unchanged -= 1; + } + } + + // now do the same for the secret key counts + const auto secretKeyConsideredInFirstImports = + std::any_of(std::begin(d->imports), std::end(d->imports), [fpr](const auto i) { + return i->fpr && !strcmp(i->fpr, fpr) && (i->status & GPGME_IMPORT_SECRET); + }); + const auto secretKeyConsideredInPreviousOtherImports = + std::any_of(std::begin(other.d->imports), it, [fpr](const auto i) { + return i->fpr && !strcmp(i->fpr, fpr) && (i->status & GPGME_IMPORT_SECRET); + }); + const auto secretKeyChangedInOtherImports = + std::any_of(std::begin(other.d->imports), std::end(other.d->imports), [fpr](const auto i) { + return i->fpr && !strcmp(i->fpr, fpr) && (i->status & GPGME_IMPORT_SECRET) && (i->status != GPGME_IMPORT_SECRET); + }); + if (secretKeyConsideredInFirstImports && !secretKeyConsideredInPreviousOtherImports) { + // key was also considered during first import, but not before in the list of other imports + d->res.secret_read -= 1; + if (!secretKeyChangedInOtherImports) { + // key was (most likely) counted as unchanged in the second import; + // this needs to be corrected (regardless of whether it was changed in the first import) + d->res.secret_unchanged -= 1; + } + } + } + + // Now append the list of keys for which an import was attempted during the + // other import + d->imports.reserve(d->imports.size() + other.d->imports.size()); + std::transform(std::begin(other.d->imports), std::end(other.d->imports), + std::back_inserter(d->imports), + [](const auto import) { + gpgme_import_status_t copy = new _gpgme_import_status{*import}; + if (import->fpr) { + copy->fpr = strdup(import->fpr); + } + copy->next = nullptr; // should already be null, but better safe than sorry + return copy; + }); + + // Finally, merge the error if there was none yet + if (!bool(error())) { + Result::operator=(other); + } +} + int GpgME::ImportResult::numConsidered() const { return d ? d->res.considered : 0 ; diff --git a/lang/cpp/src/importresult.h b/lang/cpp/src/importresult.h index bcd956c..5936698 100644 --- a/lang/cpp/src/importresult.h +++ b/lang/cpp/src/importresult.h @@ -60,6 +60,16 @@ public: swap(this->d, other.d); } + /** + * Merges the result @p other into this result (and all of its copies). + * + * @note The merge algorithm assumes that @p other is the result of an + * import that was performed after the import of this result. + * @note Some numbers cannot be consolidated reliably, e.g. the number of + * keys without user ID. + */ + void mergeWith(const ImportResult &other); + bool isNull() const; int numConsidered() const; diff --git a/lang/cpp/src/key.cpp b/lang/cpp/src/key.cpp index b893a7c..293c9e5 100644 --- a/lang/cpp/src/key.cpp +++ b/lang/cpp/src/key.cpp @@ -1250,16 +1250,22 @@ std::ostream &operator<<(std::ostream &os, const Subkey &subkey) os << "GpgME::Subkey("; if (!subkey.isNull()) { os << "\n fingerprint: " << protect(subkey.fingerprint()) + << "\n keyGrip: " << protect(subkey.keyGrip()) << "\n creationTime: " << subkey.creationTime() << "\n expirationTime:" << subkey.expirationTime() << "\n isRevoked: " << subkey.isRevoked() << "\n isExpired: " << subkey.isExpired() - << "\n isInvalid: " << subkey.isRevoked() - << "\n isDisabled: " << subkey.isInvalid() + << "\n isInvalid: " << subkey.isInvalid() + << "\n isDisabled: " << subkey.isDisabled() << "\n canSign: " << subkey.canSign() << "\n canEncrypt: " << subkey.canEncrypt() << "\n canCertify: " << subkey.canCertify() - << "\n canAuth: " << subkey.canAuthenticate(); + << "\n canAuth: " << subkey.canAuthenticate() + << "\n isSecret: " << subkey.isSecret() + << "\n isQualified: " << subkey.isQualified() + << "\n isDeVs: " << subkey.isDeVs() + << "\n isCardKey: " << subkey.isCardKey() + << "\n cardSerialNumber:" << protect(subkey.cardSerialNumber()); } return os << ')'; } diff --git a/lang/cpp/src/result.h b/lang/cpp/src/result.h index 5ed52a8..a587afb 100644 --- a/lang/cpp/src/result.h +++ b/lang/cpp/src/result.h @@ -50,6 +50,14 @@ public: { return mError; } + /** + * Replaces the error set during construction with \p error. + * Use with care, e.g. to set a more suitable error. + */ + void setError(const Error &error) + { + mError = error; + } protected: Error mError; diff --git a/lang/cpp/src/util.h b/lang/cpp/src/util.h index b6f9ca5..cb6df0d 100644 --- a/lang/cpp/src/util.h +++ b/lang/cpp/src/util.h @@ -89,19 +89,15 @@ static inline gpgme_keylist_mode_t add_to_gpgme_keylist_mode_t(unsigned int oldm if (newmodes & GpgME::WithSecret) { oldmode |= GPGME_KEYLIST_MODE_WITH_SECRET; } + if (newmodes & GpgME::ForceExtern) { + oldmode |= GPGME_KEYLIST_MODE_FORCE_EXTERN; + } #ifndef NDEBUG - if (newmodes & ~(GpgME::Local | - GpgME::Extern | - GpgME::Signatures | - GpgME::SignatureNotations | - GpgME::Validate | - GpgME::Ephemeral | - GpgME::WithTofu | - GpgME::WithKeygrip | - GpgME::WithSecret)) { + if (newmodes & ~(GpgME::KeyListModeMask)) { //std::cerr << "GpgME::Context: keylist mode must be one of Local, " //"Extern, Signatures, SignatureNotations, Validate, Ephemeral, WithTofu, " - //"WithKeygrip, WithSecret, or a combination thereof!" << std::endl; + //"WithKeygrip, WithSecret, ForceExtern, or a combination thereof!" + //<< std::endl; } #endif return static_cast<gpgme_keylist_mode_t>(oldmode); @@ -137,6 +133,9 @@ static inline unsigned int convert_from_gpgme_keylist_mode_t(unsigned int mode) if (mode & GPGME_KEYLIST_MODE_VALIDATE) { result |= GpgME::Validate; } + if (mode & GPGME_KEYLIST_MODE_FORCE_EXTERN) { + result |= GpgME::ForceExtern; + } #ifndef NDEBUG if (mode & ~(GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_EXTERN | @@ -146,7 +145,8 @@ static inline unsigned int convert_from_gpgme_keylist_mode_t(unsigned int mode) GPGME_KEYLIST_MODE_WITH_TOFU | GPGME_KEYLIST_MODE_WITH_KEYGRIP | GPGME_KEYLIST_MODE_EPHEMERAL | - GPGME_KEYLIST_MODE_VALIDATE)) { + GPGME_KEYLIST_MODE_VALIDATE | + GPGME_KEYLIST_MODE_FORCE_EXTERN)) { //std::cerr << "GpgME: WARNING: gpgme_get_keylist_mode() returned an unknown flag!" << std::endl; } #endif // NDEBUG @@ -177,6 +177,19 @@ static inline gpgme_sig_notation_flags_t add_to_gpgme_sig_notation_flags_t(unsi return static_cast<gpgme_sig_notation_flags_t>(result); } +static inline std::vector<std::string> split(const std::string &text, char delimiter) +{ + std::vector<std::string> result; + if (!text.empty()) { + std::istringstream stream{text}; + std::string line; + while (std::getline(stream, line, delimiter)) { + result.push_back(line); + } + } + return result; +} + /** * Adapter for passing a vector of strings as NULL-terminated array of * const char* to the C-interface of gpgme. diff --git a/lang/cpp/tests/Makefile.in b/lang/cpp/tests/Makefile.in index 280ffec..46dd9f0 100644 --- a/lang/cpp/tests/Makefile.in +++ b/lang/cpp/tests/Makefile.in @@ -111,6 +111,7 @@ noinst_PROGRAMS = run-getkey$(EXEEXT) run-keylist$(EXEEXT) \ subdir = lang/cpp/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -274,6 +275,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/cpp/tests/run-getkey.cpp b/lang/cpp/tests/run-getkey.cpp index 35b15eb..c47da0b 100644 --- a/lang/cpp/tests/run-getkey.cpp +++ b/lang/cpp/tests/run-getkey.cpp @@ -60,6 +60,8 @@ show_usage (int ex) " --ephemeral use GPGME_KEYLIST_MODE_EPHEMERAL\n" " --validate use GPGME_KEYLIST_MODE_VALIDATE\n" " --locate use GPGME_KEYLIST_MODE_LOCATE\n" + " --force-extern use GPGME_KEYLIST_MODE_FORCE_EXTERN\n" + " --locate-external use GPGME_KEYLIST_MODE_LOCATE_EXTERNAL\n" , stderr); exit (ex); } @@ -116,6 +118,12 @@ main (int argc, char **argv) } else if (!strcmp (*argv, "--locate")) { argc--; argv++; mode |= KeyListMode::Locate; + } else if (!strcmp (*argv, "--force-extern")) { + argc--; argv++; + mode |= KeyListMode::ForceExtern; + } else if (!strcmp (*argv, "--locate-external")) { + argc--; argv++; + mode |= KeyListMode::LocateExternal; } else if (!strncmp (*argv, "--", 2)) { show_usage (1); } @@ -132,6 +140,12 @@ main (int argc, char **argv) return -1; } ctx->setKeyListMode (mode); + if (ctx->keyListMode() != mode) { + // unfortunately, Context::setKeyListMode() does not return the error + // returned by gpgme + std::cerr << "Failed to set keylist mode. You may have used an invalid combination of options."; + return -1; + } Error err; const GpgME::Key key = ctx->key (*argv, err, only_secret); std::stringstream ss; diff --git a/lang/cpp/tests/run-keylist.cpp b/lang/cpp/tests/run-keylist.cpp index 5457739..9e7d763 100644 --- a/lang/cpp/tests/run-keylist.cpp +++ b/lang/cpp/tests/run-keylist.cpp @@ -61,6 +61,8 @@ show_usage (int ex) " --ephemeral use GPGME_KEYLIST_MODE_EPHEMERAL\n" " --validate use GPGME_KEYLIST_MODE_VALIDATE\n" " --locate use GPGME_KEYLIST_MODE_LOCATE\n" + " --force-extern use GPGME_KEYLIST_MODE_FORCE_EXTERN\n" + " --locate-external use GPGME_KEYLIST_MODE_LOCATE_EXTERNAL\n" , stderr); exit (ex); } @@ -117,7 +119,17 @@ main (int argc, char **argv) } else if (!strcmp (*argv, "--locate")) { argc--; argv++; mode |= KeyListMode::Locate; + } else if (!strcmp (*argv, "--with-secret")) { + argc--; argv++; + mode |= KeyListMode::WithSecret; + } else if (!strcmp (*argv, "--force-extern")) { + argc--; argv++; + mode |= KeyListMode::ForceExtern; + } else if (!strcmp (*argv, "--locate-external")) { + argc--; argv++; + mode |= KeyListMode::LocateExternal; } else if (!strncmp (*argv, "--", 2)) { + std::cerr << "Error: Unknown option: " << *argv << std::endl; show_usage (1); } } @@ -133,6 +145,12 @@ main (int argc, char **argv) return -1; } ctx->setKeyListMode (mode); + if (ctx->keyListMode() != mode) { + // unfortunately, Context::setKeyListMode() does not return the error + // returned by gpgme + std::cerr << "Failed to set keylist mode. You may have used an invalid combination of options.\n"; + return -1; + } Error err = ctx->startKeyListing (*argv, only_secret); if (err) { std::cout << "Error: " << err.asString() << "\n"; diff --git a/lang/js/BrowserTestExtension/Makefile.in b/lang/js/BrowserTestExtension/Makefile.in index 68fa1ac..5f6fae4 100644 --- a/lang/js/BrowserTestExtension/Makefile.in +++ b/lang/js/BrowserTestExtension/Makefile.in @@ -110,6 +110,7 @@ host_triplet = @host@ subdir = lang/js/BrowserTestExtension ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -199,6 +200,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/js/DemoExtension/Makefile.in b/lang/js/DemoExtension/Makefile.in index ce14e1f..70d5b1c 100644 --- a/lang/js/DemoExtension/Makefile.in +++ b/lang/js/DemoExtension/Makefile.in @@ -110,6 +110,7 @@ host_triplet = @host@ subdir = lang/js/DemoExtension ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -199,6 +200,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/js/Makefile.in b/lang/js/Makefile.in index 8810732..a0904d9 100644 --- a/lang/js/Makefile.in +++ b/lang/js/Makefile.in @@ -109,6 +109,7 @@ host_triplet = @host@ subdir = lang/js ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -258,6 +259,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/js/src/Makefile.in b/lang/js/src/Makefile.in index 05f1d7a..b11046d 100644 --- a/lang/js/src/Makefile.in +++ b/lang/js/src/Makefile.in @@ -110,6 +110,7 @@ host_triplet = @host@ subdir = lang/js/src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -199,6 +200,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/python/Makefile.in b/lang/python/Makefile.in index c0fc091..b3e6b56 100644 --- a/lang/python/Makefile.in +++ b/lang/python/Makefile.in @@ -109,6 +109,7 @@ host_triplet = @host@ subdir = lang/python ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -259,6 +260,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/python/doc/Makefile.in b/lang/python/doc/Makefile.in index a53283b..2d9e826 100644 --- a/lang/python/doc/Makefile.in +++ b/lang/python/doc/Makefile.in @@ -109,6 +109,7 @@ host_triplet = @host@ subdir = lang/python/doc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -198,6 +199,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/python/examples/Makefile.in b/lang/python/examples/Makefile.in index 06ea925..87c0c20 100644 --- a/lang/python/examples/Makefile.in +++ b/lang/python/examples/Makefile.in @@ -109,6 +109,7 @@ host_triplet = @host@ subdir = lang/python/examples ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -198,6 +199,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/python/src/Makefile.in b/lang/python/src/Makefile.in index f250242..c711628 100644 --- a/lang/python/src/Makefile.in +++ b/lang/python/src/Makefile.in @@ -109,6 +109,7 @@ host_triplet = @host@ subdir = lang/python/src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -198,6 +199,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/python/src/core.py b/lang/python/src/core.py index 9618adc..c7b312b 100644 --- a/lang/python/src/core.py +++ b/lang/python/src/core.py @@ -106,9 +106,13 @@ class GpgmeWrapper(object): set_func = getattr(gpgme, "{}set_{}".format(self._cprefix, key)) def get(slf): + if not slf.wrapped: + return False return bool(get_func(slf.wrapped)) def set_(slf, value): + if not slf.wrapped: + return set_func(slf.wrapped, bool(value)) p = property(get, set_, doc="{} flag".format(key)) @@ -135,6 +139,8 @@ class GpgmeWrapper(object): if self._errorcheck(name): def _funcwrap(slf, *args): + if not slf.wrapped: + return None result = func(slf.wrapped, *args) if slf._callback_excinfo: gpgme.gpg_raise_callback_exception(slf) @@ -142,6 +148,8 @@ class GpgmeWrapper(object): else: def _funcwrap(slf, *args): + if not slf.wrapped: + return None result = func(slf.wrapped, *args) if slf._callback_excinfo: gpgme.gpg_raise_callback_exception(slf) @@ -332,8 +340,7 @@ class Context(GpgmeWrapper): finally: if passphrase is not None: self.pinentry_mode = old_pinentry_mode - if old_passphrase_cb: - self.set_passphrase_cb(*old_passphrase_cb[1:]) + gpgme.gpg_set_passphrase_cb(self, old_passphrase_cb) result = self.op_encrypt_result() assert not result.invalid_recipients @@ -426,8 +433,7 @@ class Context(GpgmeWrapper): finally: if passphrase is not None: self.pinentry_mode = old_pinentry_mode - if old_passphrase_cb: - self.set_passphrase_cb(*old_passphrase_cb[1:]) + gpgme.gpg_set_passphrase_cb(self, old_passphrase_cb) result = self.op_decrypt_result() @@ -851,8 +857,7 @@ class Context(GpgmeWrapper): 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:]) + gpgme.gpg_set_passphrase_cb(self, old_passphrase_cb) return self.op_genkey_result() @@ -934,8 +939,7 @@ class Context(GpgmeWrapper): 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:]) + gpgme.gpg_set_passphrase_cb(self, old_passphrase_cb) return self.op_genkey_result() @@ -1102,6 +1106,8 @@ class Context(GpgmeWrapper): @property def signers(self): """Keys used for signing""" + if not self.wrapped: + return None return [self.signers_enum(i) for i in range(self.signers_count())] @signers.setter @@ -1137,6 +1143,8 @@ class Context(GpgmeWrapper): @property def home_dir(self): """Engine's home directory""" + if not self.wrapped: + return None return self.engine_info.home_dir @home_dir.setter @@ -1182,7 +1190,7 @@ class Context(GpgmeWrapper): return self def __exit__(self, type, value, tb): - self.__del__() + return False def op_keylist_all(self, *args, **kwargs): self.op_keylist_start(*args, **kwargs) @@ -1520,7 +1528,7 @@ class Data(GpgmeWrapper): return self def __exit__(self, type, value, tb): - self.__del__() + return False def _free_datacbs(self): self._data_cbs = None diff --git a/lang/python/tests/Makefile.in b/lang/python/tests/Makefile.in index 9e60086..cbc6980 100644 --- a/lang/python/tests/Makefile.in +++ b/lang/python/tests/Makefile.in @@ -108,6 +108,7 @@ host_triplet = @host@ subdir = lang/python/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -197,6 +198,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/python/tests/t-idiomatic.py b/lang/python/tests/t-idiomatic.py index bc05e6c..faa4190 100755 --- a/lang/python/tests/t-idiomatic.py +++ b/lang/python/tests/t-idiomatic.py @@ -35,6 +35,9 @@ with gpg.Context() as c, gpg.Data() as d: d.write(b"Halloechen") leak_c = c leak_d = d + +leak_c.__del__() +leak_d.__del__() assert leak_c.wrapped is None assert leak_d.wrapped is None diff --git a/lang/qt/Makefile.in b/lang/qt/Makefile.in index d186d88..b8d894b 100644 --- a/lang/qt/Makefile.in +++ b/lang/qt/Makefile.in @@ -110,6 +110,7 @@ host_triplet = @host@ subdir = lang/qt ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -259,6 +260,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/qt/doc/Makefile.in b/lang/qt/doc/Makefile.in index eadc021..56ffde0 100644 --- a/lang/qt/doc/Makefile.in +++ b/lang/qt/doc/Makefile.in @@ -109,6 +109,7 @@ host_triplet = @host@ subdir = lang/qt/doc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -198,6 +199,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am index d47da89..3923e5b 100644 --- a/lang/qt/src/Makefile.am +++ b/lang/qt/src/Makefile.am @@ -21,7 +21,7 @@ lib_LTLIBRARIES = libqgpgme.la EXTRA_DIST = QGpgmeConfig.cmake.in.in QGpgmeConfigVersion.cmake.in \ qgpgme_debug.h qgpgme_version.h.in \ - QGpgmeConfig.cmake.in.in + QGpgmeConfig-w32.cmake.in.in qgpgme_sources = \ dataprovider.cpp \ @@ -35,14 +35,17 @@ qgpgme_sources = \ qgpgmeimportjob.cpp qgpgmekeygenerationjob.cpp qgpgmekeylistjob.cpp \ qgpgmelistallkeysjob.cpp qgpgmenewcryptoconfig.cpp \ qgpgmereceivekeysjob.cpp \ - qgpgmerefreshkeysjob.cpp \ + qgpgmerefreshsmimekeysjob.cpp \ + qgpgmerevokekeyjob.cpp \ + qgpgmesetprimaryuseridjob.cpp \ qgpgmesignencryptjob.cpp \ qgpgmesignjob.cpp qgpgmesignkeyjob.cpp qgpgmeverifydetachedjob.cpp \ qgpgmeverifyopaquejob.cpp qgpgmewkdlookupjob.cpp threadedjobmixin.cpp \ qgpgmekeyformailboxjob.cpp qgpgme_debug.cpp \ qgpgmetofupolicyjob.cpp qgpgmequickjob.cpp \ defaultkeygenerationjob.cpp qgpgmewkspublishjob.cpp \ - qgpgmegpgcardjob.cpp changeexpiryjob.cpp importjob.cpp \ + qgpgmegpgcardjob.cpp changeexpiryjob.cpp encryptjob.cpp importjob.cpp \ + signencryptjob.cpp \ dn.cpp cryptoconfig.cpp wkdlookupresult.cpp \ util.cpp @@ -70,6 +73,8 @@ qgpgme_headers= \ qgpgmenewcryptoconfig.h \ quickjob.h \ receivekeysjob.h \ + revokekeyjob.h \ + setprimaryuseridjob.h \ specialjob.h \ signjob.h \ signkeyjob.h \ @@ -114,6 +119,8 @@ camelcase_headers= \ QGpgMENewCryptoConfig \ QuickJob \ ReceiveKeysJob \ + RevokeKeyJob \ + SetPrimaryUserIDJob \ SpecialJob \ SignJob \ SignKeyJob \ @@ -158,7 +165,9 @@ private_qgpgme_headers = \ qgpgmekeylistjob.h \ qgpgmelistallkeysjob.h \ qgpgmereceivekeysjob.h \ - qgpgmerefreshkeysjob.h \ + qgpgmerefreshsmimekeysjob.h \ + qgpgmerevokekeyjob.h \ + qgpgmesetprimaryuseridjob.h \ qgpgmesignencryptjob.h \ qgpgmesignjob.h \ qgpgmesignkeyjob.h \ @@ -211,7 +220,9 @@ qgpgme_moc_sources = \ qgpgmekeylistjob.moc \ qgpgmelistallkeysjob.moc \ qgpgmereceivekeysjob.moc \ - qgpgmerefreshkeysjob.moc \ + qgpgmerefreshsmimekeysjob.moc \ + qgpgmerevokekeyjob.moc \ + qgpgmesetprimaryuseridjob.moc \ qgpgmesignencryptjob.moc \ qgpgmesignjob.moc \ qgpgmesignkeyjob.moc \ @@ -223,6 +234,8 @@ qgpgme_moc_sources = \ qgpgmetofupolicyjob.moc \ receivekeysjob.moc \ refreshkeysjob.moc \ + revokekeyjob.moc \ + setprimaryuseridjob.moc \ signencryptjob.moc \ signjob.moc \ signkeyjob.moc \ diff --git a/lang/qt/src/Makefile.in b/lang/qt/src/Makefile.in index 2f09648..52ab451 100644 --- a/lang/qt/src/Makefile.in +++ b/lang/qt/src/Makefile.in @@ -92,6 +92,7 @@ host_triplet = @host@ subdir = lang/qt/src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -155,15 +156,17 @@ am__objects_1 = dataprovider.lo debug.lo job.lo multideletejob.lo \ qgpgmeimportjob.lo qgpgmekeygenerationjob.lo \ qgpgmekeylistjob.lo qgpgmelistallkeysjob.lo \ qgpgmenewcryptoconfig.lo qgpgmereceivekeysjob.lo \ - qgpgmerefreshkeysjob.lo qgpgmesignencryptjob.lo \ + qgpgmerefreshsmimekeysjob.lo qgpgmerevokekeyjob.lo \ + qgpgmesetprimaryuseridjob.lo qgpgmesignencryptjob.lo \ qgpgmesignjob.lo qgpgmesignkeyjob.lo \ qgpgmeverifydetachedjob.lo qgpgmeverifyopaquejob.lo \ qgpgmewkdlookupjob.lo threadedjobmixin.lo \ qgpgmekeyformailboxjob.lo qgpgme_debug.lo \ qgpgmetofupolicyjob.lo qgpgmequickjob.lo \ defaultkeygenerationjob.lo qgpgmewkspublishjob.lo \ - qgpgmegpgcardjob.lo changeexpiryjob.lo importjob.lo dn.lo \ - cryptoconfig.lo wkdlookupresult.lo util.lo + qgpgmegpgcardjob.lo changeexpiryjob.lo encryptjob.lo \ + importjob.lo signencryptjob.lo dn.lo cryptoconfig.lo \ + wkdlookupresult.lo util.lo am__objects_2 = am_libqgpgme_la_OBJECTS = $(am__objects_1) $(am__objects_2) \ $(am__objects_2) @@ -195,9 +198,9 @@ am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/changeexpiryjob.Plo \ ./$(DEPDIR)/cryptoconfig.Plo ./$(DEPDIR)/dataprovider.Plo \ ./$(DEPDIR)/debug.Plo ./$(DEPDIR)/defaultkeygenerationjob.Plo \ - ./$(DEPDIR)/dn.Plo ./$(DEPDIR)/importjob.Plo \ - ./$(DEPDIR)/job.Plo ./$(DEPDIR)/multideletejob.Plo \ - ./$(DEPDIR)/qgpgme_debug.Plo \ + ./$(DEPDIR)/dn.Plo ./$(DEPDIR)/encryptjob.Plo \ + ./$(DEPDIR)/importjob.Plo ./$(DEPDIR)/job.Plo \ + ./$(DEPDIR)/multideletejob.Plo ./$(DEPDIR)/qgpgme_debug.Plo \ ./$(DEPDIR)/qgpgmeaddexistingsubkeyjob.Plo \ ./$(DEPDIR)/qgpgmeadduseridjob.Plo \ ./$(DEPDIR)/qgpgmebackend.Plo \ @@ -220,7 +223,9 @@ am__depfiles_remade = ./$(DEPDIR)/changeexpiryjob.Plo \ ./$(DEPDIR)/qgpgmenewcryptoconfig.Plo \ ./$(DEPDIR)/qgpgmequickjob.Plo \ ./$(DEPDIR)/qgpgmereceivekeysjob.Plo \ - ./$(DEPDIR)/qgpgmerefreshkeysjob.Plo \ + ./$(DEPDIR)/qgpgmerefreshsmimekeysjob.Plo \ + ./$(DEPDIR)/qgpgmerevokekeyjob.Plo \ + ./$(DEPDIR)/qgpgmesetprimaryuseridjob.Plo \ ./$(DEPDIR)/qgpgmesignencryptjob.Plo \ ./$(DEPDIR)/qgpgmesignjob.Plo ./$(DEPDIR)/qgpgmesignkeyjob.Plo \ ./$(DEPDIR)/qgpgmetofupolicyjob.Plo \ @@ -228,6 +233,7 @@ am__depfiles_remade = ./$(DEPDIR)/changeexpiryjob.Plo \ ./$(DEPDIR)/qgpgmeverifyopaquejob.Plo \ ./$(DEPDIR)/qgpgmewkdlookupjob.Plo \ ./$(DEPDIR)/qgpgmewkspublishjob.Plo \ + ./$(DEPDIR)/signencryptjob.Plo \ ./$(DEPDIR)/threadedjobmixin.Plo ./$(DEPDIR)/util.Plo \ ./$(DEPDIR)/wkdlookupresult.Plo am__mv = mv -f @@ -351,6 +357,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ @@ -524,7 +531,7 @@ top_srcdir = @top_srcdir@ lib_LTLIBRARIES = libqgpgme.la EXTRA_DIST = QGpgmeConfig.cmake.in.in QGpgmeConfigVersion.cmake.in \ qgpgme_debug.h qgpgme_version.h.in \ - QGpgmeConfig.cmake.in.in + QGpgmeConfig-w32.cmake.in.in qgpgme_sources = \ dataprovider.cpp \ @@ -538,14 +545,17 @@ qgpgme_sources = \ qgpgmeimportjob.cpp qgpgmekeygenerationjob.cpp qgpgmekeylistjob.cpp \ qgpgmelistallkeysjob.cpp qgpgmenewcryptoconfig.cpp \ qgpgmereceivekeysjob.cpp \ - qgpgmerefreshkeysjob.cpp \ + qgpgmerefreshsmimekeysjob.cpp \ + qgpgmerevokekeyjob.cpp \ + qgpgmesetprimaryuseridjob.cpp \ qgpgmesignencryptjob.cpp \ qgpgmesignjob.cpp qgpgmesignkeyjob.cpp qgpgmeverifydetachedjob.cpp \ qgpgmeverifyopaquejob.cpp qgpgmewkdlookupjob.cpp threadedjobmixin.cpp \ qgpgmekeyformailboxjob.cpp qgpgme_debug.cpp \ qgpgmetofupolicyjob.cpp qgpgmequickjob.cpp \ defaultkeygenerationjob.cpp qgpgmewkspublishjob.cpp \ - qgpgmegpgcardjob.cpp changeexpiryjob.cpp importjob.cpp \ + qgpgmegpgcardjob.cpp changeexpiryjob.cpp encryptjob.cpp importjob.cpp \ + signencryptjob.cpp \ dn.cpp cryptoconfig.cpp wkdlookupresult.cpp \ util.cpp @@ -574,6 +584,8 @@ qgpgme_headers = \ qgpgmenewcryptoconfig.h \ quickjob.h \ receivekeysjob.h \ + revokekeyjob.h \ + setprimaryuseridjob.h \ specialjob.h \ signjob.h \ signkeyjob.h \ @@ -618,6 +630,8 @@ camelcase_headers = \ QGpgMENewCryptoConfig \ QuickJob \ ReceiveKeysJob \ + RevokeKeyJob \ + SetPrimaryUserIDJob \ SpecialJob \ SignJob \ SignKeyJob \ @@ -662,7 +676,9 @@ private_qgpgme_headers = \ qgpgmekeylistjob.h \ qgpgmelistallkeysjob.h \ qgpgmereceivekeysjob.h \ - qgpgmerefreshkeysjob.h \ + qgpgmerefreshsmimekeysjob.h \ + qgpgmerevokekeyjob.h \ + qgpgmesetprimaryuseridjob.h \ qgpgmesignencryptjob.h \ qgpgmesignjob.h \ qgpgmesignkeyjob.h \ @@ -715,7 +731,9 @@ qgpgme_moc_sources = \ qgpgmekeylistjob.moc \ qgpgmelistallkeysjob.moc \ qgpgmereceivekeysjob.moc \ - qgpgmerefreshkeysjob.moc \ + qgpgmerefreshsmimekeysjob.moc \ + qgpgmerevokekeyjob.moc \ + qgpgmesetprimaryuseridjob.moc \ qgpgmesignencryptjob.moc \ qgpgmesignjob.moc \ qgpgmesignkeyjob.moc \ @@ -727,6 +745,8 @@ qgpgme_moc_sources = \ qgpgmetofupolicyjob.moc \ receivekeysjob.moc \ refreshkeysjob.moc \ + revokekeyjob.moc \ + setprimaryuseridjob.moc \ signencryptjob.moc \ signjob.moc \ signkeyjob.moc \ @@ -861,6 +881,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defaultkeygenerationjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dn.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encryptjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/importjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/multideletejob.Plo@am__quote@ # am--include-marker @@ -887,7 +908,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmenewcryptoconfig.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmequickjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmereceivekeysjob.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmerefreshkeysjob.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmerefreshsmimekeysjob.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmerevokekeyjob.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmesetprimaryuseridjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmesignencryptjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmesignjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmesignkeyjob.Plo@am__quote@ # am--include-marker @@ -896,6 +919,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmeverifyopaquejob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmewkdlookupjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qgpgmewkspublishjob.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signencryptjob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/threadedjobmixin.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wkdlookupresult.Plo@am__quote@ # am--include-marker @@ -1135,6 +1159,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/debug.Plo -rm -f ./$(DEPDIR)/defaultkeygenerationjob.Plo -rm -f ./$(DEPDIR)/dn.Plo + -rm -f ./$(DEPDIR)/encryptjob.Plo -rm -f ./$(DEPDIR)/importjob.Plo -rm -f ./$(DEPDIR)/job.Plo -rm -f ./$(DEPDIR)/multideletejob.Plo @@ -1161,7 +1186,9 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/qgpgmenewcryptoconfig.Plo -rm -f ./$(DEPDIR)/qgpgmequickjob.Plo -rm -f ./$(DEPDIR)/qgpgmereceivekeysjob.Plo - -rm -f ./$(DEPDIR)/qgpgmerefreshkeysjob.Plo + -rm -f ./$(DEPDIR)/qgpgmerefreshsmimekeysjob.Plo + -rm -f ./$(DEPDIR)/qgpgmerevokekeyjob.Plo + -rm -f ./$(DEPDIR)/qgpgmesetprimaryuseridjob.Plo -rm -f ./$(DEPDIR)/qgpgmesignencryptjob.Plo -rm -f ./$(DEPDIR)/qgpgmesignjob.Plo -rm -f ./$(DEPDIR)/qgpgmesignkeyjob.Plo @@ -1170,6 +1197,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/qgpgmeverifyopaquejob.Plo -rm -f ./$(DEPDIR)/qgpgmewkdlookupjob.Plo -rm -f ./$(DEPDIR)/qgpgmewkspublishjob.Plo + -rm -f ./$(DEPDIR)/signencryptjob.Plo -rm -f ./$(DEPDIR)/threadedjobmixin.Plo -rm -f ./$(DEPDIR)/util.Plo -rm -f ./$(DEPDIR)/wkdlookupresult.Plo @@ -1226,6 +1254,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/debug.Plo -rm -f ./$(DEPDIR)/defaultkeygenerationjob.Plo -rm -f ./$(DEPDIR)/dn.Plo + -rm -f ./$(DEPDIR)/encryptjob.Plo -rm -f ./$(DEPDIR)/importjob.Plo -rm -f ./$(DEPDIR)/job.Plo -rm -f ./$(DEPDIR)/multideletejob.Plo @@ -1252,7 +1281,9 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/qgpgmenewcryptoconfig.Plo -rm -f ./$(DEPDIR)/qgpgmequickjob.Plo -rm -f ./$(DEPDIR)/qgpgmereceivekeysjob.Plo - -rm -f ./$(DEPDIR)/qgpgmerefreshkeysjob.Plo + -rm -f ./$(DEPDIR)/qgpgmerefreshsmimekeysjob.Plo + -rm -f ./$(DEPDIR)/qgpgmerevokekeyjob.Plo + -rm -f ./$(DEPDIR)/qgpgmesetprimaryuseridjob.Plo -rm -f ./$(DEPDIR)/qgpgmesignencryptjob.Plo -rm -f ./$(DEPDIR)/qgpgmesignjob.Plo -rm -f ./$(DEPDIR)/qgpgmesignkeyjob.Plo @@ -1261,6 +1292,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/qgpgmeverifyopaquejob.Plo -rm -f ./$(DEPDIR)/qgpgmewkdlookupjob.Plo -rm -f ./$(DEPDIR)/qgpgmewkspublishjob.Plo + -rm -f ./$(DEPDIR)/signencryptjob.Plo -rm -f ./$(DEPDIR)/threadedjobmixin.Plo -rm -f ./$(DEPDIR)/util.Plo -rm -f ./$(DEPDIR)/wkdlookupresult.Plo diff --git a/lang/qt/src/RevokeKeyJob b/lang/qt/src/RevokeKeyJob new file mode 100644 index 0000000..5d0f973 --- /dev/null +++ b/lang/qt/src/RevokeKeyJob @@ -0,0 +1 @@ +#include "qgpgme/revokekeyjob.h" diff --git a/lang/qt/src/SetPrimaryUserIDJob b/lang/qt/src/SetPrimaryUserIDJob new file mode 100644 index 0000000..f61a24c --- /dev/null +++ b/lang/qt/src/SetPrimaryUserIDJob @@ -0,0 +1 @@ +#include "qgpgme/setprimaryuseridjob.h" diff --git a/lang/qt/src/decryptjob.h b/lang/qt/src/decryptjob.h index 7753e18..5195407 100644 --- a/lang/qt/src/decryptjob.h +++ b/lang/qt/src/decryptjob.h @@ -84,8 +84,6 @@ public: If \a plainText is non-null, the plaintext is written there. Otherwise, it will be delivered in the second argument of result(). - - \throws GpgME::Exception if starting fails */ virtual void start(const std::shared_ptr<QIODevice> &cipherText, const std::shared_ptr<QIODevice> &plainText = std::shared_ptr<QIODevice>()) = 0; diff --git a/lang/qt/src/decryptverifyjob.h b/lang/qt/src/decryptverifyjob.h index e5c4346..8444e4d 100644 --- a/lang/qt/src/decryptverifyjob.h +++ b/lang/qt/src/decryptverifyjob.h @@ -85,8 +85,6 @@ public: If \a plainText is non-null, the plaintext is written there. Otherwise, it will be delivered in the third argument of result(). - - \throws GpgME::Exception if starting fails */ virtual void start(const std::shared_ptr<QIODevice> &cipherText, const std::shared_ptr<QIODevice> &plainText = std::shared_ptr<QIODevice>()) = 0; diff --git a/lang/qt/src/downloadjob.h b/lang/qt/src/downloadjob.h index 7c8bb72..b916c41 100644 --- a/lang/qt/src/downloadjob.h +++ b/lang/qt/src/downloadjob.h @@ -38,6 +38,7 @@ #include "job.h" #include <QtCore/QByteArray> +#include <QtCore/QStringList> #include <memory> @@ -46,7 +47,6 @@ namespace GpgME class Error; } -class QStringList; class QIODevice; class QByteArray; diff --git a/lang/qt/src/encryptjob.cpp b/lang/qt/src/encryptjob.cpp new file mode 100644 index 0000000..7533bd9 --- /dev/null +++ b/lang/qt/src/encryptjob.cpp @@ -0,0 +1,61 @@ +/* + encryptjob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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 "encryptjob.h" +#include "job_p.h" + +using namespace QGpgME; + +namespace +{ +struct EncryptJobPrivate : public JobPrivate +{ + QString m_fileName; +}; +} + +void EncryptJob::setFileName(const QString &fileName) +{ + auto d = jobPrivate<EncryptJobPrivate>(this); + d->m_fileName = fileName; +} + +QString EncryptJob::fileName() const +{ + auto d = jobPrivate<EncryptJobPrivate>(this); + return d->m_fileName; +} diff --git a/lang/qt/src/encryptjob.h b/lang/qt/src/encryptjob.h index 937ee8d..8135053 100644 --- a/lang/qt/src/encryptjob.h +++ b/lang/qt/src/encryptjob.h @@ -5,6 +5,8 @@ Copyright (c) 2004, 2007 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -80,6 +82,9 @@ protected: public: ~EncryptJob(); + void setFileName(const QString &fileName); + QString fileName() const; + /** Starts the encryption operation. \a recipients is the a list of keys to encrypt \a plainText to. Empty (null) keys are @@ -99,8 +104,6 @@ public: If \a cipherText is non-null, the ciphertext is written there. Otherwise, it will be delivered in the second argument of result(). - - \throws GpgME::Exception if starting fails */ virtual void start(const std::vector<GpgME::Key> &recipients, const std::shared_ptr<QIODevice> &plainText, diff --git a/lang/qt/src/exportjob.h b/lang/qt/src/exportjob.h index 7f79ea0..4fdb468 100644 --- a/lang/qt/src/exportjob.h +++ b/lang/qt/src/exportjob.h @@ -39,14 +39,13 @@ #include "job.h" #include <QtCore/QByteArray> +#include <QtCore/QStringList> namespace GpgME { class Error; } -class QStringList; - namespace QGpgME { diff --git a/lang/qt/src/gpgcardjob.h b/lang/qt/src/gpgcardjob.h index 63f5cf7..3f1b239 100644 --- a/lang/qt/src/gpgcardjob.h +++ b/lang/qt/src/gpgcardjob.h @@ -44,24 +44,6 @@ class Error; namespace QGpgME { -/** - @short Get the best key to use for a Mailbox - - To use the keyformailboxjob, first obtain an instance from the - CryptoBackend and either exec it or start and - conncet the result() signals to a suitable slot. - The job will be automatically deleted in which - case the KeylistJob instance will have schedules it's own - destruction with a call to QObject::deleteLater(). - - The best key is defined as the key with a UID that has an - E-Mail that matches the mailbox provided. If multiple - keys are found the one with the highest validity is returned. - - After result() is emitted, the - KeyListJob will schedule it's own destruction by calling - QObject::deleteLater(). -*/ class QGPGME_EXPORT GpgCardJob: public Job { Q_OBJECT diff --git a/lang/qt/src/job.cpp b/lang/qt/src/job.cpp index a9edc8e..98f408b 100644 --- a/lang/qt/src/job.cpp +++ b/lang/qt/src/job.cpp @@ -72,6 +72,8 @@ #include "quickjob.h" #include "gpgcardjob.h" #include "receivekeysjob.h" +#include "revokekeyjob.h" +#include "setprimaryuseridjob.h" #include <QCoreApplication> #include <QDebug> @@ -172,6 +174,8 @@ make_job_subclass(WKSPublishJob) make_job_subclass(TofuPolicyJob) make_job_subclass(QuickJob) make_job_subclass(GpgCardJob) +make_job_subclass(RevokeKeyJob) +make_job_subclass(SetPrimaryUserIDJob) #undef make_job_subclass @@ -208,3 +212,5 @@ make_job_subclass(GpgCardJob) #include "quickjob.moc" #include "gpgcardjob.moc" #include "receivekeysjob.moc" +#include "revokekeyjob.moc" +#include "setprimaryuseridjob.moc" diff --git a/lang/qt/src/keyformailboxjob.h b/lang/qt/src/keyformailboxjob.h index 42d1729..5e08907 100644 --- a/lang/qt/src/keyformailboxjob.h +++ b/lang/qt/src/keyformailboxjob.h @@ -57,18 +57,14 @@ namespace QGpgME To use the keyformailboxjob, first obtain an instance from the CryptoBackend and either exec it or start and - conncet the result() signals to a suitable slot. - The job will be automatically deleted in which - case the KeylistJob instance will have schedules it's own - destruction with a call to QObject::deleteLater(). + connect the result() signal to a suitable slot. The best key is defined as the key with a UID that has an E-Mail that matches the mailbox provided. If multiple keys are found the one with the highest validity is returned. - After result() is emitted, the - KeyListJob will schedule it's own destruction by calling - QObject::deleteLater(). + After result() is emitted, the job will schedule it's own + destruction by calling QObject::deleteLater(). */ class QGPGME_EXPORT KeyForMailboxJob: public Job { diff --git a/lang/qt/src/keylistjob.h b/lang/qt/src/keylistjob.h index 88eac87..ace9fbe 100644 --- a/lang/qt/src/keylistjob.h +++ b/lang/qt/src/keylistjob.h @@ -44,6 +44,8 @@ # include <gpgme++/key.h> #endif +#include <QtCore/QStringList> + #include <vector> namespace GpgME @@ -52,8 +54,6 @@ class Error; class KeyListResult; } -class QStringList; - namespace QGpgME { @@ -64,7 +64,7 @@ namespace QGpgME CryptoBackend implementation, connect the nextKey(), progress() and result() signals to suitable slots and then start the key listing with a call to start(). This call might fail, in which - case the KeylistJob instance will have schedules it's own + case the KeylistJob instance will have scheduled it's own destruction with a call to QObject::deleteLater(). During keylisting, you will receive new key objects through the diff --git a/lang/qt/src/protocol.h b/lang/qt/src/protocol.h index 62b2cb2..019633a 100644 --- a/lang/qt/src/protocol.h +++ b/lang/qt/src/protocol.h @@ -71,6 +71,8 @@ class TofuPolicyJob; class QuickJob; class GpgCardJob; class ReceiveKeysJob; +class RevokeKeyJob; +class SetPrimaryUserIDJob; /** The main entry point for QGpgME Comes in OpenPGP and SMIME(CMS) flavors. * @@ -134,6 +136,12 @@ public: virtual DeleteJob *deleteJob() const = 0; virtual SignEncryptJob *signEncryptJob(bool armor = false, bool textMode = false) const = 0; virtual DecryptVerifyJob *decryptVerifyJob(bool textmode = false) const = 0; + + /** + * For S/MIME keys this job performs a full validation check of the keys + * with updated CRLs. + * For OpenPGP keys, use receiveKeysJob. + */ virtual RefreshKeysJob *refreshKeysJob() const = 0; virtual ChangeExpiryJob *changeExpiryJob() const = 0; virtual SignKeyJob *signKeyJob() const = 0; @@ -173,6 +181,14 @@ public: virtual ExportJob *secretSubkeyExportJob(bool armor = false) const = 0; virtual AddExistingSubkeyJob *addExistingSubkeyJob() const = 0; virtual ReceiveKeysJob *receiveKeysJob() const = 0; + + virtual RevokeKeyJob *revokeKeyJob() const = 0; + + /** + * Returns a job for flagging a user ID as the primary user ID of an + * OpenPGP key. + */ + virtual SetPrimaryUserIDJob *setPrimaryUserIDJob() const = 0; }; /** Obtain a reference to the OpenPGP Protocol. diff --git a/lang/qt/src/protocol_p.h b/lang/qt/src/protocol_p.h index 4211e00..685ac4d 100644 --- a/lang/qt/src/protocol_p.h +++ b/lang/qt/src/protocol_p.h @@ -42,7 +42,7 @@ #include "qgpgmelistallkeysjob.h" #include "qgpgmedecryptjob.h" #include "qgpgmedecryptverifyjob.h" -#include "qgpgmerefreshkeysjob.h" +#include "qgpgmerefreshsmimekeysjob.h" #include "qgpgmedeletejob.h" #include "qgpgmedownloadjob.h" #include "qgpgmesignencryptjob.h" @@ -65,6 +65,8 @@ #include "qgpgmetofupolicyjob.h" #include "qgpgmequickjob.h" #include "qgpgmereceivekeysjob.h" +#include "qgpgmerevokekeyjob.h" +#include "qgpgmesetprimaryuseridjob.h" namespace { @@ -282,12 +284,11 @@ public: QGpgME::RefreshKeysJob *refreshKeysJob() const Q_DECL_OVERRIDE { - if (mProtocol != GpgME::CMS) { // fixme: add support for gpg, too + if (mProtocol != GpgME::CMS) { return nullptr; } - // this operation is not supported by gpgme, so we have to call gpgsm ourselves: - return new QGpgME::QGpgMERefreshKeysJob(); + return new QGpgME::QGpgMERefreshSMIMEKeysJob; } QGpgME::DownloadJob *downloadJob(bool armor) const Q_DECL_OVERRIDE @@ -421,7 +422,7 @@ public: if (!context) { return nullptr; } - context->setKeyListMode(GpgME::Extern | GpgME::Local | GpgME::Signatures | GpgME::Validate); + context->setKeyListMode(GpgME::Locate | GpgME::Signatures | GpgME::Validate); return new QGpgME::QGpgMEKeyListJob(context); } @@ -481,6 +482,30 @@ public: } return new QGpgME::QGpgMEQuickJob(context); } + + QGpgME::RevokeKeyJob *revokeKeyJob() const Q_DECL_OVERRIDE + { + if (mProtocol != GpgME::OpenPGP) { + return nullptr; + } + GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); + if (!context) { + return nullptr; + } + return new QGpgME::QGpgMERevokeKeyJob(context); + } + + QGpgME::SetPrimaryUserIDJob *setPrimaryUserIDJob() const override + { + if (mProtocol != GpgME::OpenPGP) { + return nullptr; + } + GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); + if (!context) { + return nullptr; + } + return new QGpgME::QGpgMESetPrimaryUserIDJob{context}; + } }; } diff --git a/lang/qt/src/qgpgmedecryptverifyjob.cpp b/lang/qt/src/qgpgmedecryptverifyjob.cpp index 5f9a8ab..e2b3724 100644 --- a/lang/qt/src/qgpgmedecryptverifyjob.cpp +++ b/lang/qt/src/qgpgmedecryptverifyjob.cpp @@ -67,8 +67,7 @@ static QGpgMEDecryptVerifyJob::result_type decrypt_verify(Context *ctx, QThread const std::weak_ptr<QIODevice> &cipherText_, const std::weak_ptr<QIODevice> &plainText_) { - - qCDebug(QGPGME_LOG); + qCDebug(QGPGME_LOG) << __func__; const std::shared_ptr<QIODevice> cipherText = cipherText_.lock(); const std::shared_ptr<QIODevice> plainText = plainText_.lock(); @@ -86,7 +85,7 @@ static QGpgMEDecryptVerifyJob::result_type decrypt_verify(Context *ctx, QThread const std::pair<DecryptionResult, VerificationResult> res = ctx->decryptAndVerify(indata, outdata); Error ae; const QString log = _detail::audit_log_as_html(ctx, ae); - qCDebug(QGPGME_LOG) << "End no plainText. Error: " << ae; + qCDebug(QGPGME_LOG) << __func__ << "- End no plainText. Error:" << ae.asString(); return std::make_tuple(res.first, res.second, out.data(), log, ae); } else { QGpgME::QIODeviceDataProvider out(plainText); @@ -95,10 +94,9 @@ static QGpgMEDecryptVerifyJob::result_type decrypt_verify(Context *ctx, QThread const std::pair<DecryptionResult, VerificationResult> res = ctx->decryptAndVerify(indata, outdata); Error ae; const QString log = _detail::audit_log_as_html(ctx, ae); - qCDebug(QGPGME_LOG) << "End plainText. Error: " << ae; + qCDebug(QGPGME_LOG) << __func__ << "- End plainText. Error:" << ae.asString(); return std::make_tuple(res.first, res.second, QByteArray(), log, ae); } - } static QGpgMEDecryptVerifyJob::result_type decrypt_verify_qba(Context *ctx, const QByteArray &cipherText) diff --git a/lang/qt/src/qgpgmeencryptjob.cpp b/lang/qt/src/qgpgmeencryptjob.cpp index 8fb3dd3..cd54496 100644 --- a/lang/qt/src/qgpgmeencryptjob.cpp +++ b/lang/qt/src/qgpgmeencryptjob.cpp @@ -5,6 +5,8 @@ Copyright (c) 2004,2007,2008 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -45,7 +47,7 @@ #include "data.h" #include <QBuffer> - +#include <QFileInfo> #include <cassert> @@ -71,7 +73,8 @@ static QGpgMEEncryptJob::result_type encrypt(Context *ctx, QThread *thread, const std::weak_ptr<QIODevice> &plainText_, const std::weak_ptr<QIODevice> &cipherText_, const Context::EncryptionFlags eflags, - bool outputIsBsse64Encoded) + bool outputIsBsse64Encoded, + const QString &fileName) { const std::shared_ptr<QIODevice> plainText = plainText_.lock(); @@ -81,7 +84,12 @@ static QGpgMEEncryptJob::result_type encrypt(Context *ctx, QThread *thread, const _detail::ToThreadMover ptMover(plainText, thread); QGpgME::QIODeviceDataProvider in(plainText); - const Data indata(&in); + Data indata(&in); + + const auto pureFileName = QFileInfo{fileName}.fileName().toStdString(); + if (!pureFileName.empty()) { + indata.setFileName(pureFileName.c_str()); + } if (!cipherText) { QGpgME::QByteArrayDataProvider out; @@ -111,20 +119,20 @@ static QGpgMEEncryptJob::result_type encrypt(Context *ctx, QThread *thread, } -static QGpgMEEncryptJob::result_type encrypt_qba(Context *ctx, const std::vector<Key> &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded) +static QGpgMEEncryptJob::result_type encrypt_qba(Context *ctx, const std::vector<Key> &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded, const QString &fileName) { const std::shared_ptr<QBuffer> buffer(new QBuffer); buffer->setData(plainText); if (!buffer->open(QIODevice::ReadOnly)) { assert(!"This should never happen: QBuffer::open() failed"); } - return encrypt(ctx, nullptr, recipients, buffer, std::shared_ptr<QIODevice>(), eflags, outputIsBsse64Encoded); + return encrypt(ctx, nullptr, recipients, buffer, std::shared_ptr<QIODevice>(), eflags, outputIsBsse64Encoded, fileName); } Error QGpgMEEncryptJob::start(const std::vector<Key> &recipients, const QByteArray &plainText, bool alwaysTrust) { run(std::bind(&encrypt_qba, std::placeholders::_1, recipients, plainText, - alwaysTrust ? Context::AlwaysTrust : Context::None, mOutputIsBase64Encoded)); + alwaysTrust ? Context::AlwaysTrust : Context::None, mOutputIsBase64Encoded, fileName())); return Error(); } @@ -136,14 +144,15 @@ void QGpgMEEncryptJob::start(const std::vector<Key> &recipients, const std::shar recipients, std::placeholders::_3, std::placeholders::_4, eflags, - mOutputIsBase64Encoded), + mOutputIsBase64Encoded, + fileName()), plainText, cipherText); } EncryptionResult QGpgMEEncryptJob::exec(const std::vector<Key> &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, QByteArray &cipherText) { - const result_type r = encrypt_qba(context(), recipients, plainText, eflags, mOutputIsBase64Encoded); + const result_type r = encrypt_qba(context(), recipients, plainText, eflags, mOutputIsBase64Encoded, fileName()); cipherText = std::get<1>(r); resultHook(r); return mResult; diff --git a/lang/qt/src/qgpgmekeyformailboxjob.cpp b/lang/qt/src/qgpgmekeyformailboxjob.cpp index 534e9a3..b6ab3db 100644 --- a/lang/qt/src/qgpgmekeyformailboxjob.cpp +++ b/lang/qt/src/qgpgmekeyformailboxjob.cpp @@ -71,7 +71,7 @@ static bool subkeyIsOk(const Subkey s) static QGpgMEKeyForMailboxJob::result_type do_work(Context *ctx, const QString &mailbox, bool canEncrypt) { /* Do a Keylisting. */ - ctx->setKeyListMode(GpgME::Extern | GpgME::Local | GpgME::Signatures | GpgME::Validate); + ctx->setKeyListMode(GpgME::Locate | GpgME::Signatures | GpgME::Validate); std::vector<Key> keys; QGpgMEKeyListJob *keylist = new QGpgMEKeyListJob(ctx); diff --git a/lang/qt/src/qgpgmerefreshkeysjob.cpp b/lang/qt/src/qgpgmerefreshsmimekeysjob.cpp index 5f78f2a..3187a0c 100644 --- a/lang/qt/src/qgpgmerefreshkeysjob.cpp +++ b/lang/qt/src/qgpgmerefreshsmimekeysjob.cpp @@ -1,5 +1,5 @@ /* - qgpgmerefreshkeysjob.cpp + qgpgmerefreshsmimekeysjob.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004 Klarävdalens Datakonsult AB @@ -38,21 +38,27 @@ #include "config.h" #endif -#include "qgpgmerefreshkeysjob.h" +#include "qgpgmerefreshsmimekeysjob.h" +#include "util.h" #include <QDebug> #include "qgpgme_debug.h" #include "context.h" +#include <key.h> #include <QByteArray> +#include <QMetaObject> +#include <QProcess> #include <QStringList> #include <gpg-error.h> #include <assert.h> -QGpgME::QGpgMERefreshKeysJob::QGpgMERefreshKeysJob() +using namespace QGpgME; + +QGpgMERefreshSMIMEKeysJob::QGpgMERefreshSMIMEKeysJob() : RefreshKeysJob(nullptr), mProcess(nullptr), mError(0) @@ -60,12 +66,12 @@ QGpgME::QGpgMERefreshKeysJob::QGpgMERefreshKeysJob() } -QGpgME::QGpgMERefreshKeysJob::~QGpgMERefreshKeysJob() +QGpgMERefreshSMIMEKeysJob::~QGpgMERefreshSMIMEKeysJob() { } -GpgME::Error QGpgME::QGpgMERefreshKeysJob::start(const QStringList &patterns) +GpgME::Error QGpgMERefreshSMIMEKeysJob::start(const QStringList &patterns) { assert(mPatternsToDo.empty()); @@ -79,11 +85,31 @@ GpgME::Error QGpgME::QGpgMERefreshKeysJob::start(const QStringList &patterns) return startAProcess(); } +GpgME::Error QGpgMERefreshSMIMEKeysJob::start(const std::vector<GpgME::Key> &keys) +{ + if (keys.empty()) { + QMetaObject::invokeMethod(this, [this]() { + Q_EMIT slotProcessExited(0, QProcess::NormalExit); + }, Qt::QueuedConnection); + return {}; + } + + const bool gotWrongKeys = std::any_of(std::begin(keys), std::end(keys), [](const auto &k) { + return k.protocol() != GpgME::CMS; + }); + if (gotWrongKeys) { + qCDebug(QGPGME_LOG) << "Error: At least one of the keys is not an S/MIME key"; + return GpgME::Error::fromCode(GPG_ERR_INV_VALUE); + } + + return start(toFingerprints(keys)); +} + #if MAX_CMD_LENGTH < 65 + 128 #error MAX_CMD_LENGTH is too low #endif -GpgME::Error QGpgME::QGpgMERefreshKeysJob::startAProcess() +GpgME::Error QGpgMERefreshSMIMEKeysJob::startAProcess() { if (mPatternsToDo.empty()) { return GpgME::Error(); @@ -122,10 +148,12 @@ GpgME::Error QGpgME::QGpgMERefreshKeysJob::startAProcess() connect(mProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(slotProcessExited(int,QProcess::ExitStatus))); - connect(mProcess, SIGNAL(readyReadStandardOutput()), - SLOT(slotStdout())); - connect(mProcess, &QProcess::readyReadStandardError, - this, &QGpgMERefreshKeysJob::slotStderr); + connect(mProcess, &QProcess::readyReadStandardOutput, this, [this]() { + qCDebug(QGPGME_LOG) << "stdout:" << mProcess->readAllStandardOutput(); + }); + connect(mProcess, &QProcess::readyReadStandardError, this, [this]() { + qCDebug(QGPGME_LOG) << "stderr:" << mProcess->readAllStandardError(); + }); mProcess->start(); if (!mProcess->waitForStarted()) { @@ -137,7 +165,7 @@ GpgME::Error QGpgME::QGpgMERefreshKeysJob::startAProcess() } } -void QGpgME::QGpgMERefreshKeysJob::slotCancel() +void QGpgMERefreshSMIMEKeysJob::slotCancel() { if (mProcess) { mProcess->kill(); @@ -146,7 +174,7 @@ void QGpgME::QGpgMERefreshKeysJob::slotCancel() mError = GpgME::Error::fromCode(GPG_ERR_CANCELED, GPG_ERR_SOURCE_GPGSM); } -void QGpgME::QGpgMERefreshKeysJob::slotStatus(QProcess *proc, const QString &type, const QStringList &args) +void QGpgMERefreshSMIMEKeysJob::slotStatus(QProcess *proc, const QString &type, const QStringList &args) { if (proc != mProcess) { return; @@ -204,12 +232,7 @@ void QGpgME::QGpgMERefreshKeysJob::slotStatus(QProcess *proc, const QString &typ } } -void QGpgME::QGpgMERefreshKeysJob::slotStderr() -{ - // implement? or not? -} - -void QGpgME::QGpgMERefreshKeysJob::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) +void QGpgMERefreshSMIMEKeysJob::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) { if (!mError && !mPatternsToDo.empty()) { if (const GpgME::Error err = startAProcess()) { @@ -227,4 +250,4 @@ void QGpgME::QGpgMERefreshKeysJob::slotProcessExited(int exitCode, QProcess::Exi Q_EMIT result(mError); deleteLater(); } -#include "qgpgmerefreshkeysjob.moc" +#include "qgpgmerefreshsmimekeysjob.moc" diff --git a/lang/qt/src/qgpgmerefreshkeysjob.h b/lang/qt/src/qgpgmerefreshsmimekeysjob.h index 4dfd942..b5b70e9 100644 --- a/lang/qt/src/qgpgmerefreshkeysjob.h +++ b/lang/qt/src/qgpgmerefreshsmimekeysjob.h @@ -1,5 +1,5 @@ /* - qgpgmerefreshkeysjob.h + qgpgmerefreshsmimekeysjob.h This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004 Klarälvdalens Datakonsult AB @@ -32,8 +32,8 @@ your version. */ -#ifndef __QGPGME_QGPGMEREFRESHKEYSJOB_H__ -#define __QGPGME_QGPGMEREFRESHKEYSJOB_H__ +#ifndef __QGPGME_QGPGMEREFRESHSMIMEKEYSJOB_H__ +#define __QGPGME_QGPGMEREFRESHSMIMEKEYSJOB_H__ #include "refreshkeysjob.h" #ifdef BUILDING_QGPGME @@ -48,22 +48,23 @@ namespace QGpgME { -class QGpgMERefreshKeysJob : public RefreshKeysJob +class QGpgMERefreshSMIMEKeysJob : public RefreshKeysJob { Q_OBJECT public: - QGpgMERefreshKeysJob(); - ~QGpgMERefreshKeysJob(); + QGpgMERefreshSMIMEKeysJob(); + ~QGpgMERefreshSMIMEKeysJob(); /* from RefreshKeysJob */ GpgME::Error start(const QStringList &patterns) Q_DECL_OVERRIDE; + GpgME::Error start(const std::vector<GpgME::Key> &keys) override; + private Q_SLOTS: /* from Job */ void slotCancel() Q_DECL_OVERRIDE; void slotStatus(QProcess *, const QString &, const QStringList &); - void slotStderr(); void slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus); private: @@ -77,4 +78,4 @@ private: } -#endif // __QGPGME_QGPGMEREFRESHKEYSJOB_H__ +#endif // __QGPGME_QGPGMEREFRESHSMIMEKEYSJOB_H__ diff --git a/lang/qt/src/qgpgmerevokekeyjob.cpp b/lang/qt/src/qgpgmerevokekeyjob.cpp new file mode 100644 index 0000000..8a2c224 --- /dev/null +++ b/lang/qt/src/qgpgmerevokekeyjob.cpp @@ -0,0 +1,128 @@ +/* + qgpgmerevokekeyjob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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 "qgpgmerevokekeyjob.h" + +#include "dataprovider.h" + +#include <context.h> +#include <data.h> +#include <gpgrevokekeyeditinteractor.h> +#include <key.h> + +#include <gpg-error.h> + +#include "qgpgme_debug.h" + +using namespace QGpgME; +using namespace GpgME; + +QGpgMERevokeKeyJob::QGpgMERevokeKeyJob(Context *context) + : mixin_type{context} +{ + lateInitialization(); +} + +QGpgMERevokeKeyJob::~QGpgMERevokeKeyJob() = default; + + +static Error check_arguments(const Key &key, + RevocationReason reason, + const std::vector<std::string> &description) +{ + if (key.isNull()) { + qWarning(QGPGME_LOG) << "Error: Key is null key"; + return Error::fromCode(GPG_ERR_INV_ARG); + } + if (reason < RevocationReason::Unspecified || reason > RevocationReason::NoLongerUsed) { + qWarning(QGPGME_LOG) << "Error: Invalid revocation reason" << static_cast<int>(reason); + return Error::fromCode(GPG_ERR_INV_VALUE); + } + if (std::any_of(std::begin(description), std::end(description), + [](const std::string &line) { + return line.empty() || line.find('\n') != std::string::npos; + })) { + qWarning(QGPGME_LOG) << "Error: Revocation description contains empty lines or lines with endline characters"; + return Error::fromCode(GPG_ERR_INV_VALUE); + } + return {}; +} + +static QGpgMERevokeKeyJob::result_type revoke_key(Context *ctx, const Key &key, + RevocationReason reason, + const std::vector<std::string> &description) +{ + std::unique_ptr<GpgRevokeKeyEditInteractor> interactor{new GpgRevokeKeyEditInteractor}; + interactor->setReason(reason, description); + + QGpgME::QByteArrayDataProvider dp; + Data outData(&dp); + assert(!outData.isNull()); + + ctx->setFlag("extended-edit", "1"); + + const Error err = ctx->edit(key, std::unique_ptr<EditInteractor>(interactor.release()), outData); + Error ae; + const QString log = _detail::audit_log_as_html(ctx, ae); + return std::make_tuple(err, log, ae); +} + +Error QGpgMERevokeKeyJob::start(const GpgME::Key &key, + GpgME::RevocationReason reason, + const std::vector<std::string> &description) +{ + Error err = check_arguments(key, reason, description); + if (!err) { + run(std::bind(&revoke_key, std::placeholders::_1, key, reason, description)); + } + return err; +} + +Error QGpgMERevokeKeyJob::exec(const GpgME::Key &key, + GpgME::RevocationReason reason, + const std::vector<std::string> &description) +{ + Error err = check_arguments(key, reason, description); + if (!err) { + const result_type r = revoke_key(context(), key, reason, description); + resultHook(r); + err = std::get<0>(r); + } + return err; +} + +#include "qgpgmerevokekeyjob.moc" diff --git a/lang/qt/src/qgpgmerevokekeyjob.h b/lang/qt/src/qgpgmerevokekeyjob.h new file mode 100644 index 0000000..0eba5cb --- /dev/null +++ b/lang/qt/src/qgpgmerevokekeyjob.h @@ -0,0 +1,70 @@ +/* + qgpgmerevokekeyjob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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. +*/ + +#ifndef __QGPGME_QGPGMEREVOKEKEYJOB_H__ +#define __QGPGME_QGPGMEREVOKEKEYJOB_H__ + +#include "threadedjobmixin.h" +#include "revokekeyjob.h" + +namespace QGpgME +{ + +class QGpgMERevokeKeyJob +#ifdef Q_MOC_RUN + : public RevokeKeyJob +#else + : public _detail::ThreadedJobMixin<RevokeKeyJob> +#endif +{ + Q_OBJECT +#ifdef Q_MOC_RUN +public Q_SLOTS: + void slotFinished(); +#endif +public: + explicit QGpgMERevokeKeyJob(GpgME::Context *context); + ~QGpgMERevokeKeyJob() override; + + GpgME::Error start(const GpgME::Key &key, + GpgME::RevocationReason reason = GpgME::RevocationReason::Unspecified, + const std::vector<std::string> &description = {}) override; + + GpgME::Error exec(const GpgME::Key &key, + GpgME::RevocationReason reason = GpgME::RevocationReason::Unspecified, + const std::vector<std::string> &description = {}) override; +}; + +} + +#endif // __QGPGME_QGPGMEREVOKEKEYJOB_H__ diff --git a/lang/qt/src/qgpgmesetprimaryuseridjob.cpp b/lang/qt/src/qgpgmesetprimaryuseridjob.cpp new file mode 100644 index 0000000..32da1fc --- /dev/null +++ b/lang/qt/src/qgpgmesetprimaryuseridjob.cpp @@ -0,0 +1,75 @@ +/* + qgpgmesetprimaryuseridjob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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 "qgpgmesetprimaryuseridjob.h" + +#include "util.h" + +#include <engineinfo.h> + +using namespace QGpgME; +using namespace GpgME; + +static bool quickSetPrimayUidSupportsUidHash() +{ + return GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.3.8"; +} + +static QGpgMESetPrimaryUserIDJob::result_type set_primary_userid(Context *ctx, const GpgME::UserID &userId) +{ + auto err = ctx->setPrimaryUid(userId.parent(), quickSetPrimayUidSupportsUidHash() ? userId.uidhash() : userId.id()); + return std::make_tuple(err, QString(), Error()); +} + +QGpgMESetPrimaryUserIDJob::QGpgMESetPrimaryUserIDJob(Context *context) + : mixin_type{context} +{ + lateInitialization(); +} + +QGpgMESetPrimaryUserIDJob::~QGpgMESetPrimaryUserIDJob() = default; + +GpgME::Error QGpgMESetPrimaryUserIDJob::start(const GpgME::UserID &userId) +{ + if (userId.isNull()) { + return Error{make_error(GPG_ERR_INV_ARG)}; + } + run([userId](Context *ctx) { return set_primary_userid(ctx, userId); }); + return {}; +} + +#include "qgpgmesetprimaryuseridjob.moc" diff --git a/lang/qt/src/qgpgmesetprimaryuseridjob.h b/lang/qt/src/qgpgmesetprimaryuseridjob.h new file mode 100644 index 0000000..4ee967a --- /dev/null +++ b/lang/qt/src/qgpgmesetprimaryuseridjob.h @@ -0,0 +1,64 @@ +/* + qgpgmesetprimaryuseridjob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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. +*/ + +#ifndef __QGPGME_QGPGMESETPRIMARYUSERIDJOB_H__ +#define __QGPGME_QGPGMESETPRIMARYUSERIDJOB_H__ + +#include "setprimaryuseridjob.h" +#include "threadedjobmixin.h" + +namespace QGpgME +{ + +class QGpgMESetPrimaryUserIDJob +#ifdef Q_MOC_RUN + : public SetPrimaryUserIDJob +#else + : public _detail::ThreadedJobMixin<SetPrimaryUserIDJob> +#endif +{ + Q_OBJECT +#ifdef Q_MOC_RUN +public Q_SLOTS: + void slotFinished(); +#endif +public: + explicit QGpgMESetPrimaryUserIDJob(GpgME::Context *context); + ~QGpgMESetPrimaryUserIDJob() override; + + GpgME::Error start(const GpgME::UserID &userId) override; +}; + +} + +#endif // __QGPGME_QGPGMESETPRIMARYUSERIDJOB_H__ diff --git a/lang/qt/src/qgpgmesignencryptjob.cpp b/lang/qt/src/qgpgmesignencryptjob.cpp index 284c110..5466c54 100644 --- a/lang/qt/src/qgpgmesignencryptjob.cpp +++ b/lang/qt/src/qgpgmesignencryptjob.cpp @@ -5,6 +5,8 @@ Copyright (c) 2004, 2007 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -46,7 +48,7 @@ #include "exception.h" #include <QBuffer> - +#include <QFileInfo> #include <cassert> @@ -69,7 +71,7 @@ void QGpgMESignEncryptJob::setOutputIsBase64Encoded(bool on) static QGpgMESignEncryptJob::result_type sign_encrypt(Context *ctx, QThread *thread, const std::vector<Key> &signers, const std::vector<Key> &recipients, const std::weak_ptr<QIODevice> &plainText_, - const std::weak_ptr<QIODevice> &cipherText_, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded) + const std::weak_ptr<QIODevice> &cipherText_, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded, const QString &fileName) { const std::shared_ptr<QIODevice> &plainText = plainText_.lock(); const std::shared_ptr<QIODevice> &cipherText = cipherText_.lock(); @@ -78,7 +80,12 @@ static QGpgMESignEncryptJob::result_type sign_encrypt(Context *ctx, QThread *thr const _detail::ToThreadMover ptMover(plainText, thread); QGpgME::QIODeviceDataProvider in(plainText); - const Data indata(&in); + Data indata(&in); + + const auto pureFileName = QFileInfo{fileName}.fileName().toStdString(); + if (!pureFileName.empty()) { + indata.setFileName(pureFileName.c_str()); + } ctx->clearSigningKeys(); Q_FOREACH (const Key &signer, signers) @@ -116,26 +123,26 @@ static QGpgMESignEncryptJob::result_type sign_encrypt(Context *ctx, QThread *thr } static QGpgMESignEncryptJob::result_type sign_encrypt_qba(Context *ctx, const std::vector<Key> &signers, - const std::vector<Key> &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded) + const std::vector<Key> &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded, const QString &fileName) { const std::shared_ptr<QBuffer> buffer(new QBuffer); buffer->setData(plainText); if (!buffer->open(QIODevice::ReadOnly)) { assert(!"This should never happen: QBuffer::open() failed"); } - return sign_encrypt(ctx, nullptr, signers, recipients, buffer, std::shared_ptr<QIODevice>(), eflags, outputIsBsse64Encoded); + return sign_encrypt(ctx, nullptr, signers, recipients, buffer, std::shared_ptr<QIODevice>(), eflags, outputIsBsse64Encoded, fileName); } Error QGpgMESignEncryptJob::start(const std::vector<Key> &signers, const std::vector<Key> &recipients, const QByteArray &plainText, bool alwaysTrust) { - run(std::bind(&sign_encrypt_qba, std::placeholders::_1, signers, recipients, plainText, alwaysTrust ? Context::AlwaysTrust : Context::None, mOutputIsBase64Encoded)); + run(std::bind(&sign_encrypt_qba, std::placeholders::_1, signers, recipients, plainText, alwaysTrust ? Context::AlwaysTrust : Context::None, mOutputIsBase64Encoded, fileName())); return Error(); } void QGpgMESignEncryptJob::start(const std::vector<Key> &signers, const std::vector<Key> &recipients, const std::shared_ptr<QIODevice> &plainText, const std::shared_ptr<QIODevice> &cipherText, const Context::EncryptionFlags eflags) { - run(std::bind(&sign_encrypt, std::placeholders::_1, std::placeholders::_2, signers, recipients, std::placeholders::_3, std::placeholders::_4, eflags, mOutputIsBase64Encoded), plainText, cipherText); + run(std::bind(&sign_encrypt, std::placeholders::_1, std::placeholders::_2, signers, recipients, std::placeholders::_3, std::placeholders::_4, eflags, mOutputIsBase64Encoded, fileName()), plainText, cipherText); } void QGpgMESignEncryptJob::start(const std::vector<Key> &signers, const std::vector<Key> &recipients, const std::shared_ptr<QIODevice> &plainText, const std::shared_ptr<QIODevice> &cipherText, bool alwaysTrust) @@ -145,7 +152,7 @@ void QGpgMESignEncryptJob::start(const std::vector<Key> &signers, const std::vec std::pair<SigningResult, EncryptionResult> QGpgMESignEncryptJob::exec(const std::vector<Key> &signers, const std::vector<Key> &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, QByteArray &cipherText) { - const result_type r = sign_encrypt_qba(context(), signers, recipients, plainText, eflags, mOutputIsBase64Encoded); + const result_type r = sign_encrypt_qba(context(), signers, recipients, plainText, eflags, mOutputIsBase64Encoded, fileName()); cipherText = std::get<2>(r); resultHook(r); return mResult; diff --git a/lang/qt/src/qgpgmesignkeyjob.cpp b/lang/qt/src/qgpgmesignkeyjob.cpp index 5036a9b..506d64a 100644 --- a/lang/qt/src/qgpgmesignkeyjob.cpp +++ b/lang/qt/src/qgpgmesignkeyjob.cpp @@ -127,11 +127,11 @@ static QGpgMESignKeyJob::result_type sign_key(Context *ctx, const Key &key, cons if (expirationDate.isValid()) { // on 2106-02-07, the Unix time will reach 0xFFFFFFFF; since gpg uses uint32 internally - // for the expiration date clip it at 2106-02-06 - static const QDate maxAllowedDate{2106, 2, 6}; + // for the expiration date clip it at 2106-02-05 to avoid problems with negative time zones + static const QDate maxAllowedDate{2106, 2, 5}; const auto clippedExpirationDate = expirationDate <= maxAllowedDate ? expirationDate : maxAllowedDate; if (clippedExpirationDate != expirationDate) { - qCWarning(QGPGME_LOG) << "Expiration of certification has been changed to" << clippedExpirationDate; + qCDebug(QGPGME_LOG) << "Expiration of certification has been changed to" << clippedExpirationDate; } // use the "days from now" format to specify the expiration date of the certification; // this format is the most appropriate regardless of the local timezone diff --git a/lang/qt/src/refreshkeysjob.h b/lang/qt/src/refreshkeysjob.h index c4ba74a..67774c2 100644 --- a/lang/qt/src/refreshkeysjob.h +++ b/lang/qt/src/refreshkeysjob.h @@ -38,6 +38,8 @@ #include "job.h" #include "qgpgme_export.h" +#include <QtCore/QStringList> + #include <vector> namespace GpgME @@ -46,8 +48,6 @@ class Error; class Key; } -class QStringList; - namespace QGpgME { @@ -61,7 +61,7 @@ namespace QGpgME RefreshKeysJob instance will have scheduled its own destruction with a call to QObject::deleteLater(). - After result() is emitted, the KeyListJob will schedule it's own + After result() is emitted, the job will schedule it's own destruction by calling QObject::deleteLater(). */ class QGPGME_EXPORT RefreshKeysJob : public Job @@ -73,21 +73,22 @@ public: ~RefreshKeysJob(); /** - Starts the keylist operation. \a pattern is a list of patterns + Starts the refresh operation. \a pattern is a list of patterns used to restrict the list of keys returned. Empty patterns are ignored. If \a pattern is empty or contains only empty strings, - all keys are returned (however, the backend is free to truncate - the result and should do so; when this happens, it will be - reported by the reult object). + all keys are refreshed. - If \a secretOnly is true, only keys for which the secret key is - also available are returned. Use this if you need to select a - key for signing. + Only implemented for S/MIME. */ virtual GpgME::Error start(const QStringList &patterns) = 0; + /** + Starts a refresh of the \a keys. + */ + virtual GpgME::Error start(const std::vector<GpgME::Key> &keys) = 0; + Q_SIGNALS: - void result(const GpgME::Error &error); + void result(const GpgME::Error &result); }; } diff --git a/lang/qt/src/revokekeyjob.h b/lang/qt/src/revokekeyjob.h new file mode 100644 index 0000000..69aef06 --- /dev/null +++ b/lang/qt/src/revokekeyjob.h @@ -0,0 +1,86 @@ +/* + revokekeyjob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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. +*/ + +#ifndef __QGPGME_REVOKEKEYJOB_H__ +#define __QGPGME_REVOKEKEYJOB_H__ + +#include "job.h" +#include "qgpgme_export.h" + +class QString; + +namespace GpgME +{ +class Error; +class Key; +} + +namespace QGpgME +{ + +class QGPGME_EXPORT RevokeKeyJob : public Job +{ + Q_OBJECT +protected: + explicit RevokeKeyJob(QObject *parent); + +public: + ~RevokeKeyJob(); + + /** + Starts the operation. \a key is the key to revoke with reason \a reason and + optional description \a description. The individual elements of \a description + must be non-empty strings and they must not contain any endline characters. + + The job deletes itself after it has completed the operation. + */ + virtual GpgME::Error start(const GpgME::Key &key, + GpgME::RevocationReason reason = GpgME::RevocationReason::Unspecified, + const std::vector<std::string> &description = {}) = 0; + + /** + Runs the operation. \a key is the key to revoke with reason \a reason and + optional description \a description. The individual elements of \a description + must be non-empty strings and they must not contain any endline characters. + */ + virtual GpgME::Error exec(const GpgME::Key &key, + GpgME::RevocationReason reason = GpgME::RevocationReason::Unspecified, + const std::vector<std::string> &description = {}) = 0; + +Q_SIGNALS: + void result(const GpgME::Error &result, const QString &auditLogAsHtml = {}, const GpgME::Error &auditLogError = {}); +}; + +} + +#endif // __QGPGME_REVOKEKEYJOB_H__ diff --git a/lang/qt/src/setprimaryuseridjob.h b/lang/qt/src/setprimaryuseridjob.h new file mode 100644 index 0000000..fa76199 --- /dev/null +++ b/lang/qt/src/setprimaryuseridjob.h @@ -0,0 +1,69 @@ +/* + setprimaryuseridjob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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. +*/ + +#ifndef __QGPGME_SETPRIMARYUSERIDJOB_H__ +#define __QGPGME_SETPRIMARYUSERIDJOB_H__ + +#include "job.h" + +#include "qgpgme_export.h" + +namespace GpgME +{ +class Error; +class UserID; +} + +namespace QGpgME +{ + +class QGPGME_EXPORT SetPrimaryUserIDJob : public Job +{ + Q_OBJECT +public: + explicit SetPrimaryUserIDJob(QObject *parent); + ~SetPrimaryUserIDJob() override; + + /** + * Starts setting user ID \a userId as the primary user ID. + */ + virtual GpgME::Error start(const GpgME::UserID &userId) = 0; + +Q_SIGNALS: + void result(const GpgME::Error &error, + const QString &auditLogAsHtml = QString(), const GpgME::Error &auditLogError = GpgME::Error()); +}; + +} + +#endif // __QGPGME_SETPRIMARYUSERIDJOB_H__ diff --git a/lang/qt/src/signencryptjob.cpp b/lang/qt/src/signencryptjob.cpp new file mode 100644 index 0000000..aa02fca --- /dev/null +++ b/lang/qt/src/signencryptjob.cpp @@ -0,0 +1,61 @@ +/* + signencryptjob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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 "signencryptjob.h" +#include "job_p.h" + +using namespace QGpgME; + +namespace +{ +struct SignEncryptJobPrivate : public JobPrivate +{ + QString m_fileName; +}; +} + +void SignEncryptJob::setFileName(const QString &fileName) +{ + auto d = jobPrivate<SignEncryptJobPrivate>(this); + d->m_fileName = fileName; +} + +QString SignEncryptJob::fileName() const +{ + auto d = jobPrivate<SignEncryptJobPrivate>(this); + return d->m_fileName; +} diff --git a/lang/qt/src/signencryptjob.h b/lang/qt/src/signencryptjob.h index 61ab5c6..ebb866d 100644 --- a/lang/qt/src/signencryptjob.h +++ b/lang/qt/src/signencryptjob.h @@ -5,6 +5,8 @@ Copyright (c) 2004, 2007 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -85,6 +87,9 @@ protected: public: ~SignEncryptJob(); + void setFileName(const QString &fileName); + QString fileName() const; + /** Starts the combined signing and encrypting operation. \a signers is the list of keys to sign \a plainText with. \a recipients is @@ -106,8 +111,6 @@ public: If \a cipherText is non-null, the ciphertext is written there. Otherwise, it will be delivered in the third argument of result(). - - \throws GpgME::Exception if starting fails */ virtual void start(const std::vector<GpgME::Key> &signers, const std::vector<GpgME::Key> &recipients, diff --git a/lang/qt/src/signjob.h b/lang/qt/src/signjob.h index 57d2d17..c05231c 100644 --- a/lang/qt/src/signjob.h +++ b/lang/qt/src/signjob.h @@ -95,8 +95,6 @@ public: If \a signature is non-null the signature is written there. Otherwise, it will be delivered in the second argument of result(). - - \throws GpgME::Exception if starting fails */ virtual void start(const std::vector<GpgME::Key> &signers, const std::shared_ptr<QIODevice> &plainText, diff --git a/lang/qt/src/signkeyjob.h b/lang/qt/src/signkeyjob.h index f4b3ed8..d0e90c2 100644 --- a/lang/qt/src/signkeyjob.h +++ b/lang/qt/src/signkeyjob.h @@ -149,7 +149,7 @@ public: * Sets the expiration date of the key signature to @a expiration. By default, * key signatures do not expire. * - * Note: Expiration dates after 2106-02-06 will be set to 2106-02-06. + * Note: Expiration dates after 2106-02-05 will be set to 2106-02-05. * * Not pure virtual for ABI compatibility. **/ diff --git a/lang/qt/src/util.cpp b/lang/qt/src/util.cpp index 3d10e2f..66c7eed 100644 --- a/lang/qt/src/util.cpp +++ b/lang/qt/src/util.cpp @@ -31,9 +31,17 @@ your version. */ +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + #include "util.h" #include <QStringList> + +#include <key.h> + +#include <algorithm> #include <functional> std::vector<std::string> toStrings(const QStringList &l) @@ -45,3 +53,13 @@ std::vector<std::string> toStrings(const QStringList &l) std::mem_fn(&QString::toStdString)); return v; } + +QStringList toFingerprints(const std::vector<GpgME::Key> &keys) +{ + QStringList fprs; + fprs.reserve(keys.size()); + std::transform(std::begin(keys), std::end(keys), std::back_inserter(fprs), [](const auto &k) { + return QString::fromLatin1(k.primaryFingerprint()); + }); + return fprs; +} diff --git a/lang/qt/src/util.h b/lang/qt/src/util.h index 94c9733..6aba62e 100644 --- a/lang/qt/src/util.h +++ b/lang/qt/src/util.h @@ -36,11 +36,17 @@ #include <gpgme.h> +#include <sstream> #include <string> #include <vector> class QStringList; +namespace GpgME +{ +class Key; +} + static inline gpgme_error_t make_error(gpgme_err_code_t code) { return gpgme_err_make((gpgme_err_source_t)22, code); @@ -48,4 +54,14 @@ static inline gpgme_error_t make_error(gpgme_err_code_t code) std::vector<std::string> toStrings(const QStringList &l); +QStringList toFingerprints(const std::vector<GpgME::Key> &keys); + +template<class Result> +std::string toLogString(const Result &result) +{ + std::stringstream stream; + stream << result; + return stream.str(); +} + #endif // __QGPGME_UTIL_H__ diff --git a/lang/qt/src/verifydetachedjob.h b/lang/qt/src/verifydetachedjob.h index 2293f3a..12fdfbb 100644 --- a/lang/qt/src/verifydetachedjob.h +++ b/lang/qt/src/verifydetachedjob.h @@ -81,11 +81,6 @@ public: virtual GpgME::Error start(const QByteArray &signature, const QByteArray &signedData) = 0; - /*! - \overload - - \throws GpgME::Exception if starting fails. - */ virtual void start(const std::shared_ptr<QIODevice> &signature, const std::shared_ptr<QIODevice> &signedData) = 0; virtual GpgME::VerificationResult exec(const QByteArray &signature, diff --git a/lang/qt/src/verifyopaquejob.h b/lang/qt/src/verifyopaquejob.h index bfa34e9..c9b2247 100644 --- a/lang/qt/src/verifyopaquejob.h +++ b/lang/qt/src/verifyopaquejob.h @@ -85,8 +85,6 @@ public: If \a plainText is non-null, the plaintext is written there. Otherwise, it will be delivered in the second argument of result(). - - \throws GpgME::Exception if starting fails */ virtual void start(const std::shared_ptr<QIODevice> &signedData, const std::shared_ptr<QIODevice> &plainText = std::shared_ptr<QIODevice>()) = 0; diff --git a/lang/qt/src/wkspublishjob.h b/lang/qt/src/wkspublishjob.h index ff3f21e..2b4daf5 100644 --- a/lang/qt/src/wkspublishjob.h +++ b/lang/qt/src/wkspublishjob.h @@ -52,11 +52,9 @@ namespace QGpgME { * The workflow is to call startCreate, check for errors and then * send the RFC822 mail returned in returnedData. * - * When the response is received start a startRecieve with the + * When the response is received call startReceive with the * RFC822 mail received as parameter response. Check for errors - * and then send again send the result from returnedData back to - * the server. - * + * and then send the result from returnedData back to the server. */ class QGPGME_EXPORT WKSPublishJob: public Job { @@ -66,7 +64,6 @@ protected: public: ~WKSPublishJob(); - /** Start a check if WKS Publishing is supported. As this involves * an HTTP Query it might take a while. Returns GPG_ERR_NOT_SUPPORED * result if GnuPG is too old or the required tools are not installed. diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am index 6c082b0..5724a2d 100644 --- a/lang/qt/tests/Makefile.am +++ b/lang/qt/tests/Makefile.am @@ -30,7 +30,7 @@ the_tests = \ t-addexistingsubkey \ t-keylist t-keylocate t-ownertrust t-tofuinfo \ t-encrypt t-verify t-various t-config t-remarks t-trustsignatures \ - t-changeexpiryjob t-wkdlookup t-import + t-changeexpiryjob t-wkdlookup t-import t-revokekey t-setprimaryuserid TESTS = initial.test $(the_tests) final.test @@ -39,7 +39,8 @@ 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-various.moc t-config.moc t-remarks.moc t-trustsignatures.moc \ - t-changeexpiryjob.moc t-wkdlookup.moc t-import.moc + t-changeexpiryjob.moc t-wkdlookup.moc t-import.moc t-revokekey.moc \ + t-setprimaryuserid.moc AM_LDFLAGS = -no-install @@ -70,10 +71,13 @@ t_trustsignatures_SOURCES = t-trustsignatures.cpp $(support_src) t_changeexpiryjob_SOURCES = t-changeexpiryjob.cpp $(support_src) t_wkdlookup_SOURCES = t-wkdlookup.cpp $(support_src) t_import_SOURCES = t-import.cpp $(support_src) +t_revokekey_SOURCES = t-revokekey.cpp $(support_src) +t_setprimaryuserid_SOURCES = t-setprimaryuserid.cpp $(support_src) run_exportjob_SOURCES = run-exportjob.cpp run_importjob_SOURCES = run-importjob.cpp run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp run_receivekeysjob_SOURCES = run-receivekeysjob.cpp +run_refreshkeysjob_SOURCES = run-refreshkeysjob.cpp nodist_t_keylist_SOURCES = $(moc_files) @@ -83,8 +87,9 @@ noinst_PROGRAMS = \ t-addexistingsubkey \ t-keylist t-keylocate t-ownertrust t-tofuinfo t-encrypt \ run-keyformailboxjob t-wkspublish t-verify t-various t-config t-remarks \ - t-trustsignatures t-changeexpiryjob t-wkdlookup t-import run-importjob \ - run-exportjob run-receivekeysjob + t-trustsignatures t-changeexpiryjob t-wkdlookup t-import t-revokekey \ + t-setprimaryuserid \ + run-importjob run-exportjob run-receivekeysjob run-refreshkeysjob CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \ diff --git a/lang/qt/tests/Makefile.in b/lang/qt/tests/Makefile.in index 89fad15..1c4b87a 100644 --- a/lang/qt/tests/Makefile.in +++ b/lang/qt/tests/Makefile.in @@ -114,11 +114,14 @@ noinst_PROGRAMS = t-addexistingsubkey$(EXEEXT) t-keylist$(EXEEXT) \ t-wkspublish$(EXEEXT) t-verify$(EXEEXT) t-various$(EXEEXT) \ t-config$(EXEEXT) t-remarks$(EXEEXT) \ t-trustsignatures$(EXEEXT) t-changeexpiryjob$(EXEEXT) \ - t-wkdlookup$(EXEEXT) t-import$(EXEEXT) run-importjob$(EXEEXT) \ - run-exportjob$(EXEEXT) run-receivekeysjob$(EXEEXT) + t-wkdlookup$(EXEEXT) t-import$(EXEEXT) t-revokekey$(EXEEXT) \ + t-setprimaryuserid$(EXEEXT) run-importjob$(EXEEXT) \ + run-exportjob$(EXEEXT) run-receivekeysjob$(EXEEXT) \ + run-refreshkeysjob$(EXEEXT) subdir = lang/qt/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ $(top_srcdir)/m4/ax_pkg_swig.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \ @@ -161,6 +164,11 @@ run_receivekeysjob_OBJECTS = $(am_run_receivekeysjob_OBJECTS) run_receivekeysjob_LDADD = $(LDADD) run_receivekeysjob_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \ ../src/libqgpgme.la ../../../src/libgpgme.la +am_run_refreshkeysjob_OBJECTS = run-refreshkeysjob.$(OBJEXT) +run_refreshkeysjob_OBJECTS = $(am_run_refreshkeysjob_OBJECTS) +run_refreshkeysjob_LDADD = $(LDADD) +run_refreshkeysjob_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \ + ../src/libqgpgme.la ../../../src/libgpgme.la am__objects_1 = t-support.$(OBJEXT) am_t_addexistingsubkey_OBJECTS = t-addexistingsubkey.$(OBJEXT) \ $(am__objects_1) @@ -212,6 +220,17 @@ t_remarks_OBJECTS = $(am_t_remarks_OBJECTS) t_remarks_LDADD = $(LDADD) t_remarks_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \ ../src/libqgpgme.la ../../../src/libgpgme.la +am_t_revokekey_OBJECTS = t-revokekey.$(OBJEXT) $(am__objects_1) +t_revokekey_OBJECTS = $(am_t_revokekey_OBJECTS) +t_revokekey_LDADD = $(LDADD) +t_revokekey_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \ + ../src/libqgpgme.la ../../../src/libgpgme.la +am_t_setprimaryuserid_OBJECTS = t-setprimaryuserid.$(OBJEXT) \ + $(am__objects_1) +t_setprimaryuserid_OBJECTS = $(am_t_setprimaryuserid_OBJECTS) +t_setprimaryuserid_LDADD = $(LDADD) +t_setprimaryuserid_DEPENDENCIES = ../../cpp/src/libgpgmepp.la \ + ../src/libqgpgme.la ../../../src/libgpgme.la am_t_tofuinfo_OBJECTS = t-tofuinfo.$(OBJEXT) $(am__objects_1) t_tofuinfo_OBJECTS = $(am_t_tofuinfo_OBJECTS) t_tofuinfo_LDADD = $(LDADD) @@ -262,11 +281,13 @@ am__depfiles_remade = ./$(DEPDIR)/run-exportjob.Po \ ./$(DEPDIR)/run-importjob.Po \ ./$(DEPDIR)/run-keyformailboxjob.Po \ ./$(DEPDIR)/run-receivekeysjob.Po \ + ./$(DEPDIR)/run-refreshkeysjob.Po \ ./$(DEPDIR)/t-addexistingsubkey.Po \ ./$(DEPDIR)/t-changeexpiryjob.Po ./$(DEPDIR)/t-config.Po \ ./$(DEPDIR)/t-encrypt.Po ./$(DEPDIR)/t-import.Po \ ./$(DEPDIR)/t-keylist.Po ./$(DEPDIR)/t-keylocate.Po \ ./$(DEPDIR)/t-ownertrust.Po ./$(DEPDIR)/t-remarks.Po \ + ./$(DEPDIR)/t-revokekey.Po ./$(DEPDIR)/t-setprimaryuserid.Po \ ./$(DEPDIR)/t-support.Po ./$(DEPDIR)/t-tofuinfo.Po \ ./$(DEPDIR)/t-trustsignatures.Po ./$(DEPDIR)/t-various.Po \ ./$(DEPDIR)/t-verify.Po ./$(DEPDIR)/t-wkdlookup.Po \ @@ -310,23 +331,26 @@ am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(run_exportjob_SOURCES) $(run_importjob_SOURCES) \ $(run_keyformailboxjob_SOURCES) $(run_receivekeysjob_SOURCES) \ - $(t_addexistingsubkey_SOURCES) $(t_changeexpiryjob_SOURCES) \ - $(t_config_SOURCES) $(t_encrypt_SOURCES) $(t_import_SOURCES) \ - $(t_keylist_SOURCES) $(nodist_t_keylist_SOURCES) \ - $(t_keylocate_SOURCES) $(t_ownertrust_SOURCES) \ - $(t_remarks_SOURCES) $(t_tofuinfo_SOURCES) \ - $(t_trustsignatures_SOURCES) $(t_various_SOURCES) \ - $(t_verify_SOURCES) $(t_wkdlookup_SOURCES) \ - $(t_wkspublish_SOURCES) -DIST_SOURCES = $(run_exportjob_SOURCES) $(run_importjob_SOURCES) \ - $(run_keyformailboxjob_SOURCES) $(run_receivekeysjob_SOURCES) \ - $(t_addexistingsubkey_SOURCES) $(t_changeexpiryjob_SOURCES) \ - $(t_config_SOURCES) $(t_encrypt_SOURCES) $(t_import_SOURCES) \ - $(t_keylist_SOURCES) $(t_keylocate_SOURCES) \ + $(run_refreshkeysjob_SOURCES) $(t_addexistingsubkey_SOURCES) \ + $(t_changeexpiryjob_SOURCES) $(t_config_SOURCES) \ + $(t_encrypt_SOURCES) $(t_import_SOURCES) $(t_keylist_SOURCES) \ + $(nodist_t_keylist_SOURCES) $(t_keylocate_SOURCES) \ $(t_ownertrust_SOURCES) $(t_remarks_SOURCES) \ + $(t_revokekey_SOURCES) $(t_setprimaryuserid_SOURCES) \ $(t_tofuinfo_SOURCES) $(t_trustsignatures_SOURCES) \ $(t_various_SOURCES) $(t_verify_SOURCES) \ $(t_wkdlookup_SOURCES) $(t_wkspublish_SOURCES) +DIST_SOURCES = $(run_exportjob_SOURCES) $(run_importjob_SOURCES) \ + $(run_keyformailboxjob_SOURCES) $(run_receivekeysjob_SOURCES) \ + $(run_refreshkeysjob_SOURCES) $(t_addexistingsubkey_SOURCES) \ + $(t_changeexpiryjob_SOURCES) $(t_config_SOURCES) \ + $(t_encrypt_SOURCES) $(t_import_SOURCES) $(t_keylist_SOURCES) \ + $(t_keylocate_SOURCES) $(t_ownertrust_SOURCES) \ + $(t_remarks_SOURCES) $(t_revokekey_SOURCES) \ + $(t_setprimaryuserid_SOURCES) $(t_tofuinfo_SOURCES) \ + $(t_trustsignatures_SOURCES) $(t_various_SOURCES) \ + $(t_verify_SOURCES) $(t_wkdlookup_SOURCES) \ + $(t_wkspublish_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -378,7 +402,8 @@ am__EXEEXT_1 = t-addexistingsubkey$(EXEEXT) t-keylist$(EXEEXT) \ t-encrypt$(EXEEXT) t-verify$(EXEEXT) t-various$(EXEEXT) \ t-config$(EXEEXT) t-remarks$(EXEEXT) \ t-trustsignatures$(EXEEXT) t-changeexpiryjob$(EXEEXT) \ - t-wkdlookup$(EXEEXT) t-import$(EXEEXT) + t-wkdlookup$(EXEEXT) t-import$(EXEEXT) t-revokekey$(EXEEXT) \ + t-setprimaryuserid$(EXEEXT) am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp \ $(top_srcdir)/build-aux/mkinstalldirs @@ -432,6 +457,7 @@ GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@ GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@ GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@ GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@ +GPGME_CPP_CFLAGS = @GPGME_CPP_CFLAGS@ GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@ GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@ GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@ @@ -589,14 +615,15 @@ the_tests = \ t-addexistingsubkey \ t-keylist t-keylocate t-ownertrust t-tofuinfo \ t-encrypt t-verify t-various t-config t-remarks t-trustsignatures \ - t-changeexpiryjob t-wkdlookup t-import + t-changeexpiryjob t-wkdlookup t-import t-revokekey t-setprimaryuserid moc_files = \ t-addexistingsubkey.moc \ 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-various.moc t-config.moc t-remarks.moc t-trustsignatures.moc \ - t-changeexpiryjob.moc t-wkdlookup.moc t-import.moc + t-changeexpiryjob.moc t-wkdlookup.moc t-import.moc t-revokekey.moc \ + t-setprimaryuserid.moc AM_LDFLAGS = -no-install LDADD = ../../cpp/src/libgpgmepp.la ../src/libqgpgme.la \ @@ -625,10 +652,13 @@ t_trustsignatures_SOURCES = t-trustsignatures.cpp $(support_src) t_changeexpiryjob_SOURCES = t-changeexpiryjob.cpp $(support_src) t_wkdlookup_SOURCES = t-wkdlookup.cpp $(support_src) t_import_SOURCES = t-import.cpp $(support_src) +t_revokekey_SOURCES = t-revokekey.cpp $(support_src) +t_setprimaryuserid_SOURCES = t-setprimaryuserid.cpp $(support_src) run_exportjob_SOURCES = run-exportjob.cpp run_importjob_SOURCES = run-importjob.cpp run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp run_receivekeysjob_SOURCES = run-receivekeysjob.cpp +run_refreshkeysjob_SOURCES = run-refreshkeysjob.cpp nodist_t_keylist_SOURCES = $(moc_files) BUILT_SOURCES = $(moc_files) pubring-stamp CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ @@ -696,6 +726,10 @@ run-receivekeysjob$(EXEEXT): $(run_receivekeysjob_OBJECTS) $(run_receivekeysjob_ @rm -f run-receivekeysjob$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(run_receivekeysjob_OBJECTS) $(run_receivekeysjob_LDADD) $(LIBS) +run-refreshkeysjob$(EXEEXT): $(run_refreshkeysjob_OBJECTS) $(run_refreshkeysjob_DEPENDENCIES) $(EXTRA_run_refreshkeysjob_DEPENDENCIES) + @rm -f run-refreshkeysjob$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(run_refreshkeysjob_OBJECTS) $(run_refreshkeysjob_LDADD) $(LIBS) + t-addexistingsubkey$(EXEEXT): $(t_addexistingsubkey_OBJECTS) $(t_addexistingsubkey_DEPENDENCIES) $(EXTRA_t_addexistingsubkey_DEPENDENCIES) @rm -f t-addexistingsubkey$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(t_addexistingsubkey_OBJECTS) $(t_addexistingsubkey_LDADD) $(LIBS) @@ -732,6 +766,14 @@ t-remarks$(EXEEXT): $(t_remarks_OBJECTS) $(t_remarks_DEPENDENCIES) $(EXTRA_t_rem @rm -f t-remarks$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(t_remarks_OBJECTS) $(t_remarks_LDADD) $(LIBS) +t-revokekey$(EXEEXT): $(t_revokekey_OBJECTS) $(t_revokekey_DEPENDENCIES) $(EXTRA_t_revokekey_DEPENDENCIES) + @rm -f t-revokekey$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(t_revokekey_OBJECTS) $(t_revokekey_LDADD) $(LIBS) + +t-setprimaryuserid$(EXEEXT): $(t_setprimaryuserid_OBJECTS) $(t_setprimaryuserid_DEPENDENCIES) $(EXTRA_t_setprimaryuserid_DEPENDENCIES) + @rm -f t-setprimaryuserid$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(t_setprimaryuserid_OBJECTS) $(t_setprimaryuserid_LDADD) $(LIBS) + t-tofuinfo$(EXEEXT): $(t_tofuinfo_OBJECTS) $(t_tofuinfo_DEPENDENCIES) $(EXTRA_t_tofuinfo_DEPENDENCIES) @rm -f t-tofuinfo$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(t_tofuinfo_OBJECTS) $(t_tofuinfo_LDADD) $(LIBS) @@ -766,6 +808,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run-importjob.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run-keyformailboxjob.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run-receivekeysjob.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run-refreshkeysjob.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-addexistingsubkey.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-changeexpiryjob.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-config.Po@am__quote@ # am--include-marker @@ -775,6 +818,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-keylocate.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-ownertrust.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-remarks.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-revokekey.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-setprimaryuserid.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-support.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-tofuinfo.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-trustsignatures.Po@am__quote@ # am--include-marker @@ -1044,6 +1089,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/run-importjob.Po -rm -f ./$(DEPDIR)/run-keyformailboxjob.Po -rm -f ./$(DEPDIR)/run-receivekeysjob.Po + -rm -f ./$(DEPDIR)/run-refreshkeysjob.Po -rm -f ./$(DEPDIR)/t-addexistingsubkey.Po -rm -f ./$(DEPDIR)/t-changeexpiryjob.Po -rm -f ./$(DEPDIR)/t-config.Po @@ -1053,6 +1099,8 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/t-keylocate.Po -rm -f ./$(DEPDIR)/t-ownertrust.Po -rm -f ./$(DEPDIR)/t-remarks.Po + -rm -f ./$(DEPDIR)/t-revokekey.Po + -rm -f ./$(DEPDIR)/t-setprimaryuserid.Po -rm -f ./$(DEPDIR)/t-support.Po -rm -f ./$(DEPDIR)/t-tofuinfo.Po -rm -f ./$(DEPDIR)/t-trustsignatures.Po @@ -1109,6 +1157,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/run-importjob.Po -rm -f ./$(DEPDIR)/run-keyformailboxjob.Po -rm -f ./$(DEPDIR)/run-receivekeysjob.Po + -rm -f ./$(DEPDIR)/run-refreshkeysjob.Po -rm -f ./$(DEPDIR)/t-addexistingsubkey.Po -rm -f ./$(DEPDIR)/t-changeexpiryjob.Po -rm -f ./$(DEPDIR)/t-config.Po @@ -1118,6 +1167,8 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/t-keylocate.Po -rm -f ./$(DEPDIR)/t-ownertrust.Po -rm -f ./$(DEPDIR)/t-remarks.Po + -rm -f ./$(DEPDIR)/t-revokekey.Po + -rm -f ./$(DEPDIR)/t-setprimaryuserid.Po -rm -f ./$(DEPDIR)/t-support.Po -rm -f ./$(DEPDIR)/t-tofuinfo.Po -rm -f ./$(DEPDIR)/t-trustsignatures.Po diff --git a/lang/qt/tests/run-refreshkeysjob.cpp b/lang/qt/tests/run-refreshkeysjob.cpp new file mode 100644 index 0000000..940c0c7 --- /dev/null +++ b/lang/qt/tests/run-refreshkeysjob.cpp @@ -0,0 +1,155 @@ +/* + run-refreshkeysjob.cpp + + This file is part of QGpgME's test suite. + Copyright (c) 2022 by g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + QGpgME is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + 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 <protocol.h> +#include <refreshkeysjob.h> +#include <receivekeysjob.h> + +#include <QCoreApplication> +#include <QDebug> + +#include <context.h> +#include <importresult.h> + +#include <iostream> + +using namespace GpgME; + +std::ostream &operator<<(std::ostream &os, const QString &s) +{ + return os << s.toLocal8Bit().constData(); +} + +const char *displayName(Protocol protocol) +{ + switch (protocol) { + case GpgME::OpenPGP: + return "OpenPGP"; + case GpgME::CMS: + return "S/MIME"; + default: + return "Unknown protocol"; + } +} + +struct KeyAndError { + Key key; + Error error; +}; + +KeyAndError getKey(const QString &keyId, Protocol protocol) +{ + KeyAndError result; + + auto ctx = Context::create(protocol); + if (!ctx) { + result.error = Error::fromCode(GPG_ERR_GENERAL); + return result; + } + + result.key = ctx->key(keyId.toLatin1().constData(), result.error); + if (result.error.code() == GPG_ERR_EOF) { + result.error = Error{}; + } + return result; +} + +int main(int argc, char **argv) +{ + GpgME::initializeLibrary(); + + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " KEYID" << std::endl; + return 1; + } + + QCoreApplication app{argc, argv}; + const auto keyId = qApp->arguments().last(); + + auto openPGPKey = getKey(keyId, GpgME::OpenPGP); + auto smimeKey = getKey(keyId, GpgME::CMS); + if ((!openPGPKey.key.isNull() && !smimeKey.key.isNull()) + || (openPGPKey.error.code() == GPG_ERR_AMBIGUOUS_NAME) + || (smimeKey.error.code() == GPG_ERR_AMBIGUOUS_NAME)) { + std::cerr << "Error: Multiple keys matching '" << keyId << "' found" << std::endl; + return 1; + } else if (openPGPKey.key.isNull() && smimeKey.key.isNull()) { + std::cerr << "Error: No key matching '" << keyId << "' found" << std::endl; + return 1; + } + if (openPGPKey.error) { + std::cerr << "Warning: Error while getting OpenPGP key: " << openPGPKey.error.asString() << std::endl; + } + if (smimeKey.error) { + std::cerr << "Warning: Error while getting S/MIME key: " << openPGPKey.error.asString() << std::endl; + } + auto key = openPGPKey.key.isNull() ? smimeKey.key : openPGPKey.key; + std::cout << "Refreshing " << displayName(key.protocol()) << " key " << key.userID(0).id() << std::endl; + + if (key.protocol() == GpgME::OpenPGP) { + auto job = QGpgME::openpgp()->receiveKeysJob(); + if (!job) { + std::cerr << "Error: Could not create job to refresh OpenPGP key" << std::endl; + return 1; + } + QObject::connect(job, &QGpgME::ReceiveKeysJob::result, &app, [](const GpgME::ImportResult &result, const QString &, const GpgME::Error &) { + std::cout << "Result: " << result << std::endl; + qApp->quit(); + }); + const auto err = job->start({QString::fromLatin1(key.primaryFingerprint())}); + if (err) { + std::cerr << "Error: " << err.asString() << std::endl; + return 1; + } + } else { + auto job = QGpgME::smime()->refreshKeysJob(); + if (!job) { + std::cerr << "Error: Could not create job to refresh S/MIME key" << std::endl; + return 1; + } + QObject::connect(job, &QGpgME::RefreshKeysJob::result, &app, [](const GpgME::Error &err) { + std::cout << "Result: " << err.asString() << std::endl; + qApp->quit(); + }); + const auto err = job->start({key}); + if (err) { + std::cerr << "Error: " << err.asString() << std::endl; + return 1; + } + } + + return app.exec(); +} diff --git a/lang/qt/tests/t-revokekey.cpp b/lang/qt/tests/t-revokekey.cpp new file mode 100644 index 0000000..83d5e71 --- /dev/null +++ b/lang/qt/tests/t-revokekey.cpp @@ -0,0 +1,338 @@ +/* t-revokekey.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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 "t-support.h" + +#include <protocol.h> +#include <revokekeyjob.h> + +#include <QDebug> +#include <QProcess> +#include <QRegularExpression> +#include <QSignalSpy> +#include <QTest> + +#include <context.h> +#include <data.h> + +#include <algorithm> + +using namespace QGpgME; +using namespace GpgME; + +/* Test keys + sec ed25519 2022-03-29 [SC] + 604122B94C86BE846EAFE637FC2BCFB1B19A1CF4 + uid [ultimate] revoke-me@example.net + ssb cv25519 2022-03-29 [E] + * generated with +export GNUPGHOME=$(mktemp -d) +gpg -K +gpg --batch --pinentry-mode loopback --passphrase abc --quick-gen-key revoke-me@example.net default default never +gpg -K +gpg --export-secret-keys --armor --batch --pinentry-mode loopback --passphrase abc --comment revoke-me@example.net revoke-me@example.net | sed 's/\(.*\)/ "\1\\n"/' +#rm -rf ${GNUPGHOME} +unset GNUPGHOME +*/ +static const char *testKeyData = + "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + "Comment: revoke-me@example.net\n" + "\n" + "lIYEYkLSGhYJKwYBBAHaRw8BAQdAWKBjYOZIW33CjwlHKKGIgqXDOGhmbPCStkj1\n" + "+2/cVFL+BwMCXJpRHkD8EcT8DMWdVo84Lx4w7RNDCQx5xnm6rO5kvtmh+PjgM3qt\n" + "CQVGy8H7Dq35yzi0Hihm5zvHxVGYdAu96ShAI2ZqqVL7is0CdAmAibQVcmV2b2tl\n" + "LW1lQGV4YW1wbGUubmV0iJQEExYKADwWIQRgQSK5TIa+hG6v5jf8K8+xsZoc9AUC\n" + "YkLSGgIbAwULCQgHAgMiAgEGFQoJCAsCBBYCAwECHgcCF4AACgkQ/CvPsbGaHPSH\n" + "LAD/RNFgm1Bp6ltDXLS6oS0S5Bgjjg3CBpbdxWTvLjPpaagBAIU2pTLrsGNDKIZq\n" + "EAY7hY50tdcvOfT4OSAySJACJzMFnIsEYkLSGhIKKwYBBAGXVQEFAQEHQIOTbPEz\n" + "hUtL72BHfetUWESlEbh2IF/NEUWASUtQJDghAwEIB/4HAwJGE5naBnwwcfyPC+Nq\n" + "DwY5FO28hQVAzgNu9KAncmPtpST1J8sEPAtJGhtq/9fki9eSvBMbAa64VVpFHKHK\n" + "ravZxr2uCrK6J/u4rTvnR8HgiHgEGBYKACAWIQRgQSK5TIa+hG6v5jf8K8+xsZoc\n" + "9AUCYkLSGgIbDAAKCRD8K8+xsZoc9ANAAP9rX/xanm7YvcGFIxPclmy4h33lLaG8\n" + "dE5RA6zeSg7DqQD8Dae82iKaqKfTpe2+2vIEyxBVy8+WttoElUoXiwr0AQg=\n" + "=/5re\n" + "-----END PGP PRIVATE KEY BLOCK-----\n"; + +class RevokeKeyJobTest : public QGpgMETest +{ + Q_OBJECT + +private Q_SLOTS: + + void initTestCase() + { + QGpgMETest::initTestCase(); + + // set up the test fixture for this test + qputenv("GNUPGHOME", mGnupgHomeTestFixture.path().toUtf8()); + QVERIFY(importSecretKeys(testKeyData, 1)); + } + + void init() + { + // set up a copy of the test fixture for each test function + mGnupgHomeTestCopy.reset(new QTemporaryDir{}); + QVERIFY(copyKeyrings(mGnupgHomeTestFixture.path(), mGnupgHomeTestCopy->path())); + qputenv("GNUPGHOME", mGnupgHomeTestCopy->path().toUtf8()); + } + + void testAsync() + { + // Get the key that shall be revoked + auto key = getTestKey("revoke-me@example.net"); + QVERIFY(!key.isNull()); + QVERIFY(!key.isRevoked()); + + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + hookUpPassphraseProvider(job.get()); + + Error result; + connect(job.get(), &RevokeKeyJob::result, + job.get(), [this, &result](const Error &result_) { + result = result_; + Q_EMIT asyncDone(); + }); + QVERIFY(!job->start(key, RevocationReason::NoLongerUsed, + {"This key is not used anymore."})); + job.release(); // after the job has been started it's on its own + + QSignalSpy spy (this, SIGNAL(asyncDone())); + QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); + + QVERIFY(result.code() == GPG_ERR_NO_ERROR); + key.update(); + QVERIFY(key.isRevoked()); + verifyReason(key, RevocationReason::NoLongerUsed, + {"This key is not used anymore."}); + } + + void testSync_noReasonDescription() + { + // Get the key that shall be revoked + auto key = getTestKey("revoke-me@example.net"); + QVERIFY(!key.isNull()); + QVERIFY(!key.isRevoked()); + + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + hookUpPassphraseProvider(job.get()); + + const auto result = job->exec(key); + + QVERIFY(result.code() == GPG_ERR_NO_ERROR); + key.update(); + QVERIFY(key.isRevoked()); + verifyReason(key, RevocationReason::Unspecified, {}); + } + + void testSync_oneLineReasonDescription() + { + // Get the key that shall be revoked + auto key = getTestKey("revoke-me@example.net"); + QVERIFY(!key.isNull()); + QVERIFY(!key.isRevoked()); + + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + hookUpPassphraseProvider(job.get()); + + const auto result = job->exec(key, RevocationReason::Compromised, + {"The secret key was stolen."}); + + QVERIFY(result.code() == GPG_ERR_NO_ERROR); + key.update(); + QVERIFY(key.isRevoked()); + verifyReason(key, RevocationReason::Compromised, + {"The secret key was stolen."}); + } + + void testSync_twoLinesReasonDescription() + { + // Get the key that shall be revoked + auto key = getTestKey("revoke-me@example.net"); + QVERIFY(!key.isNull()); + QVERIFY(!key.isRevoked()); + + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + hookUpPassphraseProvider(job.get()); + + const auto result = job->exec(key, RevocationReason::Superseded, + {"This key has been superseded by key", + "0000 1111 2222 3333 4444 5555 6666 7777 8888 9999."}); + + QVERIFY(result.code() == GPG_ERR_NO_ERROR); + key.update(); + QVERIFY(key.isRevoked()); + verifyReason(key, RevocationReason::Superseded, + {"This key has been superseded by key", + "0000 1111 2222 3333 4444 5555 6666 7777 8888 9999."}); + } + + void testErrorHandling_nullKey() + { + { + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + QTest::ignoreMessage(QtWarningMsg, "Error: Key is null key"); + const auto result = job->exec(Key{}); + QVERIFY(result.code() == GPG_ERR_INV_ARG); + } + { + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + QTest::ignoreMessage(QtWarningMsg, "Error: Key is null key"); + const auto result = job->start(Key{}); + QVERIFY(result.code() == GPG_ERR_INV_ARG); + } + } + + void testErrorHandling_invalidReason() + { + // Get the key that shall be revoked + auto key = getTestKey("revoke-me@example.net"); + QVERIFY(!key.isNull()); + QVERIFY(!key.isRevoked()); + + { + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + QTest::ignoreMessage(QtWarningMsg, QRegularExpression{"^Error: Invalid revocation reason"}); + const auto result = job->exec(key, static_cast<RevocationReason>(-1)); + QVERIFY(result.code() == GPG_ERR_INV_VALUE); + } + { + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + QTest::ignoreMessage(QtWarningMsg, QRegularExpression{"^Error: Invalid revocation reason"}); + const auto result = job->start(key, static_cast<RevocationReason>(4)); + QVERIFY(result.code() == GPG_ERR_INV_VALUE); + } + } + + void testErrorHandling_invalidDescription() + { + // Get the key that shall be revoked + auto key = getTestKey("revoke-me@example.net"); + QVERIFY(!key.isNull()); + QVERIFY(!key.isRevoked()); + + { + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + QTest::ignoreMessage(QtWarningMsg, "Error: Revocation description contains empty lines or lines with endline characters"); + const auto result = job->exec(key, RevocationReason::Unspecified, + {"line1", "", "line3"}); + QVERIFY(result.code() == GPG_ERR_INV_VALUE); + } + { + auto job = std::unique_ptr<RevokeKeyJob>{openpgp()->revokeKeyJob()}; + QTest::ignoreMessage(QtWarningMsg, "Error: Revocation description contains empty lines or lines with endline characters"); + const auto result = job->start(key, RevocationReason::Unspecified, + {"line1\nline2"}); + QVERIFY(result.code() == GPG_ERR_INV_VALUE); + } + } + +private: + Key getTestKey(const char *pattern) + { + auto ctx = Context::create(OpenPGP); + VERIFY_OR_OBJECT(ctx); + + Error err; + auto key = ctx->key(pattern, err, /*secret=*/true); + VERIFY_OR_OBJECT(!err); + VERIFY_OR_OBJECT(!key.isNull()); + return key; + } + + bool verifyReason(const Key &key, RevocationReason reason, const QStringList &description) + { + static const auto startTimeout = std::chrono::milliseconds{1000}; + static const auto finishTimeout = std::chrono::milliseconds{2000}; + static const QStringList hexCodeForReason = { + QStringLiteral("00"), /* no particular reason */ + QStringLiteral("02"), /* key has been compromised */ + QStringLiteral("01"), /* key is superseded */ + QStringLiteral("03") /* key is no longer used */ + }; + + QProcess p; + p.setProgram(dirInfo("gpg-name")); + p.setArguments({QStringLiteral("-K"), + QStringLiteral("--with-colon"), + QStringLiteral("--with-sig-list"), + QLatin1String{key.primaryFingerprint()} + }); + + p.start(); + + if (!p.waitForStarted(startTimeout.count())) { + qWarning() << "Timeout while waiting for start of" << p.program() << p.arguments().join(u' '); + return false; + } + if (!p.waitForFinished(finishTimeout.count())) { + qWarning() << "Timeout while waiting for completion of" << p.program() << p.arguments().join(u' '); + return false; + } + if (p.exitStatus() != QProcess::NormalExit) { + qWarning() << p.program() << "terminated abnormally with exit status" << p.exitStatus(); + return false; + } + + const auto lines = QString::fromUtf8(p.readAllStandardOutput()).split(u'\n'); + for (const auto &l : lines) { + const auto fields = l.split(u':'); + if (fields[0] == QLatin1String{"rev"}) { + // or "rev" the signature class may be followed by a comma + // and a 2 digit hexnumber with the revocation reason + const auto sigClass = fields.value(10); + const auto revReason = sigClass.split(u',').value(1); + COMPARE_OR_FALSE(revReason, hexCodeForReason.value(static_cast<int>(reason))); + + // decode the \n in the C-style quoted comment field + const auto comment = fields.value(20).replace(QStringLiteral("\\n"), QStringLiteral("\n")); + COMPARE_OR_FALSE(comment, description.join(u'\n')); + return true; + } + if (fields[0] == QLatin1String{"uid"}) { + qWarning() << "Found uid before rev in key listing:\n" << stdout; + return false; + } + } + return false; + } + +private: + QTemporaryDir mGnupgHomeTestFixture; + std::unique_ptr<QTemporaryDir> mGnupgHomeTestCopy; +}; + +QTEST_MAIN(RevokeKeyJobTest) + +#include "t-revokekey.moc" diff --git a/lang/qt/tests/t-setprimaryuserid.cpp b/lang/qt/tests/t-setprimaryuserid.cpp new file mode 100644 index 0000000..c1bd106 --- /dev/null +++ b/lang/qt/tests/t-setprimaryuserid.cpp @@ -0,0 +1,165 @@ +/* t-setprimaryuserid.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2022 g10 Code GmbH + Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + + 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 "t-support.h" + +#include <keylistjob.h> +#include <protocol.h> + +#include <context.h> +#include <engineinfo.h> +#include <keylistresult.h> + +using namespace QGpgME; +using namespace GpgME; + +class TestSetPrimaryUserID: public QGpgMETest +{ + Q_OBJECT + +private Q_SLOTS: + void testSetPrimaryUserID() + { + Key key; + { + std::unique_ptr<KeyListJob> job{openpgp()->keyListJob()}; + std::vector<GpgME::Key> keys; + GpgME::KeyListResult result = job->exec({QStringLiteral("alfa@example.net")}, true, keys); + QVERIFY(!result.error()); + QVERIFY(keys.size() == 1); + key = keys.front(); + } + + QCOMPARE(key.numUserIDs(), 3u); + const std::string oldPrimaryUserId = key.userID(0).id(); + const std::string newPrimaryUserId = key.userID(1).id(); + const std::string newPrimaryUserIdHash = key.userID(1).uidhash(); + + { + std::unique_ptr<Context> ctx{Context::createForProtocol(key.protocol())}; + QVERIFY(ctx); + hookUpPassphraseProvider(ctx.get()); + + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.3.8") { + QVERIFY(!ctx->setPrimaryUid(key, newPrimaryUserIdHash.c_str())); + } else { + QVERIFY(!ctx->setPrimaryUid(key, newPrimaryUserId.c_str())); + } + } + key.update(); + + QCOMPARE(key.userID(0).id(), newPrimaryUserId); + + { + std::unique_ptr<Context> ctx{Context::createForProtocol(key.protocol())}; + QVERIFY(ctx); + hookUpPassphraseProvider(ctx.get()); + + QVERIFY(!ctx->setPrimaryUid(key, oldPrimaryUserId.c_str())); + } + key.update(); + + QCOMPARE(key.userID(0).id(), oldPrimaryUserId); + } + + void testErrorHandling_noSecretKey() + { + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.3.8") { + QSKIP("gpg < 2.3.8 does not report status error"); + } + Key key; + { + std::unique_ptr<KeyListJob> job{openpgp()->keyListJob()}; + std::vector<GpgME::Key> keys; + GpgME::KeyListResult result = job->exec({QStringLiteral("bravo@example.net")}, false, keys); + QVERIFY(!result.error()); + QVERIFY(keys.size() == 1); + key = keys.front(); + } + + QCOMPARE(key.numUserIDs(), 2u); + const std::string newPrimaryUserId = key.userID(1).id(); + + { + std::unique_ptr<Context> ctx{Context::createForProtocol(key.protocol())}; + QVERIFY(ctx); + auto err = ctx->setPrimaryUid(key, newPrimaryUserId.c_str()); + QCOMPARE(err.code(), static_cast<int>(GPG_ERR_NO_SECKEY)); + } + } + + void testErrorHandling_noUserID() + { + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.3.8") { + QSKIP("gpg < 2.3.8 does not report status error"); + } + Key key; + { + std::unique_ptr<KeyListJob> job{openpgp()->keyListJob()}; + std::vector<GpgME::Key> keys; + GpgME::KeyListResult result = job->exec({QStringLiteral("alfa@example.net")}, true, keys); + QVERIFY(!result.error()); + QVERIFY(keys.size() == 1); + key = keys.front(); + } + { + std::unique_ptr<Context> ctx{Context::createForProtocol(key.protocol())}; + QVERIFY(ctx); + auto err = ctx->setPrimaryUid(key, "bravo"); + QCOMPARE(err.code(), static_cast<int>(GPG_ERR_NO_USER_ID)); + } + } + + void initTestCase() + { + QGpgMETest::initTestCase(); + const QString gpgHome = qgetenv("GNUPGHOME"); + QVERIFY(copyKeyrings(gpgHome, mDir.path())); + qputenv("GNUPGHOME", mDir.path().toUtf8()); + QFile conf(mDir.path() + QStringLiteral("/gpg.conf")); + QVERIFY(conf.open(QIODevice::WriteOnly)); + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.2.18") { + conf.write("allow-weak-key-signatures\n"); + } + conf.close(); + } + +private: + QTemporaryDir mDir; +}; + +QTEST_MAIN(TestSetPrimaryUserID) + +#include "t-setprimaryuserid.moc" diff --git a/lang/qt/tests/t-various.cpp b/lang/qt/tests/t-various.cpp index b630350..336ad34 100644 --- a/lang/qt/tests/t-various.cpp +++ b/lang/qt/tests/t-various.cpp @@ -328,8 +328,6 @@ private Q_SLOTS: } }); - QTest::ignoreMessage(QtWarningMsg, "Expiration of certification has been changed to QDate(\"2106-02-06\")"); - job->start(target); QSignalSpy spy{this, &TestVarious::asyncDone}; QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); @@ -339,7 +337,15 @@ private Q_SLOTS: const auto keySignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(!keySignature.neverExpires()); const auto expirationDate = QDateTime::fromSecsSinceEpoch(uint_least32_t(keySignature.expirationTime())).date(); - QCOMPARE(expirationDate, QDate(2106, 2, 6)); // expiration date is capped at 2106-02-06 + // expiration date is capped at 2106-02-05; we also allow 2106-02-04 as expiration date because for locations that use DST + // the expiration date may be 2106-02-04-23:xx:xx (in local non-DST time) if the current time is 00:xx::xx (in local DST time) + const auto expectedExpirationRange = std::make_pair(QDate{2106, 2, 4}, QDate{2106, 2, 5}); + QVERIFY2(expirationDate >= expectedExpirationRange.first, + ("\n Actual : " + expirationDate.toString(Qt::ISODate).toLatin1() + + "\n Expected: " + expectedExpirationRange.first.toString(Qt::ISODate).toLatin1()).constData()); + QVERIFY2(expirationDate <= expectedExpirationRange.second, + ("\n Actual : " + expirationDate.toString(Qt::ISODate).toLatin1() + + "\n Expected: " + expectedExpirationRange.second.toString(Qt::ISODate).toLatin1()).constData()); } void testVersion() diff --git a/lang/qt/tests/t-verify.cpp b/lang/qt/tests/t-verify.cpp index e6b0c8a..0dbecf7 100644 --- a/lang/qt/tests/t-verify.cpp +++ b/lang/qt/tests/t-verify.cpp @@ -81,7 +81,7 @@ private Q_SLOTS: QVERIFY(!key.isNull()); bool found = false; - for (const auto subkey: key.subkeys()) { + for (const auto &subkey: key.subkeys()) { if (!strcmp (subkey.fingerprint(), sig.fingerprint())) { found = true; } |