Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / Zip / ZipHandler.cpp
1 // ZipHandler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "Common/ComTry.h"\r
6 #include "Common/IntToString.h"\r
7 \r
8 #include "Windows/PropVariant.h"\r
9 #include "Windows/Time.h"\r
10 \r
11 #include "../../IPassword.h"\r
12 \r
13 #include "../../Common/FilterCoder.h"\r
14 #include "../../Common/ProgressUtils.h"\r
15 #include "../../Common/StreamObjects.h"\r
16 #include "../../Common/StreamUtils.h"\r
17 \r
18 #include "../../Compress/CopyCoder.h"\r
19 #include "../../Compress/LzmaDecoder.h"\r
20 #include "../../Compress/ImplodeDecoder.h"\r
21 #include "../../Compress/PpmdZip.h"\r
22 #include "../../Compress/ShrinkDecoder.h"\r
23 \r
24 #include "../../Crypto/WzAes.h"\r
25 #include "../../Crypto/ZipCrypto.h"\r
26 #include "../../Crypto/ZipStrong.h"\r
27 \r
28 #include "../Common/ItemNameUtils.h"\r
29 #include "../Common/OutStreamWithCRC.h"\r
30 \r
31 #include "ZipHandler.h"\r
32 \r
33 using namespace NWindows;\r
34 \r
35 namespace NArchive {\r
36 namespace NZip {\r
37 \r
38 static const CMethodId kMethodId_ZipBase = 0x040100;\r
39 static const CMethodId kMethodId_BZip2 = 0x040202;\r
40 \r
41 static const char *kHostOS[] =\r
42 {\r
43   "FAT",\r
44   "AMIGA",\r
45   "VMS",\r
46   "Unix",\r
47   "VM/CMS",\r
48   "Atari",\r
49   "HPFS",\r
50   "Macintosh",\r
51   "Z-System",\r
52   "CP/M",\r
53   "TOPS-20",\r
54   "NTFS",\r
55   "SMS/QDOS",\r
56   "Acorn",\r
57   "VFAT",\r
58   "MVS",\r
59   "BeOS",\r
60   "Tandem",\r
61   "OS/400",\r
62   "OS/X"\r
63 };\r
64 \r
65 static const char *kUnknownOS = "Unknown";\r
66 \r
67 static const char *kMethods[] =\r
68 {\r
69   "Store",\r
70   "Shrink",\r
71   "Reduced1",\r
72   "Reduced2",\r
73   "Reduced3",\r
74   "Reduced4",\r
75   "Implode",\r
76   "Tokenizing",\r
77   "Deflate",\r
78   "Deflate64",\r
79   "PKImploding"\r
80 };\r
81 \r
82 static const char *kBZip2Method = "BZip2";\r
83 static const char *kLZMAMethod = "LZMA";\r
84 static const char *kJpegMethod = "Jpeg";\r
85 static const char *kWavPackMethod = "WavPack";\r
86 static const char *kPPMdMethod = "PPMd";\r
87 static const char *kAESMethod = "AES";\r
88 static const char *kZipCryptoMethod = "ZipCrypto";\r
89 static const char *kStrongCryptoMethod = "StrongCrypto";\r
90 \r
91 static struct CStrongCryptoPair\r
92 {\r
93   UInt16 Id;\r
94   const char *Name;\r
95 } g_StrongCryptoPairs[] =\r
96 {\r
97   { NStrongCryptoFlags::kDES, "DES" },\r
98   { NStrongCryptoFlags::kRC2old, "RC2a" },\r
99   { NStrongCryptoFlags::k3DES168, "3DES-168" },\r
100   { NStrongCryptoFlags::k3DES112, "3DES-112" },\r
101   { NStrongCryptoFlags::kAES128, "pkAES-128" },\r
102   { NStrongCryptoFlags::kAES192, "pkAES-192" },\r
103   { NStrongCryptoFlags::kAES256, "pkAES-256" },\r
104   { NStrongCryptoFlags::kRC2, "RC2" },\r
105   { NStrongCryptoFlags::kBlowfish, "Blowfish" },\r
106   { NStrongCryptoFlags::kTwofish, "Twofish" },\r
107   { NStrongCryptoFlags::kRC4, "RC4" }\r
108 };\r
109 \r
110 static STATPROPSTG kProps[] =\r
111 {\r
112   { NULL, kpidPath, VT_BSTR},\r
113   { NULL, kpidIsDir, VT_BOOL},\r
114   { NULL, kpidSize, VT_UI8},\r
115   { NULL, kpidPackSize, VT_UI8},\r
116   { NULL, kpidMTime, VT_FILETIME},\r
117   { NULL, kpidCTime, VT_FILETIME},\r
118   { NULL, kpidATime, VT_FILETIME},\r
119   { NULL, kpidAttrib, VT_UI4},\r
120   { NULL, kpidEncrypted, VT_BOOL},\r
121   { NULL, kpidComment, VT_BSTR},\r
122   { NULL, kpidCRC, VT_UI4},\r
123   { NULL, kpidMethod, VT_BSTR},\r
124   { NULL, kpidHostOS, VT_BSTR},\r
125   { NULL, kpidUnpackVer, VT_UI4}\r
126 };\r
127 \r
128 static STATPROPSTG kArcProps[] =\r
129 {\r
130   { NULL, kpidBit64, VT_BOOL},\r
131   { NULL, kpidComment, VT_BSTR},\r
132   { NULL, kpidPhySize, VT_UI8},\r
133   { NULL, kpidOffset, VT_UI8}\r
134 };\r
135 \r
136 CHandler::CHandler()\r
137 {\r
138   InitMethodProperties();\r
139 }\r
140 \r
141 static AString BytesToString(const CByteBuffer &data)\r
142 {\r
143   AString s;\r
144   int size = (int)data.GetCapacity();\r
145   if (size > 0)\r
146   {\r
147     char *p = s.GetBuffer(size + 1);\r
148     memcpy(p, (const Byte *)data, size);\r
149     p[size] = '\0';\r
150     s.ReleaseBuffer();\r
151   }\r
152   return s;\r
153 }\r
154 \r
155 IMP_IInArchive_Props\r
156 IMP_IInArchive_ArcProps\r
157 \r
158 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)\r
159 {\r
160   COM_TRY_BEGIN\r
161   NWindows::NCOM::CPropVariant prop;\r
162   switch(propID)\r
163   {\r
164     case kpidBit64:  if (m_Archive.IsZip64) prop = m_Archive.IsZip64; break;\r
165     case kpidComment:  prop = MultiByteToUnicodeString(BytesToString(m_Archive.ArcInfo.Comment), CP_ACP); break;\r
166     case kpidPhySize:  prop = m_Archive.ArcInfo.GetPhySize(); break;\r
167     case kpidOffset:  if (m_Archive.ArcInfo.StartPosition != 0) prop = m_Archive.ArcInfo.StartPosition; break;\r
168   }\r
169   prop.Detach(value);\r
170   COM_TRY_END\r
171   return S_OK;\r
172 }\r
173 \r
174 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
175 {\r
176   *numItems = m_Items.Size();\r
177   return S_OK;\r
178 }\r
179 \r
180 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)\r
181 {\r
182   COM_TRY_BEGIN\r
183   NWindows::NCOM::CPropVariant prop;\r
184   const CItemEx &item = m_Items[index];\r
185   switch(propID)\r
186   {\r
187     case kpidPath:  prop = NItemName::GetOSName2(item.GetUnicodeString(item.Name)); break;\r
188     case kpidIsDir:  prop = item.IsDir(); break;\r
189     case kpidSize:  prop = item.UnPackSize; break;\r
190     case kpidPackSize:  prop = item.PackSize; break;\r
191     case kpidTimeType:\r
192     {\r
193       FILETIME ft;\r
194       UInt32 unixTime;\r
195       if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, ft))\r
196         prop = (UInt32)NFileTimeType::kWindows;\r
197       else if (item.CentralExtra.GetUnixTime(NFileHeader::NUnixTime::kMTime, unixTime))\r
198         prop = (UInt32)NFileTimeType::kUnix;\r
199       else\r
200         prop = (UInt32)NFileTimeType::kDOS;\r
201       break;\r
202     }\r
203     case kpidCTime:\r
204     {\r
205       FILETIME ft;\r
206       if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kCTime, ft))\r
207         prop = ft;\r
208       break;\r
209     }\r
210     case kpidATime:\r
211     {\r
212       FILETIME ft;\r
213       if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kATime, ft))\r
214         prop = ft;\r
215       break;\r
216     }\r
217     case kpidMTime:\r
218     {\r
219       FILETIME utc;\r
220       if (!item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, utc))\r
221       {\r
222         UInt32 unixTime;\r
223         if (item.CentralExtra.GetUnixTime(NFileHeader::NUnixTime::kMTime, unixTime))\r
224           NTime::UnixTimeToFileTime(unixTime, utc);\r
225         else\r
226         {\r
227           FILETIME localFileTime;\r
228           if (!NTime::DosTimeToFileTime(item.Time, localFileTime) ||\r
229               !LocalFileTimeToFileTime(&localFileTime, &utc))\r
230             utc.dwHighDateTime = utc.dwLowDateTime = 0;\r
231         }\r
232       }\r
233       prop = utc;\r
234       break;\r
235     }\r
236     case kpidAttrib:  prop = item.GetWinAttributes(); break;\r
237     case kpidEncrypted:  prop = item.IsEncrypted(); break;\r
238     case kpidComment:  prop = item.GetUnicodeString(BytesToString(item.Comment)); break;\r
239     case kpidCRC:  if (item.IsThereCrc()) prop = item.FileCRC; break;\r
240     case kpidMethod:\r
241     {\r
242       UInt16 methodId = item.CompressionMethod;\r
243       AString method;\r
244       if (item.IsEncrypted())\r
245       {\r
246         if (methodId == NFileHeader::NCompressionMethod::kWzAES)\r
247         {\r
248           method = kAESMethod;\r
249           CWzAesExtraField aesField;\r
250           if (item.CentralExtra.GetWzAesField(aesField))\r
251           {\r
252             method += '-';\r
253             char s[32];\r
254             ConvertUInt64ToString((aesField.Strength + 1) * 64 , s);\r
255             method += s;\r
256             method += ' ';\r
257             methodId = aesField.Method;\r
258           }\r
259         }\r
260         else\r
261         {\r
262           if (item.IsStrongEncrypted())\r
263           {\r
264             CStrongCryptoField f;\r
265             bool finded = false;\r
266             if (item.CentralExtra.GetStrongCryptoField(f))\r
267             {\r
268               for (int i = 0; i < sizeof(g_StrongCryptoPairs) / sizeof(g_StrongCryptoPairs[0]); i++)\r
269               {\r
270                 const CStrongCryptoPair &pair = g_StrongCryptoPairs[i];\r
271                 if (f.AlgId == pair.Id)\r
272                 {\r
273                   method += pair.Name;\r
274                   finded = true;\r
275                   break;\r
276                 }\r
277               }\r
278             }\r
279             if (!finded)\r
280               method += kStrongCryptoMethod;\r
281           }\r
282           else\r
283             method += kZipCryptoMethod;\r
284           method += ' ';\r
285         }\r
286       }\r
287       if (methodId < sizeof(kMethods) / sizeof(kMethods[0]))\r
288         method += kMethods[methodId];\r
289       else switch (methodId)\r
290       {\r
291         case NFileHeader::NCompressionMethod::kLZMA:\r
292           method += kLZMAMethod;\r
293           if (item.IsLzmaEOS())\r
294             method += ":EOS";\r
295           break;\r
296         case NFileHeader::NCompressionMethod::kBZip2: method += kBZip2Method; break;\r
297         case NFileHeader::NCompressionMethod::kJpeg: method += kJpegMethod; break;\r
298         case NFileHeader::NCompressionMethod::kWavPack: method += kWavPackMethod; break;\r
299         case NFileHeader::NCompressionMethod::kPPMd: method += kPPMdMethod; break;\r
300         default:\r
301         {\r
302           char s[32];\r
303           ConvertUInt64ToString(methodId, s);\r
304           method += s;\r
305         }\r
306       }\r
307       prop = method;\r
308       break;\r
309     }\r
310     case kpidHostOS:\r
311       prop = (item.MadeByVersion.HostOS < sizeof(kHostOS) / sizeof(kHostOS[0])) ?\r
312         (kHostOS[item.MadeByVersion.HostOS]) : kUnknownOS;\r
313       break;\r
314     case kpidUnpackVer:\r
315       prop = (UInt32)item.ExtractVersion.Version;\r
316       break;\r
317   }\r
318   prop.Detach(value);\r
319   return S_OK;\r
320   COM_TRY_END\r
321 }\r
322 \r
323 class CProgressImp: public CProgressVirt\r
324 {\r
325   CMyComPtr<IArchiveOpenCallback> _callback;\r
326 public:\r
327   STDMETHOD(SetTotal)(UInt64 numFiles);\r
328   STDMETHOD(SetCompleted)(UInt64 numFiles);\r
329   CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {}\r
330 };\r
331 \r
332 STDMETHODIMP CProgressImp::SetTotal(UInt64 numFiles)\r
333 {\r
334   if (_callback)\r
335     return _callback->SetTotal(&numFiles, NULL);\r
336   return S_OK;\r
337 }\r
338 \r
339 STDMETHODIMP CProgressImp::SetCompleted(UInt64 numFiles)\r
340 {\r
341   if (_callback)\r
342     return _callback->SetCompleted(&numFiles, NULL);\r
343   return S_OK;\r
344 }\r
345 \r
346 STDMETHODIMP CHandler::Open(IInStream *inStream,\r
347     const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback)\r
348 {\r
349   COM_TRY_BEGIN\r
350   try\r
351   {\r
352     Close();\r
353     RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));\r
354     RINOK(m_Archive.Open(inStream, maxCheckStartPosition));\r
355     CProgressImp progressImp(callback);\r
356     return m_Archive.ReadHeaders(m_Items, &progressImp);\r
357   }\r
358   catch(const CInArchiveException &) { Close(); return S_FALSE; }\r
359   catch(...) { Close(); throw; }\r
360   COM_TRY_END\r
361 }\r
362 \r
363 STDMETHODIMP CHandler::Close()\r
364 {\r
365   m_Items.Clear();\r
366   m_Archive.Close();\r
367   return S_OK;\r
368 }\r
369 \r
370 //////////////////////////////////////\r
371 // CHandler::DecompressItems\r
372 \r
373 class CLzmaDecoder:\r
374   public ICompressCoder,\r
375   public CMyUnknownImp\r
376 {\r
377   NCompress::NLzma::CDecoder *DecoderSpec;\r
378   CMyComPtr<ICompressCoder> Decoder;\r
379 public:\r
380   CLzmaDecoder();\r
381   STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,\r
382       const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);\r
383 \r
384   MY_UNKNOWN_IMP\r
385 };\r
386 \r
387 CLzmaDecoder::CLzmaDecoder()\r
388 {\r
389   DecoderSpec = new NCompress::NLzma::CDecoder;\r
390   Decoder = DecoderSpec;\r
391 }\r
392 \r
393 HRESULT CLzmaDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,\r
394     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)\r
395 {\r
396   Byte buf[9];\r
397   RINOK(ReadStream_FALSE(inStream, buf, 9));\r
398   if (buf[2] != 5 || buf[3] != 0)\r
399     return E_NOTIMPL;\r
400   RINOK(DecoderSpec->SetDecoderProperties2(buf + 4, 5));\r
401   return Decoder->Code(inStream, outStream, NULL, outSize, progress);\r
402 }\r
403 \r
404 struct CMethodItem\r
405 {\r
406   UInt16 ZipMethod;\r
407   CMyComPtr<ICompressCoder> Coder;\r
408 };\r
409 \r
410 class CZipDecoder\r
411 {\r
412   NCrypto::NZip::CDecoder *_zipCryptoDecoderSpec;\r
413   NCrypto::NZipStrong::CDecoder *_pkAesDecoderSpec;\r
414   NCrypto::NWzAes::CDecoder *_wzAesDecoderSpec;\r
415 \r
416   CMyComPtr<ICompressFilter> _zipCryptoDecoder;\r
417   CMyComPtr<ICompressFilter> _pkAesDecoder;\r
418   CMyComPtr<ICompressFilter> _wzAesDecoder;\r
419 \r
420   CFilterCoder *filterStreamSpec;\r
421   CMyComPtr<ISequentialInStream> filterStream;\r
422   CMyComPtr<ICryptoGetTextPassword> getTextPassword;\r
423   CObjectVector<CMethodItem> methodItems;\r
424 \r
425 public:\r
426   CZipDecoder():\r
427       _zipCryptoDecoderSpec(0),\r
428       _pkAesDecoderSpec(0),\r
429       _wzAesDecoderSpec(0),\r
430       filterStreamSpec(0) {}\r
431 \r
432   HRESULT Decode(\r
433     DECL_EXTERNAL_CODECS_LOC_VARS\r
434     CInArchive &archive, const CItemEx &item,\r
435     ISequentialOutStream *realOutStream,\r
436     IArchiveExtractCallback *extractCallback,\r
437     ICompressProgressInfo *compressProgress,\r
438     UInt32 numThreads, Int32 &res);\r
439 };\r
440 \r
441 HRESULT CZipDecoder::Decode(\r
442     DECL_EXTERNAL_CODECS_LOC_VARS\r
443     CInArchive &archive, const CItemEx &item,\r
444     ISequentialOutStream *realOutStream,\r
445     IArchiveExtractCallback *extractCallback,\r
446     ICompressProgressInfo *compressProgress,\r
447     UInt32 numThreads, Int32 &res)\r
448 {\r
449   res = NExtract::NOperationResult::kDataError;\r
450   CInStreamReleaser inStreamReleaser;\r
451 \r
452   bool needCRC = true;\r
453   bool wzAesMode = false;\r
454   bool pkAesMode = false;\r
455   UInt16 methodId = item.CompressionMethod;\r
456   if (item.IsEncrypted())\r
457   {\r
458     if (item.IsStrongEncrypted())\r
459     {\r
460       CStrongCryptoField f;\r
461       if (item.CentralExtra.GetStrongCryptoField(f))\r
462       {\r
463         pkAesMode = true;\r
464       }\r
465       if (!pkAesMode)\r
466       {\r
467         res = NExtract::NOperationResult::kUnSupportedMethod;\r
468         return S_OK;\r
469       }\r
470     }\r
471     if (methodId == NFileHeader::NCompressionMethod::kWzAES)\r
472     {\r
473       CWzAesExtraField aesField;\r
474       if (item.CentralExtra.GetWzAesField(aesField))\r
475       {\r
476         wzAesMode = true;\r
477         needCRC = aesField.NeedCrc();\r
478       }\r
479     }\r
480   }\r
481     \r
482   COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;\r
483   CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;\r
484   outStreamSpec->SetStream(realOutStream);\r
485   outStreamSpec->Init(needCRC);\r
486   \r
487   UInt64 authenticationPos;\r
488   \r
489   CMyComPtr<ISequentialInStream> inStream;\r
490   {\r
491     UInt64 packSize = item.PackSize;\r
492     if (wzAesMode)\r
493     {\r
494       if (packSize < NCrypto::NWzAes::kMacSize)\r
495         return S_OK;\r
496       packSize -= NCrypto::NWzAes::kMacSize;\r
497     }\r
498     UInt64 dataPos = item.GetDataPosition();\r
499     inStream.Attach(archive.CreateLimitedStream(dataPos, packSize));\r
500     authenticationPos = dataPos + packSize;\r
501   }\r
502   \r
503   CMyComPtr<ICompressFilter> cryptoFilter;\r
504   if (item.IsEncrypted())\r
505   {\r
506     if (wzAesMode)\r
507     {\r
508       CWzAesExtraField aesField;\r
509       if (!item.CentralExtra.GetWzAesField(aesField))\r
510         return S_OK;\r
511       methodId = aesField.Method;\r
512       if (!_wzAesDecoder)\r
513       {\r
514         _wzAesDecoderSpec = new NCrypto::NWzAes::CDecoder;\r
515         _wzAesDecoder = _wzAesDecoderSpec;\r
516       }\r
517       cryptoFilter = _wzAesDecoder;\r
518       Byte properties = aesField.Strength;\r
519       RINOK(_wzAesDecoderSpec->SetDecoderProperties2(&properties, 1));\r
520     }\r
521     else if (pkAesMode)\r
522     {\r
523       if (!_pkAesDecoder)\r
524       {\r
525         _pkAesDecoderSpec = new NCrypto::NZipStrong::CDecoder;\r
526         _pkAesDecoder = _pkAesDecoderSpec;\r
527       }\r
528       cryptoFilter = _pkAesDecoder;\r
529     }\r
530     else\r
531     {\r
532       if (!_zipCryptoDecoder)\r
533       {\r
534         _zipCryptoDecoderSpec = new NCrypto::NZip::CDecoder;\r
535         _zipCryptoDecoder = _zipCryptoDecoderSpec;\r
536       }\r
537       cryptoFilter = _zipCryptoDecoder;\r
538     }\r
539     CMyComPtr<ICryptoSetPassword> cryptoSetPassword;\r
540     RINOK(cryptoFilter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword));\r
541     \r
542     if (!getTextPassword)\r
543       extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword);\r
544     \r
545     if (getTextPassword)\r
546     {\r
547       CMyComBSTR password;\r
548       RINOK(getTextPassword->CryptoGetTextPassword(&password));\r
549       AString charPassword;\r
550       if (wzAesMode || pkAesMode)\r
551       {\r
552         charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_ACP);\r
553         /*\r
554         for (int i = 0;; i++)\r
555         {\r
556           wchar_t c = password[i];\r
557           if (c == 0)\r
558             break;\r
559           if (c >= 0x80)\r
560           {\r
561             res = NExtract::NOperationResult::kDataError;\r
562             return S_OK;\r
563           }\r
564           charPassword += (char)c;\r
565         }\r
566         */\r
567       }\r
568       else\r
569       {\r
570         // we use OEM. WinZip/Windows probably use ANSI for some files\r
571         charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP);\r
572       }\r
573       HRESULT result = cryptoSetPassword->CryptoSetPassword(\r
574         (const Byte *)(const char *)charPassword, charPassword.Length());\r
575       if (result != S_OK)\r
576         return S_OK;\r
577     }\r
578     else\r
579     {\r
580       RINOK(cryptoSetPassword->CryptoSetPassword(0, 0));\r
581     }\r
582   }\r
583   \r
584   int m;\r
585   for (m = 0; m < methodItems.Size(); m++)\r
586     if (methodItems[m].ZipMethod == methodId)\r
587       break;\r
588 \r
589   if (m == methodItems.Size())\r
590   {\r
591     CMethodItem mi;\r
592     mi.ZipMethod = methodId;\r
593     if (methodId == NFileHeader::NCompressionMethod::kStored)\r
594       mi.Coder = new NCompress::CCopyCoder;\r
595     else if (methodId == NFileHeader::NCompressionMethod::kShrunk)\r
596       mi.Coder = new NCompress::NShrink::CDecoder;\r
597     else if (methodId == NFileHeader::NCompressionMethod::kImploded)\r
598       mi.Coder = new NCompress::NImplode::NDecoder::CCoder;\r
599     else if (methodId == NFileHeader::NCompressionMethod::kLZMA)\r
600       mi.Coder = new CLzmaDecoder;\r
601     else if (methodId == NFileHeader::NCompressionMethod::kPPMd)\r
602       mi.Coder = new NCompress::NPpmdZip::CDecoder(true);\r
603     else\r
604     {\r
605       CMethodId szMethodID;\r
606       if (methodId == NFileHeader::NCompressionMethod::kBZip2)\r
607         szMethodID = kMethodId_BZip2;\r
608       else\r
609       {\r
610         if (methodId > 0xFF)\r
611         {\r
612           res = NExtract::NOperationResult::kUnSupportedMethod;\r
613           return S_OK;\r
614         }\r
615         szMethodID = kMethodId_ZipBase + (Byte)methodId;\r
616       }\r
617 \r
618       RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS szMethodID, mi.Coder, false));\r
619 \r
620       if (mi.Coder == 0)\r
621       {\r
622         res = NExtract::NOperationResult::kUnSupportedMethod;\r
623         return S_OK;\r
624       }\r
625     }\r
626     m = methodItems.Add(mi);\r
627   }\r
628   ICompressCoder *coder = methodItems[m].Coder;\r
629   \r
630   {\r
631     CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;\r
632     coder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties);\r
633     if (setDecoderProperties)\r
634     {\r
635       Byte properties = (Byte)item.Flags;\r
636       RINOK(setDecoderProperties->SetDecoderProperties2(&properties, 1));\r
637     }\r
638   }\r
639   \r
640   #ifndef _7ZIP_ST\r
641   {\r
642     CMyComPtr<ICompressSetCoderMt> setCoderMt;\r
643     coder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);\r
644     if (setCoderMt)\r
645     {\r
646       RINOK(setCoderMt->SetNumberOfThreads(numThreads));\r
647     }\r
648   }\r
649   #endif\r
650   \r
651   {\r
652     HRESULT result = S_OK;\r
653     CMyComPtr<ISequentialInStream> inStreamNew;\r
654     if (item.IsEncrypted())\r
655     {\r
656       if (!filterStream)\r
657       {\r
658         filterStreamSpec = new CFilterCoder;\r
659         filterStream = filterStreamSpec;\r
660       }\r
661       filterStreamSpec->Filter = cryptoFilter;\r
662       if (wzAesMode)\r
663       {\r
664         result = _wzAesDecoderSpec->ReadHeader(inStream);\r
665       }\r
666       else if (pkAesMode)\r
667       {\r
668         result =_pkAesDecoderSpec->ReadHeader(inStream, item.FileCRC, item.UnPackSize);\r
669         if (result == S_OK)\r
670         {\r
671           bool passwOK;\r
672           result = _pkAesDecoderSpec->CheckPassword(passwOK);\r
673           if (result == S_OK && !passwOK)\r
674             result = S_FALSE;\r
675         }\r
676       }\r
677       else\r
678       {\r
679         result = _zipCryptoDecoderSpec->ReadHeader(inStream);\r
680       }\r
681 \r
682       if (result == S_OK)\r
683       {\r
684         RINOK(filterStreamSpec->SetInStream(inStream));\r
685         inStreamReleaser.FilterCoder = filterStreamSpec;\r
686         inStreamNew = filterStream;\r
687         if (wzAesMode)\r
688         {\r
689           if (!_wzAesDecoderSpec->CheckPasswordVerifyCode())\r
690             result = S_FALSE;\r
691         }\r
692       }\r
693     }\r
694     else\r
695       inStreamNew = inStream;\r
696     if (result == S_OK)\r
697       result = coder->Code(inStreamNew, outStream, NULL, &item.UnPackSize, compressProgress);\r
698     if (result == S_FALSE)\r
699       return S_OK;\r
700     if (result == E_NOTIMPL)\r
701     {\r
702       res = NExtract::NOperationResult::kUnSupportedMethod;\r
703       return S_OK;\r
704     }\r
705 \r
706     RINOK(result);\r
707   }\r
708   bool crcOK = true;\r
709   bool authOk = true;\r
710   if (needCRC)\r
711     crcOK = (outStreamSpec->GetCRC() == item.FileCRC);\r
712   if (wzAesMode)\r
713   {\r
714     inStream.Attach(archive.CreateLimitedStream(authenticationPos, NCrypto::NWzAes::kMacSize));\r
715     if (_wzAesDecoderSpec->CheckMac(inStream, authOk) != S_OK)\r
716       authOk = false;\r
717   }\r
718   \r
719   res = ((crcOK && authOk) ?\r
720     NExtract::NOperationResult::kOK :\r
721     NExtract::NOperationResult::kCRCError);\r
722   return S_OK;\r
723 }\r
724 \r
725 \r
726 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
727     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
728 {\r
729   COM_TRY_BEGIN\r
730   CZipDecoder myDecoder;\r
731   UInt64 totalUnPacked = 0, totalPacked = 0;\r
732   bool allFilesMode = (numItems == (UInt32)-1);\r
733   if (allFilesMode)\r
734     numItems = m_Items.Size();\r
735   if(numItems == 0)\r
736     return S_OK;\r
737   UInt32 i;\r
738   for (i = 0; i < numItems; i++)\r
739   {\r
740     const CItemEx &item = m_Items[allFilesMode ? i : indices[i]];\r
741     totalUnPacked += item.UnPackSize;\r
742     totalPacked += item.PackSize;\r
743   }\r
744   RINOK(extractCallback->SetTotal(totalUnPacked));\r
745 \r
746   UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0;\r
747   UInt64 currentItemUnPacked, currentItemPacked;\r
748   \r
749   CLocalProgress *lps = new CLocalProgress;\r
750   CMyComPtr<ICompressProgressInfo> progress = lps;\r
751   lps->Init(extractCallback, false);\r
752 \r
753   for (i = 0; i < numItems; i++, currentTotalUnPacked += currentItemUnPacked,\r
754       currentTotalPacked += currentItemPacked)\r
755   {\r
756     currentItemUnPacked = 0;\r
757     currentItemPacked = 0;\r
758 \r
759     lps->InSize = currentTotalPacked;\r
760     lps->OutSize = currentTotalUnPacked;\r
761     RINOK(lps->SetCur());\r
762 \r
763     CMyComPtr<ISequentialOutStream> realOutStream;\r
764     Int32 askMode = testMode ?\r
765         NExtract::NAskMode::kTest :\r
766         NExtract::NAskMode::kExtract;\r
767     Int32 index = allFilesMode ? i : indices[i];\r
768 \r
769     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));\r
770 \r
771     CItemEx item = m_Items[index];\r
772     if (!item.FromLocal)\r
773     {\r
774       HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item);\r
775       if (res == S_FALSE)\r
776       {\r
777         if (item.IsDir() || realOutStream || testMode)\r
778         {\r
779           RINOK(extractCallback->PrepareOperation(askMode));\r
780           realOutStream.Release();\r
781           RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod));\r
782         }\r
783         continue;\r
784       }\r
785       RINOK(res);\r
786     }\r
787 \r
788     if (item.IsDir() || item.IgnoreItem())\r
789     {\r
790       // if (!testMode)\r
791       {\r
792         RINOK(extractCallback->PrepareOperation(askMode));\r
793         realOutStream.Release();\r
794         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));\r
795       }\r
796       continue;\r
797     }\r
798 \r
799     currentItemUnPacked = item.UnPackSize;\r
800     currentItemPacked = item.PackSize;\r
801 \r
802     if (!testMode && !realOutStream)\r
803       continue;\r
804 \r
805     RINOK(extractCallback->PrepareOperation(askMode));\r
806 \r
807     Int32 res;\r
808     RINOK(myDecoder.Decode(\r
809         EXTERNAL_CODECS_VARS\r
810         m_Archive, item, realOutStream, extractCallback,\r
811         progress, _numThreads, res));\r
812     realOutStream.Release();\r
813     \r
814     RINOK(extractCallback->SetOperationResult(res))\r
815   }\r
816   return S_OK;\r
817   COM_TRY_END\r
818 }\r
819 \r
820 IMPL_ISetCompressCodecsInfo\r
821 \r
822 }}\r