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 /** @param s A string.
152 * @return true if the string contains only space, newline or tab characters, or is empty.
155 dcp::empty_or_white_space (string s)
157 for (size_t i = 0; i < s.length(); ++i) {
158 if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
166 /** Set up various bits that the library needs. Should be called one
167 * by client applications.
172 if (xmlSecInit() < 0) {
173 throw MiscError ("could not initialise xmlsec");
176 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
177 if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
178 throw MiscError ("unable to load default xmlsec-crypto library");
182 if (xmlSecCryptoAppInit(0) < 0) {
183 throw MiscError ("could not initialise crypto");
186 if (xmlSecCryptoInit() < 0) {
187 throw MiscError ("could not initialise xmlsec-crypto");
190 OpenSSL_add_all_algorithms();
193 /** Decode a base64 string. The base64 decode routine in KM_util.cpp
194 * gives different values to both this and the command-line base64
195 * for some inputs. Not sure why.
197 * @param in base64-encoded string.
198 * @param out Output buffer.
199 * @param out_length Length of output buffer.
200 * @return Number of characters written to the output buffer.
203 dcp::base64_decode (string const & in, unsigned char* out, int out_length)
205 BIO* b64 = BIO_new (BIO_f_base64 ());
207 /* This means the input should have no newlines */
208 BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
210 /* Copy our input string, removing newlines */
211 char in_buffer[in.size() + 1];
213 for (size_t i = 0; i < in.size(); ++i) {
214 if (in[i] != '\n' && in[i] != '\r') {
219 BIO* bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
220 bmem = BIO_push (b64, bmem);
221 int const N = BIO_read (bmem, out, out_length);
227 /** @param p Path to open.
228 * @param t mode flags, as for fopen(3).
229 * @return FILE pointer or 0 on error.
231 * Apparently there is no way to create an ofstream using a UTF-8
232 * filename under Windows. We are hence reduced to using fopen
236 dcp::fopen_boost (boost::filesystem::path p, string t)
238 #ifdef LIBDCP_WINDOWS
239 wstring w (t.begin(), t.end());
240 /* c_str() here should give a UTF-16 string */
241 return _wfopen (p.c_str(), w.c_str ());
243 return fopen (p.c_str(), t.c_str ());
247 optional<boost::filesystem::path>
248 dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
250 boost::filesystem::path::const_iterator i = root.begin ();
251 boost::filesystem::path::const_iterator j = file.begin ();
253 while (i != root.end() && j != file.end() && *i == *j) {
258 if (i != root.end ()) {
259 return optional<boost::filesystem::path> ();
262 boost::filesystem::path rel;
263 while (j != file.end ()) {
271 dcp::ids_equal (string a, string b)
273 transform (a.begin(), a.end(), a.begin(), ::tolower);
274 transform (b.begin(), b.end(), b.begin(), ::tolower);
281 dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
283 uintmax_t len = boost::filesystem::file_size (p);
284 if (len > max_length) {
285 throw MiscError (String::compose ("Unexpectedly long file (%1)", p.string()));
288 FILE* f = fopen_boost (p, "r");
290 throw FileError ("could not open file", p, errno);
293 char* c = new char[len];
294 /* This may read less than `len' if we are on Windows and we have CRLF in the file */
295 int const N = fread (c, 1, len, f);
304 /** @param key RSA private key in PEM format (optionally with -----BEGIN... / -----END...)
305 * @return SHA1 fingerprint of key
308 dcp::private_key_fingerprint (string key)
310 boost::replace_all (key, "-----BEGIN RSA PRIVATE KEY-----\n", "");
311 boost::replace_all (key, "\n-----END RSA PRIVATE KEY-----\n", "");
313 unsigned char buffer[4096];
314 int const N = base64_decode (key, buffer, sizeof (buffer));
318 SHA1_Update (&sha, buffer, N);
320 SHA1_Final (digest, &sha);
322 char digest_base64[64];
323 return Kumu::base64encode (digest, 20, digest_base64, 64);
327 dcp::find_child (xmlpp::Node const * node, string name)
329 xmlpp::Node::NodeList c = node->get_children ();
330 xmlpp::Node::NodeList::iterator i = c.begin();
331 while (i != c.end() && (*i)->get_name() != name) {
335 DCP_ASSERT (i != c.end ());
340 dcp::remove_urn_uuid (string raw)
342 DCP_ASSERT (raw.substr(0, 9) == "urn:uuid:");
343 return raw.substr (9);
347 dcp::openjpeg_version ()
349 return opj_version ();