Mercurial > vba-clojure
view src/win32/7zip/7z/CPP/7zip/Archive/Rar/RarIn.cpp @ 1:f9f4f1b99eed
importing src directory
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:31:27 -0600 |
parents | |
children |
line wrap: on
line source
1 // Archive/RarIn.cpp3 #include "StdAfx.h"5 #include "Common/StringConvert.h"6 #include "Common/UTFConvert.h"8 #include "RarIn.h"9 #include "../../Common/LimitedStreams.h"10 #include "../../Common/StreamUtils.h"12 #include "../Common/FindSignature.h"14 extern "C"15 {16 #include "../../../../C/7zCrc.h"17 }19 namespace NArchive {20 namespace NRar {22 void CInArchive::ThrowExceptionWithCode(23 CInArchiveException::CCauseType cause)24 {25 throw CInArchiveException(cause);26 }28 HRESULT CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit)29 {30 try31 {32 Close();33 HRESULT res = Open2(inStream, searchHeaderSizeLimit);34 if (res == S_OK)35 return res;36 Close();37 return res;38 }39 catch(...) { Close(); throw; }40 }42 void CInArchive::Close()43 {44 m_Stream.Release();45 }48 static inline bool TestMarkerCandidate(const void *aTestBytes)49 {50 for (UInt32 i = 0; i < NHeader::kMarkerSize; i++)51 if (((const Byte *)aTestBytes)[i] != NHeader::kMarker[i])52 return false;53 return true;54 }56 HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit)57 {58 RINOK(FindSignatureInStream(stream,59 NHeader::kMarker, NHeader::kMarkerSize,60 searchHeaderSizeLimit, m_ArchiveStartPosition));61 m_Stream = stream;62 m_Position = m_ArchiveStartPosition + NHeader::kMarkerSize;63 return m_Stream->Seek(m_Position, STREAM_SEEK_SET, NULL);64 }66 void CInArchive::ThrowUnexpectedEndOfArchiveException()67 {68 ThrowExceptionWithCode(CInArchiveException::kUnexpectedEndOfArchive);69 }71 bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size)72 {73 if (m_CryptoMode)74 {75 const Byte *bufData = (const Byte *)m_DecryptedData;76 UInt32 bufSize = m_DecryptedDataSize;77 UInt32 i;78 for (i = 0; i < size && m_CryptoPos < bufSize; i++)79 ((Byte *)data)[i] = bufData[m_CryptoPos++];80 return (i == size);81 }82 return (ReadStream_FALSE(m_Stream, data, size) == S_OK);83 }85 void CInArchive::ReadBytesAndTestResult(void *data, UInt32 size)86 {87 if(!ReadBytesAndTestSize(data,size))88 ThrowUnexpectedEndOfArchiveException();89 }91 HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize)92 {93 size_t realProcessedSize = size;94 HRESULT result = ReadStream(m_Stream, data, &realProcessedSize);95 if (processedSize != NULL)96 *processedSize = (UInt32)realProcessedSize;97 AddToSeekValue(realProcessedSize);98 return result;99 }101 static UInt32 CrcUpdateUInt16(UInt32 crc, UInt16 v)102 {103 crc = CRC_UPDATE_BYTE(crc, (Byte)(v & 0xFF));104 crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 8) & 0xFF));105 return crc;106 }108 static UInt32 CrcUpdateUInt32(UInt32 crc, UInt32 v)109 {110 crc = CRC_UPDATE_BYTE(crc, (Byte)(v & 0xFF));111 crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 8) & 0xFF));112 crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 16) & 0xFF));113 crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 24) & 0xFF));114 return crc;115 }118 HRESULT CInArchive::Open2(IInStream *stream, const UInt64 *searchHeaderSizeLimit)119 {120 m_CryptoMode = false;121 RINOK(stream->Seek(0, STREAM_SEEK_SET, &m_StreamStartPosition));122 m_Position = m_StreamStartPosition;124 RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit));126 Byte buf[NHeader::NArchive::kArchiveHeaderSize];127 UInt32 processedSize;128 ReadBytes(buf, sizeof(buf), &processedSize);129 if (processedSize != sizeof(buf))130 return S_FALSE;131 m_CurData = buf;132 m_CurPos = 0;133 m_PosLimit = sizeof(buf);135 m_ArchiveHeader.CRC = ReadUInt16();136 m_ArchiveHeader.Type = ReadByte();137 m_ArchiveHeader.Flags = ReadUInt16();138 m_ArchiveHeader.Size = ReadUInt16();139 m_ArchiveHeader.Reserved1 = ReadUInt16();140 m_ArchiveHeader.Reserved2 = ReadUInt32();141 m_ArchiveHeader.EncryptVersion = 0;143 UInt32 crc = CRC_INIT_VAL;144 crc = CRC_UPDATE_BYTE(crc, m_ArchiveHeader.Type);145 crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Flags);146 crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Size);147 crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Reserved1);148 crc = CrcUpdateUInt32(crc, m_ArchiveHeader.Reserved2);150 if (m_ArchiveHeader.IsThereEncryptVer() && m_ArchiveHeader.Size > NHeader::NArchive::kArchiveHeaderSize)151 {152 ReadBytes(&m_ArchiveHeader.EncryptVersion, 1, &processedSize);153 if (processedSize != 1)154 return S_FALSE;155 crc = CRC_UPDATE_BYTE(crc, m_ArchiveHeader.EncryptVersion);156 }158 if(m_ArchiveHeader.CRC != (CRC_GET_DIGEST(crc) & 0xFFFF))159 ThrowExceptionWithCode(CInArchiveException::kArchiveHeaderCRCError);160 if (m_ArchiveHeader.Type != NHeader::NBlockType::kArchiveHeader)161 return S_FALSE;162 m_ArchiveCommentPosition = m_Position;163 m_SeekOnArchiveComment = true;164 return S_OK;165 }167 void CInArchive::SkipArchiveComment()168 {169 if (!m_SeekOnArchiveComment)170 return;171 AddToSeekValue(m_ArchiveHeader.Size - m_ArchiveHeader.GetBaseSize());172 m_SeekOnArchiveComment = false;173 }175 void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const176 {177 archiveInfo.StartPosition = m_ArchiveStartPosition;178 archiveInfo.Flags = m_ArchiveHeader.Flags;179 archiveInfo.CommentPosition = m_ArchiveCommentPosition;180 archiveInfo.CommentSize = (UInt16)(m_ArchiveHeader.Size - NHeader::NArchive::kArchiveHeaderSize);181 }183 static void DecodeUnicodeFileName(const char *name, const Byte *encName,184 int encSize, wchar_t *unicodeName, int maxDecSize)185 {186 int encPos = 0;187 int decPos = 0;188 int flagBits = 0;189 Byte flags = 0;190 Byte highByte = encName[encPos++];191 while (encPos < encSize && decPos < maxDecSize)192 {193 if (flagBits == 0)194 {195 flags = encName[encPos++];196 flagBits = 8;197 }198 switch(flags >> 6)199 {200 case 0:201 unicodeName[decPos++] = encName[encPos++];202 break;203 case 1:204 unicodeName[decPos++] = (wchar_t)(encName[encPos++] + (highByte << 8));205 break;206 case 2:207 unicodeName[decPos++] = (wchar_t)(encName[encPos] + (encName[encPos + 1] << 8));208 encPos += 2;209 break;210 case 3:211 {212 int length = encName[encPos++];213 if (length & 0x80)214 {215 Byte correction = encName[encPos++];216 for (length = (length & 0x7f) + 2;217 length > 0 && decPos < maxDecSize; length--, decPos++)218 unicodeName[decPos] = (wchar_t)(((name[decPos] + correction) & 0xff) + (highByte << 8));219 }220 else221 for (length += 2; length > 0 && decPos < maxDecSize; length--, decPos++)222 unicodeName[decPos] = name[decPos];223 }224 break;225 }226 flags <<= 2;227 flagBits -= 2;228 }229 unicodeName[decPos < maxDecSize ? decPos : maxDecSize - 1] = 0;230 }232 void CInArchive::ReadName(CItemEx &item, int nameSize)233 {234 item.UnicodeName.Empty();235 if (nameSize > 0)236 {237 m_NameBuffer.EnsureCapacity(nameSize + 1);238 char *buffer = (char *)m_NameBuffer;240 for (int i = 0; i < nameSize; i++)241 buffer[i] = ReadByte();243 int mainLen;244 for (mainLen = 0; mainLen < nameSize; mainLen++)245 if (buffer[mainLen] == '\0')246 break;247 buffer[mainLen] = '\0';248 item.Name = buffer;250 if(item.HasUnicodeName())251 {252 if(mainLen < nameSize)253 {254 int unicodeNameSizeMax = MyMin(nameSize, (0x400));255 _unicodeNameBuffer.EnsureCapacity(unicodeNameSizeMax + 1);256 DecodeUnicodeFileName(buffer, (const Byte *)buffer + mainLen + 1,257 nameSize - (mainLen + 1), _unicodeNameBuffer, unicodeNameSizeMax);258 item.UnicodeName = _unicodeNameBuffer;259 }260 else if (!ConvertUTF8ToUnicode(item.Name, item.UnicodeName))261 item.UnicodeName.Empty();262 }263 }264 else265 item.Name.Empty();266 }268 Byte CInArchive::ReadByte()269 {270 if (m_CurPos >= m_PosLimit)271 throw CInArchiveException(CInArchiveException::kIncorrectArchive);272 return m_CurData[m_CurPos++];273 }275 UInt16 CInArchive::ReadUInt16()276 {277 UInt16 value = 0;278 for (int i = 0; i < 2; i++)279 {280 Byte b = ReadByte();281 value |= (UInt16(b) << (8 * i));282 }283 return value;284 }286 UInt32 CInArchive::ReadUInt32()287 {288 UInt32 value = 0;289 for (int i = 0; i < 4; i++)290 {291 Byte b = ReadByte();292 value |= (UInt32(b) << (8 * i));293 }294 return value;295 }297 void CInArchive::ReadTime(Byte mask, CRarTime &rarTime)298 {299 rarTime.LowSecond = (Byte)(((mask & 4) != 0) ? 1 : 0);300 int numDigits = (mask & 3);301 rarTime.SubTime[0] = rarTime.SubTime[1] = rarTime.SubTime[2] = 0;302 for (int i = 0; i < numDigits; i++)303 rarTime.SubTime[3 - numDigits + i] = ReadByte();304 }306 void CInArchive::ReadHeaderReal(CItemEx &item)307 {308 item.Flags = m_BlockHeader.Flags;309 item.PackSize = ReadUInt32();310 item.Size = ReadUInt32();311 item.HostOS = ReadByte();312 item.FileCRC = ReadUInt32();313 item.MTime.DosTime = ReadUInt32();314 item.UnPackVersion = ReadByte();315 item.Method = ReadByte();316 int nameSize = ReadUInt16();317 item.Attrib = ReadUInt32();319 item.MTime.LowSecond = 0;320 item.MTime.SubTime[0] =321 item.MTime.SubTime[1] =322 item.MTime.SubTime[2] = 0;324 if((item.Flags & NHeader::NFile::kSize64Bits) != 0)325 {326 item.PackSize |= ((UInt64)ReadUInt32() << 32);327 item.Size |= ((UInt64)ReadUInt32() << 32);328 }330 ReadName(item, nameSize);332 if (item.HasSalt())333 for (int i = 0; i < sizeof(item.Salt); i++)334 item.Salt[i] = ReadByte();336 // some rar archives have HasExtTime flag without field.337 if (m_CurPos < m_PosLimit && item.HasExtTime())338 {339 Byte accessMask = (Byte)(ReadByte() >> 4);340 Byte b = ReadByte();341 Byte modifMask = (Byte)(b >> 4);342 Byte createMask = (Byte)(b & 0xF);343 if ((modifMask & 8) != 0)344 ReadTime(modifMask, item.MTime);345 item.CTimeDefined = ((createMask & 8) != 0);346 if (item.CTimeDefined)347 {348 item.CTime.DosTime = ReadUInt32();349 ReadTime(createMask, item.CTime);350 }351 item.ATimeDefined = ((accessMask & 8) != 0);352 if (item.ATimeDefined)353 {354 item.ATime.DosTime = ReadUInt32();355 ReadTime(accessMask, item.ATime);356 }357 }359 UInt16 fileHeaderWithNameSize = (UInt16)m_CurPos;361 item.Position = m_Position;362 item.MainPartSize = fileHeaderWithNameSize;363 item.CommentSize = (UInt16)(m_BlockHeader.HeadSize - fileHeaderWithNameSize);365 if (m_CryptoMode)366 item.AlignSize = (UInt16)((16 - ((m_BlockHeader.HeadSize) & 0xF)) & 0xF);367 else368 item.AlignSize = 0;369 AddToSeekValue(m_BlockHeader.HeadSize);370 }372 void CInArchive::AddToSeekValue(UInt64 addValue)373 {374 m_Position += addValue;375 }377 HRESULT CInArchive::GetNextItem(CItemEx &item, ICryptoGetTextPassword *getTextPassword)378 {379 if (m_SeekOnArchiveComment)380 SkipArchiveComment();381 for (;;)382 {383 if(!SeekInArchive(m_Position))384 return S_FALSE;385 if (!m_CryptoMode && (m_ArchiveHeader.Flags &386 NHeader::NArchive::kBlockHeadersAreEncrypted) != 0)387 {388 m_CryptoMode = false;389 if (getTextPassword == 0)390 return S_FALSE;391 if(!SeekInArchive(m_Position))392 return S_FALSE;393 if (!m_RarAES)394 {395 m_RarAESSpec = new NCrypto::NRar29::CDecoder;396 m_RarAES = m_RarAESSpec;397 }398 m_RarAESSpec->SetRar350Mode(m_ArchiveHeader.IsEncryptOld());400 // Salt401 const UInt32 kSaltSize = 8;402 Byte salt[kSaltSize];403 if(!ReadBytesAndTestSize(salt, kSaltSize))404 return S_FALSE;405 m_Position += kSaltSize;406 RINOK(m_RarAESSpec->SetDecoderProperties2(salt, kSaltSize))407 // Password408 CMyComBSTR password;409 RINOK(getTextPassword->CryptoGetTextPassword(&password))410 UString unicodePassword(password);412 CByteBuffer buffer;413 const UInt32 sizeInBytes = unicodePassword.Length() * 2;414 buffer.SetCapacity(sizeInBytes);415 for (int i = 0; i < unicodePassword.Length(); i++)416 {417 wchar_t c = unicodePassword[i];418 ((Byte *)buffer)[i * 2] = (Byte)c;419 ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);420 }422 RINOK(m_RarAESSpec->CryptoSetPassword((const Byte *)buffer, sizeInBytes));424 const UInt32 kDecryptedBufferSize = (1 << 12);425 if (m_DecryptedData.GetCapacity() == 0)426 {427 m_DecryptedData.SetCapacity(kDecryptedBufferSize);428 }429 RINOK(m_RarAES->Init());430 size_t decryptedDataSizeT = kDecryptedBufferSize;431 RINOK(ReadStream(m_Stream, (Byte *)m_DecryptedData, &decryptedDataSizeT));432 m_DecryptedDataSize = (UInt32)decryptedDataSizeT;433 m_DecryptedDataSize = m_RarAES->Filter((Byte *)m_DecryptedData, m_DecryptedDataSize);435 m_CryptoMode = true;436 m_CryptoPos = 0;437 }439 m_FileHeaderData.EnsureCapacity(7);440 if(!ReadBytesAndTestSize((Byte *)m_FileHeaderData, 7))441 return S_FALSE;443 m_CurData = (Byte *)m_FileHeaderData;444 m_CurPos = 0;445 m_PosLimit = 7;446 m_BlockHeader.CRC = ReadUInt16();447 m_BlockHeader.Type = ReadByte();448 m_BlockHeader.Flags = ReadUInt16();449 m_BlockHeader.HeadSize = ReadUInt16();451 if (m_BlockHeader.HeadSize < 7)452 ThrowExceptionWithCode(CInArchiveException::kIncorrectArchive);454 if (m_BlockHeader.Type == NHeader::NBlockType::kEndOfArchive)455 return S_FALSE;457 if (m_BlockHeader.Type == NHeader::NBlockType::kFileHeader)458 {459 m_FileHeaderData.EnsureCapacity(m_BlockHeader.HeadSize);460 m_CurData = (Byte *)m_FileHeaderData;461 m_PosLimit = m_BlockHeader.HeadSize;462 ReadBytesAndTestResult(m_CurData + m_CurPos, m_BlockHeader.HeadSize - 7);463 ReadHeaderReal(item);464 if ((CrcCalc(m_CurData + 2,465 m_BlockHeader.HeadSize - item.CommentSize - 2) & 0xFFFF) != m_BlockHeader.CRC)466 ThrowExceptionWithCode(CInArchiveException::kFileHeaderCRCError);468 FinishCryptoBlock();469 m_CryptoMode = false;470 SeekInArchive(m_Position); // Move Position to compressed Data;471 AddToSeekValue(item.PackSize); // m_Position points to next header;472 return S_OK;473 }474 if (m_CryptoMode && m_BlockHeader.HeadSize > (1 << 12))475 return E_FAIL; // it's for bad passwords476 if ((m_BlockHeader.Flags & NHeader::NBlock::kLongBlock) != 0)477 {478 m_FileHeaderData.EnsureCapacity(7 + 4);479 m_CurData = (Byte *)m_FileHeaderData;480 ReadBytesAndTestResult(m_CurData + m_CurPos, 4); // test it481 m_PosLimit = 7 + 4;482 UInt32 dataSize = ReadUInt32();483 AddToSeekValue(dataSize);484 if (m_CryptoMode && dataSize > (1 << 27))485 return E_FAIL; // it's for bad passwords486 m_CryptoPos = m_BlockHeader.HeadSize;487 }488 else489 m_CryptoPos = 0;490 AddToSeekValue(m_BlockHeader.HeadSize);491 FinishCryptoBlock();492 m_CryptoMode = false;493 }494 }496 bool CInArchive::SeekInArchive(UInt64 position)497 {498 UInt64 newPosition;499 m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition);500 return newPosition == position;501 }503 ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size)504 {505 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;506 CMyComPtr<ISequentialInStream> inStream(streamSpec);507 SeekInArchive(position);508 streamSpec->SetStream(m_Stream);509 streamSpec->Init(size);510 return inStream.Detach();511 }513 }}