resultitemwidget.cpp 12.1 KB
Newer Older
Frank Osterfeld's avatar
Frank Osterfeld committed
1
2
3
4
/* -*- mode: c++; c-basic-offset:4 -*-
    crypto/gui/resultitemwidget.cpp

    This file is part of Kleopatra, the KDE keymanager
5
    SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
Frank Osterfeld's avatar
Frank Osterfeld committed
6

7
8
    SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
    SPDX-FileContributor: Intevation GmbH
Frank Osterfeld's avatar
Frank Osterfeld committed
9

10
    SPDX-License-Identifier: GPL-2.0-or-later
Frank Osterfeld's avatar
Frank Osterfeld committed
11
12
13
14
15
16
*/

#include <config-kleopatra.h>

#include "resultitemwidget.h"

Andre Heinecke's avatar
Andre Heinecke committed
17
18
#include "utils/auditlog.h"
#include "commands/command.h"
19
20
21
#include "commands/importcertificatefromfilecommand.h"
#include "commands/lookupcertificatescommand.h"
#include "crypto/decryptverifytask.h"
22
#include "view/urllabel.h"
Thomas McGuire's avatar
Thomas McGuire committed
23

24
#include <Libkleo/MessageBox>
25
#include <Libkleo/Classify>
Frank Osterfeld's avatar
Frank Osterfeld committed
26

27
#include <gpgme++/key.h>
28
#include <gpgme++/decryptionresult.h>
29

Frank Osterfeld's avatar
Frank Osterfeld committed
30
#include <KLocalizedString>
Laurent Montel's avatar
Laurent Montel committed
31
#include <QPushButton>
32
#include <KStandardGuiItem>
Laurent Montel's avatar
Laurent Montel committed
33
#include "kleopatra_debug.h"
Frank Osterfeld's avatar
Frank Osterfeld committed
34
35
36
37
#include <QHBoxLayout>
#include <QLabel>
#include <QUrl>
#include <QVBoxLayout>
Laurent Montel's avatar
Laurent Montel committed
38
#include <KGuiItem>
39
#include <KColorScheme>
Frank Osterfeld's avatar
Frank Osterfeld committed
40
41
42
43

using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
44

Laurent Montel's avatar
Laurent Montel committed
45
46
namespace
{
47
// TODO move out of here
Laurent Montel's avatar
Laurent Montel committed
48
49
50
51
static QColor colorForVisualCode(Task::Result::VisualCode code)
{
    switch (code) {
    case Task::Result::AllGood:
52
        return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color();
Laurent Montel's avatar
Laurent Montel committed
53
54
    case Task::Result::NeutralError:
    case Task::Result::Warning:
55
        return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NormalBackground).color();
Laurent Montel's avatar
Laurent Montel committed
56
    case Task::Result::Danger:
57
        return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color();
Laurent Montel's avatar
Laurent Montel committed
58
59
    case Task::Result::NeutralSuccess:
    default:
60
61
62
63
64
65
66
67
68
69
        return QColor(0x00, 0x80, 0xFF); // light blue
    }
}
static QColor txtColorForVisualCode(Task::Result::VisualCode code)
{
    switch (code) {
    case Task::Result::AllGood:
        return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::PositiveText).color();
    case Task::Result::NeutralError:
    case Task::Result::Warning:
70
        return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NormalText).color();
71
72
73
74
75
    case Task::Result::Danger:
        return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color();
    case Task::Result::NeutralSuccess:
    default:
        return QColor(0xFF, 0xFF, 0xFF); // white
Frank Osterfeld's avatar
Frank Osterfeld committed
76
77
    }
}
Laurent Montel's avatar
Laurent Montel committed
78
}
Frank Osterfeld's avatar
Frank Osterfeld committed
79

Laurent Montel's avatar
Laurent Montel committed
80
81
82
class ResultItemWidget::Private
{
    ResultItemWidget *const q;
Frank Osterfeld's avatar
Frank Osterfeld committed
83
public:
Laurent Montel's avatar
Laurent Montel committed
84
    explicit Private(const std::shared_ptr<const Task::Result> &result, ResultItemWidget *qq) : q(qq), m_result(result)
Laurent Montel's avatar
Laurent Montel committed
85
    {
86
        Q_ASSERT(m_result);
Laurent Montel's avatar
Laurent Montel committed
87
    }
Frank Osterfeld's avatar
Frank Osterfeld committed
88

Laurent Montel's avatar
Laurent Montel committed
89
    void slotLinkActivated(const QString &);
Frank Osterfeld's avatar
Frank Osterfeld committed
90
91
    void updateShowDetailsLabel();

92
    void addKeyImportButton(QBoxLayout *lay, bool search);
93
    void addIgnoreMDCButton(QBoxLayout *lay);
94
95
96

