diff --git a/src/commands/lookupcertificatescommand.cpp b/src/commands/lookupcertificatescommand.cpp index a550ff1ce..de4e41837 100644 --- a/src/commands/lookupcertificatescommand.cpp +++ b/src/commands/lookupcertificatescommand.cpp @@ -1,616 +1,612 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/lookupcertificatescommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008, 2009 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "lookupcertificatescommand.h" #include "importcertificatescommand_p.h" #include "detailscommand.h" #include #include "view/tabwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Dialogs; using namespace GpgME; using namespace QGpgME; class LookupCertificatesCommand::Private : public ImportCertificatesCommand::Private { friend class ::Kleo::Commands::LookupCertificatesCommand; LookupCertificatesCommand *q_func() const { return static_cast(q); } public: explicit Private(LookupCertificatesCommand *qq, KeyListController *c); ~Private() override; void init(); private: void slotSearchTextChanged(const QString &str); void slotNextKey(const Key &key); void slotKeyListResult(const KeyListResult &result); void slotWKDLookupResult(const WKDLookupResult &result); void tryToFinishKeyLookup(); void slotImportRequested(const std::vector &keys); void slotDetailsRequested(const Key &key); void slotSaveAsRequested(const std::vector &keys); void slotDialogRejected() { canceled(); } private: using ImportCertificatesCommand::Private::showError; void showError(QWidget *parent, const KeyListResult &result); void showResult(QWidget *parent, const KeyListResult &result); void createDialog(); KeyListJob *createKeyListJob(GpgME::Protocol proto) const { const auto cbp = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); return cbp ? cbp->keyListJob(true) : nullptr; } WKDLookupJob *createWKDLookupJob() const { const auto cbp = QGpgME::openpgp(); return cbp ? cbp->wkdLookupJob() : nullptr; } ImportFromKeyserverJob *createImportJob(GpgME::Protocol proto) const { const auto cbp = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); return cbp ? cbp->importFromKeyserverJob() : nullptr; } void startKeyListJob(GpgME::Protocol proto, const QString &str); void startWKDLookupJob(const QString &str); bool checkConfig() const; QWidget *dialogOrParentWidgetOrView() const { if (dialog) { return dialog; } else { return parentWidgetOrView(); } } private: GpgME::Protocol protocol = GpgME::UnknownProtocol; QString query; bool autoStartLookup = false; QPointer dialog; QPointer progress; struct KeyListingVariables { QPointer cms, openpgp; QPointer wkdJob; QString pattern; KeyListResult result; std::vector keys; int numKeysWithoutUserId = 0; std::set wkdKeyFingerprints; QByteArray wkdKeyData; QString wkdSource; bool cmsKeysHaveNoFingerprints = false; bool openPgpKeysHaveNoFingerprints = false; void reset() { *this = KeyListingVariables(); } } keyListing; }; LookupCertificatesCommand::Private *LookupCertificatesCommand::d_func() { return static_cast(d.get()); } const LookupCertificatesCommand::Private *LookupCertificatesCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() LookupCertificatesCommand::Private::Private(LookupCertificatesCommand *qq, KeyListController *c) : ImportCertificatesCommand::Private(qq, c) , dialog() { if (!Settings{}.cmsEnabled()) { protocol = GpgME::OpenPGP; } } LookupCertificatesCommand::Private::~Private() { qCDebug(KLEOPATRA_LOG); delete dialog; } LookupCertificatesCommand::LookupCertificatesCommand(KeyListController *c) : ImportCertificatesCommand(new Private(this, c)) { d->init(); } LookupCertificatesCommand::LookupCertificatesCommand(const QString &query, KeyListController *c) : ImportCertificatesCommand(new Private(this, c)) { d->init(); d->query = query; d->autoStartLookup = true; } LookupCertificatesCommand::LookupCertificatesCommand(QAbstractItemView *v, KeyListController *c) : ImportCertificatesCommand(v, new Private(this, c)) { d->init(); if (c->tabWidget()) { d->query = c->tabWidget()->stringFilter(); // do not start the lookup automatically to prevent unwanted leaking // of information } } void LookupCertificatesCommand::Private::init() { } LookupCertificatesCommand::~LookupCertificatesCommand() { qCDebug(KLEOPATRA_LOG); } void LookupCertificatesCommand::setProtocol(GpgME::Protocol protocol) { d->protocol = protocol; } GpgME::Protocol LookupCertificatesCommand::protocol() const { return d->protocol; } void LookupCertificatesCommand::doStart() { if (!d->checkConfig()) { d->finished(); return; } d->createDialog(); Q_ASSERT(d->dialog); // if we have a prespecified query, load it into find field // and start the search, if auto-start is enabled if (!d->query.isEmpty()) { d->dialog->setSearchText(d->query); if (d->autoStartLookup) { d->slotSearchTextChanged(d->query); } } else { d->dialog->setPassive(false); } d->dialog->show(); } void LookupCertificatesCommand::Private::createDialog() { if (dialog) { return; } dialog = new LookupCertificatesDialog; applyWindowID(dialog); dialog->setAttribute(Qt::WA_DeleteOnClose); const bool wkdOnly = !haveKeyserverConfigured() && !haveX509DirectoryServerConfigured(); dialog->setQueryMode(wkdOnly ? LookupCertificatesDialog::EmailQuery : LookupCertificatesDialog::AnyQuery); connect(dialog, &LookupCertificatesDialog::searchTextChanged, q, [this](const QString &text) { slotSearchTextChanged(text); }); using CertsVec = std::vector; connect(dialog, &LookupCertificatesDialog::saveAsRequested, q, [this](const CertsVec &certs) { slotSaveAsRequested(certs); }); connect(dialog, &LookupCertificatesDialog::importRequested, q, [this](const CertsVec &certs) { slotImportRequested(certs); }); connect(dialog, &LookupCertificatesDialog::detailsRequested, q, [this](const GpgME::Key &gpgKey) { slotDetailsRequested(gpgKey); }); connect(dialog, &QDialog::rejected, q, [this]() { slotDialogRejected(); }); } static auto searchTextToEmailAddress(const QString &s) { return QString::fromStdString(UserID::addrSpecFromString(s.toStdString().c_str())); } void LookupCertificatesCommand::Private::slotSearchTextChanged(const QString &str) { // pressing return might trigger both search and dialog destruction (search focused and default key set) // On Windows, the dialog is then destroyed before this slot is called if (dialog) { // thus test dialog->setPassive(true); dialog->setCertificates(std::vector()); - dialog->showInformation({}); } keyListing.reset(); keyListing.pattern = str; if (protocol != GpgME::OpenPGP) { startKeyListJob(CMS, str); } if (protocol != GpgME::CMS) { static const QRegularExpression rx(QRegularExpression::anchoredPattern(QLatin1StringView("[0-9a-fA-F]{6,}"))); if (rx.match(str).hasMatch()) { qCDebug(KLEOPATRA_LOG) << "Adding 0x prefix to query" << str; startKeyListJob(OpenPGP, QLatin1StringView{"0x"} + str); } else { startKeyListJob(OpenPGP, str); } if (str.contains(QLatin1Char{'@'}) && !searchTextToEmailAddress(str).isEmpty()) { startWKDLookupJob(str); } } const auto jobCount = int(!keyListing.cms.isNull()) + int(!keyListing.openpgp.isNull()) + int(!keyListing.wkdJob.isNull()); if (jobCount > 0) { progress = new QProgressDialog{dialog}; progress->setAttribute(Qt::WA_DeleteOnClose); progress->setLabelText(i18nc("@info", "Searching for matching certificates ...")); progress->setMaximum(jobCount); progress->setMinimumDuration(0); progress->setValue(0); } } void LookupCertificatesCommand::Private::startKeyListJob(GpgME::Protocol proto, const QString &str) { if ((proto == GpgME::OpenPGP) && !haveKeyserverConfigured()) { // avoid starting an OpenPGP key server lookup if key server usage has been disabled; // for S/MIME we start the job regardless of configured directory servers to account for // dirmngr knowing better than our check for directory servers return; } KeyListJob *const klj = createKeyListJob(proto); if (!klj) { return; } connect(klj, &QGpgME::KeyListJob::result, q, [this](const GpgME::KeyListResult &result) { slotKeyListResult(result); }); connect(klj, &QGpgME::KeyListJob::nextKey, q, [this](const GpgME::Key &key) { slotNextKey(key); }); if (const Error err = klj->start(QStringList(str))) { keyListing.result.mergeWith(KeyListResult(err)); } else if (proto == CMS) { keyListing.cms = klj; } else { keyListing.openpgp = klj; } } void LookupCertificatesCommand::Private::startWKDLookupJob(const QString &str) { const auto job = createWKDLookupJob(); if (!job) { qCDebug(KLEOPATRA_LOG) << "Failed to create WKDLookupJob"; return; } connect(job, &WKDLookupJob::result, q, [this](const WKDLookupResult &result) { slotWKDLookupResult(result); }); if (const Error err = job->start(str)) { keyListing.result.mergeWith(KeyListResult{err}); } else { keyListing.wkdJob = job; } } void LookupCertificatesCommand::Private::slotNextKey(const Key &key) { if (!key.primaryFingerprint()) { qCDebug(KLEOPATRA_LOG) << __func__ << "ignoring key without fingerprint" << key; if (q->sender() == keyListing.cms) { keyListing.cmsKeysHaveNoFingerprints = true; } else if (q->sender() == keyListing.openpgp) { keyListing.openPgpKeysHaveNoFingerprints = true; } } else if (key.numUserIDs() == 0) { qCDebug(KLEOPATRA_LOG) << __func__ << "ignoring key without user IDs" << key; keyListing.numKeysWithoutUserId++; } else { qCDebug(KLEOPATRA_LOG) << __func__ << "got key" << key; keyListing.keys.push_back(key); } } void LookupCertificatesCommand::Private::slotKeyListResult(const KeyListResult &r) { if (q->sender() == keyListing.cms) { keyListing.cms = nullptr; } else if (q->sender() == keyListing.openpgp) { keyListing.openpgp = nullptr; } else { qCDebug(KLEOPATRA_LOG) << "unknown sender()" << q->sender(); } keyListing.result.mergeWith(r); tryToFinishKeyLookup(); } static auto removeKeysNotMatchingEmail(const std::vector &keys, const std::string &email) { std::vector filteredKeys; const auto addrSpec = UserID::addrSpecFromString(email.c_str()); std::copy_if(std::begin(keys), std::end(keys), std::back_inserter(filteredKeys), [addrSpec](const auto &key) { const auto uids = key.userIDs(); return std::any_of(std::begin(uids), std::end(uids), [addrSpec](const auto &uid) { return uid.addrSpec() == addrSpec; }); }); return filteredKeys; } void LookupCertificatesCommand::Private::slotWKDLookupResult(const WKDLookupResult &result) { if (q->sender() == keyListing.wkdJob) { keyListing.wkdJob = nullptr; } else { qCDebug(KLEOPATRA_LOG) << __func__ << "unknown sender()" << q->sender(); } // we do not want to bother the user with errors during the WKD lookup; // therefore, we log the result, but we do not merge it into keyListing.result qCDebug(KLEOPATRA_LOG) << "Result of WKD lookup:" << result.error(); const auto keys = removeKeysNotMatchingEmail(result.keyData().toKeys(GpgME::OpenPGP), result.pattern()); if (!keys.empty()) { keyListing.wkdKeyData = QByteArray::fromStdString(result.keyData().toString()); keyListing.wkdSource = QString::fromStdString(result.source()); std::copy(std::begin(keys), std::end(keys), std::back_inserter(keyListing.keys)); // remember the keys retrieved via WKD for import std::transform(std::begin(keys), std::end(keys), std::inserter(keyListing.wkdKeyFingerprints, std::begin(keyListing.wkdKeyFingerprints)), [](const auto &k) { return k.primaryFingerprint(); }); } tryToFinishKeyLookup(); } namespace { void showKeysWithoutFingerprintsNotification(QWidget *parent, GpgME::Protocol protocol) { if (protocol != GpgME::CMS && protocol != GpgME::OpenPGP) { return; } QString message; if (protocol == GpgME::CMS) { message = xi18nc("@info", "One of the X.509 directory services returned certificates without " "fingerprints. Those certificates are ignored because fingerprints " "are required as unique identifiers for certificates." "You may want to configure a different X.509 directory service " "in the configuration dialog."); } else { message = xi18nc("@info", "The OpenPGP keyserver returned certificates without " "fingerprints. Those certificates are ignored because fingerprints " "are required as unique identifiers for certificates." "You may want to configure a different OpenPGP keyserver " "in the configuration dialog."); } KMessageBox::information(parent, message, i18nc("@title", "Invalid Server Reply"), QStringLiteral("certificates-lookup-missing-fingerprints")); } } void LookupCertificatesCommand::Private::tryToFinishKeyLookup() { if (progress) { progress->setValue(progress->value() + 1); } if (keyListing.cms || keyListing.openpgp || keyListing.wkdJob) { // still waiting for jobs to complete return; } if (progress) { progress->setValue(progress->maximum()); } if (keyListing.result.error() && !keyListing.result.error().isCanceled() && (keyListing.result.error().code() != GPG_ERR_NOT_FOUND)) { showError(dialog, keyListing.result); } if (keyListing.result.isTruncated()) { showResult(dialog, keyListing.result); } if (keyListing.cmsKeysHaveNoFingerprints) { showKeysWithoutFingerprintsNotification(dialog, GpgME::CMS); } if (keyListing.openPgpKeysHaveNoFingerprints) { showKeysWithoutFingerprintsNotification(dialog, GpgME::OpenPGP); } if (dialog) { dialog->setPassive(false); dialog->setCertificates(keyListing.keys); if (keyListing.numKeysWithoutUserId > 0) { - dialog->showInformation(i18ncp("@info", - "One certificate without name and email address was ignored.", - "%1 certificates without name and email address were ignored.", - keyListing.numKeysWithoutUserId)); + qCDebug(KLEOPATRA_LOG) << keyListing.numKeysWithoutUserId << "certificates without user IDs were ignored"; } } else { finished(); } } void LookupCertificatesCommand::Private::slotImportRequested(const std::vector &keys) { dialog = nullptr; Q_ASSERT(!keys.empty()); Q_ASSERT(std::none_of(keys.cbegin(), keys.cend(), [](const Key &key) { return key.isNull(); })); std::vector wkdKeys, otherKeys; otherKeys.reserve(keys.size()); kdtools::separate_if(std::begin(keys), std::end(keys), std::back_inserter(wkdKeys), std::back_inserter(otherKeys), [this](const auto &key) { return key.primaryFingerprint() && keyListing.wkdKeyFingerprints.find(key.primaryFingerprint()) != std::end(keyListing.wkdKeyFingerprints); }); std::vector pgp, cms; pgp.reserve(otherKeys.size()); cms.reserve(otherKeys.size()); kdtools::separate_if(otherKeys.begin(), otherKeys.end(), std::back_inserter(pgp), std::back_inserter(cms), [](const Key &key) { return key.protocol() == GpgME::OpenPGP; }); setWaitForMoreJobs(true); if (!wkdKeys.empty()) { // set an import filter, so that only user IDs matching the email address used for the WKD lookup are imported const QString importFilter = QLatin1StringView{"keep-uid=mbox = "} + searchTextToEmailAddress(keyListing.pattern); startImport(OpenPGP, keyListing.wkdKeyData, keyListing.wkdSource, {importFilter, Key::OriginWKD, keyListing.wkdSource}); } if (!pgp.empty()) { startImport(OpenPGP, pgp, i18nc(R"(@title %1:"OpenPGP" or "S/MIME")", "%1 Certificate Server", Formatting::displayName(OpenPGP))); } if (!cms.empty()) { startImport(CMS, cms, i18nc(R"(@title %1:"OpenPGP" or "S/MIME")", "%1 Certificate Server", Formatting::displayName(CMS))); } setWaitForMoreJobs(false); } void LookupCertificatesCommand::Private::slotSaveAsRequested(const std::vector &keys) { Q_UNUSED(keys) qCDebug(KLEOPATRA_LOG) << "not implemented"; } void LookupCertificatesCommand::Private::slotDetailsRequested(const Key &key) { Command *const cmd = new DetailsCommand(key); cmd->setParentWidget(dialogOrParentWidgetOrView()); cmd->start(); } void LookupCertificatesCommand::doCancel() { ImportCertificatesCommand::doCancel(); if (QDialog *const dlg = d->dialog) { d->dialog = nullptr; dlg->close(); } } void LookupCertificatesCommand::Private::showError(QWidget *parent, const KeyListResult &result) { if (!result.error()) { return; } KMessageBox::information(parent, i18nc("@info", "Failed to search on certificate server. The error returned was:\n%1", Formatting::errorAsString(result.error()))); } void LookupCertificatesCommand::Private::showResult(QWidget *parent, const KeyListResult &result) { if (result.isTruncated()) KMessageBox::information(parent, xi18nc("@info", "The query result has been truncated." "Either the local or a remote limit on " "the maximum number of returned hits has " "been exceeded." "You can try to increase the local limit " "in the configuration dialog, but if one " "of the configured servers is the limiting " "factor, you have to refine your search."), i18nc("@title", "Result Truncated"), QStringLiteral("lookup-certificates-truncated-result")); } bool LookupCertificatesCommand::Private::checkConfig() const { // unless CMS-only lookup is requested we always try a lookup via WKD const bool ok = (protocol != GpgME::CMS) || haveX509DirectoryServerConfigured(); if (!ok) { information(xi18nc("@info", "You do not have any directory servers configured." "You need to configure at least one directory server to " "search on one." "You can configure directory servers here: " "Settings->Configure Kleopatra."), i18nc("@title", "No Directory Servers Configured")); } return ok; } #undef d #undef q #include "moc_lookupcertificatescommand.cpp" diff --git a/src/dialogs/lookupcertificatesdialog.cpp b/src/dialogs/lookupcertificatesdialog.cpp index 212e02d53..8ec5c64ed 100644 --- a/src/dialogs/lookupcertificatesdialog.cpp +++ b/src/dialogs/lookupcertificatesdialog.cpp @@ -1,416 +1,398 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/lookupcertificatesdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "lookupcertificatesdialog.h" #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 GpgME; class LookupCertificatesDialog::Private { friend class ::Kleo::Dialogs::LookupCertificatesDialog; LookupCertificatesDialog *const q; public: explicit Private(LookupCertificatesDialog *qq); ~Private(); private: void slotSelectionChanged() { enableDisableWidgets(); } void slotSearchTextChanged() { enableDisableWidgets(); } void slotSearchClicked() { Q_EMIT q->searchTextChanged(searchText()); } void slotDetailsClicked() { Q_ASSERT(q->selectedCertificates().size() == 1); Q_EMIT q->detailsRequested(q->selectedCertificates().front()); } void slotSaveAsClicked() { Q_EMIT q->saveAsRequested(q->selectedCertificates()); } void readConfig(); void writeConfig(); void enableDisableWidgets(); QString searchText() const { return ui.findED->text().trimmed(); } std::vector selectedCertificates() const { const QAbstractItemView *const view = ui.resultTV->view(); if (!view) { return std::vector(); } const auto *const model = dynamic_cast(view->model()); Q_ASSERT(model); const QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); return model->keys(sm->selectedRows()); } int numSelectedCertificates() const { return ui.resultTV->selectedKeys().size(); } QValidator *queryValidator(); void updateQueryMode(); private: QueryMode queryMode = AnyQuery; bool passive; QValidator *anyQueryValidator = nullptr; QValidator *emailQueryValidator = nullptr; struct Ui { QLabel *guidanceLabel; QLabel *findLB; QLineEdit *findED; QPushButton *findPB; Kleo::KeyTreeView *resultTV; QPushButton *selectAllPB; QPushButton *deselectAllPB; QPushButton *detailsPB; QPushButton *saveAsPB; - KMessageWidget *messageWidget; QDialogButtonBox *buttonBox; void setupUi(QDialog *dialog) { auto verticalLayout = new QVBoxLayout{dialog}; auto gridLayout = new QGridLayout{}; int row = 0; guidanceLabel = new QLabel{dialog}; gridLayout->addWidget(guidanceLabel, row, 0, 1, 3); row++; findLB = new QLabel{i18n("Find:"), dialog}; gridLayout->addWidget(findLB, row, 0, 1, 1); findED = new QLineEdit{dialog}; findLB->setBuddy(findED); gridLayout->addWidget(findED, row, 1, 1, 1); findPB = new QPushButton{i18n("Search"), dialog}; findPB->setAutoDefault(false); gridLayout->addWidget(findPB, row, 2, 1, 1); row++; gridLayout->addWidget(new KSeparator{Qt::Horizontal, dialog}, row, 0, 1, 3); row++; resultTV = new Kleo::KeyTreeView(dialog); resultTV->setEnabled(true); resultTV->setMinimumSize(QSize(400, 0)); gridLayout->addWidget(resultTV, row, 0, 1, 2); auto buttonLayout = new QVBoxLayout{}; selectAllPB = new QPushButton{i18n("Select All"), dialog}; selectAllPB->setEnabled(false); selectAllPB->setAutoDefault(false); buttonLayout->addWidget(selectAllPB); deselectAllPB = new QPushButton{i18n("Deselect All"), dialog}; deselectAllPB->setEnabled(false); deselectAllPB->setAutoDefault(false); buttonLayout->addWidget(deselectAllPB); buttonLayout->addStretch(); detailsPB = new QPushButton{i18n("Details..."), dialog}; detailsPB->setEnabled(false); detailsPB->setAutoDefault(false); buttonLayout->addWidget(detailsPB); saveAsPB = new QPushButton{i18n("Save As..."), dialog}; saveAsPB->setEnabled(false); saveAsPB->setAutoDefault(false); buttonLayout->addWidget(saveAsPB); gridLayout->addLayout(buttonLayout, row, 2, 1, 1); - row++; - messageWidget = new KMessageWidget{dialog}; - messageWidget->setMessageType(KMessageWidget::Information); - messageWidget->setVisible(false); - gridLayout->addWidget(messageWidget, row, 0, 1, 3); - verticalLayout->addLayout(gridLayout); buttonBox = new QDialogButtonBox{dialog}; buttonBox->setStandardButtons(QDialogButtonBox::Close | QDialogButtonBox::Save); verticalLayout->addWidget(buttonBox); QObject::connect(findED, SIGNAL(returnPressed()), findPB, SLOT(animateClick())); QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); QObject::connect(findPB, SIGNAL(clicked()), dialog, SLOT(slotSearchClicked())); QObject::connect(detailsPB, SIGNAL(clicked()), dialog, SLOT(slotDetailsClicked())); QObject::connect(saveAsPB, SIGNAL(clicked()), dialog, SLOT(slotSaveAsClicked())); QObject::connect(findED, SIGNAL(textChanged(QString)), dialog, SLOT(slotSearchTextChanged())); QMetaObject::connectSlotsByName(dialog); } explicit Ui(LookupCertificatesDialog *q) { q->setWindowTitle(i18nc("@title:window", "Lookup on Server")); setupUi(q); saveAsPB->hide(); // ### not yet implemented in LookupCertificatesCommand findED->setClearButtonEnabled(true); resultTV->setFlatModel(AbstractKeyListModel::createFlatKeyListModel(q)); resultTV->setHierarchicalView(false); importPB()->setText(i18n("Import")); importPB()->setEnabled(false); connect(resultTV->view(), SIGNAL(doubleClicked(QModelIndex)), q, SLOT(slotDetailsClicked())); findED->setFocus(); connect(selectAllPB, &QPushButton::clicked, resultTV->view(), &QTreeView::selectAll); connect(deselectAllPB, &QPushButton::clicked, resultTV->view(), &QTreeView::clearSelection); } QPushButton *importPB() const { return buttonBox->button(QDialogButtonBox::Save); } QPushButton *closePB() const { return buttonBox->button(QDialogButtonBox::Close); } } ui; }; LookupCertificatesDialog::Private::Private(LookupCertificatesDialog *qq) : q(qq) , passive(false) , ui(q) { connect(ui.resultTV->view()->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), q, SLOT(slotSelectionChanged())); updateQueryMode(); } LookupCertificatesDialog::Private::~Private() { } void LookupCertificatesDialog::Private::readConfig() { KConfigGroup configGroup(KSharedConfig::openStateConfig(), QStringLiteral("LookupCertificatesDialog")); KConfigGroup resultKeysConfig = configGroup.group(QStringLiteral("ResultKeysView")); ui.resultTV->restoreLayout(resultKeysConfig); const QSize size = configGroup.readEntry("Size", QSize(600, 400)); if (size.isValid()) { q->resize(size); } } void LookupCertificatesDialog::Private::writeConfig() { KConfigGroup configGroup(KSharedConfig::openStateConfig(), QStringLiteral("LookupCertificatesDialog")); configGroup.writeEntry("Size", q->size()); configGroup.sync(); } static QString guidanceText(LookupCertificatesDialog::QueryMode mode) { switch (mode) { default: qCWarning(KLEOPATRA_LOG) << __func__ << "Unknown query mode:" << mode; [[fallthrough]]; case LookupCertificatesDialog::AnyQuery: return xi18nc("@info", "Enter a search term to search for matching certificates."); case LookupCertificatesDialog::EmailQuery: return xi18nc("@info", "Enter an email address to search for matching certificates."); }; } QValidator *LookupCertificatesDialog::Private::queryValidator() { switch (queryMode) { default: qCWarning(KLEOPATRA_LOG) << __func__ << "Unknown query mode:" << queryMode; [[fallthrough]]; case AnyQuery: { if (!anyQueryValidator) { // allow any query with at least one non-whitespace character anyQueryValidator = new QRegularExpressionValidator{QRegularExpression{QStringLiteral(".*\\S+.*")}, q}; } return anyQueryValidator; } case EmailQuery: { if (!emailQueryValidator) { // allow anything that looks remotely like an email address, i.e. // anything with an '@' surrounded by non-whitespace characters const QRegularExpression simpleEmailRegex{QStringLiteral(".*\\S+@\\S+.*")}; emailQueryValidator = new QRegularExpressionValidator{simpleEmailRegex, q}; } return emailQueryValidator; } } } void LookupCertificatesDialog::Private::updateQueryMode() { ui.guidanceLabel->setText(guidanceText(queryMode)); ui.findED->setValidator(queryValidator()); } LookupCertificatesDialog::LookupCertificatesDialog(QWidget *p, Qt::WindowFlags f) : QDialog(p, f) , d(new Private(this)) { d->ui.findPB->setEnabled(false); d->readConfig(); } LookupCertificatesDialog::~LookupCertificatesDialog() { d->writeConfig(); } void LookupCertificatesDialog::setQueryMode(QueryMode mode) { d->queryMode = mode; d->updateQueryMode(); } LookupCertificatesDialog::QueryMode LookupCertificatesDialog::queryMode() const { return d->queryMode; } void LookupCertificatesDialog::setCertificates(const std::vector &certs) { d->ui.resultTV->view()->setFocus(); d->ui.resultTV->setKeys(certs); if (certs.size() == 1) { d->ui.resultTV->view()->setCurrentIndex(d->ui.resultTV->view()->model()->index(0, 0)); } } std::vector LookupCertificatesDialog::selectedCertificates() const { return d->selectedCertificates(); } void LookupCertificatesDialog::setPassive(bool on) { if (d->passive == on) { return; } d->passive = on; d->enableDisableWidgets(); } bool LookupCertificatesDialog::isPassive() const { return d->passive; } void LookupCertificatesDialog::setSearchText(const QString &text) { d->ui.findED->setText(text); } QString LookupCertificatesDialog::searchText() const { return d->ui.findED->text(); } -void LookupCertificatesDialog::showInformation(const QString &message) -{ - d->ui.messageWidget->setText(message); - if (message.isEmpty()) { - d->ui.messageWidget->animatedHide(); - } else { - d->ui.messageWidget->animatedShow(); - } -} - void LookupCertificatesDialog::accept() { Q_ASSERT(!selectedCertificates().empty()); Q_EMIT importRequested(selectedCertificates()); QDialog::accept(); } void LookupCertificatesDialog::Private::enableDisableWidgets() { // enable/disable everything except 'close', based on passive: const QList list = q->children(); for (QObject *const o : list) { if (QWidget *const w = qobject_cast(o)) { w->setDisabled(passive && w != ui.closePB() && w != ui.buttonBox); } } if (passive) { return; } ui.findPB->setEnabled(ui.findED->hasAcceptableInput()); const int n = q->selectedCertificates().size(); ui.detailsPB->setEnabled(n == 1); ui.saveAsPB->setEnabled(n == 1); ui.importPB()->setEnabled(n != 0); ui.importPB()->setDefault(false); // otherwise Import becomes default button if enabled and return triggers both a search and accept() } #include "moc_lookupcertificatesdialog.cpp" diff --git a/src/dialogs/lookupcertificatesdialog.h b/src/dialogs/lookupcertificatesdialog.h index 82bacf310..c0a47e86b 100644 --- a/src/dialogs/lookupcertificatesdialog.h +++ b/src/dialogs/lookupcertificatesdialog.h @@ -1,72 +1,71 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/lookupcertificatesdialog.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include #include namespace GpgME { class Key; } namespace Kleo { namespace Dialogs { class LookupCertificatesDialog : public QDialog { Q_OBJECT public: enum QueryMode { AnyQuery, //< any query is allowed EmailQuery, //< only email queries are allowed }; explicit LookupCertificatesDialog(QWidget *parent = nullptr, Qt::WindowFlags f = {}); ~LookupCertificatesDialog() override; void setQueryMode(QueryMode mode); QueryMode queryMode() const; void setCertificates(const std::vector &certs); std::vector selectedCertificates() const; void setPassive(bool passive); bool isPassive() const; void setSearchText(const QString &text); QString searchText() const; - void showInformation(const QString &message); Q_SIGNALS: void searchTextChanged(const QString &text); void saveAsRequested(const std::vector &certs); void importRequested(const std::vector &certs); void detailsRequested(const GpgME::Key &certs); public Q_SLOTS: void accept() override; private: class Private; kdtools::pimpl_ptr d; Q_PRIVATE_SLOT(d, void slotSearchTextChanged()) Q_PRIVATE_SLOT(d, void slotSearchClicked()) Q_PRIVATE_SLOT(d, void slotSelectionChanged()) Q_PRIVATE_SLOT(d, void slotDetailsClicked()) Q_PRIVATE_SLOT(d, void slotSaveAsClicked()) }; } }