1 // RarHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/CpuArch.h"
6
7 #include "../../../Common/ComTry.h"
8 #include "../../../Common/IntToString.h"
9 #include "../../../Common/MyBuffer2.h"
10 #include "../../../Common/UTFConvert.h"
11
12 #include "../../../Windows/PropVariantUtils.h"
13 #include "../../../Windows/TimeUtils.h"
14
15 #include "../../IPassword.h"
16
17 #include "../../Common/CreateCoder.h"
18 #include "../../Common/FilterCoder.h"
19 #include "../../Common/LimitedStreams.h"
20 #include "../../Common/MethodId.h"
21 #include "../../Common/ProgressUtils.h"
22 #include "../../Common/RegisterArc.h"
23 #include "../../Common/StreamUtils.h"
24
25 #include "../../Compress/CopyCoder.h"
26
27 #include "../../Crypto/Rar20Crypto.h"
28 #include "../../Crypto/RarAes.h"
29
30 #include "../Common/FindSignature.h"
31 #include "../Common/ItemNameUtils.h"
32 #include "../Common/OutStreamWithCRC.h"
33
34 #include "../HandlerCont.h"
35
36 #include "RarVol.h"
37 #include "RarHandler.h"
38
39 using namespace NWindows;
40
41 #define Get16(p) GetUi16(p)
42 #define Get32(p) GetUi32(p)
43
44 namespace NArchive {
45 namespace NRar {
46
47 static const Byte kMarker[NHeader::kMarkerSize] =
48 { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
49
50 const unsigned kPasswordLen_MAX = 127;
51
IgnoreItem() const52 bool CItem::IgnoreItem() const
53 {
54 switch (HostOS)
55 {
56 case NHeader::NFile::kHostMSDOS:
57 case NHeader::NFile::kHostOS2:
58 case NHeader::NFile::kHostWin32:
59 return ((Attrib & NHeader::NFile::kLabelFileAttribute) != 0);
60 }
61 return false;
62 }
63
IsDir() const64 bool CItem::IsDir() const
65 {
66 if (GetDictSize() == NHeader::NFile::kDictDirectoryValue)
67 return true;
68 switch (HostOS)
69 {
70 case NHeader::NFile::kHostMSDOS:
71 case NHeader::NFile::kHostOS2:
72 case NHeader::NFile::kHostWin32:
73 if ((Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
74 return true;
75 }
76 return false;
77 }
78
GetWinAttrib() const79 UInt32 CItem::GetWinAttrib() const
80 {
81 UInt32 a;
82 switch (HostOS)
83 {
84 case NHeader::NFile::kHostMSDOS:
85 case NHeader::NFile::kHostOS2:
86 case NHeader::NFile::kHostWin32:
87 a = Attrib;
88 break;
89 default:
90 a = 0; // must be converted from unix value;
91 }
92 if (IsDir())
93 a |= NHeader::NFile::kWinFileDirectoryAttributeMask;
94 return a;
95 }
96
97 static const char * const kHostOS[] =
98 {
99 "MS DOS"
100 , "OS/2"
101 , "Win32"
102 , "Unix"
103 , "Mac OS"
104 , "BeOS"
105 };
106
107 static const char * const k_Flags[] =
108 {
109 "Volume"
110 , "Comment"
111 , "Lock"
112 , "Solid"
113 , "NewVolName" // pack_comment in old versuons
114 , "Authenticity"
115 , "Recovery"
116 , "BlockEncryption"
117 , "FirstVolume"
118 , "EncryptVer" // 9
119 };
120
121 enum EErrorType
122 {
123 k_ErrorType_OK,
124 k_ErrorType_Corrupted,
125 k_ErrorType_UnexpectedEnd,
126 k_ErrorType_DecryptionError
127 };
128
129 class CInArchive
130 {
131 IInStream *m_Stream;
132 UInt64 m_StreamStartPosition;
133 UString _unicodeNameBuffer;
134 CByteBuffer _comment;
135 CByteBuffer m_FileHeaderData;
136 NHeader::NBlock::CBlock m_BlockHeader;
137 NCrypto::NRar3::CDecoder *m_RarAESSpec;
138 CMyComPtr<ICompressFilter> m_RarAES;
139 CAlignedBuffer m_DecryptedDataAligned;
140 UInt32 m_DecryptedDataSize;
141 bool m_CryptoMode;
142 UInt32 m_CryptoPos;
143
144
145 HRESULT ReadBytesSpec(void *data, size_t *size);
146 bool ReadBytesAndTestSize(void *data, UInt32 size);
147 void ReadName(const Byte *p, unsigned nameSize, CItem &item);
148 bool ReadHeaderReal(const Byte *p, unsigned size, CItem &item);
149
150 HRESULT Open2(IInStream *stream, const UInt64 *searchHeaderSizeLimit);
151
AddToSeekValue(UInt64 addValue)152 void AddToSeekValue(UInt64 addValue)
153 {
154 m_Position += addValue;
155 }
156
FinishCryptoBlock()157 void FinishCryptoBlock()
158 {
159 if (m_CryptoMode)
160 while ((m_CryptoPos & 0xF) != 0)
161 {
162 m_CryptoPos++;
163 m_Position++;
164 }
165 }
166
167 public:
168 UInt64 m_Position;
169 CInArcInfo ArcInfo;
170 bool HeaderErrorWarning;
171
172 HRESULT Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit);
173 HRESULT GetNextItem(CItem &item, ICryptoGetTextPassword *getTextPassword,
174 bool &filled, EErrorType &error);
175 };
176
CheckHeaderCrc(const Byte * header,size_t headerSize)177 static bool CheckHeaderCrc(const Byte *header, size_t headerSize)
178 {
179 return Get16(header) == (UInt16)(CrcCalc(header + 2, headerSize - 2) & 0xFFFF);
180 }
181
Open(IInStream * stream,const UInt64 * searchHeaderSizeLimit)182 HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
183 {
184 HeaderErrorWarning = false;
185 m_CryptoMode = false;
186 RINOK(InStream_GetPos_GetSize(stream, m_StreamStartPosition, ArcInfo.FileSize))
187 m_Position = m_StreamStartPosition;
188
189 UInt64 arcStartPos = m_StreamStartPosition;
190 {
191 Byte marker[NHeader::kMarkerSize];
192 RINOK(ReadStream_FALSE(stream, marker, NHeader::kMarkerSize))
193 if (memcmp(marker, kMarker, NHeader::kMarkerSize) == 0)
194 m_Position += NHeader::kMarkerSize;
195 else
196 {
197 if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0)
198 return S_FALSE;
199 RINOK(InStream_SeekSet(stream, m_StreamStartPosition))
200 RINOK(FindSignatureInStream(stream, kMarker, NHeader::kMarkerSize,
201 searchHeaderSizeLimit, arcStartPos))
202 m_Position = arcStartPos + NHeader::kMarkerSize;
203 RINOK(InStream_SeekSet(stream, m_Position))
204 }
205 }
206 Byte buf[NHeader::NArchive::kArchiveHeaderSize + 1];
207
208 RINOK(ReadStream_FALSE(stream, buf, NHeader::NArchive::kArchiveHeaderSize))
209 AddToSeekValue(NHeader::NArchive::kArchiveHeaderSize);
210
211
212 const UInt32 blockSize = Get16(buf + 5);
213
214 ArcInfo.EncryptVersion = 0;
215 ArcInfo.Flags = Get16(buf + 3);
216
217 UInt32 headerSize = NHeader::NArchive::kArchiveHeaderSize;
218
219 /*
220 if (ArcInfo.IsThereEncryptVer())
221 {
222 if (blockSize <= headerSize)
223 return S_FALSE;
224 RINOK(ReadStream_FALSE(stream, buf + NHeader::NArchive::kArchiveHeaderSize, 1));
225 AddToSeekValue(1);
226 ArcInfo.EncryptVersion = buf[NHeader::NArchive::kArchiveHeaderSize];
227 headerSize += 1;
228 }
229 */
230
231 if (blockSize < headerSize
232 || buf[2] != NHeader::NBlockType::kArchiveHeader
233 || !CheckHeaderCrc(buf, headerSize))
234 return S_FALSE;
235
236 size_t commentSize = blockSize - headerSize;
237 _comment.Alloc(commentSize);
238 RINOK(ReadStream_FALSE(stream, _comment, commentSize))
239 AddToSeekValue(commentSize);
240 m_Stream = stream;
241 ArcInfo.StartPos = arcStartPos;
242 return S_OK;
243 }
244
ReadBytesSpec(void * data,size_t * resSize)245 HRESULT CInArchive::ReadBytesSpec(void *data, size_t *resSize)
246 {
247 if (m_CryptoMode)
248 {
249 size_t size = *resSize;
250 *resSize = 0;
251 const Byte *bufData = m_DecryptedDataAligned;
252 UInt32 bufSize = m_DecryptedDataSize;
253 size_t i;
254 for (i = 0; i < size && m_CryptoPos < bufSize; i++)
255 ((Byte *)data)[i] = bufData[m_CryptoPos++];
256 *resSize = i;
257 return S_OK;
258 }
259 return ReadStream(m_Stream, data, resSize);
260 }
261
ReadBytesAndTestSize(void * data,UInt32 size)262 bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size)
263 {
264 size_t processed = size;
265 if (ReadBytesSpec(data, &processed) != S_OK)
266 return false;
267 return processed == size;
268 }
269
270
DecodeUnicodeFileName(const Byte * name,const Byte * encName,unsigned encSize,wchar_t * unicodeName,unsigned maxDecSize)271 static unsigned DecodeUnicodeFileName(const Byte *name, const Byte *encName,
272 unsigned encSize, wchar_t *unicodeName, unsigned maxDecSize)
273 {
274 unsigned encPos = 0;
275 unsigned decPos = 0;
276 unsigned flagBits = 0;
277 Byte flags = 0;
278
279 if (encPos >= encSize)
280 return 0; // error
281 const unsigned highBits = ((unsigned)encName[encPos++]) << 8;
282
283 while (encPos < encSize && decPos < maxDecSize)
284 {
285 if (flagBits == 0)
286 {
287 flags = encName[encPos++];
288 flagBits = 8;
289 }
290
291 if (encPos >= encSize)
292 break; // error
293 unsigned len = encName[encPos++];
294
295 flagBits -= 2;
296 const unsigned mode = (flags >> flagBits) & 3;
297
298 if (mode != 3)
299 {
300 if (mode == 1)
301 len += highBits;
302 else if (mode == 2)
303 {
304 if (encPos >= encSize)
305 break; // error
306 len += ((unsigned)encName[encPos++] << 8);
307 }
308 unicodeName[decPos++] = (wchar_t)len;
309 }
310 else
311 {
312 if (len & 0x80)
313 {
314 if (encPos >= encSize)
315 break; // error
316 Byte correction = encName[encPos++];
317 for (len = (len & 0x7f) + 2; len > 0 && decPos < maxDecSize; len--, decPos++)
318 unicodeName[decPos] = (wchar_t)(((name[decPos] + correction) & 0xff) + highBits);
319 }
320 else
321 for (len += 2; len > 0 && decPos < maxDecSize; len--, decPos++)
322 unicodeName[decPos] = name[decPos];
323 }
324 }
325
326 return decPos < maxDecSize ? decPos : maxDecSize - 1;
327 }
328
329
ReadName(const Byte * p,unsigned nameSize,CItem & item)330 void CInArchive::ReadName(const Byte *p, unsigned nameSize, CItem &item)
331 {
332 item.UnicodeName.Empty();
333 if (nameSize > 0)
334 {
335 unsigned i;
336 for (i = 0; i < nameSize && p[i] != 0; i++);
337 item.Name.SetFrom((const char *)p, i);
338
339 if (item.HasUnicodeName())
340 {
341 if (i < nameSize)
342 {
343 i++;
344 unsigned uNameSizeMax = MyMin(nameSize, (unsigned)0x400);
345 unsigned len = DecodeUnicodeFileName(p, p + i, nameSize - i, _unicodeNameBuffer.GetBuf(uNameSizeMax), uNameSizeMax);
346 _unicodeNameBuffer.ReleaseBuf_SetEnd(len);
347 item.UnicodeName = _unicodeNameBuffer;
348 }
349 else if (!ConvertUTF8ToUnicode(item.Name, item.UnicodeName))
350 item.UnicodeName.Empty();
351 }
352 }
353 else
354 item.Name.Empty();
355 }
356
ReadTime(const Byte * p,unsigned size,Byte mask,CRarTime & rarTime)357 static int ReadTime(const Byte *p, unsigned size, Byte mask, CRarTime &rarTime)
358 {
359 rarTime.LowSecond = (Byte)(((mask & 4) != 0) ? 1 : 0);
360 const unsigned numDigits = (mask & 3);
361 rarTime.SubTime[0] =
362 rarTime.SubTime[1] =
363 rarTime.SubTime[2] = 0;
364 if (numDigits > size)
365 return -1;
366 for (unsigned i = 0; i < numDigits; i++)
367 rarTime.SubTime[3 - numDigits + i] = p[i];
368 return (int)numDigits;
369 }
370
371 #define READ_TIME(_mask_, _ttt_) \
372 { int size2 = ReadTime(p, size, _mask_, _ttt_); if (size2 < 0) return false; p += (unsigned)size2, size -= (unsigned)size2; }
373
374 #define READ_TIME_2(_mask_, _def_, _ttt_) \
375 _def_ = ((_mask_ & 8) != 0); if (_def_) \
376 { if (size < 4) return false; \
377 _ttt_ .DosTime = Get32(p); p += 4; size -= 4; \
378 READ_TIME(_mask_, _ttt_); } \
379
380
ReadHeaderReal(const Byte * p,unsigned size,CItem & item)381 bool CInArchive::ReadHeaderReal(const Byte *p, unsigned size, CItem &item)
382 {
383 const Byte *pStart = p;
384
385 item.Clear();
386 item.Flags = m_BlockHeader.Flags;
387
388 const unsigned kFileHeaderSize = 25;
389
390 if (size < kFileHeaderSize)
391 return false;
392
393 item.PackSize = Get32(p);
394 item.Size = Get32(p + 4);
395 item.HostOS = p[8];
396 item.FileCRC = Get32(p + 9);
397 item.MTime.DosTime = Get32(p + 13);
398 item.UnPackVersion = p[17];
399 item.Method = p[18];
400 unsigned nameSize = Get16(p + 19);
401 item.Attrib = Get32(p + 21);
402
403 item.MTime.LowSecond = 0;
404 item.MTime.SubTime[0] =
405 item.MTime.SubTime[1] =
406 item.MTime.SubTime[2] = 0;
407
408 p += kFileHeaderSize;
409 size -= kFileHeaderSize;
410 if ((item.Flags & NHeader::NFile::kSize64Bits) != 0)
411 {
412 if (size < 8)
413 return false;
414 item.PackSize |= ((UInt64)Get32(p) << 32);
415 if (item.PackSize >= ((UInt64)1 << 63))
416 return false;
417 item.Size |= ((UInt64)Get32(p + 4) << 32);
418 p += 8;
419 size -= 8;
420 }
421 if (nameSize > size)
422 return false;
423 ReadName(p, nameSize, item);
424 p += nameSize;
425 size -= nameSize;
426
427 /*
428 // It was commented, since it's difficult to support alt Streams for solid archives.
429 if (m_BlockHeader.Type == NHeader::NBlockType::kSubBlock)
430 {
431 if (item.HasSalt())
432 {
433 if (size < sizeof(item.Salt))
434 return false;
435 size -= sizeof(item.Salt);
436 p += sizeof(item.Salt);
437 }
438 if (item.Name == "ACL" && size == 0)
439 {
440 item.IsAltStream = true;
441 item.Name.Empty();
442 item.UnicodeName.SetFromAscii(".ACL");
443 }
444 else if (item.Name == "STM" && size != 0 && (size & 1) == 0)
445 {
446 item.IsAltStream = true;
447 item.Name.Empty();
448 for (UInt32 i = 0; i < size; i += 2)
449 {
450 wchar_t c = Get16(p + i);
451 if (c == 0)
452 return false;
453 item.UnicodeName += c;
454 }
455 }
456 }
457 */
458
459 if (item.HasSalt())
460 {
461 if (size < sizeof(item.Salt))
462 return false;
463 for (unsigned i = 0; i < sizeof(item.Salt); i++)
464 item.Salt[i] = p[i];
465 p += sizeof(item.Salt);
466 size -= (unsigned)sizeof(item.Salt);
467 }
468
469 // some rar archives have HasExtTime flag without field.
470 if (size >= 2 && item.HasExtTime())
471 {
472 Byte aMask = (Byte)(p[0] >> 4);
473 Byte b = p[1];
474 p += 2;
475 size -= 2;
476 Byte mMask = (Byte)(b >> 4);
477 Byte cMask = (Byte)(b & 0xF);
478 if ((mMask & 8) != 0)
479 {
480 READ_TIME(mMask, item.MTime)
481 }
482 READ_TIME_2(cMask, item.CTimeDefined, item.CTime)
483 READ_TIME_2(aMask, item.ATimeDefined, item.ATime)
484 }
485
486 unsigned fileHeaderWithNameSize = 7 + (unsigned)(p - pStart);
487
488 item.Position = m_Position;
489 item.MainPartSize = fileHeaderWithNameSize;
490 item.CommentSize = (UInt16)(m_BlockHeader.HeadSize - fileHeaderWithNameSize);
491
492 if (m_CryptoMode)
493 item.AlignSize = (UInt16)((16 - ((m_BlockHeader.HeadSize) & 0xF)) & 0xF);
494 else
495 item.AlignSize = 0;
496 AddToSeekValue(m_BlockHeader.HeadSize);
497
498 // return (m_BlockHeader.Type != NHeader::NBlockType::kSubBlock || item.IsAltStream);
499 return true;
500 }
501
GetNextItem(CItem & item,ICryptoGetTextPassword * getTextPassword,bool & filled,EErrorType & error)502 HRESULT CInArchive::GetNextItem(CItem &item, ICryptoGetTextPassword *getTextPassword, bool &filled, EErrorType &error)
503 {
504 filled = false;
505 error = k_ErrorType_OK;
506 for (;;)
507 {
508 RINOK(InStream_SeekSet(m_Stream, m_Position))
509 ArcInfo.EndPos = m_Position;
510 if (!m_CryptoMode && (ArcInfo.Flags &
511 NHeader::NArchive::kBlockHeadersAreEncrypted) != 0)
512 {
513 m_CryptoMode = false;
514 if (!getTextPassword)
515 {
516 error = k_ErrorType_DecryptionError;
517 return S_OK; // return S_FALSE;
518 }
519 if (!m_RarAES)
520 {
521 m_RarAESSpec = new NCrypto::NRar3::CDecoder;
522 m_RarAES = m_RarAESSpec;
523 }
524 // m_RarAESSpec->SetRar350Mode(ArcInfo.IsEncryptOld());
525
526 {
527 // Salt
528 const UInt32 kSaltSize = 8;
529 Byte salt[kSaltSize];
530 if (!ReadBytesAndTestSize(salt, kSaltSize))
531 return S_FALSE;
532 m_Position += kSaltSize;
533 RINOK(m_RarAESSpec->SetDecoderProperties2(salt, kSaltSize))
534 }
535
536 {
537 // Password
538 CMyComBSTR_Wipe password;
539 RINOK(getTextPassword->CryptoGetTextPassword(&password))
540 unsigned len = 0;
541 if (password)
542 len = MyStringLen(password);
543 if (len > kPasswordLen_MAX)
544 len = kPasswordLen_MAX;
545
546 CByteBuffer_Wipe buffer(len * 2);
547 for (unsigned i = 0; i < len; i++)
548 {
549 wchar_t c = password[i];
550 ((Byte *)buffer)[i * 2] = (Byte)c;
551 ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
552 }
553
554 m_RarAESSpec->SetPassword((const Byte *)buffer, len * 2);
555 }
556
557 const UInt32 kDecryptedBufferSize = (1 << 12);
558 if (m_DecryptedDataAligned.Size() == 0)
559 {
560 // const UInt32 kAlign = 16;
561 m_DecryptedDataAligned.AllocAtLeast(kDecryptedBufferSize);
562 if (!m_DecryptedDataAligned.IsAllocated())
563 return E_OUTOFMEMORY;
564 }
565 RINOK(m_RarAES->Init())
566 size_t decryptedDataSizeT = kDecryptedBufferSize;
567 RINOK(ReadStream(m_Stream, m_DecryptedDataAligned, &decryptedDataSizeT))
568 m_DecryptedDataSize = (UInt32)decryptedDataSizeT;
569 m_DecryptedDataSize = m_RarAES->Filter(m_DecryptedDataAligned, m_DecryptedDataSize);
570
571 m_CryptoMode = true;
572 m_CryptoPos = 0;
573 }
574
575 m_FileHeaderData.AllocAtLeast(7);
576 size_t processed = 7;
577 RINOK(ReadBytesSpec((Byte *)m_FileHeaderData, &processed))
578 if (processed != 7)
579 {
580 if (processed != 0)
581 error = k_ErrorType_UnexpectedEnd;
582 ArcInfo.EndPos = m_Position + processed; // test it
583 return S_OK;
584 }
585
586 const Byte *p = m_FileHeaderData;
587 m_BlockHeader.CRC = Get16(p + 0);
588 m_BlockHeader.Type = p[2];
589 m_BlockHeader.Flags = Get16(p + 3);
590 m_BlockHeader.HeadSize = Get16(p + 5);
591
592 if (m_BlockHeader.HeadSize < 7)
593 {
594 error = k_ErrorType_Corrupted;
595 return S_OK;
596 // ThrowExceptionWithCode(CInArchiveException::kIncorrectArchive);
597 }
598
599 if (m_BlockHeader.Type < NHeader::NBlockType::kFileHeader ||
600 m_BlockHeader.Type > NHeader::NBlockType::kEndOfArchive)
601 {
602 error = m_CryptoMode ?
603 k_ErrorType_DecryptionError :
604 k_ErrorType_Corrupted;
605 return S_OK;
606 }
607
608 if (m_BlockHeader.Type == NHeader::NBlockType::kEndOfArchive)
609 {
610 bool footerError = false;
611
612 unsigned expectHeadLen = 7;
613 if (m_BlockHeader.Flags & NHeader::NArchive::kEndOfArc_Flags_DataCRC)
614 expectHeadLen += 4;
615 if (m_BlockHeader.Flags & NHeader::NArchive::kEndOfArc_Flags_VolNumber)
616 expectHeadLen += 2;
617 if (m_BlockHeader.Flags & NHeader::NArchive::kEndOfArc_Flags_RevSpace)
618 expectHeadLen += 7;
619
620 // rar 5.0 beta 1 writes incorrect RevSpace and headSize
621
622 if (m_BlockHeader.HeadSize < expectHeadLen)
623 HeaderErrorWarning = true;
624
625 if (m_BlockHeader.HeadSize > 7)
626 {
627 /* We suppose that EndOfArchive header is always small.
628 It's only 20 bytes for multivolume
629 Fix the limit, if larger footers are possible */
630 if (m_BlockHeader.HeadSize > (1 << 8))
631 footerError = true;
632 else
633 {
634 if (m_FileHeaderData.Size() < m_BlockHeader.HeadSize)
635 m_FileHeaderData.ChangeSize_KeepData(m_BlockHeader.HeadSize, 7);
636 UInt32 afterSize = m_BlockHeader.HeadSize - 7;
637 if (ReadBytesAndTestSize(m_FileHeaderData + 7, afterSize))
638 processed += afterSize;
639 else
640 {
641 if (!m_CryptoMode)
642 {
643 error = k_ErrorType_UnexpectedEnd;
644 return S_OK;
645 }
646 footerError = true;
647 }
648 }
649 }
650
651 if (footerError || !CheckHeaderCrc(m_FileHeaderData, m_BlockHeader.HeadSize))
652 {
653 error = m_CryptoMode ?
654 k_ErrorType_DecryptionError :
655 k_ErrorType_Corrupted;
656 }
657 else
658 {
659 ArcInfo.EndFlags = m_BlockHeader.Flags;
660 UInt32 offset = 7;
661
662 if (m_BlockHeader.Flags & NHeader::NArchive::kEndOfArc_Flags_DataCRC)
663 {
664 if (processed < offset + 4)
665 error = k_ErrorType_Corrupted;
666 else
667 ArcInfo.DataCRC = Get32(m_FileHeaderData + offset);
668 offset += 4;
669 }
670
671 if (m_BlockHeader.Flags & NHeader::NArchive::kEndOfArc_Flags_VolNumber)
672 {
673 if (processed < offset + 2)
674 error = k_ErrorType_Corrupted;
675 else
676 ArcInfo.VolNumber = (UInt32)Get16(m_FileHeaderData + offset);
677 }
678
679 ArcInfo.EndOfArchive_was_Read = true;
680 }
681
682 m_Position += processed;
683 FinishCryptoBlock();
684 ArcInfo.EndPos = m_Position;
685 return S_OK;
686 }
687
688 if (m_BlockHeader.Type == NHeader::NBlockType::kFileHeader
689 /* || m_BlockHeader.Type == NHeader::NBlockType::kSubBlock */)
690 {
691 if (m_FileHeaderData.Size() < m_BlockHeader.HeadSize)
692 m_FileHeaderData.ChangeSize_KeepData(m_BlockHeader.HeadSize, 7);
693 // m_CurData = (Byte *)m_FileHeaderData;
694 // m_PosLimit = m_BlockHeader.HeadSize;
695 if (!ReadBytesAndTestSize(m_FileHeaderData + 7, m_BlockHeader.HeadSize - 7))
696 {
697 error = k_ErrorType_UnexpectedEnd;
698 return S_OK;
699 }
700
701 bool okItem = ReadHeaderReal(m_FileHeaderData + 7, m_BlockHeader.HeadSize - 7, item);
702 if (okItem)
703 {
704 if (!CheckHeaderCrc(m_FileHeaderData, (unsigned)m_BlockHeader.HeadSize - item.CommentSize))
705 {
706 error = k_ErrorType_Corrupted; // ThrowExceptionWithCode(CInArchiveException::kFileHeaderCRCError);
707 return S_OK;
708 }
709 filled = true;
710 }
711
712 FinishCryptoBlock();
713 m_CryptoMode = false;
714 // Move Position to compressed Data;
715 RINOK(InStream_SeekSet(m_Stream, m_Position))
716 AddToSeekValue(item.PackSize); // m_Position points to next header;
717 // if (okItem)
718 return S_OK;
719 /*
720 else
721 continue;
722 */
723 }
724
725 if (m_CryptoMode && m_BlockHeader.HeadSize > (1 << 10))
726 {
727 error = k_ErrorType_DecryptionError;
728 return S_OK;
729 }
730
731 if ((m_BlockHeader.Flags & NHeader::NBlock::kLongBlock) != 0)
732 {
733 if (m_FileHeaderData.Size() < 7 + 4)
734 m_FileHeaderData.ChangeSize_KeepData(7 + 4, 7);
735 if (!ReadBytesAndTestSize(m_FileHeaderData + 7, 4))
736 {
737 error = k_ErrorType_UnexpectedEnd;
738 return S_OK;
739 }
740 UInt32 dataSize = Get32(m_FileHeaderData + 7);
741 AddToSeekValue(dataSize);
742 if (m_CryptoMode && dataSize > (1 << 27))
743 {
744 error = k_ErrorType_DecryptionError;
745 return S_OK;
746 }
747 m_CryptoPos = m_BlockHeader.HeadSize;
748 }
749 else
750 m_CryptoPos = 0;
751
752 {
753 UInt64 newPos = m_Position + m_BlockHeader.HeadSize;
754 if (newPos > ArcInfo.FileSize)
755 {
756 error = k_ErrorType_UnexpectedEnd;
757 return S_OK;
758 }
759 }
760 AddToSeekValue(m_BlockHeader.HeadSize);
761 FinishCryptoBlock();
762 m_CryptoMode = false;
763 }
764 }
765
766
767 static const Byte kProps[] =
768 {
769 kpidPath,
770 kpidIsDir,
771 kpidSize,
772 kpidPackSize,
773 kpidMTime,
774 kpidCTime,
775 kpidATime,
776 kpidAttrib,
777
778 kpidEncrypted,
779 kpidSolid,
780 kpidCommented,
781 kpidSplitBefore,
782 kpidSplitAfter,
783 kpidCRC,
784 kpidHostOS,
785 kpidMethod,
786 kpidUnpackVer,
787
788 kpidVolumeIndex
789 };
790
791 static const Byte kArcProps[] =
792 {
793 kpidTotalPhySize,
794 kpidCharacts,
795 kpidSolid,
796 kpidNumBlocks,
797 // kpidEncrypted,
798 kpidIsVolume,
799 kpidVolumeIndex,
800 kpidNumVolumes
801 // kpidCommented
802 };
803
804 IMP_IInArchive_Props
805 IMP_IInArchive_ArcProps
806
GetPackSize(unsigned refIndex) const807 UInt64 CHandler::GetPackSize(unsigned refIndex) const
808 {
809 const CRefItem &refItem = _refItems[refIndex];
810 UInt64 totalPackSize = 0;
811 for (unsigned i = 0; i < refItem.NumItems; i++)
812 totalPackSize += _items[refItem.ItemIndex + i].PackSize;
813 return totalPackSize;
814 }
815
IsSolid(unsigned refIndex) const816 bool CHandler::IsSolid(unsigned refIndex) const
817 {
818 const CItem &item = _items[_refItems[refIndex].ItemIndex];
819 if (item.UnPackVersion < 20)
820 {
821 if (_arcInfo.IsSolid())
822 return (refIndex > 0);
823 return false;
824 }
825 return item.IsSolid();
826 }
827
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))828 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
829 {
830 COM_TRY_BEGIN
831 NCOM::CPropVariant prop;
832 switch (propID)
833 {
834 case kpidVolumeIndex: if (_arcInfo.Is_VolNumber_Defined()) prop = (UInt32)_arcInfo.VolNumber; break;
835 case kpidSolid: prop = _arcInfo.IsSolid(); break;
836 case kpidCharacts:
837 {
838 AString s (FlagsToString(k_Flags, Z7_ARRAY_SIZE(k_Flags), _arcInfo.Flags));
839 // FLAGS_TO_PROP(k_Flags, _arcInfo.Flags, prop);
840 if (_arcInfo.Is_DataCRC_Defined())
841 {
842 s.Add_Space_if_NotEmpty();
843 s += "VolCRC";
844 }
845 prop = s;
846 break;
847 }
848 // case kpidEncrypted: prop = _arcInfo.IsEncrypted(); break; // it's for encrypted names.
849 case kpidIsVolume: prop = _arcInfo.IsVolume(); break;
850 case kpidNumVolumes: prop = (UInt32)_arcs.Size(); break;
851 case kpidOffset: if (_arcs.Size() == 1 && _arcInfo.StartPos != 0) prop = _arcInfo.StartPos; break;
852
853 case kpidTotalPhySize:
854 {
855 if (_arcs.Size() > 1)
856 {
857 UInt64 sum = 0;
858 FOR_VECTOR (v, _arcs)
859 sum += _arcs[v].PhySize;
860 prop = sum;
861 }
862 break;
863 }
864
865 case kpidPhySize:
866 {
867 if (_arcs.Size() != 0)
868 prop = _arcInfo.GetPhySize();
869 break;
870 }
871
872 // case kpidCommented: prop = _arcInfo.IsCommented(); break;
873
874 case kpidNumBlocks:
875 {
876 UInt32 numBlocks = 0;
877 FOR_VECTOR (i, _refItems)
878 if (!IsSolid(i))
879 numBlocks++;
880 prop = (UInt32)numBlocks;
881 break;
882 }
883
884
885 case kpidError:
886 {
887 // if (!_errorMessage.IsEmpty()) prop = _errorMessage; break;
888
889 if (/* &_missingVol || */ !_missingVolName.IsEmpty())
890 {
891 UString s ("Missing volume : ");
892 s += _missingVolName;
893 prop = s;
894 }
895 break;
896 }
897
898 case kpidErrorFlags:
899 {
900 UInt32 v = _errorFlags;
901 if (!_isArc)
902 v |= kpv_ErrorFlags_IsNotArc;
903 prop = v;
904 break;
905 }
906
907 case kpidWarningFlags:
908 {
909 if (_warningFlags != 0)
910 prop = _warningFlags;
911 break;
912 }
913
914 case kpidExtension:
915 if (_arcs.Size() == 1)
916 {
917 if (_arcInfo.Is_VolNumber_Defined())
918 {
919 AString s ("part");
920 UInt32 v = (UInt32)_arcInfo.VolNumber + 1;
921 if (v < 10)
922 s += '0';
923 s.Add_UInt32(v);
924 s += ".rar";
925 prop = s;
926 }
927 }
928 break;
929 }
930 prop.Detach(value);
931 return S_OK;
932 COM_TRY_END
933 }
934
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))935 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
936 {
937 *numItems = _refItems.Size();
938 return S_OK;
939 }
940
RarTimeToFileTime(const CRarTime & rarTime,FILETIME & ft)941 static bool RarTimeToFileTime(const CRarTime &rarTime, FILETIME &ft)
942 {
943 if (!NTime::DosTime_To_FileTime(rarTime.DosTime, ft))
944 return false;
945 UInt64 v = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
946 v += (UInt32)rarTime.LowSecond * 10000000;
947 v +=
948 ((UInt32)rarTime.SubTime[2] << 16) +
949 ((UInt32)rarTime.SubTime[1] << 8) +
950 ((UInt32)rarTime.SubTime[0]);
951 ft.dwLowDateTime = (DWORD)v;
952 ft.dwHighDateTime = (DWORD)(v >> 32);
953 return true;
954 }
955
RarTimeToProp(const CRarTime & rarTime,NCOM::CPropVariant & prop)956 static void RarTimeToProp(const CRarTime &rarTime, NCOM::CPropVariant &prop)
957 {
958 FILETIME localFileTime, utc;
959 if (RarTimeToFileTime(rarTime, localFileTime)
960 && LocalFileTimeToFileTime(&localFileTime, &utc))
961 prop.SetAsTimeFrom_FT_Prec(utc, k_PropVar_TimePrec_100ns);
962 /*
963 else
964 utc.dwHighDateTime = utc.dwLowDateTime = 0;
965 // prop.SetAsTimeFrom_FT_Prec(utc, k_PropVar_TimePrec_100ns);
966 */
967 }
968
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))969 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
970 {
971 COM_TRY_BEGIN
972 NCOM::CPropVariant prop;
973 const CRefItem &refItem = _refItems[index];
974 const CItem &item = _items[refItem.ItemIndex];
975 const CItem &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1];
976
977 /*
978 const CItem *mainItem = &item;
979 if (item.BaseFileIndex >= 0)
980 mainItem = &_items[_refItems[item.BaseFileIndex].ItemIndex];
981 */
982 switch (propID)
983 {
984 case kpidPath:
985 {
986 /*
987 UString u;
988 if (item.BaseFileIndex >= 0)
989 u = mainItem->GetName();
990 u += item.GetName();
991 */
992 prop = (const wchar_t *)NItemName::WinPathToOsPath(item.GetName());
993 break;
994 }
995 case kpidIsDir: prop = item.IsDir(); break;
996 case kpidSize: if (lastItem.Is_Size_Defined()) prop = lastItem.Size; break;
997 case kpidPackSize: prop = GetPackSize(index); break;
998 case kpidMTime: RarTimeToProp(item.MTime, prop); break;
999 case kpidCTime: if (item.CTimeDefined) RarTimeToProp(item.CTime, prop); break;
1000 case kpidATime: if (item.ATimeDefined) RarTimeToProp(item.ATime, prop); break;
1001 case kpidAttrib: prop = item.GetWinAttrib(); break;
1002 case kpidEncrypted: prop = item.IsEncrypted(); break;
1003 case kpidSolid: prop = IsSolid(index); break;
1004 case kpidCommented: prop = item.IsCommented(); break;
1005 case kpidSplitBefore: prop = item.IsSplitBefore(); break;
1006 case kpidSplitAfter: prop = _items[refItem.ItemIndex + refItem.NumItems - 1].IsSplitAfter(); break;
1007
1008 case kpidVolumeIndex:
1009 if (_arcInfo.Is_VolNumber_Defined())
1010 prop = (UInt32)(_arcInfo.VolNumber + refItem.VolumeIndex);
1011 break;
1012
1013 case kpidCRC:
1014 {
1015 prop = ((lastItem.IsSplitAfter()) ? item.FileCRC : lastItem.FileCRC);
1016 break;
1017 }
1018 case kpidUnpackVer: prop = item.UnPackVersion; break;
1019 case kpidMethod:
1020 {
1021 char s[16];
1022 Byte m = item.Method;
1023 if (m < (Byte)'0' || m > (Byte)'5')
1024 ConvertUInt32ToString(m, s);
1025 else
1026 {
1027 s[0] = 'm';
1028 s[1] = (char)m;
1029 s[2] = 0;
1030 if (!item.IsDir())
1031 {
1032 s[2] = ':';
1033 ConvertUInt32ToString(16 + item.GetDictSize(), &s[3]);
1034 }
1035 }
1036 prop = s;
1037 break;
1038 }
1039 case kpidHostOS:
1040 TYPE_TO_PROP(kHostOS, item.HostOS, prop);
1041 break;
1042 }
1043 prop.Detach(value);
1044 return S_OK;
1045 COM_TRY_END
1046 }
1047
1048
Open2(IInStream * stream,const UInt64 * maxCheckStartPosition,IArchiveOpenCallback * openCallback)1049 HRESULT CHandler::Open2(IInStream *stream,
1050 const UInt64 *maxCheckStartPosition,
1051 IArchiveOpenCallback *openCallback)
1052 {
1053 {
1054 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
1055 CMyComPtr<ICryptoGetTextPassword> getTextPassword;
1056
1057 CVolumeName seqName;
1058
1059 UInt64 totalBytes = 0;
1060 UInt64 curBytes = 0;
1061
1062 if (openCallback)
1063 {
1064 openCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
1065 openCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword);
1066 }
1067
1068 bool nextVol_is_Required = false;
1069
1070 CInArchive archive;
1071
1072 for (;;)
1073 {
1074 CMyComPtr<IInStream> inStream;
1075 if (!_arcs.IsEmpty())
1076 {
1077 if (!openVolumeCallback)
1078 break;
1079
1080 if (_arcs.Size() == 1)
1081 {
1082 if (!_arcInfo.IsVolume())
1083 break;
1084 UString baseName;
1085 {
1086 NCOM::CPropVariant prop;
1087 RINOK(openVolumeCallback->GetProperty(kpidName, &prop))
1088 if (prop.vt != VT_BSTR)
1089 break;
1090 baseName = prop.bstrVal;
1091 }
1092 if (!seqName.InitName(baseName, _arcInfo.HaveNewVolumeName()))
1093 break;
1094 /*
1095 if (_arcInfo.HaveNewVolumeName() && !_arcInfo.IsFirstVolume())
1096 {
1097 seqName.MakeBeforeFirstName();
1098 }
1099 */
1100 }
1101
1102 const UString volName = seqName.GetNextName();
1103
1104 HRESULT result = openVolumeCallback->GetStream(volName, &inStream);
1105
1106 if (result != S_OK && result != S_FALSE)
1107 return result;
1108
1109 if (!inStream || result != S_OK)
1110 {
1111 if (nextVol_is_Required)
1112 _missingVolName = volName;
1113 break;
1114 }
1115 }
1116 else
1117 inStream = stream;
1118
1119 UInt64 endPos;
1120 RINOK(InStream_AtBegin_GetSize(inStream, endPos))
1121 if (openCallback)
1122 {
1123 totalBytes += endPos;
1124 RINOK(openCallback->SetTotal(NULL, &totalBytes))
1125 }
1126
1127 RINOK(archive.Open(inStream, maxCheckStartPosition))
1128 _isArc = true;
1129 CItem item;
1130
1131 for (;;)
1132 {
1133 if (archive.m_Position > endPos)
1134 {
1135 _errorFlags |= kpv_ErrorFlags_UnexpectedEnd;
1136 break;
1137 }
1138
1139 EErrorType error;
1140 // bool decryptionError;
1141 // AString errorMessageLoc;
1142 bool filled;
1143 HRESULT result = archive.GetNextItem(item, getTextPassword, filled, error);
1144
1145 if (error != k_ErrorType_OK)
1146 {
1147 if (error == k_ErrorType_UnexpectedEnd)
1148 _errorFlags |= kpv_ErrorFlags_UnexpectedEnd;
1149 else if (error == k_ErrorType_Corrupted)
1150 _errorFlags |= kpv_ErrorFlags_HeadersError;
1151 else if (error == k_ErrorType_DecryptionError)
1152 _errorFlags |= kpv_ErrorFlags_EncryptedHeadersError;
1153
1154 // AddErrorMessage(errorMessageLoc);
1155 }
1156 RINOK(result)
1157
1158 if (!filled)
1159 {
1160 if (error == k_ErrorType_DecryptionError && _items.IsEmpty())
1161 return S_FALSE;
1162
1163 if (archive.ArcInfo.ExtraZeroTail_is_Possible())
1164 {
1165 /* if there is recovery record for multivolume archive,
1166 RAR adds 18 bytes (ZERO bytes) at the end for alignment.
1167 We must skip these bytes to prevent phySize warning. */
1168 RINOK(InStream_SeekSet(inStream, archive.ArcInfo.EndPos))
1169 bool areThereNonZeros;
1170 UInt64 numZeros;
1171 const UInt64 maxSize = 1 << 12;
1172 RINOK(ReadZeroTail(inStream, areThereNonZeros, numZeros, maxSize))
1173 if (!areThereNonZeros && numZeros != 0 && numZeros <= maxSize)
1174 archive.ArcInfo.EndPos += numZeros;
1175 }
1176 break;
1177 }
1178
1179 if (item.IgnoreItem())
1180 continue;
1181
1182 bool needAdd = true;
1183
1184 if (item.IsSplitBefore())
1185 {
1186 if (!_refItems.IsEmpty())
1187 {
1188 CRefItem &refItem = _refItems.Back();
1189 refItem.NumItems++;
1190 needAdd = false;
1191 }
1192 }
1193
1194 if (needAdd)
1195 {
1196 CRefItem refItem;
1197 refItem.ItemIndex = _items.Size();
1198 refItem.NumItems = 1;
1199 refItem.VolumeIndex = _arcs.Size();
1200 _refItems.Add(refItem);
1201 }
1202
1203 _items.Add(item);
1204
1205 if (openCallback && _items.Size() % 100 == 0)
1206 {
1207 UInt64 numFiles = _items.Size();
1208 UInt64 numBytes = curBytes + item.Position;
1209 RINOK(openCallback->SetCompleted(&numFiles, &numBytes))
1210 }
1211 }
1212
1213 if (archive.HeaderErrorWarning)
1214 _warningFlags |= kpv_ErrorFlags_HeadersError;
1215
1216 /*
1217 if (archive.m_Position < endPos)
1218 _warningFlags |= kpv_ErrorFlags_DataAfterEnd;
1219 */
1220 if (_arcs.IsEmpty())
1221 _arcInfo = archive.ArcInfo;
1222 // _arcInfo.EndPos = archive.EndPos;
1223
1224 curBytes += endPos;
1225 {
1226 CArc &arc = _arcs.AddNew();
1227 arc.PhySize = archive.ArcInfo.GetPhySize();
1228 arc.Stream = inStream;
1229 }
1230
1231 nextVol_is_Required = false;
1232
1233 if (!archive.ArcInfo.IsVolume())
1234 break;
1235
1236 if (archive.ArcInfo.EndOfArchive_was_Read)
1237 {
1238 if (!archive.ArcInfo.AreMoreVolumes())
1239 break;
1240 nextVol_is_Required = true;
1241 }
1242 }
1243 }
1244
1245 /*
1246 int baseFileIndex = -1;
1247 for (unsigned i = 0; i < _refItems.Size(); i++)
1248 {
1249 CItem &item = _items[_refItems[i].ItemIndex];
1250 if (item.IsAltStream)
1251 item.BaseFileIndex = baseFileIndex;
1252 else
1253 baseFileIndex = i;
1254 }
1255 */
1256 return S_OK;
1257 }
1258
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 * maxCheckStartPosition,IArchiveOpenCallback * openCallback))1259 Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
1260 const UInt64 *maxCheckStartPosition,
1261 IArchiveOpenCallback *openCallback))
1262 {
1263 COM_TRY_BEGIN
1264 Close();
1265 // try
1266 {
1267 HRESULT res = Open2(stream, maxCheckStartPosition, openCallback);
1268 /*
1269 if (res != S_OK)
1270 Close();
1271 */
1272
1273 return res;
1274 }
1275 // catch(const CInArchiveException &) { Close(); return S_FALSE; }
1276 // catch(...) { Close(); throw; }
1277 COM_TRY_END
1278 }
1279
Z7_COM7F_IMF(CHandler::Close ())1280 Z7_COM7F_IMF(CHandler::Close())
1281 {
1282 COM_TRY_BEGIN
1283 // _errorMessage.Empty();
1284 _missingVolName.Empty();
1285 _errorFlags = 0;
1286 _warningFlags = 0;
1287 _isArc = false;
1288 _refItems.Clear();
1289 _items.Clear();
1290 _arcs.Clear();
1291 return S_OK;
1292 COM_TRY_END
1293 }
1294
1295 struct CMethodItem
1296 {
1297 Byte RarUnPackVersion;
1298 CMyComPtr<ICompressCoder> Coder;
1299 };
1300
1301
1302 Z7_CLASS_IMP_NOQIB_1(
1303 CVolsInStream
1304 , ISequentialInStream
1305 )
1306 UInt64 _rem;
1307 ISequentialInStream *_stream;
1308 const CObjectVector<CArc> *_arcs;
1309 const CObjectVector<CItem> *_items;
1310 CRefItem _refItem;
1311 unsigned _curIndex;
1312 UInt32 _crc;
1313 bool _calcCrc;
1314
1315 public:
1316 void Init(const CObjectVector<CArc> *arcs,
1317 const CObjectVector<CItem> *items,
1318 const CRefItem &refItem)
1319 {
1320 _arcs = arcs;
1321 _items = items;
1322 _refItem = refItem;
1323 _curIndex = 0;
1324 _stream = NULL;
1325 CrcIsOK = true;
1326 }
1327
1328 bool CrcIsOK;
1329 };
1330
1331
1332 Z7_COM7F_IMF(CVolsInStream::Read(void *data, UInt32 size, UInt32 *processedSize))
1333 {
1334 if (processedSize)
1335 *processedSize = 0;
1336 UInt32 realProcessedSize = 0;
1337
1338 while (size != 0)
1339 {
1340 if (!_stream)
1341 {
1342 if (_curIndex >= _refItem.NumItems)
1343 break;
1344 const CItem &item = (*_items)[_refItem.ItemIndex + _curIndex];
1345 unsigned volIndex = _refItem.VolumeIndex + _curIndex;
1346 if (volIndex >= _arcs->Size())
1347 {
1348 return S_OK;
1349 // return S_FALSE;
1350 }
1351 IInStream *s = (*_arcs)[volIndex].Stream;
1352 RINOK(InStream_SeekSet(s, item.GetDataPosition()))
1353 _stream = s;
1354 _calcCrc = (CrcIsOK && item.IsSplitAfter());
1355 _crc = CRC_INIT_VAL;
1356 _rem = item.PackSize;
1357 }
1358 {
1359 UInt32 cur = size;
1360 if (cur > _rem)
1361 cur = (UInt32)_rem;
1362 UInt32 num = cur;
1363 HRESULT res = _stream->Read(data, cur, &cur);
1364 if (_calcCrc)
1365 _crc = CrcUpdate(_crc, data, cur);
1366 realProcessedSize += cur;
1367 if (processedSize)
1368 *processedSize = realProcessedSize;
1369 data = (Byte *)data + cur;
1370 size -= cur;
1371 _rem -= cur;
1372 if (_rem == 0)
1373 {
1374 const CItem &item = (*_items)[_refItem.ItemIndex + _curIndex];
1375 _curIndex++;
1376 if (_calcCrc && CRC_GET_DIGEST(_crc) != item.FileCRC)
1377 CrcIsOK = false;
1378 _stream = NULL;
1379 }
1380 if (res != S_OK)
1381 return res;
1382 if (realProcessedSize != 0)
1383 return S_OK;
1384 if (cur == 0 && num != 0)
1385 return S_OK;
1386 }
1387 }
1388
1389 return S_OK;
1390 }
1391
1392 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1393 Int32 testMode, IArchiveExtractCallback *extractCallback))
1394 {
1395 COM_TRY_BEGIN
1396 CMyComPtr<ICryptoGetTextPassword> getTextPassword;
1397 UInt64 // censoredTotalUnPacked = 0,
1398 // censoredTotalPacked = 0,
1399 importantTotalUnPacked = 0;
1400 // importantTotalPacked = 0;
1401 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1402 if (allFilesMode)
1403 numItems = _refItems.Size();
1404 if (numItems == 0)
1405 return S_OK;
1406 unsigned lastIndex = 0;
1407 CRecordVector<unsigned> importantIndexes;
1408 CRecordVector<bool> extractStatuses;
1409
1410 bool isThereUndefinedSize = false;
1411
1412 for (UInt32 t = 0; t < numItems; t++)
1413 {
1414 unsigned index = allFilesMode ? t : indices[t];
1415
1416 {
1417 const CRefItem &refItem = _refItems[index];
1418 const CItem &item = _items[refItem.ItemIndex + refItem.NumItems - 1];
1419
1420 if (item.Is_Size_Defined())
1421 {
1422 // censoredTotalUnPacked += item.Size;
1423 }
1424 else
1425 isThereUndefinedSize = true;
1426
1427 // censoredTotalPacked += item.PackSize;
1428 }
1429
1430 unsigned j;
1431 for (j = lastIndex; j <= index; j++)
1432 // if (!_items[_refItems[j].ItemIndex].IsSolid())
1433 if (!IsSolid(j))
1434 lastIndex = j;
1435
1436 for (j = lastIndex; j <= index; j++)
1437 {
1438 const CRefItem &refItem = _refItems[j];
1439 const CItem &item = _items[refItem.ItemIndex + refItem.NumItems - 1];
1440
1441 if (item.Is_Size_Defined())
1442 importantTotalUnPacked += item.Size;
1443 else
1444 isThereUndefinedSize = true;
1445 // importantTotalPacked += item.PackSize;
1446 importantIndexes.Add(j);
1447 extractStatuses.Add(j == index);
1448 }
1449
1450 lastIndex = index + 1;
1451 }
1452
1453 if (importantTotalUnPacked != 0 || !isThereUndefinedSize)
1454 {
1455 RINOK(extractCallback->SetTotal(importantTotalUnPacked))
1456 }
1457
1458 UInt64 currentImportantTotalUnPacked = 0;
1459 UInt64 currentImportantTotalPacked = 0;
1460 UInt64 currentUnPackSize, currentPackSize;
1461
1462 CObjectVector<CMethodItem> methodItems;
1463
1464 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
1465 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
1466
1467 CFilterCoder *filterStreamSpec = new CFilterCoder(false);
1468 CMyComPtr<ISequentialInStream> filterStream = filterStreamSpec;
1469
1470 NCrypto::NRar2::CDecoder *rar20CryptoDecoderSpec = NULL;
1471 CMyComPtr<ICompressFilter> rar20CryptoDecoder;
1472 NCrypto::NRar3::CDecoder *rar3CryptoDecoderSpec = NULL;
1473 CMyComPtr<ICompressFilter> rar3CryptoDecoder;
1474
1475 CVolsInStream *volsInStreamSpec = NULL;
1476 CMyComPtr<ISequentialInStream> volsInStream;
1477
1478 CLocalProgress *lps = new CLocalProgress;
1479 CMyComPtr<ICompressProgressInfo> progress = lps;
1480 lps->Init(extractCallback, false);
1481
1482 bool solidStart = true;
1483
1484 for (unsigned i = 0;;
1485 i++,
1486 currentImportantTotalUnPacked += currentUnPackSize,
1487 currentImportantTotalPacked += currentPackSize)
1488 {
1489 lps->InSize = currentImportantTotalPacked;
1490 lps->OutSize = currentImportantTotalUnPacked;
1491 RINOK(lps->SetCur())
1492
1493 if (i >= importantIndexes.Size())
1494 break;
1495
1496 CMyComPtr<ISequentialOutStream> realOutStream;
1497
1498 Int32 askMode;
1499 if (extractStatuses[i])
1500 askMode = testMode ?
1501 NExtract::NAskMode::kTest :
1502 NExtract::NAskMode::kExtract;
1503 else
1504 askMode = NExtract::NAskMode::kSkip;
1505
1506 UInt32 index = importantIndexes[i];
1507
1508 const CRefItem &refItem = _refItems[index];
1509 const CItem &item = _items[refItem.ItemIndex];
1510 const CItem &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1];
1511
1512 UInt64 outSize = (UInt64)(Int64)-1;
1513 currentUnPackSize = 0;
1514 if (lastItem.Is_Size_Defined())
1515 {
1516 outSize = lastItem.Size;
1517 currentUnPackSize = outSize;
1518 }
1519
1520 currentPackSize = GetPackSize(index);
1521
1522 if (item.IgnoreItem())
1523 continue;
1524
1525 RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
1526
1527 if (!IsSolid(index))
1528 solidStart = true;
1529 if (item.IsDir())
1530 {
1531 RINOK(extractCallback->PrepareOperation(askMode))
1532 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
1533 continue;
1534 }
1535
1536 bool mustBeProcessedAnywhere = false;
1537 if (i < importantIndexes.Size() - 1)
1538 {
1539 // const CRefItem &nextRefItem = _refItems[importantIndexes[i + 1]];
1540 // const CItem &nextItemInfo = _items[nextRefItem.ItemIndex];
1541 // mustBeProcessedAnywhere = nextItemInfo.IsSolid();
1542 mustBeProcessedAnywhere = IsSolid(importantIndexes[i + 1]);
1543 }
1544
1545 if (!mustBeProcessedAnywhere && !testMode && !realOutStream)
1546 continue;
1547
1548 if (!realOutStream && !testMode)
1549 askMode = NExtract::NAskMode::kSkip;
1550
1551 RINOK(extractCallback->PrepareOperation(askMode))
1552
1553 COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
1554 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
1555 outStreamSpec->SetStream(realOutStream);
1556 outStreamSpec->Init();
1557 realOutStream.Release();
1558
1559 if (!volsInStream)
1560 {
1561 volsInStreamSpec = new CVolsInStream;
1562 volsInStream = volsInStreamSpec;
1563 }
1564
1565 volsInStreamSpec->Init(&_arcs, &_items, refItem);
1566
1567 UInt64 packSize = currentPackSize;
1568
1569 // packedPos += item.PackSize;
1570 // unpackedPos += 0;
1571
1572 CMyComPtr<ISequentialInStream> inStream;
1573
1574 if (item.IsEncrypted())
1575 {
1576 // CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
1577
1578 if (item.UnPackVersion >= 29)
1579 {
1580 if (!rar3CryptoDecoder)
1581 {
1582 rar3CryptoDecoderSpec = new NCrypto::NRar3::CDecoder;
1583 rar3CryptoDecoder = rar3CryptoDecoderSpec;
1584 }
1585 // rar3CryptoDecoderSpec->SetRar350Mode(item.UnPackVersion < 36);
1586 /*
1587 CMyComPtr<ICompressSetDecoderProperties2> cryptoProperties;
1588 RINOK(rar3CryptoDecoder.QueryInterface(IID_ICompressSetDecoderProperties2,
1589 &cryptoProperties));
1590 */
1591 RINOK(rar3CryptoDecoderSpec->SetDecoderProperties2(item.Salt, item.HasSalt() ? sizeof(item.Salt) : 0))
1592 filterStreamSpec->Filter = rar3CryptoDecoder;
1593 }
1594 else if (item.UnPackVersion >= 20)
1595 {
1596 if (!rar20CryptoDecoder)
1597 {
1598 rar20CryptoDecoderSpec = new NCrypto::NRar2::CDecoder;
1599 rar20CryptoDecoder = rar20CryptoDecoderSpec;
1600 }
1601 filterStreamSpec->Filter = rar20CryptoDecoder;
1602 }
1603 else
1604 {
1605 outStream.Release();
1606 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnsupportedMethod))
1607 continue;
1608 }
1609
1610 // RINOK(filterStreamSpec->Filter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword));
1611
1612 if (!getTextPassword)
1613 extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword);
1614
1615 if (!getTextPassword)
1616 {
1617 outStream.Release();
1618 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnsupportedMethod))
1619 continue;
1620 }
1621
1622 // if (getTextPassword)
1623 {
1624 CMyComBSTR_Wipe password;
1625 RINOK(getTextPassword->CryptoGetTextPassword(&password))
1626
1627 if (item.UnPackVersion >= 29)
1628 {
1629 unsigned len = 0;
1630 if (password)
1631 len = MyStringLen(password);
1632 if (len > kPasswordLen_MAX)
1633 len = kPasswordLen_MAX;
1634 CByteBuffer_Wipe buffer(len * 2);
1635 for (unsigned k = 0; k < len; k++)
1636 {
1637 wchar_t c = password[k];
1638 ((Byte *)buffer)[k * 2] = (Byte)c;
1639 ((Byte *)buffer)[k * 2 + 1] = (Byte)(c >> 8);
1640 }
1641 rar3CryptoDecoderSpec->SetPassword((const Byte *)buffer, len * 2);
1642 }
1643 else
1644 {
1645 AString_Wipe oemPassword;
1646 if (password)
1647 {
1648 UString_Wipe unicode;
1649 unicode.SetFromBstr(password);
1650 if (unicode.Len() > kPasswordLen_MAX)
1651 unicode.DeleteFrom(kPasswordLen_MAX);
1652 UnicodeStringToMultiByte2(oemPassword, unicode, CP_OEMCP);
1653 }
1654 rar20CryptoDecoderSpec->SetPassword((const Byte *)(const char *)oemPassword, oemPassword.Len());
1655 }
1656 }
1657 /*
1658 else
1659 {
1660 RINOK(cryptoSetPassword->CryptoSetPassword(NULL, 0));
1661 }
1662 */
1663
1664 filterStreamSpec->SetInStream(volsInStream);
1665 filterStreamSpec->SetOutStreamSize(NULL);
1666 inStream = filterStream;
1667 }
1668 else
1669 {
1670 inStream = volsInStream;
1671 }
1672
1673 CMyComPtr<ICompressCoder> commonCoder;
1674
1675 switch (item.Method)
1676 {
1677 case '0':
1678 {
1679 commonCoder = copyCoder;
1680 break;
1681 }
1682 case '1':
1683 case '2':
1684 case '3':
1685 case '4':
1686 case '5':
1687 {
1688 unsigned m;
1689 for (m = 0; m < methodItems.Size(); m++)
1690 if (methodItems[m].RarUnPackVersion == item.UnPackVersion)
1691 break;
1692 if (m == methodItems.Size())
1693 {
1694 CMethodItem mi;
1695 mi.RarUnPackVersion = item.UnPackVersion;
1696
1697 mi.Coder.Release();
1698 if (item.UnPackVersion <= 40)
1699 {
1700 UInt32 methodID = 0x40300;
1701 if (item.UnPackVersion < 20)
1702 methodID += 1;
1703 else if (item.UnPackVersion < 29)
1704 methodID += 2;
1705 else
1706 methodID += 3;
1707 RINOK(CreateCoder_Id(EXTERNAL_CODECS_VARS methodID, false, mi.Coder))
1708 }
1709
1710 if (!mi.Coder)
1711 {
1712 outStream.Release();
1713 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnsupportedMethod))
1714 continue;
1715 }
1716
1717 m = methodItems.Add(mi);
1718 }
1719 CMyComPtr<ICompressCoder> decoder = methodItems[m].Coder;
1720
1721 CMyComPtr<ICompressSetDecoderProperties2> compressSetDecoderProperties;
1722 RINOK(decoder.QueryInterface(IID_ICompressSetDecoderProperties2,
1723 &compressSetDecoderProperties))
1724
1725 Byte isSolid = (Byte)((IsSolid(index) || item.IsSplitBefore()) ? 1: 0);
1726 if (solidStart)
1727 {
1728 isSolid = 0;
1729 solidStart = false;
1730 }
1731
1732
1733 RINOK(compressSetDecoderProperties->SetDecoderProperties2(&isSolid, 1))
1734
1735 commonCoder = decoder;
1736 break;
1737 }
1738 default:
1739 outStream.Release();
1740 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnsupportedMethod))
1741 continue;
1742 }
1743
1744 HRESULT result = commonCoder->Code(inStream, outStream, &packSize, &outSize, progress);
1745
1746 if (item.IsEncrypted())
1747 filterStreamSpec->ReleaseInStream();
1748
1749 if (outSize == (UInt64)(Int64)-1)
1750 currentUnPackSize = outStreamSpec->GetSize();
1751
1752 int opRes = (volsInStreamSpec->CrcIsOK && outStreamSpec->GetCRC() == lastItem.FileCRC) ?
1753 NExtract::NOperationResult::kOK:
1754 NExtract::NOperationResult::kCRCError;
1755 outStream.Release();
1756
1757 if (result != S_OK)
1758 {
1759 if (result == S_FALSE)
1760 opRes = NExtract::NOperationResult::kDataError;
1761 else if (result == E_NOTIMPL)
1762 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1763 else
1764 return result;
1765 }
1766 RINOK(extractCallback->SetOperationResult(opRes))
1767 }
1768
1769 return S_OK;
1770 COM_TRY_END
1771 }
1772
1773 IMPL_ISetCompressCodecsInfo
1774
1775 REGISTER_ARC_I(
1776 "Rar", "rar r00", NULL, 3,
1777 kMarker,
1778 0,
1779 NArcInfoFlags::kFindSignature,
1780 NULL)
1781
1782 }}
1783