| /* -*- 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 <ndn-cxx/face.hpp> |
| #include <ndn-cxx/interest.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 { |
| |
| 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(new QProcess()) |
| , 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(this, SIGNAL(showApp()), this, SLOT(showPref())); |
| 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_context->setContextProperty("startStopButtonText", QVariant::fromValue(QString("Start NFD"))); |
| m_context->setContextProperty("acText", QVariant::fromValue(QString(""))); |
| |
| 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); |
| connect(m_tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), |
| this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); |
| m_tray->setIcon(QIcon(DISCONNECT_ICON)); |
| m_tray->show(); |
| if (isAutoConfigEnabled()) { |
| stopAutoConfig(); // Make sure no more than one auto-config process exist |
| QTimer* mTimer = new QTimer(this); |
| mTimer->setSingleShot(true); |
| connect(mTimer, SIGNAL(timeout()), SLOT(startAutoConfig())); |
| mTimer->start(2000); |
| } |
| } |
| |
| TrayMenu::~TrayMenu() |
| { |
| } |
| |
| void |
| TrayMenu::appendMsg(QString &target, QString source) |
| { |
| int maxLine = 100; // Max line that will show in auto-config status tab view |
| if (target.count(QString("\n")) > maxLine) { // Only when target QString has more line than maxLine, it needs remove line |
| int end = target.indexOf(QString("\n")); |
| target.remove(0, end + 1); // Remove the first line of target |
| } |
| target.append(source); |
| } |
| |
| void |
| TrayMenu::showPref() |
| { |
| m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg)); // Update auto-config status tab view |
| } |
| |
| void |
| TrayMenu::processOutput() |
| { |
| std::string msg = m_acProc->readAllStandardOutput().toStdString(); |
| if (msg != "") { |
| appendMsg(m_autoConfigMsg, QString::fromStdString(msg)); |
| } |
| msg = m_acProc->readAllStandardError().toStdString(); |
| if (msg != "") { |
| appendMsg(m_autoConfigMsg, QString::fromStdString(msg)); |
| } |
| m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg)); |
| } |
| |
| Q_INVOKABLE bool |
| TrayMenu::isAutoConfigEnabled() |
| { |
| bool sAutoConfig = m_settings.value("main/auto-config", QVariant(false)).toBool(); |
| return sAutoConfig; |
| } |
| |
| void |
| TrayMenu::startAutoConfig() |
| { |
| if (m_isNfdRunning) { |
| appendMsg(m_autoConfigMsg, QString("NDN auto configuration will start...\n")); |
| connect(m_acProc, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput())); |
| connect(m_acProc, SIGNAL(readyReadStandardError()), this, SLOT(processOutput())); |
| m_acProc->start((QCoreApplication::applicationDirPath().toStdString() + "/../Platform/ndn-autoconfig").c_str(), |
| QStringList() |
| << "--daemon"); |
| } |
| } |
| |
| void |
| TrayMenu::stopAutoConfig() |
| { |
| #ifdef OSX_BUILD |
| QProcess* proc = new QProcess(); |
| connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); |
| proc->startDetached("killall", QStringList() << "ndn-autoconfig"); |
| #endif |
| } |
| |
| Q_INVOKABLE void |
| TrayMenu::startStopAutoConfig(bool autoConfig) |
| { |
| if (m_isNfdRunning) { |
| if (autoConfig) { |
| stopAutoConfig(); // Make sure no more than one auto-config process exist |
| QTimer* mTimer = new QTimer(this); |
| mTimer->setSingleShot(true); |
| connect(mTimer, SIGNAL(timeout()), SLOT(startAutoConfig())); |
| mTimer->start(2000); |
| m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg)); |
| m_settings.setValue("main/auto-config", true); |
| } |
| else { |
| stopAutoConfig(); |
| appendMsg(m_autoConfigMsg, QString("NDN auto configuration will be stopped!\n")); |
| m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg)); |
| m_settings.setValue("main/auto-config", false); |
| } |
| } |
| else { // No need to start or stop auto-config when NFD is not running, but it needs to update settings |
| if (autoConfig) { |
| m_settings.setValue("main/auto-config", true); |
| } |
| else { |
| m_settings.setValue("main/auto-config", false); |
| } |
| } |
| } |
| |
| void |
| TrayMenu::quitApp() |
| { |
| QCoreApplication::exit(0); |
| } |
| |
| void |
| TrayMenu::iconActivated(QSystemTrayIcon::ActivationReason reason) |
| { |
| switch (reason) { |
| // case QSystemTrayIcon::Trigger: |
| // emit showApp(); |
| // break; |
| case QSystemTrayIcon::Context: |
| break; |
| default: |
| break; |
| } |
| } |
| |
| Q_INVOKABLE void |
| TrayMenu::startStopNfd(bool autoConfig) |
| { |
| if (!m_isNfdRunning) { |
| startNfd(); |
| if (autoConfig) { |
| stopAutoConfig(); // Make sure no more than one auto-config process exist |
| QTimer* mTimer = new QTimer(this); |
| mTimer->setSingleShot(true); |
| connect(mTimer, SIGNAL(timeout()), SLOT(startAutoConfig())); |
| mTimer->start(2000); |
| m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg)); |
| } |
| } |
| else { |
| stopNfd(); |
| stopAutoConfig(); |
| appendMsg(m_autoConfigMsg, QString("NDN auto configuration will be stopped!\n")); |
| m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg)); |
| } |
| } |
| |
| void |
| TrayMenu::startNfd() |
| { |
| #ifdef OSX_BUILD |
| QProcess* proc = new QProcess(); |
| connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); |
| proc->startDetached((QCoreApplication::applicationDirPath().toStdString() + "/../Platform/nfd").c_str(), |
| QStringList() |
| << "--config" |
| << (QCoreApplication::applicationDirPath().toStdString() + "/../etc/ndn/nfd.conf").c_str()); |
| |
| |
| // #endif |
| // QProcess * proc = new QProcess(); |
| // connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); |
| // #ifdef __linux__ |
| // proc->start("gksudo", QStringList() << NFD_START_COMMAND); |
| // #else |
| // proc->start("osascript", QStringList() |
| // << "-e" |
| // << "do shell script \"" NFD_START_COMMAND "\" with administrator privileges"); |
| #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 |
| } |
| |
| 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)); |
| m_context->setContextProperty("startStopButtonText", QVariant::fromValue(QString("Stop NFD"))); |
| } |
| else { |
| m_tray->setIcon(QIcon(DISCONNECT_ICON)); |
| m_context->setContextProperty("startStopButtonText", QVariant::fromValue(QString("Start NFD"))); |
| } |
| } |
| |
| void |
| TrayMenu::enableCli() |
| { |
| #ifdef OSX_BUILD |
| AuthorizationRef authorizationRef; |
| OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, |
| kAuthorizationFlagDefaults, &authorizationRef); |
| if (status != errAuthorizationSuccess) |
| return; |
| |
| AuthorizationItem item = { kAuthorizationRightExecute, 0, 0, 0 }; |
| AuthorizationRights rights = { 1, &item }; |
| const AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
| | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights; |
| |
| status = AuthorizationCopyRights(authorizationRef, &rights, kAuthorizationEmptyEnvironment, |
| flags, 0); |
| if (status != errAuthorizationSuccess) |
| return; |
| |
| char const* mkdir_arg[] = { "-p", "/usr/local/bin", nullptr }; |
| char const* mkdir = "/bin/mkdir"; |
| AuthorizationExecuteWithPrivileges(authorizationRef, |
| mkdir, |
| kAuthorizationFlagDefaults, (char**)mkdir_arg, nullptr); |
| |
| std::vector<std::string> arguments = { "-f", "-s", |
| QCoreApplication::applicationDirPath().toStdString() + "/../Resources/ndn", |
| "/usr/local/bin/ndn" }; |
| std::vector<const char*> args; |
| for (const auto& i : arguments) { |
| args.push_back(i.c_str()); |
| } |
| args.push_back(nullptr); |
| |
| char const* helperTool = "/bin/ln"; |
| AuthorizationExecuteWithPrivileges(authorizationRef, |
| helperTool, |
| kAuthorizationFlagDefaults, |
| (char**)args.data(), NULL); |
| |
| AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights); |
| #endif |
| } |
| |
| #ifdef OSX_BUILD |
| void |
| TrayMenu::checkForUpdates() |
| { |
| m_sparkle.checkForUpdates(); |
| } |
| #endif // OSX_BUILD |
| |
| } // namespace ncc |
| } // namespace ndn |