Page MenuHome GnuPG

No OneTemporary

diff --git a/src/kleopatraapplication.cpp b/src/kleopatraapplication.cpp
index 994b6374e..3a7bb18d0 100644
--- a/src/kleopatraapplication.cpp
+++ b/src/kleopatraapplication.cpp
@@ -1,831 +1,824 @@
/*
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 "kleopatra_options.h"
#include "mainwindow.h"
#include "settings.h"
#include "smimevalidationpreferences.h"
#include "systrayicon.h"
#include <conf/configuredialog.h>
#include <smartcard/readerstatus.h>
#include <Libkleo/GnuPG>
#include <utils/kdpipeiodevice.h>
#include <utils/log.h>
#include <gpgme++/key.h>
#include <Libkleo/Classify>
#include <Libkleo/Dn>
#include <Libkleo/FileSystemWatcher>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyFilterManager>
#include <Libkleo/KeyGroupConfig>
#include <Libkleo/SystemInfo>
#include <uiserver/uiserver.h>
#include "commands/checksumcreatefilescommand.h"
#include "commands/checksumverifyfilescommand.h"
#include "commands/decryptverifyfilescommand.h"
#include "commands/detailscommand.h"
#include "commands/importcertificatefromfilecommand.h"
#include "commands/lookupcertificatescommand.h"
#include "commands/newcertificatesigningrequestcommand.h"
#include "commands/newopenpgpcertificatecommand.h"
#include "commands/signencryptfilescommand.h"
#include "dialogs/updatenotification.h"
#include "kleopatra_debug.h"
#include <KIconLoader>
#include <KLocalizedString>
#include <KMessageBox>
#include <KWindowSystem>
#include <QDesktopServices>
#include <QDir>
#include <QFile>
#include <QFocusFrame>
#if QT_CONFIG(graphicseffect)
#include <QGraphicsEffect>
#endif
#include <QPointer>
#include <QStyleOption>
#include <QStylePainter>
#include <KSharedConfig>
#include <memory>
#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;
}
namespace
{
class FocusFrame : public QFocusFrame
{
Q_OBJECT
public:
using QFocusFrame::QFocusFrame;
protected:
void paintEvent(QPaintEvent *event) override;
};
static QRect effectiveWidgetRect(const QWidget *w)
{
// based on QWidgetPrivate::effectiveRectFor
#if QT_CONFIG(graphicseffect)
if (auto graphicsEffect = w->graphicsEffect(); graphicsEffect && graphicsEffect->isEnabled())
return graphicsEffect->boundingRectFor(w->rect()).toAlignedRect();
#endif // QT_CONFIG(graphicseffect)
return w->rect();
}
static QRect clipRect(const QWidget *w)
{
// based on QWidgetPrivate::clipRect
if (!w->isVisible()) {
return QRect();
}
QRect r = effectiveWidgetRect(w);
int ox = 0;
int oy = 0;
while (w && w->isVisible() && !w->isWindow() && w->parentWidget()) {
ox -= w->x();
oy -= w->y();
w = w->parentWidget();
r &= QRect(ox, oy, w->width(), w->height());
}
return r;
}
void FocusFrame::paintEvent(QPaintEvent *)
{
if (!widget()) {
return;
}
QStylePainter p(this);
QStyleOptionFocusRect option;
initStyleOption(&option);
const int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &option);
const int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option);
const QRect rect = clipRect(widget()).adjusted(0, 0, hmargin * 2, vmargin * 2);
p.setClipRect(rect);
p.drawPrimitive(QStyle::PE_FrameFocusRect, option);
}
}
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()
{
#ifndef QT_NO_SYSTEMTRAYICON
Q_ASSERT(readerStatus);
sysTray = new SysTrayIcon();
sysTray->setFirstCardWithNullPin(readerStatus->firstCardWithNullPin());
sysTray->setAnyCardCanLearnKeys(readerStatus->anyCardCanLearnKeys());
connect(readerStatus.get(), &SmartCard::ReaderStatus::firstCardWithNullPinChanged, sysTray, &SysTrayIcon::setFirstCardWithNullPin);
connect(readerStatus.get(), &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<FocusFrame> focusFrame;
QPointer<ConfigureDialog> configureDialog;
QPointer<MainWindow> mainWindow;
std::unique_ptr<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());
// always enable remarks (aka tags); in particular, this triggers a
// relisting of the keys with signatures and signature notations
// after the initial (fast) key listing
keyCache->enableRemarks(true);
}
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);
if (logAll || options.contains("pipeio")) {
KDPipeIODevice::setDebugLevel(KDPipeIODevice::Debug);
}
UiServer::setLogStream(log->logFile());
}
void updateFocusFrame(QWidget *focusWidget)
{
if (focusWidget && focusWidget->inherits("QLabel") && focusWidget->window()->testAttribute(Qt::WA_KeyboardFocusChange)) {
if (!focusFrame) {
focusFrame = new FocusFrame{focusWidget};
}
focusFrame->setWidget(focusWidget);
} else if (focusFrame) {
focusFrame->setWidget(nullptr);
}
}
};
KleopatraApplication::KleopatraApplication(int &argc, char *argv[])
: QApplication(argc, argv)
, d(new Private(this))
{
// disable parent<->child navigation in tree views with left/right arrow keys
// because this interferes with column by column navigation that is required
// for accessibility
setStyleSheet(QStringLiteral("QTreeView { arrow-keys-navigate-into-children: 0; }"));
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();
d->readerStatus.reset(new SmartCard::ReaderStatus);
connect(d->readerStatus.get(), &SmartCard::ReaderStatus::startOfGpgAgentRequested, this, &KleopatraApplication::startGpgAgent);
d->setupKeyCache();
d->setUpSysTrayIcon();
d->setUpFilterManager();
d->setupLogging();
-#ifdef Q_OS_WIN
- // Under Linux the Theme is set outside. On Windows we have to do
- // it ourself.
- if (SystemInfo::isDarkModeActive()) {
- QIcon::setThemeName(QStringLiteral("breeze-dark"));
- }
-#endif
#ifndef QT_NO_SYSTEMTRAYICON
d->sysTray->show();
#endif
setQuitOnLastWindowClosed(false);
// Sync config when we are about to quit
connect(this, &QApplication::aboutToQuit, this, []() {
KSharedConfig::openConfig()->sync();
});
}
KleopatraApplication::~KleopatraApplication()
{
delete d->mainWindow;
}
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::error(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"))) {
if (protocol == GpgME::CMS) {
const Kleo::Settings settings{};
if (settings.cmsEnabled() && settings.cmsCertificateCreationAllowed()) {
auto cmd = new NewCertificateSigningRequestCommand;
cmd->setParentWId(parentId);
cmd->start();
} else {
return i18n("You are not allowed to create S/MIME certificate signing requests.");
}
} else {
auto cmd = new NewOpenPGPCertificateCommand;
cmd->setParentWId(parentId);
cmd->start();
}
return QString();
}
// Check for --config command
if (parser.isSet(QStringLiteral("config"))) {
openConfigDialogWithForeignParent(parentId);
return QString();
}
struct FuncInfo {
QString optionName;
Func func;
};
// While most of these options can be handled by the content autodetection
// below it might be useful to override the autodetection if the input is in
// doubt and you e.g. only want to import .asc files or fail and not decrypt them
// if they are actually encrypted data.
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);
}
if (dynamic_cast<ImportCertificateFromFileCommand *>(cmd)) {
openOrRaiseMainWindow();
}
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)
{
#ifdef Q_OS_WIN
if (w->isMinimized()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "unminimizing and raising window";
w->raise();
} else if (w->isVisible()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "raising window";
w->raise();
#else
if (w->isVisible()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "activating window";
KWindowSystem::updateStartupId(w->windowHandle());
KWindowSystem::activateWindow(w->windowHandle());
#endif
} else {
qCDebug(KLEOPATRA_LOG) << __func__ << "showing window";
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()
{
Q_ASSERT(d->readerStatus);
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::error(mainWindow(), i18n("Opening an external link is administratively prohibited."), i18n("Prohibited"));
}
void KleopatraApplication::startGpgAgent()
{
Kleo::launchGpgAgent();
}
#include "kleopatraapplication.moc"
diff --git a/src/main.cpp b/src/main.cpp
index 3aaec3797..90e65b2c7 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,301 +1,306 @@
/*
main.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2001, 2002, 2004, 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 "aboutdata.h"
#include "kleopatraapplication.h"
#include "mainwindow.h"
#include "accessibility/accessiblewidgetfactory.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <Kdelibs4ConfigMigrator>
#endif
#include <commands/reloadkeyscommand.h>
#include <commands/selftestcommand.h>
#include "utils/kuniqueservice.h"
#include "utils/userinfo.h"
#include <Libkleo/GnuPG>
#include <utils/archivedefinition.h>
#include <uiserver/assuancommand.h>
#include <uiserver/createchecksumscommand.h>
#include <uiserver/decryptcommand.h>
#include <uiserver/decryptfilescommand.h>
#include <uiserver/decryptverifyfilescommand.h>
#include <uiserver/echocommand.h>
#include <uiserver/encryptcommand.h>
#include <uiserver/importfilescommand.h>
#include <uiserver/prepencryptcommand.h>
#include <uiserver/prepsigncommand.h>
#include <uiserver/selectcertificatecommand.h>
#include <uiserver/signcommand.h>
#include <uiserver/signencryptfilescommand.h>
#include <uiserver/uiserver.h>
#include <uiserver/verifychecksumscommand.h>
#include <uiserver/verifycommand.h>
#include <uiserver/verifyfilescommand.h>
#include <Libkleo/ChecksumDefinition>
#include "kleopatra_debug.h"
#include "kleopatra_options.h"
#include <KCrash>
#include <KLocalizedString>
#include <KMessageBox>
#include <QAccessible>
#include <QElapsedTimer>
#include <QEventLoop>
#include <QMessageBox>
#include <QTextDocument> // for Qt::escape
#include <QThreadPool>
#include <QTime>
#include <QTimer>
#include <gpgme++/error.h>
#include <gpgme++/global.h>
#include <QCommandLineParser>
#include <iostream>
#include <memory>
QElapsedTimer startupTimer;
static bool selfCheck()
{
Kleo::Commands::SelfTestCommand cmd(nullptr);
cmd.setAutoDelete(false);
cmd.setAutomaticMode(true);
QEventLoop loop;
QObject::connect(&cmd, &Kleo::Commands::SelfTestCommand::finished, &loop, &QEventLoop::quit);
QTimer::singleShot(0, &cmd, &Kleo::Command::start); // start() may Q_EMIT finished()...
loop.exec();
if (cmd.isCanceled()) {
return false;
} else {
return true;
}
}
static void fillKeyCache(Kleo::UiServer *server)
{
auto cmd = new Kleo::ReloadKeysCommand(nullptr);
QObject::connect(cmd, SIGNAL(finished()), server, SLOT(enableCryptoCommands()));
cmd->start();
}
int main(int argc, char **argv)
{
startupTimer.start();
+#ifdef Q_OS_WIN
+ // note: requires https://invent.kde.org/frameworks/kiconthemes/-/merge_requests/109
+ qputenv("QT_QPA_PLATFORM", "windows:darkmode=2");
+#endif
+
// Qt::AA_EnableHighDpiScaling must be set before QGuiApplication is constructed.
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
#endif
KleopatraApplication app(argc, argv);
// Set OrganizationDomain early as this is used to generate the service
// name that will be registered on the bus.
app.setOrganizationDomain(QStringLiteral("kde.org"));
STARTUP_TIMING << "Application created";
/* Create the unique service ASAP to prevent double starts if
* the application is started twice very quickly. */
KUniqueService service;
QObject::connect(&service, &KUniqueService::activateRequested, &app, &KleopatraApplication::slotActivateRequested);
QObject::connect(&app, &KleopatraApplication::setExitValue, &service, [&service](int i) {
service.setExitValue(i);
});
STARTUP_TIMING << "Service created";
KCrash::initialize();
QAccessible::installFactory(Kleo::accessibleWidgetFactory);
qCDebug(KLEOPATRA_LOG) << "Application created";
app.setWindowIcon(QIcon::fromTheme(QStringLiteral("kleopatra"), app.windowIcon()));
KLocalizedString::setApplicationDomain("kleopatra");
// Initialize GpgME
{
const GpgME::Error gpgmeInitError = GpgME::initializeLibrary(0);
if (gpgmeInitError) {
KMessageBox::error(nullptr,
xi18nc("@info",
"<para>The version of the <application>GpgME</application> library you are running against "
"is older than the one that the <application>GpgME++</application> library was built against.</para>"
"<para><application>Kleopatra</application> will not function in this setting.</para>"
"<para>Please ask your administrator for help in resolving this issue.</para>"),
i18nc("@title", "GpgME Too Old"));
return EXIT_FAILURE;
}
STARTUP_TIMING << "GPGME Initialized";
}
AboutData aboutData;
KAboutData::setApplicationData(aboutData);
/* This is more expensive as it sounds as it might run a verification
* on a signed Version file and initializes the whole GpgME::Engine. */
STARTUP_TIMING << "Versions checked";
if (Kleo::userIsElevated()) {
/* This is a safeguard against bugreports that something fails because
* of permission problems on windows. Some users still have the Windows
* Vista behavior of running things as Administrator. This can break
* GnuPG in horrible ways for example if a stale lockfile is left that
* can't be removed without another elevation.
*
* Note: This is not the same as running as root on Linux. Elevated means
* that you are temporarily running with the "normal" user environment but
* with elevated permissions.
* */
if (KMessageBox::warningContinueCancel(nullptr,
xi18nc("@info",
"<para><application>Kleopatra</application> cannot be run as adminstrator without "
"breaking file permissions in the GnuPG data folder.</para>"
"<para>To manage keys for other users please manage them as a normal user and "
"copy the <filename>AppData\\Roaming\\gnupg</filename> directory with proper permissions.</para>")
+ xi18n("<para>Are you sure that you want to continue?</para>"),
i18nc("@title", "Running as Administrator"))
!= KMessageBox::Continue) {
return EXIT_FAILURE;
}
qCWarning(KLEOPATRA_LOG) << "User is running with administrative permissions.";
}
// Delay init after KUniqueservice call as this might already
// have terminated us and so we can avoid overhead (e.g. keycache
// setup / systray icon).
app.init();
STARTUP_TIMING << "Application initialized";
QCommandLineParser parser;
aboutData.setupCommandLine(&parser);
kleopatra_options(&parser);
parser.process(QApplication::arguments());
aboutData.processCommandLine(&parser);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Kdelibs4ConfigMigrator migrate(QStringLiteral("kleopatra"));
migrate.setConfigFiles(QStringList() << QStringLiteral("kleopatrarc") << QStringLiteral("libkleopatrarc"));
migrate.setUiFiles(QStringList() << QStringLiteral("kleopatra.rc"));
migrate.migrate();
#endif
{
const unsigned int threads = QThreadPool::globalInstance()->maxThreadCount();
QThreadPool::globalInstance()->setMaxThreadCount(qMax(2U, threads));
}
Kleo::ChecksumDefinition::setInstallPath(Kleo::gpg4winInstallPath());
Kleo::ArchiveDefinition::setInstallPath(Kleo::gnupgInstallPath());
#ifndef DISABLE_UISERVER
int rc;
Kleo::UiServer *server = nullptr;
try {
server = new Kleo::UiServer(parser.value(QStringLiteral("uiserver-socket")));
STARTUP_TIMING << "UiServer created";
QObject::connect(server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow);
QObject::connect(server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog);
#define REGISTER(Command) server->registerCommandFactory(std::shared_ptr<Kleo::AssuanCommandFactory>(new Kleo::GenericAssuanCommandFactory<Kleo::Command>))
REGISTER(CreateChecksumsCommand);
REGISTER(DecryptCommand);
REGISTER(DecryptFilesCommand);
REGISTER(DecryptVerifyFilesCommand);
REGISTER(EchoCommand);
REGISTER(EncryptCommand);
REGISTER(EncryptFilesCommand);
REGISTER(EncryptSignFilesCommand);
REGISTER(ImportFilesCommand);
REGISTER(PrepEncryptCommand);
REGISTER(PrepSignCommand);
REGISTER(SelectCertificateCommand);
REGISTER(SignCommand);
REGISTER(SignEncryptFilesCommand);
REGISTER(SignFilesCommand);
REGISTER(VerifyChecksumsCommand);
REGISTER(VerifyCommand);
REGISTER(VerifyFilesCommand);
#undef REGISTER
server->start();
STARTUP_TIMING << "UiServer started";
} catch (const std::exception &e) {
qCDebug(KLEOPATRA_LOG) << "Failed to start UI Server: " << e.what();
#ifdef Q_OS_WIN
// We should probably change the UIServer to be only run on Windows at all because
// only the Windows Explorer Plugin uses it. But the plan of GnuPG devs as of 2022 is to
// change the Windows Explorer Plugin to use the command line and then remove the
// UiServer for everyone.
QMessageBox::information(nullptr,
i18n("GPG UI Server Error"),
i18nc("This error message is only shown on Windows when the socket to communicate with "
"Windows Explorer could not be created. This often times means that the whole installation is "
"buggy. e.g. GnuPG is not installed at all.",
"<qt>The Kleopatra Windows Explorer Module could not be initialized.<br/>"
"The error given was: <b>%1</b><br/>"
"This likely means that there is a problem with your installation. Try reinstalling or "
"contact your Administrator for support.<br/>"
"You can try to continue to use Kleopatra but there might be other problems.</qt>",
QString::fromUtf8(e.what()).toHtmlEscaped()));
#endif
}
#endif // DISABLE_UISERVER
const bool daemon = parser.isSet(QStringLiteral("daemon"));
if (!daemon && app.isSessionRestored()) {
app.restoreMainWindow();
}
if (!selfCheck()) {
return EXIT_FAILURE;
}
STARTUP_TIMING << "SelfCheck completed";
if (server) {
fillKeyCache(server);
}
#ifndef QT_NO_SYSTEMTRAYICON
app.startMonitoringSmartCard();
#endif
app.setIgnoreNewInstance(false);
if (!daemon) {
const QString err = app.newInstance(parser);
if (!err.isEmpty()) {
std::cerr << i18n("Invalid arguments: %1", err).toLocal8Bit().constData() << "\n";
return EXIT_FAILURE;
}
STARTUP_TIMING << "new instance created";
}
rc = app.exec();
app.setIgnoreNewInstance(true);
QObject::disconnect(server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow);
QObject::disconnect(server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog);
if (server) {
server->stop();
server->waitForStopped();
delete server;
}
return rc;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 5, 9:33 PM (17 h, 28 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
4c/9f/d88e722aa6205bf3caa803e29f2e

Event Timeline