2 Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
21 #include "config_dialog.h"
22 #include "static_text.h"
23 #include "check_box.h"
24 #include "nag_dialog.h"
25 #include "dcpomatic_button.h"
34 using boost::optional;
35 using boost::shared_ptr;
36 using boost::function;
37 #if BOOST_VERSION >= 106100
38 using namespace boost::placeholders;
48 Page::Page (wxSize panel_size, int border)
51 , _panel_size (panel_size)
52 , _window_exists (false)
54 _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
58 Page::create_window (wxWindow* parent)
60 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
61 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
65 _window_exists = true;
68 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
74 Page::config_changed_wrapper ()
82 Page::window_destroyed ()
84 _window_exists = false;
88 StockPage::StockPage (Kind kind, wxSize panel_size, int border)
89 : wxStockPreferencesPage (kind)
90 , Page (panel_size, border)
96 StockPage::CreateWindow (wxWindow* parent)
98 return create_window (parent);
101 StandardPage::StandardPage (wxSize panel_size, int border)
102 : Page (panel_size, border)
108 StandardPage::CreateWindow (wxWindow* parent)
110 return create_window (parent);
113 GeneralPage::GeneralPage (wxSize panel_size, int border)
114 : StockPage (Kind_General, panel_size, border)
120 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
122 _set_language = new CheckBox (_panel, _("Set language"));
123 table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
124 _language = new wxChoice (_panel, wxID_ANY);
125 vector<pair<string, string> > languages;
126 languages.push_back (make_pair ("Čeština", "cs_CZ"));
127 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
128 languages.push_back (make_pair ("Dansk", "da_DK"));
129 languages.push_back (make_pair ("Deutsch", "de_DE"));
130 languages.push_back (make_pair ("English", "en_GB"));
131 languages.push_back (make_pair ("Español", "es_ES"));
132 languages.push_back (make_pair ("Français", "fr_FR"));
133 languages.push_back (make_pair ("Italiano", "it_IT"));
134 languages.push_back (make_pair ("Nederlands", "nl_NL"));
135 languages.push_back (make_pair ("Русский", "ru_RU"));
136 languages.push_back (make_pair ("Polski", "pl_PL"));
137 languages.push_back (make_pair ("Português europeu", "pt_PT"));
138 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
139 languages.push_back (make_pair ("Svenska", "sv_SE"));
140 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
141 languages.push_back (make_pair ("Türkçe", "tr_TR"));
142 languages.push_back (make_pair ("українська мова", "uk_UA"));
143 checked_set (_language, languages);
144 table->Add (_language, wxGBPosition (r, 1));
147 wxStaticText* restart = add_label_to_sizer (
148 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
150 wxFont font = restart->GetFont();
151 font.SetStyle (wxFONTSTYLE_ITALIC);
152 font.SetPointSize (font.GetPointSize() - 1);
153 restart->SetFont (font);
156 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
157 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
161 GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
163 _sound = new CheckBox (_panel, _("Play sound via"));
164 table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
165 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
166 _sound_output = new wxChoice (_panel, wxID_ANY);
167 s->Add (_sound_output, 0);
168 _sound_output_details = new wxStaticText (_panel, wxID_ANY, wxT(""));
169 s->Add (_sound_output_details, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP);
170 table->Add (s, wxGBPosition(r, 1));
173 wxFont font = _sound_output_details->GetFont();
174 font.SetStyle (wxFONTSTYLE_ITALIC);
175 font.SetPointSize (font.GetPointSize() - 1);
176 _sound_output_details->SetFont (font);
178 RtAudio audio (DCPOMATIC_RTAUDIO_API);
179 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
180 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
181 if (dev.probed && dev.outputChannels > 0) {
182 _sound_output->Append (std_to_wx (dev.name));
186 _sound->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::sound_changed, this));
187 _sound_output->Bind (wxEVT_CHOICE, bind (&GeneralPage::sound_output_changed, this));
191 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
193 _check_for_updates = new CheckBox (_panel, _("Check for updates on startup"));
194 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
197 _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
198 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
201 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
202 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
206 GeneralPage::config_changed ()
208 Config* config = Config::instance ();
210 checked_set (_set_language, static_cast<bool>(config->language()));
212 /* Backwards compatibility of config file */
214 map<string, string> compat_map;
215 compat_map["fr"] = "fr_FR";
216 compat_map["it"] = "it_IT";
217 compat_map["es"] = "es_ES";
218 compat_map["sv"] = "sv_SE";
219 compat_map["de"] = "de_DE";
220 compat_map["nl"] = "nl_NL";
221 compat_map["ru"] = "ru_RU";
222 compat_map["pl"] = "pl_PL";
223 compat_map["da"] = "da_DK";
224 compat_map["pt"] = "pt_PT";
225 compat_map["sk"] = "sk_SK";
226 compat_map["cs"] = "cs_CZ";
227 compat_map["uk"] = "uk_UA";
229 string lang = config->language().get_value_or ("en_GB");
230 if (compat_map.find (lang) != compat_map.end ()) {
231 lang = compat_map[lang];
234 checked_set (_language, lang);
236 checked_set (_check_for_updates, config->check_for_updates ());
237 checked_set (_check_for_test_updates, config->check_for_test_updates ());
239 checked_set (_sound, config->sound ());
241 optional<string> const current_so = get_sound_output ();
242 optional<string> configured_so;
244 if (config->sound_output()) {
245 configured_so = config->sound_output().get();
247 /* No configured output means we should use the default */
248 RtAudio audio (DCPOMATIC_RTAUDIO_API);
250 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
251 } catch (RtAudioError& e) {
252 /* Probably no audio devices at all */
256 if (configured_so && current_so != configured_so) {
257 /* Update _sound_output with the configured value */
259 while (i < _sound_output->GetCount()) {
260 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
261 _sound_output->SetSelection (i);
268 RtAudio audio (DCPOMATIC_RTAUDIO_API);
270 map<int, wxString> apis;
271 apis[RtAudio::MACOSX_CORE] = _("CoreAudio");
272 apis[RtAudio::WINDOWS_ASIO] = _("ASIO");
273 apis[RtAudio::WINDOWS_DS] = _("Direct Sound");
274 apis[RtAudio::WINDOWS_WASAPI] = _("WASAPI");
275 apis[RtAudio::UNIX_JACK] = _("JACK");
276 apis[RtAudio::LINUX_ALSA] = _("ALSA");
277 apis[RtAudio::LINUX_PULSE] = _("PulseAudio");
278 apis[RtAudio::LINUX_OSS] = _("OSS");
279 apis[RtAudio::RTAUDIO_DUMMY] = _("Dummy");
283 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
284 RtAudio::DeviceInfo info = audio.getDeviceInfo(i);
285 if (info.name == *configured_so && info.outputChannels > 0) {
286 channels = info.outputChannels;
291 _sound_output_details->SetLabel (
292 wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()])
295 setup_sensitivity ();
299 GeneralPage::setup_sensitivity ()
301 _language->Enable (_set_language->GetValue ());
302 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
303 _sound_output->Enable (_sound->GetValue ());
306 /** @return Currently-selected preview sound output in the dialogue */
308 GeneralPage::get_sound_output ()
310 int const sel = _sound_output->GetSelection ();
311 if (sel == wxNOT_FOUND) {
312 return optional<string> ();
315 return wx_to_std (_sound_output->GetString (sel));
319 GeneralPage::set_language_changed ()
321 setup_sensitivity ();
322 if (_set_language->GetValue ()) {
325 Config::instance()->unset_language ();
330 GeneralPage::language_changed ()
332 int const sel = _language->GetSelection ();
334 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
336 Config::instance()->unset_language ();
341 GeneralPage::check_for_updates_changed ()
343 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
347 GeneralPage::check_for_test_updates_changed ()
349 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
353 GeneralPage::sound_changed ()
355 Config::instance()->set_sound (_sound->GetValue ());
359 GeneralPage::sound_output_changed ()
361 RtAudio audio (DCPOMATIC_RTAUDIO_API);
362 optional<string> const so = get_sound_output();
363 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
364 Config::instance()->unset_sound_output ();
366 Config::instance()->set_sound_output (*so);
370 CertificateChainEditor::CertificateChainEditor (
374 function<void (shared_ptr<dcp::CertificateChain>)> set,
375 function<shared_ptr<const dcp::CertificateChain> (void)> get,
376 function<bool (void)> nag_alter
378 : wxDialog (parent, wxID_ANY, title)
381 , _nag_alter (nag_alter)
383 wxFont subheading_font (*wxNORMAL_FONT);
384 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
386 _sizer = new wxBoxSizer (wxVERTICAL);
389 wxStaticText* m = new StaticText (this, title);
390 m->SetFont (subheading_font);
391 _sizer->Add (m, 0, wxALL, border);
394 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
395 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
397 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
402 ip.SetText (_("Type"));
404 _certificates->InsertColumn (0, ip);
410 ip.SetText (_("Thumbprint"));
413 wxFont font = ip.GetFont ();
414 font.SetFamily (wxFONTFAMILY_TELETYPE);
417 _certificates->InsertColumn (1, ip);
420 certificates_sizer->Add (_certificates, 1, wxEXPAND);
423 wxSizer* s = new wxBoxSizer (wxVERTICAL);
424 _add_certificate = new Button (this, _("Add..."));
425 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
426 _remove_certificate = new Button (this, _("Remove"));
427 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
428 _export_certificate = new Button (this, _("Export"));
429 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
430 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
433 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
434 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
437 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
438 _private_key = new StaticText (this, wxT(""));
439 wxFont font = _private_key->GetFont ();
440 font.SetFamily (wxFONTFAMILY_TELETYPE);
441 _private_key->SetFont (font);
442 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
443 _import_private_key = new Button (this, _("Import..."));
444 table->Add (_import_private_key, wxGBPosition (r, 2));
445 _export_private_key = new Button (this, _("Export..."));
446 table->Add (_export_private_key, wxGBPosition (r, 3));
449 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
450 _remake_certificates = new Button (this, _("Re-make certificates and key..."));
451 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
452 _export_chain = new Button (this, _("Export chain..."));
453 _button_sizer->Add (_export_chain, 1, wxRIGHT, border);
454 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
457 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
458 font = *wxSMALL_FONT;
459 font.SetWeight (wxFONTWEIGHT_BOLD);
460 _private_key_bad->SetFont (font);
461 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
464 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
465 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
466 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
467 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
468 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
469 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
470 _export_chain->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_chain, this));
471 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
472 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
474 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
476 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
479 SetSizerAndFit (_sizer);
481 update_certificate_list ();
482 update_private_key ();
483 update_sensitivity ();
487 CertificateChainEditor::add_button (wxWindow* button)
489 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
494 CertificateChainEditor::add_certificate ()
496 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
498 if (d->ShowModal() == wxID_OK) {
503 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
504 } catch (boost::filesystem::filesystem_error& e) {
505 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
510 if (!extra.empty ()) {
513 _("This file contains other certificates (or other data) after its first certificate. "
514 "Only the first certificate will be used.")
517 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
519 if (!chain->chain_valid ()) {
522 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
523 "Add certificates in order from root to intermediate to leaf.")
528 update_certificate_list ();
530 } catch (dcp::MiscError& e) {
531 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
537 update_sensitivity ();
541 CertificateChainEditor::remove_certificate ()
544 /* Cancel was clicked */
548 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
553 _certificates->DeleteItem (i);
554 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
558 update_sensitivity ();
562 CertificateChainEditor::export_certificate ()
564 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
569 wxFileDialog* d = new wxFileDialog (
570 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
571 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
574 dcp::CertificateChain::List all = _get()->root_to_leaf ();
575 dcp::CertificateChain::List::iterator j = all.begin ();
576 for (int k = 0; k < i; ++k) {
580 if (d->ShowModal () == wxID_OK) {
581 boost::filesystem::path path (wx_to_std(d->GetPath()));
582 FILE* f = fopen_boost (path, "w");
584 throw OpenFileError (path, errno, OpenFileError::WRITE);
587 string const s = j->certificate (true);
588 checked_fwrite (s.c_str(), s.length(), f, path);
595 CertificateChainEditor::export_chain ()
597 wxFileDialog* d = new wxFileDialog (
598 this, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT("PEM files (*.pem)|*.pem"),
599 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
602 if (d->ShowModal () == wxID_OK) {
603 boost::filesystem::path path (wx_to_std(d->GetPath()));
604 FILE* f = fopen_boost (path, "w");
606 throw OpenFileError (path, errno, OpenFileError::WRITE);
609 string const s = _get()->chain();
610 checked_fwrite (s.c_str(), s.length(), f, path);
618 CertificateChainEditor::update_certificate_list ()
620 _certificates->DeleteAllItems ();
622 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
623 BOOST_FOREACH (dcp::Certificate const & i, certs) {
626 _certificates->InsertItem (item);
627 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
630 _certificates->SetItem (n, 0, _("Root"));
631 } else if (n == (certs.size() - 1)) {
632 _certificates->SetItem (n, 0, _("Leaf"));
634 _certificates->SetItem (n, 0, _("Intermediate"));
640 static wxColour normal = _private_key_bad->GetForegroundColour ();
642 if (_get()->private_key_valid()) {
643 _private_key_bad->Hide ();
644 _private_key_bad->SetForegroundColour (normal);
646 _private_key_bad->Show ();
647 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
652 CertificateChainEditor::remake_certificates ()
654 shared_ptr<const dcp::CertificateChain> chain = _get();
656 string subject_organization_name;
657 string subject_organizational_unit_name;
658 string root_common_name;
659 string intermediate_common_name;
660 string leaf_common_name;
662 dcp::CertificateChain::List all = chain->root_to_leaf ();
664 if (all.size() >= 1) {
666 subject_organization_name = chain->root().subject_organization_name ();
667 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
668 root_common_name = chain->root().subject_common_name ();
671 if (all.size() >= 2) {
673 leaf_common_name = chain->leaf().subject_common_name ();
676 if (all.size() >= 3) {
677 /* Have an intermediate */
678 dcp::CertificateChain::List::iterator i = all.begin ();
680 intermediate_common_name = i->subject_common_name ();
684 /* Cancel was clicked */
688 MakeChainDialog* d = new MakeChainDialog (
690 subject_organization_name,
691 subject_organizational_unit_name,
693 intermediate_common_name,
697 if (d->ShowModal () == wxID_OK) {
699 shared_ptr<dcp::CertificateChain> (
700 new dcp::CertificateChain (
703 d->organisational_unit (),
704 d->root_common_name (),
705 d->intermediate_common_name (),
706 d->leaf_common_name ()
711 update_certificate_list ();
712 update_private_key ();
719 CertificateChainEditor::update_sensitivity ()
721 /* We can only remove the leaf certificate */
722 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
723 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
727 CertificateChainEditor::update_private_key ()
729 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
734 CertificateChainEditor::import_private_key ()
736 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
738 if (d->ShowModal() == wxID_OK) {
740 boost::filesystem::path p (wx_to_std (d->GetPath ()));
741 if (boost::filesystem::file_size (p) > 8192) {
744 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
749 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
750 chain->set_key (dcp::file_to_string (p));
752 update_private_key ();
753 } catch (dcp::MiscError& e) {
754 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
760 update_sensitivity ();
764 CertificateChainEditor::export_private_key ()
766 optional<string> key = _get()->key();
771 wxFileDialog* d = new wxFileDialog (
772 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
773 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
776 if (d->ShowModal () == wxID_OK) {
777 boost::filesystem::path path (wx_to_std(d->GetPath()));
778 FILE* f = fopen_boost (path, "w");
780 throw OpenFileError (path, errno, OpenFileError::WRITE);
783 string const s = _get()->key().get ();
784 checked_fwrite (s.c_str(), s.length(), f, path);
791 KeysPage::GetName () const
799 wxFont subheading_font (*wxNORMAL_FONT);
800 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
802 wxSizer* sizer = _panel->GetSizer();
805 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
806 m->SetFont (subheading_font);
807 sizer->Add (m, 0, wxALL, _border);
810 wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
811 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
812 wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
813 sizer->Add (export_settings, 0, wxLEFT, _border);
814 wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
815 sizer->Add (import_settings, 0, wxLEFT, _border);
816 wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
817 sizer->Add (decryption_advanced, 0, wxALL, _border);
819 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
820 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
821 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
822 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
825 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
826 m->SetFont (subheading_font);
827 sizer->Add (m, 0, wxALL, _border);
830 wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
831 sizer->Add (signing_advanced, 0, wxLEFT, _border);
832 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
836 KeysPage::decryption_advanced ()
838 CertificateChainEditor* c = new CertificateChainEditor (
839 _panel, _("Decrypting KDMs"), _border,
840 bind (&Config::set_decryption_chain, Config::instance (), _1),
841 bind (&Config::decryption_chain, Config::instance ()),
842 bind (&KeysPage::nag_alter_decryption_chain, this)
849 KeysPage::signing_advanced ()
851 CertificateChainEditor* c = new CertificateChainEditor (
852 _panel, _("Signing DCPs and KDMs"), _border,
853 bind (&Config::set_signer_chain, Config::instance (), _1),
854 bind (&Config::signer_chain, Config::instance ()),
862 KeysPage::export_decryption_chain_and_key ()
864 wxFileDialog* d = new wxFileDialog (
865 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
866 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
869 if (d->ShowModal () == wxID_OK) {
870 boost::filesystem::path path (wx_to_std(d->GetPath()));
871 FILE* f = fopen_boost (path, "w");
873 throw OpenFileError (path, errno, OpenFileError::WRITE);
876 string const chain = Config::instance()->decryption_chain()->chain();
877 checked_fwrite (chain.c_str(), chain.length(), f, path);
878 optional<string> const key = Config::instance()->decryption_chain()->key();
879 DCPOMATIC_ASSERT (key);
880 checked_fwrite (key->c_str(), key->length(), f, path);
888 KeysPage::import_decryption_chain_and_key ()
890 if (NagDialog::maybe_nag (
892 Config::NAG_IMPORT_DECRYPTION_CHAIN,
893 _("If you continue with this operation you will no longer be able to use any DKDMs that you have created with the current certificates and key. Also, any KDMs that have been sent to you for those certificates will become useless. Proceed with caution!"),
899 wxFileDialog* d = new wxFileDialog (
900 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
903 if (d->ShowModal () == wxID_OK) {
904 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
906 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
908 throw OpenFileError (wx_to_std (d->GetPath ()), errno, OpenFileError::WRITE);
914 if (fgets (buffer, 128, f) == 0) {
918 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
919 new_chain->add (dcp::Certificate (current));
921 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
922 new_chain->set_key (current);
928 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
929 Config::instance()->set_decryption_chain (new_chain);
931 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
938 KeysPage::nag_alter_decryption_chain ()
940 return NagDialog::maybe_nag (
942 Config::NAG_ALTER_DECRYPTION_CHAIN,
943 _("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!"),
949 KeysPage::export_decryption_certificate ()
951 wxFileDialog* d = new wxFileDialog (
952 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
953 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
956 if (d->ShowModal () == wxID_OK) {
957 boost::filesystem::path path (wx_to_std(d->GetPath()));
958 FILE* f = fopen_boost (path, "w");
960 throw OpenFileError (path, errno, OpenFileError::WRITE);
963 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
964 checked_fwrite (s.c_str(), s.length(), f, path);