    void oneImportFinished();

97
    const std::shared_ptr<const Task::Result> m_result;
Laurent Montel's avatar
Laurent Montel committed
98
    QLabel *m_detailsLabel = nullptr;
99
    UrlLabel *m_auditLogLabel = nullptr;
Laurent Montel's avatar
Laurent Montel committed
100
101
    QPushButton *m_closeButton = nullptr;
    bool m_importCanceled = false;
Frank Osterfeld's avatar
Frank Osterfeld committed
102
103
};

104
105
void ResultItemWidget::Private::oneImportFinished()
{
106
107
108
109
110
111
112
    if (m_importCanceled) {
        return;
    }
    if (m_result->parentTask()) {
        m_result->parentTask()->start();
    }
    q->setVisible(false);
113
114
}

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
void ResultItemWidget::Private::addIgnoreMDCButton(QBoxLayout *lay)
{
    if (!m_result || !lay) {
        return;
    }

    const auto dvResult = dynamic_cast<const DecryptVerifyResult *>(m_result.get());
    if (!dvResult) {
        return;
    }
    const auto decResult = dvResult->decryptionResult();

    if (decResult.isNull() || !decResult.error() || !decResult.isLegacyCipherNoMDC())
    {
        return;
    }

    auto btn = new QPushButton(i18n("Force decryption"));
    btn->setFixedSize(btn->sizeHint());

    connect (btn, &QPushButton::clicked, q, [this] () {
        if (m_result->parentTask()) {
            const auto dvTask = dynamic_cast<DecryptVerifyTask*>(m_result->parentTask().data());
            dvTask->setIgnoreMDCError(true);
            dvTask->start();
            q->setVisible(false);
        } else {
            qCWarning(KLEOPATRA_LOG) << "Failed to get parent task";
        }
    });
    lay->addWidget(btn);
}

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
void ResultItemWidget::Private::addKeyImportButton(QBoxLayout *lay, bool search)
{
    if (!m_result || !lay) {
        return;
    }

    const auto dvResult = dynamic_cast<const DecryptVerifyResult *>(m_result.get());
    if (!dvResult) {
        return;
    }
    const auto verifyResult = dvResult->verificationResult();

    if (verifyResult.isNull()) {
        return;
    }

Laurent Montel's avatar
Laurent Montel committed
164
    for (const auto &sig: verifyResult.signatures()) {
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
        if (!(sig.summary() & GpgME::Signature::KeyMissing)) {
            continue;
        }

        auto btn = new QPushButton;
        QString suffix;
        const auto keyid = QLatin1String(sig.fingerprint());
        if (verifyResult.numSignatures() > 1) {
            suffix = QLatin1Char(' ') + keyid;
        }
        btn = new QPushButton(search ? i18nc("1 is optional keyid. No space is intended as it can be empty.",
                                       "Search%1", suffix)
                                     : i18nc("1 is optional keyid. No space is intended as it can be empty.",
                                       "Import%1", suffix));

        if (search) {
Laurent Montel's avatar
Laurent Montel committed
181
            btn->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
182
183
            connect (btn, &QPushButton::clicked, q, [this, btn, keyid] () {
                btn->setEnabled(false);
184
                m_importCanceled = false;
185
                auto cmd = new Kleo::Commands::LookupCertificatesCommand(keyid, nullptr);
186
187
                connect(cmd, &Kleo::Commands::LookupCertificatesCommand::canceled,
                        q, [this]() { m_importCanceled = true; });
188
189
190
191
192
193
194
195
196
                connect(cmd, &Kleo::Commands::LookupCertificatesCommand::finished,
                        q, [this, btn]() {
                    btn->setEnabled(true);
                    oneImportFinished();
                });
                cmd->setParentWidget(q);
                cmd->start();
            });
        } else {
Laurent Montel's avatar
Laurent Montel committed
197
            btn->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-import")));
198
            connect (btn, &QPushButton::clicked, q, [this, btn] () {
199
200
                btn->setEnabled(false);
                m_importCanceled = false;
201
                auto cmd = new Kleo::ImportCertificateFromFileCommand();
202
203
                connect(cmd, &Kleo::ImportCertificateFromFileCommand::canceled,
                        q, [this]() { m_importCanceled = true; });
204
205
206
207
208
209
210
211
212
213
214
215
216
217
                connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished,
                        q, [this, btn]() {
                    btn->setEnabled(true);
                    oneImportFinished();
                });
                cmd->setParentWidget(q);
                cmd->start();
            });
        }
        btn->setFixedSize(btn->sizeHint());
        lay->addWidget(btn);
    }
}

