diff --git a/src/commands/exportsecretkeycommand.cpp b/src/commands/exportsecretkeycommand.cpp index 702e0de50..1d37b7e44 100644 --- a/src/commands/exportsecretkeycommand.cpp +++ b/src/commands/exportsecretkeycommand.cpp @@ -1,171 +1,171 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/exportsecretkeycommand.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "exportsecretkeycommand.h" #include "fileoperationspreferences.h" #include "command_p.h" #include #include #include #include #include using namespace Kleo; using namespace Kleo::Commands; using namespace GpgME; ExportSecretKeyCommand::ExportSecretKeyCommand(KeyListController *c) : GnuPGProcessCommand(c) { } ExportSecretKeyCommand::ExportSecretKeyCommand(QAbstractItemView *v, KeyListController *c) : GnuPGProcessCommand(v, c) { } ExportSecretKeyCommand::ExportSecretKeyCommand(const Key &key) : GnuPGProcessCommand(key) { } ExportSecretKeyCommand::~ExportSecretKeyCommand() {} void ExportSecretKeyCommand::setFileName(const QString &fileName) { m_filename = fileName; } bool ExportSecretKeyCommand::preStartHook(QWidget *parent) const { if (!m_filename.isEmpty()) { return true; } const auto key = d->key(); const auto protocol = key.protocol(); QString proposedFileName; const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt(); proposedFileName = QString::fromLatin1(key.primaryFingerprint()) + QLatin1Char('.') + QString::fromLatin1(outputFileExtension(protocol == OpenPGP ? Class::OpenPGP | Class::Ascii | Class::Certificate : Class::CMS | Class::Binary | Class::ExportedPSM, usePGPFileExt)) ; m_filename = FileDialog::getSaveFileNameEx(parent ? parent : d->parentWidgetOrView(), i18n("Export Secret Key"), QStringLiteral("imp"), proposedFileName, protocol == GpgME::OpenPGP ? i18n("Secret Key Files") + QLatin1String(" (*.asc *.gpg *.pgp)") : i18n("Secret Key Files") + QLatin1String(" (*.p12)")); m_armor = m_filename.endsWith (QLatin1String (".asc")); return !m_filename.isEmpty (); } QStringList ExportSecretKeyCommand::arguments() const { const Key key = d->key(); QStringList result; if (key.protocol() == OpenPGP) { result << gpgPath() << QStringLiteral("--batch"); } else { result << gpgSmPath(); } result << QStringLiteral("--output") << m_filename; if (m_armor) { result << QStringLiteral("--armor"); } if (key.protocol() == CMS) { - result << QStringLiteral("--p12-charset") << QLatin1String("utf-8"); + result << QStringLiteral("--p12-charset") << QStringLiteral("utf-8"); } if (key.protocol() == OpenPGP) { result << QStringLiteral("--export-secret-key"); } else { result << QStringLiteral("--export-secret-key-p12"); } result << QLatin1String(key.primaryFingerprint()); return result; } QString ExportSecretKeyCommand::errorCaption() const { return i18nc("@title:window", "Secret Key Export Error"); } QString ExportSecretKeyCommand::successCaption() const { return i18nc("@title:window", "Secret Key Export Finished"); } QString ExportSecretKeyCommand::crashExitMessage(const QStringList &args) const { return xi18nc("@info", "The GPG or GpgSM process that tried to export the secret key " "ended prematurely because of an unexpected error." "Please check the output of %1 for details.", args.join(QLatin1Char(' '))); } QString ExportSecretKeyCommand::errorExitMessage(const QStringList &args) const { return xi18nc("@info", "An error occurred while trying to export the secret key. " "The output from %1 was: %2", args[0], errorString()); } QString ExportSecretKeyCommand::successMessage(const QStringList &) const { return i18nc("@info", "Secret key successfully exported."); } diff --git a/src/dialogs/certificatedetailswidget.cpp b/src/dialogs/certificatedetailswidget.cpp index 0b66a46d8..44ba819cb 100644 --- a/src/dialogs/certificatedetailswidget.cpp +++ b/src/dialogs/certificatedetailswidget.cpp @@ -1,628 +1,628 @@ /* Copyright (c) 2016 Klarälvdalens Datakonsult AB 2017 Intevation GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "certificatedetailswidget.h" #include "ui_certificatedetailswidget.h" #include "kleopatra_debug.h" #include "exportdialog.h" #include "trustchainwidget.h" #include "subkeyswidget.h" #include "weboftrustdialog.h" #include "commands/changepassphrasecommand.h" #include "commands/changeexpirycommand.h" #include "commands/certifycertificatecommand.h" #include "commands/adduseridcommand.h" #include "commands/genrevokecommand.h" #include "commands/detailscommand.h" #include "commands/dumpcertificatecommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HIDE_ROW(row) \ ui.row->setVisible(false); \ ui.row##Lbl->setVisible(false); Q_DECLARE_METATYPE(GpgME::UserID) using namespace Kleo; class CertificateDetailsWidget::Private { public: Private(CertificateDetailsWidget *parent) : q(parent) {} void setupCommonProperties(); void setupPGPProperties(); void setupSMIMEProperties(); void revokeUID(const GpgME::UserID &uid); void genRevokeCert(); void certifyClicked(); void webOfTrustClicked(); void exportClicked(); 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); void setUpdatedKey(const GpgME::Key &key); void keyListDone(const GpgME::KeyListResult &, const std::vector &, const QString &, const GpgME::Error &); Ui::CertificateDetailsWidget ui; GpgME::Key key; bool updateInProgress; private: CertificateDetailsWidget *q; }; 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); ui.genRevokeBtn->setVisible(isOpenPGP && hasSecret); ui.certifyBtn->setVisible(isOpenPGP && !hasSecret); ui.changeExpirationBtn->setVisible(isOpenPGP && hasSecret); ui.addUserIDBtn->setVisible(hasSecret && isOpenPGP); ui.webOfTrustBtn->setVisible(isOpenPGP); ui.hboxLayout_1->addStretch(1); 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)); ui.fingerprint->setText(Formatting::prettyID(key.primaryFingerprint())); if (Kleo::Formatting::complianceMode().isEmpty()) { HIDE_ROW(compliance) } else { ui.complianceLbl->setText(Kleo::Formatting::complianceStringForKey(key)); } ui.userIDTable->clear(); QStringList headers = { i18n("Email"), i18n("Name"), i18n("Trust Level") }; if (canRevokeUID) { headers << QString(); } ui.userIDTable->setColumnCount(headers.count()); ui.userIDTable->setColumnWidth(0, 200); ui.userIDTable->setColumnWidth(1, 200); ui.userIDTable->setHeaderLabels(headers); const auto uids = key.userIDs(); for (unsigned int i = 0; i < uids.size(); ++i) { const auto &uid = uids[i]; auto item = new QTreeWidgetItem; const QString toolTip = tofuTooltipString(uid); item->setData(0, Qt::UserRole, QVariant::fromValue(uid)); auto pMail = Kleo::Formatting::prettyEMail(uid); auto pName = Kleo::Formatting::prettyName(uid); if (!isOpenPGP && pMail.isEmpty() && !pName.isEmpty()) { // 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; } } 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. pName = QString::fromLatin1(uid.id()); } item->setData(0, Qt::DisplayRole, pMail); item->setData(0, Qt::ToolTipRole, toolTip); item->setData(1, Qt::DisplayRole, pName); item->setData(1, Qt::ToolTipRole, toolTip); QIcon trustIcon; if (updateInProgress) { trustIcon = QIcon::fromTheme(QStringLiteral("emblem-question")); item->setData(2, Qt::DisplayRole, i18n("Updating...")); } else { 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)); } item->setData(2, Qt::DecorationRole, trustIcon); item->setData(2, Qt::ToolTipRole, toolTip); 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); } } } void CertificateDetailsWidget::Private::revokeUID(const GpgME::UserID &uid) { Q_UNUSED(uid); 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, - q, [this, cmd]() { + q, [this]() { ui.changePassphraseBtn->setEnabled(true); }); ui.changePassphraseBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::genRevokeCert() { auto cmd = new Kleo::Commands::GenRevokeCommand(key); QObject::connect(cmd, &Kleo::Commands::GenRevokeCommand::finished, - q, [this, cmd]() { + q, [this]() { ui.genRevokeBtn->setEnabled(true); }); ui.genRevokeBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::certifyClicked() { auto cmd = new Kleo::Commands::CertifyCertificateCommand(key); QObject::connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, - q, [this, cmd]() { + q, [this]() { ui.certifyBtn->setEnabled(true); }); ui.certifyBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::webOfTrustClicked() { QScopedPointer dlg(new WebOfTrustDialog(q)); dlg->setKey(key); dlg->exec(); } void CertificateDetailsWidget::Private::exportClicked() { QScopedPointer dlg(new ExportDialog(q)); dlg->setKey(key); dlg->exec(); } void CertificateDetailsWidget::Private::addUserID() { auto cmd = new Kleo::Commands::AddUserIDCommand(key); QObject::connect(cmd, &Kleo::Commands::AddUserIDCommand::finished, - q, [this, cmd]() { + q, [this]() { ui.addUserIDBtn->setEnabled(true); }); ui.addUserIDBtn->setEnabled(false); cmd->start(); } void CertificateDetailsWidget::Private::keysMayHaveChanged() { auto newKey = Kleo::KeyCache::instance()->findByFingerprint(key.primaryFingerprint()); if (!newKey.isNull()) { setUpdatedKey(newKey); } } void CertificateDetailsWidget::Private::showTrustChainDialog() { QScopedPointer 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(); QMenu *menu = new QMenu(q); menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-sign")), i18n("Certify ..."), q, [this, userID]() { auto cmd = new Kleo::Commands::CertifyCertificateCommand(userID); ui.userIDTable->setEnabled(false); connect(cmd, &Kleo::Commands::CertifyCertificateCommand::finished, q, [this]() { ui.userIDTable->setEnabled(true); }); cmd->start(); }); 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 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(""); const auto appendRow = [&html](const QString &lbl, const QString &val) { html += QStringLiteral("" "" "" "") .arg(lbl, val); }; const auto appendHeader = [this, &html](const QString &hdr) { html += QStringLiteral("") .arg(q->palette().highlight().color().name(), q->palette().highlightedText().color().name(), hdr); }; const auto dateTime = [](long ts) { return ts == 0 ? i18n("never") : QDateTime::fromTime_t(ts).toString(Qt::SystemLocaleShortDate); }; 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("
%1:%2
%3
"); // 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("").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); }); } static QString formatDNToolTip(const Kleo::DN &dn) { QString html = QStringLiteral(""); const auto appendRow = [&html, dn](const QString &lbl, const QString &attr) { const QString val = dn[attr]; if (!val.isEmpty()) { html += QStringLiteral( "" "" "").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("
%1:%2
"); return html; } 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(" of ", "%1 of %2", name, o); } ui.smimeOwner->setText(owner); ui.smimeOwner->setTextInteractionFlags(Qt::TextBrowserInteraction); const Kleo::DN issuerDN(key.issuerName()); const QString issuerCN = issuerDN[QStringLiteral("CN")]; const QString issuer = issuerCN.isEmpty() ? QString::fromUtf8(key.issuerName()) : issuerCN; ui.smimeIssuer->setText(QStringLiteral("%1").arg(issuer)); ui.smimeIssuer->setToolTip(formatDNToolTip(issuerDN)); ui.smimeOwner->setToolTip(formatDNToolTip(dn)); } void CertificateDetailsWidget::Private::smimeLinkActivated(const QString &link) { 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(); return; } qCWarning(KLEOPATRA_LOG) << "Unknown link activated:" << link; } 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(); }); connect(d->ui.genRevokeBtn, &QPushButton::clicked, this, [this]() { d->genRevokeCert(); }); 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(); }); connect(d->ui.certifyBtn, &QPushButton::clicked, this, [this]() { d->certifyClicked(); }); connect(d->ui.webOfTrustBtn, &QPushButton::clicked, this, [this]() { d->webOfTrustClicked(); }); connect(d->ui.exportBtn, &QPushButton::clicked, this, [this]() { d->exportClicked(); }); connect(Kleo::KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() { d->keysMayHaveChanged(); }); } CertificateDetailsWidget::~CertificateDetailsWidget() { } void CertificateDetailsWidget::Private::keyListDone(const GpgME::KeyListResult &, const std::vector &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); } void CertificateDetailsWidget::Private::setUpdatedKey(const GpgME::Key &k) { key = k; setupCommonProperties(); if (key.protocol() == GpgME::OpenPGP) { setupPGPProperties(); } else { setupSMIMEProperties(); } } 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); // Windows QGpgME new style connect problem makes this necessary. connect(job, SIGNAL(result(GpgME::KeyListResult,std::vector,QString,GpgME::Error)), this, SLOT(keyListDone(GpgME::KeyListResult,std::vector,QString,GpgME::Error))); job->start(QStringList() << QLatin1String(key.primaryFingerprint()), key.hasSecret()); } GpgME::Key CertificateDetailsWidget::key() const { return d->key; } CertificateDetailsDialog::CertificateDetailsDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("Certificate Details")); 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(); Q_ASSERT(w); w->setKey(key); } GpgME::Key CertificateDetailsDialog::key() const { auto w = findChild(); Q_ASSERT(w); return w->key(); } #include "moc_certificatedetailswidget.cpp" diff --git a/src/dialogs/certificateselectiondialog.cpp b/src/dialogs/certificateselectiondialog.cpp index 020310dbd..d875eea3e 100644 --- a/src/dialogs/certificateselectiondialog.cpp +++ b/src/dialogs/certificateselectiondialog.cpp @@ -1,397 +1,397 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/certificateselectiondialog.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2008 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "certificateselectiondialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Dialogs; using namespace Kleo::Commands; using namespace GpgME; class CertificateSelectionDialog::Private { friend class ::Kleo::Dialogs::CertificateSelectionDialog; CertificateSelectionDialog *const q; public: explicit Private(CertificateSelectionDialog *qq); private: void reload() { Command *const cmd = new ReloadKeysCommand(nullptr); cmd->setParentWidget(q); cmd->start(); } void create() { NewCertificateCommand *cmd = new NewCertificateCommand(nullptr); cmd->setParentWidget(q); if ((options & AnyFormat) != AnyFormat) { cmd->setProtocol((options & OpenPGPFormat) ? OpenPGP : CMS); } cmd->start(); } void lookup() { Command *const cmd = new LookupCertificatesCommand(nullptr); cmd->setParentWidget(q); cmd->start(); } void slotKeysMayHaveChanged(); void slotCurrentViewChanged(QAbstractItemView *newView); void slotSelectionChanged(); void slotDoubleClicked(const QModelIndex &idx); private: bool acceptable(const std::vector &keys) { return !keys.empty(); } void updateLabelText() { ui.label.setText(!customLabelText.isEmpty() ? customLabelText : (options & MultiSelection) ? i18n("Please select one or more of the following certificates:") : i18n("Please select one of the following certificates:")); } private: QPointer lastView; QString customLabelText; Options options; struct UI { QLabel label; SearchBar searchBar; TabWidget tabWidget; QDialogButtonBox buttonBox; QVBoxLayout vlay; explicit UI(CertificateSelectionDialog *q) : label(q), searchBar(q), tabWidget(q), buttonBox(q), vlay(q) { KDAB_SET_OBJECT_NAME(label); KDAB_SET_OBJECT_NAME(searchBar); KDAB_SET_OBJECT_NAME(tabWidget); KDAB_SET_OBJECT_NAME(buttonBox); KDAB_SET_OBJECT_NAME(vlay); vlay.addWidget(&label); vlay.addWidget(&searchBar); vlay.addWidget(&tabWidget, 1); vlay.addWidget(&buttonBox); QPushButton *const ok = buttonBox.addButton(QDialogButtonBox::Ok); ok->setEnabled(false); QPushButton *const cancel = buttonBox.addButton(QDialogButtonBox::Close); Q_UNUSED(cancel); QPushButton *const reload = buttonBox.addButton(i18n("Reload"), QDialogButtonBox::ActionRole); QPushButton *const import = buttonBox.addButton(i18n("Import..."), QDialogButtonBox::ActionRole); QPushButton *const lookup = buttonBox.addButton(i18n("Lookup..."), QDialogButtonBox::ActionRole); QPushButton *const create = buttonBox.addButton(i18n("New..."), QDialogButtonBox::ActionRole); import->setToolTip(i18nc("@info:tooltip", "Import certificate from file")); lookup->setToolTip(i18nc("@info:tooltip", "Lookup certificates on server")); reload->setToolTip(i18nc("@info:tooltip", "Refresh certificate list")); create->setToolTip(i18nc("@info:tooltip", "Create a new certificate")); connect(&buttonBox, &QDialogButtonBox::accepted, q, &CertificateSelectionDialog::accept); connect(&buttonBox, &QDialogButtonBox::rejected, q, &CertificateSelectionDialog::reject); connect(reload, SIGNAL(clicked()), q, SLOT(reload())); connect(lookup, SIGNAL(clicked()), q, SLOT(lookup())); connect(create, SIGNAL(clicked()), q, SLOT(create())); connect(KeyCache::instance().get(), SIGNAL(keysMayHaveChanged()), q, SLOT(slotKeysMayHaveChanged())); connect(import, &QPushButton::clicked, q, [import, q] () { import->setEnabled(false); auto cmd = new Kleo::ImportCertificateFromFileCommand(); connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, - q, [import, q]() { + q, [import]() { import->setEnabled(true); }); cmd->setParentWidget(q); cmd->start(); }); } } ui; }; CertificateSelectionDialog::Private::Private(CertificateSelectionDialog *qq) : q(qq), ui(q) { ui.tabWidget.setFlatModel(AbstractKeyListModel::createFlatKeyListModel()); ui.tabWidget.setHierarchicalModel(AbstractKeyListModel::createHierarchicalKeyListModel()); ui.tabWidget.connectSearchBar(&ui.searchBar); connect(&ui.tabWidget, SIGNAL(currentViewChanged(QAbstractItemView*)), q, SLOT(slotCurrentViewChanged(QAbstractItemView*))); updateLabelText(); q->setWindowTitle(i18n("Certificate Selection")); } CertificateSelectionDialog::CertificateSelectionDialog(QWidget *parent) : QDialog(parent), d(new Private(this)) { const KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kleopatracertificateselectiondialogrc")); d->ui.tabWidget.loadViews(config.data()); const KConfigGroup geometry(config, "Geometry"); resize(geometry.readEntry("size", size())); d->slotKeysMayHaveChanged(); } CertificateSelectionDialog::~CertificateSelectionDialog() {} void CertificateSelectionDialog::setCustomLabelText(const QString &txt) { if (txt == d->customLabelText) { return; } d->customLabelText = txt; d->updateLabelText(); } QString CertificateSelectionDialog::customLabelText() const { return d->customLabelText; } void CertificateSelectionDialog::setOptions(Options options) { if (d->options == options) { return; } d->options = options; d->ui.tabWidget.setMultiSelection(options & MultiSelection); d->slotKeysMayHaveChanged(); } CertificateSelectionDialog::Options CertificateSelectionDialog::options() const { return d->options; } void CertificateSelectionDialog::setStringFilter(const QString &filter) { d->ui.tabWidget.setStringFilter(filter); } void CertificateSelectionDialog::setKeyFilter(const std::shared_ptr &filter) { d->ui.tabWidget.setKeyFilter(filter); } void CertificateSelectionDialog::selectCertificates(const std::vector &keys) { const QAbstractItemView *const view = d->ui.tabWidget.currentView(); if (!view) { return; } const auto *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); Q_FOREACH (const QModelIndex &idx, model->indexes(keys)) if (idx.isValid()) { sm->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); } } void CertificateSelectionDialog::selectCertificate(const Key &key) { selectCertificates(std::vector(1, key)); } std::vector CertificateSelectionDialog::selectedCertificates() const { const QAbstractItemView *const view = d->ui.tabWidget.currentView(); if (!view) { return std::vector(); } const auto *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); const QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); return model->keys(sm->selectedRows()); } Key CertificateSelectionDialog::selectedCertificate() const { const std::vector keys = selectedCertificates(); return keys.empty() ? Key() : keys.front(); } void CertificateSelectionDialog::hideEvent(QHideEvent *e) { KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kleopatracertificateselectiondialogrc")); d->ui.tabWidget.saveViews(config.data()); KConfigGroup geometry(config, "Geometry"); geometry.writeEntry("size", size()); QDialog::hideEvent(e); } void CertificateSelectionDialog::Private::slotKeysMayHaveChanged() { q->setEnabled(true); std::vector keys = (options & SecretKeys) ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys(); q->filterAllowedKeys(keys, options); const std::vector selected = q->selectedCertificates(); if (AbstractKeyListModel *const model = ui.tabWidget.flatModel()) { model->setKeys(keys); } if (AbstractKeyListModel *const model = ui.tabWidget.hierarchicalModel()) { model->setKeys(keys); } q->selectCertificates(selected); } void CertificateSelectionDialog::filterAllowedKeys(std::vector &keys, int options) { std::vector::iterator end = keys.end(); switch (options & AnyFormat) { case OpenPGPFormat: end = std::remove_if(keys.begin(), end, [](const Key &key) { return key.protocol() != OpenPGP; }); break; case CMSFormat: end = std::remove_if(keys.begin(), end, [](const Key &key) { return key.protocol() != CMS; }); break; default: case AnyFormat: ; } switch (options & AnyCertificate) { case SignOnly: end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.canReallySign(); }); break; case EncryptOnly: end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.canEncrypt(); }); break; default: case AnyCertificate: ; } if (options & SecretKeys) { end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.hasSecret(); }); } keys.erase(end, keys.end()); } void CertificateSelectionDialog::Private::slotCurrentViewChanged(QAbstractItemView *newView) { if (lastView) { disconnect(lastView, SIGNAL(doubleClicked(QModelIndex)), q, SLOT(slotDoubleClicked(QModelIndex))); Q_ASSERT(lastView->selectionModel()); disconnect(lastView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(slotSelectionChanged())); } lastView = newView; if (newView) { connect(newView, SIGNAL(doubleClicked(QModelIndex)), q, SLOT(slotDoubleClicked(QModelIndex))); Q_ASSERT(newView->selectionModel()); connect(newView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(slotSelectionChanged())); } slotSelectionChanged(); } void CertificateSelectionDialog::Private::slotSelectionChanged() { if (QPushButton *const pb = ui.buttonBox.button(QDialogButtonBox::Ok)) { pb->setEnabled(acceptable(q->selectedCertificates())); } } void CertificateSelectionDialog::Private::slotDoubleClicked(const QModelIndex &idx) { QAbstractItemView *const view = ui.tabWidget.currentView(); Q_ASSERT(view); const auto *const model = ui.tabWidget.currentModel(); Q_ASSERT(model); Q_UNUSED(model); QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); sm->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); QMetaObject::invokeMethod(q, [this]() {q->accept();}, Qt::QueuedConnection); } void CertificateSelectionDialog::accept() { if (d->acceptable(selectedCertificates())) { QDialog::accept(); } } #include "moc_certificateselectiondialog.cpp" diff --git a/src/dialogs/updatenotification.cpp b/src/dialogs/updatenotification.cpp index 9c30e4c60..e9b2e699e 100644 --- a/src/dialogs/updatenotification.cpp +++ b/src/dialogs/updatenotification.cpp @@ -1,244 +1,244 @@ /* dialogs/updatenotification.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "updatenotification.h" #include "utils/gnupg-helper.h" #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; namespace { static void gpgconf_set_update_check(bool value) { auto conf = QGpgME::cryptoConfig(); auto entry = conf->entry(QStringLiteral("dirmngr"), QStringLiteral("Enforcement"), QStringLiteral("allow-version-check")); if (!entry) { qCDebug(KLEOPATRA_LOG) << "allow-version-check entry not found"; return; } if (entry->boolValue() != value) { entry->setBoolValue(value); conf->sync(true); } } } // namespace void UpdateNotification::forceUpdateCheck(QWidget *parent) { auto proc = new QProcess; proc->setProgram(gnupgInstallPath() + QStringLiteral("/gpg-connect-agent.exe")); proc->setArguments(QStringList() << QStringLiteral("--dirmngr") << QStringLiteral("loadswdb --force") << QStringLiteral("/bye")); auto progress = new QProgressDialog(i18n("Searching for updates..."), i18n("Cancel"), 0, 0, parent); progress->setMinimumDuration(0); progress->show(); - connect(progress, &QProgressDialog::canceled, [progress, proc] () { + connect(progress, &QProgressDialog::canceled, [ proc] () { proc->kill(); qCDebug(KLEOPATRA_LOG) << "Update force canceled. Output:" << QString::fromLocal8Bit(proc->readAllStandardOutput()) << "stderr:" << QString::fromLocal8Bit(proc->readAllStandardError()); }); connect(proc, static_cast(&QProcess::finished), [parent, progress, proc](int exitCode, QProcess::ExitStatus exitStatus) { qCDebug(KLEOPATRA_LOG) << "Update force exited with status:" << exitStatus << "code:" << exitCode; delete progress; proc->deleteLater(); UpdateNotification::checkUpdate(parent, exitStatus == QProcess::NormalExit); }); qCDebug(KLEOPATRA_LOG) << "Starting:" << proc->program() << "args" << proc->arguments(); proc->start(); } void UpdateNotification::checkUpdate(QWidget *parent, bool force) { #ifdef Q_OS_WIN KConfigGroup updatecfg(KSharedConfig::openConfig(), "UpdateNotification"); if (updatecfg.readEntry("NeverShow", false) && !force) { return; } // Gpg defaults to no update check. For Gpg4win we want this // enabled if the user does not explicitly disable update // checks neverShow would be true in that case or // we would have set AllowVersionCheck once and the user // explicitly removed that. if (force || updatecfg.readEntry("AllowVersionCheckSetOnce", false)) { gpgconf_set_update_check (true); updatecfg.writeEntry("AllowVersionCheckSetOnce", true); } const auto current = gpg4winVersion(); GpgME::Error err; const auto lastshown = updatecfg.readEntry("LastShown", QDateTime()); if (!force && lastshown.isValid() && lastshown.addSecs(20 * 60 * 60) > QDateTime::currentDateTime()) { qDebug() << QDateTime::currentDateTime().addSecs(20 * 60 * 60); return; } const auto results = GpgME::SwdbResult::query("gpg4win", current.toUtf8().constData(), &err); if (err) { qCDebug(KLEOPATRA_LOG) << "update check failed: " << err.asString(); return; } if (results.size() != 1) { /* Should not happen */ qCDebug(KLEOPATRA_LOG) << "more then one result"; return; } const auto result = results[0]; if (result.update()) { const QString newVersion = QStringLiteral("%1.%2.%3").arg(result.version().major) .arg(result.version().minor) .arg(result.version().patch); qCDebug(KLEOPATRA_LOG) << "Have update to version:" << newVersion; UpdateNotification notifier(parent, newVersion); notifier.exec(); updatecfg.writeEntry("LastShown", QDateTime::currentDateTime()); updatecfg.sync(); } else { qCDebug(KLEOPATRA_LOG) << "No update for:" << current; if (force) { KMessageBox::information(parent, i18nc("@info", "No update found in the available version database."), i18nc("@title", "Up to date")); } } #else Q_UNUSED(parent); Q_UNUSED(force); #endif } UpdateNotification::UpdateNotification(QWidget *parent, const QString &version) : QDialog(parent) { resize(400, 200); auto lay = new QGridLayout(this); auto logo = new QLabel; logo->setMaximumWidth(110); setAttribute(Qt::WA_QuitOnClose, false); KIconLoader *const il = KIconLoader::global(); const QString iconPath = il->iconPath(QStringLiteral("gpg4win"), KIconLoader::User); logo->setPixmap(QIcon(iconPath).pixmap(100, 100)); auto label = new QLabel; const QString boldVersion = QStringLiteral("%1").arg(version); label->setText (i18nc("%1 is the version number", "Version %1 is available.", boldVersion) + QStringLiteral("

