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 }