Laurent Montel's avatar
Laurent Montel committed
218
219
static QUrl auditlog_url_template()
{
220
    QUrl url(QStringLiteral("kleoresultitem://showauditlog"));
Thomas McGuire's avatar
Thomas McGuire committed
221
222
223
    return url;
}

Frank Osterfeld's avatar
Frank Osterfeld committed
224
225
void ResultItemWidget::Private::updateShowDetailsLabel()
{
226
227
228
229
230
231
232
    const auto auditLogUrl = m_result->auditLog().asUrl(auditlog_url_template());
    const auto auditLogLinkText =
        m_result->hasError() ? i18n("Diagnostics")
                             : i18nc("The Audit Log is a detailed error log from the gnupg backend",
                                     "Show Audit Log");
    m_auditLogLabel->setUrl(auditLogUrl, auditLogLinkText);
    m_auditLogLabel->setVisible(!auditLogUrl.isEmpty());
Frank Osterfeld's avatar
Frank Osterfeld committed
233
234
}

235
ResultItemWidget::ResultItemWidget(const std::shared_ptr<const Task::Result> &result, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), d(new Private(result, this))
Frank Osterfeld's avatar
Frank Osterfeld committed
236
{
Laurent Montel's avatar
Laurent Montel committed
237
    const QColor color = colorForVisualCode(d->m_result->code());
238
    const QColor txtColor = txtColorForVisualCode(d->m_result->code());
239
    const QString styleSheet = QStringLiteral("QFrame,QLabel { background-color: %1; margin: 0px; }"
240
241
                                              "QFrame#resultFrame{ border-color: %2; border-style: solid; border-radius: 3px; border-width: 1px }"
                                              "QLabel { color: %3; padding: 5px; border-radius: 3px }").arg(color.name()).arg(color.darker(150).name()).arg(txtColor.name());
Laurent Montel's avatar
Laurent Montel committed
242
243
    auto topLayout = new QVBoxLayout(this);
    auto frame = new QFrame;
Laurent Montel's avatar
Laurent Montel committed
244
    frame->setObjectName(QStringLiteral("resultFrame"));
245
    frame->setStyleSheet(styleSheet);
Laurent Montel's avatar
Laurent Montel committed
246
    topLayout->addWidget(frame);
Laurent Montel's avatar
Laurent Montel committed
247
248
249
    auto layout = new QHBoxLayout(frame);
    auto vlay = new QVBoxLayout();
    auto overview = new QLabel;
Laurent Montel's avatar
Laurent Montel committed
250
251
252
253
    overview->setWordWrap(true);
    overview->setTextFormat(Qt::RichText);
    overview->setText(d->m_result->overview());
    overview->setFocusPolicy(Qt::StrongFocus);
254
    overview->setStyleSheet(styleSheet);
255
256
    setFocusPolicy(overview->focusPolicy());
    setFocusProxy(overview);
Laurent Montel's avatar
Laurent Montel committed
257
258
    connect(overview, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString)));

259
260
    vlay->addWidget(overview);
    layout->addLayout(vlay);
Frank Osterfeld's avatar
Frank Osterfeld committed
261
262

    const QString details = d->m_result->details();
263

Laurent Montel's avatar
Laurent Montel committed
264
    auto actionLayout = new QVBoxLayout;
265
266
267
268
269
270
    layout->addLayout(actionLayout);

    d->addKeyImportButton(actionLayout, false);
    // TODO: Only show if auto-key-retrieve is not set.
    d->addKeyImportButton(actionLayout, true);

271
272
    d->addIgnoreMDCButton(actionLayout);

273
274
275
276
277
    d->m_auditLogLabel = new UrlLabel;
    connect(d->m_auditLogLabel, &UrlLabel::linkActivated,
            this, [this](const auto &link) { d->slotLinkActivated(link); });
    actionLayout->addWidget(d->m_auditLogLabel);
    d->m_auditLogLabel->setStyleSheet(styleSheet);
278
    d->m_auditLogLabel->setLinkColor(txtColor);
279

Frank Osterfeld's avatar
Frank Osterfeld committed
280
    d->m_detailsLabel = new QLabel;
