File indexing completed on 2024-05-19 05:20:17
0001 /* 0002 kleopatraapplication.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 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 "kleopatraapplication.h" 0016 0017 #include "kleopatra_options.h" 0018 #include "mainwindow.h" 0019 #include "settings.h" 0020 #include "smimevalidationpreferences.h" 0021 #include "systrayicon.h" 0022 0023 #include <conf/configuredialog.h> 0024 #include <conf/groupsconfigdialog.h> 0025 #include <smartcard/readerstatus.h> 0026 0027 #include <Libkleo/GnuPG> 0028 #include <utils/kdpipeiodevice.h> 0029 #include <utils/log.h> 0030 0031 #include <gpgme++/key.h> 0032 0033 #include <Libkleo/Classify> 0034 #include <Libkleo/Dn> 0035 #include <Libkleo/FileSystemWatcher> 0036 #include <Libkleo/KeyCache> 0037 #include <Libkleo/KeyFilterManager> 0038 #include <Libkleo/KeyGroupConfig> 0039 #include <Libkleo/SystemInfo> 0040 0041 #include <uiserver/uiserver.h> 0042 0043 #include "commands/checksumcreatefilescommand.h" 0044 #include "commands/checksumverifyfilescommand.h" 0045 #include "commands/decryptverifyfilescommand.h" 0046 #include "commands/detailscommand.h" 0047 #include "commands/importcertificatefromfilecommand.h" 0048 #include "commands/lookupcertificatescommand.h" 0049 #include "commands/newcertificatesigningrequestcommand.h" 0050 #include "commands/newopenpgpcertificatecommand.h" 0051 #include "commands/signencryptfilescommand.h" 0052 0053 #include "dialogs/updatenotification.h" 0054 0055 #include "kleopatra_debug.h" 0056 #include <KColorSchemeManager> 0057 #include <KIconLoader> 0058 #include <KLocalizedString> 0059 #include <KMessageBox> 0060 #include <KWindowSystem> 0061 0062 #include <QDesktopServices> 0063 #include <QDir> 0064 #include <QFile> 0065 #include <QFocusFrame> 0066 #if QT_CONFIG(graphicseffect) 0067 #include <QGraphicsEffect> 0068 #endif 0069 #include <QPointer> 0070 #include <QSettings> 0071 #include <QStyleOption> 0072 #include <QStylePainter> 0073 0074 #include <KSharedConfig> 0075 #include <memory> 0076 0077 #ifdef Q_OS_WIN 0078 #include <QtPlatformHeaders/QWindowsWindowFunctions> 0079 #endif 0080 0081 using namespace Kleo; 0082 using namespace Kleo::Commands; 0083 0084 static void add_resources() 0085 { 0086 KIconLoader::global()->addAppDir(QStringLiteral("libkleopatra")); 0087 KIconLoader::global()->addAppDir(QStringLiteral("kwatchgnupg")); 0088 } 0089 0090 static QList<QByteArray> default_logging_options() 0091 { 0092 QList<QByteArray> result; 0093 result.push_back("io"); 0094 return result; 0095 } 0096 0097 namespace 0098 { 0099 class FocusFrame : public QFocusFrame 0100 { 0101 Q_OBJECT 0102 public: 0103 using QFocusFrame::QFocusFrame; 0104 0105 protected: 0106 void paintEvent(QPaintEvent *event) override; 0107 }; 0108 0109 static QRect effectiveWidgetRect(const QWidget *w) 0110 { 0111 // based on QWidgetPrivate::effectiveRectFor 0112 #if QT_CONFIG(graphicseffect) 0113 if (auto graphicsEffect = w->graphicsEffect(); graphicsEffect && graphicsEffect->isEnabled()) 0114 return graphicsEffect->boundingRectFor(w->rect()).toAlignedRect(); 0115 #endif // QT_CONFIG(graphicseffect) 0116 return w->rect(); 0117 } 0118 0119 static QRect clipRect(const QWidget *w) 0120 { 0121 // based on QWidgetPrivate::clipRect 0122 if (!w->isVisible()) { 0123 return QRect(); 0124 } 0125 QRect r = effectiveWidgetRect(w); 0126 int ox = 0; 0127 int oy = 0; 0128 while (w && w->isVisible() && !w->isWindow() && w->parentWidget()) { 0129 ox -= w->x(); 0130 oy -= w->y(); 0131 w = w->parentWidget(); 0132 r &= QRect(ox, oy, w->width(), w->height()); 0133 } 0134 return r; 0135 } 0136 0137 void FocusFrame::paintEvent(QPaintEvent *) 0138 { 0139 if (!widget()) { 0140 return; 0141 } 0142 0143 QStylePainter p(this); 0144 QStyleOptionFocusRect option; 0145 initStyleOption(&option); 0146 const int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &option); 0147 const int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option); 0148 const QRect rect = clipRect(widget()).adjusted(0, 0, hmargin * 2, vmargin * 2); 0149 p.setClipRect(rect); 0150 p.drawPrimitive(QStyle::PE_FrameFocusRect, option); 0151 } 0152 } 0153 0154 class KleopatraApplication::Private 0155 { 0156 friend class ::KleopatraApplication; 0157 KleopatraApplication *const q; 0158 0159 public: 0160 explicit Private(KleopatraApplication *qq) 0161 : q(qq) 0162 , ignoreNewInstance(true) 0163 , firstNewInstance(true) 0164 , sysTray(nullptr) 0165 , groupConfig{std::make_shared<KeyGroupConfig>(QStringLiteral("kleopatragroupsrc"))} 0166 { 0167 } 0168 ~Private() 0169 { 0170 #ifndef QT_NO_SYSTEMTRAYICON 0171 delete sysTray; 0172 #endif 0173 } 0174 void setUpSysTrayIcon() 0175 { 0176 #ifndef QT_NO_SYSTEMTRAYICON 0177 Q_ASSERT(readerStatus); 0178 sysTray = new SysTrayIcon(); 0179 sysTray->setFirstCardWithNullPin(readerStatus->firstCardWithNullPin()); 0180 sysTray->setAnyCardCanLearnKeys(readerStatus->anyCardCanLearnKeys()); 0181 0182 connect(readerStatus.get(), &SmartCard::ReaderStatus::firstCardWithNullPinChanged, sysTray, &SysTrayIcon::setFirstCardWithNullPin); 0183 connect(readerStatus.get(), &SmartCard::ReaderStatus::anyCardCanLearnKeysChanged, sysTray, &SysTrayIcon::setAnyCardCanLearnKeys); 0184 #endif 0185 } 0186 0187 private: 0188 void connectConfigureDialog() 0189 { 0190 if (configureDialog) { 0191 if (q->mainWindow()) { 0192 connect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted())); 0193 } 0194 connect(configureDialog, &ConfigureDialog::configCommitted, q, &KleopatraApplication::configurationChanged); 0195 } 0196 } 0197 void disconnectConfigureDialog() 0198 { 0199 if (configureDialog) { 0200 if (q->mainWindow()) { 0201 disconnect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted())); 0202 } 0203 disconnect(configureDialog, &ConfigureDialog::configCommitted, q, &KleopatraApplication::configurationChanged); 0204 } 0205 } 0206 0207 public: 0208 bool ignoreNewInstance; 0209 bool firstNewInstance; 0210 QPointer<FocusFrame> focusFrame; 0211 QPointer<ConfigureDialog> configureDialog; 0212 QPointer<GroupsConfigDialog> groupsConfigDialog; 0213 QPointer<MainWindow> mainWindow; 0214 std::unique_ptr<SmartCard::ReaderStatus> readerStatus; 0215 #ifndef QT_NO_SYSTEMTRAYICON 0216 SysTrayIcon *sysTray; 0217 #endif 0218 std::shared_ptr<KeyGroupConfig> groupConfig; 0219 std::shared_ptr<KeyCache> keyCache; 0220 std::shared_ptr<Log> log; 0221 std::shared_ptr<FileSystemWatcher> watcher; 0222 std::shared_ptr<QSettings> distroSettings; 0223 0224 public: 0225 void setupKeyCache() 0226 { 0227 keyCache = KeyCache::mutableInstance(); 0228 keyCache->setRefreshInterval(SMimeValidationPreferences{}.refreshInterval()); 0229 watcher.reset(new FileSystemWatcher); 0230 0231 watcher->whitelistFiles(gnupgFileWhitelist()); 0232 watcher->addPaths(gnupgFolderWhitelist()); 0233 watcher->setDelay(1000); 0234 keyCache->addFileSystemWatcher(watcher); 0235 keyCache->setGroupConfig(groupConfig); 0236 keyCache->setGroupsEnabled(Settings().groupsEnabled()); 0237 // always enable remarks (aka tags); in particular, this triggers a 0238 // relisting of the keys with signatures and signature notations 0239 // after the initial (fast) key listing 0240 keyCache->enableRemarks(true); 0241 } 0242 0243 void setUpFilterManager() 0244 { 0245 if (!Settings{}.cmsEnabled()) { 0246 KeyFilterManager::instance()->alwaysFilterByProtocol(GpgME::OpenPGP); 0247 } 0248 } 0249 0250 void setupLogging() 0251 { 0252 log = Log::mutableInstance(); 0253 0254 const QByteArray envOptions = qgetenv("KLEOPATRA_LOGOPTIONS"); 0255 const bool logAll = envOptions.trimmed() == "all"; 0256 const QList<QByteArray> options = envOptions.isEmpty() ? default_logging_options() : envOptions.split(','); 0257 0258 const QByteArray dirNative = qgetenv("KLEOPATRA_LOGDIR"); 0259 if (dirNative.isEmpty()) { 0260 return; 0261 } 0262 const QString dir = QFile::decodeName(dirNative); 0263 const QString logFileName = QDir(dir).absoluteFilePath(QStringLiteral("kleopatra.log.%1").arg(QCoreApplication::applicationPid())); 0264 std::unique_ptr<QFile> logFile(new QFile(logFileName)); 0265 if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append)) { 0266 qCDebug(KLEOPATRA_LOG) << "Could not open file for logging: " << logFileName << "\nLogging disabled"; 0267 return; 0268 } 0269 0270 log->setOutputDirectory(dir); 0271 if (logAll || options.contains("io")) { 0272 log->setIOLoggingEnabled(true); 0273 } 0274 qInstallMessageHandler(Log::messageHandler); 0275 0276 if (logAll || options.contains("pipeio")) { 0277 KDPipeIODevice::setDebugLevel(KDPipeIODevice::Debug); 0278 } 0279 UiServer::setLogStream(log->logFile()); 0280 } 0281 0282 void updateFocusFrame(QWidget *focusWidget) 0283 { 0284 if (focusWidget && focusWidget->inherits("QLabel") && focusWidget->window()->testAttribute(Qt::WA_KeyboardFocusChange)) { 0285 if (!focusFrame) { 0286 focusFrame = new FocusFrame{focusWidget}; 0287 } 0288 focusFrame->setWidget(focusWidget); 0289 } else if (focusFrame) { 0290 focusFrame->setWidget(nullptr); 0291 } 0292 } 0293 }; 0294 0295 KleopatraApplication::KleopatraApplication(int &argc, char *argv[]) 0296 : QApplication(argc, argv) 0297 , d(new Private(this)) 0298 { 0299 // disable parent<->child navigation in tree views with left/right arrow keys 0300 // because this interferes with column by column navigation that is required 0301 // for accessibility 0302 setStyleSheet(QStringLiteral("QTreeView { arrow-keys-navigate-into-children: 0; }")); 0303 connect(this, &QApplication::focusChanged, this, [this](QWidget *, QWidget *now) { 0304 d->updateFocusFrame(now); 0305 }); 0306 } 0307 0308 void KleopatraApplication::init() 0309 { 0310 #ifdef Q_OS_WIN 0311 QWindowsWindowFunctions::setWindowActivationBehavior(QWindowsWindowFunctions::AlwaysActivateWindow); 0312 #endif 0313 const auto blockedUrlSchemes = Settings{}.blockedUrlSchemes(); 0314 for (const auto &scheme : blockedUrlSchemes) { 0315 QDesktopServices::setUrlHandler(scheme, this, "blockUrl"); 0316 } 0317 add_resources(); 0318 DN::setAttributeOrder(Settings{}.attributeOrder()); 0319 /* Start the gpg-agent early, this is done explicitly 0320 * because on an empty keyring our keylistings wont start 0321 * the agent. In that case any assuan-connect calls to 0322 * the agent will fail. The requested start via the 0323 * connection is additionally done in case the gpg-agent 0324 * is killed while Kleopatra is running. */ 0325 startGpgAgent(); 0326 d->readerStatus.reset(new SmartCard::ReaderStatus); 0327 connect(d->readerStatus.get(), &SmartCard::ReaderStatus::startOfGpgAgentRequested, this, &KleopatraApplication::startGpgAgent); 0328 d->setupKeyCache(); 0329 d->setUpSysTrayIcon(); 0330 d->setUpFilterManager(); 0331 d->setupLogging(); 0332 #ifdef Q_OS_WIN 0333 if (!SystemInfo::isHighContrastModeActive()) { 0334 /* In high contrast mode we do not want our own colors */ 0335 new KColorSchemeManager(this); 0336 } 0337 #else 0338 new KColorSchemeManager(this); 0339 #endif 0340 0341 #ifndef QT_NO_SYSTEMTRAYICON 0342 d->sysTray->show(); 0343 #endif 0344 setQuitOnLastWindowClosed(false); 0345 0346 // Sync config when we are about to quit 0347 connect(this, &QApplication::aboutToQuit, this, []() { 0348 KSharedConfig::openConfig()->sync(); 0349 }); 0350 } 0351 0352 KleopatraApplication::~KleopatraApplication() 0353 { 0354 delete d->groupsConfigDialog; 0355 delete d->mainWindow; 0356 } 0357 0358 namespace 0359 { 0360 using Func = void (KleopatraApplication::*)(const QStringList &, GpgME::Protocol); 0361 } 0362 0363 void KleopatraApplication::slotActivateRequested(const QStringList &arguments, const QString &workingDirectory) 0364 { 0365 QCommandLineParser parser; 0366 0367 kleopatra_options(&parser); 0368 QString err; 0369 if (!arguments.isEmpty() && !parser.parse(arguments)) { 0370 err = parser.errorText(); 0371 } else if (arguments.isEmpty()) { 0372 // KDBusServices omits the application name if no other 0373 // arguments are provided. In that case the parser prints 0374 // a warning. 0375 parser.parse(QStringList() << QCoreApplication::applicationFilePath()); 0376 } 0377 0378 if (err.isEmpty()) { 0379 err = newInstance(parser, workingDirectory); 0380 } 0381 0382 if (!err.isEmpty()) { 0383 KMessageBox::error(nullptr, err.toHtmlEscaped(), i18n("Failed to execute command")); 0384 Q_EMIT setExitValue(1); 0385 return; 0386 } 0387 Q_EMIT setExitValue(0); 0388 } 0389 0390 QString KleopatraApplication::newInstance(const QCommandLineParser &parser, const QString &workingDirectory) 0391 { 0392 if (d->ignoreNewInstance) { 0393 qCDebug(KLEOPATRA_LOG) << "New instance ignored because of ignoreNewInstance"; 0394 return QString(); 0395 } 0396 0397 QStringList files; 0398 const QDir cwd = QDir(workingDirectory); 0399 bool queryMode = parser.isSet(QStringLiteral("query")) || parser.isSet(QStringLiteral("search")); 0400 0401 // Query and Search treat positional arguments differently, see below. 0402 if (!queryMode) { 0403 const auto positionalArguments = parser.positionalArguments(); 0404 for (const QString &file : positionalArguments) { 0405 // We do not check that file exists here. Better handle 0406 // these errors in the UI. 0407 if (QFileInfo(file).isAbsolute()) { 0408 files << file; 0409 } else { 0410 files << cwd.absoluteFilePath(file); 0411 } 0412 } 0413 } 0414 0415 GpgME::Protocol protocol = GpgME::UnknownProtocol; 0416 0417 if (parser.isSet(QStringLiteral("openpgp"))) { 0418 qCDebug(KLEOPATRA_LOG) << "found OpenPGP"; 0419 protocol = GpgME::OpenPGP; 0420 } 0421 0422 if (parser.isSet(QStringLiteral("cms"))) { 0423 qCDebug(KLEOPATRA_LOG) << "found CMS"; 0424 if (protocol == GpgME::OpenPGP) { 0425 return i18n("Ambiguous protocol: --openpgp and --cms"); 0426 } 0427 protocol = GpgME::CMS; 0428 } 0429 0430 // Check for Parent Window id 0431 WId parentId = 0; 0432 if (parser.isSet(QStringLiteral("parent-windowid"))) { 0433 #ifdef Q_OS_WIN 0434 // WId is not a portable type as it is a pointer type on Windows. 0435 // casting it from an integer is ok though as the values are guaranteed to 0436 // be compatible in the documentation. 0437 parentId = reinterpret_cast<WId>(parser.value(QStringLiteral("parent-windowid")).toUInt()); 0438 #else 0439 parentId = parser.value(QStringLiteral("parent-windowid")).toUInt(); 0440 #endif 0441 } 0442 0443 // Handle openpgp4fpr URI scheme 0444 QString needle; 0445 if (queryMode) { 0446 needle = parser.positionalArguments().join(QLatin1Char(' ')); 0447 } 0448 if (needle.startsWith(QLatin1StringView("openpgp4fpr:"))) { 0449 needle.remove(0, 12); 0450 } 0451 0452 // Check for --search command. 0453 if (parser.isSet(QStringLiteral("search"))) { 0454 // This is an extra command instead of a combination with the 0455 // similar query to avoid changing the older query commands behavior 0456 // and query's "show details if a certificate exist or search on a 0457 // keyserver" logic is hard to explain and use consistently. 0458 if (needle.isEmpty()) { 0459 return i18n("No search string specified for --search"); 0460 } 0461 auto const cmd = new LookupCertificatesCommand(needle, nullptr); 0462 cmd->setParentWId(parentId); 0463 cmd->start(); 0464 return QString(); 0465 } 0466 0467 // Check for --query command 0468 if (parser.isSet(QStringLiteral("query"))) { 0469 if (needle.isEmpty()) { 0470 return i18n("No fingerprint argument specified for --query"); 0471 } 0472 auto cmd = Command::commandForQuery(needle); 0473 cmd->setParentWId(parentId); 0474 cmd->start(); 0475 return QString(); 0476 } 0477 0478 // Check for --gen-key command 0479 if (parser.isSet(QStringLiteral("gen-key"))) { 0480 if (protocol == GpgME::CMS) { 0481 const Kleo::Settings settings{}; 0482 if (settings.cmsEnabled() && settings.cmsCertificateCreationAllowed()) { 0483 auto cmd = new NewCertificateSigningRequestCommand; 0484 cmd->setParentWId(parentId); 0485 cmd->start(); 0486 } else { 0487 return i18n("You are not allowed to create S/MIME certificate signing requests."); 0488 } 0489 } else { 0490 auto cmd = new NewOpenPGPCertificateCommand; 0491 cmd->setParentWId(parentId); 0492 cmd->start(); 0493 } 0494 return QString(); 0495 } 0496 0497 // Check for --config command 0498 if (parser.isSet(QStringLiteral("config"))) { 0499 openConfigDialogWithForeignParent(parentId); 0500 return QString(); 0501 } 0502 0503 struct FuncInfo { 0504 QString optionName; 0505 Func func; 0506 }; 0507 0508 // While most of these options can be handled by the content autodetection 0509 // below it might be useful to override the autodetection if the input is in 0510 // doubt and you e.g. only want to import .asc files or fail and not decrypt them 0511 // if they are actually encrypted data. 0512 static const std::vector<FuncInfo> funcMap{ 0513 {QStringLiteral("import-certificate"), &KleopatraApplication::importCertificatesFromFile}, 0514 {QStringLiteral("encrypt"), &KleopatraApplication::encryptFiles}, 0515 {QStringLiteral("sign"), &KleopatraApplication::signFiles}, 0516 {QStringLiteral("encrypt-sign"), &KleopatraApplication::signEncryptFiles}, 0517 {QStringLiteral("sign-encrypt"), &KleopatraApplication::signEncryptFiles}, 0518 {QStringLiteral("decrypt"), &KleopatraApplication::decryptFiles}, 0519 {QStringLiteral("verify"), &KleopatraApplication::verifyFiles}, 0520 {QStringLiteral("decrypt-verify"), &KleopatraApplication::decryptVerifyFiles}, 0521 {QStringLiteral("checksum"), &KleopatraApplication::checksumFiles}, 0522 }; 0523 0524 QString found; 0525 Func foundFunc = nullptr; 0526 for (const auto &[opt, fn] : funcMap) { 0527 if (parser.isSet(opt) && found.isEmpty()) { 0528 found = opt; 0529 foundFunc = fn; 0530 } else if (parser.isSet(opt)) { 0531 return i18n(R"(Ambiguous commands "%1" and "%2")", found, opt); 0532 } 0533 } 0534 0535 QStringList errors; 0536 if (!found.isEmpty()) { 0537 if (files.empty()) { 0538 return i18n("No files specified for \"%1\" command", found); 0539 } 0540 qCDebug(KLEOPATRA_LOG) << "found" << found; 0541 (this->*foundFunc)(files, protocol); 0542 } else { 0543 if (files.empty()) { 0544 if (!(d->firstNewInstance && isSessionRestored())) { 0545 qCDebug(KLEOPATRA_LOG) << "openOrRaiseMainWindow"; 0546 openOrRaiseMainWindow(); 0547 } 0548 } else { 0549 for (const QString &fileName : std::as_const(files)) { 0550 QFileInfo fi(fileName); 0551 if (!fi.isReadable()) { 0552 errors << i18n("Cannot read \"%1\"", fileName); 0553 } 0554 } 0555 handleFiles(files, parentId); 0556 } 0557 } 0558 d->firstNewInstance = false; 0559 0560 #ifdef Q_OS_WIN 0561 // On Windows we might be started from the 0562 // explorer in any working directory. E.g. 0563 // a double click on a file. To avoid preventing 0564 // the folder from deletion we set the 0565 // working directory to the users homedir. 0566 QDir::setCurrent(QDir::homePath()); 0567 #endif 0568 0569 return errors.join(QLatin1Char('\n')); 0570 } 0571 0572 void KleopatraApplication::handleFiles(const QStringList &files, WId parentId) 0573 { 0574 const QList<Command *> allCmds = Command::commandsForFiles(files); 0575 for (Command *cmd : allCmds) { 0576 if (parentId) { 0577 cmd->setParentWId(parentId); 0578 } else { 0579 MainWindow *mw = mainWindow(); 0580 if (!mw) { 0581 mw = new MainWindow; 0582 mw->setAttribute(Qt::WA_DeleteOnClose); 0583 setMainWindow(mw); 0584 d->connectConfigureDialog(); 0585 } 0586 cmd->setParentWidget(mw); 0587 } 0588 if (dynamic_cast<ImportCertificateFromFileCommand *>(cmd)) { 0589 openOrRaiseMainWindow(); 0590 } 0591 cmd->start(); 0592 } 0593 } 0594 0595 #ifndef QT_NO_SYSTEMTRAYICON 0596 const SysTrayIcon *KleopatraApplication::sysTrayIcon() const 0597 { 0598 return d->sysTray; 0599 } 0600 0601 SysTrayIcon *KleopatraApplication::sysTrayIcon() 0602 { 0603 return d->sysTray; 0604 } 0605 #endif 0606 0607 const MainWindow *KleopatraApplication::mainWindow() const 0608 { 0609 return d->mainWindow; 0610 } 0611 0612 MainWindow *KleopatraApplication::mainWindow() 0613 { 0614 return d->mainWindow; 0615 } 0616 0617 void KleopatraApplication::setMainWindow(MainWindow *mainWindow) 0618 { 0619 if (mainWindow == d->mainWindow) { 0620 return; 0621 } 0622 0623 d->disconnectConfigureDialog(); 0624 0625 d->mainWindow = mainWindow; 0626 #ifndef QT_NO_SYSTEMTRAYICON 0627 d->sysTray->setMainWindow(mainWindow); 0628 #endif 0629 0630 d->connectConfigureDialog(); 0631 } 0632 0633 static void open_or_raise(QWidget *w) 0634 { 0635 #ifdef Q_OS_WIN 0636 if (w->isMinimized()) { 0637 qCDebug(KLEOPATRA_LOG) << __func__ << "unminimizing and raising window"; 0638 w->raise(); 0639 } else if (w->isVisible()) { 0640 qCDebug(KLEOPATRA_LOG) << __func__ << "raising window"; 0641 w->raise(); 0642 #else 0643 if (w->isVisible()) { 0644 qCDebug(KLEOPATRA_LOG) << __func__ << "activating window"; 0645 KWindowSystem::updateStartupId(w->windowHandle()); 0646 KWindowSystem::activateWindow(w->windowHandle()); 0647 #endif 0648 } else { 0649 qCDebug(KLEOPATRA_LOG) << __func__ << "showing window"; 0650 w->show(); 0651 } 0652 } 0653 0654 void KleopatraApplication::toggleMainWindowVisibility() 0655 { 0656 if (mainWindow()) { 0657 mainWindow()->setVisible(!mainWindow()->isVisible()); 0658 } else { 0659 openOrRaiseMainWindow(); 0660 } 0661 if (mainWindow()->isVisible()) { 0662 mainWindow()->exportWindow(); 0663 } else { 0664 mainWindow()->unexportWindow(); 0665 } 0666 } 0667 0668 void KleopatraApplication::restoreMainWindow() 0669 { 0670 qCDebug(KLEOPATRA_LOG) << "restoring main window"; 0671 0672 // Sanity checks 0673 if (!isSessionRestored()) { 0674 qCDebug(KLEOPATRA_LOG) << "Not in session restore"; 0675 return; 0676 } 0677 0678 if (mainWindow()) { 0679 qCDebug(KLEOPATRA_LOG) << "Already have main window"; 0680 return; 0681 } 0682 0683 auto mw = new MainWindow; 0684 if (KMainWindow::canBeRestored(1)) { 0685 // restore to hidden state, Mainwindow::readProperties() will 0686 // restore saved visibility. 0687 mw->restore(1, false); 0688 } 0689 0690 mw->setAttribute(Qt::WA_DeleteOnClose); 0691 setMainWindow(mw); 0692 d->connectConfigureDialog(); 0693 } 0694 0695 void KleopatraApplication::openOrRaiseMainWindow() 0696 { 0697 MainWindow *mw = mainWindow(); 0698 if (!mw) { 0699 mw = new MainWindow; 0700 mw->setAttribute(Qt::WA_DeleteOnClose); 0701 setMainWindow(mw); 0702 d->connectConfigureDialog(); 0703 } 0704 open_or_raise(mw); 0705 UpdateNotification::checkUpdate(mw); 0706 } 0707 0708 void KleopatraApplication::openConfigDialogWithForeignParent(WId parentWId) 0709 { 0710 if (!d->configureDialog) { 0711 d->configureDialog = new ConfigureDialog; 0712 d->configureDialog->setAttribute(Qt::WA_DeleteOnClose); 0713 d->connectConfigureDialog(); 0714 } 0715 0716 // This is similar to what the commands do. 0717 if (parentWId) { 0718 if (QWidget *pw = QWidget::find(parentWId)) { 0719 d->configureDialog->setParent(pw, d->configureDialog->windowFlags()); 0720 } else { 0721 d->configureDialog->setAttribute(Qt::WA_NativeWindow, true); 0722 KWindowSystem::setMainWindow(d->configureDialog->windowHandle(), parentWId); 0723 } 0724 } 0725 0726 open_or_raise(d->configureDialog); 0727 0728 // If we have a parent we want to raise over it. 0729 if (parentWId) { 0730 d->configureDialog->raise(); 0731 } 0732 } 0733 0734 void KleopatraApplication::openOrRaiseConfigDialog() 0735 { 0736 openConfigDialogWithForeignParent(0); 0737 } 0738 0739 void KleopatraApplication::openOrRaiseGroupsConfigDialog(QWidget *parent) 0740 { 0741 if (!d->groupsConfigDialog) { 0742 d->groupsConfigDialog = new GroupsConfigDialog{parent}; 0743 d->groupsConfigDialog->setAttribute(Qt::WA_DeleteOnClose); 0744 } else { 0745 // reparent the dialog to ensure it's shown on top of the (modal) parent 0746 d->groupsConfigDialog->setParent(parent, Qt::Dialog); 0747 } 0748 open_or_raise(d->groupsConfigDialog); 0749 } 0750 0751 #ifndef QT_NO_SYSTEMTRAYICON 0752 void KleopatraApplication::startMonitoringSmartCard() 0753 { 0754 Q_ASSERT(d->readerStatus); 0755 d->readerStatus->startMonitoring(); 0756 } 0757 #endif // QT_NO_SYSTEMTRAYICON 0758 0759 void KleopatraApplication::importCertificatesFromFile(const QStringList &files, GpgME::Protocol /*proto*/) 0760 { 0761 openOrRaiseMainWindow(); 0762 if (!files.empty()) { 0763 mainWindow()->importCertificatesFromFile(files); 0764 } 0765 } 0766 0767 void KleopatraApplication::encryptFiles(const QStringList &files, GpgME::Protocol proto) 0768 { 0769 auto const cmd = new SignEncryptFilesCommand(files, nullptr); 0770 cmd->setEncryptionPolicy(Force); 0771 cmd->setSigningPolicy(Allow); 0772 if (proto != GpgME::UnknownProtocol) { 0773 cmd->setProtocol(proto); 0774 } 0775 cmd->start(); 0776 } 0777 0778 void KleopatraApplication::signFiles(const QStringList &files, GpgME::Protocol proto) 0779 { 0780 auto const cmd = new SignEncryptFilesCommand(files, nullptr); 0781 cmd->setSigningPolicy(Force); 0782 cmd->setEncryptionPolicy(Deny); 0783 if (proto != GpgME::UnknownProtocol) { 0784 cmd->setProtocol(proto); 0785 } 0786 cmd->start(); 0787 } 0788 0789 void KleopatraApplication::signEncryptFiles(const QStringList &files, GpgME::Protocol proto) 0790 { 0791 auto const cmd = new SignEncryptFilesCommand(files, nullptr); 0792 if (proto != GpgME::UnknownProtocol) { 0793 cmd->setProtocol(proto); 0794 } 0795 cmd->start(); 0796 } 0797 0798 void KleopatraApplication::decryptFiles(const QStringList &files, GpgME::Protocol /*proto*/) 0799 { 0800 auto const cmd = new DecryptVerifyFilesCommand(files, nullptr); 0801 cmd->setOperation(Decrypt); 0802 cmd->start(); 0803 } 0804 0805 void KleopatraApplication::verifyFiles(const QStringList &files, GpgME::Protocol /*proto*/) 0806 { 0807 auto const cmd = new DecryptVerifyFilesCommand(files, nullptr); 0808 cmd->setOperation(Verify); 0809 cmd->start(); 0810 } 0811 0812 void KleopatraApplication::decryptVerifyFiles(const QStringList &files, GpgME::Protocol /*proto*/) 0813 { 0814 auto const cmd = new DecryptVerifyFilesCommand(files, nullptr); 0815 cmd->start(); 0816 } 0817 0818 void KleopatraApplication::checksumFiles(const QStringList &files, GpgME::Protocol /*proto*/) 0819 { 0820 QStringList verifyFiles, createFiles; 0821 0822 for (const QString &file : files) { 0823 if (isChecksumFile(file)) { 0824 verifyFiles << file; 0825 } else { 0826 createFiles << file; 0827 } 0828 } 0829 0830 if (!verifyFiles.isEmpty()) { 0831 auto const cmd = new ChecksumVerifyFilesCommand(verifyFiles, nullptr); 0832 cmd->start(); 0833 } 0834 if (!createFiles.isEmpty()) { 0835 auto const cmd = new ChecksumCreateFilesCommand(createFiles, nullptr); 0836 cmd->start(); 0837 } 0838 } 0839 0840 void KleopatraApplication::setIgnoreNewInstance(bool ignore) 0841 { 0842 d->ignoreNewInstance = ignore; 0843 } 0844 0845 bool KleopatraApplication::ignoreNewInstance() const 0846 { 0847 return d->ignoreNewInstance; 0848 } 0849 0850 void KleopatraApplication::blockUrl(const QUrl &url) 0851 { 0852 qCDebug(KLEOPATRA_LOG) << "Blocking URL" << url; 0853 KMessageBox::error(mainWindow(), i18n("Opening an external link is administratively prohibited."), i18n("Prohibited")); 0854 } 0855 0856 void KleopatraApplication::startGpgAgent() 0857 { 0858 Kleo::launchGpgAgent(); 0859 } 0860 0861 void KleopatraApplication::setDistributionSettings(const std::shared_ptr<QSettings> &settings) 0862 { 0863 d->distroSettings = settings; 0864 } 0865 0866 std::shared_ptr<QSettings> KleopatraApplication::distributionSettings() const 0867 { 0868 return d->distroSettings; 0869 } 0870 0871 #include "kleopatraapplication.moc" 0872 0873 #include "moc_kleopatraapplication.cpp"