blob: 104878a5d14e20a54123935c8ba3ae3b27c1e032 [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
72 , m_entryEnableCli(new QAction("Enable Command Terminal Usage...", 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
Yingdi Yu53c11c12016-03-20 12:56:49 -070099 m_menu->addAction(m_entryPref);
Qi Zhao7e524492017-03-02 15:05:45 -0800100 m_menu->addAction(m_entryStatus);
Yingdi Yu53c11c12016-03-20 12:56:49 -0700101 m_menu->addAction(m_entrySec);
Qi Zhaoae9c4312017-02-02 11:36:12 -0800102
susmit50607912016-03-21 12:00:15 -0700103#ifdef OSX_BUILD
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800104 connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700105 m_menu->addAction(m_entryEnableCli);
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800106
107 connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
108 m_menu->addAction(m_checkForUpdates);
susmit50607912016-03-21 12:00:15 -0700109#endif
Qi Zhaoae9c4312017-02-02 11:36:12 -0800110
Yingdi Yu53c11c12016-03-20 12:56:49 -0700111 m_menu->addAction(m_entryQuit);
112 m_tray = new QSystemTrayIcon(this);
113 m_tray->setContextMenu(m_menu);
Yingdi Yu53c11c12016-03-20 12:56:49 -0700114 m_tray->setIcon(QIcon(DISCONNECT_ICON));
115 m_tray->show();
Qi Zhaoae9c4312017-02-02 11:36:12 -0800116
117 // delaying for the case when NFD is already started (to avoid starting / to avoid starting autoconfig
118 if (isNfdAutoStartEnabled()) {
119 scheduleDelayedTask(std::chrono::seconds(5), [this] {
120 if (!m_isNfdRunning) {
121 startNfd();
122 }
123 });
Qi Zhao86f2b212016-12-06 12:44:16 -0800124 }
taylorchuc27dd482014-05-17 20:06:49 -0700125}
126
127TrayMenu::~TrayMenu()
128{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800129 // delete for all new's
taylorchuc27dd482014-05-17 20:06:49 -0700130}
131
Qi Zhaoae9c4312017-02-02 11:36:12 -0800132////////////////////////////////////////////////////////////////////////////////
133// NFD Control Center start on login
Qi Zhao86f2b212016-12-06 12:44:16 -0800134
135Q_INVOKABLE bool
Qi Zhaoae9c4312017-02-02 11:36:12 -0800136TrayMenu::isNccAutoStartEnabled() const
Qi Zhao86f2b212016-12-06 12:44:16 -0800137{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800138 QFileInfo file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
139 return file.exists() && file.isFile();
Qi Zhao86f2b212016-12-06 12:44:16 -0800140}
141
142Q_INVOKABLE void
Qi Zhaoae9c4312017-02-02 11:36:12 -0800143TrayMenu::enableDisableNccAutoStart(bool isEnabled)
Qi Zhao86f2b212016-12-06 12:44:16 -0800144{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800145 if (isEnabled) {
146 QFile file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
147 file.open(QIODevice::WriteOnly);
148
149 std::string plist = R"PLIST(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
150<?xml version="1.0" encoding="UTF-8"?>
151<plist version="1.0">
152 <dict>
153 <key>Label</key>
154 <string>net.named-data.control-center</string>
155 <key>ProgramArguments</key>
156 <array>
157 <string>%%PATH%%</string>
158 </array>
159 <key>RunAtLoad</key>
160 <true/>
161 <key>KeepAlive</key>
162 <false/>
163 </dict>
164</plist>
165)PLIST"; //"
166 boost::replace_all(plist, "%%PATH%%", QCoreApplication::applicationFilePath().toStdString());
167 file.write(plist.data(), plist.size());
168 file.close();
Qi Zhao7e524492017-03-02 15:05:45 -0800169 }
170 else {
Qi Zhaoae9c4312017-02-02 11:36:12 -0800171 QFile::remove(QDir::home().path() + "/" + AUTO_START_SUFFIX);
Qi Zhao86f2b212016-12-06 12:44:16 -0800172 }
taylorchuc27dd482014-05-17 20:06:49 -0700173}
174
Qi Zhaoae9c4312017-02-02 11:36:12 -0800175////////////////////////////////////////////////////////////////////////////////
176// NFD start on launch
taylorchuc27dd482014-05-17 20:06:49 -0700177
Qi Zhaoae9c4312017-02-02 11:36:12 -0800178Q_INVOKABLE bool
179TrayMenu::isNfdAutoStartEnabled() const
taylorchuc27dd482014-05-17 20:06:49 -0700180{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800181 return m_settings->value("ENABLE_AUTO_START", false).toBool();
taylorchuc27dd482014-05-17 20:06:49 -0700182}
183
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700184Q_INVOKABLE void
Qi Zhaoae9c4312017-02-02 11:36:12 -0800185TrayMenu::enableDisableNfdAutoStart(bool isEnabled)
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700186{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800187 m_settings->setValue("ENABLE_AUTO_START", isEnabled);
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700188 if (!m_isNfdRunning) {
189 startNfd();
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700190 }
191}
192
taylorchuc27dd482014-05-17 20:06:49 -0700193void
194TrayMenu::startNfd()
195{
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700196#ifdef OSX_BUILD
197 QProcess* proc = new QProcess();
198 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
Qi Zhaoae9c4312017-02-02 11:36:12 -0800199 proc->startDetached((QCoreApplication::applicationDirPath() + "/../Platform/nfd"),
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700200 QStringList()
201 << "--config"
Qi Zhaoae9c4312017-02-02 11:36:12 -0800202 << (QCoreApplication::applicationDirPath() + "/../etc/ndn/nfd.conf"));
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700203#endif
taylorchuc27dd482014-05-17 20:06:49 -0700204}
205
206void
207TrayMenu::stopNfd()
208{
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700209#ifdef OSX_BUILD
210 QProcess* proc = new QProcess();
211 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
212 proc->startDetached("killall", QStringList() << "nfd");
213#endif
taylorchuc27dd482014-05-17 20:06:49 -0700214}
215
Qi Zhaoae9c4312017-02-02 11:36:12 -0800216////////////////////////////////////////////////////////////////////////////////
217// NDN Autoconfig
218
219Q_INVOKABLE bool
220TrayMenu::isNdnAutoConfigEnabled() const
221{
222 return m_settings->value("ENABLE_NDN_AUTO_CONFIG", false).toBool();
223}
224
225Q_INVOKABLE void
226TrayMenu::enableDisableNdnAutoConfig(bool isEnabled)
227{
228 m_settings->setValue("ENABLE_NDN_AUTO_CONFIG", isEnabled);
229
230 if (isEnabled) {
231 startNdnAutoConfig();
232 }
233 else {
234 stopNdnAutoConfig();
235 }
236}
237
238void
239TrayMenu::startNdnAutoConfig()
240{
241 if (m_acProc != nullptr) {
242 return;
243 }
244
245 stopNdnAutoConfig(false); // Make sure no more than one auto-config process exist
246
247 if (!m_isNfdRunning) {
248 return;
249 }
250
251 m_acProc = new QProcess();
252
253 scheduleDelayedTask(std::chrono::seconds(2), [this] {
254 appendNdnAutoConfigStatus("NDN auto configuration starting...\n");
255 m_acProc->start(QCoreApplication::applicationDirPath() + "/../Platform/ndn-autoconfig",
256 QStringList() << "--daemon");
257 connect(m_acProc, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
258 connect(m_acProc, SIGNAL(readyReadStandardError()), this, SLOT(processOutput()));
259 });
260}
261
262void
263TrayMenu::stopNdnAutoConfig(bool appendStatus)
264{
265 m_acProc = nullptr;
266 QProcess* proc = new QProcess();
267 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
268 proc->startDetached("killall", QStringList() << "ndn-autoconfig");
269 if (appendStatus) {
270 appendNdnAutoConfigStatus("NDN auto configuration stopped\n");
271 }
272}
273
274void
275TrayMenu::appendNdnAutoConfigStatus(const QString& message)
276{
277 if (message == "") {
278 return;
279 }
280 if (m_ndnAutoConfigMsg.count("\n") > MAX_LINES_IN_AUTOCONF_STATUS) {
281 int end = m_ndnAutoConfigMsg.indexOf("\n");
282 m_ndnAutoConfigMsg.remove(0, end + 1);
283 }
284 m_ndnAutoConfigMsg.append(message);
285
286 m_context->setContextProperty("ndnAutoConfigText", m_ndnAutoConfigMsg);
287}
288
289void
290TrayMenu::processOutput()
291{
292 if (m_acProc == nullptr) {
293 return;
294 }
295 appendNdnAutoConfigStatus(m_acProc->readAllStandardOutput());
296 appendNdnAutoConfigStatus(m_acProc->readAllStandardError());
297}
298
299////////////////////////////////////////////////////////////////////////////////
300// NFD stop on shutdown
301
302Q_INVOKABLE bool
303TrayMenu::isNfdStopOnExitEnabled() const
304{
305 return m_settings->value("ENABLE_SHUTDOWN", false).toBool();
306}
307
308Q_INVOKABLE void
309TrayMenu::enableDisableNfdStopOnExit(bool isEnabled)
310{
311 m_settings->setValue("ENABLE_SHUTDOWN", isEnabled);
312}
313
314////////////////////////////////////////////////////////////////////////////////
315// Misc
316
317void
318TrayMenu::quitApp()
319{
320 if (isNfdStopOnExitEnabled()) {
321 stopNdnAutoConfig();
322 stopNfd();
323 }
324 QCoreApplication::exit(0);
325}
326
susmit4fe3cb92016-03-20 17:08:41 -0700327Q_INVOKABLE void
328TrayMenu::addDeleteRoute()
329{
330 addRoute();
331}
332
333Q_INVOKABLE void
334TrayMenu::addRoute()
335{
336 std::cout << "Adding route" << std::endl;
337 QString cmd = "nfdc register /test tcp4://localhost";
338 QProcess *addNewRoute = new QProcess();
339 connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
340 addNewRoute->start("bash", QStringList() << "-c" << cmd);
341 std::cout << "Done" << std::endl;
susmit4fe3cb92016-03-20 17:08:41 -0700342}
343
344void
345TrayMenu::deleteRoute()
346{
347 std::cout << "Deleting route" << std::endl;
348 QString cmd = "nfdc unregister /test tcp4://localhost";
349 QProcess *addNewRoute = new QProcess();
350 connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
351 addNewRoute->start("bash", QStringList() << "-c" << cmd);
352 std::cout << "Done" << std::endl;
susmit4fe3cb92016-03-20 17:08:41 -0700353}
354
taylorchuc27dd482014-05-17 20:06:49 -0700355void
356TrayMenu::updateNfdActivityIcon(bool isActive)
357{
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700358 m_isNfdRunning = isActive;
359
taylorchuc27dd482014-05-17 20:06:49 -0700360 if (isActive) {
Qi Zhaodec77d92017-02-15 15:49:04 -0800361 if(m_isConnectedToHub) {
362 m_tray->setIcon(QIcon(CONNECT_STATUS_ICON));
363 }
364 else {
365 m_tray->setIcon(QIcon(CONNECT_ICON));
366 }
Qi Zhaoae9c4312017-02-02 11:36:12 -0800367 if (isNdnAutoConfigEnabled()) {
368 startNdnAutoConfig();
369 }
taylorchuc27dd482014-05-17 20:06:49 -0700370 }
371 else {
Yingdi Yu53c11c12016-03-20 12:56:49 -0700372 m_tray->setIcon(QIcon(DISCONNECT_ICON));
taylorchuc27dd482014-05-17 20:06:49 -0700373 }
374}
375
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700376void
Qi Zhaodec77d92017-02-15 15:49:04 -0800377TrayMenu::updateConnectivity(bool isConnectedToHub)
378{
379 m_isConnectedToHub = isConnectedToHub;
380}
381
382void
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700383TrayMenu::enableCli()
384{
385#ifdef OSX_BUILD
Qi Zhaoae9c4312017-02-02 11:36:12 -0800386 QProcess* proc = new QProcess();
387 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700388
Qi Zhaoae9c4312017-02-02 11:36:12 -0800389 proc->start("osascript", QStringList()
390 << "-e"
391 << "do shell script \""
392 "/bin/mkdir -vp /usr/local/bin; "
393 "/bin/ln -s -f '" + QCoreApplication::applicationDirPath() +
394 "/../Resources/ndn" + "' /usr/local/bin/ndn;"
395 "\" with administrator privileges");
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700396#endif
397}
398
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800399#ifdef OSX_BUILD
400void
401TrayMenu::checkForUpdates()
402{
403 m_sparkle.checkForUpdates();
404}
405#endif // OSX_BUILD
406
Qi Zhaoae9c4312017-02-02 11:36:12 -0800407void
408TrayMenu::scheduleDelayedTask(std::chrono::milliseconds delays, const std::function<void()>& task)
409{
410 auto timer = new QTimer();
411 timer->setSingleShot(true);
412 timer->setInterval(delays);
413 connect(timer, &QTimer::timeout,
414 [task, timer] {
415 task();
416 timer->deleteLater();
417 });
418 timer->start();
419}
420
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800421} // namespace ncc
taylorchuc27dd482014-05-17 20:06:49 -0700422} // namespace ndn