const inline void* GetDataPtr ( const TweakedIFDEntry* tifdEntry ) const { if ( GetUns32AsIs(&tifdEntry->bytes) <= 4 ) { return &tifdEntry->dataOrPos; } else { return (this->tiffStream + GetUns32AsIs(&tifdEntry->dataOrPos)); } }
bool TIFF_MemoryReader::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const { if ( ifd > kTIFF_LastRealIFD ) XMP_Throw ( "Invalid IFD requested", kXMPErr_InternalFailure ); const TweakedIFDInfo* thisIFD = &containedIFDs[ifd]; if ( ifdMap != 0 ) ifdMap->clear(); if ( thisIFD->count == 0 ) return false; if ( ifdMap != 0 ) { for ( size_t i = 0; i < thisIFD->count; ++i ) { TweakedIFDEntry* thisTag = &(thisIFD->entries[i]); if ( (thisTag->type < kTIFF_ByteType) || (thisTag->type > kTIFF_LastType) ) continue; // Bad type, skip this tag. TagInfo info ( thisTag->id, thisTag->type, 0, 0, GetUns32AsIs(&thisTag->bytes) ); info.count = info.dataLen / (XMP_Uns32)kTIFF_TypeSizes[info.type]; info.dataPtr = this->GetDataPtr ( thisTag ); (*ifdMap)[info.id] = info; } } return true; } // TIFF_MemoryReader::GetIFD
bool TIFF_MemoryReader::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const { const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); if ( thisTag == 0 ) return false; if ( thisTag->type > kTIFF_LastType ) return false; // Unknown type. if ( GetUns32AsIs(&thisTag->bytes) != kTIFF_TypeSizes[thisTag->type] ) return false; // Wrong count. XMP_Uns32 uns32; XMP_Int32 int32; switch ( thisTag->type ) { case kTIFF_ByteType: uns32 = GetUns8 ( this->GetDataPtr ( thisTag ) ); break; case kTIFF_ShortType: uns32 = this->GetUns16 ( this->GetDataPtr ( thisTag ) ); break; case kTIFF_LongType: uns32 = this->GetUns32 ( this->GetDataPtr ( thisTag ) ); break; case kTIFF_SByteType: int32 = (XMP_Int8) GetUns8 ( this->GetDataPtr ( thisTag ) ); uns32 = (XMP_Uns32) int32; // Make sure sign bits are properly set. break; case kTIFF_SShortType: int32 = (XMP_Int16) this->GetUns16 ( this->GetDataPtr ( thisTag ) ); uns32 = (XMP_Uns32) int32; // Make sure sign bits are properly set. break; case kTIFF_SLongType: int32 = (XMP_Int32) this->GetUns32 ( this->GetDataPtr ( thisTag ) ); uns32 = (XMP_Uns32) int32; // Make sure sign bits are properly set. break; default: return false; } if ( data != 0 ) *data = uns32; return true; } // TIFF_MemoryReader::GetTag_Integer
bool TIFF_MemoryReader::GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const { const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); if ( thisTag == 0 ) return false; XMP_Uns16 thisType = GetUns16AsIs ( &thisTag->type ); XMP_Uns32 thisBytes = GetUns32AsIs ( &thisTag->bytes ); if ( (thisType < kTIFF_ByteType) || (thisType > kTIFF_LastType) ) return false; // Bad type, skip this tag. if ( info != 0 ) { info->id = GetUns16AsIs ( &thisTag->id ); info->type = thisType; info->count = thisBytes / (XMP_Uns32)kTIFF_TypeSizes[thisType]; info->dataLen = thisBytes; info->dataPtr = this->GetDataPtr ( thisTag ); } return true; } // TIFF_MemoryReader::GetTag
XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ) { TweakedIFDInfo& ifdInfo = this->containedIFDs[ifd]; if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { XMP_Error error(kXMPErr_BadTIFF, "Bad IFD offset" ); this->NotifyClient ( kXMPErrSev_FileFatal, error ); } XMP_Uns8* ifdPtr = this->tiffStream + ifdOffset; XMP_Uns16 ifdCount = this->GetUns16 ( ifdPtr ); TweakedIFDEntry* ifdEntries = (TweakedIFDEntry*)(ifdPtr+2); if ( ifdCount >= 0x8000 ) { XMP_Error error(kXMPErr_BadTIFF, "Outrageous IFD count" ); this->NotifyClient ( kXMPErrSev_FileFatal, error ); } if ( (XMP_Uns32)(2 + ifdCount*12 + 4) > (this->tiffLength - ifdOffset) ) { XMP_Error error(kXMPErr_BadTIFF, "Out of bounds IFD" ); this->NotifyClient ( kXMPErrSev_FileFatal, error ); } ifdInfo.count = ifdCount; ifdInfo.entries = ifdEntries; XMP_Int32 prevTag = -1; // ! The GPS IFD has a tag 0, so we need a signed initial value. bool needsSorting = false; for ( size_t i = 0; i < ifdCount; ++i ) { TweakedIFDEntry* thisEntry = &ifdEntries[i]; // Tweak the IFD entry to be more useful. if ( ! this->nativeEndian ) { Flip2 ( &thisEntry->id ); Flip2 ( &thisEntry->type ); Flip4 ( &thisEntry->bytes ); } if ( GetUns16AsIs(&thisEntry->id) <= prevTag ) needsSorting = true; prevTag = GetUns16AsIs ( &thisEntry->id ); if ( (GetUns16AsIs(&thisEntry->type) < kTIFF_ByteType) || (GetUns16AsIs(&thisEntry->type) > kTIFF_LastType) ) continue; // Bad type, skip this tag. #if ! SUNOS_SPARC thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type]; if ( thisEntry->bytes > 4 ) { if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos ); if ( (thisEntry->dataOrPos < 8) || (thisEntry->dataOrPos >= this->tiffLength) ) { thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. } if ( thisEntry->bytes > (this->tiffLength - thisEntry->dataOrPos) ) { thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. } } #else void *tempEntryByte = &thisEntry->bytes; XMP_Uns32 temp = GetUns32AsIs(&thisEntry->bytes); temp = temp * (XMP_Uns32)kTIFF_TypeSizes[GetUns16AsIs(&thisEntry->type)]; memcpy ( tempEntryByte, &temp, sizeof(thisEntry->bytes) ); // thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type]; if ( GetUns32AsIs(&thisEntry->bytes) > 4 ) { void *tempEntryDataOrPos = &thisEntry->dataOrPos; if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos ); if ( (GetUns32AsIs(&thisEntry->dataOrPos) < 8) || (GetUns32AsIs(&thisEntry->dataOrPos) >= this->tiffLength) ) { // thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. memset ( tempEntryByte, 0, sizeof(XMP_Uns32) ); memset ( tempEntryDataOrPos, 0, sizeof(XMP_Uns32) ); } if ( GetUns32AsIs(&thisEntry->bytes) > (this->tiffLength - GetUns32AsIs(&thisEntry->dataOrPos)) ) { // thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. memset ( tempEntryByte, 0, sizeof(XMP_Uns32) ); memset ( tempEntryDataOrPos, 0, sizeof(XMP_Uns32) ); } } #endif } ifdPtr += (2 + ifdCount*12); XMP_Uns32 nextIFDOffset = this->GetUns32 ( ifdPtr ); if ( needsSorting ) SortIFD ( &ifdInfo ); // ! Don't perturb the ifdCount used to find the next IFD offset. return nextIFDOffset; } // TIFF_MemoryReader::ProcessOneIFD
void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData /* = true */ ) { // Get rid of any current TIFF. if ( this->ownedStream ) free ( this->tiffStream ); this->ownedStream = false; this->tiffStream = 0; this->tiffLength = 0; for ( size_t i = 0; i < kTIFF_KnownIFDCount; ++i ) { this->containedIFDs[i].count = 0; this->containedIFDs[i].entries = 0; } if ( length == 0 ) return; // Allocate space for the full in-memory stream and copy it. if ( ! copyData ) { XMP_Assert ( ! this->ownedStream ); this->tiffStream = (XMP_Uns8*) data; } else { if ( length > 100*1024*1024 ) XMP_Throw ( "Outrageous length for memory-based TIFF", kXMPErr_BadTIFF ); this->tiffStream = (XMP_Uns8*) malloc(length); if ( this->tiffStream == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( this->tiffStream, data, length ); // AUDIT: Safe, malloc'ed length bytes above. this->ownedStream = true; } this->tiffLength = length; XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset. // Find and process the primary, Exif, GPS, and Interoperability IFDs. XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->tiffStream, length ); XMP_Uns32 tnailIFDOffset = 0; if ( primaryIFDOffset != 0 ) tnailIFDOffset = this->ProcessOneIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); // ! Need the thumbnail IFD for checking full Exif APP1 in some JPEG files! if ( tnailIFDOffset != 0 ) { if ( IsOffsetValid(tnailIFDOffset, 8, ifdLimit ) ) { // Ignore a bad Thumbnail IFD offset. (void) this->ProcessOneIFD ( tnailIFDOffset, kTIFF_TNailIFD ); } else { XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); this->NotifyClient (kXMPErrSev_Recoverable, error ); } } const TweakedIFDEntry* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&exifIFDTag->bytes) == 4) ) { XMP_Uns32 exifOffset = this->GetUns32 ( &exifIFDTag->dataOrPos ); (void) this->ProcessOneIFD ( exifOffset, kTIFF_ExifIFD ); } const TweakedIFDEntry* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&gpsIFDTag->bytes) == 4) ) { XMP_Uns32 gpsOffset = this->GetUns32 ( &gpsIFDTag->dataOrPos ); if ( IsOffsetValid ( gpsOffset, 8, ifdLimit ) ) { // Ignore a bad GPS IFD offset. (void) this->ProcessOneIFD ( gpsOffset, kTIFF_GPSInfoIFD ); } else { XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); this->NotifyClient (kXMPErrSev_Recoverable, error ); } } const TweakedIFDEntry* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&interopIFDTag->bytes) == 4) ) { XMP_Uns32 interopOffset = this->GetUns32 ( &interopIFDTag->dataOrPos ); if ( IsOffsetValid ( interopOffset, 8, ifdLimit ) ) { // Ignore a bad Interoperability IFD offset. (void) this->ProcessOneIFD ( interopOffset, kTIFF_InteropIFD ); } else { XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); this->NotifyClient (kXMPErrSev_Recoverable, error ); } } } // TIFF_MemoryReader::ParseMemoryStream