Supporters update.
[dcpomatic.git] / src / lib / config.cc
index 30e80b938658719b4e46ea05f04a57a84bcef268..384db5cde137be48177afc3cb3ceca8df95f090f 100644 (file)
@@ -25,7 +25,6 @@
 #include "config.h"
 #include "constants.h"
 #include "cross.h"
-#include "crypto.h"
 #include "dcp_content_type.h"
 #include "dkdm_recipient.h"
 #include "dkdm_wrapper.h"
@@ -33,6 +32,7 @@
 #include "filter.h"
 #include "log.h"
 #include "ratio.h"
+#include "unzipper.h"
 #include "zipper.h"
 #include <dcp/certificate_chain.h>
 #include <dcp/name_format.h>
@@ -106,7 +106,7 @@ Config::set_defaults ()
        _language = optional<string> ();
        _default_still_length = 10;
        _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
-       _default_dcp_audio_channels = 6;
+       _default_dcp_audio_channels = 8;
        _default_j2k_bandwidth = 150000000;
        _default_audio_delay = 0;
        _default_interop = false;
@@ -140,9 +140,9 @@ Config::set_defaults ()
        _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;
@@ -153,6 +153,7 @@ Config::set_defaults ()
        _sound_output = optional<string> ();
        _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
@@ -187,7 +188,16 @@ Config::set_defaults ()
        _custom_languages.clear ();
        _initial_paths.clear();
        _initial_paths["AddFilesPath"] = boost::none;
+       _initial_paths["AddKDMPath"] = boost::none;
        _initial_paths["AddDKDMPath"] = boost::none;
+       _initial_paths["SelectCertificatePath"] = boost::none;
+       _initial_paths["AddCombinerInputPath"] = boost::none;
+       _initial_paths["ExportSubtitlesPath"] = boost::none;
+       _initial_paths["ExportVideoPath"] = boost::none;
+       _initial_paths["DebugLogPath"] = boost::none;
+       _initial_paths["CinemaDatabasePath"] = boost::none;
+       _initial_paths["ConfigFilePath"] = boost::none;
+       _initial_paths["Preferences"] = boost::none;
        _use_isdcf_name_by_default = true;
        _write_kdms_to_disk = true;
        _email_kdms = false;
@@ -195,6 +205,8 @@ Config::set_defaults ()
        _default_kdm_duration = RoughDuration(1, RoughDuration::Unit::WEEKS);
        _auto_crop_threshold = 0.1;
        _last_release_notes_version = boost::none;
+       _allow_smpte_bv20 = false;
+       _isdcf_name_part_length = 14;
 
        _allowed_dcp_frame_rates.clear ();
        _allowed_dcp_frame_rates.push_back (24);
