1 /* -*- mode: c++; c-basic-offset:4 -*-
2     systemtrayicon.cpp
3 
4     This file is part of Kleopatra, the KDE keymanager
5     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include <config-kleopatra.h>
11 
12 #include "systrayicon.h"
13 
14 #ifndef QT_NO_SYSTEMTRAYICON
15 
16 #include "mainwindow.h"
17 #include "kleopatraapplication.h"
18 
19 #include <smartcard/readerstatus.h>
20 
21 #include <utils/clipboardmenu.h>
22 
23 #include <commands/importcertificatefromclipboardcommand.h>
24 #include <commands/encryptclipboardcommand.h>
25 #include <commands/signclipboardcommand.h>
26 #include <commands/decryptverifyclipboardcommand.h>
27 #include <commands/setinitialpincommand.h>
28 #include <commands/learncardkeyscommand.h>
29 
30 #include <QIcon>
31 #include <KLocalizedString>
32 #include <KAboutApplicationDialog>
33 #include <KAboutData>
34 #include <KActionMenu>
35 #include <QEventLoopLocker>
36 
37 #include <QMenu>
38 #include <QAction>
39 #include <QApplication>
40 #include <QClipboard>
41 #include <QPointer>
42 #include <QSignalBlocker>
43 
44 
45 using namespace Kleo;
46 using namespace Kleo::Commands;
47 using namespace Kleo::SmartCard;
48 
49 class SysTrayIcon::Private
50 {
51     friend class ::SysTrayIcon;
52     SysTrayIcon *const q;
53 public:
54     explicit Private(SysTrayIcon *qq);
55     ~Private();
56 
57 private:
slotAbout()58     void slotAbout()
59     {
60         if (!aboutDialog) {
61             aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData());
62             aboutDialog->setAttribute(Qt::WA_DeleteOnClose);
63         }
64 
65         if (aboutDialog->isVisible()) {
66             aboutDialog->raise();
67         } else {
68             aboutDialog->show();
69         }
70     }
71 
enableDisableActions()72     void enableDisableActions()
73     {
74         //work around a Qt bug (seen with Qt 4.4.0, Windows): QClipBoard->mimeData() triggers QClipboard::changed(),
75         //triggering slotEnableDisableActions again
76         const QSignalBlocker blocker(QApplication::clipboard());
77         openCertificateManagerAction.setEnabled(!q->mainWindow() || !q->mainWindow()->isVisible());
78         setInitialPinAction.setEnabled(!firstCardWithNullPin.empty());
79         learnCertificatesAction.setEnabled(anyCardCanLearnKeys);
80 
81         q->setAttentionWanted((!firstCardWithNullPin.empty() || anyCardCanLearnKeys) && !q->attentionWindow());
82     }
83 
slotSetInitialPin()84     void slotSetInitialPin()
85     {
86         if (!firstCardWithNullPin.empty()) {
87             auto cmd = new SetInitialPinCommand(firstCardWithNullPin);
88             q->setAttentionWindow(cmd->dialog());
89             startCommand(cmd);
90         }
91     }
92 
slotLearnCertificates()93     void slotLearnCertificates()
94     {
95         auto cmd = new LearnCardKeysCommand(GpgME::CMS);
96         q->setAttentionWindow(cmd->dialog());
97         startCommand(cmd);
98     }
startCommand(Command * cmd)99     void startCommand(Command *cmd)
100     {
101         Q_ASSERT(cmd);
102         cmd->setParent(q->mainWindow());
103         cmd->start();
104     }
105 
106 private:
107     std::string firstCardWithNullPin;
108     bool anyCardCanLearnKeys = false;
109     bool learningInProgress = false;
110 
111     QMenu menu;
112     QAction openCertificateManagerAction;
113     QAction configureAction;
114     QAction aboutAction;
115     QAction quitAction;
116 
117     ClipboardMenu clipboardMenu;
118 
119     QMenu cardMenu;
120     QAction updateCardStatusAction;
121     QAction setInitialPinAction;
122     QAction learnCertificatesAction;
123 
124     QPointer<KAboutApplicationDialog> aboutDialog;
125     QEventLoopLocker eventLoopLocker;
126 };
127 
Private(SysTrayIcon * qq)128 SysTrayIcon::Private::Private(SysTrayIcon *qq)
129     : q(qq),
130       menu(),
131       openCertificateManagerAction(i18n("&Open Certificate Manager..."), q),
132       configureAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("&Configure %1...", KAboutData::applicationData().componentName()), q),
133       aboutAction(QIcon::fromTheme(QStringLiteral("kleopatra")), i18n("&About %1...", KAboutData::applicationData().componentName()), q),
134       quitAction(QIcon::fromTheme(QStringLiteral("application-exit")),i18n("&Shutdown Kleopatra"), q),
135       clipboardMenu(q),
136       cardMenu(i18n("SmartCard")),
137       updateCardStatusAction(i18n("Update Card Status"), q),
138       setInitialPinAction(i18n("Set NetKey v3 Initial PIN..."), q),
139       learnCertificatesAction(i18n("Learn NetKey v3 Card Certificates"), q),
140       aboutDialog()
141 {
142     q->setNormalIcon(QIcon::fromTheme(QStringLiteral("kleopatra")));
143     q->setAttentionIcon(QIcon::fromTheme(QStringLiteral("auth-sim-locked")));
144 
145     KDAB_SET_OBJECT_NAME(menu);
146     KDAB_SET_OBJECT_NAME(openCertificateManagerAction);
147     KDAB_SET_OBJECT_NAME(configureAction);
148     KDAB_SET_OBJECT_NAME(aboutAction);
149     KDAB_SET_OBJECT_NAME(quitAction);
150     KDAB_SET_OBJECT_NAME(clipboardMenu);
151     KDAB_SET_OBJECT_NAME(cardMenu);
152     KDAB_SET_OBJECT_NAME(setInitialPinAction);
153     KDAB_SET_OBJECT_NAME(learnCertificatesAction);
154 
155     connect(&openCertificateManagerAction, SIGNAL(triggered()), qApp, SLOT(openOrRaiseMainWindow()));
156     connect(&configureAction, SIGNAL(triggered()), qApp, SLOT(openOrRaiseConfigDialog()));
157     connect(&aboutAction, SIGNAL(triggered()), q, SLOT(slotAbout()));
158     connect(&quitAction, &QAction::triggered, QCoreApplication::instance(), &QCoreApplication::quit);
159     connect(&updateCardStatusAction, &QAction::triggered, ReaderStatus::instance(), &ReaderStatus::updateStatus);
160     connect(&setInitialPinAction, SIGNAL(triggered()), q, SLOT(slotSetInitialPin()));
161     connect(&learnCertificatesAction, SIGNAL(triggered()), q, SLOT(slotLearnCertificates()));
162 
163     connect(QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)),
164             q, SLOT(slotEnableDisableActions()));
165 
166     menu.addAction(&openCertificateManagerAction);
167     menu.addAction(&configureAction);
168     menu.addAction(&aboutAction);
169     menu.addSeparator();
170     menu.addMenu(clipboardMenu.clipboardMenu()->menu());
171     menu.addSeparator();
172     menu.addMenu(&cardMenu);
173     cardMenu.addAction(&updateCardStatusAction);
174     cardMenu.addAction(&setInitialPinAction);
175     cardMenu.addAction(&learnCertificatesAction);
176     menu.addSeparator();
177     menu.addAction(&quitAction);
178 
179     q->setContextMenu(&menu);
180     clipboardMenu.setMainWindow(q->mainWindow());
181 }
182 
~Private()183 SysTrayIcon::Private::~Private() {}
184 
SysTrayIcon(QObject * p)185 SysTrayIcon::SysTrayIcon(QObject *p)
186     : SystemTrayIcon(p), d(new Private(this))
187 {
188     slotEnableDisableActions();
189 }
190 
~SysTrayIcon()191 SysTrayIcon::~SysTrayIcon()
192 {
193 }
194 
mainWindow() const195 MainWindow *SysTrayIcon::mainWindow() const
196 {
197     return static_cast<MainWindow *>(SystemTrayIcon::mainWindow());
198 }
199 
attentionWindow() const200 QDialog *SysTrayIcon::attentionWindow() const
201 {
202     return static_cast<QDialog *>(SystemTrayIcon::attentionWindow());
203 }
204 
doActivated()205 void SysTrayIcon::doActivated()
206 {
207     if (const QWidget *const aw = attentionWindow())
208         if (aw->isVisible()) {
209             return;    // ignore clicks while an attention window is open.
210         }
211     if (!d->firstCardWithNullPin.empty()) {
212         d->slotSetInitialPin();
213     } else if (d->anyCardCanLearnKeys) {
214         d->slotLearnCertificates();
215     } else {
216         // Toggle visibility of MainWindow
217         KleopatraApplication::instance()->toggleMainWindowVisibility();
218     }
219 }
220 
setFirstCardWithNullPin(const std::string & serialNumber)221 void SysTrayIcon::setFirstCardWithNullPin(const std::string &serialNumber)
222 {
223     if (d->firstCardWithNullPin == serialNumber) {
224         return;
225     }
226     d->firstCardWithNullPin = serialNumber;
227     slotEnableDisableActions();
228 }
229 
setAnyCardCanLearnKeys(bool on)230 void SysTrayIcon::setAnyCardCanLearnKeys(bool on)
231 {
232     if (d->anyCardCanLearnKeys == on || d->learningInProgress) {
233         return;
234     }
235     d->anyCardCanLearnKeys = on;
236     slotEnableDisableActions();
237 }
238 
slotEnableDisableActions()239 void SysTrayIcon::slotEnableDisableActions()
240 {
241     d->enableDisableActions();
242 }
243 
244 /* We need this as the readerstatus might update even
245  * while the loading is in progress. */
setLearningInProgress(bool value)246 void SysTrayIcon::setLearningInProgress(bool value)
247 {
248     if (value) {
249         setAnyCardCanLearnKeys(false);
250     }
251     d->learningInProgress = value;
252 }
253 
254 #include "moc_systrayicon.cpp"
255 
256 #endif // QT_NO_SYSTEMTRAYICON
257