File indexing completed on 2024-05-12 05:20:01

0001 /*
0002     main.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2001, 2002, 2004, 2008 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
0008     SPDX-FileContributor: Intevation GmbH
0009 
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 #include <config-kleopatra.h>
0014 
0015 #include "aboutdata.h"
0016 #include "kleopatraapplication.h"
0017 #include "mainwindow.h"
0018 
0019 #include "accessibility/accessiblewidgetfactory.h"
0020 
0021 #include <commands/reloadkeyscommand.h>
0022 #include <commands/selftestcommand.h>
0023 
0024 #include "utils/kuniqueservice.h"
0025 #include "utils/userinfo.h"
0026 #include <Libkleo/GnuPG>
0027 #include <utils/archivedefinition.h>
0028 
0029 #include <uiserver/assuancommand.h>
0030 #include <uiserver/createchecksumscommand.h>
0031 #include <uiserver/decryptcommand.h>
0032 #include <uiserver/decryptfilescommand.h>
0033 #include <uiserver/decryptverifyfilescommand.h>
0034 #include <uiserver/echocommand.h>
0035 #include <uiserver/encryptcommand.h>
0036 #include <uiserver/importfilescommand.h>
0037 #include <uiserver/prepencryptcommand.h>
0038 #include <uiserver/prepsigncommand.h>
0039 #include <uiserver/selectcertificatecommand.h>
0040 #include <uiserver/signcommand.h>
0041 #include <uiserver/signencryptfilescommand.h>
0042 #include <uiserver/uiserver.h>
0043 #include <uiserver/verifychecksumscommand.h>
0044 #include <uiserver/verifycommand.h>
0045 #include <uiserver/verifyfilescommand.h>
0046 
0047 #include <Libkleo/ChecksumDefinition>
0048 
0049 #include "kleopatra_debug.h"
0050 #include "kleopatra_options.h"
0051 
0052 #include <KCrash>
0053 #include <KLocalizedString>
0054 #include <KMessageBox>
0055 
0056 #include <QAccessible>
0057 #include <QElapsedTimer>
0058 #include <QEventLoop>
0059 #include <QMessageBox>
0060 #include <QTextDocument> // for Qt::escape
0061 #include <QThreadPool>
0062 #include <QTime>
0063 #include <QTimer>
0064 
0065 #include <gpgme++/error.h>
0066 #include <gpgme++/global.h>
0067 
0068 #include <QCommandLineParser>
0069 #include <iostream>
0070 #include <memory>
0071 
0072 QElapsedTimer startupTimer;
0073 
0074 static bool selfCheck()
0075 {
0076     Kleo::Commands::SelfTestCommand cmd(nullptr);
0077     cmd.setAutoDelete(false);
0078     cmd.setAutomaticMode(true);
0079     QEventLoop loop;
0080     QObject::connect(&cmd, &Kleo::Commands::SelfTestCommand::finished, &loop, &QEventLoop::quit);
0081     QTimer::singleShot(0, &cmd, &Kleo::Command::start); // start() may Q_EMIT finished()...
0082     loop.exec();
0083     if (cmd.isCanceled()) {
0084         return false;
0085     } else {
0086         return true;
0087     }
0088 }
0089 
0090 static void fillKeyCache(Kleo::UiServer *server)
0091 {
0092     auto cmd = new Kleo::ReloadKeysCommand(nullptr);
0093     QObject::connect(cmd, SIGNAL(finished()), server, SLOT(enableCryptoCommands()));
0094     cmd->start();
0095 }
0096 
0097 int main(int argc, char **argv)
0098 {
0099     startupTimer.start();
0100 #ifdef Q_OS_WIN
0101     // note: requires https://invent.kde.org/frameworks/kiconthemes/-/merge_requests/109
0102     qputenv("QT_QPA_PLATFORM", "windows:darkmode=2");
0103 #endif
0104 
0105     KleopatraApplication app(argc, argv);
0106     // Set OrganizationDomain early as this is used to generate the service
0107     // name that will be registered on the bus.
0108     app.setOrganizationDomain(QStringLiteral("kde.org"));
0109 
0110     STARTUP_TIMING << "Application created";
0111     /* Create the unique service ASAP to prevent double starts if
0112      * the application is started twice very quickly. */
0113     KUniqueService service;
0114     QObject::connect(&service, &KUniqueService::activateRequested, &app, &KleopatraApplication::slotActivateRequested);
0115     QObject::connect(&app, &KleopatraApplication::setExitValue, &service, [&service](int i) {
0116         service.setExitValue(i);
0117     });
0118     STARTUP_TIMING << "Service created";
0119 
0120     KCrash::initialize();
0121     QAccessible::installFactory(Kleo::accessibleWidgetFactory);
0122     qCDebug(KLEOPATRA_LOG) << "Application created";
0123 
0124     app.setWindowIcon(QIcon::fromTheme(QStringLiteral("kleopatra"), app.windowIcon()));
0125 
0126     KLocalizedString::setApplicationDomain(QByteArrayLiteral("kleopatra"));
0127 
0128     // Initialize GpgME
0129     {
0130         const GpgME::Error gpgmeInitError = GpgME::initializeLibrary(0);
0131         if (gpgmeInitError) {
0132             KMessageBox::error(nullptr,
0133                                xi18nc("@info",
0134                                       "<para>The version of the <application>GpgME</application> library you are running against "
0135                                       "is older than the one that the <application>GpgME++</application> library was built against.</para>"
0136                                       "<para><application>Kleopatra</application> will not function in this setting.</para>"
0137                                       "<para>Please ask your administrator for help in resolving this issue.</para>"),
0138                                i18nc("@title", "GpgME Too Old"));
0139             return EXIT_FAILURE;
0140         }
0141         STARTUP_TIMING << "GPGME Initialized";
0142     }
0143 
0144     AboutData aboutData;
0145     KAboutData::setApplicationData(aboutData);
0146 
0147     if (Kleo::userIsElevated()) {
0148         /* This is a safeguard against bugreports that something fails because
0149          * of permission problems on windows.  Some users still have the Windows
0150          * Vista behavior of running things as Administrator.  This can break
0151          * GnuPG in horrible ways for example if a stale lockfile is left that
0152          * can't be removed without another elevation.
0153          *
0154          * Note: This is not the same as running as root on Linux. Elevated means
0155          * that you are temporarily running with the "normal" user environment but
0156          * with elevated permissions.
0157          * */
0158         if (KMessageBox::warningContinueCancel(nullptr,
0159                                                xi18nc("@info",
0160                                                       "<para><application>Kleopatra</application> cannot be run as adminstrator without "
0161                                                       "breaking file permissions in the GnuPG data folder.</para>"
0162                                                       "<para>To manage keys for other users please manage them as a normal user and "
0163                                                       "copy the <filename>AppData\\Roaming\\gnupg</filename> directory with proper permissions.</para>")
0164                                                    + xi18n("<para>Are you sure that you want to continue?</para>"),
0165                                                i18nc("@title", "Running as Administrator"))
0166             != KMessageBox::Continue) {
0167             return EXIT_FAILURE;
0168         }
0169         qCWarning(KLEOPATRA_LOG) << "User is running with administrative permissions.";
0170     }
0171     // Delay init after KUniqueservice call as this might already
0172     // have terminated us and so we can avoid overhead (e.g. keycache
0173     // setup / systray icon).
0174     app.init();
0175     STARTUP_TIMING << "Application initialized";
0176 
0177     QCommandLineParser parser;
0178     aboutData.setupCommandLine(&parser);
0179     kleopatra_options(&parser);
0180 
0181     parser.process(QApplication::arguments());
0182     aboutData.processCommandLine(&parser);
0183     {
0184         const unsigned int threads = QThreadPool::globalInstance()->maxThreadCount();
0185         QThreadPool::globalInstance()->setMaxThreadCount(qMax(2U, threads));
0186     }
0187 
0188     Kleo::ChecksumDefinition::setInstallPath(Kleo::gpg4winInstallPath());
0189     Kleo::ArchiveDefinition::setInstallPath(Kleo::gnupgInstallPath());
0190 
0191 #ifndef DISABLE_UISERVER
0192     int rc;
0193     Kleo::UiServer *server = nullptr;
0194     try {
0195         server = new Kleo::UiServer(parser.value(QStringLiteral("uiserver-socket")));
0196         STARTUP_TIMING << "UiServer created";
0197 
0198         QObject::connect(server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow);
0199 
0200         QObject::connect(server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog);
0201 
0202 #define REGISTER(Command) server->registerCommandFactory(std::shared_ptr<Kleo::AssuanCommandFactory>(new Kleo::GenericAssuanCommandFactory<Kleo::Command>))
0203         REGISTER(CreateChecksumsCommand);
0204         REGISTER(DecryptCommand);
0205         REGISTER(DecryptFilesCommand);
0206         REGISTER(DecryptVerifyFilesCommand);
0207         REGISTER(EchoCommand);
0208         REGISTER(EncryptCommand);
0209         REGISTER(EncryptFilesCommand);
0210         REGISTER(EncryptSignFilesCommand);
0211         REGISTER(ImportFilesCommand);
0212         REGISTER(PrepEncryptCommand);
0213         REGISTER(PrepSignCommand);
0214         REGISTER(SelectCertificateCommand);
0215         REGISTER(SignCommand);
0216         REGISTER(SignEncryptFilesCommand);
0217         REGISTER(SignFilesCommand);
0218         REGISTER(VerifyChecksumsCommand);
0219         REGISTER(VerifyCommand);
0220         REGISTER(VerifyFilesCommand);
0221 #undef REGISTER
0222 
0223         server->start();
0224         STARTUP_TIMING << "UiServer started";
0225     } catch (const std::exception &e) {
0226         qCDebug(KLEOPATRA_LOG) << "Failed to start UI Server: " << e.what();
0227 #ifdef Q_OS_WIN
0228         // We should probably change the UIServer to be only run on Windows at all because
0229         // only the Windows Explorer Plugin uses it. But the plan of GnuPG devs as of 2022 is to
0230         // change the Windows Explorer Plugin to use the command line and then remove the
0231         // UiServer for everyone.
0232         QMessageBox::information(nullptr,
0233                                  i18n("GPG UI Server Error"),
0234                                  i18nc("This error message is only shown on Windows when the socket to communicate with "
0235                                        "Windows Explorer could not be created. This often times means that the whole installation is "
0236                                        "buggy. e.g. GnuPG is not installed at all.",
0237                                        "<qt>The Kleopatra Windows Explorer Module could not be initialized.<br/>"
0238                                        "The error given was: <b>%1</b><br/>"
0239                                        "This likely means that there is a problem with your installation. Try reinstalling or "
0240                                        "contact your Administrator for support.<br/>"
0241                                        "You can try to continue to use Kleopatra but there might be other problems.</qt>",
0242                                        QString::fromUtf8(e.what()).toHtmlEscaped()));
0243 #endif
0244     }
0245 #endif // DISABLE_UISERVER
0246     const bool daemon = parser.isSet(QStringLiteral("daemon"));
0247     if (!daemon && app.isSessionRestored()) {
0248         app.restoreMainWindow();
0249     }
0250 
0251     if (!selfCheck()) {
0252         return EXIT_FAILURE;
0253     }
0254     STARTUP_TIMING << "SelfCheck completed";
0255 
0256     if (server) {
0257         fillKeyCache(server);
0258     }
0259 #ifndef QT_NO_SYSTEMTRAYICON
0260     app.startMonitoringSmartCard();
0261 #endif
0262     app.setIgnoreNewInstance(false);
0263 
0264     if (!daemon) {
0265         const QString err = app.newInstance(parser);
0266         if (!err.isEmpty()) {
0267             std::cerr << i18n("Invalid arguments: %1", err).toLocal8Bit().constData() << "\n";
0268             return EXIT_FAILURE;
0269         }
0270         STARTUP_TIMING << "new instance created";
0271     }
0272 
0273     rc = app.exec();
0274 
0275     app.setIgnoreNewInstance(true);
0276     QObject::disconnect(server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow);
0277     QObject::disconnect(server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog);
0278 
0279     if (server) {
0280         server->stop();
0281         server->waitForStopped();
0282         delete server;
0283     }
0284 
0285     return rc;
0286 }