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 _export_chain = new Button (this, _("Export chain..."));
412 _button_sizer->Add (_export_chain, 1, wxRIGHT, border);
413 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
416 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
417 font = *wxSMALL_FONT;
418 font.SetWeight (wxFONTWEIGHT_BOLD);
419 _private_key_bad->SetFont (font);
420 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
423 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
424 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
425 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
426 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
427 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
428 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
429 _export_chain->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_chain, this));
430 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
431 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
433 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
435 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
438 SetSizerAndFit (_sizer);
440 update_certificate_list ();
441 update_private_key ();
442 update_sensitivity ();
446 CertificateChainEditor::add_button (wxWindow* button)
448 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
453 CertificateChainEditor::add_certificate ()
455 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
457 if (d->ShowModal() == wxID_OK) {
462 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
463 } catch (boost::filesystem::filesystem_error& e) {
464 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
469 if (!extra.empty ()) {
472 _("This file contains other certificates (or other data) after its first certificate. "
473 "Only the first certificate will be used.")
476 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
478 if (!chain->chain_valid ()) {
481 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
482 "Add certificates in order from root to intermediate to leaf.")
487 update_certificate_list ();
489 } catch (dcp::MiscError& e) {
490 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
496 update_sensitivity ();
500 CertificateChainEditor::remove_certificate ()
502 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
507 _certificates->DeleteItem (i);
508 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
512 update_sensitivity ();
516 CertificateChainEditor::export_certificate ()
518 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
523 wxFileDialog* d = new wxFileDialog (
524 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
525 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
528 dcp::CertificateChain::List all = _get()->root_to_leaf ();
529 dcp::CertificateChain::List::iterator j = all.begin ();
530 for (int k = 0; k < i; ++k) {
534 if (d->ShowModal () == wxID_OK) {
535 boost::filesystem::path path (wx_to_std(d->GetPath()));
536 FILE* f = fopen_boost (path, "w");
538 throw OpenFileError (path, errno, false);
541 string const s = j->certificate (true);
542 checked_fwrite (s.c_str(), s.length(), f, path);
549 CertificateChainEditor::export_chain ()
551 wxFileDialog* d = new wxFileDialog (
552 this, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT("PEM files (*.pem)|*.pem"),
553 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
556 if (d->ShowModal () == wxID_OK) {
557 boost::filesystem::path path (wx_to_std(d->GetPath()));
558 FILE* f = fopen_boost (path, "w");
560 throw OpenFileError (path, errno, false);
563 string const s = _get()->chain();
564 checked_fwrite (s.c_str(), s.length(), f, path);
572 CertificateChainEditor::update_certificate_list ()
574 _certificates->DeleteAllItems ();
576 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
577 BOOST_FOREACH (dcp::Certificate const & i, certs) {
580 _certificates->InsertItem (item);
581 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
584 _certificates->SetItem (n, 0, _("Root"));
585 } else if (n == (certs.size() - 1)) {
586 _certificates->SetItem (n, 0, _("Leaf"));
588 _certificates->SetItem (n, 0, _("Intermediate"));
594 static wxColour normal = _private_key_bad->GetForegroundColour ();
596 if (_get()->private_key_valid()) {
597 _private_key_bad->Hide ();
598 _private_key_bad->SetForegroundColour (normal);
600 _private_key_bad->Show ();
601 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
606 CertificateChainEditor::remake_certificates ()
608 shared_ptr<const dcp::CertificateChain> chain = _get();
610 string subject_organization_name;
611 string subject_organizational_unit_name;
612 string root_common_name;
613 string intermediate_common_name;
614 string leaf_common_name;
616 dcp::CertificateChain::List all = chain->root_to_leaf ();
618 if (all.size() >= 1) {
620 subject_organization_name = chain->root().subject_organization_name ();
621 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
622 root_common_name = chain->root().subject_common_name ();
625 if (all.size() >= 2) {
627 leaf_common_name = chain->leaf().subject_common_name ();
630 if (all.size() >= 3) {
631 /* Have an intermediate */
632 dcp::CertificateChain::List::iterator i = all.begin ();
634 intermediate_common_name = i->subject_common_name ();
638 /* Cancel was clicked */
642 MakeChainDialog* d = new MakeChainDialog (
644 subject_organization_name,
645 subject_organizational_unit_name,
647 intermediate_common_name,
651 if (d->ShowModal () == wxID_OK) {
653 shared_ptr<dcp::CertificateChain> (
654 new dcp::CertificateChain (
657 d->organisational_unit (),
658 d->root_common_name (),
659 d->intermediate_common_name (),
660 d->leaf_common_name ()
665 update_certificate_list ();
666 update_private_key ();
673 CertificateChainEditor::update_sensitivity ()
675 /* We can only remove the leaf certificate */
676 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
677 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
681 CertificateChainEditor::update_private_key ()
683 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
688 CertificateChainEditor::import_private_key ()
690 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
692 if (d->ShowModal() == wxID_OK) {
694 boost::filesystem::path p (wx_to_std (d->GetPath ()));
695 if (boost::filesystem::file_size (p) > 8192) {
698 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
703 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
704 chain->set_key (dcp::file_to_string (p));
706 update_private_key ();
707 } catch (dcp::MiscError& e) {
708 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
714 update_sensitivity ();
718 CertificateChainEditor::export_private_key ()
720 optional<string> key = _get()->key();
725 wxFileDialog* d = new wxFileDialog (
726 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
727 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
730 if (d->ShowModal () == wxID_OK) {
731 boost::filesystem::path path (wx_to_std(d->GetPath()));
732 FILE* f = fopen_boost (path, "w");
734 throw OpenFileError (path, errno, false);
737 string const s = _get()->key().get ();
738 checked_fwrite (s.c_str(), s.length(), f, path);
745 KeysPage::GetName () const
753 wxFont subheading_font (*wxNORMAL_FONT);
754 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
756 wxSizer* sizer = _panel->GetSizer();
759 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
760 m->SetFont (subheading_font);
761 sizer->Add (m, 0, wxALL, _border);
764 wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
765 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
766 wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
767 sizer->Add (export_settings, 0, wxLEFT, _border);
768 wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
769 sizer->Add (import_settings, 0, wxLEFT, _border);
770 wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
771 sizer->Add (decryption_advanced, 0, wxALL, _border);
773 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
774 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
775 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
776 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
779 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
780 m->SetFont (subheading_font);
781 sizer->Add (m, 0, wxALL, _border);
784 wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
785 sizer->Add (signing_advanced, 0, wxLEFT, _border);
786 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
790 KeysPage::decryption_advanced ()
792 CertificateChainEditor* c = new CertificateChainEditor (
793 _panel, _("Decrypting KDMs"), _border,
794 bind (&Config::set_decryption_chain, Config::instance (), _1),
795 bind (&Config::decryption_chain, Config::instance ()),
796 bind (&KeysPage::nag_remake_decryption_chain, this)
803 KeysPage::signing_advanced ()
805 CertificateChainEditor* c = new CertificateChainEditor (
806 _panel, _("Signing DCPs and KDMs"), _border,
807 bind (&Config::set_signer_chain, Config::instance (), _1),
808 bind (&Config::signer_chain, Config::instance ()),
816 KeysPage::export_decryption_chain_and_key ()
818 wxFileDialog* d = new wxFileDialog (
819 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
820 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
823 if (d->ShowModal () == wxID_OK) {
824 boost::filesystem::path path (wx_to_std(d->GetPath()));
825 FILE* f = fopen_boost (path, "w");
827 throw OpenFileError (path, errno, false);
830 string const chain = Config::instance()->decryption_chain()->chain();
831 checked_fwrite (chain.c_str(), chain.length(), f, path);
832 optional<string> const key = Config::instance()->decryption_chain()->key();
833 DCPOMATIC_ASSERT (key);
834 checked_fwrite (key->c_str(), key->length(), f, path);
842 KeysPage::import_decryption_chain_and_key ()
844 if (NagDialog::maybe_nag (
846 Config::NAG_IMPORT_DECRYPTION_CHAIN,
847 _("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!"),
853 wxFileDialog* d = new wxFileDialog (
854 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
857 if (d->ShowModal () == wxID_OK) {
858 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
860 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
862 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
868 if (fgets (buffer, 128, f) == 0) {
872 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
873 new_chain->add (dcp::Certificate (current));
875 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
876 new_chain->set_key (current);
882 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
883 Config::instance()->set_decryption_chain (new_chain);
885 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
892 KeysPage::nag_remake_decryption_chain ()
894 return NagDialog::maybe_nag (
896 Config::NAG_REMAKE_DECRYPTION_CHAIN,
897 _("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!"),
903 KeysPage::export_decryption_certificate ()
905 wxFileDialog* d = new wxFileDialog (
906 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
907 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
910 if (d->ShowModal () == wxID_OK) {
911 boost::filesystem::path path (wx_to_std(d->GetPath()));
912 FILE* f = fopen_boost (path, "w");
914 throw OpenFileError (path, errno, false);
917 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
918 checked_fwrite (s.c_str(), s.length(), f, path);