diff --git a/src/Makefile b/src/Makefile index 1fc049ac..d533d7e6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -110,6 +110,7 @@ BINSRC = addmoddel.cpp \ iptcprint.cpp \ iptctest.cpp \ key-test.cpp \ + largeiptc-test.cpp \ makernote-test.cpp \ taglist.cpp \ write-test.cpp \ diff --git a/src/error.cpp b/src/error.cpp index 57e7ae74..5f9fd075 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -78,6 +78,7 @@ namespace Exiv2 { ErrMsg( 34, "%1: Not supported"), // %1=function ErrMsg( 35, "ImageFactory registry full"), ErrMsg( 36, "Failed to decode %1 metadata"), // %1=type of metadata (Exif, IPTC) + ErrMsg( 37, "Size of %1 JPEG segment is larger than 65535 bytes"), // %1=type of metadata (Exif, IPTC, JPEG comment) // Last error message (message is not used) ErrMsg( -2, "(Unknown Error)") diff --git a/src/jpgimage.cpp b/src/jpgimage.cpp index 17e7ce66..2211c77c 100644 --- a/src/jpgimage.cpp +++ b/src/jpgimage.cpp @@ -71,8 +71,8 @@ namespace Exiv2 { long sizePsData, uint16_t psTag, const byte** record, - uint16_t *const sizeHdr, - uint16_t *const sizeData) + uint32_t *const sizeHdr, + uint32_t *const sizeData) { assert(record); assert(sizeHdr); @@ -95,12 +95,12 @@ namespace Exiv2 { if (position >= sizePsData) return -2; // Data is also padded to be even - long dataSize = getULong(pPsData + position, bigEndian); + uint32_t dataSize = getULong(pPsData + position, bigEndian); position += 4; - if (dataSize > sizePsData - position) return -2; + if (dataSize > static_cast(sizePsData - position)) return -2; if (type == psTag) { - *sizeData = static_cast(dataSize); + *sizeData = dataSize; *sizeHdr = psSize + 10; *record = hrd; return 0; @@ -113,8 +113,8 @@ namespace Exiv2 { int Photoshop::locateIptcIrb(const byte* pPsData, long sizePsData, const byte** record, - uint16_t *const sizeHdr, - uint16_t *const sizeData) + uint32_t *const sizeHdr, + uint32_t *const sizeData) { return locateIrb(pPsData, sizePsData, iptc_, record, sizeHdr, sizeData); @@ -131,17 +131,17 @@ namespace Exiv2 { else hexdump(std::cerr, pPsData, sizePsData); #endif const byte* record = pPsData; - uint16_t sizeIptc = 0; - uint16_t sizeHdr = 0; + uint32_t sizeIptc = 0; + uint32_t sizeHdr = 0; // Safe to call with zero psData.size_ Photoshop::locateIptcIrb(pPsData, sizePsData, &record, &sizeHdr, &sizeIptc); Blob psBlob; // Data is rounded to be even - const uint16_t sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1); - const uint16_t sizeFront = static_cast(record - pPsData); - const uint16_t sizeEnd = static_cast(sizePsData - sizeFront - sizeOldData); + const uint32_t sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1); + const uint32_t sizeFront = record - pPsData; + const uint32_t sizeEnd = sizePsData - sizeFront - sizeOldData; // Write data before old record. if (sizePsData > 0 && sizeFront > 0) { @@ -309,8 +309,8 @@ namespace Exiv2 { io_->read(psData.pData_, psData.size_); if (io_->error() || io_->eof()) throw Error(14); const byte *record = 0; - uint16_t sizeIptc = 0; - uint16_t sizeHdr = 0; + uint32_t sizeIptc = 0; + uint32_t sizeHdr = 0; // Find actual Iptc data within the APP13 segment if (!Photoshop::locateIptcIrb(psData.pData_, psData.size_, &record, &sizeHdr, &sizeIptc)) { @@ -465,8 +465,10 @@ namespace Exiv2 { // Write COM marker, size of comment, and string tmpBuf[0] = 0xff; tmpBuf[1] = com_; - us2Data(tmpBuf + 2, - static_cast(comment_.length()+3), bigEndian); + + if (comment_.length() + 3 > 0xffff) throw Error(37, "JPEG comment"); + us2Data(tmpBuf + 2, static_cast(comment_.length() + 3), bigEndian); + if (outIo.write(tmpBuf, 4) != 4) throw Error(21); if (outIo.write((byte*)comment_.data(), (long)comment_.length()) != (long)comment_.length()) throw Error(21); @@ -476,15 +478,17 @@ namespace Exiv2 { } if (exifData_.count() > 0) { // Write APP1 marker, size of APP1 field, Exif id and Exif data - DataBuf rawExif(exifData_.copy()); + DataBuf rawExif = exifData_.copy(); tmpBuf[0] = 0xff; tmpBuf[1] = app1_; - us2Data(tmpBuf + 2, - static_cast(rawExif.size_+8), - bigEndian); + + if (rawExif.size_ + 8 > 0xffff) throw Error(37, "Exif"); + us2Data(tmpBuf + 2, static_cast(rawExif.size_ + 8), bigEndian); memcpy(tmpBuf + 4, exifId_, 6); if (outIo.write(tmpBuf, 10) != 10) throw Error(21); - if (outIo.write(rawExif.pData_, rawExif.size_) + + // Write new Exif data buffer + if ( outIo.write(rawExif.pData_, rawExif.size_) != rawExif.size_) throw Error(21); if (outIo.error()) throw Error(21); --search; @@ -499,6 +503,8 @@ namespace Exiv2 { // Write APP13 marker, new size, and ps3Id tmpBuf[0] = 0xff; tmpBuf[1] = app13_; + + if (newPsData.size_ + 16 > 0xffff) throw Error(37, "IPTC"); us2Data(tmpBuf + 2, static_cast(newPsData.size_ + 16), bigEndian); memcpy(tmpBuf + 4, Photoshop::ps3Id_, 14); if (outIo.write(tmpBuf, 18) != 18) throw Error(21); @@ -506,7 +512,7 @@ namespace Exiv2 { // Write new Photoshop IRB data buffer if ( outIo.write(newPsData.pData_, newPsData.size_) - != newPsData.size_) throw Error(21); + != newPsData.size_) throw Error(21); if (outIo.error()) throw Error(21); } if (iptcData_.count() > 0) { diff --git a/src/jpgimage.hpp b/src/jpgimage.hpp index 2e6a30e7..ea244c95 100644 --- a/src/jpgimage.hpp +++ b/src/jpgimage.hpp @@ -87,16 +87,16 @@ namespace Exiv2 { long sizePsData, uint16_t psTag, const byte **record, - uint16_t *const sizeHdr, - uint16_t *const sizeData); + uint32_t *const sizeHdr, + uint32_t *const sizeData); /*! @brief Forwards to locateIrb() with \em psTag = \em iptc_ */ static int locateIptcIrb(const byte *pPsData, long sizePsData, const byte **record, - uint16_t *const sizeHdr, - uint16_t *const sizeData); + uint32_t *const sizeHdr, + uint32_t *const sizeData); /*! @brief Set the new IPTC IRB, keeps existing IRBs but removes the IPTC block if there is no new IPTC data to write. diff --git a/src/largeiptc-test.cpp b/src/largeiptc-test.cpp new file mode 100644 index 00000000..a9075e62 --- /dev/null +++ b/src/largeiptc-test.cpp @@ -0,0 +1,71 @@ +// ***************************************************************** -*- C++ -*- +// Test for large (>65535 bytes) IPTC buffer + +#include "iptc.hpp" +#include "image.hpp" +#include "jpgimage.hpp" +#include "futils.hpp" +#include +#include + +int main(int argc, char* const argv[]) +try { + if (argc != 3) { + std::cout << "Usage: " << argv[0] << " image datafile\n"; + return 1; + } + std::string file(argv[1]); + std::string data(argv[2]); + + // Read data file into data buffer + Exiv2::FileIo io(data); + if (io.open() != 0) { + throw Exiv2::Error(9, io.path(), Exiv2::strError()); + } + Exiv2::DataBuf buf(io.size()); + std::cout << "Reading " << buf.size_ << " bytes from " << data << "\n"; + io.read(buf.pData_, buf.size_); + if (io.error() || io.eof()) throw Exiv2::Error(14); + + // Read metadata from file + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file); + assert(image.get() != 0); + image->readMetadata(); + + // Set Preview field to the content of the data file + Exiv2::DataValue value; + value.read(buf.pData_, buf.size_); + Exiv2::IptcData& iptcData = image->iptcData(); + std::cout << "IPTC fields: " << iptcData.size() << "\n"; + iptcData["Iptc.Application2.Preview"] = value; + std::cout << "IPTC fields: " << iptcData.size() << "\n"; + + // Set IRB, compare with IPTC raw data + Exiv2::DataBuf irb = Exiv2::Photoshop::setIptcIrb(0, 0, iptcData); + std::cout << "IRB buffer : " << irb.size_ << "\n"; + const Exiv2::byte* record; + uint32_t sizeHdr; + uint32_t sizeData; + Exiv2::Photoshop::locateIptcIrb(irb.pData_, irb.size_, &record, &sizeHdr, &sizeData); + Exiv2::DataBuf rawIptc = iptcData.copy(); + std::cout << "Comparing IPTC and IRB size... "; + if (static_cast(rawIptc.size_) != sizeData) { + std::cout << "not "; + } + std::cout << "ok\n"; + + std::cout << "Comparing IPTC and IRB data... "; + if (0 != memcmp(rawIptc.pData_, record + sizeHdr, sizeData)) { + std::cout << "not "; + } + std::cout << "ok\n"; + + // Set Iptc data and write it to the file + image->writeMetadata(); + + return 0; +} +catch (Exiv2::AnyError& e) { + std::cout << "Caught Exiv2 exception '" << e << "'\n"; + return -1; +} diff --git a/src/tiffvisitor.cpp b/src/tiffvisitor.cpp index c830ce94..d189f080 100644 --- a/src/tiffvisitor.cpp +++ b/src/tiffvisitor.cpp @@ -190,8 +190,8 @@ namespace Exiv2 { assert(pImage_ != 0); if (!object->pData()) return; byte const* record = 0; - uint16_t sizeHdr = 0; - uint16_t sizeData = 0; + uint32_t sizeHdr = 0; + uint32_t sizeData = 0; if (0 != Photoshop::locateIptcIrb(object->pData(), object->size(), &record, &sizeHdr, &sizeData)) { return; diff --git a/test/bugfixes-test.sh b/test/bugfixes-test.sh index b52b7ebe..fe1a522c 100755 --- a/test/bugfixes-test.sh +++ b/test/bugfixes-test.sh @@ -58,6 +58,10 @@ num=479 filename=`prep_file $num` $binpath/exiv2 -pt $filename +num=480 +filename=`prep_file $num` +$binpath/largeiptc-test $filename ../data/smiley1.jpg.ixgd + ) > $results 2>&1 if [ x`which unix2dos.exe` != x ]; then diff --git a/test/data/bugfixes-test.out b/test/data/bugfixes-test.out index ca368b85..a8a1f0ca 100644 --- a/test/data/bugfixes-test.out +++ b/test/data/bugfixes-test.out @@ -235,3 +235,11 @@ Exif.Image.0x9286 Undefined 264 (Binary value suppre Exif.Photo.ExifVersion Undefined 4 48 50 50 48 Exif.Photo.PixelXDimension Short 1 3173 Exif.Photo.PixelYDimension Short 1 2011 +------> Bug 480 <------- +Reading 67070 bytes from ../data/smiley1.jpg.ixgd +IPTC fields: 0 +IPTC fields: 67079 +IRB buffer : 67092 +Comparing IPTC and IRB size... ok +Comparing IPTC and IRB data... ok +Caught Exiv2 exception 'Size of IPTC JPEG segment is larger than 65535 bytes' diff --git a/test/data/exiv2-bug480.jpg b/test/data/exiv2-bug480.jpg new file mode 100644 index 00000000..9394df8d Binary files /dev/null and b/test/data/exiv2-bug480.jpg differ