1 // RarHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "Common/ComTry.h"
6 #include "Common/IntToString.h"
7 #include "Common/StringConvert.h"
8 
9 #include "Windows/PropVariant.h"
10 #include "Windows/Time.h"
11 
12 #include "../../IPassword.h"
13 
14 #include "../../Common/CreateCoder.h"
15 #include "../../Common/FilterCoder.h"
16 #include "../../Common/MethodId.h"
17 #include "../../Common/ProgressUtils.h"
18 
19 #include "../../Compress/CopyCoder.h"
20 
21 #include "../../Crypto/Rar20Crypto.h"
22 #include "../../Crypto/RarAes.h"
23 
24 #include "../Common/ItemNameUtils.h"
25 #include "../Common/OutStreamWithCRC.h"
26 
27 #include "RarHandler.h"
28 
29 using namespace NWindows;
30 using namespace NTime;
31 
32 namespace NArchive {
33 namespace NRar {
34 
35 static const wchar_t *kHostOS[] =
36 {
37   L"MS DOS",
38   L"OS/2",
39   L"Win32",
40   L"Unix",
41   L"Mac OS",
42   L"BeOS"
43 };
44 
45 static const int kNumHostOSes = sizeof(kHostOS) / sizeof(kHostOS[0]);
46 
47 static const wchar_t *kUnknownOS = L"Unknown";
48 
49 STATPROPSTG kProps[] =
50 {
51   { NULL, kpidPath, VT_BSTR},
52   { NULL, kpidIsDir, VT_BOOL},
53   { NULL, kpidSize, VT_UI8},
54   { NULL, kpidPackSize, VT_UI8},
55   { NULL, kpidMTime, VT_FILETIME},
56   { NULL, kpidCTime, VT_FILETIME},
57   { NULL, kpidATime, VT_FILETIME},
58   { NULL, kpidAttrib, VT_UI4},
59 
60   { NULL, kpidEncrypted, VT_BOOL},
61   { NULL, kpidSolid, VT_BOOL},
62   { NULL, kpidCommented, VT_BOOL},
63   { NULL, kpidSplitBefore, VT_BOOL},
64   { NULL, kpidSplitAfter, VT_BOOL},
65   { NULL, kpidCRC, VT_UI4},
66   { NULL, kpidHostOS, VT_BSTR},
67   { NULL, kpidMethod, VT_BSTR},
68   { NULL, kpidUnpackVer, VT_UI1}
69 };
70 
71 STATPROPSTG kArcProps[] =
72 {
73   { NULL, kpidSolid, VT_BOOL},
74   { NULL, kpidNumBlocks, VT_UI4},
75   // { NULL, kpidEncrypted, VT_BOOL},
76   { NULL, kpidIsVolume, VT_BOOL},
77   { NULL, kpidNumVolumes, VT_UI4},
78   { NULL, kpidPhySize, VT_UI8}
79   // { NULL, kpidCommented, VT_BOOL}
80 };
81 
82 IMP_IInArchive_Props
83 IMP_IInArchive_ArcProps
84 
GetPackSize(int refIndex) const85 UInt64 CHandler::GetPackSize(int refIndex) const
86 {
87   const CRefItem &refItem = _refItems[refIndex];
88   UInt64 totalPackSize = 0;
89   for (int i = 0; i < refItem.NumItems; i++)
90     totalPackSize += _items[refItem.ItemIndex + i].PackSize;
91   return totalPackSize;
92 }
93 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)94 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
95 {
96   // COM_TRY_BEGIN
97   NWindows::NCOM::CPropVariant prop;
98   switch(propID)
99   {
100     case kpidSolid: prop = _archiveInfo.IsSolid(); break;
101     // case kpidEncrypted: prop = _archiveInfo.IsEncrypted(); break; // it's for encrypted names.
102     case kpidIsVolume: prop = _archiveInfo.IsVolume(); break;
103     case kpidNumVolumes: prop = (UInt32)_archives.Size(); break;
104     case kpidOffset: if (_archiveInfo.StartPosition != 0) prop = _archiveInfo.StartPosition; break;
105     // case kpidCommented: prop = _archiveInfo.IsCommented(); break;
106     case kpidNumBlocks:
107     {
108       UInt32 numBlocks = 0;
109       for (int i = 0; i < _refItems.Size(); i++)
110         if (!IsSolid(i))
111           numBlocks++;
112       prop = (UInt32)numBlocks;
113       break;
114     }
115   }
116   prop.Detach(value);
117   return S_OK;
118   // COM_TRY_END
119 }
120 
GetNumberOfItems(UInt32 * numItems)121 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
122 {
123   *numItems = _refItems.Size();
124   return S_OK;
125 }
126 
RarTimeToFileTime(const CRarTime & rarTime,FILETIME & result)127 static bool RarTimeToFileTime(const CRarTime &rarTime, FILETIME &result)
128 {
129   if (!DosTimeToFileTime(rarTime.DosTime, result))
130     return false;
131   UInt64 value =  (((UInt64)result.dwHighDateTime) << 32) + result.dwLowDateTime;
132   value += (UInt64)rarTime.LowSecond * 10000000;
133   value += ((UInt64)rarTime.SubTime[2] << 16) +
134     ((UInt64)rarTime.SubTime[1] << 8) +
135     ((UInt64)rarTime.SubTime[0]);
136   result.dwLowDateTime = (DWORD)value;
137   result.dwHighDateTime = DWORD(value >> 32);
138   return true;
139 }
140 
RarTimeToProp(const CRarTime & rarTime,NWindows::NCOM::CPropVariant & prop)141 static void RarTimeToProp(const CRarTime &rarTime, NWindows::NCOM::CPropVariant &prop)
142 {
143   FILETIME localFileTime, utcFileTime;
144   if (RarTimeToFileTime(rarTime, localFileTime))
145   {
146     if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime))
147       utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
148   }
149   else
150     utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
151   prop = utcFileTime;
152 }
153 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)154 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID,  PROPVARIANT *value)
155 {
156   COM_TRY_BEGIN
157   NWindows::NCOM::CPropVariant prop;
158   const CRefItem &refItem = _refItems[index];
159   const CItemEx &item = _items[refItem.ItemIndex];
160   switch(propID)
161   {
162     case kpidPath:
163     {
164       UString u;
165       if (item.HasUnicodeName() && !item.UnicodeName.IsEmpty())
166         u = item.UnicodeName;
167       else
168         u = MultiByteToUnicodeString(item.Name, CP_OEMCP);
169       prop = (const wchar_t *)NItemName::WinNameToOSName(u);
170       break;
171     }
172     case kpidIsDir: prop = item.IsDir(); break;
173     case kpidSize: prop = item.Size; break;
174     case kpidPackSize: prop = GetPackSize(index); break;
175     case kpidMTime: RarTimeToProp(item.MTime, prop); break;
176     case kpidCTime: if (item.CTimeDefined) RarTimeToProp(item.CTime, prop); break;
177     case kpidATime: if (item.ATimeDefined) RarTimeToProp(item.ATime, prop); break;
178     case kpidAttrib: prop = item.GetWinAttributes(); break;
179     case kpidEncrypted: prop = item.IsEncrypted(); break;
180     case kpidSolid: prop = IsSolid(index); break;
181     case kpidCommented: prop = item.IsCommented(); break;
182     case kpidSplitBefore: prop = item.IsSplitBefore(); break;
183     case kpidSplitAfter: prop = _items[refItem.ItemIndex + refItem.NumItems - 1].IsSplitAfter(); break;
184     case kpidCRC:
185     {
186       const CItemEx &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1];
187       prop = ((lastItem.IsSplitAfter()) ? item.FileCRC : lastItem.FileCRC);
188       break;
189     }
190     case kpidUnpackVer: prop = item.UnPackVersion; break;
191     case kpidMethod:
192     {
193       UString method;
194       if (item.Method >= Byte('0') && item.Method <= Byte('5'))
195       {
196         method = L"m";
197         wchar_t temp[32];
198         ConvertUInt64ToString(item.Method - Byte('0'), temp);
199         method += temp;
200         if (!item.IsDir())
201         {
202           method += L":";
203           ConvertUInt64ToString(16 + item.GetDictSize(), temp);
204           method += temp;
205         }
206       }
207       else
208       {
209         wchar_t temp[32];
210         ConvertUInt64ToString(item.Method, temp);
211         method += temp;
212       }
213       prop = method;
214       break;
215     }
216     case kpidHostOS: prop = (item.HostOS < kNumHostOSes) ? (kHostOS[item.HostOS]) : kUnknownOS; break;
217   }
218   prop.Detach(value);
219   return S_OK;
220   COM_TRY_END
221 }
222 
223 class CVolumeName
224 {
225   bool _first;
226   bool _newStyle;
227   UString _unchangedPart;
228   UString _changedPart;
229   UString _afterPart;
230 public:
CVolumeName()231   CVolumeName(): _newStyle(true) {};
232 
InitName(const UString & name,bool newStyle)233   bool InitName(const UString &name, bool newStyle)
234   {
235     _first = true;
236     _newStyle = newStyle;
237     int dotPos = name.ReverseFind('.');
238     UString basePart = name;
239     if (dotPos >= 0)
240     {
241       UString ext = name.Mid(dotPos + 1);
242       if (ext.CompareNoCase(L"rar") == 0)
243       {
244         _afterPart = name.Mid(dotPos);
245         basePart = name.Left(dotPos);
246       }
247       else if (ext.CompareNoCase(L"exe") == 0)
248       {
249         _afterPart = L".rar";
250         basePart = name.Left(dotPos);
251       }
252       else if (!_newStyle)
253       {
254         if (ext.CompareNoCase(L"000") == 0 ||
255             ext.CompareNoCase(L"001") == 0 ||
256             ext.CompareNoCase(L"r00") == 0 ||
257             ext.CompareNoCase(L"r01") == 0)
258         {
259           _afterPart.Empty();
260           _first = false;
261           _changedPart = ext;
262           _unchangedPart = name.Left(dotPos + 1);
263           return true;
264         }
265       }
266     }
267 
268     if (!_newStyle)
269     {
270       _afterPart.Empty();
271       _unchangedPart = basePart + UString(L".");
272       _changedPart = L"r00";
273       return true;
274     }
275 
276     int numLetters = 1;
277     if (basePart.Right(numLetters) == L"1" || basePart.Right(numLetters) == L"0")
278     {
279       while (numLetters < basePart.Length())
280       {
281         if (basePart[basePart.Length() - numLetters - 1] != '0')
282           break;
283         numLetters++;
284       }
285     }
286     else
287       return false;
288     _unchangedPart = basePart.Left(basePart.Length() - numLetters);
289     _changedPart = basePart.Right(numLetters);
290     return true;
291   }
292 
GetNextName()293   UString GetNextName()
294   {
295     UString newName;
296     if (_newStyle || !_first)
297     {
298       int i;
299       int numLetters = _changedPart.Length();
300       for (i = numLetters - 1; i >= 0; i--)
301       {
302         wchar_t c = _changedPart[i];
303         if (c == L'9')
304         {
305           c = L'0';
306           newName = c + newName;
307           if (i == 0)
308             newName = UString(L'1') + newName;
309           continue;
310         }
311         c++;
312         newName = UString(c) + newName;
313         i--;
314         for (; i >= 0; i--)
315           newName = _changedPart[i] + newName;
316         break;
317       }
318       _changedPart = newName;
319     }
320     _first = false;
321     return _unchangedPart + _changedPart + _afterPart;
322   }
323 };
324 
Open2(IInStream * stream,const UInt64 * maxCheckStartPosition,IArchiveOpenCallback * openArchiveCallback)325 HRESULT CHandler::Open2(IInStream *stream,
326     const UInt64 *maxCheckStartPosition,
327     IArchiveOpenCallback *openArchiveCallback)
328 {
329   {
330     CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
331     CMyComPtr<ICryptoGetTextPassword> getTextPassword;
332     CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openArchiveCallback;
333 
334     CVolumeName seqName;
335 
336     UInt64 totalBytes = 0;
337     UInt64 curBytes = 0;
338 
339     if (openArchiveCallback != NULL)
340     {
341       openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback);
342       openArchiveCallbackWrap.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
343     }
344 
345     for (;;)
346     {
347       CMyComPtr<IInStream> inStream;
348       if (!_archives.IsEmpty())
349       {
350         if (!openVolumeCallback)
351           break;
352 
353         if(_archives.Size() == 1)
354         {
355           if (!_archiveInfo.IsVolume())
356             break;
357           UString baseName;
358           {
359             NCOM::CPropVariant prop;
360             RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
361             if (prop.vt != VT_BSTR)
362               break;
363             baseName = prop.bstrVal;
364           }
365           seqName.InitName(baseName, _archiveInfo.HaveNewVolumeName());
366         }
367 
368         UString fullName = seqName.GetNextName();
369         HRESULT result = openVolumeCallback->GetStream(fullName, &inStream);
370         if (result == S_FALSE)
371           break;
372         if (result != S_OK)
373           return result;
374         if (!stream)
375           break;
376       }
377       else
378         inStream = stream;
379 
380       UInt64 endPos = 0;
381       if (openArchiveCallback != NULL)
382       {
383         RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos));
384         RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
385         totalBytes += endPos;
386         RINOK(openArchiveCallback->SetTotal(NULL, &totalBytes));
387       }
388 
389       NArchive::NRar::CInArchive archive;
390       RINOK(archive.Open(inStream, maxCheckStartPosition));
391 
392       if (_archives.IsEmpty())
393         archive.GetArchiveInfo(_archiveInfo);
394 
395       CItemEx item;
396       for (;;)
397       {
398         HRESULT result = archive.GetNextItem(item, getTextPassword);
399         if (result == S_FALSE)
400           break;
401         RINOK(result);
402         if (item.IgnoreItem())
403           continue;
404 
405         bool needAdd = true;
406         if (item.IsSplitBefore())
407         {
408           if (!_refItems.IsEmpty())
409           {
410             CRefItem &refItem = _refItems.Back();
411             refItem.NumItems++;
412             needAdd = false;
413           }
414         }
415         if (needAdd)
416         {
417           CRefItem refItem;
418           refItem.ItemIndex = _items.Size();
419           refItem.NumItems = 1;
420           refItem.VolumeIndex = _archives.Size();
421           _refItems.Add(refItem);
422         }
423         _items.Add(item);
424         if (openArchiveCallback != NULL && _items.Size() % 100 == 0)
425         {
426           UInt64 numFiles = _items.Size();
427           UInt64 numBytes = curBytes + item.Position;
428           RINOK(openArchiveCallback->SetCompleted(&numFiles, &numBytes));
429         }
430       }
431       curBytes += endPos;
432       _archives.Add(archive);
433     }
434   }
435   return S_OK;
436 }
437 
Open(IInStream * stream,const UInt64 * maxCheckStartPosition,IArchiveOpenCallback * openArchiveCallback)438 STDMETHODIMP CHandler::Open(IInStream *stream,
439     const UInt64 *maxCheckStartPosition,
440     IArchiveOpenCallback *openArchiveCallback)
441 {
442   COM_TRY_BEGIN
443   Close();
444   try
445   {
446     HRESULT res = Open2(stream, maxCheckStartPosition, openArchiveCallback);
447     if (res != S_OK)
448       Close();
449     return res;
450   }
451   catch(const CInArchiveException &) { Close(); return S_FALSE; }
452   catch(...) { Close(); throw; }
453   COM_TRY_END
454 }
455 
Close()456 STDMETHODIMP CHandler::Close()
457 {
458   COM_TRY_BEGIN
459   _refItems.Clear();
460   _items.Clear();
461   _archives.Clear();
462   return S_OK;
463   COM_TRY_END
464 }
465 
466 struct CMethodItem
467 {
468   Byte RarUnPackVersion;
469   CMyComPtr<ICompressCoder> Coder;
470 };
471 
472 
Extract(const UInt32 * indices,UInt32 numItems,Int32 _aTestMode,IArchiveExtractCallback * _anExtractCallback)473 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
474     Int32 _aTestMode, IArchiveExtractCallback *_anExtractCallback)
475 {
476   COM_TRY_BEGIN
477   CMyComPtr<ICryptoGetTextPassword> getTextPassword;
478   bool testMode = (_aTestMode != 0);
479   CMyComPtr<IArchiveExtractCallback> extractCallback = _anExtractCallback;
480   UInt64 censoredTotalUnPacked = 0,
481         // censoredTotalPacked = 0,
482         importantTotalUnPacked = 0;
483         // importantTotalPacked = 0;
484   bool allFilesMode = (numItems == UInt32(-1));
485   if (allFilesMode)
486     numItems = _refItems.Size();
487   if(numItems == 0)
488     return S_OK;
489   int lastIndex = 0;
490   CRecordVector<int> importantIndexes;
491   CRecordVector<bool> extractStatuses;
492 
493   for(UInt32 t = 0; t < numItems; t++)
494   {
495     int index = allFilesMode ? t : indices[t];
496     const CRefItem &refItem = _refItems[index];
497     const CItemEx &item = _items[refItem.ItemIndex];
498     censoredTotalUnPacked += item.Size;
499     // censoredTotalPacked += item.PackSize;
500     int j;
501     for(j = lastIndex; j <= index; j++)
502       // if(!_items[_refItems[j].ItemIndex].IsSolid())
503       if(!IsSolid(j))
504         lastIndex = j;
505     for(j = lastIndex; j <= index; j++)
506     {
507       const CRefItem &refItem = _refItems[j];
508       const CItemEx &item = _items[refItem.ItemIndex];
509 
510       // const CItemEx &item = _items[j];
511 
512       importantTotalUnPacked += item.Size;
513       // importantTotalPacked += item.PackSize;
514       importantIndexes.Add(j);
515       extractStatuses.Add(j == index);
516     }
517     lastIndex = index + 1;
518   }
519 
520   extractCallback->SetTotal(importantTotalUnPacked);
521   UInt64 currentImportantTotalUnPacked = 0;
522   UInt64 currentImportantTotalPacked = 0;
523   UInt64 currentUnPackSize, currentPackSize;
524 
525   CObjectVector<CMethodItem> methodItems;
526 
527   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
528   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
529 
530   CFilterCoder *filterStreamSpec = new CFilterCoder;
531   CMyComPtr<ISequentialInStream> filterStream = filterStreamSpec;
532 
533   NCrypto::NRar20::CDecoder *rar20CryptoDecoderSpec = NULL;
534   CMyComPtr<ICompressFilter> rar20CryptoDecoder;
535   NCrypto::NRar29::CDecoder *rar29CryptoDecoderSpec = NULL;
536   CMyComPtr<ICompressFilter> rar29CryptoDecoder;
537 
538   CFolderInStream *folderInStreamSpec = NULL;
539   CMyComPtr<ISequentialInStream> folderInStream;
540 
541   CLocalProgress *lps = new CLocalProgress;
542   CMyComPtr<ICompressProgressInfo> progress = lps;
543   lps->Init(extractCallback, false);
544 
545   bool solidStart = true;
546   for(int i = 0; i < importantIndexes.Size(); i++,
547       currentImportantTotalUnPacked += currentUnPackSize,
548       currentImportantTotalPacked += currentPackSize)
549   {
550     lps->InSize = currentImportantTotalPacked;
551     lps->OutSize = currentImportantTotalUnPacked;
552     RINOK(lps->SetCur());
553     CMyComPtr<ISequentialOutStream> realOutStream;
554 
555     Int32 askMode;
556     if(extractStatuses[i])
557       askMode = testMode ?
558           NArchive::NExtract::NAskMode::kTest :
559           NArchive::NExtract::NAskMode::kExtract;
560     else
561       askMode = NArchive::NExtract::NAskMode::kSkip;
562 
563     UInt32 index = importantIndexes[i];
564 
565     const CRefItem &refItem = _refItems[index];
566     const CItemEx &item = _items[refItem.ItemIndex];
567 
568     currentUnPackSize = item.Size;
569 
570     currentPackSize = GetPackSize(index);
571 
572     if(item.IgnoreItem())
573       continue;
574 
575     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
576 
577     if (!IsSolid(index))
578       solidStart = true;
579     if(item.IsDir())
580     {
581       RINOK(extractCallback->PrepareOperation(askMode));
582       RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
583       continue;
584     }
585 
586     bool mustBeProcessedAnywhere = false;
587     if(i < importantIndexes.Size() - 1)
588     {
589       // const CRefItem &nextRefItem = _refItems[importantIndexes[i + 1]];
590       // const CItemEx &nextItemInfo = _items[nextRefItem.ItemIndex];
591       // mustBeProcessedAnywhere = nextItemInfo.IsSolid();
592       mustBeProcessedAnywhere = IsSolid(importantIndexes[i + 1]);
593     }
594 
595     if (!mustBeProcessedAnywhere && !testMode && !realOutStream)
596       continue;
597 
598     if (!realOutStream && !testMode)
599       askMode = NArchive::NExtract::NAskMode::kSkip;
600 
601     RINOK(extractCallback->PrepareOperation(askMode));
602 
603     COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
604     CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
605     outStreamSpec->SetStream(realOutStream);
606     outStreamSpec->Init();
607     realOutStream.Release();
608 
609     /*
610     for (int partIndex = 0; partIndex < 1; partIndex++)
611     {
612     CMyComPtr<ISequentialInStream> inStream;
613 
614     // item redefinition
615     const CItemEx &item = _items[refItem.ItemIndex + partIndex];
616 
617     NArchive::NRar::CInArchive &archive = _archives[refItem.VolumeIndex + partIndex];
618 
619     inStream.Attach(archive.CreateLimitedStream(item.GetDataPosition(),
620       item.PackSize));
621     */
622     if (!folderInStream)
623     {
624       folderInStreamSpec = new CFolderInStream;
625       folderInStream = folderInStreamSpec;
626     }
627 
628     folderInStreamSpec->Init(&_archives, &_items, refItem);
629 
630     UInt64 packSize = currentPackSize;
631 
632     // packedPos += item.PackSize;
633     // unpackedPos += 0;
634 
635     CMyComPtr<ISequentialInStream> inStream;
636     if (item.IsEncrypted())
637     {
638       CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
639       if (item.UnPackVersion >= 29)
640       {
641         if (!rar29CryptoDecoder)
642         {
643           rar29CryptoDecoderSpec = new NCrypto::NRar29::CDecoder;
644           rar29CryptoDecoder = rar29CryptoDecoderSpec;
645           // RINOK(rar29CryptoDecoder.CoCreateInstance(CLSID_CCryptoRar29Decoder));
646         }
647         rar29CryptoDecoderSpec->SetRar350Mode(item.UnPackVersion < 36);
648         CMyComPtr<ICompressSetDecoderProperties2> cryptoProperties;
649         RINOK(rar29CryptoDecoder.QueryInterface(IID_ICompressSetDecoderProperties2,
650             &cryptoProperties));
651         RINOK(cryptoProperties->SetDecoderProperties2(item.Salt, item.HasSalt() ? sizeof(item.Salt) : 0));
652         filterStreamSpec->Filter = rar29CryptoDecoder;
653       }
654       else if (item.UnPackVersion >= 20)
655       {
656         if (!rar20CryptoDecoder)
657         {
658           rar20CryptoDecoderSpec = new NCrypto::NRar20::CDecoder;
659           rar20CryptoDecoder = rar20CryptoDecoderSpec;
660           // RINOK(rar20CryptoDecoder.CoCreateInstance(CLSID_CCryptoRar20Decoder));
661         }
662         filterStreamSpec->Filter = rar20CryptoDecoder;
663       }
664       else
665       {
666         outStream.Release();
667         RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
668         continue;
669       }
670       RINOK(filterStreamSpec->Filter.QueryInterface(IID_ICryptoSetPassword,
671           &cryptoSetPassword));
672 
673       if (!getTextPassword)
674         extractCallback.QueryInterface(IID_ICryptoGetTextPassword,
675           &getTextPassword);
676       if (getTextPassword)
677       {
678         CMyComBSTR password;
679         RINOK(getTextPassword->CryptoGetTextPassword(&password));
680         if (item.UnPackVersion >= 29)
681         {
682           CByteBuffer buffer;
683           UString unicodePassword(password);
684           const UInt32 sizeInBytes = unicodePassword.Length() * 2;
685           buffer.SetCapacity(sizeInBytes);
686           for (int i = 0; i < unicodePassword.Length(); i++)
687           {
688             wchar_t c = unicodePassword[i];
689             ((Byte *)buffer)[i * 2] = (Byte)c;
690             ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
691           }
692           RINOK(cryptoSetPassword->CryptoSetPassword(
693             (const Byte *)buffer, sizeInBytes));
694         }
695         else
696         {
697           AString oemPassword = UnicodeStringToMultiByte(
698             (const wchar_t *)password, CP_OEMCP);
699           RINOK(cryptoSetPassword->CryptoSetPassword(
700             (const Byte *)(const char *)oemPassword, oemPassword.Length()));
701         }
702       }
703       else
704       {
705         RINOK(cryptoSetPassword->CryptoSetPassword(0, 0));
706       }
707       filterStreamSpec->SetInStream(folderInStream);
708       inStream = filterStream;
709     }
710     else
711     {
712       inStream = folderInStream;
713     }
714     CMyComPtr<ICompressCoder> commonCoder;
715     switch(item.Method)
716     {
717       case '0':
718       {
719         commonCoder = copyCoder;
720         break;
721       }
722       case '1':
723       case '2':
724       case '3':
725       case '4':
726       case '5':
727       {
728         /*
729         if (item.UnPackVersion >= 29)
730         {
731           outStream.Release();
732           RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
733           continue;
734         }
735         */
736         int m;
737         for (m = 0; m < methodItems.Size(); m++)
738           if (methodItems[m].RarUnPackVersion == item.UnPackVersion)
739             break;
740         if (m == methodItems.Size())
741         {
742           CMethodItem mi;
743           mi.RarUnPackVersion = item.UnPackVersion;
744 
745           mi.Coder.Release();
746           if (item.UnPackVersion <= 30)
747           {
748             UInt32 methodID = 0x040300;
749             if (item.UnPackVersion < 20)
750               methodID += 1;
751             else if (item.UnPackVersion < 29)
752               methodID += 2;
753             else
754               methodID += 3;
755             RINOK(CreateCoder(EXTERNAL_CODECS_VARS methodID, mi.Coder, false));
756           }
757 
758           if (mi.Coder == 0)
759           {
760             outStream.Release();
761             RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
762             continue;
763           }
764 
765           m = methodItems.Add(mi);
766         }
767         CMyComPtr<ICompressCoder> decoder = methodItems[m].Coder;
768 
769         CMyComPtr<ICompressSetDecoderProperties2> compressSetDecoderProperties;
770         RINOK(decoder.QueryInterface(IID_ICompressSetDecoderProperties2,
771             &compressSetDecoderProperties));
772 
773         Byte isSolid = (Byte)((IsSolid(index) || item.IsSplitBefore()) ? 1: 0);
774         if (solidStart)
775         {
776           isSolid = false;
777           solidStart = false;
778         }
779 
780 
781         RINOK(compressSetDecoderProperties->SetDecoderProperties2(&isSolid, 1));
782 
783         commonCoder = decoder;
784         break;
785       }
786       default:
787         outStream.Release();
788         RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
789         continue;
790     }
791     HRESULT result = commonCoder->Code(inStream, outStream, &packSize, &item.Size, progress);
792     if (item.IsEncrypted())
793       filterStreamSpec->ReleaseInStream();
794     if (result == S_FALSE)
795     {
796       outStream.Release();
797       RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kDataError));
798       continue;
799     }
800     if (result != S_OK)
801       return result;
802 
803     /*
804     if (refItem.NumItems == 1 &&
805         !item.IsSplitBefore() && !item.IsSplitAfter())
806     */
807     {
808       const CItemEx &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1];
809       bool crcOK = outStreamSpec->GetCRC() == lastItem.FileCRC;
810       outStream.Release();
811       RINOK(extractCallback->SetOperationResult(crcOK ? NArchive::NExtract::NOperationResult::kOK:
812           NArchive::NExtract::NOperationResult::kCRCError));
813     }
814     /*
815     else
816     {
817       bool crcOK = true;
818       for (int partIndex = 0; partIndex < refItem.NumItems; partIndex++)
819       {
820         const CItemEx &item = _items[refItem.ItemIndex + partIndex];
821         if (item.FileCRC != folderInStreamSpec->CRCs[partIndex])
822         {
823           crcOK = false;
824           break;
825         }
826       }
827       RINOK(extractCallback->SetOperationResult(crcOK ? NArchive::NExtract::NOperationResult::kOK:
828           NArchive::NExtract::NOperationResult::kCRCError));
829     }
830     */
831   }
832   return S_OK;
833   COM_TRY_END
834 }
835 
836 IMPL_ISetCompressCodecsInfo
837 
838 }}
839