Mercurial > vba-linux
comparison 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 |
comparison
equal
deleted
inserted
replaced
0:8ced16adf2e1 | 1:f9f4f1b99eed |
---|---|
1 // Archive/RarIn.cpp | |
2 | |
3 #include "StdAfx.h" | |
4 | |
5 #include "Common/StringConvert.h" | |
6 #include "Common/UTFConvert.h" | |
7 | |
8 #include "RarIn.h" | |
9 #include "../../Common/LimitedStreams.h" | |
10 #include "../../Common/StreamUtils.h" | |
11 | |
12 #include "../Common/FindSignature.h" | |
13 | |
14 extern "C" | |
15 { | |
16 #include "../../../../C/7zCrc.h" | |
17 } | |
18 | |
19 namespace NArchive { | |
20 namespace NRar { | |
21 | |
22 void CInArchive::ThrowExceptionWithCode( | |
23 CInArchiveException::CCauseType cause) | |
24 { | |
25 throw CInArchiveException(cause); | |
26 } | |
27 | |
28 HRESULT CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit) | |
29 { | |
30 try | |
31 { | |
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 } | |
41 | |
42 void CInArchive::Close() | |
43 { | |
44 m_Stream.Release(); | |
45 } | |
46 | |
47 | |
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 } | |
55 | |
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 } | |
65 | |
66 void CInArchive::ThrowUnexpectedEndOfArchiveException() | |
67 { | |
68 ThrowExceptionWithCode(CInArchiveException::kUnexpectedEndOfArchive); | |
69 } | |
70 | |
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 } | |
84 | |
85 void CInArchive::ReadBytesAndTestResult(void *data, UInt32 size) | |
86 { | |
87 if(!ReadBytesAndTestSize(data,size)) | |
88 ThrowUnexpectedEndOfArchiveException(); | |
89 } | |
90 | |
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 } | |
100 | |
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 } | |
107 | |
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 } | |
116 | |
117 | |
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; | |
123 | |
124 RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit)); | |
125 | |
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); | |
134 | |
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; | |
142 | |
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); | |
149 | |
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 } | |
157 | |
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 } | |
166 | |
167 void CInArchive::SkipArchiveComment() | |
168 { | |
169 if (!m_SeekOnArchiveComment) | |
170 return; | |
171 AddToSeekValue(m_ArchiveHeader.Size - m_ArchiveHeader.GetBaseSize()); | |
172 m_SeekOnArchiveComment = false; | |
173 } | |
174 | |
175 void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const | |
176 { | |
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 } | |
182 | |
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 else | |
221 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 } | |
231 | |
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; | |
239 | |
240 for (int i = 0; i < nameSize; i++) | |
241 buffer[i] = ReadByte(); | |
242 | |
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; | |
249 | |
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 else | |
265 item.Name.Empty(); | |
266 } | |
267 | |
268 Byte CInArchive::ReadByte() | |
269 { | |
270 if (m_CurPos >= m_PosLimit) | |
271 throw CInArchiveException(CInArchiveException::kIncorrectArchive); | |
272 return m_CurData[m_CurPos++]; | |
273 } | |
274 | |
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 } | |
285 | |
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 } | |
296 | |
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 } | |
305 | |
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(); | |
318 | |
319 item.MTime.LowSecond = 0; | |
320 item.MTime.SubTime[0] = | |
321 item.MTime.SubTime[1] = | |
322 item.MTime.SubTime[2] = 0; | |
323 | |
324 if((item.Flags & NHeader::NFile::kSize64Bits) != 0) | |
325 { | |
326 item.PackSize |= ((UInt64)ReadUInt32() << 32); | |
327 item.Size |= ((UInt64)ReadUInt32() << 32); | |
328 } | |
329 | |
330 ReadName(item, nameSize); | |
331 | |
332 if (item.HasSalt()) | |
333 for (int i = 0; i < sizeof(item.Salt); i++) | |
334 item.Salt[i] = ReadByte(); | |
335 | |
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 } | |
358 | |
359 UInt16 fileHeaderWithNameSize = (UInt16)m_CurPos; | |
360 | |
361 item.Position = m_Position; | |
362 item.MainPartSize = fileHeaderWithNameSize; | |
363 item.CommentSize = (UInt16)(m_BlockHeader.HeadSize - fileHeaderWithNameSize); | |
364 | |
365 if (m_CryptoMode) | |
366 item.AlignSize = (UInt16)((16 - ((m_BlockHeader.HeadSize) & 0xF)) & 0xF); | |
367 else | |
368 item.AlignSize = 0; | |
369 AddToSeekValue(m_BlockHeader.HeadSize); | |
370 } | |
371 | |
372 void CInArchive::AddToSeekValue(UInt64 addValue) | |
373 { | |
374 m_Position += addValue; | |
375 } | |
376 | |
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()); | |
399 | |
400 // Salt | |
401 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 // Password | |
408 CMyComBSTR password; | |
409 RINOK(getTextPassword->CryptoGetTextPassword(&password)) | |
410 UString unicodePassword(password); | |
411 | |
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 } | |
421 | |
422 RINOK(m_RarAESSpec->CryptoSetPassword((const Byte *)buffer, sizeInBytes)); | |
423 | |
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); | |
434 | |
435 m_CryptoMode = true; | |
436 m_CryptoPos = 0; | |
437 } | |
438 | |
439 m_FileHeaderData.EnsureCapacity(7); | |
440 if(!ReadBytesAndTestSize((Byte *)m_FileHeaderData, 7)) | |
441 return S_FALSE; | |
442 | |
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(); | |
450 | |
451 if (m_BlockHeader.HeadSize < 7) | |
452 ThrowExceptionWithCode(CInArchiveException::kIncorrectArchive); | |
453 | |
454 if (m_BlockHeader.Type == NHeader::NBlockType::kEndOfArchive) | |
455 return S_FALSE; | |
456 | |
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); | |
467 | |
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 passwords | |
476 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 it | |
481 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 passwords | |
486 m_CryptoPos = m_BlockHeader.HeadSize; | |
487 } | |
488 else | |
489 m_CryptoPos = 0; | |
490 AddToSeekValue(m_BlockHeader.HeadSize); | |
491 FinishCryptoBlock(); | |
492 m_CryptoMode = false; | |
493 } | |
494 } | |
495 | |
496 bool CInArchive::SeekInArchive(UInt64 position) | |
497 { | |
498 UInt64 newPosition; | |
499 m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition); | |
500 return newPosition == position; | |
501 } | |
502 | |
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 } | |
512 | |
513 }} |