1 /*
2  * Copyright (c) 2017-2018 Nitrokey UG
3  *
4  * This file is part of Nitrokey App.
5  *
6  * Nitrokey App is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * any later version.
10  *
11  * Nitrokey App is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Nitrokey App. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * SPDX-License-Identifier: GPL-3.0
20  */
21 
22 /*
23 TODO
24 1. remove doubled debug
25 2. remove stick10 changing pin
26 */
27 
28 #include "Tray.h"
29 #include <QThread>
30 #include "libada.h"
31 #include "bool_values.h"
32 #include "nitrokey-applet.h"
33 #include <libnitrokey/NitrokeyManager.h>
34 #include <QMenu>
35 #include <QMenuBar>
36 #include "graphicstools.h"
37 
Tray(QObject * _parent,bool _debug_mode,bool _extended_config,StorageActions * actions)38 Tray::Tray(QObject *_parent, bool _debug_mode, bool _extended_config,
39            StorageActions *actions) :
40     QObject(_parent),
41     trayMenu(nullptr),
42     trayMenuPasswdSubMenu(nullptr),
43     file_menu(nullptr),
44     worker(nullptr)
45 {
46   main_window = _parent;
47   storageActions = actions;
48   debug_mode = _debug_mode;
49   ExtendedConfigActive = _extended_config;
50 
51   createIndicator();
52   initActionsForStick10();
53   initActionsForStick20();
54   initCommonActions();
55 
56   mapper_TOTP = new QSignalMapper(this);
57   mapper_HOTP = new QSignalMapper(this);
58   mapper_PWS = new QSignalMapper(this);
59   connect(mapper_TOTP, SIGNAL(mapped(int)), main_window, SLOT(getTOTPDialog(int)));
60   connect(mapper_HOTP, SIGNAL(mapped(int)), main_window, SLOT(getHOTPDialog(int)));
61   connect(mapper_PWS, SIGNAL(mapped(int)), main_window, SLOT(PWS_ExceClickedSlot(int)));
62 }
63 
setDebug_mode(bool _debug_mode)64 void Tray::setDebug_mode(bool _debug_mode) {
65   debug_mode = _debug_mode;
66 }
67 
~Tray()68 Tray::~Tray() {
69   destroyThread();
70 }
71 
delayedShowIndicator()72 void Tray::delayedShowIndicator(){
73   if(!trayIcon->isSystemTrayAvailable()) return;
74   trayIcon->show();
75   delayedShowTimer->stop();
76 }
77 
78 /*
79  * Create the tray menu.
80  */
createIndicator()81 void Tray::createIndicator() {
82   trayIcon = new QSystemTrayIcon(this);
83   trayIcon->setIcon(GraphicsTools::loadColorize(":/images/new/icon_NK.svg", true));
84   connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this,
85           SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
86 
87   delayedShowTimer = new QTimer(this);
88   connect(delayedShowTimer, SIGNAL(timeout()), this, SLOT(delayedShowIndicator()));
89   delayedShowTimer->setSingleShot(false);
90   delayedShowTimer->start(2000);
91 
92   // Initial message
93   if (debug_mode)
94     showTrayMessage("Nitrokey App", tr("Active (debug mode)"), INFORMATION, TRAY_MSG_TIMEOUT);
95   else
96     showTrayMessage("Nitrokey App", tr("Active"), INFORMATION, TRAY_MSG_TIMEOUT);
97 }
98 
showTrayMessage(QString message)99 void Tray::showTrayMessage(QString message) {
100   showTrayMessage("Nitrokey App", message, INFORMATION, 2000);
101 }
102 
showTrayMessage(const QString & title,const QString & msg,enum trayMessageType type,int timeout)103 void Tray::showTrayMessage(const QString &title, const QString &msg,
104                                  enum trayMessageType type, int timeout) {
105   if(debug_mode)
106     qDebug() << msg;
107   if (trayIcon->supportsMessages()) {
108     switch (type) {
109       case INFORMATION:
110         trayIcon->showMessage(title, msg, QSystemTrayIcon::Information, timeout);
111         break;
112       case WARNING:
113         trayIcon->showMessage(title, msg, QSystemTrayIcon::Warning, timeout);
114         break;
115       case CRITICAL:
116         trayIcon->showMessage(title, msg, QSystemTrayIcon::Critical, timeout);
117         break;
118     }
119   } else
120     csApplet()->messageBox(msg);
121 }
122 
iconActivated(QSystemTrayIcon::ActivationReason reason)123 void Tray::iconActivated(QSystemTrayIcon::ActivationReason reason) {
124 
125   switch (reason) {
126     case QSystemTrayIcon::Context:
127 // trayMenu->close();
128 #ifdef Q_OS_MAC
129       trayMenu->popup(QCursor::pos());
130 #endif
131       break;
132     case QSystemTrayIcon::Trigger:
133 #ifndef Q_OS_MAC
134       trayMenu->popup(QCursor::pos());
135 #endif
136       break;
137     case QSystemTrayIcon::DoubleClick:
138       break;
139     case QSystemTrayIcon::MiddleClick:
140       break;
141     default:;
142   }
143 }
144 
eventFilter(QObject * obj,QEvent * event)145 bool Tray::eventFilter(QObject *obj, QEvent *event) {
146   if (event->type() == QEvent::MouseButtonPress) {
147     QMouseEvent *mEvent = static_cast<QMouseEvent *>(event);
148 
149     if (mEvent->button() == Qt::LeftButton) {
150       /*
151          QMouseEvent my_event = new QMouseEvent ( mEvent->type(), mEvent->pos(),
152          Qt::Rightbutton , mEvent->buttons(), mEvent->modifiers() );
153          QCoreApplication::postEvent ( trayIcon, my_event ); */
154       return true;
155     }
156   }
157   return QObject::eventFilter(obj, event);
158 }
159 
generateMenu(bool init,std::function<void (QMenu *)> run_before)160 void Tray::generateMenu(bool init, std::function<void(QMenu *)> run_before) {
161     static QMutex mtx;
162     QMutexLocker locker(&mtx);
163 
164     if (nullptr == trayMenu)
165       trayMenu = std::make_shared<QMenu>();
166     else
167       trayMenu->clear(); // Clear old menu
168 
169     if (nullptr == windowMenu)
170       windowMenu = std::make_shared<QMenu>("Menu");
171     else
172       windowMenu->clear(); // Clear old menu
173 
174 
175     run_before(trayMenu.get());
176 
177     if (!init){
178       // Setup the new menu
179       if (!libada::i()->isDeviceConnected()) {
180         trayMenu->addAction(tr("Nitrokey is not connected!"));
181       } else {
182         if (!libada::i()->isStorageDeviceConnected()) // Nitrokey Pro connected
183           generateMenuForProDevice();
184         else {
185           // Nitrokey Storage is connected
186           generateMenuForStorageDevice();
187         }
188       }
189     }
190 
191     // Add debug window ?
192     if (debug_mode){
193       trayMenu->addAction(DebugAction);
194       windowMenu->addAction(DebugAction);
195     }
196 
197     trayMenu->addSeparator();
198 
199     if (!long_operation_in_progress)
200         trayMenu->addAction(ShowWindowAction);
201 
202     trayMenu->addAction(ActionHelp_tray);
203     trayMenu->addAction(ActionAboutDialog_tray);
204     trayMenu->addAction(quitAction_tray);
205 
206     windowMenu->addSeparator();
207     windowMenu->addAction(ActionHelp);
208     windowMenu->addAction(ActionAboutDialog);
209     windowMenu->addAction(quitAction);
210 
211     trayIcon->setContextMenu(trayMenu.get());
212 
213     if (file_menu != nullptr && windowMenu != nullptr){
214 //      file_menu->addMenu(windowMenu.get()); // does not work for macOS
215       file_menu->addAction(windowMenu->menuAction());
216     }
217   }
218 
initActionsForStick10()219 void Tray::initActionsForStick10() {
220   UnlockPasswordSafeAction = new QAction(tr("Unlock password safe"), main_window);
221   UnlockPasswordSafeAction->setIcon(GraphicsTools::loadColorize(":/images/new/icon_safe.svg"));
222   connect(UnlockPasswordSafeAction, SIGNAL(triggered()), main_window, SLOT(PWS_Clicked_EnablePWSAccess()));
223 
224   UnlockPasswordSafeAction_tray = new QAction(tr("Unlock password safe"), main_window);
225   UnlockPasswordSafeAction_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_safe.svg", true));
226   connect(UnlockPasswordSafeAction_tray, SIGNAL(triggered()), main_window, SLOT(PWS_Clicked_EnablePWSAccess()));
227 
228   configureAction = new QAction(tr("&OTP"), main_window);
229   connect(configureAction, SIGNAL(triggered()), main_window, SLOT(startConfiguration()));
230 
231   resetAction = new QAction(tr("&Factory reset"), main_window);
232   connect(resetAction, SIGNAL(triggered()), main_window, SLOT(factoryResetAction()));
233 
234   Stick10ActionChangeUserPIN = new QAction(tr("&Change User PIN"), main_window);
235   connect(Stick10ActionChangeUserPIN, SIGNAL(triggered()), main_window,
236           SLOT(startStick20ActionChangeUserPIN()));
237 
238   Stick10ActionChangeAdminPIN = new QAction(tr("&Change Admin PIN"), main_window);
239   connect(Stick10ActionChangeAdminPIN, SIGNAL(triggered()), main_window,
240           SLOT(startStick20ActionChangeAdminPIN()));
241 }
242 
initCommonActions()243 void Tray::initCommonActions() {
244   DebugAction = new QAction(tr("&Debug"), main_window);
245   connect(DebugAction, SIGNAL(triggered()), main_window, SLOT(startStickDebug()));
246 
247   ShowWindowAction = new QAction(tr("&Overview"), main_window);
248   connect(ShowWindowAction, SIGNAL(triggered()), main_window, SLOT(startConfigurationMain()));
249 
250   quitAction_tray = new QAction(tr("&Quit"), main_window);
251   quitAction_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_quit.svg", true));
252   connect(quitAction_tray, SIGNAL(triggered()), qApp, SLOT(quit()));
253 
254   quitAction = new QAction(tr("&Quit"), main_window);
255   quitAction->setIcon(GraphicsTools::loadColorize(":/images/new/icon_quit.svg"));
256   connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
257 
258   ActionHelp = new QAction(tr("&Help"), main_window);
259   ActionHelp->setIcon(GraphicsTools::loadColorize(":/images/new/icon_fragezeichen.svg"));
260   connect(ActionHelp, SIGNAL(triggered()), main_window, SLOT(startHelpAction()));
261 
262   ActionHelp_tray = new QAction(tr("&Help"), main_window);
263   ActionHelp_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_fragezeichen.svg", true));
264   connect(ActionHelp_tray, SIGNAL(triggered()), main_window, SLOT(startHelpAction()));
265 
266   ActionAboutDialog = new QAction(tr("&About Nitrokey"), main_window);
267   ActionAboutDialog->setIcon(GraphicsTools::loadColorize(":/images/new/icon_about_nitrokey.svg"));
268   connect(ActionAboutDialog, SIGNAL(triggered()), main_window, SLOT(startAboutDialog()));
269 
270   ActionAboutDialog_tray = new QAction(tr("&About Nitrokey"), main_window);
271   ActionAboutDialog_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_about_nitrokey.svg", true));
272   connect(ActionAboutDialog_tray, SIGNAL(triggered()), main_window, SLOT(startAboutDialog()));
273 }
274 
initActionsForStick20()275 void Tray::initActionsForStick20() {
276   configureActionStick20 = new QAction(tr("&OTP and Password safe"), main_window);
277   connect(configureActionStick20, SIGNAL(triggered()), main_window, SLOT(startConfiguration()));
278 
279   Stick20ActionEnableCryptedVolume = new QAction(tr("&Unlock encrypted volume"), main_window);
280   Stick20ActionEnableCryptedVolume->setIcon(GraphicsTools::loadColorize(":/images/new/icon_harddrive.svg"));
281   connect(Stick20ActionEnableCryptedVolume, SIGNAL(triggered()), storageActions,
282           SLOT(startStick20EnableCryptedVolume()));
283 
284   Stick20ActionDisableCryptedVolume = new QAction(tr("&Lock encrypted volume"), main_window);
285   Stick20ActionDisableCryptedVolume->setIcon(GraphicsTools::loadColorize(":/images/new/icon_harddrive.svg"));
286   connect(Stick20ActionDisableCryptedVolume, SIGNAL(triggered()), storageActions,
287           SLOT(startStick20DisableCryptedVolume()));
288 
289   Stick20ActionEnableHiddenVolume = new QAction(tr("&Unlock hidden volume"), main_window);
290   Stick20ActionEnableHiddenVolume->setIcon(GraphicsTools::loadColorize(":/images/new/icon_harddrive.svg"));
291   connect(Stick20ActionEnableHiddenVolume, SIGNAL(triggered()), storageActions,
292           SLOT(startStick20EnableHiddenVolume()));
293 
294   Stick20ActionDisableHiddenVolume = new QAction(tr("&Lock hidden volume"), main_window);
295   Stick20ActionDisableHiddenVolume->setIcon(GraphicsTools::loadColorize(":/images/new/icon_harddrive.svg"));
296   connect(Stick20ActionDisableHiddenVolume, SIGNAL(triggered()), storageActions,
297           SLOT(startStick20DisableHiddenVolume()));
298 
299 
300   Stick20ActionEnableCryptedVolume_tray = new QAction(tr("&Unlock encrypted volume"), main_window);
301   Stick20ActionEnableCryptedVolume_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_harddrive.svg", true));
302   connect(Stick20ActionEnableCryptedVolume_tray, SIGNAL(triggered()), storageActions,
303           SLOT(startStick20EnableCryptedVolume()));
304 
305   Stick20ActionDisableCryptedVolume_tray = new QAction(tr("&Lock encrypted volume"), main_window);
306   Stick20ActionDisableCryptedVolume_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_harddrive.svg", true));
307   connect(Stick20ActionDisableCryptedVolume_tray, SIGNAL(triggered()), storageActions,
308           SLOT(startStick20DisableCryptedVolume()));
309 
310   Stick20ActionEnableHiddenVolume_tray = new QAction(tr("&Unlock hidden volume"), main_window);
311   Stick20ActionEnableHiddenVolume_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_harddrive.svg", true));
312   connect(Stick20ActionEnableHiddenVolume_tray, SIGNAL(triggered()), storageActions,
313           SLOT(startStick20EnableHiddenVolume()));
314 
315   Stick20ActionDisableHiddenVolume_tray = new QAction(tr("&Lock hidden volume"), main_window);
316   Stick20ActionDisableHiddenVolume_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_harddrive.svg", true));
317   connect(Stick20ActionDisableHiddenVolume_tray, SIGNAL(triggered()), storageActions,
318           SLOT(startStick20DisableHiddenVolume()));
319 
320   Stick20ActionChangeUserPIN = new QAction(tr("&Change User PIN"), main_window);
321   connect(Stick20ActionChangeUserPIN, SIGNAL(triggered()), main_window,
322           SLOT(startStick20ActionChangeUserPIN()));
323 
324   Stick20ActionChangeAdminPIN = new QAction(tr("&Change Admin PIN"), main_window);
325   connect(Stick20ActionChangeAdminPIN, SIGNAL(triggered()), main_window,
326           SLOT(startStick20ActionChangeAdminPIN()));
327 
328   Stick20ActionChangeUpdatePIN = new QAction(tr("&Change Firmware Password"), main_window);
329   connect(Stick20ActionChangeUpdatePIN, SIGNAL(triggered()), main_window,
330           SLOT(startStick20ActionChangeUpdatePIN()));
331 
332   Stick20ActionEnableFirmwareUpdate = new QAction(tr("&Enable firmware update"), main_window);
333   connect(Stick20ActionEnableFirmwareUpdate, SIGNAL(triggered()), storageActions,
334           SLOT(startStick20EnableFirmwareUpdate()));
335 
336   Stick20ActionExportFirmwareToFile = new QAction(tr("&Export firmware to file"), main_window);
337   connect(Stick20ActionExportFirmwareToFile, SIGNAL(triggered()), storageActions,
338           SLOT(startStick20ExportFirmwareToFile()));
339 
340   QSignalMapper *signalMapper_startStick20DestroyCryptedVolume =
341       new QSignalMapper(main_window);
342 
343   Stick20ActionDestroyCryptedVolume = new QAction(tr("&Destroy encrypted data"), main_window);
344   signalMapper_startStick20DestroyCryptedVolume->setMapping(Stick20ActionDestroyCryptedVolume, 0);
345   connect(Stick20ActionDestroyCryptedVolume, SIGNAL(triggered()),
346           signalMapper_startStick20DestroyCryptedVolume, SLOT(map()));
347 
348   Stick20ActionInitCryptedVolume = new QAction(tr("&Initialize device"), main_window);
349   signalMapper_startStick20DestroyCryptedVolume->setMapping(Stick20ActionInitCryptedVolume, 1);
350   connect(Stick20ActionInitCryptedVolume, SIGNAL(triggered()),
351           signalMapper_startStick20DestroyCryptedVolume, SLOT(map()));
352 
353   connect(signalMapper_startStick20DestroyCryptedVolume, SIGNAL(mapped(int)), storageActions,
354           SLOT(startStick20DestroyCryptedVolume(int)));
355 
356   Stick20ActionFillSDCardWithRandomChars =
357       new QAction(tr("&Initialize storage with random data"), main_window);
358   connect(Stick20ActionFillSDCardWithRandomChars, SIGNAL(triggered()), storageActions,
359           SLOT(startStick20FillSDCardWithRandomChars()));
360 
361   Stick20ActionSetReadonlyUncryptedVolume =
362       new QAction(tr("&Set unencrypted volume read-only"), main_window);
363   connect(Stick20ActionSetReadonlyUncryptedVolume, SIGNAL(triggered()), storageActions,
364           SLOT(startStick20SetReadOnlyUncryptedVolume()));
365 
366   Stick20ActionSetReadWriteUncryptedVolume =
367       new QAction(tr("&Set unencrypted volume read-write"), main_window);
368   connect(Stick20ActionSetReadWriteUncryptedVolume, SIGNAL(triggered()), storageActions,
369           SLOT(startStick20SetReadWriteUncryptedVolume()));
370 
371   //do not show until supported
372 #if 0
373   Stick20ActionSetReadonlyEncryptedVolume =
374       new QAction(tr("&Set encrypted volume read-only"), main_window);
375   connect(Stick20ActionSetReadonlyEncryptedVolume, SIGNAL(triggered()), storageActions,
376           SLOT(startStick20SetReadOnlyEncryptedVolume()));
377 
378   Stick20ActionSetReadWriteEncryptedVolume =
379       new QAction(tr("&Set encrypted volume read-write"), main_window);
380   connect(Stick20ActionSetReadWriteEncryptedVolume, SIGNAL(triggered()), storageActions,
381           SLOT(startStick20SetReadWriteEncryptedVolume()));
382 #endif
383 
384 //  Stick20ActionDebugAction = new QAction(tr("&Debug"), main_window);
385 //  connect(Stick20ActionDebugAction, SIGNAL(triggered()), storageActions, SLOT(startStick20DebugAction()));
386 
387   Stick20ActionSetupHiddenVolume = new QAction(tr("&Setup hidden volume"), main_window);
388   connect(Stick20ActionSetupHiddenVolume, SIGNAL(triggered()), storageActions,
389           SLOT(startStick20SetupHiddenVolume()));
390 
391   Stick20ActionClearNewSDCardFound =
392       new QAction(tr("&Disable 'initialize storage with random data' warning"), main_window);
393   connect(Stick20ActionClearNewSDCardFound, SIGNAL(triggered()), storageActions,
394           SLOT(startStick20ClearNewSdCardFound()));
395 
396   Stick20ActionLockStickHardware = new QAction(tr("&Lock stick hardware"), main_window);
397   connect(Stick20ActionLockStickHardware, SIGNAL(triggered()), storageActions,
398           SLOT(startStick20LockStickHardware()));
399 
400   Stick20ActionResetUserPassword = new QAction(tr("&Reset User PIN"), main_window);
401   connect(Stick20ActionResetUserPassword, SIGNAL(triggered()), main_window,
402           SLOT(startResetUserPassword()));
403 
404   LockDeviceAction = new QAction(tr("&Lock Device"), main_window);
405   LockDeviceAction->setIcon(GraphicsTools::loadColorize(":/images/new/icon_unsafe.svg"));
406   connect(LockDeviceAction, SIGNAL(triggered()), main_window, SLOT(startLockDeviceAction()));
407 
408   Stick20ActionUpdateStickStatus = new QAction(tr("Smartcard or SD card are not ready"), main_window);
409   connect(Stick20ActionUpdateStickStatus, SIGNAL(triggered()), main_window, SLOT(startAboutDialog()));
410 }
411 
412 
413 std::shared_ptr<QThread> thread_tray_populateOTP;
414 
doWork()415 void tray_Worker::doWork() {
416   QMutexLocker mutexLocker(&mtx);
417   auto passwordSafeUnlocked = libada::i()->isPasswordSafeUnlocked();
418   const auto total = TOTP_SLOT_COUNT+HOTP_SLOT_COUNT+
419       (passwordSafeUnlocked?PWS_SLOT_COUNT:0)+1;
420   int p = 0;
421   emit progress(0);
422 
423   //populate OTP name cache
424   for (int i=0; i < TOTP_SLOT_COUNT; i++){
425     auto slotName = libada::i()->getTOTPSlotName(i);
426     emit progress(++p * 100 / total);
427   }
428 
429   for (int i=0; i<HOTP_SLOT_COUNT; i++){
430     auto slotName = libada::i()->getHOTPSlotName(i);
431     emit progress(++p * 100 / total);
432   }
433 
434   if(passwordSafeUnlocked){
435     for (int i=0; i<PWS_SLOT_COUNT; i++){
436       if(libada::i()->getPWSSlotStatus(i))
437         auto slotName = libada::i()->getPWSSlotName(i);
438       emit progress(++p * 100 / total);
439     }
440   }
441 
442   emit progress(100);
443   emit resultReady();
444 }
445 
generatePasswordMenu()446 void Tray::generatePasswordMenu() {
447   trayMenuPasswdSubMenu = std::make_shared<QMenu>(tr("Passwords"));
448   trayMenuPasswdSubMenu->setIcon(GraphicsTools::loadColorize(":/images/new/icon_passwords.svg"));
449 
450   trayMenuPasswdSubMenu_tray = std::make_shared<QMenu>(tr("Passwords"));
451   trayMenuPasswdSubMenu_tray->setIcon(GraphicsTools::loadColorize(":/images/new/icon_passwords.svg", true));
452 
453 
454   trayMenu->addMenu(trayMenuPasswdSubMenu_tray.get());
455   trayMenu->addSeparator();
456 
457   windowMenu->addMenu(trayMenuPasswdSubMenu.get());
458   windowMenu->addSeparator();
459 
460   if (thread_tray_populateOTP!= nullptr){
461     destroyThread();
462   }
463   thread_tray_populateOTP = std::make_shared<QThread>();
464   worker = new tray_Worker;
465   worker->moveToThread(thread_tray_populateOTP.get());
466   connect(thread_tray_populateOTP.get(), &QThread::finished, worker, &QObject::deleteLater);
467   connect(thread_tray_populateOTP.get(), SIGNAL(started()), worker, SLOT(doWork()));
468   connect(worker, SIGNAL(resultReady()), this, SLOT(populateOTPPasswordMenu()));
469 
470   connect(worker, SIGNAL(resultReady()), main_window, SLOT(generateComboBoxEntrys()));
471   connect(worker, SIGNAL(progress(int)), this, SLOT(passOTPProgressFurther(int)));
472   connect(worker, SIGNAL(progress(int)), this, SLOT(showOTPProgressInTray(int)));
473 
474   thread_tray_populateOTP->start();
475 }
476 
destroyThread()477 void Tray::destroyThread() {
478   if (thread_tray_populateOTP == nullptr) return;
479   thread_tray_populateOTP->quit();
480   thread_tray_populateOTP->wait();
481 }
482 
483 #include "mainwindow.h"
populateOTPPasswordMenu()484 void Tray::populateOTPPasswordMenu() {
485   //should not run before worker is done
486   QMutexLocker mutexLocker(&worker->mtx);
487 
488   if (!trayMenuPasswdSubMenu->actions().empty()) {
489     return;
490   }
491 
492 
493   for (int i=0; i < TOTP_SLOT_COUNT; i++){
494     auto slotName = libada::i()->getTOTPSlotName(i);
495     bool slotProgrammed = libada::i()->isTOTPSlotProgrammed(i);
496     if (slotProgrammed){
497       auto action = trayMenuPasswdSubMenu->addAction(QString::fromStdString(slotName));
498       connect(action, SIGNAL(triggered()), mapper_TOTP, SLOT(map()));
499       mapper_TOTP->setMapping(action, i) ;
500     }
501   }
502 
503   for (int i=0; i<HOTP_SLOT_COUNT; i++){
504     auto slotName = libada::i()->getHOTPSlotName(i);
505     bool slotProgrammed = libada::i()->isHOTPSlotProgrammed(i);
506     if (slotProgrammed){
507       auto action = trayMenuPasswdSubMenu->addAction(QString::fromStdString(slotName));
508       connect(action, SIGNAL(triggered()), mapper_HOTP, SLOT(map()));
509       mapper_HOTP->setMapping(action, i) ;
510     }
511   }
512 
513   if (TRUE == libada::i()->isPasswordSafeUnlocked()) {
514     for (int i = 0; i < PWS_SLOT_COUNT; i++) {
515       if (libada::i()->getPWSSlotStatus(i)) {
516         auto slotName = libada::i()->getPWSSlotName(i);
517         auto action = trayMenuPasswdSubMenu->addAction(QString::fromStdString(slotName));
518         connect(action, SIGNAL(triggered()), mapper_PWS, SLOT(map()));
519         mapper_PWS->setMapping(action, i) ;
520       }
521     }
522   }
523 
524   if (trayMenuPasswdSubMenu->actions().empty()) {
525     trayMenuPasswdSubMenu->setEnabled(false);
526     trayMenuPasswdSubMenu->setTitle( trayMenuPasswdSubMenu->title() + " " + tr("(empty)") );
527   }
528 
529   trayMenuPasswdSubMenu_tray->addActions(trayMenuPasswdSubMenu->actions());
530 
531   if (trayMenuPasswdSubMenu_tray->actions().empty()) {
532     trayMenuPasswdSubMenu_tray->setEnabled(false);
533     trayMenuPasswdSubMenu_tray->setTitle( trayMenuPasswdSubMenu_tray->title() + " " + tr("(empty)") );
534   }
535 }
536 
537 
generateMenuForProDevice()538 void Tray::generateMenuForProDevice() {
539     generatePasswordMenu();
540     trayMenu->addSeparator();
541     generateMenuPasswordSafe();
542 
543     trayMenuSubConfigure = trayMenu->addMenu(tr("Configure"));
544     trayMenuSubConfigure->setIcon(GraphicsTools::loadColorize(":/images/new/icon_settings.svg"));
545 
546     if (TRUE == libada::i()->isPasswordSafeAvailable())
547       trayMenuSubConfigure->addAction(configureActionStick20);
548     else
549       trayMenuSubConfigure->addAction(configureAction);
550 
551     trayMenuSubConfigure->addSeparator();
552 
553     trayMenuSubConfigure->addAction(Stick10ActionChangeUserPIN);
554     trayMenuSubConfigure->addAction(Stick10ActionChangeAdminPIN);
555 
556     // Enable "reset user PIN" ?
557     if (0 == libada::i()->getUserPasswordRetryCount())
558     {
559       trayMenuSubConfigure->addAction(Stick20ActionResetUserPassword);
560     }
561 
562     if (ExtendedConfigActive) {
563       trayMenuSubConfigure->addSeparator();
564       trayMenuSubConfigure->addAction(resetAction);
565     }
566     windowMenu->addMenu(trayMenuSubConfigure);
567 }
568 using nm = nitrokey::NitrokeyManager;
569 
generateMenuForStorageDevice()570 void Tray::generateMenuForStorageDevice() {
571   int AddSeperator = FALSE;
572   {
573     nitrokey::proto::stick20::DeviceConfigurationResponsePacket::ResponsePayload status;
574 
575     for (int i=0; i < 20; i++){
576       try {
577         status = nm::instance()->get_status_storage();
578         if(status.ActiveSD_CardID_u32 != 0) break;
579         auto message = "No active SD card or empty Storage status received, retrying " + std::to_string(i);
580         LOG(message, nitrokey::log::Loglevel::DEBUG);
581       }
582       catch (LongOperationInProgressException &e){
583         long_operation_in_progress = true;
584         return;
585       }
586       catch (DeviceCommunicationException &e){
587         //TODO add info to tray about the error?
588         return;
589       }
590     }
591     long_operation_in_progress = false;
592 
593     if (status.ActiveSD_CardID_u32 == 0) // Is Stick 2.0 online (SD + SC
594       // accessable?)
595     {
596       trayMenu->addAction(Stick20ActionUpdateStickStatus);
597       LOG("SD card status not active, aborting", nitrokey::log::Loglevel::DEBUG);
598       return;
599     }
600 
601     // Add special entrys
602 //    if (TRUE == StickNotInitated) {
603     if (TRUE == status.StickKeysNotInitiated) {
604       trayMenu->addAction(Stick20ActionInitCryptedVolume);
605       windowMenu->addAction(Stick20ActionInitCryptedVolume);
606       AddSeperator = TRUE;
607     }
608 
609 //    if (FALSE == StickNotInitated && TRUE == SdCardNotErased) {
610     if (!status.StickKeysNotInitiated && !status.SDFillWithRandomChars_u8) {
611       trayMenu->addAction(Stick20ActionFillSDCardWithRandomChars);
612       windowMenu->addAction(Stick20ActionFillSDCardWithRandomChars);
613       AddSeperator = TRUE;
614     }
615 
616     if (TRUE == AddSeperator){
617       trayMenu->addSeparator();
618       windowMenu->addSeparator();
619     }
620 
621     generatePasswordMenu();
622     trayMenu->addSeparator();
623 
624     if (!status.StickKeysNotInitiated) {
625       // Setup entrys for password safe
626       generateMenuPasswordSafe();
627     }
628 
629     if (status.SDFillWithRandomChars_u8) { //filled randomly
630       if (!status.VolumeActiceFlag_st.encrypted){
631         trayMenu->addAction(Stick20ActionEnableCryptedVolume_tray);
632         windowMenu->addAction(Stick20ActionEnableCryptedVolume);
633       }
634       else{
635         trayMenu->addAction(Stick20ActionDisableCryptedVolume_tray);
636         windowMenu->addAction(Stick20ActionDisableCryptedVolume);
637       }
638 
639       if (!status.VolumeActiceFlag_st.hidden){
640         trayMenu->addAction(Stick20ActionEnableHiddenVolume_tray);
641         windowMenu->addAction(Stick20ActionEnableHiddenVolume);
642       }
643       else{
644         trayMenu->addAction(Stick20ActionDisableHiddenVolume_tray);
645         windowMenu->addAction(Stick20ActionDisableHiddenVolume);
646       }
647     }
648 
649     //FIXME run in separate thread
650     const auto PasswordSafeEnabled = libada::i()->isPasswordSafeUnlocked();
651     if (false != (status.VolumeActiceFlag_st.hidden || status.VolumeActiceFlag_st.encrypted || PasswordSafeEnabled)){
652       trayMenu->addAction(LockDeviceAction);
653       windowMenu->addAction(LockDeviceAction);
654     }
655 
656     trayMenuSubConfigure = trayMenu->addMenu(tr("Configure"));
657     trayMenuSubConfigure->setIcon(GraphicsTools::loadColorize(":/images/new/icon_settings.svg"));
658     trayMenuSubConfigure->addAction(configureActionStick20);
659     trayMenuSubConfigure->addSeparator();
660 
661     // Pin actions
662     trayMenuSubConfigure->addAction(Stick20ActionChangeUserPIN);
663     trayMenuSubConfigure->addAction(Stick20ActionChangeAdminPIN);
664     trayMenuSubConfigure->addAction(Stick20ActionChangeUpdatePIN);
665     trayMenuSubConfigure->addSeparator();
666 
667     // Storage actions
668     auto read_write_active = status.ReadWriteFlagUncryptedVolume_u8 == 0;
669     if (read_write_active)
670       // Set readonly active
671       trayMenuSubConfigure->addAction(Stick20ActionSetReadonlyUncryptedVolume);
672     else
673       // Set RW active
674       trayMenuSubConfigure->addAction(Stick20ActionSetReadWriteUncryptedVolume);
675 
676 #if 0
677     auto read_write_active_encrypted = status.ReadWriteFlagCryptedVolume_u8 == 0;
678     trayMenuSubConfigure->addAction(
679         read_write_active_encrypted ?
680         Stick20ActionSetReadonlyEncryptedVolume : Stick20ActionSetReadWriteEncryptedVolume
681     );
682 #endif
683 
684 //    if (FALSE == SdCardNotErased)
685     if (status.SDFillWithRandomChars_u8)
686       trayMenuSubConfigure->addAction(Stick20ActionSetupHiddenVolume);
687 
688     trayMenuSubConfigure->addAction(Stick20ActionDestroyCryptedVolume);
689     trayMenuSubConfigure->addSeparator();
690 
691     // Other actions
692 //    if (TRUE == LockHardware) //FIXME
693 //      trayMenuSubConfigure->addAction(Stick20ActionLockStickHardware);
694 
695 
696     trayMenuSubConfigure->addAction(Stick20ActionEnableFirmwareUpdate);
697     trayMenuSubConfigure->addAction(Stick20ActionExportFirmwareToFile);
698 
699 
700     if (TRUE == ExtendedConfigActive) {
701       trayMenuSubConfigure->addSeparator();
702       trayMenuSubSpecialConfigure = trayMenuSubConfigure->addMenu(tr("Special Configure"));
703       trayMenuSubSpecialConfigure->addAction(Stick20ActionFillSDCardWithRandomChars);
704 
705       if (status.NewSDCardFound_u8 && !status.SDFillWithRandomChars_u8)
706         trayMenuSubSpecialConfigure->addAction(Stick20ActionClearNewSDCardFound);
707 
708       trayMenuSubSpecialConfigure->addAction(resetAction);
709     }
710     windowMenu->addMenu(trayMenuSubConfigure);
711 
712     // Enable "reset user PIN" ?
713     if (0 == libada::i()->getUserPasswordRetryCount()) {
714       trayMenu->addSeparator();
715       trayMenu->addAction(Stick20ActionResetUserPassword);
716       windowMenu->addAction(Stick20ActionResetUserPassword);
717     }
718 
719 //    // Add debug window ?
720 //    if (TRUE == DebugWindowActive) {
721 //      trayMenu->addSeparator();
722 //      trayMenu->addAction(Stick20ActionDebugAction);
723 //    }
724 
725   }
726 }
727 
UpdateDynamicMenuEntrys(void)728 int Tray::UpdateDynamicMenuEntrys(void) {
729   generateMenu(false);
730   return (TRUE);
731 }
732 
generateMenuPasswordSafe()733 void Tray::generateMenuPasswordSafe() {
734   auto passwordSafeUnlocked = libada::i()->isPasswordSafeUnlocked();
735   if (!passwordSafeUnlocked) {
736       trayMenu->addAction(UnlockPasswordSafeAction_tray);
737       windowMenu->addAction(UnlockPasswordSafeAction);
738 
739       auto passwordSafeAvailable = libada::i()->isPasswordSafeAvailable();
740       UnlockPasswordSafeAction->setEnabled(passwordSafeAvailable);
741       UnlockPasswordSafeAction_tray->setEnabled(passwordSafeAvailable);
742   } else {
743     trayMenu->addAction(LockDeviceAction);
744     windowMenu->addAction(LockDeviceAction);
745   }
746 }
747 
regenerateMenu()748 void Tray::regenerateMenu() {
749   generateMenu(false);
750 }
751 
passOTPProgressFurther(int i)752 void Tray::passOTPProgressFurther(int i) {
753   emit progress(i);
754 }
755 
showOTPProgressInTray(int i)756 void Tray::showOTPProgressInTray(int i) {
757   static const QString &s = trayMenuPasswdSubMenu->title();
758   if (i!=100)
759     trayMenuPasswdSubMenu->setTitle(s +" ("+ QString::number(i) + "%)");
760   else
761     trayMenuPasswdSubMenu->setTitle(s);
762 }
763 
updateOperationInProgressBar(int p)764 void Tray::updateOperationInProgressBar(int p) {
765   auto te = QString(tr("Long operation in progress: %1%")).arg(p);
766   static auto a = std::make_shared<QAction>("", nullptr);
767   connect(a.get(), SIGNAL(triggered()), main_window, SLOT(show_progress_window()));
768   if (trayMenu == nullptr) {
769     generateMenu(true);
770   }
771   static bool initialized = false;
772   if (!initialized){
773     this->showTrayMessage(te);
774     trayMenu->addAction(a.get());
775     initialized = true;
776   }
777   a->setText(te);
778   trayIcon->setContextMenu(trayMenu.get());
779 }
780 
setAdmin_mode(bool _admin_mode)781 void Tray::setAdmin_mode(bool _admin_mode) {
782   ExtendedConfigActive = _admin_mode;
783 }
784 
setFile_menu(QMenuBar * file_menu)785 void Tray::setFile_menu(QMenuBar *file_menu) {
786   Tray::file_menu = file_menu;
787   generateMenu(true);
788   if (windowMenu!= nullptr)
789     file_menu->addMenu(windowMenu.get());
790 }
791 
792