diff options
Diffstat (limited to 'src/lib/kdm_cli.cc')
| -rw-r--r-- | src/lib/kdm_cli.cc | 297 |
1 files changed, 173 insertions, 124 deletions
diff --git a/src/lib/kdm_cli.cc b/src/lib/kdm_cli.cc index bba8fb7c0..20cbf33e6 100644 --- a/src/lib/kdm_cli.cc +++ b/src/lib/kdm_cli.cc @@ -25,6 +25,7 @@ #include "cinema.h" +#include "cinema_list.h" #include "cross.h" #include "config.h" #include "dkdm_wrapper.h" @@ -33,6 +34,7 @@ #include "film.h" #include "kdm_with_metadata.h" #include "screen.h" +#include "variant.h" #include <dcp/certificate.h> #include <dcp/decrypted_kdm.h> #include <dcp/encrypted_kdm.h> @@ -44,6 +46,7 @@ using std::dynamic_pointer_cast; using std::list; using std::make_shared; +using std::pair; using std::runtime_error; using std::shared_ptr; using std::string; @@ -59,13 +62,19 @@ using namespace dcpomatic; static void help (std::function<void (string)> out) { - out (String::compose("Syntax: %1 [OPTION] <FILM|CPL-ID|DKDM>", program_name)); + out (String::compose("Syntax: %1 [OPTION] [COMMAND] <FILM|CPL-ID|DKDM>", program_name)); + out ("Commands:"); + out ("create create KDMs; default if no other command is specified"); + out (variant::insert_dcpomatic("list-cinemas list known cinemas from %1 settings")); + out (variant::insert_dcpomatic("list-dkdm-cpls list CPLs for which %1 has DKDMs")); + out (variant::insert_dcpomatic("add-dkdm add DKDM to %1's list")); + out (variant::insert_dcpomatic("dump-decryption-certificate write the %1 KDM decryption certificate to the console")); out (" -h, --help show this help"); 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 (" -f, --valid-from <time> valid from time (e.g. \"2013-09-28T01:41:51+04:00\", \"2018-01-01T12:00:30\") or \"now\""); + out (" -t, --valid-to <time> valid to time (e.g. \"2014-09-28T01: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"); @@ -75,19 +84,18 @@ help (std::function<void (string)> out) out (" -v, --verbose be verbose"); 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 (" -C, --projector-certificate <file> file containing projector certificate"); + out (" -T, --trusted-device-certificate <file> file containing a trusted device's certificate"); + out (" --decryption-key <file> file containing the private key which can decrypt the given DKDM"); + out (variant::insert_dcpomatic(" (%1's configured private key will be used otherwise)")); 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 (""); - out ("CPL-ID must be the ID of a CPL that is mentioned in DCP-o-matic's DKDM list."); + out (variant::insert_dcpomatic("CPL-ID must be the ID of a CPL that is mentioned in %1's DKDM list.")); out (""); out ("For example:"); out (""); out ("Create KDMs for my_great_movie to play in all of Fred's Cinema's screens for the next two weeks and zip them up."); - out ("(Fred's Cinema must have been set up in DCP-o-matic's KDM window)"); + out (variant::insert_dcpomatic("(Fred's Cinema must have been set up in %1's KDM window)")); out (""); out (String::compose("\t%1 -c \"Fred's Cinema\" -f now -d \"2 weeks\" -z my_great_movie", program_name)); } @@ -102,17 +110,6 @@ public: }; -static boost::posix_time::ptime -time_from_string (string t) -{ - if (t == "now") { - return boost::posix_time::second_clock::local_time (); - } - - return boost::posix_time::time_from_string (t); -} - - static boost::posix_time::time_duration duration_from_string (string d) { @@ -183,39 +180,32 @@ write_files ( } -static -shared_ptr<Cinema> -find_cinema (string cinema_name) +class ScreenDetails { - auto cinemas = Config::instance()->cinemas (); - auto i = cinemas.begin(); - while ( - i != cinemas.end() && - (*i)->name != cinema_name && - find ((*i)->emails.begin(), (*i)->emails.end(), cinema_name) == (*i)->emails.end()) { - - ++i; - } - - if (i == cinemas.end ()) { - throw KDMCLIError (String::compose("could not find cinema \"%1\"", cinema_name)); - } +public: + ScreenDetails(CinemaID const& cinema_id, Cinema const& cinema, Screen const& screen) + : cinema_id(cinema_id) + , cinema(cinema) + , screen(screen) + {} - return *i; -} + CinemaID cinema_id; + Cinema cinema; + Screen screen; +}; static void from_film ( - vector<shared_ptr<Screen>> screens, + vector<ScreenDetails> const& screens, boost::filesystem::path film_dir, bool verbose, boost::filesystem::path output, dcp::NameFormat container_name_format, dcp::NameFormat filename_format, - boost::posix_time::ptime valid_from, - boost::posix_time::ptime valid_to, + dcp::LocalTime valid_from, + dcp::LocalTime valid_to, dcp::Formulation formulation, bool disable_forensic_marking_picture, optional<int> disable_forensic_marking_audio, @@ -249,17 +239,27 @@ from_film ( try { list<KDMWithMetadataPtr> kdms; - for (auto i: screens) { + for (auto screen_details: screens) { 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); + auto p = kdm_for_screen( + make_kdm, + screen_details.cinema_id, + screen_details.cinema, + screen_details.screen, + 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(), @@ -359,14 +359,14 @@ kdm_from_dkdm ( static void from_dkdm ( - vector<shared_ptr<Screen>> screens, + vector<ScreenDetails> const& screens, dcp::DecryptedKDM dkdm, bool verbose, boost::filesystem::path output, dcp::NameFormat container_name_format, dcp::NameFormat filename_format, - boost::posix_time::ptime valid_from, - boost::posix_time::ptime valid_to, + dcp::LocalTime valid_from, + dcp::LocalTime valid_to, dcp::Formulation formulation, bool disable_forensic_marking_picture, optional<int> disable_forensic_marking_audio, @@ -379,37 +379,31 @@ from_dkdm ( try { list<KDMWithMetadataPtr> kdms; - for (auto i: screens) { - if (!i->recipient) { + for (auto const& screen_details: screens) { + if (!screen_details.screen.recipient) { continue; } - 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, - i->recipient.get(), - i->trusted_device_thumbprints(), - begin, - end, + screen_details.screen.recipient.get(), + screen_details.screen.trusted_device_thumbprints(), + valid_from, + valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio ); dcp::NameFormat::Map name_values; - name_values['c'] = i->cinema ? i->cinema->name : ""; - name_values['s'] = i->name; + name_values['c'] = screen_details.cinema.name; + name_values['s'] = screen_details.screen.name; 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['b'] = valid_from.date() + " " + valid_from.time_of_day(true, false); + name_values['e'] = valid_to.date() + " " + valid_to.time_of_day(true, false); name_values['i'] = kdm.cpl_id(); - kdms.push_back(make_shared<KDMWithMetadata>(name_values, i->cinema.get(), i->cinema ? i->cinema->emails : vector<string>(), kdm)); + kdms.push_back(make_shared<KDMWithMetadata>(name_values, screen_details.cinema_id, screen_details.cinema.emails, kdm)); } write_files (kdms, zip, output, container_name_format, filename_format, verbose, out); if (email) { @@ -443,6 +437,22 @@ dump_dkdm_group (shared_ptr<DKDMGroup> group, int indent, std::function<void (st } +static +dcp::LocalTime +time_from_string(string time) +{ + if (time == "now") { + return {}; + } + + if (time.length() > 10 && time[10] == ' ') { + time[10] = 'T'; + } + + return dcp::LocalTime(time); +} + + void dump_decryption_certificate(std::function<void (string)> out) { @@ -462,17 +472,20 @@ try 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(); + /* either a cinema name to search for, or the name of a cinema to associate with certificate */ optional<string> cinema_name; - shared_ptr<Cinema> cinema; - optional<boost::filesystem::path> certificate; - optional<string> screen; - vector<shared_ptr<Screen>> screens; + /* either a screen name to search for, or the name of a screen to associate with certificate */ + optional<string> screen_name; + /* a certificate that we will use to make up a temporary cinema and screen */ + optional<boost::filesystem::path> projector_certificate; + optional<boost::filesystem::path> decryption_key; + /* trusted devices that we will use to make up a temporary cinema and screen */ + vector<TrustedDevice> trusted_devices; optional<dcp::EncryptedKDM> dkdm; - optional<boost::posix_time::ptime> valid_from; - optional<boost::posix_time::ptime> valid_to; + optional<dcp::LocalTime> valid_from; + optional<dcp::LocalTime> valid_to; bool zip = false; - bool list_cinemas = false; - bool list_dkdm_cpls = false; + string command = "create"; optional<string> duration_string; bool verbose = false; dcp::Formulation formulation = dcp::Formulation::MODIFIED_TRANSITIONAL_1; @@ -504,16 +517,14 @@ try { "verbose", no_argument, 0, 'v' }, { "cinema", required_argument, 0, 'c' }, { "screen", required_argument, 0, 'S' }, - { "certificate", required_argument, 0, 'C' }, - { "trusted-device", required_argument, 0, 'T' }, - { "list-cinemas", no_argument, 0, 'B' }, - { "list-dkdm-cpls", no_argument, 0, 'D' }, + { "projector-certificate", required_argument, 0, 'C' }, + { "trusted-device-certificate", required_argument, 0, 'T' }, + { "decryption-key", required_argument, 0, 'G' }, { "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:BDE:G", long_options, &option_index); + int c = getopt_long(argc, argv, "ho:K:Z:f:t:d:F:pae::zvc:S:C:T:E:G", long_options, &option_index); if (c == -1) { break; @@ -533,10 +544,10 @@ try container_name_format = dcp::NameFormat (optarg); break; case 'f': - valid_from = time_from_string (optarg); + valid_from = time_from_string(optarg); break; case 't': - valid_to = time_from_string (optarg); + valid_to = dcp::LocalTime(optarg); break; case 'd': duration_string = optarg; @@ -575,70 +586,92 @@ try verbose = true; break; case 'c': - /* This could be a cinema to search for in the configured list or the name of a cinema being - built up on-the-fly in the option. Cater for both possilibities here by storing the name - (for lookup) and by creating a Cinema which the next Screen will be added to. - */ cinema_name = optarg; - cinema = make_shared<Cinema>(optarg, vector<string>(), "", 0, 0); break; case 'S': - /* Similarly, this could be the name of a new (temporary) screen or the name of a screen - * to search for. - */ - screen = optarg; + screen_name = optarg; break; case 'C': - certificate = optarg; + projector_certificate = optarg; break; case 'T': - /* A trusted device ends up in the last screen we made */ - if (!screens.empty ()) { - screens.back()->trusted_devices.push_back(TrustedDevice(dcp::Certificate(dcp::file_to_string(optarg)))); - } - break; - case 'B': - list_cinemas = true; + trusted_devices.push_back(TrustedDevice(dcp::Certificate(dcp::file_to_string(optarg)))); break; - case 'D': - list_dkdm_cpls = true; + case 'G': + decryption_key = optarg; break; case 'E': cinemas_file = optarg; break; - case 'G': - dump_decryption_certificate(out); - return {}; } } + vector<string> commands = { + "create", + "list-cinemas", + "list-dkdm-cpls", + "add-dkdm", + "dump-decryption-certificate" + }; + + if (optind < argc - 1) { + /* Command with some KDM / CPL / whever specified afterwards */ + command = argv[optind++]; + } else if (optind < argc) { + /* Look for a valid command, hoping that it's not the name of the KDM / CPL / whatever */ + if (std::find(commands.begin(), commands.end(), argv[optind]) != commands.end()) { + command = argv[optind++]; + } + } + + if (std::find(commands.begin(), commands.end(), command) == commands.end()) { + throw KDMCLIError(String::compose("Unrecognised command %1", command)); + } + 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 we've been given a certificate we can make up a temporary cinema and screen (not written to the + * database) to then use for making KDMs. + */ + optional<Cinema> temp_cinema; + optional<Screen> temp_screen; + if (projector_certificate) { + temp_cinema = Cinema(cinema_name.get_value_or(""), {}, "", dcp::UTCOffset()); + dcp::CertificateChain chain(dcp::file_to_string(*projector_certificate)); + temp_screen = Screen(screen_name.get_value_or(""), "", chain.leaf(), boost::none, trusted_devices); } - if (list_cinemas) { - auto cinemas = Config::instance()->cinemas (); - for (auto i: cinemas) { - out (String::compose("%1 (%2)", i->name, Email::address_list(i->emails))); + if (command == "list-cinemas") { + CinemaList cinemas; + for (auto const& cinema: cinemas.cinemas()) { + out(String::compose("%1 (%2)", cinema.second.name, Email::address_list(cinema.second.emails))); } return {}; } - if (list_dkdm_cpls) { + if (command == "list-dkdm-cpls") { dump_dkdm_group (Config::instance()->dkdms(), 0, out); return {}; } + if (command == "dump-deccryption-certificate") { + dump_decryption_certificate(out); + return {}; + } + + if (optind >= argc) { + throw KDMCLIError("no film, CPL ID or DKDM specified"); + } + + if (command == "add-dkdm") { + auto dkdms = Config::instance()->dkdms(); + dkdms->add(make_shared<DKDM>(dcp::EncryptedKDM(dcp::file_to_string(argv[optind])))); + Config::instance()->write_config(); + return {}; + } + if (!duration_string && !valid_to) { throw KDMCLIError ("you must specify a --valid-duration or --valid-to"); } @@ -651,23 +684,37 @@ try throw KDMCLIError ("no film, CPL ID or DKDM specified"); } - if (screens.empty()) { + vector<ScreenDetails> screens; + + if (!temp_cinema) { if (!cinema_name) { - throw KDMCLIError ("you must specify either a cinema or one or more screens using certificate files"); + throw KDMCLIError("you must specify either a cinema or one or more screens using certificate files"); } - 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()); + CinemaList cinema_list; + if (auto cinema = cinema_list.cinema_by_name_or_email(*cinema_name)) { + if (screen_name) { + for (auto screen: cinema_list.screens_by_cinema_and_name(cinema->first, *screen_name)) { + screens.push_back({cinema->first, cinema->second, screen.second}); + } + } else { + for (auto screen: cinema_list.screens(cinema->first)) { + screens.push_back({cinema->first, cinema->second, screen.second}); + } + } } + } else { + DCPOMATIC_ASSERT(temp_screen); + screens.push_back({CinemaID(0), *temp_cinema, *temp_screen}); } if (duration_string) { - valid_to = valid_from.get() + duration_from_string (*duration_string); + valid_to = valid_from.get(); + valid_to->add(duration_from_string(*duration_string)); } 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()))); + out(String::compose("Making KDMs valid from %1 to %2", valid_from->as_string(), valid_to->as_string())); } string const thing = argv[optind]; @@ -699,9 +746,11 @@ try throw KDMCLIError ("could not find film or CPL ID corresponding to " + thing); } + string const key = decryption_key ? dcp::file_to_string(*decryption_key) : Config::instance()->decryption_chain()->key().get(); + from_dkdm ( screens, - dcp::DecryptedKDM (*dkdm, Config::instance()->decryption_chain()->key().get()), + dcp::DecryptedKDM(*dkdm, key), verbose, output, container_name_format, |
