From 02d6107c97b48888362e7c6a70dcac323c89d741 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 17 Dec 2023 21:23:58 +0100 Subject: ZipArchive: Update to the 4.6.9 release --- zip/ZipArchive/ZipFileHeader.cpp | 1007 +++++++++++++++++++++++++++----------- 1 file changed, 731 insertions(+), 276 deletions(-) (limited to 'zip/ZipArchive/ZipFileHeader.cpp') diff --git a/zip/ZipArchive/ZipFileHeader.cpp b/zip/ZipArchive/ZipFileHeader.cpp index e9bb080..6ab75d3 100644 --- a/zip/ZipArchive/ZipFileHeader.cpp +++ b/zip/ZipArchive/ZipFileHeader.cpp @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////////// -// This source file is part of the ZipArchive library source distribution and -// is Copyrighted 2000 - 2007 by Artpol Software - Tadeusz Dracz +// This source file is part of the ZipArchive Library Open Source distribution +// and is Copyrighted 2000 - 2022 by Artpol Software - Tadeusz Dracz // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -9,7 +9,7 @@ // // For the licensing details refer to the License.txt file. // -// Web Site: http://www.artpol-software.com +// Web Site: https://www.artpol-software.com //////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" @@ -22,42 +22,63 @@ #include "ZipCrc32Cryptograph.h" #include "BytesWriter.h" +#include "zlib/zlib.h" #define FILEHEADERSIZE 46 #define LOCALFILEHEADERSIZE 30 -#define ZIP_EXTRA_ZARCH_NAME_VER 1 +#ifdef _ZIP_UNICODE_CUSTOM + #define ZIP_EXTRA_ZARCH_NAME_VER 1 +#endif +#define ZIP_EXTRA_NTFS_TAG 0x0001 using namespace ZipArchiveLib; char CZipFileHeader::m_gszSignature[] = {0x50, 0x4b, 0x01, 0x02}; char CZipFileHeader::m_gszLocalSignature[] = {0x50, 0x4b, 0x03, 0x04}; +#pragma warning(suppress: 26495) CZipFileHeader::CZipFileHeader() +{ + Initialize(NULL); + SetSystemCompatibility(ZipPlatform::GetSystemID()); +} + +#pragma warning(suppress: 26495) +CZipFileHeader::CZipFileHeader(CZipCentralDir* pCentralDir) +{ + Initialize(pCentralDir); +} + +CZipFileHeader::~CZipFileHeader() +{ + +} + +void CZipFileHeader::Initialize(CZipCentralDir* pCentralDir) { m_uExternalAttr = 0;//ZipPlatform::GetDefaultAttributes(); m_uModDate = m_uModTime = 0; + m_tModificationTime = m_tCreationTime = m_tLastAccessTime = (time_t)0; m_uMethod = CZipCompressor::methodDeflate; - m_uVersionMadeBy = 0; + m_uVersionMadeBy = 0; m_uCrc32 = 0; // initialize to 0, because on 64 bit platform unsigned long is 8 byte and we are copying only 4 bytes in Read() m_uComprSize = m_uUncomprSize = m_uOffset = 0; m_uLocalFileNameSize = 0; m_uLocalComprSize = m_uLocalUncomprSize = 0; + m_uLocalHeaderSize = 0; m_uVolumeStart = 0; - m_pszFileName = NULL; m_uEncryptionMethod = CZipCryptograph::encNone; m_bIgnoreCrc32 = false; m_uFlag = 0; + m_pCentralDir = pCentralDir; + m_state = sfNone; } -CZipFileHeader::~CZipFileHeader() +bool CZipFileHeader::Read(bool bReadSignature) { - if (m_pszFileName != NULL) - delete m_pszFileName; -} + m_state = sfNone; -bool CZipFileHeader::Read(CZipCentralDir& centralDir, bool bReadSignature) -{ - CZipStorage *pStorage = centralDir.m_pStorage; + CZipStorage *pStorage = m_pCentralDir->GetStorage(); WORD uFileNameSize, uCommentSize, uExtraFieldSize; CZipAutoBuffer buf(FILEHEADERSIZE); if (bReadSignature) @@ -69,7 +90,8 @@ bool CZipFileHeader::Read(CZipCentralDir& centralDir, bool bReadSignature) else pStorage->Read((char*)buf + 4, FILEHEADERSIZE - 4, true); - CBytesWriter::ReadBytes(m_uVersionMadeBy, buf + 4); + WORD uVersionMadeBy; + CBytesWriter::ReadBytes(uVersionMadeBy, buf + 4); CBytesWriter::ReadBytes(m_uVersionNeeded, buf + 6); CBytesWriter::ReadBytes(m_uFlag, buf + 8); CBytesWriter::ReadBytes(m_uMethod, buf + 10); @@ -87,96 +109,137 @@ bool CZipFileHeader::Read(CZipCentralDir& centralDir, bool bReadSignature) CBytesWriter::ReadBytes(m_uOffset, buf + 42, 4); buf.Release(); + m_uVersionMadeBy = uVersionMadeBy & 0xFF; + SetSystemCompatibility((uVersionMadeBy & 0xFF00) >> 8); + // we may need to modify this later m_uEncryptionMethod = (BYTE)((m_uFlag & (WORD) 1) != 0 ? CZipCryptograph::encStandard : CZipCryptograph::encNone); - ZIP_VOLUME_TYPE uCurDsk = pStorage->GetCurrentVolume(); - m_pszFileNameBuffer.Allocate(uFileNameSize); // don't add NULL at the end - pStorage->Read(m_pszFileNameBuffer, uFileNameSize, true); + ZIP_VOLUME_TYPE uCurDsk = pStorage->GetCurrentVolume(); + m_fileName.m_buffer.Allocate(uFileNameSize); // don't add NULL at the end + pStorage->Read(m_fileName.m_buffer, uFileNameSize, true); + +#ifdef _ZIP_UNICODE_CUSTOM + const CZipStringStoreSettings& stringStoreSettings = m_pCentralDir->GetStringStoreSettings(); - if (centralDir.m_pStringSettings->IsStandardNameCodePage()) + if (stringStoreSettings.IsStandardNameCodePage()) m_stringSettings.SetDefaultNameCodePage(GetSystemCompatibility()); else - m_stringSettings.m_uNameCodePage = centralDir.m_pStringSettings->m_uNameCodePage; + m_stringSettings.m_uNameCodePage = stringStoreSettings.m_uNameCodePage; - if (!centralDir.m_pStringSettings->IsStandardCommentCodePage()) - m_stringSettings.m_uCommentCodePage = centralDir.m_pStringSettings->m_uCommentCodePage; + if (!stringStoreSettings.IsStandardCommentCodePage()) + m_stringSettings.m_uCommentCodePage = stringStoreSettings.m_uCommentCodePage; +#endif - if (!m_aCentralExtraData.Read(pStorage, uExtraFieldSize)) + if (!m_aCentralExtraData.Read(pStorage, uExtraFieldSize) && m_pCentralDir->IsConsistencyCheckOn(CZipArchive::checkInvalidExtraData)) return false; - CZipExtraData* pExtra = m_aCentralExtraData.Lookup(ZIP_EXTRA_ZARCH_NAME); - if (pExtra != NULL) - { - WORD uExtraDataSize = (WORD)pExtra->m_data.GetSize(); - int offset = 1; - if (offset > uExtraDataSize) - return false; - if (pExtra->m_data[0] <= ZIP_EXTRA_ZARCH_NAME_VER) // else don't parse it + CZipExtraData* pExtra = m_aCentralExtraData.Lookup(ZIP_EXTRA_NTFS); + if (pExtra != NULL && pExtra->m_data.GetSize() >= 32) + { + WORD temp; + CBytesWriter::ReadBytes(temp, pExtra->m_data + 4); + if (temp == ZIP_EXTRA_NTFS_TAG) { - offset++; - if (offset > uExtraDataSize) - return false; - BYTE flag = pExtra->m_data[1]; - bool bReadCommentCp = (flag & 4) != 0; - if ((flag & 1) != 0) + CBytesWriter::ReadBytes(temp, pExtra->m_data + 6); + if (temp == 24) { - // code page present - if (offset + 4 > uExtraDataSize) - return false; - // avoid warnings - DWORD temp; - CBytesWriter::ReadBytes(temp, pExtra->m_data + offset); - m_stringSettings.m_uNameCodePage = temp; - offset += 4; + m_tModificationTime = ReadFileTime(pExtra->m_data + 8); + m_tCreationTime = ReadFileTime(pExtra->m_data + 16); + m_tLastAccessTime = ReadFileTime(pExtra->m_data + 24); } + } + } - if ((flag & 3) == 3) - { - m_stringSettings.m_bStoreNameInExtraData = true; - int iFileNameSize = pExtra->m_data.GetSize() - 2 - 4; - if (bReadCommentCp) - iFileNameSize -= 4; - // code page present - if (offset + iFileNameSize > uExtraDataSize || iFileNameSize <= 0) - return false; - CZipAutoBuffer buffer; - buffer.Allocate(iFileNameSize); - memcpy(buffer, pExtra->m_data + offset, iFileNameSize); - m_pszFileName = new CZipString(_T("")); - ZipCompatibility::ConvertBufferToString(*m_pszFileName, buffer, m_stringSettings.m_uNameCodePage); - offset += iFileNameSize; - m_pszFileNameBuffer.Release(); - } - else - m_stringSettings.m_bStoreNameInExtraData = false; - if (bReadCommentCp) +#ifdef _ZIP_UNICODE_CUSTOM + if (m_state == sfNone) + { + pExtra = m_aCentralExtraData.Lookup(ZIP_EXTRA_ZARCH_NAME); + if (pExtra != NULL) + { + m_state = sfCustomUnicode; + + WORD uExtraDataSize = (WORD)pExtra->m_data.GetSize(); + int offset = 1; + if (offset > uExtraDataSize) + return false; + if (pExtra->m_data[0] <= ZIP_EXTRA_ZARCH_NAME_VER) // else don't parse it { - // code page present - if (offset + 4 > uExtraDataSize) + offset++; + if (offset > uExtraDataSize) return false; - DWORD temp; - CBytesWriter::ReadBytes(temp, pExtra->m_data + offset); - m_stringSettings.m_uCommentCodePage = temp; - // offset += 4; - } + BYTE flag = pExtra->m_data[1]; + bool bReadCommentCp = (flag & 4) != 0; + if ((flag & 1) != 0) + { + // code page present + if (offset + 4 > uExtraDataSize) + return false; + // avoid warnings + DWORD temp; + CBytesWriter::ReadBytes(temp, pExtra->m_data + offset); + m_stringSettings.m_uNameCodePage = temp; + offset += 4; + } + + if ((flag & 3) == 3) + { + m_stringSettings.m_bStoreNameInExtraData = true; + int iFileNameSize = pExtra->m_data.GetSize() - 2 - 4; + if (bReadCommentCp) + iFileNameSize -= 4; + // code page present + if (offset + iFileNameSize > uExtraDataSize || iFileNameSize <= 0) + return false; + // just in case + m_fileName.ClearString(); + m_fileName.m_buffer.Allocate(iFileNameSize); + memcpy(m_fileName.m_buffer, pExtra->m_data + offset, iFileNameSize); + offset += iFileNameSize; + } + else + m_stringSettings.m_bStoreNameInExtraData = false; + + if (bReadCommentCp) + { + // code page present + if (offset + 4 > uExtraDataSize) + return false; + DWORD temp; + CBytesWriter::ReadBytes(temp, pExtra->m_data + offset); + m_stringSettings.m_uCommentCodePage = temp; + // offset += 4; + } + } + } + else if ((m_pCentralDir->GetUnicodeMode() & CZipArchive::umCustom) != 0) + { + m_state = sfCustomUnicode; } } +#endif + if (uCommentSize) { - m_pszComment.Allocate(uCommentSize); - pStorage->Read(m_pszComment, uCommentSize, true); + m_comment.m_buffer.Allocate(uCommentSize); + pStorage->Read(m_comment.m_buffer, uCommentSize, true); } - - return pStorage->GetCurrentVolume() == uCurDsk; // check that the whole header is in one volume + + m_aCentralExtraData.RemoveInternalHeaders(); + + return pStorage->GetCurrentVolume() == uCurDsk || pStorage->IsBinarySplit(); // check that the whole header is in one volume } -time_t CZipFileHeader::GetTime()const +time_t CZipFileHeader::GetModificationTime()const { + if (m_tModificationTime != (time_t) 0) + { + return m_tModificationTime; + } struct tm atm; atm.tm_sec = (m_uModTime & ~0xFFE0) << 1; atm.tm_min = (m_uModTime & ~0xF800) >> 5; @@ -189,95 +252,214 @@ time_t CZipFileHeader::GetTime()const return mktime(&atm); } +void CZipFileHeader::SetModificationTime(const time_t& ttime, bool bFullResolution, bool bUseUtcTime) +{ + if (bFullResolution) + { + m_tModificationTime = ttime; + } + else + { + m_tModificationTime = m_tCreationTime = m_tLastAccessTime = (time_t)0; + } +#if _MSC_VER >= 1400 + tm gts; + tm* gt = >s; + if (bUseUtcTime) + { + gmtime_s(gt, &ttime); + } + else + { + localtime_s(gt, &ttime); + } +#else + tm* gt; + if (bUseUtcTime) + { + gt = gmtime(&ttime); + } + else + { + gt = localtime(&ttime); + } +#endif + WORD year, month, day, hour, min, sec; + if (gt == NULL) + { + year = 0; + month = day = 1; + hour = min = sec = 0; + } + else + { + year = (WORD)(gt->tm_year + 1900); + if (year <= 1980) + year = 0; + else + year -= 1980; + month = (WORD)gt->tm_mon + 1; + day = (WORD)gt->tm_mday; + hour = (WORD)gt->tm_hour; + min = (WORD)gt->tm_min; + sec = (WORD)gt->tm_sec; + } + + m_uModDate = (WORD) (day + ( month << 5) + (year << 9)); + m_uModTime = (WORD) ((sec >> 1) + (min << 5) + (hour << 11)); +} + +void CZipFileHeader::WriteFileTime(const time_t& ttime, char* buffer, bool bUseUtcTime) +{ + + time_t tFileTime; + if (bUseUtcTime) + { +#if _MSC_VER >= 1400 + tm gts; + tm* gt = >s; + gmtime_s(gt, &ttime); +#else + tm* gt = gmtime(&ttime); +#endif + gt->tm_isdst = -1; + tFileTime = mktime(gt); + } + else + { + tFileTime = ttime; + } + ZFILETIME ft; + ZipPlatform::ConvertTimeToFileTime(tFileTime, ft); + CBytesWriter::WriteBytes(buffer, ft.dwLowDateTime); + CBytesWriter::WriteBytes(buffer + 4, ft.dwHighDateTime); +} + +time_t CZipFileHeader::ReadFileTime(const char* buffer) +{ + ZFILETIME ft = {0}; + CBytesWriter::ReadBytes(ft.dwLowDateTime, buffer); + CBytesWriter::ReadBytes(ft.dwHighDateTime, buffer + 4); + time_t ret; + return ZipPlatform::ConvertFileTimeToTime(ft, ret) ? ret : (time_t)0; +} + + // write the header to the central dir -DWORD CZipFileHeader::Write(CZipStorage *pStorage) +DWORD CZipFileHeader::Write(CZipCentralDir* pCentralDir) { + CZipStorage* pStorage = pCentralDir->GetStorage(); m_aCentralExtraData.RemoveInternalHeaders(); + if (m_tModificationTime != (time_t)0 || m_tCreationTime != (time_t)0 || m_tLastAccessTime != (time_t)0) + { + CZipExtraData* pExtra = m_aCentralExtraData.CreateNew(ZIP_EXTRA_NTFS); + CZipAutoBuffer& buf = pExtra->m_data; + buf.Allocate(32, true); + // first 4 bytes are reserved + WORD temp = ZIP_EXTRA_NTFS_TAG; // tag for the attribute + CBytesWriter::WriteBytes((char*)(buf + 4), temp); + temp = 3 * 8; // the size of the attribute + CBytesWriter::WriteBytes((char*)(buf + 6), temp); + + bool bUseUtc = pCentralDir->GetArchive()->IsUseUtcFileTimes(); + WriteFileTime(m_tModificationTime, (char*)(buf + 8), bUseUtc); + WriteFileTime(m_tCreationTime, (char*)(buf + 16), bUseUtc); + WriteFileTime(m_tLastAccessTime, (char*)(buf + 24), bUseUtc); + } + ZIP_SIZE_TYPE uComprSize = m_uComprSize; WORD uMethod = m_uMethod; + PrepareStringBuffers(); + if (!CheckLengths(false)) CZipException::Throw(CZipException::tooLongData); - PrepareFileName(); - - - if (m_stringSettings.m_bStoreNameInExtraData) - { - if (m_pszFileName == NULL && m_pszFileNameBuffer.IsAllocated()) - GetFileName(false); // don't clear the buffer, it will be needed in a moment - ASSERT(m_pszFileName != NULL); - if (m_pszFileName->GetLength() == 0) - m_stringSettings.m_bStoreNameInExtraData = false; - } - - int iSystemCompatibility = GetSystemCompatibility(); - if (!m_stringSettings.IsStandard(iSystemCompatibility)) +#ifdef _ZIP_UNICODE_CUSTOM + if (m_state == sfCustomUnicode) { - CZipExtraData* pExtra = m_aCentralExtraData.CreateNew(ZIP_EXTRA_ZARCH_NAME); - bool bWriteCommentCp = !m_stringSettings.IsStandardCommentCodePage(); - - BYTE flag = 0; - int offset = 2; - char* data = NULL; if (m_stringSettings.m_bStoreNameInExtraData) { - CZipAutoBuffer buffer; - // m_pszFileNameBuffer contains CP_ACP page, we don't check if the current page is CP_ACP - too large dependency on PrepareFileName - ZipCompatibility::ConvertStringToBuffer(*m_pszFileName, buffer, m_stringSettings.m_uNameCodePage); - int size = 2 + 4 + buffer.GetSize(); - if (bWriteCommentCp) - size += 4; - pExtra->m_data.Allocate(size); - data = (char*)pExtra->m_data; - CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uNameCodePage); - offset += 4; - memcpy(data + offset, buffer, buffer.GetSize()); - offset += buffer.GetSize(); - flag = 3; - } - else if (!m_stringSettings.IsStandardNameCodePage(iSystemCompatibility)) - { - int size = 2 + 4; - if (bWriteCommentCp) - size += 4; - pExtra->m_data.Allocate(size); - data = (char*)pExtra->m_data; - CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uNameCodePage); - offset += 4; - flag = 1; + if (!m_fileName.HasString() && m_fileName.HasBuffer()) + GetFileName(false); // don't clear the buffer, it will be needed in a moment + ASSERT(m_fileName.HasString()); + ASSERT(m_fileName.HasBuffer()); + if (m_fileName.GetString().GetLength() == 0) + m_stringSettings.m_bStoreNameInExtraData = false; } - if (bWriteCommentCp) + if (!m_stringSettings.IsStandard(GetSystemCompatibility())) { - if (!pExtra->m_data.IsAllocated()) + CZipExtraData* pExtra = m_aCentralExtraData.CreateNew(ZIP_EXTRA_ZARCH_NAME); + bool bWriteCommentCp = !m_stringSettings.IsStandardCommentCodePage(GetSystemCompatibility()); + + BYTE flag = 0; + int offset = 2; + char* data = NULL; + if (m_stringSettings.m_bStoreNameInExtraData) { - pExtra->m_data.Allocate(2 + 4); + CZipAutoBuffer buffer; + // m_fileName.m_buffer contains CP_ACP page, we don't check if the current page is CP_ACP - too large dependency on PrepareStringBuffers + ZipCompatibility::ConvertStringToBuffer(m_fileName.GetString(), buffer, m_stringSettings.m_uNameCodePage); + int size = 2 + 4 + buffer.GetSize(); + if (bWriteCommentCp) + size += 4; + pExtra->m_data.Allocate(size); data = (char*)pExtra->m_data; + CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uNameCodePage); + offset += 4; + memcpy(data + offset, buffer, buffer.GetSize()); + offset += buffer.GetSize(); + flag = 3; + } + else if (!m_stringSettings.IsStandardNameCodePage(GetSystemCompatibility())) + { + int size = 2 + 4; + if (bWriteCommentCp) + size += 4; + pExtra->m_data.Allocate(size); + data = (char*)pExtra->m_data; + CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uNameCodePage); + offset += 4; + flag = 1; } - ASSERT(data); - CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uCommentCodePage); - flag |= 4; - } - data[0] = ZIP_EXTRA_ZARCH_NAME_VER; - data[1] = flag; + if (bWriteCommentCp) + { + if (!pExtra->m_data.IsAllocated()) + { + pExtra->m_data.Allocate(2 + 4); + data = (char*)pExtra->m_data; + } + ASSERT(data); + CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uCommentCodePage); + flag |= 4; + } + if (data != NULL) // just in case + { + data[0] = ZIP_EXTRA_ZARCH_NAME_VER; + data[1] = flag; + } + } } +#endif - WORD uFileNameSize = (WORD)m_pszFileNameBuffer.GetSize(), uCommentSize = (WORD)GetCommentSize(), + WORD uFileNameSize = (WORD)m_fileName.GetBufferSize(), uCommentSize = m_comment.GetBufferSize(), uExtraFieldSize = (WORD)m_aCentralExtraData.GetTotalSize(); DWORD uSize = FILEHEADERSIZE + uFileNameSize + uCommentSize + uExtraFieldSize; CZipAutoBuffer buf(uSize); char* dest = (char*)buf; memcpy(dest, m_gszSignature, 4); - CBytesWriter::WriteBytes(dest + 4, m_uVersionMadeBy); + WORD uVersionMadeBy = (int)m_uVersionMadeBy & 0xFF; + uVersionMadeBy |= (WORD)(m_iSystemCompatibility << 8); + CBytesWriter::WriteBytes(dest + 4, uVersionMadeBy); CBytesWriter::WriteBytes(dest + 6, m_uVersionNeeded); CBytesWriter::WriteBytes(dest + 8, m_uFlag); CBytesWriter::WriteBytes(dest + 10, uMethod); CBytesWriter::WriteBytes(dest + 12, m_uModTime); CBytesWriter::WriteBytes(dest + 14, m_uModDate); WriteCrc32(dest + 16); - CBytesWriter::WriteBytes(dest + 20, CBytesWriter::WriteSafeU32(m_uComprSize)); + CBytesWriter::WriteBytes(dest + 20, CBytesWriter::WriteSafeU32(uComprSize)); CBytesWriter::WriteBytes(dest + 24, CBytesWriter::WriteSafeU32(m_uUncomprSize)); CBytesWriter::WriteBytes(dest + 28, uFileNameSize); CBytesWriter::WriteBytes(dest + 30, uExtraFieldSize); @@ -287,13 +469,13 @@ DWORD CZipFileHeader::Write(CZipStorage *pStorage) CBytesWriter::WriteBytes(dest + 38, m_uExternalAttr); CBytesWriter::WriteBytes(dest + 42, CBytesWriter::WriteSafeU32(m_uOffset)); - memcpy(dest + 46, m_pszFileNameBuffer, uFileNameSize); + memcpy(dest + 46, m_fileName.m_buffer, uFileNameSize); if (uExtraFieldSize) m_aCentralExtraData.Write(dest + 46 + uFileNameSize); if (uCommentSize) - memcpy(dest + 46 + uFileNameSize + uExtraFieldSize, m_pszComment, uCommentSize); + memcpy(dest + 46 + uFileNameSize + uExtraFieldSize, m_comment.m_buffer, uCommentSize); pStorage->Write(dest, uSize, true); @@ -303,10 +485,18 @@ DWORD CZipFileHeader::Write(CZipStorage *pStorage) return uSize; } -bool CZipFileHeader::ReadLocal(CZipCentralDir& centralDir) -{ +bool CZipFileHeader::ReadLocal(CZipCentralDir* pCentralDir) +{ + CZipStorage* pStorage = pCentralDir->GetStorage(); + pStorage->ChangeVolume(m_uVolumeStart); + bool isBinary = pStorage->IsBinarySplit(); + if (isBinary) + pStorage->SeekInBinary(m_uOffset, true); + else + pStorage->Seek(m_uOffset); + char buf[LOCALFILEHEADERSIZE]; - CZipStorage* pStorage = centralDir.m_pStorage; + pStorage->Read(buf, LOCALFILEHEADERSIZE, true); if (memcmp(buf, m_gszLocalSignature, 4) != 0) return false; @@ -317,7 +507,7 @@ bool CZipFileHeader::ReadLocal(CZipCentralDir& centralDir) CBytesWriter::ReadBytes(uTemp, buf + 6); // do not compare the whole flag - the bits reserved by PKWARE may differ // in local and central headers - if (centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalFlag) + if (pCentralDir->IsConsistencyCheckOn(CZipArchive::checkLocalFlag) && (uTemp & 0xf) != (m_uFlag & 0xf)) return false; @@ -331,98 +521,135 @@ bool CZipFileHeader::ReadLocal(CZipCentralDir& centralDir) CBytesWriter::ReadBytes(uExtraFieldSize, buf + 28); ZIP_VOLUME_TYPE uCurDsk = pStorage->GetCurrentVolume(); // skip reading the local file name - pStorage->m_pFile->Seek(m_uLocalFileNameSize, CZipAbstractFile::current); - if (!m_aLocalExtraData.Read(pStorage, uExtraFieldSize)) + + if (isBinary) + pStorage->SeekInBinary(m_uLocalFileNameSize); + else + pStorage->m_pFile->Seek(m_uLocalFileNameSize, CZipAbstractFile::current); + + m_uLocalHeaderSize = LOCALFILEHEADERSIZE + m_uLocalFileNameSize + uExtraFieldSize; + if (!m_aLocalExtraData.Read(pStorage, uExtraFieldSize) && pCentralDir->IsConsistencyCheckOn(CZipArchive::checkInvalidExtraData)) return false; CBytesWriter::ReadBytes(m_uLocalComprSize, buf + 18, 4); CBytesWriter::ReadBytes(m_uLocalUncomprSize, buf + 22, 4); + + if (uMethod == CZipCompressor::methodWinZipAes && IsEncrypted()) CZipException::Throw(CZipException::noAES); - if (centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalMethod) + if (pCentralDir->IsConsistencyCheckOn(CZipArchive::checkLocalMethod) && uMethod != m_uMethod ) return false; - if (!bIsDataDescr && centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalCRC | CZipArchive::checkLocalSizes)) + if (!bIsDataDescr && pCentralDir->IsConsistencyCheckOn(CZipArchive::checkLocalCRC | CZipArchive::checkLocalSizes)) { - // read all at once - probably overally faster than checking and reading separately + // read all at once - probably faster than checking and reading separately DWORD uCrc32; CBytesWriter::ReadBytes(uCrc32, buf + 14); - if (centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalCRC) + if (pCentralDir->IsConsistencyCheckOn(CZipArchive::checkLocalCRC) && uCrc32 != m_uCrc32) - return false; + return false; - if (centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalSizes) + if (pCentralDir->IsConsistencyCheckOn(CZipArchive::checkLocalSizes) // do not check, if local compressed size is 0 - this usually means, that some archiver // could not update the compressed size after compression - && ( m_uLocalComprSize != 0 && m_uLocalComprSize != m_uComprSize || m_uLocalUncomprSize != m_uUncomprSize)) - return false; - } - return pStorage->GetCurrentVolume() == uCurDsk; // check that the whole header is in one volume + && ( (m_uLocalComprSize != 0 && m_uLocalComprSize != m_uComprSize) || m_uLocalUncomprSize != m_uUncomprSize)) + return false; + } + return pStorage->GetCurrentVolume() == uCurDsk || isBinary; // check that the whole header is in one volume } -void CZipFileHeader::SetTime(const time_t & ttime) -{ -#if _MSC_VER >= 1400 - tm gts; - tm* gt = >s; - localtime_s(gt, &ttime); -#else - tm* gt = localtime(&ttime); +void CZipFileHeader::ConvertFileName(CZipAutoBuffer& buffer) const +{ + if (!m_fileName.HasString()) + return; + CZipString temp = m_fileName.GetString(); + ZipCompatibility::SlashBackslashChg(temp, false); + UINT codePage; +#ifdef _ZIP_UNICODE_CUSTOM + if (m_state.IsSetAny(sfCustomUnicode)) + { + if (m_stringSettings.m_bStoreNameInExtraData) + { + codePage = GetDefaultFileNameCodePage(); + + } + else + { + codePage = m_stringSettings.m_uNameCodePage; + } + } + else #endif - WORD year, month, day, hour, min, sec; - if (gt == NULL) { - year = 0; - month = day = 1; - hour = min = sec = 0; + codePage = GetDefaultFileNameCodePage(); + } + + ZipCompatibility::ConvertStringToBuffer(temp, buffer, codePage); +} + +void CZipFileHeader::ConvertFileName(CZipString& szFileName) const +{ + if (!m_fileName.HasBuffer()) + return; + UINT codePage; +#ifdef _ZIP_UNICODE_CUSTOM + if (m_state.IsSetAny(sfCustomUnicode)) + { + codePage = m_stringSettings.m_uNameCodePage; } else +#endif { - year = (WORD)(gt->tm_year + 1900); - if (year <= 1980) - year = 0; - else - year -= 1980; - month = (WORD)gt->tm_mon + 1; - day = (WORD)gt->tm_mday; - hour = (WORD)gt->tm_hour; - min = (WORD)gt->tm_min; - sec = (WORD)gt->tm_sec; + codePage = GetDefaultFileNameCodePage(); } - - m_uModDate = (WORD) (day + ( month << 5) + (year << 9)); - m_uModTime = (WORD) ((sec >> 1) + (min << 5) + (hour << 11)); + ZipCompatibility::ConvertBufferToString(szFileName, m_fileName.m_buffer, codePage); + // some archives may have an invalid path separator stored + ZipCompatibility::NormalizePathSeparators(szFileName); } -void CZipFileHeader::ConvertFileName(CZipAutoBuffer& buffer) const +void CZipFileHeader::ConvertComment(CZipAutoBuffer& buffer) const { - if (m_pszFileName == NULL) + if (!m_comment.HasString()) return; - CZipString temp = *m_pszFileName; - ZipCompatibility::SlashBackslashChg(temp, false); - if (m_stringSettings.m_bStoreNameInExtraData) - ZipCompatibility::ConvertStringToBuffer(temp, buffer, m_stringSettings.GetDefaultNameCodePage(GetSystemCompatibility())); + UINT codePage; +#ifdef _ZIP_UNICODE_CUSTOM + if (m_state.IsSetAny(sfCustomUnicode)) + { + codePage = m_stringSettings.m_uCommentCodePage; + } else - ZipCompatibility::ConvertStringToBuffer(temp, buffer, m_stringSettings.m_uNameCodePage); +#endif + { + codePage = GetDefaultCommentCodePage(); + } + + ZipCompatibility::ConvertStringToBuffer(m_comment.GetString(), buffer, codePage); } -void CZipFileHeader::ConvertFileName(CZipString& szFileName) const +void CZipFileHeader::ConvertComment(CZipString& szComment) const { - if (!m_pszFileNameBuffer.IsAllocated() || m_pszFileNameBuffer.GetSize() == 0) + if (!m_comment.HasBuffer()) return; - ZipCompatibility::ConvertBufferToString(szFileName, m_pszFileNameBuffer, m_stringSettings.m_uNameCodePage); - int sc = ZipPlatform::GetSystemID(); - if (sc == ZipCompatibility::zcDosFat || sc == ZipCompatibility::zcNtfs) - ZipCompatibility::SlashBackslashChg(szFileName, true); - else // some archives may have an invalid path separator stored - ZipCompatibility::SlashBackslashChg(szFileName, false); + UINT codePage; +#ifdef _ZIP_UNICODE_CUSTOM + if (m_state.IsSetAny(sfCustomUnicode)) + { + codePage = m_stringSettings.m_uCommentCodePage; + } + else +#endif + { + codePage = GetDefaultCommentCodePage(); + } + ZipCompatibility::ConvertBufferToString(szComment, m_comment.m_buffer, codePage); } // write the local header void CZipFileHeader::WriteLocal(CZipStorage *pStorage) { + m_aLocalExtraData.RemoveInternalLocalHeaders(); if (IsDataDescriptor()) { m_uLocalComprSize = 0; @@ -434,16 +661,18 @@ void CZipFileHeader::WriteLocal(CZipStorage *pStorage) } else { - m_uLocalComprSize = GetDataSize(true, false); } WORD uMethod = m_uMethod; - PrepareFileName(); - m_uLocalFileNameSize = (WORD)m_pszFileNameBuffer.GetSize(); + PrepareStringBuffers(); + // this check was already performed, if a file was replaced + if (!CheckLengths(true)) + m_pCentralDir->ThrowError(CZipException::tooLongData); + m_uLocalFileNameSize = (WORD)m_fileName.GetBufferSize(); DWORD uExtraFieldSize = m_aLocalExtraData.GetTotalSize(); - DWORD iLocalSize = LOCALFILEHEADERSIZE + uExtraFieldSize + m_uLocalFileNameSize; - CZipAutoBuffer buf(iLocalSize); + m_uLocalHeaderSize = LOCALFILEHEADERSIZE + uExtraFieldSize + m_uLocalFileNameSize; + CZipAutoBuffer buf(m_uLocalHeaderSize); char* dest = (char*) buf; memcpy(dest, m_gszLocalSignature, 4); @@ -455,20 +684,23 @@ void CZipFileHeader::WriteLocal(CZipStorage *pStorage) WriteSmallDataDescriptor(dest + 14); CBytesWriter::WriteBytes(dest + 26, m_uLocalFileNameSize); CBytesWriter::WriteBytes(dest + 28, (WORD)uExtraFieldSize); - memcpy(dest + 30, m_pszFileNameBuffer, m_uLocalFileNameSize); + memcpy(dest + 30, m_fileName.m_buffer, m_uLocalFileNameSize); if (uExtraFieldSize) m_aLocalExtraData.Write(dest + 30 + m_uLocalFileNameSize); - + // possible volume change before writing to the file in the segmented archive // so write the local header first - pStorage->Write(dest, iLocalSize, true); + pStorage->Write(dest, m_uLocalHeaderSize, true); - m_uVolumeStart = pStorage->GetCurrentVolume(); - m_uOffset = pStorage->GetPosition() - iLocalSize; + m_uVolumeStart = pStorage->IsBinarySplit() ? 0 : pStorage->GetCurrentVolume(); + m_uOffset = pStorage->GetPosition() - m_uLocalHeaderSize; + + m_aLocalExtraData.RemoveInternalLocalHeaders(); ClearFileName(); } + WORD CZipFileHeader::GetDataDescriptorSize(bool bConsiderSignature) const { if (IsDataDescriptor()) @@ -488,22 +720,21 @@ bool CZipFileHeader::NeedsDataDescriptor() const void CZipFileHeader::PrepareData(int iLevel, bool bSegm) { - // could be == 1, but the way below it works for PredictMaximumFileSizeInArchive when used on an existing segmented archive - for whatever reason + // could be == 1, but the way below it works for PredictMaximumFileSizeInArchive when used on an existing segmented archive m_uInternalAttr = 0; // version made by - SetVersion((WORD)(0x14)); + m_uVersionMadeBy = (unsigned char)0x14; m_uCrc32 = 0; m_uComprSize = 0; m_uUncomprSize = 0; - ASSERT(CZipCompressor::IsCompressionSupported(m_uMethod) && ((iLevel == 0) == (m_uMethod == CZipCompressor::methodStore))); - m_uFlag = 0; if (m_uMethod == CZipCompressor::methodDeflate) + { switch (iLevel) { case 1: @@ -517,9 +748,12 @@ void CZipFileHeader::PrepareData(int iLevel, bool bSegm) m_uFlag |= 2; break; } + } UpdateFlag(bSegm); + AdjustLocalComprSize(); + m_uVersionNeeded = 0; if (m_uVersionNeeded == 0) m_uVersionNeeded = IsDirectory() ? 0xa : 0x14; // 1.0 or 2.0 @@ -569,48 +803,86 @@ bool CZipFileHeader::CheckDataDescriptor(CZipStorage* pStorage) const DWORD CZipFileHeader::GetSize()const { - DWORD uSize = FILEHEADERSIZE + PredictFileNameSize() + GetCommentSize(); + DWORD uSize = FILEHEADERSIZE + PredictFileNameSize() + PredictCommentSize(); uSize += m_aCentralExtraData.GetTotalSize(); - if (m_stringSettings.m_bStoreNameInExtraData) + +#ifdef _ZIP_UNICODE_CUSTOM + if (m_state.IsSetAny(sfCustomUnicode)) { - CZipString temp; - if (m_pszFileName != NULL) - temp = *m_pszFileName; - else - ConvertFileName(temp); - if (temp.GetLength() > 0) + if (m_stringSettings.m_bStoreNameInExtraData) { - uSize += 4 + 2 + 4; // headerID, size + version, flag + filename code page - CZipAutoBuffer buffer; - ZipCompatibility::ConvertStringToBuffer(temp, buffer, m_stringSettings.m_uNameCodePage); - uSize += buffer.GetSize(); - if (!m_stringSettings.IsStandardCommentCodePage()) - uSize += 4; + CZipString temp; + if (m_fileName.HasString()) + temp = m_fileName.GetString(); + else + ConvertFileName(temp); + if (temp.GetLength() > 0) + { + uSize += 4 + 2 + 4; // headerID, size + version, flag + filename code page + CZipAutoBuffer buffer; + ZipCompatibility::ConvertStringToBuffer(temp, buffer, m_stringSettings.m_uNameCodePage); + uSize += buffer.GetSize(); + if (!m_stringSettings.IsStandardCommentCodePage(GetSystemCompatibility())) + uSize += 4; + } } } +#endif return uSize; } DWORD CZipFileHeader::GetLocalSize(bool bReal)const { - DWORD uSize = LOCALFILEHEADERSIZE + m_aLocalExtraData.GetTotalSize(); if (bReal) - uSize += m_uLocalFileNameSize; - else - uSize += PredictFileNameSize(); + return m_uLocalHeaderSize; + + DWORD uSize = LOCALFILEHEADERSIZE + m_aLocalExtraData.GetTotalSize() + PredictFileNameSize(); return uSize; } -void CZipFileHeader::SetComment(LPCTSTR lpszComment) +bool CZipFileHeader::SetComment(LPCTSTR lpszComment) { - ZipCompatibility::ConvertStringToBuffer(lpszComment, m_pszComment, m_stringSettings.m_uCommentCodePage); + if (m_pCentralDir) + { + // update the lpszFileName to make sure the renaming is necessary + GetComment(); + CZipString newComment(lpszComment); + if (!UpdateCommentFlags(&newComment)) + { + if (m_comment.GetString().Collate(newComment) == 0) + return true; + } + // just in case + m_comment.ClearBuffer(); + + bool ret; + CZipString oldComment= m_comment.GetString(); + m_comment.SetString(lpszComment); + ret = m_pCentralDir->OnFileCentralChange(); + if (!ret) + m_comment.SetString(oldComment); + + return ret; + } + else + { + m_comment.ClearBuffer(); + m_comment.SetString(lpszComment); + return true; + } } -CZipString CZipFileHeader::GetComment() const +const CZipString& CZipFileHeader::GetComment(bool bClearBuffer) { - CZipString temp; - ZipCompatibility::ConvertBufferToString(temp, m_pszComment, m_stringSettings.m_uCommentCodePage); - return temp; + if (m_comment.HasString()) + { + return m_comment.GetString(); + } + m_comment.AllocateString(); + ConvertComment(m_comment.GetString()); + if (bClearBuffer) + m_comment.ClearBuffer(); + return m_comment.GetString(); } int CZipFileHeader::GetCompressionLevel() const @@ -627,25 +899,92 @@ int CZipFileHeader::GetCompressionLevel() const return CZipCompressor::levelDefault; } -void CZipFileHeader::SetFileName(LPCTSTR lpszFileName) +bool CZipFileHeader::SetFileName(LPCTSTR lpszFileName, bool bInCentralOnly) { - if (m_pszFileName == NULL) - m_pszFileName = new CZipString(lpszFileName); + CZipString newFileName(lpszFileName); + if (!IsDirectory() || newFileName.GetLength() != 1 || !CZipPathComponent::IsSeparator(newFileName[0])) + // do not remove from directories where only path separator is present + CZipPathComponent::RemoveSeparatorsLeft(newFileName); + if (m_pCentralDir) + { + // update the lpszFileName to make sure the renaming is necessary + GetFileName(); + + if (!UpdateFileNameFlags(&newFileName, true)) + { + if (IsDirectory()) + CZipPathComponent::AppendSeparator(newFileName); + else + CZipPathComponent::RemoveSeparators(newFileName); + + if (m_fileName.GetString().Collate(newFileName) == 0) + return true; + } + // just in case + m_fileName.ClearBuffer(); + + bool ret; + CZipString oldFileName = m_fileName.GetString(); + m_fileName.SetString(lpszFileName); + ret = m_pCentralDir->OnFileNameChange(this, bInCentralOnly); + if (!bInCentralOnly) + { + if (ret) + SetModified(); + else + m_fileName.SetString(oldFileName); + } + + return ret; + } else - *m_pszFileName = lpszFileName; - m_pszFileNameBuffer.Release(); + { + m_fileName.ClearBuffer(); + m_fileName.SetString(newFileName); + return true; + } } -CZipString& CZipFileHeader::GetFileName(bool bClearBuffer) +const CZipString& CZipFileHeader::GetFileName(bool bClearBuffer) { - if (m_pszFileName != NULL) - return *m_pszFileName; - m_pszFileName = new CZipString(_T("")); - ConvertFileName(*m_pszFileName); + if (m_fileName.HasString()) + { + return m_fileName.GetString(); + } + m_fileName.AllocateString(); + ConvertFileName(m_fileName.GetString()); // don't keep it in memory if (bClearBuffer) - m_pszFileNameBuffer.Release(); - return *m_pszFileName; + { + m_fileName.ClearBuffer(); + } + return m_fileName.GetString(); +} + +CZipString CZipFileHeader::GetFileTitle(bool bLowerCase, bool bClearBuffer) +{ + CZipString sz = GetFileName(bClearBuffer); + CZipPathComponent::RemoveSeparators(sz); + CZipPathComponent zpc(sz); + CZipString ret = zpc.GetFileTitle(); + if (bLowerCase) + { + ret.MakeLower(); + } + return ret; +} + +CZipString CZipFileHeader::GetFileExtension(bool bLowerCase, bool bClearBuffer) +{ + CZipString sz = GetFileName(bClearBuffer); + CZipPathComponent::RemoveSeparators(sz); + CZipPathComponent zpc(sz); + CZipString ret = zpc.GetFileExt(); + if (bLowerCase) + { + ret.MakeLower(); + } + return ret; } bool CZipFileHeader::IsDirectory() @@ -655,76 +994,96 @@ bool CZipFileHeader::IsDirectory() DWORD CZipFileHeader::GetSystemAttr() { - int iSystemComp = GetSystemCompatibility(); - if (ZipCompatibility::IsPlatformSupported(iSystemComp)) - { - DWORD uAttr = iSystemComp == ZipCompatibility::zcUnix ? (m_uExternalAttr >> 16) : (m_uExternalAttr & 0xFFFF); - if (!uAttr && CZipPathComponent::HasEndingSeparator(GetFileName())) - return ZipPlatform::GetDefaultDirAttributes(); // can happen + if (ZipCompatibility::IsPlatformSupported(GetSystemCompatibility())) + { + DWORD uAttr = GetSystemCompatibility() == ZipCompatibility::zcUnix ? (m_uExternalAttr >> 16) : (m_uExternalAttr & 0xFFFF); + DWORD uConvertedAttr = ZipCompatibility::ConvertToSystem(uAttr, GetSystemCompatibility(), ZipPlatform::GetSystemID()); + // some zip files seem to report size greater than 0 for folders + if ((m_uComprSize == 0 || m_uExternalAttr == 0) && !ZipPlatform::IsDirectory(uConvertedAttr) && CZipPathComponent::HasEndingSeparator(GetFileName())) + { + // can happen, a folder can have attributes set and no dir attribute (Python modules) + // TODO: [postponed] fix and cache after reading from central dir, but avoid calling GetFileName() there to keep lazy name conversion + // We convert again as uConvertedAttr were treated as a file during ZipCompatibility::ConvertToSystem above + DWORD uSystemDirAttributes = ZipCompatibility::ConvertToSystem(ZipPlatform::GetDefaultDirAttributes(), ZipPlatform::GetSystemID(), GetSystemCompatibility()); + return ZipCompatibility::ConvertToSystem(uAttr | uSystemDirAttributes, GetSystemCompatibility(), ZipPlatform::GetSystemID()); + } else - { - uAttr = ZipCompatibility::ConvertToSystem(uAttr, iSystemComp, ZipPlatform::GetSystemID()); -#ifdef ZIP_ARCHIVE_LNX + { +#ifdef _ZIP_SYSTEM_LINUX // converting from Windows attributes may create a not readable linux directory - if (iSystemComp != ZipCompatibility::zcUnix && ZipPlatform::IsDirectory(uAttr)) + if (GetSystemCompatibility() != ZipCompatibility::zcUnix && ZipPlatform::IsDirectory(uConvertedAttr)) return ZipPlatform::GetDefaultDirAttributes(); #endif - return uAttr; + return uConvertedAttr; } } else return CZipPathComponent::HasEndingSeparator(GetFileName()) ? ZipPlatform::GetDefaultDirAttributes() : ZipPlatform::GetDefaultAttributes(); } -void CZipFileHeader::SetSystemAttr(DWORD uAttr) +bool CZipFileHeader::SetSystemAttr(DWORD uAttr) { // make it readable under Unix as well, since it stores its attributes in HIWORD(uAttr) - int iSystemComp = GetSystemCompatibility(); - m_uExternalAttr = ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), iSystemComp); - if (iSystemComp == ZipCompatibility::zcUnix) - { - m_uExternalAttr <<= 16; + DWORD uNewAtrr = ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), GetSystemCompatibility()); + if (GetSystemCompatibility() == ZipCompatibility::zcUnix) + { + uNewAtrr <<= 16; if (ZipPlatform::IsDirectory(uAttr)) - m_uExternalAttr |= 0x10; // make it recognizable under other systems (all use 0x10 for directory) + uNewAtrr |= 0x10; // make it recognizable under other systems (all use 0x10 for directory) } else // make it readable under linux - m_uExternalAttr |= (ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), ZipCompatibility::zcUnix) << 16); + uNewAtrr |= (ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), ZipCompatibility::zcUnix) << 16); + + if (uNewAtrr != m_uExternalAttr) + { + if (m_pCentralDir) + { + if (!m_pCentralDir->OnFileCentralChange()) + { + return false; + } + } + m_uExternalAttr = uNewAtrr; + } + return true; } CZipFileHeader& CZipFileHeader::operator=(const CZipFileHeader& header) { m_uVersionMadeBy = header.m_uVersionMadeBy; m_uVersionNeeded = header.m_uVersionNeeded; + m_iSystemCompatibility = header.m_iSystemCompatibility; m_uFlag = header.m_uFlag; m_uMethod = header.m_uMethod; m_uModTime = header.m_uModTime; m_uModDate = header.m_uModDate; + m_tModificationTime = header.m_tModificationTime; + m_tCreationTime = header.m_tCreationTime; + m_tLastAccessTime = header.m_tLastAccessTime; m_uCrc32 = header.m_uCrc32; m_uComprSize = header.m_uComprSize; m_uUncomprSize = header.m_uUncomprSize; m_uVolumeStart = header.m_uVolumeStart; m_uInternalAttr = header.m_uInternalAttr; m_uLocalComprSize = header.m_uLocalComprSize; - m_uLocalUncomprSize = header.m_uUncomprSize; + m_uLocalHeaderSize = header.m_uLocalHeaderSize; + m_uLocalUncomprSize = header.m_uLocalUncomprSize; m_uExternalAttr = header.m_uExternalAttr; m_uLocalFileNameSize = header.m_uLocalFileNameSize;; m_uOffset = header.m_uOffset; m_aLocalExtraData = header.m_aLocalExtraData; m_aCentralExtraData = header.m_aCentralExtraData; m_uEncryptionMethod = header.m_uEncryptionMethod; - if (m_pszFileName) - delete m_pszFileName; + m_fileName = header.m_fileName; + m_comment = header.m_comment; + m_state = header.m_state; - if (header.m_pszFileName) - m_pszFileName = new CZipString(*header.m_pszFileName); - else - m_pszFileName = NULL; - - m_pszFileNameBuffer = header.m_pszFileNameBuffer; - m_pszComment = header.m_pszComment; +#ifdef _ZIP_UNICODE_CUSTOM m_stringSettings = header.m_stringSettings; - +#endif + m_pCentralDir = header.m_pCentralDir; + return *this; } @@ -767,7 +1126,7 @@ void CZipFileHeader::WriteDataDescriptor(CZipStorage* pStorage) void CZipFileHeader::UpdateLocalHeader(CZipStorage* pStorage) { - if (pStorage->IsSegmented() != 0 || IsDataDescriptor()) + if (pStorage->IsSegmented() || IsDataDescriptor()) // there is nothing to fix return; pStorage->Flush(); @@ -775,13 +1134,13 @@ void CZipFileHeader::UpdateLocalHeader(CZipStorage* pStorage) // update crc and sizes, the sizes may already be all right, // but 8 more bytes won't make a difference, we need to update crc32 anyway CZipAutoBuffer buf(12); - m_uLocalComprSize = CBytesWriter::WriteSafeU32(m_uComprSize); - m_uLocalUncomprSize = CBytesWriter::WriteSafeU32(m_uUncomprSize); + m_uLocalComprSize = CBytesWriter::WriteSafeU32(m_uComprSize); + m_uLocalUncomprSize = CBytesWriter::WriteSafeU32(m_uUncomprSize); WriteSmallDataDescriptor(buf); pStorage->Seek(m_uOffset + 14); pStorage->m_pFile->Write(buf, 12); - pStorage->m_pFile->Seek(uPos); + pStorage->m_pFile->SafeSeek(uPos); } void CZipFileHeader::WriteCrc32(char* pBuf) const @@ -790,3 +1149,99 @@ void CZipFileHeader::WriteCrc32(char* pBuf) const CBytesWriter::WriteBytes(pBuf, uCrc); } + +void CZipFileHeader::ClearFileName() +{ +#ifdef _ZIP_UNICODE_CUSTOM + if (m_state.IsSetAny(sfCustomUnicode) && m_stringSettings.m_bStoreNameInExtraData) + // we are keeping m_pszFileName, clear the buffer, we need the original, when writing extra header in central directory (can happen many times - better performance) + // and when accessing the filename + m_fileName.ClearBuffer(); + else +#endif + m_fileName.ClearString(); +} + +bool CZipFileHeader::UpdateFileNameFlags(const CZipString* szNewFileName, bool bAllowRemoveCDir) +{ +#if defined _ZIP_UNICODE || defined _ZIP_UNICODE_CUSTOM + CBitFlag iMode = m_pCentralDir->GetUnicodeMode(); +#endif + // move the buffer to the name, to ensure it is converted properly now + GetComment(); + bool centralDirChanged = false; + bool changed = false; +#ifdef _ZIP_UNICODE_CUSTOM + bool isCustom = iMode.IsSetAny(CZipArchive::umCustom); + const CZipStringStoreSettings& stringStoreSettings = m_pCentralDir->GetStringStoreSettings(); + if (m_state.ChangeWithCheck(sfCustomUnicode, isCustom)) + { + // the mode changed + if (isCustom) + { + // changed to custom + m_stringSettings.m_bStoreNameInExtraData = stringStoreSettings.m_bStoreNameInExtraData; + m_stringSettings.m_uNameCodePage = stringStoreSettings.m_uNameCodePage; + if (!m_stringSettings.m_bStoreNameInExtraData && m_stringSettings.m_uNameCodePage != GetDefaultFileNameCodePage()) + // the local filename needs to be rewritten, the name code page has changed + changed = true; + } + else + { + // changed from custom + if (!m_stringSettings.m_bStoreNameInExtraData && m_stringSettings.m_uNameCodePage != GetDefaultFileNameCodePage()) + changed = true; + + m_stringSettings.m_bStoreNameInExtraData = stringStoreSettings.m_bStoreNameInExtraData; + m_stringSettings.m_uNameCodePage = stringStoreSettings.m_uNameCodePage; + } + centralDirChanged = true; + } + else if (isCustom) + { + if (stringStoreSettings.m_bStoreNameInExtraData != m_stringSettings.m_bStoreNameInExtraData) + { + if (m_stringSettings.m_bStoreNameInExtraData) + { + changed |= (stringStoreSettings.m_uNameCodePage == GetDefaultFileNameCodePage()); + } + else + { + changed |= (m_stringSettings.m_uNameCodePage == GetDefaultFileNameCodePage()); + } + } + else if (!m_stringSettings.m_bStoreNameInExtraData) + changed |= (stringStoreSettings.m_uNameCodePage != m_stringSettings.m_uNameCodePage); + + m_stringSettings.m_bStoreNameInExtraData = stringStoreSettings.m_bStoreNameInExtraData; + m_stringSettings.m_uNameCodePage = stringStoreSettings.m_uNameCodePage; + } +#endif + if (changed || centralDirChanged) + { + m_comment.ClearBuffer(); + } + // remove the central directory in case the filename did not changed, but the comment flags changed + if (!changed && centralDirChanged && bAllowRemoveCDir && m_pCentralDir && m_comment.HasString()) + m_pCentralDir->OnFileCentralChange(); + return changed; +} + +bool CZipFileHeader::UpdateCommentFlags(const CZipString* szNewComment) +{ +#if defined _ZIP_UNICODE || defined _ZIP_UNICODE_CUSTOM + CBitFlag iMode = m_pCentralDir->GetUnicodeMode(); +#endif + bool changed = false; +#ifdef _ZIP_UNICODE_CUSTOM + bool isCustom = iMode.IsSetAny(CZipArchive::umCustom); + changed |= m_state.ChangeWithCheck(sfCustomUnicode, isCustom); + if (isCustom) + { + const CZipStringStoreSettings& stringStoreSettings = m_pCentralDir->GetStringStoreSettings(); + changed |= (m_stringSettings.m_uCommentCodePage != stringStoreSettings.m_uCommentCodePage); + m_stringSettings.m_uCommentCodePage = stringStoreSettings.m_uCommentCodePage; + } +#endif + return changed; +} -- cgit v1.2.3