Confirm overwrite of existing DCP when creating a new one.
[dcpomatic.git] / src / lib / film.cc
index 68266b4ec52a3a4f4825eaeb5cd3e5073081052e..56dce56e596bd9ac3e7ae9a516d47d06ecee7c46 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -57,6 +57,8 @@
 #include <dcp/local_time.h>
 #include <dcp/decrypted_kdm.h>
 #include <dcp/raw_convert.h>
+#include <dcp/reel_mxf.h>
+#include <dcp/reel_asset.h>
 #include <libxml++/libxml++.h>
 #include <boost/filesystem.hpp>
 #include <boost/algorithm/string.hpp>
@@ -83,6 +85,9 @@ using std::cout;
 using std::list;
 using std::set;
 using std::runtime_error;
+using std::copy;
+using std::back_inserter;
+using std::map;
 using boost::shared_ptr;
 using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
@@ -288,6 +293,35 @@ Film::make_dcp ()
                throw BadSettingError (_("name"), _("cannot contain slashes"));
        }
 
+       if (container() == 0) {
+               throw MissingSettingError (_("container"));
+       }
+
+       if (content().empty()) {
+               throw runtime_error (_("you must add some content to the DCP before creating it"));
+       }
+
+       if (dcp_content_type() == 0) {
+               throw MissingSettingError (_("content type"));
+       }
+
+       if (name().empty()) {
+               throw MissingSettingError (_("name"));
+       }
+
+       BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
+               if (!i->paths_valid()) {
+                       throw runtime_error (_("some of your content is missing"));
+               }
+               shared_ptr<const DCPContent> dcp = dynamic_pointer_cast<const DCPContent> (i);
+               if (dcp && dcp->needs_kdm()) {
+                       throw runtime_error (_("some of your content needs a KDM"));
+               }
+               if (dcp && dcp->needs_assets()) {
+                       throw runtime_error (_("some of your content needs an OV"));
+               }
+       }
+
        set_isdcf_date_today ();
 
        BOOST_FOREACH (string i, environment_info ()) {
@@ -305,22 +339,6 @@ Film::make_dcp ()
        }
        LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
 
-       if (container() == 0) {
-               throw MissingSettingError (_("container"));
-       }
-
-       if (content().empty()) {
-               throw runtime_error (_("You must add some content to the DCP before creating it"));
-       }
-
-       if (dcp_content_type() == 0) {
-               throw MissingSettingError (_("content type"));
-       }
-
-       if (name().empty()) {
-               throw MissingSettingError (_("name"));
-       }
-
        JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
 }
 
@@ -491,10 +509,11 @@ Film::read_metadata (optional<boost::filesystem::path> path)
 }
 
 /** Given a directory name, return its full path within the Film's directory.
- *  The directory (and its parents) will be created if they do not exist.
+ *  @param d directory name within the Filn's directory.
+ *  @param create true to create the directory (and its parents) if they do not exist.
  */
 boost::filesystem::path
-Film::dir (boost::filesystem::path d) const
+Film::dir (boost::filesystem::path d, bool create) const
 {
        DCPOMATIC_ASSERT (_directory);
 
@@ -502,7 +521,9 @@ Film::dir (boost::filesystem::path d) const
        p /= _directory.get();
        p /= d;
 
-       boost::filesystem::create_directories (p);
+       if (create) {
+               boost::filesystem::create_directories (p);
+       }
 
        return p;
 }
@@ -773,24 +794,10 @@ Film::dcp_name (bool if_created_now) const
 {
        string unfiltered;
        if (use_isdcf_name()) {
-               unfiltered = isdcf_name (if_created_now);
-       } else {
-               unfiltered = name ();
+               return careful_string_filter (isdcf_name (if_created_now));
        }
 
-       /* Filter out `bad' characters which cause problems with some systems.
-          There's no apparent list of what really is allowed, so this is a guess.
-       */
-
-       string filtered;
-       string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
-       for (size_t i = 0; i < unfiltered.size(); ++i) {
-               if (allowed.find (unfiltered[i]) != string::npos) {
-                       filtered += unfiltered[i];
-               }
-       }
-
-       return filtered;
+       return careful_string_filter (name ());
 }
 
 void
@@ -1219,8 +1226,44 @@ Film::make_kdm (
                throw InvalidSignerError ();
        }
 
+       /* Find keys that have been added to imported, encrypted DCP content */
+       list<dcp::DecryptedKDMKey> imported_keys;
+       BOOST_FOREACH (shared_ptr<Content> i, content()) {
+               shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
+               if (d && d->kdm()) {
+                       dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
+                       list<dcp::DecryptedKDMKey> keys = kdm.keys ();
+                       copy (keys.begin(), keys.end(), back_inserter (imported_keys));
+               }
+       }
+
+       map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
+
+       BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
+               shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
+               if (!mxf || !mxf->key_id()) {
+                       continue;
+               }
+
+               /* Get any imported key for this ID */
+               bool done = false;
+               BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
+                       if (j.id() == mxf->key_id().get()) {
+                               LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
+                               keys[mxf] = j.key();
+                               done = true;
+                       }
+               }
+
+               if (!done) {
+                       /* No imported key; it must be an asset that we encrypted */
+                       LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
+                       keys[mxf] = key();
+               }
+       }
+
        return dcp::DecryptedKDM (
-               cpl, key(), from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
+               cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
                ).encrypt (signer, recipient, trusted_devices, formulation);
 }