Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F23020619
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
24 KB
Subscribers
None
View Options
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index fc31bdfa3..9107e171e 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1,728 +1,727 @@
/* -*- 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 <config-kleopatra.h>
#include "mainwindow.h"
#include "aboutdata.h"
#include "settings.h"
#include "view/padwidget.h"
#include "view/searchbar.h"
#include "view/tabwidget.h"
#include "view/keylistcontroller.h"
#include "view/keycacheoverlay.h"
#include "view/smartcardwidget.h"
#include "view/welcomewidget.h"
#include "commands/selftestcommand.h"
#include "commands/importcrlcommand.h"
#include "commands/importcertificatefromfilecommand.h"
#include "commands/decryptverifyfilescommand.h"
#include "commands/signencryptfilescommand.h"
#include "commands/listreaderscommand.h"
#include "conf/groupsconfigdialog.h"
#include "utils/detail_p.h"
#include <Libkleo/GnuPG>
#include "utils/action_data.h"
#include "utils/filedialog.h"
#include "utils/clipboardmenu.h"
#include "dialogs/updatenotification.h"
#include <KXMLGUIFactory>
#include <QApplication>
#include <QSize>
#include <QLineEdit>
#include <KActionMenu>
#include <KActionCollection>
#include <KLocalizedString>
#include <KStandardAction>
#include <QAction>
#include <KAboutData>
#include <KMessageBox>
#include <KStandardGuiItem>
#include <KShortcutsDialog>
#include <KEditToolBar>
#include "kleopatra_debug.h"
#include <KConfigGroup>
#include <KConfigDialog>
#include <KColorScheme>
#include <QAbstractItemView>
#include <QCloseEvent>
#include <QMenu>
#include <QTimer>
#include <QProcess>
#include <QVBoxLayout>
#include <QMimeData>
#include <QDesktopServices>
#include <QDir>
#include <QStackedWidget>
#include <QStatusBar>
#include <QLabel>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/Stl_Util>
#include <Libkleo/Classify>
#include <Libkleo/KeyCache>
#include <Libkleo/DocAction>
#include <vector>
#include <KSharedConfig>
#include <chrono>
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().componentName();
KGuiItem item = KStandardGuiItem::quit();
item.setText(i18nc("Quit [ApplicationName]", "&Quit %1", app));
return item;
}
static KGuiItem KStandardGuiItem_close()
{
KGuiItem item = KStandardGuiItem::close();
item.setText(i18n("Only &Close Window"));
return item;
}
static bool isQuitting = false;
namespace
{
static const std::vector<QString> mainViewActionNames = {
QStringLiteral("view_certificate_overview"),
QStringLiteral("manage_smartcard"),
QStringLiteral("pad_view")
};
}
class MainWindow::Private
{
friend class ::MainWindow;
MainWindow *const q;
public:
explicit Private(MainWindow *qq);
~Private();
template <typename T>
void createAndStart()
{
(new T(this->currentView(), &this->controller))->start();
}
template <typename T>
void createAndStart(QAbstractItemView *view)
{
(new T(view, &this->controller))->start();
}
template <typename T>
void createAndStart(const QStringList &a)
{
(new T(a, this->currentView(), &this->controller))->start();
}
template <typename T>
void createAndStart(const QStringList &a, QAbstractItemView *view)
{
(new T(a, view, &this->controller))->start();
}
void closeAndQuit()
{
const QString app = KAboutData::applicationData().componentName();
const int rc = KMessageBox::questionYesNoCancel(q,
i18n("%1 may be used by other applications as a service.\n"
"You may instead want to close this window without exiting %1.", app),
i18n("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::No) {
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.searchBar->updateClickMessage(shortcutStr);
}
void updateStatusBar()
{
if (Kleo::gnupgUsesDeVsCompliance()) {
auto statusBar = std::make_unique<QStatusBar>();
auto statusLbl = std::make_unique<QLabel>(Formatting::deVsString(Kleo::gnupgIsDeVsCompliant()));
const auto color = KColorScheme(QPalette::Active, KColorScheme::View).foreground(
Kleo::gnupgIsDeVsCompliant() ? KColorScheme::NormalText: KColorScheme::NegativeText
).color();
const auto background = KColorScheme(QPalette::Active, KColorScheme::View).background(
Kleo::gnupgIsDeVsCompliant() ? 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<SelfTestCommand>();
}
void configureGroups()
{
if (KConfigDialog::showDialog(GroupsConfigDialog::dialogName())) {
return;
}
KConfigDialog *dialog = new GroupsConfigDialog(q);
dialog->show();
}
void showHandbook();
void gnupgLogViewer()
{
const QString exec =
QStandardPaths::findExecutable(QStringLiteral("kwatchgnupg"));
if (exec.isEmpty()) {
KMessageBox::error(
q,
i18n("Could not start the GnuPG Log Viewer (kwatchgnupg). "
"Please check your installation."),
i18n("Error Starting KWatchGnuPG"));
} else {
QProcess::startDetached(QStringLiteral("kwatchgnupg"), QStringList());
}
}
void forceUpdateCheck()
{
UpdateNotification::forceUpdateCheck(q);
}
void slotConfigCommitted();
void slotContextMenuRequested(QAbstractItemView *, const QPoint &p)
{
if (auto const menu = qobject_cast<QMenu *>(q->factory()->container(QStringLiteral("listview_popup"), q))) {
menu->exec(p);
} else {
qCDebug(KLEOPATRA_LOG) << "no \"listview_popup\" <Menu> in kleopatra's ui.rc file";
}
}
void slotFocusQuickSearch()
{
ui.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);
}
void showCertificateView()
{
showView(QStringLiteral("view_certificate_overview"),
KeyCache::instance()->keys().empty() ? ui.welcomeWidget : 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 listSmartcardReaders()
{
auto command = new ListReadersCommand(q);
command->start();
}
void restartDaemons()
{
Kleo::killDaemons();
}
private:
void setupActions();
QAbstractItemView *currentView() const
{
return ui.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 {
QWidget *searchTab;
TabWidget tabWidget;
SearchBar *searchBar;
PadWidget *padWidget;
SmartCardWidget *scWidget;
WelcomeWidget *welcomeWidget;
QStackedWidget *stackWidget;
explicit UI(MainWindow *q);
} ui;
QAction *focusToClickSearchAction;
ClipboardMenu *clipboadMenu;
};
MainWindow::Private::UI::UI(MainWindow *q)
: tabWidget(q), padWidget(nullptr)
{
KDAB_SET_OBJECT_NAME(tabWidget);
searchTab = new QWidget;
auto vbox = new QVBoxLayout(searchTab);
vbox->setSpacing(0);
searchBar = new SearchBar;
vbox->addWidget(searchBar);
tabWidget.connectSearchBar(searchBar);
vbox->addWidget(&tabWidget);
auto mainWidget = new QWidget;
auto mainLayout = new QVBoxLayout(mainWidget);
stackWidget = new QStackedWidget;
mainLayout->addWidget(stackWidget);
stackWidget->addWidget(searchTab);
new KeyCacheOverlay(mainWidget, q);
scWidget = new SmartCardWidget();
stackWidget->addWidget(scWidget);
welcomeWidget = new WelcomeWidget();
stackWidget->addWidget(welcomeWidget);
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);
AbstractKeyListModel *hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel(q);
KDAB_SET_OBJECT_NAME(flatModel);
KDAB_SET_OBJECT_NAME(hierarchicalModel);
controller.setFlatModel(flatModel);
controller.setHierarchicalModel(hierarchicalModel);
controller.setTabWidget(&ui.tabWidget);
ui.tabWidget.setFlatModel(flatModel);
ui.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"));
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> action_data = {
- // most have been MOVED TO keylistcontroller.cpp
+ // see keylistcontroller.cpp for more actions
// Tools menu
#ifndef Q_OS_WIN
{
"tools_start_kwatchgnupg", i18n("GnuPG Log Viewer"), QString(),
"kwatchgnupg", q, SLOT(gnupgLogViewer()), QString(), false, true
},
#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, SLOT(restartDaemons()), {}, false, true
},
+ // Help menu
#ifdef Q_OS_WIN
{
"help_check_updates", i18n("Check for updates"), QString(),
"gpg4win-compact", q, SLOT(forceUpdateCheck()), QString(), false, true
},
#endif
+ // View menu
{
"view_certificate_overview", i18nc("@action show certificate overview", "Certificates"),
i18n("Show certificate overview"), "view-certificate", q, SLOT(showCertificateView()), QString(), false, true
},
{
"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, SLOT(showPadView()), QString(), false, true
},
- // most have been MOVED TO keylistcontroller.cpp
+ {
+ "manage_smartcard", i18nc("@action show smartcard management view", "Smartcards"),
+ i18n("Show smartcard management"), "auth-sim-locked", q, SLOT(showSmartcardView()), QString(), false, true
+ },
// Settings menu
{
"settings_self_test", i18n("Perform Self-Test"), QString(),
nullptr, q, SLOT(selfTest()), QString(), false, true
},
{
"settings_list_readers", i18n("List Smartcard Readers"), QString(),
nullptr, q, SLOT(listSmartcardReaders()), QString(), false, true
},
{
"configure_groups", i18n("Configure Groups..."), QString(),
"group", q, SLOT(configureGroups()), QString(), false, true
- },
- {
- "manage_smartcard", i18nc("@action show smartcard management view", "Smartcards"),
- i18n("Show smartcard management"), "auth-sim-locked", q, SLOT(showSmartcardView()), QString(), false, true
}
-
- // most have been MOVED TO keylistcontroller.cpp
};
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 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.",
"handout_symmetric_encryption_gnupg_en.pdf"),
QStringLiteral("../share/doc/gnupg-vsd"));
coll->addAction(QStringLiteral("help_doc_symenc"), symguide);
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.",
"handout_sign_encrypt_gnupg_en.pdf"),
QStringLiteral("../share/doc/gnupg-vsd"));
coll->addAction(QStringLiteral("help_doc_quickguide"), quickguide);
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);
const auto man_gpg = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("GnuPG Manual"),
QStringLiteral("gnupg.pdf"), QStringLiteral("../share/doc/gnupg"));
coll->addAction(QStringLiteral("help_doc_gnupg"), man_gpg);
q->setStandardToolBarMenuEnabled(true);
controller.createActions(coll);
ui.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.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.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<ImportCertificateFromFileCommand>(files);
}
}
static QStringList extract_local_files(const QMimeData *data)
{
const QList<QUrl> urls = data->urls();
// begin workaround KDE/Qt misinterpretation of text/uri-list
QList<QUrl>::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"));
const QAction *const chosen = menu.exec(mapToGlobal(e->pos()));
if (!chosen) {
return;
}
if (chosen == signEncrypt) {
d->createAndStart<SignEncryptFilesCommand>(files);
} else if (chosen == decryptVerify) {
d->createAndStart<DecryptVerifyFilesCommand>(files);
} else if (chosen == importCerts) {
d->createAndStart<ImportCertificateFromFileCommand>(files);
} else if (chosen == importCRLs) {
d->createAndStart<ImportCrlCommand>(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 "moc_mainwindow.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, May 12, 6:30 PM (1 d, 16 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
78/21/5ca7da04daf7f714bca682e76021
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment