Some more certificate download improvements.
authorCarl Hetherington <cth@carlh.net>
Wed, 26 Mar 2014 20:53:02 +0000 (20:53 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 26 Mar 2014 20:53:02 +0000 (20:53 +0000)
src/lib/internet.cc [new file with mode: 0644]
src/lib/internet.h [new file with mode: 0644]
src/lib/wscript
src/wx/dolby_certificate_dialog.cc
src/wx/dolby_certificate_dialog.h
src/wx/doremi_certificate_dialog.cc
src/wx/download_certificate_dialog.cc
src/wx/download_certificate_dialog.h

diff --git a/src/lib/internet.cc b/src/lib/internet.cc
new file mode 100644 (file)
index 0000000..16fd672
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+    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 <boost/function.hpp>
+#include <boost/optional.hpp>
+#include <boost/filesystem.hpp>
+#include <curl/curl.h>
+#include <zip.h>
+#include "util.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::stringstream;
+using std::list;
+using boost::optional;
+using boost::function;
+
+static size_t
+get_from_zip_url_data (void* buffer, size_t size, size_t nmemb, void* stream)
+{
+       FILE* f = reinterpret_cast<FILE*> (stream);
+       return fwrite (buffer, size, nmemb, f);
+}
+
+/** @param url URL of ZIP file.
+ *  @param file Filename within ZIP file.
+ *  @param load Function passed a (temporary) filesystem path of the unpacked file.
+ */
+optional<string>
+get_from_zip_url (string url, string file, function<void (boost::filesystem::path)> load)
+{
+       /* Download the ZIP file to temp_zip */
+       CURL* curl = curl_easy_init ();
+       curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
+       
+       ScopedTemporary temp_zip;
+       FILE* f = temp_zip.open ("wb");
+       curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, get_from_zip_url_data);
+       curl_easy_setopt (curl, CURLOPT_WRITEDATA, f);
+
+       CURLcode const cr = curl_easy_perform (curl);
+
+       temp_zip.close ();
+       curl_easy_cleanup (curl);
+       if (cr != CURLE_OK) {
+               return String::compose (_("Download failed (%1/%2 error %3)"), url, file, cr);
+       }
+
+       /* Open the ZIP file and read `file' out of it */
+       
+       struct zip* zip = zip_open (temp_zip.c_str(), 0, 0);
+       if (!zip) {
+               return optional<string> (_("Could not open downloaded ZIP file"));
+       }
+       
+       struct zip_file* zip_file = zip_fopen (zip, file.c_str(), 0);
+       if (!zip_file) {
+               return optional<string> (_("Unexpected ZIP file contents"));
+       }
+       
+       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 ();
+       
+       load (temp_cert.file ());
+       return optional<string> ();
+}
+
+
+static size_t
+ftp_ls_data (void* buffer, size_t size, size_t nmemb, void* data)
+{
+       string* s = reinterpret_cast<string *> (data);
+       uint8_t* b = reinterpret_cast<uint8_t *> (buffer);
+       for (size_t i = 0; i < (size * nmemb); ++i) {
+               *s += b[i];
+       }
+       return nmemb;
+}
+
+list<string>
+ftp_ls (string url)
+{
+       CURL* curl = curl_easy_init ();
+       if (!curl) {
+               return list<string> ();
+       }
+
+       if (url.substr (url.length() - 1, 1) != "/") {
+               url += "/";
+       }
+       curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
+
+       string ls_raw;
+       struct curl_slist* commands = 0;
+       commands = curl_slist_append (commands, "NLST");
+       curl_easy_setopt (curl, CURLOPT_POSTQUOTE, commands);
+       curl_easy_setopt (curl, CURLOPT_WRITEDATA, &ls_raw);
+       curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ftp_ls_data);
+       curl_easy_setopt (curl, CURLOPT_FTP_USE_EPSV, 0);
+       CURLcode const r = curl_easy_perform (curl);
+       if (r != CURLE_OK) {
+               return list<string> ();
+       }
+
+       stringstream s (ls_raw);
+       string line;
+       list<string> ls;
+       while (s.good ()) {
+               getline (s, line);
+               if (line.length() > 55) {
+                       string const file = line.substr (55);
+                       if (file != "." && file != "..") {
+                               ls.push_back (file);
+                       }
+               }
+       }
+
+       curl_easy_cleanup (curl);
+
+       return ls;
+}
diff --git a/src/lib/internet.h b/src/lib/internet.h
new file mode 100644 (file)
index 0000000..d7427fe
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+    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 <boost/optional.hpp>
+#include <boost/function.hpp>
+#include <boost/filesystem.hpp>
+
+boost::optional<std::string> get_from_zip_url (std::string url, std::string file, boost::function<void (boost::filesystem::path)> load);
+std::list<std::string> ftp_ls (std::string dir);
index 7c9712ff8f74c3f7f4b83144ac0bb69a3dd23839..a50216f6d500755cf338c00fd8308ca075215633 100644 (file)
@@ -30,6 +30,7 @@ sources = """
           ffmpeg_examiner.cc
           film.cc
           filter.cc
+          internet.cc
           image.cc
           image_content.cc
           image_decoder.cc
index 5d9dc5a31df56d67cfdc9ba0f9f7626bcc617ec8..9960c16c917a860d88ec49565055393fdd6fc485 100644 (file)
 
 */
 
+#include <boost/algorithm/string.hpp>
 #include <curl/curl.h>
 #include "lib/compose.hpp"
+#include "lib/internet.h"
 #include "dolby_certificate_dialog.h"
 #include "wx_util.h"
 
 using std::list;
 using std::string;
+using std::vector;
 using std::stringstream;
 using std::cout;
+using boost::optional;
+using boost::algorithm::split;
+using boost::algorithm::is_any_of;
 
 DolbyCertificateDialog::DolbyCertificateDialog (wxWindow* parent, boost::function<void (boost::filesystem::path)> load)
        : DownloadCertificateDialog (parent, load)
@@ -38,71 +44,25 @@ DolbyCertificateDialog::DolbyCertificateDialog (wxWindow* parent, boost::functio
        _cinema = add (new wxChoice (this, wxID_ANY));
        _cinema->Append (N_("Motion Picture Solutions London Mobile & QC"));
 
+       add (_("Serial number"), true);
+       _serial = add (new wxChoice (this, wxID_ANY));
+
        add_common_widgets ();
 
        _country->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DolbyCertificateDialog::country_selected, this));
        _cinema->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DolbyCertificateDialog::cinema_selected, this));
+       _serial->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DolbyCertificateDialog::serial_selected, this));
        Bind (wxEVT_IDLE, boost::bind (&DolbyCertificateDialog::setup_countries, this));
 
        _country->Clear ();
        _cinema->Clear ();
 }
 
-static size_t
-ftp_data_ls (void* buffer, size_t size, size_t nmemb, void* data)
-{
-       string* s = reinterpret_cast<string *> (data);
-       uint8_t* b = reinterpret_cast<uint8_t *> (buffer);
-       for (size_t i = 0; i < (size * nmemb); ++i) {
-               *s += b[i];
-       }
-       return nmemb;
-}
-
 list<string>
-DolbyCertificateDialog::ftp_ls (string dir) const
+DolbyCertificateDialog::get_dir (string dir) const
 {
-       CURL* curl = curl_easy_init ();
-       if (!curl) {
-               _message->SetLabel (N_("Could not set up libcurl"));
-               return list<string> ();
-       }
-
        string url = String::compose ("ftp://dolbyrootcertificates:houro61l@ftp.dolby.co.uk/SHA256/%1", dir);
-       if (url.substr (url.length() - 1, 1) != "/") {
-               url += "/";
-       }
-       curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
-
-       string ls_raw;
-       struct curl_slist* commands = 0;
-       commands = curl_slist_append (commands, "NLST");
-       curl_easy_setopt (curl, CURLOPT_POSTQUOTE, commands);
-       curl_easy_setopt (curl, CURLOPT_WRITEDATA, &ls_raw);
-       curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ftp_data_ls);
-       curl_easy_setopt (curl, CURLOPT_FTP_USE_EPSV, 0);
-       CURLcode const r = curl_easy_perform (curl);
-       if (r != CURLE_OK) {
-               _message->SetLabel (_("Problem occurred when contacting Dolby."));
-               return list<string> ();
-       }
-
-       stringstream s (ls_raw);
-       string line;
-       list<string> ls;
-       while (s.good ()) {
-               getline (s, line);
-               if (line.length() > 55) {
-                       string const file = line.substr (55);
-                       if (file != "." && file != "..") {
-                               ls.push_back (file);
-                       }
-               }
-       }
-
-       curl_easy_cleanup (curl);
-
-       return ls;
+       return ftp_ls (url);
 }
 
 void
@@ -117,7 +77,7 @@ DolbyCertificateDialog::setup_countries ()
        _country->SetSelection (0);
        run_gui_loop ();
        
-       list<string> const countries = ftp_ls ("");
+       list<string> const countries = get_dir ("");
        _country->Clear ();
        for (list<string>::const_iterator i = countries.begin(); i != countries.end(); ++i) {
                _country->Append (std_to_wx (*i));
@@ -132,7 +92,7 @@ DolbyCertificateDialog::country_selected ()
        _cinema->SetSelection (0);
        run_gui_loop ();
        
-       list<string> const cinemas = ftp_ls (wx_to_std (_country->GetStringSelection()));
+       list<string> const cinemas = get_dir (wx_to_std (_country->GetStringSelection()));
        _cinema->Clear ();
        for (list<string>::const_iterator i = cinemas.begin(); i != cinemas.end(); ++i) {
                _cinema->Append (std_to_wx (*i));
@@ -141,6 +101,27 @@ DolbyCertificateDialog::country_selected ()
 
 void
 DolbyCertificateDialog::cinema_selected ()
+{
+       _serial->Clear ();
+       _serial->Append (_("Fetching..."));
+       _serial->SetSelection (0);
+       run_gui_loop ();
+
+       string const dir = String::compose ("%1/%2", wx_to_std (_country->GetStringSelection()), wx_to_std (_cinema->GetStringSelection()));
+       list<string> const zips = get_dir (dir);
+
+       _serial->Clear ();
+       for (list<string>::const_iterator i = zips.begin(); i != zips.end(); ++i) {
+               vector<string> a;
+               split (a, *i, is_any_of ("-_"));
+               if (a.size() >= 4) {
+                       _serial->Append (std_to_wx (a[3]), new wxStringClientData (std_to_wx (*i)));
+               }
+       }
+}
+
+void
+DolbyCertificateDialog::serial_selected ()
 {
        _download->Enable (true);
 }
@@ -148,5 +129,31 @@ DolbyCertificateDialog::cinema_selected ()
 void
 DolbyCertificateDialog::download ()
 {
+       _message->SetLabel (_("Downloading certificate"));
+       run_gui_loop ();
+
+       string const zip = string_client_data (_serial->GetClientObject (_serial->GetSelection ()));
+
+       string const file = String::compose (
+               "%1/%2/%3",
+               wx_to_std (_country->GetStringSelection()),
+               wx_to_std (_cinema->GetStringSelection()),
+               zip
+               );
+
+       /* Work out the certificate file name inside the zip */
+       vector<string> b;
+       split (b, zip, is_any_of ("_"));
+       if (b.size() < 2) {
+               _message->SetLabel (_("Unexpected certificate filename form"));
+               return;
+       }
+       string const cert = b[0] + "_" + b[1] + ".pem.crt";
+       
+       optional<string> error = get_from_zip_url (file, cert, _load);
+       if (error) {
+               error_dialog (this, std_to_wx (error.get ()));
+       }
 
+       _message->SetLabel (wxT (""));
 }
index 92b3407e9a7858e6e80143f78291e03614a62a4a..194150363cb11e8504c464ab8a6f187678f1dee4 100644 (file)
@@ -30,8 +30,10 @@ private:
        void setup_countries ();
        void country_selected ();
        void cinema_selected ();
-       std::list<std::string> ftp_ls (std::string) const;
+       void serial_selected ();
+       std::list<std::string> get_dir (std::string) const;
 
        wxChoice* _country;
        wxChoice* _cinema;
+       wxChoice* _serial;
 };
index 4b37b1ae6c4d3d560380746813e27a53c1c338b5..8509c97d150d502bff9c43be50dc18894be9ec2c 100644 (file)
 #include <zip.h>
 #include "lib/compose.hpp"
 #include "lib/util.h"
+#include "lib/internet.h"
 #include "doremi_certificate_dialog.h"
 #include "wx_util.h"
 
 using std::string;
 using boost::function;
+using boost::optional;
 
 DoremiCertificateDialog::DoremiCertificateDialog (wxWindow* parent, function<void (boost::filesystem::path)> load)
        : DownloadCertificateDialog (parent, load)
@@ -36,86 +38,29 @@ DoremiCertificateDialog::DoremiCertificateDialog (wxWindow* parent, function<voi
        add_common_widgets ();
 }
 
-
-
-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
 DoremiCertificateDialog::download ()
 {
        string const serial = wx_to_std (_serial->GetValue ());
        if (serial.length() != 6) {
-               _message->SetLabel (_("Doremi serial numbers must have 6 digits"));
-               return;
-       }
-       
-       CURL* curl = curl_easy_init ();
-       if (!curl) {
-               _message->SetLabel (N_("Could not set up libcurl"));
+               error_dialog (this, _("Doremi serial numbers must have 6 digits"));
                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);
 
-       _message->SetLabel (_("Downloading certificate from Doremi"));
-       run_gui_loop ();
+       _message->SetLabel (_("Downloading certificate"));
 
-       CURLcode const cr = curl_easy_perform (curl);
+       optional<string> error = get_from_zip_url (
+               String::compose (
+                       "ftp://service:t3chn1c1an@ftp.doremilabs.com/Certificates/%1xxx/dcp2000-%2.dcicerts.zip",
+                       serial.substr(0, 3), serial
+                       ),
+               String::compose ("dcp2000-%1.cert.sha256.pem", serial),
+               _load
+               );
 
-       _gauge->SetValue (50);
-       run_gui_loop ();
-       
-       temp_zip.close ();
-       curl_easy_cleanup (curl);
-       if (cr != CURLE_OK) {
-               _message->SetLabel (wxString::Format (_("Certificate download failed (%d)"), cr));
-               return;
-       }
-       
-       _message->SetLabel (_("Unpacking"));
-       run_gui_loop ();
-       
-       struct zip* zip = zip_open (temp_zip.c_str(), 0, 0);
-       if (!zip) {
-               _message->SetLabel (N_("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) {
-               _message->SetLabel (N_("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;
-               }
+       if (error) {
+               error_dialog (this, std_to_wx (error.get ()));
+       } else {
+               _message->SetLabel (wxT (""));
        }
-       temp_cert.close ();
-       
-       _gauge->SetValue (100);
-       _message->SetLabel (_("OK"));
-       _load (temp_cert.file ());
 }
index bd3841f193205491c995382d6bbff35fdad7bb54..abb1e61260aa7bf8ace8451317c8dcc6e822057d 100644 (file)
@@ -36,11 +36,13 @@ DownloadCertificateDialog::add_common_widgets ()
        add_spacer ();
        _download = add (new wxButton (this, wxID_ANY, _("Download")));
 
-       add_spacer ();
-       _gauge = add (new wxGauge (this, wxID_ANY, 100));
-
        add_spacer ();
        _message = add (new wxStaticText (this, wxID_ANY, wxT ("")));
+
+       wxFont font = _message->GetFont();
+       font.SetStyle (wxFONTSTYLE_ITALIC);
+       font.SetPointSize (font.GetPointSize() - 1);
+       _message->SetFont (font);
        
        _download->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DownloadCertificateDialog::download, this));
        _download->Enable (false);
index d881749227b8ac58a6cb6397e4e43bcbd4eb9a72..9fac23d58436e3aa0b6dd39bb09ced74bd41c41b 100644 (file)
@@ -35,7 +35,6 @@ protected:
        
        boost::function<void (boost::filesystem::path)> _load;
        wxSizer* _overall_sizer;
-       wxGauge* _gauge;
        wxStaticText* _message;
        wxButton* _download;