3386 lines
175 KiB
C++
3386 lines
175 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
||
// This source file is part of the ZipArchive library source distribution and
|
||
// is Copyrighted 2000 - 2012 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
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// TODO: remove warnings with zlib compilation (exposed at least in VS 2005 Release compilation)
|
||
#include "stdafx.h"
|
||
#include "ZipArchive.h"
|
||
#include "ZipPlatform.h"
|
||
#include "ZipCompatibility.h"
|
||
#include "Wildcard.h"
|
||
#include "BytesWriter.h"
|
||
|
||
#include <time.h>
|
||
|
||
using namespace ZipArchiveLib;
|
||
|
||
const char CZipArchive::m_gszCopyright[] = {"The ZipArchive Library Copyright (c) 2000 - 2012 Artpol Software - Tadeusz Dracz"};
|
||
const char CZipArchive::m_gszVersion[] = {"4.1.2"};
|
||
|
||
void CZipAddNewFileInfo::Defaults()
|
||
{
|
||
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;
|
||
}
|
||
|
||
|
||
|
||
CZipArchive:: CZipArchive()
|
||
{
|
||
Initialize();
|
||
}
|
||
|
||
void CZipArchive::Initialize()
|
||
{
|
||
m_bRemoveDriveLetter = true;
|
||
m_bAutoFinalize = false;
|
||
// use the default
|
||
SetCommitMode();
|
||
m_iFileOpened = nothing;
|
||
SetCaseSensitivity(ZipPlatform::GetSystemCaseSensitivity());
|
||
m_uCompressionMethod = CZipCompressor::methodDeflate;
|
||
m_iEncryptionMethod = CZipCryptograph::encStandard;
|
||
m_pCryptograph = NULL;
|
||
m_pCompressor = NULL;
|
||
m_iBufferSize = 65536;
|
||
m_centralDir.InitOnCreate(this);
|
||
}
|
||
|
||
|
||
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::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) : The open mode is not supported.\n");
|
||
return false;
|
||
}
|
||
m_storage.Open(af, iMode, bAutoClose);
|
||
OpenInternal(iMode);
|
||
return true;
|
||
}
|
||
|
||
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 (!bAllowNonReadOnly && !zip.IsReadOnly())
|
||
{
|
||
ZIPTRACE("%s(%i) : The source archive must be opened in the read-only mode.\n");
|
||
return false;
|
||
}
|
||
|
||
if (pArchiveFile != NULL && zip.m_storage.IsSegmented())
|
||
{
|
||
ZIPTRACE("%s(%i) : ZipArchive cannot share a segmented archive using a file that is not on a disk.\n");
|
||
return false;
|
||
}
|
||
|
||
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)
|
||
{
|
||
m_pszPassword.Release();
|
||
m_iFileOpened = nothing;
|
||
m_szRootPath.Empty();
|
||
m_centralDir.Init(pSource);
|
||
m_iArchiveSystCompatib = iArchiveSystCompatib;
|
||
}
|
||
|
||
void CZipArchive::OpenInternal(int iMode)
|
||
{
|
||
InitOnOpen(ZipPlatform::GetSystemID());
|
||
CBitFlag mode(iMode);
|
||
if (mode.IsSetAny(zipOpen) || mode.IsSetAll(zipOpenReadOnly))
|
||
{
|
||
m_centralDir.Read();
|
||
// 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, LPCTSTR lpszFilePath) const
|
||
{
|
||
CZipException::Throw(err, lpszFilePath != NULL ? lpszFilePath : (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.IsNewSegmented())
|
||
{
|
||
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 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);
|
||
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, !m_centralDir.IsConsistencyCheckOn(checkDecryptionVerifier)))
|
||
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);
|
||
}
|
||
|
||
|
||
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 _T("");
|
||
}
|
||
|
||
if (m_iFileOpened == extract)
|
||
CloseFile(NULL, iAfterException != afNoException);
|
||
|
||
if (m_iFileOpened == compress)
|
||
CloseNewFile(iAfterException != afNoException);
|
||
|
||
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
|
||
|
||
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;
|
||
}
|
||
}
|
||
#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);
|
||
return szFileName;
|
||
}
|
||
|
||
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)
|
||
{
|
||
#ifdef _ZIP_SYSTEM_WIN
|
||
int iRet = ZipPlatform::SetFileModTime((HANDLE)file, CurrentFile()->GetTime())
|
||
&& ZipPlatform::SetFileAttr(file.GetFilePath(), CurrentFile()->GetSystemAttr()) ? 1 : -2;
|
||
file.Close();
|
||
int iCloseRet = CloseFile(NULL);
|
||
return iRet == 1 ? iCloseRet : iRet;
|
||
#else
|
||
CZipString temp = file.GetFilePath();
|
||
file.Close();
|
||
return CloseFile(temp);
|
||
#endif
|
||
|
||
|
||
}
|
||
|
||
int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException)
|
||
{
|
||
if (m_iFileOpened != extract)
|
||
{
|
||
ZIPTRACE("%s(%i) : There is 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 (!CanModify(true))
|
||
return false;
|
||
|
||
if (GetCount() ==(WORD)USHRT_MAX)
|
||
{
|
||
ZIPTRACE("%s(%i) : Maximum file count inside archive reached.\n");
|
||
return false;
|
||
}
|
||
|
||
|
||
if (lpszFilePath)
|
||
{
|
||
DWORD uAttr = 0;
|
||
time_t ttime;
|
||
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)
|
||
ThrowError(CZipException::fileError, lpszFilePath);
|
||
}
|
||
ZipPlatform::GetFileModTime(lpszFilePath, ttime);
|
||
header.SetTime(ttime);
|
||
header.SetSystemCompatibility(m_iArchiveSystCompatib);
|
||
header.SetSystemAttr(uAttr);
|
||
}
|
||
else
|
||
{
|
||
header.SetSystemCompatibility(m_iArchiveSystCompatib, true);
|
||
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 (bEncrypted)
|
||
{
|
||
header.m_uEncryptionMethod = (BYTE)m_iEncryptionMethod;
|
||
CreateCryptograph(m_iEncryptionMethod);
|
||
}
|
||
else
|
||
{
|
||
header.m_uEncryptionMethod = CZipCryptograph::encNone;
|
||
ClearCryptograph();
|
||
}
|
||
|
||
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;
|
||
|
||
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);
|
||
|
||
// 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->PrepareStringBuffers();
|
||
// we use the local size, because the real does not exist yet
|
||
ZIP_SIZE_TYPE uFileSize = pHeader->m_uLocalComprSize + 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,
|
||
ZipPlatform::DeleteFileMode iOverwriteMode,
|
||
DWORD nBufSize)
|
||
{
|
||
|
||
if (!nBufSize && !lpszPath)
|
||
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);
|
||
if (pCallback)
|
||
pCallback->Init(szFileNameInZip, szFile);
|
||
|
||
int nRet = 0;
|
||
if (pHeader->IsDirectory())
|
||
{
|
||
if (pCallback)
|
||
pCallback->SetTotal(0); // in case of calling LeftToProcess() afterwards
|
||
|
||
while(!ZipPlatform::ForceDirectory(szFile))
|
||
{
|
||
Sleep(10);
|
||
nRet++;
|
||
if(nRet > 30)
|
||
{
|
||
pCallback->CallbackEnd();
|
||
CZipException::Throw(CZipException::abortedAction, szFile);
|
||
break;
|
||
}
|
||
else
|
||
OutputDebugString(_T("Retry Create Directory #1\n"));
|
||
}
|
||
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);
|
||
|
||
while(!ZipPlatform::ForceDirectory(zpc.GetFilePath()))
|
||
{
|
||
Sleep(10);
|
||
nRet++;
|
||
if(nRet > 30)
|
||
{
|
||
CloseFile(NULL, true);
|
||
|
||
pCallback->CallbackEnd();
|
||
CZipException::Throw(CZipException::abortedAction, szFile);
|
||
break;
|
||
}
|
||
else
|
||
OutputDebugString(_T("Retry Create Directory #2\n"));
|
||
}
|
||
|
||
if (ZipPlatform::FileExists(szFile) != 0)
|
||
{
|
||
ZipPlatform::RemoveFile(szFile, true, iOverwriteMode);
|
||
}
|
||
|
||
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_IMPL_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,
|
||
CZipAbstractFile& af,
|
||
bool bRewind,
|
||
DWORD nBufSize)
|
||
{
|
||
if (!nBufSize)
|
||
return false;
|
||
|
||
CZipFileHeader* pHeader = (*this)[uIndex];
|
||
if (!pHeader || pHeader->IsDirectory())
|
||
return false;
|
||
|
||
CZipActionCallback* pCallback = GetCallback(CZipActionCallback::cbExtract);
|
||
if (pCallback)
|
||
pCallback->Init(pHeader->GetFileName());
|
||
|
||
if (!OpenFile(uIndex))
|
||
return false;
|
||
|
||
if (pCallback)
|
||
pCallback->SetTotal(pHeader->m_uUncomprSize);
|
||
|
||
|
||
CZipAutoBuffer buf(nBufSize);
|
||
//af.SeekToEnd();
|
||
ZIP_FILE_USIZE oldPos = 0;
|
||
|
||
if (bRewind)
|
||
oldPos = af.GetPosition();
|
||
|
||
DWORD iRead;
|
||
int iAborted = 0;
|
||
for(;;)
|
||
{
|
||
iRead = ReadFile(buf, buf.GetSize());
|
||
if (!iRead)
|
||
{
|
||
if (pCallback && !pCallback->RequestLastCallback())
|
||
iAborted = CZipException::abortedSafely;
|
||
break;
|
||
}
|
||
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
|
||
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_IMPL_MFC
|
||
catch(CException* e)
|
||
{
|
||
e->Delete();
|
||
bRet = false;
|
||
}
|
||
#endif
|
||
catch(...)
|
||
{
|
||
bRet = false;
|
||
}
|
||
if (!bRet)
|
||
{
|
||
CloseFile(NULL, true);
|
||
iAborted = CZipException::abortedAction;
|
||
}
|
||
}
|
||
|
||
pCallback->CallbackEnd();
|
||
if (bRewind)
|
||
af.SafeSeek(oldPos, true);
|
||
CZipException::Throw(iAborted);
|
||
return false; // for the compiler
|
||
}
|
||
}
|
||
else
|
||
bRet = CloseFile() == 1;
|
||
|
||
if (bRewind)
|
||
af.SafeSeek(oldPos, true);
|
||
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 (!bAfterException)
|
||
Finalize(true);
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CZipArchive::RemoveFile(ZIP_INDEX_TYPE uIndex, bool bRemoveData)
|
||
{
|
||
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)
|
||
{
|
||
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 (!CanModify())
|
||
{
|
||
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();
|
||
// 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();
|
||
Finalize(true);
|
||
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<CZipDeleteInfo> 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 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;
|
||
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();
|
||
|
||
Finalize(true);
|
||
return true;
|
||
}
|
||
|
||
|
||
|
||
bool CZipArchive::ShiftData(ZIP_SIZE_TYPE uOffset)
|
||
{
|
||
if (!CanModify())
|
||
{
|
||
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 (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
|
||
Close();
|
||
CZipPathComponent zpc(szInitialPath);
|
||
zpc.SetExtension(lpszNewExt);
|
||
CZipString szNewPath = zpc.GetFullPath();
|
||
if (!ZipPlatform::RenameFile(szInitialPath, szNewPath, false))
|
||
return false;
|
||
#ifdef _ZIP_SYSTEM_LINUX
|
||
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(CZipAbstractFile& af,
|
||
LPCTSTR lpszFileNameInZip,
|
||
int iComprLevel,
|
||
int iSmartLevel,
|
||
unsigned long nBufSize)
|
||
{
|
||
CZipAddNewFileInfo zanfi(&af, 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 = m_storage.IsSegmented();
|
||
|
||
// 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))
|
||
ThrowError(CZipException::fileError, info.m_szFilePath); // we don't know whether it is a file or a directory
|
||
ZipPlatform::GetFileModTime(info.m_szFilePath, ttime);
|
||
}
|
||
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.IsSetAny(zipsmInternal01);
|
||
|
||
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.IsSetAny(zipsmCPassDir))
|
||
smcp.ClearPasswordSmartly(this);
|
||
|
||
bool bRet = OpenNewFile(header, CZipCompressor::levelStore, NULL, info.m_uReplaceIndex);
|
||
|
||
CloseNewFile();
|
||
if (pCallback)
|
||
pCallback->CallbackEnd();
|
||
|
||
return bRet;
|
||
}
|
||
|
||
CZipSmClrPass smcp;
|
||
if (m_uCompressionMethod == CZipCompressor::methodStore)
|
||
{
|
||
info.m_iComprLevel = 0;
|
||
}
|
||
|
||
bool bIsCompression = info.m_iComprLevel != 0;
|
||
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)
|
||
{
|
||
|
||
if (info.m_pFile)
|
||
uFileSize = (ZIP_SIZE_TYPE)info.m_pFile->GetLength();
|
||
else
|
||
{
|
||
if (!ZipPlatform::GetFileSize(info.m_szFilePath, uFileSize) && bEff)
|
||
uFileSize = ZIP_SIZE_TYPE(-1);
|
||
}
|
||
|
||
if (uFileSize != ZIP_SIZE_TYPE(-1))
|
||
{
|
||
if (bCheckForZeroSized && uFileSize == 0)
|
||
smcp.ClearPasswordSmartly(this);
|
||
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.IsSetAny(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;
|
||
if (!zip.Open(*pmf, zipCreate))
|
||
return false;
|
||
}
|
||
else if (!zip.Open(szTempFileName, zipCreate))
|
||
return false;
|
||
|
||
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, true))
|
||
return false;
|
||
pf = &f;
|
||
}
|
||
|
||
ASSERT(pf);
|
||
|
||
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)
|
||
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())
|
||
// temporary 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_IMPL_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;
|
||
bool m_countDirectories;
|
||
public:
|
||
ZIP_FILE_USIZE m_uTotalBytes;
|
||
ZIP_FILE_USIZE m_uTotalFiles;
|
||
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:
|
||
void OnEnumerationBegin()
|
||
{
|
||
if (m_pCallback)
|
||
m_pCallback->Init();
|
||
}
|
||
|
||
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())
|
||
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& info)
|
||
{
|
||
if (info.IsDirectory() && ((m_iSmartLevel & CZipArchive::zipsmIgnoreDirectories) != 0))
|
||
return true;
|
||
|
||
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)
|
||
{
|
||
if (IsClosed())
|
||
{
|
||
ZIPTRACE("%s(%i) : ZipArchive is closed.\n");
|
||
return false;
|
||
}
|
||
|
||
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), (iSmartLevel & CZipArchive::zipsmIgnoreDirectories) == 0);
|
||
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 file 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, UINT codePage)
|
||
{
|
||
if (!CanModify(true))
|
||
{
|
||
return false;
|
||
}
|
||
if (codePage == ZIP_DEFAULT_CODE_PAGE)
|
||
{
|
||
codePage = ZipCompatibility::GetDefaultCommentCodePage(m_iArchiveSystCompatib);
|
||
}
|
||
m_centralDir.SetComment(lpszComment, codePage);
|
||
Finalize(true);
|
||
return true;
|
||
}
|
||
|
||
ZIP_VOLUME_TYPE CZipArchive::GetCurrentVolume() const
|
||
{
|
||
return (ZIP_VOLUME_TYPE)(m_storage.GetCurrentVolume() + 1);
|
||
}
|
||
|
||
bool CZipArchive::SetPassword(LPCTSTR lpszPassword, UINT codePage)
|
||
{
|
||
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)
|
||
{
|
||
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)
|
||
{
|
||
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 (!CanModify())
|
||
{
|
||
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<CZipEncryptFileInfo> 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());
|
||
|
||
// needed for GetLocalSize
|
||
pHeader->PrepareStringBuffers();
|
||
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 += (ZIP_SIZE_TYPE)(((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 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;
|
||
|
||
info = infos[idxIdx];
|
||
}
|
||
else
|
||
{
|
||
lastNormalIdx = idx;
|
||
GetFileInfo(idx)->m_uOffset += uExtraData;
|
||
}
|
||
}
|
||
bAborted = false;
|
||
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);
|
||
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->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);
|
||
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->SafeSeek(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 (!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.IsNewSegmented())
|
||
{
|
||
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_IMPL_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::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;
|
||
#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)
|
||
{
|
||
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)
|
||
{
|
||
DWORD attr;
|
||
if (!ZipPlatform::GetFileAttr(lpszFilePath, attr))
|
||
ThrowError(CZipException::fileError, lpszFilePath);
|
||
CZipFileHeader fh;
|
||
|
||
fh.SetSystemAttr(attr);
|
||
if (!fh.IsDirectory())
|
||
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)
|
||
{
|
||
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());
|
||
DWORD uLocalSize = fh.GetLocalSize(true);
|
||
ZIP_SIZE_TYPE ret = fh.GetSize() + uLocalSize + fh.GetDataSize(false) + fh.GetDataDescriptorSize(&m_storage);
|
||
fh.m_pCentralDir = NULL;
|
||
return ret;
|
||
}
|
||
|
||
bool CZipArchive::SetAutoFinalize(bool bAutoFinalize)
|
||
{
|
||
if (!CanModify(false, false))
|
||
{
|
||
return false;
|
||
}
|
||
if (m_bAutoFinalize != bAutoFinalize)
|
||
{
|
||
if (bAutoFinalize)
|
||
{
|
||
if (IsModified())
|
||
{
|
||
ZIPTRACE("%s(%i) : Cannot enable auto finalize when there are pending changes to commit.\n");
|
||
return false;
|
||
}
|
||
}
|
||
m_bAutoFinalize = bAutoFinalize;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool CZipArchive::Finalize(bool bOnlyIfAuto)
|
||
{
|
||
if (bOnlyIfAuto && !m_bAutoFinalize)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (!CanModify(true, false))
|
||
{
|
||
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.IsNewSegmented()) // try to finalize a segmented archive without closing it
|
||
m_storage.FinalizeSegm();
|
||
return true;
|
||
}
|
||
|
||
void CZipArchive::GetCentralDirInfo(CZipCentralDir::CInfo& info)const
|
||
{
|
||
if (IsClosed())
|
||
{
|
||
ZIPTRACE("%s(%i) : ZipArchive should be opened first.\n");
|
||
return;
|
||
}
|
||
m_centralDir.GetInfo(info);
|
||
if (m_storage.IsNewSegmented() && !m_storage.IsBinarySplit())
|
||
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 remove
|
||
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.IsExistingSegmented())
|
||
{
|
||
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();
|
||
|
||
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 (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 (bKeepSystComp)
|
||
{
|
||
originalHeader.SetSystemCompatibility(m_iArchiveSystCompatib, true);
|
||
}
|
||
|
||
if (!UpdateReplaceIndex(uReplaceIndex))
|
||
return false;
|
||
|
||
bool bReplace = uReplaceIndex != ZIP_FILE_INDEX_UNSPECIFIED;
|
||
if (bReplace && bSegm)
|
||
return false;
|
||
|
||
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;
|
||
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);
|
||
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->PrepareStringBuffers();
|
||
|
||
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);
|
||
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
|
||
{
|
||
if (m_pCryptograph)
|
||
m_pCryptograph->FinishEncode(*pHeader, m_storage);
|
||
// it will be written only if needed
|
||
pHeader->WriteDataDescriptor(&m_storage);
|
||
iAborted = CZipException::abortedSafely;
|
||
}
|
||
break;
|
||
|
||
}
|
||
}
|
||
while (!bBreak);
|
||
|
||
if (pCallback)
|
||
{
|
||
if (!iAborted && !pCallback->RequestLastCallback())
|
||
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
|
||
pCallback->CallbackEnd();
|
||
CZipException::Throw(iAborted); // throw to distinguish from other return codes
|
||
}
|
||
}
|
||
}
|
||
|
||
m_centralDir.m_pOpenedFile = NULL;
|
||
|
||
// 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();
|
||
Finalize(true);
|
||
return true;
|
||
}
|
||
|
||
struct CZipChangeInfo
|
||
{
|
||
CZipChangeInfo()
|
||
{
|
||
m_index = 0;
|
||
m_startOffset = 0;
|
||
m_endOffset = 0;
|
||
m_relativeOffset = 0;
|
||
}
|
||
|
||
CZipChangeInfo(ZIP_INDEX_TYPE index,
|
||
ZIP_SIZE_TYPE startOffset,
|
||
ZIP_FILE_SIZE relativeOffset)
|
||
{
|
||
m_index = index;
|
||
m_startOffset = startOffset;
|
||
m_endOffset = 0;
|
||
m_relativeOffset = relativeOffset;
|
||
}
|
||
|
||
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;
|
||
|
||
ZIP_INDEX_TYPE uCount = GetCount();
|
||
if (uCount == 0)
|
||
return true;
|
||
|
||
CZipArray<CZipChangeInfo> 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));
|
||
}
|
||
|
||
|
||
ZIP_ARRAY_SIZE_TYPE totalInfos = aInfo.GetSize();
|
||
if (totalInfos == 0)
|
||
return true;
|
||
|
||
m_centralDir.RemoveFromDisk(); // does m_storage.Flush();
|
||
|
||
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
|
||
{
|
||
uEndOffset = GetFileInfo(aInfo.GetAt(idx + 1).m_index)->m_uOffset;
|
||
last = false;
|
||
}
|
||
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--;
|
||
}
|
||
|
||
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 (last)
|
||
break;
|
||
|
||
currentIndex--;
|
||
}
|
||
ReleaseBuffer();
|
||
|
||
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;
|
||
}
|
||
ZIP_FILE_SIZE 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;
|
||
}
|
||
|
||
Finalize(true);
|
||
|
||
if (pCallback)
|
||
pCallback->CallbackEnd();
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CZipArchive::UpdateReplaceIndex(ZIP_INDEX_TYPE& uReplaceIndex)
|
||
{
|
||
if (uReplaceIndex == ZIP_FILE_INDEX_UNSPECIFIED)
|
||
return true;
|
||
|
||
if (m_storage.IsSegmented())
|
||
{
|
||
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);
|
||
|
||
bool bAborted = false;
|
||
if (uMoveBy > 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;
|
||
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 aborted exception
|
||
pCallback->CallbackEnd();
|
||
ThrowError(CZipException::abortedAction);
|
||
}
|
||
}
|
||
|
||
if (uMoveBy > 0 && 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);
|
||
if (!pHeader)
|
||
return false;
|
||
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)
|
||
{
|
||
CBitFlag flag = callbackType;
|
||
if (flag.IsSetAny(scSpan))
|
||
m_storage.m_pSpanChangeVolumeFunc = pCallback;
|
||
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;
|
||
}
|