diff --git a/include/exiv2/error.hpp b/include/exiv2/error.hpp index 24a70bf6..cc67725b 100644 --- a/include/exiv2/error.hpp +++ b/include/exiv2/error.hpp @@ -192,6 +192,74 @@ namespace Exiv2 { return os << error.what(); } + //! Complete list of all Exiv2 error codes + enum ErrorCode { + kerGeneralError = -1, + kerSuccess = 0, + kerErrorMessage, + kerCallFailed, + kerNotAnImage, + kerInvalidDataset, + kerInvalidRecord, + kerInvalidKey, + kerInvalidTag, + kerValueNotSet, + kerDataSourceOpenFailed, + kerFileOpenFailed, + kerFileContainsUnknownImageType, + kerMemoryContainsUnknownImageType, + kerUnsupportedImageType, + kerFailedToReadImageData, + kerNotAJpeg, + kerFailedToMapFileForReadWrite, + kerFileRenameFailed, + kerTransferFailed, + kerMemoryTransferFailed, + kerInputDataReadFailed, + kerImageWriteFailed, + kerNoImageInInputData, + kerInvalidIfdId, + //! Entry::setValue: Value too large + kerValueTooLarge, + //! Entry::setDataArea: Value too large + kerDataAreaValueTooLarge, + kerOffsetOutOfRange, + kerUnsupportedDataAreaOffsetType, + kerInvalidCharset, + kerUnsupportedDateFormat, + kerUnsupportedTimeFormat, + kerWritingImageFormatUnsupported, + kerInvalidSettingForImage, + kerNotACrwImage, + kerFunctionNotSupported, + kerNoNamespaceInfoForXmpPrefix, + kerNoPrefixForNamespace, + kerTooLargeJpegSegment, + kerUnhandledXmpdatum, + kerUnhandledXmpNode, + kerXMPToolkitError, + kerDecodeLangAltPropertyFailed, + kerDecodeLangAltQualifierFailed, + kerEncodeLangAltPropertyFailed, + kerPropertyNameIdentificationFailed, + kerSchemaNamespaceNotRegistered, + kerNoNamespaceForPrefix, + kerAliasesNotSupported, + kerInvalidXmpText, + kerTooManyTiffDirectoryEntries, + kerMultipleTiffArrayElementTagsInDirectory, + kerWrongTiffArrayElementTagType, + kerInvalidKeyXmpValue, + kerInvalidIccProfile, + kerInvalidXMP, + kerTiffDirectoryTooLarge, + kerInvalidTypeValue, + kerInvalidMalloc, + kerCorruptedMetadata, + kerArithmeticOverflow, + kerMallocFailed, + }; + /*! @brief Simple error class used for exceptions. An output operator is provided to print errors to a stream. diff --git a/src/enforce.hpp b/src/enforce.hpp new file mode 100644 index 00000000..b2d77eea --- /dev/null +++ b/src/enforce.hpp @@ -0,0 +1,96 @@ +// ********************************************************* -*- C++ -*- +/* + * Copyright (C) 2004-2018 Exiv2 maintainers + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file enforce.hpp + @brief Port of D's enforce() to C++ & Exiv2 + @author Dan Čermák (D4N) + dan.cermak@cgc-instruments.com + @date 11-March-18, D4N: created + */ + +#include + +#include "error.hpp" + +/*! + * @brief Ensure that condition is true, otherwise throw an exception of the + * type exception_t + * + * @tparam exception_t Exception type that is thrown, must provide a + * constructor that accepts a single argument to which arg1 is forwarded. + * + * @todo once we have C++>=11 use variadic templates and std::forward to remove + * all overloads of enforce + */ +template +inline void enforce(bool condition, const T& arg1) +{ + if (!condition) { + throw exception_t(arg1); + } +} + +/*! + * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with + * the given error_code. + */ +inline void enforce(bool condition, Exiv2::ErrorCode err_code) +{ + if (!condition) { + throw Exiv2::Error(err_code); + } +} + +/*! + * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with + * the given error_code & arg1. + */ +template +inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1) +{ + if (!condition) { + throw Exiv2::Error(err_code, arg1); + } +} + +/*! + * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with + * the given error_code, arg1 & arg2. + */ +template +inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1, const U& arg2) +{ + if (!condition) { + throw Exiv2::Error(err_code, arg1, arg2); + } +} + +/*! + * @brief Ensure that condition is true, otherwise throw an Exiv2::Error with + * the given error_code, arg1, arg2 & arg3. + */ +template +inline void enforce(bool condition, Exiv2::ErrorCode err_code, const T& arg1, const U& arg2, const V& arg3) +{ + if (!condition) { + throw Exiv2::Error(err_code, arg1, arg2, arg3); + } +} diff --git a/src/pngchunk.cpp b/src/pngchunk.cpp index 4dcca4d..aae0f5f 100644 --- a/src/pngchunk.cpp +++ b/src/pngchunk.cpp @@ -37,6 +37,7 @@ EXIV2_RCSID("@(#) $Id$") #include "iptc.hpp" #include "image.hpp" #include "error.hpp" +#include "enforce.hpp" // + standard includes #include @@ -46,6 +47,7 @@ EXIV2_RCSID("@(#) $Id$") #include #include #include +#include #include // To uncompress or compress text chunk @@ -86,7 +88,7 @@ namespace Exiv2 { #ifdef DEBUG std::cout << "Exiv2::PngChunk::decodeTXTChunk: TXT chunk data: " - << std::string((const char*)arr.pData_, arr.size_) << "\n"; + << std::string((const char*)arr.pData_, arr.size_) << std::endl; #endif parseChunkContent(pImage, key.pData_, key.size_, arr); @@ -99,7 +101,7 @@ namespace Exiv2 { #ifdef DEBUG std::cout << "Exiv2::PngChunk::decodeTXTChunk: TXT chunk key: " - << std::string((const char*)key.pData_, key.size_) << "\n"; + << std::string((const char*)key.pData_, key.size_) << std::endl; #endif return parseTXTChunk(data, key.size_, type); @@ -164,12 +166,18 @@ namespace Exiv2 { } else if(type == iTXt_Chunk) { + const int nullSeparators = std::count(&data.pData_[keysize+3], &data.pData_[data.size_], '\0'); + + enforce(nullSeparators >= 2, Exiv2::kerCorruptedMetadata); + // Extract a deflate compressed or uncompressed UTF-8 text chunk // we get the compression flag after the key - const byte* compressionFlag = data.pData_ + keysize + 1; + const byte compressionFlag = data.pData_[keysize + 1]; // we get the compression method after the compression flag - const byte* compressionMethod = data.pData_ + keysize + 2; + const byte compressionMethod = data.pData_[keysize + 2]; + enforce(compressionFlag == 0x00 || compressionFlag == 0x01, Exiv2::kerCorruptedMetadata); + enforce(compressionMethod == 0x00, Exiv2::kerCorruptedMetadata); // language description string after the compression technique spec std::string languageText((const char*)(data.pData_ + keysize + 3)); unsigned int languageTextSize = static_cast(languageText.size()); @@ -177,7 +185,7 @@ namespace Exiv2 { std::string translatedKeyText((const char*)(data.pData_ + keysize + 3 + languageTextSize +1)); unsigned int translatedKeyTextSize = static_cast(translatedKeyText.size()); - if ( compressionFlag[0] == 0x00 ) + if ( compressionFlag == 0x00 ) { // then it's an uncompressed iTXt chunk #ifdef DEBUG @@ -191,7 +199,7 @@ namespace Exiv2 { arr.alloc(textsize); arr = DataBuf(text, textsize); } - else if ( compressionFlag[0] == 0x01 && compressionMethod[0] == 0x00 ) + else if ( compressionFlag == 0x01 && compressionMethod == 0x00 ) { // then it's a zlib compressed iTXt chunk #ifdef DEBUG diff --git a/src/pngimage.cpp b/src/pngimage.cpp index ed7399a..991da6c 100644 --- a/src/pngimage.cpp +++ b/src/pngimage.cpp @@ -375,7 +375,7 @@ namespace Exiv2 { void PngImage::readMetadata() { #ifdef DEBUG - std::cerr << "Exiv2::PngImage::readMetadata: Reading PNG file " << io_->path() << "\n"; + std::cerr << "Exiv2::PngImage::readMetadata: Reading PNG file " << io_->path() << std::endl; #endif if (io_->open() != 0) { @@ -398,7 +398,7 @@ namespace Exiv2 { // Read chunk header. #ifdef DEBUG - std::cout << "Exiv2::PngImage::readMetadata: Position: " << io_->tell() << "\n"; + std::cout << "Exiv2::PngImage::readMetadata: Position: " << io_->tell() << std::endl; #endif std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_); long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_); @@ -432,14 +432,14 @@ namespace Exiv2 { { // Last chunk found: we stop parsing. #ifdef DEBUG - std::cout << "Exiv2::PngImage::readMetadata: Found IEND chunk (length: " << dataOffset << ")\n"; + std::cout << "Exiv2::PngImage::readMetadata: Found IEND chunk with length: " << dataOffset << std::endl; #endif return; } else if (!memcmp(cheaderBuf.pData_ + 4, "IHDR", 4)) { #ifdef DEBUG - std::cout << "Exiv2::PngImage::readMetadata: Found IHDR chunk (length: " << dataOffset << ")\n"; + std::cout << "Exiv2::PngImage::readMetadata: Found IHDR chunk with length: " << dataOffset << std::endl; #endif if (cdataBuf.size_ >= 8) { PngChunk::decodeIHDRChunk(cdataBuf, &pixelWidth_, &pixelHeight_); @@ -448,21 +448,21 @@ namespace Exiv2 { else if (!memcmp(cheaderBuf.pData_ + 4, "tEXt", 4)) { #ifdef DEBUG - std::cout << "Exiv2::PngImage::readMetadata: Found tEXt chunk (length: " << dataOffset << ")\n"; + std::cout << "Exiv2::PngImage::readMetadata: Found tEXt chunk with length: " << dataOffset << std::endl; #endif PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::tEXt_Chunk); } else if (!memcmp(cheaderBuf.pData_ + 4, "zTXt", 4)) { #ifdef DEBUG - std::cout << "Exiv2::PngImage::readMetadata: Found zTXt chunk (length: " << dataOffset << ")\n"; + std::cout << "Exiv2::PngImage::readMetadata: Found zTXt chunk with length: " << dataOffset << std::endl; #endif PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::zTXt_Chunk); } else if (!memcmp(cheaderBuf.pData_ + 4, "iTXt", 4)) { #ifdef DEBUG - std::cout << "Exiv2::PngImage::readMetadata: Found iTXt chunk (length: " << dataOffset << ")\n"; + std::cout << "Exiv2::PngImage::readMetadata: Found iTXt chunk with length: " << dataOffset << std::endl; #endif PngChunk::decodeTXTChunk(this, cdataBuf, PngChunk::iTXt_Chunk); } @@ -481,7 +481,7 @@ namespace Exiv2 { // Move to the next chunk: chunk data size + 4 CRC bytes. #ifdef DEBUG - std::cout << "Exiv2::PngImage::readMetadata: Seek to offset: " << dataOffset + 4 << "\n"; + std::cout << "Exiv2::PngImage::readMetadata: Seek to offset: " << dataOffset + 4 << std::endl; #endif io_->seek(dataOffset + 4 , BasicIo::cur); if (io_->error() || io_->eof()) throw Error(14); @@ -511,8 +511,8 @@ namespace Exiv2 { if (!outIo.isopen()) throw Error(21); #ifdef DEBUG - std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << "\n"; - std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << "\n"; + std::cout << "Exiv2::PngImage::doWriteMetadata: Writing PNG file " << io_->path() << std::endl; + std::cout << "Exiv2::PngImage::doWriteMetadata: tmp file created " << outIo.path() << std::endl; #endif // Ensure that this is the correct image type