"Fossies" - the Fresh Open Source Software Archive

Member "p7zip_16.02/CPP/7zip/Archive/Zip/ZipHandler.cpp" (18 May 2016, 28043 Bytes) of package /linux/misc/p7zip_16.02_src_all.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "ZipHandler.cpp": 15.14.1_src_all_vs_16.02_src_all.

    1 // ZipHandler.cpp
    2 
    3 #include "StdAfx.h"
    4 
    5 #include "../../../Common/ComTry.h"
    6 #include "../../../Common/IntToString.h"
    7 #include "../../../Common/StringConvert.h"
    8 
    9 #include "../../../Windows/PropVariant.h"
   10 #include "../../../Windows/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 #include "../../Compress/LzmaDecoder.h"
   22 #include "../../Compress/ImplodeDecoder.h"
   23 #include "../../Compress/PpmdZip.h"
   24 #include "../../Compress/ShrinkDecoder.h"
   25 
   26 #include "../../Crypto/WzAes.h"
   27 #include "../../Crypto/ZipCrypto.h"
   28 #include "../../Crypto/ZipStrong.h"
   29 
   30 #include "../Common/ItemNameUtils.h"
   31 #include "../Common/OutStreamWithCRC.h"
   32 
   33 #include "../XzHandler.h"
   34 
   35 #include "ZipHandler.h"
   36 
   37 using namespace NWindows;
   38 
   39 namespace NArchive {
   40 namespace NZip {
   41 
   42 static const CMethodId kMethodId_ZipBase = 0x040100;
   43 static const CMethodId kMethodId_BZip2 = 0x040202;
   44 
   45 static const char * const kHostOS[] =
   46 {
   47     "FAT"
   48   , "AMIGA"
   49   , "VMS"
   50   , "Unix"
   51   , "VM/CMS"
   52   , "Atari"
   53   , "HPFS"
   54   , "Macintosh"
   55   , "Z-System"
   56   , "CP/M"
   57   , "TOPS-20"
   58   , "NTFS"
   59   , "SMS/QDOS"
   60   , "Acorn"
   61   , "VFAT"
   62   , "MVS"
   63   , "BeOS"
   64   , "Tandem"
   65   , "OS/400"
   66   , "OS/X"
   67 };
   68 
   69 static const char * const kMethods[] =
   70 {
   71     "Store"
   72   , "Shrink"
   73   , "Reduced1"
   74   , "Reduced2"
   75   , "Reduced3"
   76   , "Reduced4"
   77   , "Implode"
   78   , "Tokenizing"
   79   , "Deflate"
   80   , "Deflate64"
   81   , "PKImploding"
   82 };
   83 
   84 static const char *kMethod_AES = "AES";
   85 static const char *kMethod_ZipCrypto = "ZipCrypto";
   86 static const char *kMethod_StrongCrypto = "StrongCrypto";
   87 
   88 struct CIdToNamePair
   89 {
   90   unsigned Id;
   91   const char *Name;
   92 };
   93 
   94 static const CIdToNamePair k_MethodIdNamePairs[] =
   95 {
   96   { NFileHeader::NCompressionMethod::kBZip2, "BZip2" },
   97   { NFileHeader::NCompressionMethod::kLZMA, "LZMA" },
   98   { NFileHeader::NCompressionMethod::kXz, "xz" },
   99   { NFileHeader::NCompressionMethod::kJpeg, "Jpeg" },
  100   { NFileHeader::NCompressionMethod::kWavPack, "WavPack" },
  101   { NFileHeader::NCompressionMethod::kPPMd, "PPMd" }
  102 };
  103 
  104 static const CIdToNamePair k_StrongCryptoPairs[] =
  105 {
  106   { NStrongCrypto_AlgId::kDES, "DES" },
  107   { NStrongCrypto_AlgId::kRC2old, "RC2a" },
  108   { NStrongCrypto_AlgId::k3DES168, "3DES-168" },
  109   { NStrongCrypto_AlgId::k3DES112, "3DES-112" },
  110   { NStrongCrypto_AlgId::kAES128, "pkAES-128" },
  111   { NStrongCrypto_AlgId::kAES192, "pkAES-192" },
  112   { NStrongCrypto_AlgId::kAES256, "pkAES-256" },
  113   { NStrongCrypto_AlgId::kRC2, "RC2" },
  114   { NStrongCrypto_AlgId::kBlowfish, "Blowfish" },
  115   { NStrongCrypto_AlgId::kTwofish, "Twofish" },
  116   { NStrongCrypto_AlgId::kRC4, "RC4" }
  117 };
  118 
  119 const char *FindNameForId(const CIdToNamePair *pairs, unsigned num, unsigned id)
  120 {
  121   for (unsigned i = 0; i < num; i++)
  122   {
  123     const CIdToNamePair &pair = pairs[i];
  124     if (id == pair.Id)
  125       return pair.Name;
  126   }
  127   return NULL;
  128 }
  129 
  130 static const Byte kProps[] =
  131 {
  132   kpidPath,
  133   kpidIsDir,
  134   kpidSize,
  135   kpidPackSize,
  136   kpidMTime,
  137   kpidCTime,
  138   kpidATime,
  139   kpidAttrib,
  140   // kpidPosixAttrib,
  141   kpidEncrypted,
  142   kpidComment,
  143   kpidCRC,
  144   kpidMethod,
  145   kpidHostOS,
  146   kpidUnpackVer,
  147   kpidVolumeIndex
  148 };
  149 
  150 static const Byte kArcProps[] =
  151 {
  152   kpidEmbeddedStubSize,
  153   kpidBit64,
  154   kpidComment,
  155   kpidTotalPhySize,
  156   kpidIsVolume,
  157   kpidVolumeIndex,
  158   kpidNumVolumes
  159 };
  160 
  161 CHandler::CHandler()
  162 {
  163   InitMethodProps();
  164 }
  165 
  166 static AString BytesToString(const CByteBuffer &data)
  167 {
  168   AString s;
  169   s.SetFrom_CalcLen((const char *)(const Byte *)data, (unsigned)data.Size());
  170   return s;
  171 }
  172 
  173 IMP_IInArchive_Props
  174 IMP_IInArchive_ArcProps
  175 
  176 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
  177 {
  178   COM_TRY_BEGIN
  179   NWindows::NCOM::CPropVariant prop;
  180   switch (propID)
  181   {
  182     case kpidBit64:  if (m_Archive.IsZip64) prop = m_Archive.IsZip64; break;
  183     case kpidComment:  if (m_Archive.ArcInfo.Comment.Size() != 0) prop = MultiByteToUnicodeString(BytesToString(m_Archive.ArcInfo.Comment), CP_ACP); break;
  184 
  185     case kpidPhySize:  prop = m_Archive.GetPhySize(); break;
  186     case kpidOffset:  prop = m_Archive.GetOffset(); break;
  187 
  188     case kpidEmbeddedStubSize:
  189     {
  190       UInt64 stubSize = m_Archive.GetEmbeddedStubSize();
  191       if (stubSize != 0)
  192         prop = stubSize;
  193       break;
  194     }
  195 
  196     case kpidTotalPhySize: if (m_Archive.IsMultiVol) prop = m_Archive.Vols.GetTotalSize(); break;
  197     case kpidVolumeIndex: if (m_Archive.IsMultiVol) prop = (UInt32)m_Archive.Vols.StartVolIndex; break;
  198     case kpidIsVolume: if (m_Archive.IsMultiVol) prop = true; break;
  199     case kpidNumVolumes: if (m_Archive.IsMultiVol) prop = (UInt32)m_Archive.Vols.Streams.Size(); break;
  200 
  201     case kpidWarningFlags:
  202     {
  203       UInt32 v = 0;
  204       // if (m_Archive.ExtraMinorError) v |= kpv_ErrorFlags_HeadersError;
  205       if (m_Archive.HeadersWarning) v |= kpv_ErrorFlags_HeadersError;
  206       if (v != 0)
  207         prop = v;
  208       break;
  209     }
  210 
  211     case kpidError:
  212     {
  213       if (!m_Archive.Vols.MissingName.IsEmpty())
  214       {
  215         UString s;
  216         s.SetFromAscii("Missing volume : ");
  217         s += m_Archive.Vols.MissingName;
  218         prop = s;
  219       }
  220       break;
  221     }
  222 
  223     case kpidErrorFlags:
  224     {
  225       UInt32 v = 0;
  226       if (!m_Archive.IsArc) v |= kpv_ErrorFlags_IsNotArc;
  227       if (m_Archive.HeadersError) v |= kpv_ErrorFlags_HeadersError;
  228       if (m_Archive.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd;
  229       if (m_Archive.ArcInfo.Base < 0)
  230       {
  231         /* We try to support case when we have sfx-zip with embedded stub,
  232            but the stream has access only to zip part.
  233            In that case we ignore UnavailableStart error.
  234            maybe we must show warning in that case. */
  235         UInt64 stubSize = m_Archive.GetEmbeddedStubSize();
  236         if (stubSize < (UInt64)-m_Archive.ArcInfo.Base)
  237           v |= kpv_ErrorFlags_UnavailableStart;
  238       }
  239       if (m_Archive.NoCentralDir) v |= kpv_ErrorFlags_UnconfirmedStart;
  240       prop = v;
  241       break;
  242     }
  243 
  244     case kpidReadOnly:
  245     {
  246       if (m_Archive.IsOpen())
  247         if (!m_Archive.CanUpdate())
  248           prop = true;
  249       break;
  250     }
  251   }
  252   prop.Detach(value);
  253   COM_TRY_END
  254   return S_OK;
  255 }
  256 
  257 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
  258 {
  259   *numItems = m_Items.Size();
  260   return S_OK;
  261 }
  262 
  263 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
  264 {
  265   COM_TRY_BEGIN
  266   NWindows::NCOM::CPropVariant prop;
  267   const CItemEx &item = m_Items[index];
  268   const CExtraBlock &extra = item.GetMainExtra();
  269   
  270   switch (propID)
  271   {
  272     case kpidPath:
  273     {
  274       UString res;
  275       item.GetUnicodeString(res, item.Name, false, _forceCodePage, _specifiedCodePage);
  276       NItemName::ConvertToOSName2(res);
  277       prop = res;
  278       break;
  279     }
  280     
  281     case kpidIsDir:  prop = item.IsDir(); break;
  282     case kpidSize:  prop = item.Size; break;
  283     case kpidPackSize:  prop = item.PackSize; break;
  284     
  285     case kpidTimeType:
  286     {
  287       FILETIME ft;
  288       UInt32 unixTime;
  289       UInt32 type;
  290       if (extra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, ft))
  291         type = NFileTimeType::kWindows;
  292       else if (extra.GetUnixTime(true, NFileHeader::NUnixTime::kMTime, unixTime))
  293         type = NFileTimeType::kUnix;
  294       else
  295         type = NFileTimeType::kDOS;
  296       prop = type;
  297       break;
  298     }
  299     
  300     case kpidCTime:
  301     {
  302       FILETIME ft;
  303       if (extra.GetNtfsTime(NFileHeader::NNtfsExtra::kCTime, ft))
  304         prop = ft;
  305       break;
  306     }
  307     
  308     case kpidATime:
  309     {
  310       FILETIME ft;
  311       if (extra.GetNtfsTime(NFileHeader::NNtfsExtra::kATime, ft))
  312         prop = ft;
  313       break;
  314     }
  315     
  316     case kpidMTime:
  317     {
  318       FILETIME utc;
  319       bool defined = true;
  320       if (!extra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, utc))
  321       {
  322         UInt32 unixTime = 0;
  323         if (extra.GetUnixTime(true, NFileHeader::NUnixTime::kMTime, unixTime))
  324           NTime::UnixTimeToFileTime(unixTime, utc);
  325         else
  326         {
  327           FILETIME localFileTime;
  328           if (item.Time == 0)
  329             defined = false;
  330           else if (!NTime::DosTimeToFileTime(item.Time, localFileTime) ||
  331               !LocalFileTimeToFileTime(&localFileTime, &utc))
  332             utc.dwHighDateTime = utc.dwLowDateTime = 0;
  333         }
  334       }
  335       if (defined)
  336         prop = utc;
  337       break;
  338     }
  339     
  340     case kpidAttrib:  prop = item.GetWinAttrib(); break;
  341     
  342     case kpidPosixAttrib:
  343     {
  344       UInt32 attrib;
  345       if (item.GetPosixAttrib(attrib))
  346         prop = attrib;
  347       break;
  348     }
  349     
  350     case kpidEncrypted:  prop = item.IsEncrypted(); break;
  351     
  352     case kpidComment:
  353     {
  354       if (item.Comment.Size() != 0)
  355       {
  356         UString res;
  357         item.GetUnicodeString(res, BytesToString(item.Comment), true, _forceCodePage, _specifiedCodePage);
  358         prop = res;
  359       }
  360       break;
  361     }
  362     
  363     case kpidCRC:  if (item.IsThereCrc()) prop = item.Crc; break;
  364     
  365     case kpidMethod:
  366     {
  367       unsigned id = item.Method;
  368       AString m;
  369       
  370       if (item.IsEncrypted())
  371       {
  372         if (id == NFileHeader::NCompressionMethod::kWzAES)
  373         {
  374           m += kMethod_AES;
  375           CWzAesExtra aesField;
  376           if (extra.GetWzAes(aesField))
  377           {
  378             char s[16];
  379             s[0] = '-';
  380             ConvertUInt32ToString(((unsigned)aesField.Strength + 1) * 64 , s + 1);
  381             m += s;
  382             id = aesField.Method;
  383           }
  384         }
  385         else if (item.IsStrongEncrypted())
  386         {
  387           CStrongCryptoExtra f;
  388           f.AlgId = 0;
  389           if (extra.GetStrongCrypto(f))
  390           {
  391             const char *s = FindNameForId(k_StrongCryptoPairs, ARRAY_SIZE(k_StrongCryptoPairs), f.AlgId);
  392             if (s)
  393               m += s;
  394             else
  395             {
  396               m += kMethod_StrongCrypto;
  397               char temp[16];
  398               temp[0] = ':';
  399               ConvertUInt32ToString(f.AlgId, temp + 1);
  400               m += temp;
  401             }
  402             if (f.CertificateIsUsed())
  403               m += "-Cert";
  404           }
  405           else
  406             m += kMethod_StrongCrypto;
  407         }
  408         else
  409           m += kMethod_ZipCrypto;
  410         m += ' ';
  411       }
  412       
  413       {
  414         char temp[16];
  415         const char *s = NULL;
  416         if (id < ARRAY_SIZE(kMethods))
  417           s = kMethods[id];
  418         else
  419         {
  420           s = FindNameForId(k_MethodIdNamePairs, ARRAY_SIZE(k_MethodIdNamePairs), id);
  421           if (!s)
  422           {
  423             ConvertUInt32ToString(id, temp);
  424             s = temp;
  425           }
  426         }
  427         m += s;
  428         if (id == NFileHeader::NCompressionMethod::kLZMA && item.IsLzmaEOS())
  429           m += ":EOS";
  430       }
  431       
  432       prop = m;
  433       break;
  434     }
  435 
  436     case kpidHostOS:
  437     {
  438       Byte hostOS = item.GetHostOS();
  439       char temp[16];
  440       const char *s = NULL;
  441       if (hostOS < ARRAY_SIZE(kHostOS))
  442         s = kHostOS[hostOS];
  443       else
  444       {
  445         ConvertUInt32ToString(hostOS, temp);
  446         s = temp;
  447       }
  448       prop = s;
  449       break;
  450     }
  451     
  452     case kpidUnpackVer:
  453       prop = (UInt32)item.ExtractVersion.Version;
  454       break;
  455 
  456     case kpidVolumeIndex:
  457       prop = item.Disk;
  458       break;
  459   }
  460   
  461   prop.Detach(value);
  462   return S_OK;
  463   COM_TRY_END
  464 }
  465 
  466 
  467 STDMETHODIMP CHandler::Open(IInStream *inStream,
  468     const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback)
  469 {
  470   COM_TRY_BEGIN
  471   try
  472   {
  473     Close();
  474     HRESULT res = m_Archive.Open(inStream, maxCheckStartPosition, callback, m_Items);
  475     if (res != S_OK)
  476     {
  477       m_Items.Clear();
  478       m_Archive.ClearRefs();
  479     }
  480     return res;
  481   }
  482   catch(...) { Close(); throw; }
  483   COM_TRY_END
  484 }
  485 
  486 STDMETHODIMP CHandler::Close()
  487 {
  488   m_Items.Clear();
  489   m_Archive.Close();
  490   return S_OK;
  491 }
  492 
  493 
  494 class CLzmaDecoder:
  495   public ICompressCoder,
  496   public CMyUnknownImp
  497 {
  498   NCompress::NLzma::CDecoder *DecoderSpec;
  499   CMyComPtr<ICompressCoder> Decoder;
  500 public:
  501   CLzmaDecoder();
  502   STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
  503       const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);
  504 
  505   MY_UNKNOWN_IMP
  506 };
  507 
  508 CLzmaDecoder::CLzmaDecoder()
  509 {
  510   DecoderSpec = new NCompress::NLzma::CDecoder;
  511   Decoder = DecoderSpec;
  512 }
  513 
  514 HRESULT CLzmaDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
  515     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
  516 {
  517   Byte buf[9];
  518   RINOK(ReadStream_FALSE(inStream, buf, 9));
  519   if (buf[2] != 5 || buf[3] != 0)
  520     return E_NOTIMPL;
  521   RINOK(DecoderSpec->SetDecoderProperties2(buf + 4, 5));
  522   return Decoder->Code(inStream, outStream, NULL, outSize, progress);
  523 }
  524 
  525 
  526 class CXzDecoder:
  527   public ICompressCoder,
  528   public CMyUnknownImp
  529 {
  530   NArchive::NXz::CDecoder _decoder;
  531 public:
  532 
  533   STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
  534       const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);
  535 
  536   MY_UNKNOWN_IMP
  537 };
  538 
  539 HRESULT CXzDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
  540     const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
  541 {
  542   RINOK(_decoder.Decode(inStream, outStream, progress));
  543   Int32 opRes = _decoder.Get_Extract_OperationResult();
  544   if (opRes == NExtract::NOperationResult::kUnsupportedMethod)
  545     return E_NOTIMPL;
  546   if (opRes != NExtract::NOperationResult::kOK)
  547     return S_FALSE;
  548   return S_OK;
  549 }
  550 
  551 
  552 struct CMethodItem
  553 {
  554   unsigned ZipMethod;
  555   CMyComPtr<ICompressCoder> Coder;
  556 };
  557 
  558 
  559 
  560 class CZipDecoder
  561 {
  562   NCrypto::NZip::CDecoder *_zipCryptoDecoderSpec;
  563   NCrypto::NZipStrong::CDecoder *_pkAesDecoderSpec;
  564   NCrypto::NWzAes::CDecoder *_wzAesDecoderSpec;
  565 
  566   CMyComPtr<ICompressFilter> _zipCryptoDecoder;
  567   CMyComPtr<ICompressFilter> _pkAesDecoder;
  568   CMyComPtr<ICompressFilter> _wzAesDecoder;
  569 
  570   CFilterCoder *filterStreamSpec;
  571   CMyComPtr<ISequentialInStream> filterStream;
  572   CMyComPtr<ICryptoGetTextPassword> getTextPassword;
  573   CObjectVector<CMethodItem> methodItems;
  574 
  575 public:
  576   CZipDecoder():
  577       _zipCryptoDecoderSpec(0),
  578       _pkAesDecoderSpec(0),
  579       _wzAesDecoderSpec(0),
  580       filterStreamSpec(0) {}
  581 
  582   HRESULT Decode(
  583     DECL_EXTERNAL_CODECS_LOC_VARS
  584     CInArchive &archive, const CItemEx &item,
  585     ISequentialOutStream *realOutStream,
  586     IArchiveExtractCallback *extractCallback,
  587     ICompressProgressInfo *compressProgress,
  588     #ifndef _7ZIP_ST
  589     UInt32 numThreads,
  590     #endif
  591     Int32 &res);
  592 };
  593 
  594 
  595 static HRESULT SkipStreamData(ISequentialInStream *stream, UInt64 size)
  596 {
  597   const size_t kBufSize = 1 << 12;
  598   Byte buf[kBufSize];
  599   for (;;)
  600   {
  601     if (size == 0)
  602       return S_OK;
  603     size_t curSize = kBufSize;
  604     if (curSize > size)
  605       curSize = (size_t)size;
  606     RINOK(ReadStream_FALSE(stream, buf, curSize));
  607     size -= curSize;
  608   }
  609 }
  610 
  611 
  612 HRESULT CZipDecoder::Decode(
  613     DECL_EXTERNAL_CODECS_LOC_VARS
  614     CInArchive &archive, const CItemEx &item,
  615     ISequentialOutStream *realOutStream,
  616     IArchiveExtractCallback *extractCallback,
  617     ICompressProgressInfo *compressProgress,
  618     #ifndef _7ZIP_ST
  619     UInt32 numThreads,
  620     #endif
  621     Int32 &res)
  622 {
  623   res = NExtract::NOperationResult::kDataError;
  624   CFilterCoder::C_InStream_Releaser inStreamReleaser;
  625 
  626   bool needCRC = true;
  627   bool wzAesMode = false;
  628   bool pkAesMode = false;
  629   unsigned id = item.Method;
  630 
  631   if (item.IsEncrypted())
  632   {
  633     if (item.IsStrongEncrypted())
  634     {
  635       CStrongCryptoExtra f;
  636       if (item.CentralExtra.GetStrongCrypto(f))
  637       {
  638         pkAesMode = true;
  639       }
  640       if (!pkAesMode)
  641       {
  642         res = NExtract::NOperationResult::kUnsupportedMethod;
  643         return S_OK;
  644       }
  645     }
  646     if (!pkAesMode && id == NFileHeader::NCompressionMethod::kWzAES)
  647     {
  648       CWzAesExtra aesField;
  649       if (item.GetMainExtra().GetWzAes(aesField))
  650       {
  651         wzAesMode = true;
  652         needCRC = aesField.NeedCrc();
  653       }
  654     }
  655   }
  656     
  657   COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
  658   CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
  659   outStreamSpec->SetStream(realOutStream);
  660   outStreamSpec->Init(needCRC);
  661   
  662   CMyComPtr<ISequentialInStream> packStream;
  663 
  664   CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream;
  665   CMyComPtr<ISequentialInStream> inStream(limitedStreamSpec);
  666 
  667   {
  668     UInt64 packSize = item.PackSize;
  669     if (wzAesMode)
  670     {
  671       if (packSize < NCrypto::NWzAes::kMacSize)
  672         return S_OK;
  673       packSize -= NCrypto::NWzAes::kMacSize;
  674     }
  675     RINOK(archive.GetItemStream(item, true, packStream));
  676     if (!packStream)
  677     {
  678       res = NExtract::NOperationResult::kUnavailable;
  679       return S_OK;
  680     }
  681     limitedStreamSpec->SetStream(packStream);
  682     limitedStreamSpec->Init(packSize);
  683   }
  684   
  685   CMyComPtr<ICompressFilter> cryptoFilter;
  686   
  687   if (item.IsEncrypted())
  688   {
  689     if (wzAesMode)
  690     {
  691       CWzAesExtra aesField;
  692       if (!item.GetMainExtra().GetWzAes(aesField))
  693         return S_OK;
  694       id = aesField.Method;
  695       if (!_wzAesDecoder)
  696       {
  697         _wzAesDecoderSpec = new NCrypto::NWzAes::CDecoder;
  698         _wzAesDecoder = _wzAesDecoderSpec;
  699       }
  700       cryptoFilter = _wzAesDecoder;
  701       if (!_wzAesDecoderSpec->SetKeyMode(aesField.Strength))
  702       {
  703         res = NExtract::NOperationResult::kUnsupportedMethod;
  704         return S_OK;
  705       }
  706     }
  707     else if (pkAesMode)
  708     {
  709       if (!_pkAesDecoder)
  710       {
  711         _pkAesDecoderSpec = new NCrypto::NZipStrong::CDecoder;
  712         _pkAesDecoder = _pkAesDecoderSpec;
  713       }
  714       cryptoFilter = _pkAesDecoder;
  715     }
  716     else
  717     {
  718       if (!_zipCryptoDecoder)
  719       {
  720         _zipCryptoDecoderSpec = new NCrypto::NZip::CDecoder;
  721         _zipCryptoDecoder = _zipCryptoDecoderSpec;
  722       }
  723       cryptoFilter = _zipCryptoDecoder;
  724     }
  725     
  726     CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
  727     RINOK(cryptoFilter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword));
  728     
  729     if (!getTextPassword)
  730       extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword);
  731     
  732     if (getTextPassword)
  733     {
  734       CMyComBSTR password;
  735       RINOK(getTextPassword->CryptoGetTextPassword(&password));
  736       AString charPassword;
  737       if (password)
  738       {
  739         if (wzAesMode || pkAesMode)
  740         {
  741           charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_ACP);
  742           /*
  743           for (unsigned i = 0;; i++)
  744           {
  745             wchar_t c = password[i];
  746             if (c == 0)
  747               break;
  748             if (c >= 0x80)
  749             {
  750               res = NExtract::NOperationResult::kDataError;
  751               return S_OK;
  752             }
  753             charPassword += (char)c;
  754           }
  755           */
  756         }
  757         else
  758         {
  759           /* pkzip25 / WinZip / Windows probably use ANSI for some files
  760              We use OEM for compatibility with previous versions of 7-Zip? */
  761           charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP);
  762         }
  763       }
  764       HRESULT result = cryptoSetPassword->CryptoSetPassword(
  765         (const Byte *)(const char *)charPassword, charPassword.Len());
  766       if (result != S_OK)
  767         return S_OK;
  768     }
  769     else
  770     {
  771       RINOK(cryptoSetPassword->CryptoSetPassword(0, 0));
  772     }
  773   }
  774   
  775   unsigned m;
  776   for (m = 0; m < methodItems.Size(); m++)
  777     if (methodItems[m].ZipMethod == id)
  778       break;
  779 
  780   if (m == methodItems.Size())
  781   {
  782     CMethodItem mi;
  783     mi.ZipMethod = id;
  784     if (id == NFileHeader::NCompressionMethod::kStored)
  785       mi.Coder = new NCompress::CCopyCoder;
  786     else if (id == NFileHeader::NCompressionMethod::kShrunk)
  787       mi.Coder = new NCompress::NShrink::CDecoder;
  788     else if (id == NFileHeader::NCompressionMethod::kImploded)
  789       mi.Coder = new NCompress::NImplode::NDecoder::CCoder;
  790     else if (id == NFileHeader::NCompressionMethod::kLZMA)
  791       mi.Coder = new CLzmaDecoder;
  792     else if (id == NFileHeader::NCompressionMethod::kXz)
  793       mi.Coder = new CXzDecoder;
  794     else if (id == NFileHeader::NCompressionMethod::kPPMd)
  795       mi.Coder = new NCompress::NPpmdZip::CDecoder(true);
  796     else
  797     {
  798       CMethodId szMethodID;
  799       if (id == NFileHeader::NCompressionMethod::kBZip2)
  800         szMethodID = kMethodId_BZip2;
  801       else
  802       {
  803         if (id > 0xFF)
  804         {
  805           res = NExtract::NOperationResult::kUnsupportedMethod;
  806           return S_OK;
  807         }
  808         szMethodID = kMethodId_ZipBase + (Byte)id;
  809       }
  810 
  811       RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS szMethodID, false, mi.Coder));
  812 
  813       if (mi.Coder == 0)
  814       {
  815         res = NExtract::NOperationResult::kUnsupportedMethod;
  816         return S_OK;
  817       }
  818     }
  819     m = methodItems.Add(mi);
  820   }
  821 
  822   ICompressCoder *coder = methodItems[m].Coder;
  823   
  824   {
  825     CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
  826     coder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties);
  827     if (setDecoderProperties)
  828     {
  829       Byte properties = (Byte)item.Flags;
  830       RINOK(setDecoderProperties->SetDecoderProperties2(&properties, 1));
  831     }
  832   }
  833   
  834   #ifndef _7ZIP_ST
  835   {
  836     CMyComPtr<ICompressSetCoderMt> setCoderMt;
  837     coder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);
  838     if (setCoderMt)
  839     {
  840       RINOK(setCoderMt->SetNumberOfThreads(numThreads));
  841     }
  842   }
  843   #endif
  844   
  845   {
  846     HRESULT result = S_OK;
  847     CMyComPtr<ISequentialInStream> inStreamNew;
  848     if (item.IsEncrypted())
  849     {
  850       if (!filterStream)
  851       {
  852         filterStreamSpec = new CFilterCoder(false);
  853         filterStream = filterStreamSpec;
  854       }
  855      
  856       filterStreamSpec->Filter = cryptoFilter;
  857       
  858       if (wzAesMode)
  859       {
  860         result = _wzAesDecoderSpec->ReadHeader(inStream);
  861         if (result == S_OK)
  862         {
  863           if (!_wzAesDecoderSpec->Init_and_CheckPassword())
  864           {
  865             res = NExtract::NOperationResult::kWrongPassword;
  866             return S_OK;
  867           }
  868         }
  869       }
  870       else if (pkAesMode)
  871       {
  872         result =_pkAesDecoderSpec->ReadHeader(inStream, item.Crc, item.Size);
  873         if (result == S_OK)
  874         {
  875           bool passwOK;
  876           result = _pkAesDecoderSpec->Init_and_CheckPassword(passwOK);
  877           if (result == S_OK && !passwOK)
  878           {
  879             res = NExtract::NOperationResult::kWrongPassword;
  880             return S_OK;
  881           }
  882         }
  883       }
  884       else
  885       {
  886         result = _zipCryptoDecoderSpec->ReadHeader(inStream);
  887         if (result == S_OK)
  888         {
  889           _zipCryptoDecoderSpec->Init_BeforeDecode();
  890           
  891           /* Info-ZIP modification to ZipCrypto format:
  892                if bit 3 of the general purpose bit flag is set,
  893                it uses high byte of 16-bit File Time.
  894              Info-ZIP code probably writes 2 bytes of File Time.
  895              We check only 1 byte. */
  896 
  897           // UInt32 v1 = GetUi16(_zipCryptoDecoderSpec->_header + NCrypto::NZip::kHeaderSize - 2);
  898           // UInt32 v2 = (item.HasDescriptor() ? (item.Time & 0xFFFF) : (item.Crc >> 16));
  899 
  900           Byte v1 = _zipCryptoDecoderSpec->_header[NCrypto::NZip::kHeaderSize - 1];
  901           Byte v2 = (Byte)(item.HasDescriptor() ? (item.Time >> 8) : (item.Crc >> 24));
  902 
  903           if (v1 != v2)
  904           {
  905             res = NExtract::NOperationResult::kWrongPassword;
  906             return S_OK;
  907           }
  908         }
  909       }
  910 
  911       if (result == S_OK)
  912       {
  913         inStreamReleaser.FilterCoder = filterStreamSpec;
  914         RINOK(filterStreamSpec->SetInStream(inStream));
  915         
  916         /* IFilter::Init() does nothing in all zip crypto filters.
  917            So we can call any Initialize function in CFilterCoder. */
  918 
  919         RINOK(filterStreamSpec->Init_NoSubFilterInit());
  920         // RINOK(filterStreamSpec->SetOutStreamSize(NULL));
  921       
  922         inStreamNew = filterStream;
  923       }
  924     }
  925     else
  926       inStreamNew = inStream;
  927 
  928     if (result == S_OK)
  929       result = coder->Code(inStreamNew, outStream, NULL, &item.Size, compressProgress);
  930     
  931     if (result == S_FALSE)
  932       return S_OK;
  933     
  934     if (result == E_NOTIMPL)
  935     {
  936       res = NExtract::NOperationResult::kUnsupportedMethod;
  937       return S_OK;
  938     }
  939 
  940     RINOK(result);
  941   }
  942 
  943   bool crcOK = true;
  944   bool authOk = true;
  945   if (needCRC)
  946     crcOK = (outStreamSpec->GetCRC() == item.Crc);
  947   
  948   if (wzAesMode)
  949   {
  950     const UInt64 rem = limitedStreamSpec->GetRem();
  951     if (rem != 0)
  952       if (SkipStreamData(inStream, rem) != S_OK)
  953         authOk = false;
  954 
  955     limitedStreamSpec->Init(NCrypto::NWzAes::kMacSize);
  956     if (_wzAesDecoderSpec->CheckMac(inStream, authOk) != S_OK)
  957       authOk = false;
  958   }
  959   
  960   res = ((crcOK && authOk) ?
  961     NExtract::NOperationResult::kOK :
  962     NExtract::NOperationResult::kCRCError);
  963   return S_OK;
  964 }
  965 
  966 
  967 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
  968     Int32 testMode, IArchiveExtractCallback *extractCallback)
  969 {
  970   COM_TRY_BEGIN
  971   CZipDecoder myDecoder;
  972   UInt64 totalUnPacked = 0, totalPacked = 0;
  973   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
  974   if (allFilesMode)
  975     numItems = m_Items.Size();
  976   if (numItems == 0)
  977     return S_OK;
  978   UInt32 i;
  979   for (i = 0; i < numItems; i++)
  980   {
  981     const CItemEx &item = m_Items[allFilesMode ? i : indices[i]];
  982     totalUnPacked += item.Size;
  983     totalPacked += item.PackSize;
  984   }
  985   RINOK(extractCallback->SetTotal(totalUnPacked));
  986 
  987   UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0;
  988   UInt64 currentItemUnPacked, currentItemPacked;
  989   
  990   CLocalProgress *lps = new CLocalProgress;
  991   CMyComPtr<ICompressProgressInfo> progress = lps;
  992   lps->Init(extractCallback, false);
  993 
  994   for (i = 0; i < numItems; i++,
  995       currentTotalUnPacked += currentItemUnPacked,
  996       currentTotalPacked += currentItemPacked)
  997   {
  998     currentItemUnPacked = 0;
  999     currentItemPacked = 0;
 1000 
 1001     lps->InSize = currentTotalPacked;
 1002     lps->OutSize = currentTotalUnPacked;
 1003     RINOK(lps->SetCur());
 1004 
 1005     CMyComPtr<ISequentialOutStream> realOutStream;
 1006     Int32 askMode = testMode ?
 1007         NExtract::NAskMode::kTest :
 1008         NExtract::NAskMode::kExtract;
 1009     UInt32 index = allFilesMode ? i : indices[i];
 1010 
 1011     CItemEx item = m_Items[index];
 1012     bool isLocalOffsetOK = m_Archive.IsLocalOffsetOK(item);
 1013     bool skip = !isLocalOffsetOK && !item.IsDir();
 1014     if (skip)
 1015       askMode = NExtract::NAskMode::kSkip;
 1016 
 1017     currentItemUnPacked = item.Size;
 1018     currentItemPacked = item.PackSize;
 1019 
 1020     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
 1021 
 1022     if (!isLocalOffsetOK)
 1023     {
 1024       RINOK(extractCallback->PrepareOperation(askMode));
 1025       realOutStream.Release();
 1026       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnavailable));
 1027       continue;
 1028     }
 1029     
 1030     if (!item.FromLocal)
 1031     {
 1032       bool isAvail = true;
 1033       HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item, isAvail);
 1034       if (res == S_FALSE)
 1035       {
 1036         if (item.IsDir() || realOutStream || testMode)
 1037         {
 1038           RINOK(extractCallback->PrepareOperation(askMode));
 1039           realOutStream.Release();
 1040           RINOK(extractCallback->SetOperationResult(
 1041               isAvail ?
 1042                 NExtract::NOperationResult::kHeadersError :
 1043                 NExtract::NOperationResult::kUnavailable));
 1044         }
 1045         continue;
 1046       }
 1047       RINOK(res);
 1048     }
 1049 
 1050     if (item.IsDir())
 1051     {
 1052       // if (!testMode)
 1053       {
 1054         RINOK(extractCallback->PrepareOperation(askMode));
 1055         realOutStream.Release();
 1056         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
 1057       }
 1058       continue;
 1059     }
 1060 
 1061     if (!testMode && !realOutStream)
 1062       continue;
 1063 
 1064     RINOK(extractCallback->PrepareOperation(askMode));
 1065 
 1066     Int32 res;
 1067     HRESULT hres = myDecoder.Decode(
 1068         EXTERNAL_CODECS_VARS
 1069         m_Archive, item, realOutStream, extractCallback,
 1070         progress,
 1071         #ifndef _7ZIP_ST
 1072         _props.NumThreads,
 1073         #endif
 1074         res);
 1075     RINOK(hres);
 1076     realOutStream.Release();
 1077     
 1078     RINOK(extractCallback->SetOperationResult(res))
 1079   }
 1080   
 1081   lps->InSize = currentTotalPacked;
 1082   lps->OutSize = currentTotalUnPacked;
 1083   return lps->SetCur();
 1084   COM_TRY_END
 1085 }
 1086 
 1087 IMPL_ISetCompressCodecsInfo
 1088 
 1089 }}