Add KDM decryption keys prefs to player.
authorCarl Hetherington <cth@carlh.net>
Sun, 20 Aug 2017 22:43:12 +0000 (23:43 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 20 Aug 2017 22:43:12 +0000 (23:43 +0100)
src/wx/config_dialog.cc [new file with mode: 0644]
src/wx/config_dialog.h
src/wx/full_config_dialog.cc
src/wx/make_chain_dialog.h
src/wx/player_config_dialog.cc
src/wx/wscript

diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc
new file mode 100644 (file)
index 0000000..30585f4
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "config_dialog.h"
+#include "nag_dialog.h"
+
+using boost::bind;
+
+static
+void
+do_nothing ()
+{
+
+}
+
+Page::Page (wxSize panel_size, int border)
+       : _border (border)
+       , _panel (0)
+       , _panel_size (panel_size)
+       , _window_exists (false)
+{
+       _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
+}
+
+wxWindow*
+Page::create_window (wxWindow* parent)
+{
+       _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
+       wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
+       _panel->SetSizer (s);
+
+       setup ();
+       _window_exists = true;
+       config_changed ();
+
+       _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
+
+       return _panel;
+}
+
+void
+Page::config_changed_wrapper ()
+{
+       if (_window_exists) {
+               config_changed ();
+       }
+}
+
+void
+Page::window_destroyed ()
+{
+       _window_exists = false;
+}
+
+
+StockPage::StockPage (Kind kind, wxSize panel_size, int border)
+       : wxStockPreferencesPage (kind)
+       , Page (panel_size, border)
+{
+
+}
+
+wxWindow*
+StockPage::CreateWindow (wxWindow* parent)
+{
+       return create_window (parent);
+}
+
+StandardPage::StandardPage (wxSize panel_size, int border)
+       : Page (panel_size, border)
+{
+
+}
+
+wxWindow*
+StandardPage::CreateWindow (wxWindow* parent)
+{
+       return create_window (parent);
+}
+
+GeneralPage::GeneralPage (wxSize panel_size, int border)
+       : StockPage (Kind_General, panel_size, border)
+{
+
+}
+
+void
+GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
+{
+       _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
+       table->Add (_set_language, wxGBPosition (r, 0));
+       _language = new wxChoice (_panel, wxID_ANY);
+       std::vector<std::pair<std::string, std::string> > languages;
+       languages.push_back (std::make_pair ("Čeština", "cs_CZ"));
+       languages.push_back (std::make_pair ("汉语/漢語", "zh_CN"));
+       languages.push_back (std::make_pair ("Dansk", "da_DK"));
+       languages.push_back (std::make_pair ("Deutsch", "de_DE"));
+       languages.push_back (std::make_pair ("English", "en_GB"));
+       languages.push_back (std::make_pair ("Español", "es_ES"));
+       languages.push_back (std::make_pair ("Français", "fr_FR"));
+       languages.push_back (std::make_pair ("Italiano", "it_IT"));
+       languages.push_back (std::make_pair ("Nederlands", "nl_NL"));
+       languages.push_back (std::make_pair ("Русский", "ru_RU"));
+       languages.push_back (std::make_pair ("Polski", "pl_PL"));
+       languages.push_back (std::make_pair ("Português europeu", "pt_PT"));
+       languages.push_back (std::make_pair ("Português do Brasil", "pt_BR"));
+       languages.push_back (std::make_pair ("Svenska", "sv_SE"));
+       languages.push_back (std::make_pair ("Slovenský jazyk", "sk_SK"));
+       languages.push_back (std::make_pair ("українська мова", "uk_UA"));
+       checked_set (_language, languages);
+       table->Add (_language, wxGBPosition (r, 1));
+       ++r;
+
+       wxStaticText* restart = add_label_to_sizer (
+               table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
+               );
+       wxFont font = restart->GetFont();
+       font.SetStyle (wxFONTSTYLE_ITALIC);
+       font.SetPointSize (font.GetPointSize() - 1);
+       restart->SetFont (font);
+       ++r;
+
+       _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
+       _language->Bind     (wxEVT_CHOICE,   boost::bind (&GeneralPage::language_changed,     this));
+}
+
+void
+GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
+{
+       _sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound via"));
+       table->Add (_sound, wxGBPosition (r, 0));
+       _sound_output = new wxChoice (_panel, wxID_ANY);
+       table->Add (_sound_output, wxGBPosition (r, 1));
+       ++r;
+
+       RtAudio audio (DCPOMATIC_RTAUDIO_API);
+       for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
+               RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
+               if (dev.probed && dev.outputChannels > 0) {
+                       _sound_output->Append (std_to_wx (dev.name));
+               }
+       }
+
+       _sound->Bind        (wxEVT_CHECKBOX, boost::bind (&GeneralPage::sound_changed, this));
+       _sound_output->Bind (wxEVT_CHOICE,   boost::bind (&GeneralPage::sound_output_changed, this));
+}
+
+void
+GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
+{
+       _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
+       table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
+       ++r;
+
+       _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
+       table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
+       ++r;
+
+       _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
+       _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
+}
+
+void
+GeneralPage::config_changed ()
+{
+       Config* config = Config::instance ();
+
+       checked_set (_set_language, static_cast<bool>(config->language()));
+
+       /* Backwards compatibility of config file */
+
+       std::map<std::string, std::string> compat_map;
+       compat_map["fr"] = "fr_FR";
+       compat_map["it"] = "it_IT";
+       compat_map["es"] = "es_ES";
+       compat_map["sv"] = "sv_SE";
+       compat_map["de"] = "de_DE";
+       compat_map["nl"] = "nl_NL";
+       compat_map["ru"] = "ru_RU";
+       compat_map["pl"] = "pl_PL";
+       compat_map["da"] = "da_DK";
+       compat_map["pt"] = "pt_PT";
+       compat_map["sk"] = "sk_SK";
+       compat_map["cs"] = "cs_CZ";
+       compat_map["uk"] = "uk_UA";
+
+       std::string lang = config->language().get_value_or ("en_GB");
+       if (compat_map.find (lang) != compat_map.end ()) {
+               lang = compat_map[lang];
+       }
+
+       checked_set (_language, lang);
+
+       checked_set (_check_for_updates, config->check_for_updates ());
+       checked_set (_check_for_test_updates, config->check_for_test_updates ());
+
+       checked_set (_sound, config->sound ());
+
+       boost::optional<std::string> const current_so = get_sound_output ();
+       boost::optional<std::string> configured_so;
+
+       if (config->sound_output()) {
+               configured_so = config->sound_output().get();
+       } else {
+               /* No configured output means we should use the default */
+               RtAudio audio (DCPOMATIC_RTAUDIO_API);
+               try {
+                       configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
+               } catch (RtAudioError& e) {
+                       /* Probably no audio devices at all */
+               }
+       }
+
+       if (configured_so && current_so != configured_so) {
+               /* Update _sound_output with the configured value */
+               unsigned int i = 0;
+               while (i < _sound_output->GetCount()) {
+                       if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
+                               _sound_output->SetSelection (i);
+                               break;
+                       }
+                       ++i;
+               }
+       }
+
+       setup_sensitivity ();
+}
+
+void
+GeneralPage::setup_sensitivity ()
+{
+       _language->Enable (_set_language->GetValue ());
+       _check_for_test_updates->Enable (_check_for_updates->GetValue ());
+       _sound_output->Enable (_sound->GetValue ());
+}
+
+/** @return Currently-selected preview sound output in the dialogue */
+boost::optional<std::string>
+GeneralPage::get_sound_output ()
+{
+       int const sel = _sound_output->GetSelection ();
+       if (sel == wxNOT_FOUND) {
+               return boost::optional<std::string> ();
+       }
+
+       return wx_to_std (_sound_output->GetString (sel));
+}
+
+void
+GeneralPage::set_language_changed ()
+{
+       setup_sensitivity ();
+       if (_set_language->GetValue ()) {
+               language_changed ();
+       } else {
+               Config::instance()->unset_language ();
+       }
+}
+
+void
+GeneralPage::language_changed ()
+{
+       int const sel = _language->GetSelection ();
+       if (sel != -1) {
+               Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
+       } else {
+               Config::instance()->unset_language ();
+       }
+}
+
+void
+GeneralPage::check_for_updates_changed ()
+{
+       Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
+}
+
+void
+GeneralPage::check_for_test_updates_changed ()
+{
+       Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
+}
+
+void
+GeneralPage::sound_changed ()
+{
+       Config::instance()->set_sound (_sound->GetValue ());
+}
+
+void
+GeneralPage::sound_output_changed ()
+{
+       RtAudio audio (DCPOMATIC_RTAUDIO_API);
+       boost::optional<std::string> const so = get_sound_output();
+       if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
+               Config::instance()->unset_sound_output ();
+       } else {
+               Config::instance()->set_sound_output (*so);
+       }
+}
+
+CertificateChainEditor::CertificateChainEditor (
+       wxWindow* parent,
+       wxString title,
+       int border,
+       boost::function<void (boost::shared_ptr<dcp::CertificateChain>)> set,
+       boost::function<boost::shared_ptr<const dcp::CertificateChain> (void)> get,
+       boost::function<void (void)> nag_remake
+       )
+       : wxPanel (parent)
+       , _set (set)
+       , _get (get)
+       , _nag_remake (nag_remake)
+{
+       wxFont subheading_font (*wxNORMAL_FONT);
+       subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
+
+       _sizer = new wxBoxSizer (wxVERTICAL);
+
+       {
+               wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
+               m->SetFont (subheading_font);
+               _sizer->Add (m, 0, wxALL, border);
+       }
+
+       wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
+       _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
+
+       _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
+
+       {
+               wxListItem ip;
+               ip.SetId (0);
+               ip.SetText (_("Type"));
+               ip.SetWidth (100);
+               _certificates->InsertColumn (0, ip);
+       }
+
+       {
+               wxListItem ip;
+               ip.SetId (1);
+               ip.SetText (_("Thumbprint"));
+               ip.SetWidth (340);
+
+               wxFont font = ip.GetFont ();
+               font.SetFamily (wxFONTFAMILY_TELETYPE);
+               ip.SetFont (font);
+
+               _certificates->InsertColumn (1, ip);
+       }
+
+       certificates_sizer->Add (_certificates, 1, wxEXPAND);
+
+       {
+               wxSizer* s = new wxBoxSizer (wxVERTICAL);
+               _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
+               s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+               _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
+               s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+               _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
+               s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+               certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
+       }
+
+       wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+       _sizer->Add (table, 1, wxALL | wxEXPAND, border);
+       int r = 0;
+
+       add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
+       _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
+       wxFont font = _private_key->GetFont ();
+       font.SetFamily (wxFONTFAMILY_TELETYPE);
+       _private_key->SetFont (font);
+       table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+       _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
+       table->Add (_load_private_key, wxGBPosition (r, 2));
+       _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
+       table->Add (_export_private_key, wxGBPosition (r, 3));
+       ++r;
+
+       _button_sizer = new wxBoxSizer (wxHORIZONTAL);
+       _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
+       _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
+       table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
+       ++r;
+
+       _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
+       font = *wxSMALL_FONT;
+       font.SetWeight (wxFONTWEIGHT_BOLD);
+       _private_key_bad->SetFont (font);
+       table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
+       ++r;
+
+       _add_certificate->Bind     (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::add_certificate, this));
+       _remove_certificate->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::remove_certificate, this));
+       _export_certificate->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::export_certificate, this));
+       _certificates->Bind        (wxEVT_LIST_ITEM_SELECTED,   boost::bind (&CertificateChainEditor::update_sensitivity, this));
+       _certificates->Bind        (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
+       _remake_certificates->Bind (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::remake_certificates, this));
+       _load_private_key->Bind    (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::load_private_key, this));
+       _export_private_key->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::export_private_key, this));
+
+       SetSizerAndFit (_sizer);
+}
+
+
+void
+CertificateChainEditor::config_changed ()
+{
+       _chain.reset (new dcp::CertificateChain (*_get().get ()));
+
+       update_certificate_list ();
+       update_private_key ();
+       update_sensitivity ();
+}
+
+void
+CertificateChainEditor::add_button (wxWindow* button)
+{
+       _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
+       _sizer->Layout ();
+}
+
+void
+CertificateChainEditor::add_certificate ()
+{
+       wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
+
+       if (d->ShowModal() == wxID_OK) {
+               try {
+                       dcp::Certificate c;
+                       std::string extra;
+                       try {
+                               extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
+                       } catch (boost::filesystem::filesystem_error& e) {
+                               error_dialog (this, wxString::Format (_("Could not load certificate (%s)"), d->GetPath().data()));
+                               d->Destroy ();
+                               return;
+                       }
+
+                       if (!extra.empty ()) {
+                               message_dialog (
+                                       this,
+                                       _("This file contains other certificates (or other data) after its first certificate. "
+                                         "Only the first certificate will be used.")
+                                       );
+                       }
+                       _chain->add (c);
+                       if (!_chain->chain_valid ()) {
+                               error_dialog (
+                                       this,
+                                       _("Adding this certificate would make the chain inconsistent, so it will not be added. "
+                                         "Add certificates in order from root to intermediate to leaf.")
+                                       );
+                               _chain->remove (c);
+                       } else {
+                               _set (_chain);
+                               update_certificate_list ();
+                       }
+               } catch (dcp::MiscError& e) {
+                       error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
+               }
+       }
+
+       d->Destroy ();
+
+       update_sensitivity ();
+}
+
+void
+CertificateChainEditor::remove_certificate ()
+{
+       int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+       if (i == -1) {
+               return;
+       }
+
+       _certificates->DeleteItem (i);
+       _chain->remove (i);
+       _set (_chain);
+
+       update_sensitivity ();
+}
+
+void
+CertificateChainEditor::export_certificate ()
+{
+       int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+       if (i == -1) {
+               return;
+       }
+
+       wxFileDialog* d = new wxFileDialog (
+               this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
+               wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+               );
+
+       dcp::CertificateChain::List all = _chain->root_to_leaf ();
+       dcp::CertificateChain::List::iterator j = all.begin ();
+       for (int k = 0; k < i; ++k) {
+               ++j;
+       }
+
+       if (d->ShowModal () == wxID_OK) {
+               FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
+               if (!f) {
+                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+               }
+
+               std::string const s = j->certificate (true);
+               fwrite (s.c_str(), 1, s.length(), f);
+               fclose (f);
+       }
+       d->Destroy ();
+}
+
+void
+CertificateChainEditor::update_certificate_list ()
+{
+       _certificates->DeleteAllItems ();
+       size_t n = 0;
+       dcp::CertificateChain::List certs = _chain->root_to_leaf ();
+       BOOST_FOREACH (dcp::Certificate const & i, certs) {
+               wxListItem item;
+               item.SetId (n);
+               _certificates->InsertItem (item);
+               _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
+
+               if (n == 0) {
+                       _certificates->SetItem (n, 0, _("Root"));
+               } else if (n == (certs.size() - 1)) {
+                       _certificates->SetItem (n, 0, _("Leaf"));
+               } else {
+                       _certificates->SetItem (n, 0, _("Intermediate"));
+               }
+
+               ++n;
+       }
+
+       static wxColour normal = _private_key_bad->GetForegroundColour ();
+
+       if (_chain->private_key_valid ()) {
+               _private_key_bad->Hide ();
+               _private_key_bad->SetForegroundColour (normal);
+       } else {
+               _private_key_bad->Show ();
+               _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
+       }
+}
+
+void
+CertificateChainEditor::remake_certificates ()
+{
+       boost::shared_ptr<const dcp::CertificateChain> chain = _get ();
+
+       std::string subject_organization_name;
+       std::string subject_organizational_unit_name;
+       std::string root_common_name;
+       std::string intermediate_common_name;
+       std::string leaf_common_name;
+
+       dcp::CertificateChain::List all = chain->root_to_leaf ();
+
+       if (all.size() >= 1) {
+               /* Have a root */
+               subject_organization_name = chain->root().subject_organization_name ();
+               subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
+               root_common_name = chain->root().subject_common_name ();
+       }
+
+       if (all.size() >= 2) {
+               /* Have a leaf */
+               leaf_common_name = chain->leaf().subject_common_name ();
+       }
+
+       if (all.size() >= 3) {
+               /* Have an intermediate */
+               dcp::CertificateChain::List::iterator i = all.begin ();
+               ++i;
+               intermediate_common_name = i->subject_common_name ();
+       }
+
+       _nag_remake ();
+
+       MakeChainDialog* d = new MakeChainDialog (
+               this,
+               subject_organization_name,
+               subject_organizational_unit_name,
+               root_common_name,
+               intermediate_common_name,
+               leaf_common_name
+               );
+
+       if (d->ShowModal () == wxID_OK) {
+               _chain.reset (
+                       new dcp::CertificateChain (
+                               openssl_path (),
+                               d->organisation (),
+                               d->organisational_unit (),
+                               d->root_common_name (),
+                               d->intermediate_common_name (),
+                               d->leaf_common_name ()
+                               )
+                       );
+
+               _set (_chain);
+               update_certificate_list ();
+               update_private_key ();
+       }
+
+       d->Destroy ();
+}
+
+void
+CertificateChainEditor::update_sensitivity ()
+{
+       /* We can only remove the leaf certificate */
+       _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
+       _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
+}
+
+void
+CertificateChainEditor::update_private_key ()
+{
+       checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
+       _sizer->Layout ();
+}
+
+void
+CertificateChainEditor::load_private_key ()
+{
+       wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
+
+       if (d->ShowModal() == wxID_OK) {
+               try {
+                       boost::filesystem::path p (wx_to_std (d->GetPath ()));
+                       if (boost::filesystem::file_size (p) > 8192) {
+                               error_dialog (
+                                       this,
+                                       wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
+                                       );
+                               return;
+                       }
+
+                       _chain->set_key (dcp::file_to_string (p));
+                       _set (_chain);
+                       update_private_key ();
+               } catch (dcp::MiscError& e) {
+                       error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
+               }
+       }
+
+       d->Destroy ();
+
+       update_sensitivity ();
+}
+
+void
+CertificateChainEditor::export_private_key ()
+{
+       boost::optional<std::string> key = _chain->key ();
+       if (!key) {
+               return;
+       }
+
+       wxFileDialog* d = new wxFileDialog (
+               this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
+               wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+               );
+
+       if (d->ShowModal () == wxID_OK) {
+               FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
+               if (!f) {
+                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+               }
+
+               std::string const s = _chain->key().get ();
+               fwrite (s.c_str(), 1, s.length(), f);
+               fclose (f);
+       }
+       d->Destroy ();
+}
+
+wxString
+KeysPage::GetName () const
+{
+       return _("Keys");
+}
+
+void
+KeysPage::setup ()
+{
+       if (_sign) {
+               _signer = new CertificateChainEditor (
+                       _panel, _("Signing DCPs and KDMs"), _border,
+                       bind (&Config::set_signer_chain, Config::instance (), _1),
+                       bind (&Config::signer_chain, Config::instance ()),
+                       bind (&do_nothing)
+                       );
+
+               _panel->GetSizer()->Add (_signer);
+       }
+
+       _decryption = new CertificateChainEditor (
+               _panel, _("Decrypting KDMs"), _border,
+               bind (&Config::set_decryption_chain, Config::instance (), _1),
+               bind (&Config::decryption_chain, Config::instance ()),
+               bind (&KeysPage::nag_remake_decryption_chain, this)
+               );
+
+       _panel->GetSizer()->Add (_decryption);
+
+       _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
+       _decryption->add_button (_export_decryption_certificate);
+       _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
+       _decryption->add_button (_export_decryption_chain);
+
+       _export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
+       _export_decryption_chain->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain, this));
+}
+
+void
+KeysPage::config_changed ()
+{
+       if (_sign) {
+               _signer->config_changed ();
+       }
+       _decryption->config_changed ();
+}
+
+void
+KeysPage::nag_remake_decryption_chain ()
+{
+       NagDialog::maybe_nag (
+               _panel,
+               Config::NAG_REMAKE_DECRYPTION_CHAIN,
+               _("If you continue with this operation you will no longer be able to use any DKDMs that you have created.  Also, any KDMs that have been sent to you will become useless.  Proceed with caution!")
+               );
+}
+
+void
+KeysPage::export_decryption_chain ()
+{
+       wxFileDialog* d = new wxFileDialog (
+               _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
+               wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+               );
+
+       if (d->ShowModal () == wxID_OK) {
+               FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
+               if (!f) {
+                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+               }
+
+               std::string const s = Config::instance()->decryption_chain()->chain();
+               fwrite (s.c_str(), 1, s.length(), f);
+               fclose (f);
+       }
+       d->Destroy ();
+}
+
+void
+KeysPage::export_decryption_certificate ()
+{
+       wxFileDialog* d = new wxFileDialog (
+               _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
+               wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+               );
+
+       if (d->ShowModal () == wxID_OK) {
+               FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
+               if (!f) {
+                       throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
+               }
+
+               std::string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
+               fwrite (s.c_str(), 1, s.length(), f);
+               fclose (f);
+       }
+       d->Destroy ();
+}
index a72879407af03b9661ad0240fcd762fad14ef537..77389c375ef3d8c94bfdf0d2c67d6a5ee1b5329f 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "wx_util.h"
 #include "editable_list.h"
+#include "make_chain_dialog.h"
 #include "lib/config.h"
 #include "lib/ratio.h"
 #include "lib/filter.h"
 class Page
 {
 public:
-       Page (wxSize panel_size, int border)
-               : _border (border)
-               , _panel (0)
-               , _panel_size (panel_size)
-               , _window_exists (false)
-       {
-               _config_connection = Config::instance()->Changed.connect (boost::bind (&Page::config_changed_wrapper, this));
-       }
-
+       Page (wxSize panel_size, int border);
        virtual ~Page () {}
 
 protected:
-       wxWindow* create_window (wxWindow* parent)
-       {
-               _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
-               wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
-               _panel->SetSizer (s);
-
-               setup ();
-               _window_exists = true;
-               config_changed ();
-
-               _panel->Bind (wxEVT_DESTROY, boost::bind (&Page::window_destroyed, this));
-
-               return _panel;
-       }
+       wxWindow* create_window (wxWindow* parent);
 
        int _border;
        wxPanel* _panel;
@@ -80,17 +60,8 @@ private:
        virtual void config_changed () = 0;
        virtual void setup () = 0;
 
-       void config_changed_wrapper ()
-       {
-               if (_window_exists) {
-                       config_changed ();
-               }
-       }
-
-       void window_destroyed ()
-       {
-               _window_exists = false;
-       }
+       void config_changed_wrapper ();
+       void window_destroyed ();
 
        wxSize _panel_size;
        boost::signals2::scoped_connection _config_connection;
@@ -100,247 +71,121 @@ private:
 class StockPage : public wxStockPreferencesPage, public Page
 {
 public:
-       StockPage (Kind kind, wxSize panel_size, int border)
-               : wxStockPreferencesPage (kind)
-               , Page (panel_size, border)
-       {}
-
-       wxWindow* CreateWindow (wxWindow* parent)
-       {
-               return create_window (parent);
-       }
+       StockPage (Kind kind, wxSize panel_size, int border);
+       wxWindow* CreateWindow (wxWindow* parent);
 };
 
 class StandardPage : public wxPreferencesPage, public Page
 {
 public:
-       StandardPage (wxSize panel_size, int border)
-               : Page (panel_size, border)
-       {}
-
-       wxWindow* CreateWindow (wxWindow* parent)
-       {
-               return create_window (parent);
-       }
+       StandardPage (wxSize panel_size, int border);
+       wxWindow* CreateWindow (wxWindow* parent);
 };
 
 class GeneralPage : public StockPage
 {
 public:
-       GeneralPage (wxSize panel_size, int border)
-               : StockPage (Kind_General, panel_size, border)
-       {}
+       GeneralPage (wxSize panel_size, int border);
 
 protected:
-       void add_language_controls (wxGridBagSizer* table, int& r)
-       {
-               _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
-               table->Add (_set_language, wxGBPosition (r, 0));
-               _language = new wxChoice (_panel, wxID_ANY);
-               std::vector<std::pair<std::string, std::string> > languages;
-               languages.push_back (std::make_pair ("Čeština", "cs_CZ"));
-               languages.push_back (std::make_pair ("汉语/漢語", "zh_CN"));
-               languages.push_back (std::make_pair ("Dansk", "da_DK"));
-               languages.push_back (std::make_pair ("Deutsch", "de_DE"));
-               languages.push_back (std::make_pair ("English", "en_GB"));
-               languages.push_back (std::make_pair ("Español", "es_ES"));
-               languages.push_back (std::make_pair ("Français", "fr_FR"));
-               languages.push_back (std::make_pair ("Italiano", "it_IT"));
-               languages.push_back (std::make_pair ("Nederlands", "nl_NL"));
-               languages.push_back (std::make_pair ("Русский", "ru_RU"));
-               languages.push_back (std::make_pair ("Polski", "pl_PL"));
-               languages.push_back (std::make_pair ("Português europeu", "pt_PT"));
-               languages.push_back (std::make_pair ("Português do Brasil", "pt_BR"));
-               languages.push_back (std::make_pair ("Svenska", "sv_SE"));
-               languages.push_back (std::make_pair ("Slovenský jazyk", "sk_SK"));
-               languages.push_back (std::make_pair ("українська мова", "uk_UA"));
-               checked_set (_language, languages);
-               table->Add (_language, wxGBPosition (r, 1));
-               ++r;
-
-               wxStaticText* restart = add_label_to_sizer (
-                       table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
-                       );
-               wxFont font = restart->GetFont();
-               font.SetStyle (wxFONTSTYLE_ITALIC);
-               font.SetPointSize (font.GetPointSize() - 1);
-               restart->SetFont (font);
-               ++r;
-
-               _set_language->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::set_language_changed, this));
-               _language->Bind     (wxEVT_CHOICE,   boost::bind (&GeneralPage::language_changed,     this));
-       }
+       void add_language_controls (wxGridBagSizer* table, int& r);
+       void add_play_sound_controls (wxGridBagSizer* table, int& r);
+       void add_update_controls (wxGridBagSizer* table, int& r);
+       virtual void config_changed ();
 
-       void add_play_sound_controls (wxGridBagSizer* table, int& r)
-       {
-               _sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound via"));
-               table->Add (_sound, wxGBPosition (r, 0));
-                _sound_output = new wxChoice (_panel, wxID_ANY);
-                table->Add (_sound_output, wxGBPosition (r, 1));
-                ++r;
-
-                RtAudio audio (DCPOMATIC_RTAUDIO_API);
-                for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
-                        RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
-                        if (dev.probed && dev.outputChannels > 0) {
-                                _sound_output->Append (std_to_wx (dev.name));
-                        }
-                }
-
-               _sound->Bind        (wxEVT_CHECKBOX, boost::bind (&GeneralPage::sound_changed, this));
-               _sound_output->Bind (wxEVT_CHOICE,   boost::bind (&GeneralPage::sound_output_changed, this));
-       }
-
-       void add_update_controls (wxGridBagSizer* table, int& r)
-       {
-               _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
-               table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
-               ++r;
-
-               _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
-               table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
-               ++r;
+private:
+       void setup_sensitivity ();
+        boost::optional<std::string> get_sound_output ();
+       void set_language_changed ();
+       void language_changed ();
+       void check_for_updates_changed ();
+       void check_for_test_updates_changed ();
+       void sound_changed ();
+        void sound_output_changed ();
 
-               _check_for_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_updates_changed, this));
-               _check_for_test_updates->Bind (wxEVT_CHECKBOX, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
-       }
+       wxCheckBox* _set_language;
+       wxChoice* _language;
+       wxCheckBox* _sound;
+       wxChoice* _sound_output;
+       wxCheckBox* _check_for_updates;
+       wxCheckBox* _check_for_test_updates;
+};
 
-       virtual void config_changed ()
-       {
-               Config* config = Config::instance ();
-
-               checked_set (_set_language, static_cast<bool>(config->language()));
-
-               /* Backwards compatibility of config file */
-
-               std::map<std::string, std::string> compat_map;
-               compat_map["fr"] = "fr_FR";
-               compat_map["it"] = "it_IT";
-               compat_map["es"] = "es_ES";
-               compat_map["sv"] = "sv_SE";
-               compat_map["de"] = "de_DE";
-               compat_map["nl"] = "nl_NL";
-               compat_map["ru"] = "ru_RU";
-               compat_map["pl"] = "pl_PL";
-               compat_map["da"] = "da_DK";
-               compat_map["pt"] = "pt_PT";
-               compat_map["sk"] = "sk_SK";
-               compat_map["cs"] = "cs_CZ";
-               compat_map["uk"] = "uk_UA";
-
-               std::string lang = config->language().get_value_or ("en_GB");
-               if (compat_map.find (lang) != compat_map.end ()) {
-                       lang = compat_map[lang];
-               }
-
-               checked_set (_language, lang);
-
-               checked_set (_check_for_updates, config->check_for_updates ());
-               checked_set (_check_for_test_updates, config->check_for_test_updates ());
-
-               checked_set (_sound, config->sound ());
-
-               boost::optional<std::string> const current_so = get_sound_output ();
-                boost::optional<std::string> configured_so;
-
-                if (config->sound_output()) {
-                        configured_so = config->sound_output().get();
-                } else {
-                        /* No configured output means we should use the default */
-                        RtAudio audio (DCPOMATIC_RTAUDIO_API);
-                       try {
-                               configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
-                       } catch (RtAudioError& e) {
-                               /* Probably no audio devices at all */
-                       }
-                }
-
-                if (configured_so && current_so != configured_so) {
-                        /* Update _sound_output with the configured value */
-                        unsigned int i = 0;
-                        while (i < _sound_output->GetCount()) {
-                                if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
-                                        _sound_output->SetSelection (i);
-                                        break;
-                                }
-                                ++i;
-                        }
-                }
-
-               setup_sensitivity ();
-       }
+class CertificateChainEditor : public wxPanel
+{
+public:
+       CertificateChainEditor (
+               wxWindow* parent,
+               wxString title,
+               int border,
+               boost::function<void (boost::shared_ptr<dcp::CertificateChain>)> set,
+               boost::function<boost::shared_ptr<const dcp::CertificateChain> (void)> get,
+               boost::function<void (void)> nag_remake
+               );
+
+       void config_changed ();
+       void add_button (wxWindow* button);
 
 private:
-       void setup_sensitivity ()
-       {
-               _language->Enable (_set_language->GetValue ());
-               _check_for_test_updates->Enable (_check_for_updates->GetValue ());
-               _sound_output->Enable (_sound->GetValue ());
-       }
-
-        /** @return Currently-selected preview sound output in the dialogue */
-        boost::optional<std::string> get_sound_output ()
-        {
-                int const sel = _sound_output->GetSelection ();
-                if (sel == wxNOT_FOUND) {
-                        return boost::optional<std::string> ();
-                }
-
-                return wx_to_std (_sound_output->GetString (sel));
-        }
+       void add_certificate ();
+       void remove_certificate ();
+       void export_certificate ();
+       void update_certificate_list ();
+       void remake_certificates ();
+       void update_sensitivity ();
+       void update_private_key ();
+       void load_private_key ();
+       void export_private_key ();
+
+       wxListCtrl* _certificates;
+       wxButton* _add_certificate;
+       wxButton* _export_certificate;
+       wxButton* _remove_certificate;
+       wxButton* _remake_certificates;
+       wxStaticText* _private_key;
+       wxButton* _load_private_key;
+       wxButton* _export_private_key;
+       wxStaticText* _private_key_bad;
+       wxSizer* _sizer;
+       wxBoxSizer* _button_sizer;
+       boost::shared_ptr<dcp::CertificateChain> _chain;
+       boost::function<void (boost::shared_ptr<dcp::CertificateChain>)> _set;
+       boost::function<boost::shared_ptr<const dcp::CertificateChain> (void)> _get;
+       boost::function<void (void)> _nag_remake;
+};
 
