Fix thinko.
[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 <boost/foreach.hpp>
59 #include <stdexcept>
60 #include <iostream>
61 #include <iomanip>
62
63 using std::string;
64 using std::wstring;
65 using std::cout;
66 using std::min;
67 using std::max;
68 using std::list;
69 using std::setw;
70 using std::setfill;
71 using std::ostream;
72 using boost::shared_ptr;
73 using boost::shared_array;
74 using boost::optional;
75 using boost::function;
76 using boost::algorithm::trim;
77 using namespace dcp;
78
79
80 /* Some ASDCP objects store this as a *&, for reasons which are not
81  * at all clear, so we have to keep this around forever.
82  */
83 ASDCP::Dictionary const* dcp::asdcp_smpte_dict = 0;
84
85
86 /** Create a UUID.
87  *  @return UUID.
88  */
89 string
90 dcp::make_uuid ()
91 {
92         char buffer[64];
93         Kumu::UUID id;
94         Kumu::GenRandomValue (id);
95         id.EncodeHex (buffer, 64);
96         return string (buffer);
97 }
98
99 string
100 dcp::make_digest (Data data)
101 {
102         SHA_CTX sha;
103         SHA1_Init (&sha);
104         SHA1_Update (&sha, data.data().get(), data.size());
105         byte_t byte_buffer[SHA_DIGEST_LENGTH];
106         SHA1_Final (byte_buffer, &sha);
107         char digest[64];
108         return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
109 }
110
111 /** Create a digest for a file.
112  *  @param filename File name.
113  *  @param progress Optional progress reporting function.  The function will be called
114  *  with a progress value between 0 and 1.
115  *  @return Digest.
116  */
117 string
118 dcp::make_digest (boost::filesystem::path filename, function<void (float)> progress)
119 {
120         Kumu::FileReader reader;
121         Kumu::Result_t r = reader.OpenRead (filename.string().c_str ());
122         if (ASDCP_FAILURE (r)) {
123                 boost::throw_exception (FileError ("could not open file to compute digest", filename, r));
124         }
125
126         SHA_CTX sha;
127         SHA1_Init (&sha);
128
129         int const buffer_size = 65536;
130         Kumu::ByteString read_buffer (buffer_size);
131
132         Kumu::fsize_t done = 0;
133         Kumu::fsize_t const size = reader.Size ();
134         while (1) {
135                 ui32_t read = 0;
136                 Kumu::Result_t r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
137
138                 if (r == Kumu::RESULT_ENDOFFILE) {
139                         break;
140                 } else if (ASDCP_FAILURE (r)) {
141                         boost::throw_exception (FileError ("could not read file to compute digest", filename, r));
142                 }
143
144                 SHA1_Update (&sha, read_buffer.Data(), read);
145
146                 if (progress) {
147                         progress (float (done) / size);
148                         done += read;
149                 }
150         }
151
152         byte_t byte_buffer[SHA_DIGEST_LENGTH];
153         SHA1_Final (byte_buffer, &sha);
154
155         char digest[64];
156         return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
157 }
158
159 /** @param s A string.
160  *  @return true if the string contains only space, newline or tab characters, or is empty.
161  */
162 bool
163 dcp::empty_or_white_space (string s)
164 {
165         for (size_t i = 0; i < s.length(); ++i) {
166                 if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
167                         return false;
168                 }
169         }
170
171         return true;
172 }
173
174 /** Set up various bits that the library needs.  Should be called once
175  *  by client applications.
176  */
177 void
178 dcp::init ()
179 {
180         if (xmlSecInit() < 0) {
181                 throw MiscError ("could not initialise xmlsec");
182         }
183
184 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
185         if (xmlSecCryptoDLLoadLibrary(BAD_CAST "openssl") < 0) {
186                 throw MiscError ("unable to load openssl xmlsec-crypto library");
187         }
188 #endif
189
190         if (xmlSecCryptoAppInit(0) < 0) {
191                 throw MiscError ("could not initialise crypto");
192         }
193
194         if (xmlSecCryptoInit() < 0) {
195                 throw MiscError ("could not initialise xmlsec-crypto");
196         }
197
198         OpenSSL_add_all_algorithms();
199
200         asdcp_smpte_dict = &ASDCP::DefaultSMPTEDict();
201 }
202
203 /** Decode a base64 string.  The base64 decode routine in KM_util.cpp
204  *  gives different values to both this and the command-line base64
205  *  for some inputs.  Not sure why.
206  *
207  *  @param in base64-encoded string.
208  *  @param out Output buffer.
209  *  @param out_length Length of output buffer.
210  *  @return Number of characters written to the output buffer.
211  */
212 int
213 dcp::base64_decode (string const & in, unsigned char* out, int out_length)
214 {
215         BIO* b64 = BIO_new (BIO_f_base64 ());
216
217         /* This means the input should have no newlines */
218         BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
219
220         /* Copy our input string, removing newlines */
221         char in_buffer[in.size() + 1];
222         char* p = in_buffer;
223         for (size_t i = 0; i < in.size(); ++i) {
224                 if (in[i] != '\n' && in[i] != '\r') {
225                         *p++ = in[i];
226                 }
227         }
228
229         BIO* bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
230         bmem = BIO_push (b64, bmem);
231         int const N = BIO_read (bmem, out, out_length);
232         BIO_free_all (bmem);
233
234         return N;
235 }
236
237 /** @param p Path to open.
238  *  @param t mode flags, as for fopen(3).
239  *  @return FILE pointer or 0 on error.
240  *
241  *  Apparently there is no way to create an ofstream using a UTF-8
242  *  filename under Windows.  We are hence reduced to using fopen
243  *  with this wrapper.
244  */
245 FILE *
246 dcp::fopen_boost (boost::filesystem::path p, string t)
247 {
248 #ifdef LIBDCP_WINDOWS
249         wstring w (t.begin(), t.end());
250         /* c_str() here should give a UTF-16 string */
251         return _wfopen (p.c_str(), w.c_str ());
252 #else
253         return fopen (p.c_str(), t.c_str ());
254 #endif
255 }
256
257 optional<boost::filesystem::path>
258 dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
259 {
260         boost::filesystem::path::const_iterator i = root.begin ();
261         boost::filesystem::path::const_iterator j = file.begin ();
262
263         while (i != root.end() && j != file.end() && *i == *j) {
264                 ++i;
265                 ++j;
266         }
267
268         if (i != root.end ()) {
269                 return optional<boost::filesystem::path> ();
270         }
271
272         boost::filesystem::path rel;
273         while (j != file.end ()) {
274                 rel /= *j++;
275         }
276
277         return rel;
278 }
279
280 bool
281 dcp::ids_equal (string a, string b)
282 {
283         transform (a.begin(), a.end(), a.begin(), ::tolower);
284         transform (b.begin(), b.end(), b.begin(), ::tolower);
285         trim (a);
286         trim (b);
287         return a == b;
288 }
289
290 string
291 dcp::file_to_string (boost::filesystem::path p, uintmax_t max_length)
292 {
293         uintmax_t len = boost::filesystem::file_size (p);
294         if (len > max_length) {
295                 throw MiscError (String::compose ("Unexpectedly long file (%1)", p.string()));
296         }
297
298         FILE* f = fopen_boost (p, "r");
299         if (!f) {
300                 throw FileError ("could not open file", p, errno);
301         }
302
303         char* c = new char[len];
304         /* This may read less than `len' if we are on Windows and we have CRLF in the file */
305         int const N = fread (c, 1, len, f);
306         fclose (f);
307
308         string s (c, N);
309         delete[] c;
310
311         return s;
312 }
313
314 /** @param key RSA private key in PEM format (optionally with -----BEGIN... / -----END...)
315  *  @return SHA1 fingerprint of key
316  */
317 string
318 dcp::private_key_fingerprint (string key)
319 {
320         boost::replace_all (key, "-----BEGIN RSA PRIVATE KEY-----\n", "");
321         boost::replace_all (key, "\n-----END RSA PRIVATE KEY-----\n", "");
322
323         unsigned char buffer[4096];
324         int const N = base64_decode (key, buffer, sizeof (buffer));
325
326         SHA_CTX sha;
327         SHA1_Init (&sha);
328         SHA1_Update (&sha, buffer, N);
329         uint8_t digest[20];
330         SHA1_Final (digest, &sha);
331
332         char digest_base64[64];
333         return Kumu::base64encode (digest, 20, digest_base64, 64);
334 }
335
336 xmlpp::Node *
337 dcp::find_child (xmlpp::Node const * node, string name)
338 {
339         xmlpp::Node::NodeList c = node->get_children ();
340         xmlpp::Node::NodeList::iterator i = c.begin();
341         while (i != c.end() && (*i)->get_name() != name) {
342                 ++i;
343         }
344
345         DCP_ASSERT (i != c.end ());
346         return *i;
347 }
348
349 string
350 dcp::remove_urn_uuid (string raw)
351 {
352         DCP_ASSERT (raw.substr(0, 9) == "urn:uuid:");
353         return raw.substr (9);
354 }
355
356 string
357 dcp::openjpeg_version ()
358 {
359         return opj_version ();
360 }
361
362 string
363 dcp::spaces (int n)
364 {
365         string s = "";
366         for (int i = 0; i < n; ++i) {
367                 s += " ";
368         }
369         return s;
370 }
371
372 void
373 dcp::indent (xmlpp::Element* element, int initial)
374 {
375         xmlpp::Node* last = 0;
376         BOOST_FOREACH (xmlpp::Node * n, element->get_children()) {
377                 xmlpp::Element* e = dynamic_cast<xmlpp::Element*>(n);
378                 if (e) {
379                         element->add_child_text_before (e, "\n" + spaces(initial + 2));
380                         indent (e, initial + 2);
381                         last = n;
382                 }
383         }
384         if (last) {
385                 element->add_child_text (last, "\n" + spaces(initial));
386         }
387 }
388
389 /** @return true if the day represented by \ref a is less than or
390  *  equal to the one represented by \ref b, ignoring the time parts.
391  */
392 bool
393 dcp::day_less_than_or_equal (LocalTime a, LocalTime b)
394 {
395         if (a.year() != b.year()) {
396                 return a.year() < b.year();
397         }
398
399         if (a.month() != b.month()) {
400                 return a.month() < b.month();
401         }
402
403         return a.day() <= b.day();
404 }
405
406 /** @return true if the day represented by \ref a is greater than or
407  *  equal to the one represented by \ref b, ignoring the time parts.
408  */
409 bool
410 dcp::day_greater_than_or_equal (LocalTime a, LocalTime b)
411 {
412         if (a.year() != b.year()) {
413                 return a.year() > b.year();
414         }
415
416         if (a.month() != b.month()) {
417                 return a.month() > b.month();
418         }
419
420         return a.day() >= b.day();
421 }
422
423 /** Try quite hard to find a string which starts with \ref base and is
424  *  not in \ref existing.
425  */
426 string
427 dcp::unique_string (list<string> existing, string base)
428 {
429         int const max_tries = existing.size() + 1;
430         for (int i = 0; i < max_tries; ++i) {
431                 string trial = String::compose("%1%2", base, i);
432                 if (find(existing.begin(), existing.end(), trial) == existing.end()) {
433                         return trial;
434                 }
435         }
436
437         DCP_ASSERT (false);
438 }
439
440
441 ASDCPErrorSuspender::ASDCPErrorSuspender ()
442         : _old (Kumu::DefaultLogSink())
443 {
444         _sink = new Kumu::EntryListLogSink(_log);
445         Kumu::SetDefaultLogSink (_sink);
446 }
447
448
449 ASDCPErrorSuspender::~ASDCPErrorSuspender ()
450 {
451         Kumu::SetDefaultLogSink (&_old);
452         delete _sink;
453 }
454
455