@@ -286,7 +298,7 @@ Config::read_config()
 try
 {
        cxml::Document f ("Config");
-       f.read_file (config_read_file());
+       f.read_file(dcp::filesystem::fix_long_path(config_read_file()));
 
        auto version = f.optional_number_child<int> ("Version");
        if (version && *version < _current_version) {
@@ -496,6 +508,7 @@ try
                        case BAD_SIGNER_UTF8_STRINGS:
                        case BAD_SIGNER_INCONSISTENT:
                        case BAD_SIGNER_VALIDITY_TOO_LONG:
+                       case BAD_SIGNER_DN_QUALIFIER:
                                _signer_chain = create_certificate_chain ();
                                break;
                        case BAD_DECRYPTION_INCONSISTENT:
@@ -618,6 +631,17 @@ try
        _main_divider_sash_position = f.optional_number_child<int>("MainDividerSashPosition");
        _main_content_divider_sash_position = f.optional_number_child<int>("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);
+       _isdcf_name_part_length = f.optional_number_child<int>("ISDCFNamePartLength").get_value_or(14);
+
        _export.read(f.optional_node_child("Export"));
 }
 catch (...) {
@@ -638,10 +662,10 @@ catch (...) {
 void
 Config::read_cinemas()
 {
-       if (boost::filesystem::exists (_cinemas_file)) {
+       if (dcp::filesystem::exists(_cinemas_file)) {
                try {
                        cxml::Document f("Cinemas");
-                       f.read_file(_cinemas_file);
+                       f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
                        read_cinemas(f);
                } catch (...) {
                        backup();
@@ -655,10 +679,10 @@ Config::read_cinemas()
 void
 Config::read_dkdm_recipients()
 {
-       if (boost::filesystem::exists (_dkdm_recipients_file)) {
+       if (dcp::filesystem::exists(_dkdm_recipients_file)) {
                try {
                        cxml::Document f("DKDMRecipients");
-                       f.read_file(_dkdm_recipients_file);
+                       f.read_file(dcp::filesystem::fix_long_path(_dkdm_recipients_file));
                        read_dkdm_recipients(f);
                } catch (...) {
                        backup();
@@ -1027,7 +1051,7 @@ Config::write_config () const
        }
 
        /* [XML] PlayerMode <code>window</code> for a single window, <code>full</code> for full-screen and <code>dual</code> for full screen playback
-          with controls on another monitor.
+          with separate (advanced) controls.
        */
        switch (_player_mode) {
        case PLAYER_MODE_WINDOW:
@@ -1095,6 +1119,15 @@ Config::write_config () const
                root->add_child("MainContentDividerSashPosition")->add_child_text(raw_convert<string>(*_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");
+       /* [XML] ISDCFNamePartLength Maximum length of the "name" part of an ISDCF name, which should be 14 according to the standard */
+       root->add_child("ISDCFNamePartLength")->add_child_text(raw_convert<string>(_isdcf_name_part_length));
+
        _export.write(root->add_child("Export"));
 
        auto target = config_write_file();
@@ -1108,8 +1141,8 @@ Config::write_config () const
                }
                f.checked_write(s.c_str(), s.bytes());
                f.close();
-               boost::filesystem::remove (target);
-               boost::filesystem::rename (tmp, target);
+               dcp::filesystem::remove(target);
+               dcp::filesystem::rename(tmp, target);
        } catch (xmlpp::exception& e) {
                string s = e.what ();
                trim (s);
@@ -1132,8 +1165,8 @@ write_file (string root_node, string node, string version, list<shared_ptr<T>> t
 
        try {
                doc.write_to_file_formatted (file.string() + ".tmp");
-               boost::filesystem::remove (file);
-               boost::filesystem::rename (file.string() + ".tmp", file);
+               dcp::filesystem::remove(file);
+               dcp::filesystem::rename(file.string() + ".tmp", file);
        } catch (xmlpp::exception& e) {
                string s = e.what ();
                trim (s);
@@ -1176,7 +1209,7 @@ Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::
        }
 
        boost::system::error_code ec;
-       auto const e = boost::filesystem::exists (*dir, ec);
+       auto const e = dcp::filesystem::exists(*dir, ec);
        if (ec || !e) {
                return a;
        }
@@ -1188,7 +1221,7 @@ void
 Config::drop ()
 {
        delete _instance;
-       _instance = 0;
+       _instance = nullptr;
 }
 
 void
@@ -1271,7 +1304,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 ()
 {
@@ -1299,7 +1332,7 @@ Config::clean_history_internal (vector<boost::filesystem::path>& h)
        h.clear ();
        for (auto i: old) {
                try {
-                       if (boost::filesystem::is_directory(i)) {
+                       if (dcp::filesystem::is_directory(i)) {
                                h.push_back (i);
                        }
                } catch (...) {
@@ -1312,7 +1345,7 @@ Config::clean_history_internal (vector<boost::filesystem::path>& h)
 bool
 Config::have_existing (string file)
 {
-       return boost::filesystem::exists (read_path(file));
+       return dcp::filesystem::exists(read_path(file));
 }
 
 
@@ -1339,10 +1372,10 @@ Config::set_cinemas_file (boost::filesystem::path file)
 
        _cinemas_file = file;
 
-       if (boost::filesystem::exists (_cinemas_file)) {
+       if (dcp::filesystem::exists(_cinemas_file)) {
                /* Existing file; read it in */
                cxml::Document f ("Cinemas");
-               f.read_file (_cinemas_file);
+               f.read_file(dcp::filesystem::fix_long_path(_cinemas_file));
                read_cinemas (f);
        }
 
@@ -1371,12 +1404,12 @@ Config::save_template (shared_ptr<const Film> film, string name) const
 list<string>
 Config::templates () const
 {
-       if (!boost::filesystem::exists(read_path("templates"))) {
+       if (!dcp::filesystem::exists(read_path("templates"))) {
                return {};
        }
 
        list<string> n;
-       for (auto const& i: boost::filesystem::directory_iterator(read_path("templates"))) {
+       for (auto const& i: dcp::filesystem::directory_iterator(read_path("templates"))) {
                n.push_back (i.path().filename().string());
        }
        return n;
@@ -1385,7 +1418,7 @@ Config::templates () const
 bool
 Config::existing_template (string name) const
 {
-       return boost::filesystem::exists (template_read_path(name));
+       return dcp::filesystem::exists(template_read_path(name));
 }
 
 
@@ -1406,13 +1439,13 @@ Config::template_write_path (string name) const
 void
 Config::rename_template (string old_name, string new_name) const
 {
-       boost::filesystem::rename (template_read_path(old_name), template_write_path(new_name));
+       dcp::filesystem::rename(template_read_path(old_name), template_write_path(new_name));
 }
 
 void
 Config::delete_template (string name) const
 {
-       boost::filesystem::remove (template_write_path(name));
+       dcp::filesystem::remove(template_write_path(name));
 }
 
 /** @return Path to the config.xml containing the actual settings, following a link if required */
@@ -1420,14 +1453,14 @@ boost::filesystem::path
 config_file (boost::filesystem::path main)
 {
        cxml::Document f ("Config");
-       if (!boost::filesystem::exists (main)) {
+       if (!dcp::filesystem::exists(main)) {
                /* It doesn't exist, so there can't be any links; just return it */
                return main;
        }
 
        /* See if there's a link */
        try {
-               f.read_file (main);
+               f.read_file(dcp::filesystem::fix_long_path(main));
                auto link = f.optional_string_child("Link");
                if (link) {
                        return *link;
@@ -1481,7 +1514,7 @@ void
 Config::copy_and_link (boost::filesystem::path new_file) const
 {
        write ();
-       boost::filesystem::copy_file (config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
+       dcp::filesystem::copy_file(config_read_file(), new_file, boost::filesystem::copy_option::overwrite_if_exists);
        link (new_file);
 }
 
@@ -1565,6 +1598,9 @@ Config::check_certificates () const
                if ((i.not_after().year() - i.not_before().year()) > 15) {
                        bad = BAD_SIGNER_VALIDITY_TOO_LONG;
                }
+               if (dcp::escape_digest(i.subject_dn_qualifier()) != dcp::public_key_digest(i.public_key())) {
+                       bad = BAD_SIGNER_DN_QUALIFIER;
+               }
        }
 
        if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
@@ -1586,10 +1622,10 @@ save_all_config_as_zip (boost::filesystem::path 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())) {
+       if (dcp::filesystem::exists(config->cinemas_file())) {
                zipper.add ("cinemas.xml", dcp::file_to_string(config->cinemas_file()));
        }
-       if (boost::filesystem::exists(config->dkdm_recipients_file())) {
+       if (dcp::filesystem::exists(config->dkdm_recipients_file())) {
                zipper.add ("dkdm_recipients.xml", dcp::file_to_string(config->dkdm_recipients_file()));
        }
 
@@ -1597,6 +1633,38 @@ save_all_config_as_zip (boost::filesystem::path zip_file)
 }
 
 
+void
+Config::load_from_zip(boost::filesystem::path zip_file)
+{
+       Unzipper unzipper(zip_file);
+       dcp::write_string_to_file(unzipper.get("config.xml"), config_write_file());
+
+       try {
+               dcp::write_string_to_file(unzipper.get("cinemas.xml"), cinemas_file());
+               dcp::write_string_to_file(unzipper.get("dkdm_recipient.xml"), dkdm_recipients_file());
+       } catch (std::runtime_error&) {}
+
+       read();
+
+       changed(Property::USE_ANY_SERVERS);
+       changed(Property::SERVERS);
+       changed(Property::CINEMAS);
+       changed(Property::DKDM_RECIPIENTS);
+       changed(Property::SOUND);
+       changed(Property::SOUND_OUTPUT);
+       changed(Property::PLAYER_CONTENT_DIRECTORY);
+       changed(Property::PLAYER_PLAYLIST_DIRECTORY);
+       changed(Property::PLAYER_DEBUG_LOG);
+       changed(Property::HISTORY);
+       changed(Property::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS);
+       changed(Property::AUDIO_MAPPING);
+       changed(Property::AUTO_CROP_THRESHOLD);
+       changed(Property::ALLOW_SMPTE_BV20);
+       changed(Property::ISDCF_NAME_PART_LENGTH);
+       changed(Property::OTHER);
+}
+
+
 void
 Config::set_initial_path(string id, boost::filesystem::path path)
 {
@@ -1611,7 +1679,9 @@ optional<boost::filesystem::path>
 Config::initial_path(string id) const
 {
        auto iter = _initial_paths.find(id);
-       DCPOMATIC_ASSERT(iter != _initial_paths.end());
+       if (iter == _initial_paths.end()) {
+               return {};
+       }
        return iter->second;
 }