-       void set_language_changed ()
-       {
-               setup_sensitivity ();
-               if (_set_language->GetValue ()) {
-                       language_changed ();
-               } else {
-                       Config::instance()->unset_language ();
-               }
-       }
+class KeysPage : public StandardPage
+{
+public:
+       KeysPage (wxSize panel_size, int border, bool sign)
+               : StandardPage (panel_size, border)
+               , _sign (sign)
+       {}
 
-       void language_changed ()
-       {
-               int const sel = _language->GetSelection ();
-               if (sel != -1) {
-                       Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
-               } else {
-                       Config::instance()->unset_language ();
-               }
-       }
+       wxString GetName () const;
 
-       void check_for_updates_changed ()
+#ifdef DCPOMATIC_OSX
+       wxBitmap GetLargeIcon () const
        {
-               Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
+               return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
        }
+#endif
 
-       void check_for_test_updates_changed ()
-       {
-               Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
-       }
+private:
 
-       void sound_changed ()
-       {
-               Config::instance()->set_sound (_sound->GetValue ());
-       }
+       void setup ();
 
-        void sound_output_changed ()
-        {
-                RtAudio audio (DCPOMATIC_RTAUDIO_API);
-               boost::optional<std::string> const so = get_sound_output();
-                if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
-                        Config::instance()->unset_sound_output ();
-                } else {
-                        Config::instance()->set_sound_output (*so);
-                }
-        }
+       void export_decryption_certificate ();
+       void export_decryption_chain ();
+       void config_changed ();
+       void nag_remake_decryption_chain ();
 
