Continue to implement fullsync
[kdepim.git] / kleopatra / kleopatraapplication.cpp
blob0d80d1d5c6af257633fd1b802179e5df731375eb
1 /*
2 kleopatraapplication.cpp
4 This file is part of Kleopatra, the KDE keymanager
5 Copyright (c) 2008 Klarälvdalens Datakonsult AB
7 Kleopatra is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Kleopatra is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
33 #include <config-kleopatra.h>
35 #include "kleopatraapplication.h"
37 #include "mainwindow.h"
38 #include "kleopatra_options.h"
39 #include "systrayicon.h"
40 #include <smartcard/readerstatus.h>
41 #include <conf/configuredialog.h>
43 #include <utils/gnupg-helper.h>
44 #include <utils/filesystemwatcher.h>
45 #include <utils/kdpipeiodevice.h>
46 #include <utils/log.h>
47 #include <utils/getpid.h>
49 #include <gpgme++/key.h>
50 #include <models/keycache.h>
52 #ifdef HAVE_USABLE_ASSUAN
53 # include <uiserver/uiserver.h>
54 #endif
56 #include <commands/signencryptfilescommand.h>
57 #include <commands/decryptverifyfilescommand.h>
58 #include <commands/lookupcertificatescommand.h>
59 #include <commands/detailscommand.h>
61 #include <KIconLoader>
62 #include <KLocalizedString>
63 #include "kleopatra_debug.h"
64 #include <KMessageBox>
65 #include <KWindowSystem>
66 #include <QUrl>
68 #include <QFile>
69 #include <QDir>
70 #include <QPointer>
71 #include <QCommandLineOption>
73 #include <boost/shared_ptr.hpp>
75 #include <memory>
76 #include <KSharedConfig>
78 using namespace Kleo;
79 using namespace Kleo::Commands;
80 using namespace boost;
82 static void add_resources()
84 KIconLoader::global()->addAppDir(QStringLiteral("libkleopatra"));
85 KIconLoader::global()->addAppDir(QStringLiteral("kwatchgnupg"));
88 static QList<QByteArray> default_logging_options()
90 QList<QByteArray> result;
91 result.push_back("io");
92 return result;
95 class KleopatraApplication::Private
97 friend class ::KleopatraApplication;
98 KleopatraApplication *const q;
99 public:
100 explicit Private(KleopatraApplication *qq)
101 : q(qq),
102 ignoreNewInstance(true)
104 KDAB_SET_OBJECT_NAME(readerStatus);
105 #ifndef QT_NO_SYSTEMTRAYICON
106 KDAB_SET_OBJECT_NAME(sysTray);
108 sysTray.setAnyCardHasNullPin(readerStatus.anyCardHasNullPin());
109 sysTray.setAnyCardCanLearnKeys(readerStatus.anyCardCanLearnKeys());
111 connect(&readerStatus, &SmartCard::ReaderStatus::anyCardHasNullPinChanged,
112 &sysTray, &SysTrayIcon::setAnyCardHasNullPin);
113 connect(&readerStatus, &SmartCard::ReaderStatus::anyCardCanLearnKeysChanged,
114 &sysTray, &SysTrayIcon::setAnyCardCanLearnKeys);
115 #endif
118 private:
119 void connectConfigureDialog()
121 if (configureDialog && q->mainWindow()) {
122 connect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted()));
125 void disconnectConfigureDialog()
127 if (configureDialog && q->mainWindow()) {
128 disconnect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted()));
132 public:
133 bool ignoreNewInstance;
134 QPointer<ConfigureDialog> configureDialog;
135 QPointer<MainWindow> mainWindow;
136 SmartCard::ReaderStatus readerStatus;
137 #ifndef QT_NO_SYSTEMTRAYICON
138 SysTrayIcon sysTray;
139 #endif
140 shared_ptr<KeyCache> keyCache;
141 shared_ptr<Log> log;
142 shared_ptr<FileSystemWatcher> watcher;
144 public:
145 void setupKeyCache()
147 keyCache = KeyCache::mutableInstance();
148 watcher.reset(new FileSystemWatcher);
150 watcher->blacklistFiles(gnupgFileBlacklist());
151 watcher->addPath(gnupgHomeDirectory());
152 watcher->setDelay(1000);
153 keyCache->addFileSystemWatcher(watcher);
156 void setupLogging()
158 log = Log::mutableInstance();
160 const QByteArray envOptions = qgetenv("KLEOPATRA_LOGOPTIONS");
161 const bool logAll = envOptions.trimmed() == "all";
162 const QList<QByteArray> options = envOptions.isEmpty() ? default_logging_options() : envOptions.split(',');
164 const QByteArray dirNative = qgetenv("KLEOPATRA_LOGDIR");
165 if (dirNative.isEmpty()) {
166 return;
168 const QString dir = QFile::decodeName(dirNative);
169 const QString logFileName = QDir(dir).absoluteFilePath(QStringLiteral("kleopatra.log.%1").arg(mygetpid()));
170 std::unique_ptr<QFile> logFile(new QFile(logFileName));
171 if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append)) {
172 qCDebug(KLEOPATRA_LOG) << "Could not open file for logging: " << logFileName << "\nLogging disabled";
173 return;
176 log->setOutputDirectory(dir);
177 if (logAll || options.contains("io")) {
178 log->setIOLoggingEnabled(true);
180 qInstallMsgHandler(Log::messageHandler);
182 #ifdef HAVE_USABLE_ASSUAN
183 if (logAll || options.contains("pipeio")) {
184 KDPipeIODevice::setDebugLevel(KDPipeIODevice::Debug);
186 UiServer::setLogStream(log->logFile());
187 #endif
192 KleopatraApplication::KleopatraApplication(int &argc, char *argv[])
193 : QApplication(argc, argv), d(new Private(this))
195 add_resources();
196 d->setupKeyCache();
197 d->setupLogging();
198 #ifndef QT_NO_SYSTEMTRAYICON
199 d->sysTray.show();
200 #endif
201 setQuitOnLastWindowClosed(false);
202 KWindowSystem::allowExternalProcessWindowActivation();
205 KleopatraApplication::~KleopatraApplication()
207 // work around kdelibs bug https://bugs.kde.org/show_bug.cgi?id=162514
208 KSharedConfig::openConfig()->sync();
211 namespace
213 typedef void (KleopatraApplication::*Func)(const QStringList &, GpgME::Protocol);
216 void KleopatraApplication::slotActivateRequested(const QStringList &arguments,
217 const QString &workingDirectory)
219 QCommandLineParser parser;
221 kleopatra_options(&parser);
222 QString err;
223 if (!arguments.isEmpty() && !parser.parse(arguments)) {
224 err = parser.errorText();
225 } else if (arguments.isEmpty()) {
226 // KDBusServices omits the application name if no other
227 // arguments are provided. In that case the parser prints
228 // a warning.
229 parser.parse(QStringList() << QCoreApplication::applicationFilePath());
232 if (err.isEmpty()) {
233 err = newInstance(parser, workingDirectory);
236 if (!err.isEmpty()) {
237 KMessageBox::sorry(NULL, err.toHtmlEscaped(), i18n("Failed to execute command"));
238 Q_EMIT setExitValue(1);
242 QString KleopatraApplication::newInstance(const QCommandLineParser &parser,
243 const QString &workingDirectory)
245 if (d->ignoreNewInstance) {
246 qCDebug(KLEOPATRA_LOG) << "New instance ignored because of ignoreNewInstance";
247 return QString();
250 QStringList files;
251 const QDir cwd = QDir(workingDirectory);
252 Q_FOREACH (const QString &file, parser.positionalArguments()) {
253 // We do not check that file exists here. Better handle
254 // these errors in the UI.
255 if (QFileInfo(file).isAbsolute()) {
256 files << file;
257 } else {
258 files << cwd.absoluteFilePath(file);
262 GpgME::Protocol protocol = GpgME::UnknownProtocol;
264 if (parser.isSet(QStringLiteral("openpgp"))) {
265 qCDebug(KLEOPATRA_LOG) << "found OpenPGP";
266 protocol = GpgME::OpenPGP;
269 if (parser.isSet(QStringLiteral("cms"))) {
270 qCDebug(KLEOPATRA_LOG) << "found CMS";
271 if (protocol == GpgME::OpenPGP) {
272 return i18n("Ambiguous protocol: --openpgp and --cms");
274 protocol = GpgME::CMS;
277 // Check for --query command
278 if (parser.isSet(QStringLiteral("query"))) {
279 const QString fingerPrint = parser.value(QStringLiteral("query"));
280 if (fingerPrint.isEmpty()) {
281 return i18n("No fingerprint argument specified for --query");
284 // Check for Parent Window id
285 WId parentId = 0;
286 if (parser.isSet(QStringLiteral("parent-windowid"))) {
287 #ifdef Q_OS_WIN
288 // WId is not a portable type as it is a pointer type on Windows.
289 // casting it from an integer is ok though as the values are guranteed to
290 // be compatible in the documentation.
291 parentId = reinterpret_cast<WId>(parser.value(QStringLiteral("parent-windowid")).toUInt());
292 #else
293 parentId = parser.value(QStringLiteral("parent-windowid")).toUInt();
294 #endif
297 // Search for local keys
298 const GpgME::Key &key = Kleo::KeyCache::instance()->findByKeyIDOrFingerprint(fingerPrint.toLocal8Bit().data());
299 if (key.isNull()) {
300 // Show external search dialog
301 LookupCertificatesCommand *const cmd = new LookupCertificatesCommand(fingerPrint, 0);
302 if (parentId != 0) {
303 cmd->setParentWId(parentId);
305 cmd->start();
306 } else {
307 // show local detail
308 DetailsCommand *const cmd = new DetailsCommand(key, 0);
309 if (parentId != 0) {
310 cmd->setParentWId(parentId);
312 cmd->start();
314 return QString();
317 static const QMap<QString, Func> funcMap {
318 { QStringLiteral("import-certificate"), &KleopatraApplication::importCertificatesFromFile },
319 { QStringLiteral("encrypt"), &KleopatraApplication::encryptFiles },
320 { QStringLiteral("sign"), &KleopatraApplication::signFiles },
321 { QStringLiteral("encrypt-sign"), &KleopatraApplication::signEncryptFiles },
322 { QStringLiteral("sign-encrypt"), &KleopatraApplication::signEncryptFiles },
323 { QStringLiteral("decrypt"), &KleopatraApplication::decryptFiles },
324 { QStringLiteral("verify"), &KleopatraApplication::verifyFiles },
325 { QStringLiteral("decrypt-verify"), &KleopatraApplication::decryptVerifyFiles }
328 QString found;
329 Q_FOREACH (const QString &opt, funcMap.keys()) {
330 if (parser.isSet(opt) && found.isEmpty()) {
331 found = opt;
332 } else if (parser.isSet(opt)) {
333 return i18n("Ambiguous commands \"%1\" and \"%2\"", found, opt);
337 if (!found.isEmpty()) {
338 if (files.empty()) {
339 return i18n("No files specified for \"%1\" command", found);
341 qCDebug(KLEOPATRA_LOG) << "found" << found;
342 (this->*funcMap.value(found))(files, protocol);
343 } else {
344 if (files.empty()) {
345 if (!isSessionRestored()) {
346 qCDebug(KLEOPATRA_LOG) << "openOrRaiseMainWindow";
347 openOrRaiseMainWindow();
349 } else {
350 return i18n("No command provided but arguments present");
354 return QString();
357 #ifndef QT_NO_SYSTEMTRAYICON
358 const SysTrayIcon *KleopatraApplication::sysTrayIcon() const
360 return &d->sysTray;
363 SysTrayIcon *KleopatraApplication::sysTrayIcon()
365 return &d->sysTray;
367 #endif
369 const MainWindow *KleopatraApplication::mainWindow() const
371 return d->mainWindow;
374 MainWindow *KleopatraApplication::mainWindow()
376 return d->mainWindow;
379 void KleopatraApplication::setMainWindow(MainWindow *mainWindow)
381 if (mainWindow == d->mainWindow) {
382 return;
385 d->disconnectConfigureDialog();
387 d->mainWindow = mainWindow;
388 #ifndef QT_NO_SYSTEMTRAYICON
389 d->sysTray.setMainWindow(mainWindow);
390 #endif
392 d->connectConfigureDialog();
395 static void open_or_raise(QWidget *w)
397 if (w->isMinimized()) {
398 KWindowSystem::unminimizeWindow(w->winId());
399 w->raise();
400 } else if (w->isVisible()) {
401 w->raise();
402 } else {
403 w->show();
407 void KleopatraApplication::toggleMainWindowVisibility()
409 if (mainWindow()) {
410 mainWindow()->setVisible(!mainWindow()->isVisible());
411 } else {
412 openOrRaiseMainWindow();
416 void KleopatraApplication::restoreMainWindow()
418 qCDebug(KLEOPATRA_LOG) << "restoring main window";
420 // Sanity checks
421 if (!isSessionRestored()) {
422 qCDebug(KLEOPATRA_LOG) << "Not in session restore";
423 return;
426 if (mainWindow()) {
427 qCDebug(KLEOPATRA_LOG) << "Already have main window";
428 return;
431 MainWindow *mw = new MainWindow;
432 if (KMainWindow::canBeRestored(1)) {
433 // restore to hidden state, Mainwindow::readProperties() will
434 // restore saved visibility.
435 mw->restore(1, false);
438 mw->setAttribute(Qt::WA_DeleteOnClose);
439 setMainWindow(mw);
440 d->connectConfigureDialog();
444 void KleopatraApplication::openOrRaiseMainWindow()
446 MainWindow *mw = mainWindow();
447 if (!mw) {
448 mw = new MainWindow;
449 mw->setAttribute(Qt::WA_DeleteOnClose);
450 setMainWindow(mw);
451 d->connectConfigureDialog();
453 open_or_raise(mw);
456 void KleopatraApplication::openOrRaiseConfigDialog()
458 if (!d->configureDialog) {
459 d->configureDialog = new ConfigureDialog;
460 d->configureDialog->setAttribute(Qt::WA_DeleteOnClose);
461 d->connectConfigureDialog();
463 open_or_raise(d->configureDialog);
466 #ifndef QT_NO_SYSTEMTRAYICON
467 void KleopatraApplication::startMonitoringSmartCard()
469 d->readerStatus.startMonitoring();
471 #endif // QT_NO_SYSTEMTRAYICON
473 void KleopatraApplication::importCertificatesFromFile(const QStringList &files, GpgME::Protocol /*proto*/)
475 openOrRaiseMainWindow();
476 if (!files.empty()) {
477 mainWindow()->importCertificatesFromFile(files);
481 void KleopatraApplication::encryptFiles(const QStringList &files, GpgME::Protocol proto)
483 SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, 0);
484 cmd->setEncryptionPolicy(Force);
485 cmd->setSigningPolicy(Allow);
486 if (proto != GpgME::UnknownProtocol) {
487 cmd->setProtocol(proto);
489 cmd->start();
492 void KleopatraApplication::signFiles(const QStringList &files, GpgME::Protocol proto)
494 SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, 0);
495 cmd->setSigningPolicy(Force);
496 cmd->setEncryptionPolicy(Deny);
497 if (proto != GpgME::UnknownProtocol) {
498 cmd->setProtocol(proto);
500 cmd->start();
503 void KleopatraApplication::signEncryptFiles(const QStringList &files, GpgME::Protocol proto)
505 SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, 0);
506 if (proto != GpgME::UnknownProtocol) {
507 cmd->setProtocol(proto);
509 cmd->start();
512 void KleopatraApplication::decryptFiles(const QStringList &files, GpgME::Protocol /*proto*/)
514 DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, 0);
515 cmd->setOperation(Decrypt);
516 cmd->start();
519 void KleopatraApplication::verifyFiles(const QStringList &files, GpgME::Protocol /*proto*/)
521 DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, 0);
522 cmd->setOperation(Verify);
523 cmd->start();
526 void KleopatraApplication::decryptVerifyFiles(const QStringList &files, GpgME::Protocol /*proto*/)
528 DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, 0);
529 cmd->start();
532 void KleopatraApplication::setIgnoreNewInstance(bool ignore)
534 d->ignoreNewInstance = ignore;
537 bool KleopatraApplication::ignoreNewInstance() const
539 return d->ignoreNewInstance;