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