Blame lang/cpp/src/editinteractor.cpp

Packit d7e8d0
/*
Packit d7e8d0
  editinteractor.cpp - Interface for edit interactors
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 "editinteractor.h"
Packit d7e8d0
#include "callbacks.h"
Packit d7e8d0
#include "error.h"
Packit d7e8d0
Packit d7e8d0
#include <gpgme.h>
Packit d7e8d0
Packit d7e8d0
#ifdef _WIN32
Packit d7e8d0
# include <io.h>
Packit d7e8d0
#include <windows.h>
Packit d7e8d0
#else
Packit d7e8d0
# include <unistd.h>
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include <cerrno>
Packit d7e8d0
#include <cstring>
Packit d7e8d0
Packit d7e8d0
#ifndef GPG_ERR_ALREADY_SIGNED
Packit d7e8d0
# define GPG_ERR_ALREADY_SIGNED GPG_ERR_USER_1
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
using namespace GpgME;
Packit d7e8d0
Packit d7e8d0
static const char *status_to_string(unsigned int status);
Packit d7e8d0
static Error status_to_error(unsigned int status);
Packit d7e8d0
Packit d7e8d0
class EditInteractor::Private
Packit d7e8d0
{
Packit d7e8d0
    friend class ::GpgME::EditInteractor;
Packit d7e8d0
    friend class ::GpgME::CallbackHelper;
Packit d7e8d0
    EditInteractor *const q;
Packit d7e8d0
public:
Packit d7e8d0
    explicit Private(EditInteractor *qq);
Packit d7e8d0
    ~Private();
Packit d7e8d0
Packit d7e8d0
private:
Packit d7e8d0
    unsigned int state;
Packit d7e8d0
    Error error;
Packit d7e8d0
    std::FILE *debug;
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
class GpgME::CallbackHelper
Packit d7e8d0
{
Packit d7e8d0
private:
Packit d7e8d0
    static int writeAll(int fd, const void *buf, size_t count)
Packit d7e8d0
    {
Packit d7e8d0
        size_t toWrite = count;
Packit d7e8d0
        while (toWrite > 0) {
Packit d7e8d0
            const int n = gpgme_io_write(fd, buf, toWrite);
Packit d7e8d0
            if (n < 0) {
Packit d7e8d0
                return n;
Packit d7e8d0
            }
Packit d7e8d0
            toWrite -= n;
Packit d7e8d0
        }
Packit d7e8d0
        return count;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
public:
Packit d7e8d0
    static int edit_interactor_callback_impl(void *opaque, gpgme_status_code_t status, const char *args, int fd)
Packit d7e8d0
    {
Packit d7e8d0
        EditInteractor::Private *ei = (EditInteractor::Private *)opaque;
Packit d7e8d0
Packit d7e8d0
        Error err = status_to_error(status);
Packit d7e8d0
Packit d7e8d0
        if (!err) {
Packit d7e8d0
Packit d7e8d0
            // advance to next state based on input:
Packit d7e8d0
            const unsigned int oldState = ei->state;
Packit d7e8d0
            ei->state = ei->q->nextState(status, args, err);
Packit d7e8d0
            if (ei->debug) {
Packit d7e8d0
                std::fprintf(ei->debug, "EditInteractor: %u -> nextState( %s, %s ) -> %u\n",
Packit d7e8d0
                             oldState, status_to_string(status), args ? args : "<null>", ei->state);
Packit d7e8d0
            }
Packit d7e8d0
            if (err) {
Packit d7e8d0
                ei->state = oldState;
Packit d7e8d0
                goto error;
Packit d7e8d0
            }
Packit d7e8d0
Packit d7e8d0
            if (ei->state != oldState &&
Packit d7e8d0
                    // if there was an error from before, we stop here (### this looks weird, can this happen at all?)
Packit d7e8d0
                    ei->error.code() == GPG_ERR_NO_ERROR) {
Packit d7e8d0
Packit d7e8d0
                // successful state change -> call action
Packit d7e8d0
                if (const char *const result = ei->q->action(err)) {
Packit d7e8d0
                    if (err) {
Packit d7e8d0
                        goto error;
Packit d7e8d0
                    }
Packit d7e8d0
                    if (ei->debug) {
Packit d7e8d0
                        std::fprintf(ei->debug, "EditInteractor: action result \"%s\"\n", result);
Packit d7e8d0
                    }
Packit d7e8d0
                    // if there's a result, write it:
Packit d7e8d0
                    if (*result) {
Packit d7e8d0
                        gpgme_err_set_errno(0);
Packit d7e8d0
                        const ssize_t len = std::strlen(result);
Packit d7e8d0
                        if (writeAll(fd, result, len) != len) {
Packit d7e8d0
                            err = Error::fromSystemError();
Packit d7e8d0
                            if (ei->debug) {
Packit d7e8d0
                                std::fprintf(ei->debug, "EditInteractor: Could not write to fd %d (%s)\n", fd, err.asString());
Packit d7e8d0
                            }
Packit d7e8d0
                            goto error;
Packit d7e8d0
                        }
Packit d7e8d0
                    }
Packit d7e8d0
                    gpgme_err_set_errno(0);
Packit d7e8d0
                    if (writeAll(fd, "\n", 1) != 1) {
Packit d7e8d0
                        err = Error::fromSystemError();
Packit d7e8d0
                        if (ei->debug) {
Packit d7e8d0
                            std::fprintf(ei->debug, "EditInteractor: Could not write to fd %d (%s)\n", fd, err.asString());
Packit d7e8d0
                        }
Packit d7e8d0
                        goto error;
Packit d7e8d0
                    }
Packit d7e8d0
                } else {
Packit d7e8d0
                    if (err) {
Packit d7e8d0
                        goto error;
Packit d7e8d0
                    }
Packit d7e8d0
                    if (ei->debug) {
Packit d7e8d0
                        std::fprintf(ei->debug, "EditInteractor: no action result\n");
Packit d7e8d0
                    }
Packit d7e8d0
                }
Packit d7e8d0
            } else {
Packit d7e8d0
                if (ei->debug) {
Packit d7e8d0
                    std::fprintf(ei->debug, "EditInteractor: no action executed\n");
Packit d7e8d0
                }
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
    error:
Packit d7e8d0
        if (err) {
Packit d7e8d0
            ei->error = err;
Packit d7e8d0
            ei->state = EditInteractor::ErrorState;
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
        if (ei->debug) {
Packit d7e8d0
            std::fprintf(ei->debug, "EditInteractor: error now %u (%s)\n",
Packit d7e8d0
                         ei->error.encodedError(), gpgme_strerror(ei->error.encodedError()));
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
        return ei->error.encodedError();
Packit d7e8d0
    }
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t edit_interactor_callback(void *opaque, gpgme_status_code_t status, const char *args, int fd)
Packit d7e8d0
{
Packit d7e8d0
    return CallbackHelper::edit_interactor_callback_impl(opaque, status, args, fd);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
const gpgme_edit_cb_t GpgME::edit_interactor_callback = ::edit_interactor_callback;
Packit d7e8d0
Packit d7e8d0
EditInteractor::Private::Private(EditInteractor *qq)
Packit d7e8d0
    : q(qq),
Packit d7e8d0
      state(StartState),
Packit d7e8d0
      error(),
Packit Service 30b792
      debug(nullptr)
Packit d7e8d0
{
Packit d7e8d0
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
EditInteractor::Private::~Private() {}
Packit d7e8d0
Packit d7e8d0
EditInteractor::EditInteractor()
Packit d7e8d0
    : d(new Private(this))
Packit d7e8d0
{
Packit d7e8d0
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
EditInteractor::~EditInteractor()
Packit d7e8d0
{
Packit d7e8d0
    delete d;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
unsigned int EditInteractor::state() const
Packit d7e8d0
{
Packit d7e8d0
    return d->state;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Error EditInteractor::lastError() const
Packit d7e8d0
{
Packit d7e8d0
    return d->error;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
bool EditInteractor::needsNoResponse(unsigned int status) const
Packit d7e8d0
{
Packit d7e8d0
    switch (status) {
Packit d7e8d0
    case GPGME_STATUS_ALREADY_SIGNED:
Packit d7e8d0
    case GPGME_STATUS_ERROR:
Packit d7e8d0
    case GPGME_STATUS_GET_BOOL:
Packit d7e8d0
    case GPGME_STATUS_GET_LINE:
Packit d7e8d0
    case GPGME_STATUS_KEY_CREATED:
Packit d7e8d0
    case GPGME_STATUS_NEED_PASSPHRASE_SYM:
Packit d7e8d0
    case GPGME_STATUS_SC_OP_FAILURE:
Packit d7e8d0
    case GPGME_STATUS_CARDCTRL:
Packit d7e8d0
    case GPGME_STATUS_BACKUP_KEY_CREATED:
Packit d7e8d0
        return false;
Packit d7e8d0
    default:
Packit d7e8d0
        return true;
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
// static
Packit d7e8d0
Error status_to_error(unsigned int status)
Packit d7e8d0
{
Packit d7e8d0
    switch (status) {
Packit d7e8d0
    case GPGME_STATUS_MISSING_PASSPHRASE:
Packit d7e8d0
        return Error::fromCode(GPG_ERR_NO_PASSPHRASE);
Packit d7e8d0
    case GPGME_STATUS_ALREADY_SIGNED:
Packit d7e8d0
        return Error::fromCode(GPG_ERR_ALREADY_SIGNED);
Packit d7e8d0
    case GPGME_STATUS_SIGEXPIRED:
Packit d7e8d0
        return Error::fromCode(GPG_ERR_SIG_EXPIRED);
Packit d7e8d0
    }
Packit d7e8d0
    return Error();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
void EditInteractor::setDebugChannel(std::FILE *debug)
Packit d7e8d0
{
Packit d7e8d0
    d->debug = debug;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static const char *const status_strings[] = {
Packit d7e8d0
    "EOF",
Packit d7e8d0
    /* mkstatus processing starts here */
