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 dcp::make_digest (Data data)
96 SHA1_Update (&sha, data.data().get(), data.size());
97 byte_t byte_buffer[SHA_DIGEST_LENGTH];
98 SHA1_Final (byte_buffer, &sha);
100 return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
103 /** Create a digest for a file.
104 * @param filename File name.
105 * @param progress Optional progress reporting function. The function will be called
106 * with a progress value between 0 and 1.
110 dcp::make_digest (boost::filesystem::path filename, function<void (float)> progress)
112 Kumu::FileReader reader;
113 Kumu::Result_t r = reader.OpenRead (filename.string().c_str ());
114 if (ASDCP_FAILURE (r)) {
115 boost::throw_exception (FileError ("could not open file to compute digest", filename, r));
121 int const buffer_size = 65536;
122 Kumu::ByteString read_buffer (buffer_size);
124 Kumu::fsize_t done = 0;
125 Kumu::fsize_t const size = reader.Size ();
128 Kumu::Result_t r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
130 if (r == Kumu::RESULT_ENDOFFILE) {
132 } else if (ASDCP_FAILURE (r)) {
133 boost::throw_exception (FileError ("could not read file to compute digest", filename, r));
136 SHA1_Update (&sha, read_buffer.Data(), read);
139 progress (float (done) / size);
144 byte_t byte_buffer[SHA_DIGEST_LENGTH];
145 SHA1_Final (byte_buffer, &sha);
148 return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
151 /** Convert a content kind to a string which can be used in a
152 * <ContentKind> node.
153 * @param kind ContentKind.
157 dcp::content_kind_to_string (ContentKind kind)
169 return "transitional";
176 case PUBLIC_SERVICE_ANNOUNCEMENT:
179 return "advertisement";
185 /** Convert a string from a <ContentKind> node to a libdcp ContentKind.
186 * Reasonably tolerant about varying case.
187 * @param kind Content kind string.
188 * @return libdcp ContentKind.
191 dcp::content_kind_from_string (string kind)
193 transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
195 if (kind == "feature") {
197 } else if (kind == "short") {
199 } else if (kind == "trailer") {
201 } else if (kind == "test") {
203 } else if (kind == "transitional") {
205 } else if (kind == "rating") {
207 } else if (kind == "teaser") {
209 } else if (kind == "policy") {
211 } else if (kind == "psa") {
212 return PUBLIC_SERVICE_ANNOUNCEMENT;
213 } else if (kind == "advertisement") {
214 return ADVERTISEMENT;
220 /** @param s A string.
221 * @return true if the string contains only space, newline or tab characters, or is empty.
224 dcp::empty_or_white_space (string s)
226 for (size_t i = 0; i < s.length(); ++i) {
227 if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
235 /** Set up various bits that the library needs. Should be called one
236 * by client applications.
241 if (xmlSecInit() < 0) {
242 throw MiscError ("could not initialise xmlsec");
245 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
246 if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
247 throw MiscError ("unable to load default xmlsec-crypto library");
251 if (xmlSecCryptoAppInit(0) < 0) {
252 throw MiscError ("could not initialise crypto");
255 if (xmlSecCryptoInit() < 0) {
256 throw MiscError ("could not initialise xmlsec-crypto");
259 OpenSSL_add_all_algorithms();
262 /** Decode a base64 string. The base64 decode routine in KM_util.cpp
263 * gives different values to both this and the command-line base64
264 * for some inputs. Not sure why.
266 * @param in base64-encoded string.
267 * @param out Output buffer.
268 * @param out_length Length of output buffer.
269 * @return Number of characters written to the output buffer.
272 dcp::base64_decode (string const & in, unsigned char* out, int out_length)
274 BIO* b64 = BIO_new (BIO_f_base64 ());
276 /* This means the input should have no newlines */
277 BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
279 /* Copy our input string, removing newlines */
280 char in_buffer[in.size() + 1];
282 for (size_t i = 0; i < in.size(); ++i) {
283 if (in[i] != '\n' && in[i] != '\r') {
288 BIO* bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
289 bmem = BIO_push (b64, bmem);
290 int const N = BIO_read (bmem, out, out_length);
296 /** @param p Path to open.
297 * @param t mode flags, as for fopen(3).
298 * @return FILE pointer or 0 on error.
300 * Apparently there is no way to create an ofstream using a UTF-8
301 * filename under Windows. We are hence reduced to using fopen
305 dcp::fopen_boost (boost::filesystem::path p, string t)
307 #ifdef LIBDCP_WINDOWS
308 wstring w (t.begin(), t.end());
309 /* c_str() here should give a UTF-16 string */
310 return _wfopen (p.c_str(), w.c_str ());
312 return fopen (p.c_str(), t.c_str ());
316 optional<boost::filesystem::path>
317 dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
319 boost::filesystem::path::const_iterator i = root.begin ();
320 boost::filesystem::path::const_iterator j = file.begin ();
322 while (i != root.end() && j != file.end() && *i == *j) {
327 if (i != root.end ()) {
328 return optional<boost::filesystem::path> ();
331 boost::filesystem::path rel;
332 while (j != file.end ()) {
340 dcp::ids_equal (string a, string b)
342 transform (a.begin(), a.end(), a.begin(), ::tolower);
343 transform (b.begin(), b.end(), b.begin(), ::tolower);
350 dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
352 uintmax_t len = boost::filesystem::file_size (p);
353 if (len > max_length) {
354 throw MiscError (String::compose ("Unexpectedly long file (%1)", p.string()));
357 FILE* f = fopen_boost (p, "r");
359 throw FileError ("could not open file", p, errno);
362 char* c = new char[len];
363 /* This may read less than `len' if we are on Windows and we have CRLF in the file */
364 int const N = fread (c, 1, len, f);
373 /** @param key RSA private key in PEM format (optionally with -----BEGIN... / -----END...)
374 * @return SHA1 fingerprint of key
377 dcp::private_key_fingerprint (string key)
379 boost::replace_all (key, "-----BEGIN RSA PRIVATE KEY-----\n", "");
380 boost::replace_all (key, "\n-----END RSA PRIVATE KEY-----\n", "");
382 unsigned char buffer[4096];
383 int const N = base64_decode (key, buffer, sizeof (buffer));
387 SHA1_Update (&sha, buffer, N);
389 SHA1_Final (digest, &sha);
391 char digest_base64[64];
392 return Kumu::base64encode (digest, 20, digest_base64, 64);
396 dcp::find_child (xmlpp::Node const * node, string name)
398 xmlpp::Node::NodeList c = node->get_children ();
399 xmlpp::Node::NodeList::iterator i = c.begin();
400 while (i != c.end() && (*i)->get_name() != name) {
404 DCP_ASSERT (i != c.end ());
409 dcp::remove_urn_uuid (string raw)
411 DCP_ASSERT (raw.substr(0, 9) == "urn:uuid:");
412 return raw.substr (9);
416 dcp::openjpeg_version ()
418 return opj_version ();