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<void (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, wxString::Format (_("Could not import certificate (%s)"), d->GetPath().data()));
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, wxString::Format (_("Could not read certificate file (%s)"), 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 ();
609 MakeChainDialog* d = new MakeChainDialog (
611 subject_organization_name,
612 subject_organizational_unit_name,
614 intermediate_common_name,
618 if (d->ShowModal () == wxID_OK) {
620 shared_ptr<dcp::CertificateChain> (
621 new dcp::CertificateChain (
624 d->organisational_unit (),
625 d->root_common_name (),
626 d->intermediate_common_name (),
627 d->leaf_common_name ()
632 update_certificate_list ();
633 update_private_key ();
640 CertificateChainEditor::update_sensitivity ()
642 /* We can only remove the leaf certificate */
643 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
644 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
648 CertificateChainEditor::update_private_key ()
650 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
655 CertificateChainEditor::import_private_key ()
657 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
659 if (d->ShowModal() == wxID_OK) {
661 boost::filesystem::path p (wx_to_std (d->GetPath ()));
662 if (boost::filesystem::file_size (p) > 8192) {
665 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
670 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
671 chain->set_key (dcp::file_to_string (p));
673 update_private_key ();
674 } catch (dcp::MiscError& e) {
675 error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
681 update_sensitivity ();
685 CertificateChainEditor::export_private_key ()
687 optional<string> key = _get()->key();
692 wxFileDialog* d = new wxFileDialog (
693 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
694 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
697 if (d->ShowModal () == wxID_OK) {
698 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
700 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
703 string const s = _get()->key().get ();
704 fwrite (s.c_str(), 1, s.length(), f);
711 KeysPage::GetName () const
719 wxFont subheading_font (*wxNORMAL_FONT);
720 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
722 wxSizer* sizer = _panel->GetSizer();
725 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Decrypting KDMs"));
726 m->SetFont (subheading_font);
727 sizer->Add (m, 0, wxALL, _border);
730 wxButton* export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export KDM decryption certificate..."));
731 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
732 wxButton* export_decryption_chain = new wxButton (_panel, wxID_ANY, _("Export KDM decryption chain..."));
733 sizer->Add (export_decryption_chain, 0, wxLEFT, _border);
734 wxButton* export_settings = new wxButton (_panel, wxID_ANY, _("Export all KDM decryption settings..."));
735 sizer->Add (export_settings, 0, wxLEFT, _border);
736 wxButton* import_settings = new wxButton (_panel, wxID_ANY, _("Import all KDM decryption settings..."));
737 sizer->Add (import_settings, 0, wxLEFT, _border);
738 wxButton* decryption_advanced = new wxButton (_panel, wxID_ANY, _("Advanced..."));
739 sizer->Add (decryption_advanced, 0, wxALL, _border);
741 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
742 export_decryption_chain->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain, this));
743 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
744 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
745 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
748 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Signing DCPs and KDMs"));
749 m->SetFont (subheading_font);
750 sizer->Add (m, 0, wxALL, _border);
753 wxButton* signing_advanced = new wxButton (_panel, wxID_ANY, _("Advanced..."));
754 sizer->Add (signing_advanced, 0, wxLEFT, _border);
755 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
759 KeysPage::decryption_advanced ()
761 CertificateChainEditor* c = new CertificateChainEditor (
762 _panel, _("Decrypting KDMs"), _border,
763 bind (&Config::set_decryption_chain, Config::instance (), _1),
764 bind (&Config::decryption_chain, Config::instance ()),
765 bind (&KeysPage::nag_remake_decryption_chain, this)
772 KeysPage::signing_advanced ()
774 CertificateChainEditor* c = new CertificateChainEditor (
775 _panel, _("Signing DCPs and KDMs"), _border,
776 bind (&Config::set_signer_chain, Config::instance (), _1),
777 bind (&Config::signer_chain, Config::instance ()),
785 KeysPage::export_decryption_chain_and_key ()
787 wxFileDialog* d = new wxFileDialog (
788 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
789 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
792 if (d->ShowModal () == wxID_OK) {
793 FILE* f = fopen_boost (path_from_file_dialog (d, "dom"), "w");
795 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
798 string const chain = Config::instance()->decryption_chain()->chain();
799 fwrite (chain.c_str(), 1, chain.length(), f);
800 optional<string> const key = Config::instance()->decryption_chain()->key();
801 DCPOMATIC_ASSERT (key);
802 fwrite (key->c_str(), 1, key->length(), f);
810 KeysPage::import_decryption_chain_and_key ()
812 wxFileDialog* d = new wxFileDialog (
813 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
816 if (d->ShowModal () == wxID_OK) {
817 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
819 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
821 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
827 if (fgets (buffer, 128, f) == 0) {
831 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
832 new_chain->add (dcp::Certificate (current));
834 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
835 new_chain->set_key (current);
841 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
842 Config::instance()->set_decryption_chain (new_chain);
844 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
851 KeysPage::nag_remake_decryption_chain ()
853 NagDialog::maybe_nag (
855 Config::NAG_REMAKE_DECRYPTION_CHAIN,
856 _("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!")
861 KeysPage::export_decryption_chain ()
863 wxFileDialog* d = new wxFileDialog (
864 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
865 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
868 if (d->ShowModal () == wxID_OK) {
869 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
871 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
874 string const s = Config::instance()->decryption_chain()->chain();
875 fwrite (s.c_str(), 1, s.length(), f);
882 KeysPage::export_decryption_certificate ()
884 wxFileDialog* d = new wxFileDialog (
885 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
886 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
889 if (d->ShowModal () == wxID_OK) {
890 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
892 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
895 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
896 fwrite (s.c_str(), 1, s.length(), f);