41 #include "compose.hpp"
44 #include "language_tag.h"
50 #include <asdcp/KM_util.h>
51 #include <asdcp/KM_fileio.h>
52 #include <asdcp/AS_DCP.h>
53 #include <xmlsec/xmldsig.h>
54 #include <xmlsec/dl.h>
55 #include <xmlsec/app.h>
56 #include <xmlsec/crypto.h>
57 #include <libxml++/nodes/element.h>
58 #include <libxml++/document.h>
59 #include <openssl/sha.h>
60 #include <boost/algorithm/string.hpp>
61 #if BOOST_VERSION >= 106100
62 #include <boost/dll/runtime_symbol_info.hpp>
64 #include <boost/filesystem.hpp>
78 using std::shared_ptr;
80 using boost::shared_array;
81 using boost::optional;
82 using boost::function;
83 using boost::algorithm::trim;
90 ASDCP::Dictionary
const* dcp::asdcp_smpte_dict =
nullptr;
98 Kumu::GenRandomValue (
id);
99 id.EncodeHex (buffer, 64);
100 return string (buffer);
109 SHA1_Update (&sha, data.data(), data.
size());
110 byte_t byte_buffer[SHA_DIGEST_LENGTH];
111 SHA1_Final (byte_buffer, &sha);
113 return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
118 dcp::make_digest (boost::filesystem::path filename,
function<
void (
float)> progress)
120 Kumu::FileReader reader;
121 auto r = reader.OpenRead (filename.string().c_str ());
122 if (ASDCP_FAILURE(r)) {
123 boost::throw_exception (
FileError(
"could not open file to compute digest", filename, r));
129 int const buffer_size = 65536;
130 Kumu::ByteString read_buffer (buffer_size);
132 Kumu::fsize_t done = 0;
133 Kumu::fsize_t
const size = reader.Size ();
136 auto r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
138 if (r == Kumu::RESULT_ENDOFFILE) {
140 }
else if (ASDCP_FAILURE (r)) {
141 boost::throw_exception (
FileError(
"could not read file to compute digest", filename, r));
144 SHA1_Update (&sha, read_buffer.Data(), read);
147 progress (
float (done) / size);
152 byte_t byte_buffer[SHA_DIGEST_LENGTH];
153 SHA1_Final (byte_buffer, &sha);
156 return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
163 for (
size_t i = 0; i < s.length(); ++i) {
164 if (s[i] !=
' ' && s[i] !=
'\n' && s[i] !=
'\t') {
174 dcp::init (optional<boost::filesystem::path> given_resources_directory)
176 if (xmlSecInit() < 0) {
177 throw MiscError (
"could not initialise xmlsec");
180 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
181 if (xmlSecCryptoDLLoadLibrary(BAD_CAST
"openssl") < 0) {
182 throw MiscError (
"unable to load openssl xmlsec-crypto library");
186 if (xmlSecCryptoAppInit(0) < 0) {
187 throw MiscError (
"could not initialise crypto");
190 if (xmlSecCryptoInit() < 0) {
191 throw MiscError (
"could not initialise xmlsec-crypto");
194 OpenSSL_add_all_algorithms();
196 asdcp_smpte_dict = &ASDCP::DefaultSMPTEDict();
198 auto res = given_resources_directory.get_value_or(resources_directory());
200 load_language_tag_lists (res /
"tags");
201 load_rating_list (res /
"ratings");
208 auto b64 = BIO_new (BIO_f_base64());
211 BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
214 char in_buffer[in.size() + 1];
216 for (
size_t i = 0; i < in.size(); ++i) {
217 if (in[i] !=
'\n' && in[i] !=
'\r') {
222 auto bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
223 bmem = BIO_push (b64, bmem);
224 int const N = BIO_read (bmem, out, out_length);
234 #ifdef LIBDCP_WINDOWS
235 wstring w (t.begin(), t.end());
237 return _wfopen (p.c_str(), w.c_str ());
239 return fopen (p.c_str(), t.c_str ());
244 optional<boost::filesystem::path>
245 dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
247 auto i = root.begin ();
248 auto j = file.begin ();
250 while (i != root.end() && j != file.end() && *i == *j) {
255 if (i != root.end()) {
259 boost::filesystem::path rel;
260 while (j != file.end()) {
269 dcp::ids_equal (
string a,
string b)
271 transform (a.begin(), a.end(), a.begin(), ::tolower);
272 transform (b.begin(), b.end(), b.begin(), ::tolower);
280 dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
282 auto len = boost::filesystem::file_size (p);
283 if (len > max_length) {
284 throw MiscError (String::compose(
"Unexpectedly long file (%1)", p.string()));
289 throw FileError (
"could not open file", p, errno);
292 char* c =
new char[len];
294 int const N = fread (c, 1, len, f);
307 boost::replace_all (key,
"-----BEGIN RSA PRIVATE KEY-----\n",
"");
308 boost::replace_all (key,
"\n-----END RSA PRIVATE KEY-----\n",
"");
310 unsigned char buffer[4096];
315 SHA1_Update (&sha, buffer, N);
317 SHA1_Final (digest, &sha);
319 char digest_base64[64];
320 return Kumu::base64encode (digest, 20, digest_base64, 64);
325 dcp::find_child (xmlpp::Node
const * node,
string name)
327 auto c = node->get_children ();
329 while (i != c.end() && (*i)->get_name() != name) {
333 DCP_ASSERT (i != c.end ());
339 dcp::remove_urn_uuid (
string raw)
341 DCP_ASSERT (raw.substr(0, 9) ==
"urn:uuid:");
342 return raw.substr (9);
347 dcp::openjpeg_version ()
349 return opj_version ();
357 for (
int i = 0; i < n; ++i) {
365 dcp::indent (xmlpp::Element* element,
int initial)
367 xmlpp::Node* last =
nullptr;
368 for (
auto n: element->get_children()) {
369 auto e =
dynamic_cast<xmlpp::Element*
>(n);
371 element->add_child_text_before (e,
"\n" + spaces(initial + 2));
372 indent (e, initial + 2);
377 element->add_child_text (last,
"\n" + spaces(initial));
385 if (a.year() != b.year()) {
386 return a.year() < b.year();
389 if (a.month() != b.month()) {
390 return a.month() < b.month();
393 return a.day() <= b.day();
400 if (a.year() != b.year()) {
401 return a.year() > b.year();
404 if (a.month() != b.month()) {
405 return a.month() > b.month();
408 return a.day() >= b.day();
415 int const max_tries = existing.size() + 1;
416 for (
int i = 0; i < max_tries; ++i) {
417 string trial = String::compose(
"%1%2", base, i);
418 if (find(existing.begin(), existing.end(), trial) == existing.end()) {
427 ASDCPErrorSuspender::ASDCPErrorSuspender ()
428 : _old (Kumu::DefaultLogSink())
430 _sink =
new Kumu::EntryListLogSink(_log);
431 Kumu::SetDefaultLogSink (_sink);
435 ASDCPErrorSuspender::~ASDCPErrorSuspender ()
437 Kumu::SetDefaultLogSink (&_old);
442 boost::filesystem::path dcp::directory_containing_executable ()
444 #if BOOST_VERSION >= 106100
445 return boost::filesystem::canonical(boost::dll::program_location().parent_path());
447 char buffer[PATH_MAX];
448 ssize_t N = readlink (
"/proc/self/exe", buffer, PATH_MAX);
449 return boost::filesystem::path(
string(buffer, N)).parent_path();
454 boost::filesystem::path dcp::resources_directory ()
457 char* prefix = getenv(
"LIBDCP_RESOURCES");
462 #if defined(LIBDCP_OSX)
463 return directory_containing_executable().parent_path() /
"Resources";
464 #elif defined(LIBDCP_WINDOWS)
465 return directory_containing_executable().parent_path();
467 return directory_containing_executable().parent_path() /
"share" /
"libdcp";
Class to hold an arbitrary block of data.
int size() const override
An exception related to a file.
A representation of a local time (down to the second), including its offset from GMT (equivalent to x...
A miscellaneous exception.
Exceptions thrown by libdcp.
Namespace for everything in libdcp.
std::string unique_string(std::vector< std::string > existing, std::string base)
bool empty_or_white_space(std::string s)
void init(boost::optional< boost::filesystem::path > resources_directory=boost::optional< boost::filesystem::path >())
int base64_decode(std::string const &in, unsigned char *out, int out_length)
std::string private_key_fingerprint(std::string key)
std::string make_digest(boost::filesystem::path filename, boost::function< void(float)>)
bool day_less_than_or_equal(LocalTime a, LocalTime b)
FILE * fopen_boost(boost::filesystem::path, std::string)
bool day_greater_than_or_equal(LocalTime a, LocalTime b)
Utility methods and classes.