Page MenuHome GnuPG

No OneTemporary

diff --git a/src/kleopatraapplication.cpp b/src/kleopatraapplication.cpp
index f2be9b338..bbbd01724 100644
--- a/src/kleopatraapplication.cpp
+++ b/src/kleopatraapplication.cpp
@@ -1,719 +1,736 @@
/*
kleopatraapplication.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 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
*/
#include <config-kleopatra.h>
#include "kleopatraapplication.h"
#include "mainwindow.h"
#include "kleopatra_options.h"
#include "systrayicon.h"
#include "settings.h"
#include "smimevalidationpreferences.h"
#include <smartcard/readerstatus.h>
#include <conf/configuredialog.h>
#include <Libkleo/GnuPG>
#include <utils/kdpipeiodevice.h>
#include <utils/log.h>
#include <gpgme++/key.h>
#include <Libkleo/Dn>
#include <Libkleo/FileSystemWatcher>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyFilterManager>
#include <Libkleo/KeyGroupConfig>
#include <Libkleo/Classify>
#ifdef HAVE_USABLE_ASSUAN
# include <uiserver/uiserver.h>
#endif
#include "commands/signencryptfilescommand.h"
#include "commands/decryptverifyfilescommand.h"
#include "commands/lookupcertificatescommand.h"
#include "commands/checksumcreatefilescommand.h"
#include "commands/checksumverifyfilescommand.h"
#include "commands/detailscommand.h"
#include "commands/newcertificatecommand.h"
#include "dialogs/updatenotification.h"
#include <KIconLoader>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <KMessageBox>
#include <KWindowSystem>
#include <QDesktopServices>
#include <QFile>
#include <QDir>
+#include <QFocusFrame>
#include <QPointer>
#include <memory>
#include <KSharedConfig>
#ifdef Q_OS_WIN
#include <QtPlatformHeaders/QWindowsWindowFunctions>
#endif
using namespace Kleo;
using namespace Kleo::Commands;
static void add_resources()
{
KIconLoader::global()->addAppDir(QStringLiteral("libkleopatra"));
KIconLoader::global()->addAppDir(QStringLiteral("kwatchgnupg"));
}
static QList<QByteArray> default_logging_options()
{
QList<QByteArray> result;
result.push_back("io");
return result;
}
class KleopatraApplication::Private
{
friend class ::KleopatraApplication;
KleopatraApplication *const q;
public:
explicit Private(KleopatraApplication *qq)
: q(qq)
, ignoreNewInstance(true)
, firstNewInstance(true)
, sysTray(nullptr)
, groupConfig{std::make_shared<KeyGroupConfig>(QStringLiteral("kleopatragroupsrc"))}
{
}
~Private() {
#ifndef QT_NO_SYSTEMTRAYICON
delete sysTray;
#endif
}
void setUpSysTrayIcon()
{
KDAB_SET_OBJECT_NAME(readerStatus);
#ifndef QT_NO_SYSTEMTRAYICON
sysTray = new SysTrayIcon();
sysTray->setFirstCardWithNullPin(readerStatus.firstCardWithNullPin());
sysTray->setAnyCardCanLearnKeys(readerStatus.anyCardCanLearnKeys());
connect(&readerStatus, &SmartCard::ReaderStatus::firstCardWithNullPinChanged,
sysTray, &SysTrayIcon::setFirstCardWithNullPin);
connect(&readerStatus, &SmartCard::ReaderStatus::anyCardCanLearnKeysChanged,
sysTray, &SysTrayIcon::setAnyCardCanLearnKeys);
#endif
}
private:
void connectConfigureDialog()
{
if (configureDialog) {
if (q->mainWindow()) {
connect(configureDialog, SIGNAL(configCommitted()),
q->mainWindow(), SLOT(slotConfigCommitted()));
}
connect(configureDialog, &ConfigureDialog::configCommitted,
q, &KleopatraApplication::configurationChanged);
}
}
void disconnectConfigureDialog()
{
if (configureDialog) {
if (q->mainWindow()) {
disconnect(configureDialog, SIGNAL(configCommitted()),
q->mainWindow(), SLOT(slotConfigCommitted()));
}
disconnect(configureDialog, &ConfigureDialog::configCommitted,
q, &KleopatraApplication::configurationChanged);
}
}
public:
bool ignoreNewInstance;
bool firstNewInstance;
+ QPointer<QFocusFrame> focusFrame;
QPointer<ConfigureDialog> configureDialog;
QPointer<MainWindow> mainWindow;
SmartCard::ReaderStatus readerStatus;
#ifndef QT_NO_SYSTEMTRAYICON
SysTrayIcon *sysTray;
#endif
std::shared_ptr<KeyGroupConfig> groupConfig;
std::shared_ptr<KeyCache> keyCache;
std::shared_ptr<Log> log;
std::shared_ptr<FileSystemWatcher> watcher;
public:
void setupKeyCache()
{
keyCache = KeyCache::mutableInstance();
keyCache->setRefreshInterval(SMimeValidationPreferences{}.refreshInterval());
watcher.reset(new FileSystemWatcher);
watcher->whitelistFiles(gnupgFileWhitelist());
watcher->addPaths(gnupgFolderWhitelist());
watcher->setDelay(1000);
keyCache->addFileSystemWatcher(watcher);
keyCache->setGroupConfig(groupConfig);
keyCache->setGroupsEnabled(Settings().groupsEnabled());
}
void setUpFilterManager()
{
if (!Settings{}.cmsEnabled()) {
KeyFilterManager::instance()->alwaysFilterByProtocol(GpgME::OpenPGP);
}
}
void setupLogging()
{
log = Log::mutableInstance();
const QByteArray envOptions = qgetenv("KLEOPATRA_LOGOPTIONS");
const bool logAll = envOptions.trimmed() == "all";
const QList<QByteArray> options = envOptions.isEmpty() ? default_logging_options() : envOptions.split(',');
const QByteArray dirNative = qgetenv("KLEOPATRA_LOGDIR");
if (dirNative.isEmpty()) {
return;
}
const QString dir = QFile::decodeName(dirNative);
const QString logFileName = QDir(dir).absoluteFilePath(QStringLiteral("kleopatra.log.%1").arg(QCoreApplication::applicationPid()));
std::unique_ptr<QFile> logFile(new QFile(logFileName));
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append)) {
qCDebug(KLEOPATRA_LOG) << "Could not open file for logging: " << logFileName << "\nLogging disabled";
return;
}
log->setOutputDirectory(dir);
if (logAll || options.contains("io")) {
log->setIOLoggingEnabled(true);
}
qInstallMessageHandler(Log::messageHandler);
#ifdef HAVE_USABLE_ASSUAN
if (logAll || options.contains("pipeio")) {
KDPipeIODevice::setDebugLevel(KDPipeIODevice::Debug);
}
UiServer::setLogStream(log->logFile());
#endif
+ }
+ void updateFocusFrame(QWidget *focusWidget)
+ {
+ if (focusWidget && focusWidget->inherits("QLabel")) {
+ if (!focusFrame) {
+ focusFrame = new QFocusFrame{focusWidget};
+ }
+ focusFrame->setWidget(focusWidget);
+ } else if (focusFrame) {
+ focusFrame->setWidget(nullptr);
+ }
}
};
KleopatraApplication::KleopatraApplication(int &argc, char *argv[])
: QApplication(argc, argv), d(new Private(this))
{
+ connect(this, &QApplication::focusChanged,
+ this, [this](QWidget *, QWidget *now) {
+ d->updateFocusFrame(now);
+ });
}
void KleopatraApplication::init()
{
#ifdef Q_OS_WIN
QWindowsWindowFunctions::setWindowActivationBehavior(
QWindowsWindowFunctions::AlwaysActivateWindow);
#endif
const auto blockedUrlSchemes = Settings{}.blockedUrlSchemes();
for (const auto &scheme : blockedUrlSchemes) {
QDesktopServices::setUrlHandler(scheme, this, "blockUrl");
}
add_resources();
DN::setAttributeOrder(Settings{}.attributeOrder());
/* Start the gpg-agent early, this is done explicitly
* because on an empty keyring our keylistings wont start
* the agent. In that case any assuan-connect calls to
* the agent will fail. The requested start via the
* connection is additionally done in case the gpg-agent
* is killed while Kleopatra is running. */
startGpgAgent();
connect(&d->readerStatus, &SmartCard::ReaderStatus::startOfGpgAgentRequested,
this, &KleopatraApplication::startGpgAgent);
d->setupKeyCache();
d->setUpSysTrayIcon();
d->setUpFilterManager();
d->setupLogging();
#ifndef QT_NO_SYSTEMTRAYICON
d->sysTray->show();
#endif
setQuitOnLastWindowClosed(false);
KWindowSystem::allowExternalProcessWindowActivation();
}
KleopatraApplication::~KleopatraApplication()
{
// main window doesn't receive "close" signal and cannot
// save settings before app exit
delete d->mainWindow;
// work around kdelibs bug https://bugs.kde.org/show_bug.cgi?id=162514
KSharedConfig::openConfig()->sync();
}
namespace
{
using Func = void (KleopatraApplication::*)(const QStringList &, GpgME::Protocol);
}
void KleopatraApplication::slotActivateRequested(const QStringList &arguments,
const QString &workingDirectory)
{
QCommandLineParser parser;
kleopatra_options(&parser);
QString err;
if (!arguments.isEmpty() && !parser.parse(arguments)) {
err = parser.errorText();
} else if (arguments.isEmpty()) {
// KDBusServices omits the application name if no other
// arguments are provided. In that case the parser prints
// a warning.
parser.parse(QStringList() << QCoreApplication::applicationFilePath());
}
if (err.isEmpty()) {
err = newInstance(parser, workingDirectory);
}
if (!err.isEmpty()) {
KMessageBox::sorry(nullptr, err.toHtmlEscaped(), i18n("Failed to execute command"));
Q_EMIT setExitValue(1);
return;
}
Q_EMIT setExitValue(0);
}
QString KleopatraApplication::newInstance(const QCommandLineParser &parser,
const QString &workingDirectory)
{
if (d->ignoreNewInstance) {
qCDebug(KLEOPATRA_LOG) << "New instance ignored because of ignoreNewInstance";
return QString();
}
QStringList files;
const QDir cwd = QDir(workingDirectory);
bool queryMode = parser.isSet(QStringLiteral("query")) || parser.isSet(QStringLiteral("search"));
// Query and Search treat positional arguments differently, see below.
if (!queryMode) {
const auto positionalArguments = parser.positionalArguments();
for (const QString &file : positionalArguments) {
// We do not check that file exists here. Better handle
// these errors in the UI.
if (QFileInfo(file).isAbsolute()) {
files << file;
} else {
files << cwd.absoluteFilePath(file);
}
}
}
GpgME::Protocol protocol = GpgME::UnknownProtocol;
if (parser.isSet(QStringLiteral("openpgp"))) {
qCDebug(KLEOPATRA_LOG) << "found OpenPGP";
protocol = GpgME::OpenPGP;
}
if (parser.isSet(QStringLiteral("cms"))) {
qCDebug(KLEOPATRA_LOG) << "found CMS";
if (protocol == GpgME::OpenPGP) {
return i18n("Ambiguous protocol: --openpgp and --cms");
}
protocol = GpgME::CMS;
}
// Check for Parent Window id
WId parentId = 0;
if (parser.isSet(QStringLiteral("parent-windowid"))) {
#ifdef Q_OS_WIN
// WId is not a portable type as it is a pointer type on Windows.
// casting it from an integer is ok though as the values are guaranteed to
// be compatible in the documentation.
parentId = reinterpret_cast<WId>(parser.value(QStringLiteral("parent-windowid")).toUInt());
#else
parentId = parser.value(QStringLiteral("parent-windowid")).toUInt();
#endif
}
// Handle openpgp4fpr URI scheme
QString needle;
if (queryMode) {
needle = parser.positionalArguments().join(QLatin1Char(' '));
}
if (needle.startsWith(QLatin1String("openpgp4fpr:"))) {
needle.remove(0, 12);
}
// Check for --search command.
if (parser.isSet(QStringLiteral("search"))) {
// This is an extra command instead of a combination with the
// similar query to avoid changing the older query commands behavior
// and query's "show details if a certificate exist or search on a
// keyserver" logic is hard to explain and use consistently.
if (needle.isEmpty()) {
return i18n("No search string specified for --search");
}
auto const cmd = new LookupCertificatesCommand(needle, nullptr);
cmd->setParentWId(parentId);
cmd->start();
return QString();
}
// Check for --query command
if (parser.isSet(QStringLiteral("query"))) {
if (needle.isEmpty()) {
return i18n("No fingerprint argument specified for --query");
}
auto cmd = Command::commandForQuery(needle);
cmd->setParentWId(parentId);
cmd->start();
return QString();
}
// Check for --gen-key command
if (parser.isSet(QStringLiteral("gen-key"))) {
auto cmd = new NewCertificateCommand(nullptr);
cmd->setParentWId(parentId);
cmd->setProtocol(protocol);
cmd->start();
return QString();
}
// Check for --config command
if (parser.isSet(QStringLiteral("config"))) {
openConfigDialogWithForeignParent(parentId);
return QString();
}
struct FuncInfo {
QString optionName;
Func func;
};
static const std::vector<FuncInfo> funcMap {
{ QStringLiteral("import-certificate"), &KleopatraApplication::importCertificatesFromFile },
{ QStringLiteral("encrypt"), &KleopatraApplication::encryptFiles },
{ QStringLiteral("sign"), &KleopatraApplication::signFiles },
{ QStringLiteral("encrypt-sign"), &KleopatraApplication::signEncryptFiles },
{ QStringLiteral("sign-encrypt"), &KleopatraApplication::signEncryptFiles },
{ QStringLiteral("decrypt"), &KleopatraApplication::decryptFiles },
{ QStringLiteral("verify"), &KleopatraApplication::verifyFiles },
{ QStringLiteral("decrypt-verify"), &KleopatraApplication::decryptVerifyFiles },
{ QStringLiteral("checksum"), &KleopatraApplication::checksumFiles },
};
QString found;
Func foundFunc = nullptr;
for (const auto &[opt, fn] : funcMap) {
if (parser.isSet(opt) && found.isEmpty()) {
found = opt;
foundFunc = fn;
} else if (parser.isSet(opt)) {
return i18n(R"(Ambiguous commands "%1" and "%2")", found, opt);
}
}
QStringList errors;
if (!found.isEmpty()) {
if (files.empty()) {
return i18n("No files specified for \"%1\" command", found);
}
qCDebug(KLEOPATRA_LOG) << "found" << found;
(this->*foundFunc)(files, protocol);
} else {
if (files.empty()) {
if (!(d->firstNewInstance && isSessionRestored())) {
qCDebug(KLEOPATRA_LOG) << "openOrRaiseMainWindow";
openOrRaiseMainWindow();
}
} else {
for (const QString& fileName : std::as_const(files)) {
QFileInfo fi(fileName);
if (!fi.isReadable()) {
errors << i18n("Cannot read \"%1\"", fileName);
}
}
const QVector<Command *> allCmds = Command::commandsForFiles(files);
for (Command *cmd : allCmds) {
if (parentId) {
cmd->setParentWId(parentId);
} else {
MainWindow *mw = mainWindow();
if (!mw) {
mw = new MainWindow;
mw->setAttribute(Qt::WA_DeleteOnClose);
setMainWindow(mw);
d->connectConfigureDialog();
}
cmd->setParentWidget(mw);
}
cmd->start();
}
}
}
d->firstNewInstance = false;
#ifdef Q_OS_WIN
// On Windows we might be started from the
// explorer in any working directory. E.g.
// a double click on a file. To avoid preventing
// the folder from deletion we set the
// working directory to the users homedir.
QDir::setCurrent(QDir::homePath());
#endif
return errors.join(QLatin1Char('\n'));
}
#ifndef QT_NO_SYSTEMTRAYICON
const SysTrayIcon *KleopatraApplication::sysTrayIcon() const
{
return d->sysTray;
}
SysTrayIcon *KleopatraApplication::sysTrayIcon()
{
return d->sysTray;
}
#endif
const MainWindow *KleopatraApplication::mainWindow() const
{
return d->mainWindow;
}
MainWindow *KleopatraApplication::mainWindow()
{
return d->mainWindow;
}
void KleopatraApplication::setMainWindow(MainWindow *mainWindow)
{
if (mainWindow == d->mainWindow) {
return;
}
d->disconnectConfigureDialog();
d->mainWindow = mainWindow;
#ifndef QT_NO_SYSTEMTRAYICON
d->sysTray->setMainWindow(mainWindow);
#endif
d->connectConfigureDialog();
}
static void open_or_raise(QWidget *w)
{
if (w->isMinimized()) {
KWindowSystem::unminimizeWindow(w->winId());
w->raise();
} else if (w->isVisible()) {
w->raise();
} else {
w->show();
}
}
void KleopatraApplication::toggleMainWindowVisibility()
{
if (mainWindow()) {
mainWindow()->setVisible(!mainWindow()->isVisible());
} else {
openOrRaiseMainWindow();
}
}
void KleopatraApplication::restoreMainWindow()
{
qCDebug(KLEOPATRA_LOG) << "restoring main window";
// Sanity checks
if (!isSessionRestored()) {
qCDebug(KLEOPATRA_LOG) << "Not in session restore";
return;
}
if (mainWindow()) {
qCDebug(KLEOPATRA_LOG) << "Already have main window";
return;
}
auto mw = new MainWindow;
if (KMainWindow::canBeRestored(1)) {
// restore to hidden state, Mainwindow::readProperties() will
// restore saved visibility.
mw->restore(1, false);
}
mw->setAttribute(Qt::WA_DeleteOnClose);
setMainWindow(mw);
d->connectConfigureDialog();
}
void KleopatraApplication::openOrRaiseMainWindow()
{
MainWindow *mw = mainWindow();
if (!mw) {
mw = new MainWindow;
mw->setAttribute(Qt::WA_DeleteOnClose);
setMainWindow(mw);
d->connectConfigureDialog();
}
open_or_raise(mw);
UpdateNotification::checkUpdate(mw);
}
void KleopatraApplication::openConfigDialogWithForeignParent(WId parentWId)
{
if (!d->configureDialog) {
d->configureDialog = new ConfigureDialog;
d->configureDialog->setAttribute(Qt::WA_DeleteOnClose);
d->connectConfigureDialog();
}
// This is similar to what the commands do.
if (parentWId) {
if (QWidget *pw = QWidget::find(parentWId)) {
d->configureDialog->setParent(pw, d->configureDialog->windowFlags());
} else {
d->configureDialog->setAttribute(Qt::WA_NativeWindow, true);
KWindowSystem::setMainWindow(d->configureDialog->windowHandle(), parentWId);
}
}
open_or_raise(d->configureDialog);
// If we have a parent we want to raise over it.
if (parentWId) {
d->configureDialog->raise();
}
}
void KleopatraApplication::openOrRaiseConfigDialog()
{
openConfigDialogWithForeignParent(0);
}
#ifndef QT_NO_SYSTEMTRAYICON
void KleopatraApplication::startMonitoringSmartCard()
{
d->readerStatus.startMonitoring();
}
#endif // QT_NO_SYSTEMTRAYICON
void KleopatraApplication::importCertificatesFromFile(const QStringList &files, GpgME::Protocol /*proto*/)
{
openOrRaiseMainWindow();
if (!files.empty()) {
mainWindow()->importCertificatesFromFile(files);
}
}
void KleopatraApplication::encryptFiles(const QStringList &files, GpgME::Protocol proto)
{
auto const cmd = new SignEncryptFilesCommand(files, nullptr);
cmd->setEncryptionPolicy(Force);
cmd->setSigningPolicy(Allow);
if (proto != GpgME::UnknownProtocol) {
cmd->setProtocol(proto);
}
cmd->start();
}
void KleopatraApplication::signFiles(const QStringList &files, GpgME::Protocol proto)
{
auto const cmd = new SignEncryptFilesCommand(files, nullptr);
cmd->setSigningPolicy(Force);
cmd->setEncryptionPolicy(Deny);
if (proto != GpgME::UnknownProtocol) {
cmd->setProtocol(proto);
}
cmd->start();
}
void KleopatraApplication::signEncryptFiles(const QStringList &files, GpgME::Protocol proto)
{
auto const cmd = new SignEncryptFilesCommand(files, nullptr);
if (proto != GpgME::UnknownProtocol) {
cmd->setProtocol(proto);
}
cmd->start();
}
void KleopatraApplication::decryptFiles(const QStringList &files, GpgME::Protocol /*proto*/)
{
auto const cmd = new DecryptVerifyFilesCommand(files, nullptr);
cmd->setOperation(Decrypt);
cmd->start();
}
void KleopatraApplication::verifyFiles(const QStringList &files, GpgME::Protocol /*proto*/)
{
auto const cmd = new DecryptVerifyFilesCommand(files, nullptr);
cmd->setOperation(Verify);
cmd->start();
}
void KleopatraApplication::decryptVerifyFiles(const QStringList &files, GpgME::Protocol /*proto*/)
{
auto const cmd = new DecryptVerifyFilesCommand(files, nullptr);
cmd->start();
}
void KleopatraApplication::checksumFiles(const QStringList &files, GpgME::Protocol /*proto*/)
{
QStringList verifyFiles, createFiles;
for (const QString &file : files) {
if (isChecksumFile(file)) {
verifyFiles << file;
} else {
createFiles << file;
}
}
if (!verifyFiles.isEmpty()) {
auto const cmd = new ChecksumVerifyFilesCommand(verifyFiles, nullptr);
cmd->start();
}
if (!createFiles.isEmpty()) {
auto const cmd = new ChecksumCreateFilesCommand(createFiles, nullptr);
cmd->start();
}
}
void KleopatraApplication::setIgnoreNewInstance(bool ignore)
{
d->ignoreNewInstance = ignore;
}
bool KleopatraApplication::ignoreNewInstance() const
{
return d->ignoreNewInstance;
}
void KleopatraApplication::blockUrl(const QUrl &url)
{
qCDebug(KLEOPATRA_LOG) << "Blocking URL" << url;
KMessageBox::sorry(mainWindow(),i18n ("Opening an external link is administratively prohibited."),
i18n ("Prohibited"));
}
void KleopatraApplication::startGpgAgent()
{
Kleo::launchGpgAgent();
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 29, 7:10 AM (13 h, 22 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
aa/81/e802fd2fdd3bda5ac01980fcd85b

Event Timeline