diff options
Diffstat (limited to 'CPP/7zip/Archive/Zip/ZipHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/Zip/ZipHandler.cpp | 180 |
1 files changed, 126 insertions, 54 deletions
diff --git a/CPP/7zip/Archive/Zip/ZipHandler.cpp b/CPP/7zip/Archive/Zip/ZipHandler.cpp index f556068c..3e29a880 100644 --- a/CPP/7zip/Archive/Zip/ZipHandler.cpp +++ b/CPP/7zip/Archive/Zip/ZipHandler.cpp @@ -28,6 +28,8 @@ #include "../Common/ItemNameUtils.h" #include "../Common/OutStreamWithCRC.h" +#include "../XzHandler.h" + #include "ZipHandler.h" using namespace NWindows; @@ -38,7 +40,7 @@ namespace NZip { static const CMethodId kMethodId_ZipBase = 0x040100; static const CMethodId kMethodId_BZip2 = 0x040202; -static const char *kHostOS[] = +static const char * const kHostOS[] = { "FAT" , "AMIGA" @@ -62,7 +64,7 @@ static const char *kHostOS[] = , "OS/X" }; -static const char *kMethods[] = +static const char * const kMethods[] = { "Store" , "Shrink" @@ -91,6 +93,7 @@ static const CIdToNamePair k_MethodIdNamePairs[] = { { NFileHeader::NCompressionMethod::kBZip2, "BZip2" }, { NFileHeader::NCompressionMethod::kLZMA, "LZMA" }, + { NFileHeader::NCompressionMethod::kXz, "xz" }, { NFileHeader::NCompressionMethod::kJpeg, "Jpeg" }, { NFileHeader::NCompressionMethod::kWavPack, "WavPack" }, { NFileHeader::NCompressionMethod::kPPMd, "PPMd" } @@ -156,14 +159,7 @@ CHandler::CHandler() static AString BytesToString(const CByteBuffer &data) { AString s; - unsigned size = (unsigned)data.Size(); - if (size > 0) - { - char *p = s.GetBuffer(size); - memcpy(p, (const Byte *)data, size); - p[size] = 0; - s.ReleaseBuffer(); - } + s.SetFrom_CalcLen((const char *)(const Byte *)data, (unsigned)data.Size()); return s; } @@ -220,6 +216,14 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) prop = v; break; } + + case kpidReadOnly: + { + if (m_Archive.IsOpen()) + if (!m_Archive.CanUpdate()) + prop = true; + break; + } } prop.Detach(value); COM_TRY_END @@ -334,12 +338,12 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val case kpidMethod: { - UInt16 methodId = item.Method; + unsigned id = item.Method; AString m; if (item.IsEncrypted()) { - if (methodId == NFileHeader::NCompressionMethod::kWzAES) + if (id == NFileHeader::NCompressionMethod::kWzAES) { m += kMethod_AES; CWzAesExtra aesField; @@ -349,7 +353,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val s[0] = '-'; ConvertUInt32ToString(((unsigned)aesField.Strength + 1) * 64 , s + 1); m += s; - methodId = aesField.Method; + id = aesField.Method; } } else if (item.IsStrongEncrypted()) @@ -381,19 +385,19 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val { char temp[16]; const char *s = NULL; - if (methodId < ARRAY_SIZE(kMethods)) - s = kMethods[methodId]; + if (id < ARRAY_SIZE(kMethods)) + s = kMethods[id]; else { - s = FindNameForId(k_MethodIdNamePairs, ARRAY_SIZE(k_MethodIdNamePairs), methodId); + s = FindNameForId(k_MethodIdNamePairs, ARRAY_SIZE(k_MethodIdNamePairs), id); if (!s) { - ConvertUInt32ToString(methodId, temp); + ConvertUInt32ToString(id, temp); s = temp; } } m += s; - if (methodId == NFileHeader::NCompressionMethod::kLZMA && item.IsLzmaEOS()) + if (id == NFileHeader::NCompressionMethod::kLZMA && item.IsLzmaEOS()) m += ":EOS"; } @@ -507,9 +511,36 @@ HRESULT CLzmaDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream * return Decoder->Code(inStream, outStream, NULL, outSize, progress); } + +class CXzDecoder: + public ICompressCoder, + public CMyUnknownImp +{ + NArchive::NXz::CDecoder _decoder; +public: + + STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); + + MY_UNKNOWN_IMP +}; + +HRESULT CXzDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress) +{ + RINOK(_decoder.Decode(inStream, outStream, progress)); + Int32 opRes = _decoder.Get_Extract_OperationResult(); + if (opRes == NExtract::NOperationResult::kUnsupportedMethod) + return E_NOTIMPL; + if (opRes != NExtract::NOperationResult::kOK) + return S_FALSE; + return S_OK; +} + + struct CMethodItem { - UInt16 ZipMethod; + unsigned ZipMethod; CMyComPtr<ICompressCoder> Coder; }; @@ -559,12 +590,13 @@ HRESULT CZipDecoder::Decode( Int32 &res) { res = NExtract::NOperationResult::kDataError; - CInStreamReleaser inStreamReleaser; + CFilterCoder::C_InStream_Releaser inStreamReleaser; bool needCRC = true; bool wzAesMode = false; bool pkAesMode = false; - UInt16 methodId = item.Method; + unsigned id = item.Method; + if (item.IsEncrypted()) { if (item.IsStrongEncrypted()) @@ -580,7 +612,7 @@ HRESULT CZipDecoder::Decode( return S_OK; } } - if (!pkAesMode && methodId == NFileHeader::NCompressionMethod::kWzAES) + if (!pkAesMode && id == NFileHeader::NCompressionMethod::kWzAES) { CWzAesExtra aesField; if (item.CentralExtra.GetWzAes(aesField)) @@ -613,6 +645,7 @@ HRESULT CZipDecoder::Decode( } CMyComPtr<ICompressFilter> cryptoFilter; + if (item.IsEncrypted()) { if (wzAesMode) @@ -620,15 +653,18 @@ HRESULT CZipDecoder::Decode( CWzAesExtra aesField; if (!item.CentralExtra.GetWzAes(aesField)) return S_OK; - methodId = aesField.Method; + id = aesField.Method; if (!_wzAesDecoder) { _wzAesDecoderSpec = new NCrypto::NWzAes::CDecoder; _wzAesDecoder = _wzAesDecoderSpec; } cryptoFilter = _wzAesDecoder; - Byte properties = aesField.Strength; - RINOK(_wzAesDecoderSpec->SetDecoderProperties2(&properties, 1)); + if (!_wzAesDecoderSpec->SetKeyMode(aesField.Strength)) + { + res = NExtract::NOperationResult::kUnsupportedMethod; + return S_OK; + } } else if (pkAesMode) { @@ -648,6 +684,7 @@ HRESULT CZipDecoder::Decode( } cryptoFilter = _zipCryptoDecoder; } + CMyComPtr<ICryptoSetPassword> cryptoSetPassword; RINOK(cryptoFilter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword)); @@ -699,39 +736,41 @@ HRESULT CZipDecoder::Decode( unsigned m; for (m = 0; m < methodItems.Size(); m++) - if (methodItems[m].ZipMethod == methodId) + if (methodItems[m].ZipMethod == id) break; if (m == methodItems.Size()) { CMethodItem mi; - mi.ZipMethod = methodId; - if (methodId == NFileHeader::NCompressionMethod::kStored) + mi.ZipMethod = id; + if (id == NFileHeader::NCompressionMethod::kStored) mi.Coder = new NCompress::CCopyCoder; - else if (methodId == NFileHeader::NCompressionMethod::kShrunk) + else if (id == NFileHeader::NCompressionMethod::kShrunk) mi.Coder = new NCompress::NShrink::CDecoder; - else if (methodId == NFileHeader::NCompressionMethod::kImploded) + else if (id == NFileHeader::NCompressionMethod::kImploded) mi.Coder = new NCompress::NImplode::NDecoder::CCoder; - else if (methodId == NFileHeader::NCompressionMethod::kLZMA) + else if (id == NFileHeader::NCompressionMethod::kLZMA) mi.Coder = new CLzmaDecoder; - else if (methodId == NFileHeader::NCompressionMethod::kPPMd) + else if (id == NFileHeader::NCompressionMethod::kXz) + mi.Coder = new CXzDecoder; + else if (id == NFileHeader::NCompressionMethod::kPPMd) mi.Coder = new NCompress::NPpmdZip::CDecoder(true); else { CMethodId szMethodID; - if (methodId == NFileHeader::NCompressionMethod::kBZip2) + if (id == NFileHeader::NCompressionMethod::kBZip2) szMethodID = kMethodId_BZip2; else { - if (methodId > 0xFF) + if (id > 0xFF) { res = NExtract::NOperationResult::kUnsupportedMethod; return S_OK; } - szMethodID = kMethodId_ZipBase + (Byte)methodId; + szMethodID = kMethodId_ZipBase + (Byte)id; } - RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS szMethodID, mi.Coder, false)); + RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS szMethodID, false, mi.Coder)); if (mi.Coder == 0) { @@ -741,6 +780,7 @@ HRESULT CZipDecoder::Decode( } m = methodItems.Add(mi); } + ICompressCoder *coder = methodItems[m].Coder; { @@ -771,13 +811,23 @@ HRESULT CZipDecoder::Decode( { if (!filterStream) { - filterStreamSpec = new CFilterCoder; + filterStreamSpec = new CFilterCoder(false); filterStream = filterStreamSpec; } + filterStreamSpec->Filter = cryptoFilter; + if (wzAesMode) { result = _wzAesDecoderSpec->ReadHeader(inStream); + if (result == S_OK) + { + if (!_wzAesDecoderSpec->Init_and_CheckPassword()) + { + res = NExtract::NOperationResult::kWrongPassword; + return S_OK; + } + } } else if (pkAesMode) { @@ -785,43 +835,64 @@ HRESULT CZipDecoder::Decode( if (result == S_OK) { bool passwOK; - result = _pkAesDecoderSpec->CheckPassword(passwOK); + result = _pkAesDecoderSpec->Init_and_CheckPassword(passwOK); if (result == S_OK && !passwOK) - result = S_FALSE; + { + res = NExtract::NOperationResult::kWrongPassword; + return S_OK; + } } } else { result = _zipCryptoDecoderSpec->ReadHeader(inStream); + if (result == S_OK) + { + _zipCryptoDecoderSpec->Init_BeforeDecode(); + + /* Info-ZIP modification to ZipCrypto format: + if bit 3 of the general purpose bit flag is set, + it uses high byte of 16-bit File Time. + Info-ZIP code probably writes 2 bytes of File Time. + We check only 1 byte. */ + + // UInt32 v1 = GetUi16(_zipCryptoDecoderSpec->_header + NCrypto::NZip::kHeaderSize - 2); + // UInt32 v2 = (item.HasDescriptor() ? (item.Time & 0xFFFF) : (item.Crc >> 16)); + + Byte v1 = _zipCryptoDecoderSpec->_header[NCrypto::NZip::kHeaderSize - 1]; + Byte v2 = (Byte)(item.HasDescriptor() ? (item.Time >> 8) : (item.Crc >> 24)); + + if (v1 != v2) + { + res = NExtract::NOperationResult::kWrongPassword; + return S_OK; + } + } } if (result == S_OK) { - if (pkAesMode) - { - /* 9.31: The BUG in 9.24-9.30 was fixed. pkAes archives didn't work. - We don't need to call CAesCbcCoder::Init() to reset IV for data. */ - filterStreamSpec->SetInStream_NoSubFilterInit(inStream); - } - else - { - RINOK(filterStreamSpec->SetInStream(inStream)); - } inStreamReleaser.FilterCoder = filterStreamSpec; + RINOK(filterStreamSpec->SetInStream(inStream)); + + /* IFilter::Init() does nothing in all zip crypto filters. + So we can call any Initialize function in CFilterCoder. */ + + RINOK(filterStreamSpec->Init_NoSubFilterInit()); + // RINOK(filterStreamSpec->SetOutStreamSize(NULL)); + inStreamNew = filterStream; - if (wzAesMode) - { - if (!_wzAesDecoderSpec->CheckPasswordVerifyCode()) - result = S_FALSE; - } } } else inStreamNew = inStream; + if (result == S_OK) result = coder->Code(inStreamNew, outStream, NULL, &item.Size, compressProgress); + if (result == S_FALSE) return S_OK; + if (result == E_NOTIMPL) { res = NExtract::NOperationResult::kUnsupportedMethod; @@ -830,6 +901,7 @@ HRESULT CZipDecoder::Decode( RINOK(result); } + bool crcOK = true; bool authOk = true; if (needCRC) |