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