xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Rar/RarHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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