summaryrefslogtreecommitdiff
path: root/lang/cpp/src
diff options
context:
space:
mode:
Diffstat (limited to 'lang/cpp/src')
-rw-r--r--lang/cpp/src/GpgmeppConfig-w32.cmake.in.in4
-rw-r--r--lang/cpp/src/GpgmeppConfig.cmake.in.in4
-rw-r--r--lang/cpp/src/Makefile.am5
-rw-r--r--lang/cpp/src/Makefile.in14
-rw-r--r--lang/cpp/src/context.cpp38
-rw-r--r--lang/cpp/src/context.h14
-rw-r--r--lang/cpp/src/context_p.h1
-rw-r--r--lang/cpp/src/editinteractor.cpp19
-rw-r--r--lang/cpp/src/editinteractor.h3
-rw-r--r--lang/cpp/src/global.h15
-rw-r--r--lang/cpp/src/gpgrevokekeyeditinteractor.cpp216
-rw-r--r--lang/cpp/src/gpgrevokekeyeditinteractor.h62
-rw-r--r--lang/cpp/src/importresult.cpp125
-rw-r--r--lang/cpp/src/importresult.h10
-rw-r--r--lang/cpp/src/key.cpp12
-rw-r--r--lang/cpp/src/result.h8
-rw-r--r--lang/cpp/src/util.h35
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.