blob: 9bcdaa4284e201f39834c66ecc76be0cc3a0d28c [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"
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070033
34#include <Security/Authorization.h>
35#include <Security/AuthorizationTags.h>
taylorchuc27dd482014-05-17 20:06:49 -070036#else
Alexander Afanasyev0621cec2016-03-20 23:18:27 -070037#define CONNECT_ICON ":/res/icon-connected-white.png"
38#define DISCONNECT_ICON ":/res/icon-disconnected-white.png"
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070039#endif // OSX_BUILD
taylorchuc27dd482014-05-17 20:06:49 -070040
taylorchuc27dd482014-05-17 20:06:49 -070041namespace ndn {
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080042namespace ncc {
taylorchuc27dd482014-05-17 20:06:49 -070043
Qi Zhaoae9c4312017-02-02 11:36:12 -080044/**
45 * @brief Maximum number of lines that can show up in auto-config status tab view
46 */
47const int MAX_LINES_IN_AUTOCONF_STATUS = 100;
48
49#ifdef OSX_BUILD
50const QString AUTO_START_SUFFIX = "Library/LaunchAgents/net.named-data.control-center.plist";
51#endif // OSX_BUILD
52
Alexander Afanasyev81509f32016-03-21 17:02:38 -070053TrayMenu::TrayMenu(QQmlContext* context, Face& face)
Alexander Afanasyev4086d512014-07-11 15:56:33 -070054 : m_context(context)
55 , m_isNfdRunning(false)
Yingdi Yu53c11c12016-03-20 12:56:49 -070056 , m_menu(new QMenu(this))
57 , m_entryPref(new QAction("Preferences...", m_menu))
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070058 , m_entrySec(new QAction("Security...", m_menu))
Qi Zhaoae9c4312017-02-02 11:36:12 -080059 , m_acProc(nullptr)
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080060 , m_settings(new QSettings())
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070061#ifdef OSX_BUILD
62 , m_entryEnableCli(new QAction("Enable Command Terminal Usage...", m_menu))
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080063 , m_checkForUpdates(new QAction("Check for updates", m_menu))
64 , m_sparkle("https://irl.cs.ucla.edu/~cawka/ndn-control-center.xml")
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070065#endif
Yingdi Yu53c11c12016-03-20 12:56:49 -070066 , m_entryQuit(new QAction("Quit", m_menu))
Yingdi Yu1f824642016-03-20 17:07:22 -070067 , m_keyViewerDialog(new ncc::KeyViewerDialog)
Alexander Afanasyev81509f32016-03-21 17:02:38 -070068 , m_face(face)
Yingdi Yu53c11c12016-03-20 12:56:49 -070069{
70 connect(m_entryPref, SIGNAL(triggered()), this, SIGNAL(showApp()));
Yingdi Yu1f824642016-03-20 17:07:22 -070071 connect(m_entrySec, SIGNAL(triggered()), m_keyViewerDialog, SLOT(present()));
Yingdi Yu53c11c12016-03-20 12:56:49 -070072 connect(m_entryQuit, SIGNAL(triggered()), this, SLOT(quitApp()));
taylorchuc27dd482014-05-17 20:06:49 -070073
Alexander Afanasyev4086d512014-07-11 15:56:33 -070074 connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)),
75 Qt::QueuedConnection);
76
Yingdi Yu53c11c12016-03-20 12:56:49 -070077 m_menu->addAction(m_entryPref);
78 m_menu->addAction(m_entrySec);
Qi Zhaoae9c4312017-02-02 11:36:12 -080079
susmit50607912016-03-21 12:00:15 -070080#ifdef OSX_BUILD
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080081 connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -070082 m_menu->addAction(m_entryEnableCli);
Alexander Afanasyevfda42a82017-02-01 18:03:39 -080083
84 connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
85 m_menu->addAction(m_checkForUpdates);
susmit50607912016-03-21 12:00:15 -070086#endif
Qi Zhaoae9c4312017-02-02 11:36:12 -080087
Yingdi Yu53c11c12016-03-20 12:56:49 -070088 m_menu->addAction(m_entryQuit);
89 m_tray = new QSystemTrayIcon(this);
90 m_tray->setContextMenu(m_menu);
Yingdi Yu53c11c12016-03-20 12:56:49 -070091 m_tray->setIcon(QIcon(DISCONNECT_ICON));
92 m_tray->show();
Qi Zhaoae9c4312017-02-02 11:36:12 -080093
94 // delaying for the case when NFD is already started (to avoid starting / to avoid starting autoconfig
95 if (isNfdAutoStartEnabled()) {
96 scheduleDelayedTask(std::chrono::seconds(5), [this] {
97 if (!m_isNfdRunning) {
98 startNfd();
99 }
100 });
Qi Zhao86f2b212016-12-06 12:44:16 -0800101 }
taylorchuc27dd482014-05-17 20:06:49 -0700102}
103
104TrayMenu::~TrayMenu()
105{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800106 // delete for all new's
taylorchuc27dd482014-05-17 20:06:49 -0700107}
108
Qi Zhaoae9c4312017-02-02 11:36:12 -0800109////////////////////////////////////////////////////////////////////////////////
110// NFD Control Center start on login
Qi Zhao86f2b212016-12-06 12:44:16 -0800111
112Q_INVOKABLE bool
Qi Zhaoae9c4312017-02-02 11:36:12 -0800113TrayMenu::isNccAutoStartEnabled() const
Qi Zhao86f2b212016-12-06 12:44:16 -0800114{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800115 QFileInfo file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
116 return file.exists() && file.isFile();
Qi Zhao86f2b212016-12-06 12:44:16 -0800117}
118
119Q_INVOKABLE void
Qi Zhaoae9c4312017-02-02 11:36:12 -0800120TrayMenu::enableDisableNccAutoStart(bool isEnabled)
Qi Zhao86f2b212016-12-06 12:44:16 -0800121{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800122 if (isEnabled) {
123 QFile file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
124 file.open(QIODevice::WriteOnly);
125
126 std::string plist = R"PLIST(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
127<?xml version="1.0" encoding="UTF-8"?>
128<plist version="1.0">
129 <dict>
130 <key>Label</key>
131 <string>net.named-data.control-center</string>
132 <key>ProgramArguments</key>
133 <array>
134 <string>%%PATH%%</string>
135 </array>
136 <key>RunAtLoad</key>
137 <true/>
138 <key>KeepAlive</key>
139 <false/>
140 </dict>
141</plist>
142)PLIST"; //"
143 boost::replace_all(plist, "%%PATH%%", QCoreApplication::applicationFilePath().toStdString());
144 file.write(plist.data(), plist.size());
145 file.close();
146 } else {
147 QFile::remove(QDir::home().path() + "/" + AUTO_START_SUFFIX);
Qi Zhao86f2b212016-12-06 12:44:16 -0800148 }
taylorchuc27dd482014-05-17 20:06:49 -0700149}
150
Qi Zhaoae9c4312017-02-02 11:36:12 -0800151////////////////////////////////////////////////////////////////////////////////
152// NFD start on launch
taylorchuc27dd482014-05-17 20:06:49 -0700153
Qi Zhaoae9c4312017-02-02 11:36:12 -0800154Q_INVOKABLE bool
155TrayMenu::isNfdAutoStartEnabled() const
taylorchuc27dd482014-05-17 20:06:49 -0700156{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800157 return m_settings->value("ENABLE_AUTO_START", false).toBool();
taylorchuc27dd482014-05-17 20:06:49 -0700158}
159
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700160Q_INVOKABLE void
Qi Zhaoae9c4312017-02-02 11:36:12 -0800161TrayMenu::enableDisableNfdAutoStart(bool isEnabled)
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700162{
Qi Zhaoae9c4312017-02-02 11:36:12 -0800163 m_settings->setValue("ENABLE_AUTO_START", isEnabled);
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700164 if (!m_isNfdRunning) {
165 startNfd();
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700166 }
167}
168
taylorchuc27dd482014-05-17 20:06:49 -0700169void
170TrayMenu::startNfd()
171{
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700172#ifdef OSX_BUILD
173 QProcess* proc = new QProcess();
174 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
Qi Zhaoae9c4312017-02-02 11:36:12 -0800175 proc->startDetached((QCoreApplication::applicationDirPath() + "/../Platform/nfd"),
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700176 QStringList()
177 << "--config"
Qi Zhaoae9c4312017-02-02 11:36:12 -0800178 << (QCoreApplication::applicationDirPath() + "/../etc/ndn/nfd.conf"));
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700179#endif
taylorchuc27dd482014-05-17 20:06:49 -0700180}
181
182void
183TrayMenu::stopNfd()
184{
Alexander Afanasyev8e986f82016-03-21 14:19:15 -0700185#ifdef OSX_BUILD
186 QProcess* proc = new QProcess();
187 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
188 proc->startDetached("killall", QStringList() << "nfd");
189#endif
taylorchuc27dd482014-05-17 20:06:49 -0700190}
191
Qi Zhaoae9c4312017-02-02 11:36:12 -0800192////////////////////////////////////////////////////////////////////////////////
193// NDN Autoconfig
194
195Q_INVOKABLE bool
196TrayMenu::isNdnAutoConfigEnabled() const
197{
198 return m_settings->value("ENABLE_NDN_AUTO_CONFIG", false).toBool();
199}
200
201Q_INVOKABLE void
202TrayMenu::enableDisableNdnAutoConfig(bool isEnabled)
203{
204 m_settings->setValue("ENABLE_NDN_AUTO_CONFIG", isEnabled);
205
206 if (isEnabled) {
207 startNdnAutoConfig();
208 }
209 else {
210 stopNdnAutoConfig();
211 }
212}
213
214void
215TrayMenu::startNdnAutoConfig()
216{
217 if (m_acProc != nullptr) {
218 return;
219 }
220
221 stopNdnAutoConfig(false); // Make sure no more than one auto-config process exist
222
223 if (!m_isNfdRunning) {
224 return;
225 }
226
227 m_acProc = new QProcess();
228
229 scheduleDelayedTask(std::chrono::seconds(2), [this] {
230 appendNdnAutoConfigStatus("NDN auto configuration starting...\n");
231 m_acProc->start(QCoreApplication::applicationDirPath() + "/../Platform/ndn-autoconfig",
232 QStringList() << "--daemon");
233 connect(m_acProc, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
234 connect(m_acProc, SIGNAL(readyReadStandardError()), this, SLOT(processOutput()));
235 });
236}
237
238void
239TrayMenu::stopNdnAutoConfig(bool appendStatus)
240{
241 m_acProc = nullptr;
242 QProcess* proc = new QProcess();
243 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
244 proc->startDetached("killall", QStringList() << "ndn-autoconfig");
245 if (appendStatus) {
246 appendNdnAutoConfigStatus("NDN auto configuration stopped\n");
247 }
248}
249
250void
251TrayMenu::appendNdnAutoConfigStatus(const QString& message)
252{
253 if (message == "") {
254 return;
255 }
256 if (m_ndnAutoConfigMsg.count("\n") > MAX_LINES_IN_AUTOCONF_STATUS) {
257 int end = m_ndnAutoConfigMsg.indexOf("\n");
258 m_ndnAutoConfigMsg.remove(0, end + 1);
259 }
260 m_ndnAutoConfigMsg.append(message);
261
262 m_context->setContextProperty("ndnAutoConfigText", m_ndnAutoConfigMsg);
263}
264
265void
266TrayMenu::processOutput()
267{
268 if (m_acProc == nullptr) {
269 return;
270 }
271 appendNdnAutoConfigStatus(m_acProc->readAllStandardOutput());
272 appendNdnAutoConfigStatus(m_acProc->readAllStandardError());
273}
274
275////////////////////////////////////////////////////////////////////////////////
276// NFD stop on shutdown
277
278Q_INVOKABLE bool
279TrayMenu::isNfdStopOnExitEnabled() const
280{
281 return m_settings->value("ENABLE_SHUTDOWN", false).toBool();
282}
283
284Q_INVOKABLE void
285TrayMenu::enableDisableNfdStopOnExit(bool isEnabled)
286{
287 m_settings->setValue("ENABLE_SHUTDOWN", isEnabled);
288}
289
290////////////////////////////////////////////////////////////////////////////////
291// Misc
292
293void
294TrayMenu::quitApp()
295{
296 if (isNfdStopOnExitEnabled()) {
297 stopNdnAutoConfig();
298 stopNfd();
299 }
300 QCoreApplication::exit(0);
301}
302
susmit4fe3cb92016-03-20 17:08:41 -0700303Q_INVOKABLE void
304TrayMenu::addDeleteRoute()
305{
306 addRoute();
307}
308
309Q_INVOKABLE void
310TrayMenu::addRoute()
311{
312 std::cout << "Adding route" << std::endl;
313 QString cmd = "nfdc register /test tcp4://localhost";
314 QProcess *addNewRoute = new QProcess();
315 connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
316 addNewRoute->start("bash", QStringList() << "-c" << cmd);
317 std::cout << "Done" << std::endl;
susmit4fe3cb92016-03-20 17:08:41 -0700318}
319
320void
321TrayMenu::deleteRoute()
322{
323 std::cout << "Deleting route" << std::endl;
324 QString cmd = "nfdc unregister /test tcp4://localhost";
325 QProcess *addNewRoute = new QProcess();
326 connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
327 addNewRoute->start("bash", QStringList() << "-c" << cmd);
328 std::cout << "Done" << std::endl;
susmit4fe3cb92016-03-20 17:08:41 -0700329}
330
taylorchuc27dd482014-05-17 20:06:49 -0700331void
332TrayMenu::updateNfdActivityIcon(bool isActive)
333{
Alexander Afanasyev4086d512014-07-11 15:56:33 -0700334 m_isNfdRunning = isActive;
335
taylorchuc27dd482014-05-17 20:06:49 -0700336 if (isActive) {
Yingdi Yu53c11c12016-03-20 12:56:49 -0700337 m_tray->setIcon(QIcon(CONNECT_ICON));
Qi Zhaoae9c4312017-02-02 11:36:12 -0800338 if (isNdnAutoConfigEnabled()) {
339 startNdnAutoConfig();
340 }
taylorchuc27dd482014-05-17 20:06:49 -0700341 }
342 else {
Yingdi Yu53c11c12016-03-20 12:56:49 -0700343 m_tray->setIcon(QIcon(DISCONNECT_ICON));
taylorchuc27dd482014-05-17 20:06:49 -0700344 }
345}
346
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700347void
348TrayMenu::enableCli()
349{
350#ifdef OSX_BUILD
Qi Zhaoae9c4312017-02-02 11:36:12 -0800351 QProcess* proc = new QProcess();
352 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700353
Qi Zhaoae9c4312017-02-02 11:36:12 -0800354 proc->start("osascript", QStringList()
355 << "-e"
356 << "do shell script \""
357 "/bin/mkdir -vp /usr/local/bin; "
358 "/bin/ln -s -f '" + QCoreApplication::applicationDirPath() +
359 "/../Resources/ndn" + "' /usr/local/bin/ndn;"
360 "\" with administrator privileges");
Alexander Afanasyev11ae34d2016-03-21 11:55:16 -0700361#endif
362}
363
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800364#ifdef OSX_BUILD
365void
366TrayMenu::checkForUpdates()
367{
368 m_sparkle.checkForUpdates();
369}
370#endif // OSX_BUILD
371
Qi Zhaoae9c4312017-02-02 11:36:12 -0800372void
373TrayMenu::scheduleDelayedTask(std::chrono::milliseconds delays, const std::function<void()>& task)
374{
375 auto timer = new QTimer();
376 timer->setSingleShot(true);
377 timer->setInterval(delays);
378 connect(timer, &QTimer::timeout,
379 [task, timer] {
380 task();
381 timer->deleteLater();
382 });
383 timer->start();
384}
385
Alexander Afanasyevfda42a82017-02-01 18:03:39 -0800386} // namespace ncc
taylorchuc27dd482014-05-17 20:06:49 -0700387} // namespace ndn