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
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>
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>
71 #include <QCommandLineOption>
73 #include <boost/shared_ptr.hpp>
76 #include <KSharedConfig>
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");
95 class KleopatraApplication::Private
97 friend class ::KleopatraApplication
;
98 KleopatraApplication
*const q
;
100 explicit Private(KleopatraApplication
*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
);
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()));
133 bool ignoreNewInstance
;
134 QPointer
<ConfigureDialog
> configureDialog
;
135 QPointer
<MainWindow
> mainWindow
;
136 SmartCard::ReaderStatus readerStatus
;
137 #ifndef QT_NO_SYSTEMTRAYICON
140 shared_ptr
<KeyCache
> keyCache
;
142 shared_ptr
<FileSystemWatcher
> watcher
;
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
);
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()) {
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";
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());
192 KleopatraApplication::KleopatraApplication(int &argc
, char *argv
[])
193 : QApplication(argc
, argv
), d(new Private(this))
198 #ifndef QT_NO_SYSTEMTRAYICON
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();
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
);
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
229 parser
.parse(QStringList() << QCoreApplication::applicationFilePath());
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";
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()) {
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
286 if (parser
.isSet(QStringLiteral("parent-windowid"))) {
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());
293 parentId
= parser
.value(QStringLiteral("parent-windowid")).toUInt();
297 // Search for local keys
298 const GpgME::Key
&key
= Kleo::KeyCache::instance()->findByKeyIDOrFingerprint(fingerPrint
.toLocal8Bit().data());
300 // Show external search dialog
301 LookupCertificatesCommand
*const cmd
= new LookupCertificatesCommand(fingerPrint
, 0);
303 cmd
->setParentWId(parentId
);
308 DetailsCommand
*const cmd
= new DetailsCommand(key
, 0);
310 cmd
->setParentWId(parentId
);
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
}
329 Q_FOREACH (const QString
&opt
, funcMap
.keys()) {
330 if (parser
.isSet(opt
) && found
.isEmpty()) {
332 } else if (parser
.isSet(opt
)) {
333 return i18n("Ambiguous commands \"%1\" and \"%2\"", found
, opt
);
337 if (!found
.isEmpty()) {
339 return i18n("No files specified for \"%1\" command", found
);
341 qCDebug(KLEOPATRA_LOG
) << "found" << found
;
342 (this->*funcMap
.value(found
))(files
, protocol
);
345 if (!isSessionRestored()) {
346 qCDebug(KLEOPATRA_LOG
) << "openOrRaiseMainWindow";
347 openOrRaiseMainWindow();
350 return i18n("No command provided but arguments present");
357 #ifndef QT_NO_SYSTEMTRAYICON
358 const SysTrayIcon
*KleopatraApplication::sysTrayIcon() const
363 SysTrayIcon
*KleopatraApplication::sysTrayIcon()
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
) {
385 d
->disconnectConfigureDialog();
387 d
->mainWindow
= mainWindow
;
388 #ifndef QT_NO_SYSTEMTRAYICON
389 d
->sysTray
.setMainWindow(mainWindow
);
392 d
->connectConfigureDialog();
395 static void open_or_raise(QWidget
*w
)
397 if (w
->isMinimized()) {
398 KWindowSystem::unminimizeWindow(w
->winId());
400 } else if (w
->isVisible()) {
407 void KleopatraApplication::toggleMainWindowVisibility()
410 mainWindow()->setVisible(!mainWindow()->isVisible());
412 openOrRaiseMainWindow();
416 void KleopatraApplication::restoreMainWindow()
418 qCDebug(KLEOPATRA_LOG
) << "restoring main window";
421 if (!isSessionRestored()) {
422 qCDebug(KLEOPATRA_LOG
) << "Not in session restore";
427 qCDebug(KLEOPATRA_LOG
) << "Already have main window";
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
);
440 d
->connectConfigureDialog();
444 void KleopatraApplication::openOrRaiseMainWindow()
446 MainWindow
*mw
= mainWindow();
449 mw
->setAttribute(Qt::WA_DeleteOnClose
);
451 d
->connectConfigureDialog();
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
);
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
);
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
);
512 void KleopatraApplication::decryptFiles(const QStringList
&files
, GpgME::Protocol
/*proto*/)
514 DecryptVerifyFilesCommand
*const cmd
= new DecryptVerifyFilesCommand(files
, 0);
515 cmd
->setOperation(Decrypt
);
519 void KleopatraApplication::verifyFiles(const QStringList
&files
, GpgME::Protocol
/*proto*/)
521 DecryptVerifyFilesCommand
*const cmd
= new DecryptVerifyFilesCommand(files
, 0);
522 cmd
->setOperation(Verify
);
526 void KleopatraApplication::decryptVerifyFiles(const QStringList
&files
, GpgME::Protocol
/*proto*/)
528 DecryptVerifyFilesCommand
*const cmd
= new DecryptVerifyFilesCommand(files
, 0);
532 void KleopatraApplication::setIgnoreNewInstance(bool ignore
)
534 d
->ignoreNewInstance
= ignore
;
537 bool KleopatraApplication::ignoreNewInstance() const
539 return d
->ignoreNewInstance
;