Merge feature branch 'feature-auto-update'

Change-Id: I8949b3bb24d853e486b00a82b0e3a63d0d5cf1e2
diff --git a/gui/chronosharegui.cpp b/gui/chronosharegui.cpp
index 038f05a..8d811e8 100644
--- a/gui/chronosharegui.cpp
+++ b/gui/chronosharegui.cpp
@@ -49,6 +49,9 @@
 #ifdef ADHOC_SUPPORTED
   , m_executor (1)
 #endif
+#ifdef SPARKLE_SUPPORTED
+  , m_autoUpdate(new SparkleAutoUpdate(tr("http://no-url.org")))
+#endif
 {
   setWindowTitle("Settings");
 
@@ -167,6 +170,10 @@
 #ifdef ADHOC_SUPPORTED
   delete m_wifiAction;
 #endif
+#ifdef SPARKLE_SUPPORTED
+  delete m_autoUpdate;
+  delete m_checkForUpdates;
+#endif
   delete m_openFolder;
   delete m_viewSettings;
   delete m_changeFolder;
@@ -228,6 +235,11 @@
   connect (m_wifiAction, SIGNAL (toggled(bool)), this, SLOT(onAdHocChange(bool)));
 #endif
 
+#ifdef SPARKLE_SUPPORTED
+  m_checkForUpdates = new QAction (tr("Check For Updates"), this);
+  connect (m_checkForUpdates, SIGNAL(triggered()), this, SLOT(onCheckForUpdates()));
+#endif
+
   // create the "quit program" action
   m_quitProgram = new QAction(tr("&Quit"), this);
   connect(m_quitProgram, SIGNAL(triggered()), qApp, SLOT(quit()));
@@ -299,6 +311,14 @@
 #endif
 }
 
+#ifdef SPARKLE_SUPPORTED
+void
+ChronoShareGui::onCheckForUpdates()
+{
+  m_autoUpdate->checkForUpdates();
+}
+#endif
+
 void ChronoShareGui::setIcon()
 {
   // set the icon image
diff --git a/gui/chronosharegui.h b/gui/chronosharegui.h
index 3e9e0c3..c094f3e 100644
--- a/gui/chronosharegui.h
+++ b/gui/chronosharegui.h
@@ -25,6 +25,11 @@
 
 #include "adhoc.h"
 
+#if __APPLE__ && HAVE_SPARKLE
+#define SPARKLE_SUPPORTED 1
+#include "sparkle-auto-update.h"
+#endif
+
 #include <QtGui>
 #include <QWidget>
 #include <QSystemTrayIcon>
@@ -72,7 +77,9 @@
 
   // click on adhoc button
   void onAdHocChange (bool state); // cannot be protected with #ifdef. otherwise something fishy with QT
-
+#ifdef SPARKLE_SUPPORTED
+  void onCheckForUpdates();
+#endif
 
 private:
   // create actions that result from clicking a menu option
@@ -111,6 +118,7 @@
   QAction* m_viewSettings; // chronoShare settings
   QAction* m_changeFolder; // change the shared folder action
   QAction* m_quitProgram; // quit program action
+  QAction *m_checkForUpdates;
 
   QAction *m_wifiAction;
 
@@ -137,6 +145,9 @@
   Executor m_executor;
 #endif
 
+#ifdef SPARKLE_SUPPORTED
+  AutoUpdate *m_autoUpdate;
+#endif
   // QString m_settingsFilePath; // settings file path
   // QString m_settings;
 };
diff --git a/osx/auto-update/auto-update.h b/osx/auto-update/auto-update.h
new file mode 100644
index 0000000..86068c8
--- /dev/null
+++ b/osx/auto-update/auto-update.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef AUTO_UPDATE_H
+#define AUTO_UPDATE_H
+class AutoUpdate
+{
+public:
+  virtual ~AutoUpdate() {};
+  virtual void checkForUpdates() = 0;
+};
+#endif
diff --git a/osx/auto-update/sparkle-auto-update.h b/osx/auto-update/sparkle-auto-update.h
new file mode 100644
index 0000000..1a19b9c
--- /dev/null
+++ b/osx/auto-update/sparkle-auto-update.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef SPARKLE_AUTO_UPDATE_H
+#define SPARKLE_AUTO_UPDATE_H
+
+#include "auto-update.h"
+#include <QString>
+
+class SparkleAutoUpdate : public AutoUpdate
+{
+public:
+  SparkleAutoUpdate (const QString &url);
+  virtual ~SparkleAutoUpdate ();
+  virtual void checkForUpdates();
+private:
+  class Private;
+  Private *d;
+};
+
+#endif
diff --git a/osx/auto-update/sparkle-auto-update.mm b/osx/auto-update/sparkle-auto-update.mm
new file mode 100644
index 0000000..5b35a76
--- /dev/null
+++ b/osx/auto-update/sparkle-auto-update.mm
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include "sparkle-auto-update.h"
+#import <Foundation/Foundation.h>
+#import <Sparkle/Sparkle.h>
+
+class SparkleAutoUpdate::Private
+{
+  public:
+    SUUpdater *updater;
+};
+
+SparkleAutoUpdate::SparkleAutoUpdate(const QString &updateUrl)
+{
+  d = new Private;
+  d->updater = [SUUpdater sharedUpdater];
+  [d->updater setAutomaticallyChecksForUpdates:YES];
+  NSURL *url = [NSURL URLWithString: [NSString stringWithUTF8String: updateUrl.toUtf8().data()]];
+  [d->updater setFeedURL: url];
+}
+
+SparkleAutoUpdate::~SparkleAutoUpdate()
+{
+  delete d;
+  // presummably SUUpdater handles garbage collection
+}
+
+void SparkleAutoUpdate::checkForUpdates()
+{
+  [d->updater checkForUpdatesInBackground];
+}
diff --git a/wscript b/wscript
index fd62ae7..ff61844 100644
--- a/wscript
+++ b/wscript
@@ -1,5 +1,4 @@
 # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
