Backport of: From 9c78968498f3321bd50c817bae585bb0936e5e08 Mon Sep 17 00:00:00 2001 From: arpitapanda05 <79362772+arpitapanda05@users.noreply.github.com> Date: Thu, 15 Jul 2021 22:39:17 +0530 Subject: Security Fixes and Exif Time Zone Metadata Support Co-authored-by: prigupta --- XMPCore/source/XMPUtils-FileInfo.cpp | 9 +- XMPFiles/source/FileHandlers/FLV_Handler.cpp | 2 + XMPFiles/source/FileHandlers/P2_Handler.cpp | 38 ++++--- XMPFiles/source/FileHandlers/PSD_Handler.cpp | 14 ++- XMPFiles/source/FileHandlers/SWF_Handler.cpp | 10 ++ XMPFiles/source/FileHandlers/TIFF_Handler.cpp | 2 +- XMPFiles/source/FileHandlers/UCF_Handler.cpp | 7 +- XMPFiles/source/FileHandlers/UCF_Handler.hpp | 1 + XMPFiles/source/FormatSupport/ASF_Support.cpp | 11 +- XMPFiles/source/FormatSupport/ID3_Support.cpp | 2 +- XMPFiles/source/FormatSupport/P2_Support.cpp | 9 +- XMPFiles/source/FormatSupport/PNG_Support.cpp | 2 +- .../source/FormatSupport/PostScript_Support.cpp | 6 +- XMPFiles/source/FormatSupport/RIFF.cpp | 4 + XMPFiles/source/FormatSupport/ReconcileLegacy.hpp | 24 ++-- XMPFiles/source/FormatSupport/ReconcileTIFF.cpp | 121 +++++++++++++++++---- XMPFiles/source/FormatSupport/SVG_Adapter.cpp | 8 +- XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp | 36 +++--- .../source/FormatSupport/TIFF_MemoryReader.cpp | 8 +- XMPFiles/source/FormatSupport/TIFF_Support.hpp | 30 ++++- .../source/PluginHandler/FileHandlerInstance.cpp | 5 +- XMPFiles/source/WXMPFiles.cpp | 13 ++- XMPFiles/source/XMPFiles.cpp | 6 +- public/include/TXMPFiles.hpp | 3 +- 24 files changed, 264 insertions(+), 107 deletions(-) --- a/XMPCore/source/XMPUtils-FileInfo.cpp +++ b/XMPCore/source/XMPUtils-FileInfo.cpp @@ -782,9 +782,12 @@ AppendSubtree ( const XMP_Node * sourceN for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim && destNode!= NULL; ++sourceNum ) { const XMP_Node * sourceField = sourceNode->children[sourceNum]; AppendSubtree ( sourceField, destNode, mergeCompound, replaceOld, deleteEmpty ); - if ( deleteEmpty && destNode->children.empty() ) { - delete ( destNode ); - destParent->children.erase ( destPos ); + + if (deleteEmpty && destNode->children.empty()) + { + delete (destNode); + destNode = NULL; + destParent->children.erase(destPos); } } --- a/XMPFiles/source/FileHandlers/FLV_Handler.cpp +++ b/XMPFiles/source/FileHandlers/FLV_Handler.cpp @@ -210,6 +210,8 @@ static void GetTagInfo ( XMP_IO* fileRef static XMP_Uns32 GetASValueLen ( const XMP_Uns8 * asValue, const XMP_Uns8 * asLimit ) { + if (asValue > asLimit) + return 0; XMP_Uns32 valueLen = 0; const XMP_Uns8 * itemPtr; XMP_Uns32 arrayCount; --- a/XMPFiles/source/FileHandlers/P2_Handler.cpp +++ b/XMPFiles/source/FileHandlers/P2_Handler.cpp @@ -1129,6 +1129,7 @@ void P2_MetaHandler::ProcessXMP() XML_NodePtr legacyContext, clipMetadata, legacyProp; if ( ! this->p2ClipManager.IsValidP2() ) return; P2_Clip* p2Clip=this->p2ClipManager.GetManagedClip(); + if( p2Clip->GetP2RootNode() == 0) return; XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); std::string oldDigest, newDigest; bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", &oldDigest, 0 ); @@ -1299,24 +1300,27 @@ void P2_MetaHandler::UpdateFile ( bool d if (frameFormat == "50Timecode" || frameFormat == "5994DropTimecode" || frameFormat == "5994NonDropTimecode") { p2Clip = this->p2ClipManager.GetManagedClip(); - XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); - XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode(); - if (legacyVideoContext != 0) + if( p2Clip->GetP2RootNode() != 0 ) { - legacyVideoContext = legacyVideoContext->GetNamedElement(p2NS, "Video"); - XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement(p2NS, "StartTimecode"); - if ((legacyProp != 0) && legacyProp->IsLeafContentNode()) - { - AdjustTimeCode( xmpStartTimeCode, true ); - if (xmpStartTimeCode != legacyProp->GetLeafContentValue()) - { - legacyProp->SetLeafContentValue(xmpStartTimeCode.c_str()); - updateLegacyXML = true; - } - } - } - } - } + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); + XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode(); + if (legacyVideoContext != 0) + { + legacyVideoContext = legacyVideoContext->GetNamedElement(p2NS, "Video"); + XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement(p2NS, "StartTimecode"); + if ((legacyProp != 0) && legacyProp->IsLeafContentNode()) + { + AdjustTimeCode( xmpStartTimeCode, true ); + if (xmpStartTimeCode != legacyProp->GetLeafContentValue()) + { + legacyProp->SetLeafContentValue(xmpStartTimeCode.c_str()); + updateLegacyXML = true; + } + } + } + } + } + } std::string newDigest; this->p2ClipManager.GetManagedClip()->CreateDigest ( &newDigest ); --- a/XMPFiles/source/FileHandlers/PSD_Handler.cpp +++ b/XMPFiles/source/FileHandlers/PSD_Handler.cpp @@ -140,7 +140,13 @@ void PSD_MetaHandler::CacheFileData() cmLen = GetUns32BE ( &psdHeader[26] ); - XMP_Int64 psirOrigin = 26 + 4 + cmLen; + XMP_Int64 psirOrigin = 26 + 4 + static_cast(cmLen); + XMP_Int64 fileLength = fileRef->Length(); + + if (psirOrigin > fileLength) + { + XMP_Throw("Invalid PSD chunk length", kXMPErr_BadPSD); + } filePos = fileRef->Seek ( psirOrigin, kXMP_SeekFromStart ); if ( filePos != psirOrigin ) return; // Throw? @@ -151,7 +157,9 @@ void PSD_MetaHandler::CacheFileData() this->psirMgr.ParseFileResources ( fileRef, psirLen ); PSIR_Manager::ImgRsrcInfo xmpInfo; - bool found = this->psirMgr.GetImgRsrc ( kPSIR_XMP, &xmpInfo ); + bool found = this->psirMgr.GetImgRsrc(kPSIR_XMP, &xmpInfo); + if (psirLen < xmpInfo.dataLen) + return; if ( found ) { @@ -425,4 +433,4 @@ void PSD_MetaHandler::WriteTempFile ( XM } // PSD_MetaHandler::WriteTempFile -// ================================================================================================= +// ================================================================================================= \ No newline at end of file --- a/XMPFiles/source/FileHandlers/SWF_Handler.cpp +++ b/XMPFiles/source/FileHandlers/SWF_Handler.cpp @@ -139,6 +139,7 @@ void SWF_MetaHandler::CacheFileData() { // Look for the FileAttributes and Metadata tags. + if(this->expandedSize <= SWF_IO::HeaderPrefixSize ) return; // Throw? this->firstTagOffset = SWF_IO::FileHeaderSize ( this->expandedSWF[SWF_IO::HeaderPrefixSize] ); XMP_Uns32 currOffset = this->firstTagOffset; @@ -235,6 +236,9 @@ void SWF_MetaHandler::UpdateFile ( bool PutUns16LE ( ((SWF_IO::FileAttributesTagID << 6) | 4), &buffer[0] ); PutUns32LE ( SWF_IO::HasMetadataMask, &buffer[2] ); + if(this->expandedSWF.size() < this->firstTagOffset ){ + XMP_Throw ( "Index not valid.Invalid SWF, can't update.", kXMPErr_BadIndex ); + } this->expandedSWF.insert ( (this->expandedSWF.begin() + this->firstTagOffset), 6, 0 ); memcpy ( &this->expandedSWF[this->firstTagOffset], &buffer[0], 6 ); @@ -271,6 +275,9 @@ void SWF_MetaHandler::UpdateFile ( bool this->metadataTag.tagOffset += attrTagLength; // The FileAttributes tag will become in front. } + if(this->expandedSWF.size() < this->firstTagOffset ){ + XMP_Throw ( "Index not valid.Invalid SWF, can't update.", kXMPErr_BadIndex ); + } this->expandedSWF.insert ( (this->expandedSWF.begin() + this->firstTagOffset), attrTagLength, 0 ); memcpy ( &this->expandedSWF[this->firstTagOffset], &attrTag[0], attrTagLength ); @@ -301,6 +308,9 @@ void SWF_MetaHandler::UpdateFile ( bool this->metadataTag.contentLength = this->xmpPacket.size(); XMP_Uns32 newMetaLength = 6 + this->metadataTag.contentLength; // Always use a long tag header. + if(this->expandedSWF.size() < this->metadataTag.tagOffset ){ + XMP_Throw ( "Index not valid.Invalid SWF, can't update.", kXMPErr_BadIndex ); + } this->expandedSWF.insert ( (this->expandedSWF.begin() + this->metadataTag.tagOffset), newMetaLength, 0 ); PutUns16LE ( ((SWF_IO::MetadataTagID << 6) | SWF_IO::TagLengthMask), &this->expandedSWF[this->metadataTag.tagOffset] ); --- a/XMPFiles/source/FileHandlers/UCF_Handler.cpp +++ b/XMPFiles/source/FileHandlers/UCF_Handler.cpp @@ -486,6 +486,9 @@ void UCF_MetaHandler::CacheFileData() } have = CHUNK - strm.avail_out; + if ((bytesWritten + have) > sizeUncompressed){ + XMP_Throw("UCF Bad XMP block", kXMPErr_BadBlockFormat); + } memcpy( (unsigned char*) packetStr + bytesWritten , out , have ); bytesWritten += have; @@ -547,7 +550,6 @@ void UCF_MetaHandler::UpdateFile ( bool uncomprPacketLen = (XMP_StringLen) xmpPacket.size(); finalPacketStr = uncomprPacketStr; // will be overriden if compressedXMP==true finalPacketLen = uncomprPacketLen; - std::string compressedPacket; // moot if non-compressed, still here for scope reasons (having to keep a .c_str() alive) if ( !x ) // if new XMP... { @@ -584,6 +586,9 @@ void UCF_MetaHandler::UpdateFile ( bool unsigned int have; z_stream strm; unsigned char out[CHUNK]; + /* initilalisation for fix to CTECHXMP-4170441*/ + strm.total_out = 0; + strm.total_in = 0; /* allocate deflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; --- a/XMPFiles/source/FileHandlers/UCF_Handler.hpp +++ b/XMPFiles/source/FileHandlers/UCF_Handler.hpp @@ -710,6 +710,7 @@ private: XMP_StringLen uncomprPacketLen; XMP_StringPtr finalPacketStr; XMP_StringLen finalPacketLen; + std::string compressedPacket; std::vector cdEntries; EndOfCD endOfCD; void writeOut( XMP_IO* sourceFile, XMP_IO* targetFile, bool isRewrite, bool isInPlace); --- a/XMPFiles/source/FormatSupport/ASF_Support.cpp +++ b/XMPFiles/source/FormatSupport/ASF_Support.cpp @@ -145,10 +145,11 @@ bool ASF_Support::ReadHeaderObject ( XMP pos += bufferSize; // read contained header objects - /*XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] );*/ + XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] ); ASF_ObjectBase objectBase; - while ( read < newObject.len ) { + while (read < newObject.len && numberOfHeaders > 0) + { fileRef->Seek ( pos, kXMP_SeekFromStart ); if ( kASF_ObjectBaseLen != fileRef->Read ( &objectBase, kASF_ObjectBaseLen, true ) ) break; @@ -226,7 +227,7 @@ bool ASF_Support::ReadHeaderObject ( XMP XMP_Uns32 fieldPos = 28; // copyright URL is 3. element with variable size - for ( int i = 1; i <= 3 ; ++i ) { + for ( int i = 1; i <= 3 && fieldPos < buffer.size() ; ++i ) { XMP_Uns32 len = GetUns32LE ( &buffer[fieldPos] ); if ( i == 3 ) { std::string copyrightURLStr = buffer.substr ( fieldPos + 4, len ); @@ -276,8 +277,8 @@ bool ASF_Support::ReadHeaderObject ( XMP pos += objectBase.size; read += objectBase.size; + numberOfHeaders--; } - } catch ( ... ) { return false; @@ -316,7 +317,7 @@ bool ASF_Support::WriteHeaderObject ( XM pos += bufferSize; // read contained header objects - /*XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] );*/ + XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] ); ASF_ObjectBase objectBase; // prepare new header in memory @@ -328,7 +329,7 @@ bool ASF_Support::WriteHeaderObject ( XM header.append ( buffer.c_str(), bufferSize ); - while ( read < object.len ) { + while ( read < object.len && numberOfHeaders > 0 ) { sourceRef->Seek ( pos, kXMP_SeekFromStart ); if ( kASF_ObjectBaseLen != sourceRef->Read ( &objectBase, kASF_ObjectBaseLen, true ) ) break; @@ -503,7 +504,7 @@ bool ASF_Support::WriteHeaderObject ( XM pos += objectBase.size; read += objectBase.size; - + numberOfHeaders--; writtenObjects ++; } --- a/XMPFiles/source/FormatSupport/ID3_Support.cpp +++ b/XMPFiles/source/FormatSupport/ID3_Support.cpp @@ -462,7 +462,7 @@ ID3v2Frame::ID3v2Frame ( XMP_Uns32 id_ ) void ID3v2Frame::release() { - if ( this->content != 0 ) delete [] this->content; + if ( this->content != 0 ) delete [] content; this->content = 0; this->contentSize = 0; } --- a/XMPFiles/source/FormatSupport/P2_Support.cpp +++ b/XMPFiles/source/FormatSupport/P2_Support.cpp @@ -108,7 +108,10 @@ void P2_Clip::CacheClipContent() { if (headContentCached) return; headContentCached = true; - XMP_StringPtr p2NameSpace=GetP2RootNode()->ns.c_str(); + XML_NodePtr p2RootNode = GetP2RootNode(); + if( p2RootNode == 0 ) return; + XMP_StringPtr p2NameSpace = p2RootNode->ns.c_str(); + p2ClipContent = GetP2RootNode()->GetNamedElement ( p2NameSpace, "ClipContent" ); if ( p2ClipContent == 0 ) return; XML_NodePtr p2node; @@ -356,7 +359,9 @@ void P2_SpannedClip::CreateDigest ( std: digestStr->erase(); if ( this->headContent.clipMetadata == 0 ) return; // Bail if we don't have any legacy XML. - XMP_StringPtr p2NS = this->GetP2RootNode()->ns.c_str(); + XML_NodePtr p2RootNode = this->GetP2RootNode(); // Return if there is no root node. + if( p2RootNode == 0 ) return; + XMP_StringPtr p2NS = p2RootNode->ns.c_str(); XML_NodePtr legacyContext; MD5_CTX md5Context; unsigned char digestBin [16]; --- a/XMPFiles/source/FormatSupport/PostScript_Support.cpp +++ b/XMPFiles/source/FormatSupport/PostScript_Support.cpp @@ -1014,14 +1014,7 @@ std::string PostScript_Support::ConvertT if(itr!=tokenzs.end()) { ++itr; - if (itr == tokenzs.end()) - { - // bug 101914 - corrupt file make us - // reach the end. -- Hub - // https://bugs.freedesktop.org/show_bug.cgi?id=101914 - break; - } - if (itr->noOfDelimiter==0 && IsNumeric(itr->token[0]) ) + if (itrnoOfDelimiter==0 && IsNumeric(itr->token[0]) ) { const char * str=itr->token.c_str(); short day= GetNumber(&str); @@ -1030,6 +1023,10 @@ std::string PostScript_Support::ConvertT date.day=day; } } + else if (itr == tokenzs.end()) + { + break; + } } } } --- a/XMPFiles/source/FormatSupport/RIFF.cpp +++ b/XMPFiles/source/FormatSupport/RIFF.cpp @@ -185,6 +185,10 @@ Chunk::Chunk( ContainerChunk* parent_, R bool repairFile = XMP_OptionIsSet ( handler->parent->openFlags, kXMPFiles_OpenRepairFile ); if ( (! isUpdate) || (repairFile && (parent_ == 0)) ) { this->oldSize = chunkLimit - this->oldPos; + if (this->oldSize < 8) + { + XMP_Throw("Invalid RIFF chunk size", kXMPErr_BadFileFormat); + } } else { XMP_Throw ( "Bad RIFF chunk size", kXMPErr_BadFileFormat ); } --- a/XMPFiles/source/FormatSupport/ReconcileLegacy.hpp +++ b/XMPFiles/source/FormatSupport/ReconcileLegacy.hpp @@ -85,7 +85,7 @@ extern void ExportPhotoData ( XMP_FileFo // Here are the primary (0th) IFD tags that get special treatment: // // 270, 33432 - ASCII mapped to alt-text['x-default'] -// 306 - DateTime master +// 306 - DateTime main // 315 - ASCII mapped to text seq[1] // // Here are the primary (0th) IFD tags that get mapped by type and count: @@ -97,7 +97,7 @@ extern void ExportPhotoData ( XMP_FileFo // // 34856, 41484 - OECF/SFR table // 36864, 40960 - 4 ASCII chars to text -// 36867, 36868 - DateTime master +// 36867, 36868 - DateTime main // 37121 - 4 UInt8 to integer seq // 37385 - Flash struct // 37510 - explicitly encoded text to alt-text['x-default'] @@ -114,8 +114,8 @@ extern void ExportPhotoData ( XMP_FileFo // Here are the GPS IFD tags that get special treatment: // // 0 - 4 UInt8 to text "n.n.n.n" -// 2, 4, 20, 22 - Latitude or longitude master -// 7 - special DateTime master, the time part +// 2, 4, 20, 22 - Latitude or longitude main +// 7 - special DateTime main, the time part // 27, 28 - explicitly encoded text // // Here are the GPS IFD tags that get mapped by type and count: @@ -161,7 +161,7 @@ extern void ExportPhotoData ( XMP_FileFo // 296 SHORT 1 ResolutionUnit integer // 301 SHORT 3*256 TransferFunction integer seq // 305 ASCII Any Software text, xmp:CreatorTool -// 306 ASCII 20 DateTime date, master of 37520, xmp:DateTime +// 306 ASCII 20 DateTime date, main of 37520, xmp:DateTime // 315 ASCII Any Artist text, dc:creator[1] // 318 RATIONAL 2 WhitePoint rational seq // 319 RATIONAL 6 PrimaryChromaticities rational seq @@ -181,8 +181,8 @@ extern void ExportPhotoData ( XMP_FileFo // 34855 SHORT Any ISOSpeedRatings integer seq // 34856 UNDEFINED Any OECF OECF/SFR table // 36864 UNDEFINED 4 ExifVersion text, Exif has 4 ASCII chars -// 36867 ASCII 20 DateTimeOriginal date, master of 37521 -// 36868 ASCII 20 DateTimeDigitized date, master of 37522 +// 36867 ASCII 20 DateTimeOriginal date, main of 37521 +// 36868 ASCII 20 DateTimeDigitized date, main of 37522 // 37121 UNDEFINED 4 ComponentsConfiguration integer seq, Exif has 4 UInt8 // 37122 RATIONAL 1 CompressedBitsPerPixel rational // 37377 SRATIONAL 1 ShutterSpeedValue rational @@ -235,12 +235,12 @@ extern void ExportPhotoData ( XMP_FileFo // // 0 BYTE 4 GPSVersionID text, "n.n.n.n", Exif has 4 UInt8 // 1 ASCII 2 GPSLatitudeRef latitude, with 2 -// 2 RATIONAL 3 GPSLatitude latitude, master of 2 +// 2 RATIONAL 3 GPSLatitude latitude, main of 2 // 3 ASCII 2 GPSLongitudeRef longitude, with 4 -// 4 RATIONAL 3 GPSLongitude longitude, master of 3 +// 4 RATIONAL 3 GPSLongitude longitude, main of 3 // 5 BYTE 1 GPSAltitudeRef integer // 6 RATIONAL 1 GPSAltitude rational -// 7 RATIONAL 3 GPSTimeStamp date, master of 29 +// 7 RATIONAL 3 GPSTimeStamp date, main of 29 // 8 ASCII Any GPSSatellites text // 9 ASCII 2 GPSStatus text // 10 ASCII 2 GPSMeasureMode text @@ -253,9 +253,9 @@ extern void ExportPhotoData ( XMP_FileFo // 17 RATIONAL 1 GPSImgDirection rational // 18 ASCII Any GPSMapDatum text // 19 ASCII 2 GPSDestLatitudeRef latitude, with 20 -// 20 RATIONAL 3 GPSDestLatitude latitude, master of 19 +// 20 RATIONAL 3 GPSDestLatitude latitude, main of 19 // 21 ASCII 2 GPSDestLongitudeRef longitude, with 22 -// 22 RATIONAL 3 GPSDestLongitude logitude, master of 21 +// 22 RATIONAL 3 GPSDestLongitude logitude, main of 21 // 23 ASCII 2 GPSDestBearingRef text // 24 RATIONAL 1 GPSDestBearing rational // 25 ASCII 2 GPSDestDistanceRef text --- a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp +++ b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp @@ -116,6 +116,9 @@ static const TIFF_MappingToXMP sExifIFDM { /* 40964 */ kTIFF_RelatedSoundFile, kTIFF_ASCIIType, kAnyCount, kExport_Always, kXMP_NS_EXIF, "RelatedSoundFile" }, // ! Exif spec says count of 13. { /* 36867 */ kTIFF_DateTimeOriginal, kTIFF_ASCIIType, 20, kExport_Always, "", "" }, // ! Has a special mapping. { /* 36868 */ kTIFF_DateTimeDigitized, kTIFF_ASCIIType, 20, kExport_Always, "", "" }, // ! Has a special mapping. + { /* 36880 */ kTIFF_OffsetTime, kTIFF_ASCIIType, 7, kExport_Always, "", "" }, // ! Has a special mapping. + { /* 36881 */ kTIFF_OffsetTimeOriginal, kTIFF_ASCIIType, 7, kExport_Always, "", "" }, // ! Has a special mapping. + { /* 36882 */ kTIFF_OffsetTimeDigitized, kTIFF_ASCIIType, 7, kExport_Always, "", "" }, // ! Has a special mapping. { /* 42016 */ kTIFF_ImageUniqueID, kTIFF_ASCIIType, 33, kExport_InjectOnly, kXMP_NS_EXIF, "ImageUniqueID" }, { /* 42032 */ kTIFF_CameraOwnerName, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, kXMP_NS_ExifEX, "CameraOwnerName" }, { /* 42033 */ kTIFF_BodySerialNumber, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, kXMP_NS_ExifEX, "BodySerialNumber" }, @@ -1248,6 +1251,14 @@ ImportTIFF_StandardMappings ( XMP_Uns8 i bool found = tiff.GetTag ( ifd, mapInfo.id, &tagInfo ); if ( ! found ) continue; + /* tag length need to be checked in case of TIFF_MemoryReader, as a possible case + the data length value might be changed (flippig because of endianess) by next overlapping IFD leading to crash. + To avoid that, rechecking the datalen just before its access. Fixing CTECHXMP-4170409*/ + if(tiff.IsCheckTagLength() && + tagInfo.dataLen > tiff.GetTiffLength() - ((XMP_Uns8*)tagInfo.dataPtr - tiff.GetTiffStream())) { + continue; // Bad Tag + } + XMP_Assert ( tagInfo.type != kTIFF_UndefinedType ); // These must have a special mapping. if ( tagInfo.type == kTIFF_UndefinedType ) continue; @@ -1277,7 +1288,7 @@ ImportTIFF_StandardMappings ( XMP_Uns8 i // ImportTIFF_Date // =============== // -// Convert an Exif 2.2 master date/time tag plus associated fractional seconds to an XMP date/time. +// Convert an Exif 2.2 main date/time tag plus associated fractional seconds to an XMP date/time. // The Exif date/time part is a 20 byte ASCII value formatted as "YYYY:MM:DD HH:MM:SS" with a // terminating nul. Any of the numeric portions can be blanks if unknown. The fractional seconds // are a nul terminated ASCII string with possible space padding. They are literally the fractional @@ -1287,11 +1298,14 @@ static void ImportTIFF_Date ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & dateInfo, SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) { - XMP_Uns16 secID = 0; + XMP_Uns16 secID = 0, offsetID = 0; switch ( dateInfo.id ) { - case kTIFF_DateTime : secID = kTIFF_SubSecTime; break; - case kTIFF_DateTimeOriginal : secID = kTIFF_SubSecTimeOriginal; break; - case kTIFF_DateTimeDigitized : secID = kTIFF_SubSecTimeDigitized; break; + case kTIFF_DateTime : secID = kTIFF_SubSecTime; + offsetID = kTIFF_OffsetTime; break; + case kTIFF_DateTimeOriginal : secID = kTIFF_SubSecTimeOriginal; + offsetID = kTIFF_OffsetTimeOriginal; break; + case kTIFF_DateTimeDigitized : secID = kTIFF_SubSecTimeDigitized; + offsetID = kTIFF_OffsetTimeDigitized; break; } try { // Don't let errors with one stop the others. @@ -1332,7 +1346,31 @@ ImportTIFF_Date ( const TIFF_Manager & t for ( ; digits < 9; ++digits ) binValue.nanoSecond *= 10; if ( binValue.nanoSecond != 0 ) binValue.hasTime = true; } + // The offset time tags were added to EXIF spec 2.3.1., therefore we not + // supporting read/write in older versions + // We need EXIF spec version to figure out the same. + bool haveOldExif = true; // Default to old Exif if no version tag. + TIFF_Manager::TagInfo tagInfo; + bool foundExif = tiff.GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, &tagInfo ); + if ( foundExif && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) { + haveOldExif = (strncmp ( (char*)tagInfo.dataPtr, "0231", 4 ) < 0); + } + + if (!haveOldExif) + { + TIFF_Manager::TagInfo timezoneInfo; + found = tiff.GetTag ( kTIFF_ExifIFD, offsetID, &timezoneInfo ); + if ( found && (timezoneInfo.type == kTIFF_ASCIIType) && (timezoneInfo.count == 7) ) { + const char * timezoneStr = (const char *) timezoneInfo.dataPtr; + if ( (timezoneStr[0] == '+') || (timezoneStr[0] == '-') || (timezoneStr[3] == ':') ) { + binValue.tzSign = (timezoneStr[0] == '-') ? -1 : 1; + binValue.tzHour = GatherInt ( &timezoneStr[1], 2 ); + binValue.tzMinute = GatherInt ( &timezoneStr[4], 2 ); + binValue.hasTimeZone = true; + } + } + } xmp->SetProperty_Date ( xmpNS, xmpProp, binValue ); } catch ( ... ) { @@ -2205,13 +2243,13 @@ PhotoDataUtils::Import2WayExif ( const T xmp->SetProperty ( kXMP_NS_EXIF, "GPSVersionID", strOut ); } - // 2 GPSLatitude is a GPS coordinate master. + // 2 GPSLatitude is a GPS coordinate main. found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSLatitude, &tagInfo ); if ( found ) { ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSLatitude" ); } - // 4 GPSLongitude is a GPS coordinate master. + // 4 GPSLongitude is a GPS coordinate main. found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSLongitude, &tagInfo ); if ( found ) { ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSLongitude" ); @@ -2223,13 +2261,13 @@ PhotoDataUtils::Import2WayExif ( const T ImportTIFF_GPSTimeStamp ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSTimeStamp" ); } - // 20 GPSDestLatitude is a GPS coordinate master. + // 20 GPSDestLatitude is a GPS coordinate main. found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude, &tagInfo ); if ( found ) { ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLatitude" ); } - // 22 GPSDestLongitude is a GPS coordinate master. + // 22 GPSDestLongitude is a GPS coordinate main. found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude, &tagInfo ); if ( found ) { ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLongitude" ); @@ -2665,7 +2703,7 @@ ExportTIFF_StandardMappings ( XMP_Uns8 i // ExportTIFF_Date // =============== // -// Convert an XMP date/time to an Exif 2.2 master date/time tag plus associated fractional seconds. +// Convert an XMP date/time to an Exif 2.2 main date/time tag plus associated fractional seconds. // The Exif date/time part is a 20 byte ASCII value formatted as "YYYY:MM:DD HH:MM:SS" with a // terminating nul. The fractional seconds are a nul terminated ASCII string with possible space // padding. They are literally the fractional part, the digits that would be to the right of the @@ -2676,10 +2714,18 @@ ExportTIFF_Date ( const SXMPMeta & xmp, { XMP_Uns8 mainIFD = kTIFF_ExifIFD; XMP_Uns16 fracID=0; + XMP_Uns16 offsetID=0; switch ( mainID ) { - case kTIFF_DateTime : mainIFD = kTIFF_PrimaryIFD; fracID = kTIFF_SubSecTime; break; - case kTIFF_DateTimeOriginal : fracID = kTIFF_SubSecTimeOriginal; break; - case kTIFF_DateTimeDigitized : fracID = kTIFF_SubSecTimeDigitized; break; + case kTIFF_DateTime : mainIFD = kTIFF_PrimaryIFD; + fracID = kTIFF_SubSecTime; + offsetID = kTIFF_OffsetTime; + break; + case kTIFF_DateTimeOriginal : fracID = kTIFF_SubSecTimeOriginal; + offsetID = kTIFF_OffsetTimeOriginal; + break; + case kTIFF_DateTimeDigitized : fracID = kTIFF_SubSecTimeDigitized; + offsetID = kTIFF_OffsetTimeDigitized; + break; } try { // Don't let errors with one stop the others. @@ -2689,6 +2735,7 @@ ExportTIFF_Date ( const SXMPMeta & xmp, if ( ! foundXMP ) { tiff->DeleteTag ( mainIFD, mainID ); tiff->DeleteTag ( kTIFF_ExifIFD, fracID ); // ! The subseconds are always in the Exif IFD. + tiff->DeleteTag ( kTIFF_ExifIFD, offsetID );// ! The offsetTime are always in the Exif IFD. return; } @@ -2733,23 +2780,53 @@ ExportTIFF_Date ( const SXMPMeta & xmp, if ( xmpBin.nanoSecond == 0 ) { tiff->DeleteTag ( kTIFF_ExifIFD, fracID ); - - } else { - - snprintf ( buffer, sizeof(buffer), "%09d", xmpBin.nanoSecond ); // AUDIT: Use of sizeof(buffer) is safe. - for ( size_t i = strlen(buffer)-1; i > 0; --i ) { - if ( buffer[i] != '0' ) break; - buffer[i] = 0; // Strip trailing zero digits. + } + else + { + snprintf(buffer, sizeof(buffer), "%09d", xmpBin.nanoSecond); // AUDIT: Use of sizeof(buffer) is safe. + for (size_t i = strlen(buffer) - 1; i > 0; --i) + { + if (buffer[i] != '0') + break; + buffer[i] = 0; // Strip trailing zero digits. } + tiff->SetTag_ASCII(kTIFF_ExifIFD, fracID, buffer); // ! The subseconds are always in the Exif IFD. + } - tiff->SetTag_ASCII ( kTIFF_ExifIFD, fracID, buffer ); // ! The subseconds are always in the Exif IFD. + bool haveOldExif = true; // Default to old Exif if no version tag. + TIFF_Manager::TagInfo tagInfo; + bool foundExif = tiff->GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, &tagInfo ); + if ( foundExif && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) { + haveOldExif = (strncmp ( (char*)tagInfo.dataPtr, "0231", 4 ) < 0); + } + if (!haveOldExif) + { + // The offset time tags were added to EXIF spec 2.3.1., therefore we are not + // supporting read/write in older versions + // We need EXIF spec version to figure out the same. + + if ( xmpBin.hasTimeZone == 0 || (xmpBin.tzSign != -1 && xmpBin.tzSign != 1) ){ + + tiff->DeleteTag ( kTIFF_ExifIFD, offsetID ); + + }else + { + char tzSign = '+'; + if (xmpBin.tzSign == -1) + tzSign = '-'; + + char offsetBuffer[7]; + snprintf(offsetBuffer, sizeof(offsetBuffer), "%c%02d:%02d", // AUDIT: Use of sizeof(offsetBuffer) is safe. + tzSign, xmpBin.tzHour, xmpBin.tzMinute); + tiff->SetTag_ASCII(kTIFF_ExifIFD, offsetID, offsetBuffer); // ! The OffsetTime are always in the Exif IFD. + } } } catch ( ... ) { // Do nothing, let other exports proceed. // ? Notify client? - } + } } // ExportTIFF_Date --- a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp +++ b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp @@ -971,17 +971,17 @@ void TIFF_FileWriter::IntegrateFromPShop } // TIFF_FileWriter::IntegrateFromPShop6 // ================================================================================================= -// TIFF_FileWriter::CopyTagToMasterIFD +// TIFF_FileWriter::CopyTagToMainIFD // =================================== // -// Create a new master IFD entry from a buried Photoshop 6 IFD entry. Don't try to get clever with +// Create a new main IFD entry from a buried Photoshop 6 IFD entry. Don't try to get clever with // large values, just create a new copy. This preserves a clean separation between the memory-based // and file-based TIFF processing. -void* TIFF_FileWriter::CopyTagToMasterIFD ( const TagInfo & ps6Tag, InternalIFDInfo * masterIFD ) +void* TIFF_FileWriter::CopyTagToMainIFD ( const TagInfo & ps6Tag, InternalIFDInfo * mainIFD ) { InternalTagMap::value_type mapValue ( ps6Tag.id, InternalTagInfo ( ps6Tag.id, ps6Tag.type, ps6Tag.count, this->fileParsed ) ); - InternalTagMap::iterator newPos = masterIFD->tagMap.insert ( masterIFD->tagMap.end(), mapValue ); + InternalTagMap::iterator newPos = mainIFD->tagMap.insert ( mainIFD->tagMap.end(), mapValue ); InternalTagInfo& newTag = newPos->second; newTag.dataLen = ps6Tag.dataLen; @@ -998,11 +998,11 @@ void* TIFF_FileWriter::CopyTagToMasterIF newTag.changed = true; // ! See comments with ProcessPShop6IFD. XMP_Assert ( (newTag.origDataLen == 0) && (newTag.origDataOffset == 0) ); - masterIFD->changed = true; + mainIFD->changed = true; return newPos->second.dataPtr; // ! Return the address within the map entry for small values. -} // TIFF_FileWriter::CopyTagToMasterIFD +} // TIFF_FileWriter::CopyTagToMainIFD // ================================================================================================= // FlipCFATable @@ -1016,10 +1016,10 @@ static bool FlipCFATable ( void* voidPtr XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; - Flip2 ( &u16Ptr[0] ); // Flip the counts to match the master TIFF. + Flip2 ( &u16Ptr[0] ); // Flip the counts to match the main TIFF. Flip2 ( &u16Ptr[1] ); - XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the master TIFF's routine. + XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the main TIFF's routine. XMP_Uns16 rows = GetUns16 ( &u16Ptr[1] ); if ( tagLen != (XMP_Uns32)(4 + columns*rows) ) return false; @@ -1061,10 +1061,10 @@ static bool FlipOECFSFRTable ( void* voi { XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; - Flip2 ( &u16Ptr[0] ); // Flip the data to match the master TIFF. + Flip2 ( &u16Ptr[0] ); // Flip the data to match the main TIFF. Flip2 ( &u16Ptr[1] ); - XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the master TIFF's routine. + XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the main TIFF's routine. XMP_Uns16 rows = GetUns16 ( &u16Ptr[1] ); XMP_Uns32 minLen = 4 + columns + (8 * columns * rows); // Minimum legit tag size. @@ -1111,25 +1111,25 @@ void TIFF_FileWriter::ProcessPShop6IFD ( bool needsFlipping = (this->bigEndian != buriedExif.IsBigEndian()); - InternalIFDInfo* masterIFD = &this->containedIFDs[ifd]; + InternalIFDInfo* mainIFD = &this->containedIFDs[ifd]; TagInfoMap::const_iterator ps6Pos = ps6IFD.begin(); TagInfoMap::const_iterator ps6End = ps6IFD.end(); for ( ; ps6Pos != ps6End; ++ps6Pos ) { - // Copy buried tags to the master IFD if they don't already exist there. + // Copy buried tags to the main IFD if they don't already exist there. const TagInfo& ps6Tag = ps6Pos->second; - if ( this->FindTagInIFD ( ifd, ps6Tag.id ) != 0 ) continue; // Keep existing master tags. + if ( this->FindTagInIFD ( ifd, ps6Tag.id ) != 0 ) continue; // Keep existing main tags. if ( needsFlipping && (ps6Tag.id == 37500) ) continue; // Don't copy an unflipped MakerNote. if ( (ps6Tag.id == kTIFF_ExifIFDPointer) || // Skip the tags that are explicit offsets. (ps6Tag.id == kTIFF_GPSInfoIFDPointer) || (ps6Tag.id == kTIFF_JPEGInterchangeFormat) || (ps6Tag.id == kTIFF_InteroperabilityIFDPointer) ) continue; - void* voidPtr = this->CopyTagToMasterIFD ( ps6Tag, masterIFD ); + void* voidPtr = this->CopyTagToMainIFD ( ps6Tag, mainIFD ); if ( needsFlipping ) { switch ( ps6Tag.type ) { @@ -1509,8 +1509,9 @@ void TIFF_FileWriter::UpdateMemByAppend if ( (appendAll | currTag.changed) && (currTag.dataLen > 4) ) { XMP_Uns32 valueOffset = this->GetUns32 ( &currTag.smallValue ); + bool inplaceUpdate = (currTag.dataLen <= currTag.origDataLen) && (! appendAll); - if ( (currTag.dataLen <= currTag.origDataLen) && (! appendAll) ) { + if ( inplaceUpdate ) { XMP_Assert ( valueOffset == currTag.origDataOffset ); } else { XMP_Assert ( valueOffset == appendedOffset ); @@ -1520,7 +1521,10 @@ void TIFF_FileWriter::UpdateMemByAppend XMP_Assert ( valueOffset <= newLength ); // Provably true, valueOffset is in the old span, newLength is the new bigger span. if ( currTag.dataLen > (newLength - valueOffset) ) XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure ); memcpy ( (newStream + valueOffset), currTag.dataPtr, currTag.dataLen ); // AUDIT: Protected by the above check. - if ( (currTag.dataLen & 1) != 0 ) newStream[valueOffset+currTag.dataLen] = 0; + if ( !inplaceUpdate && ((currTag.dataLen & 1) != 0) ) { + newStream[valueOffset+currTag.dataLen] = 0; + + } } --- a/XMPFiles/source/FormatSupport/TIFF_Support.hpp +++ b/XMPFiles/source/FormatSupport/TIFF_Support.hpp @@ -97,9 +97,9 @@ enum { // Constants for the type field o static const size_t kTIFF_TypeSizes[] = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4 }; -static const bool kTIFF_IsIntegerType[] = { 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0 }; -static const bool kTIFF_IsRationalType[] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 }; -static const bool kTIFF_IsFloatType[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; +static const bool kTIFF_IsIntegerType[] = { 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0 ,0 }; +static const bool kTIFF_IsRationalType[] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 ,0 }; +static const bool kTIFF_IsFloatType[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 ,0 }; enum { // Encodings for SetTag_EncodedString. kTIFF_EncodeUndefined = 0, @@ -187,6 +187,9 @@ enum { kTIFF_RelatedSoundFile = 40964, kTIFF_DateTimeOriginal = 36867, kTIFF_DateTimeDigitized = 36868, + kTIFF_OffsetTime = 36880, + kTIFF_OffsetTimeOriginal = 36881, + kTIFF_OffsetTimeDigitized = 36882, kTIFF_SubSecTime = 37520, kTIFF_SubSecTimeOriginal = 37521, kTIFF_SubSecTimeDigitized = 37522, @@ -360,6 +363,9 @@ static const XMP_Uns16 sKnownExifIFDTags kTIFF_ExifVersion, // 36864 kTIFF_DateTimeOriginal, // 36867 kTIFF_DateTimeDigitized, // 36868 + kTIFF_OffsetTime, // 36880 + kTIFF_OffsetTimeOriginal, // 36881 + kTIFF_OffsetTimeDigitized, // 36882 kTIFF_ComponentsConfiguration, // 37121 kTIFF_CompressedBitsPerPixel, // 37122 kTIFF_ShutterSpeedValue, // 37377 @@ -512,6 +518,8 @@ public: bool IsBigEndian() const { return this->bigEndian; }; bool IsLittleEndian() const { return (! this->bigEndian); }; bool IsNativeEndian() const { return this->nativeEndian; }; + bool IsCheckTagLength () const { return this->checkTagLength; } + // --------------------------------------------------------------------------------------------- // The TIFF_Manager only keeps explicit knowledge of up to 4 IFDs: @@ -557,6 +565,9 @@ public: // new tag will have type short or long. virtual bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0; + virtual XMP_Uns32 GetTiffLength() const = 0; + + virtual XMP_Uns8 *GetTiffStream() const = 0; void SetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data ); @@ -671,6 +682,7 @@ public: protected: bool bigEndian, nativeEndian; + bool checkTagLength { false }; XMP_Uns32 CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length ); // The pointer is to a buffer of the first 8 bytes. The length is the overall length, used @@ -732,6 +744,9 @@ public: bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const; bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const; + XMP_Uns32 GetTiffLength() const { return tiffLength; } + + XMP_Uns8 *GetTiffStream() const { return tiffStream; } void SetTag_EncodedString ( XMP_Uns8 /*ifd*/, XMP_Uns16 /*id*/, const std::string& /*utf8Str*/, XMP_Uns8 /*encoding*/ ) { NotAppropriate(); }; @@ -747,7 +762,9 @@ public: XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) { IgnoreParam(condenseStream); if ( dataPtr != 0 ) *dataPtr = tiffStream; return tiffLength; }; void UpdateFileStream ( XMP_IO* /*fileRef*/, XMP_ProgressTracker* /*progressTracker*/ ) { NotAppropriate(); }; - TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) {}; + TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) { + checkTagLength = true; + }; virtual ~TIFF_MemoryReader() { if ( this->ownedStream ) free ( this->tiffStream ); }; @@ -847,6 +864,9 @@ public: bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const; bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const; + XMP_Uns32 GetTiffLength() const { return tiffLength; } + + XMP_Uns8 *GetTiffStream() const { return memStream; } void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ); @@ -966,7 +986,7 @@ private: void ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd ); - void* CopyTagToMasterIFD ( const TagInfo& ps6Tag, InternalIFDInfo* masterIFD ); + void* CopyTagToMainIFD ( const TagInfo& ps6Tag, InternalIFDInfo* mainIFD ); void PreflightIFDLinkage(); --- a/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp +++ b/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp @@ -23,8 +23,9 @@ XMPFileHandler( _parent ), mObject( obje FileHandlerInstance::~FileHandlerInstance() { WXMP_Error error; - mHandler->getModule()->getPluginAPIs()->mTerminateSessionProc( this->mObject, &error ); - PluginManager::removeHandlerInstance( this->mObject ); + + PluginManager::removeHandlerInstance(this->mObject); + mHandler->getModule()->getPluginAPIs()->mTerminateSessionProc(this->mObject, &error); CheckError( error ); } --- a/XMPFiles/source/WXMPFiles.cpp +++ b/XMPFiles/source/WXMPFiles.cpp @@ -321,12 +321,15 @@ void WXMPFiles_GetXMP_1 ( XMPFilesRef StartPerfCheck ( kAPIPerf_GetXMP, "" ); bool hasXMP = false; - XMP_StringPtr packetStr; - XMP_StringLen packetLen; - - if ( xmpRef == 0 ) { + XMP_StringPtr packetStr = NULL; + XMP_StringLen packetLen = 0; + /*Adding check to handle case where a client might not send XMPMetaRef but still want xmp packet in return. eg. CTECHXMP-4170329*/ + if ( xmpRef == 0 && clientPacket != 0 ) { hasXMP = thiz->GetXMP ( 0, &packetStr, &packetLen, packetInfo ); - } else { + } else if ( xmpRef == 0 && clientPacket == 0 ) { + hasXMP = thiz->GetXMP( 0, 0, 0, packetInfo ); + } + else { SXMPMeta xmpObj ( xmpRef ); hasXMP = thiz->GetXMP ( &xmpObj, &packetStr, &packetLen, packetInfo ); } --- a/public/include/TXMPFiles.hpp +++ b/public/include/TXMPFiles.hpp @@ -541,7 +541,8 @@ public: /// /// If the file is opened for update (passing \c #kXMPFiles_OpenForUpdate), the disk file remains /// open until \c CloseFile() is called. The disk file is only updated once, when \c CloseFile() - /// is called, regardless of how many calls are made to \c PutXMP(). + /// is called, regardless of how many calls are made to \c PutXMP(). When in-place update is not possible + /// we might write into a temporary file and then swap for corruption/crash safety. /* Documenatation update for CTECHXMP-4170278*/ /// /// @param closeFlags Option flags for optional closing actions. This bit-flag constant is /// defined: