2 Copyright (C) 2012-2019 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"
26 #include "audio_mapping_view.h"
27 #include <dcp/raw_convert.h>
36 using boost::optional;
37 using std::shared_ptr;
38 using boost::function;
39 #if BOOST_VERSION >= 106100
40 using namespace boost::placeholders;
50 Page::Page (wxSize panel_size, int border)
53 , _panel_size (panel_size)
54 , _window_exists (false)
56 _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
61 Page::CreateWindow (wxWindow* parent)
63 return create_window (parent);
68 Page::create_window (wxWindow* parent)
70 _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
71 wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
75 _window_exists = true;
78 _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
84 Page::config_changed_wrapper ()
92 Page::window_destroyed ()
94 _window_exists = false;
98 GeneralPage::GeneralPage (wxSize panel_size, int border)
99 : Page (panel_size, border)
106 GeneralPage::GetName () const
113 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
115 _set_language = new CheckBox (_panel, _("Set language"));
116 table->Add (_set_language, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
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 ("Türkçe", "tr_TR"));
135 languages.push_back (make_pair ("українська мова", "uk_UA"));
136 checked_set (_language, languages);
137 table->Add (_language, wxGBPosition (r, 1));
140 wxStaticText* restart = add_label_to_sizer (
141 table, _panel, _("(restart DCP-o-matic to see language changes)"), false, wxGBPosition (r, 0), wxGBSpan (1, 2)
143 wxFont font = restart->GetFont();
144 font.SetStyle (wxFONTSTYLE_ITALIC);
145 font.SetPointSize (font.GetPointSize() - 1);
146 restart->SetFont (font);
149 _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
150 _language->Bind (wxEVT_CHOICE, bind (&GeneralPage::language_changed, this));
154 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
156 _check_for_updates = new CheckBox (_panel, _("Check for updates on startup"));
157 table->Add (_check_for_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
160 _check_for_test_updates = new CheckBox (_panel, _("Check for testing updates on startup"));
161 table->Add (_check_for_test_updates, wxGBPosition (r, 0), wxGBSpan (1, 2));
164 _check_for_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_updates_changed, this));
165 _check_for_test_updates->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::check_for_test_updates_changed, this));
169 GeneralPage::config_changed ()
171 Config* config = Config::instance ();
173 checked_set (_set_language, static_cast<bool>(config->language()));
175 /* Backwards compatibility of config file */
177 map<string, string> compat_map;
178 compat_map["fr"] = "fr_FR";
179 compat_map["it"] = "it_IT";
180 compat_map["es"] = "es_ES";
181 compat_map["sv"] = "sv_SE";
182 compat_map["de"] = "de_DE";
183 compat_map["nl"] = "nl_NL";
184 compat_map["ru"] = "ru_RU";
185 compat_map["pl"] = "pl_PL";
186 compat_map["da"] = "da_DK";
187 compat_map["pt"] = "pt_PT";
188 compat_map["sk"] = "sk_SK";
189 compat_map["cs"] = "cs_CZ";
190 compat_map["uk"] = "uk_UA";
192 string lang = config->language().get_value_or ("en_GB");
193 if (compat_map.find (lang) != compat_map.end ()) {
194 lang = compat_map[lang];
197 checked_set (_language, lang);
199 checked_set (_check_for_updates, config->check_for_updates ());
200 checked_set (_check_for_test_updates, config->check_for_test_updates ());
202 setup_sensitivity ();
206 GeneralPage::setup_sensitivity ()
208 _language->Enable (_set_language->GetValue ());
209 _check_for_test_updates->Enable (_check_for_updates->GetValue ());
213 GeneralPage::set_language_changed ()
215 setup_sensitivity ();
216 if (_set_language->GetValue ()) {
219 Config::instance()->unset_language ();
224 GeneralPage::language_changed ()
226 int const sel = _language->GetSelection ();
228 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
230 Config::instance()->unset_language ();
235 GeneralPage::check_for_updates_changed ()
237 Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
241 GeneralPage::check_for_test_updates_changed ()
243 Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
246 CertificateChainEditor::CertificateChainEditor (
250 function<void (shared_ptr<dcp::CertificateChain>)> set,
251 function<shared_ptr<const dcp::CertificateChain> (void)> get,
252 function<bool (void)> nag_alter
254 : wxDialog (parent, wxID_ANY, title)
257 , _nag_alter (nag_alter)
259 _sizer = new wxBoxSizer (wxVERTICAL);
261 wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
262 _sizer->Add (certificates_sizer, 0, wxALL, border);
264 _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
269 ip.SetText (_("Type"));
271 _certificates->InsertColumn (0, ip);
277 ip.SetText (_("Thumbprint"));
280 wxFont font = ip.GetFont ();
281 font.SetFamily (wxFONTFAMILY_TELETYPE);
284 _certificates->InsertColumn (1, ip);
287 certificates_sizer->Add (_certificates, 1, wxEXPAND);
290 wxSizer* s = new wxBoxSizer (wxVERTICAL);
291 _add_certificate = new Button (this, _("Add..."));
292 s->Add (_add_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
293 _remove_certificate = new Button (this, _("Remove"));
294 s->Add (_remove_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
295 _export_certificate = new Button (this, _("Export certificate..."));
296 s->Add (_export_certificate, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
297 _export_chain = new Button (this, _("Export chain..."));
298 s->Add (_export_chain, 1, wxTOP | wxBOTTOM | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
299 certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
302 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
303 _sizer->Add (table, 1, wxALL | wxEXPAND, border);
306 add_label_to_sizer (table, this, _("Leaf private key"), true, wxGBPosition (r, 0));
307 _private_key = new StaticText (this, wxT(""));
308 wxFont font = _private_key->GetFont ();
309 font.SetFamily (wxFONTFAMILY_TELETYPE);
310 _private_key->SetFont (font);
311 table->Add (_private_key, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
312 _import_private_key = new Button (this, _("Import..."));
313 table->Add (_import_private_key, wxGBPosition (r, 2));
314 _export_private_key = new Button (this, _("Export..."));
315 table->Add (_export_private_key, wxGBPosition (r, 3));
318 _button_sizer = new wxBoxSizer (wxHORIZONTAL);
319 _remake_certificates = new Button (this, _("Re-make certificates and key..."));
320 _button_sizer->Add (_remake_certificates, 1, wxRIGHT, border);
321 table->Add (_button_sizer, wxGBPosition (r, 0), wxGBSpan (1, 4));
324 _private_key_bad = new StaticText (this, _("Leaf private key does not match leaf certificate!"));
325 font = *wxSMALL_FONT;
326 font.SetWeight (wxFONTWEIGHT_BOLD);
327 _private_key_bad->SetFont (font);
328 table->Add (_private_key_bad, wxGBPosition (r, 0), wxGBSpan (1, 3));
331 _add_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::add_certificate, this));
332 _remove_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remove_certificate, this));
333 _export_certificate->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_certificate, this));
334 _certificates->Bind (wxEVT_LIST_ITEM_SELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
335 _certificates->Bind (wxEVT_LIST_ITEM_DESELECTED, bind (&CertificateChainEditor::update_sensitivity, this));
336 _remake_certificates->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::remake_certificates, this));
337 _export_chain->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_chain, this));
338 _import_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::import_private_key, this));
339 _export_private_key->Bind (wxEVT_BUTTON, bind (&CertificateChainEditor::export_private_key, this));
341 wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
343 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
346 SetSizerAndFit (_sizer);
348 update_certificate_list ();
349 update_private_key ();
350 update_sensitivity ();
354 CertificateChainEditor::add_button (wxWindow* button)
356 _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
361 CertificateChainEditor::add_certificate ()
363 wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
365 if (d->ShowModal() == wxID_OK) {
370 extra = c.read_string (dcp::file_to_string (wx_to_std (d->GetPath ())));
371 } catch (boost::filesystem::filesystem_error& e) {
372 error_dialog (this, _("Could not import certificate (%s)"), d->GetPath());
377 if (!extra.empty ()) {
380 _("This file contains other certificates (or other data) after its first certificate. "
381 "Only the first certificate will be used.")
384 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
386 if (!chain->chain_valid ()) {
389 _("Adding this certificate would make the chain inconsistent, so it will not be added. "
390 "Add certificates in order from root to intermediate to leaf.")
395 update_certificate_list ();
397 } catch (dcp::MiscError& e) {
398 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
404 update_sensitivity ();
408 CertificateChainEditor::remove_certificate ()
411 /* Cancel was clicked */
415 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
420 _certificates->DeleteItem (i);
421 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
425 update_sensitivity ();
426 update_certificate_list ();
430 CertificateChainEditor::export_certificate ()
432 int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
437 wxFileDialog* d = new wxFileDialog (
438 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
439 auto all = _get()->root_to_leaf();
441 wxString default_name;
443 default_name = "root.pem";
444 } else if (i == static_cast<int>(all.size() - 1)) {
445 default_name = "leaf.pem";
447 default_name = "intermediate.pem";
450 auto d = new wxFileDialog(
451 this, _("Select Certificate File"), wxEmptyString, default_name, wxT ("PEM files (*.pem)|*.pem"),
452 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
455 auto j = all.begin ();
456 for (int k = 0; k < i; ++k) {
460 if (d->ShowModal () == wxID_OK) {
461 boost::filesystem::path path (wx_to_std(d->GetPath()));
462 FILE* f = fopen_boost (path, "w");
464 throw OpenFileError (path, errno, OpenFileError::WRITE);
467 string const s = j->certificate (true);
468 checked_fwrite (s.c_str(), s.length(), f, path);
475 CertificateChainEditor::export_chain ()
477 auto d = new wxFileDialog (
478 this, _("Select Chain File"), wxEmptyString, _("certificate_chain.pem"), wxT("PEM files (*.pem)|*.pem"),
479 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
482 if (d->ShowModal () == wxID_OK) {
483 boost::filesystem::path path (wx_to_std(d->GetPath()));
484 FILE* f = fopen_boost (path, "w");
486 throw OpenFileError (path, errno, OpenFileError::WRITE);
489 string const s = _get()->chain();
490 checked_fwrite (s.c_str(), s.length(), f, path);
498 CertificateChainEditor::update_certificate_list ()
500 _certificates->DeleteAllItems ();
502 dcp::CertificateChain::List certs = _get()->root_to_leaf ();
503 for (auto const& i: certs) {
506 _certificates->InsertItem (item);
507 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
510 _certificates->SetItem (n, 0, _("Root"));
511 } else if (n == (certs.size() - 1)) {
512 _certificates->SetItem (n, 0, _("Leaf"));
514 _certificates->SetItem (n, 0, _("Intermediate"));
520 static wxColour normal = _private_key_bad->GetForegroundColour ();
522 if (_get()->private_key_valid()) {
523 _private_key_bad->Hide ();
524 _private_key_bad->SetForegroundColour (normal);
526 _private_key_bad->Show ();
527 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
532 CertificateChainEditor::remake_certificates ()
534 shared_ptr<const dcp::CertificateChain> chain = _get();
536 string subject_organization_name;
537 string subject_organizational_unit_name;
538 string root_common_name;
539 string intermediate_common_name;
540 string leaf_common_name;
542 dcp::CertificateChain::List all = chain->root_to_leaf ();
544 if (all.size() >= 1) {
546 subject_organization_name = chain->root().subject_organization_name ();
547 subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
548 root_common_name = chain->root().subject_common_name ();
551 if (all.size() >= 2) {
553 leaf_common_name = chain->leaf().subject_common_name ();
556 if (all.size() >= 3) {
557 /* Have an intermediate */
558 dcp::CertificateChain::List::iterator i = all.begin ();
560 intermediate_common_name = i->subject_common_name ();
564 /* Cancel was clicked */
568 MakeChainDialog* d = new MakeChainDialog (
570 subject_organization_name,
571 subject_organizational_unit_name,
573 intermediate_common_name,
577 if (d->ShowModal () == wxID_OK) {
579 shared_ptr<dcp::CertificateChain> (
580 new dcp::CertificateChain (
583 d->organisational_unit (),
584 d->root_common_name (),
585 d->intermediate_common_name (),
586 d->leaf_common_name ()
591 update_certificate_list ();
592 update_private_key ();
599 CertificateChainEditor::update_sensitivity ()
601 /* We can only remove the leaf certificate */
602 _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == (_certificates->GetItemCount() - 1));
603 _export_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
607 CertificateChainEditor::update_private_key ()
609 checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
614 CertificateChainEditor::import_private_key ()
616 wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
618 if (d->ShowModal() == wxID_OK) {
620 boost::filesystem::path p (wx_to_std (d->GetPath ()));
621 if (boost::filesystem::file_size (p) > 8192) {
624 wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
629 shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
630 chain->set_key (dcp::file_to_string (p));
632 update_private_key ();
633 } catch (dcp::MiscError& e) {
634 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
640 update_sensitivity ();
644 CertificateChainEditor::export_private_key ()
646 optional<string> key = _get()->key();
651 auto d = new wxFileDialog (
652 this, _("Select Key File"), wxEmptyString, _("private_key.pem"), wxT ("PEM files (*.pem)|*.pem"),
653 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
656 if (d->ShowModal () == wxID_OK) {
657 boost::filesystem::path path (wx_to_std(d->GetPath()));
658 FILE* f = fopen_boost (path, "w");
660 throw OpenFileError (path, errno, OpenFileError::WRITE);
663 string const s = _get()->key().get ();
664 checked_fwrite (s.c_str(), s.length(), f, path);
671 KeysPage::GetName () const
679 wxFont subheading_font (*wxNORMAL_FONT);
680 subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
682 wxSizer* sizer = _panel->GetSizer();
685 wxStaticText* m = new StaticText (_panel, _("Decrypting KDMs"));
686 m->SetFont (subheading_font);
687 sizer->Add (m, 0, wxALL, _border);
690 wxSizer* buttons = new wxBoxSizer (wxVERTICAL);
692 wxButton* export_decryption_certificate = new Button (_panel, _("Export KDM decryption certificate..."));
693 buttons->Add (export_decryption_certificate, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
694 wxButton* export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
695 buttons->Add (export_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
696 wxButton* import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
697 buttons->Add (import_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
698 wxButton* decryption_advanced = new Button (_panel, _("Advanced..."));
699 buttons->Add (decryption_advanced, 0);
701 sizer->Add (buttons, 0, wxLEFT, _border);
703 export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
704 export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
705 import_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::import_decryption_chain_and_key, this));
706 decryption_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::decryption_advanced, this));
709 wxStaticText* m = new StaticText (_panel, _("Signing DCPs and KDMs"));
710 m->SetFont (subheading_font);
711 sizer->Add (m, 0, wxALL, _border);
714 wxButton* signing_advanced = new Button (_panel, _("Advanced..."));
715 sizer->Add (signing_advanced, 0, wxLEFT | wxBOTTOM, _border);
716 signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
720 KeysPage::decryption_advanced ()
722 CertificateChainEditor* c = new CertificateChainEditor (
723 _panel, _("Decrypting KDMs"), _border,
724 bind (&Config::set_decryption_chain, Config::instance (), _1),
725 bind (&Config::decryption_chain, Config::instance ()),
726 bind (&KeysPage::nag_alter_decryption_chain, this)
733 KeysPage::signing_advanced ()
735 CertificateChainEditor* c = new CertificateChainEditor (
736 _panel, _("Signing DCPs and KDMs"), _border,
737 bind (&Config::set_signer_chain, Config::instance (), _1),
738 bind (&Config::signer_chain, Config::instance ()),
746 KeysPage::export_decryption_chain_and_key ()
748 wxFileDialog* d = new wxFileDialog (
749 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
750 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
753 if (d->ShowModal () == wxID_OK) {
754 boost::filesystem::path path (wx_to_std(d->GetPath()));
755 FILE* f = fopen_boost (path, "w");
757 throw OpenFileError (path, errno, OpenFileError::WRITE);
760 string const chain = Config::instance()->decryption_chain()->chain();
761 checked_fwrite (chain.c_str(), chain.length(), f, path);
762 optional<string> const key = Config::instance()->decryption_chain()->key();
763 DCPOMATIC_ASSERT (key);
764 checked_fwrite (key->c_str(), key->length(), f, path);
772 KeysPage::import_decryption_chain_and_key ()
774 if (NagDialog::maybe_nag (
776 Config::NAG_IMPORT_DECRYPTION_CHAIN,
777 _("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!"),
783 wxFileDialog* d = new wxFileDialog (
784 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
787 if (d->ShowModal () == wxID_OK) {
788 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
790 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
792 throw OpenFileError (wx_to_std (d->GetPath ()), errno, OpenFileError::WRITE);
798 if (fgets (buffer, 128, f) == 0) {
802 if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
803 new_chain->add (dcp::Certificate (current));
805 } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
806 new_chain->set_key (current);
812 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
813 Config::instance()->set_decryption_chain (new_chain);
815 error_dialog (_panel, _("Invalid DCP-o-matic export file"));
822 KeysPage::nag_alter_decryption_chain ()
824 return NagDialog::maybe_nag (
826 Config::NAG_ALTER_DECRYPTION_CHAIN,
827 _("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!"),
833 KeysPage::export_decryption_certificate ()
835 wxFileDialog* d = new wxFileDialog (
836 _panel, _("Select Certificate File"), wxEmptyString, _("dcpomatic_kdm_decryption_cert.pem"), wxT ("PEM files (*.pem)|*.pem"),
837 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
840 if (d->ShowModal () == wxID_OK) {
841 boost::filesystem::path path (wx_to_std(d->GetPath()));
842 FILE* f = fopen_boost (path, "w");
844 throw OpenFileError (path, errno, OpenFileError::WRITE);
847 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
848 checked_fwrite (s.c_str(), s.length(), f, path);
856 SoundPage::GetName () const
864 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
865 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
869 _sound = new CheckBox (_panel, _("Play sound via"));
870 table->Add (_sound, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
871 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
872 _sound_output = new wxChoice (_panel, wxID_ANY);
873 s->Add (_sound_output, 0);
874 _sound_output_details = new wxStaticText (_panel, wxID_ANY, wxT(""));
875 s->Add (_sound_output_details, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP);
876 table->Add (s, wxGBPosition(r, 1));
879 add_label_to_sizer (table, _panel, _("Mapping"), true, wxGBPosition(r, 0));
880 _map = new AudioMappingView (_panel, _("DCP"), _("DCP"), _("Output"), _("output"));
881 _map->SetSize (-1, 600);
882 table->Add (_map, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND);
885 _reset_to_default = new Button (_panel, _("Reset to default"));
886 table->Add (_reset_to_default, wxGBPosition(r, 1));
889 wxFont font = _sound_output_details->GetFont();
890 font.SetStyle (wxFONTSTYLE_ITALIC);
891 font.SetPointSize (font.GetPointSize() - 1);
892 _sound_output_details->SetFont (font);
894 RtAudio audio (DCPOMATIC_RTAUDIO_API);
895 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
897 RtAudio::DeviceInfo dev = audio.getDeviceInfo (i);
898 if (dev.probed && dev.outputChannels > 0) {
899 _sound_output->Append (std_to_wx (dev.name));
901 } catch (RtAudioError&) {
902 /* Something went wrong so let's just ignore that device */
906 _sound->Bind (wxEVT_CHECKBOX, bind(&SoundPage::sound_changed, this));
907 _sound_output->Bind (wxEVT_CHOICE, bind(&SoundPage::sound_output_changed, this));
908 _map->Changed.connect (bind(&SoundPage::map_changed, this, _1));
909 _reset_to_default->Bind (wxEVT_BUTTON, bind(&SoundPage::reset_to_default, this));
913 SoundPage::reset_to_default ()
915 Config::instance()->set_audio_mapping_to_default ();
919 SoundPage::map_changed (AudioMapping m)
921 Config::instance()->set_audio_mapping (m);
925 SoundPage::sound_changed ()
927 Config::instance()->set_sound (_sound->GetValue ());
931 SoundPage::sound_output_changed ()
933 RtAudio audio (DCPOMATIC_RTAUDIO_API);
934 optional<string> const so = get_sound_output();
935 string default_device;
937 default_device = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
938 } catch (RtAudioError&) {
941 if (!so || *so == default_device) {
942 Config::instance()->unset_sound_output ();
944 Config::instance()->set_sound_output (*so);
949 SoundPage::config_changed ()
951 Config* config = Config::instance ();
953 checked_set (_sound, config->sound ());
955 optional<string> const current_so = get_sound_output ();
956 optional<string> configured_so;
958 if (config->sound_output()) {
959 configured_so = config->sound_output().get();
961 /* No configured output means we should use the default */
962 RtAudio audio (DCPOMATIC_RTAUDIO_API);
964 configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
965 } catch (RtAudioError&) {
966 /* Probably no audio devices at all */
970 if (configured_so && current_so != configured_so) {
971 /* Update _sound_output with the configured value */
973 while (i < _sound_output->GetCount()) {
974 if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
975 _sound_output->SetSelection (i);
982 RtAudio audio (DCPOMATIC_RTAUDIO_API);
984 map<int, wxString> apis;
985 apis[RtAudio::MACOSX_CORE] = _("CoreAudio");
986 apis[RtAudio::WINDOWS_ASIO] = _("ASIO");
987 apis[RtAudio::WINDOWS_DS] = _("Direct Sound");
988 apis[RtAudio::WINDOWS_WASAPI] = _("WASAPI");
989 apis[RtAudio::UNIX_JACK] = _("JACK");
990 apis[RtAudio::LINUX_ALSA] = _("ALSA");
991 apis[RtAudio::LINUX_PULSE] = _("PulseAudio");
992 apis[RtAudio::LINUX_OSS] = _("OSS");
993 apis[RtAudio::RTAUDIO_DUMMY] = _("Dummy");
997 for (unsigned int i = 0; i < audio.getDeviceCount(); ++i) {
999 RtAudio::DeviceInfo info = audio.getDeviceInfo(i);
1000 if (info.name == *configured_so && info.outputChannels > 0) {
1001 channels = info.outputChannels;
1003 } catch (RtAudioError&) {
1009 _sound_output_details->SetLabel (
1010 wxString::Format(_("%d channels on %s"), channels, apis[audio.getCurrentApi()])
1013 _map->set (Config::instance()->audio_mapping(channels));
1015 vector<NamedChannel> input;
1016 for (int i = 0; i < MAX_DCP_AUDIO_CHANNELS; ++i) {
1017 input.push_back (NamedChannel(short_audio_channel_name(i), i));
1019 _map->set_input_channels (input);
1021 vector<NamedChannel> output;
1022 for (int i = 0; i < channels; ++i) {
1023 output.push_back (NamedChannel(dcp::raw_convert<string>(i), i));
1025 _map->set_output_channels (output);
1027 setup_sensitivity ();
1031 SoundPage::setup_sensitivity ()
1033 _sound_output->Enable (_sound->GetValue());
1036 /** @return Currently-selected preview sound output in the dialogue */
1038 SoundPage::get_sound_output ()
1040 int const sel = _sound_output->GetSelection ();
1041 if (sel == wxNOT_FOUND) {
1042 return optional<string> ();
1045 return wx_to_std (_sound_output->GetString (sel));
1049 LocationsPage::LocationsPage (wxSize panel_size, int border)
1050 : Page (panel_size, border)
1056 LocationsPage::GetName () const
1058 return _("Locations");
1061 #ifdef DCPOMATIC_OSX
1063 LocationsPage::GetLargeIcon () const
1065 return wxBitmap ("locations", wxBITMAP_TYPE_PNG_RESOURCE);
1070 LocationsPage::setup ()
1074 wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
1075 _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
1077 add_label_to_sizer (table, _panel, _("Content directory"), true, wxGBPosition (r, 0));
1078 _content_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
1079 table->Add (_content_directory, wxGBPosition (r, 1));
1082 add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0));
1083 _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
1084 table->Add (_playlist_directory, wxGBPosition (r, 1));
1087 add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0));
1088 _kdm_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
1089 table->Add (_kdm_directory, wxGBPosition (r, 1));
1092 _content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this));
1093 _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this));
1094 _kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this));
1098 LocationsPage::config_changed ()
1100 Config* config = Config::instance ();
1102 if (config->player_content_directory()) {
1103 checked_set (_content_directory, *config->player_content_directory());
1105 if (config->player_playlist_directory()) {
1106 checked_set (_playlist_directory, *config->player_playlist_directory());
1108 if (config->player_kdm_directory()) {
1109 checked_set (_kdm_directory, *config->player_kdm_directory());
1114 LocationsPage::content_directory_changed ()
1116 Config::instance()->set_player_content_directory(wx_to_std(_content_directory->GetPath()));
1120 LocationsPage::playlist_directory_changed ()
1122 Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
1126 LocationsPage::kdm_directory_changed ()
1128 Config::instance()->set_player_kdm_directory(wx_to_std(_kdm_directory->GetPath()));