2 Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 * @brief Utility methods.
39 #include "exceptions.h"
41 #include "certificate.h"
42 #include "openjpeg_image.h"
43 #include "dcp_assert.h"
44 #include "compose.hpp"
46 #include <asdcp/KM_util.h>
47 #include <asdcp/KM_fileio.h>
48 #include <asdcp/AS_DCP.h>
49 #include <xmlsec/xmldsig.h>
50 #include <xmlsec/dl.h>
51 #include <xmlsec/app.h>
52 #include <xmlsec/crypto.h>
53 #include <libxml++/nodes/element.h>
54 #include <libxml++/document.h>
55 #include <openssl/sha.h>
56 #include <boost/filesystem.hpp>
57 #include <boost/algorithm/string.hpp>
71 using boost::shared_ptr;
72 using boost::shared_array;
73 using boost::optional;
74 using boost::function;
75 using boost::algorithm::trim;
86 Kumu::GenRandomValue (id);
87 id.EncodeHex (buffer, 64);
88 return string (buffer);
92 /** Create a digest for a file.
93 * @param filename File name.
94 * @param progress Optional progress reporting function. The function will be called
95 * with a progress value between 0 and 1.
99 dcp::make_digest (boost::filesystem::path filename, function<void (float)> progress)
101 Kumu::FileReader reader;
102 Kumu::Result_t r = reader.OpenRead (filename.string().c_str ());
103 if (ASDCP_FAILURE (r)) {
104 boost::throw_exception (FileError ("could not open file to compute digest", filename, r));
110 int const buffer_size = 65536;
111 Kumu::ByteString read_buffer (buffer_size);
113 Kumu::fsize_t done = 0;
114 Kumu::fsize_t const size = reader.Size ();
117 Kumu::Result_t r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
119 if (r == Kumu::RESULT_ENDOFFILE) {
121 } else if (ASDCP_FAILURE (r)) {
122 boost::throw_exception (FileError ("could not read file to compute digest", filename, r));
125 SHA1_Update (&sha, read_buffer.Data(), read);
128 progress (float (done) / size);
133 byte_t byte_buffer[SHA_DIGEST_LENGTH];
134 SHA1_Final (byte_buffer, &sha);
137 return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
140 /** Convert a content kind to a string which can be used in a
141 * <ContentKind> node.
142 * @param kind ContentKind.
146 dcp::content_kind_to_string (ContentKind kind)
158 return "transitional";
165 case PUBLIC_SERVICE_ANNOUNCEMENT:
168 return "advertisement";
174 /** Convert a string from a <ContentKind> node to a libdcp ContentKind.
175 * Reasonably tolerant about varying case.
176 * @param kind Content kind string.
177 * @return libdcp ContentKind.
180 dcp::content_kind_from_string (string kind)
182 transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
184 if (kind == "feature") {
186 } else if (kind == "short") {
188 } else if (kind == "trailer") {
190 } else if (kind == "test") {
192 } else if (kind == "transitional") {
194 } else if (kind == "rating") {
196 } else if (kind == "teaser") {
198 } else if (kind == "policy") {
200 } else if (kind == "psa") {
201 return PUBLIC_SERVICE_ANNOUNCEMENT;
202 } else if (kind == "advertisement") {
203 return ADVERTISEMENT;
209 /** @param s A string.
210 * @return true if the string contains only space, newline or tab characters, or is empty.
213 dcp::empty_or_white_space (string s)
215 for (size_t i = 0; i < s.length(); ++i) {
216 if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
224 /** Set up various bits that the library needs. Should be called one
225 * by client applications.
230 if (xmlSecInit() < 0) {
231 throw MiscError ("could not initialise xmlsec");
234 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
235 if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
236 throw MiscError ("unable to load default xmlsec-crypto library");
240 if (xmlSecCryptoAppInit(0) < 0) {
241 throw MiscError ("could not initialise crypto");
244 if (xmlSecCryptoInit() < 0) {
245 throw MiscError ("could not initialise xmlsec-crypto");
248 OpenSSL_add_all_algorithms();
251 /** Decode a base64 string. The base64 decode routine in KM_util.cpp
252 * gives different values to both this and the command-line base64
253 * for some inputs. Not sure why.
255 * @param in base64-encoded string.
256 * @param out Output buffer.
257 * @param out_length Length of output buffer.
258 * @return Number of characters written to the output buffer.
261 dcp::base64_decode (string const & in, unsigned char* out, int out_length)
263 BIO* b64 = BIO_new (BIO_f_base64 ());
265 /* This means the input should have no newlines */
266 BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
268 /* Copy our input string, removing newlines */
269 char in_buffer[in.size() + 1];
271 for (size_t i = 0; i < in.size(); ++i) {
272 if (in[i] != '\n' && in[i] != '\r') {
277 BIO* bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
278 bmem = BIO_push (b64, bmem);
279 int const N = BIO_read (bmem, out, out_length);
285 /** @param p Path to open.
286 * @param t mode flags, as for fopen(3).
287 * @return FILE pointer or 0 on error.
289 * Apparently there is no way to create an ofstream using a UTF-8
290 * filename under Windows. We are hence reduced to using fopen
294 dcp::fopen_boost (boost::filesystem::path p, string t)
296 #ifdef LIBDCP_WINDOWS
297 wstring w (t.begin(), t.end());
298 /* c_str() here should give a UTF-16 string */
299 return _wfopen (p.c_str(), w.c_str ());
301 return fopen (p.c_str(), t.c_str ());
305 optional<boost::filesystem::path>
306 dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
308 boost::filesystem::path::const_iterator i = root.begin ();
309 boost::filesystem::path::const_iterator j = file.begin ();
311 while (i != root.end() && j != file.end() && *i == *j) {
316 if (i != root.end ()) {
317 return optional<boost::filesystem::path> ();
320 boost::filesystem::path rel;
321 while (j != file.end ()) {
329 dcp::ids_equal (string a, string b)
331 transform (a.begin(), a.end(), a.begin(), ::tolower);
332 transform (b.begin(), b.end(), b.begin(), ::tolower);
339 dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
341 uintmax_t len = boost::filesystem::file_size (p);
342 if (len > max_length) {
343 throw MiscError (String::compose ("Unexpectedly long file (%1)", p.string()));
346 FILE* f = fopen_boost (p, "r");
348 throw FileError ("could not open file", p, errno);
351 char* c = new char[len];
352 /* This may read less than `len' if we are on Windows and we have CRLF in the file */
353 int const N = fread (c, 1, len, f);
362 /** @param key RSA private key in PEM format (optionally with -----BEGIN... / -----END...)
363 * @return SHA1 fingerprint of key
366 dcp::private_key_fingerprint (string key)
368 boost::replace_all (key, "-----BEGIN RSA PRIVATE KEY-----\n", "");
369 boost::replace_all (key, "\n-----END RSA PRIVATE KEY-----\n", "");
371 unsigned char buffer[4096];
372 int const N = base64_decode (key, buffer, sizeof (buffer));
376 SHA1_Update (&sha, buffer, N);
378 SHA1_Final (digest, &sha);
380 char digest_base64[64];
381 return Kumu::base64encode (digest, 20, digest_base64, 64);
385 dcp::find_child (xmlpp::Node const * node, string name)
387 xmlpp::Node::NodeList c = node->get_children ();
388 xmlpp::Node::NodeList::iterator i = c.begin();
389 while (i != c.end() && (*i)->get_name() != name) {
393 DCP_ASSERT (i != c.end ());
398 dcp::remove_urn_uuid (string raw)
400 DCP_ASSERT (raw.substr(0, 9) == "urn:uuid:");
401 return raw.substr (9);
405 dcp::openjpeg_version ()
407 return opj_version ();