Coverage Report

Created: 2026-09-01 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/image.cpp
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
// included header files
4
#include "image.hpp"
5
6
#include "basicio.hpp"
7
#include "config.h"
8
#include "enforce.hpp"
9
#include "error.hpp"
10
#include "futils.hpp"
11
#include "image_int.hpp"
12
#include "safe_op.hpp"
13
#include "slice.hpp"
14
15
#ifdef EXV_ENABLE_BMFF
16
#include "bmffimage.hpp"
17
#endif  // EXV_ENABLE_BMFF
18
19
#include "cr2image.hpp"
20
#include "crwimage.hpp"
21
#include "epsimage.hpp"
22
#include "jpgimage.hpp"
23
#include "mrwimage.hpp"
24
#ifdef EXV_HAVE_LIBZ
25
#include "pngimage.hpp"
26
#endif  // EXV_HAVE_LIBZ
27
#include "bmpimage.hpp"
28
#include "gifimage.hpp"
29
#include "jp2image.hpp"
30
#include "nikonmn_int.hpp"
31
#include "orfimage.hpp"
32
#include "pgfimage.hpp"
33
#include "psdimage.hpp"
34
#include "rafimage.hpp"
35
#include "rw2image.hpp"
36
#include "tags.hpp"
37
#include "tags_int.hpp"
38
#include "tgaimage.hpp"
39
#include "tiffimage.hpp"
40
#include "webpimage.hpp"
41
#include "xmpsidecar.hpp"
42
#ifdef EXV_ENABLE_VIDEO
43
#include "asfvideo.hpp"
44
#include "matroskavideo.hpp"
45
#include "quicktimevideo.hpp"
46
#include "riffvideo.hpp"
47
#endif  // EXV_ENABLE_VIDEO
48
49
// + standard includes
50
#include <bit>
51
#include <cstdio>
52
#include <cstring>
53
#include <set>
54
55
#ifdef _WIN32
56
#include <windows.h>
57
#endif
58
59
// *****************************************************************************
60
namespace {
61
using namespace Exiv2;
62
63
//! Struct for storing image types and function pointers.
64
struct Registry {
65
  //! Comparison operator to compare a Registry structure with an image type
66
1.92M
  bool operator==(const ImageType& imageType) const {
67
1.92M
    return imageType == imageType_;
68
1.92M
  }
69
70
  // DATA
71
  ImageType imageType_;
72
  NewInstanceFct newInstance_;
73
  IsThisTypeFct isThisType_;
74
  AccessMode exifSupport_;
75
  AccessMode iptcSupport_;
76
  AccessMode xmpSupport_;
77
  AccessMode commentSupport_;
78
};
79
80
/// \todo Use std::unordered_map for implementing the registry. Avoid to use ImageType::none
81
constexpr Registry registry[] = {
82
    // image type       creation fct     type check  Exif mode    IPTC mode    XMP mode     Comment mode
83
    //---------------  ---------------  ----------  -----------  -----------  -----------  ------------
84
    {ImageType::jpeg, newJpegInstance, isJpegType, amReadWrite, amReadWrite, amReadWrite, amReadWrite},
85
    {ImageType::exv, newExvInstance, isExvType, amReadWrite, amReadWrite, amReadWrite, amReadWrite},
86
    {ImageType::cr2, newCr2Instance, isCr2Type, amReadWrite, amReadWrite, amReadWrite, amNone},
87
    {ImageType::crw, newCrwInstance, isCrwType, amReadWrite, amNone, amNone, amReadWrite},
88
    {ImageType::mrw, newMrwInstance, isMrwType, amRead, amRead, amRead, amNone},
89
    {ImageType::tiff, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
90
    {ImageType::webp, newWebPInstance, isWebPType, amReadWrite, amNone, amReadWrite, amNone},
91
    {ImageType::dng, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
92
    {ImageType::nef, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
93
    {ImageType::pef, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
94
    {ImageType::arw, newTiffInstance, isTiffType, amRead, amRead, amRead, amNone},
95
    {ImageType::rw2, newRw2Instance, isRw2Type, amRead, amRead, amRead, amNone},
96
    {ImageType::sr2, newTiffInstance, isTiffType, amRead, amRead, amRead, amNone},
97
    {ImageType::srw, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
98
    {ImageType::orf, newOrfInstance, isOrfType, amReadWrite, amReadWrite, amReadWrite, amNone},
99
#ifdef EXV_HAVE_LIBZ
100
    {ImageType::png, newPngInstance, isPngType, amReadWrite, amReadWrite, amReadWrite, amReadWrite},
101
#endif  // EXV_HAVE_LIBZ
102
    {ImageType::pgf, newPgfInstance, isPgfType, amReadWrite, amReadWrite, amReadWrite, amReadWrite},
103
    {ImageType::raf, newRafInstance, isRafType, amRead, amRead, amRead, amNone},
104
    {ImageType::eps, newEpsInstance, isEpsType, amNone, amNone, amReadWrite, amNone},
105
    {ImageType::xmp, newXmpInstance, isXmpType, amReadWrite, amReadWrite, amReadWrite, amNone},
106
    {ImageType::gif, newGifInstance, isGifType, amNone, amNone, amNone, amNone},
107
    {ImageType::psd, newPsdInstance, isPsdType, amReadWrite, amReadWrite, amReadWrite, amNone},
108
    {ImageType::tga, newTgaInstance, isTgaType, amNone, amNone, amNone, amNone},
109
    {ImageType::bmp, newBmpInstance, isBmpType, amNone, amNone, amNone, amNone},
110
    {ImageType::jp2, newJp2Instance, isJp2Type, amReadWrite, amReadWrite, amReadWrite, amNone},
111
// needs to be before bmff because some ftyp files are handled as qt and
112
// the rest should fall through to bmff
113
#ifdef EXV_ENABLE_VIDEO
114
    {ImageType::qtime, newQTimeInstance, isQTimeType, amRead, amNone, amRead, amNone},
115
    {ImageType::asf, newAsfInstance, isAsfType, amRead, amNone, amRead, amNone},
116
    {ImageType::riff, newRiffInstance, isRiffType, amRead, amNone, amRead, amNone},
117
    {ImageType::mkv, newMkvInstance, isMkvType, amRead, amNone, amRead, amNone},
118
#endif  // EXV_ENABLE_VIDEO
119
#ifdef EXV_ENABLE_BMFF
120
    {ImageType::bmff, newBmffInstance, isBmffType, amRead, amRead, amRead, amNone},
121
#endif  // EXV_ENABLE_BMFF
122
};
123
124
#ifdef EXV_ENABLE_FILESYSTEM
125
0
std::string pathOfFileUrl(const std::string& url) {
126
0
  std::string path = url.substr(7);
127
0
  size_t found = path.find('/');
128
0
  if (found == std::string::npos)
129
0
    return path;
130
0
  return path.substr(found);
131
0
}
132
#endif
133
134
}  // namespace
135
136
// *****************************************************************************
137
// class member definitions
138
namespace Exiv2 {
139
140
ImageCtorParams::ImageCtorParams(bool create, size_t max_recursion_depth) :
141
118k
    create_(create), max_recursion_depth_(max_recursion_depth) {
142
118k
}
143
144
Image::Image(ImageType type, uint16_t supportedMetadata, BasicIo::UniquePtr io, const ImageCtorParams& params) :
145
118k
    io_(std::move(io)),
146
118k
    max_recursion_depth_(params.max_recursion_depth()),
147
118k
    imageType_(type),
148
118k
    supportedMetadata_(supportedMetadata) {
149
118k
}
150
151
118k
Image::~Image() = default;
152
153
2.46k
void Image::printStructure(std::ostream&, PrintStructureOption, size_t /*depth*/) {
154
2.46k
  throw Error(ErrorCode::kerUnsupportedImageType, io_->path());
155
2.46k
}
156
157
31.3k
bool Image::isStringType(uint16_t type) {
158
31.3k
  return type == Exiv2::asciiString || type == Exiv2::unsignedByte || type == Exiv2::signedByte ||
159
28.5k
         type == Exiv2::undefined;
160
31.3k
}
161
17.5k
bool Image::isShortType(uint16_t type) {
162
17.5k
  return type == Exiv2::unsignedShort || type == Exiv2::signedShort;
163
17.5k
}
164
13.2k
bool Image::isLongType(uint16_t type) {
165
13.2k
  return type == Exiv2::unsignedLong || type == Exiv2::signedLong;
166
13.2k
}
167
20
bool Image::isLongLongType(uint16_t type) {
168
20
  return type == Exiv2::unsignedLongLong || type == Exiv2::signedLongLong;
169
20
}
170
5.56k
bool Image::isRationalType(uint16_t type) {
171
5.56k
  return type == Exiv2::unsignedRational || type == Exiv2::signedRational;
172
5.56k
}
173
8.03k
bool Image::is2ByteType(uint16_t type) {
174
8.03k
  return isShortType(type);
175
8.03k
}
176
5.90k
bool Image::is4ByteType(uint16_t type) {
177
5.90k
  return isLongType(type) || type == Exiv2::tiffFloat || type == Exiv2::tiffIfd;
178
5.90k
}
179
1.48k
bool Image::is8ByteType(uint16_t type) {
180
1.48k
  return isRationalType(type) || isLongLongType(type) || type == Exiv2::tiffIfd8 || type == Exiv2::tiffDouble;
181
1.48k
}
182
19.0k
bool Image::isPrintXMP(uint16_t type, Exiv2::PrintStructureOption option) {
183
19.0k
  return type == 700 && option == kpsXMP;
184
19.0k
}
185
19.0k
bool Image::isPrintICC(uint16_t type, Exiv2::PrintStructureOption option) {
186
19.0k
  return type == 0x8773 && option == kpsIccProfile;
187
19.0k
}
188
189
6.59k
bool Image::isBigEndianPlatform() {
190
6.59k
  return std::endian::native == std::endian::big;
191
6.59k
}
192
15.9k
bool Image::isLittleEndianPlatform() {
193
15.9k
  return std::endian::native == std::endian::little;
194
15.9k
}
195
196
0
uint64_t Image::byteSwap(uint64_t value, bool bSwap) {
197
#ifdef __cpp_lib_byteswap
198
  return bSwap ? std::byteswap(value) : value;
199
#elif defined(_MSC_VER)
200
  return bSwap ? _byteswap_uint64(value) : value;
201
#else
202
0
  uint64_t result = 0;
203
0
  auto source_value = reinterpret_cast<const byte*>(&value);
204
0
  auto destination_value = reinterpret_cast<byte*>(&result);
205
206
0
  for (int i = 0; i < 8; i++)
207
0
    destination_value[i] = source_value[8 - i - 1];
208
209
0
  return bSwap ? result : value;
210
0
#endif
211
0
}
212
213
61.5k
uint32_t Image::byteSwap(uint32_t value, bool bSwap) {
214
#ifdef __cpp_lib_byteswap
215
  return bSwap ? std::byteswap(value) : value;
216
#elif defined(_MSC_VER)
217
  return bSwap ? _byteswap_ulong(value) : value;
218
#else
219
61.5k
  uint32_t result = 0;
220
61.5k
  result |= (value & 0x000000FFU) << 24;
221
61.5k
  result |= (value & 0x0000FF00U) << 8;
222
61.5k
  result |= (value & 0x00FF0000U) >> 8;
223
61.5k
  result |= (value & 0xFF000000U) >> 24;
224
61.5k
  return bSwap ? result : value;
225
61.5k
#endif
226
61.5k
}
227
228
44.5k
uint16_t Image::byteSwap(uint16_t value, bool bSwap) {
229
#ifdef __cpp_lib_byteswap
230
  return bSwap ? std::byteswap(value) : value;
231
#elif defined(_MSC_VER)
232
  return bSwap ? _byteswap_ushort(value) : value;
233
#else
234
44.5k
  uint16_t result = 0;
235
44.5k
  result |= (value & 0x00FFU) << 8;
236
44.5k
  result |= (value & 0xFF00U) >> 8;
237
44.5k
  return bSwap ? result : value;
238
44.5k
#endif
239
44.5k
}
240
241
44.5k
uint16_t Image::byteSwap2(const DataBuf& buf, size_t offset, bool bSwap) {
242
44.5k
  uint16_t v = 0;
243
44.5k
  auto p = reinterpret_cast<char*>(&v);
244
44.5k
  p[0] = buf.read_uint8(offset);
245
44.5k
  p[1] = buf.read_uint8(offset + 1);
246
44.5k
  return Image::byteSwap(v, bSwap);
247
44.5k
}
248
249
55.1k
uint32_t Image::byteSwap4(const DataBuf& buf, size_t offset, bool bSwap) {
250
55.1k
  uint32_t v = 0;
251
55.1k
  auto p = reinterpret_cast<char*>(&v);
252
55.1k
  p[0] = buf.read_uint8(offset);
253
55.1k
  p[1] = buf.read_uint8(offset + 1);
254
55.1k
  p[2] = buf.read_uint8(offset + 2);
255
55.1k
  p[3] = buf.read_uint8(offset + 3);
256
55.1k
  return Image::byteSwap(v, bSwap);
257
55.1k
}
258
259
/// \todo not used internally. At least we should test it
260
0
uint64_t Image::byteSwap8(const DataBuf& buf, size_t offset, bool bSwap) {
261
0
  uint64_t v = 0;
262
0
  auto p = reinterpret_cast<byte*>(&v);
263
264
0
  for (int i = 0; i < 8; i++)
265
0
    p[i] = buf.read_uint8(offset + i);
266
267
0
  return Image::byteSwap(v, bSwap);
268
0
}
269
270
9.50k
const char* Image::typeName(uint16_t tag) {
271
  //! List of TIFF image tags
272
9.50k
  const char* result = nullptr;
273
9.50k
  switch (tag) {
274
129
    case Exiv2::unsignedByte:
275
129
      result = "BYTE";
276
129
      break;
277
190
    case Exiv2::asciiString:
278
190
      result = "ASCII";
279
190
      break;
280
890
    case Exiv2::unsignedShort:
281
890
      result = "SHORT";
282
890
      break;
283
2.57k
    case Exiv2::unsignedLong:
284
2.57k
      result = "LONG";
285
2.57k
      break;
286
557
    case Exiv2::unsignedRational:
287
557
      result = "RATIONAL";
288
557
      break;
289
352
    case Exiv2::signedByte:
290
352
      result = "SBYTE";
291
352
      break;
292
842
    case Exiv2::undefined:
293
842
      result = "UNDEFINED";
294
842
      break;
295
1.22k
    case Exiv2::signedShort:
296
1.22k
      result = "SSHORT";
297
1.22k
      break;
298
731
    case Exiv2::signedLong:
299
731
      result = "SLONG";
300
731
      break;
301
901
    case Exiv2::signedRational:
302
901
      result = "SRATIONAL";
303
901
      break;
304
305
    case Exiv2::tiffFloat:
305
305
      result = "FLOAT";
306
305
      break;
307
15
    case Exiv2::tiffDouble:
308
15
      result = "DOUBLE";
309
15
      break;
310
789
    case Exiv2::tiffIfd:
311
789
      result = "IFD";
312
789
      break;
313
0
    default:
314
0
      result = "unknown";
315
0
      break;
316
9.50k
  }
317
9.50k
  return result;
318
9.50k
}
319
320
9.80k
static bool typeValid(uint16_t type) {
321
9.80k
  return type >= 1 && type <= 13;
322
9.80k
}
323
324
static std::set<size_t> visits;  // #547
325
326
void Image::printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option, size_t start,
327
20.9k
                              bool bSwap, char c, size_t depth) {
328
20.9k
  if (depth == 1)
329
4.83k
    visits.clear();
330
20.9k
  bool bFirst = true;
331
332
  // buffer
333
20.9k
  const size_t dirSize = 32;
334
20.9k
  DataBuf dir(dirSize);
335
20.9k
  const bool bPrint = option == kpsBasic || option == kpsRecursive;
336
337
21.0k
  do {
338
    // Read top of directory
339
21.0k
    io.seekOrThrow(start, BasicIo::beg, ErrorCode::kerCorruptedMetadata);
340
21.0k
    io.readOrThrow(dir.data(), 2, ErrorCode::kerCorruptedMetadata);
341
21.0k
    const uint16_t dirLength = byteSwap2(dir, 0, bSwap);
342
    // Prevent infinite loops. (GHSA-m479-7frc-gqqg)
343
21.0k
    Internal::enforce(dirLength > 0, ErrorCode::kerCorruptedMetadata);
344
345
21.0k
    if (dirLength > 500)  // tooBig
346
15
      throw Error(ErrorCode::kerTiffDirectoryTooLarge);
347
348
20.9k
    if (bFirst && bPrint) {
349
20.8k
      out << Internal::indent(depth) << stringFormat("STRUCTURE OF TIFF FILE ({}{}): {}\n", c, c, io.path());
350
20.8k
    }
351
352
    // Read the dictionary
353
30.5k
    for (int i = 0; i < dirLength; i++) {
354
25.5k
      if (visits.contains(io.tell())) {  // #547
355
15.7k
        throw Error(ErrorCode::kerCorruptedMetadata);
356
15.7k
      }
357
9.83k
      visits.insert(io.tell());
358
359
9.83k
      if (bFirst && bPrint) {
360
5.12k
        out << Internal::indent(depth) << " address |    tag                              |     "
361
5.12k
            << " type |    count |    offset | value\n";
362
5.12k
      }
363
9.83k
      bFirst = false;
364
365
9.83k
      io.readOrThrow(dir.data(), 12, ErrorCode::kerCorruptedMetadata);
366
9.83k
      const uint16_t tag = byteSwap2(dir, 0, bSwap);
367
9.83k
      const uint16_t type = byteSwap2(dir, 2, bSwap);
368
9.83k
      const uint32_t count = byteSwap4(dir, 4, bSwap);
369
9.83k
      const uint32_t offset = byteSwap4(dir, 8, bSwap);
370
371
      // Break for unknown tag types else we may segfault.
372
9.83k
      if (!typeValid(type)) {
373
219
        EXV_ERROR << "invalid type in tiff structure" << type << '\n';
374
219
        throw Error(ErrorCode::kerInvalidTypeValue);
375
219
      }
376
377
      // prepare to print the value
378
9.61k
      const uint32_t kount = [=] {
379
        // haul in all the data
380
9.58k
        if (isPrintXMP(tag, option))
381
0
          return count;
382
        // ditto
383
9.58k
        if (isPrintICC(tag, option))
384
0
          return count;
385
        // restrict long arrays
386
9.58k
        if (isStringType(type)) {
387
1.55k
          return std::min(count, 32u);
388
1.55k
        }
389
8.03k
        return std::min(count, 5u);
390
9.58k
      }();
391
9.61k
      const uint32_t pad = isStringType(type) ? 1 : 0;
392
9.61k
      const uint32_t size = [=] {
393
9.58k
        if (isStringType(type))
394
1.55k
          return 1;
395
8.03k
        if (is2ByteType(type))
396
2.12k
          return 2;
397
5.90k
        if (is4ByteType(type))
398
4.41k
          return 4;
399
1.48k
        if (is8ByteType(type))
400
1.48k
          return 8;
401
0
        return 1;
402
1.48k
      }();
403
404
      // if ( offset > io.size() ) offset = 0; // Denial of service?
405
406
      // #55 and #56 memory allocation crash test/data/POC8
407
9.61k
      const uint64_t allocate64 = (size * static_cast<uint64_t>(count)) + pad + 20;
408
9.61k
      if (allocate64 > io.size()) {
409
73
        throw Error(ErrorCode::kerInvalidMalloc);
410
73
      }
411
9.54k
      DataBuf buf(allocate64);                       // allocate a buffer
412
9.54k
      std::copy_n(dir.begin() + 8, 4, buf.begin());  // copy dir[8:11] into buffer (short strings)
413
414
      // We have already checked that this multiplication cannot overflow.
415
9.54k
      const size_t count_x_size = static_cast<size_t>(count) * size;
416
9.54k
      const bool bOffsetIsPointer = count_x_size > 4;
417
418
9.54k
      if (bOffsetIsPointer) {                                                       // read into buffer
419
4.79k
        const size_t restore = io.tell();                                           // save
420
4.79k
        io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata);      // position
421
4.79k
        io.readOrThrow(buf.data(), count_x_size, ErrorCode::kerCorruptedMetadata);  // read
422
4.79k
        io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata);     // restore
423
4.79k
      }
424
425
9.54k
      if (bPrint) {
426
9.50k
        const size_t address = start + 2 + (i * 12);
427
9.50k
        const std::string offsetString = bOffsetIsPointer ? stringFormat("{:9}", offset) : "";
428
9.50k
        std::string sp;  // output spacer
429
430
9.50k
        out << Internal::indent(depth)
431
9.50k
            << stringFormat("{:8} | {:#06x} {:<28} | {:>9} | {:>8} | {:9} | ", address, tag, tagName(tag),
432
9.50k
                            typeName(type), count, offsetString);
433
9.50k
        if (isShortType(type)) {
434
6.03k
          for (size_t k = 0; k < kount; k++) {
435
3.92k
            out << sp << byteSwap2(buf, k * size, bSwap);
436
3.92k
            sp = " ";
437
3.92k
          }
438
7.38k
        } else if (isLongType(type)) {
439
9.97k
          for (size_t k = 0; k < kount; k++) {
440
6.66k
            out << sp << byteSwap4(buf, k * size, bSwap);
441
6.66k
            sp = " ";
442
6.66k
          }
443
444
4.08k
        } else if (isRationalType(type)) {
445
3.01k
          for (size_t k = 0; k < kount; k++) {
446
1.56k
            const uint32_t a = byteSwap4(buf, (k * size) + 0, bSwap);
447
1.56k
            const uint32_t b = byteSwap4(buf, (k * size) + 4, bSwap);
448
1.56k
            out << sp << a << "/" << b;
449
1.56k
            sp = " ";
450
1.56k
          }
451
2.62k
        } else if (isStringType(type)) {
452
1.51k
          out << sp << Internal::binaryToString(makeSlice(buf, 0, kount));
453
1.51k
        }
454
455
9.50k
        sp = kount == count ? "" : " ...";
456
9.50k
        out << sp << '\n';
457
458
9.50k
        if (option == kpsRecursive && (tag == 0x8769 /* ExifTag */ || tag == 0x014a /*SubIFDs*/ || type == tiffIfd)) {
459
1.26k
          for (size_t k = 0; k < count; k++) {
460
16
            const size_t restore = io.tell();
461
16
            const uint32_t offset_inner = byteSwap4(buf, k * size, bSwap);
462
16
            printIFDStructure(io, out, option, offset_inner, bSwap, c, depth + 1);
463
16
            io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata);
464
16
          }
465
8.25k
        } else if (option == kpsRecursive && tag == 0x83bb /* IPTCNAA */) {
466
3.21k
          if (count > 0) {
467
1.97k
            if (static_cast<size_t>(Safe::add(count, offset)) > io.size()) {
468
4
              throw Error(ErrorCode::kerCorruptedMetadata);
469
4
            }
470
471
1.97k
            const size_t restore = io.tell();
472
1.97k
            io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata);  // position
473
1.97k
            auto bytes = std::make_unique<byte[]>(count);                           // allocate memory
474
1.97k
            io.readOrThrow(bytes.get(), count, ErrorCode::kerCorruptedMetadata);
475
1.97k
            io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata);
476
1.97k
            IptcData::printStructure(out, makeSliceUntil(bytes.get(), count), depth);
477
1.97k
          }
478
5.03k
        } else if (option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10) {
479
1
          const size_t restore = io.tell();  // save
480
481
1
          const uint32_t jump = 10;
482
1
          byte bytes[20];
483
1
          const auto chars = reinterpret_cast<const char*>(&bytes[0]);
484
1
          io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata);  // position
485
1
          io.readOrThrow(bytes, jump, ErrorCode::kerCorruptedMetadata);           // read
486
1
          bytes[jump] = 0;
487
488
1
          bool bNikon = ::strcmp("Nikon", chars) == 0;
489
1
          bool bSony = ::strcmp("SONY DSC ", chars) == 0;
490
491
1
          if (bNikon) {
492
            // tag is an embedded tiff
493
0
            const uint32_t byteslen = count - jump;
494
0
            auto b = DataBuf(byteslen);                                           // allocate a buffer
495
0
            io.readOrThrow(b.data(), byteslen, ErrorCode::kerCorruptedMetadata);  // read
496
0
            MemIo memIo(b.c_data(), byteslen);                                    // create a file
497
0
            printTiffStructure(memIo, out, option, depth + 1);
498
1
          } else {
499
            // tag is an IFD
500
1
            const uint32_t punt = bSony ? 12 : 0;
501
1
            io.seekOrThrow(0, BasicIo::beg, ErrorCode::kerCorruptedMetadata);  // position
502
1
            printIFDStructure(io, out, option, offset + punt, bSwap, c, depth + 1);
503
1
          }
504
505
1
          io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata);  // restore
506
1
        }
507
9.50k
      }
508
509
9.53k
      if (isPrintXMP(tag, option)) {
510
0
        buf.write_uint8(count, 0);
511
0
        out << buf.c_str();
512
0
      }
513
9.53k
      if (isPrintICC(tag, option)) {
514
0
        out.write(buf.c_str(), count);
515
0
      }
516
9.53k
    }