Packit d7e8d0
    "ENTER",
Packit d7e8d0
    "LEAVE",
Packit d7e8d0
    "ABORT",
Packit d7e8d0
Packit d7e8d0
    "GOODSIG",
Packit d7e8d0
    "BADSIG",
Packit d7e8d0
    "ERRSIG",
Packit d7e8d0
Packit d7e8d0
    "BADARMOR",
Packit d7e8d0
Packit d7e8d0
    "RSA_OR_IDEA",
Packit d7e8d0
    "KEYEXPIRED",
Packit d7e8d0
    "KEYREVOKED",
Packit d7e8d0
Packit d7e8d0
    "TRUST_UNDEFINED",
Packit d7e8d0
    "TRUST_NEVER",
Packit d7e8d0
    "TRUST_MARGINAL",
Packit d7e8d0
    "TRUST_FULLY",
Packit d7e8d0
    "TRUST_ULTIMATE",
Packit d7e8d0
Packit d7e8d0
    "SHM_INFO",
Packit d7e8d0
    "SHM_GET",
Packit d7e8d0
    "SHM_GET_BOOL",
Packit d7e8d0
    "SHM_GET_HIDDEN",
Packit d7e8d0
Packit d7e8d0
    "NEED_PASSPHRASE",
Packit d7e8d0
    "VALIDSIG",
