blob: 28d33036f99e45af658ad5bf85b3a9b59a5732dc [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
Alexander Afanasyev81509f32016-03-21 17:02:38 -070061TrayMenu::TrayMenu(QQmlContext* context, Face& face)
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))
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070067 , m_entrySec(new QAction("Security...", m_menu))
Qi Zhaoae9c4312017-02-02 11:36:12 -080068 , m_acProc(nullptr)
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080069 , m_settings(new QSettings())
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070070#ifdef OSX_BUILD
71 , m_entryEnableCli(new QAction("Enable Command Terminal Usage...", m_menu))
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080072 , m_checkForUpdates(new QAction("Check for updates", m_menu))
Alexander Afanasyeve095ed12017-02-04 23:09:55 -080073 , m_sparkle(NCC_APPCAST)
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070074#endif
Yingdi Yu53c11c12016-03-20 12:56:49 -070075 , m_entryQuit(new QAction("Quit", m_menu))
Yingdi Yu1f824642016-03-20 17:07:22 -070076 , m_keyViewerDialog(new ncc::KeyViewerDialog)
Alexander Afanasyev81509f32016-03-21 17:02:38 -070077 , m_face(face)
Yingdi Yu53c11c12016-03-20 12:56:49 -070078{
79 connect(m_entryPref, SIGNAL(triggered()), this, SIGNAL(showApp()));
Yingdi Yu1f824642016-03-20 17:07:22 -070080 connect(m_entrySec, SIGNAL(triggered()), m_keyViewerDialog, SLOT(present()));
Yingdi Yu53c11c12016-03-20 12:56:49 -070081 connect(m_entryQuit, SIGNAL(triggered()), this, SLOT(quitApp()));
taylorchuc27dd482014-05-17 20:06:49 -070082
Alexander Afanasyev4086d512014-07-11 15:56:33 -070083 connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)),
84 Qt::QueuedConnection);
Qi Zhaodec77d92017-02-15 15:49:04 -080085 connect(this, SIGNAL(connectivityUpdate(bool)), this, SLOT(updateConnectivity(bool)),
86 Qt::QueuedConnection);
Alexander Afanasyev4086d512014-07-11 15:56:33 -070087
Qi Zhao3615a102017-02-02 20:38:34 -080088 QString nccVersion = QString(NCC_VERSION) + " (ndn-cxx: " + NDN_CXX_VERSION_BUILD_STRING +
89 ", NFD: " + NFD_VERSION_BUILD_STRING +
90 ", ndn-tools: " + ::ndn::tools::VERSION +
91 ")";
92
93 m_context->setContextProperty("nccVersion", nccVersion);
94
Yingdi Yu53c11c12016-03-20 12:56:49 -070095 m_menu->addAction(m_entryPref);
96 m_menu->addAction(m_entrySec);
Qi Zhaoae9c4312017-02-02 11:36:12 -080097
susmit50607912016-03-21 12:00:15 -070098#ifdef OSX_BUILD
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080099 connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700100 m_menu->addAction(m_entryEnableCli);
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800101
102 connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
103 m_menu->addAction(m_checkForUpdates);
susmit50607912016-03-21 12:00:15 -0700104#endif
Qi Zhaoae9c4312017-02-02 11:36:12 -0800105
Yingdi Yu53c11c12016-03-20 12:56:49 -0700106 m_menu->addAction(m_entryQuit);
107 m_tray = new QSystemTrayIcon(this);
108 m_tray->setContextMenu(m_menu);
Yingdi Yu53c11c12016-03-20 12:56:49 -0700109 m_tray->setIcon(QIcon(DISCONNECT_ICON));
110 m_tray->show();
Qi Zhaoae9c4312017-02-02 11:36:12 -0800111
112 // delaying for the case when NFD is already started (to avoid starting / to avoid starting autoconfig
113 if (isNfdAutoStartEnabled()) {
114 scheduleDelayedTask(std::chrono::seconds(5), [this] {
115 if (!m_isNfdRunning) {
116 startNfd();
117 }
118 });
Qi Zhao86f2b212016-12-06 12:44:16 -0800119 }
taylorchuc27dd482014-05-17 20:06:49 -0700120}
121
122TrayMenu::~TrayMenu()
123{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800124 // delete for all new's
taylorchuc27dd482014-05-17 20:06:49 -0700125}
126
Qi Zhaoae9c4312017-02-02 11:36:12 -0800127////////////////////////////////////////////////////////////////////////////////
128// NFD Control Center start on login
Qi Zhao86f2b212016-12-06 12:44:16 -0800129
130Q_INVOKABLE bool
Qi Zhaoae9c4312017-02-02 11:36:12 -0800131TrayMenu::isNccAutoStartEnabled() const
Qi Zhao86f2b212016-12-06 12:44:16 -0800132{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800133 QFileInfo file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
134 return file.exists() && file.isFile();
Qi Zhao86f2b212016-12-06 12:44:16 -0800135}
136
137Q_INVOKABLE void
Qi Zhaoae9c4312017-02-02 11:36:12 -0800138TrayMenu::enableDisableNccAutoStart(bool isEnabled)
Qi Zhao86f2b212016-12-06 12:44:16 -0800139{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800140 if (isEnabled) {
141 QFile file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
142 file.open(QIODevice::WriteOnly);
143
144 std::string plist = R"PLIST(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
145<?xml version="1.0" encoding="UTF-8"?>
146<plist version="1.0">
147 <dict>
148 <key>Label</key>
149 <string>net.named-data.control-center</string>
150 <key>ProgramArguments</key>
151 <array>
152 <string>%%PATH%%</string>
153 </array>
154 <key>RunAtLoad</key>
155 <true/>
156 <key>KeepAlive</key>
157 <false/>
158 </dict>
159</plist>
160)PLIST"; //"
161 boost::replace_all(plist, "%%PATH%%", QCoreApplication::applicationFilePath().toStdString());
162 file.write(plist.data(), plist.size());
163 file.close();
164 } else {
165 QFile::remove(QDir::home().path() + "/" + AUTO_START_SUFFIX);
Qi Zhao86f2b212016-12-06 12:44:16 -0800166 }
taylorchuc27dd482014-05-17 20:06:49 -0700167}
168
Qi Zhaoae9c4312017-02-02 11:36:12 -0800169////////////////////////////////////////////////////////////////////////////////
170// NFD start on launch
taylorchuc27dd482014-05-17 20:06:49 -0700171
Qi Zhaoae9c4312017-02-02 11:36:12 -0800172Q_INVOKABLE bool
173TrayMenu::isNfdAutoStartEnabled() const
taylorchuc27dd482014-05-17 20:06:49 -0700174{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800175 return m_settings->value("ENABLE_AUTO_START", false).toBool();
taylorchuc27dd482014-05-17 20:06:49 -0700176}
177
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700178Q_INVOKABLE void
Qi Zhaoae9c4312017-02-02 11:36:12 -0800179TrayMenu::enableDisableNfdAutoStart(bool isEnabled)
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700180{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800181 m_settings->setValue("ENABLE_AUTO_START", isEnabled);
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700182 if (!m_isNfdRunning) {
183 startNfd();
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700184 }
185}
186
taylorchuc27dd482014-05-17 20:06:49 -0700187void
188TrayMenu::startNfd()
189{
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700190#ifdef OSX_BUILD
191 QProcess* proc = new QProcess();
192 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
Qi Zhaoae9c4312017-02-02 11:36:12 -0800193 proc->startDetached((QCoreApplication::applicationDirPath() + "/../Platform/nfd"),
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700194 QStringList()
195 << "--config"
Qi Zhaoae9c4312017-02-02 11:36:12 -0800196 << (QCoreApplication::applicationDirPath() + "/../etc/ndn/nfd.conf"));
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700197#endif
taylorchuc27dd482014-05-17 20:06:49 -0700198}
199
200void
201TrayMenu::stopNfd()
202{
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700203#ifdef OSX_BUILD
204 QProcess* proc = new QProcess();
205 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
206 proc->startDetached("killall", QStringList() << "nfd");
207#endif
taylorchuc27dd482014-05-17 20:06:49 -0700208}
209
Qi Zhaoae9c4312017-02-02 11:36:12 -0800210////////////////////////////////////////////////////////////////////////////////
211// NDN Autoconfig
212
213Q_INVOKABLE bool
214TrayMenu::isNdnAutoConfigEnabled() const
215{
216 return m_settings->value("ENABLE_NDN_AUTO_CONFIG", false).toBool();
217}
218
219Q_INVOKABLE void
220TrayMenu::enableDisableNdnAutoConfig(bool isEnabled)
221{
222 m_settings->setValue("ENABLE_NDN_AUTO_CONFIG", isEnabled);
223
224 if (isEnabled) {
225 startNdnAutoConfig();
226 }
227 else {
228 stopNdnAutoConfig();
229 }
230}
231
232void
233TrayMenu::startNdnAutoConfig()
234{
235 if (m_acProc != nullptr) {
236 return;
237 }
238
239 stopNdnAutoConfig(false); // Make sure no more than one auto-config process exist
240
241 if (!m_isNfdRunning) {
242 return;
243 }
244
245 m_acProc = new QProcess();
246
247 scheduleDelayedTask(std::chrono::seconds(2), [this] {
248 appendNdnAutoConfigStatus("NDN auto configuration starting...\n");
249 m_acProc->start(QCoreApplication::applicationDirPath() + "/../Platform/ndn-autoconfig",
250 QStringList() << "--daemon");
251 connect(m_acProc, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
252 connect(m_acProc, SIGNAL(readyReadStandardError()), this, SLOT(processOutput()));
253 });
254}
255
256void
257TrayMenu::stopNdnAutoConfig(bool appendStatus)
258{
259 m_acProc = nullptr;
260 QProcess* proc = new QProcess();
261 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
262 proc->startDetached("killall", QStringList() << "ndn-autoconfig");
263 if (appendStatus) {
264 appendNdnAutoConfigStatus("NDN auto configuration stopped\n");
265 }
266}
267
268void
269TrayMenu::appendNdnAutoConfigStatus(const QString& message)
270{
271 if (message == "") {
272 return;
273 }
274 if (m_ndnAutoConfigMsg.count("\n") > MAX_LINES_IN_AUTOCONF_STATUS) {
275 int end = m_ndnAutoConfigMsg.indexOf("\n");
276 m_ndnAutoConfigMsg.remove(0, end + 1);
277 }
278 m_ndnAutoConfigMsg.append(message);
279
280 m_context->setContextProperty("ndnAutoConfigText", m_ndnAutoConfigMsg);
281}
282
283void
284TrayMenu::processOutput()
285{
286 if (m_acProc == nullptr) {
287 return;
288 }
289 appendNdnAutoConfigStatus(m_acProc->readAllStandardOutput());
290 appendNdnAutoConfigStatus(m_acProc->readAllStandardError());
291}
292
293////////////////////////////////////////////////////////////////////////////////
294// NFD stop on shutdown
295
296Q_INVOKABLE bool
297TrayMenu::isNfdStopOnExitEnabled() const
298{
299 return m_settings->value("ENABLE_SHUTDOWN", false).toBool();
300}
301
302Q_INVOKABLE void
303TrayMenu::enableDisableNfdStopOnExit(bool isEnabled)
304{
305 m_settings->setValue("ENABLE_SHUTDOWN", isEnabled);
306}
307
308////////////////////////////////////////////////////////////////////////////////
309// Misc
310
311void
312TrayMenu::quitApp()
313{
314 if (isNfdStopOnExitEnabled()) {
315 stopNdnAutoConfig();
316 stopNfd();
317 }
318 QCoreApplication::exit(0);
319}
320
susmit4fe3cb92016-03-20 17:08:41 -0700321Q_INVOKABLE void
322TrayMenu::addDeleteRoute()
323{
324 addRoute();
325}
326
327Q_INVOKABLE void
328TrayMenu::addRoute()
329{
330 std::cout << "Adding route" << std::endl;
331 QString cmd = "nfdc register /test tcp4://localhost";
332 QProcess *addNewRoute = new QProcess();
333 connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
334 addNewRoute->start("bash", QStringList() << "-c" << cmd);
335 std::cout << "Done" << std::endl;
susmit4fe3cb92016-03-20 17:08:41 -0700336}
337
338void
339TrayMenu::deleteRoute()
340{
341 std::cout << "Deleting route" << std::endl;
342 QString cmd = "nfdc unregister /test tcp4://localhost";
343 QProcess *addNewRoute = new QProcess();
344 connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
345 addNewRoute->start("bash", QStringList() << "-c" << cmd);
346 std::cout << "Done" << std::endl;
susmit4fe3cb92016-03-20 17:08:41 -0700347}
348
taylorchuc27dd482014-05-17 20:06:49 -0700349void
350TrayMenu::updateNfdActivityIcon(bool isActive)
351{
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700352 m_isNfdRunning = isActive;
353
taylorchuc27dd482014-05-17 20:06:49 -0700354 if (isActive) {
Qi Zhaodec77d92017-02-15 15:49:04 -0800355 if(m_isConnectedToHub) {
356 m_tray->setIcon(QIcon(CONNECT_STATUS_ICON));
357 }
358 else {
359 m_tray->setIcon(QIcon(CONNECT_ICON));
360 }
Qi Zhaoae9c4312017-02-02 11:36:12 -0800361 if (isNdnAutoConfigEnabled()) {
362 startNdnAutoConfig();
363 }
taylorchuc27dd482014-05-17 20:06:49 -0700364 }
365 else {
Yingdi Yu53c11c12016-03-20 12:56:49 -0700366 m_tray->setIcon(QIcon(DISCONNECT_ICON));
taylorchuc27dd482014-05-17 20:06:49 -0700367 }
368}
369
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700370void
Qi Zhaodec77d92017-02-15 15:49:04 -0800371TrayMenu::updateConnectivity(bool isConnectedToHub)
372{
373 m_isConnectedToHub = isConnectedToHub;
374}
375
376void
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700377TrayMenu::enableCli()
378{
379#ifdef OSX_BUILD
Qi Zhaoae9c4312017-02-02 11:36:12 -0800380 QProcess* proc = new QProcess();
381 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700382
Qi Zhaoae9c4312017-02-02 11:36:12 -0800383 proc->start("osascript", QStringList()
384 << "-e"
385 << "do shell script \""
386 "/bin/mkdir -vp /usr/local/bin; "
387 "/bin/ln -s -f '" + QCoreApplication::applicationDirPath() +
388 "/../Resources/ndn" + "' /usr/local/bin/ndn;"
389 "\" with administrator privileges");
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700390#endif
391}
392
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800393#ifdef OSX_BUILD
394void
395TrayMenu::checkForUpdates()
396{
397 m_sparkle.checkForUpdates();
398}
399#endif // OSX_BUILD
400
Qi Zhaoae9c4312017-02-02 11:36:12 -0800401void
402TrayMenu::scheduleDelayedTask(std::chrono::milliseconds delays, const std::function<void()>& task)
403{
404 auto timer = new QTimer();
405 timer->setSingleShot(true);
406 timer->setInterval(delays);
407 connect(timer, &QTimer::timeout,
408 [task, timer] {
409 task();
410 timer->deleteLater();
411 });
412 timer->start();
413}
414
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800415} // namespace ncc
taylorchuc27dd482014-05-17 20:06:49 -0700416} // namespace ndn