X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Flib%2Fconfig.cc;h=6a61aae46318b8376fe3b738bee31eacc168418a;hp=4924d13babdf16c7cb06c8a5d746cd7d59c655bf;hb=87c1ac59d40f611fed94fa4120f9c21ef2baa2d2;hpb=6e3e984162ca7a181bc7c98d90c295e88e4e7f6c diff --git a/src/lib/config.cc b/src/lib/config.cc index 4924d13ba..6a61aae46 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2021 Carl Hetherington + Copyright (C) 2012-2022 Carl Hetherington This file is part of DCP-o-matic. @@ -18,24 +18,25 @@ */ -#include "config.h" -#include "filter.h" -#include "ratio.h" -#include "types.h" -#include "log.h" -#include "dcp_content_type.h" -#include "colour_conversion.h" + #include "cinema.h" -#include "util.h" -#include "cross.h" -#include "film.h" -#include "dkdm_wrapper.h" +#include "colour_conversion.h" #include "compose.hpp" +#include "config.h" +#include "constants.h" +#include "cross.h" #include "crypto.h" +#include "dcp_content_type.h" #include "dkdm_recipient.h" -#include -#include +#include "dkdm_wrapper.h" +#include "film.h" +#include "filter.h" +#include "log.h" +#include "ratio.h" +#include "zipper.h" #include +#include +#include #include #include #include @@ -48,31 +49,36 @@ #include "i18n.h" -using std::vector; + using std::cout; +using std::dynamic_pointer_cast; using std::ifstream; -using std::string; using std::list; -using std::min; +using std::make_shared; using std::max; +using std::min; using std::remove; using std::shared_ptr; -using std::make_shared; -using boost::optional; -using std::dynamic_pointer_cast; +using std::string; +using std::vector; using boost::algorithm::trim; +using boost::optional; using dcp::raw_convert; + Config* Config::_instance = 0; int const Config::_current_version = 3; -boost::signals2::signal Config::FailedToLoad; +boost::signals2::signal Config::FailedToLoad; boost::signals2::signal Config::Warning; boost::signals2::signal Config::Bad; + /** Construct default configuration */ Config::Config () /* DKDMs are not considered a thing to reset on set_defaults() */ : _dkdms (new DKDMGroup ("root")) + , _default_kdm_duration (1, RoughDuration::Unit::WEEKS) + , _export(this) { set_defaults (); } @@ -87,16 +93,18 @@ Config::set_defaults () _servers.clear (); _only_servers_encode = false; _tms_protocol = FileTransferProtocol::SCP; + _tms_passive = true; _tms_ip = ""; _tms_path = "."; _tms_user = ""; _tms_password = ""; _allow_any_dcp_frame_rate = false; _allow_any_container = false; + _allow_96khz_audio = false; + _use_all_audio_channels = false; _show_experimental_audio_processors = false; _language = optional (); _default_still_length = 10; - _default_container = Ratio::from_id ("185"); _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR"); _default_dcp_audio_channels = 6; _default_j2k_bandwidth = 150000000; @@ -125,13 +133,16 @@ Config::set_defaults () #ifdef DCPOMATIC_WINDOWS _win32_console = false; #endif + /* At the moment we don't write these files anywhere new after a version change, so they will be read from + * ~/.config/dcpomatic2 (or equivalent) and written back there. + */ _cinemas_file = read_path ("cinemas.xml"); _dkdm_recipients_file = read_path ("dkdm_recipients.xml"); _show_hints_before_make_dcp = true; _confirm_kdm_email = true; - _kdm_container_name_format = dcp::NameFormat ("KDM %f %c"); - _kdm_filename_format = dcp::NameFormat ("KDM %f %c %s"); - _dkdm_filename_format = dcp::NameFormat ("DKDM %f %c %s"); + _kdm_container_name_format = dcp::NameFormat("KDM_%f_%c"); + _kdm_filename_format = dcp::NameFormat("KDM_%f_%c_%s"); + _dkdm_filename_format = dcp::NameFormat("DKDM_%f_%c_%s"); _dcp_metadata_filename_format = dcp::NameFormat ("%t"); _dcp_asset_filename_format = dcp::NameFormat ("%t"); _jump_to_selected = true; @@ -142,6 +153,7 @@ Config::set_defaults () _sound_output = optional (); _last_kdm_write_type = KDM_WRITE_FLAT; _last_dkdm_write_type = DKDM_WRITE_INTERNAL; + _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME; /* I think the scaling factor here should be the ratio of the longest frame encode time to the shortest; if the thread count is T, longest time is L @@ -168,14 +180,25 @@ Config::set_defaults () _image_display = 0; _video_view_type = VIDEO_VIEW_SIMPLE; _respect_kdm_validity_periods = true; - _player_activity_log_file = boost::none; _player_debug_log_file = boost::none; _player_content_directory = boost::none; _player_playlist_directory = boost::none; _player_kdm_directory = boost::none; _audio_mapping = boost::none; _custom_languages.clear (); - _add_files_path = boost::none; + _initial_paths.clear(); + _initial_paths["AddFilesPath"] = boost::none; + _initial_paths["AddDKDMPath"] = boost::none; + _initial_paths["SelectCertificatePath"] = boost::none; + _initial_paths["AddCombinerInputPath"] = boost::none; + _use_isdcf_name_by_default = true; + _write_kdms_to_disk = true; + _email_kdms = false; + _default_kdm_type = dcp::Formulation::MODIFIED_TRANSITIONAL_1; + _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS); + _auto_crop_threshold = 0.1; + _last_release_notes_version = boost::none; + _allow_smpte_bv20 = false; _allowed_dcp_frame_rates.clear (); _allowed_dcp_frame_rates.push_back (24); @@ -188,6 +211,11 @@ Config::set_defaults () set_kdm_email_to_default (); set_notification_email_to_default (); set_cover_sheet_to_default (); + + _main_divider_sash_position = {}; + _main_content_divider_sash_position = {}; + + _export.set_defaults(); } void @@ -202,6 +230,7 @@ Config::create_certificate_chain () { return make_shared ( openssl_path(), + CERTIFICATE_VALIDITY_PERIOD, "dcpomatic.com", "dcpomatic.com", ".dcpomatic.smpte-430-2.ROOT", @@ -213,21 +242,51 @@ Config::create_certificate_chain () void Config::backup () { - /* Make a copy of the configuration */ - try { + using namespace boost::filesystem; + + auto copy_adding_number = [](path const& path_to_copy) { + + auto add_number = [](path const& path, int number) { + return String::compose("%1.%2", path, number); + }; + int n = 1; - while (n < 100 && boost::filesystem::exists(write_path(String::compose("config.xml.%1", n)))) { + while (n < 100 && exists(add_number(path_to_copy, n))) { ++n; } + boost::system::error_code ec; + copy_file(path_to_copy, add_number(path_to_copy, n), ec); + }; + + /* Make a backup copy of any config.xml, cinemas.xml, dkdm_recipients.xml that we might be about + * to write over. This is more intended for the situation where we have a corrupted config.xml, + * and decide to overwrite it with a new one (possibly losing important details in the corrupted + * file). But we might as well back up the other files while we're about it. + */ + + /* This uses the State::write_path stuff so, e.g. for a current version 2.16 we might copy + * ~/.config/dcpomatic2/2.16/config.xml to ~/.config/dcpomatic2/2.16/config.xml.1 + */ + copy_adding_number (config_write_file()); - boost::filesystem::copy_file(read_path("config.xml"), write_path(String::compose("config.xml.%1", n))); - boost::filesystem::copy_file(read_path("cinemas.xml"), write_path(String::compose("cinemas.xml.%1", n))); - boost::filesystem::copy_file(read_path("dkdm_recipients.xml"), write_path(String::compose("dkdm_recipients.xml.%1", n))); - } catch (...) {} + /* These do not use State::write_path, so whatever path is in the Config we will copy + * adding a number. + */ + copy_adding_number (_cinemas_file); + copy_adding_number (_dkdm_recipients_file); } void Config::read () +{ + read_config(); + read_cinemas(); + read_dkdm_recipients(); +} + + +void +Config::read_config() try { cxml::Document f ("Config"); @@ -271,6 +330,7 @@ try _only_servers_encode = f.optional_bool_child ("OnlyServersEncode").get_value_or (false); _tms_protocol = static_cast(f.optional_number_child("TMSProtocol").get_value_or(static_cast(FileTransferProtocol::SCP))); + _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true); _tms_ip = f.string_child ("TMSIP"); _tms_path = f.string_child ("TMSPath"); _tms_user = f.string_child ("TMSUser"); @@ -278,16 +338,6 @@ try _language = f.optional_string_child ("Language"); - auto c = f.optional_string_child ("DefaultContainer"); - if (c) { - _default_container = Ratio::from_id (c.get ()); - } - - if (_default_container && !_default_container->used_for_container()) { - Warning (_("Your default container is not valid and has been changed to Flat (1.85:1)")); - _default_container = Ratio::from_id ("185"); - } - _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR")); _default_dcp_audio_channels = f.optional_number_child("DefaultDCPAudioChannels").get_value_or (6); @@ -313,6 +363,20 @@ try _default_audio_delay = f.optional_number_child("DefaultAudioDelay").get_value_or (0); _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false); + try { + auto al = f.optional_string_child("DefaultAudioLanguage"); + if (al) { + _default_audio_language = dcp::LanguageTag(*al); + } + } catch (std::runtime_error&) {} + + try { + auto te = f.optional_string_child("DefaultTerritory"); + if (te) { + _default_territory = dcp::LanguageTag::RegionSubtag(*te); + } + } catch (std::runtime_error&) {} + for (auto const& i: f.node_children("DefaultMetadata")) { _default_metadata[i->string_attribute("key")] = i->content(); } @@ -373,6 +437,8 @@ try _maximum_j2k_bandwidth = f.optional_number_child ("MaximumJ2KBandwidth").get_value_or (250000000); _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false); _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false); + _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false); + _use_all_audio_channels = f.optional_bool_child("UseAllAudioChannels").get_value_or(false); _show_experimental_audio_processors = f.optional_bool_child ("ShowExperimentalAudioProcessors").get_value_or (false); _log_types = f.optional_number_child ("LogTypes").get_value_or (LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR); @@ -426,28 +492,14 @@ try } } - optional bad; - - for (auto const& i: _signer_chain->unordered()) { - if (i.has_utf8_strings()) { - bad = BAD_SIGNER_UTF8_STRINGS; - } - } - - if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) { - bad = BAD_SIGNER_INCONSISTENT; - } - - if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) { - bad = BAD_DECRYPTION_INCONSISTENT; - } - + auto bad = check_certificates (); if (bad) { auto const remake = Bad(*bad); if (remake && *remake) { switch (*bad) { case BAD_SIGNER_UTF8_STRINGS: case BAD_SIGNER_INCONSISTENT: + case BAD_SIGNER_VALIDITY_TOO_LONG: _signer_chain = create_certificate_chain (); break; case BAD_DECRYPTION_INCONSISTENT: @@ -462,7 +514,7 @@ try _dkdms = dynamic_pointer_cast (DKDMBase::read (f.node_child("DKDMGroup"))); } else { /* Old-style: one or more DKDM nodes */ - _dkdms.reset (new DKDMGroup ("root")); + _dkdms = make_shared("root"); for (auto i: f.node_children("DKDM")) { _dkdms->add (DKDMBase::read (i)); } @@ -535,11 +587,6 @@ try _video_view_type = VIDEO_VIEW_SIMPLE; } _respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true); - /* PlayerLogFile is old name */ - _player_activity_log_file = f.optional_string_child("PlayerLogFile"); - if (!_player_activity_log_file) { - _player_activity_log_file = f.optional_string_child("PlayerActivityLogFile"); - } _player_debug_log_file = f.optional_string_child("PlayerDebugLogFile"); _player_content_directory = f.optional_string_child("PlayerContentDirectory"); _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory"); @@ -558,39 +605,89 @@ try } catch (std::runtime_error& e) {} } - _add_files_path = f.optional_string_child("AddFilesPath"); - - if (boost::filesystem::exists (_cinemas_file)) { - cxml::Document f ("Cinemas"); - f.read_file (_cinemas_file); - read_cinemas (f); + for (auto& initial: _initial_paths) { + initial.second = f.optional_string_child(initial.first); } - - if (boost::filesystem::exists (_dkdm_recipients_file)) { - cxml::Document f ("DKDMRecipients"); - f.read_file (_dkdm_recipients_file); - read_dkdm_recipients (f); + _use_isdcf_name_by_default = f.optional_bool_child("UseISDCFNameByDefault").get_value_or(true); + _write_kdms_to_disk = f.optional_bool_child("WriteKDMsToDisk").get_value_or(true); + _email_kdms = f.optional_bool_child("EmailKDMs").get_value_or(false); + _default_kdm_type = dcp::string_to_formulation(f.optional_string_child("DefaultKDMType").get_value_or("modified-transitional-1")); + if (auto duration = f.optional_node_child("DefaultKDMDuration")) { + _default_kdm_duration = RoughDuration(duration); + } else { + _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS); + } + _auto_crop_threshold = f.optional_number_child("AutoCropThreshold").get_value_or(0.1); + _last_release_notes_version = f.optional_string_child("LastReleaseNotesVersion"); + _main_divider_sash_position = f.optional_number_child("MainDividerSashPosition"); + _main_content_divider_sash_position = f.optional_number_child("MainContentDividerSashPosition"); + + if (auto loc = f.optional_string_child("DefaultAddFileLocation")) { + if (*loc == "last") { + _default_add_file_location = DefaultAddFileLocation::SAME_AS_LAST_TIME; + } else if (*loc == "project") { + _default_add_file_location = DefaultAddFileLocation::SAME_AS_PROJECT; + } } + + _allow_smpte_bv20 = f.optional_bool_child("AllowSMPTEBv20").get_value_or(false); + + _export.read(f.optional_node_child("Export")); } catch (...) { - if (have_existing ("config.xml")) { + if (have_existing("config.xml")) { backup (); /* We have a config file but it didn't load */ - FailedToLoad (); + FailedToLoad(LoadFailure::CONFIG); } set_defaults (); /* Make a new set of signing certificates and key */ _signer_chain = create_certificate_chain (); /* And similar for decryption of KDMs */ _decryption_chain = create_certificate_chain (); - write (); + write_config(); +} + + +void +Config::read_cinemas() +{ + if (boost::filesystem::exists (_cinemas_file)) { + try { + cxml::Document f("Cinemas"); + f.read_file(_cinemas_file); + read_cinemas(f); + } catch (...) { + backup(); + FailedToLoad(LoadFailure::CINEMAS); + write_cinemas(); + } + } +} + + +void +Config::read_dkdm_recipients() +{ + if (boost::filesystem::exists (_dkdm_recipients_file)) { + try { + cxml::Document f("DKDMRecipients"); + f.read_file(_dkdm_recipients_file); + read_dkdm_recipients(f); + } catch (...) { + backup(); + FailedToLoad(LoadFailure::DKDM_RECIPIENTS); + write_dkdm_recipients(); + } + } } + /** @return Singleton instance */ Config * Config::instance () { - if (_instance == 0) { + if (_instance == nullptr) { _instance = new Config; _instance->read (); } @@ -644,6 +741,8 @@ Config::write_config () const root->add_child("OnlyServersEncode")->add_child_text (_only_servers_encode ? "1" : "0"); /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */ root->add_child("TMSProtocol")->add_child_text (raw_convert (static_cast (_tms_protocol))); + /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */ + root->add_child("TMSPassive")->add_child_text(_tms_passive ? "1" : "0"); /* [XML] TMSIP IP address of TMS. */ root->add_child("TMSIP")->add_child_text (_tms_ip); /* [XML] TMSPath Path on the TMS to copy files to. */ @@ -656,15 +755,8 @@ Config::write_config () const /* [XML:opt] Language Language to use in the GUI e.g. fr_FR. */ root->add_child("Language")->add_child_text (_language.get()); } - if (_default_container) { - /* [XML:opt] DefaultContainer ID of default container - to use when creating new films (185,239 or - 190). - */ - root->add_child("DefaultContainer")->add_child_text (_default_container->id ()); - } if (_default_dcp_content_type) { - /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (FTR, SHR, + /* [XML:opt] DefaultDCPContentType Default content type to use when creating new films (FTR, SHR, TLR, TST, XSN, RTG, TSR, POL, PSA or ADV). */ root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->isdcf_name ()); @@ -694,6 +786,14 @@ Config::write_config () const root->add_child("DefaultAudioDelay")->add_child_text (raw_convert (_default_audio_delay)); /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */ root->add_child("DefaultInterop")->add_child_text (_default_interop ? "1" : "0"); + if (_default_audio_language) { + /* [XML] DefaultAudioLanguage Default audio language to use for new films */ + root->add_child("DefaultAudioLanguage")->add_child_text(_default_audio_language->to_string()); + } + if (_default_territory) { + /* [XML] DefaultTerritory Default territory to use for new films */ + root->add_child("DefaultTerritory")->add_child_text(_default_territory->subtag()); + } for (auto const& i: _default_metadata) { auto c = root->add_child("DefaultMetadata"); c->set_attribute("key", i.first); @@ -703,6 +803,7 @@ Config::write_config () const /* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */ root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ()); } + _default_kdm_duration.as_xml(root->add_child("DefaultKDMDuration")); /* [XML] MailServer Hostname of SMTP server to use. */ root->add_child("MailServer")->add_child_text (_mail_server); /* [XML] MailPort Port number to use on SMTP server. */ @@ -766,6 +867,10 @@ Config::write_config () const root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0"); /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */ root->add_child("AllowAnyContainer")->add_child_text (_allow_any_container ? "1" : "0"); + /* [XML] Allow96kHzAudio 1 to allow users to make DCPs with 96kHz audio, 0 to always make 48kHz DCPs */ + root->add_child("Allow96kHzAudio")->add_child_text(_allow_96khz_audio ? "1" : "0"); + /* [XML] UseAllAudioChannels 1 to allow users to map audio to all 16 DCP channels, 0 to limit to the channels used in standard DCPs */ + root->add_child("UseAllAudioChannels")->add_child_text(_use_all_audio_channels ? "1" : "0"); /* [XML] ShowExperimentalAudioProcessors 1 to offer users the (experimental) audio upmixer processors, 0 to hide them */ root->add_child("ShowExperimentalAudioProcessors")->add_child_text (_show_experimental_audio_processors ? "1" : "0"); /* [XML] LogTypes Types of logging to write; a bitfield where 1 is general notes, 2 warnings, 4 errors, 8 debug information related @@ -962,12 +1067,8 @@ Config::write_config () const } /* [XML] RespectKDMValidityPeriods 1 to refuse to use KDMs that are out of date, 0 to ignore KDM dates. */ root->add_child("RespectKDMValidityPeriods")->add_child_text(_respect_kdm_validity_periods ? "1" : "0"); - if (_player_activity_log_file) { - /* [XML] PlayerLogFile Filename to use for player activity logs (e.g starting, stopping, playlist loads) */ - root->add_child("PlayerActivityLogFile")->add_child_text(_player_activity_log_file->string()); - } if (_player_debug_log_file) { - /* [XML] PlayerLogFile Filename to use for player debug logs */ + /* [XML] PlayerLogFile Filename to use for player debug logs. */ root->add_child("PlayerDebugLogFile")->add_child_text(_player_debug_log_file->string()); } if (_player_content_directory) { @@ -988,22 +1089,46 @@ Config::write_config () const for (auto const& i: _custom_languages) { root->add_child("CustomLanguage")->add_child_text(i.to_string()); } - if (_add_files_path) { - /* [XML] The default path that will be offered in the picker when adding files to a film */ - root->add_child("AddFilesPath")->add_child_text(_add_files_path->string()); + for (auto const& initial: _initial_paths) { + if (initial.second) { + root->add_child(initial.first)->add_child_text(initial.second->string()); + } + } + root->add_child("UseISDCFNameByDefault")->add_child_text(_use_isdcf_name_by_default ? "1" : "0"); + root->add_child("WriteKDMsToDisk")->add_child_text(_write_kdms_to_disk ? "1" : "0"); + root->add_child("EmailKDMs")->add_child_text(_email_kdms ? "1" : "0"); + root->add_child("DefaultKDMType")->add_child_text(dcp::formulation_to_string(_default_kdm_type)); + root->add_child("AutoCropThreshold")->add_child_text(raw_convert(_auto_crop_threshold)); + if (_last_release_notes_version) { + root->add_child("LastReleaseNotesVersion")->add_child_text(*_last_release_notes_version); } + if (_main_divider_sash_position) { + root->add_child("MainDividerSashPosition")->add_child_text(raw_convert(*_main_divider_sash_position)); + } + if (_main_content_divider_sash_position) { + root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert(*_main_content_divider_sash_position)); + } + + root->add_child("DefaultAddFileLocation")->add_child_text( + _default_add_file_location == DefaultAddFileLocation::SAME_AS_LAST_TIME ? "last" : "project" + ); + + /* [XML] AllowSMPTEBv20 1 to allow the user to choose SMPTE (Bv2.0 only) as a standard, otherwise 0 */ + root->add_child("AllowSMPTEBv20")->add_child_text(_allow_smpte_bv20 ? "1" : "0"); + + _export.write(root->add_child("Export")); auto target = config_write_file(); try { auto const s = doc.write_to_string_formatted (); boost::filesystem::path tmp (string(target.string()).append(".tmp")); - auto f = fopen_boost (tmp, "w"); + dcp::File f(tmp, "w"); if (!f) { throw FileError (_("Could not open file for writing"), tmp); } - checked_fwrite (s.c_str(), s.bytes(), f, tmp); - fclose (f); + f.checked_write(s.c_str(), s.bytes()); + f.close(); boost::filesystem::remove (target); boost::filesystem::rename (tmp, target); } catch (xmlpp::exception& e) { @@ -1137,6 +1262,7 @@ Config::set_cover_sheet_to_default () { _cover_sheet = _( "$CPL_NAME\n\n" + "CPL Filename: $CPL_FILENAME\n" "Type: $TYPE\n" "Format: $CONTAINER\n" "Audio: $AUDIO\n" @@ -1153,7 +1279,7 @@ Config::add_to_history (boost::filesystem::path p) add_to_history_internal (_history, p); } -/** Remove non-existant items from the history */ +/** Remove non-existent items from the history */ void Config::clean_history () { @@ -1166,7 +1292,7 @@ Config::add_to_player_history (boost::filesystem::path p) add_to_history_internal (_player_history, p); } -/** Remove non-existant items from the player history */ +/** Remove non-existent items from the player history */ void Config::clean_player_history () { @@ -1241,6 +1367,7 @@ Config::set_cinemas_file (boost::filesystem::path file) read_cinemas (f); } + changed (CINEMAS); changed (OTHER); } @@ -1250,7 +1377,7 @@ Config::read_dkdm_recipients (cxml::Document const & f) { _dkdm_recipients.clear (); for (auto i: f.node_children("DKDMRecipient")) { - _dkdm_recipients.push_back (shared_ptr(new DKDMRecipient(i))); + _dkdm_recipients.push_back (make_shared(i)); } } @@ -1382,13 +1509,8 @@ Config::copy_and_link (boost::filesystem::path new_file) const bool Config::have_write_permission () const { - auto f = fopen_boost (config_write_file(), "r+"); - if (!f) { - return false; - } - - fclose (f); - return true; + dcp::File f(config_write_file(), "r+"); + return static_cast(f); } /** @param output_channels Number of output channels in use. @@ -1451,3 +1573,66 @@ Config::add_custom_language (dcp::LanguageTag tag) } } + +optional +Config::check_certificates () const +{ + optional bad; + + for (auto const& i: _signer_chain->unordered()) { + if (i.has_utf8_strings()) { + bad = BAD_SIGNER_UTF8_STRINGS; + } + if ((i.not_after().year() - i.not_before().year()) > 15) { + bad = BAD_SIGNER_VALIDITY_TOO_LONG; + } + } + + if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) { + bad = BAD_SIGNER_INCONSISTENT; + } + + if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) { + bad = BAD_DECRYPTION_INCONSISTENT; + } + + return bad; +} + + +void +save_all_config_as_zip (boost::filesystem::path zip_file) +{ + Zipper zipper (zip_file); + + auto config = Config::instance(); + zipper.add ("config.xml", dcp::file_to_string(config->config_read_file())); + if (boost::filesystem::exists(config->cinemas_file())) { + zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file())); + } + if (boost::filesystem::exists(config->dkdm_recipients_file())) { + zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file())); + } + + zipper.close (); +} + + +void +Config::set_initial_path(string id, boost::filesystem::path path) +{ + auto iter = _initial_paths.find(id); + DCPOMATIC_ASSERT(iter != _initial_paths.end()); + iter->second = path; + changed(); +} + + +optional +Config::initial_path(string id) const +{ + auto iter = _initial_paths.find(id); + DCPOMATIC_ASSERT(iter != _initial_paths.end()); + return iter->second; +} +