mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 03:14:59 -06:00
521 lines
15 KiB
C++
Executable File
521 lines
15 KiB
C++
Executable File
// Archive/RarIn.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "../../../../C/7zCrc.h"
|
|
|
|
#include "Common/StringConvert.h"
|
|
#include "Common/UTFConvert.h"
|
|
|
|
#include "../../Common/LimitedStreams.h"
|
|
#include "../../Common/StreamUtils.h"
|
|
|
|
#include "../Common/FindSignature.h"
|
|
|
|
#include "RarIn.h"
|
|
|
|
namespace NArchive {
|
|
namespace NRar {
|
|
|
|
void CInArchive::ThrowExceptionWithCode(
|
|
CInArchiveException::CCauseType cause)
|
|
{
|
|
throw CInArchiveException(cause);
|
|
}
|
|
|
|
HRESULT CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit)
|
|
{
|
|
try
|
|
{
|
|
Close();
|
|
HRESULT res = Open2(inStream, searchHeaderSizeLimit);
|
|
if (res == S_OK)
|
|
return res;
|
|
Close();
|
|
return res;
|
|
}
|
|
catch(...) { Close(); throw; }
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
|
|
{
|
|
RINOK(FindSignatureInStream(stream,
|
|
NHeader::kMarker, NHeader::kMarkerSize,
|
|
searchHeaderSizeLimit, m_ArchiveStartPosition));
|
|
m_Stream = stream;
|
|
m_Position = m_ArchiveStartPosition + NHeader::kMarkerSize;
|
|
return m_Stream->Seek(m_Position, STREAM_SEEK_SET, NULL);
|
|
}
|
|
|
|
void CInArchive::ThrowUnexpectedEndOfArchiveException()
|
|
{
|
|
ThrowExceptionWithCode(CInArchiveException::kUnexpectedEndOfArchive);
|
|
}
|
|
|
|
bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size)
|
|
{
|
|
if (m_CryptoMode)
|
|
{
|
|
const Byte *bufData = m_DecryptedDataAligned;
|
|
UInt32 bufSize = m_DecryptedDataSize;
|
|
UInt32 i;
|
|
for (i = 0; i < size && m_CryptoPos < bufSize; i++)
|
|
((Byte *)data)[i] = bufData[m_CryptoPos++];
|
|
return (i == size);
|
|
}
|
|
return (ReadStream_FALSE(m_Stream, data, size) == S_OK);
|
|
}
|
|
|
|
void CInArchive::ReadBytesAndTestResult(void *data, UInt32 size)
|
|
{
|
|
if(!ReadBytesAndTestSize(data,size))
|
|
ThrowUnexpectedEndOfArchiveException();
|
|
}
|
|
|
|
HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize)
|
|
{
|
|
size_t realProcessedSize = size;
|
|
HRESULT result = ReadStream(m_Stream, data, &realProcessedSize);
|
|
if (processedSize != NULL)
|
|
*processedSize = (UInt32)realProcessedSize;
|
|
AddToSeekValue(realProcessedSize);
|
|
return result;
|
|
}
|
|
|
|
static UInt32 CrcUpdateUInt16(UInt32 crc, UInt16 v)
|
|
{
|
|
crc = CRC_UPDATE_BYTE(crc, (Byte)(v & 0xFF));
|
|
crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 8) & 0xFF));
|
|
return crc;
|
|
}
|
|
|
|
static UInt32 CrcUpdateUInt32(UInt32 crc, UInt32 v)
|
|
{
|
|
crc = CRC_UPDATE_BYTE(crc, (Byte)(v & 0xFF));
|
|
crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 8) & 0xFF));
|
|
crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 16) & 0xFF));
|
|
crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 24) & 0xFF));
|
|
return crc;
|
|
}
|
|
|
|
|
|
HRESULT CInArchive::Open2(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
|
|
{
|
|
m_CryptoMode = false;
|
|
RINOK(stream->Seek(0, STREAM_SEEK_SET, &m_StreamStartPosition));
|
|
m_Position = m_StreamStartPosition;
|
|
|
|
RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit));
|
|
|
|
Byte buf[NHeader::NArchive::kArchiveHeaderSize];
|
|
UInt32 processedSize;
|
|
ReadBytes(buf, sizeof(buf), &processedSize);
|
|
if (processedSize != sizeof(buf))
|
|
return S_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;
|
|
|
|
UInt32 crc = CRC_INIT_VAL;
|
|
crc = CRC_UPDATE_BYTE(crc, m_ArchiveHeader.Type);
|
|
crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Flags);
|
|
crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Size);
|
|
crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Reserved1);
|
|
crc = CrcUpdateUInt32(crc, m_ArchiveHeader.Reserved2);
|
|
|
|
if (m_ArchiveHeader.IsThereEncryptVer() && m_ArchiveHeader.Size > NHeader::NArchive::kArchiveHeaderSize)
|
|
{
|
|
ReadBytes(&m_ArchiveHeader.EncryptVersion, 1, &processedSize);
|
|
if (processedSize != 1)
|
|
return S_FALSE;
|
|
crc = CRC_UPDATE_BYTE(crc, m_ArchiveHeader.EncryptVersion);
|
|
}
|
|
|
|
if(m_ArchiveHeader.CRC != (CRC_GET_DIGEST(crc) & 0xFFFF))
|
|
ThrowExceptionWithCode(CInArchiveException::kArchiveHeaderCRCError);
|
|
if (m_ArchiveHeader.Type != NHeader::NBlockType::kArchiveHeader)
|
|
return S_FALSE;
|
|
m_ArchiveCommentPosition = m_Position;
|
|
m_SeekOnArchiveComment = true;
|
|
return S_OK;
|
|
}
|
|
|
|
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.Size = ReadUInt32();
|
|
item.HostOS = ReadByte();
|
|
item.FileCRC = ReadUInt32();
|
|
item.MTime.DosTime = ReadUInt32();
|
|
item.UnPackVersion = ReadByte();
|
|
item.Method = ReadByte();
|
|
int nameSize = ReadUInt16();
|
|
item.Attrib = ReadUInt32();
|
|
|
|
item.MTime.LowSecond = 0;
|
|
item.MTime.SubTime[0] =
|
|
item.MTime.SubTime[1] =
|
|
item.MTime.SubTime[2] = 0;
|
|
|
|
if((item.Flags & NHeader::NFile::kSize64Bits) != 0)
|
|
{
|
|
item.PackSize |= ((UInt64)ReadUInt32() << 32);
|
|
item.Size |= ((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.MTime);
|
|
item.CTimeDefined = ((createMask & 8) != 0);
|
|
if (item.CTimeDefined)
|
|
{
|
|
item.CTime.DosTime = ReadUInt32();
|
|
ReadTime(createMask, item.CTime);
|
|
}
|
|
item.ATimeDefined = ((accessMask & 8) != 0);
|
|
if (item.ATimeDefined)
|
|
{
|
|
item.ATime.DosTime = ReadUInt32();
|
|
ReadTime(accessMask, item.ATime);
|
|
}
|
|
}
|
|
|
|
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, bool &decryptionError)
|
|
{
|
|
decryptionError = false;
|
|
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 S_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)
|
|
{
|
|
const UInt32 kAlign = 16;
|
|
m_DecryptedData.SetCapacity(kDecryptedBufferSize + kAlign);
|
|
m_DecryptedDataAligned = (Byte *)((ptrdiff_t)((Byte *)m_DecryptedData + kAlign - 1) & ~(ptrdiff_t)(kAlign - 1));
|
|
}
|
|
RINOK(m_RarAES->Init());
|
|
size_t decryptedDataSizeT = kDecryptedBufferSize;
|
|
RINOK(ReadStream(m_Stream, m_DecryptedDataAligned, &decryptedDataSizeT));
|
|
m_DecryptedDataSize = (UInt32)decryptedDataSizeT;
|
|
m_DecryptedDataSize = m_RarAES->Filter(m_DecryptedDataAligned, 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 ((CrcCalc(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 << 10))
|
|
{
|
|
decryptionError = true;
|
|
return S_FALSE;
|
|
}
|
|
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))
|
|
{
|
|
decryptionError = true;
|
|
return S_FALSE;
|
|
}
|
|
m_CryptoPos = m_BlockHeader.HeadSize;
|
|
}
|
|
else
|
|
m_CryptoPos = 0;
|
|
AddToSeekValue(m_BlockHeader.HeadSize);
|
|
FinishCryptoBlock();
|
|
m_CryptoMode = false;
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
}}
|