") + i18nc("Link to NEWS style changelog", "See the new features.")); label->setOpenExternalLinks(true); label->setTextInteractionFlags(Qt::TextBrowserInteraction); label->setWordWrap(true); setWindowTitle(i18n("Update available!")); setWindowIcon(QIcon(QLatin1String("gpg4win"))); lay->addWidget(logo, 0, 0); lay->addWidget(label, 0, 1); const auto chk = new QCheckBox (i18n("Show this notification for future updates.")); lay->addWidget(chk, 1, 0, 1, -1); KConfigGroup updatecfg(KSharedConfig::openConfig(), "UpdateNotification"); chk->setChecked(!updatecfg.readEntry("NeverShow", false)); const auto bb = new QDialogButtonBox(); const auto b = bb->addButton(i18n("&Get update"), QDialogButtonBox::AcceptRole); b->setDefault(true); b->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); bb->addButton(QDialogButtonBox::Cancel); lay->addWidget(bb, 2, 0, 1, -1); connect (bb, &QDialogButtonBox::accepted, this, [this, chk]() { QDesktopServices::openUrl(QUrl(QStringLiteral("https://www.gpg4win.org/download.html"))); KConfigGroup updatecfg(KSharedConfig::openConfig(), "UpdateNotification"); updatecfg.writeEntry("NeverShow", !chk->isChecked()); gpgconf_set_update_check (chk->isChecked()); QDialog::accept(); }); connect (bb, &QDialogButtonBox::rejected, this, [this, chk]() { KConfigGroup updatecfg(KSharedConfig::openConfig(), "UpdateNotification"); updatecfg.writeEntry("NeverShow", !chk->isChecked()); gpgconf_set_update_check (chk->isChecked()); QDialog::reject(); }); } diff --git a/src/view/searchbar.cpp b/src/view/searchbar.cpp index a48cb80ee..5aeea9a8f 100644 --- a/src/view/searchbar.cpp +++ b/src/view/searchbar.cpp @@ -1,208 +1,208 @@ /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; -*- view/searchbar.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2007 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "searchbar.h" #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; class SearchBar::Private { friend class ::Kleo::SearchBar; SearchBar *const q; public: explicit Private(SearchBar *qq); ~Private(); private: void slotKeyFilterChanged(int idx) { Q_EMIT q->keyFilterChanged(keyFilter(idx)); } std::shared_ptr keyFilter(int idx) const { const QModelIndex mi = KeyFilterManager::instance()->model()->index(idx, 0); return KeyFilterManager::instance()->fromModelIndex(mi); } std::shared_ptr currentKeyFilter() const { return keyFilter(combo->currentIndex()); } QString currentKeyFilterID() const { if (const std::shared_ptr f = currentKeyFilter()) { return f->id(); } else { return QString(); } } void listNotCertifiedKeys() const { lineEdit->clear(); combo->setCurrentIndex(combo->findData(QStringLiteral("not-validated-certificates"))); Q_EMIT q->keyFilterChanged(keyFilter(combo->currentIndex())); } /* List all OpenPGP keys and see if we find one with a UID that is * not at least fully valid. If we find one, show the certify * button. */ /* XXX: It would be nice to do this every time the user certifies * a key. */ void showOrHideCertifyButton() const { QGpgME::KeyListJob *job = QGpgME::openpgp()->keyListJob(); connect(job, &QGpgME::KeyListJob::result, job, - [this, job](const GpgME::KeyListResult&, const std::vector &keys, const QString&, const GpgME::Error&) + [this](const GpgME::KeyListResult&, const std::vector &keys, const QString&, const GpgME::Error&) { for (const auto &key: keys) { if (Kleo::keyValidity(key) < GpgME::UserID::Validity::Full) { certifyButton->show(); return; } } certifyButton->hide(); }); job->start(QStringList()); } private: QLineEdit *lineEdit; QComboBox *combo; QPushButton *certifyButton; }; SearchBar::Private::Private(SearchBar *qq) : q(qq) { QHBoxLayout *layout = new QHBoxLayout(q); layout->setContentsMargins(0, 0, 0, 0); lineEdit = new QLineEdit(q); lineEdit->setClearButtonEnabled(true); lineEdit->setPlaceholderText(i18n("Search...")); layout->addWidget(lineEdit, /*stretch=*/1); combo = new QComboBox(q); layout->addWidget(combo); certifyButton = new QPushButton(q); certifyButton->setIcon(QIcon::fromTheme(QStringLiteral("security-medium"))); certifyButton->setToolTip(i18n("Some certificates are not yet certified. " "Click here to see a list of these certificates." "