Packit d7e8d0
    "SIG_ID",
Packit d7e8d0
    "ENC_TO",
Packit d7e8d0
    "NODATA",
Packit d7e8d0
    "BAD_PASSPHRASE",
Packit d7e8d0
    "NO_PUBKEY",
Packit d7e8d0
    "NO_SECKEY",
Packit d7e8d0
    "NEED_PASSPHRASE_SYM",
Packit d7e8d0
    "DECRYPTION_FAILED",
Packit d7e8d0
    "DECRYPTION_OKAY",
Packit d7e8d0
    "MISSING_PASSPHRASE",
Packit d7e8d0
    "GOOD_PASSPHRASE",
Packit d7e8d0
    "GOODMDC",
Packit d7e8d0
    "BADMDC",
Packit d7e8d0
    "ERRMDC",
Packit d7e8d0
    "IMPORTED",
Packit d7e8d0
    "IMPORT_OK",
Packit d7e8d0
    "IMPORT_PROBLEM",
Packit d7e8d0
    "IMPORT_RES",
Packit d7e8d0
    "FILE_START",
Packit d7e8d0
    "FILE_DONE",
Packit d7e8d0
    "FILE_ERROR",
Packit d7e8d0
Packit d7e8d0
    "BEGIN_DECRYPTION",
