diff options
Diffstat (limited to 'CPP/7zip/Archive/Rar/RarIn.cpp')
-rwxr-xr-x | CPP/7zip/Archive/Rar/RarIn.cpp | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Rar/RarIn.cpp b/CPP/7zip/Archive/Rar/RarIn.cpp new file mode 100755 index 00000000..9a88feb7 --- /dev/null +++ b/CPP/7zip/Archive/Rar/RarIn.cpp @@ -0,0 +1,535 @@ +// Archive/RarIn.cpp + +#include "StdAfx.h" + +#include "Common/StringConvert.h" +#include "Common/CRC.h" +#include "Common/UTFConvert.h" + +#include "RarIn.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/StreamUtils.h" + +namespace NArchive { +namespace NRar { + +static const char kEndOfString = '\0'; + +void CInArchive::ThrowExceptionWithCode( + CInArchiveException::CCauseType cause) +{ + throw CInArchiveException(cause); +} + +bool CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit) +{ + m_CryptoMode = false; + if(inStream->Seek(0, STREAM_SEEK_CUR, &m_StreamStartPosition) != S_OK) + return false; + m_Position = m_StreamStartPosition; + m_Stream = inStream; + if (ReadMarkerAndArchiveHeader(searchHeaderSizeLimit)) + return true; + m_Stream.Release(); + return false; +} + +void CInArchive::Close() +{ + m_Stream.Release(); +} + + +static inline bool TestMarkerCandidate(const void *aTestBytes) +{ + for (UInt32 i = 0; i < NHeader::kMarkerSize; i++) + if (((const Byte *)aTestBytes)[i] != NHeader::kMarker[i]) + return false; + return true; +} + +bool CInArchive::FindAndReadMarker(const UInt64 *searchHeaderSizeLimit) +{ + // if (m_Length < NHeader::kMarkerSize) + // return false; + m_ArchiveStartPosition = 0; + m_Position = m_StreamStartPosition; + if(m_Stream->Seek(m_StreamStartPosition, STREAM_SEEK_SET, NULL) != S_OK) + return false; + + Byte marker[NHeader::kMarkerSize]; + UInt32 processedSize; + ReadBytes(marker, NHeader::kMarkerSize, &processedSize); + if(processedSize != NHeader::kMarkerSize) + return false; + if (TestMarkerCandidate(marker)) + return true; + + CByteDynamicBuffer dynamicBuffer; + static const UInt32 kSearchMarkerBufferSize = 0x10000; + dynamicBuffer.EnsureCapacity(kSearchMarkerBufferSize); + Byte *buffer = dynamicBuffer; + UInt32 numBytesPrev = NHeader::kMarkerSize - 1; + memmove(buffer, marker + 1, numBytesPrev); + UInt64 curTestPos = m_StreamStartPosition + 1; + for (;;) + { + if (searchHeaderSizeLimit != NULL) + if (curTestPos - m_StreamStartPosition > *searchHeaderSizeLimit) + break; + UInt32 numReadBytes = kSearchMarkerBufferSize - numBytesPrev; + ReadBytes(buffer + numBytesPrev, numReadBytes, &processedSize); + UInt32 numBytesInBuffer = numBytesPrev + processedSize; + if (numBytesInBuffer < NHeader::kMarkerSize) + break; + UInt32 numTests = numBytesInBuffer - NHeader::kMarkerSize + 1; + for(UInt32 pos = 0; pos < numTests; pos++, curTestPos++) + { + if (TestMarkerCandidate(buffer + pos)) + { + m_ArchiveStartPosition = curTestPos; + m_Position = curTestPos + NHeader::kMarkerSize; + if(m_Stream->Seek(m_Position, STREAM_SEEK_SET, NULL) != S_OK) + return false; + return true; + } + } + numBytesPrev = numBytesInBuffer - numTests; + memmove(buffer, buffer + numTests, numBytesPrev); + } + return false; +} + +void CInArchive::ThrowUnexpectedEndOfArchiveException() +{ + ThrowExceptionWithCode(CInArchiveException::kUnexpectedEndOfArchive); +} + +bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size) +{ + if (m_CryptoMode) + { + const Byte *bufData = (const Byte *)m_DecryptedData; + UInt32 bufSize = m_DecryptedDataSize; + UInt32 i; + for (i = 0; i < size && m_CryptoPos < bufSize; i++) + ((Byte *)data)[i] = bufData[m_CryptoPos++]; + return (i == size); + } + UInt32 processedSize; + ReadStream(m_Stream, data, size, &processedSize); + return (processedSize == size); +} + +void CInArchive::ReadBytesAndTestResult(void *data, UInt32 size) +{ + if(!ReadBytesAndTestSize(data,size)) + ThrowUnexpectedEndOfArchiveException(); +} + +HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = ReadStream(m_Stream, data, size, &realProcessedSize); + if(processedSize != NULL) + *processedSize = realProcessedSize; + AddToSeekValue(realProcessedSize); + return result; +} + +bool CInArchive::ReadMarkerAndArchiveHeader(const UInt64 *searchHeaderSizeLimit) +{ + if (!FindAndReadMarker(searchHeaderSizeLimit)) + return false; + + Byte buf[NHeader::NArchive::kArchiveHeaderSize]; + UInt32 processedSize; + ReadBytes(buf, sizeof(buf), &processedSize); + if (processedSize != sizeof(buf)) + return false; + m_CurData = buf; + m_CurPos = 0; + m_PosLimit = sizeof(buf); + + m_ArchiveHeader.CRC = ReadUInt16(); + m_ArchiveHeader.Type = ReadByte(); + m_ArchiveHeader.Flags = ReadUInt16(); + m_ArchiveHeader.Size = ReadUInt16(); + m_ArchiveHeader.Reserved1 = ReadUInt16(); + m_ArchiveHeader.Reserved2 = ReadUInt32(); + m_ArchiveHeader.EncryptVersion = 0; + + CCRC crc; + crc.UpdateByte(m_ArchiveHeader.Type); + crc.UpdateUInt16(m_ArchiveHeader.Flags); + crc.UpdateUInt16(m_ArchiveHeader.Size); + crc.UpdateUInt16(m_ArchiveHeader.Reserved1); + crc.UpdateUInt32(m_ArchiveHeader.Reserved2); + + if (m_ArchiveHeader.IsThereEncryptVer() && m_ArchiveHeader.Size > NHeader::NArchive::kArchiveHeaderSize) + { + ReadBytes(&m_ArchiveHeader.EncryptVersion, 1, &processedSize); + if (processedSize != 1) + return false; + crc.UpdateByte(m_ArchiveHeader.EncryptVersion); + } + + if(m_ArchiveHeader.CRC != (crc.GetDigest() & 0xFFFF)) + ThrowExceptionWithCode(CInArchiveException::kArchiveHeaderCRCError); + if (m_ArchiveHeader.Type != NHeader::NBlockType::kArchiveHeader) + return false; + m_ArchiveCommentPosition = m_Position; + m_SeekOnArchiveComment = true; + return true; +} + +void CInArchive::SkipArchiveComment() +{ + if (!m_SeekOnArchiveComment) + return; + AddToSeekValue(m_ArchiveHeader.Size - m_ArchiveHeader.GetBaseSize()); + m_SeekOnArchiveComment = false; +} + +void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const +{ + archiveInfo.StartPosition = m_ArchiveStartPosition; + archiveInfo.Flags = m_ArchiveHeader.Flags; + archiveInfo.CommentPosition = m_ArchiveCommentPosition; + archiveInfo.CommentSize = (UInt16)(m_ArchiveHeader.Size - NHeader::NArchive::kArchiveHeaderSize); +} + +static void DecodeUnicodeFileName(const char *name, const Byte *encName, + int encSize, wchar_t *unicodeName, int maxDecSize) +{ + int encPos = 0; + int decPos = 0; + int flagBits = 0; + Byte flags = 0; + Byte highByte = encName[encPos++]; + while (encPos < encSize && decPos < maxDecSize) + { + if (flagBits == 0) + { + flags = encName[encPos++]; + flagBits = 8; + } + switch(flags >> 6) + { + case 0: + unicodeName[decPos++] = encName[encPos++]; + break; + case 1: + unicodeName[decPos++] = (wchar_t)(encName[encPos++] + (highByte << 8)); + break; + case 2: + unicodeName[decPos++] = (wchar_t)(encName[encPos] + (encName[encPos + 1] << 8)); + encPos += 2; + break; + case 3: + { + int length = encName[encPos++]; + if (length & 0x80) + { + Byte correction = encName[encPos++]; + for (length = (length & 0x7f) + 2; + length > 0 && decPos < maxDecSize; length--, decPos++) + unicodeName[decPos] = (wchar_t)(((name[decPos] + correction) & 0xff) + (highByte << 8)); + } + else + for (length += 2; length > 0 && decPos < maxDecSize; length--, decPos++) + unicodeName[decPos] = name[decPos]; + } + break; + } + flags <<= 2; + flagBits -= 2; + } + unicodeName[decPos < maxDecSize ? decPos : maxDecSize - 1] = 0; +} + +void CInArchive::ReadName(CItemEx &item, int nameSize) +{ + item.UnicodeName.Empty(); + if (nameSize > 0) + { + m_NameBuffer.EnsureCapacity(nameSize + 1); + char *buffer = (char *)m_NameBuffer; + + for (int i = 0; i < nameSize; i++) + buffer[i] = ReadByte(); + + int mainLen; + for (mainLen = 0; mainLen < nameSize; mainLen++) + if (buffer[mainLen] == '\0') + break; + buffer[mainLen] = '\0'; + item.Name = buffer; + + if(item.HasUnicodeName()) + { + if(mainLen < nameSize) + { + int unicodeNameSizeMax = MyMin(nameSize, (0x400)); + _unicodeNameBuffer.EnsureCapacity(unicodeNameSizeMax + 1); + DecodeUnicodeFileName(buffer, (const Byte *)buffer + mainLen + 1, + nameSize - (mainLen + 1), _unicodeNameBuffer, unicodeNameSizeMax); + item.UnicodeName = _unicodeNameBuffer; + } + else if (!ConvertUTF8ToUnicode(item.Name, item.UnicodeName)) + item.UnicodeName.Empty(); + } + } + else + item.Name.Empty(); +} + +Byte CInArchive::ReadByte() +{ + if (m_CurPos >= m_PosLimit) + throw CInArchiveException(CInArchiveException::kIncorrectArchive); + return m_CurData[m_CurPos++]; +} + +UInt16 CInArchive::ReadUInt16() +{ + UInt16 value = 0; + for (int i = 0; i < 2; i++) + { + Byte b = ReadByte(); + value |= (UInt16(b) << (8 * i)); + } + return value; +} + +UInt32 CInArchive::ReadUInt32() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + { + Byte b = ReadByte(); + value |= (UInt32(b) << (8 * i)); + } + return value; +} + +void CInArchive::ReadTime(Byte mask, CRarTime &rarTime) +{ + rarTime.LowSecond = (Byte)(((mask & 4) != 0) ? 1 : 0); + int numDigits = (mask & 3); + rarTime.SubTime[0] = rarTime.SubTime[1] = rarTime.SubTime[2] = 0; + for (int i = 0; i < numDigits; i++) + rarTime.SubTime[3 - numDigits + i] = ReadByte(); +} + +void CInArchive::ReadHeaderReal(CItemEx &item) +{ + item.Flags = m_BlockHeader.Flags; + item.PackSize = ReadUInt32(); + item.UnPackSize = ReadUInt32(); + item.HostOS = ReadByte(); + item.FileCRC = ReadUInt32(); + item.LastWriteTime.DosTime = ReadUInt32(); + item.UnPackVersion = ReadByte(); + item.Method = ReadByte(); + int nameSize = ReadUInt16(); + item.Attributes = ReadUInt32(); + + item.LastWriteTime.LowSecond = 0; + item.LastWriteTime.SubTime[0] = + item.LastWriteTime.SubTime[1] = + item.LastWriteTime.SubTime[2] = 0; + + if((item.Flags & NHeader::NFile::kSize64Bits) != 0) + { + item.PackSize |= ((UInt64)ReadUInt32() << 32); + item.UnPackSize |= ((UInt64)ReadUInt32() << 32); + } + + ReadName(item, nameSize); + + if (item.HasSalt()) + for (int i = 0; i < sizeof(item.Salt); i++) + item.Salt[i] = ReadByte(); + + // some rar archives have HasExtTime flag without field. + if (m_CurPos < m_PosLimit && item.HasExtTime()) + { + Byte accessMask = (Byte)(ReadByte() >> 4); + Byte b = ReadByte(); + Byte modifMask = (Byte)(b >> 4); + Byte createMask = (Byte)(b & 0xF); + if ((modifMask & 8) != 0) + ReadTime(modifMask, item.LastWriteTime); + item.IsCreationTimeDefined = ((createMask & 8) != 0); + if (item.IsCreationTimeDefined) + { + item.CreationTime.DosTime = ReadUInt32(); + ReadTime(createMask, item.CreationTime); + } + item.IsLastAccessTimeDefined = ((accessMask & 8) != 0); + if (item.IsLastAccessTimeDefined) + { + item.LastAccessTime.DosTime = ReadUInt32(); + ReadTime(accessMask, item.LastAccessTime); + } + } + + UInt16 fileHeaderWithNameSize = (UInt16)m_CurPos; + + item.Position = m_Position; + item.MainPartSize = fileHeaderWithNameSize; + item.CommentSize = (UInt16)(m_BlockHeader.HeadSize - fileHeaderWithNameSize); + + if (m_CryptoMode) + item.AlignSize = (UInt16)((16 - ((m_BlockHeader.HeadSize) & 0xF)) & 0xF); + else + item.AlignSize = 0; + AddToSeekValue(m_BlockHeader.HeadSize); +} + +void CInArchive::AddToSeekValue(UInt64 addValue) +{ + m_Position += addValue; +} + +HRESULT CInArchive::GetNextItem(CItemEx &item, ICryptoGetTextPassword *getTextPassword) +{ + if (m_SeekOnArchiveComment) + SkipArchiveComment(); + for (;;) + { + if(!SeekInArchive(m_Position)) + return S_FALSE; + if (!m_CryptoMode && (m_ArchiveHeader.Flags & + NHeader::NArchive::kBlockHeadersAreEncrypted) != 0) + { + m_CryptoMode = false; + if (getTextPassword == 0) + return S_FALSE; + if(!SeekInArchive(m_Position)) + return S_FALSE; + if (!m_RarAES) + { + m_RarAESSpec = new NCrypto::NRar29::CDecoder; + m_RarAES = m_RarAESSpec; + } + m_RarAESSpec->SetRar350Mode(m_ArchiveHeader.IsEncryptOld()); + + // Salt + const UInt32 kSaltSize = 8; + Byte salt[kSaltSize]; + if(!ReadBytesAndTestSize(salt, kSaltSize)) + return false; + m_Position += kSaltSize; + RINOK(m_RarAESSpec->SetDecoderProperties2(salt, kSaltSize)) + // Password + CMyComBSTR password; + RINOK(getTextPassword->CryptoGetTextPassword(&password)) + UString unicodePassword(password); + + CByteBuffer buffer; + const UInt32 sizeInBytes = unicodePassword.Length() * 2; + buffer.SetCapacity(sizeInBytes); + for (int i = 0; i < unicodePassword.Length(); i++) + { + wchar_t c = unicodePassword[i]; + ((Byte *)buffer)[i * 2] = (Byte)c; + ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); + } + + RINOK(m_RarAESSpec->CryptoSetPassword((const Byte *)buffer, sizeInBytes)); + + const UInt32 kDecryptedBufferSize = (1 << 12); + if (m_DecryptedData.GetCapacity() == 0) + { + m_DecryptedData.SetCapacity(kDecryptedBufferSize); + } + RINOK(m_RarAES->Init()); + RINOK(ReadStream(m_Stream, (Byte *)m_DecryptedData, kDecryptedBufferSize, &m_DecryptedDataSize)); + m_DecryptedDataSize = m_RarAES->Filter((Byte *)m_DecryptedData, m_DecryptedDataSize); + + m_CryptoMode = true; + m_CryptoPos = 0; + } + + m_FileHeaderData.EnsureCapacity(7); + if(!ReadBytesAndTestSize((Byte *)m_FileHeaderData, 7)) + return S_FALSE; + + m_CurData = (Byte *)m_FileHeaderData; + m_CurPos = 0; + m_PosLimit = 7; + m_BlockHeader.CRC = ReadUInt16(); + m_BlockHeader.Type = ReadByte(); + m_BlockHeader.Flags = ReadUInt16(); + m_BlockHeader.HeadSize = ReadUInt16(); + + if (m_BlockHeader.HeadSize < 7) + ThrowExceptionWithCode(CInArchiveException::kIncorrectArchive); + + if (m_BlockHeader.Type == NHeader::NBlockType::kEndOfArchive) + return S_FALSE; + + if (m_BlockHeader.Type == NHeader::NBlockType::kFileHeader) + { + m_FileHeaderData.EnsureCapacity(m_BlockHeader.HeadSize); + m_CurData = (Byte *)m_FileHeaderData; + m_PosLimit = m_BlockHeader.HeadSize; + ReadBytesAndTestResult(m_CurData + m_CurPos, m_BlockHeader.HeadSize - 7); + ReadHeaderReal(item); + if ((CCRC::CalculateDigest(m_CurData + 2, + m_BlockHeader.HeadSize - item.CommentSize - 2) & 0xFFFF) != m_BlockHeader.CRC) + ThrowExceptionWithCode(CInArchiveException::kFileHeaderCRCError); + + FinishCryptoBlock(); + m_CryptoMode = false; + SeekInArchive(m_Position); // Move Position to compressed Data; + AddToSeekValue(item.PackSize); // m_Position points to next header; + return S_OK; + } + if (m_CryptoMode && m_BlockHeader.HeadSize > (1 << 12)) + return E_FAIL; // it's for bad passwords + if ((m_BlockHeader.Flags & NHeader::NBlock::kLongBlock) != 0) + { + m_FileHeaderData.EnsureCapacity(7 + 4); + m_CurData = (Byte *)m_FileHeaderData; + ReadBytesAndTestResult(m_CurData + m_CurPos, 4); // test it + m_PosLimit = 7 + 4; + UInt32 dataSize = ReadUInt32(); + AddToSeekValue(dataSize); + if (m_CryptoMode && dataSize > (1 << 27)) + return E_FAIL; // it's for bad passwords + m_CryptoPos = m_BlockHeader.HeadSize; + } + else + m_CryptoPos = 0; + AddToSeekValue(m_BlockHeader.HeadSize); + FinishCryptoBlock(); + m_CryptoMode = false; + } +} + +void CInArchive::DirectGetBytes(void *data, UInt32 size) +{ + ReadStream(m_Stream, data, size, NULL); +} + +bool CInArchive::SeekInArchive(UInt64 position) +{ + UInt64 newPosition; + m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition); + return newPosition == position; +} + +ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size) +{ + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + SeekInArchive(position); + streamSpec->SetStream(m_Stream); + streamSpec->Init(size); + return inStream.Detach(); +} + +}} |