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 "nag_dialog.h"
30 using boost::optional;
31 using boost::shared_ptr;
32 using boost::function;
41 Page::Page (wxSize panel_size, int border)
44 , _panel_size (panel_size)
45 , _window_exists (false)
47 _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
51 Page::create_window (wxWindow* parent)
53 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
54 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
58 _window_exists = true;
61 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
67 Page::config_changed_wrapper ()
75 Page::window_destroyed ()
77 _window_exists = false;
81 StockPage::StockPage (Kind kind, wxSize panel_size, int border)
82 : wxStockPreferencesPage (kind)
83 , Page (panel_size, border)
89 StockPage::CreateWindow (wxWindow* parent)
91 return create_window (parent);
94 StandardPage::StandardPage (wxSize panel_size, int border)
95 : Page (panel_size, border)
101 StandardPage::CreateWindow (wxWindow* parent)
103 return create_window (parent);
106 GeneralPage::GeneralPage (wxSize panel_size, int border)
107 : StockPage (Kind_General, panel_size, border)
113 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
115 _set_language = new wxCheckBox (_panel, wxID_ANY, _("Set language"));
116 table->Add (_set_language, wxGBPosition (r, 0));
117 _language = new wxChoice (_panel, wxID_ANY);
118 vector<pair<string, string> > languages;
119 languages.push_back (make_pair ("Čeština", "cs_CZ"));
120 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
121 languages.push_back (make_pair ("Dansk", "da_DK"));
122 languages.push_back (make_pair ("Deutsch", "de_DE"));
123 languages.push_back (make_pair ("English", "en_GB"));
124 languages.push_back (make_pair ("Español", "es_ES"));
125 languages.push_back (make_pair ("Français", "fr_FR"));
126 languages.push_back (make_pair ("Italiano", "it_IT"));
127 languages.push_back (make_pair ("Nederlands", "nl_NL"));
128 languages.push_back (make_pair ("Русский", "ru_RU"));
129 languages.push_back (make_pair ("Polski", "pl_PL"));
130 languages.push_back (make_pair ("Português europeu", "pt_PT"));
131 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
132 languages.push_back (make_pair ("Svenska", "sv_SE"));
133 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
134 languages.push_back (make_pair ("українська мова", "uk_UA"));
135 checked_set (_language, languages);
136 table->Add (_language, wxGBPosition (r, 1));
139 wxStaticText* restart = add_label_to_sizer (
140 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
142 wxFont font = restart->GetFont();
143 font.SetStyle (wxFONTSTYLE_ITALIC);
144 font.SetPointSize (font.GetPointSize() - 1);
145 restart->SetFont (font);
148 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
149 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
153 GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
155 _sound = new wxCheckBox (_panel, wxID_ANY, _("Play sound via"));
156 table->Add (_sound, wxGBPosition (r, 0));
157 _sound_output = new wxChoice (_panel, wxID_ANY);
158 table->Add (_sound_output, wxGBPosition (r, 1));
161 RtAudio audio (DCPOMATIC_RTAUDIO_API);
162 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
163 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
164 if (dev.probed && dev.outputChannels > 0) {
165 _sound_output->Append (std_to_wx (dev.name));
169 _sound->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::sound_changed, this));
170 _sound_output->Bind (wxEVT_CHOICE, bind (&GeneralPage::sound_output_changed, this));
174 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
176 _check_for_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for updates on startup"));
177 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
180 _check_for_test_updates = new wxCheckBox (_panel, wxID_ANY, _("Check for testing updates on startup"));
181 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
184 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
185 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
189 GeneralPage::config_changed ()
191 Config* config = Config::instance ();
193 checked_set (_set_language, static_cast<bool>(config->language()));
195 /* Backwards compatibility of config file */
197 map<string, string> compat_map;
198 compat_map["fr"] = "fr_FR";
199 compat_map["it"] = "it_IT";
200 compat_map["es"] = "es_ES";
201 compat_map["sv"] = "sv_SE";
202 compat_map["de"] = "de_DE";
203 compat_map["nl"] = "nl_NL";
204 compat_map["ru"] = "ru_RU";
205 compat_map["pl"] = "pl_PL";
206 compat_map["da"] = "da_DK";
207 compat_map["pt"] = "pt_PT";
208 compat_map["sk"] = "sk_SK";
209 compat_map["cs"] = "cs_CZ";
210 compat_map["uk"] = "uk_UA";
212 string lang = config->language().get_value_or ("en_GB");
213 if (compat_map.find (lang) != compat_map.end ()) {
214 lang = compat_map[lang];
217 checked_set (_language, lang);
219 checked_set (_check_for_updates, config->check_for_updates ());
220 checked_set (_check_for_test_updates, config->check_for_test_updates ());
222 checked_set (_sound, config->sound ());
224 optional<string> const current_so = get_sound_output ();
225 optional<string> configured_so;
227 if (config->sound_output()) {
228 configured_so = config->sound_output().get();
230 /* No configured output means we should use the default */
231 RtAudio audio (DCPOMATIC_RTAUDIO_API);
233 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
234 } catch (RtAudioError& e) {
235 /* Probably no audio devices at all */
239 if (configured_so && current_so != configured_so) {
240 /* Update _sound_output with the configured value */
242 while (i < _sound_output->GetCount()) {
243 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
244 _sound_output->SetSelection (i);
251 setup_sensitivity ();
255 GeneralPage::setup_sensitivity ()
257 _language->Enable (_set_language->GetValue ());
258 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
259 _sound_output->Enable (_sound->GetValue ());
262 /** @return Currently-selected preview sound output in the dialogue */
264 GeneralPage::get_sound_output ()
266 int const sel = _sound_output->GetSelection ();
267 if (sel == wxNOT_FOUND) {
268 return optional<string> ();
271 return wx_to_std (_sound_output->GetString (sel));
275 GeneralPage::set_language_changed ()
277 setup_sensitivity ();
278 if (_set_language->GetValue ()) {
281 Config::instance()->unset_language ();
286 GeneralPage::language_changed ()
288 int const sel = _language->GetSelection ();
290 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
292 Config::instance()->unset_language ();
297 GeneralPage::check_for_updates_changed ()
299 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
303 GeneralPage::check_for_test_updates_changed ()
305 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
309 GeneralPage::sound_changed ()
311 Config::instance()->set_sound (_sound->GetValue ());
315 GeneralPage::sound_output_changed ()
317 RtAudio audio (DCPOMATIC_RTAUDIO_API);
318 optional<string> const so = get_sound_output();
319 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
320 Config::instance()->unset_sound_output ();
322 Config::instance()->set_sound_output (*so);
326 CertificateChainEditor::CertificateChainEditor (
330 function<void (shared_ptr<dcp::CertificateChain>)> set,
331 function<shared_ptr<const dcp::CertificateChain> (void)> get,
332 function<bool (void)> nag_remake
334 : wxDialog (parent, wxID_ANY, title)
337 , _nag_remake (nag_remake)
339 wxFont subheading_font (*wxNORMAL_FONT);
340 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
342 _sizer = new wxBoxSizer (wxVERTICAL);
345 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
346 m->SetFont (subheading_font);
347 _sizer->Add (m, 0, wxALL, border);
350 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
351 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
353 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
358 ip.SetText (_("Type"));
360 _certificates->InsertColumn (0, ip);
366 ip.SetText (_("Thumbprint"));
369 wxFont font = ip.GetFont ();
370 font.SetFamily (wxFONTFAMILY_TELETYPE);
373 _certificates->InsertColumn (1, ip);
376 certificates_sizer->Add (_certificates, 1, wxEXPAND);
379 wxSizer* s = new wxBoxSizer (wxVERTICAL);
380 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
381 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
382 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
383 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
384 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
385 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
386 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
389 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
390 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
393 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
394 _private_key = new wxStaticText (this, wxID_ANY, wxT (""));
395 wxFont font = _private_key->GetFont ();
396 font.SetFamily (wxFONTFAMILY_TELETYPE);
397 _private_key->SetFont (font);
398 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
399 _import_private_key = new wxButton (this, wxID_ANY, _("Import..."));
400 table->Add (_import_private_key, wxGBPosition (r, 2));
401 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
402 table->Add (_export_private_key, wxGBPosition (r, 3));
405 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
406 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
407 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
408 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
411 _private_key_bad = new wxStaticText (this, wxID_ANY, _("Leaf private key does not match leaf certificate!"));
412 font = *wxSMALL_FONT;
413 font.SetWeight (wxFONTWEIGHT_BOLD);
414 _private_key_bad->SetFont (font);
415 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
418 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
419 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
420 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
421 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
422 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
423 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
424 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
425 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
427 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
429 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
432 SetSizerAndFit (_sizer);
434 update_certificate_list ();
435 update_private_key ();
436 update_sensitivity ();
440 CertificateChainEditor::add_button (wxWindow* button)
442 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
447 CertificateChainEditor::add_certificate ()
449 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
451 if (d->ShowModal() == wxID_OK) {
456 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
457 } catch (boost::filesystem::filesystem_error& e) {
458 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
463 if (!extra.empty ()) {
466 _("This file contains other certificates (or other data) after its first certificate. "
467 "Only the first certificate will be used.")
470 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
472 if (!chain->chain_valid ()) {
475 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
476 "Add certificates in order from root to intermediate to leaf.")
481 update_certificate_list ();
483 } catch (dcp::MiscError& e) {
484 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
490 update_sensitivity ();
494 CertificateChainEditor::remove_certificate ()
496 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
501 _certificates->DeleteItem (i);
502 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
506 update_sensitivity ();
510 CertificateChainEditor::export_certificate ()
512 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
517 wxFileDialog* d = new wxFileDialog (
518 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
519 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
522 dcp::CertificateChain::List all = _get()->root_to_leaf ();
523 dcp::CertificateChain::List::iterator j = all.begin ();
524 for (int k = 0; k < i; ++k) {
528 if (d->ShowModal () == wxID_OK) {
529 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
531 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
534 string const s = j->certificate (true);
535 fwrite (s.c_str(), 1, s.length(), f);
542 CertificateChainEditor::update_certificate_list ()
544 _certificates->DeleteAllItems ();
546 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
547 BOOST_FOREACH (dcp::Certificate const & i, certs) {
550 _certificates->InsertItem (item);
551 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
554 _certificates->SetItem (n, 0, _("Root"));
555 } else if (n == (certs.size() - 1)) {
556 _certificates->SetItem (n, 0, _("Leaf"));
558 _certificates->SetItem (n, 0, _("Intermediate"));
564 static wxColour normal = _private_key_bad->GetForegroundColour ();
566 if (_get()->private_key_valid()) {
567 _private_key_bad->Hide ();
568 _private_key_bad->SetForegroundColour (normal);
570 _private_key_bad->Show ();
571 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
576 CertificateChainEditor::remake_certificates ()
578 shared_ptr<const dcp::CertificateChain> chain = _get();
580 string subject_organization_name;
581 string subject_organizational_unit_name;
582 string root_common_name;
583 string intermediate_common_name;
584 string leaf_common_name;
586 dcp::CertificateChain::List all = chain->root_to_leaf ();
588 if (all.size() >= 1) {
590 subject_organization_name = chain->root().subject_organization_name ();
591 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
592 root_common_name = chain->root().subject_common_name ();
595 if (all.size() >= 2) {
597 leaf_common_name = chain->leaf().subject_common_name ();
600 if (all.size() >= 3) {
601 /* Have an intermediate */
602 dcp::CertificateChain::List::iterator i = all.begin ();
604 intermediate_common_name = i->subject_common_name ();
608 /* Cancel was clicked */
612 MakeChainDialog* d = new MakeChainDialog (
614 subject_organization_name,
615 subject_organizational_unit_name,
617 intermediate_common_name,
621 if (d->ShowModal () == wxID_OK) {
623 shared_ptr<dcp::CertificateChain> (
624 new dcp::CertificateChain (
627 d->organisational_unit (),
628 d->root_common_name (),
629 d->intermediate_common_name (),
630 d->leaf_common_name ()
635 update_certificate_list ();
636 update_private_key ();
643 CertificateChainEditor::update_sensitivity ()
645 /* We can only remove the leaf certificate */
646 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
647 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
651 CertificateChainEditor::update_private_key ()
653 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
658 CertificateChainEditor::import_private_key ()
660 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
662 if (d->ShowModal() == wxID_OK) {
664 boost::filesystem::path p (wx_to_std (d->GetPath ()));
665 if (boost::filesystem::file_size (p) > 8192) {
668 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
673 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
674 chain->set_key (dcp::file_to_string (p));
676 update_private_key ();
677 } catch (dcp::MiscError& e) {
678 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
684 update_sensitivity ();
688 CertificateChainEditor::export_private_key ()
690 optional<string> key = _get()->key();
695 wxFileDialog* d = new wxFileDialog (
696 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
697 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
700 if (d->ShowModal () == wxID_OK) {
701 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
703 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
706 string const s = _get()->key().get ();
707 fwrite (s.c_str(), 1, s.length(), f);
714 KeysPage::GetName () const
722 wxFont subheading_font (*wxNORMAL_FONT);
723 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
725 wxSizer* sizer = _panel->GetSizer();
728 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Decrypting KDMs"));
729 m->SetFont (subheading_font);
730 sizer->Add (m, 0, wxALL, _border);
733 wxButton* export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export KDM decryption certificate..."));
734 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
735 wxButton* export_decryption_chain = new wxButton (_panel, wxID_ANY, _("Export KDM decryption chain..."));
736 sizer->Add (export_decryption_chain, 0, wxLEFT, _border);
737 wxButton* export_settings = new wxButton (_panel, wxID_ANY, _("Export all KDM decryption settings..."));
738 sizer->Add (export_settings, 0, wxLEFT, _border);
739 wxButton* import_settings = new wxButton (_panel, wxID_ANY, _("Import all KDM decryption settings..."));
740 sizer->Add (import_settings, 0, wxLEFT, _border);
741 wxButton* decryption_advanced = new wxButton (_panel, wxID_ANY, _("Advanced..."));
742 sizer->Add (decryption_advanced, 0, wxALL, _border);
744 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
745 export_decryption_chain->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain, this));
746 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
747 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
748 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
751 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Signing DCPs and KDMs"));
752 m->SetFont (subheading_font);
753 sizer->Add (m, 0, wxALL, _border);
756 wxButton* signing_advanced = new wxButton (_panel, wxID_ANY, _("Advanced..."));
757 sizer->Add (signing_advanced, 0, wxLEFT, _border);
758 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
762 KeysPage::decryption_advanced ()
764 CertificateChainEditor* c = new CertificateChainEditor (
765 _panel, _("Decrypting KDMs"), _border,
766 bind (&Config::set_decryption_chain, Config::instance (), _1),
767 bind (&Config::decryption_chain, Config::instance ()),
768 bind (&KeysPage::nag_remake_decryption_chain, this)
775 KeysPage::signing_advanced ()
777 CertificateChainEditor* c = new CertificateChainEditor (
778 _panel, _("Signing DCPs and KDMs"), _border,
779 bind (&Config::set_signer_chain, Config::instance (), _1),
780 bind (&Config::signer_chain, Config::instance ()),
788 KeysPage::export_decryption_chain_and_key ()
790 wxFileDialog* d = new wxFileDialog (
791 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
792 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
795 if (d->ShowModal () == wxID_OK) {
796 FILE* f = fopen_boost (path_from_file_dialog (d, "dom"), "w");
798 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
801 string const chain = Config::instance()->decryption_chain()->chain();
802 fwrite (chain.c_str(), 1, chain.length(), f);
803 optional<string> const key = Config::instance()->decryption_chain()->key();
804 DCPOMATIC_ASSERT (key);
805 fwrite (key->c_str(), 1, key->length(), f);
813 KeysPage::import_decryption_chain_and_key ()
815 wxFileDialog* d = new wxFileDialog (
816 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
819 if (d->ShowModal () == wxID_OK) {
820 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
822 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
824 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
830 if (fgets (buffer, 128, f) == 0) {
834 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
835 new_chain->add (dcp::Certificate (current));
837 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
838 new_chain->set_key (current);
844 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
845 Config::instance()->set_decryption_chain (new_chain);
847 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
854 KeysPage::nag_remake_decryption_chain ()
856 return NagDialog::maybe_nag (
858 Config::NAG_REMAKE_DECRYPTION_CHAIN,
859 _("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!"),
865 KeysPage::export_decryption_chain ()
867 wxFileDialog* d = new wxFileDialog (
868 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
869 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
872 if (d->ShowModal () == wxID_OK) {
873 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
875 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
878 string const s = Config::instance()->decryption_chain()->chain();
879 fwrite (s.c_str(), 1, s.length(), f);
886 KeysPage::export_decryption_certificate ()
888 wxFileDialog* d = new wxFileDialog (
889 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
890 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
893 if (d->ShowModal () == wxID_OK) {
894 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
896 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
899 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
900 fwrite (s.c_str(), 1, s.length(), f);