2 Copyright (C) 2012-2018 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"
33 using boost::optional;
34 using boost::shared_ptr;
35 using boost::function;
44 Page::Page (wxSize panel_size, int border)
47 , _panel_size (panel_size)
48 , _window_exists (false)
50 _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
54 Page::create_window (wxWindow* parent)
56 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
57 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
61 _window_exists = true;
64 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
70 Page::config_changed_wrapper ()
78 Page::window_destroyed ()
80 _window_exists = false;
84 StockPage::StockPage (Kind kind, wxSize panel_size, int border)
85 : wxStockPreferencesPage (kind)
86 , Page (panel_size, border)
92 StockPage::CreateWindow (wxWindow* parent)
94 return create_window (parent);
97 StandardPage::StandardPage (wxSize panel_size, int border)
98 : Page (panel_size, border)
104 StandardPage::CreateWindow (wxWindow* parent)
106 return create_window (parent);
109 GeneralPage::GeneralPage (wxSize panel_size, int border)
110 : StockPage (Kind_General, panel_size, border)
116 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
118 _set_language = new CheckBox (_panel, _("Set language"));
119 table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
120 _language = new wxChoice (_panel, wxID_ANY);
121 vector<pair<string, string> > languages;
122 languages.push_back (make_pair ("Čeština", "cs_CZ"));
123 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
124 languages.push_back (make_pair ("Dansk", "da_DK"));
125 languages.push_back (make_pair ("Deutsch", "de_DE"));
126 languages.push_back (make_pair ("English", "en_GB"));
127 languages.push_back (make_pair ("Español", "es_ES"));
128 languages.push_back (make_pair ("Français", "fr_FR"));
129 languages.push_back (make_pair ("Italiano", "it_IT"));
130 languages.push_back (make_pair ("Nederlands", "nl_NL"));
131 languages.push_back (make_pair ("Русский", "ru_RU"));
132 languages.push_back (make_pair ("Polski", "pl_PL"));
133 languages.push_back (make_pair ("Português europeu", "pt_PT"));
134 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
135 languages.push_back (make_pair ("Svenska", "sv_SE"));
136 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
137 languages.push_back (make_pair ("українська мова", "uk_UA"));
138 checked_set (_language, languages);
139 table->Add (_language, wxGBPosition (r, 1));
142 wxStaticText* restart = add_label_to_sizer (
143 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
145 wxFont font = restart->GetFont();
146 font.SetStyle (wxFONTSTYLE_ITALIC);
147 font.SetPointSize (font.GetPointSize() - 1);
148 restart->SetFont (font);
151 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
152 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
156 GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
158 _sound = new CheckBox (_panel, _("Play sound via"));
159 table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
160 _sound_output = new wxChoice (_panel, wxID_ANY);
161 table->Add (_sound_output, wxGBPosition (r, 1));
164 RtAudio audio (DCPOMATIC_RTAUDIO_API);
165 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
166 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
167 if (dev.probed && dev.outputChannels > 0) {
168 _sound_output->Append (std_to_wx (dev.name));
172 _sound->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::sound_changed, this));
173 _sound_output->Bind (wxEVT_CHOICE, bind (&GeneralPage::sound_output_changed, this));
177 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
179 _check_for_updates = new CheckBox (_panel, _("Check for updates on startup"));
180 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
183 _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
184 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
187 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
188 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
192 GeneralPage::config_changed ()
194 Config* config = Config::instance ();
196 checked_set (_set_language, static_cast<bool>(config->language()));
198 /* Backwards compatibility of config file */
200 map<string, string> compat_map;
201 compat_map["fr"] = "fr_FR";
202 compat_map["it"] = "it_IT";
203 compat_map["es"] = "es_ES";
204 compat_map["sv"] = "sv_SE";
205 compat_map["de"] = "de_DE";
206 compat_map["nl"] = "nl_NL";
207 compat_map["ru"] = "ru_RU";
208 compat_map["pl"] = "pl_PL";
209 compat_map["da"] = "da_DK";
210 compat_map["pt"] = "pt_PT";
211 compat_map["sk"] = "sk_SK";
212 compat_map["cs"] = "cs_CZ";
213 compat_map["uk"] = "uk_UA";
215 string lang = config->language().get_value_or ("en_GB");
216 if (compat_map.find (lang) != compat_map.end ()) {
217 lang = compat_map[lang];
220 checked_set (_language, lang);
222 checked_set (_check_for_updates, config->check_for_updates ());
223 checked_set (_check_for_test_updates, config->check_for_test_updates ());
225 checked_set (_sound, config->sound ());
227 optional<string> const current_so = get_sound_output ();
228 optional<string> configured_so;
230 if (config->sound_output()) {
231 configured_so = config->sound_output().get();
233 /* No configured output means we should use the default */
234 RtAudio audio (DCPOMATIC_RTAUDIO_API);
236 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
237 } catch (RtAudioError& e) {
238 /* Probably no audio devices at all */
242 if (configured_so && current_so != configured_so) {
243 /* Update _sound_output with the configured value */
245 while (i < _sound_output->GetCount()) {
246 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
247 _sound_output->SetSelection (i);
254 setup_sensitivity ();
258 GeneralPage::setup_sensitivity ()
260 _language->Enable (_set_language->GetValue ());
261 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
262 _sound_output->Enable (_sound->GetValue ());
265 /** @return Currently-selected preview sound output in the dialogue */
267 GeneralPage::get_sound_output ()
269 int const sel = _sound_output->GetSelection ();
270 if (sel == wxNOT_FOUND) {
271 return optional<string> ();
274 return wx_to_std (_sound_output->GetString (sel));
278 GeneralPage::set_language_changed ()
280 setup_sensitivity ();
281 if (_set_language->GetValue ()) {
284 Config::instance()->unset_language ();
289 GeneralPage::language_changed ()
291 int const sel = _language->GetSelection ();
293 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
295 Config::instance()->unset_language ();
300 GeneralPage::check_for_updates_changed ()
302 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
306 GeneralPage::check_for_test_updates_changed ()
308 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
312 GeneralPage::sound_changed ()
314 Config::instance()->set_sound (_sound->GetValue ());
318 GeneralPage::sound_output_changed ()
320 RtAudio audio (DCPOMATIC_RTAUDIO_API);
321 optional<string> const so = get_sound_output();
322 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
323 Config::instance()->unset_sound_output ();
325 Config::instance()->set_sound_output (*so);
329 CertificateChainEditor::CertificateChainEditor (
333 function<void (shared_ptr<dcp::CertificateChain>)> set,
334 function<shared_ptr<const dcp::CertificateChain> (void)> get,
335 function<bool (void)> nag_remake
337 : wxDialog (parent, wxID_ANY, title)
340 , _nag_remake (nag_remake)
342 wxFont subheading_font (*wxNORMAL_FONT);
343 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
345 _sizer = new wxBoxSizer (wxVERTICAL);
348 wxStaticText* m = new StaticText (this, title);
349 m->SetFont (subheading_font);
350 _sizer->Add (m, 0, wxALL, border);
353 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
354 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
356 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
361 ip.SetText (_("Type"));
363 _certificates->InsertColumn (0, ip);
369 ip.SetText (_("Thumbprint"));
372 wxFont font = ip.GetFont ();
373 font.SetFamily (wxFONTFAMILY_TELETYPE);
376 _certificates->InsertColumn (1, ip);
379 certificates_sizer->Add (_certificates, 1, wxEXPAND);
382 wxSizer* s = new wxBoxSizer (wxVERTICAL);
383 _add_certificate = new Button (this, _("Add..."));
384 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
385 _remove_certificate = new Button (this, _("Remove"));
386 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
387 _export_certificate = new Button (this, _("Export"));
388 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
389 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
392 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
393 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
396 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
397 _private_key = new StaticText (this, wxT(""));
398 wxFont font = _private_key->GetFont ();
399 font.SetFamily (wxFONTFAMILY_TELETYPE);
400 _private_key->SetFont (font);
401 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
402 _import_private_key = new Button (this, _("Import..."));
403 table->Add (_import_private_key, wxGBPosition (r, 2));
404 _export_private_key = new Button (this, _("Export..."));
405 table->Add (_export_private_key, wxGBPosition (r, 3));
408 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
409 _remake_certificates = new Button (this, _("Re-make certificates and key..."));
410 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
411 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
414 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
415 font = *wxSMALL_FONT;
416 font.SetWeight (wxFONTWEIGHT_BOLD);
417 _private_key_bad->SetFont (font);
418 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
421 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
422 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
423 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
424 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
425 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
426 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
427 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
428 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
430 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
432 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
435 SetSizerAndFit (_sizer);
437 update_certificate_list ();
438 update_private_key ();
439 update_sensitivity ();
443 CertificateChainEditor::add_button (wxWindow* button)
445 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
450 CertificateChainEditor::add_certificate ()
452 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
454 if (d->ShowModal() == wxID_OK) {
459 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
460 } catch (boost::filesystem::filesystem_error& e) {
461 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
466 if (!extra.empty ()) {
469 _("This file contains other certificates (or other data) after its first certificate. "
470 "Only the first certificate will be used.")
473 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
475 if (!chain->chain_valid ()) {
478 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
479 "Add certificates in order from root to intermediate to leaf.")
484 update_certificate_list ();
486 } catch (dcp::MiscError& e) {
487 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
493 update_sensitivity ();
497 CertificateChainEditor::remove_certificate ()
499 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
504 _certificates->DeleteItem (i);
505 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
509 update_sensitivity ();
513 CertificateChainEditor::export_certificate ()
515 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
520 wxFileDialog* d = new wxFileDialog (
521 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
522 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
525 dcp::CertificateChain::List all = _get()->root_to_leaf ();
526 dcp::CertificateChain::List::iterator j = all.begin ();
527 for (int k = 0; k < i; ++k) {
531 if (d->ShowModal () == wxID_OK) {
532 boost::filesystem::path path (wx_to_std(d->GetPath()));
533 FILE* f = fopen_boost (path, "w");
535 throw OpenFileError (path, errno, false);
538 string const s = j->certificate (true);
539 fwrite (s.c_str(), 1, s.length(), f);
546 CertificateChainEditor::update_certificate_list ()
548 _certificates->DeleteAllItems ();
550 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
551 BOOST_FOREACH (dcp::Certificate const & i, certs) {
554 _certificates->InsertItem (item);
555 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
558 _certificates->SetItem (n, 0, _("Root"));
559 } else if (n == (certs.size() - 1)) {
560 _certificates->SetItem (n, 0, _("Leaf"));
562 _certificates->SetItem (n, 0, _("Intermediate"));
568 static wxColour normal = _private_key_bad->GetForegroundColour ();
570 if (_get()->private_key_valid()) {
571 _private_key_bad->Hide ();
572 _private_key_bad->SetForegroundColour (normal);
574 _private_key_bad->Show ();
575 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
580 CertificateChainEditor::remake_certificates ()
582 shared_ptr<const dcp::CertificateChain> chain = _get();
584 string subject_organization_name;
585 string subject_organizational_unit_name;
586 string root_common_name;
587 string intermediate_common_name;
588 string leaf_common_name;
590 dcp::CertificateChain::List all = chain->root_to_leaf ();
592 if (all.size() >= 1) {
594 subject_organization_name = chain->root().subject_organization_name ();
595 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
596 root_common_name = chain->root().subject_common_name ();
599 if (all.size() >= 2) {
601 leaf_common_name = chain->leaf().subject_common_name ();
604 if (all.size() >= 3) {
605 /* Have an intermediate */
606 dcp::CertificateChain::List::iterator i = all.begin ();
608 intermediate_common_name = i->subject_common_name ();
612 /* Cancel was clicked */
616 MakeChainDialog* d = new MakeChainDialog (
618 subject_organization_name,
619 subject_organizational_unit_name,
621 intermediate_common_name,
625 if (d->ShowModal () == wxID_OK) {
627 shared_ptr<dcp::CertificateChain> (
628 new dcp::CertificateChain (
631 d->organisational_unit (),
632 d->root_common_name (),
633 d->intermediate_common_name (),
634 d->leaf_common_name ()
639 update_certificate_list ();
640 update_private_key ();
647 CertificateChainEditor::update_sensitivity ()
649 /* We can only remove the leaf certificate */
650 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
651 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
655 CertificateChainEditor::update_private_key ()
657 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
662 CertificateChainEditor::import_private_key ()
664 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
666 if (d->ShowModal() == wxID_OK) {
668 boost::filesystem::path p (wx_to_std (d->GetPath ()));
669 if (boost::filesystem::file_size (p) > 8192) {
672 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
677 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
678 chain->set_key (dcp::file_to_string (p));
680 update_private_key ();
681 } catch (dcp::MiscError& e) {
682 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
688 update_sensitivity ();
692 CertificateChainEditor::export_private_key ()
694 optional<string> key = _get()->key();
699 wxFileDialog* d = new wxFileDialog (
700 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
701 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
704 if (d->ShowModal () == wxID_OK) {
705 boost::filesystem::path path (wx_to_std(d->GetPath()));
706 FILE* f = fopen_boost (path, "w");
708 throw OpenFileError (path, errno, false);
711 string const s = _get()->key().get ();
712 fwrite (s.c_str(), 1, s.length(), f);
719 KeysPage::GetName () const
727 wxFont subheading_font (*wxNORMAL_FONT);
728 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
730 wxSizer* sizer = _panel->GetSizer();
733 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
734 m->SetFont (subheading_font);
735 sizer->Add (m, 0, wxALL, _border);
738 wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
739 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
740 wxButton* export_decryption_chain = new Button (_panel, _("Export KDM decryption chain..."));
741 sizer->Add (export_decryption_chain, 0, wxLEFT, _border);
742 wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
743 sizer->Add (export_settings, 0, wxLEFT, _border);
744 wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
745 sizer->Add (import_settings, 0, wxLEFT, _border);
746 wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
747 sizer->Add (decryption_advanced, 0, wxALL, _border);
749 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
750 export_decryption_chain->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain, this));
751 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
752 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
753 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
756 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
757 m->SetFont (subheading_font);
758 sizer->Add (m, 0, wxALL, _border);
761 wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
762 sizer->Add (signing_advanced, 0, wxLEFT, _border);
763 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
767 KeysPage::decryption_advanced ()
769 CertificateChainEditor* c = new CertificateChainEditor (
770 _panel, _("Decrypting KDMs"), _border,
771 bind (&Config::set_decryption_chain, Config::instance (), _1),
772 bind (&Config::decryption_chain, Config::instance ()),
773 bind (&KeysPage::nag_remake_decryption_chain, this)
780 KeysPage::signing_advanced ()
782 CertificateChainEditor* c = new CertificateChainEditor (
783 _panel, _("Signing DCPs and KDMs"), _border,
784 bind (&Config::set_signer_chain, Config::instance (), _1),
785 bind (&Config::signer_chain, Config::instance ()),
793 KeysPage::export_decryption_chain_and_key ()
795 wxFileDialog* d = new wxFileDialog (
796 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
797 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
800 if (d->ShowModal () == wxID_OK) {
801 boost::filesystem::path path (wx_to_std(d->GetPath()));
802 FILE* f = fopen_boost (path, "w");
804 throw OpenFileError (path, errno, false);
807 string const chain = Config::instance()->decryption_chain()->chain();
808 fwrite (chain.c_str(), 1, chain.length(), f);
809 optional<string> const key = Config::instance()->decryption_chain()->key();
810 DCPOMATIC_ASSERT (key);
811 fwrite (key->c_str(), 1, key->length(), f);
819 KeysPage::import_decryption_chain_and_key ()
821 wxFileDialog* d = new wxFileDialog (
822 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
825 if (d->ShowModal () == wxID_OK) {
826 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
828 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
830 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
836 if (fgets (buffer, 128, f) == 0) {
840 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
841 new_chain->add (dcp::Certificate (current));
843 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
844 new_chain->set_key (current);
850 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
851 Config::instance()->set_decryption_chain (new_chain);
853 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
860 KeysPage::nag_remake_decryption_chain ()
862 return NagDialog::maybe_nag (
864 Config::NAG_REMAKE_DECRYPTION_CHAIN,
865 _("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!"),
871 KeysPage::export_decryption_chain ()
873 wxFileDialog* d = new wxFileDialog (
874 _panel, _("Select Chain File"), wxEmptyString, _("dcpomatic_kdm_decryption_chain.pem"), wxT ("PEM files (*.pem)|*.pem"),
875 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
878 if (d->ShowModal () == wxID_OK) {
879 boost::filesystem::path path (wx_to_std(d->GetPath()));
880 FILE* f = fopen_boost (path, "w");
882 throw OpenFileError (path, errno, false);
885 string const s = Config::instance()->decryption_chain()->chain();
886 fwrite (s.c_str(), 1, s.length(), f);
893 KeysPage::export_decryption_certificate ()
895 wxFileDialog* d = new wxFileDialog (
896 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
897 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
900 if (d->ShowModal () == wxID_OK) {
901 boost::filesystem::path path (wx_to_std(d->GetPath()));
902 FILE* f = fopen_boost (path, "w");
904 throw OpenFileError (path, errno, false);
907 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
908 fwrite (s.c_str(), 1, s.length(), f);