blob: 3a6d76cb6e77c8a149a33cf13b15993df22caf86 [file] [log] [blame]
taylorchuc27dd482014-05-17 20:06:49 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev37667752017-02-02 13:52:12 -08003 * Copyright (c) 2013-2017, Regents of the University of California,
taylorchuc27dd482014-05-17 20:06:49 -07004 *
5 * This file is part of NFD Control Center. See AUTHORS.md for complete list of NFD
6 * authors and contributors.
7 *
8 * NFD Control Center is free software: you can redistribute it and/or modify it under the
9 * terms of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * NFD Control Center is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
14 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 *
Qi Zhao0e043e52016-12-05 18:27:09 -080016 * You should have received a copy of the GNU General Public License along with NFD
taylorchuc27dd482014-05-17 20:06:49 -070017 * Control Center, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
19
taylorchuc27dd482014-05-17 20:06:49 -070020#include "tray-menu.hpp"
Alexander Afanasyev37667752017-02-02 13:52:12 -080021#include "tray-menu.moc"
taylorchuc27dd482014-05-17 20:06:49 -070022
Qi Zhaoae9c4312017-02-02 11:36:12 -080023#include <chrono>
24#include <functional>
25
taylorchuc27dd482014-05-17 20:06:49 -070026#include <ndn-cxx/face.hpp>
27#include <ndn-cxx/interest.hpp>
Qi Zhaoae9c4312017-02-02 11:36:12 -080028#include <boost/algorithm/string/replace.hpp>
taylorchuc27dd482014-05-17 20:06:49 -070029
taylorchuc27dd482014-05-17 20:06:49 -070030#ifdef OSX_BUILD
Alexander Afanasyev0621cec2016-03-20 23:18:27 -070031#define CONNECT_ICON ":/res/icon-connected-black.png"
32#define DISCONNECT_ICON ":/res/icon-disconnected-black.png"
Qi Zhaodec77d92017-02-15 15:49:04 -080033#define CONNECT_STATUS_ICON ":/res/icon-connected-status-black.png"
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070034
35#include <Security/Authorization.h>
36#include <Security/AuthorizationTags.h>
Qi Zhao3615a102017-02-02 20:38:34 -080037
38#include "build/NFD/build/core/version.hpp"
39#include "build/ndn-tools/build/core/version.cpp"
40
taylorchuc27dd482014-05-17 20:06:49 -070041#else
Alexander Afanasyev0621cec2016-03-20 23:18:27 -070042#define CONNECT_ICON ":/res/icon-connected-white.png"
43#define DISCONNECT_ICON ":/res/icon-disconnected-white.png"
Qi Zhao3615a102017-02-02 20:38:34 -080044
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070045#endif // OSX_BUILD
taylorchuc27dd482014-05-17 20:06:49 -070046
Qi Zhao3615a102017-02-02 20:38:34 -080047#include <ndn-cxx/version.hpp>
48
taylorchuc27dd482014-05-17 20:06:49 -070049namespace ndn {
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080050namespace ncc {
taylorchuc27dd482014-05-17 20:06:49 -070051
Qi Zhaoae9c4312017-02-02 11:36:12 -080052/**
53 * @brief Maximum number of lines that can show up in auto-config status tab view
54 */
55const int MAX_LINES_IN_AUTOCONF_STATUS = 100;
56
57#ifdef OSX_BUILD
58const QString AUTO_START_SUFFIX = "Library/LaunchAgents/net.named-data.control-center.plist";
59#endif // OSX_BUILD
60
Qi Zhao7e524492017-03-02 15:05:45 -080061TrayMenu::TrayMenu(QQmlContext* context, Face& face, KeyChain& keyChain)
Alexander Afanasyev4086d512014-07-11 15:56:33 -070062 : m_context(context)
63 , m_isNfdRunning(false)
Qi Zhaodec77d92017-02-15 15:49:04 -080064 , m_isConnectedToHub(false)
Yingdi Yu53c11c12016-03-20 12:56:49 -070065 , m_menu(new QMenu(this))
66 , m_entryPref(new QAction("Preferences...", m_menu))
Qi Zhao7e524492017-03-02 15:05:45 -080067 , m_entryStatus(new QAction("Status...", m_menu))
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070068 , m_entrySec(new QAction("Security...", m_menu))
Qi Zhaoae9c4312017-02-02 11:36:12 -080069 , m_acProc(nullptr)
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080070 , m_settings(new QSettings())
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070071#ifdef OSX_BUILD
Alexander Afanasyevb0beef52017-03-17 15:26:03 -070072 , m_entryEnableCli(new QAction("Install command-line tools...", m_menu))
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080073 , m_checkForUpdates(new QAction("Check for updates", m_menu))
Alexander Afanasyeve095ed12017-02-04 23:09:55 -080074 , m_sparkle(NCC_APPCAST)
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070075#endif
Yingdi Yu53c11c12016-03-20 12:56:49 -070076 , m_entryQuit(new QAction("Quit", m_menu))
Yingdi Yu1f824642016-03-20 17:07:22 -070077 , m_keyViewerDialog(new ncc::KeyViewerDialog)
Alexander Afanasyev81509f32016-03-21 17:02:38 -070078 , m_face(face)
Qi Zhao7e524492017-03-02 15:05:45 -080079 , m_keyChain(keyChain)
80 , m_statusViewer(new StatusViewer(m_face, m_keyChain))
Yingdi Yu53c11c12016-03-20 12:56:49 -070081{
82 connect(m_entryPref, SIGNAL(triggered()), this, SIGNAL(showApp()));
Qi Zhao7e524492017-03-02 15:05:45 -080083 connect(m_entryStatus, SIGNAL(triggered()), m_statusViewer, SLOT(present()));
Yingdi Yu1f824642016-03-20 17:07:22 -070084 connect(m_entrySec, SIGNAL(triggered()), m_keyViewerDialog, SLOT(present()));
Yingdi Yu53c11c12016-03-20 12:56:49 -070085 connect(m_entryQuit, SIGNAL(triggered()), this, SLOT(quitApp()));
taylorchuc27dd482014-05-17 20:06:49 -070086
Alexander Afanasyev4086d512014-07-11 15:56:33 -070087 connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)),
88 Qt::QueuedConnection);
Qi Zhaodec77d92017-02-15 15:49:04 -080089 connect(this, SIGNAL(connectivityUpdate(bool)), this, SLOT(updateConnectivity(bool)),
90 Qt::QueuedConnection);
Alexander Afanasyev4086d512014-07-11 15:56:33 -070091
Qi Zhao3615a102017-02-02 20:38:34 -080092 QString nccVersion = QString(NCC_VERSION) + " (ndn-cxx: " + NDN_CXX_VERSION_BUILD_STRING +
93 ", NFD: " + NFD_VERSION_BUILD_STRING +
94 ", ndn-tools: " + ::ndn::tools::VERSION +
95 ")";
96
97 m_context->setContextProperty("nccVersion", nccVersion);
98
Qi Zhao7e524492017-03-02 15:05:45 -080099 m_menu->addAction(m_entryStatus);
Yingdi Yu53c11c12016-03-20 12:56:49 -0700100 m_menu->addAction(m_entrySec);
Qi Zhaoae9c4312017-02-02 11:36:12 -0800101
Alexander Afanasyev8d1a3a02017-03-11 13:00:24 -0800102 m_menu->addSeparator();
103 m_menu->addAction(m_entryPref);
104
susmit50607912016-03-21 12:00:15 -0700105#ifdef OSX_BUILD
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800106 connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700107 m_menu->addAction(m_entryEnableCli);
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800108
109 connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
110 m_menu->addAction(m_checkForUpdates);
susmit50607912016-03-21 12:00:15 -0700111#endif
Qi Zhaoae9c4312017-02-02 11:36:12 -0800112
Alexander Afanasyev8d1a3a02017-03-11 13:00:24 -0800113 m_menu->addSeparator();
Yingdi Yu53c11c12016-03-20 12:56:49 -0700114 m_menu->addAction(m_entryQuit);
115 m_tray = new QSystemTrayIcon(this);
116 m_tray->setContextMenu(m_menu);
Yingdi Yu53c11c12016-03-20 12:56:49 -0700117 m_tray->setIcon(QIcon(DISCONNECT_ICON));
118 m_tray->show();
Qi Zhaoae9c4312017-02-02 11:36:12 -0800119
120 // delaying for the case when NFD is already started (to avoid starting / to avoid starting autoconfig
121 if (isNfdAutoStartEnabled()) {
122 scheduleDelayedTask(std::chrono::seconds(5), [this] {
123 if (!m_isNfdRunning) {
124 startNfd();
125 }
126 });
Qi Zhao86f2b212016-12-06 12:44:16 -0800127 }
taylorchuc27dd482014-05-17 20:06:49 -0700128}
129
130TrayMenu::~TrayMenu()
131{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800132 // delete for all new's
taylorchuc27dd482014-05-17 20:06:49 -0700133}
134
Qi Zhaoae9c4312017-02-02 11:36:12 -0800135////////////////////////////////////////////////////////////////////////////////
136// NFD Control Center start on login
Qi Zhao86f2b212016-12-06 12:44:16 -0800137
138Q_INVOKABLE bool
Qi Zhaoae9c4312017-02-02 11:36:12 -0800139TrayMenu::isNccAutoStartEnabled() const
Qi Zhao86f2b212016-12-06 12:44:16 -0800140{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800141 QFileInfo file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
142 return file.exists() && file.isFile();
Qi Zhao86f2b212016-12-06 12:44:16 -0800143}
144
145Q_INVOKABLE void
Qi Zhaoae9c4312017-02-02 11:36:12 -0800146TrayMenu::enableDisableNccAutoStart(bool isEnabled)
Qi Zhao86f2b212016-12-06 12:44:16 -0800147{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800148 if (isEnabled) {
149 QFile file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
150 file.open(QIODevice::WriteOnly);
151
152 std::string plist = R"PLIST(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
153<?xml version="1.0" encoding="UTF-8"?>
154<plist version="1.0">
155 <dict>
156 <key>Label</key>
157 <string>net.named-data.control-center</string>
158 <key>ProgramArguments</key>
159 <array>
160 <string>%%PATH%%</string>
161 </array>
162 <key>RunAtLoad</key>
163 <true/>
164 <key>KeepAlive</key>
165 <false/>
166 </dict>
167</plist>
168)PLIST"; //"
169 boost::replace_all(plist, "%%PATH%%", QCoreApplication::applicationFilePath().toStdString());
170 file.write(plist.data(), plist.size());
171 file.close();
Qi Zhao7e524492017-03-02 15:05:45 -0800172 }
173 else {
Qi Zhaoae9c4312017-02-02 11:36:12 -0800174 QFile::remove(QDir::home().path() + "/" + AUTO_START_SUFFIX);
Qi Zhao86f2b212016-12-06 12:44:16 -0800175 }
taylorchuc27dd482014-05-17 20:06:49 -0700176}
177
Qi Zhaoae9c4312017-02-02 11:36:12 -0800178////////////////////////////////////////////////////////////////////////////////
179// NFD start on launch
taylorchuc27dd482014-05-17 20:06:49 -0700180
Qi Zhaoae9c4312017-02-02 11:36:12 -0800181Q_INVOKABLE bool
182TrayMenu::isNfdAutoStartEnabled() const
taylorchuc27dd482014-05-17 20:06:49 -0700183{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800184 return m_settings->value("ENABLE_AUTO_START", false).toBool();
taylorchuc27dd482014-05-17 20:06:49 -0700185}
186
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700187Q_INVOKABLE void
Qi Zhaoae9c4312017-02-02 11:36:12 -0800188TrayMenu::enableDisableNfdAutoStart(bool isEnabled)
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700189{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800190 m_settings->setValue("ENABLE_AUTO_START", isEnabled);
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700191 if (!m_isNfdRunning) {
192 startNfd();
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700193 }
194}
195
taylorchuc27dd482014-05-17 20:06:49 -0700196void
197TrayMenu::startNfd()
198{
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700199#ifdef OSX_BUILD
200 QProcess* proc = new QProcess();
201 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
Qi Zhaoae9c4312017-02-02 11:36:12 -0800202 proc->startDetached((QCoreApplication::applicationDirPath() + "/../Platform/nfd"),
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700203 QStringList()
204 << "--config"
Qi Zhaoae9c4312017-02-02 11:36:12 -0800205 << (QCoreApplication::applicationDirPath() + "/../etc/ndn/nfd.conf"));
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700206#endif
taylorchuc27dd482014-05-17 20:06:49 -0700207}
208
209void
210TrayMenu::stopNfd()
211{
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700212#ifdef OSX_BUILD
213 QProcess* proc = new QProcess();
214 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
215 proc->startDetached("killall", QStringList() << "nfd");
216#endif
taylorchuc27dd482014-05-17 20:06:49 -0700217}
218
Qi Zhaoae9c4312017-02-02 11:36:12 -0800219////////////////////////////////////////////////////////////////////////////////
220// NDN Autoconfig
221
222Q_INVOKABLE bool
223TrayMenu::isNdnAutoConfigEnabled() const
224{
225 return m_settings->value("ENABLE_NDN_AUTO_CONFIG", false).toBool();
226}
227
228Q_INVOKABLE void
229TrayMenu::enableDisableNdnAutoConfig(bool isEnabled)
230{
231 m_settings->setValue("ENABLE_NDN_AUTO_CONFIG", isEnabled);
232
233 if (isEnabled) {
234 startNdnAutoConfig();
235 }
236 else {
237 stopNdnAutoConfig();
238 }
239}
240
241void
242TrayMenu::startNdnAutoConfig()
243{
244 if (m_acProc != nullptr) {
245 return;
246 }
247
248 stopNdnAutoConfig(false); // Make sure no more than one auto-config process exist
249
250 if (!m_isNfdRunning) {
251 return;
252 }
253
254 m_acProc = new QProcess();
255
256 scheduleDelayedTask(std::chrono::seconds(2), [this] {
257 appendNdnAutoConfigStatus("NDN auto configuration starting...\n");
258 m_acProc->start(QCoreApplication::applicationDirPath() + "/../Platform/ndn-autoconfig",
259 QStringList() << "--daemon");
260 connect(m_acProc, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
261 connect(m_acProc, SIGNAL(readyReadStandardError()), this, SLOT(processOutput()));
262 });
263}
264
265void
266TrayMenu::stopNdnAutoConfig(bool appendStatus)
267{
268 m_acProc = nullptr;
269 QProcess* proc = new QProcess();
270 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
271 proc->startDetached("killall", QStringList() << "ndn-autoconfig");
272 if (appendStatus) {
273 appendNdnAutoConfigStatus("NDN auto configuration stopped\n");
274 }
275}
276
277void
278TrayMenu::appendNdnAutoConfigStatus(const QString& message)
279{
280 if (message == "") {
281 return;
282 }
283 if (m_ndnAutoConfigMsg.count("\n") > MAX_LINES_IN_AUTOCONF_STATUS) {
284 int end = m_ndnAutoConfigMsg.indexOf("\n");
285 m_ndnAutoConfigMsg.remove(0, end + 1);
286 }
287 m_ndnAutoConfigMsg.append(message);
288
289 m_context->setContextProperty("ndnAutoConfigText", m_ndnAutoConfigMsg);
290}
291
292void
293TrayMenu::processOutput()
294{
295 if (m_acProc == nullptr) {
296 return;
297 }
298 appendNdnAutoConfigStatus(m_acProc->readAllStandardOutput());
299 appendNdnAutoConfigStatus(m_acProc->readAllStandardError());
300}
301
302////////////////////////////////////////////////////////////////////////////////
303// NFD stop on shutdown
304
305Q_INVOKABLE bool
306TrayMenu::isNfdStopOnExitEnabled() const
307{
308 return m_settings->value("ENABLE_SHUTDOWN", false).toBool();
309}
310
311Q_INVOKABLE void
312TrayMenu::enableDisableNfdStopOnExit(bool isEnabled)
313{
314 m_settings->setValue("ENABLE_SHUTDOWN", isEnabled);
315}
316
317////////////////////////////////////////////////////////////////////////////////
318// Misc
319
320void
321TrayMenu::quitApp()
322{
323 if (isNfdStopOnExitEnabled()) {
324 stopNdnAutoConfig();
325 stopNfd();
326 }
327 QCoreApplication::exit(0);
328}
329
susmit4fe3cb92016-03-20 17:08:41 -0700330Q_INVOKABLE void
331TrayMenu::addDeleteRoute()
332{
333 addRoute();
334}
335
336Q_INVOKABLE void
337TrayMenu::addRoute()
338{
339 std::cout << "Adding route" << std::endl;
340 QString cmd = "nfdc register /test tcp4://localhost";
341 QProcess *addNewRoute = new QProcess();
342 connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
343 addNewRoute->start("bash", QStringList() << "-c" << cmd);
344 std::cout << "Done" << std::endl;
susmit4fe3cb92016-03-20 17:08:41 -0700345}
346
347void
348TrayMenu::deleteRoute()
349{
350 std::cout << "Deleting route" << std::endl;
351 QString cmd = "nfdc unregister /test tcp4://localhost";
352 QProcess *addNewRoute = new QProcess();
353 connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
354 addNewRoute->start("bash", QStringList() << "-c" << cmd);
355 std::cout << "Done" << std::endl;
susmit4fe3cb92016-03-20 17:08:41 -0700356}
357
taylorchuc27dd482014-05-17 20:06:49 -0700358void
359TrayMenu::updateNfdActivityIcon(bool isActive)
360{
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700361 m_isNfdRunning = isActive;
362
taylorchuc27dd482014-05-17 20:06:49 -0700363 if (isActive) {
Qi Zhaodec77d92017-02-15 15:49:04 -0800364 if(m_isConnectedToHub) {
365 m_tray->setIcon(QIcon(CONNECT_STATUS_ICON));
366 }
367 else {
368 m_tray->setIcon(QIcon(CONNECT_ICON));
369 }
Qi Zhaoae9c4312017-02-02 11:36:12 -0800370 if (isNdnAutoConfigEnabled()) {
371 startNdnAutoConfig();
372 }
taylorchuc27dd482014-05-17 20:06:49 -0700373 }
374 else {
Yingdi Yu53c11c12016-03-20 12:56:49 -0700375 m_tray->setIcon(QIcon(DISCONNECT_ICON));
taylorchuc27dd482014-05-17 20:06:49 -0700376 }
377}
378
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700379void
Qi Zhaodec77d92017-02-15 15:49:04 -0800380TrayMenu::updateConnectivity(bool isConnectedToHub)
381{
382 m_isConnectedToHub = isConnectedToHub;
383}
384
385void
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700386TrayMenu::enableCli()
387{
388#ifdef OSX_BUILD
Qi Zhaoae9c4312017-02-02 11:36:12 -0800389 QProcess* proc = new QProcess();
390 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700391
Qi Zhaoae9c4312017-02-02 11:36:12 -0800392 proc->start("osascript", QStringList()
393 << "-e"
394 << "do shell script \""
395 "/bin/mkdir -vp /usr/local/bin; "
396 "/bin/ln -s -f '" + QCoreApplication::applicationDirPath() +
397 "/../Resources/ndn" + "' /usr/local/bin/ndn;"
398 "\" with administrator privileges");
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700399#endif
400}
401
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800402#ifdef OSX_BUILD
403void
404TrayMenu::checkForUpdates()
405{
406 m_sparkle.checkForUpdates();
407}
408#endif // OSX_BUILD
409
Qi Zhaoae9c4312017-02-02 11:36:12 -0800410void
411TrayMenu::scheduleDelayedTask(std::chrono::milliseconds delays, const std::function<void()>& task)
412{
413 auto timer = new QTimer();
414 timer->setSingleShot(true);
415 timer->setInterval(delays);
416 connect(timer, &QTimer::timeout,
417 [task, timer] {
418 task();
419 timer->deleteLater();
420 });
421 timer->start();
422}
423
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800424} // namespace ncc
taylorchuc27dd482014-05-17 20:06:49 -0700425} // namespace ndn