Give GROK its own config changed category.
[dcpomatic.git] / src / lib / config.cc
1 /*
2     Copyright (C) 2012-2022 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
22 #include "cinema.h"
23 #include "colour_conversion.h"
24 #include "compose.hpp"
25 #include "config.h"
26 #include "constants.h"
27 #include "cross.h"
28 #include "dcp_content_type.h"
29 #include "dkdm_recipient.h"
30 #include "dkdm_wrapper.h"
31 #include "film.h"
32 #include "filter.h"
33 #include "log.h"
34 #include "ratio.h"
35 #include "zipper.h"
36 #include <dcp/certificate_chain.h>
37 #include <dcp/name_format.h>
38 #include <dcp/raw_convert.h>
39 #include <libcxml/cxml.h>
40 #include <glib.h>
41 #include <libxml++/libxml++.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/algorithm/string.hpp>
44 #include <boost/thread.hpp>
45 #include <cstdlib>
46 #include <fstream>
47 #include <iostream>
48
49 #include "i18n.h"
50
51
52 using std::cout;
53 using std::dynamic_pointer_cast;
54 using std::ifstream;
55 using std::list;
56 using std::make_shared;
57 using std::max;
58 using std::min;
59 using std::remove;
60 using std::shared_ptr;
61 using std::string;
62 using std::vector;
63 using boost::algorithm::trim;
64 using boost::optional;
65 using dcp::raw_convert;
66
67
68 Config* Config::_instance = 0;
69 int const Config::_current_version = 3;
70 boost::signals2::signal<void (Config::LoadFailure)> Config::FailedToLoad;
71 boost::signals2::signal<void (string)> Config::Warning;
72 boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
73
74
75 /** Construct default configuration */
76 Config::Config ()
77         /* DKDMs are not considered a thing to reset on set_defaults() */
78         : _dkdms (new DKDMGroup ("root"))
79         , _default_kdm_duration (1, RoughDuration::Unit::WEEKS)
80         , _export(this)
81 {
82         set_defaults ();
83 }
84
85 void
86 Config::set_defaults ()
87 {
88         _master_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
89         _server_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
90         _server_port_base = 6192;
91         _use_any_servers = true;
92         _servers.clear ();
93         _only_servers_encode = false;
94         _tms_protocol = FileTransferProtocol::SCP;
95         _tms_passive = true;
96         _tms_ip = "";
97         _tms_path = ".";
98         _tms_user = "";
99         _tms_password = "";
100         _allow_any_dcp_frame_rate = false;
101         _allow_any_container = false;
102         _allow_96khz_audio = false;
103         _use_all_audio_channels = false;
104         _show_experimental_audio_processors = false;
105         _language = optional<string> ();
106         _default_still_length = 10;
107         _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
108         _default_dcp_audio_channels = 8;
109         _default_j2k_bandwidth = 150000000;
110         _default_audio_delay = 0;
111         _default_interop = false;
112         _default_metadata.clear ();
113         _upload_after_make_dcp = false;
114         _mail_server = "";
115         _mail_port = 25;
116         _mail_protocol = EmailProtocol::AUTO;
117         _mail_user = "";
118         _mail_password = "";
119         _kdm_from = "";
120         _kdm_cc.clear ();
121         _kdm_bcc = "";
122         _notification_from = "";
123         _notification_to = "";
124         _notification_cc.clear ();
125         _notification_bcc = "";
126         _check_for_updates = false;
127         _check_for_test_updates = false;
128         _maximum_j2k_bandwidth = 250000000;
129         _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK;
130         _analyse_ebur128 = true;
131         _automatic_audio_analysis = false;
132 #ifdef DCPOMATIC_WINDOWS
133         _win32_console = false;
134 #endif
135         /* At the moment we don't write these files anywhere new after a version change, so they will be read from
136          * ~/.config/dcpomatic2 (or equivalent) and written back there.
137          */
138         _cinemas_file = read_path ("cinemas.xml");
139         _dkdm_recipients_file = read_path ("dkdm_recipients.xml");
140         _show_hints_before_make_dcp = true;
141         _confirm_kdm_email = true;
142         _kdm_container_name_format = dcp::NameFormat("KDM_%f_%c");
143         _kdm_filename_format = dcp::NameFormat("KDM_%f_%c_%s");
144         _dkdm_filename_format = dcp::NameFormat("DKDM_%f_%c_%s");
145         _dcp_metadata_filename_format = dcp::NameFormat ("%t");
146         _dcp_asset_filename_format = dcp::NameFormat ("%t");
147         _jump_to_selected = true;
148         for (int i = 0; i < NAG_COUNT; ++i) {
149                 _nagged[i] = false;
150         }
151         _sound = true;
152         _sound_output = optional<string> ();
153         _last_kdm_write_type = KDM_WRITE_FLAT;
154         _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
155         _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
156
157         /* I think the scaling factor here should be the ratio of the longest frame
158            encode time to the shortest; if the thread count is T, longest time is L
159            and the shortest time S we could encode L/S frames per thread whilst waiting
160            for the L frame to encode so we might have to store LT/S frames.
161
162            However we don't want to use too much memory, so keep it a bit lower than we'd
163            perhaps like.  A J2K frame is typically about 1Mb so 3 here will mean we could
164            use about 240Mb with 72 encoding threads.
165         */
166         _frames_in_memory_multiplier = 3;
167         _decode_reduction = optional<int>();
168         _default_notify = false;
169         for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
170                 _notification[i] = false;
171         }
172         _barco_username = optional<string>();
173         _barco_password = optional<string>();
174         _christie_username = optional<string>();
175         _christie_password = optional<string>();
176         _gdc_username = optional<string>();
177         _gdc_password = optional<string>();
178         _player_mode = PLAYER_MODE_WINDOW;
179         _image_display = 0;
180         _video_view_type = VIDEO_VIEW_SIMPLE;
181         _respect_kdm_validity_periods = true;
182         _player_debug_log_file = boost::none;
183         _player_content_directory = boost::none;
184         _player_playlist_directory = boost::none;
185         _player_kdm_directory = boost::none;
186         _audio_mapping = boost::none;
187         _custom_languages.clear ();
188         _initial_paths.clear();
189         _initial_paths["AddFilesPath"] = boost::none;
190         _initial_paths["AddKDMPath"] = boost::none;
191         _initial_paths["AddDKDMPath"] = boost::none;
192         _initial_paths["SelectCertificatePath"] = boost::none;
193         _initial_paths["AddCombinerInputPath"] = boost::none;
194         _use_isdcf_name_by_default = true;
195         _write_kdms_to_disk = true;
196         _email_kdms = false;
197         _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1;
198         _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
199         _auto_crop_threshold = 0.1;
200         _last_release_notes_version = boost::none;
201         _allow_smpte_bv20 = false;
202         _isdcf_name_part_length = 14;
203
204         _allowed_dcp_frame_rates.clear ();
205         _allowed_dcp_frame_rates.push_back (24);
206         _allowed_dcp_frame_rates.push_back (25);
207         _allowed_dcp_frame_rates.push_back (30);
208         _allowed_dcp_frame_rates.push_back (48);
209         _allowed_dcp_frame_rates.push_back (50);
210         _allowed_dcp_frame_rates.push_back (60);
211
212         set_kdm_email_to_default ();
213         set_notification_email_to_default ();
214         set_cover_sheet_to_default ();
215
216 #ifdef DCPOMATIC_GROK
217         _grok = boost::none;
218 #endif
219
220         _main_divider_sash_position = {};
221         _main_content_divider_sash_position = {};
222
223         _export.set_defaults();
224 }
225
226 void
227 Config::restore_defaults ()
228 {
229         Config::instance()->set_defaults ();
230         Config::instance()->changed ();
231 }
232
233 shared_ptr<dcp::CertificateChain>
234 Config::create_certificate_chain ()
235 {
236         return make_shared<dcp::CertificateChain> (
237                 openssl_path(),
238                 CERTIFICATE_VALIDITY_PERIOD,
239                 "dcpomatic.com",
240                 "dcpomatic.com",
241                 ".dcpomatic.smpte-430-2.ROOT",
242                 ".dcpomatic.smpte-430-2.INTERMEDIATE",
243                 "CS.dcpomatic.smpte-430-2.LEAF"
244                 );
245 }
246
247 void
248 Config::backup ()
249 {
250         using namespace boost::filesystem;
251
252         auto copy_adding_number = [](path const& path_to_copy) {
253
254                 auto add_number = [](path const& path, int number) {
255                         return String::compose("%1.%2", path, number);
256                 };
257
258                 int n = 1;
259                 while (n < 100 && exists(add_number(path_to_copy, n))) {
260                         ++n;
261                 }
262                 boost::system::error_code ec;
263                 copy_file(path_to_copy, add_number(path_to_copy, n), ec);
264         };
265
266         /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about
267          * to write over.  This is more intended for the situation where we have a corrupted config.xml,
268          * and decide to overwrite it with a new one (possibly losing important details in the corrupted
269          * file).  But we might as well back up the other files while we're about it.
270          */
271
272         /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy
273          * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1
274          */
275         copy_adding_number (config_write_file());
276
277         /* These do not use State::write_path, so whatever path is in the Config we will copy
278          * adding a number.
279          */
280         copy_adding_number (_cinemas_file);
281         copy_adding_number (_dkdm_recipients_file);
282 }
283
284 void
285 Config::read ()
286 {
287         read_config();
288         read_cinemas();
289         read_dkdm_recipients();
290 }
291
292
293 void
294 Config::read_config()
295 try
296 {
297         cxml::Document f ("Config");
298         f.read_file(dcp::filesystem::fix_long_path(config_read_file()));
299
300         auto version = f.optional_number_child<int> ("Version");
301         if (version && *version < _current_version) {
302                 /* Back up the old config before we re-write it in a back-incompatible way */
303                 backup ();
304         }
305
306         if (f.optional_number_child<int>("NumLocalEncodingThreads")) {
307                 _master_encoding_threads = _server_encoding_threads = f.optional_number_child<int>("NumLocalEncodingThreads").get();
308         } else {
309                 _master_encoding_threads = f.number_child<int>("MasterEncodingThreads");
310                 _server_encoding_threads = f.number_child<int>("ServerEncodingThreads");
311         }
312
313         _default_directory = f.optional_string_child ("DefaultDirectory");
314         if (_default_directory && _default_directory->empty ()) {
315                 /* We used to store an empty value for this to mean "none set" */
316                 _default_directory = boost::optional<boost::filesystem::path> ();
317         }
318
319         auto b = f.optional_number_child<int> ("ServerPort");
320         if (!b) {
321                 b = f.optional_number_child<int> ("ServerPortBase");
322         }
323         _server_port_base = b.get ();
324
325         auto u = f.optional_bool_child ("UseAnyServers");
326         _use_any_servers = u.get_value_or (true);
327
328         for (auto i: f.node_children("Server")) {
329                 if (i->node_children("HostName").size() == 1) {
330                         _servers.push_back (i->string_child ("HostName"));
331                 } else {
332                         _servers.push_back (i->content ());
333                 }
334         }
335
336         _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false);
337         _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
338         _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
339         _tms_ip = f.string_child ("TMSIP");
340         _tms_path = f.string_child ("TMSPath");
341         _tms_user = f.string_child ("TMSUser");
342         _tms_password = f.string_child ("TMSPassword");
343
344         _language = f.optional_string_child ("Language");
345
346         _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
347         _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
348
349         if (f.optional_string_child ("DCPMetadataIssuer")) {
350                 _dcp_issuer = f.string_child ("DCPMetadataIssuer");
351         } else if (f.optional_string_child ("DCPIssuer")) {
352                 _dcp_issuer = f.string_child ("DCPIssuer");
353         }
354
355         auto up = f.optional_bool_child("UploadAfterMakeDCP");
356         if (!up) {
357                 up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
358         }
359         _upload_after_make_dcp = up.get_value_or (false);
360         _dcp_creator = f.optional_string_child ("DCPCreator").get_value_or ("");
361         _dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
362         _dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
363         _dcp_product_version = f.optional_string_child("DCPProductVersion").get_value_or("");
364         _dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
365
366         _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
367         _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
368         _default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
369         _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
370
371         try {
372                 auto al = f.optional_string_child("DefaultAudioLanguage");
373                 if (al) {
374                         _default_audio_language = dcp::LanguageTag(*al);
375                 }
376         } catch (std::runtime_error&) {}
377
378         try {
379                 auto te = f.optional_string_child("DefaultTerritory");
380                 if (te) {
381                         _default_territory = dcp::LanguageTag::RegionSubtag(*te);
382                 }
383         } catch (std::runtime_error&) {}
384
385         for (auto const& i: f.node_children("DefaultMetadata")) {
386                 _default_metadata[i->string_attribute("key")] = i->content();
387         }
388
389         _default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
390
391         /* Read any cinemas that are still lying around in the config file
392          * from an old version.
393          */
394         read_cinemas (f);
395
396         _mail_server = f.string_child ("MailServer");
397         _mail_port = f.optional_number_child<int> ("MailPort").get_value_or (25);
398
399         {
400                 /* Make sure this matches the code in write_config */
401                 string const protocol = f.optional_string_child("MailProtocol").get_value_or("Auto");
402                 if (protocol == "Auto") {
403                         _mail_protocol = EmailProtocol::AUTO;
404                 } else if (protocol == "Plain") {
405                         _mail_protocol = EmailProtocol::PLAIN;
406                 } else if (protocol == "STARTTLS") {
407                         _mail_protocol = EmailProtocol::STARTTLS;
408                 } else if (protocol == "SSL") {
409                         _mail_protocol = EmailProtocol::SSL;
410                 }
411         }
412
413         _mail_user = f.optional_string_child("MailUser").get_value_or ("");
414         _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
415
416         _kdm_subject = f.optional_string_child ("KDMSubject").get_value_or (_("KDM delivery: $CPL_NAME"));
417         _kdm_from = f.string_child ("KDMFrom");
418         for (auto i: f.node_children("KDMCC")) {
419                 if (!i->content().empty()) {
420                         _kdm_cc.push_back (i->content ());
421                 }
422         }
423         _kdm_bcc = f.optional_string_child ("KDMBCC").get_value_or ("");
424         _kdm_email = f.string_child ("KDMEmail");
425
426         _notification_subject = f.optional_string_child("NotificationSubject").get_value_or(_("DCP-o-matic notification"));
427         _notification_from = f.optional_string_child("NotificationFrom").get_value_or("");
428         _notification_to = f.optional_string_child("NotificationTo").get_value_or("");
429         for (auto i: f.node_children("NotificationCC")) {
430                 if (!i->content().empty()) {
431                         _notification_cc.push_back (i->content ());
432                 }
433         }
434         _notification_bcc = f.optional_string_child("NotificationBCC").get_value_or("");
435         if (f.optional_string_child("NotificationEmail")) {
436                 _notification_email = f.string_child("NotificationEmail");
437         }
438
439         _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
440         _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
441
442         _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
443         _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false);
444         _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false);
445         _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false);
446         _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false);
447         _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false);
448
449         _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR);
450         _analyse_ebur128 = f.optional_bool_child("AnalyseEBUR128").get_value_or (true);
451         _automatic_audio_analysis = f.optional_bool_child ("AutomaticAudioAnalysis").get_value_or (false);
452 #ifdef DCPOMATIC_WINDOWS
453         _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
454 #endif
455
456         for (auto i: f.node_children("History")) {
457                 _history.push_back (i->content ());
458         }
459
460         for (auto i: f.node_children("PlayerHistory")) {
461                 _player_history.push_back (i->content ());
462         }
463
464         auto signer = f.optional_node_child ("Signer");
465         if (signer) {
466                 auto c = make_shared<dcp::CertificateChain>();
467                 /* Read the signing certificates and private key in from the config file */
468                 for (auto i: signer->node_children ("Certificate")) {
469                         c->add (dcp::Certificate (i->content ()));
470                 }
471                 c->set_key (signer->string_child ("PrivateKey"));
472                 _signer_chain = c;
473         } else {
474                 /* Make a new set of signing certificates and key */
475                 _signer_chain = create_certificate_chain ();
476         }
477
478         auto decryption = f.optional_node_child ("Decryption");
479         if (decryption) {
480                 auto c = make_shared<dcp::CertificateChain>();
481                 for (auto i: decryption->node_children ("Certificate")) {
482                         c->add (dcp::Certificate (i->content ()));
483                 }
484                 c->set_key (decryption->string_child ("PrivateKey"));
485                 _decryption_chain = c;
486         } else {
487                 _decryption_chain = create_certificate_chain ();
488         }
489
490         /* These must be done before we call Bad as that might set one
491            of the nags.
492         */
493         for (auto i: f.node_children("Nagged")) {
494                 auto const id = number_attribute<int>(i, "Id", "id");
495                 if (id >= 0 && id < NAG_COUNT) {
496                         _nagged[id] = raw_convert<int>(i->content());
497                 }
498         }
499
500         auto bad = check_certificates ();
501         if (bad) {
502                 auto const remake = Bad(*bad);
503                 if (remake && *remake) {
504                         switch (*bad) {
505                         case BAD_SIGNER_UTF8_STRINGS:
506                         case BAD_SIGNER_INCONSISTENT:
507                         case BAD_SIGNER_VALIDITY_TOO_LONG:
508                                 _signer_chain = create_certificate_chain ();
509                                 break;
510                         case BAD_DECRYPTION_INCONSISTENT:
511                                 _decryption_chain = create_certificate_chain ();
512                                 break;
513                         }
514                 }
515         }
516
517         if (f.optional_node_child("DKDMGroup")) {
518                 /* New-style: all DKDMs in a group */
519                 _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
520         } else {
521                 /* Old-style: one or more DKDM nodes */
522                 _dkdms = make_shared<DKDMGroup>("root");
523                 for (auto i: f.node_children("DKDM")) {
524                         _dkdms->add (DKDMBase::read (i));
525                 }
526         }
527         _cinemas_file = f.optional_string_child("CinemasFile").get_value_or(read_path("cinemas.xml").string());
528         _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(read_path("dkdm_recipients.xml").string());
529         _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
530         _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
531         _kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
532         _kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
533         _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f %c %s"));
534         _dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
535         _dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
536         _jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
537         /* The variable was renamed but not the XML tag */
538         _sound = f.optional_bool_child("PreviewSound").get_value_or (true);
539         _sound_output = f.optional_string_child("PreviewSoundOutput");
540         if (f.optional_string_child("CoverSheet")) {
541                 _cover_sheet = f.optional_string_child("CoverSheet").get();
542         }
543         _last_player_load_directory = f.optional_string_child("LastPlayerLoadDirectory");
544         if (f.optional_string_child("LastKDMWriteType")) {
545                 if (f.optional_string_child("LastKDMWriteType").get() == "flat") {
546                         _last_kdm_write_type = KDM_WRITE_FLAT;
547                 } else if (f.optional_string_child("LastKDMWriteType").get() == "folder") {
548                         _last_kdm_write_type = KDM_WRITE_FOLDER;
549                 } else if (f.optional_string_child("LastKDMWriteType").get() == "zip") {
550                         _last_kdm_write_type = KDM_WRITE_ZIP;
551                 }
552         }
553         if (f.optional_string_child("LastDKDMWriteType")) {
554                 if (f.optional_string_child("LastDKDMWriteType").get() == "internal") {
555                         _last_dkdm_write_type = DKDM_WRITE_INTERNAL;
556                 } else if (f.optional_string_child("LastDKDMWriteType").get() == "file") {
557                         _last_dkdm_write_type = DKDM_WRITE_FILE;
558                 }
559         }
560         _frames_in_memory_multiplier = f.optional_number_child<int>("FramesInMemoryMultiplier").get_value_or(3);
561         _decode_reduction = f.optional_number_child<int>("DecodeReduction");
562         _default_notify = f.optional_bool_child("DefaultNotify").get_value_or(false);
563
564         for (auto i: f.node_children("Notification")) {
565                 int const id = number_attribute<int>(i, "Id", "id");
566                 if (id >= 0 && id < NOTIFICATION_COUNT) {
567                         _notification[id] = raw_convert<int>(i->content());
568                 }
569         }
570
571         _barco_username = f.optional_string_child("BarcoUsername");
572         _barco_password = f.optional_string_child("BarcoPassword");
573         _christie_username = f.optional_string_child("ChristieUsername");
574         _christie_password = f.optional_string_child("ChristiePassword");
575         _gdc_username = f.optional_string_child("GDCUsername");
576         _gdc_password = f.optional_string_child("GDCPassword");
577
578         auto pm = f.optional_string_child("PlayerMode");
579         if (pm && *pm == "window") {
580                 _player_mode = PLAYER_MODE_WINDOW;
581         } else if (pm && *pm == "full") {
582                 _player_mode = PLAYER_MODE_FULL;
583         } else if (pm && *pm == "dual") {
584                 _player_mode = PLAYER_MODE_DUAL;
585         }
586
587         _image_display = f.optional_number_child<int>("ImageDisplay").get_value_or(0);
588         auto vc = f.optional_string_child("VideoViewType");
589         if (vc && *vc == "opengl") {
590                 _video_view_type = VIDEO_VIEW_OPENGL;
591         } else if (vc && *vc == "simple") {
592                 _video_view_type = VIDEO_VIEW_SIMPLE;
593         }
594         _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
595         _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
596         _player_content_directory = f.optional_string_child("PlayerContentDirectory");
597         _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
598         _player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
599
600         if (f.optional_node_child("AudioMapping")) {
601                 _audio_mapping = AudioMapping (f.node_child("AudioMapping"), Film::current_state_version);
602         }
603
604         for (auto i: f.node_children("CustomLanguage")) {
605                 try {
606                         /* This will fail if it's called before dcp::init() as it won't recognise the
607                          * tag.  That's OK because the Config will be reloaded again later.
608                          */
609                         _custom_languages.push_back (dcp::LanguageTag(i->content()));
610                 } catch (std::runtime_error& e) {}
611         }
612
613         for (auto& initial: _initial_paths) {
614                 initial.second = f.optional_string_child(initial.first);
615         }
616         _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true);
617         _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true);
618         _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false);
619         _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1"));
620         if (auto duration = f.optional_node_child("DefaultKDMDuration")) {
621                 _default_kdm_duration = RoughDuration(duration);
622         } else {
623                 _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
624         }
625         _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
626         _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion");
627         _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
628         _main_content_divider_sash_position = f.optional_number_child<int>("MainContentDividerSashPosition");
629
630         if (auto loc = f.optional_string_child("DefaultAddFileLocation")) {
631                 if (*loc == "last") {
632                         _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME;
633                 } else if (*loc == "project") {
634                         _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT;
635                 }
636         }
637
638         _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false);
639         _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
640
641 #ifdef DCPOMATIC_GROK
642         if (auto grok = f.optional_node_child("Grok")) {
643                 _grok = Grok(grok);
644         }
645 #endif
646
647         _export.read(f.optional_node_child("Export"));
648 }
649 catch (...) {
650         if (have_existing("config.xml")) {
651                 backup ();
652                 /* We have a config file but it didn't load */
653                 FailedToLoad(LoadFailure::CONFIG);
654         }
655         set_defaults ();
656         /* Make a new set of signing certificates and key */
657         _signer_chain = create_certificate_chain ();
658         /* And similar for decryption of KDMs */
659         _decryption_chain = create_certificate_chain ();
660         write_config();
661 }
662
663
664 void
665 Config::read_cinemas()
666 {
667         if (dcp::filesystem::exists(_cinemas_file)) {
668                 try {
669                         cxml::Document f("Cinemas");
670                         f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
671                         read_cinemas(f);
672                 } catch (...) {
673                         backup();
674                         FailedToLoad(LoadFailure::CINEMAS);
675                         write_cinemas();
676                 }
677         }
678 }
679
680
681 void
682 Config::read_dkdm_recipients()
683 {
684         if (dcp::filesystem::exists(_dkdm_recipients_file)) {
685                 try {
686                         cxml::Document f("DKDMRecipients");
687                         f.read_file(dcp::filesystem::fix_long_path(_dkdm_recipients_file));
688                         read_dkdm_recipients(f);
689                 } catch (...) {
690                         backup();
691                         FailedToLoad(LoadFailure::DKDM_RECIPIENTS);
692                         write_dkdm_recipients();
693                 }
694         }
695 }
696
697
698 /** @return Singleton instance */
699 Config *
700 Config::instance ()
701 {
702         if (_instance == nullptr) {
703                 _instance = new Config;
704                 _instance->read ();
705         }
706
707         return _instance;
708 }
709
710 /** Write our configuration to disk */
711 void
712 Config::write () const
713 {
714         write_config ();
715         write_cinemas ();
716         write_dkdm_recipients ();
717 }
718
719 void
720 Config::write_config () const
721 {
722         xmlpp::Document doc;
723         auto root = doc.create_root_node ("Config");
724
725         /* [XML] Version The version number of the configuration file format. */
726         root->add_child("Version")->add_child_text (raw_convert<string>(_current_version));
727         /* [XML] MasterEncodingThreads Number of encoding threads to use when running as master. */
728         root->add_child("MasterEncodingThreads")->add_child_text (raw_convert<string> (_master_encoding_threads));
729         /* [XML] ServerEncodingThreads Number of encoding threads to use when running as server. */
730         root->add_child("ServerEncodingThreads")->add_child_text (raw_convert<string> (_server_encoding_threads));
731         if (_default_directory) {
732                 /* [XML:opt] DefaultDirectory Default directory when creating a new film in the GUI. */
733                 root->add_child("DefaultDirectory")->add_child_text (_default_directory->string ());
734         }
735         /* [XML] ServerPortBase Port number to use for frame encoding requests.  <code>ServerPortBase</code> + 1 and
736            <code>ServerPortBase</code> + 2 are used for querying servers.  <code>ServerPortBase</code> + 3 is used
737            by the batch converter to listen for job requests.
738         */
739         root->add_child("ServerPortBase")->add_child_text (raw_convert<string> (_server_port_base));
740         /* [XML] UseAnyServers 1 to broadcast to look for encoding servers to use, 0 to use only those configured. */
741         root->add_child("UseAnyServers")->add_child_text (_use_any_servers ? "1" : "0");
742
743         for (auto i: _servers) {
744                 /* [XML:opt] Server IP address or hostname of an encoding server to use; you can use as many of these tags
745                    as you like.
746                 */
747                 root->add_child("Server")->add_child_text (i);
748         }
749
750         /* [XML] OnlyServersEncode 1 to set the master to do decoding of source content no JPEG2000 encoding; all encoding
751            is done by the encoding servers.  0 to set the master to do some encoding as well as coordinating the job.
752         */
753         root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0");
754         /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
755         root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (static_cast<int> (_tms_protocol)));
756         /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
757         root->add_child("TMSPassive")->add_child_text(_tms_passive ? "1" : "0");
758         /* [XML] TMSIP IP address of TMS. */
759         root->add_child("TMSIP")->add_child_text (_tms_ip);
760         /* [XML] TMSPath Path on the TMS to copy files to. */
761         root->add_child("TMSPath")->add_child_text (_tms_path);
762         /* [XML] TMSUser Username to log into the TMS with. */
763         root->add_child("TMSUser")->add_child_text (_tms_user);
764         /* [XML] TMSPassword Password to log into the TMS with. */
765         root->add_child("TMSPassword")->add_child_text (_tms_password);
766         if (_language) {
767                 /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
768                 root->add_child("Language")->add_child_text (_language.get());
769         }
770         if (_default_dcp_content_type) {
771                 /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (<code>FTR</code>, <code>SHR</code>,
772                    <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
773                    <code>PSA</code> or <code>ADV</code>). */
774                 root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ());
775         }
776         /* [XML] DefaultDCPAudioChannels Default number of audio channels to use when creating new films. */
777         root->add_child("DefaultDCPAudioChannels")->add_child_text (raw_convert<string> (_default_dcp_audio_channels));
778         /* [XML] DCPIssuer Issuer text to write into CPL files. */
779         root->add_child("DCPIssuer")->add_child_text (_dcp_issuer);
780         /* [XML] DCPCreator Creator text to write into CPL files. */
781         root->add_child("DCPCreator")->add_child_text (_dcp_creator);
782         /* [XML] Company name to write into MXF files. */
783         root->add_child("DCPCompanyName")->add_child_text (_dcp_company_name);
784         /* [XML] Product name to write into MXF files. */
785         root->add_child("DCPProductName")->add_child_text (_dcp_product_name);
786         /* [XML] Product version to write into MXF files. */
787         root->add_child("DCPProductVersion")->add_child_text (_dcp_product_version);
788         /* [XML] Comment to write into JPEG2000 data. */
789         root->add_child("DCPJ2KComment")->add_child_text (_dcp_j2k_comment);
790         /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
791         root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
792
793         /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
794         root->add_child("DefaultStillLength")->add_child_text (raw_convert<string> (_default_still_length));
795         /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
796         root->add_child("DefaultJ2KBandwidth")->add_child_text (raw_convert<string> (_default_j2k_bandwidth));
797         /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */
798         root->add_child("DefaultAudioDelay")->add_child_text (raw_convert<string> (_default_audio_delay));
799         /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
800         root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0");
801         if (_default_audio_language) {
802                 /* [XML] DefaultAudioLanguage Default audio language to use for new films */
803                 root->add_child("DefaultAudioLanguage")->add_child_text(_default_audio_language->to_string());
804         }
805         if (_default_territory) {
806                 /* [XML] DefaultTerritory Default territory to use for new films */
807                 root->add_child("DefaultTerritory")->add_child_text(_default_territory->subtag());
808         }
809         for (auto const& i: _default_metadata) {
810                 auto c = root->add_child("DefaultMetadata");
811                 c->set_attribute("key", i.first);
812                 c->add_child_text(i.second);
813         }
814         if (_default_kdm_directory) {
815                 /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
816                 root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
817         }
818         _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration"));
819         /* [XML] MailServer Hostname of SMTP server to use. */
820         root->add_child("MailServer")->add_child_text (_mail_server);
821         /* [XML] MailPort Port number to use on SMTP server. */
822         root->add_child("MailPort")->add_child_text (raw_convert<string> (_mail_port));
823         /* [XML] MailProtocol Protocol to use on SMTP server (Auto, Plain, STARTTLS or SSL) */
824         switch (_mail_protocol) {
825         case EmailProtocol::AUTO:
826                 root->add_child("MailProtocol")->add_child_text("Auto");
827                 break;
828         case EmailProtocol::PLAIN:
829                 root->add_child("MailProtocol")->add_child_text("Plain");
830                 break;
831         case EmailProtocol::STARTTLS:
832                 root->add_child("MailProtocol")->add_child_text("STARTTLS");
833                 break;
834         case EmailProtocol::SSL:
835                 root->add_child("MailProtocol")->add_child_text("SSL");
836                 break;
837         }
838         /* [XML] MailUser Username to use on SMTP server. */
839         root->add_child("MailUser")->add_child_text (_mail_user);
840         /* [XML] MailPassword Password to use on SMTP server. */
841         root->add_child("MailPassword")->add_child_text (_mail_password);
842
843         /* [XML] KDMSubject Subject to use for KDM emails. */
844         root->add_child("KDMSubject")->add_child_text (_kdm_subject);
845         /* [XML] KDMFrom From address to use for KDM emails. */
846         root->add_child("KDMFrom")->add_child_text (_kdm_from);
847         for (auto i: _kdm_cc) {
848                 /* [XML] KDMCC CC address to use for KDM emails; you can use as many of these tags as you like. */
849                 root->add_child("KDMCC")->add_child_text (i);
850         }
851         /* [XML] KDMBCC BCC address to use for KDM emails. */
852         root->add_child("KDMBCC")->add_child_text (_kdm_bcc);
853         /* [XML] KDMEmail Text of KDM email. */
854         root->add_child("KDMEmail")->add_child_text (_kdm_email);
855
856         /* [XML] NotificationSubject Subject to use for notification emails. */
857         root->add_child("NotificationSubject")->add_child_text (_notification_subject);
858         /* [XML] NotificationFrom From address to use for notification emails. */
859         root->add_child("NotificationFrom")->add_child_text (_notification_from);
860         /* [XML] NotificationFrom To address to use for notification emails. */
861         root->add_child("NotificationTo")->add_child_text (_notification_to);
862         for (auto i: _notification_cc) {
863                 /* [XML] NotificationCC CC address to use for notification emails; you can use as many of these tags as you like. */
864                 root->add_child("NotificationCC")->add_child_text (i);
865         }
866         /* [XML] NotificationBCC BCC address to use for notification emails. */
867         root->add_child("NotificationBCC")->add_child_text (_notification_bcc);
868         /* [XML] NotificationEmail Text of notification email. */
869         root->add_child("NotificationEmail")->add_child_text (_notification_email);
870
871         /* [XML] CheckForUpdates 1 to check dcpomatic.com for new versions, 0 to check only on request. */
872         root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
873         /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */
874         root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
875
876         /* [XML] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
877         root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
878         /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */
879         root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
880         /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */
881         root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0");
882         /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */
883         root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0");
884         /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */
885         root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0");
886         /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */
887         root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0");
888         /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related
889            to 3D, 16 debug information related to encoding, 32 debug information for timing purposes, 64 debug information related
890            to sending email, 128 debug information related to the video view, 256 information about disk writing, 512 debug information
891            related to the player, 1024 debug information related to audio analyses.
892         */
893         root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
894         /* [XML] AnalyseEBUR128 1 to do EBUR128 analyses when analysing audio, otherwise 0. */
895         root->add_child("AnalyseEBUR128")->add_child_text (_analyse_ebur128 ? "1" : "0");
896         /* [XML] AutomaticAudioAnalysis 1 to run audio analysis automatically when audio content is added to the film, otherwise 0. */
897         root->add_child("AutomaticAudioAnalysis")->add_child_text (_automatic_audio_analysis ? "1" : "0");
898 #ifdef DCPOMATIC_WINDOWS
899         if (_win32_console) {
900                 /* [XML] Win32Console 1 to open a console when running on Windows, otherwise 0.
901                  * We only write this if it's true, which is a bit of a hack to allow unit tests to work
902                  * more easily on Windows (without a platform-specific reference in config_write_utf8_test)
903                  */
904                 root->add_child("Win32Console")->add_child_text ("1");
905         }
906 #endif
907
908         /* [XML] Signer Certificate chain and private key to use when signing DCPs and KDMs.  Should contain <code>&lt;Certificate&gt;</code>
909            tags in order and a <code>&lt;PrivateKey&gt;</code> tag all containing PEM-encoded certificates or private keys as appropriate.
910         */
911         auto signer = root->add_child ("Signer");
912         DCPOMATIC_ASSERT (_signer_chain);
913         for (auto const& i: _signer_chain->unordered()) {
914                 signer->add_child("Certificate")->add_child_text (i.certificate (true));
915         }
916         signer->add_child("PrivateKey")->add_child_text (_signer_chain->key().get ());
917
918         /* [XML] Decryption Certificate chain and private key to use when decrypting KDMs */
919         auto decryption = root->add_child ("Decryption");
920         DCPOMATIC_ASSERT (_decryption_chain);
921         for (auto const& i: _decryption_chain->unordered()) {
922                 decryption->add_child("Certificate")->add_child_text (i.certificate (true));
923         }
924         decryption->add_child("PrivateKey")->add_child_text (_decryption_chain->key().get ());
925
926         /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the GUI; there can be more than one
927            of these tags.
928         */
929         for (auto i: _history) {
930                 root->add_child("History")->add_child_text (i.string ());
931         }
932
933         /* [XML] History Filename of DCP to present in the <guilabel>File</guilabel> menu of the player; there can be more than one
934            of these tags.
935         */
936         for (auto i: _player_history) {
937                 root->add_child("PlayerHistory")->add_child_text (i.string ());
938         }
939
940         /* [XML] DKDMGroup A group of DKDMs, each with a <code>Name</code> attribute, containing other <code>&lt;DKDMGroup&gt;</code>
941            or <code>&lt;DKDM&gt;</code> tags.
942         */
943         /* [XML] DKDM A DKDM as XML */
944         _dkdms->as_xml (root);
945
946         /* [XML] CinemasFile Filename of cinemas list file. */
947         root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
948         /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
949         root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
950         /* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
951         root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
952         /* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
953         root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
954         /* [XML] KDMFilenameFormat Format for KDM filenames. */
955         root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
956         /* [XML] KDMFilenameFormat Format for DKDM filenames. */
957         root->add_child("DKDMFilenameFormat")->add_child_text(_dkdm_filename_format.specification());
958         /* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
959         root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
960         /* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
961         root->add_child("DCPMetadataFilenameFormat")->add_child_text (_dcp_metadata_filename_format.specification ());
962         /* [XML] DCPAssetFilenameFormat Format for DCP asset filenames. */
963         root->add_child("DCPAssetFilenameFormat")->add_child_text (_dcp_asset_filename_format.specification ());
964         /* [XML] JumpToSelected 1 to make the GUI jump to the start of content when it is selected, otherwise 0. */
965         root->add_child("JumpToSelected")->add_child_text (_jump_to_selected ? "1" : "0");
966         /* [XML] Nagged 1 if a particular nag screen has been shown and should not be shown again, otherwise 0. */
967         for (int i = 0; i < NAG_COUNT; ++i) {
968                 xmlpp::Element* e = root->add_child ("Nagged");
969                 e->set_attribute("id", raw_convert<string>(i));
970                 e->add_child_text (_nagged[i] ? "1" : "0");
971         }
972         /* [XML] PreviewSound 1 to use sound in the GUI preview and player, otherwise 0. */
973         root->add_child("PreviewSound")->add_child_text (_sound ? "1" : "0");
974         if (_sound_output) {
975                 /* [XML:opt] PreviewSoundOutput Name of the audio output to use. */
976                 root->add_child("PreviewSoundOutput")->add_child_text (_sound_output.get());
977         }
978         /* [XML] CoverSheet Text of the cover sheet to write when making DCPs. */
979         root->add_child("CoverSheet")->add_child_text (_cover_sheet);
980         if (_last_player_load_directory) {
981                 root->add_child("LastPlayerLoadDirectory")->add_child_text(_last_player_load_directory->string());
982         }
983         /* [XML] LastKDMWriteType Last type of KDM-write: <code>flat</code> for a flat file, <code>folder</code> for a folder or <code>zip</code> for a ZIP file. */
984         if (_last_kdm_write_type) {
985                 switch (_last_kdm_write_type.get()) {
986                 case KDM_WRITE_FLAT:
987                         root->add_child("LastKDMWriteType")->add_child_text("flat");
988                         break;
989                 case KDM_WRITE_FOLDER:
990                         root->add_child("LastKDMWriteType")->add_child_text("folder");
991                         break;
992                 case KDM_WRITE_ZIP:
993                         root->add_child("LastKDMWriteType")->add_child_text("zip");
994                         break;
995                 }
996         }
997         /* [XML] LastDKDMWriteType Last type of DKDM-write: <code>file</code> for a file, <code>internal</code> to add to DCP-o-matic's list. */
998         if (_last_dkdm_write_type) {
999                 switch (_last_dkdm_write_type.get()) {
1000                 case DKDM_WRITE_INTERNAL:
1001                         root->add_child("LastDKDMWriteType")->add_child_text("internal");
1002                         break;
1003                 case DKDM_WRITE_FILE:
1004                         root->add_child("LastDKDMWriteType")->add_child_text("file");
1005                         break;
1006                 }
1007         }
1008         /* [XML] FramesInMemoryMultiplier value to multiply the encoding threads count by to get the maximum number of
1009            frames to be held in memory at once.
1010         */
1011         root->add_child("FramesInMemoryMultiplier")->add_child_text(raw_convert<string>(_frames_in_memory_multiplier));
1012
1013         /* [XML] DecodeReduction power of 2 to reduce DCP images by before decoding in the player. */
1014         if (_decode_reduction) {
1015                 root->add_child("DecodeReduction")->add_child_text(raw_convert<string>(_decode_reduction.get()));
1016         }
1017
1018         /* [XML] DefaultNotify 1 to default jobs to notify when complete, otherwise 0. */
1019         root->add_child("DefaultNotify")->add_child_text(_default_notify ? "1" : "0");
1020
1021         /* [XML] Notification 1 if a notification type is enabled, otherwise 0. */
1022         for (int i = 0; i < NOTIFICATION_COUNT; ++i) {
1023                 xmlpp::Element* e = root->add_child ("Notification");
1024                 e->set_attribute("id", raw_convert<string>(i));
1025                 e->add_child_text (_notification[i] ? "1" : "0");
1026         }
1027
1028         if (_barco_username) {
1029                 /* [XML] BarcoUsername Username for logging into Barco's servers when downloading server certificates. */
1030                 root->add_child("BarcoUsername")->add_child_text(*_barco_username);
1031         }
1032         if (_barco_password) {
1033                 /* [XML] BarcoPassword Password for logging into Barco's servers when downloading server certificates. */
1034                 root->add_child("BarcoPassword")->add_child_text(*_barco_password);
1035         }
1036
1037         if (_christie_username) {
1038                 /* [XML] ChristieUsername Username for logging into Christie's servers when downloading server certificates. */
1039                 root->add_child("ChristieUsername")->add_child_text(*_christie_username);
1040         }
1041         if (_christie_password) {
1042                 /* [XML] ChristiePassword Password for logging into Christie's servers when downloading server certificates. */
1043                 root->add_child("ChristiePassword")->add_child_text(*_christie_password);
1044         }
1045
1046         if (_gdc_username) {
1047                 /* [XML] GDCUsername Username for logging into GDC's servers when downloading server certificates. */
1048                 root->add_child("GDCUsername")->add_child_text(*_gdc_username);
1049         }
1050         if (_gdc_password) {
1051                 /* [XML] GDCPassword Password for logging into GDC's servers when downloading server certificates. */
1052                 root->add_child("GDCPassword")->add_child_text(*_gdc_password);
1053         }
1054
1055         /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
1056            with controls on another monitor.
1057         */
1058         switch (_player_mode) {
1059         case PLAYER_MODE_WINDOW:
1060                 root->add_child("PlayerMode")->add_child_text("window");
1061                 break;
1062         case PLAYER_MODE_FULL:
1063                 root->add_child("PlayerMode")->add_child_text("full");
1064                 break;
1065         case PLAYER_MODE_DUAL:
1066                 root->add_child("PlayerMode")->add_child_text("dual");
1067                 break;
1068         }
1069
1070         /* [XML] ImageDisplay Screen number to put image on in dual-screen player mode. */
1071         root->add_child("ImageDisplay")->add_child_text(raw_convert<string>(_image_display));
1072         switch (_video_view_type) {
1073         case VIDEO_VIEW_SIMPLE:
1074                 root->add_child("VideoViewType")->add_child_text("simple");
1075                 break;
1076         case VIDEO_VIEW_OPENGL:
1077                 root->add_child("VideoViewType")->add_child_text("opengl");
1078                 break;
1079         }
1080         /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */
1081         root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0");
1082         if (_player_debug_log_file) {
1083                 /* [XML] PlayerLogFile Filename to use for player debug logs. */
1084                 root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string());
1085         }
1086         if (_player_content_directory) {
1087                 /* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
1088                 root->add_child("PlayerContentDirectory")->add_child_text(_player_content_directory->string());
1089         }
1090         if (_player_playlist_directory) {
1091                 /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
1092                 root->add_child("PlayerPlaylistDirectory")->add_child_text(_player_playlist_directory->string());
1093         }
1094         if (_player_kdm_directory) {
1095                 /* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
1096                 root->add_child("PlayerKDMDirectory")->add_child_text(_player_kdm_directory->string());
1097         }
1098         if (_audio_mapping) {
1099                 _audio_mapping->as_xml (root->add_child("AudioMapping"));
1100         }
1101         for (auto const& i: _custom_languages) {
1102                 root->add_child("CustomLanguage")->add_child_text(i.to_string());
1103         }
1104         for (auto const& initial: _initial_paths) {
1105                 if (initial.second) {
1106                         root->add_child(initial.first)->add_child_text(initial.second->string());
1107                 }
1108         }
1109         root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0");
1110         root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0");
1111         root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0");
1112         root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type));
1113         root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
1114         if (_last_release_notes_version) {
1115                 root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version);
1116         }
1117         if (_main_divider_sash_position) {
1118                 root->add_child("MainDividerSashPosition")->add_child_text(raw_convert<string>(*_main_divider_sash_position));
1119         }
1120         if (_main_content_divider_sash_position) {
1121                 root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_main_content_divider_sash_position));
1122         }
1123
1124         root->add_child("DefaultAddFileLocation")->add_child_text(
1125                 _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project"
1126                 );
1127
1128         /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */
1129         root->add_child("AllowSMPTEBv20")->add_child_text(_allow_smpte_bv20 ? "1" : "0");
1130         /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
1131         root->add_child("ISDCFNamePartLength")->add_child_text(raw_convert<string>(_isdcf_name_part_length));
1132
1133 #ifdef DCPOMATIC_GROK
1134         if (_grok) {
1135                 _grok->as_xml(root->add_child("Grok"));
1136         }
1137 #endif
1138
1139         _export.write(root->add_child("Export"));
1140
1141         auto target = config_write_file();
1142
1143         try {
1144                 auto const s = doc.write_to_string_formatted ();
1145                 boost::filesystem::path tmp (string(target.string()).append(".tmp"));
1146                 dcp::File f(tmp, "w");
1147                 if (!f) {
1148                         throw FileError (_("Could not open file for writing"), tmp);
1149                 }
1150                 f.checked_write(s.c_str(), s.bytes());
1151                 f.close();
1152                 dcp::filesystem::remove(target);
1153                 dcp::filesystem::rename(tmp, target);
1154         } catch (xmlpp::exception& e) {
1155                 string s = e.what ();
1156                 trim (s);
1157                 throw FileError (s, target);
1158         }
1159 }
1160
1161
1162 template <class T>
1163 void
1164 write_file (string root_node, string node, string version, list<shared_ptr<T>> things, boost::filesystem::path file)
1165 {
1166         xmlpp::Document doc;
1167         auto root = doc.create_root_node (root_node);
1168         root->add_child("Version")->add_child_text(version);
1169
1170         for (auto i: things) {
1171                 i->as_xml (root->add_child(node));
1172         }
1173
1174         try {
1175                 doc.write_to_file_formatted (file.string() + ".tmp");
1176                 dcp::filesystem::remove(file);
1177                 dcp::filesystem::rename(file.string() + ".tmp", file);
1178         } catch (xmlpp::exception& e) {
1179                 string s = e.what ();
1180                 trim (s);
1181                 throw FileError (s, file);
1182         }
1183 }
1184
1185
1186 void
1187 Config::write_cinemas () const
1188 {
1189         write_file ("Cinemas", "Cinema", "1", _cinemas, _cinemas_file);
1190 }
1191
1192
1193 void
1194 Config::write_dkdm_recipients () const
1195 {
1196         write_file ("DKDMRecipients", "DKDMRecipient", "1", _dkdm_recipients, _dkdm_recipients_file);
1197 }
1198
1199
1200 boost::filesystem::path
1201 Config::default_directory_or (boost::filesystem::path a) const
1202 {
1203         return directory_or (_default_directory, a);
1204 }
1205
1206 boost::filesystem::path
1207 Config::default_kdm_directory_or (boost::filesystem::path a) const
1208 {
1209         return directory_or (_default_kdm_directory, a);
1210 }
1211
1212 boost::filesystem::path
1213 Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
1214 {
1215         if (!dir) {
1216                 return a;
1217         }
1218
1219         boost::system::error_code ec;
1220         auto const e = dcp::filesystem::exists(*dir, ec);
1221         if (ec || !e) {
1222                 return a;
1223         }
1224
1225         return *dir;
1226 }
1227
1228 void
1229 Config::drop ()
1230 {
1231         delete _instance;
1232         _instance = 0;
1233 }
1234
1235 void
1236 Config::changed (Property what)
1237 {
1238         Changed (what);
1239 }
1240
1241 void
1242 Config::set_kdm_email_to_default ()
1243 {
1244         _kdm_subject = _("KDM delivery: $CPL_NAME");
1245
1246         _kdm_email = _(
1247                 "Dear Projectionist\n\n"
1248                 "Please find attached KDMs for $CPL_NAME.\n\n"
1249                 "Cinema: $CINEMA_NAME\n"
1250                 "Screen(s): $SCREENS\n\n"
1251                 "The KDMs are valid from $START_TIME until $END_TIME.\n\n"
1252                 "Best regards,\nDCP-o-matic"
1253                 );
1254 }
1255
1256 void
1257 Config::set_notification_email_to_default ()
1258 {
1259         _notification_subject = _("DCP-o-matic notification");
1260
1261         _notification_email = _(
1262                 "$JOB_NAME: $JOB_STATUS"
1263                 );
1264 }
1265
1266 void
1267 Config::reset_kdm_email ()
1268 {
1269         set_kdm_email_to_default ();
1270         changed ();
1271 }
1272
1273 void
1274 Config::reset_notification_email ()
1275 {
1276         set_notification_email_to_default ();
1277         changed ();
1278 }
1279
1280 void
1281 Config::set_cover_sheet_to_default ()
1282 {
1283         _cover_sheet = _(
1284                 "$CPL_NAME\n\n"
1285                 "CPL Filename: $CPL_FILENAME\n"
1286                 "Type: $TYPE\n"
1287                 "Format: $CONTAINER\n"
1288                 "Audio: $AUDIO\n"
1289                 "Audio Language: $AUDIO_LANGUAGE\n"
1290                 "Subtitle Language: $SUBTITLE_LANGUAGE\n"
1291                 "Length: $LENGTH\n"
1292                 "Size: $SIZE\n"
1293                 );
1294 }
1295
1296 void
1297 Config::add_to_history (boost::filesystem::path p)
1298 {
1299         add_to_history_internal (_history, p);
1300 }
1301
1302 /** Remove non-existent items from the history */
1303 void
1304 Config::clean_history ()
1305 {
1306         clean_history_internal (_history);
1307 }
1308
1309 void
1310 Config::add_to_player_history (boost::filesystem::path p)
1311 {
1312         add_to_history_internal (_player_history, p);
1313 }
1314
1315 /** Remove non-existent items from the player history */
1316 void
1317 Config::clean_player_history ()
1318 {
1319         clean_history_internal (_player_history);
1320 }
1321
1322 void
1323 Config::add_to_history_internal (vector<boost::filesystem::path>& h, boost::filesystem::path p)
1324 {
1325         /* Remove existing instances of this path in the history */
1326         h.erase (remove (h.begin(), h.end(), p), h.end ());
1327
1328         h.insert (h.begin (), p);
1329         if (h.size() > HISTORY_SIZE) {
1330                 h.pop_back ();
1331         }
1332
1333         changed (HISTORY);
1334 }
1335
1336 void
1337 Config::clean_history_internal (vector<boost::filesystem::path>& h)
1338 {
1339         auto old = h;
1340         h.clear ();
1341         for (auto i: old) {
1342                 try {
1343                         if (dcp::filesystem::is_directory(i)) {
1344                                 h.push_back (i);
1345                         }
1346                 } catch (...) {
1347                         /* We couldn't find out if it's a directory for some reason; just ignore it */
1348                 }
1349         }
1350 }
1351
1352
1353 bool
1354 Config::have_existing (string file)
1355 {
1356         return dcp::filesystem::exists(read_path(file));
1357 }
1358
1359
1360 void
1361 Config::read_cinemas (cxml::Document const & f)
1362 {
1363         _cinemas.clear ();
1364         for (auto i: f.node_children("Cinema")) {
1365                 /* Slightly grotty two-part construction of Cinema here so that we can use
1366                    shared_from_this.
1367                 */
1368                 auto cinema = make_shared<Cinema>(i);
1369                 cinema->read_screens (i);
1370                 _cinemas.push_back (cinema);
1371         }
1372 }
1373
1374 void
1375 Config::set_cinemas_file (boost::filesystem::path file)
1376 {
1377         if (file == _cinemas_file) {
1378                 return;
1379         }
1380
1381         _cinemas_file = file;
1382
1383         if (dcp::filesystem::exists(_cinemas_file)) {
1384                 /* Existing file; read it in */
1385                 cxml::Document f ("Cinemas");
1386                 f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
1387                 read_cinemas (f);
1388         }
1389
1390         changed (CINEMAS);
1391         changed (OTHER);
1392 }
1393
1394
1395 void
1396 Config::read_dkdm_recipients (cxml::Document const & f)
1397 {
1398         _dkdm_recipients.clear ();
1399         for (auto i: f.node_children("DKDMRecipient")) {
1400                 _dkdm_recipients.push_back (make_shared<DKDMRecipient>(i));
1401         }
1402 }
1403
1404
1405 void
1406 Config::save_template (shared_ptr<const Film> film, string name) const
1407 {
1408         film->write_template (template_write_path(name));
1409 }
1410
1411
1412 list<string>
1413 Config::templates () const
1414 {
1415         if (!dcp::filesystem::exists(read_path("templates"))) {
1416                 return {};
1417         }
1418
1419         list<string> n;
1420         for (auto const& i: dcp::filesystem::directory_iterator(read_path("templates"))) {
1421                 n.push_back (i.path().filename().string());
1422         }
1423         return n;
1424 }
1425
1426 bool
1427 Config::existing_template (string name) const
1428 {
1429         return dcp::filesystem::exists(template_read_path(name));
1430 }
1431
1432
1433 boost::filesystem::path
1434 Config::template_read_path (string name) const
1435 {
1436         return read_path("templates") / tidy_for_filename (name);
1437 }
1438
1439
1440 boost::filesystem::path
1441 Config::template_write_path (string name) const
1442 {
1443         return write_path("templates") / tidy_for_filename (name);
1444 }
1445
1446
1447 void
1448 Config::rename_template (string old_name, string new_name) const
1449 {
1450         dcp::filesystem::rename(template_read_path(old_name), template_write_path(new_name));
1451 }
1452
1453 void
1454 Config::delete_template (string name) const
1455 {
1456         dcp::filesystem::remove(template_write_path(name));
1457 }
1458
1459 /** @return Path to the config.xml containing the actual settings, following a link if required */
1460 boost::filesystem::path
1461 config_file (boost::filesystem::path main)
1462 {
1463         cxml::Document f ("Config");
1464         if (!dcp::filesystem::exists(main)) {
1465                 /* It doesn't exist, so there can't be any links; just return it */
1466                 return main;
1467         }
1468
1469         /* See if there's a link */
1470         try {
1471                 f.read_file(dcp::filesystem::fix_long_path(main));
1472                 auto link = f.optional_string_child("Link");
1473                 if (link) {
1474                         return *link;
1475                 }
1476         } catch (xmlpp::exception& e) {
1477                 /* There as a problem reading the main configuration file,
1478                    so there can't be a link.
1479                 */
1480         }
1481
1482         return main;
1483 }
1484
1485
1486 boost::filesystem::path
1487 Config::config_read_file ()
1488 {
1489         return config_file (read_path("config.xml"));
1490 }
1491
1492
1493 boost::filesystem::path
1494 Config::config_write_file ()
1495 {
1496         return config_file (write_path("config.xml"));
1497 }
1498
1499
1500 void
1501 Config::reset_cover_sheet ()
1502 {
1503         set_cover_sheet_to_default ();
1504         changed ();
1505 }
1506
1507 void
1508 Config::link (boost::filesystem::path new_file) const
1509 {
1510         xmlpp::Document doc;
1511         doc.create_root_node("Config")->add_child("Link")->add_child_text(new_file.string());
1512         try {
1513                 doc.write_to_file_formatted(write_path("config.xml").string());
1514         } catch (xmlpp::exception& e) {
1515                 string s = e.what ();
1516                 trim (s);
1517                 throw FileError (s, write_path("config.xml"));
1518         }
1519 }
1520
1521 void
1522 Config::copy_and_link (boost::filesystem::path new_file) const
1523 {
1524         write ();
1525         dcp::filesystem::copy_file(config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
1526         link (new_file);
1527 }
1528
1529 bool
1530 Config::have_write_permission () const
1531 {
1532         dcp::File f(config_write_file(), "r+");
1533         return static_cast<bool>(f);
1534 }
1535
1536 /** @param  output_channels Number of output channels in use.
1537  *  @return Audio mapping for this output channel count (may be a default).
1538  */
1539 AudioMapping
1540 Config::audio_mapping (int output_channels)
1541 {
1542         if (!_audio_mapping || _audio_mapping->output_channels() != output_channels) {
1543                 /* Set up a default */
1544                 _audio_mapping = AudioMapping (MAX_DCP_AUDIO_CHANNELS, output_channels);
1545                 if (output_channels == 2) {
1546                         /* Special case for stereo output.
1547                            Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
1548                            Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
1549                         */
1550                         _audio_mapping->set (dcp::Channel::LEFT,   0, 1 / sqrt(2));  // L   -> Lt
1551                         _audio_mapping->set (dcp::Channel::RIGHT,  1, 1 / sqrt(2));  // R   -> Rt
1552                         _audio_mapping->set (dcp::Channel::CENTRE, 0, 1 / 2.0);      // C   -> Lt
1553                         _audio_mapping->set (dcp::Channel::CENTRE, 1, 1 / 2.0);      // C   -> Rt
1554                         _audio_mapping->set (dcp::Channel::LFE,    0, 1 / sqrt(10)); // Lfe -> Lt
1555                         _audio_mapping->set (dcp::Channel::LFE,    1, 1 / sqrt(10)); // Lfe -> Rt
1556                         _audio_mapping->set (dcp::Channel::LS,     0, 1 / sqrt(2));  // Ls  -> Lt
1557                         _audio_mapping->set (dcp::Channel::RS,     1, 1 / sqrt(2));  // Rs  -> Rt
1558                 } else {
1559                         /* 1:1 mapping */
1560                         for (int i = 0; i < min (MAX_DCP_AUDIO_CHANNELS, output_channels); ++i) {
1561                                 _audio_mapping->set (i, i, 1);
1562                         }
1563                 }
1564         }
1565
1566         return *_audio_mapping;
1567 }
1568
1569 void
1570 Config::set_audio_mapping (AudioMapping m)
1571 {
1572         _audio_mapping = m;
1573         changed (AUDIO_MAPPING);
1574 }
1575
1576 void
1577 Config::set_audio_mapping_to_default ()
1578 {
1579         DCPOMATIC_ASSERT (_audio_mapping);
1580         auto const ch = _audio_mapping->output_channels ();
1581         _audio_mapping = boost::none;
1582         _audio_mapping = audio_mapping (ch);
1583         changed (AUDIO_MAPPING);
1584 }
1585
1586
1587 void
1588 Config::add_custom_language (dcp::LanguageTag tag)
1589 {
1590         if (find(_custom_languages.begin(), _custom_languages.end(), tag) == _custom_languages.end()) {
1591                 _custom_languages.push_back (tag);
1592                 changed ();
1593         }
1594 }
1595
1596
1597 optional<Config::BadReason>
1598 Config::check_certificates () const
1599 {
1600         optional<BadReason> bad;
1601
1602         for (auto const& i: _signer_chain->unordered()) {
1603                 if (i.has_utf8_strings()) {
1604                         bad = BAD_SIGNER_UTF8_STRINGS;
1605                 }
1606                 if ((i.not_after().year() - i.not_before().year()) > 15) {
1607                         bad = BAD_SIGNER_VALIDITY_TOO_LONG;
1608                 }
1609         }
1610
1611         if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
1612                 bad = BAD_SIGNER_INCONSISTENT;
1613         }
1614
1615         if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
1616                 bad = BAD_DECRYPTION_INCONSISTENT;
1617         }
1618
1619         return bad;
1620 }
1621
1622
1623 void
1624 save_all_config_as_zip (boost::filesystem::path zip_file)
1625 {
1626         Zipper zipper (zip_file);
1627
1628         auto config = Config::instance();
1629         zipper.add ("config.xml", dcp::file_to_string(config->config_read_file()));
1630         if (dcp::filesystem::exists(config->cinemas_file())) {
1631                 zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
1632         }
1633         if (dcp::filesystem::exists(config->dkdm_recipients_file())) {
1634                 zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
1635         }
1636
1637         zipper.close ();
1638 }
1639
1640
1641 void
1642 Config::set_initial_path(string id, boost::filesystem::path path)
1643 {
1644         auto iter = _initial_paths.find(id);
1645         DCPOMATIC_ASSERT(iter != _initial_paths.end());
1646         iter->second = path;
1647         changed();
1648 }
1649
1650
1651 optional<boost::filesystem::path>
1652 Config::initial_path(string id) const
1653 {
1654         auto iter = _initial_paths.find(id);
1655         DCPOMATIC_ASSERT(iter != _initial_paths.end());
1656         return iter->second;
1657 }
1658
1659
1660 #ifdef DCPOMATIC_GROK
1661
1662 Config::Grok::Grok(cxml::ConstNodePtr node)
1663         : enable(node->bool_child("Enable"))
1664         , binary_location(node->string_child("BinaryLocation"))
1665         , selected(node->number_child<int>("Selected"))
1666         , licence_server(node->string_child("LicenceServer"))
1667         , licence_port(node->number_child<int>("LicencePort"))
1668         , licence(node->string_child("Licence"))
1669 {
1670
1671 }
1672
1673
1674 void
1675 Config::Grok::as_xml(xmlpp::Element* node) const
1676 {
1677         node->add_child("BinaryLocation")->add_child_text(binary_location.string());
1678         node->add_child("Enable")->add_child_text((enable ? "1" : "0"));
1679         node->add_child("Selected")->add_child_text(raw_convert<string>(selected));
1680         node->add_child("LicenceServer")->add_child_text(licence_server);
1681         node->add_child("LicencePort")->add_child_text(raw_convert<string>(licence_port));
1682         node->add_child("Licence")->add_child_text(licence);
1683 }
1684
1685
1686 void
1687 Config::set_grok(Grok const& grok)
1688 {
1689         _grok = grok;
1690         changed(GROK);
1691 }
1692
1693 #endif