No-op; remove some namespace qualifiers.
[dcpomatic.git] / src / wx / config_dialog.cc
1 /*
2     Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 #include "config_dialog.h"
22 #include "nag_dialog.h"
23
24 using std::string;
25 using std::vector;
26 using std::pair;
27 using std::make_pair;
28 using std::map;
29 using boost::bind;
30 using boost::optional;
31 using boost::shared_ptr;
32 using boost::function;
33
34 static
35 void
36 do_nothing ()
37 {
38
39 }
40
41 Page::Page (wxSize panel_size, int border)
42         : _border (border)
43         , _panel (0)
44         , _panel_size (panel_size)
45         , _window_exists (false)
46 {
47         _config_connection = Config::instance()->Changed.connect (bind (&Page::config_changed_wrapper, this));
48 }
49
50 wxWindow*
51 Page::create_window (wxWindow* parent)
52 {
53         _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
54         wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
55         _panel->SetSizer (s);
56
57         setup ();
58         _window_exists = true;
59         config_changed ();
60
61         _panel->Bind (wxEVT_DESTROY, bind (&Page::window_destroyed, this));
62
63         return _panel;
64 }
65
66 void
67 Page::config_changed_wrapper ()
68 {
69         if (_window_exists) {
70                 config_changed ();
71         }
72 }
73
74 void
75 Page::window_destroyed ()
76 {
77         _window_exists = false;
78 }
79
80
81 StockPage::StockPage (Kind kind, wxSize panel_size, int border)
82         : wxStockPreferencesPage (kind)
83         , Page (panel_size, border)
84 {
85
86 }
87
88 wxWindow*
89 StockPage::CreateWindow (wxWindow* parent)
90 {
91         return create_window (parent);
92 }
93
94 StandardPage::StandardPage (wxSize panel_size, int border)
95         : Page (panel_size, border)
96 {
97
98 }
99
100 wxWindow*
101 StandardPage::CreateWindow (wxWindow* parent)
102 {
103         return create_window (parent);
104 }
105
106 GeneralPage::GeneralPage (wxSize panel_size, int border)
107         : StockPage (Kind_General, panel_size, border)
108 {
109
110 }
111
112 void
113 GeneralPage::add_language_controls (wxGridBagSizer* table, int& r)
114 {
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));
137         ++r;
138
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)
141                 );
142         wxFont font = restart->GetFont();
143         font.SetStyle (wxFONTSTYLE_ITALIC);
144         font.SetPointSize (font.GetPointSize() - 1);
145         restart->SetFont (font);
146         ++r;
147
148         _set_language->Bind (wxEVT_CHECKBOX, bind (&GeneralPage::set_language_changed, this));
149         _language->Bind     (wxEVT_CHOICE,   bind (&GeneralPage::language_changed,     this));
150 }
151
152 void
153 GeneralPage::add_play_sound_controls (wxGridBagSizer* table, int& r)
154 {
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));
159         ++r;
160
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));
166                 }
167         }
168
169         _sound->Bind        (wxEVT_CHECKBOX, bind (&GeneralPage::sound_changed, this));
170         _sound_output->Bind (wxEVT_CHOICE,   bind (&GeneralPage::sound_output_changed, this));
171 }
172
173 void
174 GeneralPage::add_update_controls (wxGridBagSizer* table, int& r)
175 {
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));
178         ++r;
179
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));
182         ++r;
183
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));
186 }
187
188 void
189 GeneralPage::config_changed ()
190 {
191         Config* config = Config::instance ();
192
193         checked_set (_set_language, static_cast<bool>(config->language()));
194
195         /* Backwards compatibility of config file */
196
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";
211
212         string lang = config->language().get_value_or ("en_GB");
213         if (compat_map.find (lang) != compat_map.end ()) {
214                 lang = compat_map[lang];
215         }
216
217         checked_set (_language, lang);
218
219         checked_set (_check_for_updates, config->check_for_updates ());
220         checked_set (_check_for_test_updates, config->check_for_test_updates ());
221
222         checked_set (_sound, config->sound ());
223
224         optional<string> const current_so = get_sound_output ();
225         optional<string> configured_so;
226
227         if (config->sound_output()) {
228                 configured_so = config->sound_output().get();
229         } else {
230                 /* No configured output means we should use the default */
231                 RtAudio audio (DCPOMATIC_RTAUDIO_API);
232                 try {
233                         configured_so = audio.getDeviceInfo(audio.getDefaultOutputDevice()).name;
234                 } catch (RtAudioError& e) {
235                         /* Probably no audio devices at all */
236                 }
237         }
238
239         if (configured_so && current_so != configured_so) {
240                 /* Update _sound_output with the configured value */
241                 unsigned int i = 0;
242                 while (i < _sound_output->GetCount()) {
243                         if (_sound_output->GetString(i) == std_to_wx(*configured_so)) {
244                                 _sound_output->SetSelection (i);
245                                 break;
246                         }
247                         ++i;
248                 }
249         }
250
251         setup_sensitivity ();
252 }
253
254 void
255 GeneralPage::setup_sensitivity ()
256 {
257         _language->Enable (_set_language->GetValue ());
258         _check_for_test_updates->Enable (_check_for_updates->GetValue ());
259         _sound_output->Enable (_sound->GetValue ());
260 }
261
262 /** @return Currently-selected preview sound output in the dialogue */
263 optional<string>
264 GeneralPage::get_sound_output ()
265 {
266         int const sel = _sound_output->GetSelection ();
267         if (sel == wxNOT_FOUND) {
268                 return optional<string> ();
269         }
270
271         return wx_to_std (_sound_output->GetString (sel));
272 }
273
274 void
275 GeneralPage::set_language_changed ()
276 {
277         setup_sensitivity ();
278         if (_set_language->GetValue ()) {
279                 language_changed ();
280         } else {
281                 Config::instance()->unset_language ();
282         }
283 }
284
285 void
286 GeneralPage::language_changed ()
287 {
288         int const sel = _language->GetSelection ();
289         if (sel != -1) {
290                 Config::instance()->set_language (string_client_data (_language->GetClientObject (sel)));
291         } else {
292                 Config::instance()->unset_language ();
293         }
294 }
295
296 void
297 GeneralPage::check_for_updates_changed ()
298 {
299         Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
300 }
301
302 void
303 GeneralPage::check_for_test_updates_changed ()
304 {
305         Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
306 }
307
308 void
309 GeneralPage::sound_changed ()
310 {
311         Config::instance()->set_sound (_sound->GetValue ());
312 }
313
314 void
315 GeneralPage::sound_output_changed ()
316 {
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 ();
321         } else {
322                 Config::instance()->set_sound_output (*so);
323         }
324 }
325
326 CertificateChainEditor::CertificateChainEditor (
327         wxWindow* parent,
328         wxString title,
329         int border,
330         function<void (shared_ptr<dcp::CertificateChain>)> set,
331         function<shared_ptr<const dcp::CertificateChain> (void)> get,
332         function<void (void)> nag_remake
333         )
334         : wxDialog (parent, wxID_ANY, title)
335         , _set (set)
336         , _get (get)
337         , _nag_remake (nag_remake)
338 {
339         wxFont subheading_font (*wxNORMAL_FONT);
340         subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
341
342         _sizer = new wxBoxSizer (wxVERTICAL);
343
344         {
345                 wxStaticText* m = new wxStaticText (this, wxID_ANY, title);
346                 m->SetFont (subheading_font);
347                 _sizer->Add (m, 0, wxALL, border);
348         }
349
350         wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
351         _sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, border);
352
353         _certificates = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (440, 150), wxLC_REPORT | wxLC_SINGLE_SEL);
354
355         {
356                 wxListItem ip;
357                 ip.SetId (0);
358                 ip.SetText (_("Type"));
359                 ip.SetWidth (100);
360                 _certificates->InsertColumn (0, ip);
361         }
362
363         {
364                 wxListItem ip;
365                 ip.SetId (1);
366                 ip.SetText (_("Thumbprint"));
367                 ip.SetWidth (340);
368
369                 wxFont font = ip.GetFont ();
370                 font.SetFamily (wxFONTFAMILY_TELETYPE);
371                 ip.SetFont (font);
372
373                 _certificates->InsertColumn (1, ip);
374         }
375
376         certificates_sizer->Add (_certificates, 1, wxEXPAND);
377
378         {
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);
387         }
388
389         wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
390         _sizer->Add (table, 1, wxALL | wxEXPAND, border);
391         int r = 0;
392
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));
403         ++r;
404
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));
409         ++r;
410
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));
416         ++r;
417
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));
426
427         wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
428         if (buttons) {
429                 _sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
430         }
431
432         SetSizerAndFit (_sizer);
433
434         update_certificate_list ();
435         update_private_key ();
436         update_sensitivity ();
437 }
438
439 void
440 CertificateChainEditor::add_button (wxWindow* button)
441 {
442         _button_sizer->Add (button, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
443         _sizer->Layout ();
444 }
445
446 void
447 CertificateChainEditor::add_certificate ()
448 {
449         wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
450
451         if (d->ShowModal() == wxID_OK) {
452                 try {
453                         dcp::Certificate c;
454                         string extra;
455                         try {
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()));
459                                 d->Destroy ();
460                                 return;
461                         }
462
463                         if (!extra.empty ()) {
464                                 message_dialog (
465                                         this,
466                                         _("This file contains other certificates (or other data) after its first certificate. "
467                                           "Only the first certificate will be used.")
468                                         );
469                         }
470                         shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
471                         chain->add (c);
472                         if (!chain->chain_valid ()) {
473                                 error_dialog (
474                                         this,
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.")
477                                         );
478                                 chain->remove (c);
479                         } else {
480                                 _set (chain);
481                                 update_certificate_list ();
482                         }
483                 } catch (dcp::MiscError& e) {
484                         error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
485                 }
486         }
487
488         d->Destroy ();
489
490         update_sensitivity ();
491 }
492
493 void
494 CertificateChainEditor::remove_certificate ()
495 {
496         int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
497         if (i == -1) {
498                 return;
499         }
500
501         _certificates->DeleteItem (i);
502         shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
503         chain->remove (i);
504         _set (chain);
505
506         update_sensitivity ();
507 }
508
509 void
510 CertificateChainEditor::export_certificate ()
511 {
512         int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
513         if (i == -1) {
514                 return;
515         }
516
517         wxFileDialog* d = new wxFileDialog (
518                 this, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
519                 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
520                 );
521
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) {
525                 ++j;
526         }
527
528         if (d->ShowModal () == wxID_OK) {
529                 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
530                 if (!f) {
531                         throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
532                 }
533
534                 string const s = j->certificate (true);
535                 fwrite (s.c_str(), 1, s.length(), f);
536                 fclose (f);
537         }
538         d->Destroy ();
539 }
540
541 void
542 CertificateChainEditor::update_certificate_list ()
543 {
544         _certificates->DeleteAllItems ();
545         size_t n = 0;
546         dcp::CertificateChain::List certs = _get()->root_to_leaf ();
547         BOOST_FOREACH (dcp::Certificate const & i, certs) {
548                 wxListItem item;
549                 item.SetId (n);
550                 _certificates->InsertItem (item);
551                 _certificates->SetItem (n, 1, std_to_wx (i.thumbprint ()));
552
553                 if (n == 0) {
554                         _certificates->SetItem (n, 0, _("Root"));
555                 } else if (n == (certs.size() - 1)) {
556                         _certificates->SetItem (n, 0, _("Leaf"));
557                 } else {
558                         _certificates->SetItem (n, 0, _("Intermediate"));
559                 }
560
561                 ++n;
562         }
563
564         static wxColour normal = _private_key_bad->GetForegroundColour ();
565
566         if (_get()->private_key_valid()) {
567                 _private_key_bad->Hide ();
568                 _private_key_bad->SetForegroundColour (normal);
569         } else {
570                 _private_key_bad->Show ();
571                 _private_key_bad->SetForegroundColour (wxColour (255, 0, 0));
572         }
573 }
574
575 void
576 CertificateChainEditor::remake_certificates ()
577 {
578         shared_ptr<const dcp::CertificateChain> chain = _get();
579
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;
585
586         dcp::CertificateChain::List all = chain->root_to_leaf ();
587
588         if (all.size() >= 1) {
589                 /* Have a root */
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 ();
593         }
594
595         if (all.size() >= 2) {
596                 /* Have a leaf */
597                 leaf_common_name = chain->leaf().subject_common_name ();
598         }
599
600         if (all.size() >= 3) {
601                 /* Have an intermediate */
602                 dcp::CertificateChain::List::iterator i = all.begin ();
603                 ++i;
604                 intermediate_common_name = i->subject_common_name ();
605         }
606
607         _nag_remake ();
608
609         MakeChainDialog* d = new MakeChainDialog (
610                 this,
611                 subject_organization_name,
612                 subject_organizational_unit_name,
613                 root_common_name,
614                 intermediate_common_name,
615                 leaf_common_name
616                 );
617
618         if (d->ShowModal () == wxID_OK) {
619                 _set (
620                         shared_ptr<dcp::CertificateChain> (
621                                 new dcp::CertificateChain (
622                                         openssl_path (),
623                                         d->organisation (),
624                                         d->organisational_unit (),
625                                         d->root_common_name (),
626                                         d->intermediate_common_name (),
627                                         d->leaf_common_name ()
628                                         )
629                                 )
630                         );
631
632                 update_certificate_list ();
633                 update_private_key ();
634         }
635
636         d->Destroy ();
637 }
638
639 void
640 CertificateChainEditor::update_sensitivity ()
641 {
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);
645 }
646
647 void
648 CertificateChainEditor::update_private_key ()
649 {
650         checked_set (_private_key, dcp::private_key_fingerprint (_get()->key().get()));
651         _sizer->Layout ();
652 }
653
654 void
655 CertificateChainEditor::import_private_key ()
656 {
657         wxFileDialog* d = new wxFileDialog (this, _("Select Key File"));
658
659         if (d->ShowModal() == wxID_OK) {
660                 try {
661                         boost::filesystem::path p (wx_to_std (d->GetPath ()));
662                         if (boost::filesystem::file_size (p) > 8192) {
663                                 error_dialog (
664                                         this,
665                                         wxString::Format (_("Could not read key file; file is too long (%s)"), std_to_wx (p.string ()))
666                                         );
667                                 return;
668                         }
669
670                         shared_ptr<dcp::CertificateChain> chain(new dcp::CertificateChain(*_get().get()));
671                         chain->set_key (dcp::file_to_string (p));
672                         _set (chain);
673                         update_private_key ();
674                 } catch (dcp::MiscError& e) {
675                         error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
676                 }
677         }
678
679         d->Destroy ();
680
681         update_sensitivity ();
682 }
683
684 void
685 CertificateChainEditor::export_private_key ()
686 {
687         optional<string> key = _get()->key();
688         if (!key) {
689                 return;
690         }
691
692         wxFileDialog* d = new wxFileDialog (
693                 this, _("Select Key File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
694                 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
695                 );
696
697         if (d->ShowModal () == wxID_OK) {
698                 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
699                 if (!f) {
700                         throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
701                 }
702
703                 string const s = _get()->key().get ();
704                 fwrite (s.c_str(), 1, s.length(), f);
705                 fclose (f);
706         }
707         d->Destroy ();
708 }
709
710 wxString
711 KeysPage::GetName () const
712 {
713         return _("Keys");
714 }
715
716 void
717 KeysPage::setup ()
718 {
719         wxFont subheading_font (*wxNORMAL_FONT);
720         subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
721
722         wxSizer* sizer = _panel->GetSizer();
723
724         {
725                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Decrypting KDMs"));
726                 m->SetFont (subheading_font);
727                 sizer->Add (m, 0, wxALL, _border);
728         }
729
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);
740
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));
746
747         {
748                 wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Signing DCPs and KDMs"));
749                 m->SetFont (subheading_font);
750                 sizer->Add (m, 0, wxALL, _border);
751         }
752
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));
756 }
757
758 void
759 KeysPage::decryption_advanced ()
760 {
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)
766                 );
767
768         c->ShowModal();
769 }
770
771 void
772 KeysPage::signing_advanced ()
773 {
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 ()),
778                 bind (&do_nothing)
779                 );
780
781         c->ShowModal();
782 }
783
784 void
785 KeysPage::export_decryption_chain_and_key ()
786 {
787         wxFileDialog* d = new wxFileDialog (
788                 _panel, _("Select Export File"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom"),
789                 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
790                 );
791
792         if (d->ShowModal () == wxID_OK) {
793                 FILE* f = fopen_boost (path_from_file_dialog (d, "dom"), "w");
794                 if (!f) {
795                         throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
796                 }
797
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);
803                 fclose (f);
804         }
805         d->Destroy ();
806
807 }
808
809 void
810 KeysPage::import_decryption_chain_and_key ()
811 {
812         wxFileDialog* d = new wxFileDialog (
813                 _panel, _("Select File To Import"), wxEmptyString, wxEmptyString, wxT ("DOM files (*.dom)|*.dom")
814                 );
815
816         if (d->ShowModal () == wxID_OK) {
817                 shared_ptr<dcp::CertificateChain> new_chain(new dcp::CertificateChain());
818
819                 FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "r");
820                 if (!f) {
821                         throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
822                 }
823
824                 string current;
825                 while (!feof (f)) {
826                         char buffer[128];
827                         if (fgets (buffer, 128, f) == 0) {
828                                 break;
829                         }
830                         current += buffer;
831                         if (strncmp (buffer, "-----END CERTIFICATE-----", 25) == 0) {
832                                 new_chain->add (dcp::Certificate (current));
833                                 current = "";
834                         } else if (strncmp (buffer, "-----END RSA PRIVATE KEY-----", 29) == 0) {
835                                 new_chain->set_key (current);
836                                 current = "";
837                         }
838                 }
839                 fclose (f);
840
841                 if (new_chain->chain_valid() && new_chain->private_key_valid()) {
842                         Config::instance()->set_decryption_chain (new_chain);
843                 } else {
844                         error_dialog (_panel, _("Invalid DCP-o-matic export file"));
845                 }
846         }
847         d->Destroy ();
848 }
849
850 void
851 KeysPage::nag_remake_decryption_chain ()
852 {
853         NagDialog::maybe_nag (
854                 _panel,
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!")
857                 );
858 }
859
860 void
861 KeysPage::export_decryption_chain ()
862 {
863         wxFileDialog* d = new wxFileDialog (
864                 _panel, _("Select Chain File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
865                 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
866                 );
867
868         if (d->ShowModal () == wxID_OK) {
869                 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
870                 if (!f) {
871                         throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
872                 }
873
874                 string const s = Config::instance()->decryption_chain()->chain();
875                 fwrite (s.c_str(), 1, s.length(), f);
876                 fclose (f);
877         }
878         d->Destroy ();
879 }
880
881 void
882 KeysPage::export_decryption_certificate ()
883 {
884         wxFileDialog* d = new wxFileDialog (
885                 _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
886                 wxFD_SAVE | wxFD_OVERWRITE_PROMPT
887                 );
888
889         if (d->ShowModal () == wxID_OK) {
890                 FILE* f = fopen_boost (path_from_file_dialog (d, "pem"), "w");
891                 if (!f) {
892                         throw OpenFileError (wx_to_std (d->GetPath ()), errno, false);
893                 }
894
895                 string const s = Config::instance()->decryption_chain()->leaf().certificate (true);
896                 fwrite (s.c_str(), 1, s.length(), f);
897                 fclose (f);
898         }
899         d->Destroy ();
900 }