" "Certification is required to make sure that the certificates " "actually belong to the identity they claim to belong to.")); certifyButton->hide(); layout->addWidget(certifyButton); showOrHideCertifyButton(); combo->setModel(KeyFilterManager::instance()->model()); KDAB_SET_OBJECT_NAME(layout); KDAB_SET_OBJECT_NAME(lineEdit); KDAB_SET_OBJECT_NAME(combo); KDAB_SET_OBJECT_NAME(certifyButton); connect(lineEdit, &QLineEdit::textChanged, q, &SearchBar::stringFilterChanged); connect(combo, SIGNAL(currentIndexChanged(int)), q, SLOT(slotKeyFilterChanged(int))); connect(certifyButton, SIGNAL(clicked()), q, SLOT(listNotCertifiedKeys())); } SearchBar::Private::~Private() {} SearchBar::SearchBar(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), d(new Private(this)) { } SearchBar::~SearchBar() {} void SearchBar::updateClickMessage(const QString &shortcutStr) { d->lineEdit->setPlaceholderText(i18n("Search...<%1>", shortcutStr)); } // slot void SearchBar::setStringFilter(const QString &filter) { d->lineEdit->setText(filter); } // slot void SearchBar::setKeyFilter(const std::shared_ptr &kf) { const QModelIndex idx = KeyFilterManager::instance()->toModelIndex(kf); if (idx.isValid()) { d->combo->setCurrentIndex(idx.row()); } else { d->combo->setCurrentIndex(0); } } // slot void SearchBar::setChangeStringFilterEnabled(bool on) { d->lineEdit->setEnabled(on); } // slot void SearchBar::setChangeKeyFilterEnabled(bool on) { d->combo->setEnabled(on); } QLineEdit *SearchBar::lineEdit() const { return d->lineEdit; } #include "moc_searchbar.cpp" diff --git a/tests/test_verify.cpp b/tests/test_verify.cpp index 8c557c283..b9ad9dca4 100644 --- a/tests/test_verify.cpp +++ b/tests/test_verify.cpp @@ -1,233 +1,233 @@ /* This file is part of Kleopatra's test suite. Copyright (c) 2007 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "kleo_test.h" #include #include #include #include #include #include #include #include #include #include #include // Replace this with a gpgme version check once GnuPG Bug #2092 // ( https://bugs.gnupg.org/gnupg/issue2092 ) is fixed. #define GPGME_MULTITHREADED_KEYLIST_BROKEN Q_DECLARE_METATYPE(GpgME::VerificationResult) class VerifyTest : public QObject { Q_OBJECT private: // Data shared with all tests QByteArray mSignature; QByteArray mSignedData; const QGpgME::Protocol *mBackend; QEventLoop mEventLoop; // Data for testParallelVerifyAndKeyListJobs() QList mParallelVerifyJobs; QList mParallelKeyListJobs; // Data for testMixedParallelJobs() QList mRunningJobs; int mJobsStarted; public Q_SLOTS: void slotParallelKeyListJobFinished() { mParallelKeyListJobs.removeAll(static_cast(sender())); // When all jobs are done, quit the event loop if (mParallelVerifyJobs.isEmpty() && mParallelKeyListJobs.isEmpty()) { mEventLoop.quit(); } } void slotParallelVerifyJobFinished(const GpgME::VerificationResult &result) { // Verify the result of the job is correct QVERIFY(mParallelVerifyJobs.contains(static_cast(sender()))); QCOMPARE(result.signature(0).validity(), GpgME::Signature::Full); mParallelVerifyJobs.removeAll(static_cast(sender())); // Start a key list job QGpgME::KeyListJob *job = mBackend->keyListJob(); mParallelKeyListJobs.append(job); connect(job, &QGpgME::Job::done, this, &VerifyTest::slotParallelKeyListJobFinished); QVERIFY(!job->start(QStringList())); } void someJobDone() { // Don't bother checking any results here mRunningJobs.removeAll(static_cast(sender())); } void startAnotherJob() { static int counter = 0; counter++; // Randomly kill a running job if (counter % 10 == 0 && !mRunningJobs.isEmpty()) { mRunningJobs.at(counter % mRunningJobs.size())->slotCancel(); } // Randomly either start a keylist or a verify job QGpgME::Job *job; if (counter % 2 == 0) { QGpgME::VerifyDetachedJob *vdj = mBackend->verifyDetachedJob(); QVERIFY(!vdj->start(mSignature, mSignedData)); job = vdj; } else { QGpgME::KeyListJob *klj = mBackend->keyListJob(); QVERIFY(!klj->start(QStringList())); job = klj; } mRunningJobs.append(job); connect(job, &QGpgME::Job::done, this, &VerifyTest::someJobDone); // Quit after 2500 jobs, that should be enough mJobsStarted++; if (mJobsStarted >= 2500) { QTimer::singleShot(1000, &mEventLoop, &QEventLoop::quit); } else { QTimer::singleShot(0, this, &VerifyTest::startAnotherJob); } } private Q_SLOTS: void initTestCase() { qRegisterMetaType(); const QString sigFileName = QLatin1String(KLEO_TEST_DATADIR) + QLatin1String("/test.data.sig"); const QString dataFileName = QLatin1String(KLEO_TEST_DATADIR) + QLatin1String("/test.data"); QFile sigFile(sigFileName); QVERIFY(sigFile.open(QFile::ReadOnly)); QFile dataFile(dataFileName); QVERIFY(dataFile.open(QFile::ReadOnly)); mSignature = sigFile.readAll(); mSignedData = dataFile.readAll(); mBackend = QGpgME::openpgp(); } void testVerify() { QGpgME::VerifyDetachedJob *job = mBackend->verifyDetachedJob(); - QSignalSpy spy(job, SIGNAL(result(GpgME::VerificationResult))); + QSignalSpy spy(job, &QGpgME::VerifyDetachedJob::result); QVERIFY(spy.isValid()); GpgME::Error err = job->start(mSignature, mSignedData); QVERIFY(!err); QTest::qWait(1000); // ### we need to enter the event loop, can be done nicer though QCOMPARE(spy.count(), 1); GpgME::VerificationResult result = spy.takeFirst().at(0).value(); QCOMPARE(result.numSignatures(), 1U); GpgME::Signature sig = result.signature(0); QCOMPARE(sig.summary() & GpgME::Signature::KeyMissing, 0); QCOMPARE((quint64) sig.creationTime(), Q_UINT64_C(1530524124)); QCOMPARE(sig.validity(), GpgME::Signature::Full); } /* Test that the decrypt verify job also works with signed only, not * encrypted PGP messages */ void testDecryptVerifyOpaqueSigned() { const QString sigFileName = QLatin1String(KLEO_TEST_DATADIR) + QLatin1String("/test.data.signed-opaque.asc"); std::pair result; QByteArray plaintext; QFile sigFile(sigFileName); QVERIFY(sigFile.open(QFile::ReadOnly)); const QByteArray ciphertext = sigFile.readAll(); QGpgME::DecryptVerifyJob *job = mBackend->decryptVerifyJob(); result = job->exec(ciphertext, plaintext); QVERIFY(result.first.error().code()); QVERIFY(result.second.numSignatures()); GpgME::Signature sig = result.second.signature(0); QVERIFY(sig.validity() == GpgME::Signature::Validity::Full); QVERIFY(!sig.status().code()); QVERIFY(QString::fromUtf8(plaintext).startsWith( QLatin1String("/* -*- mode: c++; c-basic-offset:4 -*-"))); } #ifndef GPGME_MULTITHREADED_KEYLIST_BROKEN // The following two tests are disabled because they trigger an // upstream bug in gpgme. See: https://bugs.gnupg.org/gnupg/issue2092 // Which has a testcase attached that does similar things using gpgme // directly and triggers various problems. void testParallelVerifyAndKeyListJobs() { // ### Increasing 10 to 500 makes the verify jobs fail! // ^ This should also be reevaluated if the underlying bug in gpgme // is fixed. for (int i = 0; i < 10; ++i) { QGpgME::VerifyDetachedJob *job = mBackend->verifyDetachedJob(); mParallelVerifyJobs.append(job); QVERIFY(!job->start(mSignature, mSignedData)); connect(job, SIGNAL(result(GpgME::VerificationResult)), this, SLOT(slotParallelVerifyJobFinished(GpgME::VerificationResult))); } mEventLoop.exec(); } void testMixedParallelJobs() { mJobsStarted = 0; QTimer::singleShot(0, this, SLOT(startAnotherJob())); mEventLoop.exec(); } #endif }; QTEST_KLEOMAIN(VerifyTest) #include "test_verify.moc"