xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Zip/ZipHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // ZipHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/StringConvert.h"
7 
8 #include "../../../Windows/PropVariant.h"
9 #include "../../../Windows/PropVariantUtils.h"
10 #include "../../../Windows/TimeUtils.h"
11 
12 #include "../../IPassword.h"
13 
14 #include "../../Common/FilterCoder.h"
15 #include "../../Common/LimitedStreams.h"
16 #include "../../Common/ProgressUtils.h"
17 #include "../../Common/StreamObjects.h"
18 #include "../../Common/StreamUtils.h"
19 
20 #include "../../Compress/CopyCoder.h"
21 #ifndef Z7_ZIP_LZFSE_DISABLE
22 #include "../../Compress/LzfseDecoder.h"
23 #endif
24 #include "../../Compress/LzmaDecoder.h"
25 #include "../../Compress/ImplodeDecoder.h"
26 #include "../../Compress/PpmdZip.h"
27 #include "../../Compress/ShrinkDecoder.h"
28 #include "../../Compress/XzDecoder.h"
29 #include "../../Compress/ZstdDecoder.h"
30 
31 #include "../../Crypto/WzAes.h"
32 #include "../../Crypto/ZipCrypto.h"
33 #include "../../Crypto/ZipStrong.h"
34 
35 #include "../Common/ItemNameUtils.h"
36 #include "../Common/OutStreamWithCRC.h"
37 
38 
39 #include "ZipHandler.h"
40 
41 using namespace NWindows;
42 
43 namespace NArchive {
44 namespace NZip {
45 
46 static const char * const kHostOS[] =
47 {
48     "FAT"
49   , "AMIGA"
50   , "VMS"
51   , "Unix"
52   , "VM/CMS"
53   , "Atari"
54   , "HPFS"
55   , "Macintosh"
56   , "Z-System"
57   , "CP/M"
58   , "TOPS-20"
59   , "NTFS"
60   , "SMS/QDOS"
61   , "Acorn"
62   , "VFAT"
63   , "MVS"
64   , "BeOS"
65   , "Tandem"
66   , "OS/400"
67   , "OS/X"
68 };
69 
70 
71 const char * const kMethodNames1[kNumMethodNames1] =
72 {
73     "Store"
74   , "Shrink"
75   , "Reduce1"
76   , "Reduce2"
77   , "Reduce3"
78   , "Reduce4"
79   , "Implode"
80   , NULL // "Tokenize"
81   , "Deflate"
82   , "Deflate64"
83   , "PKImploding"
84   , NULL
85   , "BZip2"
86   , NULL
87   , "LZMA"
88   /*
89   , NULL
90   , NULL
91   , NULL
92   , NULL
93   , NULL
94   , "zstd-pk" // deprecated
95   */
96 };
97 
98 
99 const char * const kMethodNames2[kNumMethodNames2] =
100 {
101     "zstd"
102   , "MP3"
103   , "xz"
104   , "Jpeg"
105   , "WavPack"
106   , "PPMd"
107   , "LZFSE" // , "WzAES"
108 };
109 
110 #define kMethod_AES "AES"
111 #define kMethod_ZipCrypto "ZipCrypto"
112 #define kMethod_StrongCrypto "StrongCrypto"
113 
114 static const char * const kDeflateLevels[4] =
115 {
116     "Normal"
117   , "Maximum"
118   , "Fast"
119   , "Fastest"
120 };
121 
122 
123 static const CUInt32PCharPair g_HeaderCharacts[] =
124 {
125   { 0, "Encrypt" },
126   { 3, "Descriptor" },
127   // { 4, "Enhanced" },
128   // { 5, "Patched" },
129   { 6, kMethod_StrongCrypto },
130   { 11, "UTF8" },
131   { 14, "Alt" }
132 };
133 
134 struct CIdToNamePair
135 {
136   unsigned Id;
137   const char *Name;
138 };
139 
140 
141 static const CIdToNamePair k_StrongCryptoPairs[] =
142 {
143   { NStrongCrypto_AlgId::kDES, "DES" },
144   { NStrongCrypto_AlgId::kRC2old, "RC2a" },
145   { NStrongCrypto_AlgId::k3DES168, "3DES-168" },
146   { NStrongCrypto_AlgId::k3DES112, "3DES-112" },
147   { NStrongCrypto_AlgId::kAES128, "pkAES-128" },
148   { NStrongCrypto_AlgId::kAES192, "pkAES-192" },
149   { NStrongCrypto_AlgId::kAES256, "pkAES-256" },
150   { NStrongCrypto_AlgId::kRC2, "RC2" },
151   { NStrongCrypto_AlgId::kBlowfish, "Blowfish" },
152   { NStrongCrypto_AlgId::kTwofish, "Twofish" },
153   { NStrongCrypto_AlgId::kRC4, "RC4" }
154 };
155 
FindNameForId(const CIdToNamePair * pairs,unsigned num,unsigned id)156 static const char *FindNameForId(const CIdToNamePair *pairs, unsigned num, unsigned id)
157 {
158   for (unsigned i = 0; i < num; i++)
159   {
160     const CIdToNamePair &pair = pairs[i];
161     if (id == pair.Id)
162       return pair.Name;
163   }
164   return NULL;
165 }
166 
167 
168 static const Byte kProps[] =
169 {
170   kpidPath,
171   kpidIsDir,
172   kpidSize,
173   kpidPackSize,
174   kpidMTime,
175   kpidCTime,
176   kpidATime,
177   kpidAttrib,
178   // kpidPosixAttrib,
179   kpidEncrypted,
180   kpidComment,
181   kpidCRC,
182   kpidMethod,
183   kpidCharacts,
184   kpidHostOS,
185   kpidUnpackVer,
186   kpidVolumeIndex,
187   kpidOffset
188   // kpidIsAltStream
189   // , kpidChangeTime // for debug
190   // , 255  // for debug
191 };
192 
193 static const Byte kArcProps[] =
194 {
195   kpidEmbeddedStubSize,
196   kpidBit64,
197   kpidComment,
198   kpidCharacts,
199   kpidTotalPhySize,
200   kpidIsVolume,
201   kpidVolumeIndex,
202   kpidNumVolumes
203 };
204 
CHandler()205 CHandler::CHandler()
206 {
207   InitMethodProps();
208 }
209 
BytesToString(const CByteBuffer & data)210 static AString BytesToString(const CByteBuffer &data)
211 {
212   AString s;
213   s.SetFrom_CalcLen((const char *)(const Byte *)data, (unsigned)data.Size());
214   return s;
215 }
216 
217 IMP_IInArchive_Props
218 IMP_IInArchive_ArcProps
219 
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))220 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
221 {
222   COM_TRY_BEGIN
223   NWindows::NCOM::CPropVariant prop;
224   switch (propID)
225   {
226     case kpidBit64:  if (m_Archive.IsZip64) prop = m_Archive.IsZip64; break;
227     case kpidComment:  if (m_Archive.ArcInfo.Comment.Size() != 0) prop = MultiByteToUnicodeString(BytesToString(m_Archive.ArcInfo.Comment), CP_ACP); break;
228 
229     case kpidPhySize:  prop = m_Archive.GetPhySize(); break;
230     case kpidOffset:  prop = m_Archive.GetOffset(); break;
231 
232     case kpidEmbeddedStubSize:
233     {
234       UInt64 stubSize = m_Archive.GetEmbeddedStubSize();
235       if (stubSize != 0)
236         prop = stubSize;
237       break;
238     }
239 
240     case kpidTotalPhySize: if (m_Archive.IsMultiVol) prop = m_Archive.Vols.TotalBytesSize; break;
241     case kpidVolumeIndex: if (m_Archive.IsMultiVol) prop = (UInt32)m_Archive.Vols.StartVolIndex; break;
242     case kpidIsVolume: if (m_Archive.IsMultiVol) prop = true; break;
243     case kpidNumVolumes: if (m_Archive.IsMultiVol) prop = (UInt32)m_Archive.Vols.Streams.Size(); break;
244 
245     case kpidCharacts:
246     {
247       AString s;
248 
249       if (m_Archive.LocalsWereRead)
250       {
251         s.Add_OptSpaced("Local");
252 
253         if (m_Archive.LocalsCenterMerged)
254           s.Add_OptSpaced("Central");
255       }
256 
257       if (m_Archive.IsZip64)
258         s.Add_OptSpaced("Zip64");
259 
260       if (m_Archive.IsCdUnsorted)
261         s.Add_OptSpaced("Unsorted_CD");
262 
263       if (m_Archive.IsApk)
264         s.Add_OptSpaced("apk");
265 
266       if (m_Archive.ExtraMinorError)
267         s.Add_OptSpaced("Minor_Extra_ERROR");
268 
269       if (!s.IsEmpty())
270         prop = s;
271       break;
272     }
273 
274     case kpidWarningFlags:
275     {
276       UInt32 v = 0;
277       // if (m_Archive.ExtraMinorError) v |= kpv_ErrorFlags_HeadersError;
278       if (m_Archive.HeadersWarning) v |= kpv_ErrorFlags_HeadersError;
279       if (v != 0)
280         prop = v;
281       break;
282     }
283 
284     case kpidWarning:
285     {
286       AString s;
287       if (m_Archive.Overflow32bit)
288         s.Add_OptSpaced("32-bit overflow in headers");
289       if (m_Archive.Cd_NumEntries_Overflow_16bit)
290         s.Add_OptSpaced("16-bit overflow for number of files in headers");
291       if (!s.IsEmpty())
292         prop = s;
293       break;
294     }
295 
296     case kpidError:
297     {
298       if (!m_Archive.Vols.MissingName.IsEmpty())
299       {
300         UString s("Missing volume : ");
301         s += m_Archive.Vols.MissingName;
302         prop = s;
303       }
304       break;
305     }
306 
307     case kpidErrorFlags:
308     {
309       UInt32 v = 0;
310       if (!m_Archive.IsArc) v |= kpv_ErrorFlags_IsNotArc;
311       if (m_Archive.HeadersError) v |= kpv_ErrorFlags_HeadersError;
312       if (m_Archive.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd;
313       if (m_Archive.ArcInfo.Base < 0)
314       {
315         /* We try to support case when we have sfx-zip with embedded stub,
316            but the stream has access only to zip part.
317            In that case we ignore UnavailableStart error.
318            maybe we must show warning in that case. */
319         UInt64 stubSize = m_Archive.GetEmbeddedStubSize();
320         if (stubSize < (UInt64)-m_Archive.ArcInfo.Base)
321           v |= kpv_ErrorFlags_UnavailableStart;
322       }
323       if (m_Archive.NoCentralDir) v |= kpv_ErrorFlags_UnconfirmedStart;
324       prop = v;
325       break;
326     }
327 
328     case kpidReadOnly:
329     {
330       if (m_Archive.IsOpen())
331         if (!m_Archive.CanUpdate())
332           prop = true;
333       break;
334     }
335 
336     // case kpidIsAltStream: prop = true; break;
337     default: break;
338   }
339   return prop.Detach(value);
340   COM_TRY_END
341 }
342 
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))343 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
344 {
345   *numItems = m_Items.Size();
346   return S_OK;
347 }
348 
349 
NtfsUnixTimeToProp(bool fromCentral,const CExtraBlock & extra,unsigned ntfsIndex,unsigned unixIndex,NWindows::NCOM::CPropVariant & prop)350 static bool NtfsUnixTimeToProp(bool fromCentral,
351     const CExtraBlock &extra,
352     unsigned ntfsIndex, unsigned unixIndex, NWindows::NCOM::CPropVariant &prop)
353 {
354   {
355     FILETIME ft;
356     if (extra.GetNtfsTime(ntfsIndex, ft))
357     {
358       PropVariant_SetFrom_NtfsTime(prop, ft);
359       return true;
360     }
361   }
362   {
363     UInt32 unixTime = 0;
364     if (!extra.GetUnixTime(fromCentral, unixIndex, unixTime))
365       return false;
366     /*
367     // we allow unixTime == 0
368     if (unixTime == 0)
369       return false;
370     */
371     PropVariant_SetFrom_UnixTime(prop, unixTime);
372     return true;
373   }
374 }
375 
376 
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))377 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
378 {
379   COM_TRY_BEGIN
380   NWindows::NCOM::CPropVariant prop;
381   const CItemEx &item = m_Items[index];
382   const CExtraBlock &extra = item.GetMainExtra();
383 
384   switch (propID)
385   {
386     case kpidPath:
387     {
388       UString res;
389       item.GetUnicodeString(res, item.Name, false, _forceCodePage, _specifiedCodePage);
390       NItemName::ReplaceToOsSlashes_Remove_TailSlash(res,
391           item.Is_MadeBy_Unix() // useBackslashReplacement
392           );
393       /*
394       if (item.ParentOfAltStream >= 0)
395       {
396         const CItemEx &prevItem = m_Items[item.ParentOfAltStream];
397         UString prevName;
398         prevItem.GetUnicodeString(prevName, prevItem.Name, false, _forceCodePage, _specifiedCodePage);
399         NItemName::ReplaceToOsSlashes_Remove_TailSlash(prevName);
400         if (res.IsPrefixedBy(prevName))
401           if (IsString1PrefixedByString2(res.Ptr(prevName.Len()), k_SpecName_NTFS_STREAM))
402           {
403             res.Delete(prevName.Len(), (unsigned)strlen(k_SpecName_NTFS_STREAM));
404             res.Insert(prevName.Len(), L":");
405           }
406       }
407       */
408       prop = res;
409       break;
410     }
411 
412     case kpidIsDir:  prop = item.IsDir(); break;
413     case kpidSize:
414     {
415       if (!item.IsBadDescriptor())
416         prop = item.Size;
417       break;
418     }
419 
420     case kpidPackSize:  prop = item.PackSize; break;
421 
422     case kpidCTime:
423       NtfsUnixTimeToProp(item.FromCentral, extra,
424           NFileHeader::NNtfsExtra::kCTime,
425           NFileHeader::NUnixTime::kCTime, prop);
426       break;
427 
428     case kpidATime:
429       NtfsUnixTimeToProp(item.FromCentral, extra,
430           NFileHeader::NNtfsExtra::kATime,
431           NFileHeader::NUnixTime::kATime, prop);
432       break;
433 
434     case kpidMTime:
435     {
436       if (!NtfsUnixTimeToProp(item.FromCentral, extra,
437           NFileHeader::NNtfsExtra::kMTime,
438           NFileHeader::NUnixTime::kMTime, prop))
439       {
440         if (item.Time != 0)
441           PropVariant_SetFrom_DosTime(prop, item.Time);
442       }
443       break;
444     }
445 
446     case kpidTimeType:
447     {
448       FILETIME ft;
449       UInt32 unixTime;
450       UInt32 type;
451       if (extra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, ft))
452         type = NFileTimeType::kWindows;
453       else if (extra.GetUnixTime(item.FromCentral, NFileHeader::NUnixTime::kMTime, unixTime))
454         type = NFileTimeType::kUnix;
455       else
456         type = NFileTimeType::kDOS;
457       prop = type;
458       break;
459     }
460 
461     /*
462     // for debug to get Dos time values:
463     case kpidChangeTime: if (item.Time != 0) PropVariant_SetFrom_DosTime(prop, item.Time); break;
464     // for debug
465     // time difference (dos - utc)
466     case 255:
467     {
468       if (NtfsUnixTimeToProp(item.FromCentral, extra,
469           NFileHeader::NNtfsExtra::kMTime,
470           NFileHeader::NUnixTime::kMTime, prop))
471       {
472         FILETIME localFileTime;
473         if (item.Time != 0 && NTime::DosTime_To_FileTime(item.Time, localFileTime))
474         {
475           UInt64 t1 = FILETIME_To_UInt64(prop.filetime);
476           UInt64 t2 = FILETIME_To_UInt64(localFileTime);
477           prop.Set_Int64(t2 - t1);
478         }
479       }
480       break;
481     }
482     */
483 
484     case kpidAttrib:  prop = item.GetWinAttrib(); break;
485 
486     case kpidPosixAttrib:
487     {
488       UInt32 attrib;
489       if (item.GetPosixAttrib(attrib))
490         prop = attrib;
491       break;
492     }
493 
494     case kpidEncrypted:  prop = item.IsEncrypted(); break;
495 
496     case kpidComment:
497     {
498       if (item.Comment.Size() != 0)
499       {
500         UString res;
501         item.GetUnicodeString(res, BytesToString(item.Comment), true, _forceCodePage, _specifiedCodePage);
502         prop = res;
503       }
504       break;
505     }
506 
507     case kpidCRC:  if (item.IsThereCrc()) prop = item.Crc; break;
508 
509     case kpidMethod:
510     {
511       AString m;
512       bool isWzAes = false;
513       unsigned id = item.Method;
514 
515       if (id == NFileHeader::NCompressionMethod::kWzAES)
516       {
517         CWzAesExtra aesField;
518         if (extra.GetWzAes(aesField))
519         {
520           m += kMethod_AES;
521           m.Add_Minus();
522           m.Add_UInt32(((unsigned)aesField.Strength + 1) * 64);
523           id = aesField.Method;
524           isWzAes = true;
525         }
526       }
527 
528       if (item.IsEncrypted())
529       if (!isWzAes)
530       {
531         if (item.IsStrongEncrypted())
532         {
533           CStrongCryptoExtra f;
534           f.AlgId = 0;
535           if (extra.GetStrongCrypto(f))
536           {
537             const char *s = FindNameForId(k_StrongCryptoPairs, Z7_ARRAY_SIZE(k_StrongCryptoPairs), f.AlgId);
538             if (s)
539               m += s;
540             else
541             {
542               m += kMethod_StrongCrypto;
543               m.Add_Colon();
544               m.Add_UInt32(f.AlgId);
545             }
546             if (f.CertificateIsUsed())
547               m += "-Cert";
548           }
549           else
550             m += kMethod_StrongCrypto;
551         }
552         else
553           m += kMethod_ZipCrypto;
554       }
555 
556       m.Add_Space_if_NotEmpty();
557 
558       {
559         const char *s = NULL;
560         if (id < kNumMethodNames1)
561           s = kMethodNames1[id];
562         else
563         {
564           const int id2 = (int)id - (int)kMethodNames2Start;
565           if (id2 >= 0 && (unsigned)id2 < kNumMethodNames2)
566             s = kMethodNames2[id2];
567         }
568         if (s)
569           m += s;
570         else
571           m.Add_UInt32(id);
572       }
573       {
574         unsigned level = item.GetDeflateLevel();
575         if (level != 0)
576         {
577           if (id == NFileHeader::NCompressionMethod::kLZMA)
578           {
579             if (level & 1)
580               m += ":eos";
581             level &= ~(unsigned)1;
582           }
583           else if (id == NFileHeader::NCompressionMethod::kDeflate)
584           {
585             m.Add_Colon();
586             m += kDeflateLevels[level];
587             level = 0;
588           }
589 
590           if (level != 0)
591           {
592             m += ":v";
593             m.Add_UInt32(level);
594           }
595         }
596       }
597 
598       prop = m;
599       break;
600     }
601 
602     case kpidCharacts:
603     {
604       AString s;
605 
606       if (item.FromLocal)
607       {
608         s.Add_OptSpaced("Local");
609 
610         item.LocalExtra.PrintInfo(s);
611 
612         if (item.FromCentral)
613         {
614           s.Add_OptSpaced(":");
615           s.Add_OptSpaced("Central");
616         }
617       }
618 
619       if (item.FromCentral)
620       {
621         item.CentralExtra.PrintInfo(s);
622       }
623 
624       UInt32 flags = item.Flags;
625       flags &= ~(unsigned)6; // we don't need compression related bits here.
626 
627       if (flags != 0)
628       {
629         const AString s2 = FlagsToString(g_HeaderCharacts, Z7_ARRAY_SIZE(g_HeaderCharacts), flags);
630         if (!s2.IsEmpty())
631         {
632           if (!s.IsEmpty())
633             s.Add_OptSpaced(":");
634           s.Add_OptSpaced(s2);
635         }
636       }
637 
638       if (item.IsBadDescriptor())
639         s.Add_OptSpaced("Descriptor_ERROR");
640 
641       if (!s.IsEmpty())
642         prop = s;
643       break;
644     }
645 
646     case kpidHostOS:
647     {
648       if (item.FromCentral)
649       {
650         // 18.06: now we use HostOS only from Central::MadeByVersion
651         const Byte hostOS = item.MadeByVersion.HostOS;
652         TYPE_TO_PROP(kHostOS, hostOS, prop);
653       }
654       break;
655     }
656 
657     case kpidUnpackVer:
658       prop = (UInt32)item.ExtractVersion.Version;
659       break;
660 
661     case kpidVolumeIndex:
662       prop = item.Disk;
663       break;
664 
665     case kpidOffset:
666       prop = item.LocalHeaderPos;
667       break;
668 
669     /*
670     case kpidIsAltStream:
671       prop = (bool)(item.ParentOfAltStream >= 0); // item.IsAltStream();
672       break;
673 
674     case kpidName:
675       if (item.ParentOfAltStream >= 0)
676       {
677         // extract name of stream here
678       }
679       break;
680     */
681     default: break;
682   }
683 
684   return prop.Detach(value);
685   COM_TRY_END
686 }
687 
688 
689 
690 /*
691 Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps)
692 {
693   *numProps = 0;
694   return S_OK;
695 }
696 
697 Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID)
698 {
699   UNUSED_VAR(index);
700   *propID = 0;
701   *name = 0;
702   return S_OK;
703 }
704 
705 Z7_COM7F_IMF(CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType)
706 {
707   *parentType = NParentType::kDir;
708   *parent = (UInt32)(Int32)-1;
709   if (index >= m_Items.Size())
710     return S_OK;
711   const CItemEx &item = m_Items[index];
712 
713   if (item.ParentOfAltStream >= 0)
714   {
715     *parentType = NParentType::kAltStream;
716     *parent = item.ParentOfAltStream;
717   }
718   return S_OK;
719 }
720 
721 Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
722 {
723   UNUSED_VAR(index);
724   UNUSED_VAR(propID);
725   *data = NULL;
726   *dataSize = 0;
727   *propType = 0;
728   return S_OK;
729 }
730 
731 
732 void CHandler::MarkAltStreams(CObjectVector<CItemEx> &items)
733 {
734   int prevIndex = -1;
735   UString prevName;
736   UString name;
737 
738   for (unsigned i = 0; i < items.Size(); i++)
739   {
740     CItemEx &item = m_Items[i];
741     if (item.IsAltStream())
742     {
743       if (prevIndex == -1)
744         continue;
745       if (prevName.IsEmpty())
746       {
747         const CItemEx &prevItem = m_Items[prevIndex];
748         prevItem.GetUnicodeString(prevName, prevItem.Name, false, _forceCodePage, _specifiedCodePage);
749         NItemName::ReplaceToOsSlashes_Remove_TailSlash(prevName);
750       }
751       name.Empty();
752       item.GetUnicodeString(name, item.Name, false, _forceCodePage, _specifiedCodePage);
753       NItemName::ReplaceToOsSlashes_Remove_TailSlash(name);
754 
755       if (name.IsPrefixedBy(prevName))
756         if (IsString1PrefixedByString2(name.Ptr(prevName.Len()), k_SpecName_NTFS_STREAM))
757           item.ParentOfAltStream = prevIndex;
758     }
759     else
760     {
761       prevIndex = i;
762       prevName.Empty();
763     }
764   }
765 }
766 */
767 
Z7_COM7F_IMF(CHandler::Open (IInStream * inStream,const UInt64 * maxCheckStartPosition,IArchiveOpenCallback * callback))768 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream,
769     const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback))
770 {
771   COM_TRY_BEGIN
772   try
773   {
774     Close();
775     m_Archive.Force_ReadLocals_Mode = _force_OpenSeq;
776     // m_Archive.Disable_VolsRead = _force_OpenSeq;
777     // m_Archive.Disable_FindMarker = _force_OpenSeq;
778     HRESULT res = m_Archive.Open(inStream, maxCheckStartPosition, callback, m_Items);
779     if (res != S_OK)
780     {
781       m_Items.Clear();
782       m_Archive.ClearRefs(); // we don't want to clear error flags
783     }
784     // MarkAltStreams(m_Items);
785     return res;
786   }
787   catch(...) { Close(); throw; }
788   COM_TRY_END
789 }
790 
Z7_COM7F_IMF(CHandler::Close ())791 Z7_COM7F_IMF(CHandler::Close())
792 {
793   m_Items.Clear();
794   m_Archive.Close();
795   return S_OK;
796 }
797 
798 
799 Z7_CLASS_IMP_NOQIB_3(
800   CLzmaDecoder
801   , ICompressCoder
802   , ICompressSetFinishMode
803   , ICompressGetInStreamProcessedSize
804 )
805 public:
806   CMyComPtr2_Create<ICompressCoder, NCompress::NLzma::CDecoder> Decoder;
807 };
808 
809 static const unsigned kZipLzmaPropsSize = 4 + LZMA_PROPS_SIZE;
810 
811 Z7_COM7F_IMF(CLzmaDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
812     const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress))
813 {
814   Byte buf[kZipLzmaPropsSize];
815   RINOK(ReadStream_FALSE(inStream, buf, kZipLzmaPropsSize))
816   if (buf[2] != LZMA_PROPS_SIZE || buf[3] != 0)
817     return E_NOTIMPL;
818   RINOK(Decoder->SetDecoderProperties2(buf + 4, LZMA_PROPS_SIZE))
819   UInt64 inSize2 = 0;
820   if (inSize)
821   {
822     inSize2 = *inSize;
823     if (inSize2 < kZipLzmaPropsSize)
824       return S_FALSE;
825     inSize2 -= kZipLzmaPropsSize;
826   }
827   return Decoder.Interface()->Code(inStream, outStream, inSize ? &inSize2 : NULL, outSize, progress);
828 }
829 
830 Z7_COM7F_IMF(CLzmaDecoder::SetFinishMode(UInt32 finishMode))
831 {
832   Decoder->FinishStream = (finishMode != 0);
833   return S_OK;
834 }
835 
836 Z7_COM7F_IMF(CLzmaDecoder::GetInStreamProcessedSize(UInt64 *value))
837 {
838   *value = Decoder->GetInputProcessedSize() + kZipLzmaPropsSize;
839   return S_OK;
840 }
841 
842 
843 
844 
845 
846 
847 
848 struct CMethodItem
849 {
850   unsigned ZipMethod;
851   CMyComPtr<ICompressCoder> Coder;
852 };
853 
854 
855 
856 class CZipDecoder
857 {
858   CMyComPtr2<ICompressFilter, NCrypto::NZip::CDecoder> _zipCryptoDecoder;
859   CMyComPtr2<ICompressFilter, NCrypto::NZipStrong::CDecoder> _pkAesDecoder;
860   CMyComPtr2<ICompressFilter, NCrypto::NWzAes::CDecoder> _wzAesDecoder;
861 
862   CMyComPtr2<ISequentialInStream, CFilterCoder> filterStream;
863   CMyComPtr<ICryptoGetTextPassword> getTextPassword;
864   CObjectVector<CMethodItem> methodItems;
865 
866   CLzmaDecoder *lzmaDecoderSpec;
867 public:
868   CZipDecoder():
869       lzmaDecoderSpec(NULL)
870     {}
871 
872   HRESULT Decode(
873     DECL_EXTERNAL_CODECS_LOC_VARS
874     CInArchive &archive, const CItemEx &item,
875     ISequentialOutStream *realOutStream,
876     IArchiveExtractCallback *extractCallback,
877     ICompressProgressInfo *compressProgress,
878     #ifndef Z7_ST
879     UInt32 numThreads, UInt64 memUsage,
880     #endif
881     Int32 &res);
882 };
883 
884 
885 static HRESULT SkipStreamData(ISequentialInStream *stream,
886     ICompressProgressInfo *progress, UInt64 packSize, UInt64 unpackSize,
887     bool &thereAreData)
888 {
889   thereAreData = false;
890   const size_t kBufSize = 1 << 12;
891   Byte buf[kBufSize];
892   UInt64 prev = packSize;
893   for (;;)
894   {
895     size_t size = kBufSize;
896     RINOK(ReadStream(stream, buf, &size))
897     if (size == 0)
898       return S_OK;
899     thereAreData = true;
900     packSize += size;
901     if ((packSize - prev) >= (1 << 22))
902     {
903       prev = packSize;
904       RINOK(progress->SetRatioInfo(&packSize, &unpackSize))
905     }
906   }
907 }
908 
909 
910 
911 Z7_CLASS_IMP_NOQIB_1(
912   COutStreamWithPadPKCS7
913   , ISequentialOutStream
914 )
915   CMyComPtr<ISequentialOutStream> _stream;
916   UInt64 _size;
917   UInt64 _padPos;
918   UInt32 _padSize;
919   bool _padFailure;
920 public:
921   void SetStream(ISequentialOutStream *stream) { _stream = stream; }
922   void ReleaseStream() { _stream.Release(); }
923 
924   // padSize == 0 means (no_pad Mode)
925   void Init(UInt64 padPos, UInt32 padSize)
926   {
927     _padPos = padPos;
928     _padSize = padSize;
929     _size = 0;
930     _padFailure = false;
931   }
932   UInt64 GetSize() const { return _size; }
933   bool WasPadFailure() const { return _padFailure; }
934 };
935 
936 
937 Z7_COM7F_IMF(COutStreamWithPadPKCS7::Write(const void *data, UInt32 size, UInt32 *processedSize))
938 {
939   UInt32 written = 0;
940   HRESULT result = S_OK;
941   if (_size < _padPos)
942   {
943     const UInt64 rem = _padPos - _size;
944     UInt32 num = size;
945     if (num > rem)
946       num = (UInt32)rem;
947     result = _stream->Write(data, num, &written);
948     _size += written;
949     if (processedSize)
950       *processedSize = written;
951     if (_size != _padPos || result != S_OK)
952       return result;
953     size -= written;
954     data = ((const Byte *)data) + written;
955   }
956   _size += size;
957   written += size;
958   if (processedSize)
959     *processedSize = written;
960   if (_padSize != 0)
961   for (; size != 0; size--)
962   {
963     if (*(const Byte *)data != _padSize)
964       _padFailure = true;
965     data = ((const Byte *)data) + 1;
966   }
967   return result;
968 }
969 
970 
971 
972 HRESULT CZipDecoder::Decode(
973     DECL_EXTERNAL_CODECS_LOC_VARS
974     CInArchive &archive, const CItemEx &item,
975     ISequentialOutStream *realOutStream,
976     IArchiveExtractCallback *extractCallback,
977     ICompressProgressInfo *compressProgress,
978     #ifndef Z7_ST
979     UInt32 numThreads, UInt64 memUsage,
980     #endif
981     Int32 &res)
982 {
983   res = NExtract::NOperationResult::kHeadersError;
984 
985   CFilterCoder::C_InStream_Releaser inStreamReleaser;
986   CFilterCoder::C_Filter_Releaser filterReleaser;
987 
988   bool needCRC = true;
989   bool wzAesMode = false;
990   bool pkAesMode = false;
991 
992   bool badDescriptor = item.IsBadDescriptor();
993   if (badDescriptor)
994     needCRC = false;
995 
996 
997   unsigned id = item.Method;
998 
999   CWzAesExtra aesField;
1000   // LZFSE and WinZip's AES use same id - kWzAES.
1001 
1002   if (id == NFileHeader::NCompressionMethod::kWzAES)
1003   {
1004     if (item.GetMainExtra().GetWzAes(aesField))
1005     {
1006       if (!item.IsEncrypted())
1007       {
1008         res = NExtract::NOperationResult::kUnsupportedMethod;
1009         return S_OK;
1010       }
1011       wzAesMode = true;
1012       needCRC = aesField.NeedCrc();
1013     }
1014   }
1015 
1016   if (!wzAesMode)
1017   if (item.IsEncrypted())
1018   {
1019     if (item.IsStrongEncrypted())
1020     {
1021       CStrongCryptoExtra f;
1022       if (!item.CentralExtra.GetStrongCrypto(f))
1023       {
1024         res = NExtract::NOperationResult::kUnsupportedMethod;
1025         return S_OK;
1026       }
1027       pkAesMode = true;
1028     }
1029   }
1030 
1031   CMyComPtr2_Create<ISequentialOutStream, COutStreamWithCRC> outStream;
1032   outStream->SetStream(realOutStream);
1033   outStream->Init(needCRC);
1034 
1035   CMyComPtr<ISequentialInStream> packStream;
1036   CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStream;
1037 
1038   {
1039     UInt64 packSize = item.PackSize;
1040     if (wzAesMode)
1041     {
1042       if (packSize < NCrypto::NWzAes::kMacSize)
1043         return S_OK;
1044       packSize -= NCrypto::NWzAes::kMacSize;
1045     }
1046     RINOK(archive.GetItemStream(item, true, packStream))
1047     if (!packStream)
1048     {
1049       res = NExtract::NOperationResult::kUnavailable;
1050       return S_OK;
1051     }
1052     inStream->SetStream(packStream);
1053     inStream->Init(packSize);
1054   }
1055 
1056 
1057   res = NExtract::NOperationResult::kDataError;
1058 
1059   CMyComPtr<ICompressFilter> cryptoFilter;
1060 
1061   if (item.IsEncrypted())
1062   {
1063     if (wzAesMode)
1064     {
1065       id = aesField.Method;
1066       _wzAesDecoder.Create_if_Empty();
1067       cryptoFilter = _wzAesDecoder;
1068       if (!_wzAesDecoder->SetKeyMode(aesField.Strength))
1069       {
1070         res = NExtract::NOperationResult::kUnsupportedMethod;
1071         return S_OK;
1072       }
1073     }
1074     else if (pkAesMode)
1075     {
1076       _pkAesDecoder.Create_if_Empty();
1077       cryptoFilter = _pkAesDecoder;
1078     }
1079     else
1080     {
1081       _zipCryptoDecoder.Create_if_Empty();
1082       cryptoFilter = _zipCryptoDecoder;
1083     }
1084 
1085     CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
1086     RINOK(cryptoFilter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword))
1087     if (!cryptoSetPassword)
1088       return E_FAIL;
1089 
1090     if (!getTextPassword)
1091       extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword);
1092 
1093     if (getTextPassword)
1094     {
1095       CMyComBSTR_Wipe password;
1096       RINOK(getTextPassword->CryptoGetTextPassword(&password))
1097       AString_Wipe charPassword;
1098       if (password)
1099       {
1100 #if 0 && defined(_WIN32)
1101         // do we need UTF-8 passwords here ?
1102         if (item.GetHostOS() == NFileHeader::NHostOS::kUnix // 24.05
1103             // || item.IsUtf8() // 22.00
1104             )
1105         {
1106           // throw 1;
1107           ConvertUnicodeToUTF8((LPCOLESTR)password, charPassword);
1108         }
1109         else
1110 #endif
1111         {
1112           UnicodeStringToMultiByte2(charPassword, (LPCOLESTR)password, CP_ACP);
1113         }
1114         /*
1115         if (wzAesMode || pkAesMode)
1116         {
1117         }
1118         else
1119         {
1120           // PASSWORD encoding for ZipCrypto:
1121           // pkzip25 / WinZip / Windows probably use ANSI
1122           // 7-Zip <  4.43 creates ZIP archives with OEM encoding in password
1123           // 7-Zip >= 4.43 creates ZIP archives only with ASCII characters in password
1124           // 7-Zip <  17.00 uses CP_OEMCP for password decoding
1125           // 7-Zip >= 17.00 uses CP_ACP   for password decoding
1126         }
1127         */
1128       }
1129       HRESULT result = cryptoSetPassword->CryptoSetPassword(
1130         (const Byte *)(const char *)charPassword, charPassword.Len());
1131       if (result != S_OK)
1132       {
1133         res = NExtract::NOperationResult::kWrongPassword;
1134         return S_OK;
1135       }
1136     }
1137     else
1138     {
1139       res = NExtract::NOperationResult::kWrongPassword;
1140       return S_OK;
1141       // RINOK(cryptoSetPassword->CryptoSetPassword(NULL, 0));
1142     }
1143   }
1144 
1145   unsigned m;
1146   for (m = 0; m < methodItems.Size(); m++)
1147     if (methodItems[m].ZipMethod == id)
1148       break;
1149 
1150   if (m == methodItems.Size())
1151   {
1152     CMethodItem mi;
1153     mi.ZipMethod = id;
1154     if (id == NFileHeader::NCompressionMethod::kStore)
1155       mi.Coder = new NCompress::CCopyCoder;
1156     else if (id == NFileHeader::NCompressionMethod::kShrink)
1157       mi.Coder = new NCompress::NShrink::CDecoder;
1158     else if (id == NFileHeader::NCompressionMethod::kImplode)
1159       mi.Coder = new NCompress::NImplode::NDecoder::CCoder;
1160     else if (id == NFileHeader::NCompressionMethod::kLZMA)
1161     {
1162       lzmaDecoderSpec = new CLzmaDecoder;
1163       mi.Coder = lzmaDecoderSpec;
1164     }
1165     else if (id == NFileHeader::NCompressionMethod::kXz)
1166       mi.Coder = new NCompress::NXz::CComDecoder;
1167     else if (id == NFileHeader::NCompressionMethod::kPPMd)
1168       mi.Coder = new NCompress::NPpmdZip::CDecoder(true);
1169     else if (id == NFileHeader::NCompressionMethod::kZstdWz)
1170       mi.Coder = new NCompress::NZstd::CDecoder();
1171 #ifndef Z7_ZIP_LZFSE_DISABLE
1172     else if (id == NFileHeader::NCompressionMethod::kWzAES)
1173       mi.Coder = new NCompress::NLzfse::CDecoder;
1174 #endif
1175     else
1176     {
1177       CMethodId szMethodID;
1178       if (id == NFileHeader::NCompressionMethod::kBZip2)
1179         szMethodID = kMethodId_BZip2;
1180       else
1181       {
1182         if (id > 0xFF)
1183         {
1184           res = NExtract::NOperationResult::kUnsupportedMethod;
1185           return S_OK;
1186         }
1187         szMethodID = kMethodId_ZipBase + (Byte)id;
1188       }
1189 
1190       RINOK(CreateCoder_Id(EXTERNAL_CODECS_LOC_VARS szMethodID, false, mi.Coder))
1191 
1192       if (!mi.Coder)
1193       {
1194         res = NExtract::NOperationResult::kUnsupportedMethod;
1195         return S_OK;
1196       }
1197     }
1198     m = methodItems.Add(mi);
1199   }
1200 
1201   const CMethodItem &mi = methodItems[m];
1202   ICompressCoder *coder = mi.Coder;
1203 
1204 
1205   #ifndef Z7_ST
1206   {
1207     CMyComPtr<ICompressSetCoderMt> setCoderMt;
1208     coder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);
1209     if (setCoderMt)
1210     {
1211       RINOK(setCoderMt->SetNumberOfThreads(numThreads))
1212     }
1213   }
1214   // if (memUsage != 0)
1215   {
1216     CMyComPtr<ICompressSetMemLimit> setMemLimit;
1217     coder->QueryInterface(IID_ICompressSetMemLimit, (void **)&setMemLimit);
1218     if (setMemLimit)
1219     {
1220       RINOK(setMemLimit->SetMemLimit(memUsage))
1221     }
1222   }
1223   #endif
1224 
1225   {
1226     CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
1227     coder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties);
1228     if (setDecoderProperties)
1229     {
1230       Byte properties = (Byte)item.Flags;
1231       RINOK(setDecoderProperties->SetDecoderProperties2(&properties, 1))
1232     }
1233   }
1234 
1235 
1236   bool isFullStreamExpected = (!item.HasDescriptor() || item.PackSize != 0);
1237   bool needReminderCheck = false;
1238 
1239   bool dataAfterEnd = false;
1240   bool truncatedError = false;
1241   bool lzmaEosError = false;
1242   bool headersError  = false;
1243   bool padError = false;
1244   bool readFromFilter = false;
1245 
1246   const bool useUnpackLimit = (id == NFileHeader::NCompressionMethod::kStore
1247       || !item.HasDescriptor()
1248       || item.Size >= ((UInt64)1 << 32)
1249       || item.LocalExtra.IsZip64
1250       || item.CentralExtra.IsZip64
1251       );
1252 
1253   {
1254     HRESULT result = S_OK;
1255     if (item.IsEncrypted())
1256     {
1257       if (!filterStream.IsDefined())
1258         filterStream.SetFromCls(new CFilterCoder(false));
1259 
1260       filterReleaser.FilterCoder = filterStream.ClsPtr();
1261       filterStream->Filter = cryptoFilter;
1262 
1263       if (wzAesMode)
1264       {
1265         result = _wzAesDecoder->ReadHeader(inStream);
1266         if (result == S_OK)
1267         {
1268           if (!_wzAesDecoder->Init_and_CheckPassword())
1269           {
1270             res = NExtract::NOperationResult::kWrongPassword;
1271             return S_OK;
1272           }
1273         }
1274       }
1275       else if (pkAesMode)
1276       {
1277         isFullStreamExpected = false;
1278         result = _pkAesDecoder->ReadHeader(inStream, item.Crc, item.Size);
1279         if (result == S_OK)
1280         {
1281           bool passwOK;
1282           result = _pkAesDecoder->Init_and_CheckPassword(passwOK);
1283           if (result == S_OK && !passwOK)
1284           {
1285             res = NExtract::NOperationResult::kWrongPassword;
1286             return S_OK;
1287           }
1288         }
1289       }
1290       else
1291       {
1292         result = _zipCryptoDecoder->ReadHeader(inStream);
1293         if (result == S_OK)
1294         {
1295           _zipCryptoDecoder->Init_BeforeDecode();
1296 
1297           /* Info-ZIP modification to ZipCrypto format:
1298                if bit 3 of the general purpose bit flag is set,
1299                it uses high byte of 16-bit File Time.
1300              Info-ZIP code probably writes 2 bytes of File Time.
1301              We check only 1 byte. */
1302 
1303           // UInt32 v1 = GetUi16(_zipCryptoDecoder->_header + NCrypto::NZip::kHeaderSize - 2);
1304           // UInt32 v2 = (item.HasDescriptor() ? (item.Time & 0xFFFF) : (item.Crc >> 16));
1305 
1306           Byte v1 = _zipCryptoDecoder->_header[NCrypto::NZip::kHeaderSize - 1];
1307           Byte v2 = (Byte)(item.HasDescriptor() ? (item.Time >> 8) : (item.Crc >> 24));
1308 
1309           if (v1 != v2)
1310           {
1311             res = NExtract::NOperationResult::kWrongPassword;
1312             return S_OK;
1313           }
1314         }
1315       }
1316     }
1317 
1318     if (result == S_OK)
1319     {
1320       CMyComPtr<ICompressSetFinishMode> setFinishMode;
1321       coder->QueryInterface(IID_ICompressSetFinishMode, (void **)&setFinishMode);
1322       if (setFinishMode)
1323       {
1324         RINOK(setFinishMode->SetFinishMode(BoolToUInt(true)))
1325       }
1326 
1327       const UInt64 coderPackSize = inStream->GetRem();
1328 
1329       if (id == NFileHeader::NCompressionMethod::kStore && item.IsEncrypted())
1330       {
1331         // for debug : we can disable this code (kStore + 50), if we want to test CopyCoder+Filter
1332         // here we use filter without CopyCoder
1333         readFromFilter = false;
1334 
1335         COutStreamWithPadPKCS7 *padStreamSpec = NULL;
1336         CMyComPtr<ISequentialOutStream> padStream;
1337         UInt32 padSize = 0;
1338 
1339         if (pkAesMode)
1340         {
1341           padStreamSpec = new COutStreamWithPadPKCS7;
1342           padStream = padStreamSpec;
1343           padSize = _pkAesDecoder->GetPadSize((UInt32)item.Size);
1344           padStreamSpec->SetStream(outStream);
1345           padStreamSpec->Init(item.Size, padSize);
1346         }
1347 
1348         // Here we decode minimal required size, including padding
1349         const UInt64 expectedSize = item.Size + padSize;
1350         UInt64 size = coderPackSize;
1351         if (item.Size > coderPackSize)
1352           headersError = true;
1353         else if (expectedSize != coderPackSize)
1354         {
1355           headersError = true;
1356           if (coderPackSize > expectedSize)
1357             size = expectedSize;
1358         }
1359 
1360         result = filterStream->Code(inStream, padStream ?
1361             padStream.Interface() :
1362             outStream.Interface(),
1363             NULL, &size, compressProgress);
1364 
1365         if (outStream->GetSize() != item.Size)
1366           truncatedError = true;
1367 
1368         if (pkAesMode)
1369         {
1370           if (padStreamSpec->GetSize() != size)
1371             truncatedError = true;
1372           if (padStreamSpec->WasPadFailure())
1373             padError = true;
1374         }
1375       }
1376       else
1377       {
1378         if (item.IsEncrypted())
1379         {
1380           readFromFilter = true;
1381           inStreamReleaser.FilterCoder = filterStream.ClsPtr();
1382           RINOK(filterStream->SetInStream(inStream))
1383 
1384           /* IFilter::Init() does nothing in all zip crypto filters.
1385           So we can call any Initialize function in CFilterCoder. */
1386 
1387           RINOK(filterStream->Init_NoSubFilterInit())
1388           // RINOK(filterStream->SetOutStreamSize(NULL));
1389         }
1390 
1391         try {
1392         result = coder->Code(readFromFilter ?
1393               filterStream.Interface() :
1394               inStream.Interface(),
1395             outStream,
1396             isFullStreamExpected ? &coderPackSize : NULL,
1397             // NULL,
1398             useUnpackLimit ? &item.Size : NULL,
1399             compressProgress);
1400         } catch (...) { return E_FAIL; }
1401 
1402         if (result == S_OK)
1403         {
1404         CMyComPtr<ICompressGetInStreamProcessedSize> getInStreamProcessedSize;
1405         coder->QueryInterface(IID_ICompressGetInStreamProcessedSize, (void **)&getInStreamProcessedSize);
1406         if (getInStreamProcessedSize && setFinishMode)
1407         {
1408           UInt64 processed;
1409           RINOK(getInStreamProcessedSize->GetInStreamProcessedSize(&processed))
1410           if (processed != (UInt64)(Int64)-1)
1411           {
1412             if (pkAesMode)
1413             {
1414               const UInt32 padSize = _pkAesDecoder->GetPadSize((UInt32)processed);
1415               if (processed + padSize > coderPackSize)
1416                 truncatedError = true;
1417               else if (processed + padSize < coderPackSize)
1418                 dataAfterEnd = true;
1419               else
1420               {
1421                 {
1422                   // here we check PKCS7 padding data from reminder (it can be inside stream buffer in coder).
1423                   CMyComPtr<ICompressReadUnusedFromInBuf> readInStream;
1424                   coder->QueryInterface(IID_ICompressReadUnusedFromInBuf, (void **)&readInStream);
1425                   // CCopyCoder() for kStore doesn't read data outside of (item.Size)
1426                   if (readInStream || id == NFileHeader::NCompressionMethod::kStore)
1427                   {
1428                     // change pad size, if we support another block size in ZipStrong.
1429                     // here we request more data to detect error with data after end.
1430                     const UInt32 kBufSize = NCrypto::NZipStrong::kAesPadAllign + 16;
1431                     Byte buf[kBufSize];
1432                     UInt32 processedSize = 0;
1433                     if (readInStream)
1434                     {
1435                       RINOK(readInStream->ReadUnusedFromInBuf(buf, kBufSize, &processedSize))
1436                     }
1437                     if (processedSize > padSize)
1438                       dataAfterEnd = true;
1439                     else
1440                     {
1441                       size_t processedSize2 = kBufSize - processedSize;
1442                       result = ReadStream(filterStream, buf + processedSize, &processedSize2);
1443                       if (result == S_OK)
1444                       {
1445                         processedSize2 += processedSize;
1446                         if (processedSize2 > padSize)
1447                           dataAfterEnd = true;
1448                         else if (processedSize2 < padSize)
1449                           truncatedError = true;
1450                         else
1451                           for (unsigned i = 0; i < padSize; i++)
1452                             if (buf[i] != padSize)
1453                               padError = true;
1454                       }
1455                     }
1456                   }
1457                 }
1458               }
1459             }
1460             else
1461             {
1462               if (processed < coderPackSize)
1463               {
1464                 if (isFullStreamExpected)
1465                   dataAfterEnd = true;
1466               }
1467               else if (processed > coderPackSize)
1468               {
1469                 // that case is additional check, that can show the bugs in code (coder)
1470                 truncatedError = true;
1471               }
1472               needReminderCheck = isFullStreamExpected;
1473             }
1474           }
1475         }
1476         }
1477       }
1478 
1479       if (result == S_OK && id == NFileHeader::NCompressionMethod::kLZMA)
1480         if (!lzmaDecoderSpec->Decoder->CheckFinishStatus(item.IsLzmaEOS()))
1481           lzmaEosError = true;
1482     }
1483 
1484     if (result == S_FALSE)
1485       return S_OK;
1486 
1487     if (result == E_NOTIMPL)
1488     {
1489       res = NExtract::NOperationResult::kUnsupportedMethod;
1490       return S_OK;
1491     }
1492 
1493     RINOK(result)
1494   }
1495 
1496   bool crcOK = true;
1497   bool authOk = true;
1498   if (needCRC)
1499     crcOK = (outStream->GetCRC() == item.Crc);
1500 
1501   if (useUnpackLimit)
1502     if (outStream->GetSize() != item.Size)
1503       truncatedError = true;
1504 
1505   if (wzAesMode)
1506   {
1507     const UInt64 unpackSize = outStream->GetSize();
1508     const UInt64 packSize = inStream->GetSize();
1509     bool thereAreData = false;
1510     // read to the end from filter or from packed stream
1511     if (SkipStreamData(readFromFilter ?
1512           filterStream.Interface() :
1513           inStream.Interface(),
1514         compressProgress, packSize, unpackSize, thereAreData) != S_OK)
1515       authOk = false;
1516     if (needReminderCheck && thereAreData)
1517       dataAfterEnd = true;
1518 
1519     if (inStream->GetRem() != 0)
1520       truncatedError = true;
1521     else
1522     {
1523       inStream->Init(NCrypto::NWzAes::kMacSize);
1524       if (_wzAesDecoder->CheckMac(inStream, authOk) != S_OK)
1525         authOk = false;
1526     }
1527   }
1528 
1529   res = NExtract::NOperationResult::kCRCError;
1530 
1531   if (crcOK && authOk)
1532   {
1533     res = NExtract::NOperationResult::kOK;
1534 
1535     if (dataAfterEnd)
1536       res = NExtract::NOperationResult::kDataAfterEnd;
1537     else if (padError)
1538       res = NExtract::NOperationResult::kCRCError;
1539     else if (truncatedError)
1540       res = NExtract::NOperationResult::kUnexpectedEnd;
1541     else if (headersError)
1542       res = NExtract::NOperationResult::kHeadersError;
1543     else if (lzmaEosError)
1544       res = NExtract::NOperationResult::kHeadersError;
1545     else if (badDescriptor)
1546       res = NExtract::NOperationResult::kUnexpectedEnd;
1547 
1548     // CheckDescriptor() supports only data descriptor with signature and
1549     // it doesn't support "old" pkzip's data descriptor without signature.
1550     // So we disable that check.
1551     /*
1552     if (item.HasDescriptor() && archive.CheckDescriptor(item) != S_OK)
1553       res = NExtract::NOperationResult::kHeadersError;
1554     */
1555   }
1556 
1557   return S_OK;
1558 }
1559 
1560 
1561 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1562     Int32 testMode, IArchiveExtractCallback *extractCallback))
1563 {
1564   COM_TRY_BEGIN
1565   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1566   if (allFilesMode)
1567     numItems = m_Items.Size();
1568   if (numItems == 0)
1569     return S_OK;
1570   UInt64 total = 0; // , totalPacked = 0;
1571   UInt32 i;
1572   for (i = 0; i < numItems; i++)
1573   {
1574     const CItemEx &item = m_Items[allFilesMode ? i : indices[i]];
1575     total += item.Size;
1576     // totalPacked += item.PackSize;
1577   }
1578   RINOK(extractCallback->SetTotal(total))
1579 
1580   CZipDecoder myDecoder;
1581   UInt64 cur_Unpacked, cur_Packed;
1582 
1583   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1584   lps->Init(extractCallback, false);
1585 
1586   for (i = 0;; i++,
1587       lps->OutSize += cur_Unpacked,
1588       lps->InSize += cur_Packed)
1589   {
1590     RINOK(lps->SetCur())
1591     if (i >= numItems)
1592       return S_OK;
1593     const UInt32 index = allFilesMode ? i : indices[i];
1594     CItemEx item = m_Items[index];
1595     cur_Unpacked = item.Size;
1596     cur_Packed = item.PackSize;
1597 
1598     const bool isLocalOffsetOK = m_Archive.IsLocalOffsetOK(item);
1599     const bool skip = !isLocalOffsetOK && !item.IsDir();
1600     const Int32 askMode = skip ?
1601         NExtract::NAskMode::kSkip : testMode ?
1602         NExtract::NAskMode::kTest :
1603         NExtract::NAskMode::kExtract;
1604 
1605     Int32 opRes;
1606     {
1607     CMyComPtr<ISequentialOutStream> realOutStream;
1608     RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
1609 
1610     if (!isLocalOffsetOK)
1611     {
1612       RINOK(extractCallback->PrepareOperation(askMode))
1613       realOutStream.Release();
1614       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnavailable))
1615       continue;
1616     }
1617 
1618     bool headersError = false;
1619 
1620     if (!item.FromLocal)
1621     {
1622       bool isAvail = true;
1623       const HRESULT hres = m_Archive.Read_LocalItem_After_CdItem(item, isAvail, headersError);
1624       if (hres == S_FALSE)
1625       {
1626         if (item.IsDir() || realOutStream || testMode)
1627         {
1628           RINOK(extractCallback->PrepareOperation(askMode))
1629           realOutStream.Release();
1630           RINOK(extractCallback->SetOperationResult(
1631               isAvail ?
1632                 NExtract::NOperationResult::kHeadersError :
1633                 NExtract::NOperationResult::kUnavailable))
1634         }
1635         continue;
1636       }
1637       RINOK(hres)
1638     }
1639 
1640     if (item.IsDir())
1641     {
1642       // if (!testMode)
1643       {
1644         RINOK(extractCallback->PrepareOperation(askMode))
1645         realOutStream.Release();
1646         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
1647       }
1648       continue;
1649     }
1650 
1651     if (!testMode && !realOutStream)
1652       continue;
1653 
1654     RINOK(extractCallback->PrepareOperation(askMode))
1655 
1656     const HRESULT hres = myDecoder.Decode(
1657         EXTERNAL_CODECS_VARS
1658         m_Archive, item, realOutStream, extractCallback,
1659         lps,
1660         #ifndef Z7_ST
1661         _props._numThreads, _props._memUsage_Decompress,
1662         #endif
1663         opRes);
1664 
1665     RINOK(hres)
1666     // realOutStream.Release();
1667 
1668     if (opRes == NExtract::NOperationResult::kOK && headersError)
1669       opRes = NExtract::NOperationResult::kHeadersError;
1670     }
1671     RINOK(extractCallback->SetOperationResult(opRes))
1672   }
1673 
1674   COM_TRY_END
1675 }
1676 
1677 IMPL_ISetCompressCodecsInfo
1678 
1679 }}
1680