Add support for downloading Doremi server certificates.
authorCarl Hetherington <cth@carlh.net>
Tue, 25 Mar 2014 15:24:13 +0000 (15:24 +0000)
committerCarl Hetherington <cth@carlh.net>
Tue, 25 Mar 2014 15:24:13 +0000 (15:24 +0000)
ChangeLog
src/lib/util.cc
src/lib/util.h
src/wx/progress.cc [new file with mode: 0644]
src/wx/progress.h [new file with mode: 0644]
src/wx/screen_dialog.cc
src/wx/screen_dialog.h
src/wx/wscript

index c6fffff65e39ce91e246692e4c32bf62eea3de91..f480d59488986a9b0d7de9533254e357320254d6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2014-03-25  Carl Hetherington  <cth@carlh.net>
+
+       * Add support for downloading Doremi server certificates.
+
 2014-03-24  Carl Hetherington  <cth@carlh.net>
 
        * Version 1.66.7 released.
index 85c52b039f10fb91cf614ddd1e1014099ba24833..7a19790eb4ec261d1131ebd5e015565324f562a6 100644 (file)
@@ -1030,3 +1030,38 @@ divide_with_round (int64_t a, int64_t b)
                return a / b;
        }
 }
+
+ScopedTemporary::ScopedTemporary ()
+       : _open (0)
+{
+       _file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path ();
+}
+
+ScopedTemporary::~ScopedTemporary ()
+{
+       close ();       
+       boost::system::error_code ec;
+       boost::filesystem::remove (_file, ec);
+}
+
+char const *
+ScopedTemporary::c_str () const
+{
+       return _file.string().c_str ();
+}
+
+FILE*
+ScopedTemporary::open (char const * params)
+{
+       _open = fopen (c_str(), params);
+       return _open;
+}
+
+void
+ScopedTemporary::close ()
+{
+       if (_open) {
+               fclose (_open);
+               _open = 0;
+       }
+}
index c056808596438567d708c8335889ad2fe521c927..0bbab83058644a1e833f60834672a52db90d933f 100644 (file)
@@ -176,6 +176,24 @@ private:
        char* _old;
 };
 
+class ScopedTemporary
+{
+public:
+       ScopedTemporary ();
+       ~ScopedTemporary ();
+
+       boost::filesystem::path file () const {
+               return _file;
+       }
+       
+       char const * c_str () const;
+       FILE* open (char const *);
+       void close ();
+
+private:
+       boost::filesystem::path _file;
+       FILE* _open;
+};
 
 #endif
 
diff --git a/src/wx/progress.cc b/src/wx/progress.cc
new file mode 100644 (file)
index 0000000..9e3da6c
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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 2 of the License, or
+    (at your option) any later version.
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+#include "progress.h"
+#include "wx_util.h"
+
+using std::string;
+
+Progress::Progress (wxWindow* parent)
+       : wxPanel (parent, wxID_ANY)
+{
+       wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
+
+       _gauge = new wxGauge (this, wxID_ANY, 100);
+       s->Add (_gauge, 1, wxEXPAND);
+       _label = new wxStaticText (this, wxID_ANY, wxT (""));
+       s->Add (_label, 1, wxEXPAND);
+
+       SetSizerAndFit (s);
+}
+
+void
+Progress::set_value (int v)
+{
+       _gauge->SetValue (v);
+       run_gui_loop ();
+}
+
+void
+Progress::set_message (wxString s)
+{
+       _label->SetLabel (s);
+       run_gui_loop ();
+}
+
+void
+Progress::run_gui_loop ()
+{
+       while (wxTheApp->Pending ()) {
+               wxTheApp->Dispatch ();
+       }
+}
+
diff --git a/src/wx/progress.h b/src/wx/progress.h
new file mode 100644 (file)
index 0000000..9ce3243
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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 2 of the License, or
+    (at your option) any later version.
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/wx.h>
+
+class Progress : public wxPanel
+{
+public:
+       Progress (wxWindow* parent);
+       
+       /** Set progress value.
+        *  @param v Progress from 0 to 100.
+        */
+       void set_value (int v);
+       void set_message (wxString);
+
+private:
+       void run_gui_loop ();
+       
+       wxGauge* _gauge;
+       wxStaticText* _label;
+};
index 32a0bce43f12ac0345920a2aa28bda00137f5621..b077a9bf93b4b29115084a63d9f3303b71800145 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 #include <wx/filepicker.h>
 #include <wx/validate.h>
