diff --git a/lang/cpp/src/editinteractor.cpp b/lang/cpp/src/editinteractor.cpp index 08cb1bc9..707bf8b9 100644 --- a/lang/cpp/src/editinteractor.cpp +++ b/lang/cpp/src/editinteractor.cpp @@ -1,377 +1,383 @@ /* editinteractor.cpp - Interface for edit interactors Copyright (C) 2007 Klarälvdalens Datakonsult AB 2016 Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "editinteractor.h" #include "callbacks.h" #include "error.h" #include "util.h" #include #ifdef _WIN32 # include #include #else # include #endif #include #include #include #ifndef GPG_ERR_ALREADY_SIGNED # define GPG_ERR_ALREADY_SIGNED GPG_ERR_USER_1 #endif using namespace GpgME; static const char *status_to_string(unsigned int status); static Error status_to_error(unsigned int status); class EditInteractor::Private { friend class ::GpgME::EditInteractor; friend class ::GpgME::CallbackHelper; EditInteractor *const q; public: explicit Private(EditInteractor *qq); ~Private(); private: unsigned int state = StartState; Error error; std::FILE *debug = nullptr; bool debugNeedsClosing = false; }; class GpgME::CallbackHelper { private: static int writeAll(int fd, const void *buf, size_t count) { size_t toWrite = count; while (toWrite > 0) { const int n = gpgme_io_write(fd, buf, toWrite); if (n < 0) { return n; } toWrite -= n; } return count; } public: static int edit_interactor_callback_impl(void *opaque, gpgme_status_code_t status, const char *args, int fd) { EditInteractor::Private *ei = (EditInteractor::Private *)opaque; Error err = status_to_error(status); if (!err) { // advance to next state based on input: const unsigned int oldState = ei->state; - ei->state = ei->q->nextState(status, args, err); + + if (ei->q->needsNoResponse(status)) { + // keep state + } else { + ei->state = ei->q->nextState(status, args, err); + } + if (ei->debug) { std::fprintf(ei->debug, "EditInteractor: %u -> nextState( %s, %s ) -> %u\n", oldState, status_to_string(status), args ? args : "", ei->state); } if (err || err.isCanceled()) { ei->state = oldState; goto error; } if (ei->state != oldState && // if there was an error from before, we stop here (### this looks weird, can this happen at all?) ei->error.code() == GPG_ERR_NO_ERROR) { // successful state change -> call action if (const char *const result = ei->q->action(err)) { if (err) { goto error; } if (ei->debug) { std::fprintf(ei->debug, "EditInteractor: action result \"%s\"\n", result); } // if there's a result, write it: if (*result) { gpgme_err_set_errno(0); const ssize_t len = std::strlen(result); if (writeAll(fd, result, len) != len) { err = Error::fromSystemError(); if (ei->debug) { std::fprintf(ei->debug, "EditInteractor: Could not write to fd %d (%s)\n", fd, err.asString()); } goto error; } } gpgme_err_set_errno(0); if (writeAll(fd, "\n", 1) != 1) { err = Error::fromSystemError(); if (ei->debug) { std::fprintf(ei->debug, "EditInteractor: Could not write to fd %d (%s)\n", fd, err.asString()); } goto error; } } else { if (err) { goto error; } if (ei->debug) { std::fprintf(ei->debug, "EditInteractor: no action result\n"); } } } else { if (ei->debug) { std::fprintf(ei->debug, "EditInteractor: no action executed\n"); } } } error: if (err || err.isCanceled()) { ei->error = err; ei->state = EditInteractor::ErrorState; } if (ei->debug) { std::fprintf(ei->debug, "EditInteractor: error now %u (%s)\n", ei->error.encodedError(), gpgme_strerror(ei->error.encodedError())); } return ei->error.encodedError(); } }; static gpgme_error_t edit_interactor_callback(void *opaque, gpgme_status_code_t status, const char *args, int fd) { return CallbackHelper::edit_interactor_callback_impl(opaque, status, args, fd); } const gpgme_edit_cb_t GpgME::edit_interactor_callback = ::edit_interactor_callback; EditInteractor::Private::Private(EditInteractor *qq) : q(qq) { const char *debug_env = std::getenv("GPGMEPP_INTERACTOR_DEBUG"); if (!debug_env) { return; } if (!strcmp(debug_env, "stdout")) { debug = stdout; } else if (!strcmp(debug_env, "stderr")) { debug = stderr; } else if (debug_env) { debug = std::fopen(debug_env, "a+"); debugNeedsClosing = true; } } EditInteractor::Private::~Private() { if (debug && debugNeedsClosing) { std::fclose(debug); } } EditInteractor::EditInteractor() : d(new Private(this)) { } EditInteractor::~EditInteractor() { delete d; } unsigned int EditInteractor::state() const { return d->state; } Error EditInteractor::lastError() const { return d->error; } bool EditInteractor::needsNoResponse(unsigned int status) const { switch (status) { case GPGME_STATUS_ALREADY_SIGNED: case GPGME_STATUS_ERROR: case GPGME_STATUS_GET_BOOL: case GPGME_STATUS_GET_LINE: case GPGME_STATUS_KEY_CREATED: case GPGME_STATUS_NEED_PASSPHRASE_SYM: case GPGME_STATUS_SC_OP_FAILURE: case GPGME_STATUS_CARDCTRL: case GPGME_STATUS_BACKUP_KEY_CREATED: return false; default: return true; } } // static Error status_to_error(unsigned int status) { switch (status) { case GPGME_STATUS_MISSING_PASSPHRASE: return Error::fromCode(GPG_ERR_NO_PASSPHRASE); case GPGME_STATUS_ALREADY_SIGNED: return Error::fromCode(GPG_ERR_ALREADY_SIGNED); case GPGME_STATUS_SIGEXPIRED: return Error::fromCode(GPG_ERR_SIG_EXPIRED); } return Error(); } 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(std::stoul(fields[1]))}; } else { err = Error::fromCode(GPG_ERR_GENERAL); } return err; } static const char *const status_strings[] = { "EOF", /* mkstatus processing starts here */ "ENTER", "LEAVE", "ABORT", "GOODSIG", "BADSIG", "ERRSIG", "BADARMOR", "RSA_OR_IDEA", "KEYEXPIRED", "KEYREVOKED", "TRUST_UNDEFINED", "TRUST_NEVER", "TRUST_MARGINAL", "TRUST_FULLY", "TRUST_ULTIMATE", "SHM_INFO", "SHM_GET", "SHM_GET_BOOL", "SHM_GET_HIDDEN", "NEED_PASSPHRASE", "VALIDSIG", "SIG_ID", "ENC_TO", "NODATA", "BAD_PASSPHRASE", "NO_PUBKEY", "NO_SECKEY", "NEED_PASSPHRASE_SYM", "DECRYPTION_FAILED", "DECRYPTION_OKAY", "MISSING_PASSPHRASE", "GOOD_PASSPHRASE", "GOODMDC", "BADMDC", "ERRMDC", "IMPORTED", "IMPORT_OK", "IMPORT_PROBLEM", "IMPORT_RES", "FILE_START", "FILE_DONE", "FILE_ERROR", "BEGIN_DECRYPTION", "END_DECRYPTION", "BEGIN_ENCRYPTION", "END_ENCRYPTION", "DELETE_PROBLEM", "GET_BOOL", "GET_LINE", "GET_HIDDEN", "GOT_IT", "PROGRESS", "SIG_CREATED", "SESSION_KEY", "NOTATION_NAME", "NOTATION_DATA", "POLICY_URL", "BEGIN_STREAM", "END_STREAM", "KEY_CREATED", "USERID_HINT", "UNEXPECTED", "INV_RECP", "NO_RECP", "ALREADY_SIGNED", "SIGEXPIRED", "EXPSIG", "EXPKEYSIG", "TRUNCATED", "ERROR", "NEWSIG", "REVKEYSIG", "SIG_SUBPACKET", "NEED_PASSPHRASE_PIN", "SC_OP_FAILURE", "SC_OP_SUCCESS", "CARDCTRL", "BACKUP_KEY_CREATED", "PKA_TRUST_BAD", "PKA_TRUST_GOOD", "PLAINTEXT", }; static const unsigned int num_status_strings = sizeof status_strings / sizeof * status_strings ; const char *status_to_string(unsigned int idx) { if (idx < num_status_strings) { return status_strings[idx]; } else { return "(unknown)"; } } diff --git a/lang/cpp/src/gpgaddexistingsubkeyeditinteractor.cpp b/lang/cpp/src/gpgaddexistingsubkeyeditinteractor.cpp index 8eec7460..49e98def 100644 --- a/lang/cpp/src/gpgaddexistingsubkeyeditinteractor.cpp +++ b/lang/cpp/src/gpgaddexistingsubkeyeditinteractor.cpp @@ -1,209 +1,205 @@ /* gpgaddexistingsubkeyeditinteractor.cpp - Edit Interactor to add an existing subkey to an OpenPGP key Copyright (c) 2022 g10 Code GmbH Software engineering by Ingo Klöcker 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 "gpgaddexistingsubkeyeditinteractor.h" #include "error.h" #include // avoid conflict (msvc) #ifdef ERROR # undef ERROR #endif using namespace GpgME; class GpgAddExistingSubkeyEditInteractor::Private { enum { START = EditInteractor::StartState, COMMAND, ADD_EXISTING_KEY, KEYGRIP, FLAGS, VALID, KEY_CREATED, QUIT, SAVE, ERROR = EditInteractor::ErrorState }; GpgAddExistingSubkeyEditInteractor *const q = nullptr; public: Private(GpgAddExistingSubkeyEditInteractor *q, const std::string &keygrip) : q{q} , keygrip{keygrip} { } const char *action(Error &err) const; unsigned int nextState(unsigned int statusCode, const char *args, Error &err) const; std::string keygrip; std::string expiry; }; const char *GpgAddExistingSubkeyEditInteractor::Private::action(Error &err) const { switch (q->state()) { case COMMAND: return "addkey"; case ADD_EXISTING_KEY: return "keygrip"; case KEYGRIP: return keygrip.c_str(); case FLAGS: return "Q"; // do not toggle any usage flags case VALID: return expiry.empty() ? "0" : expiry.c_str(); case QUIT: return "quit"; case SAVE: return "Y"; case START: case KEY_CREATED: case ERROR: return nullptr; default: err = Error::fromCode(GPG_ERR_GENERAL); return nullptr; } } unsigned int GpgAddExistingSubkeyEditInteractor::Private::nextState(unsigned int status, const char *args, Error &err) const { using std::strcmp; static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL); static const Error NO_KEY_ERROR = Error::fromCode(GPG_ERR_NO_KEY); static const Error INV_TIME_ERROR = Error::fromCode(GPG_ERR_INV_TIME); - if (q->needsNoResponse(status)) { - return q->state(); - } - switch (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_LINE && strcmp(args, "keygen.algo") == 0) { return ADD_EXISTING_KEY; } err = GENERAL_ERROR; return ERROR; case ADD_EXISTING_KEY: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.keygrip") == 0) { return KEYGRIP; } err = GENERAL_ERROR; return ERROR; case KEYGRIP: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.flags") == 0) { return FLAGS; } else if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.keygrip") == 0) { err = NO_KEY_ERROR; return ERROR; } err = GENERAL_ERROR; return ERROR; case FLAGS: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.valid") == 0) { return VALID; } err = GENERAL_ERROR; return ERROR; case VALID: if (status == GPGME_STATUS_KEY_CREATED) { return KEY_CREATED; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } else if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.valid") == 0) { err = INV_TIME_ERROR; return ERROR; } err = GENERAL_ERROR; return ERROR; case KEY_CREATED: return QUIT; case QUIT: if (status == GPGME_STATUS_GET_BOOL && strcmp(args, "keyedit.save.okay") == 0) { return 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; default: err = GENERAL_ERROR; return ERROR; } } GpgAddExistingSubkeyEditInteractor::GpgAddExistingSubkeyEditInteractor(const std::string &keygrip) : EditInteractor{} , d{new Private{this, keygrip}} { } GpgAddExistingSubkeyEditInteractor::~GpgAddExistingSubkeyEditInteractor() = default; void GpgAddExistingSubkeyEditInteractor::setExpiry(const std::string &timeString) { d->expiry = timeString; } const char *GpgAddExistingSubkeyEditInteractor::action(Error &err) const { return d->action(err); } unsigned int GpgAddExistingSubkeyEditInteractor::nextState(unsigned int status, const char *args, Error &err) const { return d->nextState(status, args, err); } diff --git a/lang/cpp/src/gpgadduserideditinteractor.cpp b/lang/cpp/src/gpgadduserideditinteractor.cpp index c26f5593..1f27c84f 100644 --- a/lang/cpp/src/gpgadduserideditinteractor.cpp +++ b/lang/cpp/src/gpgadduserideditinteractor.cpp @@ -1,195 +1,191 @@ /* gpgadduserideditinteractor.cpp - Edit Interactor to add a new UID to an OpenPGP key Copyright (C) 2008 Klarälvdalens Datakonsult AB 2016 Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gpgadduserideditinteractor.h" #include "error.h" #include #include using std::strcmp; // avoid conflict (msvc) #ifdef ERROR # undef ERROR #endif using namespace GpgME; GpgAddUserIDEditInteractor::GpgAddUserIDEditInteractor() : EditInteractor(), m_name(), m_email(), m_comment() { } GpgAddUserIDEditInteractor::~GpgAddUserIDEditInteractor() {} void GpgAddUserIDEditInteractor::setNameUtf8(const std::string &name) { m_name = name; } void GpgAddUserIDEditInteractor::setEmailUtf8(const std::string &email) { m_email = email; } void GpgAddUserIDEditInteractor::setCommentUtf8(const std::string &comment) { m_comment = comment; } // work around --enable-final namespace GpgAddUserIDEditInteractor_Private { enum { START = EditInteractor::StartState, COMMAND, NAME, EMAIL, COMMENT, QUIT, SAVE, ERROR = EditInteractor::ErrorState }; } const char *GpgAddUserIDEditInteractor::action(Error &err) const { using namespace GpgAddUserIDEditInteractor_Private; switch (state()) { case COMMAND: return "adduid"; case NAME: return m_name.c_str(); case EMAIL: return m_email.c_str(); case COMMENT: return m_comment.c_str(); case QUIT: return "quit"; case SAVE: return "Y"; case START: case ERROR: return nullptr; default: err = Error::fromCode(GPG_ERR_GENERAL); return nullptr; } } unsigned int GpgAddUserIDEditInteractor::nextState(unsigned int status, const char *args, Error &err) const { static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL); static const Error INV_NAME_ERROR = Error::fromCode(GPG_ERR_INV_NAME); static const Error INV_EMAIL_ERROR = Error::fromCode(GPG_ERR_INV_USER_ID); static const Error INV_COMMENT_ERROR = Error::fromCode(GPG_ERR_INV_USER_ID); - if (needsNoResponse(status)) { - return state(); - } - using namespace GpgAddUserIDEditInteractor_Private; switch (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_LINE && strcmp(args, "keygen.name") == 0) { return NAME; } err = GENERAL_ERROR; return ERROR; case NAME: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.email") == 0) { return EMAIL; } err = GENERAL_ERROR; if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.name") == 0) { err = INV_NAME_ERROR; } return ERROR; case EMAIL: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.comment") == 0) { return COMMENT; } err = GENERAL_ERROR; if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.email") == 0) { err = INV_EMAIL_ERROR; } return ERROR; case COMMENT: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } err = GENERAL_ERROR; if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.comment") == 0) { err = INV_COMMENT_ERROR; } return ERROR; case QUIT: if (status == GPGME_STATUS_GET_BOOL && strcmp(args, "keyedit.save.okay") == 0) { return SAVE; } err = GENERAL_ERROR; return ERROR; case ERROR: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } err = lastError(); return ERROR; default: err = GENERAL_ERROR; return ERROR; } } diff --git a/lang/cpp/src/gpggencardkeyinteractor.cpp b/lang/cpp/src/gpggencardkeyinteractor.cpp index a28169ec..cd226c27 100644 --- a/lang/cpp/src/gpggencardkeyinteractor.cpp +++ b/lang/cpp/src/gpggencardkeyinteractor.cpp @@ -1,473 +1,469 @@ /* gpggencardkeyinteractor.cpp - Edit Interactor to generate a key on a card Copyright (C) 2017 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gpggencardkeyinteractor.h" #include "error.h" #include using namespace GpgME; class GpgGenCardKeyInteractor::Private { public: Private() : keysize("2048") { } std::string name, email, backupFileName, expiry, serial, keysize; bool backup = false; Algo algo = RSA; std::string curve; }; GpgGenCardKeyInteractor::~GpgGenCardKeyInteractor() = default; GpgGenCardKeyInteractor::GpgGenCardKeyInteractor(const std::string &serial): d(new Private) { d->serial = serial; } void GpgGenCardKeyInteractor::setNameUtf8(const std::string &name) { d->name = name; } void GpgGenCardKeyInteractor::setEmailUtf8(const std::string &email) { d->email = email; } void GpgGenCardKeyInteractor::setDoBackup(bool value) { d->backup = value; } void GpgGenCardKeyInteractor::setKeySize(int value) { d->keysize = std::to_string(value); } void GpgGenCardKeyInteractor::setExpiry(const std::string &timeStr) { d->expiry = timeStr; } std::string GpgGenCardKeyInteractor::backupFileName() const { return d->backupFileName; } void GpgGenCardKeyInteractor::setAlgo(Algo algo) { d->algo = algo; } void GpgGenCardKeyInteractor::setCurve(Curve curve) { if (curve == DefaultCurve) { d->curve.clear(); } else if (curve >= 1 && curve <= LastCurve) { d->curve = std::to_string(static_cast(curve)); } } namespace GpgGenCardKeyInteractor_Private { enum { START = EditInteractor::StartState, DO_ADMIN, EXPIRE, GOT_SERIAL, COMMAND, NAME, EMAIL, COMMENT, BACKUP, REPLACE, SIZE, SIZE2, SIZE3, BACKUP_KEY_CREATED, KEY_CREATED, QUIT, SAVE, KEY_ATTR, KEY_ALGO1, KEY_ALGO2, KEY_ALGO3, KEY_CURVE1, KEY_CURVE2, KEY_CURVE3, ERROR = EditInteractor::ErrorState }; } const char *GpgGenCardKeyInteractor::action(Error &err) const { using namespace GpgGenCardKeyInteractor_Private; switch (state()) { case DO_ADMIN: return "admin"; case COMMAND: return "generate"; case KEY_ATTR: return "key-attr"; case KEY_ALGO1: case KEY_ALGO2: case KEY_ALGO3: return d->algo == RSA ? "1" : "2"; case KEY_CURVE1: case KEY_CURVE2: case KEY_CURVE3: return d->curve.empty() ? "1" : d->curve.c_str(); // default is Curve25519 case NAME: return d->name.c_str(); case EMAIL: return d->email.c_str(); case EXPIRE: return d->expiry.c_str(); case BACKUP: return d->backup ? "Y" : "N"; case REPLACE: return "Y"; case SIZE: case SIZE2: case SIZE3: return d->keysize.c_str(); case COMMENT: return ""; case SAVE: return "Y"; case QUIT: return "quit"; case KEY_CREATED: case START: case GOT_SERIAL: case BACKUP_KEY_CREATED: case ERROR: return nullptr; default: err = Error::fromCode(GPG_ERR_GENERAL); return nullptr; } } unsigned int GpgGenCardKeyInteractor::nextState(unsigned int status, const char *args, Error &err) const { static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL); static const Error INV_NAME_ERROR = Error::fromCode(GPG_ERR_INV_NAME); static const Error INV_EMAIL_ERROR = Error::fromCode(GPG_ERR_INV_USER_ID); static const Error INV_COMMENT_ERROR = Error::fromCode(GPG_ERR_INV_USER_ID); - if (needsNoResponse(status)) { - return state(); - } - using namespace GpgGenCardKeyInteractor_Private; switch (state()) { case START: if (status == GPGME_STATUS_CARDCTRL && !d->serial.empty()) { const std::string sArgs = args; if (sArgs.find(d->serial) == std::string::npos) { // Wrong smartcard err = Error::fromCode(GPG_ERR_WRONG_CARD); return ERROR; } else { printf("EditInteractor: Confirmed S/N: %s %s\n", d->serial.c_str(), sArgs.c_str()); } return GOT_SERIAL; } else if (d->serial.empty()) { return GOT_SERIAL; } err = GENERAL_ERROR; return ERROR; case GOT_SERIAL: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return DO_ADMIN; } err = GENERAL_ERROR; return ERROR; case DO_ADMIN: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return KEY_ATTR; } err = GENERAL_ERROR; return ERROR; // Handling for key-attr subcommand case KEY_ATTR: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { // Happens if key attr is not yet supported. return COMMAND; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.algo") == 0) { return KEY_ALGO1; } err = GENERAL_ERROR; return ERROR; case KEY_ALGO1: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.size") == 0) { return SIZE; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.curve") == 0) { return KEY_CURVE1; } err = GENERAL_ERROR; return ERROR; case KEY_ALGO2: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.size") == 0) { return SIZE2; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.curve") == 0) { return KEY_CURVE2; } err = GENERAL_ERROR; return ERROR; case KEY_ALGO3: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.size") == 0) { return SIZE3; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.curve") == 0) { return KEY_CURVE3; } err = GENERAL_ERROR; return ERROR; case KEY_CURVE1: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.algo") == 0) { return KEY_ALGO2; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return COMMAND; } err = GENERAL_ERROR; return ERROR; case KEY_CURVE2: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.algo") == 0) { return KEY_ALGO3; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return COMMAND; } err = GENERAL_ERROR; return ERROR; case KEY_CURVE3: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.algo") == 0) { return KEY_ALGO3; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return COMMAND; } err = GENERAL_ERROR; return ERROR; // End key-attr handling case COMMAND: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.backup_enc") == 0) { return BACKUP; } err = GENERAL_ERROR; return ERROR; case BACKUP: if (status == GPGME_STATUS_GET_BOOL && strcmp(args, "cardedit.genkeys.replace_keys") == 0) { return REPLACE; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.size") == 0) { return SIZE; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.valid") == 0) { return EXPIRE; } err = GENERAL_ERROR; return ERROR; case REPLACE: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.size") == 0) { return SIZE; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.valid") == 0) { return EXPIRE; } err = GENERAL_ERROR; return ERROR; case SIZE: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.size") == 0) { return SIZE2; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.valid") == 0) { return EXPIRE; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.algo") == 0) { return KEY_ALGO2; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return COMMAND; } err = GENERAL_ERROR; return ERROR; case SIZE2: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.size") == 0) { return SIZE3; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.valid") == 0) { return EXPIRE; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.genkeys.algo") == 0) { return KEY_ALGO3; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return COMMAND; } err = GENERAL_ERROR; return ERROR; case SIZE3: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.valid") == 0) { return EXPIRE; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return COMMAND; } err = GENERAL_ERROR; return ERROR; case EXPIRE: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.name") == 0) { return NAME; } err = GENERAL_ERROR; return ERROR; case NAME: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.email") == 0) { return EMAIL; } err = GENERAL_ERROR; if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.name") == 0) { err = INV_NAME_ERROR; } return ERROR; case EMAIL: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.comment") == 0) { return COMMENT; } err = GENERAL_ERROR; if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.email") == 0) { err = INV_EMAIL_ERROR; } return ERROR; case COMMENT: if (status == GPGME_STATUS_BACKUP_KEY_CREATED) { std::string sArgs = args; const auto pos = sArgs.rfind(" "); if (pos != std::string::npos) { d->backupFileName = sArgs.substr(pos + 1); return BACKUP_KEY_CREATED; } } if (status == GPGME_STATUS_KEY_CREATED) { return KEY_CREATED; } if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } err = GENERAL_ERROR; if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.comment") == 0) { err = INV_COMMENT_ERROR; } return ERROR; case BACKUP_KEY_CREATED: if (status == GPGME_STATUS_KEY_CREATED) { return KEY_CREATED; } err = GENERAL_ERROR; return ERROR; case KEY_CREATED: return QUIT; case QUIT: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "cardedit.prompt") == 0) { return QUIT; } err = GENERAL_ERROR; return ERROR; case ERROR: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } err = lastError(); return ERROR; default: err = GENERAL_ERROR; return ERROR; } } diff --git a/lang/cpp/src/gpgrevokekeyeditinteractor.cpp b/lang/cpp/src/gpgrevokekeyeditinteractor.cpp index 86b3c3c4..fd584099 100644 --- a/lang/cpp/src/gpgrevokekeyeditinteractor.cpp +++ b/lang/cpp/src/gpgrevokekeyeditinteractor.cpp @@ -1,216 +1,212 @@ /* gpgrevokekeyeditinteractor.cpp - Edit Interactor to revoke own OpenPGP keys Copyright (c) 2022 g10 Code GmbH Software engineering by Ingo Klöcker 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 #include #include // 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 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 &description) { d->reasonCode = std::to_string(static_cast(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/gpgsetexpirytimeeditinteractor.cpp b/lang/cpp/src/gpgsetexpirytimeeditinteractor.cpp index 2936d194..2409ef19 100644 --- a/lang/cpp/src/gpgsetexpirytimeeditinteractor.cpp +++ b/lang/cpp/src/gpgsetexpirytimeeditinteractor.cpp @@ -1,147 +1,143 @@ /* gpgsetexpirytimeeditinteractor.cpp - Edit Interactor to change the expiry time of an OpenPGP key Copyright (C) 2007 Klarälvdalens Datakonsult AB 2016 Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gpgsetexpirytimeeditinteractor.h" #include "error.h" #include #include using std::strcmp; // avoid conflict (msvc) #ifdef ERROR # undef ERROR #endif using namespace GpgME; GpgSetExpiryTimeEditInteractor::GpgSetExpiryTimeEditInteractor(const std::string &t) : EditInteractor(), m_strtime(t) { } GpgSetExpiryTimeEditInteractor::~GpgSetExpiryTimeEditInteractor() {} // work around --enable-final namespace GpgSetExpiryTimeEditInteractor_Private { enum { START = EditInteractor::StartState, COMMAND, DATE, QUIT, SAVE, ERROR = EditInteractor::ErrorState }; } const char *GpgSetExpiryTimeEditInteractor::action(Error &err) const { using namespace GpgSetExpiryTimeEditInteractor_Private; switch (state()) { case COMMAND: return "expire"; case DATE: return m_strtime.c_str(); case QUIT: return "quit"; case SAVE: return "Y"; case START: case ERROR: return nullptr; default: err = Error::fromCode(GPG_ERR_GENERAL); return nullptr; } } unsigned int GpgSetExpiryTimeEditInteractor::nextState(unsigned int status, const char *args, Error &err) const { static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL); static const Error INV_TIME_ERROR = Error::fromCode(GPG_ERR_INV_TIME); - if (needsNoResponse(status)) { - return state(); - } - using namespace GpgSetExpiryTimeEditInteractor_Private; switch (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_LINE && strcmp(args, "keygen.valid") == 0) { return DATE; } err = GENERAL_ERROR; return ERROR; case DATE: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } else if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keygen.valid")) { err = INV_TIME_ERROR; return ERROR; } err = GENERAL_ERROR; return ERROR; case QUIT: if (status == GPGME_STATUS_GET_BOOL && strcmp(args, "keyedit.save.okay") == 0) { return SAVE; } err = GENERAL_ERROR; return ERROR; case ERROR: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } err = lastError(); return ERROR; default: err = GENERAL_ERROR; return ERROR; } } diff --git a/lang/cpp/src/gpgsetownertrusteditinteractor.cpp b/lang/cpp/src/gpgsetownertrusteditinteractor.cpp index f1e99ea9..e28951ba 100644 --- a/lang/cpp/src/gpgsetownertrusteditinteractor.cpp +++ b/lang/cpp/src/gpgsetownertrusteditinteractor.cpp @@ -1,157 +1,153 @@ /* gpgsetownertrusteditinteractor.cpp - Edit Interactor to change the expiry time of an OpenPGP key Copyright (C) 2007 Klarälvdalens Datakonsult AB 2016 Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gpgsetownertrusteditinteractor.h" #include "error.h" #include #include using std::strcmp; // avoid conflict (msvc) #ifdef ERROR # undef ERROR #endif using namespace GpgME; GpgSetOwnerTrustEditInteractor::GpgSetOwnerTrustEditInteractor(Key::OwnerTrust ot) : EditInteractor(), m_ownertrust(ot) { } GpgSetOwnerTrustEditInteractor::~GpgSetOwnerTrustEditInteractor() {} // work around --enable-final namespace GpgSetOwnerTrustEditInteractor_Private { enum { START = EditInteractor::StartState, COMMAND, VALUE, REALLY_ULTIMATE, QUIT, SAVE, ERROR = EditInteractor::ErrorState }; } const char *GpgSetOwnerTrustEditInteractor::action(Error &err) const { static const char truststrings[][2] = { "1", "1", "2", "3", "4", "5" }; using namespace GpgSetOwnerTrustEditInteractor_Private; switch (state()) { case COMMAND: return "trust"; case VALUE: return truststrings[m_ownertrust]; case REALLY_ULTIMATE: return "Y"; case QUIT: return "quit"; case SAVE: return "Y"; case START: case ERROR: return nullptr; default: err = Error::fromCode(GPG_ERR_GENERAL); return nullptr; } } unsigned int GpgSetOwnerTrustEditInteractor::nextState(unsigned int status, const char *args, Error &err) const { static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL); //static const Error INV_TIME_ERROR = Error::fromCode( GPG_ERR_INV_TIME ); - if (needsNoResponse(status)) { - return state(); - } - using namespace GpgSetOwnerTrustEditInteractor_Private; switch (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_LINE && strcmp(args, "edit_ownertrust.value") == 0) { return VALUE; } err = GENERAL_ERROR; return ERROR; case VALUE: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } else if (status == GPGME_STATUS_GET_BOOL && strcmp(args, "edit_ownertrust.set_ultimate.okay") == 0) { return REALLY_ULTIMATE; } err = GENERAL_ERROR; return ERROR; case REALLY_ULTIMATE: 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 SAVE; } err = GENERAL_ERROR; return ERROR; case ERROR: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return QUIT; } err = lastError(); return ERROR; default: err = GENERAL_ERROR; return ERROR; }; } diff --git a/lang/cpp/src/gpgsignkeyeditinteractor.cpp b/lang/cpp/src/gpgsignkeyeditinteractor.cpp index 1c8af148..164cfc03 100644 --- a/lang/cpp/src/gpgsignkeyeditinteractor.cpp +++ b/lang/cpp/src/gpgsignkeyeditinteractor.cpp @@ -1,398 +1,395 @@ /* gpgsignkeyeditinteractor.cpp - Edit Interactor to change the expiry time of an OpenPGP key Copyright (C) 2007 Klarälvdalens Datakonsult AB 2016 Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH This file is part of GPGME++. GPGME++ is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GPGME++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with GPGME++; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gpgsignkeyeditinteractor.h" #include "error.h" #include "key.h" #include #include #include #include #include #include using std::strcmp; // avoid conflict (msvc) #ifdef ERROR # undef ERROR #endif #ifdef _MSC_VER #undef snprintf #define snprintf _snprintf #endif using namespace GpgME; class GpgSignKeyEditInteractor::Private { public: Private(); std::string scratch; bool started; int options; std::vector userIDs; std::vector::const_iterator currentId, nextId; unsigned int checkLevel; bool dupeOk; Key key; struct { TrustSignatureTrust trust; std::string depth; std::string scope; } trustSignature; const char *command() const { const bool local = (options & Exportable) == 0; const bool nonRevoc = options & NonRevocable; const bool trust = options & Trust; //TODO: check if all combinations are valid if (local && nonRevoc && trust) { return "ltnrsign"; } if (local && nonRevoc) { return "lnrsign"; } if (local && trust) { return "ltsign"; } if (local) { return "lsign"; } if (nonRevoc && trust) { return "tnrsign"; } if (nonRevoc) { return "nrsign"; } if (trust) { return "tsign"; } return "sign"; } bool signAll() const { return userIDs.empty(); } unsigned int nextUserID() { assert(nextId != userIDs.end()); currentId = nextId++; return currentUserID(); } bool allUserIDsListed() const { return nextId == userIDs.end(); } unsigned int currentUserID() const { assert(currentId != userIDs.end()); return *currentId + 1; } }; GpgSignKeyEditInteractor::Private::Private() : started(false), options(0), userIDs(), currentId(), nextId(), checkLevel(0), dupeOk(false), trustSignature{TrustSignatureTrust::None, "0", {}} { } GpgSignKeyEditInteractor::GpgSignKeyEditInteractor() : EditInteractor(), d(new Private) { } GpgSignKeyEditInteractor::~GpgSignKeyEditInteractor() { delete d; } // work around --enable-final namespace GpgSignKeyEditInteractor_Private { enum SignKeyState { START = EditInteractor::StartState, COMMAND, UIDS_ANSWER_SIGN_ALL, UIDS_LIST_SEPARATELY, // all these free slots belong to UIDS_LIST_SEPARATELY, too // (we increase state() by one for each UID, so that action() is called) UIDS_LIST_SEPARATELY_DONE = 1000000, SET_EXPIRE, SET_CHECK_LEVEL, SET_TRUST_VALUE, SET_TRUST_DEPTH, SET_TRUST_REGEXP, CONFIRM, CONFIRM2, DUPE_OK, DUPE_OK2, REJECT_SIGN_EXPIRED, QUIT, SAVE, ERROR = EditInteractor::ErrorState }; typedef std::map, SignKeyState> TransitionMap; } static const char *answer(bool b) { return b ? "Y" : "N"; } static GpgSignKeyEditInteractor_Private::TransitionMap makeTable() { using namespace GpgSignKeyEditInteractor_Private; TransitionMap tab; const unsigned int GET_BOOL = GPGME_STATUS_GET_BOOL; const unsigned int GET_LINE = GPGME_STATUS_GET_LINE; #define addEntry( s1, status, str, s2 ) tab[std::make_tuple( s1, status, str)] = s2 addEntry(START, GET_LINE, "keyedit.prompt", COMMAND); addEntry(COMMAND, GET_BOOL, "keyedit.sign_all.okay", UIDS_ANSWER_SIGN_ALL); addEntry(COMMAND, GET_BOOL, "sign_uid.expired_okay", REJECT_SIGN_EXPIRED); addEntry(COMMAND, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(COMMAND, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM2); addEntry(COMMAND, GET_BOOL, "sign_uid.dupe_okay", DUPE_OK); addEntry(COMMAND, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE); addEntry(UIDS_ANSWER_SIGN_ALL, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(UIDS_ANSWER_SIGN_ALL, GET_BOOL, "sign_uid.dupe_okay", DUPE_OK); addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.expire", SET_EXPIRE); addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL); addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE); addEntry(SET_TRUST_VALUE, GET_LINE, "trustsig_prompt.trust_depth", SET_TRUST_DEPTH); addEntry(SET_TRUST_DEPTH, GET_LINE, "trustsig_prompt.trust_regexp", SET_TRUST_REGEXP); addEntry(SET_TRUST_REGEXP, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(SET_CHECK_LEVEL, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(SET_EXPIRE, GET_BOOL, "sign_uid.class", SET_CHECK_LEVEL); addEntry(CONFIRM, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM); addEntry(DUPE_OK, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(DUPE_OK2, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(DUPE_OK, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE); addEntry(DUPE_OK2, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE); addEntry(CONFIRM, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(CONFIRM2, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(CONFIRM, GET_LINE, "keyedit.prompt", COMMAND); addEntry(CONFIRM, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE); addEntry(CONFIRM, GET_LINE, "sign_uid.expire", SET_EXPIRE); addEntry(CONFIRM, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL); addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM); addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "keyedit.prompt", COMMAND); addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "trustsig_prompt.trust_value", SET_TRUST_VALUE); addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.expire", SET_EXPIRE); addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL); addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.okay", CONFIRM); addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.dupe_okay", DUPE_OK); addEntry(DUPE_OK, GET_BOOL, "sign_uid.dupe_okay", DUPE_OK2); addEntry(DUPE_OK2, GET_BOOL, "sign_uid.dupe_okay", DUPE_OK); addEntry(CONFIRM, GET_LINE, "keyedit.prompt", QUIT); addEntry(REJECT_SIGN_EXPIRED, GET_LINE, "keyedit.prompt", QUIT); addEntry(ERROR, GET_LINE, "keyedit.prompt", QUIT); addEntry(QUIT, GET_BOOL, "keyedit.save.okay", SAVE); #undef addEntry return tab; } const char *GpgSignKeyEditInteractor::action(Error &err) const { static const char check_level_strings[][2] = { "0", "1", "2", "3" }; using namespace GpgSignKeyEditInteractor_Private; using namespace std; switch (const unsigned int st = state()) { case COMMAND: return d->command(); case UIDS_ANSWER_SIGN_ALL: return answer(d->signAll()); case UIDS_LIST_SEPARATELY_DONE: return d->command(); case SET_EXPIRE: return answer(true); case SET_TRUST_VALUE: return d->trustSignature.trust == TrustSignatureTrust::Partial ? "1" : "2"; case SET_TRUST_DEPTH: return d->trustSignature.depth.c_str(); case SET_TRUST_REGEXP: return d->trustSignature.scope.c_str(); case SET_CHECK_LEVEL: return check_level_strings[d->checkLevel]; case DUPE_OK: case DUPE_OK2: return answer(d->dupeOk); case CONFIRM2: case CONFIRM: return answer(true); case REJECT_SIGN_EXPIRED: err = Error::fromCode(GPG_ERR_KEY_EXPIRED); return answer(false); case QUIT: return "quit"; case SAVE: return answer(true); default: if (st >= UIDS_LIST_SEPARATELY && st < UIDS_LIST_SEPARATELY_DONE) { std::stringstream ss; auto nextID = d->nextUserID(); const char *hash; assert (nextID); if (!d->key.isNull() && (hash = d->key.userID(nextID - 1).uidhash())) { /* Prefer uidhash if it is available as it might happen * that uidattrs break the ordering of the uids in the * edit-key interface */ ss << "uid " << hash; } else { ss << nextID; } d->scratch = ss.str(); return d->scratch.c_str(); } // fall through case ERROR: err = Error::fromCode(GPG_ERR_GENERAL); return nullptr; } } unsigned int GpgSignKeyEditInteractor::nextState(unsigned int status, const char *args, Error &err) const { d->started = true; using namespace GpgSignKeyEditInteractor_Private; static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL); //static const Error INV_TIME_ERROR = Error::fromCode( GPG_ERR_INV_TIME ); static const TransitionMap table(makeTable()); - if (needsNoResponse(status)) { - return state(); - } using namespace GpgSignKeyEditInteractor_Private; //lookup transition in map const TransitionMap::const_iterator it = table.find(std::make_tuple(static_cast(state()), status, std::string(args))); if (it != table.end()) { return it->second; } //handle cases that cannot be handled via the map switch (const unsigned int st = state()) { case UIDS_ANSWER_SIGN_ALL: if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { if (!d->signAll()) { return UIDS_LIST_SEPARATELY; } err = Error::fromCode(GPG_ERR_UNUSABLE_PUBKEY); return ERROR; } break; default: if (st >= UIDS_LIST_SEPARATELY && st < UIDS_LIST_SEPARATELY_DONE) { if (status == GPGME_STATUS_GET_LINE && strcmp(args, "keyedit.prompt") == 0) { return d->allUserIDsListed() ? UIDS_LIST_SEPARATELY_DONE : st + 1 ; } } break; case CONFIRM: case ERROR: err = lastError(); return ERROR; } err = GENERAL_ERROR; return ERROR; } void GpgSignKeyEditInteractor::setKey(const Key &key) { d->key = key; } void GpgSignKeyEditInteractor::setCheckLevel(unsigned int checkLevel) { assert(!d->started); assert(checkLevel <= 3); d->checkLevel = checkLevel; } void GpgSignKeyEditInteractor::setUserIDsToSign(const std::vector &userIDsToSign) { assert(!d->started); d->userIDs = userIDsToSign; d->nextId = d->userIDs.begin(); d->currentId = d->userIDs.end(); } void GpgSignKeyEditInteractor::setSigningOptions(int options) { assert(!d->started); d->options = options; } void GpgSignKeyEditInteractor::setDupeOk(bool value) { assert(!d->started); d->dupeOk = value; } void GpgSignKeyEditInteractor::setTrustSignatureTrust(GpgME::TrustSignatureTrust trust) { assert(!d->started); assert(trust != TrustSignatureTrust::None); d->trustSignature.trust = trust; } void GpgSignKeyEditInteractor::setTrustSignatureDepth(unsigned short depth) { assert(!d->started); assert(depth <= 255); d->trustSignature.depth = std::to_string(depth); } void GpgSignKeyEditInteractor::setTrustSignatureScope(const std::string &scope) { assert(!d->started); d->trustSignature.scope = scope; }