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;
45 Page::Page (wxSize panel_size, int border)
48 , _panel_size (panel_size)
49 , _window_exists (false)
51 _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
55 Page::create_window (wxWindow* parent)
57 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
58 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
62 _window_exists = true;
65 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
71 Page::config_changed_wrapper ()
79 Page::window_destroyed ()
81 _window_exists = false;
85 StockPage::StockPage (Kind kind, wxSize panel_size, int border)
86 : wxStockPreferencesPage (kind)
87 , Page (panel_size, border)
93 StockPage::CreateWindow (wxWindow* parent)
95 return create_window (parent);
98 StandardPage::StandardPage (wxSize panel_size, int border)
99 : Page (panel_size, border)
105 StandardPage::CreateWindow (wxWindow* parent)
107 return create_window (parent);
110 GeneralPage::GeneralPage (wxSize panel_size, int border)
111 : StockPage (Kind_General, panel_size, border)
117 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
119 _set_language = new CheckBox (_panel, _("Set language"));
120 table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
121 _language = new wxChoice (_panel, wxID_ANY);
122 vector<pair<string, string> > languages;
123 languages.push_back (make_pair ("Čeština", "cs_CZ"));
124 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
125 languages.push_back (make_pair ("Dansk", "da_DK"));
126 languages.push_back (make_pair ("Deutsch", "de_DE"));
127 languages.push_back (make_pair ("English", "en_GB"));
128 languages.push_back (make_pair ("Español", "es_ES"));
129 languages.push_back (make_pair ("Français", "fr_FR"));
130 languages.push_back (make_pair ("Italiano", "it_IT"));
131 languages.push_back (make_pair ("Nederlands", "nl_NL"));
132 languages.push_back (make_pair ("Русский", "ru_RU"));
133 languages.push_back (make_pair ("Polski", "pl_PL"));
134 languages.push_back (make_pair ("Português europeu", "pt_PT"));
135 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
136 languages.push_back (make_pair ("Svenska", "sv_SE"));
137 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
138 languages.push_back (make_pair ("Türkçe", "tr_TR"));
139 languages.push_back (make_pair ("українська мова", "uk_UA"));
140 checked_set (_language, languages);
141 table->Add (_language, wxGBPosition (r, 1));
144 wxStaticText* restart = add_label_to_sizer (
145 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
147 wxFont font = restart->GetFont();
148 font.SetStyle (wxFONTSTYLE_ITALIC);
149 font.SetPointSize (font.GetPointSize() - 1);
150 restart->SetFont (font);
153 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
154 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
158 GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
160 _sound = new CheckBox (_panel, _("Play sound via"));
161 table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
162 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
163 _sound_output = new wxChoice (_panel, wxID_ANY);
164 s->Add (_sound_output, 0);
165 _sound_output_details = new wxStaticText (_panel, wxID_ANY, wxT(""));
166 s->Add (_sound_output_details, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP);
167 table->Add (s, wxGBPosition(r, 1));
170 wxFont font = _sound_output_details->GetFont();
171 font.SetStyle (wxFONTSTYLE_ITALIC);
172 font.SetPointSize (font.GetPointSize() - 1);
173 _sound_output_details->SetFont (font);
175 RtAudio audio (DCPOMATIC_RTAUDIO_API);
176 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
177 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
178 if (dev.probed && dev.outputChannels > 0) {
179 _sound_output->Append (std_to_wx (dev.name));
183 _sound->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::sound_changed, this));
184 _sound_output->Bind (wxEVT_CHOICE, bind (&GeneralPage::sound_output_changed, this));
188 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
190 _check_for_updates = new CheckBox (_panel, _("Check for updates on startup"));
191 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
194 _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
195 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
198 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
199 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
203 GeneralPage::config_changed ()
205 Config* config = Config::instance ();
207 checked_set (_set_language, static_cast<bool>(config->language()));
209 /* Backwards compatibility of config file */
211 map<string, string> compat_map;
212 compat_map["fr"] = "fr_FR";
213 compat_map["it"] = "it_IT";
214 compat_map["es"] = "es_ES";
215 compat_map["sv"] = "sv_SE";
216 compat_map["de"] = "de_DE";
217 compat_map["nl"] = "nl_NL";
218 compat_map["ru"] = "ru_RU";
219 compat_map["pl"] = "pl_PL";
220 compat_map["da"] = "da_DK";
221 compat_map["pt"] = "pt_PT";
222 compat_map["sk"] = "sk_SK";
223 compat_map["cs"] = "cs_CZ";
224 compat_map["uk"] = "uk_UA";
226 string lang = config->language().get_value_or ("en_GB");
227 if (compat_map.find (lang) != compat_map.end ()) {
228 lang = compat_map[lang];
231 checked_set (_language, lang);
233 checked_set (_check_for_updates, config->check_for_updates ());
234 checked_set (_check_for_test_updates, config->check_for_test_updates ());
236 checked_set (_sound, config->sound ());
238 optional<string> const current_so = get_sound_output ();
239 optional<string> configured_so;
241 if (config->sound_output()) {
242 configured_so = config->sound_output().get();
244 /* No configured output means we should use the default */
245 RtAudio audio (DCPOMATIC_RTAUDIO_API);
247 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
248 } catch (RtAudioError& e) {
249 /* Probably no audio devices at all */
253 if (configured_so && current_so != configured_so) {
254 /* Update _sound_output with the configured value */
256 while (i < _sound_output->GetCount()) {
257 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
258 _sound_output->SetSelection (i);
265 RtAudio audio (DCPOMATIC_RTAUDIO_API);
267 map<int, wxString> apis;
268 apis[RtAudio::MACOSX_CORE] = _("CoreAudio");
269 apis[RtAudio::WINDOWS_ASIO] = _("ASIO");
270 apis[RtAudio::WINDOWS_DS] = _("Direct Sound");
271 apis[RtAudio::WINDOWS_WASAPI] = _("WASAPI");
272 apis[RtAudio::UNIX_JACK] = _("JACK");
273 apis[RtAudio::LINUX_ALSA] = _("ALSA");
274 apis[RtAudio::LINUX_PULSE] = _("PulseAudio");
275 apis[RtAudio::LINUX_OSS] = _("OSS");
276 apis[RtAudio::RTAUDIO_DUMMY] = _("Dummy");
280 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
281 RtAudio::DeviceInfo info = audio.getDeviceInfo(i);
282 if (info.name == *configured_so && info.outputChannels > 0) {
283 channels = info.outputChannels;
288 _sound_output_details->SetLabel (
289 wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()])
292 setup_sensitivity ();
296 GeneralPage::setup_sensitivity ()
298 _language->Enable (_set_language->GetValue ());
299 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
300 _sound_output->Enable (_sound->GetValue ());
303 /** @return Currently-selected preview sound output in the dialogue */
305 GeneralPage::get_sound_output ()
307 int const sel = _sound_output->GetSelection ();
308 if (sel == wxNOT_FOUND) {
309 return optional<string> ();
312 return wx_to_std (_sound_output->GetString (sel));
316 GeneralPage::set_language_changed ()
318 setup_sensitivity ();
319 if (_set_language->GetValue ()) {
322 Config::instance()->unset_language ();
327 GeneralPage::language_changed ()
329 int const sel = _language->GetSelection ();
331 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
333 Config::instance()->unset_language ();
338 GeneralPage::check_for_updates_changed ()
340 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
344 GeneralPage::check_for_test_updates_changed ()
346 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
350 GeneralPage::sound_changed ()
352 Config::instance()->set_sound (_sound->GetValue ());
356 GeneralPage::sound_output_changed ()
358 RtAudio audio (DCPOMATIC_RTAUDIO_API);
359 optional<string> const so = get_sound_output();
360 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
361 Config::instance()->unset_sound_output ();
363 Config::instance()->set_sound_output (*so);
367 CertificateChainEditor::CertificateChainEditor (
371 function<void (shared_ptr<dcp::CertificateChain>)> set,
372 function<shared_ptr<const dcp::CertificateChain> (void)> get,
373 function<bool (void)> nag_alter
375 : wxDialog (parent, wxID_ANY, title)
378 , _nag_alter (nag_alter)
380 wxFont subheading_font (*wxNORMAL_FONT);
381 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
383 _sizer = new wxBoxSizer (wxVERTICAL);
386 wxStaticText* m = new StaticText (this, title);
387 m->SetFont (subheading_font);
388 _sizer->Add (m, 0, wxALL, border);
391 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
392 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
394 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
399 ip.SetText (_("Type"));
401 _certificates->InsertColumn (0, ip);
407 ip.SetText (_("Thumbprint"));
410 wxFont font = ip.GetFont ();
411 font.SetFamily (wxFONTFAMILY_TELETYPE);
414 _certificates->InsertColumn (1, ip);
417 certificates_sizer->Add (_certificates, 1, wxEXPAND);
420 wxSizer* s = new wxBoxSizer (wxVERTICAL);
421 _add_certificate = new Button (this, _("Add..."));
422 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
423 _remove_certificate = new Button (this, _("Remove"));
424 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
425 _export_certificate = new Button (this, _("Export"));
426 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
427 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
430 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
431 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
434 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
435 _private_key = new StaticText (this, wxT(""));
436 wxFont font = _private_key->GetFont ();
437 font.SetFamily (wxFONTFAMILY_TELETYPE);
438 _private_key->SetFont (font);
439 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
440 _import_private_key = new Button (this, _("Import..."));
441 table->Add (_import_private_key, wxGBPosition (r, 2));
442 _export_private_key = new Button (this, _("Export..."));
443 table->Add (_export_private_key, wxGBPosition (r, 3));
446 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
447 _remake_certificates = new Button (this, _("Re-make certificates and key..."));
448 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
449 _export_chain = new Button (this, _("Export chain..."));
450 _button_sizer->Add (_export_chain, 1, wxRIGHT, border);
451 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
454 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
455 font = *wxSMALL_FONT;
456 font.SetWeight (wxFONTWEIGHT_BOLD);
457 _private_key_bad->SetFont (font);
458 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
461 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
462 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
463 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
464 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
465 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
466 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
467 _export_chain->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_chain, this));
468 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
469 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
471 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
473 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
476 SetSizerAndFit (_sizer);
478 update_certificate_list ();
479 update_private_key ();
480 update_sensitivity ();
484 CertificateChainEditor::add_button (wxWindow* button)
486 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
491 CertificateChainEditor::add_certificate ()
493 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
495 if (d->ShowModal() == wxID_OK) {
500 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
501 } catch (boost::filesystem::filesystem_error& e) {
502 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
507 if (!extra.empty ()) {
510 _("This file contains other certificates (or other data) after its first certificate. "
511 "Only the first certificate will be used.")
514 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
516 if (!chain->chain_valid ()) {
519 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
520 "Add certificates in order from root to intermediate to leaf.")
525 update_certificate_list ();
527 } catch (dcp::MiscError& e) {
528 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
534 update_sensitivity ();
538 CertificateChainEditor::remove_certificate ()
541 /* Cancel was clicked */
545 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
550 _certificates->DeleteItem (i);
551 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
555 update_sensitivity ();
556 update_certificate_list ();
560 CertificateChainEditor::export_certificate ()
562 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
567 wxFileDialog* d = new wxFileDialog (
568 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
569 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
572 dcp::CertificateChain::List all = _get()->root_to_leaf ();
573 dcp::CertificateChain::List::iterator j = all.begin ();
574 for (int k = 0; k < i; ++k) {
578 if (d->ShowModal () == wxID_OK) {
579 boost::filesystem::path path (wx_to_std(d->GetPath()));
580 FILE* f = fopen_boost (path, "w");
582 throw OpenFileError (path, errno, false);
585 string const s = j->certificate (true);
586 checked_fwrite (s.c_str(), s.length(), f, path);
593 CertificateChainEditor::export_chain ()
595 wxFileDialog* d = new wxFileDialog (
596 this, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT("PEM files (*.pem)|*.pem"),
597 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
600 if (d->ShowModal () == wxID_OK) {
601 boost::filesystem::path path (wx_to_std(d->GetPath()));
602 FILE* f = fopen_boost (path, "w");
604 throw OpenFileError (path, errno, false);
607 string const s = _get()->chain();
608 checked_fwrite (s.c_str(), s.length(), f, path);
616 CertificateChainEditor::update_certificate_list ()
618 _certificates->DeleteAllItems ();
620 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
621 BOOST_FOREACH (dcp::Certificate const & i, certs) {
624 _certificates->InsertItem (item);
625 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
628 _certificates->SetItem (n, 0, _("Root"));
629 } else if (n == (certs.size() - 1)) {
630 _certificates->SetItem (n, 0, _("Leaf"));
632 _certificates->SetItem (n, 0, _("Intermediate"));
638 static wxColour normal = _private_key_bad->GetForegroundColour ();
640 if (_get()->private_key_valid()) {
641 _private_key_bad->Hide ();
642 _private_key_bad->SetForegroundColour (normal);
644 _private_key_bad->Show ();
645 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
650 CertificateChainEditor::remake_certificates ()
652 shared_ptr<const dcp::CertificateChain> chain = _get();
654 string subject_organization_name;
655 string subject_organizational_unit_name;
656 string root_common_name;
657 string intermediate_common_name;
658 string leaf_common_name;
660 dcp::CertificateChain::List all = chain->root_to_leaf ();
662 if (all.size() >= 1) {
664 subject_organization_name = chain->root().subject_organization_name ();
665 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
666 root_common_name = chain->root().subject_common_name ();
669 if (all.size() >= 2) {
671 leaf_common_name = chain->leaf().subject_common_name ();
674 if (all.size() >= 3) {
675 /* Have an intermediate */
676 dcp::CertificateChain::List::iterator i = all.begin ();
678 intermediate_common_name = i->subject_common_name ();
682 /* Cancel was clicked */
686 MakeChainDialog* d = new MakeChainDialog (
688 subject_organization_name,
689 subject_organizational_unit_name,
691 intermediate_common_name,
695 if (d->ShowModal () == wxID_OK) {
697 shared_ptr<dcp::CertificateChain> (
698 new dcp::CertificateChain (
701 d->organisational_unit (),
702 d->root_common_name (),
703 d->intermediate_common_name (),
704 d->leaf_common_name ()
709 update_certificate_list ();
710 update_private_key ();
717 CertificateChainEditor::update_sensitivity ()
719 /* We can only remove the leaf certificate */
720 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
721 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
725 CertificateChainEditor::update_private_key ()
727 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
732 CertificateChainEditor::import_private_key ()
734 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
736 if (d->ShowModal() == wxID_OK) {
738 boost::filesystem::path p (wx_to_std (d->GetPath ()));
739 if (boost::filesystem::file_size (p) > 8192) {
742 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
747 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
748 chain->set_key (dcp::file_to_string (p));
750 update_private_key ();
751 } catch (dcp::MiscError& e) {
752 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
758 update_sensitivity ();
762 CertificateChainEditor::export_private_key ()
764 optional<string> key = _get()->key();
769 wxFileDialog* d = new wxFileDialog (
770 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
771 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
774 if (d->ShowModal () == wxID_OK) {
775 boost::filesystem::path path (wx_to_std(d->GetPath()));
776 FILE* f = fopen_boost (path, "w");
778 throw OpenFileError (path, errno, false);
781 string const s = _get()->key().get ();
782 checked_fwrite (s.c_str(), s.length(), f, path);
789 KeysPage::GetName () const
797 wxFont subheading_font (*wxNORMAL_FONT);
798 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
800 wxSizer* sizer = _panel->GetSizer();
803 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
804 m->SetFont (subheading_font);
805 sizer->Add (m, 0, wxALL, _border);
808 wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
809 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
810 wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
811 sizer->Add (export_settings, 0, wxLEFT, _border);
812 wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
813 sizer->Add (import_settings, 0, wxLEFT, _border);
814 wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
815 sizer->Add (decryption_advanced, 0, wxALL, _border);
817 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
818 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
819 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
820 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
823 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
824 m->SetFont (subheading_font);
825 sizer->Add (m, 0, wxALL, _border);
828 wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
829 sizer->Add (signing_advanced, 0, wxLEFT, _border);
830 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
834 KeysPage::decryption_advanced ()
836 CertificateChainEditor* c = new CertificateChainEditor (
837 _panel, _("Decrypting KDMs"), _border,
838 bind (&Config::set_decryption_chain, Config::instance (), _1),
839 bind (&Config::decryption_chain, Config::instance ()),
840 bind (&KeysPage::nag_alter_decryption_chain, this)
847 KeysPage::signing_advanced ()
849 CertificateChainEditor* c = new CertificateChainEditor (
850 _panel, _("Signing DCPs and KDMs"), _border,
851 bind (&Config::set_signer_chain, Config::instance (), _1),
852 bind (&Config::signer_chain, Config::instance ()),
860 KeysPage::export_decryption_chain_and_key ()
862 wxFileDialog* d = new wxFileDialog (
863 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
864 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
867 if (d->ShowModal () == wxID_OK) {
868 boost::filesystem::path path (wx_to_std(d->GetPath()));
869 FILE* f = fopen_boost (path, "w");
871 throw OpenFileError (path, errno, false);
874 string const chain = Config::instance()->decryption_chain()->chain();
875 checked_fwrite (chain.c_str(), chain.length(), f, path);
876 optional<string> const key = Config::instance()->decryption_chain()->key();
877 DCPOMATIC_ASSERT (key);
878 checked_fwrite (key->c_str(), key->length(), f, path);
886 KeysPage::import_decryption_chain_and_key ()
888 if (NagDialog::maybe_nag (
890 Config::NAG_IMPORT_DECRYPTION_CHAIN,
891 _("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!"),
897 wxFileDialog* d = new wxFileDialog (
898 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
901 if (d->ShowModal () == wxID_OK) {
902 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
904 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
906 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
912 if (fgets (buffer, 128, f) == 0) {
916 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
917 new_chain->add (dcp::Certificate (current));
919 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
920 new_chain->set_key (current);
926 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
927 Config::instance()->set_decryption_chain (new_chain);
929 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
936 KeysPage::nag_alter_decryption_chain ()
938 return NagDialog::maybe_nag (
940 Config::NAG_ALTER_DECRYPTION_CHAIN,
941 _("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!"),
947 KeysPage::export_decryption_certificate ()
949 wxFileDialog* d = new wxFileDialog (
950 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
951 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
954 if (d->ShowModal () == wxID_OK) {
955 boost::filesystem::path path (wx_to_std(d->GetPath()));
956 FILE* f = fopen_boost (path, "w");
958 throw OpenFileError (path, errno, false);
961 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
962 checked_fwrite (s.c_str(), s.length(), f, path);