| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /** |
| * Copyright (c) 2013-2017, Regents of the University of California, |
| * |
| * This file is part of NFD Control Center. See AUTHORS.md for complete list of NFD |
| * authors and contributors. |
| * |
| * NFD Control Center is free software: you can redistribute it and/or modify it under the |
| * terms of the GNU General Public License as published by the Free Software Foundation, |
| * either version 3 of the License, or (at your option) any later version. |
| * |
| * NFD Control Center is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
| * PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along with NFD |
| * Control Center, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "tray-menu.hpp" |
| #include "tray-menu.moc" |
| |
| #include <chrono> |
| #include <functional> |
| |
| #include <ndn-cxx/face.hpp> |
| #include <ndn-cxx/interest.hpp> |
| #include <boost/algorithm/string/replace.hpp> |
| |
| #ifdef OSX_BUILD |
| #define CONNECT_ICON ":/res/icon-connected-black.png" |
| #define DISCONNECT_ICON ":/res/icon-disconnected-black.png" |
| |
| #include <Security/Authorization.h> |
| #include <Security/AuthorizationTags.h> |
| #else |
| #define CONNECT_ICON ":/res/icon-connected-white.png" |
| #define DISCONNECT_ICON ":/res/icon-disconnected-white.png" |
| #endif // OSX_BUILD |
| |
| namespace ndn { |
| namespace ncc { |
| |
| /** |
| * @brief Maximum number of lines that can show up in auto-config status tab view |
| */ |
| const int MAX_LINES_IN_AUTOCONF_STATUS = 100; |
| |
| #ifdef OSX_BUILD |
| const QString AUTO_START_SUFFIX = "Library/LaunchAgents/net.named-data.control-center.plist"; |
| #endif // OSX_BUILD |
| |
| TrayMenu::TrayMenu(QQmlContext* context, Face& face) |
| : m_context(context) |
| , m_isNfdRunning(false) |
| , m_menu(new QMenu(this)) |
| , m_entryPref(new QAction("Preferences...", m_menu)) |
| , m_entrySec(new QAction("Security...", m_menu)) |
| , m_acProc(nullptr) |
| , m_settings(new QSettings()) |
| #ifdef OSX_BUILD |
| , m_entryEnableCli(new QAction("Enable Command Terminal Usage...", m_menu)) |
| , m_checkForUpdates(new QAction("Check for updates", m_menu)) |
| , m_sparkle("https://irl.cs.ucla.edu/~cawka/ndn-control-center.xml") |
| #endif |
| , m_entryQuit(new QAction("Quit", m_menu)) |
| , m_keyViewerDialog(new ncc::KeyViewerDialog) |
| , m_face(face) |
| { |
| connect(m_entryPref, SIGNAL(triggered()), this, SIGNAL(showApp())); |
| connect(m_entrySec, SIGNAL(triggered()), m_keyViewerDialog, SLOT(present())); |
| connect(m_entryQuit, SIGNAL(triggered()), this, SLOT(quitApp())); |
| |
| connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)), |
| Qt::QueuedConnection); |
| |
| m_menu->addAction(m_entryPref); |
| m_menu->addAction(m_entrySec); |
| |
| #ifdef OSX_BUILD |
| connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli())); |
| m_menu->addAction(m_entryEnableCli); |
| |
| connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(checkForUpdates())); |
| m_menu->addAction(m_checkForUpdates); |
| #endif |
| |
| m_menu->addAction(m_entryQuit); |
| m_tray = new QSystemTrayIcon(this); |
| m_tray->setContextMenu(m_menu); |
| m_tray->setIcon(QIcon(DISCONNECT_ICON)); |
| m_tray->show(); |
| |
| // delaying for the case when NFD is already started (to avoid starting / to avoid starting autoconfig |
| if (isNfdAutoStartEnabled()) { |
| scheduleDelayedTask(std::chrono::seconds(5), [this] { |
| if (!m_isNfdRunning) { |
| startNfd(); |
| } |
| }); |
| } |
| } |
| |
| TrayMenu::~TrayMenu() |
| { |
| // delete for all new's |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // NFD Control Center start on login |
| |
| Q_INVOKABLE bool |
| TrayMenu::isNccAutoStartEnabled() const |
| { |
| QFileInfo file(QDir::home().path() + "/" + AUTO_START_SUFFIX); |
| return file.exists() && file.isFile(); |
| } |
| |
| Q_INVOKABLE void |
| TrayMenu::enableDisableNccAutoStart(bool isEnabled) |
| { |
| if (isEnabled) { |
| QFile file(QDir::home().path() + "/" + AUTO_START_SUFFIX); |
| file.open(QIODevice::WriteOnly); |
| |
| std::string plist = R"PLIST(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| <?xml version="1.0" encoding="UTF-8"?> |
| <plist version="1.0"> |
| <dict> |
| <key>Label</key> |
| <string>net.named-data.control-center</string> |
| <key>ProgramArguments</key> |
| <array> |
| <string>%%PATH%%</string> |
| </array> |
| <key>RunAtLoad</key> |
| <true/> |
| <key>KeepAlive</key> |
| <false/> |
| </dict> |
| </plist> |
| )PLIST"; //" |
| boost::replace_all(plist, "%%PATH%%", QCoreApplication::applicationFilePath().toStdString()); |
| file.write(plist.data(), plist.size()); |
| file.close(); |
| } else { |
| QFile::remove(QDir::home().path() + "/" + AUTO_START_SUFFIX); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // NFD start on launch |
| |
| Q_INVOKABLE bool |
| TrayMenu::isNfdAutoStartEnabled() const |
| { |
| return m_settings->value("ENABLE_AUTO_START", false).toBool(); |
| } |
| |
| Q_INVOKABLE void |
| TrayMenu::enableDisableNfdAutoStart(bool isEnabled) |
| { |
| m_settings->setValue("ENABLE_AUTO_START", isEnabled); |
| if (!m_isNfdRunning) { |
| startNfd(); |
| } |
| } |
| |
| void |
| TrayMenu::startNfd() |
| { |
| #ifdef OSX_BUILD |
| QProcess* proc = new QProcess(); |
| connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); |
| proc->startDetached((QCoreApplication::applicationDirPath() + "/../Platform/nfd"), |
| QStringList() |
| << "--config" |
| << (QCoreApplication::applicationDirPath() + "/../etc/ndn/nfd.conf")); |
| #endif |
| } |
| |
| void |
| TrayMenu::stopNfd() |
| { |
| #ifdef OSX_BUILD |
| QProcess* proc = new QProcess(); |
| connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); |
| proc->startDetached("killall", QStringList() << "nfd"); |
| #endif |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // NDN Autoconfig |
| |
| Q_INVOKABLE bool |
| TrayMenu::isNdnAutoConfigEnabled() const |
| { |
| return m_settings->value("ENABLE_NDN_AUTO_CONFIG", false).toBool(); |
| } |
| |
| Q_INVOKABLE void |
| TrayMenu::enableDisableNdnAutoConfig(bool isEnabled) |
| { |
| m_settings->setValue("ENABLE_NDN_AUTO_CONFIG", isEnabled); |
| |
| if (isEnabled) { |
| startNdnAutoConfig(); |
| } |
| else { |
| stopNdnAutoConfig(); |
| } |
| } |
| |
| void |
| TrayMenu::startNdnAutoConfig() |
| { |
| if (m_acProc != nullptr) { |
| return; |
| } |
| |
| stopNdnAutoConfig(false); // Make sure no more than one auto-config process exist |
| |
| if (!m_isNfdRunning) { |
| return; |
| } |
| |
| m_acProc = new QProcess(); |
| |
| scheduleDelayedTask(std::chrono::seconds(2), [this] { |
| appendNdnAutoConfigStatus("NDN auto configuration starting...\n"); |
| m_acProc->start(QCoreApplication::applicationDirPath() + "/../Platform/ndn-autoconfig", |
| QStringList() << "--daemon"); |
| connect(m_acProc, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput())); |
| connect(m_acProc, SIGNAL(readyReadStandardError()), this, SLOT(processOutput())); |
| }); |
| } |
| |
| void |
| TrayMenu::stopNdnAutoConfig(bool appendStatus) |
| { |
| m_acProc = nullptr; |
| QProcess* proc = new QProcess(); |
| connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); |
| proc->startDetached("killall", QStringList() << "ndn-autoconfig"); |
| if (appendStatus) { |
| appendNdnAutoConfigStatus("NDN auto configuration stopped\n"); |
| } |
| } |
| |
| void |
| TrayMenu::appendNdnAutoConfigStatus(const QString& message) |
| { |
| if (message == "") { |
| return; |
| } |
| if (m_ndnAutoConfigMsg.count("\n") > MAX_LINES_IN_AUTOCONF_STATUS) { |
| int end = m_ndnAutoConfigMsg.indexOf("\n"); |
| m_ndnAutoConfigMsg.remove(0, end + 1); |
| } |
| m_ndnAutoConfigMsg.append(message); |
| |
| m_context->setContextProperty("ndnAutoConfigText", m_ndnAutoConfigMsg); |
| } |
| |
| void |
| TrayMenu::processOutput() |
| { |
| if (m_acProc == nullptr) { |
| return; |
| } |
| appendNdnAutoConfigStatus(m_acProc->readAllStandardOutput()); |
| appendNdnAutoConfigStatus(m_acProc->readAllStandardError()); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // NFD stop on shutdown |
| |
| Q_INVOKABLE bool |
| TrayMenu::isNfdStopOnExitEnabled() const |
| { |
| return m_settings->value("ENABLE_SHUTDOWN", false).toBool(); |
| } |
| |
| Q_INVOKABLE void |
| TrayMenu::enableDisableNfdStopOnExit(bool isEnabled) |
| { |
| m_settings->setValue("ENABLE_SHUTDOWN", isEnabled); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Misc |
| |
| void |
| TrayMenu::quitApp() |
| { |
| if (isNfdStopOnExitEnabled()) { |
| stopNdnAutoConfig(); |
| stopNfd(); |
| } |
| QCoreApplication::exit(0); |
| } |
| |
| Q_INVOKABLE void |
| TrayMenu::addDeleteRoute() |
| { |
| addRoute(); |
| } |
| |
| Q_INVOKABLE void |
| TrayMenu::addRoute() |
| { |
| std::cout << "Adding route" << std::endl; |
| QString cmd = "nfdc register /test tcp4://localhost"; |
| QProcess *addNewRoute = new QProcess(); |
| connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater())); |
| addNewRoute->start("bash", QStringList() << "-c" << cmd); |
| std::cout << "Done" << std::endl; |
| } |
| |
| void |
| TrayMenu::deleteRoute() |
| { |
| std::cout << "Deleting route" << std::endl; |
| QString cmd = "nfdc unregister /test tcp4://localhost"; |
| QProcess *addNewRoute = new QProcess(); |
| connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater())); |
| addNewRoute->start("bash", QStringList() << "-c" << cmd); |
| std::cout << "Done" << std::endl; |
| } |
| |
| void |
| TrayMenu::updateNfdActivityIcon(bool isActive) |
| { |
| m_isNfdRunning = isActive; |
| |
| if (isActive) { |
| m_tray->setIcon(QIcon(CONNECT_ICON)); |
| if (isNdnAutoConfigEnabled()) { |
| startNdnAutoConfig(); |
| } |
| } |
| else { |
| m_tray->setIcon(QIcon(DISCONNECT_ICON)); |
| } |
| } |
| |
| void |
| TrayMenu::enableCli() |
| { |
| #ifdef OSX_BUILD |
| QProcess* proc = new QProcess(); |
| connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); |
| |
| proc->start("osascript", QStringList() |
| << "-e" |
| << "do shell script \"" |
| "/bin/mkdir -vp /usr/local/bin; " |
| "/bin/ln -s -f '" + QCoreApplication::applicationDirPath() + |
| "/../Resources/ndn" + "' /usr/local/bin/ndn;" |
| "\" with administrator privileges"); |
| #endif |
| } |
| |
| #ifdef OSX_BUILD |
| void |
| TrayMenu::checkForUpdates() |
| { |
| m_sparkle.checkForUpdates(); |
| } |
| #endif // OSX_BUILD |
| |
| void |
| TrayMenu::scheduleDelayedTask(std::chrono::milliseconds delays, const std::function<void()>& task) |
| { |
| auto timer = new QTimer(); |
| timer->setSingleShot(true); |
| timer->setInterval(delays); |
| connect(timer, &QTimer::timeout, |
| [task, timer] { |
| task(); |
| timer->deleteLater(); |
| }); |
| timer->start(); |
| } |
| |
| } // namespace ncc |
| } // namespace ndn |