Page MenuHome GnuPG

No OneTemporary

diff --git a/src/ui/newkeyapprovaldialog.cpp b/src/ui/newkeyapprovaldialog.cpp
index 247ff1fa..17a2b096 100644
--- a/src/ui/newkeyapprovaldialog.cpp
+++ b/src/ui/newkeyapprovaldialog.cpp
@@ -1,917 +1,934 @@
/* -*- c++ -*-
newkeyapprovaldialog.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2018 Intevation GmbH
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "newkeyapprovaldialog.h"
#include "keyselectioncombo.h"
#include "progressdialog.h"
#include <libkleo/algorithm.h>
#include <libkleo/compliance.h>
#include <libkleo/debug.h>
#include <libkleo/defaultkeyfilter.h>
#include <libkleo/formatting.h>
#include <libkleo/gnupg.h>
#include <libkleo/keyhelpers.h>
#include <libkleo/systeminfo.h>
#include <libkleo_debug.h>
#include <KColorScheme>
#include <KLocalizedString>
#include <KMessageBox>
#include <QGpgME/Protocol>
#include <QGpgME/QuickJob>
#include <QButtonGroup>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QMap>
#include <QPushButton>
#include <QRadioButton>
#include <QScreen>
#include <QScrollArea>
#include <QToolTip>
#include <QVBoxLayout>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
#include <gpgme++/keygenerationresult.h>
using namespace Kleo;
using namespace GpgME;
namespace
{
class EncryptFilter : public DefaultKeyFilter
{
public:
EncryptFilter()
: DefaultKeyFilter()
{
setHasEncrypt(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_encryptFilter = std::shared_ptr<KeyFilter>(new EncryptFilter);
class OpenPGPFilter : public DefaultKeyFilter
{
public:
OpenPGPFilter()
: DefaultKeyFilter()
{
setIsOpenPGP(DefaultKeyFilter::Set);
setHasEncrypt(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_pgpEncryptFilter = std::shared_ptr<KeyFilter>(new OpenPGPFilter);
class OpenPGPSignFilter : public DefaultKeyFilter
{
public:
OpenPGPSignFilter()
: DefaultKeyFilter()
{
/* Also list unusable keys to make it transparent why they are unusable */
setDisabled(DefaultKeyFilter::NotSet);
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setCanSign(DefaultKeyFilter::Set);
setHasSecret(DefaultKeyFilter::Set);
setIsOpenPGP(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_pgpSignFilter = std::shared_ptr<KeyFilter>(new OpenPGPSignFilter);
class SMIMEFilter : public DefaultKeyFilter
{
public:
SMIMEFilter()
: DefaultKeyFilter()
{
setIsOpenPGP(DefaultKeyFilter::NotSet);
setHasEncrypt(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_smimeEncryptFilter = std::shared_ptr<KeyFilter>(new SMIMEFilter);
class SMIMESignFilter : public DefaultKeyFilter
{
public:
SMIMESignFilter()
: DefaultKeyFilter()
{
setDisabled(DefaultKeyFilter::NotSet);
setRevoked(DefaultKeyFilter::NotSet);
setExpired(DefaultKeyFilter::NotSet);
setCanSign(DefaultKeyFilter::Set);
setIsOpenPGP(DefaultKeyFilter::NotSet);
setHasSecret(DefaultKeyFilter::Set);
}
};
static std::shared_ptr<KeyFilter> s_smimeSignFilter = std::shared_ptr<KeyFilter>(new SMIMESignFilter);
/* Some decoration and a button to remove the filter for a keyselectioncombo */
class ComboWidget : public QWidget
{
Q_OBJECT
public:
explicit ComboWidget(KeySelectionCombo *combo)
: mCombo(combo)
, mFilterBtn(new QPushButton)
{
auto hLay = new QHBoxLayout(this);
auto infoBtn = new QPushButton;
infoBtn->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual")));
infoBtn->setIconSize(QSize(22, 22));
infoBtn->setFlat(true);
infoBtn->setAccessibleName(i18nc("@action:button", "Show Details"));
hLay->addWidget(infoBtn);
hLay->addWidget(combo, 1);
hLay->addWidget(mFilterBtn, 0);
connect(infoBtn, &QPushButton::clicked, this, [this, infoBtn]() {
QToolTip::showText(infoBtn->mapToGlobal(QPoint()) + QPoint(infoBtn->width(), 0),
mCombo->currentData(Qt::ToolTipRole).toString(),
infoBtn,
QRect(),
30000);
});
// FIXME: This is ugly to enforce but otherwise the
// icon is broken.
combo->setMinimumHeight(22);
mFilterBtn->setMinimumHeight(23);
updateFilterButton();
connect(mFilterBtn, &QPushButton::clicked, this, [this]() {
const QString curFilter = mCombo->idFilter();
if (curFilter.isEmpty()) {
setIdFilter(mLastIdFilter);
mLastIdFilter = QString();
} else {
setIdFilter(QString());
mLastIdFilter = curFilter;
}
});
}
void setIdFilter(const QString &id)
{
mCombo->setIdFilter(id);
updateFilterButton();
}
void updateFilterButton()
{
if (mCombo->idFilter().isEmpty()) {
mFilterBtn->setIcon(QIcon::fromTheme(QStringLiteral("kt-add-filters")));
mFilterBtn->setAccessibleName(i18nc("@action:button", "Show Matching Keys"));
mFilterBtn->setToolTip(i18n("Show keys matching the email address"));
} else {
mFilterBtn->setIcon(QIcon::fromTheme(QStringLiteral("kt-remove-filters")));
mFilterBtn->setAccessibleName(i18nc("@action:button short for 'Show all keys'", "Show All"));
mFilterBtn->setToolTip(i18n("Show all keys"));
}
}
KeySelectionCombo *combo()
{
return mCombo;
}
GpgME::Protocol fixedProtocol() const
{
return mFixedProtocol;
}
void setFixedProtocol(GpgME::Protocol proto)
{
mFixedProtocol = proto;
}
private:
KeySelectionCombo *mCombo;
QPushButton *mFilterBtn;
QString mLastIdFilter;
GpgME::Protocol mFixedProtocol = GpgME::UnknownProtocol;
};
static bool key_has_addr(const GpgME::Key &key, const QString &addr)
{
for (const auto &uid : key.userIDs()) {
if (QString::fromStdString(uid.addrSpec()).toLower() == addr.toLower()) {
return true;
}
}
return false;
}
Key findfirstKeyOfType(const std::vector<Key> &keys, GpgME::Protocol protocol)
{
const auto it = std::find_if(std::begin(keys), std::end(keys), [protocol](const auto &key) {
return key.protocol() == protocol;
});
return it != std::end(keys) ? *it : Key();
}
} // namespace
class NewKeyApprovalDialog::Private
{
private:
enum Action {
Unset,
GenerateKey,
IgnoreKey,
};
public:
enum {
OpenPGPButtonId = 1,
SMIMEButtonId = 2,
};
Private(NewKeyApprovalDialog *qq,
bool encrypt,
bool sign,
GpgME::Protocol forcedProtocol,
GpgME::Protocol presetProtocol,
const QString &sender,
bool allowMixed)
: mForcedProtocol{forcedProtocol}
, mSender{sender}
, mSign{sign}
, mEncrypt{encrypt}
, mAllowMixed{allowMixed}
, q{qq}
{
Q_ASSERT(forcedProtocol == GpgME::UnknownProtocol || presetProtocol == GpgME::UnknownProtocol || presetProtocol == forcedProtocol);
Q_ASSERT(!allowMixed || (allowMixed && forcedProtocol == GpgME::UnknownProtocol));
Q_ASSERT(!(!allowMixed && presetProtocol == GpgME::UnknownProtocol));
// We do the translation here to avoid having the same string multiple times.
mGenerateTooltip = i18nc(
"@info:tooltip for a 'Generate new key pair' action "
"in a combobox when a user does not yet have an OpenPGP or S/MIME key.",
"Generate a new key using your E-Mail address.<br/><br/>"
"The key is necessary to decrypt and sign E-Mails. "
"You will be asked for a passphrase to protect this key and the protected key "
"will be stored in your home directory.");
mMainLay = new QVBoxLayout;
QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
mOkButton = btnBox->button(QDialogButtonBox::Ok);
#ifndef NDEBUG
mOkButton->setObjectName(QStringLiteral("ok button"));
#endif
QObject::connect(btnBox, &QDialogButtonBox::accepted, q, [this]() {
accepted();
});
QObject::connect(btnBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
mScrollArea = new QScrollArea;
mScrollArea->setWidget(new QWidget);
mScrollLayout = new QVBoxLayout;
mScrollArea->widget()->setLayout(mScrollLayout);
mScrollArea->setWidgetResizable(true);
mScrollArea->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
mScrollArea->setFrameStyle(QFrame::NoFrame);
mScrollLayout->setContentsMargins(0, 0, 0, 0);
q->setWindowTitle(i18nc("@title:window", "Security approval"));
auto fmtLayout = new QHBoxLayout;
mFormatBtns = new QButtonGroup(qq);
QAbstractButton *pgpBtn;
QAbstractButton *smimeBtn;
if (mAllowMixed) {
pgpBtn = new QCheckBox(i18n("OpenPGP"));
smimeBtn = new QCheckBox(i18n("S/MIME"));
} else {
pgpBtn = new QRadioButton(i18n("OpenPGP"));
smimeBtn = new QRadioButton(i18n("S/MIME"));
}
#ifndef NDEBUG
pgpBtn->setObjectName(QStringLiteral("openpgp button"));
smimeBtn->setObjectName(QStringLiteral("smime button"));
#endif
mFormatBtns->addButton(pgpBtn, OpenPGPButtonId);
mFormatBtns->addButton(smimeBtn, SMIMEButtonId);
mFormatBtns->setExclusive(!mAllowMixed);
connect(mFormatBtns, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), q, [this]() {
updateOkButton();
});
fmtLayout->addStretch(-1);
fmtLayout->addWidget(pgpBtn);
fmtLayout->addWidget(smimeBtn);
mMainLay->addLayout(fmtLayout);
if (mForcedProtocol != GpgME::UnknownProtocol) {
pgpBtn->setChecked(mForcedProtocol == GpgME::OpenPGP);
smimeBtn->setChecked(mForcedProtocol == GpgME::CMS);
pgpBtn->setVisible(false);
smimeBtn->setVisible(false);
} else {
pgpBtn->setChecked(presetProtocol == GpgME::OpenPGP || presetProtocol == GpgME::UnknownProtocol);
smimeBtn->setChecked(presetProtocol == GpgME::CMS || presetProtocol == GpgME::UnknownProtocol);
}
QObject::connect(mFormatBtns, &QButtonGroup::idClicked, q, [this](int buttonId) {
// ensure that at least one protocol button is checked
if (mAllowMixed && !mFormatBtns->button(OpenPGPButtonId)->isChecked() && !mFormatBtns->button(SMIMEButtonId)->isChecked()) {
mFormatBtns->button(buttonId == OpenPGPButtonId ? SMIMEButtonId : OpenPGPButtonId)->setChecked(true);
}
updateWidgets();
});
mMainLay->addWidget(mScrollArea);
mComplianceLbl = new QLabel;
mComplianceLbl->setVisible(false);
#ifndef NDEBUG
mComplianceLbl->setObjectName(QStringLiteral("compliance label"));
#endif
auto btnLayout = new QHBoxLayout;
btnLayout->addWidget(mComplianceLbl);
btnLayout->addWidget(btnBox);
mMainLay->addLayout(btnLayout);
q->setLayout(mMainLay);
}
~Private() = default;
GpgME::Protocol currentProtocol()
{
const bool openPGPButtonChecked = mFormatBtns->button(OpenPGPButtonId)->isChecked();
const bool smimeButtonChecked = mFormatBtns->button(SMIMEButtonId)->isChecked();
if (mAllowMixed) {
if (openPGPButtonChecked && !smimeButtonChecked) {
return GpgME::OpenPGP;
}
if (!openPGPButtonChecked && smimeButtonChecked) {
return GpgME::CMS;
}
} else if (openPGPButtonChecked) {
return GpgME::OpenPGP;
} else if (smimeButtonChecked) {
return GpgME::CMS;
}
return GpgME::UnknownProtocol;
}
auto findVisibleKeySelectionComboWithGenerateKey()
{
const auto it = std::find_if(std::begin(mAllCombos), std::end(mAllCombos), [](auto combo) {
return combo->isVisible() && combo->currentData(Qt::UserRole).toInt() == GenerateKey;
});
return it != std::end(mAllCombos) ? *it : nullptr;
}
void generateKey(KeySelectionCombo *combo)
{
+ if (!mRunningJobs.empty()) {
+ return;
+ }
const auto &addr = combo->property("address").toString();
auto job = QGpgME::openpgp()->quickJob();
auto progress =
new Kleo::ProgressDialog(job, i18n("Generating key for '%1'...", addr) + QStringLiteral("\n\n") + i18n("This can take several minutes."), q);
progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowContextHelpButtonHint);
progress->setWindowTitle(i18nc("@title:window", "Key generation"));
progress->setModal(true);
progress->setAutoClose(true);
progress->setMinimumDuration(0);
progress->setValue(0);
mRunningJobs << job;
- connect(job, &QGpgME::QuickJob::result, q, [this, job, combo]() {
- handleKeyGenResult(QGpgME::Job::context(job)->keyGenerationResult(), job, combo);
- });
+ if (!connect(job, &QGpgME::QuickJob::result, q, [this, job, combo]() {
+ handleKeyGenResult(QGpgME::Job::context(job)->keyGenerationResult(), job, combo);
+ })) {
+ qCWarning(LIBKLEO_LOG) << "new-style connect failed; connecting to QGpgME::QuickJob::result the old way";
+ connect(job, SIGNAL(result(const GpgME::Error &)), q, SLOT(handleKeyGenResult()));
+ }
job->startCreate(addr, nullptr);
return;
}
void handleKeyGenResult(const GpgME::KeyGenerationResult &result, QGpgME::Job *job, KeySelectionCombo *combo)
{
mLastError = result.error();
if (!mLastError) {
connect(combo, &KeySelectionCombo::keyListingFinished, q, [this, job]() {
mRunningJobs.removeAll(job);
});
// update all combos showing the GenerateKey item
for (auto c : std::as_const(mAllCombos)) {
if (c->currentData(Qt::UserRole).toInt() == GenerateKey) {
c->setDefaultKey(QString::fromLatin1(result.fingerprint()), GpgME::OpenPGP);
c->refreshKeys();
}
}
} else {
mRunningJobs.removeAll(job);
}
}
void checkAccepted()
{
if (mLastError) {
KMessageBox::error(q, Formatting::errorAsString(mLastError), i18n("Operation Failed"));
mRunningJobs.clear();
return;
}
if (!mRunningJobs.empty()) {
return;
}
/* Save the keys */
mAcceptedResult.protocol = currentProtocol();
for (const auto combo : std::as_const(mEncCombos)) {
const auto addr = combo->property("address").toString();
const auto key = combo->currentKey();
if (!combo->isVisible() || key.isNull()) {
continue;
}
mAcceptedResult.encryptionKeys[addr].push_back(key);
}
for (const auto combo : std::as_const(mSigningCombos)) {
const auto key = combo->currentKey();
if (!combo->isVisible() || key.isNull()) {
continue;
}
mAcceptedResult.signingKeys.push_back(key);
}
q->accept();
}
void accepted()
{
// We can assume everything was validly resolved, otherwise
// the OK button would have been disabled.
// Handle custom items now.
if (auto combo = findVisibleKeySelectionComboWithGenerateKey()) {
generateKey(combo);
return;
}
checkAccepted();
}
auto encryptionKeyFilter(GpgME::Protocol protocol)
{
switch (protocol) {
case OpenPGP:
return s_pgpEncryptFilter;
case CMS:
return s_smimeEncryptFilter;
default:
return s_encryptFilter;
}
}
void updateWidgets()
{
const GpgME::Protocol protocol = currentProtocol();
const auto encryptionFilter = encryptionKeyFilter(protocol);
for (auto combo : std::as_const(mSigningCombos)) {
auto widget = qobject_cast<ComboWidget *>(combo->parentWidget());
if (!widget) {
qCDebug(LIBKLEO_LOG) << "Failed to find signature combo widget";
continue;
}
widget->setVisible(protocol == GpgME::UnknownProtocol || widget->fixedProtocol() == GpgME::UnknownProtocol || widget->fixedProtocol() == protocol);
}
for (auto combo : std::as_const(mEncCombos)) {
auto widget = qobject_cast<ComboWidget *>(combo->parentWidget());
if (!widget) {
qCDebug(LIBKLEO_LOG) << "Failed to find combo widget";
continue;
}
widget->setVisible(protocol == GpgME::UnknownProtocol || widget->fixedProtocol() == GpgME::UnknownProtocol || widget->fixedProtocol() == protocol);
if (widget->isVisible() && combo->property("address") != mSender) {
combo->setKeyFilter(encryptionFilter);
}
}
// hide the labels indicating the protocol of the sender's keys if only a single protocol is active
const auto protocolLabels = q->findChildren<QLabel *>(QStringLiteral("protocol label"));
for (auto label : protocolLabels) {
label->setVisible(protocol == GpgME::UnknownProtocol);
}
}
auto createProtocolLabel(GpgME::Protocol protocol)
{
auto label = new QLabel(Formatting::displayName(protocol));
label->setObjectName(QStringLiteral("protocol label"));
return label;
}
ComboWidget *createSigningCombo(const QString &addr, const GpgME::Key &key, GpgME::Protocol protocol = GpgME::UnknownProtocol)
{
Q_ASSERT(!key.isNull() || protocol != UnknownProtocol);
protocol = !key.isNull() ? key.protocol() : protocol;
auto combo = new KeySelectionCombo{true, KeyUsage::Sign};
auto comboWidget = new ComboWidget(combo);
#ifndef NDEBUG
combo->setObjectName(QStringLiteral("signing key"));
#endif
if (protocol == GpgME::OpenPGP) {
combo->setKeyFilter(s_pgpSignFilter);
} else if (protocol == GpgME::CMS) {
combo->setKeyFilter(s_smimeSignFilter);
}
if (key.isNull() || key_has_addr(key, mSender)) {
comboWidget->setIdFilter(mSender);
}
comboWidget->setFixedProtocol(protocol);
if (!key.isNull()) {
combo->setDefaultKey(QString::fromLatin1(key.primaryFingerprint()), protocol);
}
if (key.isNull() && protocol == OpenPGP) {
combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Generate a new key pair"), GenerateKey, mGenerateTooltip);
}
combo->appendCustomItem(Formatting::unavailableIcon(),
i18n("Don't confirm identity and integrity"),
IgnoreKey,
i18nc("@info:tooltip for not selecting a key for signing.", "The E-Mail will not be cryptographically signed."));
mSigningCombos << combo;
mAllCombos << combo;
combo->setProperty("address", addr);
connect(combo, &KeySelectionCombo::currentKeyChanged, q, [this]() {
updateOkButton();
});
connect(combo, qOverload<int>(&QComboBox::currentIndexChanged), q, [this]() {
updateOkButton();
});
return comboWidget;
}
void setSigningKeys(const std::vector<GpgME::Key> &preferredKeys, const std::vector<GpgME::Key> &alternativeKeys)
{
auto group = new QGroupBox(i18nc("Caption for signing key selection", "Confirm identity '%1' as:", mSender));
group->setAlignment(Qt::AlignLeft);
auto sigLayout = new QVBoxLayout(group);
const bool mayNeedOpenPGP = mForcedProtocol != CMS;
const bool mayNeedCMS = mForcedProtocol != OpenPGP;
if (mayNeedOpenPGP) {
if (mAllowMixed) {
sigLayout->addWidget(createProtocolLabel(OpenPGP));
}
const Key preferredKey = findfirstKeyOfType(preferredKeys, OpenPGP);
const Key alternativeKey = findfirstKeyOfType(alternativeKeys, OpenPGP);
if (!preferredKey.isNull()) {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << preferredKey;
auto comboWidget = createSigningCombo(mSender, preferredKey);
sigLayout->addWidget(comboWidget);
} else if (!alternativeKey.isNull()) {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << alternativeKey;
auto comboWidget = createSigningCombo(mSender, alternativeKey);
sigLayout->addWidget(comboWidget);
} else {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for OpenPGP key";
auto comboWidget = createSigningCombo(mSender, Key(), OpenPGP);
sigLayout->addWidget(comboWidget);
}
}
if (mayNeedCMS) {
if (mAllowMixed) {
sigLayout->addWidget(createProtocolLabel(CMS));
}
const Key preferredKey = findfirstKeyOfType(preferredKeys, CMS);
const Key alternativeKey = findfirstKeyOfType(alternativeKeys, CMS);
if (!preferredKey.isNull()) {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << preferredKey;
auto comboWidget = createSigningCombo(mSender, preferredKey);
sigLayout->addWidget(comboWidget);
} else if (!alternativeKey.isNull()) {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << alternativeKey;
auto comboWidget = createSigningCombo(mSender, alternativeKey);
sigLayout->addWidget(comboWidget);
} else {
qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for S/MIME key";
auto comboWidget = createSigningCombo(mSender, Key(), CMS);
sigLayout->addWidget(comboWidget);
}
}
mScrollLayout->addWidget(group);
}
ComboWidget *createEncryptionCombo(const QString &addr, const GpgME::Key &key, GpgME::Protocol fixedProtocol)
{
auto combo = new KeySelectionCombo{false, KeyUsage::Encrypt};
auto comboWidget = new ComboWidget(combo);
#ifndef NDEBUG
combo->setObjectName(QStringLiteral("encryption key"));
#endif
if (fixedProtocol == GpgME::OpenPGP) {
combo->setKeyFilter(s_pgpEncryptFilter);
} else if (fixedProtocol == GpgME::CMS) {
combo->setKeyFilter(s_smimeEncryptFilter);
} else {
combo->setKeyFilter(s_encryptFilter);
}
if (key.isNull() || key_has_addr(key, addr)) {
comboWidget->setIdFilter(addr);
}
comboWidget->setFixedProtocol(fixedProtocol);
if (!key.isNull()) {
combo->setDefaultKey(QString::fromLatin1(key.primaryFingerprint()), fixedProtocol);
}
if (addr == mSender && key.isNull() && fixedProtocol == OpenPGP) {
combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Generate a new key pair"), GenerateKey, mGenerateTooltip);
}
combo->appendCustomItem(Formatting::unavailableIcon(),
i18n("No key. Recipient will be unable to decrypt."),
IgnoreKey,
i18nc("@info:tooltip for No Key selected for a specific recipient.",
"Do not select a key for this recipient.<br/><br/>"
"The recipient will receive the encrypted E-Mail, but it can only "
"be decrypted with the other keys selected in this dialog."));
connect(combo, &KeySelectionCombo::currentKeyChanged, q, [this]() {
updateOkButton();
});
connect(combo, qOverload<int>(&QComboBox::currentIndexChanged), q, [this]() {
updateOkButton();
});
mEncCombos << combo;
mAllCombos << combo;
combo->setProperty("address", addr);
return comboWidget;
}
void addEncryptionAddr(const QString &addr,
GpgME::Protocol preferredKeysProtocol,
const std::vector<GpgME::Key> &preferredKeys,
GpgME::Protocol alternativeKeysProtocol,
const std::vector<GpgME::Key> &alternativeKeys,
QGridLayout *encGrid)
{
if (addr == mSender) {
const bool mayNeedOpenPGP = mForcedProtocol != CMS;
const bool mayNeedCMS = mForcedProtocol != OpenPGP;
if (mayNeedOpenPGP) {
if (mAllowMixed) {
encGrid->addWidget(createProtocolLabel(OpenPGP), encGrid->rowCount(), 0);
}
for (const auto &key : preferredKeys) {
if (key.protocol() == OpenPGP) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, OpenPGP);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
for (const auto &key : alternativeKeys) {
if (key.protocol() == OpenPGP) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, OpenPGP);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
if (!anyKeyHasProtocol(preferredKeys, OpenPGP) && !anyKeyHasProtocol(alternativeKeys, OpenPGP)) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for OpenPGP key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), OpenPGP);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
if (mayNeedCMS) {
if (mAllowMixed) {
encGrid->addWidget(createProtocolLabel(CMS), encGrid->rowCount(), 0);
}
for (const auto &key : preferredKeys) {
if (key.protocol() == CMS) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, CMS);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
for (const auto &key : alternativeKeys) {
if (key.protocol() == CMS) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, CMS);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
if (!anyKeyHasProtocol(preferredKeys, CMS) && !anyKeyHasProtocol(alternativeKeys, CMS)) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for S/MIME key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), CMS);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
} else {
encGrid->addWidget(new QLabel(addr), encGrid->rowCount(), 0);
for (const auto &key : preferredKeys) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, preferredKeysProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
for (const auto &key : alternativeKeys) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
auto comboWidget = createEncryptionCombo(addr, key, alternativeKeysProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
if (!mAllowMixed) {
if (preferredKeys.empty()) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << Formatting::displayName(preferredKeysProtocol)
<< "key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), preferredKeysProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
if (alternativeKeys.empty() && alternativeKeysProtocol != UnknownProtocol) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for"
<< Formatting::displayName(alternativeKeysProtocol) << "key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), alternativeKeysProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
} else {
if (preferredKeys.empty() && alternativeKeys.empty()) {
qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for any key";
auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), UnknownProtocol);
encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
}
}
}
}
void setEncryptionKeys(GpgME::Protocol preferredKeysProtocol,
const QMap<QString, std::vector<GpgME::Key>> &preferredKeys,
GpgME::Protocol alternativeKeysProtocol,
const QMap<QString, std::vector<GpgME::Key>> &alternativeKeys)
{
{
auto group = new QGroupBox(i18nc("Encrypt to self (email address):", "Encrypt to self (%1):", mSender));
#ifndef NDEBUG
group->setObjectName(QStringLiteral("encrypt-to-self box"));
#endif
group->setAlignment(Qt::AlignLeft);
auto encGrid = new QGridLayout(group);
addEncryptionAddr(mSender, preferredKeysProtocol, preferredKeys.value(mSender), alternativeKeysProtocol, alternativeKeys.value(mSender), encGrid);
encGrid->setColumnStretch(1, -1);
mScrollLayout->addWidget(group);
}
const bool hasOtherRecipients = std::any_of(preferredKeys.keyBegin(), preferredKeys.keyEnd(), [this](const auto &recipient) {
return recipient != mSender;
});
if (hasOtherRecipients) {
auto group = new QGroupBox(i18n("Encrypt to others:"));
#ifndef NDEBUG
group->setObjectName(QStringLiteral("encrypt-to-others box"));
#endif
group->setAlignment(Qt::AlignLeft);
auto encGrid = new QGridLayout{group};
for (auto it = std::begin(preferredKeys); it != std::end(preferredKeys); ++it) {
const auto &address = it.key();
const auto &keys = it.value();
if (address != mSender) {
addEncryptionAddr(address, preferredKeysProtocol, keys, alternativeKeysProtocol, alternativeKeys.value(address), encGrid);
}
}
encGrid->setColumnStretch(1, -1);
mScrollLayout->addWidget(group);
}
mScrollLayout->addStretch(-1);
}
void updateOkButton()
{
static QString origOkText = mOkButton->text();
const bool isGenerate = bool(findVisibleKeySelectionComboWithGenerateKey());
const bool allVisibleEncryptionKeysAreIgnored = Kleo::all_of(mEncCombos, [](auto combo) {
return !combo->isVisible() || combo->currentData(Qt::UserRole).toInt() == IgnoreKey;
});
const bool allVisibleEncryptionKeysAreUsable = Kleo::all_of(mEncCombos, [](auto combo) {
return !combo->isVisible() || combo->currentKey().isNull() || Kleo::canBeUsedForEncryption(combo->currentKey());
});
// If we don't encrypt, then the OK button is always enabled. Likewise,
// if the "generate key" option is selected. Otherwise,
// we only enable it if we encrypt to at least one recipient.
mOkButton->setEnabled(isGenerate || !mEncrypt || (!allVisibleEncryptionKeysAreIgnored && allVisibleEncryptionKeysAreUsable));
mOkButton->setText(isGenerate ? i18n("Generate") : origOkText);
if (!DeVSCompliance::isActive()) {
return;
}
// Handle compliance
bool de_vs = DeVSCompliance::isCompliant();
if (de_vs) {
const GpgME::Protocol protocol = currentProtocol();
for (const auto combo : std::as_const(mAllCombos)) {
if (!combo->isVisible()) {
continue;
}
const auto key = combo->currentKey();
if (key.isNull()) {
continue;
}
if (protocol != GpgME::UnknownProtocol && key.protocol() != protocol) {
continue;
}
if (!DeVSCompliance::keyIsCompliant(key)) {
de_vs = false;
break;
}
}
}
DeVSCompliance::decorate(mOkButton, de_vs);
mComplianceLbl->setText(DeVSCompliance::name(de_vs));
mComplianceLbl->setVisible(true);
}
GpgME::Protocol mForcedProtocol;
QList<KeySelectionCombo *> mSigningCombos;
QList<KeySelectionCombo *> mEncCombos;
QList<KeySelectionCombo *> mAllCombos;
QScrollArea *mScrollArea;
QVBoxLayout *mScrollLayout;
QPushButton *mOkButton;
QVBoxLayout *mMainLay;
QButtonGroup *mFormatBtns;
QString mSender;
bool mSign;
bool mEncrypt;
bool mAllowMixed;
NewKeyApprovalDialog *q;
QList<QGpgME::Job *> mRunningJobs;
GpgME::Error mLastError;
QLabel *mComplianceLbl;
KeyResolver::Solution mAcceptedResult;
QString mGenerateTooltip;
};
NewKeyApprovalDialog::NewKeyApprovalDialog(bool encrypt,
bool sign,
const QString &sender,
KeyResolver::Solution preferredSolution,
KeyResolver::Solution alternativeSolution,
bool allowMixed,
GpgME::Protocol forcedProtocol,
QWidget *parent,
Qt::WindowFlags f)
: QDialog(parent, f)
, d{std::make_unique<Private>(this, encrypt, sign, forcedProtocol, preferredSolution.protocol, sender, allowMixed)}
{
if (sign) {
d->setSigningKeys(std::move(preferredSolution.signingKeys), std::move(alternativeSolution.signingKeys));
}
if (encrypt) {
d->setEncryptionKeys(allowMixed ? UnknownProtocol : preferredSolution.protocol,
std::move(preferredSolution.encryptionKeys),
allowMixed ? UnknownProtocol : alternativeSolution.protocol,
std::move(alternativeSolution.encryptionKeys));
}
d->updateWidgets();
d->updateOkButton();
const auto size = sizeHint();
const auto desk = screen()->size();
resize(QSize(desk.width() / 3, qMin(size.height(), desk.height() / 2)));
}
Kleo::NewKeyApprovalDialog::~NewKeyApprovalDialog() = default;
KeyResolver::Solution NewKeyApprovalDialog::result()
{
return d->mAcceptedResult;
}
+void NewKeyApprovalDialog::handleKeyGenResult()
+{
+ if (d->mRunningJobs.empty()) {
+ qCWarning(LIBKLEO_LOG) << __func__ << "No running job";
+ }
+ const auto job = d->mRunningJobs.front();
+ const auto result = QGpgME::Job::context(job)->keyGenerationResult();
+ const auto combo = d->findVisibleKeySelectionComboWithGenerateKey();
+ d->handleKeyGenResult(result, job, combo);
+}
+
#include "newkeyapprovaldialog.moc"
#include "moc_newkeyapprovaldialog.cpp"
diff --git a/src/ui/newkeyapprovaldialog.h b/src/ui/newkeyapprovaldialog.h
index 9eccb416..96a382db 100644
--- a/src/ui/newkeyapprovaldialog.h
+++ b/src/ui/newkeyapprovaldialog.h
@@ -1,79 +1,82 @@
/* -*- c++ -*-
newkeyapprovaldialog.h
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2018 Intevation GmbH
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <Libkleo/KeyResolver>
#include <QDialog>
#include <memory>
namespace Kleo
{
/** @brief A dialog to show for encryption / signing key approval or selection.
*
* This class is intended to replace the old KeyApprovalDialog with a new
* and simpler interface.
*
* Resolved recipients in this API means a recipient could be resolved
* to a single useful key. An unresolved recipient is a recipient for
* whom no key could be found. Import / Search will be offered for such
* a recipient. Multiple keys for signing / recipient can come e.g. from
* group configuration or Addressbook / Identity configuration.
*
* The Dialog uses the Level System for validity display and shows an
* overall outgoing level.
*
*/
class KLEO_EXPORT NewKeyApprovalDialog : public QDialog
{
Q_OBJECT
public:
/** @brief Create a new Key Approval Dialog.
*
* @param sender: The address of the sender, this may be used if signing is not
* specified to identify a recipient for which "Generate Key" should
* be offered.
* @param preferredSolution: The preferred signing and/or encryption keys for the sender
* and the recipients.
* @param alternativeSolution: An alternative set of signing and/or encryption keys for the sender
* and the recipients. Typically, S/MIME-only, if preferred solution is OpenPGP-only,
* and vice versa. Ignored, if mixed protocol selection is allowed.
* @param allowMixed: Whether or not the dialog should allow mixed S/MIME / OpenPGP key selection.
* @param forcedProtocol: A specific forced protocol.
* @param parent: The parent widget.
* @param f: The Qt window flags.
*/
explicit NewKeyApprovalDialog(bool encrypt,
bool sign,
const QString &sender,
KeyResolver::Solution preferredSolution,
KeyResolver::Solution alternativeSolution,
bool allowMixed,
GpgME::Protocol forcedProtocol,
QWidget *parent = nullptr,
Qt::WindowFlags f = Qt::WindowFlags());
~NewKeyApprovalDialog() override;
/** @brief The selected signing and/or encryption keys. Only valid after the dialog was accepted. */
KeyResolver::Solution result();
+private Q_SLOTS:
+ void handleKeyGenResult();
+
private:
class Private;
std::unique_ptr<Private> d;
};
} // namespace kleo
diff --git a/src/ui/progressdialog.cpp b/src/ui/progressdialog.cpp
index 04f70901..e5537b97 100644
--- a/src/ui/progressdialog.cpp
+++ b/src/ui/progressdialog.cpp
@@ -1,72 +1,78 @@
/*
progressdialog.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "progressdialog.h"
#ifndef QT_NO_PROGRESSDIALOG
#include "progressbar.h"
#include <kleo_ui_debug.h>
#include <KLocalizedString>
#include <QTimer>
Kleo::ProgressDialog::ProgressDialog(QGpgME::Job *job, const QString &baseText, QWidget *creator, Qt::WindowFlags f)
: QProgressDialog(creator, f)
, mBaseText(baseText)
{
Q_ASSERT(job);
setBar(new ProgressBar(this /*, "replacement progressbar in Kleo::ProgressDialog"*/));
setMinimumDuration(2000 /*ms*/);
setAutoReset(false);
setAutoClose(false);
setLabelText(baseText);
setModal(false);
setRange(0, 0); // activate busy indicator
- connect(job, &QGpgME::Job::jobProgress, this, &ProgressDialog::slotProgress);
- connect(job, &QGpgME::Job::done, this, &ProgressDialog::slotDone);
+ if (!connect(job, &QGpgME::Job::jobProgress, this, &ProgressDialog::slotProgress)) {
+ qCWarning(KLEO_UI_LOG) << "new-style connect failed; connecting to QGpgME::Job::jobProgress the old way";
+ connect(job, SIGNAL(jobProgress(int, int)), this, SLOT(slotProgress(int, int)));
+ }
+ if (!connect(job, &QGpgME::Job::done, this, &ProgressDialog::slotDone)) {
+ qCWarning(KLEO_UI_LOG) << "new-style connect failed; connecting to QGpgME::Job::done the old way";
+ connect(job, SIGNAL(done()), this, SLOT(slotDone()));
+ }
connect(this, &QProgressDialog::canceled, job, &QGpgME::Job::slotCancel);
QTimer::singleShot(minimumDuration(), this, &ProgressDialog::forceShow);
}
Kleo::ProgressDialog::~ProgressDialog()
{
}
void Kleo::ProgressDialog::setMinimumDuration(int ms)
{
if (0 < ms && ms < minimumDuration()) {
QTimer::singleShot(ms, this, &ProgressDialog::forceShow);
}
QProgressDialog::setMinimumDuration(ms);
}
void Kleo::ProgressDialog::slotProgress(int current, int total)
{
qCDebug(KLEO_UI_LOG) << "Kleo::ProgressDialog::slotProgress(" << current << "," << total << ")";
setRange(current, total);
}
void Kleo::ProgressDialog::slotDone()
{
qCDebug(KLEO_UI_LOG) << "Kleo::ProgressDialog::slotDone()";
hide();
deleteLater();
}
#endif // QT_NO_PROGRESSDIALOG
#include "moc_progressdialog.cpp"
diff --git a/src/utils/gnupg.cpp b/src/utils/gnupg.cpp
index e6a64ff1..de52626f 100644
--- a/src/utils/gnupg.cpp
+++ b/src/utils/gnupg.cpp
@@ -1,759 +1,810 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/gnupg.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020-2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "gnupg.h"
#include "assuan.h"
#include "compat.h"
#include "compliance.h"
#include "cryptoconfig.h"
#include "hex.h"
#include <libkleo_debug.h>
#include <KLocalizedString>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <QByteArray>
#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QPointer>
#include <QProcess>
#include <QRegExp>
#include <QRegularExpression>
#include <QStandardPaths>
#include <QString>
#include <gpgme++/engineinfo.h>
#include <gpgme++/error.h>
#include <gpgme++/key.h>
#include <gpg-error.h>
#ifdef Q_OS_WIN
#include "gnupg-registry.h"
#endif // Q_OS_WIN
#include <algorithm>
#include <array>
using namespace GpgME;
QString Kleo::gnupgHomeDirectory()
{
static const QString homeDir = QString::fromUtf8(GpgME::dirInfo("homedir"));
return homeDir;
}
QString Kleo::gnupgPrivateKeysDirectory()
{
static const QString dir = QDir{gnupgHomeDirectory()}.filePath(QStringLiteral("private-keys-v1.d"));
return dir;
}
int Kleo::makeGnuPGError(int code)
{
return gpg_error(static_cast<gpg_err_code_t>(code));
}
static QString findGpgExe(GpgME::Engine engine, const QString &exe)
{
const GpgME::EngineInfo info = GpgME::engineInfo(engine);
return info.fileName() ? QFile::decodeName(info.fileName()) : QStandardPaths::findExecutable(exe);
}
QString Kleo::gpgConfPath()
{
static const auto path = findGpgExe(GpgME::GpgConfEngine, QStringLiteral("gpgconf"));
return path;
}
QString Kleo::gpgSmPath()
{
static const auto path = findGpgExe(GpgME::GpgSMEngine, QStringLiteral("gpgsm"));
return path;
}
QString Kleo::gpgPath()
{
static const auto path = findGpgExe(GpgME::GpgEngine, QStringLiteral("gpg"));
return path;
}
QStringList Kleo::gnupgFileWhitelist()
{
return {
// The obvious pubring
QStringLiteral("pubring.gpg"),
// GnuPG 2.1 pubring
QStringLiteral("pubring.kbx"),
// Trust in X509 Certificates
QStringLiteral("trustlist.txt"),
// Trustdb controls ownertrust and thus WOT validity
QStringLiteral("trustdb.gpg"),
// We want to update when smartcard status changes
QStringLiteral("reader*.status"),
// No longer used in 2.1 but for 2.0 we want this
QStringLiteral("secring.gpg"),
// Secret keys (living under private-keys-v1.d/)
QStringLiteral("*.key"),
// Changes to the trustmodel / compliance mode might
// affect validity so we check this, too.
// Globbing for gpg.conf* here will trigger too often
// as gpgconf creates files like gpg.conf.bak or
// gpg.conf.tmp12312.gpgconf that should not trigger
// a change.
QStringLiteral("gpg.conf"),
QStringLiteral("gpg.conf-?"),
QStringLiteral("gpg.conf-?.?"),
};
}
QStringList Kleo::gnupgFolderWhitelist()
{
static const QDir gnupgHome{gnupgHomeDirectory()};
return {
gnupgHome.path(),
gnupgPrivateKeysDirectory(),
};
}
namespace
{
class Gpg4win
{
public:
static const Gpg4win *instance()
{
// We use singleton to do the signature check only once.
static Gpg4win *inst = nullptr;
if (!inst) {
inst = new Gpg4win();
}
return inst;
}
private:
QString mVersion;
QString mDescription;
QString mDescLong;
QString mBrandingWindowTitle;
QString mBrandingIcon;
bool mSignedVersion;
Gpg4win()
: mVersion(QStringLiteral("Unknown Windows Version"))
, mDescription(i18n("Certificate Manager and Unified Crypto GUI"))
, mDescLong(QStringLiteral("<a href=https://www.gpg4win.org>Visit the Gpg4win homepage</a>"))
, mSignedVersion(false)
{
const QString instPath = Kleo::gpg4winInstallPath();
const QString verPath = instPath + QStringLiteral("/../VERSION");
QFile versionFile(verPath);
// Open the file first to avoid a verify and then read issue where
// "auditors" might say its an issue,...
if (!versionFile.open(QIODevice::ReadOnly)) {
return;
}
// Expect a three line format of three HTML strings.
const auto versVersion = QString::fromUtf8(versionFile.readLine()).trimmed();
const auto versDescription = QString::fromUtf8(versionFile.readLine()).trimmed();
const auto versDescLong = QString::fromUtf8(versionFile.readLine()).trimmed();
// read optional two branding strings
const auto brandingWindowTitle = QString::fromUtf8(versionFile.readLine()).trimmed();
const auto brandingIcon = QString::fromUtf8(versionFile.readLine()).trimmed();
const QString sigPath = verPath + QStringLiteral(".sig");
QFileInfo versionSig(instPath + QStringLiteral("/../VERSION.sig"));
if (versionSig.exists()) {
/* We have a signed version so let us check it against the GnuPG
* release keys. */
QProcess gpgv;
gpgv.setProgram(Kleo::gpgPath().replace(QStringLiteral("gpg.exe"), QStringLiteral("gpgv.exe")));
const QString keyringPath(QStringLiteral("%1/../share/gnupg/distsigkey.gpg").arg(Kleo::gnupgInstallPath()));
gpgv.setArguments(QStringList() << QStringLiteral("--keyring") << keyringPath << QStringLiteral("--") << sigPath << verPath);
gpgv.start();
gpgv.waitForFinished();
if (gpgv.exitStatus() == QProcess::NormalExit && !gpgv.exitCode()) {
qCDebug(LIBKLEO_LOG) << "Valid Version: " << versVersion;
mVersion = versVersion;
mDescription = versDescription;
mDescLong = versDescLong;
mBrandingWindowTitle = brandingWindowTitle;
mBrandingIcon = brandingIcon;
mSignedVersion = true;
} else {
qCDebug(LIBKLEO_LOG) << "gpgv failed with stderr: " << gpgv.readAllStandardError();
qCDebug(LIBKLEO_LOG) << "gpgv stdout" << gpgv.readAllStandardOutput();
}
} else {
qCDebug(LIBKLEO_LOG) << "No signed VERSION file found.";
}
// Also take Version information from unsigned Versions.
mVersion = versVersion;
}
public:
const QString &version() const
{
return mVersion;
}
const QString &description() const
{
return mDescription;
}
const QString &longDescription() const
{
return mDescLong;
}
bool isSignedVersion() const
{
return mSignedVersion;
}
const QString &brandingWindowTitle() const
{
return mBrandingWindowTitle;
}
const QString &brandingIcon() const
{
return mBrandingIcon;
}
};
} // namespace
bool Kleo::gpg4winSignedversion()
{
return Gpg4win::instance()->isSignedVersion();
}
QString Kleo::gpg4winVersionNumber()
{
// extract the actual version number from the string returned by Gpg4win::version();
// we assume that Gpg4win::version() returns a version number (conforming to the semantic
// versioning spec) optionally prefixed with some text followed by a dash,
// e.g. "Gpg4win-3.1.15-beta15"; see https://dev.gnupg.org/T5663
static const QRegularExpression catchSemVerRegExp{QLatin1String{R"(-([0-9]+(?:\.[0-9]+)*(?:-[.0-9A-Za-z-]+)?(?:\+[.0-9a-zA-Z-]+)?)$)"}};
QString ret;
const auto match = catchSemVerRegExp.match(gpg4winVersion());
if (match.hasMatch()) {
ret = match.captured(1);
} else {
ret = gpg4winVersion();
}
qCDebug(LIBKLEO_LOG) << __func__ << "returns" << ret;
return ret;
}
QString Kleo::gpg4winVersion()
{
return Gpg4win::instance()->version();
}
QString Kleo::gpg4winDescription()
{
return Gpg4win::instance()->description();
}
QString Kleo::gpg4winLongDescription()
{
return Gpg4win::instance()->longDescription();
}
QString Kleo::brandingWindowTitle()
{
return Gpg4win::instance()->brandingWindowTitle();
}
QString Kleo::brandingIcon()
{
return Gpg4win::instance()->brandingIcon();
}
QString Kleo::gpg4winInstallPath()
{
#ifdef Q_OS_WIN
// QApplication::applicationDirPath is only used as a fallback
// to support the case where Kleopatra is not installed from
// Gpg4win but Gpg4win is also installed.
char *instDir = read_w32_registry_string("HKEY_LOCAL_MACHINE", "Software\\GPG4Win", "Install Directory");
if (!instDir) {
// Fallback to HKCU
instDir = read_w32_registry_string("HKEY_CURRENT_USER", "Software\\GPG4Win", "Install Directory");
}
if (instDir) {
QString ret = QString::fromLocal8Bit(instDir) + QStringLiteral("/bin");
free(instDir);
return ret;
}
qCDebug(LIBKLEO_LOG) << "Gpg4win not found. Falling back to Kleopatra instdir.";
#endif
return QCoreApplication::applicationDirPath();
}
QString Kleo::gnupgInstallPath()
{
#ifdef Q_OS_WIN
// QApplication::applicationDirPath is only used as a fallback
// to support the case where Kleopatra is not installed from
// Gpg4win but Gpg4win is also installed.
char *instDir = read_w32_registry_string("HKEY_LOCAL_MACHINE", "Software\\GnuPG", "Install Directory");
if (!instDir) {
// Fallback to HKCU
instDir = read_w32_registry_string("HKEY_CURRENT_USER", "Software\\GnuPG", "Install Directory");
}
if (instDir) {
QString ret = QString::fromLocal8Bit(instDir) + QStringLiteral("/bin");
free(instDir);
return ret;
}
qCDebug(LIBKLEO_LOG) << "GnuPG not found. Falling back to gpgconf list dir.";
#endif
return gpgConfListDir("bindir");
}
QString Kleo::gpgConfListDir(const char *which)
{
if (!which || !*which) {
return QString();
}
const QString gpgConfPath = Kleo::gpgConfPath();
if (gpgConfPath.isEmpty()) {
return QString();
}
QProcess gpgConf;
qCDebug(LIBKLEO_LOG) << "gpgConfListDir: starting " << qPrintable(gpgConfPath) << " --list-dirs";
gpgConf.start(gpgConfPath, QStringList() << QStringLiteral("--list-dirs"));
if (!gpgConf.waitForFinished()) {
qCDebug(LIBKLEO_LOG) << "gpgConfListDir(): failed to execute gpgconf: " << qPrintable(gpgConf.errorString());
qCDebug(LIBKLEO_LOG) << "output was:\n" << gpgConf.readAllStandardError().constData();
return QString();
}
const QList<QByteArray> lines = gpgConf.readAllStandardOutput().split('\n');
for (const QByteArray &line : lines) {
if (line.startsWith(which) && line[qstrlen(which)] == ':') {
const int begin = qstrlen(which) + 1;
int end = line.size();
while (end && (line[end - 1] == '\n' || line[end - 1] == '\r')) {
--end;
}
const QString result = QDir::fromNativeSeparators(QFile::decodeName(hexdecode(line.mid(begin, end - begin))));
qCDebug(LIBKLEO_LOG) << "gpgConfListDir: found " << qPrintable(result) << " for '" << which << "'entry";
return result;
}
}
qCDebug(LIBKLEO_LOG) << "gpgConfListDir(): didn't find '" << which << "'"
<< "entry in output:\n"
<< gpgConf.readAllStandardError().constData();
return QString();
}
static std::array<int, 3> getVersionFromString(const char *actual, bool &ok)
{
std::array<int, 3> ret;
ok = false;
if (!actual) {
return ret;
}
QString versionString = QString::fromLatin1(actual);
// Try to fix it up
QRegExp rx(QLatin1String(R"((\d+)\.(\d+)\.(\d+)(?:-svn\d+)?.*)"));
for (int i = 0; i < 3; i++) {
if (!rx.exactMatch(versionString)) {
versionString += QStringLiteral(".0");
} else {
ok = true;
break;
}
}
if (!ok) {
qCDebug(LIBKLEO_LOG) << "Can't parse version " << actual;
return ret;
}
for (int i = 0; i < 3; ++i) {
ret[i] = rx.cap(i + 1).toUInt(&ok);
if (!ok) {
return ret;
}
}
ok = true;
return ret;
}
bool Kleo::versionIsAtLeast(const char *minimum, const char *actual)
{
if (!minimum || !actual) {
return false;
}
bool ok;
const auto minimum_version = getVersionFromString(minimum, ok);
if (!ok) {
return false;
}
const auto actual_version = getVersionFromString(actual, ok);
if (!ok) {
return false;
}
return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(minimum_version), std::end(minimum_version));
}
bool Kleo::engineIsVersion(int major, int minor, int patch, GpgME::Engine engine)
{
static QMap<Engine, std::array<int, 3>> cachedVersions;
const int required_version[] = {major, minor, patch};
// Gpgconf means spawning processes which is expensive on windows.
std::array<int, 3> actual_version;
if (!cachedVersions.contains(engine)) {
const Error err = checkEngine(engine);
if (err.code() == GPG_ERR_INV_ENGINE) {
qCDebug(LIBKLEO_LOG) << "isVersion: invalid engine. '";
return false;
}
const char *actual = GpgME::engineInfo(engine).version();
bool ok;
actual_version = getVersionFromString(actual, ok);
qCDebug(LIBKLEO_LOG) << "Parsed" << actual << "as: " << actual_version[0] << '.' << actual_version[1] << '.' << actual_version[2] << '.';
if (!ok) {
return false;
}
cachedVersions.insert(engine, actual_version);
} else {
actual_version = cachedVersions.value(engine);
}
// return ! ( actual_version < required_version )
return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(required_version), std::end(required_version));
}
const QString &Kleo::paperKeyInstallPath()
{
static const QString pkPath = (QStandardPaths::findExecutable(QStringLiteral("paperkey"), QStringList() << QCoreApplication::applicationDirPath()).isEmpty()
? QStandardPaths::findExecutable(QStringLiteral("paperkey"))
: QStandardPaths::findExecutable(QStringLiteral("paperkey"), QStringList() << QCoreApplication::applicationDirPath()));
return pkPath;
}
bool Kleo::haveKeyserverConfigured()
{
if (engineIsVersion(2, 1, 19)) {
// since 2.1.19 there is a builtin keyserver
return true;
}
return !Kleo::keyserver().isEmpty();
}
QString Kleo::keyserver()
{
QString result = getCryptoConfigStringValue("gpg", "keyserver");
if (result.isEmpty()) {
result = getCryptoConfigStringValue("dirmngr", "keyserver");
}
return result;
}
bool Kleo::haveX509DirectoryServerConfigured()
{
return !getCryptoConfigUrlList("dirmngr", "ldapserver").empty() //
|| !getCryptoConfigUrlList("dirmngr", "LDAP Server").empty() //
|| !getCryptoConfigUrlList("gpgsm", "keyserver").empty();
}
bool Kleo::gpgComplianceP(const char *mode)
{
const auto conf = QGpgME::cryptoConfig();
const auto entry = getCryptoConfigEntry(conf, "gpg", "compliance");
return entry && entry->stringValue() == QString::fromLatin1(mode);
}
bool Kleo::gnupgUsesDeVsCompliance()
{
return DeVSCompliance::isActive();
}
bool Kleo::gnupgIsDeVsCompliant()
{
return DeVSCompliance::isCompliant();
}
#ifdef Q_OS_WIN
static unsigned int guessConsoleOutputCodePage()
{
/* Qt on Windows uses GetACP while GnuPG prefers
* GetConsoleOutputCP.
*
* As we are not a console application GetConsoleOutputCP
* usually returns 0.
* From experience the closest thing that let's us guess
* what GetConsoleOutputCP returns for a console application
* it appears to be the OEMCP.
*/
unsigned int cpno = GetConsoleOutputCP();
if (!cpno) {
cpno = GetOEMCP();
}
if (!cpno) {
cpno = GetACP();
}
if (!cpno) {
qCDebug(LIBKLEO_LOG) << __func__ << "Failed to find native codepage";
}
qCDebug(LIBKLEO_LOG) << __func__ << "returns" << cpno;
return cpno;
}
static QString fromEncoding(unsigned int src_encoding, const char *data)
{
if (!data || !*data) {
return {};
}
// returns necessary buffer size including the terminating null character
int n = MultiByteToWideChar(src_encoding, 0, data, -1, NULL, 0);
if (n <= 0) {
qCDebug(LIBKLEO_LOG) << __func__ << "determining necessary buffer size failed with error code" << GetLastError();
return QString();
}
wchar_t *result = (wchar_t *)malloc((n + 1) * sizeof *result);
n = MultiByteToWideChar(src_encoding, 0, data, -1, result, n);
if (n <= 0) {
free(result);
qCDebug(LIBKLEO_LOG) << __func__ << "conversion failed with error code" << GetLastError();
return QString();
}
const auto ret = QString::fromWCharArray(result, n - 1);
free(result);
return ret;
}
static QString stringFromGpgOutput_legacy(const QByteArray &ba)
{
static const unsigned int cpno = guessConsoleOutputCodePage();
if (cpno) {
qCDebug(LIBKLEO_LOG) << __func__ << "trying to decode" << ba << "using codepage" << cpno;
const auto rawData = QByteArray{ba}.replace("\r\n", "\n");
const auto s = fromEncoding(cpno, rawData.constData());
if (!s.isEmpty() || ba.isEmpty()) {
return s;
}
qCDebug(LIBKLEO_LOG) << __func__ << "decoding output failed; falling back to QString::fromLocal8Bit()";
}
qCDebug(LIBKLEO_LOG) << __func__ << "decoding from local encoding:" << ba;
return QString::fromLocal8Bit(ba);
}
#endif
QString Kleo::stringFromGpgOutput(const QByteArray &ba)
{
#ifdef Q_OS_WIN
// since 2.2.28, GnuPG always uses UTF-8 for console output (and input)
if (Kleo::engineIsVersion(2, 2, 28, GpgME::GpgEngine)) {
return QString::fromUtf8(ba);
} else {
return stringFromGpgOutput_legacy(ba);
}
#else
return QString::fromLocal8Bit(ba);
#endif
}
QStringList Kleo::backendVersionInfo()
{
QStringList versions;
if (Kleo::engineIsVersion(2, 2, 24, GpgME::GpgConfEngine)) {
QProcess p;
qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions ...";
p.start(Kleo::gpgConfPath(), {QStringLiteral("--show-versions")});
// wait at most 1 second
if (!p.waitForFinished(1000)) {
qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions timed out after 1 second.";
} else if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) {
qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions failed:" << p.errorString();
qCDebug(LIBKLEO_LOG) << "gpgconf stderr:" << p.readAllStandardError();
qCDebug(LIBKLEO_LOG) << "gpgconf stdout:" << p.readAllStandardOutput();
} else {
const QByteArray output = p.readAllStandardOutput().replace("\r\n", "\n");
qCDebug(LIBKLEO_LOG) << "gpgconf stdout:" << p.readAllStandardOutput();
const auto lines = output.split('\n');
for (const auto &line : lines) {
if (line.startsWith("* GnuPG") || line.startsWith("* Libgcrypt")) {
const auto components = line.split(' ');
versions.push_back(QString::fromLatin1(components.at(1) + ' ' + components.value(2)));
}
}
}
}
return versions;
}
namespace
{
template<typename Function1, typename Function2>
auto startGpgConf(const QStringList &arguments, Function1 onSuccess, Function2 onFailure)
{
auto process = new QProcess;
process->setProgram(Kleo::gpgConfPath());
process->setArguments(arguments);
QObject::connect(process, &QProcess::started, [process]() {
qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") was started successfully";
});
QObject::connect(process, &QProcess::errorOccurred, [process, onFailure](auto error) {
qCDebug(LIBKLEO_LOG).nospace() << "Error while running gpgconf (" << process << "): " << error;
process->deleteLater();
onFailure();
});
QObject::connect(process, &QProcess::readyReadStandardError, [process]() {
for (const auto &line : process->readAllStandardError().trimmed().split('\n')) {
qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") stderr: " << line;
}
});
QObject::connect(process, &QProcess::readyReadStandardOutput, [process]() {
(void)process->readAllStandardOutput(); /* ignore stdout */
});
QObject::connect(process,
qOverload<int, QProcess::ExitStatus>(&QProcess::finished),
[process, onSuccess, onFailure](int exitCode, QProcess::ExitStatus exitStatus) {
if (exitStatus == QProcess::NormalExit) {
qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") exited (exit code: " << exitCode << ")";
if (exitCode == 0) {
onSuccess();
} else {
onFailure();
}
} else {
qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") crashed (exit code: " << exitCode << ")";
onFailure();
}
process->deleteLater();
});
qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgconf (" << process << ") with arguments " << process->arguments().join(QLatin1Char(' ')) << " ...";
process->start();
return process;
}
static auto startGpgConf(const QStringList &arguments)
{
return startGpgConf(
arguments,
[]() {},
[]() {});
}
}
void Kleo::launchGpgAgent()
{
static QPointer<QProcess> process;
static qint64 mSecsSinceEpochOfLastLaunch = 0;
static int numberOfFailedLaunches = 0;
if (Kleo::Assuan::agentIsRunning()) {
qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already running";
return;
}
if (process) {
qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already being launched";
return;
}
const auto now = QDateTime::currentMSecsSinceEpoch();
if (now - mSecsSinceEpochOfLastLaunch < 1000) {
// reduce attempts to launch the agent to 1 attempt per second
return;
}
mSecsSinceEpochOfLastLaunch = now;
if (numberOfFailedLaunches > 5) {
qCWarning(LIBKLEO_LOG) << __func__ << ": Launching gpg-agent failed" << numberOfFailedLaunches << "times in a row. Giving up.";
return;
}
process = startGpgConf(
{QStringLiteral("--launch"), QStringLiteral("gpg-agent")},
[]() {
numberOfFailedLaunches = 0;
},
[]() {
numberOfFailedLaunches++;
});
}
void Kleo::killDaemons()
{
static QPointer<QProcess> process;
if (process) {
qCDebug(LIBKLEO_LOG) << __func__ << ": The daemons are already being shut down";
return;
}
process = startGpgConf({QStringLiteral("--kill"), QStringLiteral("all")});
}
const std::vector<std::string> &Kleo::availableAlgorithms()
{
static const std::vector<std::string> algos = {
"brainpoolP256r1",
"brainpoolP384r1",
"brainpoolP512r1",
"curve25519",
"curve448",
"nistp256",
"nistp384",
"nistp521",
"rsa2048",
"rsa3072",
"rsa4096",
// "secp256k1", // Curve secp256k1 is explicitly ignored
};
return algos;
}
const std::vector<std::string> &Kleo::preferredAlgorithms()
{
static const std::vector<std::string> algos = {
"curve25519",
"brainpoolP256r1",
"rsa3072",
"rsa2048",
};
return algos;
}
const std::vector<std::string> &Kleo::ignoredAlgorithms()
{
static const std::vector<std::string> algos = {
"secp256k1", // Curve secp256k1 is not useful
};
return algos;
}
+
+bool Kleo::gpgvVerify(const QString &filePath, const QString &sigPath, const QString &keyring, const QStringList &additionalSearchPaths)
+{
+ const QFileInfo verifyFi(filePath);
+ if (!verifyFi.isReadable()) {
+ return false;
+ } else {
+ qCDebug(LIBKLEO_LOG) << "Verifying" << filePath;
+ }
+
+ const auto gpgvPath = QStandardPaths::findExecutable(QStringLiteral("gpgv"), additionalSearchPaths);
+ if (gpgvPath.isEmpty()) {
+ qCDebug(LIBKLEO_LOG) << "Could not find gpgv";
+ return false;
+ }
+
+ QFileInfo sigFi;
+ if (!sigPath.isEmpty()) {
+ sigFi.setFile(sigPath);
+ } else {
+ sigFi.setFile(filePath + QStringLiteral(".sig"));
+ }
+
+ if (!sigFi.isReadable()) {
+ qCDebug(LIBKLEO_LOG) << "No signature found at" << sigFi.absoluteFilePath();
+ return false;
+ }
+
+ auto process = QProcess();
+ process.setProgram(gpgvPath);
+ QStringList args;
+ if (!keyring.isEmpty()) {
+ args << QStringLiteral("--keyring") << keyring;
+ }
+ args << QStringLiteral("--") << sigFi.absoluteFilePath() << verifyFi.absoluteFilePath();
+ process.setArguments(args);
+ qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgv (" << gpgvPath << ") with arguments " << args.join(QLatin1Char(' ')) << " ...";
+ process.start();
+
+ if (!process.waitForFinished(-1)) {
+ qCDebug(LIBKLEO_LOG) << "Failed to execute gpgv" << process.errorString();
+ }
+ bool ret = (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0);
+
+ if (!ret) {
+ qCDebug(LIBKLEO_LOG) << "Failed to verify file";
+ qCDebug(LIBKLEO_LOG) << "gpgv stdout:" << QString::fromUtf8(process.readAllStandardOutput());
+ qCDebug(LIBKLEO_LOG) << "gpgv stderr:" << QString::fromUtf8(process.readAllStandardError());
+ }
+ return ret;
+}
diff --git a/src/utils/gnupg.h b/src/utils/gnupg.h
index 830208eb..83510361 100644
--- a/src/utils/gnupg.h
+++ b/src/utils/gnupg.h
@@ -1,133 +1,148 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/gnupg.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2020-2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <QStringList>
#include <gpgme++/engineinfo.h>
#include <gpgme++/key.h>
class QString;
class QByteArray;
namespace Kleo
{
KLEO_EXPORT QString gnupgHomeDirectory();
KLEO_EXPORT QString gnupgPrivateKeysDirectory();
KLEO_EXPORT QString gpgConfPath();
KLEO_EXPORT QString gpgSmPath();
KLEO_EXPORT QString gpgPath();
KLEO_EXPORT QString gpgConfListDir(const char *which);
KLEO_EXPORT QString gpg4winInstallPath();
-// Returns the version number.
-KLEO_EXPORT QString gpg4winVersionNumber();
-// Returns the version number with an optional product specific prefix.
-KLEO_EXPORT QString gpg4winVersion();
-KLEO_EXPORT bool gpg4winSignedversion();
-KLEO_EXPORT QString gpg4winDescription();
-KLEO_EXPORT QString gpg4winLongDescription();
KLEO_EXPORT QString gnupgInstallPath();
KLEO_EXPORT const QString &paperKeyInstallPath();
-KLEO_EXPORT QString brandingWindowTitle();
-KLEO_EXPORT QString brandingIcon();
+/* Deprecated. Better read this from a settings file which
+ * can optionally be verified with gpgvVerify. */
+KLEO_DEPRECATED_EXPORT QString gpg4winVersionNumber();
+KLEO_DEPRECATED_EXPORT QString gpg4winVersion();
+KLEO_DEPRECATED_EXPORT bool gpg4winSignedversion();
+KLEO_DEPRECATED_EXPORT QString gpg4winDescription();
+KLEO_DEPRECATED_EXPORT QString gpg4winLongDescription();
+KLEO_DEPRECATED_EXPORT QString brandingWindowTitle();
+KLEO_DEPRECATED_EXPORT QString brandingIcon();
+
+/**
+ * Verify \p filePath using gpgv. If \p sigPath is provided it uses
+ * this signature, otherwise it adds .sig to the \p filePath. If
+ * \p keyring is provided that is the keyring where the signature is
+ * checked against. Otherwise it uses the default of gpgv.
+ * \p additionalSearchPaths can be used to specify where gpgv is
+ * searched for first.
+ *
+ * Blocks until the verification is done which can be indefinetly to
+ * allow for very large files.
+ *
+ * Returns true if the verification was successful, false if any problem
+ * occured. */
+KLEO_EXPORT bool gpgvVerify(const QString &filePath, const QString &sigPath = {}, const QString &keyring = {}, const QStringList &additionalSearchPaths = {});
/**
* Returns a list of filename globs of files in one of the whitelisted folders
* to watch for changes.
* \sa gnupgFolderWhitelist, Kleo::FileSystemWatcher
*/
KLEO_EXPORT QStringList gnupgFileWhitelist();
/**
* Returns a list of absolute paths of folders to watch for changes.
* \sa gnupgFileWhitelist, Kleo::FileSystemWatcher
*/
KLEO_EXPORT QStringList gnupgFolderWhitelist();
KLEO_EXPORT int makeGnuPGError(int code);
KLEO_EXPORT bool engineIsVersion(int major, int minor, int patch, GpgME::Engine = GpgME::GpgConfEngine);
/** Returns true, if GnuPG knows which keyserver to use for keyserver
* operations.
* Since version 2.1.19 GnuPG has a builtin default keyserver, so that this
* function always returns true. For older versions of GnuPG it checks if
* a keyserver has been configured.
*/
KLEO_EXPORT bool haveKeyserverConfigured();
/** Returns the configured keyserver or an empty string if no keyserver is
* configured.
* Note: Since GnuPG 2.1.19 gpg/dirmngr uses a default keyserver if no
* keyserver is configured.
*/
KLEO_EXPORT QString keyserver();
/** Returns true, if GnuPG knows which server to use for directory service
* operations for X.509 certificates.
*/
KLEO_EXPORT bool haveX509DirectoryServerConfigured();
/* Use gnupgUsesDeVsCompliance() or gnupgIsDeVsCompliant() instead. */
KLEO_DEPRECATED_EXPORT bool gpgComplianceP(const char *mode);
/**
* Use Kleo::DeVSCompliance::isActive() instead.
*/
KLEO_DEPRECATED_EXPORT bool gnupgUsesDeVsCompliance();
/**
* Use Kleo::DeVSCompliance::isCompliant() instead.
*/
KLEO_DEPRECATED_EXPORT bool gnupgIsDeVsCompliant();
/* Convert GnuPG output to a QString with proper encoding.
* Takes Gpg Quirks into account and might handle future
* changes in GnuPG Output. */
KLEO_EXPORT QString stringFromGpgOutput(const QByteArray &ba);
/* Check if a minimum version is there. Strings should be in the format:
* 1.2.3 */
KLEO_EXPORT bool versionIsAtLeast(const char *minimum, const char *actual);
/** Returns a list of component names (e.g. GnuPG, libgcrypt) followed by
* version numbers. This is meant for displaying in the About dialog.
*/
KLEO_EXPORT QStringList backendVersionInfo();
/** Launch the GnuPG agent if it is not already running. */
KLEO_EXPORT void launchGpgAgent();
/** Shut down all GnuPG daemons. They will be restarted automatically when
* needed.
*/
KLEO_EXPORT void killDaemons();
/**
* Returns a static list of the available algorithms.
*/
KLEO_EXPORT const std::vector<std::string> &availableAlgorithms();
/**
* Returns a static list of the preferred algorithms with decreasing preference.
*/
KLEO_EXPORT const std::vector<std::string> &preferredAlgorithms();
/**
* Returns a static list of algorithms that are explicitly not supported.
*/
KLEO_EXPORT const std::vector<std::string> &ignoredAlgorithms();
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Dec 5, 4:39 AM (1 d, 5 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
99/75/fd7be1352a2a7ec77f650a4268dc

Event Timeline