Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F18826160
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
47 KB
Subscribers
None
View Options
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 7e85de621..ac34a0d83 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1,1006 +1,1010 @@
/* -*- 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 "mainwindow.h"
#include "aboutdata.h"
#include "kleopatraapplication.h"
#include "settings.h"
#include <config-kleopatra.h>
#include <interfaces/focusfirstchild.h>
#include "view/keycacheoverlay.h"
#include "view/keylistcontroller.h"
#include "view/padwidget.h"
#include "view/searchbar.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 "utils/action_data.h"
#include "utils/clipboardmenu.h"
#include "utils/detail_p.h"
#include "utils/filedialog.h"
#include "utils/gui-helper.h"
#include "utils/keyexportdraghandler.h"
#include "utils/userinfo.h"
#include <Libkleo/GnuPG>
#include "dialogs/debugdialog.h"
#include "dialogs/updatenotification.h"
// needed for GPGME_VERSION_NUMBER
#include <gpgme.h>
#include "kleopatra_debug.h"
#include <KAboutApplicationDialog>
#include <KAboutData>
#include <KActionCollection>
#include <KActionMenu>
#include <KColorScheme>
#include <KColorSchemeManager>
#include <KColorSchemeMenu>
#include <KConfigDialog>
#include <KConfigGroup>
#include <KEditToolBar>
#include <KHelpMenu>
#include <KLocalizedString>
#include <KMessageBox>
#include <KShortcutsDialog>
#include <KStandardAction>
#include <KStandardGuiItem>
#include <KToolBar>
#include <KXMLGUIFactory>
#include <QAction>
#include <QApplication>
#include <QLineEdit>
#include <QSize>
#include <QAbstractItemView>
#include <QCloseEvent>
#include <QDesktopServices>
#include <QDir>
#include <QLabel>
#include <QMenu>
#include <QMimeData>
#include <QPixmap>
#include <QProcess>
#include <QSettings>
#include <QStackedWidget>
#include <QStatusBar>
#include <QTimer>
#include <QVBoxLayout>
#include <Libkleo/Classify>
#include <Libkleo/Compliance>
#include <Libkleo/DocAction>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/Stl_Util>
#include <Libkleo/SystemInfo>
#include <KSharedConfig>
#if __has_include(<KWaylandExtras>)
#include <KWaylandExtras>
#define HAVE_WAYLAND
#endif
#include <chrono>
#include <vector>
using namespace std::chrono_literals;
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
using namespace Qt::Literals::StringLiterals;
static KGuiItem KStandardGuiItem_quit()
{
static const QString app = KAboutData::applicationData().displayName();
KGuiItem item = KStandardGuiItem::quit();
item.setText(xi18nc("@action:button", "&Quit <application>%1</application>", 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<QString> mainViewActionNames = {
QStringLiteral("view_certificate_overview"),
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{KeyTreeView::Option::NoDefaultContextMenu, q};
vbox->addWidget(tabWidget);
tabWidget->connectSearchBar(searchBar);
}
} ui;
};
}
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()
{
if (Kleo::userIsElevated()) {
// For users running Kleo with elevated permissions on Windows we
// always quit the application to avoid some problems.
qApp->quit();
}
const QString app = KAboutData::applicationData().displayName();
const int rc = KMessageBox::questionTwoActionsCancel(q,
xi18nc("@info",
"<application>%1</application> may be used by other applications as a service.<nl/>"
"You may instead want to close this window without exiting <application>%1</application>.",
app),
i18nc("@title:window", "Really Quit?"),
KStandardGuiItem_close(),
KStandardGuiItem_quit(),
KStandardGuiItem::cancel(),
QLatin1StringView("really-quit-") + app.toLower());
if (rc == KMessageBox::Cancel) {
return;
}
isQuitting = rc == KMessageBox::ButtonCode::SecondaryAction;
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(QKeySequence::NativeText);
ui.searchTab->searchBar()->updateClickMessage(shortcutStr);
}
void updateStatusBar()
{
auto statusBar = std::make_unique<QStatusBar>();
auto settings = KleopatraApplication::instance()->distributionSettings();
bool showStatusbar = false;
if (settings) {
const QString statusline = settings->value(QStringLiteral("statusline"), {}).toString();
if (!statusline.isEmpty()) {
auto customStatusLbl = new QLabel(statusline);
statusBar->insertWidget(0, customStatusLbl);
showStatusbar = true;
}
}
if (DeVSCompliance::isActive()) {
auto statusLbl = std::make_unique<QLabel>(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(), background.name()));
}
statusBar->insertPermanentWidget(0, statusLbl.release());
showStatusbar = true;
}
if (showStatusbar) {
q->setStatusBar(statusBar.release()); // QMainWindow takes ownership
} else {
q->setStatusBar(nullptr);
}
}
void selfTest()
{
createAndStart<SelfTestCommand>();
}
void configureGroups()
{
// open groups config dialog as independent top-level window
KleopatraApplication::instance()->openOrRaiseGroupsConfigDialog(nullptr);
}
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<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.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<Kleo::FocusFirstChild *>(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 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::restartGpgAgent();
}
void showAboutDialog()
{
// we show the About dialog ourselves so that we can pass up-to-date about data to it;
// KXmlGuiWindow takes a copy of the about data on creation and this copy might not
// contain the backend version information that's set by a background thread
if (!aboutDialog) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Creating About dialog";
aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData(), q);
aboutDialog->setAttribute(Qt::WA_DeleteOnClose);
}
if (aboutDialog->isMinimized()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Unminimizing About dialog";
aboutDialog->setWindowState((aboutDialog->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
}
qCDebug(KLEOPATRA_LOG) << __func__ << "Showing About dialog";
aboutDialog->show();
}
private:
void setupActions();
QAbstractItemView *currentView() const
{
return ui.searchTab->tabWidget()->currentView();
}
void keyListingDone()
{
const auto curWidget = ui.stackWidget->currentWidget();
if (curWidget == ui.padWidget) {
return;
}
showCertificateView();
}
private:
Kleo::KeyListController controller;
bool firstShow : 1;
struct UI {
CertificateView *searchTab = nullptr;
PadWidget *padWidget = nullptr;
WelcomeWidget *welcomeWidget = nullptr;
QStackedWidget *stackWidget = nullptr;
explicit UI(MainWindow *q);
} ui;
QAction *focusToClickSearchAction = nullptr;
ClipboardMenu *clipboadMenu = nullptr;
QPointer<KAboutApplicationDialog> aboutDialog;
};
MainWindow::Private::UI::UI(MainWindow *q)
: padWidget(nullptr)
{
auto mainWidget = new QWidget{q};
auto mainLayout = new QVBoxLayout(mainWidget);
mainLayout->setContentsMargins({});
stackWidget = new QStackedWidget{q};
searchTab = new CertificateView{q};
stackWidget->addWidget(searchTab);
new KeyCacheOverlay(mainWidget, q);
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)
{
Q_SET_OBJECT_NAME(controller);
AbstractKeyListModel *flatModel = AbstractKeyListModel::createFlatKeyListModel(q);
AbstractKeyListModel *hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel(q);
Q_SET_OBJECT_NAME(flatModel);
Q_SET_OBJECT_NAME(hierarchicalModel);
// check for GpgME >= 1.24.0
#if GPGME_VERSION_NUMBER >= 0x011800 && !defined(Q_OS_WIN)
auto keyExportDragHandler = std::make_shared<KeyExportDragHandler>();
flatModel->setDragHandler(keyExportDragHandler);
hierarchicalModel->setDragHandler(keyExportDragHandler);
#endif
controller.setFlatModel(flatModel);
controller.setHierarchicalModel(hierarchicalModel);
controller.setTabWidget(ui.searchTab->tabWidget());
ui.searchTab->tabWidget()->setFlatModel(flatModel);
ui.searchTab->tabWidget()->setHierarchicalModel(hierarchicalModel);
#ifdef HAVE_WAYLAND
connect(KWaylandExtras::self(), &KWaylandExtras::windowExported, q, [this](const auto &window, const auto &token) {
if (window == q->windowHandle()) {
qputenv("PINENTRY_GEOM_HINT", QUrl::toPercentEncoding(token));
}
});
QMetaObject::invokeMethod(
q,
[this]() {
q->exportWindow();
},
Qt::QueuedConnection);
#endif
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"));
if (auto helpMenu = q->findChild<KHelpMenu *>()) {
qCDebug(KLEOPATRA_LOG) << "Hook into the help menu to show the About dialog ourselves";
connect(helpMenu, &KHelpMenu::showAboutApplication, q, [this]() {
showAboutDialog();
});
}
// make toolbar buttons accessible by keyboard
auto toolbar = q->findChild<KToolBar *>();
if (toolbar) {
const auto toolbarButtons = toolbar->findChildren<QToolButton *>();
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<QWidget *>();
std::for_each(std::rbegin(toolbarChildren), std::rend(toolbarChildren), [toolbar](auto w) {
forceSetTabOrder(toolbar, w);
});
}
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();
}
// delay setting the models to use the key cache so that the UI (including
// the "Loading certificate cache..." overlay) is shown before the
// blocking key cache initialization happens
QMetaObject::invokeMethod(
q,
[flatModel, hierarchicalModel]() {
flatModel->useKeyCache(true, KeyList::AllKeys);
hierarchicalModel->useKeyCache(true, KeyList::AllKeys);
},
Qt::QueuedConnection);
}
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 = {
// see keylistcontroller.cpp for more actions
// Tools menu
#ifndef Q_OS_WIN
{
"tools_start_kwatchgnupg",
i18n("GnuPG Log Viewer"),
QString(),
"org.kde.kwatchgnupg",
q,
[this](bool) {
gnupgLogViewer();
},
QString(),
},
#endif
{
"tools_debug_view",
i18n("Show GnuPG Configuration"),
QString(),
"",
q,
[this](bool) {
auto dialog = new DebugDialog(q);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->open();
},
QString(),
},
{
"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,
[](bool) {
KleopatraApplication::instance()->openOrRaiseSmartCardWindow();
},
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(),
},
// Toolbar
{
"configure_groups_toolbar",
i18nc("@action:intoolbar", "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;
}
if (auto action = coll->action(QStringLiteral("configure_groups_toolbar"))) {
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);
auto manager = KColorSchemeManager::instance();
KActionMenu *schemeMenu = KColorSchemeMenu::createMenu(manager, q);
coll->addAction(QStringLiteral("colorscheme_menu"), schemeMenu->menu()->menuAction());
focusToClickSearchAction = new QAction(i18nc("@action", "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{u":/gpg4win/gpg4win-compact"_s},
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"),
QUrl(),
coll);
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("&Quick Guide Encrypt and Sign"),
i18nc("Only available in German and English. Leave to English for other languages.", "encrypt_and_sign_gnupgvsd_en.pdf"),
QStringLiteral("../share/doc/gnupg-vsd"),
QUrl(),
coll);
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"),
- QUrl(),
- coll);
- coll->addAction(QStringLiteral("help_doc_symenc"), symguide);
+ coll->addAction(QStringLiteral("help_doc_symenc"), createSymmetricGuideAction(coll).release());
const auto groups = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")),
i18n("Certificate &Groups"),
i18nc("Only available in German and English. Leave to English for other languages.", "groupfeature_gnupgvsd_en.pdf"),
QStringLiteral("../share/doc/gnupg-vsd"),
QUrl(),
coll);
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"),
QUrl(),
coll);
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"),
QUrl(),
coll);
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"),
QUrl(),
coll);
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"),
QUrl(QStringLiteral("https://gnupg.org/documentation/manuals/gnupg/")),
coll);
coll->addAction(QStringLiteral("help_doc_gnupg"), man_gnupg);
/* The secops */
const auto approvalmanual =
new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")),
i18n("Manual for VS-NfD Approval"),
i18nc("Only available in German and English. Keep the English file name for other languages.", "handbuch_zulassung_gnupgvsd_en.pdf"),
QStringLiteral("../share/doc/gnupg-vsd"),
QUrl(),
coll);
coll->addAction(QStringLiteral("help_doc_approval_manual"), approvalmanual);
const auto vsa =
new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")),
i18n("SecOps for VS-NfD Approval"),
i18nc("Only available in German and English. Keep the English file name for other languages.", "BSI-VSA-10867_ENG_secops.pdf"),
QStringLiteral("../share/doc/gnupg-vsd"),
QUrl(),
coll);
coll->addAction(QStringLiteral("help_doc_vsa"), vsa);
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);
}
}
unexportWindow();
if (isQuitting || qApp->isSavingSession() || Kleo::userIsElevated()) {
d->ui.searchTab->tabWidget()->saveViews();
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::openStateConfig(), QStringLiteral("KeyList"));
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 (e->source()) {
// The event comes from kleopatra itself; we don't want to import in this case.
return;
}
if (!can_decode_local_files(e->mimeData())) {
return;
}
e->setDropAction(Qt::CopyAction);
const QStringList files = extract_local_files(e->mimeData());
KleopatraApplication::instance()->handleFiles(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());
}
void MainWindow::exportWindow()
{
#ifdef HAVE_WAYLAND
(void)winId(); // Ensures that windowHandle() returns the window
KWaylandExtras::self()->exportWindow(windowHandle());
#endif
}
void MainWindow::unexportWindow()
{
#ifdef HAVE_WAYLAND
KWaylandExtras::self()->unexportWindow(windowHandle());
#endif
}
KeyListController *MainWindow::keyListController()
{
return &d->controller;
}
+std::unique_ptr<DocAction> MainWindow::createSymmetricGuideAction(QObject *parent)
+{
+ return std::make_unique<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"),
+ QUrl(),
+ parent);
+}
+
#include "mainwindow.moc"
#include "moc_mainwindow.cpp"
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 7469a0e56..269defb18 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -1,59 +1,62 @@
/* -*- mode: c++; c-basic-offset:4 -*-
mainwindow.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <KXmlGuiWindow>
#include <memory>
namespace Kleo
{
class KeyListController;
+class DocAction;
}
class MainWindow : public KXmlGuiWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr, Qt::WindowFlags f = {});
~MainWindow() override;
Kleo::KeyListController *keyListController();
+ static std::unique_ptr<Kleo::DocAction> createSymmetricGuideAction(QObject *parent);
+
public Q_SLOTS:
void importCertificatesFromFile(const QStringList &files);
void exportWindow();
void unexportWindow();
protected:
QByteArray savedGeometry;
void closeEvent(QCloseEvent *e) override;
void showEvent(QShowEvent *e) override;
void hideEvent(QHideEvent *e) override;
void dragEnterEvent(QDragEnterEvent *) override;
void dropEvent(QDropEvent *) override;
void readProperties(const KConfigGroup &cg) override;
void saveProperties(KConfigGroup &cg) override;
private:
class Private;
const std::unique_ptr<Private> d;
Q_PRIVATE_SLOT(d, void closeAndQuit())
Q_PRIVATE_SLOT(d, void configureToolbars())
Q_PRIVATE_SLOT(d, void editKeybindings())
Q_PRIVATE_SLOT(d, void slotConfigCommitted())
Q_PRIVATE_SLOT(d, void slotContextMenuRequested(QAbstractItemView *, QPoint))
Q_PRIVATE_SLOT(d, void slotFocusQuickSearch())
Q_PRIVATE_SLOT(d, void showPadView())
};
diff --git a/src/view/welcomewidget.cpp b/src/view/welcomewidget.cpp
index 2a71653dd..631279385 100644
--- a/src/view/welcomewidget.cpp
+++ b/src/view/welcomewidget.cpp
@@ -1,232 +1,272 @@
/* view/welcomewidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "welcomewidget.h"
#include "htmllabel.h"
+#include "kleopatra_debug.h"
+#include "kleopatraapplication.h"
+#include "mainwindow.h"
+
+#include <Libkleo/DocAction>
#include <KAboutData>
#include <QAction>
+#include <QDesktopServices>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QToolButton>
#include <QVBoxLayout>
#include "commands/importcertificatefromfilecommand.h"
#include "commands/newopenpgpcertificatecommand.h"
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
+#include <KXmlGuiWindow>
static const QString templ = QStringLiteral(
"<h3>%1</h3>" // Welcome
- "<p>%2<p/><p>%3</p>" // Intro + Explanation
+ "<p>%2<p/>" // Intro
+ "<p>%7</p>" // (optional) VSD Text
+ "<p>%8</p>" // (optional) Password explanation
+ "<p>%3</p>" // Explanation
"<ul><li>%4</li><li>%5</li></ul>" //
"<p>%6</p>" // More info
"");
using namespace Kleo;
namespace
{
/**
* A tool button that can be activated with the Enter and Return keys additionally to the Space key.
*/
class ToolButton : public QToolButton
{
Q_OBJECT
public:
using QToolButton::QToolButton;
protected:
void keyPressEvent(QKeyEvent *e) override
{
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return: {
// forward as key press of Key_Select to QToolButton
QKeyEvent alternateEvent{e->type(),
Qt::Key_Select,
e->modifiers(),
e->nativeScanCode(),
e->nativeVirtualKey(),
e->nativeModifiers(),
e->text(),
e->isAutoRepeat(),
static_cast<ushort>(e->count())};
QToolButton::keyPressEvent(&alternateEvent);
if (!alternateEvent.isAccepted()) {
e->ignore();
}
break;
}
default:
QToolButton::keyPressEvent(e);
}
}
void keyReleaseEvent(QKeyEvent *e) override
{
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return: {
// forward as key release of Key_Select to QToolButton
QKeyEvent alternateEvent{e->type(),
Qt::Key_Select,
e->modifiers(),
e->nativeScanCode(),
e->nativeVirtualKey(),
e->nativeModifiers(),
e->text(),
e->isAutoRepeat(),
static_cast<ushort>(e->count())};
QToolButton::keyReleaseEvent(&alternateEvent);
if (!alternateEvent.isAccepted()) {
e->ignore();
}
break;
}
default:
QToolButton::keyReleaseEvent(e);
}
}
};
}
class WelcomeWidget::Private
{
public:
Private(WelcomeWidget *qq)
: q(qq)
{
auto vLay = new QVBoxLayout(q);
auto hLay = new QHBoxLayout;
const QString welcome = i18nc("%1 is version", "Welcome to Kleopatra %1", KAboutData::applicationData().version());
const QString introduction = i18n("Kleopatra is a front-end for the crypto software <a href=\"https://gnupg.org\">GnuPG</a>.");
- const QString keyExplanation = i18n("For most actions you need either a public key (certificate) or your own private key.");
+ QString keyExplanation = i18n("For most actions you need either a public key (certificate) or your own private key.");
const QString privateKeyExplanation = i18n("The private key is needed to decrypt or sign.");
const QString publicKeyExplanation = i18n("The public key can be used by others to verify your identity or encrypt to you.");
const QString wikiUrl = i18nc("More info about public key cryptography, please link to your local version of Wikipedia",
"https://en.wikipedia.org/wiki/Public-key_cryptography");
- const QString learnMore = i18nc("%1 is link a wiki article", "You can learn more about this on <a href=\"%1\">Wikipedia</a>.", wikiUrl);
+ QString learnMore = i18nc("%1 is a link to a wiki article", "You can learn more about this on <a href=\"%1\">Wikipedia</a>.", wikiUrl);
+
+ QString vsdText;
+ QString symExplanation;
+
+ if (MainWindow::createSymmetricGuideAction(nullptr)->isEnabled()) {
+ vsdText =
+ i18nc("@info", "Kleopatra can encrypt using different methods. Please follow the regulations for classified information of your organization.");
+ symExplanation = i18nc("@info", "For password based encryption see this <a href=\"action:help_doc_symenc\">guide</a>.");
+ keyExplanation = i18nc("@info", "For public key encryption you generally have to create your own private key.");
+ learnMore =
+ i18nc("@info", "You can find step-by-step instructions for public key encryption in this <a href=\"action:help_doc_quickguide\">guide</a>.");
+ }
- const auto labelText = templ.arg(welcome).arg(introduction).arg(keyExplanation).arg(privateKeyExplanation).arg(publicKeyExplanation).arg(learnMore);
+ const auto labelText = templ.arg(welcome)
+ .arg(introduction)
+ .arg(keyExplanation)
+ .arg(privateKeyExplanation)
+ .arg(publicKeyExplanation)
+ .arg(learnMore)
+ .arg(vsdText)
+ .arg(symExplanation);
mLabel = new HtmlLabel{labelText, q};
mLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
- mLabel->setOpenExternalLinks(true);
+ connect(mLabel, &QLabel::linkActivated, q, [](const auto &link) {
+ QUrl url(link);
+ if (url.scheme() == QStringLiteral("action")) {
+ if (const auto action = KleopatraApplication::instance()->mainWindow()->action(url.path())) {
+ action->trigger();
+ } else {
+ qCWarning(KLEOPATRA_LOG) << "action" << url.path() << "not found";
+ }
+ return;
+ }
+ QDesktopServices::openUrl(url);
+ });
auto genKeyAction = new QAction(q);
genKeyAction->setText(i18n("New Key Pair..."));
genKeyAction->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-add")));
auto importAction = new QAction(q);
importAction->setText(i18n("Import..."));
importAction->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-import")));
connect(importAction, &QAction::triggered, q, [this]() {
import();
});
connect(genKeyAction, &QAction::triggered, q, [this]() {
generate();
});
mGenerateBtn = new ToolButton{q};
mGenerateBtn->setDefaultAction(genKeyAction);
mGenerateBtn->setIconSize(QSize(64, 64));
mGenerateBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
const auto generateBtnDescription = kxi18nc("@info",
"Create a new OpenPGP key pair.<nl/>"
"To create an S/MIME certificate request use "
"<interface>New S/MIME Certification Request</interface> "
"from the <interface>File</interface> menu instead.");
mGenerateBtn->setToolTip(generateBtnDescription.toString());
mGenerateBtn->setAccessibleDescription(generateBtnDescription.toString(Kuit::PlainText));
KConfigGroup restrictions(KSharedConfig::openConfig(), QStringLiteral("KDE Action Restrictions"));
mGenerateBtn->setEnabled(restrictions.readEntry("action/file_new_certificate", true));
mImportBtn = new ToolButton{q};
mImportBtn->setDefaultAction(importAction);
mImportBtn->setIconSize(QSize(64, 64));
mImportBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
const auto importBtnDescription = kxi18nc("@info",
"Import certificate from a file.<nl/>"
"To import from a public keyserver use <interface>Lookup on Server</interface> instead.");
mImportBtn->setToolTip(importBtnDescription.toString());
mImportBtn->setAccessibleDescription(importBtnDescription.toString(Kuit::PlainText));
mImportBtn->setEnabled(restrictions.readEntry("action/file_import_certificate", true));
auto btnLayout = new QHBoxLayout;
btnLayout->addStretch(-1);
btnLayout->addWidget(mGenerateBtn);
btnLayout->addWidget(mImportBtn);
btnLayout->addStretch(-1);
vLay->addStretch(-1);
vLay->addLayout(hLay);
vLay->addLayout(btnLayout);
vLay->addStretch(-1);
hLay->addStretch(-1);
hLay->addWidget(mLabel);
hLay->addStretch(-1);
}
void import()
{
mImportBtn->setEnabled(false);
auto cmd = new Kleo::ImportCertificateFromFileCommand();
cmd->setParentWidget(q);
QObject::connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, q, [this]() {
mImportBtn->setEnabled(true);
});
cmd->start();
}
void generate()
{
mGenerateBtn->setEnabled(false);
auto cmd = new NewOpenPGPCertificateCommand;
cmd->setParentWidget(q);
QObject::connect(cmd, &NewOpenPGPCertificateCommand::finished, q, [this]() {
mGenerateBtn->setEnabled(true);
});
cmd->start();
}
WelcomeWidget *const q;
HtmlLabel *mLabel = nullptr;
ToolButton *mGenerateBtn = nullptr;
ToolButton *mImportBtn = nullptr;
};
WelcomeWidget::WelcomeWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
void WelcomeWidget::focusFirstChild(Qt::FocusReason reason)
{
d->mLabel->setFocus(reason);
}
#include "welcomewidget.moc"
#include "moc_welcomewidget.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 23, 4:31 PM (9 h, 42 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f0/be/39917d5565b39bcde024f05a6b75
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment