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"
45 #include <asdcp/KM_util.h>
46 #include <asdcp/KM_fileio.h>
47 #include <asdcp/AS_DCP.h>
48 #include <xmlsec/xmldsig.h>
49 #include <xmlsec/dl.h>
50 #include <xmlsec/app.h>
51 #include <xmlsec/crypto.h>
52 #include <libxml++/nodes/element.h>
53 #include <libxml++/document.h>
54 #include <openssl/sha.h>
55 #include <boost/filesystem.hpp>
56 #include <boost/algorithm/string.hpp>
70 using boost::shared_ptr;
71 using boost::shared_array;
72 using boost::optional;
73 using boost::function;
74 using boost::algorithm::trim;
85 Kumu::GenRandomValue (id);
86 id.EncodeHex (buffer, 64);
87 return string (buffer);
91 /** Create a digest for a file.
92 * @param filename File name.
93 * @param progress Optional progress reporting function. The function will be called
94 * with a progress value between 0 and 1.
98 dcp::make_digest (boost::filesystem::path filename, function<void (float)> progress)
100 Kumu::FileReader reader;
101 Kumu::Result_t r = reader.OpenRead (filename.string().c_str ());
102 if (ASDCP_FAILURE (r)) {
103 boost::throw_exception (FileError ("could not open file to compute digest", filename, r));
109 int const buffer_size = 65536;
110 Kumu::ByteString read_buffer (buffer_size);
112 Kumu::fsize_t done = 0;
113 Kumu::fsize_t const size = reader.Size ();
116 Kumu::Result_t r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
118 if (r == Kumu::RESULT_ENDOFFILE) {
120 } else if (ASDCP_FAILURE (r)) {
121 boost::throw_exception (FileError ("could not read file to compute digest", filename, r));
124 SHA1_Update (&sha, read_buffer.Data(), read);
127 progress (float (done) / size);
132 byte_t byte_buffer[SHA_DIGEST_LENGTH];
133 SHA1_Final (byte_buffer, &sha);
136 return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
139 /** Convert a content kind to a string which can be used in a
140 * <ContentKind> node.
141 * @param kind ContentKind.
145 dcp::content_kind_to_string (ContentKind kind)
157 return "transitional";
164 case PUBLIC_SERVICE_ANNOUNCEMENT:
167 return "advertisement";
173 /** Convert a string from a <ContentKind> node to a libdcp ContentKind.
174 * Reasonably tolerant about varying case.
175 * @param kind Content kind string.
176 * @return libdcp ContentKind.
179 dcp::content_kind_from_string (string kind)
181 transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
183 if (kind == "feature") {
185 } else if (kind == "short") {
187 } else if (kind == "trailer") {
189 } else if (kind == "test") {
191 } else if (kind == "transitional") {
193 } else if (kind == "rating") {
195 } else if (kind == "teaser") {
197 } else if (kind == "policy") {
199 } else if (kind == "psa") {
200 return PUBLIC_SERVICE_ANNOUNCEMENT;
201 } else if (kind == "advertisement") {
202 return ADVERTISEMENT;
208 /** @param s A string.
209 * @return true if the string contains only space, newline or tab characters, or is empty.
212 dcp::empty_or_white_space (string s)
214 for (size_t i = 0; i < s.length(); ++i) {
215 if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
223 /** Set up various bits that the library needs. Should be called one
224 * by client applications.
229 if (xmlSecInit() < 0) {
230 throw MiscError ("could not initialise xmlsec");
233 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
234 if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
235 throw MiscError ("unable to load default xmlsec-crypto library");
239 if (xmlSecCryptoAppInit(0) < 0) {
240 throw MiscError ("could not initialise crypto");
243 if (xmlSecCryptoInit() < 0) {
244 throw MiscError ("could not initialise xmlsec-crypto");
247 OpenSSL_add_all_algorithms();
250 /** Decode a base64 string. The base64 decode routine in KM_util.cpp
251 * gives different values to both this and the command-line base64
252 * for some inputs. Not sure why.
254 * @param in base64-encoded string.
255 * @param out Output buffer.
256 * @param out_length Length of output buffer.
257 * @return Number of characters written to the output buffer.
260 dcp::base64_decode (string const & in, unsigned char* out, int out_length)
262 BIO* b64 = BIO_new (BIO_f_base64 ());
264 /* This means the input should have no newlines */
265 BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
267 /* Copy our input string, removing newlines */
268 char in_buffer[in.size() + 1];
270 for (size_t i = 0; i < in.size(); ++i) {
271 if (in[i] != '\n' && in[i] != '\r') {
276 BIO* bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
277 bmem = BIO_push (b64, bmem);
278 int const N = BIO_read (bmem, out, out_length);
284 /** @param p Path to open.
285 * @param t mode flags, as for fopen(3).
286 * @return FILE pointer or 0 on error.
288 * Apparently there is no way to create an ofstream using a UTF-8
289 * filename under Windows. We are hence reduced to using fopen
293 dcp::fopen_boost (boost::filesystem::path p, string t)
295 #ifdef LIBDCP_WINDOWS
296 wstring w (t.begin(), t.end());
297 /* c_str() here should give a UTF-16 string */
298 return _wfopen (p.c_str(), w.c_str ());
300 return fopen (p.c_str(), t.c_str ());
304 optional<boost::filesystem::path>
305 dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
307 boost::filesystem::path::const_iterator i = root.begin ();
308 boost::filesystem::path::const_iterator j = file.begin ();
310 while (i != root.end() && j != file.end() && *i == *j) {
315 if (i != root.end ()) {
316 return optional<boost::filesystem::path> ();
319 boost::filesystem::path rel;
320 while (j != file.end ()) {
328 dcp::ids_equal (string a, string b)
330 transform (a.begin(), a.end(), a.begin(), ::tolower);
331 transform (b.begin(), b.end(), b.begin(), ::tolower);
338 dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
340 uintmax_t len = boost::filesystem::file_size (p);
341 if (len > max_length) {
342 throw MiscError (String::compose ("Unexpectedly long file (%1)", p.string()));
345 FILE* f = fopen_boost (p, "r");
347 throw FileError ("could not open file", p, errno);
350 char* c = new char[len];
351 /* This may read less than `len' if we are on Windows and we have CRLF in the file */
352 int const N = fread (c, 1, len, f);
361 /** @param key RSA private key in PEM format (optionally with -----BEGIN... / -----END...)
362 * @return SHA1 fingerprint of key
365 dcp::private_key_fingerprint (string key)
367 boost::replace_all (key, "-----BEGIN RSA PRIVATE KEY-----\n", "");
368 boost::replace_all (key, "\n-----END RSA PRIVATE KEY-----\n", "");
370 unsigned char buffer[4096];
371 int const N = base64_decode (key, buffer, sizeof (buffer));
375 SHA1_Update (&sha, buffer, N);
377 SHA1_Final (digest, &sha);
379 char digest_base64[64];
380 return Kumu::base64encode (digest, 20, digest_base64, 64);
384 dcp::find_child (xmlpp::Node const * node, string name)
386 xmlpp::Node::NodeList c = node->get_children ();
387 xmlpp::Node::NodeList::iterator i = c.begin();
388 while (i != c.end() && (*i)->get_name() != name) {
392 DCP_ASSERT (i != c.end ());
397 dcp::remove_urn_uuid (string raw)
399 DCP_ASSERT (raw.substr(0, 9) == "urn:uuid:");
400 return raw.substr (9);