-
 VERSION='0.1'
 APPNAME='chronoshare'
 
@@ -11,6 +10,9 @@
     opt.add_option('--yes',action='store_true',default=False) # for autoconf/automake/make compatibility
     opt.add_option('--log4cxx', action='store_true',default=False,dest='log4cxx',help='''Compile with log4cxx logging support''')
 
+    if Utils.unversioned_sys_platform () == "darwin":
+        opt.add_option('--auto-update', action='store_true',default=False,dest='autoupdate',help='''(OSX) Download sparkle framework and enable autoupdate feature''')
+
     opt.load('compiler_c compiler_cxx boost ccnx protoc qt4')
 
 def configure(conf):
@@ -23,8 +25,44 @@
     conf.check_cfg(package='libevent_pthreads', args=['--cflags', '--libs'], uselib_store='LIBEVENT_PTHREADS', mandatory=True)
 
     if Utils.unversioned_sys_platform () == "darwin":
-        conf.check_cxx(framework_name='Foundation', uselib_store='OSX_FOUNDATION', cxxflags="-ObjC++", mandatory=False)
-        conf.check_cxx(framework_name='CoreWLAN',   uselib_store='OSX_COREWLAN',   cxxflags="-ObjC++", define_name='HAVE_COREWLAN', mandatory=False)
+        conf.check_cxx(framework_name='Foundation', uselib_store='OSX_FOUNDATION', mandatory=False, compile_filename='test.mm')
+        conf.check_cxx(framework_name='CoreWLAN',   uselib_store='OSX_COREWLAN',   define_name='HAVE_COREWLAN', mandatory=False, compile_filename='test.mm')
+
+        if conf.options.autoupdate:
+            def check_sparkle(**kwargs):
+              conf.check_cxx (framework_name='Sparkle', header_name="Foundation/Foundation.h",
+                              uselib_store='OSX_SPARKLE', define_name='HAVE_SPARKLE', mandatory=True,
+                              compile_filename='test.mm',
+                              **kwargs
+                              )
+            try:
+                # Try standard paths first
+                check_sparkle()
+            except:
+                try:
+                    # Try local path
+                    Logs.info ("Check local version of Sparkle framework")
+                    check_sparkle(cxxflags="-F%s/build/Sparkle" % conf.path.abspath(),
+                              linkflags="-F%s/build/Sparkle" % conf.path.abspath())
+                except:
+                    # Download to local path and retry
+                    Logs.info ("Sparkle framework not found, trying to download it to 'build/'")
+
+                    import urllib, subprocess, os
+                    urllib.urlretrieve ("http://sparkle.andymatuschak.org/files/Sparkle%201.5b6.zip", "build/Sparkle.zip")
+                    if os.path.exists('build/Sparkle.zip'):
+                      try:
+                        subprocess.check_call (['unzip', '-qq', 'build/Sparkle.zip', '-d', 'build/Sparkle'])
+                        os.remove ("build/Sparkle.zip")
+                        check_sparkle(cxxflags="-F%s/build/Sparkle" % conf.path.abspath(),
+                              linkflags="-F%s/build/Sparkle" % conf.path.abspath())
+                      except subprocess.CalledProcessError as e:
+                        conf.fatal("Cannot find Sparkle framework. Auto download failed: '%s' returned %s" % (' '.join(e.cmd), e.returncode))
+                      except:
+                        conf.fatal("Unknown Error happened when auto downloading Sparkle framework")
+
+            if conf.is_defined('HAVE_SPARKLE'):
+                conf.env.HAVE_SPARKLE = 1 # small cheat for wscript
 
     if not conf.check_cfg(package='openssl', args=['--cflags', '--libs'], uselib_store='SSL', mandatory=False):
         libcrypto = conf.check_cc(lib='crypto',
@@ -179,8 +217,11 @@
         qt.mac_app = "ChronoShare.app"
         qt.mac_plist = app_plist % "ChronoShare"
         qt.use += " OSX_FOUNDATION OSX_COREWLAN adhoc"
-        # qt.use += " OSX_FOUNDATION OSX_COREWLAN adhoc"
 
+        if bld.env['HAVE_SPARKLE']:
+            qt.use += " OSX_SPARKLE"
+            qt.source += ["osx/auto-update/sparkle-auto-update.mm"]
+            qt.includes += " osx/auto-update"
 
     cmdline = bld (
         target = "csd",