Fix missing version string when Popen communicate returns byte strings.
[libdcp.git] / src / util.cc
1 /*
2     Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
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.
10
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.
15
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/>.
18
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
23     including the two.
24
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.
32 */
33
34 /** @file  src/util.cc
35  *  @brief Utility methods.
36  */
37
38 #include "util.h"
39 #include "exceptions.h"
40 #include "types.h"
41 #include "certificate.h"
42 #include "openjpeg_image.h"
43 #include "dcp_assert.h"
44 #include "compose.hpp"
45 #include <openjpeg.h>
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>
58 #include <stdexcept>
59 #include <iostream>
60 #include <iomanip>
61
62 using std::string;
63 using std::wstring;
64 using std::cout;
65 using std::min;
66 using std::max;
67 using std::list;
68 using std::setw;
69 using std::setfill;
70 using std::ostream;
71 using boost::shared_ptr;
72 using boost::shared_array;
73 using boost::optional;
74 using boost::function;
75 using boost::algorithm::trim;
76 using namespace dcp;
77
78 /** Create a UUID.
79  *  @return UUID.
80  */
81 string
82 dcp::make_uuid ()
83 {
84         char buffer[64];
85         Kumu::UUID id;
86         Kumu::GenRandomValue (id);
87         id.EncodeHex (buffer, 64);
88         return string (buffer);
89 }
90
91 string
92 dcp::make_digest (Data data)
93 {
94         SHA_CTX sha;
95         SHA1_Init (&sha);
96         SHA1_Update (&sha, data.data().get(), data.size());
97         byte_t byte_buffer[SHA_DIGEST_LENGTH];
98         SHA1_Final (byte_buffer, &sha);
99         char digest[64];
100         return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
101 }
102
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.
107  *  @return Digest.
108  */
109 string
110 dcp::make_digest (boost::filesystem::path filename, function<void (float)> progress)
111 {
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));
116         }
117
118         SHA_CTX sha;
119         SHA1_Init (&sha);
120
121         int const buffer_size = 65536;
122         Kumu::ByteString read_buffer (buffer_size);
123
124         Kumu::fsize_t done = 0;
125         Kumu::fsize_t const size = reader.Size ();
126         while (1) {
127                 ui32_t read = 0;
128                 Kumu::Result_t r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
129
130                 if (r == Kumu::RESULT_ENDOFFILE) {
131                         break;
132                 } else if (ASDCP_FAILURE (r)) {
133                         boost::throw_exception (FileError ("could not read file to compute digest", filename, r));
134                 }
135
136                 SHA1_Update (&sha, read_buffer.Data(), read);
137
138                 if (progress) {
139                         progress (float (done) / size);
140                         done += read;
141                 }
142         }
143
144         byte_t byte_buffer[SHA_DIGEST_LENGTH];
145         SHA1_Final (byte_buffer, &sha);
146
147         char digest[64];
148         return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
149 }
150
151 /** Convert a content kind to a string which can be used in a
152  *  &lt;ContentKind&gt; node.
153  *  @param kind ContentKind.
154  *  @return string.
155  */
156 string
157 dcp::content_kind_to_string (ContentKind kind)
158 {
159         switch (kind) {
160         case FEATURE:
161                 return "feature";
162         case SHORT:
163                 return "short";
164         case TRAILER:
165                 return "trailer";
166         case TEST:
167                 return "test";
168         case TRANSITIONAL:
169                 return "transitional";
170         case RATING:
171                 return "rating";
172         case TEASER:
173                 return "teaser";
174         case POLICY:
175                 return "policy";
176         case PUBLIC_SERVICE_ANNOUNCEMENT:
177                 return "psa";
178         case ADVERTISEMENT:
179                 return "advertisement";
180         }
181
182         DCP_ASSERT (false);
183 }
184
185 /** Convert a string from a &lt;ContentKind&gt; node to a libdcp ContentKind.
186  *  Reasonably tolerant about varying case.
187  *  @param kind Content kind string.
188  *  @return libdcp ContentKind.
189  */
190 dcp::ContentKind
191 dcp::content_kind_from_string (string kind)
192 {
193         transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
194
195         if (kind == "feature") {
196                 return FEATURE;
197         } else if (kind == "short") {
198                 return SHORT;
199         } else if (kind == "trailer") {
200                 return TRAILER;
201         } else if (kind == "test") {
202                 return TEST;
203         } else if (kind == "transitional") {
204                 return TRANSITIONAL;
205         } else if (kind == "rating") {
206                 return RATING;
207         } else if (kind == "teaser") {
208                 return TEASER;
209         } else if (kind == "policy") {
210                 return POLICY;
211         } else if (kind == "psa") {
212                 return PUBLIC_SERVICE_ANNOUNCEMENT;
213         } else if (kind == "advertisement") {
214                 return ADVERTISEMENT;
215         }
216
217         DCP_ASSERT (false);
218 }
219
220 /** @param s A string.
221  *  @return true if the string contains only space, newline or tab characters, or is empty.
222  */
223 bool
224 dcp::empty_or_white_space (string s)
225 {
226         for (size_t i = 0; i < s.length(); ++i) {
227                 if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
228                         return false;
229                 }
230         }
231
232         return true;
233 }
234
235 /** Set up various bits that the library needs.  Should be called one
236  *  by client applications.
237  */
238 void
239 dcp::init ()
240 {
241         if (xmlSecInit() < 0) {
242                 throw MiscError ("could not initialise xmlsec");
243         }
244
245 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
246         if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
247                 throw MiscError ("unable to load default xmlsec-crypto library");
248         }
249 #endif
250
251         if (xmlSecCryptoAppInit(0) < 0) {
252                 throw MiscError ("could not initialise crypto");
253         }
254
255         if (xmlSecCryptoInit() < 0) {
256                 throw MiscError ("could not initialise xmlsec-crypto");
257         }
258
259         OpenSSL_add_all_algorithms();
260 }
261
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.
265  *
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.
270  */
271 int
272 dcp::base64_decode (string const & in, unsigned char* out, int out_length)
273 {
274         BIO* b64 = BIO_new (BIO_f_base64 ());
275
276         /* This means the input should have no newlines */
277         BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
278
279         /* Copy our input string, removing newlines */
280         char in_buffer[in.size() + 1];
281         char* p = in_buffer;
282         for (size_t i = 0; i < in.size(); ++i) {
283                 if (in[i] != '\n' && in[i] != '\r') {
284                         *p++ = in[i];
285                 }
286         }
287
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);
291         BIO_free_all (bmem);
292
293         return N;
294 }
295
296 /** @param p Path to open.
297  *  @param t mode flags, as for fopen(3).
298  *  @return FILE pointer or 0 on error.
299  *
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
302  *  with this wrapper.
303  */
304 FILE *
305 dcp::fopen_boost (boost::filesystem::path p, string t)
306 {
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 ());
311 #else
312         return fopen (p.c_str(), t.c_str ());
313 #endif
314 }
315
316 optional<boost::filesystem::path>
317 dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
318 {
319         boost::filesystem::path::const_iterator i = root.begin ();
320         boost::filesystem::path::const_iterator j = file.begin ();
321
322         while (i != root.end() && j != file.end() && *i == *j) {
323                 ++i;
324                 ++j;
325         }
326
327         if (i != root.end ()) {
328                 return optional<boost::filesystem::path> ();
329         }
330
331         boost::filesystem::path rel;
332         while (j != file.end ()) {
333                 rel /= *j++;
334         }
335
336         return rel;
337 }
338
339 bool
340 dcp::ids_equal (string a, string b)
341 {
342         transform (a.begin(), a.end(), a.begin(), ::tolower);
343         transform (b.begin(), b.end(), b.begin(), ::tolower);
344         trim (a);
345         trim (b);
346         return a == b;
347 }
348
349 string
350 dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
351 {
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()));
355         }
356
357         FILE* f = fopen_boost (p, "r");
358         if (!f) {
359                 throw FileError ("could not open file", p, errno);
360         }
361
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);
365         fclose (f);
366
367         string s (c, N);
368         delete[] c;
369
370         return s;
371 }
372
373 /** @param key RSA private key in PEM format (optionally with -----BEGIN... / -----END...)
374  *  @return SHA1 fingerprint of key
375  */
376 string
377 dcp::private_key_fingerprint (string key)
378 {
379         boost::replace_all (key, "-----BEGIN RSA PRIVATE KEY-----\n", "");
380         boost::replace_all (key, "\n-----END RSA PRIVATE KEY-----\n", "");
381
382         unsigned char buffer[4096];
383         int const N = base64_decode (key, buffer, sizeof (buffer));
384
385         SHA_CTX sha;
386         SHA1_Init (&sha);
387         SHA1_Update (&sha, buffer, N);
388         uint8_t digest[20];
389         SHA1_Final (digest, &sha);
390
391         char digest_base64[64];
392         return Kumu::base64encode (digest, 20, digest_base64, 64);
393 }
394
395 xmlpp::Node *
396 dcp::find_child (xmlpp::Node const * node, string name)
397 {
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) {
401                 ++i;
402         }
403
404         DCP_ASSERT (i != c.end ());
405         return *i;
406 }
407
408 string
409 dcp::remove_urn_uuid (string raw)
410 {
411         DCP_ASSERT (raw.substr(0, 9) == "urn:uuid:");
412         return raw.substr (9);
413 }
414
415 string
416 dcp::openjpeg_version ()
417 {
418         return opj_version ();
419 }