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"
32 using boost::optional;
33 using boost::shared_ptr;
34 using boost::function;
43 Page::Page (wxSize panel_size, int border)
46 , _panel_size (panel_size)
47 , _window_exists (false)
49 _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
53 Page::create_window (wxWindow* parent)
55 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
56 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
60 _window_exists = true;
63 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
69 Page::config_changed_wrapper ()
77 Page::window_destroyed ()
79 _window_exists = false;
83 StockPage::StockPage (Kind kind, wxSize panel_size, int border)
84 : wxStockPreferencesPage (kind)
85 , Page (panel_size, border)
91 StockPage::CreateWindow (wxWindow* parent)
93 return create_window (parent);
96 StandardPage::StandardPage (wxSize panel_size, int border)
97 : Page (panel_size, border)
103 StandardPage::CreateWindow (wxWindow* parent)
105 return create_window (parent);
108 GeneralPage::GeneralPage (wxSize panel_size, int border)
109 : StockPage (Kind_General, panel_size, border)
115 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
117 _set_language = new CheckBox (_panel, _("Set language"));
118 table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
119 _language = new wxChoice (_panel, wxID_ANY);
120 vector<pair<string, string> > languages;
121 languages.push_back (make_pair ("Čeština", "cs_CZ"));
122 languages.push_back (make_pair ("汉语/漢語", "zh_CN"));
123 languages.push_back (make_pair ("Dansk", "da_DK"));
124 languages.push_back (make_pair ("Deutsch", "de_DE"));
125 languages.push_back (make_pair ("English", "en_GB"));
126 languages.push_back (make_pair ("Español", "es_ES"));
127 languages.push_back (make_pair ("Français", "fr_FR"));
128 languages.push_back (make_pair ("Italiano", "it_IT"));
129 languages.push_back (make_pair ("Nederlands", "nl_NL"));
130 languages.push_back (make_pair ("Русский", "ru_RU"));
131 languages.push_back (make_pair ("Polski", "pl_PL"));
132 languages.push_back (make_pair ("Português europeu", "pt_PT"));
133 languages.push_back (make_pair ("Português do Brasil", "pt_BR"));
134 languages.push_back (make_pair ("Svenska", "sv_SE"));
135 languages.push_back (make_pair ("Slovenský jazyk", "sk_SK"));
136 languages.push_back (make_pair ("українська мова", "uk_UA"));
137 checked_set (_language, languages);
138 table->Add (_language, wxGBPosition (r, 1));
141 wxStaticText* restart = add_label_to_sizer (
142 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
144 wxFont font = restart->GetFont();
145 font.SetStyle (wxFONTSTYLE_ITALIC);
146 font.SetPointSize (font.GetPointSize() - 1);
147 restart->SetFont (font);
150 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
151 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
155 GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
157 _sound = new CheckBox (_panel, _("Play sound via"));
158 table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
159 _sound_output = new wxChoice (_panel, wxID_ANY);
160 table->Add (_sound_output, wxGBPosition (r, 1));
163 RtAudio audio (DCPOMATIC_RTAUDIO_API);
164 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
165 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
166 if (dev.probed && dev.outputChannels > 0) {
167 _sound_output->Append (std_to_wx (dev.name));
171 _sound->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::sound_changed, this));
172 _sound_output->Bind (wxEVT_CHOICE, bind (&GeneralPage::sound_output_changed, this));
176 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
178 _check_for_updates = new CheckBox (_panel, _("Check for updates on startup"));
179 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
182 _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
183 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
186 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
187 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
191 GeneralPage::config_changed ()
193 Config* config = Config::instance ();
195 checked_set (_set_language, static_cast<bool>(config->language()));
197 /* Backwards compatibility of config file */
199 map<string, string> compat_map;
200 compat_map["fr"] = "fr_FR";
201 compat_map["it"] = "it_IT";
202 compat_map["es"] = "es_ES";
203 compat_map["sv"] = "sv_SE";
204 compat_map["de"] = "de_DE";
205 compat_map["nl"] = "nl_NL";
206 compat_map["ru"] = "ru_RU";
207 compat_map["pl"] = "pl_PL";
208 compat_map["da"] = "da_DK";
209 compat_map["pt"] = "pt_PT";
210 compat_map["sk"] = "sk_SK";
211 compat_map["cs"] = "cs_CZ";
212 compat_map["uk"] = "uk_UA";
214 string lang = config->language().get_value_or ("en_GB");
215 if (compat_map.find (lang) != compat_map.end ()) {
216 lang = compat_map[lang];
219 checked_set (_language, lang);
221 checked_set (_check_for_updates, config->check_for_updates ());
222 checked_set (_check_for_test_updates, config->check_for_test_updates ());
224 checked_set (_sound, config->sound ());
226 optional<string> const current_so = get_sound_output ();
227 optional<string> configured_so;
229 if (config->sound_output()) {
230 configured_so = config->sound_output().get();
232 /* No configured output means we should use the default */
233 RtAudio audio (DCPOMATIC_RTAUDIO_API);
235 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
236 } catch (RtAudioError& e) {
237 /* Probably no audio devices at all */
241 if (configured_so && current_so != configured_so) {
242 /* Update _sound_output with the configured value */
244 while (i < _sound_output->GetCount()) {
245 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
246 _sound_output->SetSelection (i);
253 setup_sensitivity ();
257 GeneralPage::setup_sensitivity ()
259 _language->Enable (_set_language->GetValue ());
260 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
261 _sound_output->Enable (_sound->GetValue ());
264 /** @return Currently-selected preview sound output in the dialogue */
266 GeneralPage::get_sound_output ()
268 int const sel = _sound_output->GetSelection ();
269 if (sel == wxNOT_FOUND) {
270 return optional<string> ();
273 return wx_to_std (_sound_output->GetString (sel));
277 GeneralPage::set_language_changed ()
279 setup_sensitivity ();
280 if (_set_language->GetValue ()) {
283 Config::instance()->unset_language ();
288 GeneralPage::language_changed ()
290 int const sel = _language->GetSelection ();
292 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
294 Config::instance()->unset_language ();
299 GeneralPage::check_for_updates_changed ()
301 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
305 GeneralPage::check_for_test_updates_changed ()
307 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
311 GeneralPage::sound_changed ()
313 Config::instance()->set_sound (_sound->GetValue ());
317 GeneralPage::sound_output_changed ()
319 RtAudio audio (DCPOMATIC_RTAUDIO_API);
320 optional<string> const so = get_sound_output();
321 if (!so || *so == audio.getDeviceInfo(audio.getDefaultOutputDevice()).name) {
322 Config::instance()->unset_sound_output ();
324 Config::instance()->set_sound_output (*so);
328 CertificateChainEditor::CertificateChainEditor (
332 function<void (shared_ptr<dcp::CertificateChain>)> set,
333 function<shared_ptr<const dcp::CertificateChain> (void)> get,
334 function<bool (void)> nag_remake
336 : wxDialog (parent, wxID_ANY, title)
339 , _nag_remake (nag_remake)
341 wxFont subheading_font (*wxNORMAL_FONT);
342 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
344 _sizer = new wxBoxSizer (wxVERTICAL);
347 wxStaticText* m = new StaticText (this, title);
348 m->SetFont (subheading_font);
349 _sizer->Add (m, 0, wxALL, border);
352 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
353 _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
355 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
360 ip.SetText (_("Type"));
362 _certificates->InsertColumn (0, ip);
368 ip.SetText (_("Thumbprint"));
371 wxFont font = ip.GetFont ();
372 font.SetFamily (wxFONTFAMILY_TELETYPE);
375 _certificates->InsertColumn (1, ip);
378 certificates_sizer->Add (_certificates, 1, wxEXPAND);
381 wxSizer* s = new wxBoxSizer (wxVERTICAL);
382 _add_certificate = new wxButton (this, wxID_ANY, _("Add..."));
383 s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
384 _remove_certificate = new wxButton (this, wxID_ANY, _("Remove"));
385 s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
386 _export_certificate = new wxButton (this, wxID_ANY, _("Export"));
387 s->Add (_export_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
388 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
391 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
392 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
395 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
396 _private_key = new StaticText (this, wxT(""));
397 wxFont font = _private_key->GetFont ();
398 font.SetFamily (wxFONTFAMILY_TELETYPE);
399 _private_key->SetFont (font);
400 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
401 _import_private_key = new wxButton (this, wxID_ANY, _("Import..."));
402 table->Add (_import_private_key, wxGBPosition (r, 2));
403 _export_private_key = new wxButton (this, wxID_ANY, _("Export..."));
404 table->Add (_export_private_key, wxGBPosition (r, 3));
407 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
408 _remake_certificates = new wxButton (this, wxID_ANY, _("Re-make certificates and key..."));
409 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
410 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
413 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
414 font = *wxSMALL_FONT;
415 font.SetWeight (wxFONTWEIGHT_BOLD);
416 _private_key_bad->SetFont (font);
417 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
420 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
421 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
422 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
423 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
424 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
425 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
426 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
427 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
429 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
431 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
434 SetSizerAndFit (_sizer);
436 update_certificate_list ();
437 update_private_key ();
438 update_sensitivity ();
442 CertificateChainEditor::add_button (wxWindow* button)
444 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
449 CertificateChainEditor::add_certificate ()
451 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
453 if (d->ShowModal() == wxID_OK) {
458 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
459 } catch (boost::filesystem::filesystem_error& e) {
460 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
465 if (!extra.empty ()) {
468 _("This file contains other certificates (or other data) after its first certificate. "
469 "Only the first certificate will be used.")
472 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
474 if (!chain->chain_valid ()) {
477 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
478 "Add certificates in order from root to intermediate to leaf.")
483 update_certificate_list ();
485 } catch (dcp::MiscError& e) {
486 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
492 update_sensitivity ();
496 CertificateChainEditor::remove_certificate ()
498 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
503 _certificates->DeleteItem (i);
504 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
508 update_sensitivity ();
512 CertificateChainEditor::export_certificate ()
514 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
519 wxFileDialog* d = new wxFileDialog (
520 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
521 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
524 dcp::CertificateChain::List all = _get()->root_to_leaf ();
525 dcp::CertificateChain::List::iterator j = all.begin ();
526 for (int k = 0; k < i; ++k) {
530 if (d->ShowModal () == wxID_OK) {
531 boost::filesystem::path path (wx_to_std(d->GetPath()));
532 FILE* f = fopen_boost (path, "w");
534 throw OpenFileError (path, errno, false);
537 string const s = j->certificate (true);
538 fwrite (s.c_str(), 1, s.length(), f);
545 CertificateChainEditor::update_certificate_list ()
547 _certificates->DeleteAllItems ();
549 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
550 BOOST_FOREACH (dcp::Certificate const & i, certs) {
553 _certificates->InsertItem (item);
554 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
557 _certificates->SetItem (n, 0, _("Root"));
558 } else if (n == (certs.size() - 1)) {
559 _certificates->SetItem (n, 0, _("Leaf"));
561 _certificates->SetItem (n, 0, _("Intermediate"));
567 static wxColour normal = _private_key_bad->GetForegroundColour ();
569 if (_get()->private_key_valid()) {
570 _private_key_bad->Hide ();
571 _private_key_bad->SetForegroundColour (normal);
573 _private_key_bad->Show ();
574 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
579 CertificateChainEditor::remake_certificates ()
581 shared_ptr<const dcp::CertificateChain> chain = _get();
583 string subject_organization_name;
584 string subject_organizational_unit_name;
585 string root_common_name;
586 string intermediate_common_name;
587 string leaf_common_name;
589 dcp::CertificateChain::List all = chain->root_to_leaf ();
591 if (all.size() >= 1) {
593 subject_organization_name = chain->root().subject_organization_name ();
594 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
595 root_common_name = chain->root().subject_common_name ();
598 if (all.size() >= 2) {
600 leaf_common_name = chain->leaf().subject_common_name ();
603 if (all.size() >= 3) {
604 /* Have an intermediate */
605 dcp::CertificateChain::List::iterator i = all.begin ();
607 intermediate_common_name = i->subject_common_name ();
611 /* Cancel was clicked */
615 MakeChainDialog* d = new MakeChainDialog (
617 subject_organization_name,
618 subject_organizational_unit_name,
620 intermediate_common_name,
624 if (d->ShowModal () == wxID_OK) {
626 shared_ptr<dcp::CertificateChain> (
627 new dcp::CertificateChain (
630 d->organisational_unit (),
631 d->root_common_name (),
632 d->intermediate_common_name (),
633 d->leaf_common_name ()
638 update_certificate_list ();
639 update_private_key ();
646 CertificateChainEditor::update_sensitivity ()
648 /* We can only remove the leaf certificate */
649 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
650 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
654 CertificateChainEditor::update_private_key ()
656 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
661 CertificateChainEditor::import_private_key ()
663 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
665 if (d->ShowModal() == wxID_OK) {
667 boost::filesystem::path p (wx_to_std (d->GetPath ()));
668 if (boost::filesystem::file_size (p) > 8192) {
671 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
676 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
677 chain->set_key (dcp::file_to_string (p));
679 update_private_key ();
680 } catch (dcp::MiscError& e) {
681 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
687 update_sensitivity ();
691 CertificateChainEditor::export_private_key ()
693 optional<string> key = _get()->key();
698 wxFileDialog* d = new wxFileDialog (
699 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
700 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
703 if (d->ShowModal () == wxID_OK) {
704 boost::filesystem::path path (wx_to_std(d->GetPath()));
705 FILE* f = fopen_boost (path, "w");
707 throw OpenFileError (path, errno, false);
710 string const s = _get()->key().get ();
711 fwrite (s.c_str(), 1, s.length(), f);
718 KeysPage::GetName () const
726 wxFont subheading_font (*wxNORMAL_FONT);
727 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
729 wxSizer* sizer = _panel->GetSizer();
732 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
733 m->SetFont (subheading_font);
734 sizer->Add (m, 0, wxALL, _border);
737 wxButton* export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export KDM decryption certificate..."));
738 sizer->Add (export_decryption_certificate, 0, wxLEFT, _border);
739 wxButton* export_decryption_chain = new wxButton (_panel, wxID_ANY, _("Export KDM decryption chain..."));
740 sizer->Add (export_decryption_chain, 0, wxLEFT, _border);
741 wxButton* export_settings = new wxButton (_panel, wxID_ANY, _("Export all KDM decryption settings..."));
742 sizer->Add (export_settings, 0, wxLEFT, _border);
743 wxButton* import_settings = new wxButton (_panel, wxID_ANY, _("Import all KDM decryption settings..."));
744 sizer->Add (import_settings, 0, wxLEFT, _border);
745 wxButton* decryption_advanced = new wxButton (_panel, wxID_ANY, _("Advanced..."));
746 sizer->Add (decryption_advanced, 0, wxALL, _border);
748 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
749 export_decryption_chain->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain, this));
750 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
751 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
752 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
755 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
756 m->SetFont (subheading_font);
757 sizer->Add (m, 0, wxALL, _border);
760 wxButton* signing_advanced = new wxButton (_panel, wxID_ANY, _("Advanced..."));
761 sizer->Add (signing_advanced, 0, wxLEFT, _border);
762 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
766 KeysPage::decryption_advanced ()
768 CertificateChainEditor* c = new CertificateChainEditor (
769 _panel, _("Decrypting KDMs"), _border,
770 bind (&Config::set_decryption_chain, Config::instance (), _1),
771 bind (&Config::decryption_chain, Config::instance ()),
772 bind (&KeysPage::nag_remake_decryption_chain, this)
779 KeysPage::signing_advanced ()
781 CertificateChainEditor* c = new CertificateChainEditor (
782 _panel, _("Signing DCPs and KDMs"), _border,
783 bind (&Config::set_signer_chain, Config::instance (), _1),
784 bind (&Config::signer_chain, Config::instance ()),
792 KeysPage::export_decryption_chain_and_key ()
794 wxFileDialog* d = new wxFileDialog (
795 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
796 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
799 if (d->ShowModal () == wxID_OK) {
800 boost::filesystem::path path (wx_to_std(d->GetPath()));
801 FILE* f = fopen_boost (path, "w");
803 throw OpenFileError (path, errno, false);
806 string const chain = Config::instance()->decryption_chain()->chain();
807 fwrite (chain.c_str(), 1, chain.length(), f);
808 optional<string> const key = Config::instance()->decryption_chain()->key();
809 DCPOMATIC_ASSERT (key);
810 fwrite (key->c_str(), 1, key->length(), f);
818 KeysPage::import_decryption_chain_and_key ()
820 wxFileDialog* d = new wxFileDialog (
821 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
824 if (d->ShowModal () == wxID_OK) {
825 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
827 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
829 throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
835 if (fgets (buffer, 128, f) == 0) {
839 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
840 new_chain->add (dcp::Certificate (current));
842 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
843 new_chain->set_key (current);
849 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
850 Config::instance()->set_decryption_chain (new_chain);
852 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
859 KeysPage::nag_remake_decryption_chain ()
861 return NagDialog::maybe_nag (
863 Config::NAG_REMAKE_DECRYPTION_CHAIN,
864 _("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!"),
870 KeysPage::export_decryption_chain ()
872 wxFileDialog* d = new wxFileDialog (
873 _panel, _("Select Chain File"), wxEmptyString, _("dcpomatic_kdm_decryption_chain.pem"), wxT ("PEM files (*.pem)|*.pem"),
874 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
877 if (d->ShowModal () == wxID_OK) {
878 boost::filesystem::path path (wx_to_std(d->GetPath()));
879 FILE* f = fopen_boost (path, "w");
881 throw OpenFileError (path, errno, false);
884 string const s = Config::instance()->decryption_chain()->chain();
885 fwrite (s.c_str(), 1, s.length(), f);
892 KeysPage::export_decryption_certificate ()
894 wxFileDialog* d = new wxFileDialog (
895 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
896 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
899 if (d->ShowModal () == wxID_OK) {
900 boost::filesystem::path path (wx_to_std(d->GetPath()));
901 FILE* f = fopen_boost (path, "w");
903 throw OpenFileError (path, errno, false);
906 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
907 fwrite (s.c_str(), 1, s.length(), f);