Laurent Montel's avatar
Laurent Montel committed
281
282
283
284
285
    d->m_detailsLabel->setWordWrap(true);
    d->m_detailsLabel->setTextFormat(Qt::RichText);
    d->m_detailsLabel->setText(details);
    d->m_detailsLabel->setFocusPolicy(Qt::StrongFocus);
    d->m_detailsLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
286
    d->m_detailsLabel->setStyleSheet(styleSheet);
287

Laurent Montel's avatar
Laurent Montel committed
288
    connect(d->m_detailsLabel, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString)));
289
    vlay->addWidget(d->m_detailsLabel);
290

Laurent Montel's avatar
Laurent Montel committed
291
    d->m_closeButton = new QPushButton;
Laurent Montel's avatar
Laurent Montel committed
292
293
    KGuiItem::assign(d->m_closeButton, KStandardGuiItem::close());
    d->m_closeButton->setFixedSize(d->m_closeButton->sizeHint());
294
    connect(d->m_closeButton, &QAbstractButton::clicked, this, &ResultItemWidget::closeButtonClicked);
295
    actionLayout->addWidget(d->m_closeButton);
Laurent Montel's avatar
Laurent Montel committed
296
    d->m_closeButton->setVisible(false);
297

298
299
300
301
    layout->setStretch(0, 1);
    actionLayout->addStretch(-1);
    vlay->addStretch(-1);

Frank Osterfeld's avatar
Frank Osterfeld committed
302
    d->updateShowDetailsLabel();
303
    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
Frank Osterfeld's avatar
Frank Osterfeld committed
304
305
306
307
308
309
}

ResultItemWidget::~ResultItemWidget()
{
}

Laurent Montel's avatar
Laurent Montel committed
310
void ResultItemWidget::showCloseButton(bool show)
311
{
Laurent Montel's avatar
Laurent Montel committed
312
    d->m_closeButton->setVisible(show);
313
314
}

Frank Osterfeld's avatar
Frank Osterfeld committed
315
316
317
318
319
bool ResultItemWidget::hasErrorResult() const
{
    return d->m_result->hasError();
}

Laurent Montel's avatar
Laurent Montel committed
320
void ResultItemWidget::Private::slotLinkActivated(const QString &link)
Frank Osterfeld's avatar
Frank Osterfeld committed
321
{
322
    Q_ASSERT(m_result);
Andre Heinecke's avatar
Andre Heinecke committed
323
    qCDebug(KLEOPATRA_LOG) << "Link activated: " << link;
Laurent Montel's avatar
Laurent Montel committed
324
    if (link.startsWith(QLatin1String("key:"))) {
Andre Heinecke's avatar
Andre Heinecke committed
325
326
327
328
329
330
331
332
333
        auto split = link.split(QLatin1Char(':'));
        auto fpr = split.value(1);
        if (split.size() == 2 && isFingerprint(fpr)) {
            /* There might be a security consideration here if somehow
             * a short keyid is used in a link and it collides with another.
             * So we additionally check that it really is a fingerprint. */
            auto cmd = Command::commandForQuery(fpr);
            cmd->setParentWId(q->effectiveWinId());
            cmd->start();
Laurent Montel's avatar
Laurent Montel committed
334
        } else {
Andre Heinecke's avatar
Andre Heinecke committed
335
            qCWarning(KLEOPATRA_LOG) << "key link invalid " << link;
Laurent Montel's avatar
Laurent Montel committed
336
        }
Frank Osterfeld's avatar
Frank Osterfeld committed
337
        return;
Frank Osterfeld's avatar
Frank Osterfeld committed
338
    }
339

Laurent Montel's avatar
Laurent Montel committed
340
    const QUrl url(link);
341

Laurent Montel's avatar
Laurent Montel committed
342
    if (url.host() == QLatin1String("showauditlog")) {
Frank Osterfeld's avatar
Frank Osterfeld committed
343
344
345
        q->showAuditLog();
        return;
    }
Laurent Montel's avatar
Laurent Montel committed
346
    qCWarning(KLEOPATRA_LOG) << "Unexpected link scheme: " << link;
Frank Osterfeld's avatar
Frank Osterfeld committed
347
348
}

Laurent Montel's avatar
Laurent Montel committed
349
350
351
void ResultItemWidget::showAuditLog()
{
    MessageBox::auditLog(parentWidget(), d->m_result->auditLog().text());
Frank Osterfeld's avatar
Frank Osterfeld committed
352
353
}

Stephen Kelly's avatar
Stephen Kelly committed
354
#include "moc_resultitemwidget.cpp"