-       wxCheckBox* _set_language;
-       wxChoice* _language;
-       wxCheckBox* _sound;
-       wxChoice* _sound_output;
-       wxCheckBox* _check_for_updates;
-       wxCheckBox* _check_for_test_updates;
+       CertificateChainEditor* _signer;
+       CertificateChainEditor* _decryption;
+       wxButton* _export_decryption_certificate;
+       wxButton* _export_decryption_chain;
+       bool _sign;
 };
 
+
 #endif
index 56a765af297c9203e9bdb6fd3ab57112c44ad126..b5f9a4809374bdbf94f7039ae227dfaff3713609 100644 (file)
@@ -69,13 +69,6 @@ using boost::function;
 using boost::optional;
 using dcp::locale_convert;
 
-static
-void
-do_nothing ()
-{
-
-}
-
 class FullGeneralPage : public GeneralPage
 {
 public:
@@ -574,508 +567,6 @@ private:
        EditableList<string, ServerDialog>* _servers_list;
 };
 
-class CertificateChainEditor : public wxPanel
-{
-public:
-       CertificateChainEditor (
-               wxWindow* parent,
-               wxString title,
-               int border,
-               function<void (shared_ptr<dcp::CertificateChain>)> set,
-               function<shared_ptr<const dcp::CertificateChain> (void)> get,
-               function<void (void)> nag_remake
-               )
-               : wxPanel (parent)
-               , _set (set)
-               , _get (get)
-               , _nag_remake (nag_remake)
-       {
-               wxFont subheading_font (*wxNORMAL_FONT);
-               subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
-
-               _sizer = new wxBoxSizer (wxVERTICAL);
-
-               {
-                       wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
-                       m->SetFont (subheading_font);
-                       _sizer->Add (m, 0, wxALL, border);
-               }
-
-               wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
-               _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
-
-               _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
-
-               {
-                       wxListItem ip;
-                       ip.SetId (0);
-                       ip.SetText (_("Type"));
-                       ip.SetWidth (100);
-                       _certificates->InsertColumn (0, ip);
-               }
-
-               {
-                       wxListItem ip;
-                       ip.SetId (1);
-                       ip.SetText (_("Thumbprint"));
-                       ip.SetWidth (340);
-
-                       wxFont font = ip.GetFont ();
-                       font.SetFamily (wxFONTFAMILY_TELETYPE);
-                       ip.SetFont (font);
-
-                       _certificates->InsertColumn (1, ip);
-               }
-
-               certificates_sizer->Add (_certificates, 1, wxEXPAND);
-
-               {
-                       wxSizer* s = new wxBoxSizer (wxVERTICAL);
-                       _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
-                       s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-                       _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
-                       s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-                       _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
-                       s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-                       certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
-               }
-
-               wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
-               _sizer->Add (table, 1, wxALL | wxEXPAND, border);
-               int r = 0;
-
-               add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
-               _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
-               wxFont font = _private_key->GetFont ();
-               font.SetFamily (wxFONTFAMILY_TELETYPE);
-               _private_key->SetFont (font);
-               table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
-               _load_private_key = new wxButton (this, wxID_ANY, _("Load..."));
-               table->Add (_load_private_key, wxGBPosition (r, 2));
-               _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
-               table->Add (_export_private_key, wxGBPosition (r, 3));
-               ++r;
-
-               _button_sizer = new wxBoxSizer (wxHORIZONTAL);
-               _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates\nand key..."));
-               _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
-               table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
-               ++r;
-
-               _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
-               font = *wxSMALL_FONT;
-               font.SetWeight (wxFONTWEIGHT_BOLD);
-               _private_key_bad->SetFont (font);
-               table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
-               ++r;
-
-               _add_certificate->Bind     (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::add_certificate, this));
-               _remove_certificate->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::remove_certificate, this));
-               _export_certificate->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::export_certificate, this));
-               _certificates->Bind        (wxEVT_LIST_ITEM_SELECTED,   boost::bind (&CertificateChainEditor::update_sensitivity, this));
-               _certificates->Bind        (wxEVT_LIST_ITEM_DESELECTED, boost::bind (&CertificateChainEditor::update_sensitivity, this));
-               _remake_certificates->Bind (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::remake_certificates, this));
-               _load_private_key->Bind    (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::load_private_key, this));
-               _export_private_key->Bind  (wxEVT_BUTTON,       boost::bind (&CertificateChainEditor::export_private_key, this));
-
-               SetSizerAndFit (_sizer);
-       }
-
-       void config_changed ()
-       {
-               _chain.reset (new dcp::CertificateChain (*_get().get ()));
-
-               update_certificate_list ();
-               update_private_key ();
-               update_sensitivity ();
-       }
-
-       void add_button (wxWindow* button)
-       {
-               _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
-               _sizer->Layout ();
-       }
-
-private:
-       void add_certificate ()
-       {
-               wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
-
-               if (d->ShowModal() == wxID_OK) {
-                       try {
-                               dcp::Certificate c;
-                               string extra;
-                               try {
-                                       extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
-                               } catch (boost::filesystem::filesystem_error& e) {
-                                       error_dialog (this, wxString::Format (_("Could not load certificate (%s)"), d->GetPath().data()));
-                                       d->Destroy ();
-                                       return;
-                               }
-
-                               if (!extra.empty ()) {
-                                       message_dialog (
-                                               this,
-                                               _("This file contains other certificates (or other data) after its first certificate. "
-                                                 "Only the first certificate will be used.")
-                                               );
-                               }
-                               _chain->add (c);
-                               if (!_chain->chain_valid ()) {
-                                       error_dialog (
-                                               this,
-                                               _("Adding this certificate would make the chain inconsistent, so it will not be added. "
-                                                 "Add certificates in order from root to intermediate to leaf.")
-                                               );
-                                       _chain->remove (c);
-                               } else {
-                                       _set (_chain);
-                                       update_certificate_list ();
-                               }
-                       } catch (dcp::MiscError& e) {
-                               error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
-                       }
-               }
-
-               d->Destroy ();
-
-               update_sensitivity ();
-       }
-
-       void remove_certificate ()
-       {
-               int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-               if (i == -1) {
-                       return;
-               }
-
-               _certificates->DeleteItem (i);
-               _chain->remove (i);
-               _set (_chain);
-
-               update_sensitivity ();
-       }
-
-       void export_certificate ()
-       {
-               int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-               if (i == -1) {
-                       return;
-               }
-
-               wxFileDialog* d = new wxFileDialog (
-                       this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
-                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
-                       );
-
-               dcp::CertificateChain::List all = _chain->root_to_leaf ();
-               dcp::CertificateChain::List::iterator j = all.begin ();
-               for (int k = 0; k < i; ++k) {
-                       ++j;
-               }
-
-               if (d->ShowModal () == wxID_OK) {
-                       FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
-                       if (!f) {
-                               throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
-                       }
-
-                       string const s = j->certificate (true);
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
-       }
-
-       void update_certificate_list ()
-       {
-               _certificates->DeleteAllItems ();
-               size_t n = 0;
-               dcp::CertificateChain::List certs = _chain->root_to_leaf ();
-               BOOST_FOREACH (dcp::Certificate const & i, certs) {
-                       wxListItem item;
-                       item.SetId (n);
-                       _certificates->InsertItem (item);
-                       _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
-
-                       if (n == 0) {
-                               _certificates->SetItem (n, 0, _("Root"));
-                       } else if (n == (certs.size() - 1)) {
-                               _certificates->SetItem (n, 0, _("Leaf"));
-                       } else {
-                               _certificates->SetItem (n, 0, _("Intermediate"));
-                       }
-
-                       ++n;
-               }
-
-               static wxColour normal = _private_key_bad->GetForegroundColour ();
-
-               if (_chain->private_key_valid ()) {
-                       _private_key_bad->Hide ();
-                       _private_key_bad->SetForegroundColour (normal);
-               } else {
-                       _private_key_bad->Show ();
-                       _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
-               }
-       }
-
-       void remake_certificates ()
-       {
-               shared_ptr<const dcp::CertificateChain> chain = _get ();
-
-               string subject_organization_name;
-               string subject_organizational_unit_name;
-               string root_common_name;
-               string intermediate_common_name;
-               string leaf_common_name;
-
-               dcp::CertificateChain::List all = chain->root_to_leaf ();
-
-               if (all.size() >= 1) {
-                       /* Have a root */
-                       subject_organization_name = chain->root().subject_organization_name ();
-                       subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
-                       root_common_name = chain->root().subject_common_name ();
-               }
-
-               if (all.size() >= 2) {
-                       /* Have a leaf */
-                       leaf_common_name = chain->leaf().subject_common_name ();
-               }
-
-               if (all.size() >= 3) {
-                       /* Have an intermediate */
-                       dcp::CertificateChain::List::iterator i = all.begin ();
-                       ++i;
-                       intermediate_common_name = i->subject_common_name ();
-               }
-
-               _nag_remake ();
-
-               MakeChainDialog* d = new MakeChainDialog (
-                       this,
-                       subject_organization_name,
-                       subject_organizational_unit_name,
-                       root_common_name,
-                       intermediate_common_name,
-                       leaf_common_name
-                       );
-
-               if (d->ShowModal () == wxID_OK) {
-                       _chain.reset (
-                               new dcp::CertificateChain (
-                                       openssl_path (),
-                                       d->organisation (),
-                                       d->organisational_unit (),
-                                       d->root_common_name (),
-                                       d->intermediate_common_name (),
-                                       d->leaf_common_name ()
-                                       )
-                               );
-
-                       _set (_chain);
-                       update_certificate_list ();
-                       update_private_key ();
-               }
-
-               d->Destroy ();
-       }
-
-       void update_sensitivity ()
-       {
-               /* We can only remove the leaf certificate */
-               _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
-               _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
-       }
-
-       void update_private_key ()
-       {
-               checked_set (_private_key, dcp::private_key_fingerprint (_chain->key().get ()));
-               _sizer->Layout ();
-       }
-
-       void load_private_key ()
-       {
-               wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
-
-               if (d->ShowModal() == wxID_OK) {
-                       try {
-                               boost::filesystem::path p (wx_to_std (d->GetPath ()));
-                               if (boost::filesystem::file_size (p) > 8192) {
-                                       error_dialog (
-                                               this,
-                                               wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
-                                               );
-                                       return;
-                               }
-
-                               _chain->set_key (dcp::file_to_string (p));
-                               _set (_chain);
-                               update_private_key ();
-                       } catch (dcp::MiscError& e) {
-                               error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
-                       }
-               }
-
-               d->Destroy ();
-
-               update_sensitivity ();
-       }
-
-       void export_private_key ()
-       {
-               optional<string> key = _chain->key ();
-               if (!key) {
-                       return;
-               }
-
-               wxFileDialog* d = new wxFileDialog (
-                       this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
-                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
-                       );
-
-               if (d->ShowModal () == wxID_OK) {
-                       FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
-                       if (!f) {
-                               throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
-                       }
-
-                       string const s = _chain->key().get ();
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
-       }
-
-       wxListCtrl* _certificates;
-       wxButton* _add_certificate;
-       wxButton* _export_certificate;
-       wxButton* _remove_certificate;
-       wxButton* _remake_certificates;
-       wxStaticText* _private_key;
-       wxButton* _load_private_key;
-       wxButton* _export_private_key;
-       wxStaticText* _private_key_bad;
-       wxSizer* _sizer;
-       wxBoxSizer* _button_sizer;
-       shared_ptr<dcp::CertificateChain> _chain;
-       boost::function<void (shared_ptr<dcp::CertificateChain>)> _set;
-       boost::function<shared_ptr<const dcp::CertificateChain> (void)> _get;
-       boost::function<void (void)> _nag_remake;
-};
-
-class KeysPage : public StandardPage
-{
-public:
-       KeysPage (wxSize panel_size, int border)
-               : StandardPage (panel_size, border)
-       {}
-
-       wxString GetName () const
-       {
-               return _("Keys");
-       }
-
-#ifdef DCPOMATIC_OSX
-       wxBitmap GetLargeIcon () const
-       {
-               return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
-       }
-#endif
-
-private:
-
-       void setup ()
-       {
-               _signer = new CertificateChainEditor (
-                       _panel, _("Signing DCPs and KDMs"), _border,
-                       boost::bind (&Config::set_signer_chain, Config::instance (), _1),
-                       boost::bind (&Config::signer_chain, Config::instance ()),
-                       boost::bind (&do_nothing)
-                       );
-
-               _panel->GetSizer()->Add (_signer);
-
-               _decryption = new CertificateChainEditor (
-                       _panel, _("Decrypting KDMs"), _border,
-                       boost::bind (&Config::set_decryption_chain, Config::instance (), _1),
-                       boost::bind (&Config::decryption_chain, Config::instance ()),
-                       boost::bind (&KeysPage::nag_remake_decryption_chain, this)
-                       );
-
-               _panel->GetSizer()->Add (_decryption);
-
-               _export_decryption_certificate = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\ncertificate..."));
-               _decryption->add_button (_export_decryption_certificate);
-               _export_decryption_chain = new wxButton (_decryption, wxID_ANY, _("Export KDM decryption\nchain..."));
-               _decryption->add_button (_export_decryption_chain);
-
-               _export_decryption_certificate->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_certificate, this));
-               _export_decryption_chain->Bind (wxEVT_BUTTON, boost::bind (&KeysPage::export_decryption_chain, this));
-       }
-
-       void export_decryption_certificate ()
-       {
-               wxFileDialog* d = new wxFileDialog (
-                       _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
-                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
-                       );
-
-               if (d->ShowModal () == wxID_OK) {
-                       FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
-                       if (!f) {
-                               throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
-                       }
-
-                       string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
-       }
-
-       void export_decryption_chain ()
-       {
-               wxFileDialog* d = new wxFileDialog (
-                       _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
-                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT
-                       );
-
-               if (d->ShowModal () == wxID_OK) {
-                       FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
-                       if (!f) {
-                               throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
-                       }
-
-                       string const s = Config::instance()->decryption_chain()->chain();
-                       fwrite (s.c_str(), 1, s.length(), f);
-                       fclose (f);
-               }
-               d->Destroy ();
-       }
-
-       void config_changed ()
-       {
-               _signer->config_changed ();
-               _decryption->config_changed ();
-       }
-
-       void nag_remake_decryption_chain ()
-       {
-               NagDialog::maybe_nag (
-                       _panel,
-                       Config::NAG_REMAKE_DECRYPTION_CHAIN,
-                       _("If you continue with this operation you will no longer be able to use any DKDMs that you have created.  Also, any KDMs that have been sent to you will become useless.  Proceed with caution!")
-                       );
-       }
-
-       CertificateChainEditor* _signer;
-       CertificateChainEditor* _decryption;
-       wxButton* _export_decryption_certificate;
-       wxButton* _export_decryption_chain;
-};
-
 class TMSPage : public StandardPage
 {
 public:
@@ -1664,7 +1155,7 @@ create_full_config_dialog ()
        e->AddPage (new FullGeneralPage (ps, border));
        e->AddPage (new DefaultsPage (ps, border));
        e->AddPage (new EncodingServersPage (ps, border));
-       e->AddPage (new KeysPage (ps, border));
+       e->AddPage (new KeysPage (ps, border, true));
        e->AddPage (new TMSPage (ps, border));
        e->AddPage (new KDMEmailPage (ps, border));
        e->AddPage (new CoverSheetPage (ps, border));
index b9fb0d449f526c3920d4e09c5220c9af1a96fbfd..5ad62430e25f2f1efbd51931ee095d98dd2a7de6 100644 (file)
@@ -18,6 +18,9 @@
 
 */
 
+#ifndef DCPOMATIC_MAKE_CHAIN_DIALOG_H
+#define DCPOMATIC_MAKE_CHAIN_DIALOG_H
+
 #include "table_dialog.h"
 #include "wx_util.h"
 
@@ -61,3 +64,5 @@ private:
        wxTextCtrl* _intermediate_common_name;
        wxTextCtrl* _leaf_common_name;
 };
+
+#endif
index 028fd6d550b9ebac9c30df33f33458966283e4ce..eba23164ff3c51b760be93ed4bc28af69cb8bfb7 100644 (file)
@@ -107,5 +107,6 @@ create_player_config_dialog ()
 #endif
 
        e->AddPage (new PlayerGeneralPage (ps, border));
+       e->AddPage (new KeysPage (ps, border, false));
        return e;
 }
index a18060513409b2dd2ace3373dee9a904cda839b4..7346f3a2f4073fc250831207fe18190c98d31bda 100644 (file)
@@ -34,6 +34,7 @@ sources = """
           batch_job_view.cc
           cinema_dialog.cc
           colour_conversion_editor.cc
+          config_dialog.cc
           config_move_dialog.cc
           confirm_kdm_email_dialog.cc
           content_colour_conversion_dialog.cc