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"