+#include <curl/curl.h>
+#include <zip.h>
 #include <libdcp/exceptions.h>
 #include "lib/compose.hpp"
+#include "lib/util.h"
 #include "screen_dialog.h"
 #include "wx_util.h"
+#include "progress.h"
 
 using std::string;
 using std::cout;
@@ -39,9 +43,25 @@ ScreenDialog::ScreenDialog (wxWindow* parent, string title, string name, shared_
        _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (320, -1));
        table->Add (_name, 1, wxEXPAND);
 
+       add_label_to_sizer (table, this, "Server manufacturer", true);
+       _manufacturer = new wxChoice (this, wxID_ANY);
+       table->Add (_manufacturer, 1, wxEXPAND);
+
+       add_label_to_sizer (table, this, "Server serial number", true);
+       _serial = new wxTextCtrl (this, wxID_ANY);
+       table->Add (_serial, 1, wxEXPAND);
+       
        add_label_to_sizer (table, this, "Certificate", true);
-       _certificate_load = new wxButton (this, wxID_ANY, wxT ("Load from file..."));
-       table->Add (_certificate_load, 1, wxEXPAND);
+       wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+       _load_certificate = new wxButton (this, wxID_ANY, _("Load from file..."));
+       _download_certificate = new wxButton (this, wxID_ANY, _("Download"));
+       s->Add (_load_certificate, 1, wxEXPAND);
+       s->Add (_download_certificate, 1, wxEXPAND);
+       table->Add (s, 1, wxEXPAND);
+
+       table->AddSpacer (0);
+       _progress = new Progress (this);
+       table->Add (_progress, 1, wxEXPAND);
 
        table->AddSpacer (0);
        _certificate_text = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxSize (320, 256), wxTE_MULTILINE | wxTE_READONLY);
@@ -65,7 +85,15 @@ ScreenDialog::ScreenDialog (wxWindow* parent, string title, string name, shared_
        overall_sizer->Layout ();
        overall_sizer->SetSizeHints (this);
 
-       _certificate_load->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ScreenDialog::load_certificate, this));
+       _manufacturer->Append (_("Unknown"));
+       _manufacturer->Append (_("Doremi"));
+       _manufacturer->Append (_("Other"));
+       _manufacturer->SetSelection (0);
+
+       _load_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ScreenDialog::select_certificate, this));
+       _download_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ScreenDialog::download_certificate, this));
+       _manufacturer->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&ScreenDialog::setup_sensitivity, this));
+       _serial->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ScreenDialog::setup_sensitivity, this));
 
        setup_sensitivity ();
 }
@@ -83,17 +111,23 @@ ScreenDialog::certificate () const
 }
 
 void
-ScreenDialog::load_certificate ()
+ScreenDialog::load_certificate (boost::filesystem::path file)
+{
+       try {
+               _certificate.reset (new libdcp::Certificate (file));
+               _certificate_text->SetValue (_certificate->certificate ());
+       } catch (libdcp::MiscError& e) {
+               error_dialog (this, String::compose ("Could not read certificate file (%1)", e.what()));
+       }
+}
+
+void
+ScreenDialog::select_certificate ()
 {
        wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
 
        if (d->ShowModal () == wxID_OK) {
-               try {
-                       _certificate.reset (new libdcp::Certificate (boost::filesystem::path (wx_to_std (d->GetPath ()))));
-                       _certificate_text->SetValue (_certificate->certificate ());
-               } catch (libdcp::MiscError& e) {
-                       error_dialog (this, String::compose ("Could not read certificate file (%1)", e.what()));
-               }
+               load_certificate (boost::filesystem::path (wx_to_std (d->GetPath ())));
        }
        
        d->Destroy ();
@@ -101,9 +135,87 @@ ScreenDialog::load_certificate ()
        setup_sensitivity ();
 }
 
