diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a836bf37f..5238bb75f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,912 +1,914 @@ /* -*- mode: c++; c-basic-offset:4 -*- mainwindow.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "aboutdata.h" #include "mainwindow.h" #include "settings.h" #include #include "view/keycacheoverlay.h" #include "view/keylistcontroller.h" #include "view/padwidget.h" #include "view/searchbar.h" #include "view/smartcardwidget.h" #include "view/tabwidget.h" #include "view/welcomewidget.h" #include "commands/decryptverifyfilescommand.h" #include "commands/importcertificatefromfilecommand.h" #include "commands/importcrlcommand.h" #include "commands/selftestcommand.h" #include "commands/signencryptfilescommand.h" #include "conf/groupsconfigdialog.h" #include "utils/action_data.h" #include "utils/clipboardmenu.h" #include "utils/detail_p.h" #include "utils/filedialog.h" #include "utils/gui-helper.h" #include "utils/qt-cxx20-compat.h" #include #include "dialogs/updatenotification.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; using namespace Kleo; using namespace Kleo::Commands; using namespace GpgME; static KGuiItem KStandardGuiItem_quit() { static const QString app = KAboutData::applicationData().displayName(); KGuiItem item = KStandardGuiItem::quit(); item.setText(xi18nc("@action:button", "&Quit %1", app)); return item; } static KGuiItem KStandardGuiItem_close() { KGuiItem item = KStandardGuiItem::close(); item.setText(i18nc("@action:button", "Only &Close Window")); return item; } static bool isQuitting = false; namespace { static const std::vector mainViewActionNames = { QStringLiteral("view_certificate_overview"), QStringLiteral("manage_smartcard"), QStringLiteral("pad_view"), }; class CertificateView : public QWidget, public FocusFirstChild { Q_OBJECT public: CertificateView(QWidget *parent = nullptr) : QWidget{parent} , ui{this} { } SearchBar *searchBar() const { return ui.searchBar; } TabWidget *tabWidget() const { return ui.tabWidget; } void focusFirstChild(Qt::FocusReason reason) override { ui.searchBar->lineEdit()->setFocus(reason); } private: struct UI { TabWidget *tabWidget = nullptr; SearchBar *searchBar = nullptr; explicit UI(CertificateView *q) { auto vbox = new QVBoxLayout{q}; vbox->setSpacing(0); searchBar = new SearchBar{q}; vbox->addWidget(searchBar); tabWidget = new TabWidget{q}; vbox->addWidget(tabWidget); tabWidget->connectSearchBar(searchBar); } } ui; }; } class MainWindow::Private { friend class ::MainWindow; MainWindow *const q; public: explicit Private(MainWindow *qq); ~Private(); template void createAndStart() { (new T(this->currentView(), &this->controller))->start(); } template void createAndStart(QAbstractItemView *view) { (new T(view, &this->controller))->start(); } template void createAndStart(const QStringList &a) { (new T(a, this->currentView(), &this->controller))->start(); } template void createAndStart(const QStringList &a, QAbstractItemView *view) { (new T(a, view, &this->controller))->start(); } void closeAndQuit() { const QString app = KAboutData::applicationData().displayName(); const int rc = KMessageBox::questionTwoActionsCancel(q, xi18n("%1 may be used by other applications as a service." "You may instead want to close this window without exiting %1.", app), i18nc("@title:window", "Really Quit?"), KStandardGuiItem_close(), KStandardGuiItem_quit(), KStandardGuiItem::cancel(), QLatin1String("really-quit-") + app.toLower()); if (rc == KMessageBox::Cancel) { return; } isQuitting = true; if (!q->close()) { return; } // WARNING: 'this' might be deleted at this point! if (rc == KMessageBox::ButtonCode::SecondaryAction) { qApp->quit(); } } void configureToolbars() { KEditToolBar dlg(q->factory()); dlg.exec(); } void editKeybindings() { KShortcutsDialog::showDialog(q->actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, q); updateSearchBarClickMessage(); } void updateSearchBarClickMessage() { const QString shortcutStr = focusToClickSearchAction->shortcut().toString(); ui.searchTab->searchBar()->updateClickMessage(shortcutStr); } void updateStatusBar() { if (DeVSCompliance::isActive()) { auto statusBar = std::make_unique(); auto statusLbl = std::make_unique(DeVSCompliance::name()); if (!SystemInfo::isHighContrastModeActive()) { const auto color = KColorScheme(QPalette::Active, KColorScheme::View) .foreground(DeVSCompliance::isCompliant() ? KColorScheme::NormalText : KColorScheme::NegativeText) .color(); const auto background = KColorScheme(QPalette::Active, KColorScheme::View) .background(DeVSCompliance::isCompliant() ? KColorScheme::PositiveBackground : KColorScheme::NegativeBackground) .color(); statusLbl->setStyleSheet(QStringLiteral("QLabel { color: %1; background-color: %2; }").arg(color.name()).arg(background.name())); } statusBar->insertPermanentWidget(0, statusLbl.release()); q->setStatusBar(statusBar.release()); // QMainWindow takes ownership } else { q->setStatusBar(nullptr); } } void selfTest() { createAndStart(); } void configureGroups() { if (KConfigDialog::showDialog(GroupsConfigDialog::dialogName())) { return; } KConfigDialog *dialog = new GroupsConfigDialog(q); dialog->show(); } void showHandbook(); void gnupgLogViewer() { // Warning: Don't assume that the program needs to be in PATH. On Windows, it will also be found next to the calling process. if (!QProcess::startDetached(QStringLiteral("kwatchgnupg"), QStringList())) KMessageBox::error(q, i18n("Could not start the GnuPG Log Viewer (kwatchgnupg). " "Please check your installation."), i18n("Error Starting KWatchGnuPG")); } void forceUpdateCheck() { UpdateNotification::forceUpdateCheck(q); } void slotConfigCommitted(); void slotContextMenuRequested(QAbstractItemView *, const QPoint &p) { if (auto const menu = qobject_cast(q->factory()->container(QStringLiteral("listview_popup"), q))) { menu->exec(p); } else { qCDebug(KLEOPATRA_LOG) << "no \"listview_popup\" in kleopatra's ui.rc file"; } } void slotFocusQuickSearch() { ui.searchTab->searchBar()->lineEdit()->setFocus(); } void showView(const QString &actionName, QWidget *widget) { const auto coll = q->actionCollection(); if (coll) { for (const QString &name : mainViewActionNames) { if (auto action = coll->action(name)) { action->setChecked(name == actionName); } } } ui.stackWidget->setCurrentWidget(widget); if (auto ffci = dynamic_cast(widget)) { ffci->focusFirstChild(Qt::TabFocusReason); } } void showCertificateView() { if (KeyCache::instance()->keys().empty()) { showView(QStringLiteral("view_certificate_overview"), ui.welcomeWidget); } else { showView(QStringLiteral("view_certificate_overview"), ui.searchTab); } } void showSmartcardView() { showView(QStringLiteral("manage_smartcard"), ui.scWidget); } void showPadView() { if (!ui.padWidget) { ui.padWidget = new PadWidget; ui.stackWidget->addWidget(ui.padWidget); } showView(QStringLiteral("pad_view"), ui.padWidget); ui.stackWidget->resize(ui.padWidget->sizeHint()); } void restartDaemons() { Kleo::killDaemons(); } private: void setupActions(); QAbstractItemView *currentView() const { return ui.searchTab->tabWidget()->currentView(); } void keyListingDone() { const auto curWidget = ui.stackWidget->currentWidget(); if (curWidget == ui.scWidget || curWidget == ui.padWidget) { return; } showCertificateView(); } private: Kleo::KeyListController controller; bool firstShow : 1; struct UI { CertificateView *searchTab = nullptr; PadWidget *padWidget = nullptr; SmartCardWidget *scWidget = nullptr; WelcomeWidget *welcomeWidget = nullptr; QStackedWidget *stackWidget = nullptr; explicit UI(MainWindow *q); } ui; QAction *focusToClickSearchAction = nullptr; ClipboardMenu *clipboadMenu = nullptr; }; MainWindow::Private::UI::UI(MainWindow *q) : padWidget(nullptr) { auto mainWidget = new QWidget{q}; auto mainLayout = new QVBoxLayout(mainWidget); stackWidget = new QStackedWidget{q}; searchTab = new CertificateView{q}; stackWidget->addWidget(searchTab); new KeyCacheOverlay(mainWidget, q); scWidget = new SmartCardWidget{q}; stackWidget->addWidget(scWidget); welcomeWidget = new WelcomeWidget{q}; stackWidget->addWidget(welcomeWidget); mainLayout->addWidget(stackWidget); q->setCentralWidget(mainWidget); } MainWindow::Private::Private(MainWindow *qq) : q(qq) , controller(q) , firstShow(true) , ui(q) { KDAB_SET_OBJECT_NAME(controller); AbstractKeyListModel *flatModel = AbstractKeyListModel::createFlatKeyListModel(q); + flatModel->useKeyCache(true, KeyList::AllKeys); AbstractKeyListModel *hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel(q); + hierarchicalModel->useKeyCache(true, KeyList::AllKeys); KDAB_SET_OBJECT_NAME(flatModel); KDAB_SET_OBJECT_NAME(hierarchicalModel); controller.setFlatModel(flatModel); controller.setHierarchicalModel(hierarchicalModel); controller.setTabWidget(ui.searchTab->tabWidget()); ui.searchTab->tabWidget()->setFlatModel(flatModel); ui.searchTab->tabWidget()->setHierarchicalModel(hierarchicalModel); setupActions(); ui.stackWidget->setCurrentWidget(ui.searchTab); if (auto action = q->actionCollection()->action(QStringLiteral("view_certificate_overview"))) { action->setChecked(true); } connect(&controller, SIGNAL(contextMenuRequested(QAbstractItemView *, QPoint)), q, SLOT(slotContextMenuRequested(QAbstractItemView *, QPoint))); connect(KeyCache::instance().get(), &KeyCache::keyListingDone, q, [this]() { keyListingDone(); }); q->createGUI(QStringLiteral("kleopatra.rc")); // make toolbar buttons accessible by keyboard auto toolbar = q->findChild(); if (toolbar) { auto toolbarButtons = toolbar->findChildren(); for (auto b : toolbarButtons) { b->setFocusPolicy(Qt::TabFocus); } // move toolbar and its child widgets before the central widget in the tab order; // this is necessary to make Shift+Tab work as expected forceSetTabOrder(q, toolbar); auto toolbarChildren = toolbar->findChildren(); std::for_each(std::rbegin(toolbarChildren), std::rend(toolbarChildren), [toolbar](auto w) { forceSetTabOrder(toolbar, w); }); } const auto title = Kleo::brandingWindowTitle(); if (!title.isEmpty()) { QApplication::setApplicationDisplayName(title); } const auto icon = Kleo::brandingIcon(); if (!icon.isEmpty()) { const auto dir = QDir(Kleo::gpg4winInstallPath() + QStringLiteral("/../share/kleopatra/pics")); qCDebug(KLEOPATRA_LOG) << "Loading branding icon:" << dir.absoluteFilePath(icon); QPixmap brandingIcon(dir.absoluteFilePath(icon)); if (!brandingIcon.isNull()) { auto w = new QWidget; auto hl = new QHBoxLayout; auto lbl = new QLabel; w->setLayout(hl); hl->addWidget(lbl); lbl->setPixmap(brandingIcon); toolbar->addSeparator(); toolbar->addWidget(w); } } // Disable whats this as the vast majority of things does not have whats this // set in Kleopatra. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // With Qt 6 this is default and the attribute is no longer documented. QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); #endif if (auto action = q->actionCollection()->action(QStringLiteral("help_whats_this"))) { delete action; } q->setAcceptDrops(true); // set default window size q->resize(QSize(1024, 500)); q->setAutoSaveSettings(); updateSearchBarClickMessage(); updateStatusBar(); if (KeyCache::instance()->initialized()) { keyListingDone(); } } MainWindow::Private::~Private() { } MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : KXmlGuiWindow(parent, flags) , d(new Private(this)) { } MainWindow::~MainWindow() { } void MainWindow::Private::setupActions() { KActionCollection *const coll = q->actionCollection(); const std::vector action_data = { // see keylistcontroller.cpp for more actions // Tools menu #ifndef Q_OS_WIN { "tools_start_kwatchgnupg", i18n("GnuPG Log Viewer"), QString(), "kwatchgnupg", q, [this](bool) { gnupgLogViewer(); }, QString(), }, #endif { "tools_restart_backend", i18nc("@action:inmenu", "Restart Background Processes"), i18nc("@info:tooltip", "Restart the background processes, e.g. after making changes to the configuration."), "view-refresh", q, [this](bool) { restartDaemons(); }, {}, }, // Help menu #ifdef Q_OS_WIN { "help_check_updates", i18n("Check for updates"), QString(), "gpg4win-compact", q, [this](bool) { forceUpdateCheck(); }, QString(), }, #endif // View menu { "view_certificate_overview", i18nc("@action show certificate overview", "Certificates"), i18n("Show certificate overview"), "view-certificate", q, [this](bool) { showCertificateView(); }, QString(), }, { "pad_view", i18nc("@action show input / output area for encrypting/signing resp. decrypting/verifying text", "Notepad"), i18n("Show pad for encrypting/decrypting and signing/verifying text"), "note", q, [this](bool) { showPadView(); }, QString(), }, { "manage_smartcard", i18nc("@action show smartcard management view", "Smartcards"), i18n("Show smartcard management"), "auth-sim-locked", q, [this](bool) { showSmartcardView(); }, QString(), }, // Settings menu { "settings_self_test", i18n("Perform Self-Test"), QString(), nullptr, q, [this](bool) { selfTest(); }, QString(), }, { "configure_groups", i18n("Configure Groups..."), QString(), "group", q, [this](bool) { configureGroups(); }, QString(), }}; make_actions_from_data(action_data, coll); if (!Settings().groupsEnabled()) { if (auto action = coll->action(QStringLiteral("configure_groups"))) { delete action; } } for (const QString &name : mainViewActionNames) { if (auto action = coll->action(name)) { action->setCheckable(true); } } KStandardAction::close(q, SLOT(close()), coll); KStandardAction::quit(q, SLOT(closeAndQuit()), coll); KStandardAction::configureToolbars(q, SLOT(configureToolbars()), coll); KStandardAction::keyBindings(q, SLOT(editKeybindings()), coll); KStandardAction::preferences(qApp, SLOT(openOrRaiseConfigDialog()), coll); focusToClickSearchAction = new QAction(i18n("Set Focus to Quick Search"), q); coll->addAction(QStringLiteral("focus_to_quickseach"), focusToClickSearchAction); coll->setDefaultShortcut(focusToClickSearchAction, QKeySequence(Qt::ALT | Qt::Key_Q)); connect(focusToClickSearchAction, SIGNAL(triggered(bool)), q, SLOT(slotFocusQuickSearch())); clipboadMenu = new ClipboardMenu(q); clipboadMenu->setMainWindow(q); clipboadMenu->clipboardMenu()->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste"))); clipboadMenu->clipboardMenu()->setPopupMode(QToolButton::InstantPopup); coll->addAction(QStringLiteral("clipboard_menu"), clipboadMenu->clipboardMenu()); /* Add additional help actions for documentation */ const auto compendium = new DocAction(QIcon::fromTheme(QStringLiteral("gpg4win-compact")), i18n("Gpg4win Compendium"), i18nc("The Gpg4win compendium is only available" "at this point (24.7.2017) in german and english." "Please check with Gpg4win before translating this filename.", "gpg4win-compendium-en.pdf"), QStringLiteral("../share/gpg4win")); coll->addAction(QStringLiteral("help_doc_compendium"), compendium); /* Documentation centered around the german approved VS-NfD mode for official * RESTRICTED communication. This is only available in some distributions with * the focus on official communications. */ const auto quickguide = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("&Quickguide"), i18nc("Only available in German and English. Leave to English for other languages.", "encrypt_and_sign_gnupgvsd_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_quickguide"), quickguide); const auto symguide = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("&Password-based encryption"), i18nc("Only available in German and English. Leave to English for other languages.", "symmetric_encryption_gnupgvsd_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_symenc"), symguide); const auto groups = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("&Group configuration"), i18nc("Only available in German and English. Leave to English for other languages.", "groupfeature_gnupgvsd_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_groups"), groups); #ifdef Q_OS_WIN const auto gpgol = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("&Mail encryption in Outlook"), i18nc("Only available in German and English. Leave to English for other languages. Only shown on Windows.", "gpgol_outlook_addin_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_gpgol"), gpgol); #endif /* The submenu with advanced topics */ const auto certmngmnt = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("&Certification Management"), i18nc("Only available in German and English. Leave to English for other languages.", "certification_management_gnupgvsd_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_cert_management"), certmngmnt); const auto smartcard = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("&Smartcard setup"), i18nc("Only available in German and English. Leave to English for other languages.", "smartcard_setup_gnupgvsd_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_smartcard"), smartcard); const auto man_gnupg = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("GnuPG Command&line"), QStringLiteral("gnupg_manual_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_gnupg"), man_gnupg); /* The secops */ const auto vsa10573 = new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), i18n("SecOps VSA-10573"), i18nc("Only available in German and English. Leave to English for other languages.", "BSI-VSA-10573-ENG_secops-20220207.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_vsa10573"), vsa10573); const auto vsa10584 = new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), i18n("SecOps VSA-10584"), i18nc("Only available in German and English. Leave to English for other languages.", "BSI-VSA-10584-ENG_secops-20220207.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_vsa10584"), vsa10584); q->setStandardToolBarMenuEnabled(true); controller.createActions(coll); ui.searchTab->tabWidget()->createActions(coll); } void MainWindow::Private::slotConfigCommitted() { controller.updateConfig(); updateStatusBar(); } void MainWindow::closeEvent(QCloseEvent *e) { // KMainWindow::closeEvent() insists on quitting the application, // so do not let it touch the event... qCDebug(KLEOPATRA_LOG); if (d->controller.hasRunningCommands()) { if (d->controller.shutdownWarningRequired()) { const int ret = KMessageBox::warningContinueCancel(this, i18n("There are still some background operations ongoing. " "These will be terminated when closing the window. " "Proceed?"), i18n("Ongoing Background Tasks")); if (ret != KMessageBox::Continue) { e->ignore(); return; } } d->controller.cancelCommands(); if (d->controller.hasRunningCommands()) { // wait for them to be finished: setEnabled(false); QEventLoop ev; QTimer::singleShot(100ms, &ev, &QEventLoop::quit); connect(&d->controller, &KeyListController::commandsExecuting, &ev, &QEventLoop::quit); ev.exec(); if (d->controller.hasRunningCommands()) qCWarning(KLEOPATRA_LOG) << "controller still has commands running, this may crash now..."; setEnabled(true); } } if (isQuitting || qApp->isSavingSession()) { d->ui.searchTab->tabWidget()->saveViews(KSharedConfig::openConfig().data()); KConfigGroup grp(KConfigGroup(KSharedConfig::openConfig(), autoSaveGroup())); saveMainWindowSettings(grp); e->accept(); } else { e->ignore(); hide(); } } void MainWindow::showEvent(QShowEvent *e) { KXmlGuiWindow::showEvent(e); if (d->firstShow) { d->ui.searchTab->tabWidget()->loadViews(KSharedConfig::openConfig().data()); d->firstShow = false; } if (!savedGeometry.isEmpty()) { restoreGeometry(savedGeometry); } } void MainWindow::hideEvent(QHideEvent *e) { savedGeometry = saveGeometry(); KXmlGuiWindow::hideEvent(e); } void MainWindow::importCertificatesFromFile(const QStringList &files) { if (!files.empty()) { d->createAndStart(files); } } static QStringList extract_local_files(const QMimeData *data) { const QList urls = data->urls(); // begin workaround KDE/Qt misinterpretation of text/uri-list QList::const_iterator end = urls.end(); if (urls.size() > 1 && !urls.back().isValid()) { --end; } // end workaround QStringList result; std::transform(urls.begin(), end, std::back_inserter(result), std::mem_fn(&QUrl::toLocalFile)); result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&QString::isEmpty)), result.end()); return result; } static bool can_decode_local_files(const QMimeData *data) { if (!data) { return false; } return !extract_local_files(data).empty(); } void MainWindow::dragEnterEvent(QDragEnterEvent *e) { qCDebug(KLEOPATRA_LOG); if (can_decode_local_files(e->mimeData())) { e->acceptProposedAction(); } } void MainWindow::dropEvent(QDropEvent *e) { qCDebug(KLEOPATRA_LOG); if (!can_decode_local_files(e->mimeData())) { return; } e->setDropAction(Qt::CopyAction); const QStringList files = extract_local_files(e->mimeData()); const unsigned int classification = classify(files); QMenu menu; QAction *const signEncrypt = menu.addAction(i18n("Sign/Encrypt...")); QAction *const decryptVerify = mayBeAnyMessageType(classification) ? menu.addAction(i18n("Decrypt/Verify...")) : nullptr; if (signEncrypt || decryptVerify) { menu.addSeparator(); } QAction *const importCerts = mayBeAnyCertStoreType(classification) ? menu.addAction(i18n("Import Certificates")) : nullptr; QAction *const importCRLs = mayBeCertificateRevocationList(classification) ? menu.addAction(i18n("Import CRLs")) : nullptr; if (importCerts || importCRLs) { menu.addSeparator(); } if (!signEncrypt && !decryptVerify && !importCerts && !importCRLs) { return; } menu.addAction(i18n("Cancel")); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) const QAction *const chosen = menu.exec(mapToGlobal(e->pos())); #else const QAction *const chosen = menu.exec(mapToGlobal(e->position().toPoint())); #endif if (!chosen) { return; } if (chosen == signEncrypt) { d->createAndStart(files); } else if (chosen == decryptVerify) { d->createAndStart(files); } else if (chosen == importCerts) { d->createAndStart(files); } else if (chosen == importCRLs) { d->createAndStart(files); } e->accept(); } void MainWindow::readProperties(const KConfigGroup &cg) { qCDebug(KLEOPATRA_LOG); KXmlGuiWindow::readProperties(cg); setHidden(cg.readEntry("hidden", false)); } void MainWindow::saveProperties(KConfigGroup &cg) { qCDebug(KLEOPATRA_LOG); KXmlGuiWindow::saveProperties(cg); cg.writeEntry("hidden", isHidden()); } #include "mainwindow.moc" #include "moc_mainwindow.cpp" diff --git a/src/view/keylistcontroller.cpp b/src/view/keylistcontroller.cpp index 1bbc682b9..0accb11df 100644 --- a/src/view/keylistcontroller.cpp +++ b/src/view/keylistcontroller.cpp @@ -1,1049 +1,1011 @@ /* -*- mode: c++; c-basic-offset:4 -*- controllers/keylistcontroller.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2022 Felix Tiede SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "keylistcontroller.h" #include "tabwidget.h" #include #include #include "commands/exportcertificatecommand.h" #include "commands/exportopenpgpcertstoservercommand.h" #include "kleopatra_debug.h" #include "tooltippreferences.h" #include #ifdef MAILAKONADI_ENABLED #include "commands/exportopenpgpcerttoprovidercommand.h" #endif // MAILAKONADI_ENABLED #include "commands/adduseridcommand.h" #include "commands/certifycertificatecommand.h" #include "commands/changeexpirycommand.h" #include "commands/changeownertrustcommand.h" #include "commands/changepassphrasecommand.h" #include "commands/changeroottrustcommand.h" #include "commands/checksumcreatefilescommand.h" #include "commands/checksumverifyfilescommand.h" #include "commands/clearcrlcachecommand.h" #include "commands/decryptverifyfilescommand.h" #include "commands/deletecertificatescommand.h" #include "commands/detailscommand.h" #include "commands/dumpcertificatecommand.h" #include "commands/dumpcrlcachecommand.h" #include "commands/exportpaperkeycommand.h" #include "commands/exportsecretkeycommand.h" #include "commands/importcertificatefromfilecommand.h" #include "commands/importcrlcommand.h" #include "commands/lookupcertificatescommand.h" #include "commands/newcertificatesigningrequestcommand.h" #include "commands/newopenpgpcertificatecommand.h" #include "commands/refreshopenpgpcertscommand.h" #include "commands/refreshx509certscommand.h" #include "commands/reloadkeyscommand.h" #include "commands/revokecertificationcommand.h" #include "commands/revokekeycommand.h" #include "commands/signencryptfilescommand.h" #include "commands/signencryptfoldercommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // needed for GPGME_VERSION_NUMBER #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::SmartCard; using namespace GpgME; class KeyListController::Private { friend class ::Kleo::KeyListController; KeyListController *const q; public: explicit Private(KeyListController *qq); ~Private(); void connectView(QAbstractItemView *view); void connectCommand(Command *cmd); void connectTabWidget(); void disconnectTabWidget(); void addCommand(Command *cmd) { connectCommand(cmd); commands.insert(std::lower_bound(commands.begin(), commands.end(), cmd), cmd); } void addView(QAbstractItemView *view) { connectView(view); views.insert(std::lower_bound(views.begin(), views.end(), view), view); } void removeView(QAbstractItemView *view) { view->disconnect(q); view->selectionModel()->disconnect(q); views.erase(std::remove(views.begin(), views.end(), view), views.end()); } public: void slotDestroyed(QObject *o) { qCDebug(KLEOPATRA_LOG) << (void *)o; views.erase(std::remove(views.begin(), views.end(), o), views.end()); commands.erase(std::remove(commands.begin(), commands.end(), o), commands.end()); } void slotDoubleClicked(const QModelIndex &idx); void slotActivated(const QModelIndex &idx); void slotSelectionChanged(const QItemSelection &old, const QItemSelection &new_); void slotContextMenu(const QPoint &pos); void slotCommandFinished(); - void slotAddKey(const Key &key); - void slotAboutToRemoveKey(const Key &key); void slotActionTriggered(QAction *action); void slotCurrentViewChanged(QAbstractItemView *view) { if (view && !std::binary_search(views.cbegin(), views.cend(), view)) { qCDebug(KLEOPATRA_LOG) << "you need to register view" << view << "before trying to set it as the current view!"; addView(view); } currentView = view; q->enableDisableActions(view ? view->selectionModel() : nullptr); } private: int toolTipOptions() const; private: static Command::Restrictions calculateRestrictionsMask(const QItemSelectionModel *sm); private: struct action_item { QPointer action; Command::Restrictions restrictions; Command *(*createCommand)(QAbstractItemView *, KeyListController *); }; std::vector actions; std::vector views; std::vector commands; QPointer parentWidget; QPointer tabWidget; QPointer currentView; QPointer flatModel, hierarchicalModel; std::vector m_connections; }; KeyListController::Private::Private(KeyListController *qq) : q(qq) , actions() , views() , commands() , parentWidget() , tabWidget() , flatModel() , hierarchicalModel() { - connect(KeyCache::instance().get(), &KeyCache::added, q, [this](const GpgME::Key &key) { - slotAddKey(key); - }); - connect(KeyCache::instance().get(), &KeyCache::aboutToRemove, q, [this](const GpgME::Key &key) { - slotAboutToRemoveKey(key); - }); } KeyListController::Private::~Private() { } KeyListController::KeyListController(QObject *p) : QObject(p) , d(new Private(this)) { } KeyListController::~KeyListController() { } -void KeyListController::Private::slotAddKey(const Key &key) -{ - // ### make model act on keycache directly... - if (flatModel) { - flatModel->addKey(key); - } - if (hierarchicalModel) { - hierarchicalModel->addKey(key); - } -} - -void KeyListController::Private::slotAboutToRemoveKey(const Key &key) -{ - // ### make model act on keycache directly... - if (flatModel) { - flatModel->removeKey(key); - } - if (hierarchicalModel) { - hierarchicalModel->removeKey(key); - } -} - void KeyListController::addView(QAbstractItemView *view) { if (!view || std::binary_search(d->views.cbegin(), d->views.cend(), view)) { return; } d->addView(view); } void KeyListController::removeView(QAbstractItemView *view) { if (!view || !std::binary_search(d->views.cbegin(), d->views.cend(), view)) { return; } d->removeView(view); } void KeyListController::setCurrentView(QAbstractItemView *view) { d->slotCurrentViewChanged(view); } std::vector KeyListController::views() const { return d->views; } void KeyListController::setFlatModel(AbstractKeyListModel *model) { if (model == d->flatModel) { return; } d->flatModel = model; if (model) { - model->clear(); - if (KeyCache::instance()->initialized()) { - model->addKeys(KeyCache::instance()->keys()); - } model->setToolTipOptions(d->toolTipOptions()); } } void KeyListController::setHierarchicalModel(AbstractKeyListModel *model) { if (model == d->hierarchicalModel) { return; } d->hierarchicalModel = model; if (model) { - model->clear(); - if (KeyCache::instance()->initialized()) { - model->addKeys(KeyCache::instance()->keys()); - } model->setToolTipOptions(d->toolTipOptions()); } } void KeyListController::setTabWidget(TabWidget *tabWidget) { if (tabWidget == d->tabWidget) { return; } d->disconnectTabWidget(); d->tabWidget = tabWidget; d->connectTabWidget(); d->slotCurrentViewChanged(tabWidget ? tabWidget->currentView() : nullptr); } void KeyListController::setParentWidget(QWidget *parent) { d->parentWidget = parent; } QWidget *KeyListController::parentWidget() const { return d->parentWidget; } void KeyListController::Private::connectTabWidget() { if (!tabWidget) { return; } const auto views = tabWidget->views(); std::for_each(views.cbegin(), views.cend(), [this](QAbstractItemView *view) { addView(view); }); m_connections.reserve(3); m_connections.push_back(connect(tabWidget, &TabWidget::viewAdded, q, &KeyListController::addView)); m_connections.push_back(connect(tabWidget, &TabWidget::viewAboutToBeRemoved, q, &KeyListController::removeView)); m_connections.push_back(connect(tabWidget, &TabWidget::currentViewChanged, q, [this](QAbstractItemView *view) { slotCurrentViewChanged(view); })); } void KeyListController::Private::disconnectTabWidget() { if (!tabWidget) { return; } for (const auto &connection : m_connections) { disconnect(connection); } m_connections.clear(); const auto views = tabWidget->views(); std::for_each(views.cbegin(), views.cend(), [this](QAbstractItemView *view) { removeView(view); }); } AbstractKeyListModel *KeyListController::flatModel() const { return d->flatModel; } AbstractKeyListModel *KeyListController::hierarchicalModel() const { return d->hierarchicalModel; } QAbstractItemView *KeyListController::currentView() const { return d->currentView; } TabWidget *KeyListController::tabWidget() const { return d->tabWidget; } void KeyListController::createActions(KActionCollection *coll) { const std::vector common_and_openpgp_action_data = { // File menu { "file_new_certificate", i18n("New OpenPGP Key Pair..."), i18n("Create a new OpenPGP certificate"), "view-certificate-add", nullptr, nullptr, QStringLiteral("Ctrl+N"), }, { "file_export_certificates", i18n("Export..."), i18n("Export the selected certificate (public key) to a file"), "view-certificate-export", nullptr, nullptr, QStringLiteral("Ctrl+E"), }, { "file_export_certificates_to_server", i18n("Publish on Server..."), i18n("Publish the selected certificate (public key) on a public keyserver"), "view-certificate-export-server", nullptr, nullptr, QStringLiteral("Ctrl+Shift+E"), }, #ifdef MAILAKONADI_ENABLED { "file_export_certificate_to_provider", i18n("Publish at Mail Provider..."), i18n("Publish the selected certificate (public key) at mail provider's Web Key Directory if offered"), "view-certificate-export", nullptr, nullptr, QString(), }, #endif // MAILAKONADI_ENABLED { "file_export_secret_keys", i18n("Backup Secret Keys..."), QString(), "view-certificate-export-secret", nullptr, nullptr, QString(), }, { "file_export_paper_key", i18n("Print Secret Key..."), QString(), "document-print", nullptr, nullptr, QString(), }, { "file_lookup_certificates", i18n("Lookup on Server..."), i18n("Search for certificates online using a public keyserver"), "edit-find", nullptr, nullptr, QStringLiteral("Shift+Ctrl+I"), }, { "file_import_certificates", i18n("Import..."), i18n("Import a certificate from a file"), "view-certificate-import", nullptr, nullptr, QStringLiteral("Ctrl+I"), }, { "file_decrypt_verify_files", i18n("Decrypt/Verify..."), i18n("Decrypt and/or verify files"), "document-edit-decrypt-verify", nullptr, nullptr, QString(), }, { "file_sign_encrypt_files", i18n("Sign/Encrypt..."), i18n("Encrypt and/or sign files"), "document-edit-sign-encrypt", nullptr, nullptr, QString(), }, { "file_sign_encrypt_folder", i18n("Sign/Encrypt Folder..."), i18n("Encrypt and/or sign folders"), nullptr /*"folder-edit-sign-encrypt"*/, nullptr, nullptr, QString(), }, { "file_checksum_create_files", i18n("Create Checksum Files..."), QString(), nullptr /*"document-checksum-create"*/, nullptr, nullptr, QString(), }, { "file_checksum_verify_files", i18n("Verify Checksum Files..."), QString(), nullptr /*"document-checksum-verify"*/, nullptr, nullptr, QString(), }, // View menu { "view_redisplay", i18n("Redisplay"), QString(), "view-refresh", nullptr, nullptr, QStringLiteral("F5"), }, { "view_stop_operations", i18n("Stop Operation"), QString(), "process-stop", this, [this](bool) { cancelCommands(); }, QStringLiteral("Escape"), RegularQAction, Disabled, }, { "view_certificate_details", i18n("Details"), QString(), "dialog-information", nullptr, nullptr, QString(), }, // Certificate menu { "certificates_revoke", i18n("Revoke Certificate..."), i18n("Revoke the selected OpenPGP certificate"), "view-certificate-revoke", nullptr, nullptr, {}, }, { "certificates_delete", i18n("Delete"), i18n("Delete selected certificates"), "edit-delete", nullptr, nullptr, QStringLiteral("Delete"), }, { "certificates_certify_certificate", i18n("Certify..."), i18n("Certify the validity of the selected certificate"), "view-certificate-sign", nullptr, nullptr, QString(), }, { "certificates_revoke_certification", i18n("Revoke Certification..."), i18n("Revoke the certification of the selected certificate"), "view-certificate-revoke", nullptr, nullptr, QString(), }, { "certificates_change_expiry", i18n("Change End of Validity Period..."), QString(), nullptr, nullptr, nullptr, QString(), }, { "certificates_change_owner_trust", i18nc("@action:inmenu", "Change Certification Power..."), i18nc("@info:tooltip", "Grant or revoke the certification power of the selected certificate"), nullptr, nullptr, nullptr, QString(), }, { "certificates_change_passphrase", i18n("Change Passphrase..."), QString(), nullptr, nullptr, nullptr, QString(), }, { "certificates_add_userid", i18n("Add User ID..."), QString(), nullptr, nullptr, nullptr, QString(), }, // Tools menu { "tools_refresh_openpgp_certificates", i18n("Refresh OpenPGP Certificates"), QString(), "view-refresh", nullptr, nullptr, QString(), }, // Window menu // (come from TabWidget) // Help menu // (come from MainWindow) }; static const action_data cms_create_csr_action_data = { "file_new_certificate_signing_request", i18n("New S/MIME Certification Request..."), i18n("Create a new S/MIME certificate signing request (CSR)"), "view-certificate-add", nullptr, nullptr, {}, }; static const std::vector cms_action_data = { // Certificate menu { "certificates_trust_root", i18n("Trust Root Certificate"), QString(), nullptr, nullptr, nullptr, QString(), }, { "certificates_distrust_root", i18n("Distrust Root Certificate"), QString(), nullptr, nullptr, nullptr, QString(), }, { "certificates_dump_certificate", i18n("Technical Details"), QString(), nullptr, nullptr, nullptr, QString(), }, // Tools menu { "tools_refresh_x509_certificates", i18n("Refresh S/MIME Certificates"), QString(), "view-refresh", nullptr, nullptr, QString(), }, { "crl_clear_crl_cache", i18n("Clear CRL Cache"), QString(), nullptr, nullptr, nullptr, QString(), }, { "crl_dump_crl_cache", i18n("Dump CRL Cache"), QString(), nullptr, nullptr, nullptr, QString(), }, { "crl_import_crl", i18n("Import CRL From File..."), QString(), nullptr, nullptr, nullptr, QString(), }, }; std::vector action_data = common_and_openpgp_action_data; if (const Kleo::Settings settings{}; settings.cmsEnabled()) { if (settings.cmsCertificateCreationAllowed()) { action_data.push_back(cms_create_csr_action_data); } action_data.reserve(action_data.size() + cms_action_data.size()); std::copy(std::begin(cms_action_data), std::end(cms_action_data), std::back_inserter(action_data)); } make_actions_from_data(action_data, coll); if (QAction *action = coll->action(QStringLiteral("view_stop_operations"))) { connect(this, &KeyListController::commandsExecuting, action, &QAction::setEnabled); } // ### somehow make this better... registerActionForCommand(coll->action(QStringLiteral("file_new_certificate"))); registerActionForCommand(coll->action(QStringLiteral("file_new_certificate_signing_request"))); //--- registerActionForCommand(coll->action(QStringLiteral("file_lookup_certificates"))); registerActionForCommand(coll->action(QStringLiteral("file_import_certificates"))); //--- registerActionForCommand(coll->action(QStringLiteral("file_export_certificates"))); registerActionForCommand(coll->action(QStringLiteral("file_export_secret_keys"))); registerActionForCommand(coll->action(QStringLiteral("file_export_paper_key"))); registerActionForCommand(coll->action(QStringLiteral("file_export_certificates_to_server"))); #ifdef MAILAKONADI_ENABLED registerActionForCommand(coll->action(QStringLiteral("file_export_certificate_to_provider"))); #endif // MAILAKONADI_ENABLED //--- registerActionForCommand(coll->action(QStringLiteral("file_decrypt_verify_files"))); registerActionForCommand(coll->action(QStringLiteral("file_sign_encrypt_files"))); registerActionForCommand(coll->action(QStringLiteral("file_sign_encrypt_folder"))); //--- registerActionForCommand(coll->action(QStringLiteral("file_checksum_create_files"))); registerActionForCommand(coll->action(QStringLiteral("file_checksum_verify_files"))); registerActionForCommand(coll->action(QStringLiteral("view_redisplay"))); // coll->action( "view_stop_operations" ) <-- already dealt with in make_actions_from_data() registerActionForCommand(coll->action(QStringLiteral("view_certificate_details"))); registerActionForCommand(coll->action(QStringLiteral("certificates_change_owner_trust"))); registerActionForCommand(coll->action(QStringLiteral("certificates_trust_root"))); registerActionForCommand(coll->action(QStringLiteral("certificates_distrust_root"))); //--- registerActionForCommand(coll->action(QStringLiteral("certificates_certify_certificate"))); if (RevokeCertificationCommand::isSupported()) { registerActionForCommand(coll->action(QStringLiteral("certificates_revoke_certification"))); } //--- registerActionForCommand(coll->action(QStringLiteral("certificates_change_expiry"))); registerActionForCommand(coll->action(QStringLiteral("certificates_change_passphrase"))); registerActionForCommand(coll->action(QStringLiteral("certificates_add_userid"))); //--- registerActionForCommand(coll->action(QStringLiteral("certificates_revoke"))); registerActionForCommand(coll->action(QStringLiteral("certificates_delete"))); //--- registerActionForCommand(coll->action(QStringLiteral("certificates_dump_certificate"))); registerActionForCommand(coll->action(QStringLiteral("tools_refresh_x509_certificates"))); registerActionForCommand(coll->action(QStringLiteral("tools_refresh_openpgp_certificates"))); //--- registerActionForCommand(coll->action(QStringLiteral("crl_import_crl"))); //--- registerActionForCommand(coll->action(QStringLiteral("crl_clear_crl_cache"))); registerActionForCommand(coll->action(QStringLiteral("crl_dump_crl_cache"))); enableDisableActions(nullptr); } void KeyListController::registerAction(QAction *action, Command::Restrictions restrictions, Command *(*create)(QAbstractItemView *, KeyListController *)) { if (!action) { return; } Q_ASSERT(!action->isCheckable()); // can be added later, for now, disallow const Private::action_item ai = {action, restrictions, create}; connect(action, &QAction::triggered, this, [this, action]() { d->slotActionTriggered(action); }); d->actions.push_back(ai); } void KeyListController::registerCommand(Command *cmd) { if (!cmd || std::binary_search(d->commands.cbegin(), d->commands.cend(), cmd)) { return; } d->addCommand(cmd); qCDebug(KLEOPATRA_LOG) << (void *)cmd; if (d->commands.size() == 1) { Q_EMIT commandsExecuting(true); } } bool KeyListController::hasRunningCommands() const { return !d->commands.empty(); } bool KeyListController::shutdownWarningRequired() const { return std::any_of(d->commands.cbegin(), d->commands.cend(), std::mem_fn(&Command::warnWhenRunningAtShutdown)); } // slot void KeyListController::cancelCommands() { std::for_each(d->commands.begin(), d->commands.end(), std::mem_fn(&Command::cancel)); } void KeyListController::Private::connectView(QAbstractItemView *view) { connect(view, &QObject::destroyed, q, [this](QObject *obj) { slotDestroyed(obj); }); connect(view, &QAbstractItemView::doubleClicked, q, [this](const QModelIndex &index) { slotDoubleClicked(index); }); connect(view, &QAbstractItemView::activated, q, [this](const QModelIndex &index) { slotActivated(index); }); connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, q, [this](const QItemSelection &oldSel, const QItemSelection &newSel) { slotSelectionChanged(oldSel, newSel); }); view->setContextMenuPolicy(Qt::CustomContextMenu); connect(view, &QWidget::customContextMenuRequested, q, [this](const QPoint &pos) { slotContextMenu(pos); }); } void KeyListController::Private::connectCommand(Command *cmd) { if (!cmd) { return; } connect(cmd, &QObject::destroyed, q, [this](QObject *obj) { slotDestroyed(obj); }); connect(cmd, &Command::finished, q, [this] { slotCommandFinished(); }); // connect( cmd, SIGNAL(canceled()), q, SLOT(slotCommandCanceled()) ); connect(cmd, &Command::progress, q, &KeyListController::progress); } void KeyListController::Private::slotDoubleClicked(const QModelIndex &idx) { QAbstractItemView *const view = qobject_cast(q->sender()); if (!view || !std::binary_search(views.cbegin(), views.cend(), view)) { return; } if (const auto *const keyListModel = dynamic_cast(view->model())) { DetailsCommand *const c = new DetailsCommand{keyListModel->key(idx)}; c->setParentWidget(parentWidget ? parentWidget : view); c->start(); } } void KeyListController::Private::slotActivated(const QModelIndex &idx) { Q_UNUSED(idx) QAbstractItemView *const view = qobject_cast(q->sender()); if (!view || !std::binary_search(views.cbegin(), views.cend(), view)) { return; } } void KeyListController::Private::slotSelectionChanged(const QItemSelection &old, const QItemSelection &new_) { Q_UNUSED(old) Q_UNUSED(new_) const QItemSelectionModel *const sm = qobject_cast(q->sender()); if (!sm) { return; } q->enableDisableActions(sm); } void KeyListController::Private::slotContextMenu(const QPoint &p) { QAbstractItemView *const view = qobject_cast(q->sender()); if (view && std::binary_search(views.cbegin(), views.cend(), view)) { Q_EMIT q->contextMenuRequested(view, view->viewport()->mapToGlobal(p)); } else { qCDebug(KLEOPATRA_LOG) << "sender is not a QAbstractItemView*!"; } } void KeyListController::Private::slotCommandFinished() { Command *const cmd = qobject_cast(q->sender()); if (!cmd || !std::binary_search(commands.cbegin(), commands.cend(), cmd)) { return; } qCDebug(KLEOPATRA_LOG) << (void *)cmd; if (commands.size() == 1) { Q_EMIT q->commandsExecuting(false); } } void KeyListController::enableDisableActions(const QItemSelectionModel *sm) const { const Command::Restrictions restrictionsMask = d->calculateRestrictionsMask(sm); for (const Private::action_item &ai : std::as_const(d->actions)) if (ai.action) { ai.action->setEnabled(ai.restrictions == (ai.restrictions & restrictionsMask)); } } static bool all_secret_are_not_owner_trust_ultimate(const std::vector &keys) { for (const Key &key : keys) if (key.hasSecret() && key.ownerTrust() == Key::Ultimate) { return false; } return true; } Command::Restrictions find_root_restrictions(const std::vector &keys) { bool trusted = false, untrusted = false; for (const Key &key : keys) if (key.isRoot()) if (key.userID(0).validity() == UserID::Ultimate) { trusted = true; } else { untrusted = true; } else { return Command::NoRestriction; } if (trusted) if (untrusted) { return Command::NoRestriction; } else { return Command::MustBeTrustedRoot; } else if (untrusted) { return Command::MustBeUntrustedRoot; } else { return Command::NoRestriction; } } Command::Restrictions KeyListController::Private::calculateRestrictionsMask(const QItemSelectionModel *sm) { if (!sm) { return Command::NoRestriction; } const KeyListModelInterface *const m = dynamic_cast(sm->model()); if (!m) { return Command::NoRestriction; } const std::vector keys = m->keys(sm->selectedRows()); if (keys.empty()) { return Command::NoRestriction; } Command::Restrictions result = Command::NeedSelection; if (keys.size() == 1) { result |= Command::OnlyOneKey; } #if GPGME_VERSION_NUMBER >= 0x011102 // 1.17.2 // we need to check the primary subkey because Key::hasSecret() is also true if just the secret key stub of an offline key is available const auto primaryKeyCanBeUsedForSecretKeyOperations = [](const auto &k) { return k.subkey(0).isSecret(); }; #else // older versions of GpgME did not always set the secret flag for card keys const auto primaryKeyCanBeUsedForSecretKeyOperations = [](const auto &k) { return k.subkey(0).isSecret() || k.subkey(0).isCardKey(); }; #endif if (std::all_of(keys.cbegin(), keys.cend(), primaryKeyCanBeUsedForSecretKeyOperations)) { result |= Command::NeedSecretKey; } if (std::all_of(std::begin(keys), std::end(keys), [](const auto &k) { return k.subkey(0).isSecret() && !k.subkey(0).isCardKey(); })) { result |= Command::NeedSecretKeyData; } if (std::all_of(keys.cbegin(), keys.cend(), [](const Key &key) { return key.protocol() == OpenPGP; })) { result |= Command::MustBeOpenPGP; } else if (std::all_of(keys.cbegin(), keys.cend(), [](const Key &key) { return key.protocol() == CMS; })) { result |= Command::MustBeCMS; } if (Kleo::all_of(keys, [](const auto &key) { return !key.isBad(); })) { result |= Command::MustBeValid; } if (all_secret_are_not_owner_trust_ultimate(keys)) { result |= Command::MayOnlyBeSecretKeyIfOwnerTrustIsNotYetUltimate; } result |= find_root_restrictions(keys); if (const ReaderStatus *rs = ReaderStatus::instance()) { if (!rs->firstCardWithNullPin().empty()) { result |= Command::AnyCardHasNullPin; } if (rs->anyCardCanLearnKeys()) { result |= Command::AnyCardCanLearnKeys; } } return result; } void KeyListController::Private::slotActionTriggered(QAction *sender) { const auto it = std::find_if(actions.cbegin(), actions.cend(), [sender](const action_item &item) { return item.action == sender; }); if (it != actions.end()) if (Command *const c = it->createCommand(this->currentView, q)) { if (parentWidget) { c->setParentWidget(parentWidget); } c->start(); } else qCDebug(KLEOPATRA_LOG) << "createCommand() == NULL for action(?) \"" << qPrintable(sender->objectName()) << "\""; else { qCDebug(KLEOPATRA_LOG) << "I don't know anything about action(?) \"%s\"", qPrintable(sender->objectName()); } } int KeyListController::Private::toolTipOptions() const { using namespace Kleo::Formatting; static const int validityFlags = Validity | Issuer | ExpiryDates | CertificateUsage; static const int ownerFlags = Subject | UserIDs | OwnerTrust; static const int detailsFlags = StorageLocation | CertificateType | SerialNumber | Fingerprint; const TooltipPreferences prefs; int flags = KeyID; flags |= prefs.showValidity() ? validityFlags : 0; flags |= prefs.showOwnerInformation() ? ownerFlags : 0; flags |= prefs.showCertificateDetails() ? detailsFlags : 0; return flags; } void KeyListController::updateConfig() { const int opts = d->toolTipOptions(); if (d->flatModel) { d->flatModel->setToolTipOptions(opts); } if (d->hierarchicalModel) { d->hierarchicalModel->setToolTipOptions(opts); } } #include "moc_keylistcontroller.cpp"