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/ZipArchive.cpp | 1454 ++++++++++++++++++++++++++--------------- 1 file changed, 909 insertions(+), 545 deletions(-) (limited to 'zip/ZipArchive/ZipArchive.cpp') diff --git a/zip/ZipArchive/ZipArchive.cpp b/zip/ZipArchive/ZipArchive.cpp index 8e29c3a..d2fe23e 100644 --- a/zip/ZipArchive/ZipArchive.cpp +++ b/zip/ZipArchive/ZipArchive.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,9 +9,9 @@ // // For the licensing details refer to the License.txt file. // -// Web Site: http://www.artpol-software.com +// Web Site: https://www.artpol-software.com //////////////////////////////////////////////////////////////////////////////// - +// TODO: remove warnings with zlib compilation (exposed at least in VS 2005 Release compilation) #include "stdafx.h" #include "ZipArchive.h" #include "ZipPlatform.h" @@ -23,29 +23,82 @@ using namespace ZipArchiveLib; -const char CZipArchive::m_gszCopyright[] = {"ZipArchive Library Copyright (C) 2000 - 2007 Artpol Software - Tadeusz Dracz"}; -const char CZipArchive::m_gszVersion[] = {"3.2.0"}; +const char CZipArchive::m_gszCopyright[] = {"The ZipArchive Library Copyright (c) 2000 - 2022 Artpol Software - Tadeusz Dracz"}; +const char CZipArchive::m_gszVersion[] = {"4.6.9"}; void CZipAddNewFileInfo::Defaults() { - m_iSmartLevel = CZipArchive::zipsmSafeSmart; + m_iSmartLevel = CZipArchive::zipsmSafeSmart; m_uReplaceIndex = ZIP_FILE_INDEX_UNSPECIFIED; m_nBufSize = 65536; m_iComprLevel = -1; // default + m_szFileNameInZip = _T(""); + m_szFilePath = _T(""); + m_bFullPath = true; + m_pFile = NULL; +} + +CZipAddFilesEnumerator::CZipAddFilesEnumerator(LPCTSTR lpszDirectory, + bool bRecursive, + int iComprLevel, + int iSmartLevel, + unsigned long nBufSize) + :ZipArchiveLib::CDirEnumerator(lpszDirectory, bRecursive) +{ + m_iComprLevel = iComprLevel; + m_nBufSize = nBufSize; + m_iSmartLevel = iSmartLevel; + m_pZip = NULL; + m_pMultiCallback = NULL; } +bool CZipAddFilesEnumerator::Process(LPCTSTR lpszPath, const ZipArchiveLib::CFileInfo& info) +{ + if (info.IsDirectory() && ((m_iSmartLevel & CZipArchive::zipsmIgnoreDirectories) != 0)) + return true; +#if defined _MSC_VER && _MSC_VER < 1300 + CZipAddNewFileInfo zanfi(lpszPath, m_pZip->GetRootPath().IsEmpty() == TRUE); +#else + CZipAddNewFileInfo zanfi(lpszPath, m_pZip->GetRootPath().IsEmpty()); +#endif + + zanfi.m_iComprLevel = m_iComprLevel; + zanfi.m_iSmartLevel = m_iSmartLevel; + zanfi.m_nBufSize = m_nBufSize; + UpdateAddNewFileInfo(&zanfi); + bool ret = m_pZip->AddNewFile(zanfi); + if (ret && m_pMultiCallback) + { + if (!m_pMultiCallback->MultiActionsNext(zanfi.m_szFileNameInZip)) + CZipException::Throw(CZipException::abortedSafely); + } + return ret; +} - CZipArchive:: CZipArchive() +void CReplacingAddFilesEnumerator::UpdateAddNewFileInfo(CZipAddNewFileInfo* info) { + CZipArchive* zip = GetZip(); + CZipString fileName = zip->PredictFileNameInZip(info->m_szFilePath, info->m_bFullPath); + ZIP_INDEX_TYPE index = zip->FindFile(fileName); + + if (index != ZIP_FILE_INDEX_NOT_FOUND) + info->m_uReplaceIndex = index; +} + + +#pragma warning(suppress: 26495) +CZipArchive:: CZipArchive() +{ Initialize(); } -void CZipArchive::Initialize() +void CZipArchive::Initialize() { - m_bRemoveDriveLetter = true; - m_bExhaustiveRead = false; - m_bAutoFlush = false; + m_bSafePaths = true; + m_bAutoFinalize = false; + // use the default + SetCommitMode(); m_iFileOpened = nothing; SetCaseSensitivity(ZipPlatform::GetSystemCaseSensitivity()); m_uCompressionMethod = CZipCompressor::methodDeflate; @@ -53,6 +106,9 @@ void CZipArchive::Initialize() m_pCryptograph = NULL; m_pCompressor = NULL; m_iBufferSize = 65536; + m_centralDir.InitOnCreate(this); + m_bStoreFullFileTimes = false; + m_bUseUtcFileTimes = true; } @@ -63,7 +119,7 @@ void CZipArchive::Initialize() ClearCryptograph(); } -bool CZipArchive::Open(LPCTSTR szPathName, int iMode, ZIP_SIZE_TYPE uVolumeSize) +bool CZipArchive::Open(LPCTSTR szPathName, int iMode, ZIP_SIZE_TYPE uVolumeSize) { if (!IsClosed()) { @@ -75,62 +131,92 @@ bool CZipArchive::Open(LPCTSTR szPathName, int iMode, ZIP_SIZE_TYPE uVolumeSize return true; } -bool CZipArchive::Open(CZipAbstractFile& af, int iMode) +bool CZipArchive::IsZipArchive(LPCTSTR lpszPathName) +{ + CZipArchive zip; + zip.m_storage.Open(lpszPathName, zipOpenReadOnly, 0); + return zip.m_centralDir.LocateSignature() != CZipStorage::SignatureNotFound; +} + + +bool CZipArchive::IsZipArchive(CZipAbstractFile& af, bool bAutoClose) +{ + CZipArchive zip; + zip.m_storage.Open(af, zipOpenReadOnly, bAutoClose); + return zip.m_centralDir.LocateSignature() != CZipStorage::SignatureNotFound; +} + +bool CZipArchive::Open(CZipAbstractFile& af, int iMode, bool bAutoClose) { if (!IsClosed()) { ZIPTRACE("%s(%i) : ZipArchive is already opened.\n"); return false; } + if (iMode != zipOpen && iMode != zipOpenReadOnly && iMode != zipCreate && iMode != zipCreateAppend) { - ZIPTRACE("%s(%i) : Mode is not supported.\n"); + ZIPTRACE("%s(%i) : The open mode is not supported.\n"); return false; } - m_storage.Open(af, iMode); + m_storage.Open(af, iMode, bAutoClose); OpenInternal(iMode); return true; } -bool CZipArchive::OpenFrom( CZipArchive& zip) +bool CZipArchive::OpenFrom(CZipArchive& zip, CZipAbstractFile* pArchiveFile, bool bAllowNonReadOnly) { if (zip.IsClosed()) { ZIPTRACE("%s(%i) : The source archive must be opened.\n"); return false; } - if (!zip.IsReadOnly()) + if (!bAllowNonReadOnly && !zip.IsReadOnly()) { ZIPTRACE("%s(%i) : The source archive must be opened in the read-only mode.\n"); return false; } - if (zip.m_storage.m_bInMemory) + + if (pArchiveFile != NULL && zip.m_storage.IsSegmented()) { - ZIPTRACE("%s(%i) : ZipArchive cannot share an archive in memory.\n"); + ZIPTRACE("%s(%i) : ZipArchive cannot share a segmented archive using a file that is not on a disk.\n"); return false; } - m_storage.Open(zip.GetArchivePath(), CZipArchive::zipOpenReadOnly, zip.m_storage.IsSplit() ? 1 : 0); + int mode = CZipArchive::zipOpenReadOnly; + if (zip.m_storage.IsBinarySplit()) + mode |= CZipArchive::zipOpenBinSplit; + else if (zip.m_storage.IsSplit()) + mode |= CZipArchive::zipOpenSplit; + + if (pArchiveFile != NULL) + m_storage.Open(*pArchiveFile, mode, false); + else if (zip.m_storage.m_pFile->HasFilePath()) + m_storage.Open(zip.GetArchivePath(), mode, 0); + else + m_storage.Open(*zip.m_storage.m_pFile, mode, false); InitOnOpen(zip.GetSystemCompatibility(), &zip.m_centralDir); return true; } -void CZipArchive::InitOnOpen(int iArchiveSystCompatib, CZipCentralDir* pSource) +void CZipArchive::InitOnOpen(int iArchiveSystCompatib, CZipCentralDir* pSource) { m_pszPassword.Release(); m_iFileOpened = nothing; m_szRootPath.Empty(); - m_centralDir.Init(&m_storage, &m_callbacks, &m_stringSettings, pSource); + m_centralDir.Init(pSource); m_iArchiveSystCompatib = iArchiveSystCompatib; + m_bStoreFullFileTimes = false; } -void CZipArchive::OpenInternal(int iMode) +void CZipArchive::OpenInternal(int iMode) { - InitOnOpen(ZipPlatform::GetSystemID()); - if ((iMode == zipOpen) ||(iMode == zipOpenReadOnly)) + InitOnOpen(ZipPlatform::GetSystemID()); + const CBitFlag mode(iMode); + if (mode.IsSetAny(zipOpen) || mode.IsSetAll(zipOpenReadOnly)) { - m_centralDir.Read(m_bExhaustiveRead); + m_centralDir.Read(); // if there is at least one file, get system comp. from the first one if (m_centralDir.IsValidIndex(0)) { @@ -141,12 +227,12 @@ void CZipArchive::OpenInternal(int iMode) } } -void CZipArchive::ThrowError(int err) +void CZipArchive::ThrowError(int err, LPCTSTR lpszFilePath) const { - CZipException::Throw(err, IsClosed() ? _T("") : (LPCTSTR)m_storage.m_pFile->GetFilePath()); + CZipException::Throw(err, lpszFilePath != NULL ? lpszFilePath : (IsClosed() ? _T("") : (LPCTSTR)m_storage.m_pFile->GetFilePath())); } -bool CZipArchive::GetFileInfo(CZipFileHeader & fhInfo, ZIP_INDEX_TYPE uIndex) const +bool CZipArchive::GetFileInfo(CZipFileHeader & fhInfo, ZIP_INDEX_TYPE uIndex) const { if (IsClosed()) { @@ -161,7 +247,7 @@ bool CZipArchive::GetFileInfo(CZipFileHeader & fhInfo, ZIP_INDEX_TYPE uIndex) c return true; } -CZipFileHeader* CZipArchive::GetFileInfo(ZIP_INDEX_TYPE uIndex) +CZipFileHeader* CZipArchive::GetFileInfo(ZIP_INDEX_TYPE uIndex) { if (IsClosed()) { @@ -174,7 +260,7 @@ CZipFileHeader* CZipArchive::GetFileInfo(ZIP_INDEX_TYPE uIndex) return m_centralDir[uIndex]; } -const CZipFileHeader* CZipArchive::GetFileInfo(ZIP_INDEX_TYPE uIndex) const +const CZipFileHeader* CZipArchive::GetFileInfo(ZIP_INDEX_TYPE uIndex) const { if (IsClosed()) { @@ -187,9 +273,7 @@ const CZipFileHeader* CZipArchive::GetFileInfo(ZIP_INDEX_TYPE uIndex) const return m_centralDir[uIndex]; } - - -ZIP_INDEX_TYPE CZipArchive::FindFile(LPCTSTR lpszFileName, int iCaseSensitive, bool bFileNameOnly) +ZIP_INDEX_TYPE CZipArchive::FindFile(LPCTSTR lpszFileName, int iCaseSensitive, bool bFileNameOnly) { if (IsClosed()) { @@ -215,7 +299,7 @@ ZIP_INDEX_TYPE CZipArchive::FindFile(LPCTSTR lpszFileName, int iCaseSensitive, return m_centralDir.FindFile(lpszFileName, bCS, bSporadically, bFileNameOnly); } -bool CZipArchive::OpenFile(ZIP_INDEX_TYPE uIndex) +bool CZipArchive::OpenFile(ZIP_INDEX_TYPE uIndex) { if (IsClosed()) { @@ -228,7 +312,7 @@ bool CZipArchive::OpenFile(ZIP_INDEX_TYPE uIndex) ASSERT(FALSE); return false; } - if (m_storage.IsSegmented() == 1) + if (m_storage.IsNewSegmented()) { ZIPTRACE("%s(%i) : ZipArchive Library cannot extract from a segmented archive in creation.\n"); return false; @@ -242,8 +326,9 @@ bool CZipArchive::OpenFile(ZIP_INDEX_TYPE uIndex) m_centralDir.OpenFile(uIndex); - // check it now, not when reading central to allow reading information - // but disallow extraction now - unsupported method + // Check it now, not when reading central information + // but disallow extraction now - unsupported method. + // This is to allow reading of local information without throwing an exception. if (!CZipCompressor::IsCompressionSupported(CurrentFile()->m_uMethod)) { m_centralDir.CloseFile(true); @@ -259,7 +344,7 @@ bool CZipArchive::OpenFile(ZIP_INDEX_TYPE uIndex) ThrowError(CZipException::badPassword); } CreateCryptograph(CurrentFile()->m_uEncryptionMethod); - if (!m_pCryptograph->InitDecode(m_pszPassword, *CurrentFile(), m_storage)) + if (!m_pCryptograph->InitDecode(m_pszPassword, *CurrentFile(), m_storage, !m_centralDir.IsConsistencyCheckOn(checkDecryptionVerifier))) ThrowError(CZipException::badPassword); } @@ -279,13 +364,13 @@ bool CZipArchive::OpenFile(ZIP_INDEX_TYPE uIndex) return true; } -CZipFileHeader* CZipArchive::CurrentFile() +CZipFileHeader* CZipArchive::CurrentFile() { ASSERT(m_centralDir.m_pOpenedFile); return m_centralDir.m_pOpenedFile; } -DWORD CZipArchive::ReadFile(void *pBuf, DWORD uSize) +DWORD CZipArchive::ReadFile(void *pBuf, DWORD uSize) { if (m_iFileOpened != extract) { @@ -300,22 +385,30 @@ DWORD CZipArchive::ReadFile(void *pBuf, DWORD uSize) } -void CZipArchive::Close(int iAfterException, bool bUpdateTimeStamp) +CZipString CZipArchive::Close(int iAfterException, bool bUpdateTimeStamp) { // if after an exception - the archive may be closed, but the file may be opened if (IsClosed() && (!iAfterException || IsClosed(false))) { ZIPTRACE("%s(%i) : ZipArchive is already closed.\n"); - return; + return _T(""); } if (m_iFileOpened == extract) + // when CloseFile returns -1, it doesn't necessarily mean error here (e.g. when using multi seek feature) + // if (CloseFile(NULL, iAfterException != afNoException) == -1) + // ThrowError(CZipException::badZipFile); CloseFile(NULL, iAfterException != afNoException); if (m_iFileOpened == compress) CloseNewFile(iAfterException != afNoException); - bool bWrite = iAfterException != afAfterException && !IsClosed(false);// in segmented archive when user aborts + if (iAfterException == afNoException) + { + CommitChanges(); + } + + bool bWrite = iAfterException != afAfterException && !IsReadOnly() && !IsClosed(false);// in segmented archive when user aborts if (bWrite) WriteCentralDirectory(false); // we will flush in CZipStorage::Close @@ -327,26 +420,30 @@ void CZipArchive::Close(int iAfterException, bool bUpdateTimeStamp) ZIP_INDEX_TYPE iSize = (ZIP_INDEX_TYPE)m_centralDir.GetCount(); for (ZIP_INDEX_TYPE i = 0; i < iSize; i++) { - time_t tFileInZipTime = m_centralDir[i]->GetTime(); + time_t tFileInZipTime = m_centralDir[i]->GetModificationTime(); if (tFileInZipTime > tNewestTime) tNewestTime = tFileInZipTime; } } - m_centralDir.Close(); - m_stringSettings.Reset(); - CZipString szFileName = m_storage.Close(!bWrite); +#ifdef _ZIP_UNICODE_CUSTOM + ResetStringStoreSettings(); +#endif + m_centralDir.Close(); + CZipString szFileName = m_storage.Close(bWrite, iAfterException != afAfterException); + m_pszPassword.Release(); if (bUpdateTimeStamp && !szFileName.IsEmpty()) - ZipPlatform::SetFileModTime(szFileName, tNewestTime); + ZipPlatform::SetFileTimes(szFileName, &tNewestTime); + return szFileName; } -void CZipArchive::WriteCentralDirectory(bool bFlush) +void CZipArchive::WriteCentralDirectory(bool bFlush) { m_centralDir.Write(); if (bFlush) m_storage.Flush(); } -void CZipArchive::SetAdvanced(int iWriteBuffer, int iGeneralBuffer, int iSearchBuffer) +void CZipArchive::SetAdvanced(int iWriteBuffer, int iGeneralBuffer, int iSearchBuffer) { if (!IsClosed()) { @@ -359,21 +456,35 @@ void CZipArchive::SetAdvanced(int iWriteBuffer, int iGeneralBuffer, int iSearch m_storage.m_iLocateBufferSize = iSearchBuffer < 1024 ? 1024 : iSearchBuffer; } -int CZipArchive::CloseFile(CZipFile &file) -{ +int CZipArchive::CloseFile(CZipFile &file) +{ +#ifdef _ZIP_SYSTEM_WIN + CZipFileHeader* pCurrentFile = CurrentFile(); + time_t tModificationTime = pCurrentFile->GetModificationTime(); + time_t tCreationTime = pCurrentFile->GetCreationTime(); + time_t tLastAccessTime = pCurrentFile->GetLastAccessTime(); + int iRet = ZipPlatform::SetFileTimes((HANDLE)file, &tModificationTime, &tCreationTime, &tLastAccessTime) + && ZipPlatform::SetFileAttr(file.GetFilePath(), pCurrentFile->GetSystemAttr()) ? 1 : -2; + file.Close(); + int iCloseRet = CloseFile(NULL); + return iCloseRet == 1 ? iRet : iCloseRet; +#else CZipString temp = file.GetFilePath(); file.Close(); return CloseFile(temp); +#endif + + } -int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException) +int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException) { if (m_iFileOpened != extract) { - ZIPTRACE("%s(%i) : No opened file.\n"); + ZIPTRACE("%s(%i) : There is no opened file.\n"); return 0; } - + CZipFileHeader* pCurrentFile = CurrentFile(); int iRet = 1; if (bAfterException) m_pCompressor->FinishDecompression(true); @@ -382,7 +493,7 @@ int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException) if (m_pCompressor->m_uUncomprLeft == 0) { if (m_centralDir.IsConsistencyCheckOn(checkCRC) - && !CurrentFile()->m_bIgnoreCrc32 + && !pCurrentFile->m_bIgnoreCrc32 && m_pCompressor->m_uCrc32 != CurrentFile()->m_uCrc32) ThrowError(CZipException::badCrc); } @@ -393,12 +504,15 @@ int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException) if (lpszFilePath) { - if (!ZipPlatform::SetFileModTime(lpszFilePath, CurrentFile()->GetTime()) - ||!ZipPlatform::SetFileAttr(lpszFilePath, CurrentFile()->GetSystemAttr())) + time_t tModificationTime = pCurrentFile->GetModificationTime(); + time_t tCreationTime = pCurrentFile->GetCreationTime(); + time_t tLastAccessTime = pCurrentFile->GetLastAccessTime(); + if (!ZipPlatform::SetFileTimes(lpszFilePath, &tModificationTime, &tCreationTime, &tLastAccessTime ) + ||!ZipPlatform::SetFileAttr(lpszFilePath, pCurrentFile->GetSystemAttr())) iRet = -2; } if (m_pCryptograph) - m_pCryptograph->FinishDecode(*CurrentFile(), m_storage); + m_pCryptograph->FinishDecode(*pCurrentFile, m_storage); } m_centralDir.CloseFile(bAfterException); @@ -408,26 +522,11 @@ int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException) return iRet; } -bool CZipArchive::OpenNewFile(CZipFileHeader & header, int iLevel, LPCTSTR lpszFilePath, +bool CZipArchive::OpenNewFile(CZipFileHeader & header, int iLevel, LPCTSTR lpszFilePath, ZIP_INDEX_TYPE uReplaceIndex) { - if (IsClosed()) - { - ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); - return false; - } - - if (m_iFileOpened) - { - ZIPTRACE("%s(%i) : A file already opened.\n"); - return false; - } - - if (m_storage.IsSegmented() == -1) - { - ZIPTRACE("%s(%i) : ZipArchive Library cannot add files to an existing segmented archive.\n"); + if (!CanModify(true)) return false; - } if (GetCount() ==(WORD)USHRT_MAX) { @@ -435,27 +534,46 @@ bool CZipArchive::OpenNewFile(CZipFileHeader & header, int iLevel, LPCTSTR lpsz return false; } - DWORD uAttr = 0; - time_t ttime; + if (lpszFilePath) { + DWORD uAttr = 0; if (!ZipPlatform::GetFileAttr(lpszFilePath, uAttr)) + { // do not continue - if the file was a directory then not recognizing it will cause // serious errors (need uAttr to recognize it) - return false; - ZipPlatform::GetFileModTime(lpszFilePath, ttime); - } - - if (lpszFilePath) - { - header.SetTime(ttime); - SetFileHeaderAttr(header, uAttr); // set system compatibility as well + ThrowError(CZipException::fileError, lpszFilePath); + } + if (m_bStoreFullFileTimes) + { + time_t tModTime = (time_t)0, tCreateTime = (time_t)0, tLastAccessTime = (time_t)0; + ZipPlatform::GetFileTimes(lpszFilePath, &tModTime, &tCreateTime, &tLastAccessTime); + header.SetModificationTime(tModTime, true, m_bUseUtcFileTimes); + header.SetCreationTime(tCreateTime); + header.SetLastAccessTime(tLastAccessTime); + } + else + { + time_t ttime; + ZipPlatform::GetFileTimes(lpszFilePath, &ttime); + header.SetModificationTime(ttime, false, m_bUseUtcFileTimes); + } + header.SetSystemCompatibility(m_iArchiveSystCompatib); + header.SetSystemAttr(uAttr); } else { - header.SetSystemCompatibility(m_iArchiveSystCompatib); + header.SetSystemCompatibility(m_iArchiveSystCompatib, true); if (!header.HasTime()) - header.SetTime(time(NULL)); + { + time_t tNow = time(NULL); + header.SetModificationTime(tNow, m_bStoreFullFileTimes, m_bUseUtcFileTimes); + if (m_bStoreFullFileTimes) + { + header.SetCreationTime(tNow); + header.SetLastAccessTime(tNow); + } + } } CZipString szFileName = header.GetFileName(); @@ -486,9 +604,6 @@ bool CZipArchive::OpenNewFile(CZipFileHeader & header, int iLevel, LPCTSTR lpsz #endif bool bReplace = uReplaceIndex != ZIP_FILE_INDEX_UNSPECIFIED; - - if (iLevel < -1 || iLevel > 9) - iLevel = -1; if (bEncrypted) { @@ -501,10 +616,22 @@ bool CZipArchive::OpenNewFile(CZipFileHeader & header, int iLevel, LPCTSTR lpsz ClearCryptograph(); } - if (iLevel == 0 || bIsDirectory) + if (iLevel < -1 || iLevel > 9) + iLevel = -1; + + header.m_uMethod = m_uCompressionMethod; + + if (iLevel != 0 && m_uCompressionMethod == CZipCompressor::methodStore) + iLevel = 0; + else if (iLevel == 0 && m_uCompressionMethod != CZipCompressor::methodStore) header.m_uMethod = CZipCompressor::methodStore; - else - header.m_uMethod = m_uCompressionMethod; + + if (bIsDirectory && iLevel != 0) + { + iLevel = 0; + if (m_uCompressionMethod != CZipCompressor::methodStore) + header.m_uMethod = CZipCompressor::methodStore; + } CreateCompressor(header.m_uMethod); CZipFileHeader* pHeader = m_centralDir.AddNewFile(header, uReplaceIndex, iLevel); @@ -513,9 +640,9 @@ bool CZipArchive::OpenNewFile(CZipFileHeader & header, int iLevel, LPCTSTR lpsz if (bReplace) { // this will be used in GetLocalSize and WriteLocal - pHeader->PrepareFileName(); + pHeader->PrepareStringBuffers(); // we use the local size, because the real does not exist yet - ZIP_SIZE_TYPE uFileSize = pHeader->GetDataSize(true, false) + pHeader->GetLocalSize(false) + pHeader->GetDataDescriptorSize(&m_storage); + ZIP_SIZE_TYPE uFileSize = pHeader->m_uLocalComprSize + pHeader->GetLocalSize(false) + pHeader->GetDataDescriptorSize(&m_storage); InitBuffer(); MakeSpaceForReplace(uReplaceIndex, uFileSize, szFileName); ReleaseBuffer(); @@ -532,11 +659,11 @@ bool CZipArchive::OpenNewFile(CZipFileHeader & header, int iLevel, LPCTSTR lpsz return true; } - -bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, +bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, LPCTSTR lpszPath, bool bFullPath, LPCTSTR lpszNewName, + ZipPlatform::FileOverwriteMode iOverwriteMode, DWORD nBufSize) { @@ -544,6 +671,8 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, return false; CZipFileHeader* pHeader = (*this)[uIndex]; + if (!pHeader) + return false; CZipString szFileNameInZip = pHeader->GetFileName(); CZipString szFile = PredictExtractedFileName(szFileNameInZip, lpszPath, bFullPath, lpszNewName); CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbExtract); @@ -565,18 +694,49 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, } else { + int iAborted = 0; + + if (ZipPlatform::FileExists(szFile) != 0) + { + if ((iOverwriteMode & ZipPlatform::fomOnlyIfNewer) != 0) + { + time_t fileInZipTime = pHeader->GetModificationTime(); + if (fileInZipTime > 0) + { + time_t fileTime; + if (ZipPlatform::GetFileTimes(szFile, &fileTime) && fileTime > 0 && fileTime >= fileInZipTime) + { + if (pCallback) + { + pCallback->SetTotal(pHeader->m_uUncomprSize); + if (!pCallback->RequestCallback(pHeader->m_uUncomprSize) || !pCallback->RequestLastCallback()) + { + iAborted = CZipException::abortedSafely; + } + pCallback->CallbackEnd(); + if (iAborted) + CZipException::Throw(iAborted, szFile); + } + return true; + } + } + } + ZipPlatform::RemoveFile(szFile, true, iOverwriteMode); + } + if (!OpenFile(uIndex)) return false; + CZipPathComponent zpc(szFile); + ZipPlatform::ForceDirectory(zpc.GetFilePath()); if (pCallback) pCallback->SetTotal(pHeader->m_uUncomprSize); - CZipPathComponent zpc(szFile); - ZipPlatform::ForceDirectory(zpc.GetFilePath()); + CZipFile f(szFile, CZipFile::modeWrite | - CZipFile::modeCreate | CZipFile::shareDenyWrite); + CZipFile::modeCreate | CZipFile::shareDenyWrite); DWORD iRead; CZipAutoBuffer buf(nBufSize); - int iAborted = 0; + for(;;) { iRead = ReadFile(buf, buf.GetSize()); @@ -597,13 +757,13 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, } } + int iResult; if (pCallback) { if (!iAborted) { - bool bRet = CloseFile(f) == 1; - pCallback->CallbackEnd(); - return bRet; + iResult = CloseFile(f); + pCallback->CallbackEnd(); } else { @@ -618,7 +778,7 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, } // if any exception was thrown, then we are not successful // catch all exceptions to throw aborted exception only -#ifdef ZIP_ARCHIVE_MFC +#ifdef _ZIP_IMPL_MFC catch(CException* e) { e->Delete(); @@ -642,12 +802,17 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, } } else - return CloseFile(f) == 1; + { + iResult = CloseFile(f); + } + if (iResult == -1) + ThrowError(CZipException::badZipFile, szFile); + return iResult == 1; } } -bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, - CZipMemFile& mf, +bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, + CZipAbstractFile& af, bool bRewind, DWORD nBufSize) { @@ -655,11 +820,14 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, return false; CZipFileHeader* pHeader = (*this)[uIndex]; + if (!pHeader || pHeader->IsDirectory()) + return false; + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbExtract); if (pCallback) pCallback->Init(pHeader->GetFileName()); - if (pHeader->IsDirectory() || !OpenFile(uIndex)) + if (!OpenFile(uIndex)) return false; if (pCallback) @@ -667,11 +835,11 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, CZipAutoBuffer buf(nBufSize); - //mf.SeekToEnd(); + //af.SeekToEnd(); ZIP_FILE_USIZE oldPos = 0; if (bRewind) - oldPos = mf.GetPosition(); + oldPos = af.GetPosition(); DWORD iRead; int iAborted = 0; @@ -684,7 +852,7 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, iAborted = CZipException::abortedSafely; break; } - mf.Write(buf, iRead); + af.Write(buf, iRead); if (pCallback && !pCallback->RequestCallback(iRead)) { if (iRead == buf.GetSize() && ReadFile(buf, 1) != 0) // test one byte if there is something left @@ -700,8 +868,12 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, { if (!iAborted) { - bRet = CloseFile() == 1; - pCallback->CallbackEnd(); + int iResult = CloseFile(); + pCallback->CallbackEnd(); + + if (iResult == -1) + ThrowError(CZipException::badZipFile); + bRet = iResult == 1; } else { @@ -716,7 +888,7 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, } // if any exception was thrown, then we are not successful // catch all exceptions to thrown aborted exception only - #ifdef ZIP_ARCHIVE_MFC + #ifdef _ZIP_IMPL_MFC catch(CException* e) { e->Delete(); @@ -736,21 +908,28 @@ bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, pCallback->CallbackEnd(); if (bRewind) - mf.Seek(oldPos, CZipMemFile::begin); + af.SafeSeek(oldPos, true); CZipException::Throw(iAborted); return false; // for the compiler } } else - bRet = CloseFile() == 1; + { + int iResult = CloseFile(); + // when using seeking, it's expected to return -1 + + if (iResult == -1) + ThrowError(CZipException::badZipFile); + bRet = iResult == 1; + } if (bRewind) - mf.Seek(oldPos, CZipMemFile::begin); + af.SafeSeek(oldPos, true); return bRet; } -bool CZipArchive::WriteNewFile(const void *pBuf, DWORD uSize) +bool CZipArchive::WriteNewFile(const void *pBuf, DWORD uSize) { if (m_iFileOpened != compress) { @@ -762,7 +941,7 @@ bool CZipArchive::WriteNewFile(const void *pBuf, DWORD uSize) return true; } -bool CZipArchive::CloseNewFile(bool bAfterException) +bool CZipArchive::CloseNewFile(bool bAfterException) { if (m_iFileOpened != compress) { @@ -782,20 +961,45 @@ bool CZipArchive::CloseNewFile(bool bAfterException) } m_iFileOpened = nothing; ClearCryptograph(); - if (m_bAutoFlush && !bAfterException) - Flush(); + if (!bAfterException) + Finalize(true); return true; } -bool CZipArchive::RemoveFile(ZIP_INDEX_TYPE uIndex) +bool CZipArchive::RemoveFile(ZIP_INDEX_TYPE uIndex, bool bRemoveData) { - CZipIndexesArray indexes; - indexes.Add(uIndex); - return RemoveFiles(indexes); + if (bRemoveData) + { + CZipIndexesArray indexes; + indexes.Add(uIndex); + return RemoveFiles(indexes); + } + else + { + if (!CanModify()) + return false; + + if (GetCount() == 0) + { + ZIPTRACE("%s(%i) : There is nothing to delete: the archive is empty.\n"); + return false; + } + + m_centralDir.RemoveFromDisk(); + if (m_centralDir.IsValidIndex(uIndex)) + { + m_centralDir.RemoveFile(NULL, uIndex, false); + return true; + } + else + { + return false; + } + } } -void CZipArchive::GetIndexes(const CZipStringArray &aNames, CZipIndexesArray& aIndexes) +void CZipArchive::GetIndexes(const CZipStringArray &aNames, CZipIndexesArray& aIndexes) { if (IsClosed()) { @@ -807,7 +1011,7 @@ void CZipArchive::GetIndexes(const CZipStringArray &aNames, CZipIndexesArray& a aIndexes.Add(FindFile(aNames[(ZIP_ARRAY_SIZE_TYPE)i], ffDefault, false)); } -bool CZipArchive::RemoveFiles(const CZipStringArray &aNames) +bool CZipArchive::RemoveFiles(const CZipStringArray &aNames) { CZipIndexesArray indexes; GetIndexes(aNames, indexes); @@ -823,23 +1027,10 @@ struct CZipDeleteInfo bool m_bDelete; }; -bool CZipArchive::RemoveFiles(CZipIndexesArray &aIndexes) +bool CZipArchive::RemoveFiles(CZipIndexesArray &aIndexes) { - if (IsClosed()) + if (!CanModify()) { - ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); - return false; - } - - if (m_storage.IsSegmented()) - { - ZIPTRACE("%s(%i) : ZipArchive Library cannot delete files from a segmented archive.\n"); - return false; - } - - if (m_iFileOpened) - { - ZIPTRACE("%s(%i) : ZipArchive Library cannot delete files if there is a file opened.\n"); return false; } @@ -881,10 +1072,10 @@ bool CZipArchive::RemoveFiles(CZipIndexesArray &aIndexes) } m_centralDir.RemoveFromDisk(); - m_storage.m_pFile->SetLength((ZIP_FILE_USIZE) m_storage.m_uBytesBeforeZip); + // we take into account the bytes present in an archive that was created with CZipArchive::zipCreateAppend + m_storage.m_pFile->SetLength(GetFileInfo(0)->m_uOffset + (ZIP_FILE_USIZE) m_storage.m_uBytesBeforeZip); m_centralDir.RemoveAll(); - if (m_bAutoFlush) - Flush(); + Finalize(true); if (pCallback) pCallback->CallbackEnd(); return true; @@ -973,7 +1164,7 @@ bool CZipArchive::RemoveFiles(CZipIndexesArray &aIndexes) while(i > 0) { i--; - // cannot use a decreasing loop because i is unsigned and instead negative at the end of the loop it will be maximum positive + // cannot use a decreasing loop because it is unsigned and instead negative at the end of the loop it will be maximum positive const CZipDeleteInfo& di = aInfo[(ZIP_ARRAY_SIZE_TYPE)i]; if (!di.m_bDelete) uTotalToMoveBytes += uLastOffset - di.m_pHeader->m_uOffset; @@ -1040,30 +1231,16 @@ bool CZipArchive::RemoveFiles(CZipIndexesArray &aIndexes) if (pCallback) pCallback->CallbackEnd(); - if (m_bAutoFlush) - Flush(); + Finalize(true); return true; } -bool CZipArchive::ShiftData(ZIP_SIZE_TYPE uOffset) +bool CZipArchive::ShiftData(ZIP_SIZE_TYPE uOffset) { - if (IsClosed()) + if (!CanModify()) { - ZIPTRACE("%s(%i) : ZipArchive should be opened first.\n"); - return false; - } - - if (m_storage.IsSegmented() != 0) - { - ZIPTRACE("%s(%i) : Cannot shift data for a segmented archive.\n"); - return false; - } - - if (m_iFileOpened) - { - ZIPTRACE("%s(%i) : A file should not be opened.\n"); return false; } @@ -1101,13 +1278,13 @@ bool CZipArchive::ShiftData(ZIP_SIZE_TYPE uOffset) return true; } -bool CZipArchive::PrependData(LPCTSTR lpszFilePath, LPCTSTR lpszNewExt) +bool CZipArchive::PrependData(LPCTSTR lpszFilePath, LPCTSTR lpszNewExt) { CZipFile file(lpszFilePath, CZipFile::modeRead | CZipFile::shareDenyNone); return PrependData(file, lpszNewExt); } -bool CZipArchive::PrependData(CZipAbstractFile& file, LPCTSTR lpszNewExt) +bool CZipArchive::PrependData(CZipAbstractFile& file, LPCTSTR lpszNewExt) { if (file.IsClosed()) { @@ -1145,25 +1322,28 @@ bool CZipArchive::PrependData(CZipAbstractFile& file, LPCTSTR lpszNewExt) } while (!bBreak); - if (m_storage.m_bInMemory || lpszNewExt == NULL) + if (lpszNewExt == NULL) return true; CZipString szInitialPath = m_storage.m_pFile->GetFilePath(); + if (szInitialPath.IsEmpty()) // is the case for in-memory archives + return true; // must close to rename +#pragma warning(suppress: 26444) Close(); CZipPathComponent zpc(szInitialPath); zpc.SetExtension(lpszNewExt); CZipString szNewPath = zpc.GetFullPath(); if (!ZipPlatform::RenameFile(szInitialPath, szNewPath, false)) return false; -#ifdef ZIP_ARCHIVE_LNX +#ifdef _ZIP_SYSTEM_LINUX return ZipPlatform::SetExeAttr(szNewPath); #else return true; #endif } -bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath, +bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath, int iComprLevel, bool bFullPath, int iSmartLevel, @@ -1177,7 +1357,7 @@ bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath, return AddNewFile(zanfi); } -bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath, +bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath, LPCTSTR lpszFileNameInZip, int iComprLevel, int iSmartLevel, @@ -1190,13 +1370,13 @@ bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath, return AddNewFile(zanfi); } -bool CZipArchive::AddNewFile(CZipMemFile& mf, +bool CZipArchive::AddNewFile(CZipAbstractFile& af, LPCTSTR lpszFileNameInZip, int iComprLevel, int iSmartLevel, unsigned long nBufSize) { - CZipAddNewFileInfo zanfi(&mf, lpszFileNameInZip); + CZipAddNewFileInfo zanfi(&af, lpszFileNameInZip); zanfi.m_iComprLevel = iComprLevel; zanfi.m_iSmartLevel = iSmartLevel; zanfi.m_nBufSize = nBufSize; @@ -1232,7 +1412,7 @@ private: CZipArchive* m_pZip; }; -bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) +bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) { // no need for ASSERT and TRACE here - it will be done by OpenNewFile @@ -1248,7 +1428,7 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) return false; } - bool bSegm = GetSegmMode() != 0; + bool bSegm = m_storage.IsSegmented(); // checking the replace index if (!UpdateReplaceIndex(info.m_uReplaceIndex)) @@ -1257,25 +1437,38 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) bool bReplace = info.m_uReplaceIndex != ZIP_FILE_INDEX_UNSPECIFIED; DWORD uAttr; - time_t ttime; + time_t tModificationTime = (time_t) 0, tCreationTime = time_t(0), tLastAccessTime = (time_t)0; if (info.m_pFile) { uAttr = ZipPlatform::GetDefaultAttributes(); - ttime = time(NULL); + tModificationTime = time(NULL); + if (m_bStoreFullFileTimes) + { + tCreationTime = tLastAccessTime = tModificationTime; + } } else { if (!ZipPlatform::GetFileAttr(info.m_szFilePath, uAttr)) - return false; // we don't know whether it is a file or a directory - ZipPlatform::GetFileModTime(info.m_szFilePath, ttime); + ThrowError(CZipException::fileError, info.m_szFilePath); // we don't know whether it is a file or a directory + if (m_bStoreFullFileTimes) + ZipPlatform::GetFileTimes(info.m_szFilePath, &tModificationTime, &tCreationTime, &tLastAccessTime); + else + ZipPlatform::GetFileTimes(info.m_szFilePath, &tModificationTime); } - CZipFileHeader header; - SetFileHeaderAttr(header, uAttr); + CZipFileHeader header; + header.SetSystemCompatibility(m_iArchiveSystCompatib); + header.SetSystemAttr(uAttr); if (info.m_szFileNameInZip.IsEmpty()) info.m_szFileNameInZip = PredictFileNameInZip(info.m_szFilePath, info.m_bFullPath, header.IsDirectory() ? prDir : prFile); header.SetFileName(info.m_szFileNameInZip); - header.SetTime(ttime); - bool bInternal = (info.m_iSmartLevel & zipsmInternal01) != 0; + header.SetModificationTime(tModificationTime, m_bStoreFullFileTimes, m_bUseUtcFileTimes); + if (m_bStoreFullFileTimes) + { + header.SetCreationTime(tCreationTime); + header.SetLastAccessTime(tLastAccessTime); + } + bool bInternal = info.m_iSmartLevel.IsSetAny(zipsmInternal01); if (header.IsDirectory()) // will never be when m_pFile is not NULL, so we don't check it { @@ -1292,7 +1485,7 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) // clear password for a directory CZipSmClrPass smcp; - if (info.m_iSmartLevel & zipsmCPassDir) + if (info.m_iSmartLevel.IsSetAny(zipsmCPassDir)) smcp.ClearPasswordSmartly(this); bool bRet = OpenNewFile(header, CZipCompressor::levelStore, NULL, info.m_uReplaceIndex); @@ -1305,10 +1498,15 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) } CZipSmClrPass smcp; + if (m_uCompressionMethod == CZipCompressor::methodStore) + { + info.m_iComprLevel = 0; + } + bool bIsCompression = info.m_iComprLevel != 0; - bool bEff = (info.m_iSmartLevel & zipsmCheckForEff)&& bIsCompression; - bool bCheckForZeroSized = (info.m_iSmartLevel & zipsmCPFile0) && WillEncryptNextFile(); - bool bCheckForSmallFiles = (info.m_iSmartLevel & zipsmNotCompSmall) && bIsCompression; + bool bEff = info.m_iSmartLevel.IsSetAny(zipsmCheckForEff)&& bIsCompression; + bool bCheckForZeroSized = info.m_iSmartLevel.IsSetAny(zipsmCPFile0) && WillEncryptNextFile(); + bool bCheckForSmallFiles = info.m_iSmartLevel.IsSetAny(zipsmNotCompSmall) && bIsCompression; ZIP_SIZE_TYPE uFileSize = ZIP_SIZE_TYPE(-1); bool bNeedTempArchive = (bEff && bSegm) || (bReplace && bIsCompression); if (bCheckForSmallFiles || bCheckForZeroSized || bNeedTempArchive) @@ -1319,7 +1517,7 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) else { if (!ZipPlatform::GetFileSize(info.m_szFilePath, uFileSize) && bEff) - bEff = false; // the file size is needed only when efficient in a segmented archive + uFileSize = ZIP_SIZE_TYPE(-1); } if (uFileSize != ZIP_SIZE_TYPE(-1)) @@ -1329,27 +1527,36 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) if (bCheckForSmallFiles && uFileSize < 5) info.m_iComprLevel = 0; } + else + { + bEff = false; + bNeedTempArchive = false; + if (bReplace && bIsCompression) + { + info.m_iComprLevel = 0; + bIsCompression = false; + } + } } - bool bEffInMem = bEff && (info.m_iSmartLevel & zipsmMemoryFlag); + bool bEffInMem = bEff && info.m_iSmartLevel.IsSetAny(zipsmMemoryFlag); CZipString szTempFileName; if (bNeedTempArchive && (bEffInMem || - !(szTempFileName = ZipPlatform::GetTmpFileName( - m_szTempPath.IsEmpty() ? NULL : (LPCTSTR)m_szTempPath, uFileSize) - ).IsEmpty())) + !(szTempFileName = ZipPlatform::GetTmpFileName(m_szTempPath.IsEmpty() ? NULL : (LPCTSTR)m_szTempPath, uFileSize)).IsEmpty())) { CZipMemFile* pmf = NULL; - CZipArchive zip; + CZipArchive zip; try { - // compress first to a temporary file, if ok - copy the data, if not - add storing - + // compress first to a temporary file, if ok - copy the data, if not - add storing if (bEffInMem) { pmf = new CZipMemFile; - zip.Open(*pmf, zipCreate); + if (!zip.Open(*pmf, zipCreate)) + return false; } - else - zip.Open(szTempFileName, zipCreate); + else if (!zip.Open(szTempFileName, zipCreate)) + return false; + zip.SetRootPath(m_szRootPath); zip.SetPassword(GetPassword()); zip.SetEncryptionMethod(m_iEncryptionMethod); @@ -1380,7 +1587,8 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) } catch (bool bRet) { - zip.Close(!bRet); // that doesn't really matter how it will be closed +#pragma warning(suppress: 26444) + zip.Close(bRet ? CZipArchive::afNoException : CZipArchive::afAfterException); // that doesn't really matter how it will be closed if (pmf) delete pmf; if (!bEffInMem) @@ -1390,7 +1598,8 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) } catch (...) { - zip.Close(true); +#pragma warning(suppress: 26444) + zip.Close(CZipArchive::afAfterException); if (pmf) delete pmf; if (!bEffInMem) @@ -1413,15 +1622,21 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) // cannot be shareDenyWrite // If you specify the GENERIC_READ and GENERIC_WRITE access modes along with the FILE_SHARE_READ and FILE_SHARE_WRITE sharing modes in your first call to CreateFile. If you specify the GENERIC_READ and GENERIC_WRITE access modes and the FILE_SHARE_READ sharing mode only in your second call to CreateFile, the function will fail with a sharing violation because the read-only sharing mode specified in the second call conflicts with the read/write access that has been granted in the first call. // Original information was here (but not any longer): http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/creating_and_opening_files.asp - if (!f.Open(info.m_szFilePath, CZipFile::modeRead | CZipFile::shareDenyNone, false)) + if (!f.Open(info.m_szFilePath, CZipFile::modeRead | CZipFile::shareDenyNone, true)) return false; pf = &f; } ASSERT(pf); - // call init before opening (in case of exception we have the names) - uFileSize = (ZIP_SIZE_TYPE)pf->GetLength(); + if (uFileSize == ZIP_SIZE_TYPE(-1)) + { + uFileSize = (ZIP_SIZE_TYPE)pf->GetLength(); + if (uFileSize == ZIP_SIZE_TYPE(-1)) + { + return false; + } + } // predict sizes in local header, so that zip64 can write extra header if needed header.m_uLocalUncomprSize = uFileSize; if (!bIsCompression) @@ -1451,12 +1666,12 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) pCallback->SetTotal(uFileSize); } - CZipAutoBuffer buf(info.m_nBufSize); + CZipAutoBuffer buf((DWORD)info.m_nBufSize); DWORD iRead; int iAborted = 0; do { - iRead = pf->Read(buf, info.m_nBufSize); + iRead = (DWORD)pf->Read(buf, (UINT)info.m_nBufSize); if (iRead) { WriteNewFile(buf, iRead); @@ -1487,7 +1702,7 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) if (pCallback) { if (!iAborted && !pCallback->RequestLastCallback()) - // temporaty value - safe abort + // temporary value - safe abort iAborted = CZipException::aborted; if (!iAborted) @@ -1500,12 +1715,11 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) // possible safe abort if (iAborted == CZipException::aborted) { - bool bRet; try { bRet = CloseNewFile(); } -#ifdef ZIP_ARCHIVE_MFC +#ifdef _ZIP_IMPL_MFC catch(CException* e) { e->Delete(); @@ -1548,7 +1762,7 @@ bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) return true; } -bool CZipArchive::RemoveLast(bool bRemoveAnyway) +bool CZipArchive::RemoveLast(bool bRemoveAnyway) { if (GetCount() == 0) return false; @@ -1587,13 +1801,15 @@ public: class CCalculateAddFilesEnumerator : public ZipArchiveLib::CDirEnumerator { CZipActionCallback* m_pCallback; + bool m_countDirectories; public: ZIP_FILE_USIZE m_uTotalBytes; ZIP_FILE_USIZE m_uTotalFiles; - CCalculateAddFilesEnumerator(LPCTSTR lpszDirectory, bool bRecursive, CZipActionCallback* pCallback) + CCalculateAddFilesEnumerator(LPCTSTR lpszDirectory, bool bRecursive, CZipActionCallback* pCallback, bool countDirectories) :ZipArchiveLib::CDirEnumerator(lpszDirectory, bRecursive) { m_pCallback = pCallback; + m_countDirectories = countDirectories; m_uTotalFiles = m_uTotalBytes = 0; } protected: @@ -1605,6 +1821,10 @@ protected: bool Process(LPCTSTR, const ZipArchiveLib::CFileInfo& info) { + // do not count directories + if (info.IsDirectory() && !m_countDirectories) + return true; + m_uTotalFiles++; m_uTotalBytes += info.m_uSize; if (m_pCallback && !m_pCallback->RequestCallback()) @@ -1627,40 +1847,7 @@ protected: } }; -class CAddFilesEnumerator : public ZipArchiveLib::CDirEnumerator -{ - CZipArchive* m_pZip; - CZipActionCallback* m_pMultiCallback; - int m_iComprLevel; - int m_iSmartLevel; - unsigned long m_nBufSize; -public: - CAddFilesEnumerator(LPCTSTR lpszDirectory, - bool bRecursive, - CZipArchive* pZip, - int iComprLevel, - int iSmartLevel, - unsigned long nBufSize, - CZipActionCallback* pMultiCallback) - :ZipArchiveLib::CDirEnumerator(lpszDirectory, bRecursive), m_pZip(pZip) - { - m_iComprLevel = iComprLevel; - m_nBufSize = nBufSize; - m_iSmartLevel = iSmartLevel; - m_pMultiCallback = pMultiCallback; - } -protected: - bool Process(LPCTSTR lpszPath, const ZipArchiveLib::CFileInfo&) - { - bool ret = m_pZip->AddNewFile(lpszPath, m_iComprLevel, m_pZip->GetRootPath().IsEmpty() != 0, m_iSmartLevel, m_nBufSize); - if (ret && m_pMultiCallback) - if (!m_pMultiCallback->MultiActionsNext()) - CZipException::Throw(CZipException::abortedSafely); - return ret; - } -}; - -bool CZipArchive::AddNewFiles(LPCTSTR lpszPath, +bool CZipArchive::AddNewFiles(LPCTSTR lpszPath, CFileFilter& filter, bool bRecursive, int iComprLevel, @@ -1668,16 +1855,35 @@ bool CZipArchive::AddNewFiles(LPCTSTR lpszPath, int iSmartLevel, unsigned long nBufSize) { + CZipAddFilesEnumerator addFilesEnumerator(lpszPath, bRecursive, iComprLevel, iSmartLevel, nBufSize); + return AddNewFiles(addFilesEnumerator, filter, bSkipInitialPath); +} + +bool CZipArchive::AddNewFiles(CZipAddFilesEnumerator& addFilesEnumerator, CFileFilter& filter, bool bSkipInitialPath) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + addFilesEnumerator.Initialize(this); CZipRootPathRestorer restorer; if (bSkipInitialPath) - restorer.SetNewRootPath(this, lpszPath); + { + CZipString directory = addFilesEnumerator.GetDirectory(); + if (directory.IsEmpty() || directory == _T(".")) // an empty directory is set to "." in the constructor of CDirEnumerator + restorer.SetNewRootPath(this, NULL); + else + restorer.SetNewRootPath(this, directory); + } CZipActionCallback* pMultiCallback = GetCallback(CZipActionCallback::cbMultiAdd); if (pMultiCallback) { // if multi callback is set, calculate total data to process // call cbCalculateForMulti in the meantime - CCalculateAddFilesEnumerator calculateEnumerator(lpszPath, bRecursive, GetCallback(CZipActionCallback::cbCalculateForMulti)); + CCalculateAddFilesEnumerator calculateEnumerator(addFilesEnumerator.GetDirectory(), addFilesEnumerator.IsRecursive(), + GetCallback(CZipActionCallback::cbCalculateForMulti), (addFilesEnumerator.m_iSmartLevel & CZipArchive::zipsmIgnoreDirectories) == 0); if (!calculateEnumerator.Start(filter)) return false; if (pMultiCallback->m_iType != CZipActionCallback::cbMultiAdd) @@ -1688,7 +1894,6 @@ bool CZipArchive::AddNewFiles(LPCTSTR lpszPath, try { - CAddFilesEnumerator addFilesEnumerator(lpszPath, bRecursive, this, iComprLevel, iSmartLevel, nBufSize, pMultiCallback); bool ret = addFilesEnumerator.Start(filter); if (pMultiCallback) pMultiCallback->MultiActionsEnd(); @@ -1703,17 +1908,17 @@ bool CZipArchive::AddNewFiles(LPCTSTR lpszPath, } -CZipString CZipArchive::GetArchivePath() const +CZipString CZipArchive::GetArchivePath() const { if (IsClosed(false)) { - ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + ZIPTRACE("%s(%i) : ZipArchive file is closed.\n"); return _T(""); } return m_storage.m_pFile->GetFilePath(); } -CZipString CZipArchive::GetGlobalComment() const +CZipString CZipArchive::GetGlobalComment() const { if (IsClosed()) { @@ -1725,53 +1930,27 @@ CZipString CZipArchive::GetGlobalComment() const return temp; } -bool CZipArchive::SetGlobalComment(LPCTSTR lpszComment) +bool CZipArchive::SetGlobalComment(LPCTSTR lpszComment, UINT codePage) { - if (IsClosed()) + if (!CanModify(true)) { - ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); return false; } - if (m_storage.IsSegmented() == -1) + if (codePage == ZIP_DEFAULT_CODE_PAGE) { - ZIPTRACE("%s(%i) : ZipArchive Library cannot modify the global comment of an existing segmented archive.\n"); - return false; + codePage = ZipCompatibility::GetDefaultCommentCodePage(m_iArchiveSystCompatib); } - - m_centralDir.SetComment(lpszComment); - if (m_bAutoFlush) - Flush(); - + m_centralDir.SetComment(lpszComment, codePage); + Finalize(true); return true; } - - -ZIP_VOLUME_TYPE CZipArchive::GetCurrentVolume() const +ZIP_VOLUME_TYPE CZipArchive::GetCurrentVolume() const { return (ZIP_VOLUME_TYPE)(m_storage.GetCurrentVolume() + 1); } -bool CZipArchive::SetFileComment(ZIP_INDEX_TYPE uIndex, LPCTSTR lpszComment) -{ - if (IsClosed()) - { - ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); - return false; - } - if (m_storage.IsSegmented() == -1) - { - ZIPTRACE("%s(%i) : ZipArchive Library cannot modify the file comment in an existing segmented archive.\n"); - return false; - } - - m_centralDir.SetFileComment(uIndex, lpszComment); - if (m_bAutoFlush) - Flush(); - return true; -} - -bool CZipArchive::SetPassword(LPCTSTR lpszPassword) +bool CZipArchive::SetPassword(LPCTSTR lpszPassword, UINT codePage) { if (m_iFileOpened != nothing) { @@ -1782,14 +1961,23 @@ bool CZipArchive::SetPassword(LPCTSTR lpszPassword) { ZIPTRACE("%s(%i) : Setting the password for a closed archive has no effect.\n"); } + if (lpszPassword) - ZipCompatibility::ConvertStringToBuffer(lpszPassword, m_pszPassword, CP_ACP); + { + if (codePage == ZIP_DEFAULT_CODE_PAGE) + { + codePage = ZipCompatibility::GetDefaultPasswordCodePage(m_iArchiveSystCompatib); + } + ZipCompatibility::ConvertStringToBuffer(lpszPassword, m_pszPassword, codePage); + } else + { m_pszPassword.Release(); + } return true; } -bool CZipArchive::SetEncryptionMethod(int iEncryptionMethod) +bool CZipArchive::SetEncryptionMethod(int iEncryptionMethod) { if (m_iFileOpened == compress) { @@ -1811,6 +1999,7 @@ struct CZipEncryptFileInfo m_uLocalSizeDiff = 0; m_uDescriptorSizeDiff = 0; m_uIndex = 0; + m_uUncompressedOffset = 0; } CZipEncryptFileInfo(CZipFileHeader* pHeader, DWORD uLocalSizeDiff, DWORD uDescriptorSizeDiff, ZIP_INDEX_TYPE uIndex, ZIP_SIZE_TYPE uDataOffset) @@ -1830,29 +2019,10 @@ struct CZipEncryptFileInfo } }; -bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) +bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) { - if (IsClosed()) - { - ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); - return false; - } - - if (m_storage.IsSegmented()) + if (!CanModify()) { - ZIPTRACE("%s(%i) : ZipArchive Library cannot encrypt existing files in a segmented archive.\n"); - return false; - } - - if (m_storage.m_bReadOnly) - { - ZIPTRACE("%s(%i) : ZipArchive Library cannot encrypt files in a read-only archive.\n"); - return false; - } - - if (m_iFileOpened) - { - ZIPTRACE("%s(%i) : ZipArchive Library cannot encrypt files if there is a file opened.\n"); return false; } @@ -1928,13 +2098,13 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) DWORD uOrigDescriptorSize = pHeader->GetDataDescriptorSize(&m_storage); pHeader->m_uEncryptionMethod = (BYTE)m_iEncryptionMethod; - pHeader->UpdateFlag(m_storage.IsSegmented() != 0); + pHeader->UpdateFlag(m_storage.IsSegmented()); // needed for GetLocalSize - pHeader->PrepareFileName(); + pHeader->PrepareStringBuffers(); DWORD uLocalDiff = pHeader->GetLocalSize(false) - uOrigSize; DWORD uDescriptorDiff = pHeader->GetDataDescriptorSize(&m_storage) - uOrigDescriptorSize; - uExtraData += uLocalDiff + uDescriptorDiff; + uExtraData += (ZIP_SIZE_TYPE)uLocalDiff + uDescriptorDiff; infos.Add(CZipEncryptFileInfo(pHeader, uLocalDiff, uDescriptorDiff, idx, pHeader->m_uOffset + uOrigSize)); if (pCallback && !pCallback->RequestCallback()) { @@ -2017,7 +2187,7 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) DWORD uExtraBefore = CZipCryptograph::GetEncryptedInfoSizeBeforeData(m_iEncryptionMethod); DWORD uExtraAfter = CZipCryptograph::GetEncryptedInfoSizeAfterData(m_iEncryptionMethod); // the total amount of extra data - uExtraData += ((uExtraBefore + uExtraAfter) * infos.GetSize()); + uExtraData += ((ZIP_SIZE_TYPE)uExtraBefore + uExtraAfter) * (DWORD)infos.GetSize(); ZIP_SIZE_TYPE uLastOffset = m_storage.GetLastDataOffset(); ZIP_INDEX_TYPE lastNormalIdx = ZIP_FILE_INDEX_UNSPECIFIED; @@ -2042,7 +2212,7 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) pCallback->CallbackEnd(); lastNormalIdx = ZIP_FILE_INDEX_UNSPECIFIED; } - uExtraData -= (uExtraAfter + info.m_uDescriptorSizeDiff); + uExtraData -= ((ZIP_SIZE_TYPE)uExtraAfter + info.m_uDescriptorSizeDiff); if (pCallback) { pCallback->Init(); @@ -2056,8 +2226,8 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) if (++idxIdx == uSize) break; - uExtraData -= (uExtraBefore + info.m_uLocalSizeDiff); - // use original offsett + uExtraData -= ((ZIP_SIZE_TYPE)uExtraBefore + info.m_uLocalSizeDiff); + // use original offset uLastOffset = info.m_pHeader->m_uOffset; // now change the offset (not counting expanded local header - it changed the offset of data, not the offset of local header) info.m_pHeader->m_uOffset += uExtraData; @@ -2071,7 +2241,7 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) } } bAborted = false; - ZIP_SIZE_TYPE uToEncrypt = 0; + ZIP_SIZE_TYPE uToEncrypt; i = uSize; // now encrypt the files (starting from the first one in the archive - this way the general direction of data copying is kept CreateCryptograph(m_iEncryptionMethod); @@ -2084,13 +2254,15 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) CZipEncryptFileInfo inf = infos[i]; CZipFileHeader* pHeader = inf.m_pHeader; uToEncrypt = pHeader->m_uComprSize; + const CZipString& szFileName = pHeader->GetFileName(); if (pCallback) { - pCallback->Init(pHeader->GetFileName()); + pCallback->Init(szFileName); pCallback->SetTotal(uToEncrypt); } m_storage.Seek(pHeader->m_uOffset); + pHeader->AdjustLocalComprSize(); pHeader->WriteLocal(&m_storage); // take the number of bytes to encode, before m_uComprSize is modified m_pCryptograph->InitEncode(m_pszPassword, *pHeader, m_storage); @@ -2117,7 +2289,7 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) bBreak = true; m_pCryptograph->Encode(buf, uSizeRead); - pFile->Seek(uPosition); + pFile->SafeSeek(uPosition); pFile->Write(buf, uSizeRead); uPosition += uSizeRead; @@ -2127,7 +2299,7 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) break; } if (pMultiCallback) - pMultiCallback->MultiActionsNext(); + pMultiCallback->MultiActionsNext(szFileName); } while (!bBreak); } @@ -2180,19 +2352,13 @@ bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) } - -bool CZipArchive::SetCompressionMethod(WORD uCompressionMethod) +bool CZipArchive::SetCompressionMethod(WORD uCompressionMethod) { if (m_iFileOpened == compress) { ZIPTRACE("%s(%i) : ZipArchive Library cannot change the compression method when there is a file opened for compression.\n"); return false; } - if (uCompressionMethod == CZipCompressor::methodStore) - { - ZIPTRACE("%s(%i) : Use the compression level CZipCompressor::levelStore when compressing files instead.\n"); - return false; - } if (!CZipCompressor::IsCompressionSupported(uCompressionMethod)) { @@ -2204,14 +2370,14 @@ bool CZipArchive::SetCompressionMethod(WORD uCompressionMethod) return true; } -CZipString CZipArchive::GetPassword()const +CZipString CZipArchive::GetPassword()const { CZipString temp; ZipCompatibility::ConvertBufferToString(temp, m_pszPassword, CP_ACP); return temp; } -bool CZipArchive::TestFile(ZIP_INDEX_TYPE uIndex, DWORD uBufSize) +bool CZipArchive::TestFile(ZIP_INDEX_TYPE uIndex, DWORD uBufSize) { if (IsClosed()) { @@ -2219,7 +2385,7 @@ bool CZipArchive::TestFile(ZIP_INDEX_TYPE uIndex, DWORD uBufSize) return false; } - if (m_storage.IsSegmented() == 1) + if (m_storage.IsNewSegmented()) { ZIPTRACE("%s(%i) : ZipArchive Library cannot test a segmented archive in creation.\n"); return false; @@ -2261,7 +2427,7 @@ bool CZipArchive::TestFile(ZIP_INDEX_TYPE uIndex, DWORD uBufSize) pCallback->SetTotal(pHeader->m_uUncomprSize); if (!OpenFile(uIndex)) - return false; + return false; CZipAutoBuffer buf(uBufSize); DWORD iRead; int iAborted = 0; @@ -2308,7 +2474,7 @@ bool CZipArchive::TestFile(ZIP_INDEX_TYPE uIndex, DWORD uBufSize) } // if any exception was thrown, then we are not successful // catch all exceptions to thrown aborted exception only -#ifdef ZIP_ARCHIVE_MFC +#ifdef _ZIP_IMPL_MFC catch(CException* e) { e->Delete(); @@ -2339,13 +2505,7 @@ bool CZipArchive::TestFile(ZIP_INDEX_TYPE uIndex, DWORD uBufSize) } } -void CZipArchive::SetFileHeaderAttr(CZipFileHeader& header, DWORD uAttr)const -{ - header.SetSystemCompatibility(m_iArchiveSystCompatib); - header.SetSystemAttr(uAttr); -} - -void CZipArchive::EnableFindFast(bool bEnable) +void CZipArchive::EnableFindFast(bool bEnable) { if (IsClosed()) { @@ -2355,7 +2515,7 @@ void CZipArchive::EnableFindFast(bool bEnable) m_centralDir.EnableFindFast(bEnable, m_bCaseSensitive); } -bool CZipArchive::SetSystemCompatibility(int iSystemComp) +bool CZipArchive::SetSystemCompatibility(int iSystemComp) { if (IsClosed()) { @@ -2371,15 +2531,17 @@ bool CZipArchive::SetSystemCompatibility(int iSystemComp) if (!ZipCompatibility::IsPlatformSupported(iSystemComp)) return false; +#ifdef _ZIP_UNICODE_CUSTOM // change the name coding page, if it was not changed before if (m_stringSettings.IsStandardNameCodePage(m_iArchiveSystCompatib)) m_stringSettings.SetDefaultNameCodePage(iSystemComp); +#endif m_iArchiveSystCompatib = iSystemComp; return true; } -void CZipArchive::SetRootPath(LPCTSTR szPath) +void CZipArchive::SetRootPath(LPCTSTR szPath) { if (IsClosed()) { @@ -2402,7 +2564,7 @@ void CZipArchive::SetRootPath(LPCTSTR szPath) m_szRootPath.Empty(); } -CZipString CZipArchive::TrimRootPath(CZipPathComponent &zpc)const +CZipString CZipArchive::TrimRootPath(CZipPathComponent &zpc)const { if (m_szRootPath.IsEmpty()) return zpc.GetFileName(); @@ -2410,13 +2572,23 @@ CZipString CZipArchive::TrimRootPath(CZipPathComponent &zpc)const return RemovePathBeginning(m_szRootPath, szPath, m_pZipCompare) ? szPath : zpc.GetFileName(); } -bool CZipArchive::RemovePathBeginning(LPCTSTR lpszBeginning, CZipString& szPath, ZIPSTRINGCOMPARE pCompareFunction) +bool CZipArchive::RemovePathBeginning(LPCTSTR lpszBeginning, CZipString& szPath, ZIPSTRINGCOMPARE pCompareFunction) { CZipString szBeginning(lpszBeginning); CZipPathComponent::RemoveSeparators(szBeginning); + CZipString szNormalizedPath(szPath); + + // normalize paths to allow using slashes in paths under Windows +#ifdef _ZIP_SYSTEM_WIN + // this is just for completness as it will always be a slash character under Windows + TCHAR cReversedSeparator = CZipPathComponent::m_cSeparator == _T('\\') ? _T('/') : _T('\\'); + szBeginning.Replace(cReversedSeparator, CZipPathComponent::m_cSeparator); + szNormalizedPath.Replace(cReversedSeparator, CZipPathComponent::m_cSeparator); +#endif + int iRootPathLength = szBeginning.GetLength(); - if (iRootPathLength && szPath.GetLength() >= iRootPathLength && - (szPath.Left(iRootPathLength).*pCompareFunction)(szBeginning) == 0) + if (iRootPathLength && szNormalizedPath.GetLength() >= iRootPathLength && + (szNormalizedPath.Left(iRootPathLength).*pCompareFunction)(szBeginning) == 0) { // the beginning is the same if (szPath.GetLength() == iRootPathLength) @@ -2437,7 +2609,7 @@ bool CZipArchive::RemovePathBeginning(LPCTSTR lpszBeginning, CZipString& szPath return false; } -void CZipArchive::SetTempPath(LPCTSTR lpszPath, bool bForce) +void CZipArchive::SetTempPath(LPCTSTR lpszPath, bool bForce) { m_szTempPath = lpszPath; if (lpszPath && bForce) @@ -2445,7 +2617,7 @@ void CZipArchive::SetTempPath(LPCTSTR lpszPath, bool bForce) CZipPathComponent::RemoveSeparators(m_szTempPath); } -CZipString CZipArchive::PredictFileNameInZip(LPCTSTR lpszFilePath, +CZipString CZipArchive::PredictFileNameInZip(LPCTSTR lpszFilePath, bool bFullPath, int iWhat)const { CZipString sz = lpszFilePath; @@ -2473,7 +2645,7 @@ CZipString CZipArchive::PredictFileNameInZip(LPCTSTR lpszFilePath, if (bFullPath) { - if (m_bRemoveDriveLetter) + if (m_bSafePaths) sz = zpc.GetNoDrive(); } else @@ -2484,97 +2656,142 @@ CZipString CZipArchive::PredictFileNameInZip(LPCTSTR lpszFilePath, return sz; } -CZipString CZipArchive::PredictExtractedFileName(LPCTSTR lpszFileNameInZip, LPCTSTR lpszPath, bool bFullPath, LPCTSTR lpszNewName)const +CZipString CZipArchive::PredictExtractedFileName(LPCTSTR lpszFileNameInZip, LPCTSTR lpszPath, bool bFullPath, LPCTSTR lpszNewName)const { CZipString szFile = lpszPath; - CZipString sz = lpszNewName ? lpszNewName : lpszFileNameInZip; + + CZipString sz; + if (lpszNewName) + sz = lpszNewName; + else + { + sz = lpszFileNameInZip; + if (m_bSafePaths) + { + CZipString szDirTraversal = _T(".."); + szDirTraversal += CZipPathComponent::m_cSeparator; + int current = 0; + while(true) + { + int start = sz.Find(szDirTraversal, current); + if (start > -1) + { + if (start == 0) + { + sz = sz.Mid(3); + } + else + { + sz = sz.Mid(0, start) + sz.Mid(start + 3); + } + } + else + break; + current = start; + } + CZipPathComponent::RemoveSeparatorsLeft(sz); + } + } if (sz.IsEmpty()) return szFile; if (!szFile.IsEmpty()) CZipPathComponent::AppendSeparator(szFile); - // remove for CZipPathComponent treating last name as a file even if dir CZipPathComponent::RemoveSeparators(sz); CZipPathComponent zpc(sz); - szFile += bFullPath ? (m_bRemoveDriveLetter ? zpc.GetNoDrive() : sz) + szFile += bFullPath ? (m_bSafePaths ? zpc.GetNoDrive() : sz) : TrimRootPath(zpc); return szFile; } -ZIP_SIZE_TYPE CZipArchive::PredictMaximumFileSizeInArchive(LPCTSTR lpszFilePath, bool bFullPath) const +ZIP_SIZE_TYPE CZipArchive::PredictMaximumFileSizeInArchive(LPCTSTR lpszFilePath, bool bFullPath) { DWORD attr; if (!ZipPlatform::GetFileAttr(lpszFilePath, attr)) - return 0; + ThrowError(CZipException::fileError, lpszFilePath); CZipFileHeader fh; - SetFileHeaderAttr(fh, attr); + + fh.SetSystemAttr(attr); if (!fh.IsDirectory()) - if (!ZipPlatform::GetFileSize(lpszFilePath, fh.m_uUncomprSize)) - return 0; - fh.SetFileName(PredictFileNameInZip(lpszFilePath, bFullPath, fh.IsDirectory() ? prDir : prFile)); + if (!ZipPlatform::GetFileSize(lpszFilePath, fh.m_uComprSize)) + return 0; + fh.SetFileName(PredictFileNameInZip(lpszFilePath, bFullPath, fh.IsDirectory() ? prDir : prFile)); return PredictMaximumFileSizeInArchive(fh); } -ZIP_SIZE_TYPE CZipArchive::PredictMaximumFileSizeInArchive(CZipFileHeader& fh) const -{ - fh.m_stringSettings = m_stringSettings; +ZIP_SIZE_TYPE CZipArchive::PredictMaximumFileSizeInArchive(CZipFileHeader& fh) +{ + fh.m_pCentralDir = &m_centralDir; + fh.SetSystemCompatibility(GetSystemCompatibility()); + fh.UpdateStringsFlags(false); fh.m_uEncryptionMethod = WillEncryptNextFile() ? (BYTE)m_iEncryptionMethod : (BYTE)CZipCryptograph::encNone; fh.m_uMethod = CZipCompressor::methodStore; - fh.PrepareData(CZipCompressor::levelStore, m_storage.IsSegmented() != 0); + fh.PrepareData(CZipCompressor::levelStore, m_storage.IsSegmented()); DWORD uLocalSize = fh.GetLocalSize(true); - return fh.GetSize() + uLocalSize + fh.GetDataSize(true, false) + fh.GetDataDescriptorSize(&m_storage); + ZIP_SIZE_TYPE ret = (ZIP_SIZE_TYPE)fh.GetSize() + uLocalSize + fh.GetDataSize(false) + fh.GetDataDescriptorSize(&m_storage); + fh.m_pCentralDir = NULL; + return ret; } -void CZipArchive::SetAutoFlush(bool bAutoFlush) +bool CZipArchive::SetAutoFinalize(bool bAutoFinalize) { - if (IsClosed()) + if (!CanModify(false, false)) { - ZIPTRACE("%s(%i) : ZipArchive should be opened first.\n"); - return; + return false; } - if (m_storage.IsSegmented() != 0) + if (m_bAutoFinalize != bAutoFinalize) { - ZIPTRACE("%s(%i) : Cannot set auto-flush for a segmented archive.\n"); - return; + if (bAutoFinalize) + { + if (IsModified()) + { + ZIPTRACE("%s(%i) : Cannot enable auto finalize when there are pending changes to commit.\n"); + return false; + } + } + m_bAutoFinalize = bAutoFinalize; } - m_bAutoFlush = bAutoFlush; + return true; } -void CZipArchive::Flush() +bool CZipArchive::Finalize(bool bOnlyIfAuto) { - if (IsClosed()) + if (bOnlyIfAuto && !m_bAutoFinalize) { - ZIPTRACE("%s(%i) : ZipArchive should be opened first.\n"); - return; + return false; } - if (m_storage.IsSegmented() < 0) + if (!CanModify(true, false)) { - ZIPTRACE("%s(%i) : Cannot flush an existing segmented archive.\n"); - return; + return false; + } + + if (IsModified()) + { + ZIPTRACE("%s(%i) : Cannot finalize when there are pending changes to commit.\n"); + return false; } WriteCentralDirectory(); m_storage.FlushFile(); - if (m_storage.IsSegmented() > 0) // try to finalize a segmented archive without closing it + if (m_storage.IsNewSegmented()) // try to finalize a segmented archive without closing it m_storage.FinalizeSegm(); + return true; } - -void CZipArchive::GetCentralDirInfo(CZipCentralDir::CInfo& info)const +void CZipArchive::GetCentralDirInfo(CZipCentralDir::CInfo& info)const { if (IsClosed()) { ZIPTRACE("%s(%i) : ZipArchive should be opened first.\n"); - return; - + return; } m_centralDir.GetInfo(info); - if (GetSegmMode() > 0) + if (m_storage.IsNewSegmented() && !m_storage.IsBinarySplit()) info.m_uLastVolume = m_storage.GetCurrentVolume(); } -void CZipArchive::FindMatches(LPCTSTR lpszPattern, CZipIndexesArray &ar, bool bFullPath) +void CZipArchive::FindMatches(LPCTSTR lpszPattern, CZipIndexesArray &ar, bool bFullPath) { if (IsClosed()) { @@ -2582,7 +2799,7 @@ void CZipArchive::FindMatches(LPCTSTR lpszPattern, CZipIndexesArray &ar, bool b return; } - // ar.RemoveAll(); don't do this + // ar.RemoveAll(); don't remove ZIP_INDEX_TYPE uCount = (ZIP_INDEX_TYPE)GetCount(); CWildcard wc(lpszPattern, m_bCaseSensitive); for (ZIP_INDEX_TYPE i = 0; i < uCount; i++) @@ -2599,7 +2816,7 @@ void CZipArchive::FindMatches(LPCTSTR lpszPattern, CZipIndexesArray &ar, bool b } } -ZIP_INDEX_TYPE CZipArchive::WillBeDuplicated(LPCTSTR lpszFilePath, bool bFullPath, bool bFileNameOnly , int iWhat) +ZIP_INDEX_TYPE CZipArchive::WillBeDuplicated(LPCTSTR lpszFilePath, bool bFullPath, bool bFileNameOnly , int iWhat) { CZipString szFile; if (bFileNameOnly) @@ -2612,8 +2829,7 @@ ZIP_INDEX_TYPE CZipArchive::WillBeDuplicated(LPCTSTR lpszFilePath, bool bFullPa return FindFile(szFile, ffDefault, bFileNameOnly); } - -bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCTSTR lpszNewFileName, ZIP_INDEX_TYPE uReplaceIndex, bool bKeepSystComp, CZipActionCallback* pCallback) +bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCTSTR lpszNewFileName, ZIP_INDEX_TYPE uReplaceIndex, bool bKeepSystComp, CZipActionCallback* pCallback) { if (this == &zip) { @@ -2633,7 +2849,7 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT return false; } - if (m_storage.IsSegmented() == -1) + if (m_storage.IsExistingSegmented()) { ZIPTRACE("%s(%i) : ZipArchive Library cannot add files to an existing segmented archive.\n"); return false; @@ -2641,7 +2857,7 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT ASSERT(m_pBuffer.GetSize() > 0); - bool bSegm = m_storage.IsSegmented() == 1; + bool bSegm = m_storage.IsSegmented(); if (!zip.m_centralDir.IsValidIndex(uIndex)) return false; @@ -2652,24 +2868,42 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT // we need a copy - we are going to modify this if (!zip.GetFileInfo(originalHeader, uIndex)) return false; - if (zip.m_storage.IsSegmented() != 0 && originalHeader.m_uLocalComprSize == 0) - // we must compensate for adding the encrypted info size to m_uLocalComprSize at the beginning of CZipFileHeader::WriteLocal() - originalHeader.m_uLocalComprSize = originalHeader.m_uComprSize - originalHeader.GetEncryptedInfoSize(); - bool bConvertSystem = !bKeepSystComp && originalHeader.GetSystemCompatibility() != m_iArchiveSystCompatib; + + if (originalHeader.IsDataDescriptor()) + { + if (originalHeader.m_uLocalComprSize == 0) + { + originalHeader.m_uLocalComprSize = originalHeader.m_uComprSize; + } + + if (originalHeader.m_uLocalUncomprSize == 0) + { + originalHeader.m_uLocalUncomprSize = originalHeader.m_uUncomprSize; + } + } + + DWORD uSize = originalHeader.GetEncryptedInfoSize(); + // just in case checking + if (uSize > 0 && originalHeader.m_uLocalComprSize >= uSize) + { + // to compensate increasing in PrepareData + originalHeader.m_uLocalComprSize -= uSize; + } + CZipString szFileName; if (lpszNewFileName != NULL) { szFileName = lpszNewFileName; + // to avoid calling on name change event + originalHeader.m_pCentralDir = NULL; originalHeader.SetFileName(lpszNewFileName); } else szFileName = originalHeader.GetFileName(); // empty - if (bConvertSystem) + if (bKeepSystComp) { - DWORD uAttr = originalHeader.GetSystemAttr(); - originalHeader.SetSystemCompatibility(m_iArchiveSystCompatib); - originalHeader.SetSystemAttr(uAttr); + originalHeader.SetSystemCompatibility(m_iArchiveSystCompatib, true); } if (!UpdateReplaceIndex(uReplaceIndex)) @@ -2679,10 +2913,15 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT if (bReplace && bSegm) return false; - int iCallbackType = 0; + int iCallbackType = CZipActionCallback::cbNothing; if (pCallback) iCallbackType = pCallback->m_iType; + // if the original header had no data descriptor it used an original CRC32 during writing CRC encryption header + // we need to skip the data descriptor as well, otherwise during decrypting, the decryptor will verify against + // a pseudo-crc (see InitDecode in CZipCrc32Cryptograph); + bool clearDataDescriptor = !originalHeader.IsDataDescriptor() && originalHeader.GetEncryptionMethod() == CZipCryptograph::encStandard; + if (!originalHeader.IsEncrypted() && WillEncryptNextFile()) { originalHeader.m_uEncryptionMethod = (BYTE)m_iEncryptionMethod; @@ -2694,10 +2933,12 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT // if the same callback is applied to cbMoveData, then the previous information about the type will be lost // we restore it later CZipFileHeader* pHeader = m_centralDir.AddNewFile(originalHeader, uReplaceIndex, originalHeader.GetCompressionLevel(), true); - pHeader->m_stringSettings.m_uCommentCodePage = originalHeader.m_stringSettings.m_uCommentCodePage; - + if (clearDataDescriptor && pHeader->IsDataDescriptor()) // the second condition is just in case + { + pHeader->m_uFlag &= ~8; + } // prepare this here: it will be used for GetLocalSize and WriteLocal - pHeader->PrepareFileName(); + pHeader->PrepareStringBuffers(); ZIP_SIZE_TYPE uTotalToMove = pHeader->m_uComprSize; @@ -2706,7 +2947,7 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT ZIP_SIZE_TYPE uDataSize; if (m_pCryptograph) // the file will be encrypted and have not yet increased pHeader->m_uComprSize (in m_pCryptograph->InitEncode) - uDataSize = pHeader->GetDataSize(false, false); + uDataSize = pHeader->GetDataSize(false); else uDataSize = uTotalToMove; MakeSpaceForReplace(uReplaceIndex, uDataSize + pHeader->GetLocalSize(false) + pHeader->GetDataDescriptorSize(&m_storage), szFileName); @@ -2762,7 +3003,13 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT iAborted = CZipException::abortedAction; } else + { + if (m_pCryptograph) + m_pCryptograph->FinishEncode(*pHeader, m_storage); + // it will be written only if needed + pHeader->WriteDataDescriptor(&m_storage); iAborted = CZipException::abortedSafely; + } break; } @@ -2772,8 +3019,18 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT if (pCallback) { if (!iAborted && !pCallback->RequestLastCallback()) - iAborted = CZipException::abortedSafely; - + { + if (uTotalToMove > 0) + iAborted = CZipException::abortedAction; + else + { + if (m_pCryptograph) + m_pCryptograph->FinishEncode(*pHeader, m_storage); + // it will be written only if needed + pHeader->WriteDataDescriptor(&m_storage); + iAborted = CZipException::abortedSafely; + } + } if (iAborted) { // when no exception, CallbackEnd() called later @@ -2784,7 +3041,6 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT } m_centralDir.m_pOpenedFile = NULL; - // copying from a not segmented to a segmented archive so add the data descriptor // we want write the additional data only if everything is all right, but we don't want to flush the storage before // (and we want to flush the storage before throwing an exception, if something is wrong) @@ -2807,7 +3063,7 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCT return true; } -bool CZipArchive::GetFromArchive( CZipArchive& zip, CZipIndexesArray &aIndexes, bool bKeepSystComp) +bool CZipArchive::GetFromArchive( CZipArchive& zip, CZipIndexesArray &aIndexes, bool bKeepSystComp, bool bReplaceMode) { aIndexes.Sort(true); ZIP_INDEX_TYPE uFiles = (ZIP_INDEX_TYPE)aIndexes.GetSize(); @@ -2817,7 +3073,14 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, CZipIndexesArray &aIndexes, for (ZIP_INDEX_TYPE i = 0; i < uFiles; i++) { ZIP_INDEX_TYPE uFileIndex = aIndexes[(ZIP_ARRAY_SIZE_TYPE)i]; - if (!GetFromArchive(zip, uFileIndex, NULL, ZIP_FILE_INDEX_UNSPECIFIED, bKeepSystComp, GetCallback(CZipActionCallback::cbGet))) + ZIP_INDEX_TYPE uReplaceIndex = ZIP_FILE_INDEX_UNSPECIFIED; + if (bReplaceMode) + { + if (!zip.m_centralDir.IsValidIndex(uFileIndex)) + return false; + uReplaceIndex = FindFile(zip[uFileIndex]->GetFileName()); + } + if (!GetFromArchive(zip, uFileIndex, NULL, uReplaceIndex, bKeepSystComp, GetCallback(CZipActionCallback::cbGet))) { ReleaseBuffer(); return false; @@ -2830,122 +3093,195 @@ bool CZipArchive::GetFromArchive( CZipArchive& zip, CZipIndexesArray &aIndexes, throw; } ReleaseBuffer(); - if (m_bAutoFlush) - Flush(); + Finalize(true); return true; } -bool CZipArchive::RenameFile(ZIP_INDEX_TYPE uIndex, LPCTSTR lpszNewName) -{ - if (IsClosed()) - { - ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); - return false; - } - - if (m_storage.IsSegmented()) + +struct CZipChangeInfo +{ + CZipChangeInfo() { - ZIPTRACE("%s(%i) : ZipArchive Library cannot rename files in a segmented archive.\n"); - return false; + m_index = 0; + m_startOffset = 0; + m_endOffset = 0; + m_relativeOffset = 0; } - - if (m_iFileOpened) + + CZipChangeInfo(ZIP_INDEX_TYPE index, + ZIP_SIZE_TYPE startOffset, + ZIP_FILE_SIZE relativeOffset) { - ZIPTRACE("%s(%i) : ZipArchive Library cannot rename a file if there is a file opened.\n"); - return false; + m_index = index; + m_startOffset = startOffset; + m_endOffset = 0; + m_relativeOffset = relativeOffset; } - CZipFileHeader* pHeader = (*this)[uIndex]; - if (pHeader == NULL) + + ZIP_INDEX_TYPE m_index; + ZIP_SIZE_TYPE m_startOffset; + ZIP_SIZE_TYPE m_endOffset; + ZIP_FILE_SIZE m_relativeOffset; +}; + +bool CZipArchive::CommitChanges() +{ + if (!CanModify()) return false; - CZipString szNewName(lpszNewName); - if (pHeader->IsDirectory()) - CZipPathComponent::AppendSeparator(szNewName); - else - CZipPathComponent::RemoveSeparators(szNewName); - CZipString szPreviousFileName = pHeader->GetFileName(); - if (szPreviousFileName.Collate(szNewName) == 0) + ZIP_INDEX_TYPE uCount = GetCount(); + if (uCount == 0) return true; + + CZipArray aInfo; + ZIP_FILE_SIZE relativeOffset = 0; + for (ZIP_INDEX_TYPE i = 0; i < uCount; i++) + { + CZipFileHeader* pHeader = GetFileInfo(i); + if (!pHeader->IsModified()) + continue; + + ReadLocalHeaderInternal(i); + // used in GetLocalSize and WriteLocal + pHeader->PrepareStringBuffers(); + DWORD localSize = pHeader->GetLocalSize(true); + + ASSERT(pHeader->m_fileName.m_buffer.IsAllocated()); + DWORD newLocalSize = pHeader->GetLocalSize(false); + int offset = newLocalSize - localSize; + relativeOffset += offset; + + aInfo.Add(CZipChangeInfo(i, pHeader->m_uOffset + localSize, relativeOffset)); + } - // don't copy the comment code page, it already could have been set using it's own code page (set CD CP, change comment, change CD CP) - pHeader->m_stringSettings.m_bStoreNameInExtraData = m_stringSettings.m_bStoreNameInExtraData; - pHeader->m_stringSettings.m_uNameCodePage = m_stringSettings.m_uNameCodePage; - pHeader->SetFileName(szNewName); + + ZIP_ARRAY_SIZE_TYPE totalInfos = aInfo.GetSize(); + if (totalInfos == 0) + return true; m_centralDir.RemoveFromDisk(); // does m_storage.Flush(); - // read local data - it may differ from central data - char localInfo[4]; - m_storage.Seek(pHeader->m_uOffset + 26); - m_storage.m_pFile->Read(localInfo, 4); // read at once - WORD uFileNameLen, uExtraFieldSize; - CBytesWriter::ReadBytes(uFileNameLen, localInfo); - // skip endian issues - the variable will not be used, but written back as it is - memcpy(&uExtraFieldSize, localInfo + 2, 2); - - // filename buffer already cleared (GetFileName), the conversion will take place - pHeader->PrepareFileName(); - ASSERT(pHeader->m_pszFileNameBuffer.IsAllocated()); - WORD uNewFileNameLen = (WORD)pHeader->m_pszFileNameBuffer.GetSize(); - int iDelta = uNewFileNameLen - uFileNameLen; - int iOffset = 0; - CZipAutoBuffer buf, *pBuf; - - if (iDelta != 0) - { - // we need to make more or less space - InitBuffer(); - ZIP_SIZE_TYPE uStartOffset = pHeader->m_uOffset + 30 + uFileNameLen; - ZIP_SIZE_TYPE uFileLen = (ZIP_SIZE_TYPE)m_storage.m_pFile->GetLength(); - ZIP_SIZE_TYPE uEndOffset = uFileLen - m_storage.m_uBytesBeforeZip; - CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbRename); - if (pCallback) + + ZIP_SIZE_TYPE uFileLen = (ZIP_SIZE_TYPE)m_storage.m_pFile->GetLength(); + ZIP_ARRAY_SIZE_TYPE lastInfoIndex = totalInfos - 1; + ZIP_SIZE_TYPE uTotal = 0; + ZIP_ARRAY_SIZE_TYPE idx = 0; + // calculate end offsets and total data to process + for(;;) + { + bool last; + ZIP_SIZE_TYPE uEndOffset; + if (idx == lastInfoIndex) + { + uEndOffset = uFileLen - m_storage.m_uBytesBeforeZip; + last = true; + } + else { - // do it right and sent the notification - pCallback->Init(szPreviousFileName, GetArchivePath()); - pCallback->SetTotal(uEndOffset - uStartOffset); + uEndOffset = GetFileInfo(aInfo.GetAt(idx + 1).m_index)->m_uOffset; + last = false; } - bool bForward = iDelta > 0; - if (bForward) - m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen + iDelta)); // ensure the seek is correct + CZipChangeInfo& info = aInfo[idx]; + info.m_endOffset = uEndOffset; + uTotal += uEndOffset - info.m_startOffset; + if (last) + break; + idx++; + } + + ZIP_ARRAY_SIZE_TYPE currentIndex = lastInfoIndex; + // the total offset is the last offset in the array (offsets are accumulated) + ZIP_FILE_SIZE totalOffset = aInfo.GetAt(currentIndex).m_relativeOffset; + if (totalOffset > 0) + { + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen + totalOffset)); // ensure the seek is correct + } + + InitBuffer(); + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbModify); + if (pCallback) + { + // do it right and sent the notification + pCallback->Init(NULL, GetArchivePath()); + pCallback->SetTotal(uTotal); + } + for(;;) + { + CZipChangeInfo& info = aInfo[currentIndex]; + bool last; + + if (info.m_relativeOffset > 0) + { + last = currentIndex == 0; + MovePackedFiles(info.m_startOffset, info.m_endOffset, (ZIP_SIZE_TYPE)info.m_relativeOffset, pCallback, true, last); + } + else + { + ZIP_ARRAY_SIZE_TYPE limitIndex = currentIndex; + while (currentIndex > 0 && aInfo.GetAt(currentIndex - 1).m_relativeOffset <= 0) + { + currentIndex--; + } - MovePackedFiles(uStartOffset, uEndOffset, abs(iDelta), pCallback, bForward); - if (pCallback) - pCallback->CallbackEnd(); + last = currentIndex == 0; + + for(ZIP_ARRAY_SIZE_TYPE uSubIndex = currentIndex; ; uSubIndex++) + { + CZipChangeInfo& newInfo = aInfo[uSubIndex]; + bool subLast = uSubIndex == limitIndex; + MovePackedFiles(newInfo.m_startOffset, newInfo.m_endOffset, (ZIP_SIZE_TYPE)(-newInfo.m_relativeOffset), pCallback, false, last && subLast); + if (subLast) + break; + } + } - if (!bForward) - m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen + iDelta)); // delta < 0; shrink the file + if (last) + break; - ReleaseBuffer(); + currentIndex--; + } + ReleaseBuffer(); - ZIP_INDEX_TYPE uSize = (ZIP_INDEX_TYPE)GetCount(); - for (ZIP_INDEX_TYPE i = (ZIP_INDEX_TYPE)(uIndex + 1); i < uSize; i++) - m_centralDir[i]->m_uOffset += iDelta; - buf.Allocate(4 + uNewFileNameLen); - CBytesWriter::WriteBytes(buf, uNewFileNameLen); - // the variable was not used - we write it back as it was (no endian issues) - // to write everything at once - memcpy((char*)buf + 2, &uExtraFieldSize, 2); - memcpy((char*)buf + 4, pHeader->m_pszFileNameBuffer, uNewFileNameLen); - pBuf = &buf; - iOffset = -4; + if (totalOffset < 0) + { + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen + totalOffset)); // totalOffset < 0; shrink the file + } + + for (idx = 0; ; idx++) + { + bool last = idx == lastInfoIndex; + // update offsets of the files that lie in between (will be written to central headers only) + CZipChangeInfo& info = aInfo[idx]; + CZipFileHeader* pHeader = GetFileInfo(info.m_index); + ZIP_INDEX_TYPE endIndex = last ? GetCount() : aInfo.GetAt(idx + 1).m_index; +#ifdef _ZIP_STRICT_U16 + if (info.m_index < endIndex) // the index type is unsigned +#endif + for (ZIP_INDEX_TYPE uSubIndex = (ZIP_INDEX_TYPE)(info.m_index + 1); uSubIndex < endIndex; uSubIndex ++) + { + GetFileInfo(uSubIndex)->m_uOffset += (ZIP_SIZE_TYPE)info.m_relativeOffset; + } + relativeOffset = idx == 0 ? 0 : aInfo.GetAt(idx - 1).m_relativeOffset; + m_storage.Seek(pHeader->m_uOffset + relativeOffset); + pHeader->WriteLocal(&m_storage); // will update offset and local filename size + pHeader->SetModified(false); + m_storage.Flush(); + if (last) + break; } - else - pBuf = &pHeader->m_pszFileNameBuffer; - - m_storage.Seek(pHeader->m_uOffset + 30 + iOffset); - m_storage.m_pFile->Write(*pBuf, pBuf->GetSize()); - m_centralDir.RebuildFindFastArray(); - if (m_bAutoFlush) - Flush(); + Finalize(true); + + if (pCallback) + pCallback->CallbackEnd(); + return true; } -bool CZipArchive::UpdateReplaceIndex(ZIP_INDEX_TYPE& uReplaceIndex) +bool CZipArchive::UpdateReplaceIndex(ZIP_INDEX_TYPE& uReplaceIndex) { if (uReplaceIndex == ZIP_FILE_INDEX_UNSPECIFIED) return true; - if (GetSegmMode() != 0) + if (m_storage.IsSegmented()) { ZIPTRACE("%s(%i) : ZipArchive Library cannot replace files in a segmented archive.\n"); return false; @@ -2964,7 +3300,7 @@ bool CZipArchive::UpdateReplaceIndex(ZIP_INDEX_TYPE& uReplaceIndex) return true; } -void CZipArchive::MakeSpaceForReplace(ZIP_INDEX_TYPE uReplaceIndex, ZIP_SIZE_TYPE uTotal, LPCTSTR lpszFileName) +void CZipArchive::MakeSpaceForReplace(ZIP_INDEX_TYPE uReplaceIndex, ZIP_SIZE_TYPE uTotal, LPCTSTR lpszFileName) { ASSERT(uReplaceIndex < GetCount() - 1); // we can't use the offset from the central directory here, because the header is already replaced @@ -3022,53 +3358,55 @@ void CZipArchive::MakeSpaceForReplace(ZIP_INDEX_TYPE uReplaceIndex, ZIP_SIZE_TY pCallback->CallbackEnd(); } -void CZipArchive::MovePackedFiles(ZIP_SIZE_TYPE uStartOffset, ZIP_SIZE_TYPE uEndOffset, ZIP_SIZE_TYPE uMoveBy, +void CZipArchive::MovePackedFiles(ZIP_SIZE_TYPE uStartOffset, ZIP_SIZE_TYPE uEndOffset, ZIP_SIZE_TYPE uMoveBy, CZipActionCallback* pCallback, bool bForward, bool bLastCall) { ASSERT(m_pBuffer.GetSize() > 0); - ZIP_SIZE_TYPE uTotalToMove = uEndOffset - uStartOffset; - ZIP_SIZE_TYPE uPack = uTotalToMove > m_pBuffer.GetSize() ? m_pBuffer.GetSize() : uTotalToMove; - char* buf = (char*)m_pBuffer; - - - UINT uSizeRead; - bool bBreak = false; bool bAborted = false; - do + if (uMoveBy > 0) { - if (uEndOffset - uStartOffset < uPack) + ZIP_SIZE_TYPE uTotalToMove = uEndOffset - uStartOffset; + ZIP_SIZE_TYPE uPack = uTotalToMove > m_pBuffer.GetSize() ? m_pBuffer.GetSize() : uTotalToMove; + char* buf = (char*)m_pBuffer; + + UINT uSizeRead; + bool bBreak = false; + do { - uPack = uEndOffset - uStartOffset; - if (!uPack) + if (uEndOffset - uStartOffset < uPack) + { + uPack = uEndOffset - uStartOffset; + if (!uPack) + break; + bBreak = true; + + } + ZIP_SIZE_TYPE uPosition = bForward ? uEndOffset - uPack : uStartOffset; + + m_storage.Seek(uPosition); + uSizeRead = (UINT)m_storage.m_pFile->Read(buf, (UINT)uPack); + if (!uSizeRead) break; - bBreak = true; + if (bForward) + uPosition += uMoveBy; + else + uPosition -= uMoveBy; + m_storage.Seek(uPosition); + m_storage.m_pFile->Write(buf, uSizeRead); + if (bForward) + uEndOffset -= uSizeRead; + else + uStartOffset += uSizeRead; + if (pCallback && !pCallback->RequestCallback(uSizeRead)) + { + bAborted = true; + break; + } } - ZIP_SIZE_TYPE uPosition = bForward ? uEndOffset - uPack : uStartOffset; - - m_storage.Seek(uPosition); - uSizeRead = m_storage.m_pFile->Read(buf, (UINT)uPack); - if (!uSizeRead) - break; - - if (bForward) - uPosition += uMoveBy; - else - uPosition -= uMoveBy; - m_storage.Seek(uPosition); - m_storage.m_pFile->Write(buf, uSizeRead); - if (bForward) - uEndOffset -= uSizeRead; - else - uStartOffset += uSizeRead; - if (pCallback && !pCallback->RequestCallback(uSizeRead)) - { - bAborted = true; - break; - } + while (!bBreak); } - while (!bBreak); if (pCallback) { @@ -3078,19 +3416,18 @@ void CZipArchive::MovePackedFiles(ZIP_SIZE_TYPE uStartOffset, ZIP_SIZE_TYPE uEn //pCallback->CallbackEnd(); if (bAborted) { - // call here before throwing the the aborted exception + // call here before throwing the aborted exception pCallback->CallbackEnd(); ThrowError(CZipException::abortedAction); } } - if (uEndOffset != uStartOffset) + if (uMoveBy > 0 && uEndOffset != uStartOffset) ThrowError(CZipException::internalError); } - -bool CZipArchive::RemoveCentralDirectoryFromArchive() +bool CZipArchive::RemoveCentralDirectoryFromArchive() { if (IsClosed()) { @@ -3107,7 +3444,7 @@ bool CZipArchive::RemoveCentralDirectoryFromArchive() return true; } -bool CZipArchive::OverwriteLocalHeader(ZIP_INDEX_TYPE uIndex) +bool CZipArchive::OverwriteLocalHeader(ZIP_INDEX_TYPE uIndex) { if (IsClosed()) { @@ -3122,12 +3459,14 @@ bool CZipArchive::OverwriteLocalHeader(ZIP_INDEX_TYPE uIndex) } CZipFileHeader* pHeader = GetFileInfo(uIndex); - m_storage.Seek(pHeader->m_uOffset); + if (!pHeader) + return false; + m_storage.Seek(pHeader->m_uOffset); pHeader->WriteLocal(&m_storage); return true; } -bool CZipArchive::ReadLocalHeader(ZIP_INDEX_TYPE uIndex) +bool CZipArchive::ReadLocalHeader(ZIP_INDEX_TYPE uIndex) { if (IsClosed()) { @@ -3143,10 +3482,35 @@ bool CZipArchive::ReadLocalHeader(ZIP_INDEX_TYPE uIndex) return true; } -void CZipArchive::SetSegmCallback(CZipSegmCallback* pCallback, int callbackType) +void CZipArchive::SetSegmCallback(CZipSegmCallback* pCallback, int callbackType) { - if ((callbackType & scSpan) != 0) + CBitFlag flag = callbackType; + if (flag.IsSetAny(scSpan)) m_storage.m_pSpanChangeVolumeFunc = pCallback; - if ((callbackType & scSplit) != 0) + if (flag.IsSetAny(scSplit)) m_storage.m_pSplitChangeVolumeFunc = pCallback; } + + +bool CZipArchive::ResetCurrentVolume() +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + + // the second condition is just in case + if (!m_storage.IsExistingSegmented() || m_iFileOpened == compress) + { + ZIPTRACE("%s(%i) : ZipArchive is not an existing segmented archive.\n"); + return false; + } + + if (m_iFileOpened) + { + CloseFile(NULL, true); + } + m_storage.m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED; + return true; +} -- cgit v1.2.3