+static size_t
+ftp_data (void* buffer, size_t size, size_t nmemb, void* stream)
+{
+       FILE* f = reinterpret_cast<FILE*> (stream);
+       return fwrite (buffer, size, nmemb, f);
+}
+
+void
+ScreenDialog::download_certificate ()
+{
+       if (_manufacturer->GetStringSelection() == _("Doremi")) {
+               string const serial = wx_to_std (_serial->GetValue ());
+               if (serial.length() != 6) {
+                       error_dialog (this, _("Doremi serial numbers must have 6 numbers"));
+                       return;
+               }
+
+               CURL* curl = curl_easy_init ();
+               if (!curl) {
+                       error_dialog (this, N_("Could not set up libcurl"));
+                       return;
+               }
+
+               string const url = String::compose (
+                       "ftp://service:t3chn1c1an@ftp.doremilabs.com/Certificates/%1xxx/dcp2000-%2.dcicerts.zip",
+                       serial.substr(0, 3), serial
+                       );
+
+               curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
+
+               ScopedTemporary temp_zip;
+               FILE* f = temp_zip.open ("wb");
+               curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ftp_data);
+               curl_easy_setopt (curl, CURLOPT_WRITEDATA, f);
+               _progress->set_message (_("Downloading certificate from Doremi"));
+               CURLcode const cr = curl_easy_perform (curl);
+               _progress->set_value (50);
+               temp_zip.close ();
+               curl_easy_cleanup (curl);
+               if (cr != CURLE_OK) {
+                       _progress->set_message (wxString::Format (_("Certificate download failed (%d)"), cr));
+                       return;
+               }
+
+               _progress->set_message (_("Unpacking"));
+               struct zip* zip = zip_open (temp_zip.c_str(), 0, 0);
+               if (!zip) {
+                       _progress->set_message ("Could not open certificate ZIP file");
+                       return;
+               }
+
+               string const name_in_zip = String::compose ("dcp2000-%1.cert.sha256.pem", serial);
+               struct zip_file* zip_file = zip_fopen (zip, name_in_zip.c_str(), 0);
+               if (!zip_file) {
+                       _progress->set_message ("Could not find certificate in ZIP file");
+                       return;
+               }
+
+               ScopedTemporary temp_cert;
+               f = temp_cert.open ("wb");
+               char buffer[4096];
+               while (1) {
+                       int const N = zip_fread (zip_file, buffer, sizeof (buffer));
+                       fwrite (buffer, 1, N, f);
+                       if (N < int (sizeof (buffer))) {
+                               break;
+                       }
+               }
+               temp_cert.close ();
+
+               _progress->set_value (100);
+               _progress->set_message (_("OK"));
+               load_certificate (temp_cert.file ());
+       }
+}
+
 void
 ScreenDialog::setup_sensitivity ()
 {
        wxButton* ok = dynamic_cast<wxButton*> (FindWindowById (wxID_OK, this));
        ok->Enable (_certificate);
+
+       _download_certificate->Enable (_manufacturer->GetStringSelection() == _("Doremi") && !_serial->GetValue().IsEmpty ());
 }
index 271ae2055a884123bb916004f7602f41c444319f..1fcc8d5641085a4e2f2b99e31848e3afd294c3db 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
 #include <boost/shared_ptr.hpp>
 #include <libdcp/certificates.h>
 
+class Progress;
+
 class ScreenDialog : public wxDialog
 {
 public:
@@ -30,11 +32,17 @@ public:
        boost::shared_ptr<libdcp::Certificate> certificate () const;
        
 private:
-       void load_certificate ();
+       void select_certificate ();
+       void load_certificate (boost::filesystem::path);
+       void download_certificate ();
        void setup_sensitivity ();
        
        wxTextCtrl* _name;
-       wxButton* _certificate_load;
+       wxChoice* _manufacturer;
+       wxTextCtrl* _serial;
+       Progress* _progress;
+       wxButton* _load_certificate;
+       wxButton* _download_certificate;
        wxTextCtrl* _certificate_text;
 
        boost::shared_ptr<libdcp::Certificate> _certificate;
index 1ffaa6097fd276cc3e086c8e3171dc5249a2a041..56476c4ce9a5128b690f737e26991beb81e63258 100644 (file)
@@ -29,6 +29,7 @@ sources = """
           kdm_dialog.cc
           new_film_dialog.cc
           preset_colour_conversion_dialog.cc
+          progress.cc
           properties_dialog.cc
           repeat_dialog.cc
           screen_dialog.cc