#include "cinema.h"
#include "config.h"
#include "dkdm_wrapper.h"
-#include "emailer.h"
+#include "email.h"
#include "exceptions.h"
#include "film.h"
#include "kdm_with_metadata.h"
#include <dcp/certificate.h>
#include <dcp/decrypted_kdm.h>
#include <dcp/encrypted_kdm.h>
+#include <dcp/filesystem.h>
#include <getopt.h>
{
out (String::compose("Syntax: %1 [OPTION] <FILM|CPL-ID|DKDM>", program_name));
out (" -h, --help show this help");
- out (" -o, --output output file or directory");
- out (" -K, --filename-format filename format for KDMs");
- out (" -Z, --container-name-format filename format for ZIP containers");
- out (" -f, --valid-from valid from time (in local time zone of the cinema) (e.g. \"2013-09-28 01:41:51\") or \"now\"");
- out (" -t, --valid-to valid to time (in local time zone of the cinema) (e.g. \"2014-09-28 01:41:51\")");
- out (" -d, --valid-duration valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")");
- out (" -F, --formulation modified-transitional-1, multiple-modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]");
+ out (" -o, --output <path> output file or directory");
+ out (" -K, --filename-format <format> filename format for KDMs");
+ out (" -Z, --container-name-format <format> filename format for ZIP containers");
+ out (" -f, --valid-from <time> valid from time (in local time zone of the cinema) (e.g. \"2013-09-28 01:41:51\") or \"now\"");
+ out (" -t, --valid-to <time> valid to time (in local time zone of the cinema) (e.g. \"2014-09-28 01:41:51\")");
+ out (" -d, --valid-duration <duration> valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")");
+ out (" -F, --formulation <formulation> modified-transitional-1, multiple-modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]");
out (" -p, --disable-forensic-marking-picture disable forensic marking of pictures essences");
out (" -a, --disable-forensic-marking-audio disable forensic marking of audio essences (optionally above a given channel, e.g 12)");
out (" -e, --email email KDMs to cinemas");
out (" -z, --zip ZIP each cinema's KDMs into its own file");
out (" -v, --verbose be verbose");
- out (" -c, --cinema specify a cinema, either by name or email address");
- out (" -S, --screen screen description");
- out (" -C, --certificate file containing projector certificate");
- out (" -T, --trusted-device file containing a trusted device's certificate");
+ out (" -c, --cinema <name|email> cinema name (when using -C) or name/email (to filter cinemas)");
+ out (" -S, --screen <name> screen name (when using -C) or screen name (to filter screens when using -c)");
+ out (" -C, --certificate <file> file containing projector certificate");
+ out (" -T, --trusted-device <file> file containing a trusted device's certificate");
+ out (" --cinemas-file <file> use the given file as a list of cinemas instead of the current configuration");
+ out (" --dump-decryption-certificate write the DCP-o-matic KDM decryption certificate to the console");
out (" --list-cinemas list known cinemas from the DCP-o-matic settings");
out (" --list-dkdm-cpls list CPLs for which DCP-o-matic has DKDMs");
out ("");
static
void
from_film (
- list<shared_ptr<Screen>> screens,
+ vector<shared_ptr<Screen>> screens,
boost::filesystem::path film_dir,
bool verbose,
boost::filesystem::path output,
auto cpl = cpls.front().cpl_file;
+ std::vector<KDMCertificatePeriod> period_checks;
+
try {
list<KDMWithMetadataPtr> kdms;
for (auto i: screens) {
- auto p = kdm_for_screen (film, cpl, i, valid_from, valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio);
+ std::function<dcp::DecryptedKDM (dcp::LocalTime, dcp::LocalTime)> make_kdm = [film, cpl](dcp::LocalTime begin, dcp::LocalTime end) {
+ return film->make_kdm(cpl, begin, end);
+ };
+ auto p = kdm_for_screen(make_kdm, i, valid_from, valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio, period_checks);
if (p) {
kdms.push_back (p);
}
}
+
+
+ if (find_if(
+ period_checks.begin(),
+ period_checks.end(),
+ [](KDMCertificatePeriod const& p) { return p.overlap == KDMCertificateOverlap::KDM_OUTSIDE_CERTIFICATE; }
+ ) != period_checks.end()) {
+ throw KDMCLIError(
+ "Some KDMs would have validity periods which are completely outside the recipient certificate periods. Such KDMs are very unlikely to work, so will not be created."
+ );
+ }
+
+ if (find_if(
+ period_checks.begin(),
+ period_checks.end(),
+ [](KDMCertificatePeriod const& p) { return p.overlap == KDMCertificateOverlap::KDM_OVERLAPS_CERTIFICATE; }
+ ) != period_checks.end()) {
+ out("For some of these KDMs the recipient certificate's validity period will not cover the whole of the KDM validity period. This might cause problems with the KDMs.");
+ }
+
write_files (kdms, zip, output, container_name_format, filename_format, verbose, out);
if (email) {
- send_emails ({kdms}, container_name_format, filename_format, film->dcp_name());
+ send_emails ({kdms}, container_name_format, filename_format, film->dcp_name(), {});
}
} catch (FileError& e) {
throw KDMCLIError (String::compose("%1 (%2)", e.what(), e.file().string()));
static
void
from_dkdm (
- list<shared_ptr<Screen>> screens,
+ vector<shared_ptr<Screen>> screens,
dcp::DecryptedKDM dkdm,
bool verbose,
boost::filesystem::path output,
continue;
}
- dcp::LocalTime begin(valid_from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute());
- dcp::LocalTime end(valid_to, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute());
+ int const offset_hour = i->cinema ? i->cinema->utc_offset_hour() : 0;
+ int const offset_minute = i->cinema ? i->cinema->utc_offset_minute() : 0;
+
+ dcp::LocalTime begin(valid_from, dcp::UTCOffset(offset_hour, offset_minute));
+ dcp::LocalTime end(valid_to, dcp::UTCOffset(offset_hour, offset_minute));
auto const kdm = kdm_from_dkdm(
dkdm,
);
dcp::NameFormat::Map name_values;
- name_values['c'] = i->cinema->name;
+ name_values['c'] = i->cinema ? i->cinema->name : "";
name_values['s'] = i->name;
- name_values['f'] = dkdm.annotation_text().get_value_or("");
+ name_values['f'] = kdm.content_title_text();
name_values['b'] = begin.date() + " " + begin.time_of_day(true, false);
name_values['e'] = end.date() + " " + end.time_of_day(true, false);
name_values['i'] = kdm.cpl_id();
- kdms.push_back (make_shared<KDMWithMetadata>(name_values, i->cinema.get(), i->cinema->emails, kdm));
+ kdms.push_back(make_shared<KDMWithMetadata>(name_values, i->cinema.get(), i->cinema ? i->cinema->emails : vector<string>(), kdm));
}
write_files (kdms, zip, output, container_name_format, filename_format, verbose, out);
if (email) {
- send_emails ({kdms}, container_name_format, filename_format, dkdm.annotation_text().get_value_or(""));
+ send_emails ({kdms}, container_name_format, filename_format, dkdm.annotation_text().get_value_or(""), {});
}
} catch (FileError& e) {
throw KDMCLIError (String::compose("%1 (%2)", e.what(), e.file().string()));
}
+void
+dump_decryption_certificate(std::function<void (string)> out)
+{
+ vector<string> lines;
+ boost::split(lines, Config::instance()->decryption_chain()->leaf().certificate(true), boost::is_any_of("\n"));
+ for (auto const& line: lines) {
+ out(line);
+ }
+}
+
+
optional<string>
kdm_cli (int argc, char* argv[], std::function<void (string)> out)
try
{
- boost::filesystem::path output = boost::filesystem::current_path();
+ boost::filesystem::path output = dcp::filesystem::current_path();
auto container_name_format = Config::instance()->kdm_container_name_format();
auto filename_format = Config::instance()->kdm_filename_format();
optional<string> cinema_name;
shared_ptr<Cinema> cinema;
- string screen_description;
- list<shared_ptr<Screen>> screens;
+ optional<boost::filesystem::path> certificate;
+ optional<string> screen;
+ vector<shared_ptr<Screen>> screens;
optional<dcp::EncryptedKDM> dkdm;
optional<boost::posix_time::ptime> valid_from;
optional<boost::posix_time::ptime> valid_to;
bool disable_forensic_marking_picture = false;
optional<int> disable_forensic_marking_audio;
bool email = false;
+ optional<boost::filesystem::path> cinemas_file;
program_name = argv[0];
+ /* Reset getopt() so we can call this method several times in one test process */
+ optind = 1;
+
int option_index = 0;
while (true) {
static struct option long_options[] = {
{ "trusted-device", required_argument, 0, 'T' },
{ "list-cinemas", no_argument, 0, 'B' },
{ "list-dkdm-cpls", no_argument, 0, 'D' },
+ { "cinemas-file", required_argument, 0, 'E' },
+ { "dump-decryption-certificate", no_argument, 0, 'G' },
{ 0, 0, 0, 0 }
};
- int c = getopt_long (argc, argv, "ho:K:Z:f:t:d:F:pae::zvc:S:C:T:BD", long_options, &option_index);
+ int c = getopt_long (argc, argv, "ho:K:Z:f:t:d:F:pae::zvc:S:C:T:BDE:G", long_options, &option_index);
if (c == -1) {
break;
switch (c) {
case 'h':
help (out);
- exit (EXIT_SUCCESS);
+ return {};
case 'o':
output = optarg;
break;
(for lookup) and by creating a Cinema which the next Screen will be added to.
*/
cinema_name = optarg;
- cinema = make_shared<Cinema>(optarg, list<string>(), "", 0, 0);
+ cinema = make_shared<Cinema>(optarg, vector<string>(), "", 0, 0);
break;
case 'S':
- screen_description = optarg;
+ /* Similarly, this could be the name of a new (temporary) screen or the name of a screen
+ * to search for.
+ */
+ screen = optarg;
break;
case 'C':
- {
- /* Make a new screen and add it to the current cinema */
- dcp::CertificateChain chain (dcp::file_to_string(optarg));
- auto screen = make_shared<Screen>(screen_description, "", chain.leaf(), vector<TrustedDevice>());
- if (cinema) {
- cinema->add_screen (screen);
- }
- screens.push_back (screen);
+ certificate = optarg;
break;
- }
case 'T':
/* A trusted device ends up in the last screen we made */
if (!screens.empty ()) {
case 'D':
list_dkdm_cpls = true;
break;
+ case 'E':
+ cinemas_file = optarg;
+ break;
+ case 'G':
+ dump_decryption_certificate(out);
+ return {};
+ }
+ }
+
+ if (cinemas_file) {
+ Config::instance()->set_cinemas_file(*cinemas_file);
+ }
+
+ if (certificate) {
+ /* Make a new screen and add it to the current cinema */
+ dcp::CertificateChain chain(dcp::file_to_string(*certificate));
+ auto screen_to_add = std::make_shared<Screen>(screen.get_value_or(""), "", chain.leaf(), boost::none, vector<TrustedDevice>());
+ if (cinema) {
+ cinema->add_screen(screen_to_add);
}
+ screens.push_back(screen_to_add);
}
if (list_cinemas) {
auto cinemas = Config::instance()->cinemas ();
for (auto i: cinemas) {
- out (String::compose("%1 (%2)", i->name, Emailer::address_list (i->emails)));
+ out (String::compose("%1 (%2)", i->name, Email::address_list(i->emails)));
}
- exit (EXIT_SUCCESS);
+ return {};
}
if (list_dkdm_cpls) {
dump_dkdm_group (Config::instance()->dkdms(), 0, out);
- exit (EXIT_SUCCESS);
+ return {};
}
if (!duration_string && !valid_to) {
}
screens = find_cinema (*cinema_name)->screens ();
+ if (screen) {
+ screens.erase(std::remove_if(screens.begin(), screens.end(), [&screen](shared_ptr<Screen> s) { return s->name != *screen; }), screens.end());
+ }
}
if (duration_string) {
valid_to = valid_from.get() + duration_from_string (*duration_string);
}
- dcpomatic_setup_path_encoding ();
- dcpomatic_setup ();
-
if (verbose) {
out (String::compose("Making KDMs valid from %1 to %2", boost::posix_time::to_simple_string(valid_from.get()), boost::posix_time::to_simple_string(valid_to.get())));
}
string const thing = argv[optind];
- if (boost::filesystem::is_directory(thing) && boost::filesystem::is_regular_file(boost::filesystem::path(thing) / "metadata.xml")) {
+ if (dcp::filesystem::is_directory(thing) && dcp::filesystem::is_regular_file(boost::filesystem::path(thing) / "metadata.xml")) {
from_film (
screens,
thing,
out
);
} else {
- if (boost::filesystem::is_regular_file(thing)) {
+ if (dcp::filesystem::is_regular_file(thing)) {
dkdm = dcp::EncryptedKDM (dcp::file_to_string (thing));
} else {
dkdm = find_dkdm (thing);