diff options
Diffstat (limited to 'lang/cpp/src')
-rw-r--r-- | lang/cpp/src/GpgmeppConfig-w32.cmake.in.in | 4 | ||||
-rw-r--r-- | lang/cpp/src/GpgmeppConfig.cmake.in.in | 4 | ||||
-rw-r--r-- | lang/cpp/src/Makefile.am | 5 | ||||
-rw-r--r-- | lang/cpp/src/Makefile.in | 14 | ||||
-rw-r--r-- | lang/cpp/src/context.cpp | 38 | ||||
-rw-r--r-- | lang/cpp/src/context.h | 14 | ||||
-rw-r--r-- | lang/cpp/src/context_p.h | 1 | ||||
-rw-r--r-- | lang/cpp/src/editinteractor.cpp | 19 | ||||
-rw-r--r-- | lang/cpp/src/editinteractor.h | 3 | ||||
-rw-r--r-- | lang/cpp/src/global.h | 15 | ||||
-rw-r--r-- | lang/cpp/src/gpgrevokekeyeditinteractor.cpp | 216 | ||||
-rw-r--r-- | lang/cpp/src/gpgrevokekeyeditinteractor.h | 62 | ||||
-rw-r--r-- | lang/cpp/src/importresult.cpp | 125 | ||||
-rw-r--r-- | lang/cpp/src/importresult.h | 10 | ||||
-rw-r--r-- | lang/cpp/src/key.cpp | 12 | ||||
-rw-r--r-- | lang/cpp/src/result.h | 8 | ||||
-rw-r--r-- | lang/cpp/src/util.h | 35 |
17 files changed, 553 insertions, 32 deletions
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. |