Blame lang/cpp/src/gpgsignkeyeditinteractor.cpp

Packit d7e8d0
/*
Packit d7e8d0
  gpgsignkeyeditinteractor.cpp - Edit Interactor to change the expiry time of an OpenPGP key
Packit d7e8d0
  Copyright (C) 2007 Klarälvdalens Datakonsult AB
Packit d7e8d0
  2016 Bundesamt für Sicherheit in der Informationstechnik
Packit d7e8d0
  Software engineering by Intevation GmbH
Packit d7e8d0
Packit d7e8d0
  This file is part of GPGME++.
Packit d7e8d0
Packit d7e8d0
  GPGME++ is free software; you can redistribute it and/or
Packit d7e8d0
  modify it under the terms of the GNU Library General Public
Packit d7e8d0
  License as published by the Free Software Foundation; either
Packit d7e8d0
  version 2 of the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
  GPGME++ is distributed in the hope that it will be useful,
Packit d7e8d0
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit d7e8d0
  GNU Library General Public License for more details.
Packit d7e8d0
Packit d7e8d0
  You should have received a copy of the GNU Library General Public License
Packit d7e8d0
  along with GPGME++; see the file COPYING.LIB.  If not, write to the
Packit d7e8d0
  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Packit d7e8d0
  Boston, MA 02110-1301, USA.
Packit d7e8d0
*/
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_CONFIG_H
Packit d7e8d0
 #include "config.h"
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include "gpgsignkeyeditinteractor.h"
Packit d7e8d0
#include "error.h"
Packit d7e8d0
#include "key.h"
Packit d7e8d0
Packit d7e8d0
#include <gpgme.h>
Packit d7e8d0
Packit d7e8d0
#include <map>
Packit d7e8d0
#include <string>
Packit d7e8d0
#include <sstream>
Packit d7e8d0
Packit d7e8d0
#include <cassert>
Packit d7e8d0
#include <cstring>
Packit d7e8d0
Packit d7e8d0
using std::strcmp;
Packit d7e8d0
Packit d7e8d0
// avoid conflict (msvc)
Packit d7e8d0
#ifdef ERROR
Packit d7e8d0
# undef ERROR
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#ifdef _MSC_VER
Packit d7e8d0
#undef snprintf
Packit d7e8d0
#define snprintf _snprintf
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
using namespace GpgME;
Packit d7e8d0
Packit d7e8d0
class GpgSignKeyEditInteractor::Private
Packit d7e8d0
{
Packit d7e8d0
public:
Packit d7e8d0
    Private();
Packit d7e8d0
Packit d7e8d0
    std::string scratch;
Packit d7e8d0
    bool started;
Packit d7e8d0
    int options;
Packit d7e8d0
    std::vector<unsigned int> userIDs;
Packit d7e8d0
    std::vector<unsigned int>::const_iterator currentId, nextId;
Packit d7e8d0
    unsigned int checkLevel;
Packit d7e8d0
Packit d7e8d0
    const char *command() const
Packit d7e8d0
    {
Packit d7e8d0
        const bool local = (options & Exportable) == 0;
Packit d7e8d0
        const bool nonRevoc = options & NonRevocable;
Packit d7e8d0
        const bool trust = options & Trust;
Packit d7e8d0
        //TODO: check if all combinations are valid
Packit d7e8d0
        if (local && nonRevoc && trust) {
Packit d7e8d0
            return "ltnrsign";
Packit d7e8d0
        }
Packit d7e8d0
        if (local && nonRevoc) {
Packit d7e8d0
            return "lnrsign";
Packit d7e8d0
        }
Packit d7e8d0
        if (local && trust) {
Packit d7e8d0
            return "ltsign";
Packit d7e8d0
        }
Packit d7e8d0
        if (local) {
Packit d7e8d0
            return "lsign";
Packit d7e8d0
        }
Packit d7e8d0
        if (nonRevoc && trust) {
Packit d7e8d0
            return "tnrsign";
Packit d7e8d0
        }
Packit d7e8d0
        if (nonRevoc) {
Packit d7e8d0
            return "nrsign";
Packit d7e8d0
        }
Packit d7e8d0
        if (trust) {
Packit d7e8d0
            return "tsign";
Packit d7e8d0
        }
Packit d7e8d0
        return "sign";
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    bool signAll() const
Packit d7e8d0
    {
Packit d7e8d0
        return userIDs.empty();
Packit d7e8d0
    }
Packit d7e8d0
    unsigned int nextUserID()
Packit d7e8d0
    {
Packit d7e8d0
        assert(nextId != userIDs.end());
Packit d7e8d0
        currentId = nextId++;
Packit d7e8d0
        return currentUserID();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    bool allUserIDsListed() const
Packit d7e8d0
    {
Packit d7e8d0
        return nextId == userIDs.end();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    unsigned int currentUserID() const
Packit d7e8d0
    {
Packit d7e8d0
        assert(currentId != userIDs.end());
Packit d7e8d0
        return *currentId + 1;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
GpgSignKeyEditInteractor::Private::Private()
Packit d7e8d0
    :
Packit d7e8d0
    started(false),
Packit d7e8d0
    options(0),
Packit d7e8d0
    userIDs(),
Packit d7e8d0
    currentId(),
Packit d7e8d0
    nextId(),
Packit d7e8d0
    checkLevel(0)
Packit d7e8d0
{
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
GpgSignKeyEditInteractor::GpgSignKeyEditInteractor()
Packit d7e8d0
    : EditInteractor(), d(new Private)
Packit d7e8d0
{
Packit d7e8d0
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
GpgSignKeyEditInteractor::~GpgSignKeyEditInteractor()
Packit d7e8d0
{
Packit d7e8d0
    delete d;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
// work around --enable-final
Packit d7e8d0
namespace GpgSignKeyEditInteractor_Private
Packit d7e8d0
{
Packit d7e8d0
enum SignKeyState {
Packit d7e8d0
    START = EditInteractor::StartState,
Packit d7e8d0
    COMMAND,
Packit d7e8d0
    UIDS_ANSWER_SIGN_ALL,
Packit d7e8d0
    UIDS_LIST_SEPARATELY,
Packit d7e8d0
    // all these free slots belong to UIDS_LIST_SEPARATELY, too
Packit d7e8d0
    // (we increase state() by one for each UID, so that action() is called)
Packit d7e8d0
    UIDS_LIST_SEPARATELY_DONE = 1000000,
Packit d7e8d0
    SET_EXPIRE,
Packit d7e8d0
    SET_CHECK_LEVEL,
Packit d7e8d0
    SET_TRUST_VALUE,
Packit d7e8d0
    SET_TRUST_DEPTH,
Packit d7e8d0
    SET_TRUST_REGEXP,
Packit d7e8d0
    CONFIRM,
Packit d7e8d0
    CONFIRM2,
Packit d7e8d0
    QUIT,
Packit d7e8d0
    SAVE,
Packit d7e8d0
    ERROR = EditInteractor::ErrorState
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
typedef std::map<std::tuple<SignKeyState, unsigned int, std::string>, SignKeyState> TransitionMap;
Packit d7e8d0
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static const char *answer(bool b)
Packit d7e8d0
{
Packit d7e8d0
    return b ? "Y" : "N";
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static GpgSignKeyEditInteractor_Private::TransitionMap makeTable()
Packit d7e8d0
{
Packit d7e8d0
    using namespace GpgSignKeyEditInteractor_Private;
Packit d7e8d0
    TransitionMap tab;
Packit d7e8d0
    const unsigned int GET_BOOL = GPGME_STATUS_GET_BOOL;
Packit d7e8d0
    const unsigned int GET_LINE = GPGME_STATUS_GET_LINE;
Packit d7e8d0
#define addEntry( s1, status, str, s2 ) tab[std::make_tuple( s1, status, str)] = s2
Packit d7e8d0
    addEntry(START, GET_LINE, "keyedit.prompt", COMMAND);
Packit d7e8d0
    addEntry(COMMAND, GET_BOOL, "keyedit.sign_all.okay", UIDS_ANSWER_SIGN_ALL);
Packit d7e8d0
    addEntry(COMMAND, GET_BOOL, "sign_uid.okay", CONFIRM);
Packit d7e8d0
    addEntry(COMMAND, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM2);
Packit d7e8d0
    addEntry(UIDS_ANSWER_SIGN_ALL, GET_BOOL, "sign_uid.okay", CONFIRM);
Packit d7e8d0
    addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.expire", SET_EXPIRE);
Packit d7e8d0
    addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
Packit d7e8d0
    addEntry(SET_TRUST_VALUE, GET_LINE, "trustsign_prompt.trust_depth", SET_TRUST_DEPTH);
Packit d7e8d0
    addEntry(SET_TRUST_DEPTH, GET_LINE, "trustsign_prompt.trust_regexp", SET_TRUST_REGEXP);
Packit d7e8d0
    addEntry(SET_TRUST_REGEXP, GET_LINE, "sign_uid.okay", CONFIRM);
Packit d7e8d0
    addEntry(SET_CHECK_LEVEL, GET_BOOL, "sign_uid.okay", CONFIRM);
Packit d7e8d0
    addEntry(SET_EXPIRE, GET_BOOL, "sign_uid.class", SET_CHECK_LEVEL);
Packit d7e8d0
    addEntry(CONFIRM, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM);
Packit d7e8d0
    addEntry(CONFIRM, GET_BOOL, "sign_uid.okay", CONFIRM);
Packit d7e8d0
    addEntry(CONFIRM2, GET_BOOL, "sign_uid.okay", CONFIRM);
Packit d7e8d0
    addEntry(CONFIRM, GET_LINE, "keyedit.prompt", COMMAND);
Packit d7e8d0
    addEntry(CONFIRM, GET_LINE, "trustsign_prompt.trust_value", SET_TRUST_VALUE);
Packit d7e8d0
    addEntry(CONFIRM, GET_LINE, "sign_uid.expire", SET_EXPIRE);
Packit d7e8d0
    addEntry(CONFIRM, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
Packit d7e8d0
    addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM);
Packit d7e8d0
    addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "keyedit.prompt", COMMAND);
Packit d7e8d0
    addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "trustsign_prompt.trust_value", SET_TRUST_VALUE);
Packit d7e8d0
    addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.expire", SET_EXPIRE);
Packit d7e8d0
    addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
Packit d7e8d0
    addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.okay", CONFIRM);
Packit d7e8d0
    addEntry(CONFIRM, GET_LINE, "keyedit.prompt", QUIT);
Packit d7e8d0
    addEntry(ERROR, GET_LINE, "keyedit.prompt", QUIT);
Packit d7e8d0
    addEntry(QUIT, GET_BOOL, "keyedit.save.okay", SAVE);
Packit d7e8d0
#undef addEntry
Packit d7e8d0
    return tab;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
const char *GpgSignKeyEditInteractor::action(Error &err) const
Packit d7e8d0
{
Packit d7e8d0
    static const char check_level_strings[][2] = { "0", "1", "2", "3" };
Packit d7e8d0
    using namespace GpgSignKeyEditInteractor_Private;
Packit d7e8d0
    using namespace std;
Packit d7e8d0
Packit d7e8d0
    switch (const unsigned int st = state()) {
Packit d7e8d0
    case COMMAND:
Packit d7e8d0
        return d->command();
Packit d7e8d0
    case UIDS_ANSWER_SIGN_ALL:
Packit d7e8d0
        return answer(d->signAll());
Packit d7e8d0
    case UIDS_LIST_SEPARATELY_DONE:
Packit d7e8d0
        return d->command();
Packit d7e8d0
    case SET_EXPIRE:
Packit d7e8d0
        return answer(true);
Packit d7e8d0
    case SET_TRUST_VALUE:
Packit d7e8d0
    // TODO
Packit d7e8d0
    case SET_TRUST_DEPTH:
Packit d7e8d0
    //TODO
Packit d7e8d0
    case SET_TRUST_REGEXP:
Packit d7e8d0
        //TODO
Packit Service 30b792
        return nullptr;
Packit d7e8d0
    case SET_CHECK_LEVEL:
Packit d7e8d0
        return check_level_strings[d->checkLevel];
Packit d7e8d0
    case CONFIRM2:
Packit d7e8d0
    case CONFIRM:
Packit d7e8d0
        return answer(true);
Packit d7e8d0
    case QUIT:
Packit d7e8d0
        return "quit";
Packit d7e8d0
    case SAVE:
Packit d7e8d0
        return answer(true);
Packit d7e8d0
    default:
Packit d7e8d0
        if (st >= UIDS_LIST_SEPARATELY && st < UIDS_LIST_SEPARATELY_DONE) {
Packit d7e8d0
            std::stringstream ss;
Packit d7e8d0
            ss << d->nextUserID();
Packit d7e8d0
            d->scratch = ss.str();
Packit d7e8d0
            return d->scratch.c_str();
Packit d7e8d0
        }
Packit d7e8d0
    // fall through
Packit d7e8d0
    case ERROR:
Packit d7e8d0
        err = Error::fromCode(GPG_ERR_GENERAL);
Packit Service 30b792
        return nullptr;
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
unsigned int GpgSignKeyEditInteractor::nextState(unsigned int status, const char *args, Error &err) const
Packit d7e8d0
{
Packit d7e8d0
    d->started = true;
Packit d7e8d0
    using namespace GpgSignKeyEditInteractor_Private;
Packit d7e8d0
    static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL);
Packit d7e8d0
    //static const Error INV_TIME_ERROR = Error::fromCode( GPG_ERR_INV_TIME );
Packit d7e8d0
    static const TransitionMap table(makeTable());
Packit d7e8d0
    if (needsNoResponse(status)) {
Packit d7e8d0
        return state();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    using namespace GpgSignKeyEditInteractor_Private;
Packit d7e8d0
Packit d7e8d0
    //lookup transition in map
Packit d7e8d0
    const TransitionMap::const_iterator it = table.find(std::make_tuple(static_cast<SignKeyState>(state()), status, std::string(args)));
Packit d7e8d0
    if (it != table.end()) {
Packit d7e8d0
        return it->second;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    //handle cases that cannot be handled via the map
Packit d7e8d0
    switch (const unsigned int st = state()) {
Packit d7e8d0
    case UIDS_ANSWER_SIGN_ALL:
Packit d7e8d0
        if (status == GPGME_STATUS_GET_LINE &&
Packit d7e8d0
                strcmp(args, "keyedit.prompt") == 0) {
Packit d7e8d0
            if (!d->signAll()) {
Packit d7e8d0
                return UIDS_LIST_SEPARATELY;
Packit d7e8d0
            }
Packit d7e8d0
            err = Error::fromCode(GPG_ERR_UNUSABLE_PUBKEY);
Packit d7e8d0
            return ERROR;
Packit d7e8d0
        }
Packit d7e8d0
        break;
Packit d7e8d0
    default:
Packit d7e8d0
        if (st >= UIDS_LIST_SEPARATELY && st < UIDS_LIST_SEPARATELY_DONE) {
Packit d7e8d0
            if (status == GPGME_STATUS_GET_LINE &&
Packit d7e8d0
                    strcmp(args, "keyedit.prompt") == 0) {
Packit d7e8d0
                return d->allUserIDsListed() ? UIDS_LIST_SEPARATELY_DONE : st + 1 ;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
        break;
Packit d7e8d0
    case CONFIRM:
Packit d7e8d0
    case ERROR:
Packit d7e8d0
        err = lastError();
Packit d7e8d0
        return ERROR;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    err = GENERAL_ERROR;
Packit d7e8d0
    return ERROR;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
void GpgSignKeyEditInteractor::setCheckLevel(unsigned int checkLevel)
Packit d7e8d0
{
Packit d7e8d0
    assert(!d->started);
Packit d7e8d0
    assert(checkLevel <= 3);
Packit d7e8d0
    d->checkLevel = checkLevel;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
void GpgSignKeyEditInteractor::setUserIDsToSign(const std::vector<unsigned int> &userIDsToSign)
Packit d7e8d0
{
Packit d7e8d0
    assert(!d->started);
Packit d7e8d0
    d->userIDs = userIDsToSign;
Packit d7e8d0
    d->nextId = d->userIDs.begin();
Packit d7e8d0
    d->currentId = d->userIDs.end();
Packit d7e8d0
Packit d7e8d0
}
Packit d7e8d0
void GpgSignKeyEditInteractor::setSigningOptions(int options)
Packit d7e8d0
{
Packit d7e8d0
    assert(!d->started);
Packit d7e8d0
    d->options = options;
Packit d7e8d0
}