Use SafeStringStream instead of std::stringstream to try to fix random crashes on...
[dcpomatic.git] / src / lib / update.cc
index 6e9d1169acbfc968c9f28bb1f0196ed7f8291d00..7bec061e9292e15d044c360e8c13d60dc6a0faab 100644 (file)
 */
 
 #include <string>
-#include <sstream>
+#include <boost/algorithm/string.hpp>
 #include <curl/curl.h>
 #include <libcxml/cxml.h>
+#include <libdcp/raw_convert.h>
 #include "update.h"
 #include "version.h"
+#include "ui_signaller.h"
+#include "safe_stringstream.h"
 
 #define BUFFER_SIZE 1024
 
 using std::cout;
 using std::min;
 using std::string;
-using std::stringstream;
+using libdcp::raw_convert;
+
+UpdateChecker* UpdateChecker::_instance = 0;
+
+static size_t
+write_callback_wrapper (void* data, size_t size, size_t nmemb, void* user)
+{
+       return reinterpret_cast<UpdateChecker*>(user)->write_callback (data, size, nmemb);
+}
 
 UpdateChecker::UpdateChecker ()
        : _buffer (new char[BUFFER_SIZE])
        , _offset (0)
+       , _curl (0)
+       , _state (NOT_RUN)
+       , _emits (0)
+       , _to_do (0)
 {
+       curl_global_init (CURL_GLOBAL_ALL);
+       _curl = curl_easy_init ();
+
+       curl_easy_setopt (_curl, CURLOPT_URL, "http://dcpomatic.com/update");
+       curl_easy_setopt (_curl, CURLOPT_WRITEFUNCTION, write_callback_wrapper);
+       curl_easy_setopt (_curl, CURLOPT_WRITEDATA, this);
+       curl_easy_setopt (_curl, CURLOPT_TIMEOUT, 20);
        
+       string const agent = "dcpomatic/" + string (dcpomatic_version);
+       curl_easy_setopt (_curl, CURLOPT_USERAGENT, agent.c_str ());
+
+       _thread = new boost::thread (boost::bind (&UpdateChecker::thread, this));
 }
 
 UpdateChecker::~UpdateChecker ()
 {
+       /* We are not cleaning up our thread, but hey well */
+       
+       curl_easy_cleanup (_curl);
+       curl_global_cleanup ();
        delete[] _buffer;
 }
 
-static size_t
-write_callback_wrapper (void* data, size_t size, size_t nmemb, void* user)
+void
+UpdateChecker::run ()
 {
-       return reinterpret_cast<UpdateChecker*>(user)->write_callback (data, size, nmemb);
+       boost::mutex::scoped_lock lm (_process_mutex);
+       _to_do++;
+       _condition.notify_one ();
 }
 
-UpdateChecker::Result
-UpdateChecker::run ()
+void
+UpdateChecker::thread ()
 {
-       curl_global_init (CURL_GLOBAL_ALL);
-       CURL* curl = curl_easy_init ();
-       if (!curl) {
-               return MAYBE;
+       while (true) {
+               boost::mutex::scoped_lock lock (_process_mutex);
+               while (_to_do == 0) {
+                       _condition.wait (lock);
+               }
+               --_to_do;
+               lock.unlock ();
+               
+               try {
+                       _offset = 0;
+                       
+                       int r = curl_easy_perform (_curl);
+                       if (r != CURLE_OK) {
+                               set_state (FAILED);
+                               return;
+                       }
+                       
+                       _buffer[_offset] = '\0';
+                       string s (_buffer);
+                       cxml::Document doc ("Update");
+                       doc.read_string (s);
+                       
+                       {
+                               boost::mutex::scoped_lock lm (_data_mutex);
+                               _stable = doc.string_child ("Stable");
+                               _test = doc.string_child ("Test");
+                       }
+                       
+                       string current = string (dcpomatic_version);
+                       bool current_pre = false;
+                       if (boost::algorithm::ends_with (current, "pre")) {
+                               current = current.substr (0, current.length() - 3);
+                               current_pre = true;
+                       }
+                       
+                       float current_float = raw_convert<float> (current);
+                       if (current_pre) {
+                               current_float -= 0.005;
+                       }
+                       
+                       if (current_float < raw_convert<float> (_stable)) {
+                               set_state (YES);
+                       } else {
+                               set_state (NO);
+                       }
+               } catch (...) {
+                       set_state (FAILED);
+               }
        }
-
-       curl_easy_setopt (curl, CURLOPT_URL, "http://dcpomatic.com/update.php");
-       curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_callback_wrapper);
-       curl_easy_setopt (curl, CURLOPT_WRITEDATA, this);
-       string const agent = "dcpomatic/" + string (dcpomatic_version);
-       curl_easy_setopt (curl, CURLOPT_USERAGENT, agent.c_str ());
-       int r = curl_easy_perform (curl);
-       if (r != CURLE_OK) {
-               return MAYBE;
-       }
-
-       _buffer[BUFFER_SIZE-1] = '\0';
-       stringstream s;
-       s << _buffer;
-       cxml::Document doc ("Update");
-       doc.read_stream (s);
-
-       cout << doc.string_child ("Stable") << "\n";
-       return YES;
 }
-
+       
 size_t
 UpdateChecker::write_callback (void* data, size_t size, size_t nmemb)
 {
-       size_t const t = min (size * nmemb, size_t (BUFFER_SIZE - _offset));
+       size_t const t = min (size * nmemb, size_t (BUFFER_SIZE - _offset - 1));
        memcpy (_buffer + _offset, data, t);
        _offset += t;
        return t;
 }
+
+void
+UpdateChecker::set_state (State s)
+{
+       {
+               boost::mutex::scoped_lock lm (_data_mutex);
+               _state = s;
+               _emits++;
+       }
+
+       ui_signaller->emit (boost::bind (boost::ref (StateChanged)));
+}
+
+UpdateChecker *
+UpdateChecker::instance ()
+{
+       if (!_instance) {
+               _instance = new UpdateChecker ();
+       }
+
+       return _instance;
+}
+
+