From 16f738ecee689c6feb2acb7e4ef4d9bb4144ae7d Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 8 Jun 2008 11:04:43 +0200 Subject: Initial commit --- zip/ZipArchive/ZipArchive.cpp | 3152 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3152 insertions(+) create mode 100644 zip/ZipArchive/ZipArchive.cpp (limited to 'zip/ZipArchive/ZipArchive.cpp') diff --git a/zip/ZipArchive/ZipArchive.cpp b/zip/ZipArchive/ZipArchive.cpp new file mode 100644 index 0000000..8e29c3a --- /dev/null +++ b/zip/ZipArchive/ZipArchive.cpp @@ -0,0 +1,3152 @@ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyrighted 2000 - 2007 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 +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// For the licensing details refer to the License.txt file. +// +// Web Site: http://www.artpol-software.com +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ZipArchive.h" +#include "ZipPlatform.h" +#include "ZipCompatibility.h" +#include "Wildcard.h" +#include "BytesWriter.h" + +#include + +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"}; + +void CZipAddNewFileInfo::Defaults() +{ + m_iSmartLevel = CZipArchive::zipsmSafeSmart; + m_uReplaceIndex = ZIP_FILE_INDEX_UNSPECIFIED; + m_nBufSize = 65536; + m_iComprLevel = -1; // default +} + + + + CZipArchive:: CZipArchive() +{ + Initialize(); +} + +void CZipArchive::Initialize() +{ + m_bRemoveDriveLetter = true; + m_bExhaustiveRead = false; + m_bAutoFlush = false; + m_iFileOpened = nothing; + SetCaseSensitivity(ZipPlatform::GetSystemCaseSensitivity()); + m_uCompressionMethod = CZipCompressor::methodDeflate; + m_iEncryptionMethod = CZipCryptograph::encStandard; + m_pCryptograph = NULL; + m_pCompressor = NULL; + m_iBufferSize = 65536; +} + + + CZipArchive::~ CZipArchive() +{ + // Close(); // cannot be here: if an exception is thrown strange things can happen + ClearCompressor(); + ClearCryptograph(); +} + +bool CZipArchive::Open(LPCTSTR szPathName, int iMode, ZIP_SIZE_TYPE uVolumeSize) +{ + if (!IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive already opened.\n"); + return false; + } + m_storage.Open(szPathName, iMode, uVolumeSize); + OpenInternal(iMode); + return true; +} + +bool CZipArchive::Open(CZipAbstractFile& af, int iMode) +{ + 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"); + return false; + } + m_storage.Open(af, iMode); + OpenInternal(iMode); + return true; +} + +bool CZipArchive::OpenFrom( CZipArchive& zip) +{ + if (zip.IsClosed()) + { + ZIPTRACE("%s(%i) : The source archive must be opened.\n"); + return false; + } + if (!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) + { + ZIPTRACE("%s(%i) : ZipArchive cannot share an archive in memory.\n"); + return false; + } + + m_storage.Open(zip.GetArchivePath(), CZipArchive::zipOpenReadOnly, zip.m_storage.IsSplit() ? 1 : 0); + InitOnOpen(zip.GetSystemCompatibility(), &zip.m_centralDir); + + return true; +} + +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_iArchiveSystCompatib = iArchiveSystCompatib; +} + +void CZipArchive::OpenInternal(int iMode) +{ + InitOnOpen(ZipPlatform::GetSystemID()); + if ((iMode == zipOpen) ||(iMode == zipOpenReadOnly)) + { + m_centralDir.Read(m_bExhaustiveRead); + // if there is at least one file, get system comp. from the first one + if (m_centralDir.IsValidIndex(0)) + { + int iSystemComp = m_centralDir[0]->GetSystemCompatibility(); + if (ZipCompatibility::IsPlatformSupported(iSystemComp)) + m_iArchiveSystCompatib = iSystemComp; + } + } +} + +void CZipArchive::ThrowError(int err) +{ + CZipException::Throw(err, IsClosed() ? _T("") : (LPCTSTR)m_storage.m_pFile->GetFilePath()); +} + +bool CZipArchive::GetFileInfo(CZipFileHeader & fhInfo, ZIP_INDEX_TYPE uIndex) const +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + + if (!m_centralDir.IsValidIndex(uIndex)) + return false; + + fhInfo = *(m_centralDir[uIndex]); + return true; +} + +CZipFileHeader* CZipArchive::GetFileInfo(ZIP_INDEX_TYPE uIndex) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return NULL; + } + + if (!m_centralDir.IsValidIndex(uIndex)) + return NULL; + return m_centralDir[uIndex]; +} + +const CZipFileHeader* CZipArchive::GetFileInfo(ZIP_INDEX_TYPE uIndex) const +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return NULL; + } + + if (!m_centralDir.IsValidIndex(uIndex)) + return NULL; + return m_centralDir[uIndex]; +} + + + +ZIP_INDEX_TYPE CZipArchive::FindFile(LPCTSTR lpszFileName, int iCaseSensitive, bool bFileNameOnly) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return ZIP_FILE_INDEX_NOT_FOUND; + } + bool bCS; + bool bSporadically; + switch (iCaseSensitive) + { + case ffCaseSens: + bCS = true; + bSporadically = true; + break; + case ffNoCaseSens: + bCS = false; + bSporadically = true; + break; + default: + bCS = m_bCaseSensitive; + bSporadically = false; + } + return m_centralDir.FindFile(lpszFileName, bCS, bSporadically, bFileNameOnly); +} + +bool CZipArchive::OpenFile(ZIP_INDEX_TYPE uIndex) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + + if (!m_centralDir.IsValidIndex(uIndex)) + { + ASSERT(FALSE); + return false; + } + if (m_storage.IsSegmented() == 1) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot extract from a segmented archive in creation.\n"); + return false; + } + + if (m_iFileOpened) + { + ZIPTRACE("%s(%i) : A file already opened.\n"); + return false; + } + + m_centralDir.OpenFile(uIndex); + + // check it now, not when reading central to allow reading information + // but disallow extraction now - unsupported method + if (!CZipCompressor::IsCompressionSupported(CurrentFile()->m_uMethod)) + { + m_centralDir.CloseFile(true); + ZIPTRACE("%s(%i) : The compression method is not supported.\n"); + return false; + } + + if (CurrentFile()->IsEncrypted()) + { + if (m_pszPassword.GetSize() == 0) + { + ZIPTRACE("%s(%i) : Password not set for the encrypted file.\n"); + ThrowError(CZipException::badPassword); + } + CreateCryptograph(CurrentFile()->m_uEncryptionMethod); + if (!m_pCryptograph->InitDecode(m_pszPassword, *CurrentFile(), m_storage)) + ThrowError(CZipException::badPassword); + + } + else + { + ClearCryptograph(); + if (m_pszPassword.GetSize() != 0) + { + ZIPTRACE("%s(%i) : Password set for a not encrypted file. Ignoring password.\n"); + } + } + + CreateCompressor(CurrentFile()->m_uMethod); + m_pCompressor->InitDecompression(CurrentFile(), m_pCryptograph); + + m_iFileOpened = extract; + return true; +} + +CZipFileHeader* CZipArchive::CurrentFile() +{ + ASSERT(m_centralDir.m_pOpenedFile); + return m_centralDir.m_pOpenedFile; +} + +DWORD CZipArchive::ReadFile(void *pBuf, DWORD uSize) +{ + if (m_iFileOpened != extract) + { + ZIPTRACE("%s(%i) : Current file must be opened.\n"); + return 0; + } + + if (!pBuf || !uSize) + return 0; + + return m_pCompressor->Decompress(pBuf, uSize); +} + + +void 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; + } + + if (m_iFileOpened == extract) + CloseFile(NULL, iAfterException != afNoException); + + if (m_iFileOpened == compress) + CloseNewFile(iAfterException != afNoException); + + bool bWrite = iAfterException != afAfterException && !IsClosed(false);// in segmented archive when user aborts + + if (bWrite) + WriteCentralDirectory(false); // we will flush in CZipStorage::Close + + time_t tNewestTime = 0; + + if (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(); + if (tFileInZipTime > tNewestTime) + tNewestTime = tFileInZipTime; + } + } + m_centralDir.Close(); + m_stringSettings.Reset(); + CZipString szFileName = m_storage.Close(!bWrite); + if (bUpdateTimeStamp && !szFileName.IsEmpty()) + ZipPlatform::SetFileModTime(szFileName, tNewestTime); +} + +void CZipArchive::WriteCentralDirectory(bool bFlush) +{ + m_centralDir.Write(); + if (bFlush) + m_storage.Flush(); +} + +void CZipArchive::SetAdvanced(int iWriteBuffer, int iGeneralBuffer, int iSearchBuffer) +{ + if (!IsClosed()) + { + ZIPTRACE("%s(%i) : Set these options before opening the archive.\n"); + return; + } + + m_storage.m_iWriteBufferSize = iWriteBuffer < 1024 ? 1024 : iWriteBuffer; + m_iBufferSize = iGeneralBuffer < 1024 ? 1024 : iGeneralBuffer; + m_storage.m_iLocateBufferSize = iSearchBuffer < 1024 ? 1024 : iSearchBuffer; +} + +int CZipArchive::CloseFile(CZipFile &file) +{ + CZipString temp = file.GetFilePath(); + file.Close(); + return CloseFile(temp); +} + +int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException) +{ + if (m_iFileOpened != extract) + { + ZIPTRACE("%s(%i) : No opened file.\n"); + return 0; + } + + int iRet = 1; + if (bAfterException) + m_pCompressor->FinishDecompression(true); + else + { + if (m_pCompressor->m_uUncomprLeft == 0) + { + if (m_centralDir.IsConsistencyCheckOn(checkCRC) + && !CurrentFile()->m_bIgnoreCrc32 + && m_pCompressor->m_uCrc32 != CurrentFile()->m_uCrc32) + ThrowError(CZipException::badCrc); + } + else + iRet = -1; + + m_pCompressor->FinishDecompression(false); + + if (lpszFilePath) + { + if (!ZipPlatform::SetFileModTime(lpszFilePath, CurrentFile()->GetTime()) + ||!ZipPlatform::SetFileAttr(lpszFilePath, CurrentFile()->GetSystemAttr())) + iRet = -2; + } + if (m_pCryptograph) + m_pCryptograph->FinishDecode(*CurrentFile(), m_storage); + } + + m_centralDir.CloseFile(bAfterException); + + m_iFileOpened = nothing; + ClearCryptograph(); + return iRet; +} + +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"); + return false; + } + + if (GetCount() ==(WORD)USHRT_MAX) + { + ZIPTRACE("%s(%i) : Maximum file count inside archive reached.\n"); + return false; + } + + DWORD uAttr = 0; + time_t ttime; + if (lpszFilePath) + { + 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 + } + else + { + header.SetSystemCompatibility(m_iArchiveSystCompatib); + if (!header.HasTime()) + header.SetTime(time(NULL)); + } + + CZipString szFileName = header.GetFileName(); + + + bool bIsDirectory = header.IsDirectory(); + if (bIsDirectory) + { + int iNameLen = szFileName.GetLength(); + if (!iNameLen || !CZipPathComponent::IsSeparator(szFileName[iNameLen-1])) + { + szFileName += CZipPathComponent::m_cSeparator; + header.SetFileName(szFileName); + } + } + + if (szFileName.IsEmpty()) + { + szFileName.Format(_T("file%u"), GetCount()); + header.SetFileName(szFileName); + } + + bool bEncrypted = WillEncryptNextFile(); + +#if defined _DEBUG && !defined NOZIPTRACE + if (bIsDirectory && bEncrypted) + ZIPTRACE("%s(%i) : Encrypting a directory. You may want to consider clearing the password before adding a directory.\n"); +#endif + + bool bReplace = uReplaceIndex != ZIP_FILE_INDEX_UNSPECIFIED; + + if (iLevel < -1 || iLevel > 9) + iLevel = -1; + + if (bEncrypted) + { + header.m_uEncryptionMethod = (BYTE)m_iEncryptionMethod; + CreateCryptograph(m_iEncryptionMethod); + } + else + { + header.m_uEncryptionMethod = CZipCryptograph::encNone; + ClearCryptograph(); + } + + if (iLevel == 0 || bIsDirectory) + header.m_uMethod = CZipCompressor::methodStore; + else + header.m_uMethod = m_uCompressionMethod; + + CreateCompressor(header.m_uMethod); + CZipFileHeader* pHeader = m_centralDir.AddNewFile(header, uReplaceIndex, iLevel); + + // replace can happen only from AddNewFile and the compressed size is already known and set (the file is stored, not compressed) + if (bReplace) + { + // this will be used in GetLocalSize and WriteLocal + pHeader->PrepareFileName(); + // 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); + InitBuffer(); + MakeSpaceForReplace(uReplaceIndex, uFileSize, szFileName); + ReleaseBuffer(); + } + + CurrentFile()->WriteLocal(&m_storage); + + if (m_pCryptograph) + m_pCryptograph->InitEncode(m_pszPassword, *pHeader, m_storage); + + m_pCompressor->InitCompression(iLevel, CurrentFile(), m_pCryptograph); + + m_iFileOpened = compress; + return true; +} + + +bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, + LPCTSTR lpszPath, + bool bFullPath, + LPCTSTR lpszNewName, + DWORD nBufSize) +{ + + if (!nBufSize && !lpszPath) + return false; + + CZipFileHeader* pHeader = (*this)[uIndex]; + CZipString szFileNameInZip = pHeader->GetFileName(); + CZipString szFile = PredictExtractedFileName(szFileNameInZip, lpszPath, bFullPath, lpszNewName); + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbExtract); + if (pCallback) + pCallback->Init(szFileNameInZip, szFile); + + + if (pHeader->IsDirectory()) + { + if (pCallback) + pCallback->SetTotal(0); // in case of calling LeftToProcess() afterwards + + ZipPlatform::ForceDirectory(szFile); + ZipPlatform::SetFileAttr(szFile, pHeader->GetSystemAttr()); + + if (pCallback) + pCallback->CallbackEnd(); + return true; + } + else + { + if (!OpenFile(uIndex)) + return false; + if (pCallback) + pCallback->SetTotal(pHeader->m_uUncomprSize); + + CZipPathComponent zpc(szFile); + ZipPlatform::ForceDirectory(zpc.GetFilePath()); + CZipFile f(szFile, CZipFile::modeWrite | + CZipFile::modeCreate | CZipFile::shareDenyWrite); + DWORD iRead; + CZipAutoBuffer buf(nBufSize); + int iAborted = 0; + for(;;) + { + iRead = ReadFile(buf, buf.GetSize()); + if (!iRead) + { + if (pCallback && !pCallback->RequestLastCallback()) + iAborted = CZipException::abortedSafely; + break; + } + f.Write(buf, iRead); + if (pCallback && !pCallback->RequestCallback(iRead)) + { + if (iRead == buf.GetSize() && ReadFile(buf, 1) != 0) // test one byte if there is something left + iAborted = CZipException::abortedAction; + else + iAborted = CZipException::abortedSafely; + break; + } + } + + if (pCallback) + { + if (!iAborted) + { + bool bRet = CloseFile(f) == 1; + pCallback->CallbackEnd(); + return bRet; + } + else + { + if (iAborted == CZipException::abortedAction) + CloseFile(NULL, true); + else + { + bool bRet; + try + { + bRet = CloseFile(f) == 1; + } + // if any exception was thrown, then we are not successful + // catch all exceptions to throw aborted exception only +#ifdef ZIP_ARCHIVE_MFC + catch(CException* e) + { + e->Delete(); + bRet = false; + } +#endif + catch(...) + { + bRet = false; + } + if (!bRet) + { + CloseFile(NULL, true); + iAborted = CZipException::abortedAction; + } + } + + pCallback->CallbackEnd(); + CZipException::Throw(iAborted, szFile); + return false; // for the compiler + } + } + else + return CloseFile(f) == 1; + } +} + +bool CZipArchive::ExtractFile(ZIP_INDEX_TYPE uIndex, + CZipMemFile& mf, + bool bRewind, + DWORD nBufSize) +{ + if (!nBufSize) + return false; + + CZipFileHeader* pHeader = (*this)[uIndex]; + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbExtract); + if (pCallback) + pCallback->Init(pHeader->GetFileName()); + + if (pHeader->IsDirectory() || !OpenFile(uIndex)) + return false; + + if (pCallback) + pCallback->SetTotal(pHeader->m_uUncomprSize); + + + CZipAutoBuffer buf(nBufSize); + //mf.SeekToEnd(); + ZIP_FILE_USIZE oldPos = 0; + + if (bRewind) + oldPos = mf.GetPosition(); + + DWORD iRead; + int iAborted = 0; + for(;;) + { + iRead = ReadFile(buf, buf.GetSize()); + if (!iRead) + { + if (pCallback && !pCallback->RequestLastCallback()) + iAborted = CZipException::abortedSafely; + break; + } + mf.Write(buf, iRead); + if (pCallback && !pCallback->RequestCallback(iRead)) + { + if (iRead == buf.GetSize() && ReadFile(buf, 1) != 0) // test one byte if there is something left + iAborted = CZipException::abortedAction; + else + iAborted = CZipException::abortedSafely; // we did it! + break; + } + } + + bool bRet; + if (pCallback) + { + if (!iAborted) + { + bRet = CloseFile() == 1; + pCallback->CallbackEnd(); + } + else + { + if (iAborted == CZipException::abortedAction) + CloseFile(NULL, true); + else + { + bRet = false; + try + { + bRet = CloseFile() == 1; + } + // if any exception was thrown, then we are not successful + // catch all exceptions to thrown aborted exception only + #ifdef ZIP_ARCHIVE_MFC + catch(CException* e) + { + e->Delete(); + bRet = false; + } + #endif + catch(...) + { + bRet = false; + } + if (!bRet) + { + CloseFile(NULL, true); + iAborted = CZipException::abortedAction; + } + } + + pCallback->CallbackEnd(); + if (bRewind) + mf.Seek(oldPos, CZipMemFile::begin); + CZipException::Throw(iAborted); + return false; // for the compiler + } + } + else + bRet = CloseFile() == 1; + + if (bRewind) + mf.Seek(oldPos, CZipMemFile::begin); + return bRet; +} + + +bool CZipArchive::WriteNewFile(const void *pBuf, DWORD uSize) +{ + if (m_iFileOpened != compress) + { + ZIPTRACE("%s(%i) : A new file must be opened.\n"); + return false; + } + + m_pCompressor->Compress(pBuf, uSize); + return true; +} + +bool CZipArchive::CloseNewFile(bool bAfterException) +{ + if (m_iFileOpened != compress) + { + ZIPTRACE("%s(%i) : A new file must be opened.\n"); + return false; + } + + m_pCompressor->FinishCompression(bAfterException); + if (bAfterException) + m_centralDir.m_pOpenedFile = NULL; + else + { + if (m_pCryptograph) + m_pCryptograph->FinishEncode(*CurrentFile(), m_storage); + + m_centralDir.CloseNewFile(); + } + m_iFileOpened = nothing; + ClearCryptograph(); + if (m_bAutoFlush && !bAfterException) + Flush(); + + return true; +} + +bool CZipArchive::RemoveFile(ZIP_INDEX_TYPE uIndex) +{ + CZipIndexesArray indexes; + indexes.Add(uIndex); + return RemoveFiles(indexes); +} + +void CZipArchive::GetIndexes(const CZipStringArray &aNames, CZipIndexesArray& aIndexes) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return; + } + ZIP_INDEX_TYPE uSize = (ZIP_INDEX_TYPE)aNames.GetSize(); + for (ZIP_INDEX_TYPE i = 0; i < uSize; i++) + aIndexes.Add(FindFile(aNames[(ZIP_ARRAY_SIZE_TYPE)i], ffDefault, false)); +} + +bool CZipArchive::RemoveFiles(const CZipStringArray &aNames) +{ + CZipIndexesArray indexes; + GetIndexes(aNames, indexes); + return RemoveFiles(indexes); +} + +struct CZipDeleteInfo +{ + CZipDeleteInfo(){m_pHeader = NULL; m_bDelete = false;} + CZipDeleteInfo(CZipFileHeader* pHeader, bool bDelete) + :m_pHeader(pHeader), m_bDelete (bDelete){} + CZipFileHeader* m_pHeader; + bool m_bDelete; +}; + +bool CZipArchive::RemoveFiles(CZipIndexesArray &aIndexes) +{ + if (IsClosed()) + { + 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; + } + + if (GetCount() == 0) + { + ZIPTRACE("%s(%i) : There is nothing to delete: the archive is empty.\n"); + return false; + } + + ZIP_INDEX_TYPE uSize = (ZIP_INDEX_TYPE)aIndexes.GetSize(); + if (!uSize) + { + ZIPTRACE("%s(%i) : The indexes array is empty.\n"); + return true; + } + + aIndexes.Sort(true); + // remove all - that's easy so don't waste the time + if (uSize == GetCount()) + { + // check that the indexes are correct + bool allIncluded = true; + // iterate all indexes, if all are sorted then the condition should always be true + for (ZIP_INDEX_TYPE i = 0; i < uSize; i++) + if (aIndexes[(ZIP_ARRAY_SIZE_TYPE)i] != i) + { + allIncluded = false; + break; + } + + if (allIncluded) + { + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbDelete); + if (pCallback) + { + // do it right and sent the notification + pCallback->Init(); + pCallback->SetTotal(uSize); + } + + m_centralDir.RemoveFromDisk(); + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE) m_storage.m_uBytesBeforeZip); + m_centralDir.RemoveAll(); + if (m_bAutoFlush) + Flush(); + if (pCallback) + pCallback->CallbackEnd(); + return true; + } + } + else + { + for (ZIP_INDEX_TYPE i = 0; i < uSize; i++) + if (!m_centralDir.IsValidIndex(aIndexes[(ZIP_ARRAY_SIZE_TYPE)i])) + return false; + } + + ZIP_INDEX_TYPE i; + + CZipArray aInfo; + + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbDeleteCnt); + if (pCallback) + { + pCallback->Init(); + pCallback->SetTotal(GetCount()); + } + + ZIP_INDEX_TYPE uDelIndex = 0; + ZIP_INDEX_TYPE uMaxDelIndex = aIndexes[(ZIP_ARRAY_SIZE_TYPE)(uSize - 1)]; + i = aIndexes[0]; + // GetCount() is greater than 0 (checked before) and when it is unsigned we do not cause overflow + ZIP_INDEX_TYPE uLastPosition = (ZIP_INDEX_TYPE)(GetCount() - 1); + bool bAborted = false; + if (i <= uLastPosition) + for(;;) + { + CZipFileHeader* pHeader = m_centralDir[i]; + bool bDelete; + if (i <= uMaxDelIndex && i == aIndexes[(ZIP_ARRAY_SIZE_TYPE)uDelIndex]) + { + uDelIndex++; + bDelete = true; + } + else + bDelete = false; + aInfo.Add(CZipDeleteInfo(pHeader, bDelete)); + if (i == uLastPosition) + { + if (pCallback && !pCallback->RequestLastCallback(1)) + bAborted = true; + break; + } + else + { + if (pCallback && !pCallback->RequestCallback()) + { + bAborted = true; + break; + } + i++; + } + } + + ASSERT(uDelIndex == uSize); + + if (pCallback) + { + pCallback->CallbackEnd(); + if (bAborted) + ThrowError(CZipException::abortedSafely); + } + + uSize = (ZIP_INDEX_TYPE)aInfo.GetSize(); + if (!uSize) // it is possible + return true; + + // they should already be sorted after reading the in CZipCentralDir::ReadHeaders and when replacing, the index is placed at the same place as the old one + //aInfo.Sort(true); // sort by offsets (when operators uncommented in CZipDeleteInfo) + + // now we start deleting (not safe to break) + pCallback = GetCallback(CZipActionCallback::cbDelete); + if (pCallback) + pCallback->Init(); + + m_centralDir.RemoveFromDisk(); + + ZIP_SIZE_TYPE uTotalToMoveBytes = 0, uLastOffset = m_storage.GetLastDataOffset(); + // count the number of bytes to move + i = uSize; + 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 + const CZipDeleteInfo& di = aInfo[(ZIP_ARRAY_SIZE_TYPE)i]; + if (!di.m_bDelete) + uTotalToMoveBytes += uLastOffset - di.m_pHeader->m_uOffset; + uLastOffset = di.m_pHeader->m_uOffset; + } + + if (pCallback) + pCallback->SetTotal(uTotalToMoveBytes); + + + InitBuffer(); + + ZIP_SIZE_TYPE uMoveBy = 0, uOffsetStart = 0; + for (i = 0; i < uSize; i++) + { + const CZipDeleteInfo& di = aInfo[(ZIP_ARRAY_SIZE_TYPE)i]; + + if (di.m_bDelete) + { + // next hole + ZIP_SIZE_TYPE uTemp = di.m_pHeader->m_uOffset; + m_centralDir.RemoveFile(di.m_pHeader); // first remove + if (uOffsetStart) + { + // copy the files over a previous holes + MovePackedFiles(uOffsetStart, uTemp, uMoveBy, pCallback, false, false); + uOffsetStart = 0; // never be at the beginning, because the first file is always to be deleted + } + if (i == uSize - 1) + uTemp = (m_storage.GetLastDataOffset()) - uTemp; + else + uTemp = aInfo[(ZIP_ARRAY_SIZE_TYPE)(i + 1)].m_pHeader->m_uOffset - uTemp; + + uMoveBy += uTemp; + + } + else + { + if (uOffsetStart == 0) // find continuous area to move + uOffsetStart = di.m_pHeader->m_uOffset; + di.m_pHeader->m_uOffset -= uMoveBy; + } + } + + if (uOffsetStart) + { + // will call the last callback, if necessary + MovePackedFiles(uOffsetStart, m_storage.GetLastDataOffset(), uMoveBy, pCallback); + } + else + { + // call last callback (it was not called in the MovePackedFiles calls in the loop) + if (pCallback && !pCallback->RequestLastCallback()) + { + pCallback->CallbackEnd(); + ThrowError(CZipException::abortedAction); + } + } + + ReleaseBuffer(); + if (uMoveBy) // just in case + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(m_storage.m_pFile->GetLength() - uMoveBy)); + + if (pCallback) + pCallback->CallbackEnd(); + + if (m_bAutoFlush) + Flush(); + return true; +} + + + +bool CZipArchive::ShiftData(ZIP_SIZE_TYPE uOffset) +{ + if (IsClosed()) + { + 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; + } + + if (m_storage.m_uBytesBeforeZip != 0) + { + ZIPTRACE("%s(%i) : Bytes before zip file must not be present.\n"); + return false; + } + + if (uOffset == 0) + return true; + + m_centralDir.RemoveFromDisk(); // does m_storage.Flush(); + InitBuffer(); + + ZIP_SIZE_TYPE uFileLen = (ZIP_SIZE_TYPE)m_storage.m_pFile->GetLength(); + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbMoveData); + if (pCallback) + { + pCallback->Init(NULL, GetArchivePath()); + pCallback->SetTotal(uFileLen); + } + + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen + uOffset)); // ensure the seek is correct + + MovePackedFiles(0, uFileLen, uOffset, pCallback, true); + + ZIP_INDEX_TYPE uSize = GetCount(); + for (ZIP_INDEX_TYPE i = 0; i < uSize; i++) + m_centralDir[i]->m_uOffset += uOffset; + + if (pCallback) + pCallback->CallbackEnd(); + + return true; +} + +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) +{ + if (file.IsClosed()) + { + ZIPTRACE("%s(%i) : File to prepend should be opened.\n"); + return false; + } + + ZIP_SIZE_TYPE uOffset = (ZIP_SIZE_TYPE)file.GetLength(); + if (uOffset == 0) + return true; + + if (!ShiftData(uOffset)) + return false; + file.SeekToBegin(); + // do not use callback - self-extracting stubs should be small + m_storage.Seek(0); + + char* buf = (char*)m_pBuffer; + + ZIP_SIZE_TYPE uTotalToMove = uOffset; + ZIP_SIZE_TYPE uToRead; + UINT uSizeRead; + bool bBreak = false; + DWORD bufSize = m_pBuffer.GetSize(); + do + { + uToRead = uTotalToMove > bufSize ? bufSize : uTotalToMove; + uSizeRead = (UINT)file.Read(buf, (UINT)uToRead); + if (!uSizeRead) + break; + uTotalToMove -= uSizeRead; + if (uTotalToMove == 0) + bBreak = true; + m_storage.m_pFile->Write(buf, uSizeRead); + } + while (!bBreak); + + if (m_storage.m_bInMemory || lpszNewExt == NULL) + return true; + + CZipString szInitialPath = m_storage.m_pFile->GetFilePath(); + // must close to rename + Close(); + CZipPathComponent zpc(szInitialPath); + zpc.SetExtension(lpszNewExt); + CZipString szNewPath = zpc.GetFullPath(); + if (!ZipPlatform::RenameFile(szInitialPath, szNewPath, false)) + return false; +#ifdef ZIP_ARCHIVE_LNX + return ZipPlatform::SetExeAttr(szNewPath); +#else + return true; +#endif +} + +bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath, + int iComprLevel, + bool bFullPath, + int iSmartLevel, + unsigned long nBufSize) +{ + + CZipAddNewFileInfo zanfi (lpszFilePath, bFullPath); + zanfi.m_iComprLevel = iComprLevel; + zanfi.m_iSmartLevel = iSmartLevel; + zanfi.m_nBufSize = nBufSize; + return AddNewFile(zanfi); +} + +bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath, + LPCTSTR lpszFileNameInZip, + int iComprLevel, + int iSmartLevel, + unsigned long nBufSize) +{ + CZipAddNewFileInfo zanfi(lpszFilePath, lpszFileNameInZip); + zanfi.m_iComprLevel = iComprLevel; + zanfi.m_iSmartLevel = iSmartLevel; + zanfi.m_nBufSize = nBufSize; + return AddNewFile(zanfi); +} + +bool CZipArchive::AddNewFile(CZipMemFile& mf, + LPCTSTR lpszFileNameInZip, + int iComprLevel, + int iSmartLevel, + unsigned long nBufSize) +{ + CZipAddNewFileInfo zanfi(&mf, lpszFileNameInZip); + zanfi.m_iComprLevel = iComprLevel; + zanfi.m_iSmartLevel = iSmartLevel; + zanfi.m_nBufSize = nBufSize; + return AddNewFile(zanfi); +} + +/** + A structure for the internal use only. Clears the password if necessary and + restores it later (also in case of an exception). +*/ +struct CZipSmClrPass +{ + CZipSmClrPass() + { + m_pZip = NULL; + } + + void ClearPasswordSmartly( CZipArchive* pZip) + { + m_pZip = pZip; + m_szPass = pZip->GetPassword(); + if (!m_szPass.IsEmpty()) + pZip->SetPassword(); + } + + ~CZipSmClrPass() + { + if (!m_szPass.IsEmpty()) + m_pZip->SetPassword(m_szPass); + } +private: + CZipString m_szPass; + CZipArchive* m_pZip; +}; + +bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info) +{ + // no need for ASSERT and TRACE here - it will be done by OpenNewFile + + if (!m_iBufferSize) + return false; + + if (info.m_pFile) + info.m_szFilePath = info.m_pFile->GetFilePath(); + else + { + CZipPathComponent::RemoveSeparators(info.m_szFilePath); + if (info.m_szFilePath.IsEmpty()) + return false; + } + + bool bSegm = GetSegmMode() != 0; + + // checking the replace index + if (!UpdateReplaceIndex(info.m_uReplaceIndex)) + return false; + + bool bReplace = info.m_uReplaceIndex != ZIP_FILE_INDEX_UNSPECIFIED; + + DWORD uAttr; + time_t ttime; + if (info.m_pFile) + { + uAttr = ZipPlatform::GetDefaultAttributes(); + ttime = time(NULL); + } + 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); + } + CZipFileHeader header; + SetFileHeaderAttr(header, 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; + + if (header.IsDirectory()) // will never be when m_pFile is not NULL, so we don't check it + { + ASSERT(!info.m_pFile); // should never happened + ASSERT(!bInternal); + + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbAdd); + + if (pCallback) + { + pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath); + pCallback->SetTotal(0); // in case of calling LeftToProcess() afterwards + } + + // clear password for a directory + CZipSmClrPass smcp; + if (info.m_iSmartLevel & zipsmCPassDir) + smcp.ClearPasswordSmartly(this); + + bool bRet = OpenNewFile(header, CZipCompressor::levelStore, NULL, info.m_uReplaceIndex); + + CloseNewFile(); + if (pCallback) + pCallback->CallbackEnd(); + + return bRet; + } + + CZipSmClrPass smcp; + 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; + ZIP_SIZE_TYPE uFileSize = ZIP_SIZE_TYPE(-1); + bool bNeedTempArchive = (bEff && bSegm) || (bReplace && bIsCompression); + if (bCheckForSmallFiles || bCheckForZeroSized || bNeedTempArchive) + { + + if (info.m_pFile) + uFileSize = (ZIP_SIZE_TYPE)info.m_pFile->GetLength(); + else + { + if (!ZipPlatform::GetFileSize(info.m_szFilePath, uFileSize) && bEff) + bEff = false; // the file size is needed only when efficient in a segmented archive + } + + if (uFileSize != ZIP_SIZE_TYPE(-1)) + { + if (bCheckForZeroSized && uFileSize == 0) + smcp.ClearPasswordSmartly(this); + if (bCheckForSmallFiles && uFileSize < 5) + info.m_iComprLevel = 0; + } + } + bool bEffInMem = bEff && (info.m_iSmartLevel & zipsmMemoryFlag); + CZipString szTempFileName; + if (bNeedTempArchive && (bEffInMem || + !(szTempFileName = ZipPlatform::GetTmpFileName( + m_szTempPath.IsEmpty() ? NULL : (LPCTSTR)m_szTempPath, uFileSize) + ).IsEmpty())) + { + CZipMemFile* pmf = NULL; + CZipArchive zip; + try + { + // compress first to a temporary file, if ok - copy the data, if not - add storing + + if (bEffInMem) + { + pmf = new CZipMemFile; + zip.Open(*pmf, zipCreate); + } + else + zip.Open(szTempFileName, zipCreate); + zip.SetRootPath(m_szRootPath); + zip.SetPassword(GetPassword()); + zip.SetEncryptionMethod(m_iEncryptionMethod); + zip.SetSystemCompatibility(m_iArchiveSystCompatib); + zip.SetCallback(GetCallback(CZipActionCallback::cbAdd), CZipActionCallback::cbAdd); + // create a temporary file + ZIP_INDEX_TYPE uTempReplaceIndex = info.m_uReplaceIndex; + info.m_iSmartLevel = zipsmLazy; + info.m_uReplaceIndex = ZIP_FILE_INDEX_UNSPECIFIED; + if (!zip.AddNewFile(info)) + throw false; + info.m_uReplaceIndex = uTempReplaceIndex; + + // this may also happen when bReplace, but not in a segmented archive + if (bEff) + { + if (!zip[0]->CompressionEfficient()) + { + info.m_iComprLevel = 0; + info.m_iSmartLevel = zipsmInternal01; + // compression is not efficient, store instead + throw AddNewFile(info); + } + } + zip.m_storage.Flush(); + InitBuffer(); + throw GetFromArchive(zip, 0, NULL, info.m_uReplaceIndex, true, GetCallback(CZipActionCallback::cbAddTmp)); + } + catch (bool bRet) + { + zip.Close(!bRet); // that doesn't really matter how it will be closed + if (pmf) + delete pmf; + if (!bEffInMem) + ZipPlatform::RemoveFile(szTempFileName, false); + ReleaseBuffer(); + return bRet; + } + catch (...) + { + zip.Close(true); + if (pmf) + delete pmf; + if (!bEffInMem) + ZipPlatform::RemoveFile(szTempFileName, false); + ReleaseBuffer(); + throw; + } + } + + // try to open before adding + CZipFile f; + CZipAbstractFile *pf; + if (info.m_pFile) + { + pf = info.m_pFile; + pf->SeekToBegin(); + } + else + { + // 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)) + return false; + pf = &f; + } + + ASSERT(pf); + // call init before opening (in case of exception we have the names) + uFileSize = (ZIP_SIZE_TYPE)pf->GetLength(); + + // predict sizes in local header, so that zip64 can write extra header if needed + header.m_uLocalUncomprSize = uFileSize; + if (!bIsCompression) + header.m_uLocalComprSize = uFileSize; + + bool bRet; + if (bReplace) + { + ASSERT(!bIsCompression); + bRet = OpenNewFile(header, CZipCompressor::levelStore, NULL, info.m_uReplaceIndex); + } + else + bRet = OpenNewFile(header, info.m_iComprLevel); + if (!bRet) + return false; + + // we do it here, because if in OpenNewFile is replacing + // then we get called cbMoveData callback before and it would + // overwrite callback information written in pCallback->Init() + CZipActionCallback* pCallback = GetCallback(bInternal ? CZipActionCallback::cbAddStore : CZipActionCallback::cbAdd); + + if (pCallback) + { + // Init cbAdd here as well - after smart add - to avoid double initiation when + // temporary archive is used - it would init cbAdd again + pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath); + pCallback->SetTotal(uFileSize); + } + + CZipAutoBuffer buf(info.m_nBufSize); + DWORD iRead; + int iAborted = 0; + do + { + iRead = pf->Read(buf, info.m_nBufSize); + if (iRead) + { + WriteNewFile(buf, iRead); + if (pCallback && !pCallback->RequestCallback(iRead)) + { + if (iRead == buf.GetSize() && pf->Read(buf, 1) != 0) // test one byte if there is something left + { + if (!m_storage.IsSegmented() && !bReplace) + { + RemoveLast(true); + iAborted = CZipException::abortedSafely; + } + else + iAborted = CZipException::abortedAction; + // close new file with bException set to true, even if abortedSafely, + // because in that case we have removed the last file - there is nothing to close + CloseNewFile(true); + } + else + // temporary value - possible safe abort + iAborted = CZipException::aborted; + break; + } + } + } + while (iRead == buf.GetSize()); + + if (pCallback) + { + if (!iAborted && !pCallback->RequestLastCallback()) + // temporaty value - safe abort + iAborted = CZipException::aborted; + + if (!iAborted) + { + CloseNewFile(); + pCallback->CallbackEnd(); + } + else + { + // possible safe abort + if (iAborted == CZipException::aborted) + { + bool bRet; + try + { + bRet = CloseNewFile(); + } +#ifdef ZIP_ARCHIVE_MFC + catch(CException* e) + { + e->Delete(); + bRet = false; + } +#endif + catch(...) + { + bRet = false; + } + if (bRet) + iAborted = CZipException::abortedSafely; + else + { + CloseNewFile(true); + iAborted = CZipException::abortedAction; + } + } + pCallback->CallbackEnd(); + CZipException::Throw(iAborted); // throw to distinguish from other return codes + } + } + else + CloseNewFile(); + + if (bEff) + { + // remove the last file and add it without the compression if needed + if (!info.m_pFile) + f.Close(); + + buf.Release(); + if (RemoveLast()) + { + info.m_iComprLevel = 0; + info.m_iSmartLevel = zipsmInternal01; + return AddNewFile(info); + } + } + return true; +} + +bool CZipArchive::RemoveLast(bool bRemoveAnyway) +{ + if (GetCount() == 0) + return false; + ZIP_INDEX_TYPE uIndex = (ZIP_INDEX_TYPE)(GetCount() - 1); + CZipFileHeader* pHeader = m_centralDir[uIndex]; + + if (!bRemoveAnyway && pHeader->CompressionEfficient()) + return false; + + m_centralDir.RemoveLastFile(pHeader, uIndex); + return true; +} + +class CZipRootPathRestorer +{ + CZipString m_szOldRootPath; + CZipArchive* m_pZip; +public: + CZipRootPathRestorer() + { + m_pZip = NULL; + } + void SetNewRootPath( CZipArchive* pZip, LPCTSTR lpszNewRoot) + { + m_pZip = pZip; + m_szOldRootPath = m_pZip->GetRootPath(); + m_pZip->SetRootPath(lpszNewRoot); + } + ~CZipRootPathRestorer() + { + if (m_pZip) + m_pZip->SetRootPath(m_szOldRootPath); + } +}; + +class CCalculateAddFilesEnumerator : public ZipArchiveLib::CDirEnumerator +{ + CZipActionCallback* m_pCallback; +public: + ZIP_FILE_USIZE m_uTotalBytes; + ZIP_FILE_USIZE m_uTotalFiles; + CCalculateAddFilesEnumerator(LPCTSTR lpszDirectory, bool bRecursive, CZipActionCallback* pCallback) + :ZipArchiveLib::CDirEnumerator(lpszDirectory, bRecursive) + { + m_pCallback = pCallback; + m_uTotalFiles = m_uTotalBytes = 0; + } +protected: + void OnEnumerationBegin() + { + if (m_pCallback) + m_pCallback->Init(); + } + + bool Process(LPCTSTR, const ZipArchiveLib::CFileInfo& info) + { + m_uTotalFiles++; + m_uTotalBytes += info.m_uSize; + if (m_pCallback && !m_pCallback->RequestCallback()) + return false; + else + return true; + } + + void OnEnumerationEnd(bool bResult) + { + if (m_pCallback) + { + if (bResult) + bResult = m_pCallback->RequestLastCallback(); + m_pCallback->CallbackEnd(); + // can be false only, if the callback returns false + if (!bResult) + CZipException::Throw(CZipException::abortedSafely); + } + } +}; + +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, + CFileFilter& filter, + bool bRecursive, + int iComprLevel, + bool bSkipInitialPath, + int iSmartLevel, + unsigned long nBufSize) +{ + CZipRootPathRestorer restorer; + if (bSkipInitialPath) + restorer.SetNewRootPath(this, lpszPath); + + 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)); + if (!calculateEnumerator.Start(filter)) + return false; + if (pMultiCallback->m_iType != CZipActionCallback::cbMultiAdd) + // may happen, if it is the same as calculate + pMultiCallback->m_iType = CZipActionCallback::cbMultiAdd; + pMultiCallback->MultiActionsInit((ZIP_SIZE_TYPE)calculateEnumerator.m_uTotalFiles, (ZIP_SIZE_TYPE)calculateEnumerator.m_uTotalBytes, CZipActionCallback::cbAdd); + } + + try + { + CAddFilesEnumerator addFilesEnumerator(lpszPath, bRecursive, this, iComprLevel, iSmartLevel, nBufSize, pMultiCallback); + bool ret = addFilesEnumerator.Start(filter); + if (pMultiCallback) + pMultiCallback->MultiActionsEnd(); + return ret; + } + catch(...) + { + if (pMultiCallback) + pMultiCallback->MultiActionsEnd(); + throw; + } +} + + +CZipString CZipArchive::GetArchivePath() const +{ + if (IsClosed(false)) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return _T(""); + } + return m_storage.m_pFile->GetFilePath(); +} + +CZipString CZipArchive::GetGlobalComment() const +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return _T(""); + } + CZipString temp; + m_centralDir.GetComment(temp); + return temp; +} + +bool CZipArchive::SetGlobalComment(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 global comment of an existing segmented archive.\n"); + return false; + } + + m_centralDir.SetComment(lpszComment); + if (m_bAutoFlush) + Flush(); + + return true; +} + + + +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) +{ + if (m_iFileOpened != nothing) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot change the password when a file is opened.\n"); + return false; // it's important not to change the password when the file inside archive is opened + } + if (IsClosed()) + { + ZIPTRACE("%s(%i) : Setting the password for a closed archive has no effect.\n"); + } + if (lpszPassword) + ZipCompatibility::ConvertStringToBuffer(lpszPassword, m_pszPassword, CP_ACP); + else + m_pszPassword.Release(); + return true; +} + +bool CZipArchive::SetEncryptionMethod(int iEncryptionMethod) +{ + if (m_iFileOpened == compress) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot change the encryption method when there is a file opened for compression.\n"); + return false; + } + + if (iEncryptionMethod != CZipCryptograph::encNone && !CZipCryptograph::IsEncryptionSupported(iEncryptionMethod)) + return false; + m_iEncryptionMethod = iEncryptionMethod; + return true; +} + +struct CZipEncryptFileInfo +{ + CZipEncryptFileInfo() + { + m_pHeader = NULL; + m_uLocalSizeDiff = 0; + m_uDescriptorSizeDiff = 0; + m_uIndex = 0; + } + CZipEncryptFileInfo(CZipFileHeader* pHeader, DWORD uLocalSizeDiff, + DWORD uDescriptorSizeDiff, ZIP_INDEX_TYPE uIndex, ZIP_SIZE_TYPE uDataOffset) + :m_pHeader(pHeader), m_uLocalSizeDiff(uLocalSizeDiff), + m_uDescriptorSizeDiff(uDescriptorSizeDiff), m_uIndex(uIndex), m_uUncompressedOffset(uDataOffset) + { + } + + CZipFileHeader* m_pHeader; + DWORD m_uLocalSizeDiff; + DWORD m_uDescriptorSizeDiff; + ZIP_INDEX_TYPE m_uIndex; + ZIP_SIZE_TYPE m_uUncompressedOffset; + ZIP_SIZE_TYPE GetLastDataOffset() + { + return m_uUncompressedOffset + m_pHeader->m_uOffset; + } +}; + +bool CZipArchive::EncryptFilesInternal(CZipIndexesArray* pIndexes) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + + if (m_storage.IsSegmented()) + { + 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; + } + + if (GetCount() == 0) + { + ZIPTRACE("%s(%i) : There is nothing to encrypt: the archive is empty.\n"); + return false; + } + + if (!WillEncryptNextFile()) + { + ZIPTRACE("%s(%i) : An encryption method and a password must be set.\n"); + return false; + } + + bool bAll; + ZIP_ARRAY_SIZE_TYPE i; + if (pIndexes == NULL) + { + bAll = true; + i = (ZIP_ARRAY_SIZE_TYPE)GetCount(); + } + else + { + bAll = false; + pIndexes->Sort(true); + i = pIndexes->GetSize(); + } + + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbEncryptPrepare); + if (pCallback) + { + pCallback->Init(); + pCallback->SetTotal((ZIP_SIZE_TYPE)i); + } + bool bAborted = false; + + CZipArray infos; + ZIP_SIZE_TYPE uExtraData = 0; + while (i > 0) + { + i--; + ZIP_INDEX_TYPE idx; + if (bAll) + idx = (ZIP_INDEX_TYPE)i; + else + { + idx = pIndexes->GetAt(i); + if (!m_centralDir.IsValidIndex(idx)) + { + if (pCallback && !pCallback->RequestCallback()) + { + bAborted = true; + break; + } + continue; + } + } + CZipFileHeader* pHeader = GetFileInfo(idx); + + if (pHeader->IsEncrypted()) + { + if (pCallback && !pCallback->RequestCallback()) + { + bAborted = true; + break; + } + continue; + } + + ReadLocalHeaderInternal(idx); + DWORD uOrigSize = pHeader->GetLocalSize(true); + DWORD uOrigDescriptorSize = pHeader->GetDataDescriptorSize(&m_storage); + + pHeader->m_uEncryptionMethod = (BYTE)m_iEncryptionMethod; + pHeader->UpdateFlag(m_storage.IsSegmented() != 0); + + // needed for GetLocalSize + pHeader->PrepareFileName(); + DWORD uLocalDiff = pHeader->GetLocalSize(false) - uOrigSize; + DWORD uDescriptorDiff = pHeader->GetDataDescriptorSize(&m_storage) - uOrigDescriptorSize; + uExtraData += uLocalDiff + uDescriptorDiff; + infos.Add(CZipEncryptFileInfo(pHeader, uLocalDiff, uDescriptorDiff, idx, pHeader->m_uOffset + uOrigSize)); + if (pCallback && !pCallback->RequestCallback()) + { + bAborted = true; + break; + } + } + + if (pCallback) + { + if (!bAborted && !pCallback->RequestLastCallback()) + bAborted = true; + pCallback->CallbackEnd(); + if (bAborted) + CZipException::Throw(CZipException::abortedAction); + } + + ZIP_ARRAY_SIZE_TYPE uSize = infos.GetSize(); + if (!uSize) + { + ZIPTRACE("%s(%i) : There are no files to encrypt.\n"); + return true; + } + + m_centralDir.RemoveFromDisk(); + + CZipActionCallback* pMultiCallback = GetCallback(CZipActionCallback::cbMultiEncrypt); + + ZIP_ARRAY_SIZE_TYPE idxIdx; + ZIP_INDEX_TYPE idx; + + if (pMultiCallback) + { + ZIP_SIZE_TYPE uTotalToMove = 0; + ZIP_SIZE_TYPE uTotalToEncrypt = 0; + + // move files + idxIdx = 0; + // infos array has data from largest index to the smallest + CZipEncryptFileInfo info = infos[0]; + idx = GetCount(); + + ZIP_SIZE_TYPE uLastOffset = m_storage.GetLastDataOffset(); + ZIP_INDEX_TYPE lastNormalIdx = ZIP_FILE_INDEX_UNSPECIFIED; + while (idx > 0) + { + idx--; + if (idx == info.m_uIndex) + { + if (lastNormalIdx != ZIP_FILE_INDEX_UNSPECIFIED) + { + // compensate changed offset + uTotalToMove += uLastOffset - GetFileInfo(lastNormalIdx)->m_uOffset; + lastNormalIdx = ZIP_FILE_INDEX_UNSPECIFIED; + } + uTotalToMove += info.m_pHeader->m_uComprSize; + uTotalToEncrypt += info.m_pHeader->m_uComprSize; + + // no more files to encrypt + if (++idxIdx == uSize) + break; + uLastOffset = info.m_pHeader->m_uOffset; + info = infos[idxIdx]; + } + else + lastNormalIdx = idx; + } + pMultiCallback->MultiActionsInit((ZIP_SIZE_TYPE)uSize, uTotalToMove + uTotalToEncrypt, CZipActionCallback::cbEncryptMoveData); + } + + + try + { + // move files + idxIdx = 0; + // infos array has data from largest index to the smallest + CZipEncryptFileInfo info = infos[0]; + idx = GetCount(); + + DWORD uExtraBefore = CZipCryptograph::GetEncryptedInfoSizeBeforeData(m_iEncryptionMethod); + DWORD uExtraAfter = CZipCryptograph::GetEncryptedInfoSizeAfterData(m_iEncryptionMethod); + // the total amount of extra data + uExtraData += ((uExtraBefore + uExtraAfter) * infos.GetSize()); + + ZIP_SIZE_TYPE uLastOffset = m_storage.GetLastDataOffset(); + ZIP_INDEX_TYPE lastNormalIdx = ZIP_FILE_INDEX_UNSPECIFIED; + InitBuffer(); + pCallback = GetCallback(CZipActionCallback::cbEncryptMoveData); + while (idx > 0) + { + idx--; + if (idx == info.m_uIndex) + { + if (lastNormalIdx != ZIP_FILE_INDEX_UNSPECIFIED) + { + // compensate changed offset + ZIP_SIZE_TYPE uStartOffset = GetFileInfo(lastNormalIdx)->m_uOffset - uExtraData; + if (pCallback) + { + pCallback->Init(); + pCallback->SetTotal(uLastOffset - uStartOffset); + } + MovePackedFiles(uStartOffset, uLastOffset, uExtraData, pCallback, true); + if (pCallback) + pCallback->CallbackEnd(); + lastNormalIdx = ZIP_FILE_INDEX_UNSPECIFIED; + } + uExtraData -= (uExtraAfter + info.m_uDescriptorSizeDiff); + if (pCallback) + { + pCallback->Init(); + pCallback->SetTotal(info.m_pHeader->m_uComprSize); + } + MovePackedFiles(info.m_uUncompressedOffset, info.m_uUncompressedOffset + info.m_pHeader->m_uComprSize, uExtraData, pCallback, true); + if (pCallback) + pCallback->CallbackEnd(); + + // no more files to encrypt + if (++idxIdx == uSize) + break; + + uExtraData -= (uExtraBefore + info.m_uLocalSizeDiff); + // use original offsett + 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; + + info = infos[idxIdx]; + } + else + { + lastNormalIdx = idx; + GetFileInfo(idx)->m_uOffset += uExtraData; + } + } + bAborted = false; + ZIP_SIZE_TYPE uToEncrypt = 0; + 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); + if (pMultiCallback) + pMultiCallback->SetReactType(CZipActionCallback::cbEncrypt); + pCallback = GetCallback(CZipActionCallback::cbEncrypt); + while (i > 0) + { + i--; + CZipEncryptFileInfo inf = infos[i]; + CZipFileHeader* pHeader = inf.m_pHeader; + uToEncrypt = pHeader->m_uComprSize; + if (pCallback) + { + pCallback->Init(pHeader->GetFileName()); + pCallback->SetTotal(uToEncrypt); + } + + m_storage.Seek(pHeader->m_uOffset); + pHeader->WriteLocal(&m_storage); + // take the number of bytes to encode, before m_uComprSize is modified + m_pCryptograph->InitEncode(m_pszPassword, *pHeader, m_storage); + m_storage.Flush(); + + if (uToEncrypt) + { + DWORD bufSize = m_pBuffer.GetSize(); + char* buf = (char*)m_pBuffer; + ZIP_SIZE_TYPE uToRead; + UINT uSizeRead; + bool bBreak = false; + CZipAbstractFile* pFile = m_storage.m_pFile; + ZIP_FILE_USIZE uPosition = pFile->GetPosition(); + // the file pointer should be already positioned on the data + do + { + uToRead = uToEncrypt > bufSize ? bufSize : uToEncrypt; + uSizeRead = (UINT)pFile->Read(buf, (UINT)uToRead); + if (!uSizeRead) + break; + uToEncrypt -= uSizeRead; + if (uToEncrypt == 0) + bBreak = true; + + m_pCryptograph->Encode(buf, uSizeRead); + pFile->Seek(uPosition); + pFile->Write(buf, uSizeRead); + uPosition += uSizeRead; + + if (pCallback && !pCallback->RequestCallback(uSizeRead)) + { + bAborted = true; + break; + } + if (pMultiCallback) + pMultiCallback->MultiActionsNext(); + } + while (!bBreak); + } + + // 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) + if (uToEncrypt == 0) + { + m_pCryptograph->FinishEncode(*pHeader, m_storage); + // it will be written only if needed + pHeader->WriteDataDescriptor(&m_storage); + m_storage.Flush(); + } + + if (pCallback) + { + if (!bAborted && !pCallback->RequestLastCallback()) + bAborted = true; + + if (bAborted) + { + pCallback->CallbackEnd(); + CZipException::Throw(CZipException::abortedAction); + } + } + + if (uToEncrypt > 0) + ThrowError(CZipException::badZipFile); + + if (pCallback) + pCallback->CallbackEnd(); + } + + m_storage.FlushFile(); + ClearCryptograph(); + + } + catch(...) + { + if (pMultiCallback) + pMultiCallback->MultiActionsEnd(); + throw; + } + + if (pMultiCallback) + pMultiCallback->MultiActionsEnd(); + return true; + +} + + +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)) + { + ZIPTRACE("%s(%i) : The compression method is not supported.\n"); + return false; + } + + m_uCompressionMethod = uCompressionMethod; + return true; +} + +CZipString CZipArchive::GetPassword()const +{ + CZipString temp; + ZipCompatibility::ConvertBufferToString(temp, m_pszPassword, CP_ACP); + return temp; +} + +bool CZipArchive::TestFile(ZIP_INDEX_TYPE uIndex, DWORD uBufSize) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + + if (m_storage.IsSegmented() == 1) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot test a segmented archive in creation.\n"); + return false; + } + if (!uBufSize) + return false; + + CZipFileHeader* pHeader = m_centralDir[uIndex]; + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbTest); + if (pCallback) + { + pCallback->Init(pHeader->GetFileName()); + } + + if (pHeader->IsDirectory()) + { + if (pCallback) + pCallback->SetTotal(0); + + // we do not test whether the password for the encrypted directory + // is correct, since it seems to be senseless (anyway password + // encrypted directories should be avoided - it adds 12 bytes) + ZIP_SIZE_TYPE uSize = pHeader->m_uComprSize; + if ((uSize != 0 || uSize != pHeader->m_uUncomprSize) + // different treating compressed directories + && !(pHeader->IsEncrypted() && uSize == 12 && !pHeader->m_uUncomprSize)) + CZipException::Throw(CZipException::dirWithSize); + + if (pCallback) + pCallback->CallbackEnd(); + + return true; + } + else + { + try + { + if (pCallback) + pCallback->SetTotal(pHeader->m_uUncomprSize); + + if (!OpenFile(uIndex)) + return false; + CZipAutoBuffer buf(uBufSize); + DWORD iRead; + int iAborted = 0; + for(;;) + { + iRead = ReadFile(buf, buf.GetSize()); + if (!iRead) + { + if (pCallback && !pCallback->RequestLastCallback()) + iAborted = CZipException::abortedSafely; + break; + } + if (pCallback && !pCallback->RequestCallback(iRead)) + { + if (iRead == buf.GetSize() && ReadFile(buf, 1) != 0) // test one byte if there is something left + iAborted = CZipException::abortedAction; + else + iAborted = CZipException::abortedSafely; // we did it! + break; + } + } + + if (!iAborted) + { + if (CloseFile() == 1) + { + if (pCallback) + pCallback->CallbackEnd(); + return true; + } + else + CZipException::Throw(CZipException::badZipFile); + } + else + { + if (iAborted == CZipException::abortedAction) + CloseFile(NULL, true); + else + { + bool bRet; + try + { + bRet = CloseFile() == 1; + } + // if any exception was thrown, then we are not successful + // catch all exceptions to thrown aborted exception only +#ifdef ZIP_ARCHIVE_MFC + catch(CException* e) + { + e->Delete(); + bRet = false; + } +#endif + catch(...) + { + bRet = false; + } + if (!bRet) + { + CloseFile(NULL, true); + iAborted = CZipException::abortedAction; + } + } + + pCallback->CallbackEnd(); + CZipException::Throw(iAborted); + } + return false; // to satisfy some compilers (some will complain)... + } + catch(...) + { + CloseFile(NULL, true); + throw; + } + } +} + +void CZipArchive::SetFileHeaderAttr(CZipFileHeader& header, DWORD uAttr)const +{ + header.SetSystemCompatibility(m_iArchiveSystCompatib); + header.SetSystemAttr(uAttr); +} + +void CZipArchive::EnableFindFast(bool bEnable) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : Set it after opening the archive.\n"); + return; + } + m_centralDir.EnableFindFast(bEnable, m_bCaseSensitive); +} + +bool CZipArchive::SetSystemCompatibility(int iSystemComp) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : Set it after opening the archive.\n"); + return false; + } + + if (m_iFileOpened == compress) + { + ZIPTRACE("%s(%i) : Set it before opening a file inside archive.\n"); + return false; + } + + if (!ZipCompatibility::IsPlatformSupported(iSystemComp)) + return false; + // change the name coding page, if it was not changed before + if (m_stringSettings.IsStandardNameCodePage(m_iArchiveSystCompatib)) + m_stringSettings.SetDefaultNameCodePage(iSystemComp); + m_iArchiveSystCompatib = iSystemComp; + + return true; +} + +void CZipArchive::SetRootPath(LPCTSTR szPath) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : Set it after opening the archive.\n"); + return; + } + + if (m_iFileOpened != nothing) + { + ZIPTRACE("%s(%i) : Set it before opening a file inside archive.\n"); + return; + } + + if (szPath) + { + m_szRootPath = szPath; + CZipPathComponent::RemoveSeparators(m_szRootPath); + } + else + m_szRootPath.Empty(); +} + +CZipString CZipArchive::TrimRootPath(CZipPathComponent &zpc)const +{ + if (m_szRootPath.IsEmpty()) + return zpc.GetFileName(); + CZipString szPath = zpc.GetFullPath(); + return RemovePathBeginning(m_szRootPath, szPath, m_pZipCompare) ? szPath : zpc.GetFileName(); +} + +bool CZipArchive::RemovePathBeginning(LPCTSTR lpszBeginning, CZipString& szPath, ZIPSTRINGCOMPARE pCompareFunction) +{ + CZipString szBeginning(lpszBeginning); + CZipPathComponent::RemoveSeparators(szBeginning); + int iRootPathLength = szBeginning.GetLength(); + if (iRootPathLength && szPath.GetLength() >= iRootPathLength && + (szPath.Left(iRootPathLength).*pCompareFunction)(szBeginning) == 0) + { + // the beginning is the same + if (szPath.GetLength() == iRootPathLength) + { + szPath.Empty(); + return true; + } + // is the end of m_szPathRoot only a beginning of a directory name? + // check for a separator + // we know the length is larger, so we can write: + if (CZipPathComponent::IsSeparator(szPath[iRootPathLength])) + { + szPath = szPath.Mid(iRootPathLength); + CZipPathComponent::RemoveSeparatorsLeft(szPath); + return true; + } + } + return false; +} + +void CZipArchive::SetTempPath(LPCTSTR lpszPath, bool bForce) +{ + m_szTempPath = lpszPath; + if (lpszPath && bForce) + ZipPlatform::ForceDirectory(lpszPath); + CZipPathComponent::RemoveSeparators(m_szTempPath); +} + +CZipString CZipArchive::PredictFileNameInZip(LPCTSTR lpszFilePath, + bool bFullPath, int iWhat)const +{ + CZipString sz = lpszFilePath; + if (sz.IsEmpty()) + return _T(""); + bool bAppend; + switch (iWhat) + { + case prFile: + bAppend = false; + break; + case prDir: + bAppend = true; + break; + default: + bAppend = CZipPathComponent::IsSeparator(sz[sz.GetLength() - 1]); + } + + // remove for CZipPathComponent treating last name as a file even if dir + CZipPathComponent::RemoveSeparators(sz); + // it may be empty after removing separators, e.g.: "/" + if (sz.IsEmpty()) + return _T(""); + CZipPathComponent zpc(sz); + + if (bFullPath) + { + if (m_bRemoveDriveLetter) + sz = zpc.GetNoDrive(); + } + else + sz = TrimRootPath(zpc); + + if (bAppend && !sz.IsEmpty()) + CZipPathComponent::AppendSeparator(sz); + return sz; +} + +CZipString CZipArchive::PredictExtractedFileName(LPCTSTR lpszFileNameInZip, LPCTSTR lpszPath, bool bFullPath, LPCTSTR lpszNewName)const +{ + CZipString szFile = lpszPath; + CZipString sz = lpszNewName ? lpszNewName : lpszFileNameInZip; + 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) + : TrimRootPath(zpc); + return szFile; +} + +ZIP_SIZE_TYPE CZipArchive::PredictMaximumFileSizeInArchive(LPCTSTR lpszFilePath, bool bFullPath) const +{ + DWORD attr; + if (!ZipPlatform::GetFileAttr(lpszFilePath, attr)) + return 0; + CZipFileHeader fh; + SetFileHeaderAttr(fh, attr); + if (!fh.IsDirectory()) + if (!ZipPlatform::GetFileSize(lpszFilePath, fh.m_uUncomprSize)) + 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; + fh.m_uEncryptionMethod = WillEncryptNextFile() ? (BYTE)m_iEncryptionMethod : (BYTE)CZipCryptograph::encNone; + fh.m_uMethod = CZipCompressor::methodStore; + fh.PrepareData(CZipCompressor::levelStore, m_storage.IsSegmented() != 0); + DWORD uLocalSize = fh.GetLocalSize(true); + return fh.GetSize() + uLocalSize + fh.GetDataSize(true, false) + fh.GetDataDescriptorSize(&m_storage); +} + +void CZipArchive::SetAutoFlush(bool bAutoFlush) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive should be opened first.\n"); + return; + } + if (m_storage.IsSegmented() != 0) + { + ZIPTRACE("%s(%i) : Cannot set auto-flush for a segmented archive.\n"); + return; + } + m_bAutoFlush = bAutoFlush; +} + +void CZipArchive::Flush() +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive should be opened first.\n"); + return; + } + + if (m_storage.IsSegmented() < 0) + { + ZIPTRACE("%s(%i) : Cannot flush an existing segmented archive.\n"); + return; + } + WriteCentralDirectory(); + m_storage.FlushFile(); + if (m_storage.IsSegmented() > 0) // try to finalize a segmented archive without closing it + m_storage.FinalizeSegm(); +} + + +void CZipArchive::GetCentralDirInfo(CZipCentralDir::CInfo& info)const +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive should be opened first.\n"); + return; + + } + m_centralDir.GetInfo(info); + if (GetSegmMode() > 0) + info.m_uLastVolume = m_storage.GetCurrentVolume(); +} + +void CZipArchive::FindMatches(LPCTSTR lpszPattern, CZipIndexesArray &ar, bool bFullPath) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return; + } + + // ar.RemoveAll(); don't do this + ZIP_INDEX_TYPE uCount = (ZIP_INDEX_TYPE)GetCount(); + CWildcard wc(lpszPattern, m_bCaseSensitive); + for (ZIP_INDEX_TYPE i = 0; i < uCount; i++) + { + CZipString sz = m_centralDir[i]->GetFileName(); + if (!bFullPath) + { + CZipPathComponent::RemoveSeparators(sz); + CZipPathComponent zpc(sz); + sz = zpc.GetFileName(); + } + if (wc.IsMatch(sz)) + ar.Add(i); + } +} + +ZIP_INDEX_TYPE CZipArchive::WillBeDuplicated(LPCTSTR lpszFilePath, bool bFullPath, bool bFileNameOnly , int iWhat) +{ + CZipString szFile; + if (bFileNameOnly) + { + CZipPathComponent zpc(lpszFilePath); + szFile = PredictFileNameInZip(zpc.GetFileName(), false, iWhat); + } + else + szFile = PredictFileNameInZip(lpszFilePath, bFullPath, iWhat); + return FindFile(szFile, ffDefault, bFileNameOnly); +} + + +bool CZipArchive::GetFromArchive( CZipArchive& zip, ZIP_INDEX_TYPE uIndex, LPCTSTR lpszNewFileName, ZIP_INDEX_TYPE uReplaceIndex, bool bKeepSystComp, CZipActionCallback* pCallback) +{ + if (this == &zip) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot get files from the same archive.\n"); + return false; + } + + if (IsClosed() || zip.IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + + if (m_iFileOpened || zip.m_iFileOpened) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot get files from another archive if there is a file opened.\n"); + return false; + } + + if (m_storage.IsSegmented() == -1) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot add files to an existing segmented archive.\n"); + return false; + } + + ASSERT(m_pBuffer.GetSize() > 0); + + bool bSegm = m_storage.IsSegmented() == 1; + + if (!zip.m_centralDir.IsValidIndex(uIndex)) + return false; + + zip.ReadLocalHeaderInternal(uIndex); + + CZipFileHeader originalHeader; + // 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; + CZipString szFileName; + if (lpszNewFileName != NULL) + { + szFileName = lpszNewFileName; + originalHeader.SetFileName(lpszNewFileName); + } + else + szFileName = originalHeader.GetFileName(); // empty + + if (bConvertSystem) + { + DWORD uAttr = originalHeader.GetSystemAttr(); + originalHeader.SetSystemCompatibility(m_iArchiveSystCompatib); + originalHeader.SetSystemAttr(uAttr); + } + + if (!UpdateReplaceIndex(uReplaceIndex)) + return false; + + bool bReplace = uReplaceIndex != ZIP_FILE_INDEX_UNSPECIFIED; + if (bReplace && bSegm) + return false; + + int iCallbackType = 0; + if (pCallback) + iCallbackType = pCallback->m_iType; + + if (!originalHeader.IsEncrypted() && WillEncryptNextFile()) + { + originalHeader.m_uEncryptionMethod = (BYTE)m_iEncryptionMethod; + CreateCryptograph(m_iEncryptionMethod); + } + else + ClearCryptograph(); + + // 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; + + // prepare this here: it will be used for GetLocalSize and WriteLocal + pHeader->PrepareFileName(); + + ZIP_SIZE_TYPE uTotalToMove = pHeader->m_uComprSize; + + if (bReplace) + { + 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); + else + uDataSize = uTotalToMove; + MakeSpaceForReplace(uReplaceIndex, uDataSize + pHeader->GetLocalSize(false) + pHeader->GetDataDescriptorSize(&m_storage), szFileName); + } + + if (pCallback) + { + // must be set before Init() + pCallback->m_iType = iCallbackType; + pCallback->Init(szFileName, zip.GetArchivePath()); + pCallback->SetTotal(pHeader->m_uComprSize); + } + + // must be written as not converted + pHeader->WriteLocal(&m_storage); + if (m_pCryptograph) + m_pCryptograph->InitEncode(m_pszPassword, *pHeader, m_storage); + + + char* buf = (char*)m_pBuffer; + + ZIP_SIZE_TYPE uToRead; + DWORD uSizeRead; + int iAborted = 0; + bool bBreak = false; + if (uTotalToMove) + { + DWORD bufSize = m_pBuffer.GetSize(); + do + { + uToRead = uTotalToMove > bufSize ? bufSize : uTotalToMove; + uSizeRead = (UINT)zip.m_storage.Read(buf, (UINT)uToRead, false); + if (!uSizeRead) + break; + uTotalToMove -= uSizeRead; + if (uTotalToMove == 0) + bBreak = true; + + if (m_pCryptograph) + m_pCryptograph->Encode(m_pBuffer, uSizeRead); + + m_storage.Write(buf, uSizeRead, false); + if (pCallback && !pCallback->RequestCallback(uSizeRead)) + { + if (uTotalToMove != 0) + { + if (!bSegm && !bReplace) + { + m_centralDir.RemoveLastFile(); + iAborted = CZipException::abortedSafely; + } + else + iAborted = CZipException::abortedAction; + } + else + iAborted = CZipException::abortedSafely; + break; + + } + } + while (!bBreak); + + if (pCallback) + { + if (!iAborted && !pCallback->RequestLastCallback()) + iAborted = CZipException::abortedSafely; + + if (iAborted) + { + // when no exception, CallbackEnd() called later + pCallback->CallbackEnd(); + CZipException::Throw(iAborted); // throw to distinguish from other return codes + } + } + } + + 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) + if (uTotalToMove == 0) + { + if (m_pCryptograph) + m_pCryptograph->FinishEncode(*pHeader, m_storage); + // it will be written only if needed + pHeader->WriteDataDescriptor(&m_storage); + } + + m_storage.Flush(); + if (uTotalToMove > 0) + ThrowError(CZipException::badZipFile); + + if (pCallback) + pCallback->CallbackEnd(); + + ClearCryptograph(); + return true; +} + +bool CZipArchive::GetFromArchive( CZipArchive& zip, CZipIndexesArray &aIndexes, bool bKeepSystComp) +{ + aIndexes.Sort(true); + ZIP_INDEX_TYPE uFiles = (ZIP_INDEX_TYPE)aIndexes.GetSize(); + InitBuffer(); + try + { + 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))) + { + ReleaseBuffer(); + return false; + } + } + } + catch (...) + { + ReleaseBuffer(); + throw; + } + ReleaseBuffer(); + if (m_bAutoFlush) + Flush(); + 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()) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot rename files in a segmented archive.\n"); + return false; + } + + if (m_iFileOpened) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot rename a file if there is a file opened.\n"); + return false; + } + CZipFileHeader* pHeader = (*this)[uIndex]; + if (pHeader == NULL) + return false; + + CZipString szNewName(lpszNewName); + if (pHeader->IsDirectory()) + CZipPathComponent::AppendSeparator(szNewName); + else + CZipPathComponent::RemoveSeparators(szNewName); + CZipString szPreviousFileName = pHeader->GetFileName(); + if (szPreviousFileName.Collate(szNewName) == 0) + return true; + + // 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); + + 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) + { + // do it right and sent the notification + pCallback->Init(szPreviousFileName, GetArchivePath()); + pCallback->SetTotal(uEndOffset - uStartOffset); + } + bool bForward = iDelta > 0; + if (bForward) + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen + iDelta)); // ensure the seek is correct + + MovePackedFiles(uStartOffset, uEndOffset, abs(iDelta), pCallback, bForward); + if (pCallback) + pCallback->CallbackEnd(); + + if (!bForward) + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen + iDelta)); // delta < 0; shrink the file + + 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; + } + 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(); + + return true; +} + +bool CZipArchive::UpdateReplaceIndex(ZIP_INDEX_TYPE& uReplaceIndex) +{ + if (uReplaceIndex == ZIP_FILE_INDEX_UNSPECIFIED) + return true; + + if (GetSegmMode() != 0) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot replace files in a segmented archive.\n"); + return false; + } + + if (!m_centralDir.IsValidIndex(uReplaceIndex)) + { + ZIPTRACE("%s(%i) : Not valid replace index.\n"); + return false; + } + if (uReplaceIndex == GetCount() - 1) // replacing last file in the archive + { + RemoveLast(true); + uReplaceIndex = ZIP_FILE_INDEX_UNSPECIFIED; + } + return true; +} + +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 + ZIP_SIZE_TYPE uReplaceStart = (ZIP_SIZE_TYPE)m_storage.m_pFile->GetPosition() - m_storage.m_uBytesBeforeZip; + + // find the next offset (files in the central directory may not necessarily be ordered by offset) + ZIP_SIZE_TYPE uReplaceEnd = ZIP_SIZE_TYPE(-1); + ZIP_INDEX_TYPE i; + for (i = 0; i < (ZIP_INDEX_TYPE)m_centralDir.GetCount(); i++) + if (i != uReplaceIndex) + { + ZIP_SIZE_TYPE uOffset = m_centralDir[i]->m_uOffset; + if (uOffset > uReplaceStart && uOffset < uReplaceEnd) + uReplaceEnd = uOffset; + } + + ZIP_SIZE_TYPE uReplaceTotal = uReplaceEnd - uReplaceStart; + if (uTotal == uReplaceTotal) + return; + + bool bForward = uTotal > uReplaceTotal; + ZIP_SIZE_TYPE uDelta; + if (bForward) + uDelta = uTotal - uReplaceTotal; + else + uDelta = uReplaceTotal - uTotal; + + + // InitBuffer(); don't - the calling functions will + CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbMoveData); + ZIP_SIZE_TYPE uFileLen = (ZIP_SIZE_TYPE)m_storage.m_pFile->GetLength(); + ZIP_SIZE_TYPE uUpperLimit = uFileLen - m_storage.m_uBytesBeforeZip; // will be added in m_storage.Seek + if (pCallback) + { + pCallback->Init(lpszFileName, GetArchivePath()); + pCallback->SetTotal(uUpperLimit - uReplaceEnd); + } + + if (bForward) + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen + uDelta)); // ensure the seek is correct + + MovePackedFiles(uReplaceEnd, uUpperLimit, uDelta, pCallback, bForward); + + if (!bForward) + m_storage.m_pFile->SetLength((ZIP_FILE_USIZE)(uFileLen - uDelta)); + + m_storage.Seek(uReplaceStart); + ZIP_INDEX_TYPE uSize = GetCount(); + for (i = (ZIP_INDEX_TYPE)(uReplaceIndex + 1); i < uSize; i++) + { + ZIP_SIZE_TYPE uOffset = m_centralDir[i]->m_uOffset; + m_centralDir[i]->m_uOffset = bForward ? uOffset + uDelta : uOffset - uDelta; + } + if (pCallback) + pCallback->CallbackEnd(); +} + +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 (uEndOffset - uStartOffset < uPack) + { + uPack = uEndOffset - uStartOffset; + if (!uPack) + break; + bBreak = true; + + } + 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); + + if (pCallback) + { + if (!bAborted && bLastCall && !pCallback->RequestLastCallback()) + bAborted = true; + // do not call here - it will be called in the calling method + //pCallback->CallbackEnd(); + if (bAborted) + { + // call here before throwing the the aborted exception + pCallback->CallbackEnd(); + ThrowError(CZipException::abortedAction); + } + } + + if (uEndOffset != uStartOffset) + ThrowError(CZipException::internalError); + +} + + +bool CZipArchive::RemoveCentralDirectoryFromArchive() +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + + if (m_storage.IsSegmented()) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot remove the central directory from a segmented archive.\n"); + return false; + } + m_centralDir.RemoveFromDisk(); + return true; +} + +bool CZipArchive::OverwriteLocalHeader(ZIP_INDEX_TYPE uIndex) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + + if (m_storage.IsSegmented()) + { + ZIPTRACE("%s(%i) : ZipArchive Library cannot overwrite local header in a segmented archive.\n"); + return false; + } + + CZipFileHeader* pHeader = GetFileInfo(uIndex); + m_storage.Seek(pHeader->m_uOffset); + pHeader->WriteLocal(&m_storage); + return true; +} + +bool CZipArchive::ReadLocalHeader(ZIP_INDEX_TYPE uIndex) +{ + if (IsClosed()) + { + ZIPTRACE("%s(%i) : ZipArchive is closed.\n"); + return false; + } + if (m_iFileOpened) + { + ZIPTRACE("%s(%i) : A file is already opened.\n"); + return false; + } + ReadLocalHeaderInternal(uIndex); + return true; +} + +void CZipArchive::SetSegmCallback(CZipSegmCallback* pCallback, int callbackType) +{ + if ((callbackType & scSpan) != 0) + m_storage.m_pSpanChangeVolumeFunc = pCallback; + if ((callbackType & scSplit) != 0) + m_storage.m_pSplitChangeVolumeFunc = pCallback; +} -- cgit v1.2.3