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