41 #include "compose.hpp"
45 #include "filesystem.h"
52 #include <asdcp/KM_util.h>
53 #include <asdcp/KM_fileio.h>
54 #include <asdcp/AS_DCP.h>
55 #include <xmlsec/xmldsig.h>
56 #include <xmlsec/dl.h>
57 #include <xmlsec/app.h>
58 #include <xmlsec/crypto.h>
59 #include <libxml++/nodes/element.h>
60 #include <libxml++/document.h>
61 #include <openssl/sha.h>
62 #include <boost/algorithm/string.hpp>
63 #if BOOST_VERSION >= 106100
64 #include <boost/dll/runtime_symbol_info.hpp>
66 #include <boost/filesystem.hpp>
80 using std::shared_ptr;
82 using boost::shared_array;
83 using boost::optional;
84 using boost::function;
85 using boost::algorithm::trim;
92 ASDCP::Dictionary
const* dcp::asdcp_smpte_dict =
nullptr;
100 Kumu::GenRandomValue (
id);
101 id.EncodeHex (buffer, 64);
102 return string (buffer);
111 SHA1_Update (&sha, data.data(), data.
size());
112 byte_t byte_buffer[SHA_DIGEST_LENGTH];
113 SHA1_Final (byte_buffer, &sha);
115 return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
120 dcp::make_digest(boost::filesystem::path filename,
function<
void (int64_t, int64_t)> progress)
122 Kumu::FileReader reader;
123 auto r = reader.OpenRead(dcp::filesystem::fix_long_path(filename).
string().c_str());
124 if (ASDCP_FAILURE(r)) {
125 boost::throw_exception (
FileError(
"could not open file to compute digest", filename, r));
131 int const buffer_size = 65536;
132 Kumu::ByteString read_buffer (buffer_size);
134 Kumu::fsize_t done = 0;
135 Kumu::fsize_t
const size = reader.Size ();
138 auto r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
140 if (r == Kumu::RESULT_ENDOFFILE) {
142 }
else if (ASDCP_FAILURE (r)) {
143 boost::throw_exception (
FileError(
"could not read file to compute digest", filename, r));
146 SHA1_Update (&sha, read_buffer.Data(), read);
149 progress(done, size);
154 byte_t byte_buffer[SHA_DIGEST_LENGTH];
155 SHA1_Final (byte_buffer, &sha);
158 return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
163 dcp::init (optional<boost::filesystem::path> given_resources_directory)
165 if (xmlSecInit() < 0) {
166 throw MiscError (
"could not initialise xmlsec");
169 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
170 if (xmlSecCryptoDLLoadLibrary(BAD_CAST
"openssl") < 0) {
171 throw MiscError (
"unable to load openssl xmlsec-crypto library");
175 if (xmlSecCryptoAppInit(0) < 0) {
176 throw MiscError (
"could not initialise crypto");
179 if (xmlSecCryptoInit() < 0) {
180 throw MiscError (
"could not initialise xmlsec-crypto");
183 OpenSSL_add_all_algorithms();
185 asdcp_smpte_dict = &ASDCP::DefaultSMPTEDict();
187 auto res = given_resources_directory.get_value_or(resources_directory());
189 load_language_tag_lists (res /
"tags");
190 load_rating_list (res /
"ratings");
197 auto b64 = BIO_new (BIO_f_base64());
200 BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
203 char in_buffer[in.size() + 1];
205 for (
size_t i = 0; i < in.size(); ++i) {
206 if (in[i] !=
'\n' && in[i] !=
'\r') {
211 auto bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
212 bmem = BIO_push (b64, bmem);
213 int const N = BIO_read (bmem, out, out_length);
220 optional<boost::filesystem::path>
221 dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
223 auto i = root.begin ();
224 auto j = file.begin ();
226 while (i != root.end() && j != file.end() && *i == *j) {
231 if (i != root.end()) {
235 boost::filesystem::path rel;
236 while (j != file.end()) {
245 dcp::ids_equal (
string a,
string b)
247 transform (a.begin(), a.end(), a.begin(), ::tolower);
248 transform (b.begin(), b.end(), b.begin(), ::tolower);
256 dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
258 auto len = filesystem::file_size(p);
259 if (len > max_length) {
260 throw MiscError (String::compose(
"Unexpectedly long file (%1)", p.string()));
265 throw FileError (
"could not open file", p, errno);
268 std::vector<char> buffer(len);
270 int const N = f.read(buffer.data(), 1, len);
271 return string(buffer.data(), N);
276 dcp::write_string_to_file(
string const&
string, boost::filesystem::path
const& path)
278 File file(path,
"w");
280 throw FileError(
"could not open file", path, errno);
283 file.write(
string.c_str(),
string.length(), 1);
290 boost::replace_all (key,
"-----BEGIN RSA PRIVATE KEY-----\n",
"");
291 boost::replace_all (key,
"\n-----END RSA PRIVATE KEY-----\n",
"");
292 boost::replace_all (key,
"-----BEGIN PRIVATE KEY-----\n",
"");
293 boost::replace_all (key,
"\n-----END PRIVATE KEY-----\n",
"");
295 unsigned char buffer[4096];
300 SHA1_Update (&sha, buffer, N);
302 SHA1_Final (digest, &sha);
304 char digest_base64[64];
305 return Kumu::base64encode (digest, 20, digest_base64, 64);
310 dcp::remove_urn_uuid (
string raw)
312 if (raw.substr(0, 9) !=
"urn:uuid:") {
316 return raw.substr (9);
321 dcp::openjpeg_version ()
323 return opj_version ();
331 for (
int i = 0; i < n; ++i) {
339 dcp::indent (xmlpp::Element* element,
int initial)
341 xmlpp::Node* last =
nullptr;
342 for (
auto n: element->get_children()) {
343 auto e =
dynamic_cast<xmlpp::Element*
>(n);
345 element->add_child_text_before (e,
"\n" + spaces(initial + 2));
346 indent (e, initial + 2);
351 element->add_child_text (last,
"\n" + spaces(initial));
359 if (a.year() != b.year()) {
360 return a.year() < b.year();
363 if (a.month() != b.month()) {
364 return a.month() < b.month();
367 return a.day() <= b.day();
374 if (a.year() != b.year()) {
375 return a.year() > b.year();
378 if (a.month() != b.month()) {
379 return a.month() > b.month();
382 return a.day() >= b.day();
389 int const max_tries = existing.size() + 1;
390 for (
int i = 0; i < max_tries; ++i) {
391 string trial = String::compose(
"%1%2", base, i);
392 if (find(existing.begin(), existing.end(), trial) == existing.end()) {
401 ASDCPErrorSuspender::ASDCPErrorSuspender ()
402 : _old (Kumu::DefaultLogSink())
404 _sink =
new Kumu::EntryListLogSink(_log);
405 Kumu::SetDefaultLogSink (_sink);
409 ASDCPErrorSuspender::~ASDCPErrorSuspender ()
411 Kumu::SetDefaultLogSink (&_old);
416 boost::filesystem::path dcp::directory_containing_executable ()
418 #if BOOST_VERSION >= 106100
419 return filesystem::canonical(boost::dll::program_location().parent_path());
421 char buffer[PATH_MAX];
422 ssize_t N = readlink (
"/proc/self/exe", buffer, PATH_MAX);
423 return boost::filesystem::path(
string(buffer, N)).parent_path();
428 boost::filesystem::path dcp::resources_directory ()
431 char* prefix = getenv(
"LIBDCP_RESOURCES");
436 #if defined(LIBDCP_OSX)
437 return directory_containing_executable().parent_path() /
"Resources";
438 #elif defined(LIBDCP_WINDOWS)
439 return directory_containing_executable().parent_path();
441 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)
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)
bool day_less_than_or_equal(LocalTime a, LocalTime b)
std::string make_digest(boost::filesystem::path filename, boost::function< void(int64_t, int64_t)>)
bool day_greater_than_or_equal(LocalTime a, LocalTime b)
Utility methods and classes.