File indexing completed on 2024-06-16 04:56:15

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     view/keylistcontroller.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
0006     SPDX-FileCopyrightText: 2022 Felix Tiede
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include <config-kleopatra.h>
0012 
0013 #include "keylistcontroller.h"
0014 #include "tabwidget.h"
0015 
0016 #include <smartcard/readerstatus.h>
0017 
0018 #include <utils/action_data.h>
0019 
0020 #include "commands/exportcertificatecommand.h"
0021 #include "commands/exportopenpgpcertstoservercommand.h"
0022 #include "kleopatra_debug.h"
0023 #include "tooltippreferences.h"
0024 #include <settings.h>
0025 #ifdef MAILAKONADI_ENABLED
0026 #include "commands/exportopenpgpcerttoprovidercommand.h"
0027 #endif // MAILAKONADI_ENABLED
0028 #include "commands/adduseridcommand.h"
0029 #include "commands/certifycertificatecommand.h"
0030 #include "commands/changeexpirycommand.h"
0031 #include "commands/changeownertrustcommand.h"
0032 #include "commands/changepassphrasecommand.h"
0033 #include "commands/changeroottrustcommand.h"
0034 #include "commands/checksumcreatefilescommand.h"
0035 #include "commands/checksumverifyfilescommand.h"
0036 #include "commands/clearcrlcachecommand.h"
0037 #include "commands/decryptverifyfilescommand.h"
0038 #include "commands/deletecertificatescommand.h"
0039 #include "commands/detailscommand.h"
0040 #include "commands/dumpcertificatecommand.h"
0041 #include "commands/dumpcrlcachecommand.h"
0042 #include "commands/exportpaperkeycommand.h"
0043 #include "commands/exportsecretkeycommand.h"
0044 #include "commands/importcertificatefromfilecommand.h"
0045 #include "commands/importcrlcommand.h"
0046 #include "commands/lookupcertificatescommand.h"
0047 #include "commands/newcertificatesigningrequestcommand.h"
0048 #include "commands/newopenpgpcertificatecommand.h"
0049 #include "commands/refreshopenpgpcertscommand.h"
0050 #include "commands/refreshx509certscommand.h"
0051 #include "commands/reloadkeyscommand.h"
0052 #include "commands/revokecertificationcommand.h"
0053 #include "commands/revokekeycommand.h"
0054 #include "commands/signencryptfilescommand.h"
0055 #include "commands/signencryptfoldercommand.h"
0056 
0057 #include <Libkleo/Algorithm>
0058 #include <Libkleo/Formatting>
0059 #include <Libkleo/KeyCache>
0060 #include <Libkleo/KeyListModel>
0061 
0062 #include <gpgme++/key.h>
0063 
0064 #include <KActionCollection>
0065 #include <KLocalizedString>
0066 
0067 #include <QAbstractItemView>
0068 #include <QAction>
0069 #include <QItemSelectionModel>
0070 #include <QPointer>
0071 
0072 #include <algorithm>
0073 #include <iterator>
0074 
0075 // needed for GPGME_VERSION_NUMBER
0076 #include <gpgme.h>
0077 
0078 using namespace Kleo;
0079 using namespace Kleo::Commands;
0080 using namespace Kleo::SmartCard;
0081 using namespace GpgME;
0082 
0083 class KeyListController::Private
0084 {
0085     friend class ::Kleo::KeyListController;
0086     KeyListController *const q;
0087 
0088 public:
0089     explicit Private(KeyListController *qq);
0090     ~Private();
0091 
0092     void connectView(QAbstractItemView *view);
0093     void connectCommand(Command *cmd);
0094 
0095     void connectTabWidget();
0096     void disconnectTabWidget();
0097 
0098     void addCommand(Command *cmd)
0099     {
0100         connectCommand(cmd);
0101         commands.insert(std::lower_bound(commands.begin(), commands.end(), cmd), cmd);
0102     }
0103     void addView(QAbstractItemView *view)
0104     {
0105         connectView(view);
0106         views.insert(std::lower_bound(views.begin(), views.end(), view), view);
0107     }
0108     void removeView(QAbstractItemView *view)
0109     {
0110         view->disconnect(q);
0111         view->selectionModel()->disconnect(q);
0112         views.erase(std::remove(views.begin(), views.end(), view), views.end());
0113     }
0114 
0115 public:
0116     void slotDestroyed(QObject *o)
0117     {
0118         qCDebug(KLEOPATRA_LOG) << (void *)o;
0119         views.erase(std::remove(views.begin(), views.end(), o), views.end());
0120         commands.erase(std::remove(commands.begin(), commands.end(), o), commands.end());
0121     }
0122     void slotDoubleClicked(const QModelIndex &idx);
0123     void slotActivated(const QModelIndex &idx);
0124     void slotSelectionChanged(const QItemSelection &old, const QItemSelection &new_);
0125     void slotContextMenu(const QPoint &pos);
0126     void slotCommandFinished();
0127     void slotActionTriggered(QAction *action);
0128     void slotCurrentViewChanged(QAbstractItemView *view)
0129     {
0130         if (view && !std::binary_search(views.cbegin(), views.cend(), view)) {
0131             qCDebug(KLEOPATRA_LOG) << "you need to register view" << view << "before trying to set it as the current view!";
0132             addView(view);
0133         }
0134         currentView = view;
0135         q->enableDisableActions(view ? view->selectionModel() : nullptr);
0136     }
0137 
0138 private:
0139     int toolTipOptions() const;
0140 
0141 private:
0142     static Command::Restrictions calculateRestrictionsMask(const QItemSelectionModel *sm);
0143 
0144 private:
0145     struct action_item {
0146         QPointer<QAction> action;
0147         Command::Restrictions restrictions;
0148         Command *(*createCommand)(QAbstractItemView *, KeyListController *);
0149     };
0150     std::vector<action_item> actions;
0151     std::vector<QAbstractItemView *> views;
0152     std::vector<Command *> commands;
0153     QPointer<QWidget> parentWidget;
0154     QPointer<TabWidget> tabWidget;
0155     QPointer<QAbstractItemView> currentView;
0156     QPointer<AbstractKeyListModel> flatModel, hierarchicalModel;
0157     std::vector<QMetaObject::Connection> m_connections;
0158 };
0159 
0160 KeyListController::Private::Private(KeyListController *qq)
0161     : q(qq)
0162     , actions()
0163     , views()
0164     , commands()
0165     , parentWidget()
0166     , tabWidget()
0167     , flatModel()
0168     , hierarchicalModel()
0169 {
0170 }
0171 
0172 KeyListController::Private::~Private()
0173 {
0174 }
0175 
0176 KeyListController::KeyListController(QObject *p)
0177     : QObject(p)
0178     , d(new Private(this))
0179 {
0180 }
0181 
0182 KeyListController::~KeyListController()
0183 {
0184 }
0185 
0186 void KeyListController::addView(QAbstractItemView *view)
0187 {
0188     if (!view || std::binary_search(d->views.cbegin(), d->views.cend(), view)) {
0189         return;
0190     }
0191     d->addView(view);
0192 }
0193 
0194 void KeyListController::removeView(QAbstractItemView *view)
0195 {
0196     if (!view || !std::binary_search(d->views.cbegin(), d->views.cend(), view)) {
0197         return;
0198     }
0199     d->removeView(view);
0200 }
0201 
0202 void KeyListController::setCurrentView(QAbstractItemView *view)
0203 {
0204     d->slotCurrentViewChanged(view);
0205 }
0206 
0207 std::vector<QAbstractItemView *> KeyListController::views() const
0208 {
0209     return d->views;
0210 }
0211 
0212 void KeyListController::setFlatModel(AbstractKeyListModel *model)
0213 {
0214     if (model == d->flatModel) {
0215         return;
0216     }
0217 
0218     d->flatModel = model;
0219 
0220     if (model) {
0221         model->setToolTipOptions(d->toolTipOptions());
0222     }
0223 }
0224 
0225 void KeyListController::setHierarchicalModel(AbstractKeyListModel *model)
0226 {
0227     if (model == d->hierarchicalModel) {
0228         return;
0229     }
0230 
0231     d->hierarchicalModel = model;
0232 
0233     if (model) {
0234         model->setToolTipOptions(d->toolTipOptions());
0235     }
0236 }
0237 
0238 void KeyListController::setTabWidget(TabWidget *tabWidget)
0239 {
0240     if (tabWidget == d->tabWidget) {
0241         return;
0242     }
0243 
0244     d->disconnectTabWidget();
0245 
0246     d->tabWidget = tabWidget;
0247 
0248     d->connectTabWidget();
0249 
0250     d->slotCurrentViewChanged(tabWidget ? tabWidget->currentView() : nullptr);
0251 }
0252 
0253 void KeyListController::setParentWidget(QWidget *parent)
0254 {
0255     d->parentWidget = parent;
0256 }
0257 
0258 QWidget *KeyListController::parentWidget() const
0259 {
0260     return d->parentWidget;
0261 }
0262 
0263 void KeyListController::Private::connectTabWidget()
0264 {
0265     if (!tabWidget) {
0266         return;
0267     }
0268     const auto views = tabWidget->views();
0269     std::for_each(views.cbegin(), views.cend(), [this](QAbstractItemView *view) {
0270         addView(view);
0271     });
0272 
0273     m_connections.reserve(3);
0274     m_connections.push_back(connect(tabWidget, &TabWidget::viewAdded, q, &KeyListController::addView));
0275     m_connections.push_back(connect(tabWidget, &TabWidget::viewAboutToBeRemoved, q, &KeyListController::removeView));
0276     m_connections.push_back(connect(tabWidget, &TabWidget::currentViewChanged, q, [this](QAbstractItemView *view) {
0277         slotCurrentViewChanged(view);
0278     }));
0279 }
0280 
0281 void KeyListController::Private::disconnectTabWidget()
0282 {
0283     if (!tabWidget) {
0284         return;
0285     }
0286     for (const auto &connection : m_connections) {
0287         disconnect(connection);
0288     }
0289     m_connections.clear();
0290 
0291     const auto views = tabWidget->views();
0292     std::for_each(views.cbegin(), views.cend(), [this](QAbstractItemView *view) {
0293         removeView(view);
0294     });
0295 }
0296 
0297 AbstractKeyListModel *KeyListController::flatModel() const
0298 {
0299     return d->flatModel;
0300 }
0301 
0302 AbstractKeyListModel *KeyListController::hierarchicalModel() const
0303 {
0304     return d->hierarchicalModel;
0305 }
0306 
0307 QAbstractItemView *KeyListController::currentView() const
0308 {
0309     return d->currentView;
0310 }
0311 
0312 TabWidget *KeyListController::tabWidget() const
0313 {
0314     return d->tabWidget;
0315 }
0316 
0317 void KeyListController::createActions(KActionCollection *coll)
0318 {
0319     const std::vector<action_data> common_and_openpgp_action_data = {
0320         // File menu
0321         {
0322             "file_new_certificate",
0323             i18n("New OpenPGP Key Pair..."),
0324             i18n("Create a new OpenPGP certificate"),
0325             "view-certificate-add",
0326             nullptr,
0327             nullptr,
0328             QStringLiteral("Ctrl+N"),
0329         },
0330         {
0331             "file_export_certificates",
0332             i18n("Export..."),
0333             i18n("Export the selected certificate (public key) to a file"),
0334             "view-certificate-export",
0335             nullptr,
0336             nullptr,
0337             QStringLiteral("Ctrl+E"),
0338         },
0339         {
0340             "file_export_certificates_to_server",
0341             i18n("Publish on Server..."),
0342             i18n("Publish the selected certificate (public key) on a public keyserver"),
0343             "view-certificate-export-server",
0344             nullptr,
0345             nullptr,
0346             QStringLiteral("Ctrl+Shift+E"),
0347         },
0348 #ifdef MAILAKONADI_ENABLED
0349         {
0350             "file_export_certificate_to_provider",
0351             i18n("Publish at Mail Provider..."),
0352             i18n("Publish the selected certificate (public key) at mail provider's Web Key Directory if offered"),
0353             "view-certificate-export",
0354             nullptr,
0355             nullptr,
0356             QString(),
0357         },
0358 #endif // MAILAKONADI_ENABLED
0359         {
0360             "file_export_secret_keys",
0361             i18n("Backup Secret Keys..."),
0362             QString(),
0363             "view-certificate-export-secret",
0364             nullptr,
0365             nullptr,
0366             QString(),
0367         },
0368         {
0369             "file_export_paper_key",
0370             i18n("Print Secret Key..."),
0371             QString(),
0372             "document-print",
0373             nullptr,
0374             nullptr,
0375             QString(),
0376         },
0377         {
0378             "file_lookup_certificates",
0379             i18n("Lookup on Server..."),
0380             i18n("Search for certificates online using a public keyserver"),
0381             "edit-find",
0382             nullptr,
0383             nullptr,
0384             QStringLiteral("Shift+Ctrl+I"),
0385         },
0386         {
0387             "file_import_certificates",
0388             i18n("Import..."),
0389             i18n("Import a certificate from a file"),
0390             "view-certificate-import",
0391             nullptr,
0392             nullptr,
0393             QStringLiteral("Ctrl+I"),
0394         },
0395         {
0396             "file_decrypt_verify_files",
0397             i18n("Decrypt/Verify..."),
0398             i18n("Decrypt and/or verify files"),
0399             "document-edit-decrypt-verify",
0400             nullptr,
0401             nullptr,
0402             QString(),
0403         },
0404         {
0405             "file_sign_encrypt_files",
0406             i18n("Sign/Encrypt..."),
0407             i18n("Encrypt and/or sign files"),
0408             "document-edit-sign-encrypt",
0409             nullptr,
0410             nullptr,
0411             QString(),
0412         },
0413         {
0414             "file_sign_encrypt_folder",
0415             i18n("Sign/Encrypt Folder..."),
0416             i18n("Encrypt and/or sign folders"),
0417             nullptr /*"folder-edit-sign-encrypt"*/,
0418             nullptr,
0419             nullptr,
0420             QString(),
0421         },
0422         {
0423             "file_checksum_create_files",
0424             i18n("Create Checksum Files..."),
0425             QString(),
0426             nullptr /*"document-checksum-create"*/,
0427             nullptr,
0428             nullptr,
0429             QString(),
0430         },
0431         {
0432             "file_checksum_verify_files",
0433             i18n("Verify Checksum Files..."),
0434             QString(),
0435             nullptr /*"document-checksum-verify"*/,
0436             nullptr,
0437             nullptr,
0438             QString(),
0439         },
0440         // View menu
0441         {
0442             "view_redisplay",
0443             i18n("Redisplay"),
0444             QString(),
0445             "view-refresh",
0446             nullptr,
0447             nullptr,
0448             QStringLiteral("F5"),
0449         },
0450         {
0451             "view_stop_operations",
0452             i18n("Stop Operation"),
0453             QString(),
0454             "process-stop",
0455             this,
0456             [this](bool) {
0457                 cancelCommands();
0458             },
0459             QStringLiteral("Escape"),
0460             RegularQAction,
0461             Disabled,
0462         },
0463         {
0464             "view_certificate_details",
0465             i18n("Details"),
0466             QString(),
0467             "dialog-information",
0468             nullptr,
0469             nullptr,
0470             QString(),
0471         },
0472         // Certificate menu
0473         {
0474             "certificates_revoke",
0475             i18n("Revoke Certificate..."),
0476             i18n("Revoke the selected OpenPGP certificate"),
0477             "view-certificate-revoke",
0478             nullptr,
0479             nullptr,
0480             {},
0481         },
0482         {
0483             "certificates_delete",
0484             i18n("Delete"),
0485             i18n("Delete selected certificates"),
0486             "edit-delete",
0487             nullptr,
0488             nullptr,
0489             QStringLiteral("Delete"),
0490         },
0491         {
0492             "certificates_certify_certificate",
0493             i18n("Certify..."),
0494             i18n("Certify the validity of the selected certificate"),
0495             "view-certificate-sign",
0496             nullptr,
0497             nullptr,
0498             QString(),
0499         },
0500         {
0501             "certificates_revoke_certification",
0502             i18n("Revoke Certification..."),
0503             i18n("Revoke the certification of the selected certificate"),
0504             "view-certificate-revoke",
0505             nullptr,
0506             nullptr,
0507             QString(),
0508         },
0509         {
0510             "certificates_change_expiry",
0511             i18n("Change End of Validity Period..."),
0512             QString(),
0513             nullptr,
0514             nullptr,
0515             nullptr,
0516             QString(),
0517         },
0518         {
0519             "certificates_change_owner_trust",
0520             i18nc("@action:inmenu", "Change Certification Power..."),
0521             i18nc("@info:tooltip", "Grant or revoke the certification power of the selected certificate"),
0522             nullptr,
0523             nullptr,
0524             nullptr,
0525             QString(),
0526         },
0527         {
0528             "certificates_change_passphrase",
0529             i18n("Change Passphrase..."),
0530             QString(),
0531             nullptr,
0532             nullptr,
0533             nullptr,
0534             QString(),
0535         },
0536         {
0537             "certificates_add_userid",
0538             i18n("Add User ID..."),
0539             QString(),
0540             nullptr,
0541             nullptr,
0542             nullptr,
0543             QString(),
0544         },
0545         // Tools menu
0546         {
0547             "tools_refresh_openpgp_certificates",
0548             i18n("Refresh OpenPGP Certificates"),
0549             QString(),
0550             "view-refresh",
0551             nullptr,
0552             nullptr,
0553             QString(),
0554         },
0555         // Window menu
0556         // (come from TabWidget)
0557         // Help menu
0558         // (come from MainWindow)
0559     };
0560 
0561     static const action_data cms_create_csr_action_data = {
0562         "file_new_certificate_signing_request",
0563         i18n("New S/MIME Certification Request..."),
0564         i18n("Create a new S/MIME certificate signing request (CSR)"),
0565         "view-certificate-add",
0566         nullptr,
0567         nullptr,
0568         {},
0569     };
0570 
0571     static const std::vector<action_data> cms_action_data = {
0572         // Certificate menu
0573         {
0574             "certificates_trust_root",
0575             i18n("Trust Root Certificate"),
0576             QString(),
0577             nullptr,
0578             nullptr,
0579             nullptr,
0580             QString(),
0581         },
0582         {
0583             "certificates_distrust_root",
0584             i18n("Distrust Root Certificate"),
0585             QString(),
0586             nullptr,
0587             nullptr,
0588             nullptr,
0589             QString(),
0590         },
0591         {
0592             "certificates_dump_certificate",
0593             i18n("Technical Details"),
0594             QString(),
0595             nullptr,
0596             nullptr,
0597             nullptr,
0598             QString(),
0599         },
0600         // Tools menu
0601         {
0602             "tools_refresh_x509_certificates",
0603             i18n("Refresh S/MIME Certificates"),
0604             QString(),
0605             "view-refresh",
0606             nullptr,
0607             nullptr,
0608             QString(),
0609         },
0610         {
0611             "crl_clear_crl_cache",
0612             i18n("Clear CRL Cache"),
0613             QString(),
0614             nullptr,
0615             nullptr,
0616             nullptr,
0617             QString(),
0618         },
0619         {
0620             "crl_dump_crl_cache",
0621             i18n("Dump CRL Cache"),
0622             QString(),
0623             nullptr,
0624             nullptr,
0625             nullptr,
0626             QString(),
0627         },
0628         {
0629             "crl_import_crl",
0630             i18n("Import CRL From File..."),
0631             QString(),
0632             nullptr,
0633             nullptr,
0634             nullptr,
0635             QString(),
0636         },
0637     };
0638 
0639     std::vector<action_data> action_data = common_and_openpgp_action_data;
0640 
0641     if (const Kleo::Settings settings{}; settings.cmsEnabled()) {
0642         if (settings.cmsCertificateCreationAllowed()) {
0643             action_data.push_back(cms_create_csr_action_data);
0644         }
0645         action_data.reserve(action_data.size() + cms_action_data.size());
0646         std::copy(std::begin(cms_action_data), std::end(cms_action_data), std::back_inserter(action_data));
0647     }
0648 
0649     make_actions_from_data(action_data, coll);
0650 
0651     if (QAction *action = coll->action(QStringLiteral("view_stop_operations"))) {
0652         connect(this, &KeyListController::commandsExecuting, action, &QAction::setEnabled);
0653     }
0654 
0655     // ### somehow make this better...
0656     registerActionForCommand<NewOpenPGPCertificateCommand>(coll->action(QStringLiteral("file_new_certificate")));
0657     registerActionForCommand<NewCertificateSigningRequestCommand>(coll->action(QStringLiteral("file_new_certificate_signing_request")));
0658     //---
0659     registerActionForCommand<LookupCertificatesCommand>(coll->action(QStringLiteral("file_lookup_certificates")));
0660     registerActionForCommand<ImportCertificateFromFileCommand>(coll->action(QStringLiteral("file_import_certificates")));
0661     //---
0662     registerActionForCommand<ExportCertificateCommand>(coll->action(QStringLiteral("file_export_certificates")));
0663     registerActionForCommand<ExportSecretKeyCommand>(coll->action(QStringLiteral("file_export_secret_keys")));
0664     registerActionForCommand<ExportPaperKeyCommand>(coll->action(QStringLiteral("file_export_paper_key")));
0665     registerActionForCommand<ExportOpenPGPCertsToServerCommand>(coll->action(QStringLiteral("file_export_certificates_to_server")));
0666 #ifdef MAILAKONADI_ENABLED
0667     registerActionForCommand<ExportOpenPGPCertToProviderCommand>(coll->action(QStringLiteral("file_export_certificate_to_provider")));
0668 #endif // MAILAKONADI_ENABLED
0669     //---
0670     registerActionForCommand<DecryptVerifyFilesCommand>(coll->action(QStringLiteral("file_decrypt_verify_files")));
0671     registerActionForCommand<SignEncryptFilesCommand>(coll->action(QStringLiteral("file_sign_encrypt_files")));
0672     registerActionForCommand<SignEncryptFolderCommand>(coll->action(QStringLiteral("file_sign_encrypt_folder")));
0673     //---
0674     registerActionForCommand<ChecksumCreateFilesCommand>(coll->action(QStringLiteral("file_checksum_create_files")));
0675     registerActionForCommand<ChecksumVerifyFilesCommand>(coll->action(QStringLiteral("file_checksum_verify_files")));
0676 
0677     registerActionForCommand<ReloadKeysCommand>(coll->action(QStringLiteral("view_redisplay")));
0678     // coll->action( "view_stop_operations" ) <-- already dealt with in make_actions_from_data()
0679     registerActionForCommand<DetailsCommand>(coll->action(QStringLiteral("view_certificate_details")));
0680 
0681     registerActionForCommand<ChangeOwnerTrustCommand>(coll->action(QStringLiteral("certificates_change_owner_trust")));
0682     registerActionForCommand<TrustRootCommand>(coll->action(QStringLiteral("certificates_trust_root")));
0683     registerActionForCommand<DistrustRootCommand>(coll->action(QStringLiteral("certificates_distrust_root")));
0684     //---
0685     registerActionForCommand<CertifyCertificateCommand>(coll->action(QStringLiteral("certificates_certify_certificate")));
0686     if (RevokeCertificationCommand::isSupported()) {
0687         registerActionForCommand<RevokeCertificationCommand>(coll->action(QStringLiteral("certificates_revoke_certification")));
0688     }
0689     //---
0690     registerActionForCommand<ChangeExpiryCommand>(coll->action(QStringLiteral("certificates_change_expiry")));
0691     registerActionForCommand<ChangePassphraseCommand>(coll->action(QStringLiteral("certificates_change_passphrase")));
0692     registerActionForCommand<AddUserIDCommand>(coll->action(QStringLiteral("certificates_add_userid")));
0693     //---
0694     registerActionForCommand<RevokeKeyCommand>(coll->action(QStringLiteral("certificates_revoke")));
0695     registerActionForCommand<DeleteCertificatesCommand>(coll->action(QStringLiteral("certificates_delete")));
0696     //---
0697     registerActionForCommand<DumpCertificateCommand>(coll->action(QStringLiteral("certificates_dump_certificate")));
0698 
0699     registerActionForCommand<RefreshX509CertsCommand>(coll->action(QStringLiteral("tools_refresh_x509_certificates")));
0700     registerActionForCommand<RefreshOpenPGPCertsCommand>(coll->action(QStringLiteral("tools_refresh_openpgp_certificates")));
0701     //---
0702     registerActionForCommand<ImportCrlCommand>(coll->action(QStringLiteral("crl_import_crl")));
0703     //---
0704     registerActionForCommand<ClearCrlCacheCommand>(coll->action(QStringLiteral("crl_clear_crl_cache")));
0705     registerActionForCommand<DumpCrlCacheCommand>(coll->action(QStringLiteral("crl_dump_crl_cache")));
0706 
0707     enableDisableActions(nullptr);
0708 }
0709 
0710 void KeyListController::registerAction(QAction *action, Command::Restrictions restrictions, Command *(*create)(QAbstractItemView *, KeyListController *))
0711 {
0712     if (!action) {
0713         return;
0714     }
0715     Q_ASSERT(!action->isCheckable()); // can be added later, for now, disallow
0716 
0717     const Private::action_item ai = {action, restrictions, create};
0718     connect(action, &QAction::triggered, this, [this, action]() {
0719         d->slotActionTriggered(action);
0720     });
0721     d->actions.push_back(ai);
0722 }
0723 
0724 void KeyListController::registerCommand(Command *cmd)
0725 {
0726     if (!cmd || std::binary_search(d->commands.cbegin(), d->commands.cend(), cmd)) {
0727         return;
0728     }
0729     d->addCommand(cmd);
0730     qCDebug(KLEOPATRA_LOG) << (void *)cmd;
0731     if (d->commands.size() == 1) {
0732         Q_EMIT commandsExecuting(true);
0733     }
0734 }
0735 
0736 bool KeyListController::hasRunningCommands() const
0737 {
0738     return !d->commands.empty();
0739 }
0740 
0741 bool KeyListController::shutdownWarningRequired() const
0742 {
0743     return std::any_of(d->commands.cbegin(), d->commands.cend(), std::mem_fn(&Command::warnWhenRunningAtShutdown));
0744 }
0745 
0746 // slot
0747 void KeyListController::cancelCommands()
0748 {
0749     std::for_each(d->commands.begin(), d->commands.end(), std::mem_fn(&Command::cancel));
0750 }
0751 
0752 void KeyListController::Private::connectView(QAbstractItemView *view)
0753 {
0754     connect(view, &QObject::destroyed, q, [this](QObject *obj) {
0755         slotDestroyed(obj);
0756     });
0757     connect(view, &QAbstractItemView::doubleClicked, q, [this](const QModelIndex &index) {
0758         slotDoubleClicked(index);
0759     });
0760     connect(view, &QAbstractItemView::activated, q, [this](const QModelIndex &index) {
0761         slotActivated(index);
0762     });
0763     connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, q, [this](const QItemSelection &oldSel, const QItemSelection &newSel) {
0764         slotSelectionChanged(oldSel, newSel);
0765     });
0766 
0767     view->setContextMenuPolicy(Qt::CustomContextMenu);
0768     connect(view, &QWidget::customContextMenuRequested, q, [this](const QPoint &pos) {
0769         slotContextMenu(pos);
0770     });
0771 }
0772 
0773 void KeyListController::Private::connectCommand(Command *cmd)
0774 {
0775     if (!cmd) {
0776         return;
0777     }
0778     connect(cmd, &QObject::destroyed, q, [this](QObject *obj) {
0779         slotDestroyed(obj);
0780     });
0781     connect(cmd, &Command::finished, q, [this] {
0782         slotCommandFinished();
0783     });
0784     // connect( cmd, SIGNAL(canceled()), q, SLOT(slotCommandCanceled()) );
0785     connect(cmd, &Command::progress, q, &KeyListController::progress);
0786 }
0787 
0788 void KeyListController::Private::slotDoubleClicked(const QModelIndex &idx)
0789 {
0790     QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
0791     if (!view || !std::binary_search(views.cbegin(), views.cend(), view)) {
0792         return;
0793     }
0794 
0795     if (const auto *const keyListModel = dynamic_cast<KeyListModelInterface *>(view->model())) {
0796         DetailsCommand *const c = new DetailsCommand{keyListModel->key(idx)};
0797         c->setParentWidget(parentWidget ? parentWidget : view);
0798         c->start();
0799     }
0800 }
0801 
0802 void KeyListController::Private::slotActivated(const QModelIndex &idx)
0803 {
0804     Q_UNUSED(idx)
0805     QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
0806     if (!view || !std::binary_search(views.cbegin(), views.cend(), view)) {
0807         return;
0808     }
0809 }
0810 
0811 void KeyListController::Private::slotSelectionChanged(const QItemSelection &old, const QItemSelection &new_)
0812 {
0813     Q_UNUSED(old)
0814     Q_UNUSED(new_)
0815 
0816     const QItemSelectionModel *const sm = qobject_cast<QItemSelectionModel *>(q->sender());
0817     if (!sm) {
0818         return;
0819     }
0820     q->enableDisableActions(sm);
0821 }
0822 
0823 void KeyListController::Private::slotContextMenu(const QPoint &p)
0824 {
0825     QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
0826     if (view && std::binary_search(views.cbegin(), views.cend(), view)) {
0827         Q_EMIT q->contextMenuRequested(view, view->viewport()->mapToGlobal(p));
0828     } else {
0829         qCDebug(KLEOPATRA_LOG) << "sender is not a QAbstractItemView*!";
0830     }
0831 }
0832 
0833 void KeyListController::Private::slotCommandFinished()
0834 {
0835     Command *const cmd = qobject_cast<Command *>(q->sender());
0836     if (!cmd || !std::binary_search(commands.cbegin(), commands.cend(), cmd)) {
0837         return;
0838     }
0839     qCDebug(KLEOPATRA_LOG) << (void *)cmd;
0840     if (commands.size() == 1) {
0841         Q_EMIT q->commandsExecuting(false);
0842     }
0843 }
0844 
0845 void KeyListController::enableDisableActions(const QItemSelectionModel *sm) const
0846 {
0847     const Command::Restrictions restrictionsMask = d->calculateRestrictionsMask(sm);
0848     for (const Private::action_item &ai : std::as_const(d->actions))
0849         if (ai.action) {
0850             ai.action->setEnabled(ai.restrictions == (ai.restrictions & restrictionsMask));
0851         }
0852 }
0853 
0854 static bool all_secret_are_not_owner_trust_ultimate(const std::vector<Key> &keys)
0855 {
0856     for (const Key &key : keys)
0857         if (key.hasSecret() && key.ownerTrust() == Key::Ultimate) {
0858             return false;
0859         }
0860     return true;
0861 }
0862 
0863 Command::Restrictions find_root_restrictions(const std::vector<Key> &keys)
0864 {
0865     bool trusted = false, untrusted = false;
0866     for (const Key &key : keys)
0867         if (key.isRoot())
0868             if (key.userID(0).validity() == UserID::Ultimate) {
0869                 trusted = true;
0870             } else {
0871                 untrusted = true;
0872             }
0873         else {
0874             return Command::NoRestriction;
0875         }
0876     if (trusted)
0877         if (untrusted) {
0878             return Command::NoRestriction;
0879         } else {
0880             return Command::MustBeTrustedRoot;
0881         }
0882     else if (untrusted) {
0883         return Command::MustBeUntrustedRoot;
0884     } else {
0885         return Command::NoRestriction;
0886     }
0887 }
0888 
0889 Command::Restrictions KeyListController::Private::calculateRestrictionsMask(const QItemSelectionModel *sm)
0890 {
0891     if (!sm) {
0892         return Command::NoRestriction;
0893     }
0894 
0895     const KeyListModelInterface *const m = dynamic_cast<const KeyListModelInterface *>(sm->model());
0896     if (!m) {
0897         return Command::NoRestriction;
0898     }
0899 
0900     const std::vector<Key> keys = m->keys(sm->selectedRows());
0901     if (keys.empty()) {
0902         return Command::NoRestriction;
0903     }
0904 
0905     Command::Restrictions result = Command::NeedSelection;
0906 
0907     if (keys.size() == 1) {
0908         result |= Command::OnlyOneKey;
0909     }
0910 
0911 #if GPGME_VERSION_NUMBER >= 0x011102 // 1.17.2
0912     // 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
0913     const auto primaryKeyCanBeUsedForSecretKeyOperations = [](const auto &k) {
0914         return k.subkey(0).isSecret();
0915     };
0916 #else
0917     // older versions of GpgME did not always set the secret flag for card keys
0918     const auto primaryKeyCanBeUsedForSecretKeyOperations = [](const auto &k) {
0919         return k.subkey(0).isSecret() || k.subkey(0).isCardKey();
0920     };
0921 #endif
0922     if (std::all_of(keys.cbegin(), keys.cend(), primaryKeyCanBeUsedForSecretKeyOperations)) {
0923         result |= Command::NeedSecretKey;
0924     }
0925 
0926     if (std::all_of(std::begin(keys), std::end(keys), [](const auto &k) {
0927             return k.subkey(0).isSecret() && !k.subkey(0).isCardKey();
0928         })) {
0929         result |= Command::NeedSecretKeyData;
0930     }
0931 
0932     if (std::all_of(keys.cbegin(), keys.cend(), [](const Key &key) {
0933             return key.protocol() == OpenPGP;
0934         })) {
0935         result |= Command::MustBeOpenPGP;
0936     } else if (std::all_of(keys.cbegin(), keys.cend(), [](const Key &key) {
0937                    return key.protocol() == CMS;
0938                })) {
0939         result |= Command::MustBeCMS;
0940     }
0941 
0942     if (Kleo::all_of(keys, [](const auto &key) {
0943             return !key.isBad();
0944         })) {
0945         result |= Command::MustBeValid;
0946     }
0947 
0948     if (all_secret_are_not_owner_trust_ultimate(keys)) {
0949         result |= Command::MayOnlyBeSecretKeyIfOwnerTrustIsNotYetUltimate;
0950     }
0951 
0952     result |= find_root_restrictions(keys);
0953 
0954     if (const ReaderStatus *rs = ReaderStatus::instance()) {
0955         if (!rs->firstCardWithNullPin().empty()) {
0956             result |= Command::AnyCardHasNullPin;
0957         }
0958         if (rs->anyCardCanLearnKeys()) {
0959             result |= Command::AnyCardCanLearnKeys;
0960         }
0961     }
0962 
0963     return result;
0964 }
0965 
0966 void KeyListController::Private::slotActionTriggered(QAction *sender)
0967 {
0968     const auto it = std::find_if(actions.cbegin(), actions.cend(), [sender](const action_item &item) {
0969         return item.action == sender;
0970     });
0971     if (it != actions.end())
0972         if (Command *const c = it->createCommand(this->currentView, q)) {
0973             if (parentWidget) {
0974                 c->setParentWidget(parentWidget);
0975             }
0976             c->start();
0977         } else
0978             qCDebug(KLEOPATRA_LOG) << "createCommand() == NULL for action(?) \"" << qPrintable(sender->objectName()) << "\"";
0979     else {
0980         qCDebug(KLEOPATRA_LOG) << "I don't know anything about action(?) \"%s\"", qPrintable(sender->objectName());
0981     }
0982 }
0983 
0984 int KeyListController::Private::toolTipOptions() const
0985 {
0986     using namespace Kleo::Formatting;
0987     static const int validityFlags = Validity | Issuer | ExpiryDates | CertificateUsage;
0988     static const int ownerFlags = Subject | UserIDs | OwnerTrust;
0989     static const int detailsFlags = StorageLocation | CertificateType | SerialNumber | Fingerprint;
0990 
0991     const TooltipPreferences prefs;
0992 
0993     int flags = KeyID;
0994     flags |= prefs.showValidity() ? validityFlags : 0;
0995     flags |= prefs.showOwnerInformation() ? ownerFlags : 0;
0996     flags |= prefs.showCertificateDetails() ? detailsFlags : 0;
0997     return flags;
0998 }
0999 
1000 void KeyListController::updateConfig()
1001 {
1002     const int opts = d->toolTipOptions();
1003     if (d->flatModel) {
1004         d->flatModel->setToolTipOptions(opts);
1005     }
1006     if (d->hierarchicalModel) {
1007         d->hierarchicalModel->setToolTipOptions(opts);
1008     }
1009 }
1010 
1011 #include "moc_keylistcontroller.cpp"