517
4.99k
    if (start) {
518
4.78k
      io.readOrThrow(dir.data(), 4, ErrorCode::kerCorruptedMetadata);
519
4.78k
      start = byteSwap4(dir, 0, bSwap);
520
4.78k
    }
521
4.99k
  } while (start);
522
523
4.97k
  if (bPrint) {
524
4.76k
    out << Internal::indent(depth) << "END " << io.path() << '\n';
525
4.76k
  }
526
4.97k
  out.flush();
527
4.97k
}
528
529
void Image::printTiffStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option, size_t depth,
530
37.1k
                               size_t offset /*=0*/) {
531
37.1k
  if (option == kpsBasic || option == kpsXMP || option == kpsRecursive || option == kpsIccProfile) {
532
    // buffer
533
20.9k
    const size_t dirSize = 32;
534
20.9k
    DataBuf dir(dirSize);
535
536
    // read header (we already know for certain that we have a Tiff file)
537
20.9k
    io.readOrThrow(dir.data(), 8, ErrorCode::kerCorruptedMetadata);
538
20.9k
    auto c = dir.read_uint8(0);
539
20.9k
    bool bSwap = (c == 'M' && isLittleEndianPlatform()) || (c == 'I' && isBigEndianPlatform());
540
20.9k
    size_t start = byteSwap4(dir, 4, bSwap);
541
20.9k
    printIFDStructure(io, out, option, start + offset, bSwap, c, depth);
542
20.9k
  }
543
37.1k
}
544
545
105k
void Image::clearMetadata() {
546
105k
  clearExifData();
547
105k
  clearIptcData();
548
105k
  clearXmpPacket();
549
105k
  clearXmpData();
550
105k
  clearComment();
551
105k
  clearIccProfile();
552
105k
}
553
554
691k
ExifData& Image::exifData() {
555
691k
  return exifData_;
556
691k
}
557
558
57.9k
IptcData& Image::iptcData() {
559
57.9k
  return iptcData_;
560
57.9k
}
561
562
96.8k
XmpData& Image::xmpData() {
563
96.8k
  return xmpData_;
564
96.8k
}
565
566
3.49k
std::string& Image::xmpPacket() {
567
  // Serialize the current XMP
568
3.49k
  if (!xmpData_.empty() && !writeXmpFromPacket()) {
569
0
    XmpParser::encode(xmpPacket_, xmpData_, XmpParser::useCompactFormat | XmpParser::omitAllFormatting);
570
0
  }
571
3.49k
  return xmpPacket_;
572
3.49k
}
573
574
/// \todo not used internally. At least we should test it
575
0
void Image::setMetadata(const Image& image) {
576
0
  if (checkMode(mdExif) & amWrite) {
577
0
    setExifData(image.exifData());
578
0
  }
579
0
  if (checkMode(mdIptc) & amWrite) {
580
0
    setIptcData(image.iptcData());
581
0
  }
582
0
  if (checkMode(mdIccProfile) & amWrite) {
583
0
    setIccProfile(image.iccProfile().clone());
584
0
  }
585
0
  if (checkMode(mdXmp) & amWrite) {
586
0
    setXmpPacket(image.xmpPacket());
587
0
    setXmpData(image.xmpData());
588
0
  }
589
0
  if (checkMode(mdComment) & amWrite) {
590
0
    setComment(image.comment());
591
0
  }
592
0
}
593
594
105k
void Image::clearExifData() {
595
105k
  exifData_.clear();
596
105k
}
597
598
449
void Image::setExifData(const ExifData& exifData) {
599
449
  exifData_ = exifData;
600
449
}
601
602
105k
void Image::clearIptcData() {
603
105k
  iptcData_.clear();
604
105k
}
605
606
449
void Image::setIptcData(const IptcData& iptcData) {
607
449
  iptcData_ = iptcData;
608
449
}
609
610
105k
void Image::clearXmpPacket() {
611
105k
  xmpPacket_.clear();
612
  // Also clear the packet cached inside xmpData_. Some formats (e.g. TIFF, where
613
  // XMP is stored in the Exif.Image.XMLPacket tag) write XMP from xmpData_'s
614
  // packet when writeXmpFromPacket() is set. Without this, a stale packet would
615
  // survive and the XMP would not be erased (e.g. "exiv2 -dx" on a TIFF).
616
105k
  xmpData_.setPacket(std::string());
617
105k
  writeXmpFromPacket(true);
618
105k
}
619
620
0
void Image::setXmpPacket(const std::string& xmpPacket) {
621
0
  const DecodeParams dp(max_recursion_depth_);
622
0
  if (XmpParser::decode(xmpData_, xmpPacket, dp)) {
623
0
    throw Error(ErrorCode::kerInvalidXMP);
624
0
  }
625
0
  xmpPacket_ = xmpPacket;
626
0
}
627
628
105k
void Image::clearXmpData() {
629
105k
  xmpData_.clear();
630
105k
  writeXmpFromPacket(false);
631
105k
}
632
633
449
void Image::setXmpData(const XmpData& xmpData) {
634
449
  xmpData_ = xmpData;
635
449
  writeXmpFromPacket(false);
636
449
}
637
638
#ifdef EXV_HAVE_XMP_TOOLKIT
639
210k
void Image::writeXmpFromPacket(bool flag) {
640
210k
  writeXmpFromPacket_ = flag;
641
210k
}
642
#else
643
void Image::writeXmpFromPacket(bool) {
644
}
645
#endif
646
647
105k
void Image::clearComment() {
648
105k
  comment_.erase();
649
105k
}
650
651
294
void Image::setComment(const std::string& comment) {
652
294
  comment_ = comment;
653
294
}
654
655
809
void Image::setIccProfile(Exiv2::DataBuf&& iccProfile, bool bTestValid) {
656
809
  iccProfile_ = std::move(iccProfile);
657
809
  if (bTestValid) {
658
809
    checkIccProfile();
659
809
  }
660
809
}
661
662
16.2k
void Image::appendIccProfile(const uint8_t* bytes, size_t size, bool bTestValid) {
663
16.2k
  if (size == 0) {
664
3.58k
    return;
665
3.58k
  }
666
12.6k
  const size_t start = iccProfile_.size();
667
12.6k
  iccProfile_.resize(Safe::add(start, size));
668
12.6k
  memcpy(iccProfile_.data(start), bytes, size);
669
12.6k
  if (bTestValid) {
670
101
    checkIccProfile();
671
101
  }
672
12.6k
}
673
674
910
void Image::checkIccProfile() const {
675
910
  if (iccProfile_.size() < sizeof(long)) {
676
46
    throw Error(ErrorCode::kerInvalidIccProfile);
677
46
  }
678
864
  const size_t size = iccProfile_.read_uint32(0, bigEndian);
679
864
  if (size != iccProfile_.size()) {
680
181
    throw Error(ErrorCode::kerInvalidIccProfile);
681
181
  }
682
864
}
683
684
105k
void Image::clearIccProfile() {
685
105k
  iccProfile_.reset();
686
105k
}
687
688
56.1k
void Image::setByteOrder(ByteOrder byteOrder) {
689
56.1k
  byteOrder_ = byteOrder;
690
56.1k
}
691
692
7.82k
ByteOrder Image::byteOrder() const {
693
7.82k
  return byteOrder_;
694
7.82k
}
695
696
5.28k
uint32_t Image::pixelWidth() const {
697
5.28k
  return pixelWidth_;
698
5.28k
}
699
700
5.28k
uint32_t Image::pixelHeight() const {
701
5.28k
  return pixelHeight_;
702
5.28k
}
703
704
1.15M
const ExifData& Image::exifData() const {
705
1.15M
  return exifData_;
706
1.15M
}
707
708
0
const IptcData& Image::iptcData() const {
709
0
  return iptcData_;
710
0
}
711
712
29.4k
const XmpData& Image::xmpData() const {
713
29.4k
  return xmpData_;
714
29.4k
}
715
716
2.10k
std::string Image::comment() const {
717
2.10k
  return comment_;
718
2.10k
}
719
720
0
const std::string& Image::xmpPacket() const {
721
0
  return xmpPacket_;
722
0
}
723
724
126k
BasicIo& Image::io() const {
725
126k
  return *io_;
726
126k
}
727
728
26.7k
bool Image::writeXmpFromPacket() const {
729
26.7k
  return writeXmpFromPacket_;
730
26.7k
}
731
732
122k
const NativePreviewList& Image::nativePreviews() const {
733
122k
  return nativePreviews_;
734
122k
}
735
736
117k
bool Image::good() const {
737
117k
  if (io_->open() != 0)
738
0
    return false;
739
117k
  IoCloser closer(*io_);
740
117k
  return ImageFactory::checkType(imageType_, *io_, false);
741
117k
}
742
743
/// \todo not used internally. At least we should test it
744
0
bool Image::supportsMetadata(MetadataId metadataId) const {
745
0
  return (supportedMetadata_ & metadataId) != 0;
746
0
}
747
748
0
AccessMode Image::checkMode(MetadataId metadataId) const {
749
0
  return ImageFactory::checkMode(imageType_, metadataId);
750
0
}
751
752
9.50k
const std::string& Image::tagName(uint16_t tag) {
753
9.50k
  if (init_) {
754
236
    int idx;
755
236
    const TagInfo* ti;
756
708
    for (ti = Internal::mnTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
757
472
      tags_[ti[idx].tag_] = ti[idx].name_;
758
1.41k
    for (ti = Internal::iopTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
759
1.18k
      tags_[ti[idx].tag_] = ti[idx].name_;
760
7.78k
    for (ti = Internal::gpsTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
761
7.55k
      tags_[ti[idx].tag_] = ti[idx].name_;
762
60.6k
    for (ti = Internal::ifdTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
763
60.4k
      tags_[ti[idx].tag_] = ti[idx].name_;
764
22.8k
    for (ti = Internal::exifTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
765
22.6k
      tags_[ti[idx].tag_] = ti[idx].name_;
766
4.72k
    for (ti = Internal::mpfTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
767
4.48k
      tags_[ti[idx].tag_] = ti[idx].name_;
768
4.01k
    for (ti = Internal::Nikon1MakerNote::tagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
769
3.77k
      tags_[ti[idx].tag_] = ti[idx].name_;
770
236
  }
771
9.50k
  init_ = false;
772
773
9.50k
  return tags_[tag];
774
9.50k
}
775
776
0
AccessMode ImageFactory::checkMode(ImageType type, MetadataId metadataId) {
777
0
  auto r = Exiv2::find(registry, type);
778
0
  if (!r)
779
0
    throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type));
780
0
  if (metadataId == mdExif)
781
0
    return r->exifSupport_;
782
0
  if (metadataId == mdIptc)
783
0
    return r->iptcSupport_;
784
0
  if (metadataId == mdXmp)
785
0
    return r->xmpSupport_;
786
0
  if (metadataId == mdComment)
787
0
    return r->commentSupport_;
788
0
  return amNone;
789
0
}
790
791
117k
bool ImageFactory::checkType(ImageType type, BasicIo& io, bool advance) {
792
117k
  if (auto r = Exiv2::find(registry, type))
793
117k
    return r->isThisType_(io, advance);
794
0
  return false;
795
117k
}
796
797
0
ImageType ImageFactory::getType([[maybe_unused]] const std::string& path) {
798
0
#ifdef EXV_ENABLE_FILESYSTEM
799
0
  FileIo fileIo(path);
800
0
  return getType(fileIo);
801
#else
802
  return ImageType::none;
803
#endif
804
0
}
805
806
0
ImageType ImageFactory::getType(const byte* data, size_t size) {
807
0
  MemIo memIo(data, size);
808
0
  return getType(memIo);
809
0
}
810
811
0
ImageType ImageFactory::getType(BasicIo& io) {
812
0
  if (io.open() != 0)
813
0
    return ImageType::none;
814
0
  IoCloser closer(io);
815
0
  for (const auto& r : registry) {
816
0
    if (r.isThisType_(io, false)) {
817
0
      return r.imageType_;
818
0
    }
819
0
  }
820
0
  return ImageType::none;
821
0
}
822
823
0
BasicIo::UniquePtr ImageFactory::createIo(const std::string& path, [[maybe_unused]] bool useCurl) {
824
0
  [[maybe_unused]] Protocol fProt = fileProtocol(path);
825
826
#ifdef EXV_USE_CURL
827
  if (useCurl && (fProt == pHttp || fProt == pHttps || fProt == pFtp)) {
828
    return std::make_unique<CurlIo>(path);  // may throw
829
  }
830
#endif
831
832
0
#ifdef EXV_ENABLE_WEBREADY
833
0
  if (fProt == pHttp)
834
0
    return std::make_unique<HttpIo>(path);  // may throw
835
0
#endif
836
0
#ifdef EXV_ENABLE_FILESYSTEM
837
0
  if (fProt == pFileUri)
838
0
    return std::make_unique<FileIo>(pathOfFileUrl(path));
839
0
  if (fProt == pStdin || fProt == pDataUri)
840
0
    return std::make_unique<XPathIo>(path);  // may throw
841
842
0
  return std::make_unique<FileIo>(path);
843
#else
844
  throw Error(ErrorCode::kerFileAccessDisabled, path);
845
#endif
846
0
}  // ImageFactory::createIo
847
848
#ifdef _WIN32
849
BasicIo::UniquePtr ImageFactory::createIo(const std::wstring& path) {
850
#ifdef EXV_ENABLE_FILESYSTEM
851
  return std::make_unique<FileIo>(path);
852
#else
853
  return nullptr;
854
#endif
855
}
856
#endif
857
858
0
Image::UniquePtr ImageFactory::open(const std::string& path, bool useCurl) {
859
0
  auto image = open(ImageFactory::createIo(path, useCurl));  // may throw
860
0
  if (!image)
861
0
    throw Error(ErrorCode::kerFileContainsUnknownImageType, path);
862
0
  return image;
863
0
}
864
865
#ifdef _WIN32
866
Image::UniquePtr ImageFactory::open(const std::wstring& path) {
867
  auto image = open(ImageFactory::createIo(path));  // may throw
868
  if (!image) {
869
    char t[1024];
870
    WideCharToMultiByte(CP_UTF8, 0, path.c_str(), -1, t, 1024, nullptr, nullptr);
871
    throw Error(ErrorCode::kerFileContainsUnknownImageType, t);
872
  }
873
  return image;
874
}
875
#endif
876
877
120k
Image::UniquePtr ImageFactory::open(const byte* data, size_t size) {
878
120k
  auto image = open(std::make_unique<MemIo>(data, size));  // may throw
879
120k
  if (!image)
880
2.80k
    throw Error(ErrorCode::kerMemoryContainsUnknownImageType);
881
117k
  return image;
882
120k
}
883
884
120k
Image::UniquePtr ImageFactory::open(BasicIo::UniquePtr io) {
885
120k
  if (io->open() != 0) {
886
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io->path(), strError());
887
0
  }
888
1.99M
  for (const auto& r : registry) {
889
1.99M
    if (r.isThisType_(*io, false)) {
890
117k
      return r.newInstance_(std::move(io), ImageCtorParams(false, 1000));
891
117k
    }
892
1.99M
  }
893
3.33k
  return nullptr;
894
120k
}
895
896
#ifdef EXV_ENABLE_FILESYSTEM
897
0
Image::UniquePtr ImageFactory::create(ImageType type, const std::string& path) {
898
0
  auto fileIo = std::make_unique<FileIo>(path);
899
  // Create or overwrite the file, then close it
900
0
  if (fileIo->open("w+b") != 0) {
901
0
    throw Error(ErrorCode::kerFileOpenFailed, path, "w+b", strError());
902
0
  }
903
0
  fileIo->close();
904
905
0
  BasicIo::UniquePtr io(std::move(fileIo));
906
0
  auto image = create(type, std::move(io));
907
0
  if (!image)
908
0
    throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type));
909
0
  return image;
910
0
}
911
#endif
912
913
449
Image::UniquePtr ImageFactory::create(ImageType type) {
914
449
  auto image = create(type, std::make_unique<MemIo>());
915
449
  if (!image)
916
0
    throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type));
917
449
  return image;
918
449
}
919
920
449
Image::UniquePtr ImageFactory::create(ImageType type, BasicIo::UniquePtr io) {
921
  // BasicIo instance does not need to be open
922
449
  if (type == ImageType::none)
923
0
    return {};
924
449
  if (auto r = Exiv2::find(registry, type))
925
449
    return r->newInstance_(std::move(io), ImageCtorParams(true, 1000));
926
0
  return {};
927
449
}
928
929
// *****************************************************************************
930
// template, inline and free functions
931
932
409k
void append(Blob& blob, const byte* buf, size_t len) {
933
409k
  if (len != 0) {
934
344k
    Blob::size_type size = blob.size();
935
344k
    if (blob.capacity() - size < len) {
936
12.2k
      blob.reserve(size + 65536);
937
12.2k
    }
938
344k
    blob.resize(size + len);
939
344k
    std::copy_n(buf, len, blob.begin() + size);
940
344k
  }
941
409k
}  // append
942
943
}  // namespace Exiv2