Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34621655
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
22 KB
Subscribers
None
View Options
diff --git a/src/view/padwidget.cpp b/src/view/padwidget.cpp
index b192e7df5..a008caa4f 100644
--- a/src/view/padwidget.cpp
+++ b/src/view/padwidget.cpp
@@ -1,538 +1,588 @@
/*
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2018 Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "padwidget.h"
#include "kleopatra_debug.h"
#include <settings.h>
#include <Libkleo/ApplicationPaletteWatcher>
#include <Libkleo/Classify>
#include <Libkleo/Compliance>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyCache>
#include <Libkleo/KleoException>
#include <Libkleo/SystemInfo>
#include "commands/importcertificatefromdatacommand.h"
#include "crypto/decryptverifytask.h"
#include "crypto/gui/resultitemwidget.h"
#include "crypto/gui/signencryptwidget.h"
#include "crypto/signencrypttask.h"
#include "utils/gui-helper.h"
#include "utils/input.h"
#include "utils/output.h"
#include <gpgme++/data.h>
#include <gpgme++/decryptionresult.h>
#include <QGpgME/DataProvider>
#include <QAccessible>
#include <QButtonGroup>
#include <QFontMetrics>
#include <QFrame>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
#include <QRadioButton>
#include <QSplitter>
#include <QStyle>
#include <QTextEdit>
#include <QVBoxLayout>
#include <KAdjustingScrollArea>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KSeparator>
#include <KSharedConfig>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
static GpgME::Protocol getProtocol(const std::shared_ptr<const Kleo::Crypto::Task::Result> &result)
{
const auto dvResult = dynamic_cast<const Kleo::Crypto::DecryptVerifyResult *>(result.get());
if (dvResult) {
for (const auto &key : KeyCache::instance()->findRecipients(dvResult->decryptionResult())) {
return key.protocol();
}
for (const auto &key : KeyCache::instance()->findSigners(dvResult->verificationResult())) {
return key.protocol();
}
}
return GpgME::UnknownProtocol;
}
class PadWidget::Private
{
friend class ::Kleo::PadWidget;
public:
enum Operation {
NoOperation,
SignEncrypt,
DecryptVerify,
Import,
};
Private(PadWidget *qq)
: q(qq)
, mEdit(new QTextEdit)
, mCryptBtn(new QPushButton(QIcon::fromTheme(QStringLiteral("document-edit-sign-encrypt")), i18n("Sign / Encrypt Notepad")))
, mDecryptBtn(new QPushButton(QIcon::fromTheme(QStringLiteral("document-edit-decrypt-verify")), i18n("Decrypt / Verify Notepad")))
, mImportBtn(new QPushButton(QIcon::fromTheme(QStringLiteral("view-certificate-import")), i18n("Import Notepad")))
, mRevertBtn(new QPushButton(QIcon::fromTheme(QStringLiteral("edit-undo")), i18n("Revert")))
, mMessageWidget{new KMessageWidget}
, mAdditionalInfoLabel(new QLabel)
, mSigEncWidget(nullptr)
, mProgressBar(new QProgressBar)
, mProgressLabel(new QLabel)
, mLastResultWidget(nullptr)
, mImportProto(GpgME::UnknownProtocol)
{
auto vLay = new QVBoxLayout(q);
vLay->setContentsMargins({});
vLay->setSpacing(0);
mMessageWidget->setMessageType(KMessageWidget::Warning);
mMessageWidget->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, q));
mMessageWidget->setText(i18n("Signing and encryption is not possible."));
mMessageWidget->setToolTip(xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"<para>You cannot use <application>Kleopatra</application> for signing or encryption "
"because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>",
DeVSCompliance::name(true)));
mMessageWidget->setCloseButtonVisible(false);
mMessageWidget->setVisible(false);
mMessageWidget->setPosition(KMessageWidget::Position::Header);
vLay->addWidget(mMessageWidget);
mRevertBtn->setVisible(false);
mProgressBar->setRange(0, 0);
mProgressBar->setVisible(false);
mProgressLabel->setVisible(false);
auto progLay = new QHBoxLayout;
progLay->addWidget(mProgressLabel);
progLay->addWidget(mProgressBar);
mStatusLay = new QVBoxLayout;
mStatusLay->addLayout(progLay);
vLay->addLayout(mStatusLay, 0);
auto splitterWidget = new QSplitter;
splitterWidget->setChildrenCollapsible(false);
vLay->addWidget(splitterWidget, 1);
splitterWidget->addWidget(mEdit);
splitterWidget->setStretchFactor(0, 1);
// The recipients area
auto scrollArea = new KAdjustingScrollArea;
scrollArea->setFocusPolicy(Qt::NoFocus);
auto recipientsWidget = new QWidget;
scrollArea->setWidget(recipientsWidget);
auto recipientsVLay = new QVBoxLayout(recipientsWidget);
mSigEncWidget = new SignEncryptWidget(nullptr, true);
recipientsVLay->addWidget(mSigEncWidget);
mCryptBtn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
auto btnLay = new QHBoxLayout;
btnLay->setSpacing(q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing));
btnLay->setContentsMargins(q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
q->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
q->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
q->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
vLay->addLayout(btnLay);
btnLay->addWidget(mDecryptBtn);
btnLay->addWidget(mImportBtn);
btnLay->addWidget(mRevertBtn);
btnLay->addStretch();
btnLay->addWidget(mAdditionalInfoLabel);
btnLay->addWidget(mCryptBtn);
splitterWidget->addWidget(scrollArea);
+ resetAccessibleNameAndDescription();
mEdit->setPlaceholderText(i18nc("@info:placeholder", "Enter a message to encrypt or decrypt..."));
auto fixedFont = QFont(QStringLiteral("Monospace"));
fixedFont.setStyleHint(QFont::TypeWriter);
mEdit->setFont(fixedFont);
mEdit->setAcceptRichText(false);
mEdit->setTabChangesFocus(true);
mEdit->setMinimumWidth(QFontMetrics(fixedFont).averageCharWidth() * 70);
updateButtons();
connect(mEdit, &QTextEdit::textChanged, q, [this]() {
onTextChanged();
});
connect(&appPaletteWatcher, &ApplicationPaletteWatcher::paletteChanged, q, [this]() {
updateButtons();
});
connect(mCryptBtn, &QPushButton::clicked, q, [this]() {
doEncryptSign();
});
connect(mSigEncWidget, &SignEncryptWidget::operationChanged, q, [this]() {
updateButtons();
});
connect(mDecryptBtn, &QPushButton::clicked, q, [this]() {
doDecryptVerify();
});
connect(mImportBtn, &QPushButton::clicked, q, [this]() {
doImport();
});
connect(mRevertBtn, &QPushButton::clicked, q, [this]() {
revert();
});
}
+ void resetAccessibleNameAndDescription()
+ {
+ mEdit->setAccessibleName(i18nc("@label:textbox for assistive tools", "Text to process"));
+ mEdit->setAccessibleDescription(i18nc("@info for assistive tools",
+ "Enter a text that you want to sign and/or encrypt, "
+ "or enter a signed or encrypted text that you want to decrypt or verify. "
+ "You can also enter exported certificates to import them."));
+ }
+
void setText(const QString &text)
{
mSettingText = true;
mEdit->setPlainText(text);
mSettingText = false;
}
void onTextChanged()
{
if (!mSettingText) {
// the user changed the text
mLastOperation = NoOperation;
+ resetAccessibleNameAndDescription();
}
updateButtons();
}
void revert()
{
mLastOperation = NoOperation;
+ resetAccessibleNameAndDescription();
setText(QString::fromUtf8(mInputData));
mRevertBtn->setVisible(false);
removeLastResultItem();
}
void updateRecipientsFromResult(const Kleo::Crypto::DecryptVerifyResult &result)
{
const auto decResult = result.decryptionResult();
for (const auto &recipient : decResult.recipients()) {
if (!recipient.keyID()) {
continue;
}
GpgME::Key key = KeyCache::instance()->findByKeyIDOrFingerprint(recipient.keyID());
if (key.isNull()) {
std::vector<std::string> subids;
subids.push_back(std::string(recipient.keyID()));
for (const auto &subkey : KeyCache::instance()->findSubkeysByKeyID(subids)) {
key = subkey.parent();
break;
}
}
if (key.isNull()) {
qCDebug(KLEOPATRA_LOG) << "Unknown key" << recipient.keyID();
mSigEncWidget->addUnknownRecipient(recipient.keyID());
continue;
}
bool keyFound = false;
for (const auto &existingKey : mSigEncWidget->recipients()) {
if (existingKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(existingKey.primaryFingerprint(), key.primaryFingerprint())) {
keyFound = true;
break;
}
}
if (!keyFound) {
mSigEncWidget->addRecipient(key);
}
}
}
void cryptDone(const std::shared_ptr<const Kleo::Crypto::Task::Result> &result)
{
updateButtons();
mProgressBar->setVisible(false);
mProgressLabel->setVisible(false);
if (!result->error().isCanceled()) {
mLastResultWidget = new ResultItemWidget(result);
mLastResultWidget->showCloseButton(true);
mStatusLay->addWidget(mLastResultWidget);
connect(mLastResultWidget, &ResultItemWidget::closeButtonClicked, q, [this]() {
removeLastResultItem();
});
}
const auto protocol = getProtocol(result);
if (protocol != GpgME::Protocol::UnknownProtocol) {
mSigEncWidget->setProtocol(protocol);
}
if (result->error()) {
if (!result->errorString().isEmpty()) {
KMessageBox::error(q, result->errorString());
}
} else if (!result->error().isCanceled()) {
- setText(QString::fromUtf8(mOutputData));
- mOutputData.clear();
- mRevertBtn->setVisible(true);
-
const auto decryptVerifyResult = dynamic_cast<const Kleo::Crypto::DecryptVerifyResult *>(result.get());
if (decryptVerifyResult) {
updateRecipientsFromResult(*decryptVerifyResult);
}
+
+ switch (mLastOperation) {
+ case SignEncrypt: {
+ switch (mSigEncWidget->currentOp()) {
+ case SignEncryptWidget::Sign:
+ mEdit->setAccessibleName(i18nc("@label:textbox for assistive tools", "Signed text"));
+ mEdit->setAccessibleDescription(i18nc("@info for assistive tools", "This is the result of the signing operation."));
+ break;
+ case SignEncryptWidget::Encrypt:
+ mEdit->setAccessibleName(i18nc("@label:textbox for assistive tools", "Encrypted text"));
+ mEdit->setAccessibleDescription(i18nc("@info for assistive tools", "This is the result of the encryption."));
+ break;
+ case SignEncryptWidget::SignAndEncrypt:
+ mEdit->setAccessibleName(i18nc("@label:textbox for assistive tools", "Signed and encrypted text"));
+ mEdit->setAccessibleDescription(i18nc("@info for assistive tools", "This is the result of the signing operation and the encryption."));
+ break;
+ default:;
+ }
+ break;
+ }
+ case DecryptVerify: {
+ const bool wasDecrypted = decryptVerifyResult && !decryptVerifyResult->decryptionResult().isNull();
+ const bool wasVerified = decryptVerifyResult && (decryptVerifyResult->verificationResult().numSignatures() > 0);
+ if (wasDecrypted && wasVerified) {
+ mEdit->setAccessibleName(i18nc("@label:textbox for assistive tools", "Decrypted and verified text"));
+ mEdit->setAccessibleDescription(i18nc("@info for assistive tools", "This is the result of the decryption and signature verification."));
+ } else if (wasDecrypted) {
+ mEdit->setAccessibleName(i18nc("@label:textbox for assistive tools", "Decrypted text"));
+ mEdit->setAccessibleDescription(i18nc("@info for assistive tools", "This is the result of the decryption."));
+ } else if (wasVerified) {
+ mEdit->setAccessibleName(i18nc("@label:textbox for assistive tools", "Verified text"));
+ mEdit->setAccessibleDescription(i18nc("@info for assistive tools", "This is the result of the signature verification."));
+ }
+ break;
+ }
+ default:
+ qCDebug(KLEOPATRA_LOG) << __func__ << "Unexpected last operation:" << mLastOperation;
+ };
+
+ setText(QString::fromUtf8(mOutputData));
+ mOutputData.clear();
+ mRevertBtn->setVisible(true);
}
}
void doDecryptVerify()
{
doCryptoCommon();
mSigEncWidget->clearAddedRecipients();
mProgressLabel->setText(i18n("Decrypt / Verify") + QStringLiteral("..."));
auto input = Input::createFromByteArray(&mInputData, i18n("Notepad"));
auto output = Output::createFromByteArray(&mOutputData, i18n("Notepad"));
AbstractDecryptVerifyTask *task;
auto classification = input->classification();
if (classification & Class::OpaqueSignature || classification & Class::ClearsignedMessage) {
auto verifyTask = new VerifyOpaqueTask();
verifyTask->setInput(input);
verifyTask->setOutput(output);
task = verifyTask;
} else {
auto decTask = new DecryptVerifyTask();
decTask->setInput(input);
decTask->setOutput(output);
task = decTask;
}
try {
task->autodetectProtocolFromInput();
} catch (const Kleo::Exception &e) {
KMessageBox::error(q, e.message());
updateButtons();
mProgressBar->setVisible(false);
mProgressLabel->setVisible(false);
return;
}
task->setDataSource(Task::Notepad);
connect(task, &Task::result, q, [this, task](const std::shared_ptr<const Kleo::Crypto::Task::Result> &result) {
qCDebug(KLEOPATRA_LOG) << "Decrypt / Verify done. Err:" << result->error().code();
task->deleteLater();
cryptDone(result);
});
mLastOperation = DecryptVerify;
task->start();
}
void removeLastResultItem()
{
if (mLastResultWidget) {
mStatusLay->removeWidget(mLastResultWidget);
delete mLastResultWidget;
mLastResultWidget = nullptr;
}
}
void doCryptoCommon()
{
mCryptBtn->setEnabled(false);
mDecryptBtn->setEnabled(false);
mImportBtn->setEnabled(false);
mProgressBar->setVisible(true);
mProgressLabel->setVisible(true);
mInputData = mEdit->toPlainText().toUtf8();
removeLastResultItem();
}
void doEncryptSign()
{
if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) {
KMessageBox::error(q->topLevelWidget(),
xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"<para>Sorry! You cannot use <application>Kleopatra</application> for signing or encryption "
"because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>",
DeVSCompliance::name(true)));
return;
}
mSigEncWidget->saveOwnKeys();
doCryptoCommon();
switch (mSigEncWidget->currentOp()) {
case SignEncryptWidget::Sign:
mProgressLabel->setText(i18nc("@info:progress", "Signing notepad..."));
break;
case SignEncryptWidget::Encrypt:
mProgressLabel->setText(i18nc("@info:progress", "Encrypting notepad..."));
break;
case SignEncryptWidget::SignAndEncrypt:
mProgressLabel->setText(i18nc("@info:progress", "Signing and encrypting notepad..."));
break;
default:;
};
auto input = Input::createFromByteArray(&mInputData, i18n("Notepad"));
auto output = Output::createFromByteArray(&mOutputData, i18n("Notepad"));
auto task = new SignEncryptTask();
task->setInput(input);
task->setOutput(output);
task->setDataSource(Task::Notepad);
const auto sigKey = mSigEncWidget->signUserId().parent();
const std::vector<GpgME::Key> recipients = mSigEncWidget->recipients();
const bool encrypt = mSigEncWidget->encryptSymmetric() || !recipients.empty();
const bool sign = !sigKey.isNull();
if (sign) {
task->setSign(true);
std::vector<GpgME::Key> signVector;
signVector.push_back(sigKey);
task->setSigners(signVector);
} else {
task->setSign(false);
}
task->setEncrypt(encrypt);
task->setRecipients(recipients);
task->setEncryptSymmetric(mSigEncWidget->encryptSymmetric());
task->setAsciiArmor(true);
if (sign && !encrypt && sigKey.protocol() == GpgME::OpenPGP) {
task->setClearsign(true);
}
connect(task, &Task::result, q, [this, task](const std::shared_ptr<const Kleo::Crypto::Task::Result> &result) {
qCDebug(KLEOPATRA_LOG) << "Encrypt / Sign done. Err:" << result->error().code();
task->deleteLater();
cryptDone(result);
});
mLastOperation = SignEncrypt;
task->start();
}
void doImport()
{
doCryptoCommon();
mProgressLabel->setText(i18n("Importing..."));
auto cmd = new Kleo::ImportCertificateFromDataCommand(mInputData, mImportProto);
connect(cmd, &Kleo::ImportCertificatesCommand::finished, q, [this]() {
updateButtons();
mProgressBar->setVisible(false);
mProgressLabel->setVisible(false);
mRevertBtn->setVisible(true);
setText(QString());
});
mLastOperation = Import;
cmd->start();
}
void checkImportProtocol()
{
QGpgME::QByteArrayDataProvider dp(mEdit->toPlainText().toUtf8());
GpgME::Data data(&dp);
auto type = data.type();
if (type == GpgME::Data::PGPKey) {
mImportProto = GpgME::OpenPGP;
} else if (type == GpgME::Data::X509Cert || type == GpgME::Data::PKCS12) {
mImportProto = GpgME::CMS;
} else {
mImportProto = GpgME::UnknownProtocol;
}
}
void updateButtons()
{
mAdditionalInfoLabel->setVisible(false);
mDecryptBtn->setEnabled(mEdit->document() && !mEdit->document()->isEmpty());
checkImportProtocol();
mImportBtn->setEnabled(mImportProto != GpgME::UnknownProtocol);
mCryptBtn->setEnabled(mSigEncWidget->currentOp() != SignEncryptWidget::NoOperation);
switch (mSigEncWidget->currentOp()) {
case SignEncryptWidget::Sign:
mCryptBtn->setText(i18nc("@action:button", "Sign Notepad"));
break;
case SignEncryptWidget::Encrypt:
mCryptBtn->setText(i18nc("@action:button", "Encrypt Notepad"));
break;
case SignEncryptWidget::SignAndEncrypt:
default:
mCryptBtn->setText(i18nc("@action:button", "Sign / Encrypt Notepad"));
};
if (!mSigEncWidget->isComplete()) {
mCryptBtn->setEnabled(false);
}
if (DeVSCompliance::isActive()) {
const bool de_vs = DeVSCompliance::isCompliant() && mSigEncWidget->isDeVsAndValid();
DeVSCompliance::decorate(mCryptBtn, de_vs);
mAdditionalInfoLabel->setText(DeVSCompliance::name(de_vs));
mAdditionalInfoLabel->setVisible(true);
if (!DeVSCompliance::isCompliant()) {
mCryptBtn->setEnabled(false);
}
mMessageWidget->setVisible(!DeVSCompliance::isCompliant());
if (mMessageWidget->isVisible() && QAccessible::isActive()) {
mMessageWidget->setFocus();
}
}
}
private:
PadWidget *const q;
ApplicationPaletteWatcher appPaletteWatcher;
QTextEdit *mEdit;
QPushButton *mCryptBtn;
QPushButton *mDecryptBtn;
QPushButton *mImportBtn;
QPushButton *mRevertBtn;
KMessageWidget *mMessageWidget;
QLabel *mAdditionalInfoLabel;
QByteArray mInputData;
QByteArray mOutputData;
SignEncryptWidget *mSigEncWidget;
QProgressBar *mProgressBar;
QLabel *mProgressLabel;
QVBoxLayout *mStatusLay;
ResultItemWidget *mLastResultWidget;
QList<GpgME::Key> mAutoAddedKeys;
GpgME::Protocol mImportProto;
bool mSettingText = false;
Operation mLastOperation = NoOperation;
};
PadWidget::PadWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
void PadWidget::focusFirstChild(Qt::FocusReason reason)
{
d->mEdit->setFocus(reason);
}
#include "moc_padwidget.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jan 20, 1:43 AM (1 d, 20 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8d/34/6028b9fbfd4b62e77a325576f835
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment