certificatedetailswidget.cpp 23.1 KB
Newer Older
1
2
/*  SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
    SPDX-FileCopyrightText: 2017 Intevation GmbH
3

4
    SPDX-License-Identifier: GPL-2.0-or-later
5
6
7
8
9
*/

#include "certificatedetailswidget.h"
#include "ui_certificatedetailswidget.h"
#include "kleopatra_debug.h"
10
#include "exportdialog.h"
11
12
#include "trustchainwidget.h"
#include "subkeyswidget.h"
13
#include "weboftrustdialog.h"
14
15
16
17

#include "commands/changepassphrasecommand.h"
#include "commands/changeexpirycommand.h"
#include "commands/certifycertificatecommand.h"
18
#include "commands/revokecertificationcommand.h"
19
#include "commands/adduseridcommand.h"
20
#include "commands/genrevokecommand.h"
21
#include "commands/detailscommand.h"
22
#include "commands/dumpcertificatecommand.h"
23
24

#include "utils/tags.h"
25

26
27
28
#include <Libkleo/Formatting>
#include <Libkleo/Dn>
#include <Libkleo/KeyCache>
29

30
#include <gpgme++/context.h>
31
#include <gpgme++/key.h>
32
#include <gpgme++/keylistresult.h>
33
34
#include <gpgme++/tofuinfo.h>

35
36
37
#include <QGpgME/Protocol>
#include <QGpgME/KeyListJob>

38
39
40
41
42
#include <QDateTime>
#include <QDialogButtonBox>
#include <QMenu>
#include <KConfigGroup>
#include <KSharedConfig>
43
#include <QLocale>
44

45
46
47
48
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
Ingo Klöcker's avatar
Ingo Klöcker committed
49
50
51
#if GPGMEPP_VERSION >= 0x10E01 // 1.14.1
#include <QGpgME/Debug>
#endif
52

53
54
55
56
#define HIDE_ROW(row) \
    ui.row->setVisible(false); \
    ui.row##Lbl->setVisible(false);

57
Q_DECLARE_METATYPE(GpgME::UserID)
58

59
60
using namespace Kleo;

61
62
63
64
class CertificateDetailsWidget::Private
{
public:
    Private(CertificateDetailsWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
65
        : updateInProgress (false), q(parent)
66
67
68
69
70
71
72
    {}

    void setupCommonProperties();
    void setupPGPProperties();
    void setupSMIMEProperties();

    void revokeUID(const GpgME::UserID &uid);
73
    void genRevokeCert();
74
    void certifyClicked();
75
    void webOfTrustClicked();
76
    void exportClicked();
77
78
79
80
81
82
83
84
85
86
87
88
89
    void addUserID();
    void changePassphrase();
    void changeExpiration();
    void keysMayHaveChanged();
    void showTrustChainDialog();
    void showMoreDetails();
    void publishCertificate();
    void userIDTableContextMenuRequested(const QPoint &p);

    QString tofuTooltipString(const GpgME::UserID &uid) const;

    void smimeLinkActivated(const QString &link);

90
    void setUpdatedKey(const GpgME::Key &key);
91
92
93
    void keyListDone(const GpgME::KeyListResult &,
                     const std::vector<GpgME::Key> &, const QString &,
                     const GpgME::Error &);
94

95
96
    Ui::CertificateDetailsWidget ui;
    GpgME::Key key;
97
    bool updateInProgress;
98
private:
Laurent Montel's avatar
Laurent Montel committed
99
    CertificateDetailsWidget *const q;
100
101
102
103
104
105
106
107
108
109
110
111
112
};

void CertificateDetailsWidget::Private::setupCommonProperties()
{
    // TODO: Enable once implemented
    HIDE_ROW(publishing)

    const bool hasSecret = key.hasSecret();
    const bool isOpenPGP = key.protocol() == GpgME::OpenPGP;
    // TODO: Enable once implemented
    const bool canRevokeUID = false; // isOpenPGP && hasSecret

    ui.changePassphraseBtn->setVisible(hasSecret);
113
    ui.genRevokeBtn->setVisible(isOpenPGP && hasSecret);
114
    ui.certifyBtn->setVisible(isOpenPGP && !hasSecret);
115
    ui.changeExpirationBtn->setVisible(isOpenPGP && hasSecret);
116
    ui.addUserIDBtn->setVisible(hasSecret && isOpenPGP);
117
    ui.webOfTrustBtn->setVisible(isOpenPGP);
118

119
120
    ui.hboxLayout_1->addStretch(1);

121
122
123
124
    ui.validFrom->setText(Kleo::Formatting::creationDateString(key));
    const QString expiry = Kleo::Formatting::expirationDateString(key);
    ui.expires->setText(expiry.isEmpty() ? i18nc("Expires", "never") : expiry);
    ui.type->setText(Kleo::Formatting::type(key));
125
    ui.fingerprint->setText(Formatting::prettyID(key.primaryFingerprint()));
126

127
128
129
130
131
132
    if (Kleo::Formatting::complianceMode().isEmpty()) {
        HIDE_ROW(compliance)
    } else {
        ui.complianceLbl->setText(Kleo::Formatting::complianceStringForKey(key));
    }

133
134
    ui.userIDTable->clear();

135
    QStringList headers = { i18n("Email"), i18n("Name"), i18n("Trust Level"), i18n("Tags") };
136
137
    if (canRevokeUID) {
        headers << QString();
138
139
140
141
142
143
144
    }
    ui.userIDTable->setColumnCount(headers.count());
    ui.userIDTable->setColumnWidth(0, 200);
    ui.userIDTable->setColumnWidth(1, 200);
    ui.userIDTable->setHeaderLabels(headers);

    const auto uids = key.userIDs();
145
146
    for (unsigned int i = 0; i < uids.size(); ++i) {
        const auto &uid = uids[i];
147
148
149
        auto item = new QTreeWidgetItem;
        const QString toolTip = tofuTooltipString(uid);
        item->setData(0, Qt::UserRole, QVariant::fromValue(uid));
150
151

        auto pMail = Kleo::Formatting::prettyEMail(uid);
152
153
        auto pName = Kleo::Formatting::prettyName(uid);
        if (!isOpenPGP && pMail.isEmpty() && !pName.isEmpty()) {
154
155
156
157
158
159
160
161
162
163
            // S/MIME UserIDs are sometimes split, with one userID
            // containing the name another the Mail, we merge these
            // UID's into a single item.

            if (i + 1 < uids.size()) {
                pMail = Kleo::Formatting::prettyEMail(uids[i + 1]);
                // skip next uid
                ++i;
            }
        }
164
165
166
167
168
169
170
171

        if (!isOpenPGP && pMail.isEmpty() && pName.isEmpty()) {
            // S/MIME certificates sometimes contain urls where both
            // name and mail is empty. In that case we print whatever
            // the uid is as name.
            //
            // Can be ugly like (3:uri24:http://ca.intevation.org), but
            // this is better then showing an empty entry.
172
            pName = QString::fromLatin1(uid.id());
173
174
        }

175
        item->setData(0, Qt::DisplayRole, pMail);
176
        item->setData(0, Qt::ToolTipRole, toolTip);
177
        item->setData(1, Qt::DisplayRole, pName);
178
        item->setData(1, Qt::ToolTipRole, toolTip);
179
180
181
182
183
184

        QIcon trustIcon;
        if (updateInProgress) {
           trustIcon = QIcon::fromTheme(QStringLiteral("emblem-question"));
           item->setData(2, Qt::DisplayRole, i18n("Updating..."));
        } else {
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
            switch (uid.validity()) {
            case GpgME::UserID::Unknown:
            case GpgME::UserID::Undefined:
                trustIcon = QIcon::fromTheme(QStringLiteral("emblem-question"));
                break;
            case GpgME::UserID::Never:
                trustIcon = QIcon::fromTheme(QStringLiteral("emblem-error"));
                break;
            case GpgME::UserID::Marginal:
                trustIcon = QIcon::fromTheme(QStringLiteral("emblem-warning"));
                break;
            case GpgME::UserID::Full:
            case GpgME::UserID::Ultimate:
                trustIcon = QIcon::fromTheme(QStringLiteral("emblem-success"));
                break;
            }
            item->setData(2, Qt::DisplayRole, Kleo::Formatting::validityShort(uid));
202
203
204
205
        }
        item->setData(2, Qt::DecorationRole, trustIcon);
        item->setData(2, Qt::ToolTipRole, toolTip);

206
        GpgME::Error err;
207
        QStringList tagList;
208
#ifdef GPGME_HAS_REMARKS
209
        for (const auto &tag: uid.remarks(Tags::tagKeys(), err)) {
210
211
212
            if (err) {
                qCWarning(KLEOPATRA_LOG) << "Getting remarks for user id" << uid.id() << "failed:" << err;
            }
213
            tagList << QString::fromStdString(tag);
214
        }
215
        qCDebug(KLEOPATRA_LOG) << "tagList:" << tagList;
216
#endif
217
218
        const auto tags = tagList.join(QStringLiteral("; "));
        item->setData(3, Qt::DisplayRole, tags);
219
220
        item->setData(3, Qt::ToolTipRole, toolTip);

221
222
223
224
225
226
227
228
229
230
        ui.userIDTable->addTopLevelItem(item);

        if (canRevokeUID) {
            auto button = new QPushButton;
            button->setIcon(QIcon::fromTheme(QStringLiteral("entry-delete")));
            button->setToolTip(i18n("Revoke this User ID"));
            button->setMaximumWidth(32);
            QObject::connect(button, &QPushButton::clicked,
                            q, [this, uid]() { revokeUID(uid); });
            ui.userIDTable->setItemWidget(item, 4, button);
231
232
        }
    }
233
    if (!Tags::tagsEnabled()) {
234
235
        ui.userIDTable->hideColumn(3);
    }
236
237
238
239
}

void CertificateDetailsWidget::Private::revokeUID(const GpgME::UserID &uid)
{
Laurent Montel's avatar
Laurent Montel committed
240
    Q_UNUSED(uid)
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
    qCWarning(KLEOPATRA_LOG) << "Revoking UserID is not implemented. How did you even get here?!?!";
}

void CertificateDetailsWidget::Private::changeExpiration()
{
    auto cmd = new Kleo::Commands::ChangeExpiryCommand(key);
    QObject::connect(cmd, &Kleo::Commands::ChangeExpiryCommand::finished,
                     q, [this]() {
                         ui.changeExpirationBtn->setEnabled(true);
                     });
    ui.changeExpirationBtn->setEnabled(false);
    cmd->start();
}

void CertificateDetailsWidget::Private::changePassphrase()
{
    auto cmd = new Kleo::Commands::ChangePassphraseCommand(key);
    QObject::connect(cmd, &Kleo::Commands::ChangePassphraseCommand::finished,
Laurent Montel's avatar
Laurent Montel committed
259
                     q, [this]() {
260
261
262
263
264
265
                         ui.changePassphraseBtn->setEnabled(true);
                     });
    ui.changePassphraseBtn->setEnabled(false);
    cmd->start();
}

266
267
268
269
void CertificateDetailsWidget::Private::genRevokeCert()
{
    auto cmd = new Kleo::Commands::GenRevokeCommand(key);
    QObject::connect(cmd, &Kleo::Commands::GenRevokeCommand::finished,
Laurent Montel's avatar
Laurent Montel committed
270
                     q, [this]() {
271
272
273
274
275
276
                         ui.genRevokeBtn->setEnabled(true);
                     });
    ui.genRevokeBtn->setEnabled(false);
    cmd->start();
}

277
278
279
280
void CertificateDetailsWidget::Private::certifyClicked()
{
    auto cmd = new Kleo::Commands::CertifyCertificateCommand(key);
    QObject::connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished,
Laurent Montel's avatar
Laurent Montel committed
281
                     q, [this]() {
282
283
284
285
286
287
                         ui.certifyBtn->setEnabled(true);
                     });
    ui.certifyBtn->setEnabled(false);
    cmd->start();
}

288
289
290
291
292
293
294
void CertificateDetailsWidget::Private::webOfTrustClicked()
{
    QScopedPointer<WebOfTrustDialog> dlg(new WebOfTrustDialog(q));
    dlg->setKey(key);
    dlg->exec();
}

295
296
297
298
299
300
301
void CertificateDetailsWidget::Private::exportClicked()
{
    QScopedPointer<ExportDialog> dlg(new ExportDialog(q));
    dlg->setKey(key);
    dlg->exec();
}

302
303
304
305
void CertificateDetailsWidget::Private::addUserID()
{
    auto cmd = new Kleo::Commands::AddUserIDCommand(key);
    QObject::connect(cmd, &Kleo::Commands::AddUserIDCommand::finished,
Laurent Montel's avatar
Laurent Montel committed
306
                     q, [this]() {
307
                         ui.addUserIDBtn->setEnabled(true);
308
309
                         key.update();
                         q->setKey(key);
310
311
312
313
314
315
316
317
318
                     });
    ui.addUserIDBtn->setEnabled(false);
    cmd->start();
}

void CertificateDetailsWidget::Private::keysMayHaveChanged()
{
    auto newKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint());
    if (!newKey.isNull()) {
319
        setUpdatedKey(newKey);
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
    }
}

void CertificateDetailsWidget::Private::showTrustChainDialog()
{
    QScopedPointer<TrustChainDialog> dlg(new TrustChainDialog(q));
    dlg->setKey(key);
    dlg->exec();
}

void CertificateDetailsWidget::Private::publishCertificate()
{
    qCWarning(KLEOPATRA_LOG) << "publishCertificateis not implemented.";
    //TODO
}

void CertificateDetailsWidget::Private::userIDTableContextMenuRequested(const QPoint &p)
{
    auto item = ui.userIDTable->itemAt(p);
    if (!item) {
        return;
    }

    const auto userID = item->data(0, Qt::UserRole).value<GpgME::UserID>();

    QMenu *menu = new QMenu(q);
    menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-sign")),
347
                    i18n("Certify..."),
348
349
350
351
                    q, [this, userID]() {
        auto cmd = new Kleo::Commands::CertifyCertificateCommand(userID);
        ui.userIDTable->setEnabled(false);
        connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished,
352
353
354
355
356
                q, [this]() {
            ui.userIDTable->setEnabled(true);
            // Trigger an update when done
            q->setKey(key);
        });
357
358
        cmd->start();
    });
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    if (Kleo::Commands::RevokeCertificationCommand::isSupported()) {
        menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-revoke")),
                        i18n("Revoke Certification..."),
                        q, [this, userID]() {
            auto cmd = new Kleo::Commands::RevokeCertificationCommand(userID);
            ui.userIDTable->setEnabled(false);
            connect(cmd, &Kleo::Commands::RevokeCertificationCommand::finished,
                    q, [this]() {
                ui.userIDTable->setEnabled(true);
                // Trigger an update when done
                q->setKey(key);
            });
            cmd->start();
        });
    }
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
    connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
    menu->popup(ui.userIDTable->viewport()->mapToGlobal(p));
}

void CertificateDetailsWidget::Private::showMoreDetails()
{
    ui.moreDetailsBtn->setEnabled(false);
    if (key.protocol() == GpgME::CMS) {
        auto cmd = new Kleo::Commands::DumpCertificateCommand(key);
        connect(cmd, &Kleo::Commands::DumpCertificateCommand::finished,
                q, [this]() {
                    ui.moreDetailsBtn->setEnabled(true);
                });
        cmd->setUseDialog(true);
        cmd->start();
    } else {
        QScopedPointer<SubKeysDialog> dlg(new SubKeysDialog(q));
        dlg->setKey(key);
        dlg->exec();
        ui.moreDetailsBtn->setEnabled(true);
    }
}

QString CertificateDetailsWidget::Private::tofuTooltipString(const GpgME::UserID &uid) const
{
    const auto tofu = uid.tofuInfo();
    if (tofu.isNull()) {
        return QString();
    }

    QString html = QStringLiteral("<table border=\"0\" cell-padding=\"5\">");
    const auto appendRow = [&html](const QString &lbl, const QString &val) {
        html += QStringLiteral("<tr>"
                               "<th style=\"text-align: right; padding-right: 5px; white-space: nowrap;\">%1:</th>"
                               "<td style=\"white-space: nowrap;\">%2</td>"
                               "</tr>")
                    .arg(lbl, val);
    };
    const auto appendHeader = [this, &html](const QString &hdr) {
        html += QStringLiteral("<tr><th colspan=\"2\" style=\"background-color: %1; color: %2\">%3</th></tr>")
                    .arg(q->palette().highlight().color().name(),
                         q->palette().highlightedText().color().name(),
                         hdr);
    };
    const auto dateTime = [](long ts) {
419
420
        QLocale l;
        return ts == 0 ? i18n("never") : l.toString(QDateTime::fromSecsSinceEpoch(ts), QLocale::ShortFormat);
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
    };
    appendHeader(i18n("Signing"));
    appendRow(i18n("First message"), dateTime(tofu.signFirst()));
    appendRow(i18n("Last message"), dateTime(tofu.signLast()));
    appendRow(i18n("Message count"), QString::number(tofu.signCount()));
    appendHeader(i18n("Encryption"));
    appendRow(i18n("First message"), dateTime(tofu.encrFirst()));
    appendRow(i18n("Last message"), dateTime(tofu.encrLast()));
    appendRow(i18n("Message count"), QString::number(tofu.encrCount()));

    html += QStringLiteral("</table>");
    // Make sure the tooltip string is different for each UserID, even if the
    // data are the same, otherwise the tooltip is not updated and moved when
    // user moves mouse from one row to another.
    html += QStringLiteral("<!-- %1 //-->").arg(QString::fromUtf8(uid.id()));
    return html;
}


void CertificateDetailsWidget::Private::setupPGPProperties()
{
    HIDE_ROW(smimeOwner)
    HIDE_ROW(smimeIssuer)
    ui.smimeRelatedAddresses->setVisible(false);
    ui.trustChainDetailsBtn->setVisible(false);

    ui.userIDTable->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui.userIDTable, &QAbstractItemView::customContextMenuRequested,
            q, [this](const QPoint &p) { userIDTableContextMenuRequested(p); });
}

452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
static QString formatDNToolTip(const Kleo::DN &dn)
{
    QString html = QStringLiteral("<table border=\"0\" cell-spacing=15>");

    const auto appendRow = [&html, dn](const QString &lbl, const QString &attr) {
                                const QString val = dn[attr];
                                if (!val.isEmpty()) {
                                    html += QStringLiteral(
                                            "<tr><th style=\"text-align: left; white-space: nowrap\">%1:</th>"
                                                "<td style=\"white-space: nowrap\">%2</td>"
                                            "</tr>").arg(lbl, val);
                                }
                            };
    appendRow(i18n("Common Name"), QStringLiteral("CN"));
    appendRow(i18n("Organization"), QStringLiteral("O"));
    appendRow(i18n("Street"), QStringLiteral("STREET"));
    appendRow(i18n("City"),  QStringLiteral("L"));
    appendRow(i18n("State"), QStringLiteral("ST"));
    appendRow(i18n("Country"), QStringLiteral("C"));
    html += QStringLiteral("</table>");

    return html;
}

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
void CertificateDetailsWidget::Private::setupSMIMEProperties()
{
    HIDE_ROW(publishing)

    const auto ownerId = key.userID(0);
    const Kleo::DN dn(ownerId.id());
    const QString cn = dn[QStringLiteral("CN")];
    const QString o = dn[QStringLiteral("O")];
    const QString dnEmail = dn[QStringLiteral("EMAIL")];
    const QString name = cn.isEmpty() ? dnEmail : cn;

    QString owner;
    if (name.isEmpty()) {
        owner = dn.dn();
    } else if (o.isEmpty()) {
        owner = name;
    } else {
        owner = i18nc("<name> of <company>", "%1 of %2", name, o);
    }
495
496
    ui.smimeOwner->setText(owner);
    ui.smimeOwner->setTextInteractionFlags(Qt::TextBrowserInteraction);
497
498
499

    const Kleo::DN issuerDN(key.issuerName());
    const QString issuerCN = issuerDN[QStringLiteral("CN")];
500
    const QString issuer = issuerCN.isEmpty() ? QString::fromUtf8(key.issuerName()) : issuerCN;
501
    ui.smimeIssuer->setText(QStringLiteral("<a href=\"#issuerDetails\">%1</a>").arg(issuer));
502
503
504
505
506

    ui.smimeIssuer->setToolTip(formatDNToolTip(issuerDN));

    ui.smimeOwner->setToolTip(formatDNToolTip(dn));

507
508
509
510
}

void CertificateDetailsWidget::Private::smimeLinkActivated(const QString &link)
{
511
512
513
514
515
516
517
518
519
    if (link == QLatin1String("#issuerDetails")) {
        const auto parentKey = KeyCache::instance()->findIssuers(key, KeyCache::NoOption);

        if (!parentKey.size()) {
            return;
        }
        auto cmd = new Kleo::Commands::DetailsCommand(parentKey[0], nullptr);
        cmd->setParentWidget(q);
        cmd->start();
520
521
        return;
    }
522
    qCWarning(KLEOPATRA_LOG) << "Unknown link activated:" << link;
523
524
525
526
527
528
529
530
531
532
533
534
}

CertificateDetailsWidget::CertificateDetailsWidget(QWidget *parent)
    : QWidget(parent)
    , d(new Private(this))
{
    d->ui.setupUi(this);

    connect(d->ui.addUserIDBtn, &QPushButton::clicked,
            this, [this]() { d->addUserID(); });
    connect(d->ui.changePassphraseBtn, &QPushButton::clicked,
            this, [this]() { d->changePassphrase(); });
535
536
    connect(d->ui.genRevokeBtn, &QPushButton::clicked,
            this, [this]() { d->genRevokeCert(); });
537
538
539
540
541
542
543
544
545
546
547
548
    connect(d->ui.changeExpirationBtn, &QPushButton::clicked,
            this, [this]() { d->changeExpiration(); });
    connect(d->ui.smimeOwner, &QLabel::linkActivated,
            this, [this](const QString &link) { d->smimeLinkActivated(link); });
    connect(d->ui.smimeIssuer, &QLabel::linkActivated,
            this, [this](const QString &link) { d->smimeLinkActivated(link); });
    connect(d->ui.trustChainDetailsBtn, &QPushButton::pressed,
            this, [this]() { d->showTrustChainDialog(); });
    connect(d->ui.moreDetailsBtn, &QPushButton::pressed,
            this, [this]() { d->showMoreDetails(); });
    connect(d->ui.publishing, &QPushButton::pressed,
            this, [this]() { d->publishCertificate(); });
549
550
    connect(d->ui.certifyBtn, &QPushButton::clicked,
            this, [this]() { d->certifyClicked(); });
551
552
    connect(d->ui.webOfTrustBtn, &QPushButton::clicked,
            this, [this]() { d->webOfTrustClicked(); });
553
554
    connect(d->ui.exportBtn, &QPushButton::clicked,
            this, [this]() { d->exportClicked(); });
555
556
557
558
559
560
561
562
563

    connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged,
            this, [this]() { d->keysMayHaveChanged(); });
}

CertificateDetailsWidget::~CertificateDetailsWidget()
{
}

564
565
566
567
568
569
570
571
572
573
574
575
576
577
void CertificateDetailsWidget::Private::keyListDone(const GpgME::KeyListResult &,
                                                    const std::vector<GpgME::Key> &keys,
                                                    const QString &,
                                                    const GpgME::Error &) {
        updateInProgress = false;
        if (keys.size() != 1) {
            qCWarning(KLEOPATRA_LOG) << "Invalid keylist result in update.";
            return;
        }
        // As we listen for keysmayhavechanged we get the update
        // after updating the keycache.
        KeyCache::mutableInstance()->insert(keys);
}

578
void CertificateDetailsWidget::Private::setUpdatedKey(const GpgME::Key &k)
579
{
580
    key = k;
581

582
    setupCommonProperties();
583
    if (key.protocol() == GpgME::OpenPGP) {
584
        setupPGPProperties();
585
    } else {
586
        setupSMIMEProperties();
587
588
589
    }
}

590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
void CertificateDetailsWidget::setKey(const GpgME::Key &key)
{
    if (key.protocol() == GpgME::CMS) {
        // For everything but S/MIME this should be quick
        // and we don't need to show another status.
        d->updateInProgress = true;
    }
    d->setUpdatedKey(key);

    // Run a keylistjob with full details (TOFU / Validate)
    QGpgME::KeyListJob *job = key.protocol() == GpgME::OpenPGP ? QGpgME::openpgp()->keyListJob(false, true, true) :
                                                                 QGpgME::smime()->keyListJob(false, true, true);

    auto ctx = QGpgME::Job::context(job);
    ctx->addKeyListMode(GpgME::WithTofu);
605
    ctx->addKeyListMode(GpgME::SignatureNotations);
606
607
608
    if (key.hasSecret()) {
        ctx->addKeyListMode(GpgME::WithSecret);
    }
609

610
    // Windows QGpgME new style connect problem makes this necessary.
Laurent Montel's avatar
Laurent Montel committed
611
612
    connect(job, SIGNAL(result(GpgME::KeyListResult,std::vector<GpgME::Key>,QString,GpgME::Error)),
            this, SLOT(keyListDone(GpgME::KeyListResult,std::vector<GpgME::Key>,QString,GpgME::Error)));
613

614
    job->start(QStringList() << QLatin1String(key.primaryFingerprint()));
615
616
}

617
618
619
620
621
622
623
624
GpgME::Key CertificateDetailsWidget::key() const
{
    return d->key;
}

CertificateDetailsDialog::CertificateDetailsDialog(QWidget *parent)
    : QDialog(parent)
{
625
    setWindowTitle(i18nc("@title:window", "Certificate Details"));
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
    auto l = new QVBoxLayout(this);
    l->addWidget(new CertificateDetailsWidget(this));

    auto bbox = new QDialogButtonBox(this);
    auto btn = bbox->addButton(QDialogButtonBox::Close);
    connect(btn, &QPushButton::pressed, this, &QDialog::accept);
    l->addWidget(bbox);
    readConfig();
}

CertificateDetailsDialog::~CertificateDetailsDialog()
{
    writeConfig();
}

void CertificateDetailsDialog::readConfig()
{
    KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog");
    const QSize size = dialog.readEntry("Size", QSize(730, 280));
    if (size.isValid()) {
        resize(size);
    }
}

void CertificateDetailsDialog::writeConfig()
{
    KConfigGroup dialog(KSharedConfig::openConfig(), "CertificateDetailsDialog");
    dialog.writeEntry("Size", size());
    dialog.sync();
}

void CertificateDetailsDialog::setKey(const GpgME::Key &key)
{
    auto w = findChild<CertificateDetailsWidget*>();
    Q_ASSERT(w);
    w->setKey(key);
}

GpgME::Key CertificateDetailsDialog::key() const
{
    auto w = findChild<CertificateDetailsWidget*>();
    Q_ASSERT(w);
    return w->key();
}
670
671

#include "moc_certificatedetailswidget.cpp"