osx: Implement auto-upgrade function using Sparkle

During the upgrade, NFD and ndn-autoupdate will be stopped (and, if
enabled, automatically started with new versions).

Change-Id: I010ae4a8aa6f99a0039d58065ab7eb0c9189a979
diff --git a/src/Info.plist b/src/Info.plist
index 40eda16..6bb818d 100644
--- a/src/Info.plist
+++ b/src/Info.plist
@@ -33,7 +33,7 @@
     <key>LSUIElement</key>
     <string>1</string>
     <key>SUFeedURL</key>
-    <string>http://named-data.net/binaries/ndn-appcast.xml</string>
+    <string>https://irl.cs.ucla.edu/~cawka/ndn-control-center.xml</string>
     <key>SUPublicDSAKeyFile</key>
     <string>ndn_sparkle_pub.pem</string>
 </dict>
diff --git a/src/main.cpp b/src/main.cpp
index d03f0cd..e39e70c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -140,7 +140,7 @@
 
   ForwarderStatusModel m_forwarderStatusModel;
   FibStatusModel m_fibModel;
-  TrayMenu m_tray;
+  ncc::TrayMenu m_tray;
 };
 
 } // namespace ndn
diff --git a/src/osx-auto-update-sparkle.hpp b/src/osx-auto-update-sparkle.hpp
new file mode 100644
index 0000000..716f1dc
--- /dev/null
+++ b/src/osx-auto-update-sparkle.hpp
@@ -0,0 +1,53 @@
+/* -*- 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/>.
+ */
+
+#ifndef NCC_OSX_AUTO_UPDATE_SPARKLE_HPP
+#define NCC_OSX_AUTO_UPDATE_SPARKLE_HPP
+
+#include "config.hpp"
+
+#ifndef OSX_BUILD
+#error "Cannot be included for non-OSX build"
+#endif // OSX_BUILD
+
+#include <string>
+#include <ndn-cxx/util/backports.hpp>
+
+namespace ndn {
+namespace ncc {
+
+class OsxAutoUpdateSparkle
+{
+public:
+  OsxAutoUpdateSparkle(const std::string& updateUrl);
+
+  ~OsxAutoUpdateSparkle();
+
+  void
+  checkForUpdates();
+
+private:
+  class Impl;
+  unique_ptr<Impl> m_impl;
+};
+
+} // namespace ncc
+} // namespace ndn
+
+#endif // NCC_OSX_AUTO_UPDATE_SPARKLE_HPP
diff --git a/src/osx-auto-update-sparkle.mm b/src/osx-auto-update-sparkle.mm
new file mode 100644
index 0000000..a4e5a0f
--- /dev/null
+++ b/src/osx-auto-update-sparkle.mm
@@ -0,0 +1,96 @@
+/* -*- 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 "osx-auto-update-sparkle.hpp"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+#import <Sparkle/Sparkle.h>
+
+@interface UpdaterDelegate : NSObject<SUUpdaterDelegate> {
+  ndn::ncc::OsxAutoUpdateSparkle* this_;
+}
+
+- (void)setSelf:(ndn::ncc::OsxAutoUpdateSparkle*)ptr;
+
+- (BOOL)updater:(SUUpdater *)updater
+        shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update
+        untilInvoking:(NSInvocation *)invocation;
+
+@end
+
+@implementation UpdaterDelegate
+
+- (void)setSelf:(ndn::ncc::OsxAutoUpdateSparkle*)ptr
+{
+  this_ = ptr;
+}
+
+- (BOOL)updater:(SUUpdater *)updater
+        shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update
+        untilInvoking:(NSInvocation *)invocation
+{
+  NSTask *task = [[NSTask alloc] init];
+  task.launchPath = @"/usr/bin/killall";
+  task.arguments = @[@"ndn-autoconfig", @"nfd"];
+  [task launch];
+  return NO;
+}
+
+@end
+
+namespace ndn {
+namespace ncc {
+
+class OsxAutoUpdateSparkle::Impl
+{
+public:
+  SUUpdater* m_updater;
+  UpdaterDelegate* delegate;
+};
+
+OsxAutoUpdateSparkle::OsxAutoUpdateSparkle(const std::string& updateUrl)
+  : m_impl(make_unique<Impl>())
+{
+  m_impl->m_updater = [[SUUpdater sharedUpdater] retain];
+  NSURL* url = [NSURL URLWithString:[NSString stringWithUTF8String:updateUrl.data()]];
+  [m_impl->m_updater setFeedURL:url];
+  [m_impl->m_updater setAutomaticallyChecksForUpdates:YES];
+  [m_impl->m_updater setUpdateCheckInterval:86400];
+
+  m_impl->delegate = [[UpdaterDelegate alloc] init];
+  [m_impl->delegate setSelf:this];
+  [m_impl->m_updater setDelegate:m_impl->delegate];
+}
+
+OsxAutoUpdateSparkle::~OsxAutoUpdateSparkle()
+{
+  [m_impl->m_updater release];
+  // presummably SUUpdater handles garbage collection
+}
+
+void
+OsxAutoUpdateSparkle::checkForUpdates()
+{
+  //[m_impl->m_updater checkForUpdatesInBackground];
+  [m_impl->m_updater checkForUpdates:nil];
+}
+
+} // namespace ncc
+} // namespace ndn
diff --git a/src/tray-menu.cpp b/src/tray-menu.cpp
index d4d2637..2ec3cb4 100644
--- a/src/tray-menu.cpp
+++ b/src/tray-menu.cpp
@@ -35,6 +35,7 @@
 #endif // OSX_BUILD
 
 namespace ndn {
+namespace ncc {
 
 TrayMenu::TrayMenu(QQmlContext* context, Face& face)
   : m_context(context)
@@ -42,24 +43,22 @@
   , 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)
-  , m_acProc(new QProcess())
-  , m_settings(new QSettings())
 {
   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()));
 
-#ifdef OSX_BUILD
-  connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
-#endif
-
   connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)),
           Qt::QueuedConnection);
 
@@ -69,7 +68,11 @@
   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);
@@ -284,7 +287,6 @@
   connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
   addNewRoute->start("bash", QStringList() << "-c" << cmd);
   std::cout << "Done" << std::endl;
-
 }
 
 void
@@ -347,4 +349,13 @@
 #endif
 }
 
+#ifdef OSX_BUILD
+void
+TrayMenu::checkForUpdates()
+{
+  m_sparkle.checkForUpdates();
+}
+#endif // OSX_BUILD
+
+} // namespace ncc
 } // namespace ndn
diff --git a/src/tray-menu.hpp b/src/tray-menu.hpp
index 3c7b0f0..841f068 100644
--- a/src/tray-menu.hpp
+++ b/src/tray-menu.hpp
@@ -39,10 +39,16 @@
 
 #include "key-viewer-dialog.hpp"
 
+#ifdef OSX_BUILD
+#include "osx-auto-update-sparkle.hpp"
+#endif // OSX_BUILD
+
 namespace ndn {
 
 class Face;
 
+namespace ncc {
+
 class TrayMenu : public QWidget
 {
   Q_OBJECT
@@ -113,6 +119,11 @@
   static void
   appendMsg(QString &target, QString source);
 
+#ifdef OSX_BUILD
+  void
+  checkForUpdates();
+#endif // OSX_BUILD
+
 private:
   QQmlContext* m_context;
   bool m_isNfdRunning;
@@ -125,7 +136,9 @@
   QSettings m_settings;
 #ifdef OSX_BUILD
   QAction* m_entryEnableCli;
-#endif
+  QAction* m_checkForUpdates;
+  OsxAutoUpdateSparkle m_sparkle;
+#endif // OSX_BUILD
 
   QAction* m_entryQuit;
 
@@ -133,6 +146,7 @@
   Face& m_face;
 };
 
+} // namespace ncc
 } // namespace ndn
 
 #endif // NCC_TRAY_MENU_HPP