Packit d7e8d0
    "END_DECRYPTION",
Packit d7e8d0
    "BEGIN_ENCRYPTION",
Packit d7e8d0
    "END_ENCRYPTION",
Packit d7e8d0
Packit d7e8d0
    "DELETE_PROBLEM",
Packit d7e8d0
    "GET_BOOL",
Packit d7e8d0
    "GET_LINE",
Packit d7e8d0
    "GET_HIDDEN",
Packit d7e8d0
    "GOT_IT",
Packit d7e8d0
    "PROGRESS",
Packit d7e8d0
    "SIG_CREATED",
Packit d7e8d0
    "SESSION_KEY",
Packit d7e8d0
    "NOTATION_NAME",
Packit d7e8d0
    "NOTATION_DATA",
Packit d7e8d0
    "POLICY_URL",
Packit d7e8d0
    "BEGIN_STREAM",
Packit d7e8d0
    "END_STREAM",
Packit d7e8d0
    "KEY_CREATED",
Packit d7e8d0
    "USERID_HINT",
Packit d7e8d0
    "UNEXPECTED",
Packit d7e8d0
    "INV_RECP",
Packit d7e8d0
    "NO_RECP",
Packit d7e8d0
    "ALREADY_SIGNED",
Packit d7e8d0
    "SIGEXPIRED",
Packit d7e8d0
    "EXPSIG",
Packit d7e8d0
    "EXPKEYSIG",
Packit d7e8d0
    "TRUNCATED",
Packit d7e8d0
    "ERROR",
Packit d7e8d0
    "NEWSIG",
Packit d7e8d0
    "REVKEYSIG",
Packit d7e8d0
    "SIG_SUBPACKET",
Packit d7e8d0
    "NEED_PASSPHRASE_PIN",
Packit d7e8d0
    "SC_OP_FAILURE",
Packit d7e8d0
    "SC_OP_SUCCESS",
Packit d7e8d0
    "CARDCTRL",
Packit d7e8d0
    "BACKUP_KEY_CREATED",
Packit d7e8d0
    "PKA_TRUST_BAD",
Packit d7e8d0
    "PKA_TRUST_GOOD",
Packit d7e8d0
Packit d7e8d0
    "PLAINTEXT",
Packit d7e8d0
};
Packit d7e8d0
static const unsigned int num_status_strings = sizeof status_strings / sizeof * status_strings ;
Packit d7e8d0
Packit d7e8d0
const char *status_to_string(unsigned int idx)
Packit d7e8d0
{
Packit d7e8d0
    if (idx < num_status_strings) {
Packit d7e8d0
        return status_strings[idx];
Packit d7e8d0
    } else {
Packit d7e8d0
        return "(unknown)";
Packit d7e8d0
    }
Packit d7e8d0
}