diff --git a/harbour/contrib/hbzlib/ZipString.cpp b/harbour/contrib/hbzlib/ZipString.cpp new file mode 100644 index 0000000000..e7c3b38f70 --- /dev/null +++ b/harbour/contrib/hbzlib/ZipString.cpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipString.cpp $ +// $Archive: /ZipArchive/ZipString.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + +#include "zipstring.h" + +ZIPSTRINGCOMPARE GetCZipStrCompFunc(bool bCaseSensitive, bool bCollate) +{ + if (bCollate) + return bCaseSensitive ? & CZipString::Collate : & CZipString::CollateNoCase; + else + return bCaseSensitive ? & CZipString::Compare : & CZipString::CompareNoCase; +} diff --git a/harbour/contrib/hbzlib/stdafx.cpp b/harbour/contrib/hbzlib/stdafx.cpp new file mode 100644 index 0000000000..98e3f3d000 --- /dev/null +++ b/harbour/contrib/hbzlib/stdafx.cpp @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// $Workfile: stdafx.cpp $ +// $Archive: /ZipArchive/stdafx.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" + + diff --git a/harbour/contrib/hbzlib/ziparchive.cpp b/harbour/contrib/hbzlib/ziparchive.cpp new file mode 100644 index 0000000000..79e5fdec0c --- /dev/null +++ b/harbour/contrib/hbzlib/ziparchive.cpp @@ -0,0 +1,3236 @@ +/////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipArchive.cpp $ +// $Archive: /ZipArchive/ZipArchive.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + + +#include "hbcomprs.h" +#include "stdafx.h" +#include "ziparchive.h" +// #include "ZipPathComponent.h" +#include "zipplatform.h" +#include "zipcompatibility.h" +#include + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +#define ZIP_COMPR_REPL_MASK 0xffffff00 +#define ZIP_COMPR_REPL_SIGN 0x0100 // first 8 bits should be 00 (reserved for compression level), next 8 should be different from ff (to distinguish from -1) + +const TCHAR CZipArchive::m_gszCopyright[] = {_T("ZipArchive library Copyright 2000 - 2003 Tadeusz Dracz")}; + + +void CZipAddNewFileInfo::Defaults() +{ + m_iSmartLevel = CZipArchive::zipsmSafeSmart; + m_iReplaceIndex = -1; + m_nBufSize = 65536; + m_iComprLevel = -1; // default + +} + +CZipArchive::CZipArchive() +{ + + m_bRemoveDriveLetter = true; + m_bIgnoreCRC = + m_bAutoFlush = false; + m_centralDir.m_pStorage= &m_storage; +// m_info.m_stream.zalloc = (alloc_func)_zliballoc; +// m_info.m_stream.zfree = (free_func)_zlibfree; +// m_bDetectZlibMemoryLeaks = true; + m_iFileOpened = nothing; + SetCaseSensitivity(ZipPlatform::GetSystemCaseSensitivity()); +} + + +CZipArchive::~CZipArchive() +{ + // Close(); // cannot be here: if an exception is thrown strange things can happen + EmptyPtrList(); + +} + + + +void CZipArchive::Open(LPCTSTR szPathName, int iMode, int iVolumeSize) +{ + if (!IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive already opened.\n"),__FILE__,__LINE__); + return; + } + m_storage.Open(szPathName, iMode, iVolumeSize); + OpenInternal(iMode); +} + +void CZipArchive::Open(CZipMemFile& mf,int iMode) +{ + if (!IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive already opened.\n"),__FILE__,__LINE__); + return; + } + if (iMode != zipOpen && iMode != zipOpenReadOnly && iMode != zipCreate) + { + TRACE(_T("%s(%i) : Mode not supported.\n"),__FILE__,__LINE__); + return; + } + m_storage.Open(mf, iMode); + OpenInternal(iMode); +} + + +void CZipArchive::OpenInternal(int iMode) +{ + m_pszPassword.Release(); + m_iFileOpened = nothing; + m_centralDir.Init(); + m_iArchiveSystCompatib = ZipPlatform::GetSystemID(); + m_szRootPath.Empty(); + if ((iMode == zipOpen) ||(iMode == 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; + } + } + +} + + +bool CZipArchive::IsClosed(bool bArchive) const +{ + return bArchive ?(m_storage.GetCurrentDisk() == -1):(!m_storage.m_pFile || m_storage.m_pFile->IsClosed()); +} + + +void CZipArchive::ThrowError(int err, bool bZlib) +{ + if (bZlib) + err = CZipException::ZlibErrToZip(err); + CZipException::Throw(err, IsClosed() ? _T("") : (LPCTSTR)m_storage.m_pFile->GetFilePath()); +} + + + +bool CZipArchive::GetFileInfo(CZipFileHeader & fhInfo, WORD uIndex) const +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return false; + } + + if (!m_centralDir.IsValidIndex(uIndex)) + return false; + + fhInfo = *(m_centralDir[uIndex]); + m_centralDir.ConvertFileName(true, false, &fhInfo); + return true; +} + +int CZipArchive::FindFile(LPCTSTR lpszFileName, int iCaseSensitive, bool bFileNameOnly) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return (int)-1; + } + 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(WORD uIndex) +{ + if (!m_centralDir.IsValidIndex(uIndex)) + { + ASSERT(FALSE); + return false; + } + if (m_storage.IsSpanMode() == 1) + { + TRACE(_T("%s(%i) : You cannot extract from the span in creation.\n"),__FILE__,__LINE__); + return false; + } + + + if (m_iFileOpened) + { + TRACE(_T("%s(%i) : A file already opened.\n"),__FILE__,__LINE__); + return false; + } + + m_info.Init(); + m_centralDir.OpenFile(uIndex); + if (CurrentFile()->IsEncrypted()) + { + + if (m_pszPassword.GetSize() == 0) + { + TRACE(_T("%s(%i) : Password not set for the encrypted file.\n"),__FILE__,__LINE__); + ThrowError(CZipException::badPassword); + } + CryptInitKeys(); + if (!CryptCheck()) + ThrowError(CZipException::badPassword); + + } + else if (m_pszPassword.GetSize() != 0) + { + TRACE(_T("%s(%i) : Password set for a not encrypted file. Ignoring password.\n"),__FILE__,__LINE__); + } + + WORD uMethod = CurrentFile()->m_uMethod; + + if ((uMethod != 0) &&(uMethod != Z_DEFLATED)) + ThrowError(CZipException::badZipFile); + + if (uMethod == Z_DEFLATED) + { +// m_info.m_stream.opaque = m_bDetectZlibMemoryLeaks ? &m_list : 0; + int err = inflateInit2(&m_info.m_stream, -MAX_WBITS); + // * windowBits is passed < 0 to tell that there is no zlib header. + // * Note that in this case inflate *requires* an extra "dummy" byte + // * after the compressed stream in order to complete decompression and + // * return Z_STREAM_END. + CheckForError(err); + } + m_info.m_uComprLeft = CurrentFile()->m_uComprSize; + if (CurrentFile()->IsEncrypted()) + m_info.m_uComprLeft -= ZIPARCHIVE_ENCR_HEADER_LEN; + m_info.m_uUncomprLeft = CurrentFile()->m_uUncomprSize; + m_info.m_uCrc32 = 0; + m_info.m_stream.total_out = 0; + m_info.m_stream.avail_in = 0; + + m_iFileOpened = extract; + return true; +} + + +int CZipArchive::GetLocalExtraField(char *pBuf, int iSize)const +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return -1; + } + + if (m_iFileOpened != extract) + { + TRACE(_T("%s(%i) : A file must be opened to get the local extra field.\n"),__FILE__,__LINE__); + return -1; + } + + int size = m_centralDir.m_pLocalExtraField.GetSize(); + if (!pBuf|| !size) + return size; + + if (iSize < size) + size = iSize; + + memcpy(pBuf, m_centralDir.m_pLocalExtraField, size); + return size; +} + +/* +void* CZipArchive::_zliballoc(void* opaque, UINT items, UINT size) +{ + void* p = new char[size * items]; + if (opaque) + { + CZipPtrList* list = (CZipPtrList*) opaque; + list->AddTail(p); + } + return p; +} + +void CZipArchive::_zlibfree(void* opaque, void* address) +{ + if (opaque) + { + CZipPtrList* list = (CZipPtrList*) opaque; + CZipPtrListIter iter = list->Find(address); + if (list->IteratorValid(iter)) + list->RemoveAt(iter); + } + delete[] (char*) address; +} +*/ + +void CZipArchive::CheckForError(int iErr) +{ + if ((iErr == Z_OK) ||(iErr == Z_NEED_DICT)) + return; + + ThrowError(iErr, true); +} + +CZipFileHeader* CZipArchive::CurrentFile() +{ + ASSERT(m_centralDir.m_pOpenedFile); + return m_centralDir.m_pOpenedFile; +} + +DWORD CZipArchive::ReadFile(void *pBuf, + DWORD iSize) +{ + if (m_iFileOpened != extract) + { + TRACE(_T("%s(%i) : Current file must be opened.\n"),__FILE__,__LINE__); + return 0; + } + + if (!pBuf || !iSize) + return 0; + + m_info.m_stream.next_out = (Bytef*)pBuf; + m_info.m_stream.avail_out = iSize > m_info.m_uUncomprLeft + ? m_info.m_uUncomprLeft : iSize; + + + DWORD iRead = 0; + + // may happen when the file is 0 sized + bool bForce = m_info.m_stream.avail_out == 0 && m_info.m_uComprLeft > 0; + while (m_info.m_stream.avail_out > 0 || (bForce && m_info.m_uComprLeft > 0)) + { + if ((m_info.m_stream.avail_in == 0) && + (m_info.m_uComprLeft >= 0)) // Also when there are zero bytes left! + { + DWORD uToRead = m_info.m_pBuffer.GetSize(); + if (m_info.m_uComprLeft < uToRead) + uToRead = m_info.m_uComprLeft; + + if (uToRead == 0) + { + uToRead = 1; // Add dummy byte at end of compressed data. + } + else + { + m_storage.Read(m_info.m_pBuffer, uToRead, false); + CryptDecodeBuffer(uToRead); + } + + m_info.m_uComprLeft -= uToRead; + + m_info.m_stream.next_in = (Bytef*)(char*)m_info.m_pBuffer; + m_info.m_stream.avail_in = uToRead; + } + + if (CurrentFile()->m_uMethod == 0) + { + DWORD uToCopy = m_info.m_stream.avail_out < m_info.m_stream.avail_in + ? m_info.m_stream.avail_out : m_info.m_stream.avail_in; + + memcpy(m_info.m_stream.next_out, m_info.m_stream.next_in, uToCopy); + + m_info.m_uCrc32 = crc32(m_info.m_uCrc32, m_info.m_stream.next_out, uToCopy); + + m_info.m_uUncomprLeft -= uToCopy; + m_info.m_stream.avail_in -= uToCopy; + m_info.m_stream.avail_out -= uToCopy; + m_info.m_stream.next_out += uToCopy; + m_info.m_stream.next_in += uToCopy; + m_info.m_stream.total_out += uToCopy; + iRead += uToCopy; + } + else + { + DWORD uTotal = m_info.m_stream.total_out; + Bytef* pOldBuf = m_info.m_stream.next_out; + int err = inflate(&m_info.m_stream, Z_SYNC_FLUSH); + DWORD uToCopy = m_info.m_stream.total_out - uTotal; + + m_info.m_uCrc32 = crc32(m_info.m_uCrc32, pOldBuf, uToCopy); + + m_info.m_uUncomprLeft -= uToCopy; + iRead += uToCopy; + + if (err == Z_STREAM_END) + return iRead; + + CheckForError(err); + } + } + + return iRead; +} + +void CZipArchive::Close(int iAfterException, bool bUpdateTimeStamp) +{ + // if after an exception - the archive may be closed, but the file may be opened + if (IsClosed() && (!iAfterException || IsClosed(false))) + { + TRACE(_T("%s(%i) : ZipArchive is already closed.\n"),__FILE__,__LINE__); + return; + } + + if (m_iFileOpened == extract) + CloseFile(NULL, iAfterException != afNoException); + + if (m_iFileOpened == compress) + CloseNewFile(iAfterException != afNoException); + + if (iAfterException != afAfterException && !IsClosed(false)) // in disk spanning when user aborts + WriteCentralDirectory(false); // we will flush in CZipStorage::Close + + time_t tNewestTime = 0; + + if (bUpdateTimeStamp) + { + int iSize = m_centralDir.m_headers.GetSize(); + for (int i = 0; i< iSize; i++) + { + time_t tFileInZipTime = m_centralDir[i]->GetTime(); + if (tFileInZipTime > tNewestTime) + tNewestTime = tFileInZipTime; + } + } + m_centralDir.Clear(); + CZipString szFileName = m_storage.Close(iAfterException == afAfterException); + if (bUpdateTimeStamp && !szFileName.IsEmpty()) + ZipPlatform::SetFileModTime(szFileName, tNewestTime); +} + +void CZipArchive::WriteCentralDirectory(bool bFlush) +{ + m_centralDir.Write(GetCallback(cbSave)); + if (bFlush) + m_storage.Flush(); +} + +void CZipArchive::SetCallback(CZipActionCallback* pCallback, int iWhich) +{ + CallbackType cbs[] = {cbAdd, cbAddTmp, cbAddStore,cbExtract,cbDeleteCnt,cbDelete,cbTest,cbSave, cbGetFromArchive, cbRename, cbReplace}; + int iCount = sizeof(cbs)/sizeof(CallbackType); + for (int i = 0; i < iCount; i++) + { + CallbackType iCallback = cbs[i]; + if (iWhich & iCallback) + m_callbacks.Set(pCallback, iCallback); + } +} + +void CZipArchive::SetAdvanced(int iWriteBuffer, int iGeneralBuffer, int iSearchBuffer) +{ + if (!IsClosed()) + { + TRACE(_T("%s(%i) : Set this options before opening the archive.\n"),__FILE__,__LINE__); + return; + } + + m_storage.m_iWriteBufferSize = iWriteBuffer < 1024 ? 1024 : iWriteBuffer; + m_info.m_iBufferSize = iGeneralBuffer < 1024 ? 1024 : iGeneralBuffer; + m_centralDir.m_iBufferSize = iSearchBuffer < 1024 ? 1024 : iSearchBuffer; +} + +int CZipArchive::CloseFile(CZipFile &file) +{ + CZipString temp = file.GetFilePath(); + file.Close(); + return CloseFile(temp); +} + +int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException) +{ + if (m_iFileOpened != extract) + { + TRACE(_T("%s(%i) : No opened file.\n"),__FILE__,__LINE__); + return false; + } + + int iRet = 1; + if (!bAfterException) + { + if (m_info.m_uUncomprLeft == 0) + { + if (!m_bIgnoreCRC && m_info.m_uCrc32 != CurrentFile()->m_uCrc32) + ThrowError(CZipException::badCrc); + } + else + iRet = -1; + + + if (CurrentFile()->m_uMethod == Z_DEFLATED) + inflateEnd(&m_info.m_stream); + + + if (lpszFilePath) + { + + if (!ZipPlatform::SetFileModTime(lpszFilePath, CurrentFile()->GetTime()) + ||!ZipPlatform::SetFileAttr(lpszFilePath, CurrentFile()->GetSystemAttr())) + iRet = -2; + } + + } + + m_centralDir.CloseFile(bAfterException); + + m_iFileOpened = nothing; + m_info.ReleaseBuf(); + EmptyPtrList(); + return iRet; +} + +bool CZipArchive::OpenNewFile(CZipFileHeader & header, + int iLevel, + LPCTSTR lpszFilePath, DWORD uInternal) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return false; + } + + if (m_iFileOpened) + { + TRACE(_T("%s(%i) : A file already opened.\n"),__FILE__,__LINE__); + return false; + } + + if (m_storage.IsSpanMode() == -1) + { + TRACE(_T("%s(%i) : You cannot add files to the existing disk spannig archive.\n"),__FILE__,__LINE__); + return false; + } + + if (GetCount() ==(WORD)USHRT_MAX) + { + TRACE(_T("%s(%i) : Maximum file count inside archive reached.\n"),__FILE__,__LINE__); + return false; + } + + DWORD uAttr = 0; // ..compiler + time_t ttime; + if (lpszFilePath) + { + + if (!ZipPlatform::GetFileAttr(lpszFilePath, uAttr)) + // do not continue - if the file was a directory then not recognizing it will cause + // serious errors (need uAttr to recognize it) + return false; + if (!ZipPlatform::GetFileModTime(lpszFilePath, ttime)) + ttime = time(NULL); + } + + m_info.Init(); + + + if (lpszFilePath) + { + header.SetTime(ttime); + SetFileHeaderAttr(header, uAttr); // set system compatibility as well + } + else + header.SetSystemCompatibility(m_iArchiveSystCompatib); + + 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%i"), GetCount()); + header.SetFileName(szFileName); + } + + // make sure that all slashes are correct (as the current system default) + // because AddNewFile calls InsertFindFastElement if necessary and + // the find array keeps all the files already converted to the current system standards + // we do not perform Oem->Ansi here, because who would pass oem values here? + // + ZipCompatibility::SlashBackslashChg(header.m_pszFileName, true); + + bool bEncrypted = m_pszPassword.GetSize() != 0; + +#ifdef _DEBUG + if (bIsDirectory && bEncrypted) + TRACE(_T("%s(%i) : Encrypting a directory. It's pointless.\n\ + Clear the password before adding a directory.\n"),__FILE__,__LINE__); +#endif + + + + int iReplaceIndex = -1; + bool bReplace = (iLevel & 0xffff) == ZIP_COMPR_REPL_SIGN; + if (bReplace) + { + int iMask = ZIP_COMPR_REPL_MASK; + iReplaceIndex = (iLevel & iMask) >> 16; + iLevel = (char)(iLevel & ~iMask); + ASSERT(iLevel == 0); + } + else + uInternal = 0; + + if (iLevel < -1 || iLevel > 9) + iLevel = -1; + + if (!header.PrepareData(iLevel, m_storage.IsSpanMode() == 1, bEncrypted)) + ThrowError(CZipException::tooLongFileName); + + if (bReplace) + { + uInternal += header.GetSize(true); + if (header.IsEncrypted()) + uInternal += ZIPARCHIVE_ENCR_HEADER_LEN; + if (header.IsDataDescr()) + uInternal += ZIPARCHIVE_DATADESCRIPTOR_LEN + 4; // CZipCentralDir::CloseNewFile + } + m_centralDir.AddNewFile(header, iReplaceIndex); + if (bReplace) + MakeSpaceForReplace(iReplaceIndex, uInternal, szFileName); + + // this ensures the conversion will take place anyway (must take because we are going + // to write the local header in a moment + m_centralDir.ConvertFileName(false, m_centralDir.m_bConvertAfterOpen); + + CurrentFile()->WriteLocal(m_storage); + + // we have written the local header, but if we keep filenames not converted + // in memory , we have to restore the non-converted value + if (m_centralDir.m_bConvertAfterOpen) + CurrentFile()->SetFileName(szFileName); + + if (bEncrypted) + { + CZipAutoBuffer buf(ZIPARCHIVE_ENCR_HEADER_LEN); + // use pseudo-crc since we don't know it yet + CryptCryptHeader((long)header.m_uModTime << 16, buf); + m_storage.Write(buf, ZIPARCHIVE_ENCR_HEADER_LEN, false); + } + + + m_info.m_uComprLeft = 0; + m_info.m_stream.avail_in = (uInt)0; + m_info.m_stream.avail_out = (uInt)m_info.m_pBuffer.GetSize(); + m_info.m_stream.next_out = (Bytef*)(char*)m_info.m_pBuffer; + m_info.m_stream.total_in = 0; + m_info.m_stream.total_out = 0; + + if (bIsDirectory && (CurrentFile()->m_uMethod != 0)) + CurrentFile()->m_uMethod = 0; + + if (CurrentFile()->m_uMethod == Z_DEFLATED) + { +// m_info.m_stream.opaque = m_bDetectZlibMemoryLeaks ? &m_list : 0; + + int err = deflateInit2(&m_info.m_stream, iLevel, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + + CheckForError(err); + } + m_iFileOpened = compress; + return true; +} + + +bool CZipArchive::ExtractFile(WORD uIndex, + LPCTSTR lpszPath, + bool bFullPath, + LPCTSTR lpszNewName, + DWORD nBufSize) +{ + + if (!nBufSize && !lpszPath) + return false; + + CZipFileHeader header; + GetFileInfo(header, uIndex); // to ensure that slash and oem conversions take place + CZipString szFileNameInZip = (LPCTSTR)header.GetFileName(); + CZipString szFile = PredictExtractedFileName(szFileNameInZip, lpszPath, bFullPath, lpszNewName); + CZipActionCallback* pCallback = GetCallback(cbExtract); + if (pCallback) + pCallback->Init(szFileNameInZip, szFile); + + + if (header.IsDirectory()) + { + if (pCallback) + pCallback->SetTotal(0); // in case of calling LeftToDo afterwards + + ZipPlatform::ForceDirectory(szFile); + ZipPlatform::SetFileAttr(szFile, header.GetSystemAttr()); + + if (pCallback) + pCallback->CallbackEnd(); + return true; + } + else + { + if (pCallback) + pCallback->SetTotal(header.m_uUncomprSize); + + if (!OpenFile(uIndex)) + return false; + + CZipPathComponent zpc(szFile); + ZipPlatform::ForceDirectory(zpc.GetFilePath()); + CZipFile f(szFile, CZipFile::modeWrite | + CZipFile::modeCreate | CZipFile::shareDenyWrite); + DWORD iRead; + CZipAutoBuffer buf(nBufSize); + int iAborted = 0; + do + { + iRead = ReadFile(buf, buf.GetSize()); + if (iRead) + { + f.Write(buf, iRead); + if (pCallback) + if (!(*pCallback)(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; + } + + } + } + while (iRead == buf.GetSize()); + bool bRet = CloseFile(f) == 1; + if (!bRet && iAborted == CZipException::abortedSafely) + iAborted = CZipException::abortedAction; // sorry, finished, but not successfull + + if (pCallback) + pCallback->CallbackEnd(); + + if (iAborted) + CZipException::Throw(iAborted, szFile); // throw to distuingiush from other return codes + return bRet; + + } +} + +bool CZipArchive::ExtractFile(WORD uIndex, + CZipMemFile& mf, + DWORD nBufSize) +{ + if (!nBufSize) + return false; + + CZipFileHeader header; + GetFileInfo(header, uIndex); // to ensure that slash and oem conversions take place + CZipActionCallback* pCallback = GetCallback(cbExtract); + if (pCallback) + { + pCallback->Init(header.GetFileName()); + pCallback->SetTotal(header.m_uUncomprSize); + } + + if (header.IsDirectory() || !OpenFile(uIndex)) + return false; + + + + + CZipAutoBuffer buf(nBufSize); + mf.SeekToEnd(); + DWORD iRead; + int iAborted = 0; + do + { + iRead = ReadFile(buf, buf.GetSize()); + if (iRead) + { + mf.Write(buf, iRead); + if (pCallback) + if (!(*pCallback)(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; + } + } + } + while (iRead == buf.GetSize()); + bool bRet = CloseFile() == 1; + if (!bRet && iAborted == CZipException::abortedSafely) + iAborted = CZipException::abortedAction; // sorry, finished, but not successfull + + if (pCallback) + pCallback->CallbackEnd(); + + if (iAborted) + CZipException::Throw(iAborted); // throw to distuingiush from other return codes + return bRet; +} + + +void CZipArchive::SetExtraField(const char *pBuf, WORD iSize) +{ + if (m_iFileOpened != compress) + { + TRACE(_T("%s(%i) : A new file must be opened.\n"),__FILE__,__LINE__); + return; + } + if (!pBuf || !iSize) + return; + + CurrentFile()->m_pExtraField.Allocate(iSize); + memcpy(CurrentFile()->m_pExtraField, pBuf, iSize); +} + +bool CZipArchive::WriteNewFile(const void *pBuf, DWORD iSize) +{ + if (m_iFileOpened != compress) + { + TRACE(_T("%s(%i) : A new file must be opened.\n"),__FILE__,__LINE__); + return false; + } + + + m_info.m_stream.next_in = (Bytef*)pBuf; + m_info.m_stream.avail_in = iSize; + CurrentFile()->m_uCrc32 = crc32(CurrentFile()->m_uCrc32, (Bytef*)pBuf, iSize); + + + while (m_info.m_stream.avail_in > 0) + { + if (m_info.m_stream.avail_out == 0) + { + CryptEncodeBuffer(); + m_storage.Write(m_info.m_pBuffer, m_info.m_uComprLeft, false); + m_info.m_uComprLeft = 0; + m_info.m_stream.avail_out = m_info.m_pBuffer.GetSize(); + m_info.m_stream.next_out = (Bytef*)(char*)m_info.m_pBuffer; + } + + if (CurrentFile()->m_uMethod == Z_DEFLATED) + { + DWORD uTotal = m_info.m_stream.total_out; + int err = deflate(&m_info.m_stream, Z_NO_FLUSH); + CheckForError(err); + m_info.m_uComprLeft += m_info.m_stream.total_out - uTotal; + } + else + { + DWORD uToCopy = (m_info.m_stream.avail_in < m_info.m_stream.avail_out) + ? m_info.m_stream.avail_in : m_info.m_stream.avail_out; + + memcpy(m_info.m_stream.next_out, m_info.m_stream.next_in, uToCopy); + + m_info.m_stream.avail_in -= uToCopy; + m_info.m_stream.avail_out -= uToCopy; + m_info.m_stream.next_in += uToCopy; + m_info.m_stream.next_out += uToCopy; + m_info.m_stream.total_in += uToCopy; + m_info.m_stream.total_out += uToCopy; + m_info.m_uComprLeft += uToCopy; + } + } + + return true; +} + +bool CZipArchive::CloseNewFile(bool bAfterException) +{ + if (m_iFileOpened != compress) + { + TRACE(_T("%s(%i) : A new file must be opened.\n"),__FILE__,__LINE__); + return false; + } + + m_info.m_stream.avail_in = 0; + if (!bAfterException) + { + int err = Z_OK; + if (CurrentFile()->m_uMethod == Z_DEFLATED) + while (err == Z_OK) + { + if (m_info.m_stream.avail_out == 0) + { + CryptEncodeBuffer(); + m_storage.Write(m_info.m_pBuffer, m_info.m_uComprLeft, false); + m_info.m_uComprLeft = 0; + m_info.m_stream.avail_out = m_info.m_pBuffer.GetSize(); + m_info.m_stream.next_out = (Bytef*)(char*)m_info.m_pBuffer; + } + DWORD uTotal = m_info.m_stream.total_out; + err = deflate(&m_info.m_stream, Z_FINISH); + m_info.m_uComprLeft += m_info.m_stream.total_out - uTotal; + } + + if (err == Z_STREAM_END) + err = Z_OK; + CheckForError(err); + + if (m_info.m_uComprLeft > 0) + { + CryptEncodeBuffer(); + m_storage.Write(m_info.m_pBuffer, m_info.m_uComprLeft, false); + } + + if (CurrentFile()->m_uMethod == Z_DEFLATED) + { + err = deflateEnd(&m_info.m_stream); + CheckForError(err); + } + + + // it may be increased by the encrypted header size + CurrentFile()->m_uComprSize += m_info.m_stream.total_out; + CurrentFile()->m_uUncomprSize = m_info.m_stream.total_in; + + m_centralDir.CloseNewFile(); + } + else + m_centralDir.m_pOpenedFile = NULL; + + m_iFileOpened = nothing; + m_info.ReleaseBuf(); + EmptyPtrList(); + + if (m_bAutoFlush && !bAfterException) + Flush(); + + return true; +} + +void CZipArchive::DeleteFile(WORD uIndex) +{ + CZipWordArray indexes; + indexes.Add(uIndex); + DeleteFiles(indexes); +} + +void CZipArchive::GetIndexes(const CZipStringArray &aNames, CZipWordArray& aIndexes) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return; + } + int iSize = aNames.GetSize(); + for (WORD i = 0; i < iSize; i++) + { + int idx = FindFile(aNames[i], ffDefault, false); + if (idx != -1) + aIndexes.Add((WORD)idx); + } +} + +void CZipArchive::DeleteFiles(const CZipStringArray &aNames) +{ + CZipWordArray indexes; + GetIndexes(aNames, indexes); + DeleteFiles(indexes); +} + + +void CZipArchive::DeleteFiles(CZipWordArray &aIndexes) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return; + } + + if (m_storage.IsSpanMode()) + { + TRACE(_T("%s(%i) : You cannot delete files from the disk spannig archive.\n"),__FILE__,__LINE__); + return; + } + + if (m_iFileOpened) + { + TRACE(_T("%s(%i) : You cannot delete files if there is a file opened.\n"),__FILE__,__LINE__); + return; + } + + CZipActionCallback* pCallback = GetCallback(cbDeleteCnt); + if (pCallback) + pCallback->Init(); + + int uSize = aIndexes.GetSize(); + if (!uSize) + { + TRACE(_T("%s(%i) : The indekses array is empty.\n"),__FILE__,__LINE__); + return; + } + + // remove all - that's easy so don't waste the time + if (uSize == GetCount()) + { + pCallback = GetCallback(cbDelete); + if (pCallback) + { + // do it right and sent the notification + pCallback->Init(); + pCallback->SetTotal(uSize); + } + + m_centralDir.RemoveFromDisk(); + m_storage.m_pFile->SetLength(m_centralDir.GetBytesBefore()); + m_centralDir.RemoveAll(); + if (m_bAutoFlush) + Flush(); + if (pCallback) + pCallback->CallbackEnd(); + return; + } + + aIndexes.Sort(true); + + CZipArray aInfo; + + int iDelIndex = 0; + + + int iStep = 0; // for the compiler + if (pCallback) + { + pCallback->SetTotal(GetCount()); + iStep = CZipActionCallback::m_iStep; // we don't want to wait forever + } + + int i; + int uMaxDelIndex = aIndexes[uSize - 1]; + for (i = aIndexes[0]; i < GetCount(); i++) + { + CZipFileHeader* pHeader = m_centralDir[i]; + bool bDelete; + if (i <= uMaxDelIndex && i == aIndexes[iDelIndex]) + { + iDelIndex++; + bDelete = true; + } + else + bDelete = false; + aInfo.Add(CZipDeleteInfo(pHeader, bDelete)); + if (pCallback && (!(i % iStep))) + if (!(*pCallback)(iStep)) + ThrowError(CZipException::abortedSafely); + } + ASSERT(iDelIndex == uSize); + + uSize = aInfo.GetSize(); + if (!uSize) // it is possible + return; + + // now we start deleting (not safe to break) + pCallback = GetCallback(cbDelete); + if (pCallback) + pCallback->Init(); + + + m_centralDir.RemoveFromDisk(); + + DWORD uTotalToMoveBytes = 0, uLastOffset = m_storage.m_pFile->GetLength() - m_centralDir.GetBytesBefore(); + // count the number of bytes to move + for (i = uSize - 1; i >=0 ; i--) + { + const CZipDeleteInfo& di = aInfo[i]; + if (!di.m_bDelete) + uTotalToMoveBytes += uLastOffset - di.m_pHeader->m_uOffset; + uLastOffset = di.m_pHeader->m_uOffset; + } + if (pCallback) + pCallback->CallbackEnd(); + + + if (pCallback) + pCallback->SetTotal(uTotalToMoveBytes); + + + m_info.Init(); + + DWORD uMoveBy = 0, uOffsetStart = 0; + for (i = 0; i < uSize; i++) + { + const CZipDeleteInfo& di = aInfo[i]; + + if (di.m_bDelete) + { + // next hole + DWORD 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); + uOffsetStart = 0; // never be at the beginning, because the first file is always to be deleted + } + if (i == uSize - 1) + uTemp = (m_storage.m_pFile->GetLength() - m_centralDir.GetBytesBefore()) - uTemp; + else + uTemp = aInfo[i+1].m_pHeader->m_uOffset - uTemp; + + uMoveBy += uTemp; + + } + else + { + if (uOffsetStart == 0) // find contiuos area to move + uOffsetStart = di.m_pHeader->m_uOffset; + di.m_pHeader->m_uOffset -= uMoveBy; + } + + } + if (uOffsetStart) + MovePackedFiles(uOffsetStart, + m_storage.m_pFile->GetLength() - m_centralDir.GetBytesBefore(), + uMoveBy, pCallback); + + m_info.ReleaseBuf(); + if (uMoveBy) // just in case + m_storage.m_pFile->SetLength(m_storage.m_pFile->GetLength() - uMoveBy); + + if (pCallback) + pCallback->CallbackEnd(); + + if (m_bAutoFlush) + Flush(); +} + + + +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 = zipsmSafeSmart; + zanfi.m_nBufSize = nBufSize; + return AddNewFile(zanfi); +} + +bool CZipArchive::AddNewFileDrv(LPCTSTR lpszFilePath, + int iComprLevel, + bool bFullPath, + int iSmartLevel, + unsigned long nBufSize) +{ + + CZipAddNewFileInfo zanfi (lpszFilePath, bFullPath); + zanfi.m_iComprLevel = iComprLevel; + zanfi.m_iSmartLevel = zipsmSafeSmart; + zanfi.m_nBufSize = nBufSize; + return AddNewFileDrv(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 = zipsmSafeSmart; + zanfi.m_nBufSize = nBufSize; + return AddNewFile(zanfi); +} + +bool CZipArchive::AddNewFile(CZipMemFile& mf, + LPCTSTR lpszFileNameInZip, + int iComprLevel, + int iSmartLevel, + unsigned long nBufSize) +{ + CZipAddNewFileInfo zanfi(&mf, lpszFileNameInZip); + zanfi.m_iComprLevel = iComprLevel; + zanfi.m_iSmartLevel = zipsmSafeSmart; + zanfi.m_nBufSize = nBufSize; + return AddNewFile(zanfi); +} + + +bool CZipArchive::AddNewFileDrv(CZipAddNewFileInfo& info) +{ + // no need for ASSERT and TRACE here - it will be done by OpenNewFile + + if (!m_info.m_iBufferSize) + return false; + CZipPathComponent::RemoveSeparators(info.m_szFilePath); + if (!info.m_szFilePath.IsEmpty()) // it may be empty after removing sep. + { + if (info.m_szFileNameInZip.IsEmpty()) + { + CZipPathComponent zpc(info.m_szFilePath); + info.m_szFileNameInZip = info.m_bFullPath ? zpc.GetNoDrive() : TrimRootPath(zpc); + } + } + else if (!info.m_pFile) + return false; + + bool bSpan = GetSpanMode() != 0; + + // checking the iReplace index + if (!UpdateReplaceIndex(info.m_iReplaceIndex, info.m_szFileNameInZip)) + return false; + + bool bReplace = info.m_iReplaceIndex >= 0; + + DWORD uAttr; + time_t ttime; + if (info.m_pFile) + { + uAttr = ZipPlatform::GetDefaultAttributes(); + ttime = time(NULL); + } + else + { + if (!ZipPlatform::GetFileAttr(info.m_szFilePath, uAttr)) + return false; // we don't know whether it is a file or a directory + if (!ZipPlatform::GetFileModTime(info.m_szFilePath, ttime)) + ttime = time(NULL); + } + CZipFileHeader header; + header.SetFileName(info.m_szFileNameInZip); + if (ZipPlatform::GetSystemID() != ZipCompatibility::zcUnix) + uAttr |= ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), ZipCompatibility::zcUnix); // make it readable under Unix as well, since it stores its attributes in HIWORD(uAttr) + SetFileHeaderAttr(header, uAttr); + header.SetTime(ttime); + bool bInternal = (info.m_iSmartLevel & zipsmInternal01) != 0; + CZipActionCallback* pCallback = NULL; + if (!bInternal) + { + pCallback = GetCallback(cbAdd); + if (pCallback) + pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath); + } + + + + 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); + + if (pCallback) + pCallback->SetTotal(0); // in case of calling LeftToDo afterwards + + // clear password for a directory + bool bRet = false; + CZipSmClrPass smcp; + if (info.m_iSmartLevel & zipsmCPassDir) + smcp.ClearPasswordSmartly(this); + + bRet = OpenNewFile(header, bReplace ? (info.m_iReplaceIndex << 16) | ZIP_COMPR_REPL_SIGN : 0); + + CloseNewFile(); + if (pCallback) + pCallback->CallbackEnd(); + + return bRet; + } + + CZipSmClrPass smcp; + bool bIsCompression = info.m_iComprLevel != 0; + bool bEff = (info.m_iSmartLevel & zipsmCheckForEff)&& bIsCompression; + bool bCheckForZeroSized = (info.m_iSmartLevel & zipsmCPFile0) && !GetPassword().IsEmpty(); + bool bCheckForSmallFiles = (info.m_iSmartLevel & zipsmNotCompSmall) && bIsCompression; + DWORD iFileSize = DWORD(-1); + bool bNeedTempArchive = (bEff && bSpan) || (bReplace && bIsCompression); + if (bCheckForSmallFiles || bCheckForZeroSized || bNeedTempArchive) + { + + if (info.m_pFile) + iFileSize = info.m_pFile->GetLength(); + else + { + if (!ZipPlatform::GetFileSize(info.m_szFilePath, iFileSize) && bEff) + bEff = false; // the file size is needed only when eff. in span mode + } + if (iFileSize != DWORD(-1)) + { + if (bCheckForZeroSized && iFileSize == 0) + smcp.ClearPasswordSmartly(this); + if (bCheckForSmallFiles && iFileSize < 5) + info.m_iComprLevel = 0; + } + } + bool bEffInMem = bEff && (info.m_iSmartLevel & zipsmMemoryFlag); + CZipString szTempFileName; + if (bNeedTempArchive && (bEffInMem || + !(szTempFileName = ZipPlatform::GetTmpFileName( + m_szTempPath.IsEmpty() ? NULL : (LPCTSTR)m_szTempPath, iFileSize) + ).IsEmpty())) + { + CZipMemFile* pmf = NULL; + CZipArchive zip; + try + { + // compress first to a temporary file, if ok - copy the data, if not - add storing + + if (bEffInMem) + { + pmf = new CZipMemFile; + zip.Open(*pmf, zipCreate); + } + else + zip.Open(szTempFileName, zipCreate); + zip.SetRootPath(m_szRootPath); + zip.SetPassword(GetPassword()); + zip.SetSystemCompatibility(m_iArchiveSystCompatib); + zip.SetCallback(pCallback, cbAdd); + // create a temporary file + int iTempReplaceIndex = info.m_iReplaceIndex; + info.m_iSmartLevel = zipsmLazy; + info.m_iReplaceIndex = -1; + if (!zip.AddNewFile(info)) + throw false; + info.m_iReplaceIndex = iTempReplaceIndex; + + // this may also happen when bReplace, but not in span mode + if (bEff) + { + CZipFileHeader fh; + zip.GetFileInfo(fh, 0); + if (!fh.CompressionEfficient()) + { + info.m_iComprLevel = 0; + info.m_iSmartLevel = zipsmInternal01; + // compression is pointless, store instead + throw AddNewFile(info); + } + } + + m_info.Init(); + throw GetFromArchive(zip, 0, info.m_iReplaceIndex, true, GetCallback(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); + m_info.ReleaseBuf(); + return bRet; + } + catch (...) + { + zip.Close(true); + + if (pmf) + delete pmf; + if (!bEffInMem) + ZipPlatform::RemoveFile(szTempFileName, false); + m_info.ReleaseBuf(); + throw; + } + } + + // try to open before adding + CZipFile f; + CZipAbstractFile *pf; + if (info.m_pFile) + pf = info.m_pFile; + else + { + if (!f.Open(info.m_szFilePath, CZipFile::modeRead | CZipFile::shareDenyWrite, false)) + { + if (pCallback) + pCallback->CallbackEnd(); + return false; + } + pf = &f; + } + + ASSERT(pf); + // call init before opening (in case of exception we have the names) + iFileSize = pf->GetLength(); + + + bool bRet; + if (bReplace) + { + ASSERT(!bIsCompression); + bRet = OpenNewFile(header, (info.m_iReplaceIndex << 16) | ZIP_COMPR_REPL_SIGN , NULL, iFileSize); + } + else + bRet = OpenNewFile(header, info.m_iComprLevel); + if (!bRet) + { + if (pCallback) + pCallback->CallbackEnd(); + + return false; + } + if (bInternal) + { + // we do it here, because if in OpenNewFile is replacing + // then we get called cbReplace callback before and it would + // overwrite callback information written in pCallback->Init + pCallback = GetCallback(cbAddStore); + if (pCallback) + pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath); + } + if (pCallback) + pCallback->SetTotal(iFileSize); + + 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) + if (!(*pCallback)(iRead)) + { + // todo: we could remove here the bytes of the file partially added if not disk-spanning + if (iRead == buf.GetSize() && pf->Read(buf, 1) != 0) // test one byte if there is something left + { + if (!m_storage.IsSpanMode() && !bReplace) + { + RemoveLast(true); + CloseNewFile(true); + iAborted = CZipException::abortedSafely; + } + else + iAborted = CZipException::abortedAction; + } + else + { + iAborted = CZipException::abortedSafely; // we did it! + CloseNewFile(); + } + break; + } + + } + + } + while (iRead == buf.GetSize()); + if (!iAborted) + CloseNewFile(); + + if (pCallback) + pCallback->CallbackEnd(); + + if (iAborted) + CZipException::Throw(iAborted); // throw to distuinguish from other return codes + + 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::AddNewFile(CZipAddNewFileInfo& info) +{ + // no need for ASSERT and TRACE here - it will be done by OpenNewFile + + if (!m_info.m_iBufferSize) + return false; + CZipPathComponent::RemoveSeparators(info.m_szFilePath); + if (!info.m_szFilePath.IsEmpty()) // it may be empty after removing sep. + { + if (info.m_szFileNameInZip.IsEmpty()) + { + CZipPathComponent zpc(info.m_szFilePath); + if (info.m_bFullPath) + { + if (m_bRemoveDriveLetter) + info.m_szFileNameInZip = zpc.GetNoDrive(); + } + else + info.m_szFileNameInZip = TrimRootPath(zpc); + } + } + else if (!info.m_pFile) + return false; + + bool bSpan = GetSpanMode() != 0; + + // checking the iReplace index + if (!UpdateReplaceIndex(info.m_iReplaceIndex, info.m_szFileNameInZip)) + return false; + + bool bReplace = info.m_iReplaceIndex >= 0; + + DWORD uAttr; + time_t ttime; + if (info.m_pFile) + { + uAttr = ZipPlatform::GetDefaultAttributes(); + ttime = time(NULL); + } + else + { + if (!ZipPlatform::GetFileAttr(info.m_szFilePath, uAttr)) + return false; // we don't know whether it is a file or a directory + if (!ZipPlatform::GetFileModTime(info.m_szFilePath, ttime)) + ttime = time(NULL); + } + CZipFileHeader header; + header.SetFileName(info.m_szFileNameInZip); + if (ZipPlatform::GetSystemID() != ZipCompatibility::zcUnix) + uAttr |= ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), ZipCompatibility::zcUnix); // make it readable under Unix as well, since it stores its attributes in HIWORD(uAttr) + SetFileHeaderAttr(header, uAttr); + header.SetTime(ttime); + bool bInternal = (info.m_iSmartLevel & zipsmInternal01) != 0; + CZipActionCallback* pCallback = NULL; + if (!bInternal) + { + pCallback = GetCallback(cbAdd); + if (pCallback) + pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath); + } + + + + 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); + + if (pCallback) + pCallback->SetTotal(0); // in case of calling LeftToDo afterwards + + // clear password for a directory + bool bRet = false; + CZipSmClrPass smcp; + if (info.m_iSmartLevel & zipsmCPassDir) + smcp.ClearPasswordSmartly(this); + + bRet = OpenNewFile(header, bReplace ? (info.m_iReplaceIndex << 16) | ZIP_COMPR_REPL_SIGN : 0); + + CloseNewFile(); + if (pCallback) + pCallback->CallbackEnd(); + + return bRet; + } + + CZipSmClrPass smcp; + bool bIsCompression = info.m_iComprLevel != 0; + bool bEff = (info.m_iSmartLevel & zipsmCheckForEff)&& bIsCompression; + bool bCheckForZeroSized = (info.m_iSmartLevel & zipsmCPFile0) && !GetPassword().IsEmpty(); + bool bCheckForSmallFiles = (info.m_iSmartLevel & zipsmNotCompSmall) && bIsCompression; + DWORD iFileSize = DWORD(-1); + bool bNeedTempArchive = (bEff && bSpan) || (bReplace && bIsCompression); + if (bCheckForSmallFiles || bCheckForZeroSized || bNeedTempArchive) + { + + if (info.m_pFile) + iFileSize = info.m_pFile->GetLength(); + else + { + if (!ZipPlatform::GetFileSize(info.m_szFilePath, iFileSize) && bEff) + bEff = false; // the file size is needed only when eff. in span mode + } + if (iFileSize != DWORD(-1)) + { + if (bCheckForZeroSized && iFileSize == 0) + smcp.ClearPasswordSmartly(this); + if (bCheckForSmallFiles && iFileSize < 5) + info.m_iComprLevel = 0; + } + } + bool bEffInMem = bEff && (info.m_iSmartLevel & zipsmMemoryFlag); + CZipString szTempFileName; + if (bNeedTempArchive && (bEffInMem || + !(szTempFileName = ZipPlatform::GetTmpFileName( + m_szTempPath.IsEmpty() ? NULL : (LPCTSTR)m_szTempPath, iFileSize) + ).IsEmpty())) + { + CZipMemFile* pmf = NULL; + CZipArchive zip; + try + { + // compress first to a temporary file, if ok - copy the data, if not - add storing + + if (bEffInMem) + { + pmf = new CZipMemFile; + zip.Open(*pmf, zipCreate); + } + else + zip.Open(szTempFileName, zipCreate); + zip.SetRootPath(m_szRootPath); + zip.SetPassword(GetPassword()); + zip.SetSystemCompatibility(m_iArchiveSystCompatib); + zip.SetCallback(pCallback, cbAdd); + // create a temporary file + int iTempReplaceIndex = info.m_iReplaceIndex; + info.m_iSmartLevel = zipsmLazy; + info.m_iReplaceIndex = -1; + if (!zip.AddNewFile(info)) + throw false; + info.m_iReplaceIndex = iTempReplaceIndex; + + // this may also happen when bReplace, but not in span mode + if (bEff) + { + CZipFileHeader fh; + zip.GetFileInfo(fh, 0); + if (!fh.CompressionEfficient()) + { + info.m_iComprLevel = 0; + info.m_iSmartLevel = zipsmInternal01; + // compression is pointless, store instead + throw AddNewFile(info); + } + } + + m_info.Init(); + throw GetFromArchive(zip, 0, info.m_iReplaceIndex, true, GetCallback(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); + m_info.ReleaseBuf(); + return bRet; + } + catch (...) + { + zip.Close(true); + if (pmf) + delete pmf; + if (!bEffInMem) + ZipPlatform::RemoveFile(szTempFileName, false); + m_info.ReleaseBuf(); + throw; + } + } + + // try to open before adding + CZipFile f; + CZipAbstractFile *pf; + if (info.m_pFile) + pf = info.m_pFile; + else + { + // cannot be shareDenyWrite + // from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/creating_and_opening_files.asp : + // 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. + if (!f.Open(info.m_szFilePath, CZipFile::modeRead | CZipFile::shareDenyNone, false)) + { + if (pCallback) + pCallback->CallbackEnd(); + return false; + } + pf = &f; + } + + ASSERT(pf); + // call init before opening (in case of exception we have the names) + iFileSize = pf->GetLength(); + + + bool bRet; + if (bReplace) + { + ASSERT(!bIsCompression); + bRet = OpenNewFile(header, (info.m_iReplaceIndex << 16) | ZIP_COMPR_REPL_SIGN , NULL, iFileSize); + } + else + bRet = OpenNewFile(header, info.m_iComprLevel); + if (!bRet) + { + if (pCallback) + pCallback->CallbackEnd(); + + return false; + } + if (bInternal) + { + // we do it here, because if in OpenNewFile is replacing + // then we get called cbReplace callback before and it would + // overwrite callback information written in pCallback->Init + pCallback = GetCallback(cbAddStore); + if (pCallback) + pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath); + } + if (pCallback) + pCallback->SetTotal(iFileSize); + + 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) + if (!(*pCallback)(iRead)) + { + // todo: we could remove here the bytes of the file partially added if not disk-spanning + if (iRead == buf.GetSize() && pf->Read(buf, 1) != 0) // test one byte if there is something left + { + if (!m_storage.IsSpanMode() && !bReplace) + { + RemoveLast(true); + CloseNewFile(true); + iAborted = CZipException::abortedSafely; + } + else + iAborted = CZipException::abortedAction; + } + else + { + iAborted = CZipException::abortedSafely; // we did it! + CloseNewFile(); + } + break; + } + + } + + } + while (iRead == buf.GetSize()); + if (!iAborted) + CloseNewFile(); + + if (pCallback) + pCallback->CallbackEnd(); + + if (iAborted) + CZipException::Throw(iAborted); // throw to distuinguish from other return codes + + 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) +{ + int iIndex = GetCount() - 1; + if (iIndex < 0) + return false; + CZipFileHeader* pHeader = m_centralDir[iIndex]; + + if (!bRemoveAnyway && pHeader->CompressionEfficient()) + return false; + + m_centralDir.RemoveLastFile(pHeader, iIndex); + return true; +} + + + + +CZipString CZipArchive::GetArchivePath() const +{ + if (IsClosed(false)) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return _T(""); + } + return m_storage.m_pFile->GetFilePath(); +} + +CZipString CZipArchive::GetGlobalComment() const +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return _T(""); + } + CZipString temp; + return SingleToWide(m_centralDir.m_pszComment, temp) != -1 ? (LPCTSTR)temp : _T(""); +} + +bool CZipArchive::SetGlobalComment(LPCTSTR lpszComment) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return false; + } + if (m_storage.IsSpanMode() == -1) + { + TRACE(_T("%s(%i) : You cannot modify the global comment of the existing disk spanning archive.\n"),__FILE__,__LINE__); + return false; + } + + WideToSingle(lpszComment, m_centralDir.m_pszComment); + m_centralDir.RemoveFromDisk(); + if (m_bAutoFlush) + Flush(); + + return true; +} + + + +int CZipArchive::GetCurrentDisk() const +{ + return m_storage.GetCurrentDisk() + 1; +} + +bool CZipArchive::SetFileComment(WORD uIndex, LPCTSTR lpszComment) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return false; + } + if (m_storage.IsSpanMode() == -1) + { + TRACE(_T("%s(%i) : You cannot modify the file comment in the existing disk spanning archive.\n"),__FILE__,__LINE__); + return false; + } + + if (!m_centralDir.IsValidIndex(uIndex)) + { + ASSERT(FALSE); + return false; + } + m_centralDir[uIndex]->SetComment(lpszComment); + m_centralDir.RemoveFromDisk(); + if (m_bAutoFlush) + Flush(); + return true; +} + + +void CZipArchive::CryptInitKeys() +{ + ASSERT(m_pszPassword.GetSize()); + m_keys[0] = 305419896L; + m_keys[1] = 591751049L; + m_keys[2] = 878082192L; + for (DWORD i = 0; i < m_pszPassword.GetSize(); i++) + CryptUpdateKeys(m_pszPassword[i]); +} + +void CZipArchive::CryptUpdateKeys(char c) +{ + + m_keys[0] = CryptCRC32(m_keys[0], c); + m_keys[1] += m_keys[0] & 0xff; + m_keys[1] = m_keys[1] * 134775813L + 1; + c = char(m_keys[1] >> 24); + m_keys[2] = CryptCRC32(m_keys[2], c); +} + +bool CZipArchive::CryptCheck() +{ + CZipAutoBuffer buf(ZIPARCHIVE_ENCR_HEADER_LEN); + m_storage.Read(buf, ZIPARCHIVE_ENCR_HEADER_LEN, false); + BYTE b = 0; + for (int i = 0; i < ZIPARCHIVE_ENCR_HEADER_LEN; i++) + { + b = buf[i]; // only temporary + CryptDecode((char&)b); + } + // check the last byte + return CurrentFile()->IsDataDescr() ? + (BYTE(CurrentFile()->m_uModTime >> 8) == b) : (BYTE(CurrentFile()->m_uCrc32 >> 24) == b); +} + +char CZipArchive::CryptDecryptByte() +{ + int temp = (m_keys[2] & 0xffff) | 2; + return (char)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +void CZipArchive::CryptDecode(char &c) +{ + c ^= CryptDecryptByte(); + CryptUpdateKeys(c); +} + +bool CZipArchive::SetPassword(LPCTSTR lpszPassword) +{ + if (m_iFileOpened != nothing) + { + TRACE(_T("%s(%i) : You cannot change the password when the file is opened.\n"),__FILE__,__LINE__); + return false; // it's important not to change the password when the file inside archive is opened + } + if (IsClosed()) + { + TRACE(_T("%s(%i) : Setting the password for a closed archive has no effect.\n"),__FILE__,__LINE__); + } + if (lpszPassword) + { + int iLen = WideToSingle(lpszPassword, m_pszPassword); + if (iLen == -1) + return false; + for (size_t i = 0; (int)i < iLen; i++) + if (m_pszPassword[i] <= 0) + { + m_pszPassword.Release(); + TRACE(_T("%s(%i) : The password contains forbidden characters. Password cleared.\n"),__FILE__,__LINE__); + return false; + } + } + else + m_pszPassword.Release(); + return true; +} + +CZipString CZipArchive::GetPassword()const +{ + CZipString temp; + CZipArchive::SingleToWide(m_pszPassword, temp); + return temp; +} + +DWORD CZipArchive::CryptCRC32(DWORD l, char c) +{ + const DWORD *CRC_TABLE = get_crc_table(); + return CRC_TABLE[(l ^ c) & 0xff] ^ (l >> 8); +} + +void CZipArchive::CryptCryptHeader(long iCrc, CZipAutoBuffer &buf) +{ + CryptInitKeys(); + srand(UINT(time(NULL))); + // genereate pseudo-random sequence + char c; + for (int i = 0; i < ZIPARCHIVE_ENCR_HEADER_LEN - 2; i++) + { + int t1 = rand(); + c = (char)(t1 >> 6); + if (!c) + c = (char)t1; + CryptEncode(c); + buf[i] = c; + + } + c = (char)((iCrc >> 16) & 0xff); + CryptEncode(c); + buf[ZIPARCHIVE_ENCR_HEADER_LEN - 2] = c; + c = (char)((iCrc >> 24) & 0xff); + CryptEncode(c); + buf[ZIPARCHIVE_ENCR_HEADER_LEN - 1] = c; +} + +void CZipArchive::CryptEncode(char &c) +{ + char t = CryptDecryptByte(); + CryptUpdateKeys(c); + c ^= t; +} + +void CZipArchive::CryptEncodeBuffer() +{ + if (CurrentFile()->IsEncrypted()) + for (DWORD i = 0; i < m_info.m_uComprLeft; i++) + CryptEncode(m_info.m_pBuffer[i]); +} + +void CZipArchive::CloseFileAfterTestFailed() +{ + if (m_iFileOpened != extract) + { + TRACE(_T("%s(%i) : No file opened.\n"),__FILE__,__LINE__); + return; + } + m_info.ReleaseBuf(); + m_centralDir.Clear(false); + m_iFileOpened = nothing; +} + +bool CZipArchive::TestFile(WORD uIndex, DWORD uBufSize) +{ + if (m_storage.IsSpanMode() == 1) + { + TRACE(_T("%s(%i) : You cannot test the spanning archive in creation.\n"),__FILE__,__LINE__); + return false; + } + if (!uBufSize) + return false; + + CZipFileHeader* pHeader = m_centralDir[uIndex]; + CZipActionCallback* pCallback = GetCallback(cbTest); + if (pCallback) + { + pCallback->Init(m_centralDir.GetProperHeaderFileName(pHeader)); + } + + 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) + DWORD iSize = pHeader->m_uComprSize; + if ((iSize != 0 || iSize != pHeader->m_uUncomprSize) + // different treating compressed directories + && !(pHeader->IsEncrypted() && iSize == 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; + do + { + iRead = ReadFile(buf, buf.GetSize()); + if (pCallback && iRead) + if (!(*pCallback)(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; + } + } + while (iRead == buf.GetSize()); + bool bRet = CloseFile() != -1; + if (!bRet && iAborted == CZipException::abortedSafely) + iAborted = CZipException::abortedAction; // sorry, finished, but not successfull + + if (pCallback) + pCallback->CallbackEnd(); + + if (iAborted) + CZipException::Throw(iAborted); // throw to distuingiush from other return codes + if (bRet) + return true; + else + CZipException::Throw(CZipException::badZipFile); + return false; // to satisfy the compiler and eliminate warning + } + catch(...) + { + CloseFileAfterTestFailed(); + throw; + } + } +} + +int CZipArchive::WideToSingle(LPCTSTR lpWide, CZipAutoBuffer &szSingle) +{ +#ifdef _UNICODE + return ZipPlatform::WideToSingle(lpWide, szSingle); +#else + + size_t iLen = strlen(lpWide); + // if not UNICODE just copy + // iLen does not include the NULL character + szSingle.Allocate(iLen); + memcpy(szSingle, lpWide, iLen); + return iLen; +#endif + +} + +int CZipArchive::SingleToWide(const CZipAutoBuffer &szSingle, CZipString& szWide) +{ + +#ifdef _UNICODE + return ZipPlatform::SingleToWide(szSingle, szWide); +#else // if not UNICODE just copy + int singleLen = szSingle.GetSize(); + // iLen does not include the NULL character + memcpy(szWide.GetBuffer(singleLen),szSingle.GetBuffer(), singleLen); + szWide.ReleaseBuffer(singleLen); + return singleLen; +#endif +} + +void CZipArchive::CryptDecodeBuffer(DWORD uCount) +{ + if (CurrentFile()->IsEncrypted()) + for (DWORD i = 0; i < uCount; i++) + CryptDecode(m_info.m_pBuffer[i]); +} + +void CZipArchive::EmptyPtrList() +{ + if (m_list.GetCount()) + { + // if some memory hasn't been freed due to an error in zlib, so free it now + CZipPtrListIter iter = m_list.GetHeadPosition(); + while (m_list.IteratorValid(iter)) + delete[] (char*) m_list.GetNext(iter); + } + m_list.RemoveAll(); +} + + + +void CZipArchive::SetFileHeaderAttr(CZipFileHeader& header, DWORD uAttr) +{ + header.SetSystemCompatibility(m_iArchiveSystCompatib); + header.SetSystemAttr(uAttr); +} + +void CZipArchive::EnableFindFast(bool bEnable) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : Set it after opening the archive.\n"),__FILE__,__LINE__); + return; + } + m_centralDir.EnableFindFast(bEnable, m_bCaseSensitive); + +} + +bool CZipArchive::SetSystemCompatibility(int iSystemComp) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : Set it after opening the archive.\n"),__FILE__,__LINE__); + return false; + } + + if (m_iFileOpened == compress) + { + TRACE(_T("%s(%i) : Set it before opening a file inside archive.\n"),__FILE__,__LINE__); + return false; + } + + if (!ZipCompatibility::IsPlatformSupported(iSystemComp)) + return false; + m_iArchiveSystCompatib = iSystemComp; + return true; +} + +void CZipArchive::SetRootPath(LPCTSTR szPath) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : Set it after opening the archive.\n"),__FILE__,__LINE__); + return; + } + + if (m_iFileOpened != nothing) + { + TRACE(_T("%s(%i) : Set it before opening a file inside archive.\n"),__FILE__,__LINE__); + 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, bool bExactly)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); + CZipPathComponent zpc(sz); + + if (bFullPath) + { + if (m_bRemoveDriveLetter) + sz = zpc.GetNoDrive(); + } + else + sz = TrimRootPath(zpc); + + if (bAppend && !sz.IsEmpty()) + CZipPathComponent::AppendSeparator(sz); + CZipFileHeader fh; // create a temporary object to convert + fh.SetFileName(sz); + if (bExactly) + { + fh.SetSystemCompatibility(m_iArchiveSystCompatib); + ZipCompatibility::FileNameUpdate(fh, false); + } + else + { + fh.SetSystemCompatibility(-1); // non existing system to prevent ansi oem conversion + ZipCompatibility::FileNameUpdate(fh, true);// update only path separators + } + + return fh.GetFileName(); +} + +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; +} + + +void CZipArchive::SetAutoFlush(bool bAutoFlush) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive not yet opened.\n"),__FILE__,__LINE__); + return; + } + if (m_storage.IsSpanMode() != 0) + { + TRACE(_T("%s(%i) : Cannot set auto-flush for the disk spanning archive.\n"),__FILE__,__LINE__); + return; + } + m_bAutoFlush = bAutoFlush; +} + +void CZipArchive::Flush() +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive not yet opened.\n"),__FILE__,__LINE__); + return; + } + + if (m_storage.IsSpanMode() < 0) + { + TRACE(_T("%s(%i) : Cannot flush an existing disk spanning archive.\n"),__FILE__,__LINE__); + return; + } + WriteCentralDirectory(); + m_storage.FlushFile(); + if (m_storage.IsSpanMode() > 0) // try to finalize disk-spanning archive without closing it + m_storage.FinalizeSpan(); +} + + +void CZipArchive::GetCentralDirInfo(CZipCentralDir::Info& info)const +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive not yet opened.\n"),__FILE__,__LINE__); + return; + + } + m_centralDir.GetInfo(info); + if (GetSpanMode() > 0) + info.m_uDiskEntriesNo = m_storage.GetCurrentDisk(); +} + +bool CZipArchive::CWildcard::IsPatternValid(LPCTSTR lpszPattern, int* iErrorType) +{ + try + { + /* loop through pattern to EOS */ + while (*lpszPattern) + { + /* determine pattern type */ + switch (*lpszPattern) + { + /* check literal escape, it cannot be at end of pattern */ + case _T('\\'): + if (!*++lpszPattern) + throw patternEsc; + lpszPattern++; + break; + + /* the [..] construct must be well formed */ + case _T('['): + lpszPattern++; + + /* if the next character is ']' then bad pattern */ + if (*lpszPattern == _T(']')) + throw patternEmpty; + + /* if end of pattern here then bad pattern */ + if (!*lpszPattern) + throw patternClose; + + /* loop to end of [..] construct */ + while (*lpszPattern != _T(']')) + { + /* check for literal escape */ + if (*lpszPattern == _T('\\')) + { + lpszPattern++; + + /* if end of pattern here then bad pattern */ + if (!*lpszPattern++) + throw patternEsc; + } + else lpszPattern++; + + /* if end of pattern here then bad pattern */ + if (!*lpszPattern) + throw patternClose; + + /* if this a range */ + if (*lpszPattern == _T('-')) + { + /* we must have an end of range */ + if (!*++lpszPattern || *lpszPattern == ']') + throw patternRange; + else + { + + /* check for literal escape */ + if (*lpszPattern == _T('\\')) + lpszPattern++; + + /* if end of pattern here + then bad pattern */ + if (!*lpszPattern++) + throw patternEsc; + } + } + } + break; + + /* all other characters are valid pattern elements */ + case '*': + case '?': + default: + lpszPattern++; /* "normal" character */ + break; + } + } + throw patternValid; + } + catch (int i) + { + if (iErrorType) + *iErrorType = i; + return i == patternValid; + } + + +} + +bool CZipArchive::CWildcard::IsPattern(LPCTSTR lpszPattern) +{ + while (*lpszPattern) + { + switch (*lpszPattern++) + { + case _T('?'): + case _T('*'): + case _T('['): + case _T('\\'): + return true; + } + } + return false; + +} + +bool CZipArchive::CWildcard::IsMatch(LPCTSTR lpszText, int *iRetCode) +{ + CZipString sz; + if (!m_bCaseSensitive) + { + sz = lpszText; + sz.MakeLower(); + lpszText = (LPCTSTR)sz; + } + int i = Match((LPCTSTR)m_szPattern, lpszText); + if (iRetCode) + *iRetCode = i; + return i == matchValid; +} + +int CZipArchive::CWildcard::MatchAfterStar(LPCTSTR p, LPCTSTR t) +{ + int iMatch = matchNone; + TCHAR nextp; + + /* pass over existing ? and * in pattern */ + + while ( *p == _T('?') || *p == _T('*') ) + { + /* take one char for each ? and + */ + + if (*p == _T('?')) + { + /* if end of text then no match */ + if (!*t++) + return matchAbort; + } + + /* move to next char in pattern */ + + p++; + } + + /* if end of pattern we have matched regardless of text left */ + + if (!*p) + return matchValid; + + /* get the next character to match which must be a literal or '[' */ + + nextp = *p; + if (nextp == _T('\\')) + { + nextp = p[1]; + + /* if end of text then we have a bad pattern */ + + if (!nextp) + return matchPattern; + } + + /* Continue until we run out of text or definite result seen */ + + do + { + /* a precondition for matching is that the next character + in the pattern match the next character in the text or that + the next pattern char is the beginning of a range. Increment + text pointer as we go here */ + + if (nextp == *t || nextp == _T('[')) + iMatch = Match(p, t); + + /* if the end of text is reached then no iMatch */ + + if (!*t++) + iMatch = matchAbort; + + } while ( iMatch != matchValid && + iMatch != matchAbort && + iMatch != matchPattern); + + /* return result */ + + return iMatch; +} + + +int CZipArchive::CWildcard::Match(LPCTSTR lpszPattern, LPCTSTR lpszText) +{ + + TCHAR range_start, range_end; /* start and end in range */ + + bool bInvert; /* is this [..] or [!..] */ + bool bMemberMatch; /* have I matched the [..] construct? */ + bool bLoop; /* should I terminate? */ + + for ( ; *lpszPattern; lpszPattern++, lpszText++) + { + /* if this is the end of the text + then this is the end of the match */ + + if (!*lpszText) + { + if ( *lpszPattern == _T('*') && *++lpszPattern == _T('\0') ) + return matchValid; + else + return matchAbort; + } + + /* determine and react to pattern type */ + + switch (*lpszPattern) + { + case _T('?'): /* single any character match */ + break; + + case _T('*'): /* multiple any character match */ + return MatchAfterStar (lpszPattern, lpszText); + + /* [..] construct, single member/exclusion character match */ + case _T('['): + { + /* move to beginning of range */ + + lpszPattern++; + + /* check if this is a member match or exclusion match */ + + bInvert = false; + if (*lpszPattern == _T('!') || *lpszPattern == _T('^')) + { + bInvert = true; + lpszPattern++; + } + + /* if closing bracket here or at range start then we have a + malformed pattern */ + + if (*lpszPattern == _T(']')) + return matchPattern; + + bMemberMatch = false; + bLoop = true; + + while (bLoop) + { + /* if end of construct then bLoop is done */ + + if (*lpszPattern == _T(']')) + { + bLoop = false; + continue; + } + + /* matching a '!', '^', '-', '\' or a ']' */ + + if (*lpszPattern == _T('\\')) + range_start = range_end = *++lpszPattern; + else + range_start = range_end = *lpszPattern; + + /* if end of pattern then bad pattern (Missing ']') */ + + if (!*lpszPattern) + return matchPattern; + + /* check for range bar */ + if (*++lpszPattern == _T('-')) + { + /* get the range end */ + + range_end = *++lpszPattern; + + /* if end of pattern or construct + then bad pattern */ + + if (range_end == _T('\0') || range_end == _T(']')) + return matchPattern; + /* special character range end */ + if (range_end == _T('\\')) + { + range_end = *++lpszPattern; + + /* if end of text then + we have a bad pattern */ + if (!range_end) + return matchPattern; + } + + /* move just beyond this range */ + lpszPattern++; + } + + /* if the text character is in range then match found. + make sure the range letters have the proper + relationship to one another before comparison */ + + if (range_start < range_end) + { + if (*lpszText >= range_start && *lpszText <= range_end) + { + bMemberMatch = true; + bLoop = false; + } + } + else + { + if (*lpszText >= range_end && *lpszText <= range_start) + { + bMemberMatch = true; + bLoop = false; + } + } + } + + /* if there was a match in an exclusion set then no match */ + /* if there was no match in a member set then no match */ + + if ((bInvert && bMemberMatch) || !(bInvert || bMemberMatch)) + return matchRange; + + /* if this is not an exclusion then skip the rest of + the [...] construct that already matched. */ + + if (bMemberMatch) + { + while (*lpszPattern != _T(']')) + { + /* bad pattern (Missing ']') */ + if (!*lpszPattern) + return matchPattern; + + /* skip exact match */ + if (*lpszPattern == _T('\\')) + { + lpszPattern++; + + /* if end of text then + we have a bad pattern */ + + if (!*lpszPattern) + return matchPattern; + } + + /* move to next pattern char */ + + lpszPattern++; + } + } + break; + } + case _T('\\'): /* next character is quoted and must match exactly */ + + /* move pattern pointer to quoted char and fall through */ + + lpszPattern++; + + /* if end of text then we have a bad pattern */ + + if (!*lpszPattern) + return matchPattern; + + /* must match this character exactly */ + + default: + if (*lpszPattern != *lpszText) + return matchPattern; + } + } + /* if end of text not reached then the pattern fails */ + + if (*lpszText) + return matchEnd; + else + return matchValid; +} + +void CZipArchive::FindMatches(LPCTSTR lpszPattern, CZipWordArray &ar, bool bFullPath) const +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return; + } + + // ar.RemoveAll(); don't do this + int iCount = GetCount(); + CWildcard wc(lpszPattern, m_bCaseSensitive); + for (int i = 0; i < iCount; i++) + { + const CZipFileHeader* pHeader = m_centralDir[i]; + CZipString sz = m_centralDir.GetProperHeaderFileName(pHeader); + if (!bFullPath) + { + CZipPathComponent::RemoveSeparators(sz); + CZipPathComponent zpc(sz); + sz = zpc.GetFileName(); + } + if (wc.IsMatch(sz)) + ar.Add(i); + } +} + +int CZipArchive::WillBeDuplicated(LPCTSTR lpszFilePath, bool bFullPath, bool bFileNameOnly , int iWhat) +{ + CZipString szFile; + // we predict with bExactly set to false, because FindFile converts all filanames anyway + if (bFileNameOnly) + { + CZipPathComponent zpc(lpszFilePath); + szFile = PredictFileNameInZip(zpc.GetFileName(), false, iWhat); + } + else + szFile = PredictFileNameInZip(lpszFilePath, bFullPath, iWhat); + return FindFile(szFile, ffDefault, bFileNameOnly); +} + + +// it'll get up to the next file or to the end of file (bad if zip corrupted or not-ordered by offsett or redundant bytes added) +bool CZipArchive::GetFromArchive(CZipArchive& zip, WORD uIndex, int iReplaceIndex, bool bKeepSystComp, CZipActionCallback* pCallback) +{ + + if (IsClosed() || zip.IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return false; + } + + if (m_iFileOpened || zip.m_iFileOpened) + { + TRACE(_T("%s(%i) : You cannot get files from another archive if there is a file opened.\n"),__FILE__,__LINE__); + return false; + } + + if (zip.m_storage.IsSpanMode()) + { + TRACE(_T("%s(%i) : You cannot get files from the disk spannig archive.\n"),__FILE__,__LINE__); + return false; + } + + if (m_storage.IsSpanMode() == -1) + { + TRACE(_T("%s(%i) : You cannot add files to the existing disk spannig archive.\n"),__FILE__,__LINE__); + return false; + } + + ASSERT(m_info.m_pBuffer.GetSize() > 0); + + bool bIsSpan = m_storage.IsSpanMode() == 1; + + CZipFileHeader fh; + if (!zip.GetFileInfo(fh, uIndex)) + return false; + CZipAbstractFile* pFile = zip.m_storage.m_pFile; + + + DWORD uEndOffset; + if (uIndex < zip.GetCount() - 1) + { + CZipFileHeader fhTemp; + if (!zip.GetFileInfo(fhTemp, uIndex+1)) + return false; + uEndOffset = fhTemp.m_uOffset; + } + else + { + CZipCentralDir::Info info; + zip.m_centralDir.GetInfo(info); + if (info.m_bOnDisk) + uEndOffset = info.m_uOffset; + else + uEndOffset = pFile->GetLength(); + } + uEndOffset += zip.m_centralDir.GetBytesBefore(); + DWORD uStartOffset = zip.m_centralDir.GetBytesBefore() + fh.m_uOffset + fh.GetSize(true); + DWORD uTotalToMove = uEndOffset - uStartOffset, uTotalMoved = 0; + + DWORD uPredictedSize = fh.m_uComprSize + + (fh.IsDataDescr() ? ZIPARCHIVE_DATADESCRIPTOR_LEN : 0); + if (uTotalToMove > uPredictedSize + 4/* may be or may be not a signature*/) + uTotalToMove = uPredictedSize + 4; + else if (uTotalToMove < uPredictedSize) + ThrowError(CZipException::badZipFile); + + // conversion stuff + CZipString szFileNameConverted, szFileName; + bool bConvertSystem = !bKeepSystComp && fh.GetSystemCompatibility() != m_iArchiveSystCompatib; + + // GetFileInfo always converts the filename regardless of zip.m_centralDir.m_bConvertAfterOpen value + szFileNameConverted = fh.GetFileName(); + if (bConvertSystem) + { + DWORD uAttr = fh.GetSystemAttr(); + fh.SetSystemCompatibility(m_iArchiveSystCompatib); + fh.SetSystemAttr(uAttr); + } + + ZipCompatibility::FileNameUpdate(fh, false); + szFileName = fh.GetFileName(); + + + bool bNeedDataDescr = bIsSpan && !fh.IsDataDescr(); + if (bNeedDataDescr) + fh.m_uFlag |= 8; // data descriptor present + + + // needed by InsertFindFastElement + if (m_centralDir.IsFindFastEnabled()) + fh.SetFileName(szFileNameConverted); + + + if (!UpdateReplaceIndex(iReplaceIndex, szFileNameConverted)) + return false; + + bool bReplace = iReplaceIndex >= 0; + int iCallbackType = 0; + if (pCallback) + iCallbackType = pCallback->m_iType; + + // if the same callback is applied to cbReplace, then the previous information about the type will be lost + CZipFileHeader* pHeader = m_centralDir.AddNewFile(fh, iReplaceIndex); // must be converted when adding because of InsertFastElement + if (bReplace) + MakeSpaceForReplace(iReplaceIndex, uTotalToMove + fh.GetSize(true), szFileNameConverted); + + if (pCallback) + { + pCallback->m_iType = iCallbackType; + pCallback->Init(szFileNameConverted, zip.GetArchivePath()); + pCallback->SetTotal(fh.m_uComprSize); + } + + if (m_centralDir.IsFindFastEnabled()) + pHeader->SetFileName(szFileName); + + // must be written as not converted + pHeader->WriteLocal(m_storage); + + // made a correction to what was set in WriteLocal + pHeader->m_uOffset -= m_centralDir.GetBytesBefore(); + + // we keep in converted in memory + if (m_centralDir.m_bConvertAfterOpen) + pHeader->SetFileName(szFileNameConverted); + + + // skip reading the local file header + + pFile->Seek(uStartOffset, CZipAbstractFile::begin); + + + DWORD uPack = uTotalToMove > m_info.m_pBuffer.GetSize() ? m_info.m_pBuffer.GetSize() : uTotalToMove; + char* buf = (char*)m_info.m_pBuffer; + + DWORD size_read; + + int iAborted = 0; + bool bBreak = false; + if (uPack) + do + { + size_read = pFile->Read(buf, uPack); + if (!size_read) + break; + if (uTotalMoved + size_read > uTotalToMove) + { + size_read = uTotalToMove - uTotalMoved; + if (!size_read) // this is for protection + break; + bBreak = true; + } + + m_storage.Write(buf, size_read, false); + uTotalMoved += size_read; + if (pCallback) + if (!(*pCallback)(size_read)) + { + if (uTotalToMove != uTotalMoved) + { + if (!bIsSpan && !bReplace) + { + m_centralDir.RemoveLastFile(); + iAborted = CZipException::abortedSafely; + } + else + iAborted = CZipException::abortedAction; + } + else + iAborted = CZipException::abortedSafely; // we did it! + break; + + } + } + while (!bBreak); + + if (iAborted) + CZipException::Throw(iAborted); // throw to distuingiush from other return codes + + // copying from non-span to span or from span without data description to span + // so add the data descriptor + + m_centralDir.m_pOpenedFile = NULL; + if (bNeedDataDescr && uTotalMoved == uTotalToMove) + { + const int iToWrite = ZIPARCHIVE_DATADESCRIPTOR_LEN + 4; + CZipAutoBuffer buf(iToWrite); + memcpy(buf, m_storage.m_gszExtHeaderSignat, 4); + pHeader->GetCrcAndSizes(buf + 4); + m_storage.Write(buf, iToWrite, true); + + } + m_storage.Flush(); + if (uTotalMoved < uTotalToMove) + ThrowError(CZipException::badZipFile); + + + if (pCallback) + pCallback->CallbackEnd(); + + return true; +} + +bool CZipArchive::GetFromArchive(CZipArchive& zip, CZipWordArray &aIndexes, bool bKeepSystComp) +{ + aIndexes.Sort(true); + int iFiles = aIndexes.GetSize(); + m_info.Init(); + try + { + for (int i = 0; i < iFiles; i++) + { + int iFileIndex = aIndexes[i]; + if (!m_centralDir.IsValidIndex(iFileIndex)) + if (!GetFromArchive(zip, iFileIndex, -1, bKeepSystComp, GetCallback(cbGetFromArchive))) + { + m_info.ReleaseBuf(); + return false; + } + } + } + catch (...) + { + m_info.ReleaseBuf(); + throw; + } + m_info.ReleaseBuf(); + if (m_bAutoFlush) + Flush(); + return true; +} +bool CZipArchive::RenameFile(WORD uIndex, LPCTSTR lpszNewName) +{ + if (IsClosed()) + { + TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__); + return false; + } + + if (m_storage.IsSpanMode()) + { + TRACE(_T("%s(%i) : You cannot rename files in the disk spannig archive.\n"),__FILE__,__LINE__); + return false; + } + + if (m_iFileOpened) + { + TRACE(_T("%s(%i) : You cannot rename a file if there is a file opened.\n"),__FILE__,__LINE__); + return false; + } + CZipFileHeader fh, fhNew; + if (!GetFileInfo(fh, uIndex)) + return false; + CZipString szNewName(lpszNewName); + if (fh.IsDirectory()) + CZipPathComponent::AppendSeparator(szNewName); + else + CZipPathComponent::RemoveSeparators(szNewName); + if (fh.GetFileName().Collate(szNewName) == 0) + return true; + fhNew.SetSystemCompatibility(m_iArchiveSystCompatib); + + fhNew.SetFileName(szNewName); + ZipCompatibility::FileNameUpdate(fhNew, false); + ZipCompatibility::FileNameUpdate(fh, false); // in case the conversion changes the filename size + WORD uFileNameLen = fh.GetFileNameSize(); + WORD uNewFileNameLen = fhNew.GetFileNameSize(); + int iDelta = uNewFileNameLen - uFileNameLen; + int iOffset = 0; + CZipAutoBuffer buf, *pBuf; + m_centralDir.RemoveFromDisk(); // does m_storage.Flush(); + if (iDelta != 0) + { + // we need to make more or less space + + m_info.Init(); + DWORD uStartOffset = fh.m_uOffset + 30 + uFileNameLen; + DWORD uFileLen = m_storage.m_pFile->GetLength(); + DWORD uEndOffset = uFileLen - m_centralDir.GetBytesBefore(); + CZipActionCallback* pCallback = GetCallback(cbRename); + if (pCallback) + { + // do it right and sent the notification + pCallback->Init(fh.GetFileName(), GetArchivePath()); + pCallback->SetTotal(uEndOffset - uStartOffset); + } + bool bForward = iDelta > 0; + if (bForward) + m_storage.m_pFile->SetLength(uFileLen + iDelta); // ensure the seek is correct + + MovePackedFiles(uStartOffset, uEndOffset, abs(iDelta), pCallback, bForward); + if (pCallback) + pCallback->CallbackEnd(); + + if (!bForward) + m_storage.m_pFile->SetLength(uFileLen + iDelta); // delta < 0; shrink the file + + m_info.ReleaseBuf(); + + int iSize = GetCount(); + for (int i = uIndex + 1; i < iSize; i++) + m_centralDir[i]->m_uOffset += iDelta; + buf.Allocate(4+uNewFileNameLen); + WORD uExtraFieldSize = fh.GetExtraFieldSize(); + memcpy(buf, &uNewFileNameLen, 2); + memcpy(buf + 2, &uExtraFieldSize, 2); // to write everything at once + memcpy(buf + 4, fhNew.m_pszFileName, uNewFileNameLen); + pBuf = &buf; + iOffset = -4; + } + else + pBuf = &fhNew.m_pszFileName; + + m_storage.m_pFile->Seek(m_centralDir.GetBytesBefore() + fh.m_uOffset + 30 + iOffset, CZipAbstractFile::begin); + m_storage.m_pFile->Write(buf, buf.GetSize()); + m_centralDir.RenameFile(uIndex, szNewName); + if (m_bAutoFlush) + Flush(); + + return true; +} + +bool CZipArchive::UpdateReplaceIndex(int& iReplaceIndex, LPCTSTR lpszNewFileName) +{ + if (iReplaceIndex == -2) + iReplaceIndex = FindFile(lpszNewFileName); + if (iReplaceIndex < 0) + { + if (iReplaceIndex != -1) + iReplaceIndex = -1; + return true; + } + + if (GetSpanMode()!=0) + { + TRACE(_T("%s(%i) : You cannot replace files in a disk-spanning archive.\n"),__FILE__,__LINE__); + return false; + } + + if (!m_centralDir.IsValidIndex(iReplaceIndex)) + { + TRACE(_T("%s(%i) : Not valid replace index.\n"),__FILE__,__LINE__); + return false; + } + if (iReplaceIndex == GetCount() - 1) // replacing last file in the archive + { + RemoveLast(true); + iReplaceIndex = -1; + } + return true; +} + +void CZipArchive::MakeSpaceForReplace(int iReplaceIndex, DWORD uTotal, LPCTSTR lpszFileName) +{ + + ASSERT(iReplaceIndex < GetCount() - 1); + DWORD uReplaceStart = m_storage.m_pFile->GetPosition() - m_centralDir.GetBytesBefore(); + DWORD uReplaceEnd = m_centralDir.m_headers[iReplaceIndex + 1]->m_uOffset; + DWORD uReplaceTotal = uReplaceEnd - uReplaceStart; + int iDelta = uTotal - uReplaceTotal; + + if (iDelta != 0) + { + + // m_info.Init(); don't - the calling functions will + CZipActionCallback* pCallback = GetCallback(CZipArchive::cbReplace); + DWORD uFileLen = m_storage.m_pFile->GetLength(); + DWORD uUpperLimit = uFileLen - m_centralDir.GetBytesBefore(); // will be added in MovePackedFiles + if (pCallback) + { + pCallback->Init(lpszFileName, GetArchivePath()); + pCallback->SetTotal(uUpperLimit - uReplaceEnd); + } + + bool bForward = iDelta > 0; + if (bForward) + m_storage.m_pFile->SetLength(uFileLen + iDelta); // ensure the seek is correct + + MovePackedFiles(uReplaceEnd, uUpperLimit, abs(iDelta), pCallback, bForward); + + if (!bForward) + m_storage.m_pFile->SetLength(uFileLen + iDelta); // delta < 0; shrink the file + + m_storage.m_pFile->Seek(uReplaceStart, CZipAbstractFile::begin); + int iSize = m_centralDir.m_headers.GetSize(); + for (int i = iReplaceIndex + 1; i < iSize; i++) + m_centralDir.m_headers[i]->m_uOffset += iDelta; + if (pCallback) + pCallback->CallbackEnd(); + } +} + +void CZipArchive::MovePackedFiles(DWORD uStartOffset, DWORD uEndOffset, DWORD uMoveBy, CZipActionCallback* pCallback, bool bForward) +{ + ASSERT(m_info.m_pBuffer.GetSize() > 0); + uStartOffset += m_centralDir.GetBytesBefore(); + uEndOffset += m_centralDir.GetBytesBefore(); + + + DWORD uTotalToMove = uEndOffset - uStartOffset; + DWORD uPack = uTotalToMove > m_info.m_pBuffer.GetSize() ? m_info.m_pBuffer.GetSize() : uTotalToMove; + char* buf = (char*)m_info.m_pBuffer; + + + DWORD size_read; + bool bBreak = false; + do + { + + if (uEndOffset - uStartOffset < uPack) + { + uPack = uEndOffset - uStartOffset; + if (!uPack) + break; + bBreak = true; + + } + DWORD uPosition = bForward ? uEndOffset - uPack : uStartOffset; + + m_storage.m_pFile->Seek(uPosition, CZipAbstractFile::begin); + size_read = m_storage.m_pFile->Read(buf, uPack); + if (!size_read) + break; + + if (bForward) + uPosition += uMoveBy; + else + uPosition -= uMoveBy; + m_storage.m_pFile->Seek(uPosition, CZipAbstractFile::begin); + m_storage.m_pFile->Write(buf, size_read); + if (bForward) + uEndOffset -= size_read; + else + uStartOffset += size_read; + if (pCallback) + if (!(*pCallback)(size_read)) + ThrowError(CZipException::abortedAction); + } + while (!bBreak); + + if (uEndOffset != uStartOffset) + ThrowError(CZipException::internal); + +} diff --git a/harbour/contrib/hbzlib/zipautobuffer.cpp b/harbour/contrib/hbzlib/zipautobuffer.cpp new file mode 100644 index 0000000000..bf15a477b7 --- /dev/null +++ b/harbour/contrib/hbzlib/zipautobuffer.cpp @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipAutoBuffer.cpp $ +// $Archive: /ZipArchive_STL/ZipAutoBuffer.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#include "zipautobuffer.h" +#include + +// #ifdef _DEBUG +// #undef THIS_FILE +// static char THIS_FILE[]=__FILE__; +// #define new DEBUG_NEW +// #endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CZipAutoBuffer::CZipAutoBuffer() +{ + m_iSize = 0; + m_pBuffer = NULL; +} + +CZipAutoBuffer::CZipAutoBuffer(DWORD iSize, bool bZeroMemory) +{ + m_iSize = 0; + m_pBuffer = NULL; + Allocate(iSize, bZeroMemory); +} + +CZipAutoBuffer::~CZipAutoBuffer() +{ + Release(); +} + + +void CZipAutoBuffer::Release() +{ + if (m_pBuffer) + { + delete [] m_pBuffer; + m_iSize = 0; + m_pBuffer = NULL; + } +} + +char* CZipAutoBuffer::Allocate(DWORD iSize, bool bZeroMemory) +{ + if (iSize != m_iSize) + Release(); + else + { + if (bZeroMemory) + memset(m_pBuffer, 0, iSize); // zerowanie bufora + return m_pBuffer; + } + + if (iSize > 0) + { + m_pBuffer = new char [iSize]; + if (bZeroMemory) + memset(m_pBuffer, 0, iSize); // zerowanie bufora + m_iSize = iSize; + } + else + m_pBuffer = NULL; + + return m_pBuffer; +} + + +CZipAutoBuffer::CZipAutoBuffer(const CZipAutoBuffer& buffer) +{ + m_pBuffer = NULL; + m_iSize = 0; + + if (buffer.m_pBuffer) + { + Allocate(buffer.m_iSize); + memcpy(m_pBuffer, buffer.m_pBuffer, buffer.m_iSize); + } +} +CZipAutoBuffer& CZipAutoBuffer::operator=(const CZipAutoBuffer& buffer) +{ + if (this == &buffer) + return *this; + Release(); + if (buffer.m_pBuffer) + { + Allocate(buffer.m_iSize); + memcpy(m_pBuffer, buffer.m_pBuffer, buffer.m_iSize); + } + return *this; +} diff --git a/harbour/contrib/hbzlib/zipcentraldir.cpp b/harbour/contrib/hbzlib/zipcentraldir.cpp new file mode 100644 index 0000000000..7b792f2b41 --- /dev/null +++ b/harbour/contrib/hbzlib/zipcentraldir.cpp @@ -0,0 +1,852 @@ +/////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipCentralDir.cpp $ +// $Archive: /ZipArchive/ZipCentralDir.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#include "zipcentraldir.h" +#include "ziparchive.h" +#include "zipfilemapping.h" +#include "zipplatform.h" + + +#define CENTRALDIRSIZE 22 + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +char CZipCentralDir::m_gszSignature[] = {0x50, 0x4b, 0x05, 0x06}; +CZipCentralDir::CZipCentralDir() +{ + m_bConvertAfterOpen = true; + m_bFindFastEnabled = false; + m_bCaseSensitive = false; + m_pCompare = GetCZipStrCompFunc(ZipPlatform::GetSystemCaseSensitivity()); + m_pStorage = NULL; + m_pOpenedFile = NULL; + m_iBufferSize = 32768; + +} + +void CZipCentralDir::Init() +{ + m_info.m_bOnDisk = false; + m_info.m_uBytesBeforeZip = m_info.m_uCentrDirPos = 0; + m_pOpenedFile = NULL; + m_pszComment.Release(); + +} + +CZipCentralDir::~CZipCentralDir() +{ + Clear(); +} + +void CZipCentralDir::Read() +{ + ASSERT(m_pStorage); + WORD uCommentSize; + m_info.m_uCentrDirPos = Locate(); + m_pStorage->m_pFile->Seek(m_info.m_uCentrDirPos, CZipAbstractFile::begin); + CZipAutoBuffer buf(CENTRALDIRSIZE); + + int uRead = m_pStorage->m_pFile->Read(buf, CENTRALDIRSIZE); + if (uRead != CENTRALDIRSIZE) + ThrowError(CZipException::badZipFile); + memcpy(&m_szSignature, buf, 4); + memcpy(&m_info.m_uThisDisk, buf + 4, 2); + memcpy(&m_info.m_uDiskWithCD, buf + 6, 2); + memcpy(&m_info.m_uDiskEntriesNo,buf + 8, 2); + memcpy(&m_info.m_uEntriesNumber,buf + 10, 2); + memcpy(&m_info.m_uSize, buf + 12, 4); + memcpy(&m_info.m_uOffset, buf + 16, 4); + memcpy(&uCommentSize, buf + 20, 2); + buf.Release(); + + + m_pStorage->UpdateSpanMode(m_info.m_uThisDisk); + // if m_uThisDisk is not zero, it is enough to say that it is a multi disk archive + ASSERT((!m_info.m_uThisDisk && (m_info.m_uEntriesNumber == m_info.m_uDiskEntriesNo) && !m_info.m_uDiskWithCD) || m_info.m_uThisDisk); + + + + if (!m_pStorage->IsSpanMode() && !m_info.CheckIfOK_1()) + ThrowError(CZipException::badZipFile); + + if (uCommentSize) + { + m_pszComment.Allocate(uCommentSize); + uRead = m_pStorage->m_pFile->Read(m_pszComment, uCommentSize); + if (uRead != uCommentSize) + ThrowError(CZipException::badZipFile); + } + + m_info.SetBytesBeforeZip(m_pStorage->IsSpanMode() != 0); + + if (!m_info.CheckIfOK_2()) + ThrowError(CZipException::badZipFile); + + m_info.m_bOnDisk = true; + m_pStorage->ChangeDisk(m_info.m_uDiskWithCD); + + if (!m_info.m_uSize) + return; + + ReadHeaders(); +} + +DWORD CZipCentralDir::Locate() +{ + + // maximum size of end of central dir record + long uMaxRecordSize = 0xffff + CENTRALDIRSIZE; + DWORD uFileSize = m_pStorage->m_pFile->GetLength(); + + if ((DWORD)uMaxRecordSize > uFileSize) + uMaxRecordSize = uFileSize; + + CZipAutoBuffer buf(m_iBufferSize); + + long uPosInFile = 0; + int uRead = 0; + // backward reading + while (uPosInFile < uMaxRecordSize) + { + uPosInFile = uRead + m_iBufferSize; + if (uPosInFile > uMaxRecordSize) + uPosInFile = uMaxRecordSize; + + int iToRead = uPosInFile - uRead; + + m_pStorage->m_pFile->Seek(-uPosInFile, CZipAbstractFile::end); + int iActuallyRead = m_pStorage->m_pFile->Read(buf, iToRead); + if (iActuallyRead != iToRead) + ThrowError(CZipException::badZipFile); + // search from the very last bytes to prevent an error if inside archive + // there are packed other arhives + for (int i = iToRead - 4; i >=0 ; i--) + if (!memcmp((char*)buf + i, m_gszSignature, 4)) + return uFileSize - (uPosInFile - i); + + uRead += iToRead - 3; + + } + + ThrowError(CZipException::cdirNotFound); + return 0; +} + +void CZipCentralDir::ThrowError(int err) const +{ + CZipException::Throw(err, m_pStorage->m_pFile->GetFilePath()); +} + + +void CZipCentralDir::ReadHeaders() +{ + m_pStorage->m_pFile->Seek(m_info.m_uOffset + m_info.m_uBytesBeforeZip, CZipAbstractFile::begin); + RemoveHeaders(); //just in case + for (int i = 0; i < m_info.m_uEntriesNumber; i++) + { + CZipFileHeader* pHeader = new CZipFileHeader; + m_headers.Add(pHeader); + + if (!pHeader->Read(m_pStorage)) + ThrowError(CZipException::badZipFile); + ConvertFileName(true, true, pHeader); + } + SortHeaders(); // this is necessary when deleting files and removing data descriptors + if (m_bFindFastEnabled) + BuildFindFastArray(m_bCaseSensitive); +} + +void CZipCentralDir::SortHeaders() +{ + // we cannot use the Sort method of the CZipWordArray, + // because we store pointers (and we need to store pointers + // to make sure that the address of the CZipFileHeader structure + // remains the same while working with the library) + int iSize = m_headers.GetSize(); + if (iSize) + qsort((void*)&(m_headers[0]),iSize , sizeof(CZipFileHeader*), CompareHeaders); +} + +void CZipCentralDir::Clear(bool bEverything) +{ + m_pOpenedFile = NULL; + m_pLocalExtraField.Release(); + if (bEverything) + { + RemoveHeaders(); + m_findarray.RemoveAll(); + m_pszComment.Release(); + } +} + + +bool CZipCentralDir::IsValidIndex(int uIndex)const +{ + return uIndex < m_headers.GetSize() && uIndex >= 0; +} + + +void CZipCentralDir::OpenFile(WORD uIndex) +{ + WORD uLocalExtraFieldSize; + m_pOpenedFile = (*this)[uIndex]; + m_pStorage->ChangeDisk(m_pOpenedFile->m_uDiskStart); + m_pStorage->m_pFile->Seek(m_pOpenedFile->m_uOffset + m_info.m_uBytesBeforeZip, CZipAbstractFile::begin); + if (!m_pOpenedFile->ReadLocal(m_pStorage, uLocalExtraFieldSize)) + ThrowError(CZipException::badZipFile); + + + m_pLocalExtraField.Release(); // just in case + if (uLocalExtraFieldSize) + { + int iCurrDsk = m_pStorage->GetCurrentDisk(); + m_pLocalExtraField.Allocate(uLocalExtraFieldSize); + m_pStorage->Read(m_pLocalExtraField, uLocalExtraFieldSize, true); + if (m_pStorage->GetCurrentDisk() != iCurrDsk) + ThrowError(CZipException::badZipFile); + } +} + +void CZipCentralDir::CloseFile(bool bAfterException) +{ + if (!m_pOpenedFile) + return; + m_pLocalExtraField.Release(); + if (!bAfterException && m_pOpenedFile->IsDataDescr()) + { + CZipAutoBuffer buf(12); + m_pStorage->Read(buf, 4, false); + // in span mode, files that are divided between disks have bit 3 of flag set + // which tell about the presence of the data descriptor after the compressed data + // This signature may be in the disk spanning archive that is one volume only + // (it is detected as a non disk spanning archive) + if (memcmp(buf, CZipStorage::m_gszExtHeaderSignat, 4) != 0) // there is no signature + m_pStorage->m_pFile->Seek(-4, CZipAbstractFile::current); + + + m_pStorage->Read(buf, 12, false); + if (!m_pOpenedFile->CheckCrcAndSizes(buf)) + ThrowError(CZipException::badZipFile); + } + m_pOpenedFile = NULL; +} + +// add new header using the argument as a template +CZipFileHeader* CZipCentralDir::AddNewFile(const CZipFileHeader & header, int iReplaceIndex) +{ + CZipFileHeader* pHeader = new CZipFileHeader(header); + m_pOpenedFile = pHeader; + WORD uIndex; + DWORD uOffset = 0; + bool bReplace = IsValidIndex(iReplaceIndex); + if (bReplace) + { + CZipFileHeader* pfh = m_headers[iReplaceIndex]; + uOffset = pfh->m_uOffset + m_info.m_uBytesBeforeZip; + RemoveFile(pfh, iReplaceIndex, false); + m_headers.InsertAt(iReplaceIndex, pHeader); + uIndex = (WORD)iReplaceIndex; + } + else + uIndex = m_headers.Add(pHeader); + + if (m_bFindFastEnabled) + InsertFindFastElement(pHeader, uIndex); // GetCount > 0, 'cos we've just added a header + RemoveFromDisk(); + if (bReplace) + m_pStorage->m_pFile->Seek(uOffset, CZipAbstractFile::begin); + else + m_pStorage->m_pFile->SeekToEnd(); + return pHeader; +} + + +void CZipCentralDir::RemoveFromDisk() +{ + if (m_info.m_bOnDisk) + { + ASSERT(!m_pStorage->IsSpanMode()); // you can't add files to the existing disk span archive or to delete them from it + m_pStorage->m_pFile->SetLength(m_info.m_uBytesBeforeZip + m_info.m_uOffset); + m_info.m_bOnDisk = false; + } + else + m_pStorage->Flush(); // if remove from disk is requested, then the archive modification will follow, so flush the buffers +} + + +void CZipCentralDir::CloseNewFile() +{ + CZipAutoBuffer buf(ZIPARCHIVE_DATADESCRIPTOR_LEN + 4); + short iToWrite = 0; + bool bIsSpan = m_pStorage->IsSpanMode() != 0; + bool bEncrypted = m_pOpenedFile->IsEncrypted(); + if (m_pOpenedFile->IsDataDescr()) + { + if (bIsSpan || bEncrypted) + { + memcpy(buf, m_pStorage->m_gszExtHeaderSignat, 4); + iToWrite += 4; + } + } + else /*if (!IsSpan)*/ + { + ASSERT(!bIsSpan && !bEncrypted); + m_pStorage->Flush(); + // the offset contains bytes before zip (set while writting the local header) + m_pStorage->m_pFile->Seek(m_pOpenedFile->m_uOffset + 14, CZipAbstractFile::begin); + // we don't have to restore the pointer, because before adding a new file, + // the pointer is moved to the end + } + + m_pOpenedFile->GetCrcAndSizes(buf + iToWrite); + iToWrite += ZIPARCHIVE_DATADESCRIPTOR_LEN; + + // offset set during writing the local header + m_pOpenedFile->m_uOffset -= m_info.m_uBytesBeforeZip; + + // write the data descriptor and a disk spanning signature at once + m_pStorage->Write(buf, iToWrite, true); + if (!bIsSpan) + { + if (bEncrypted) + { + // write the information to the local header too + m_pStorage->Flush(); + m_pStorage->m_pFile->Seek(m_info.m_uBytesBeforeZip + m_pOpenedFile->m_uOffset + 14, CZipAbstractFile::begin); + m_pStorage->Write(buf + 4, ZIPARCHIVE_DATADESCRIPTOR_LEN, true); + } + m_pStorage->Flush(); + } + + m_pOpenedFile = NULL; + +} + +void CZipCentralDir::Write(CZipActionCallback* pCallback) +{ + if (m_info.m_bOnDisk) + return; + if (!m_pStorage->IsSpanMode()) + { + m_pStorage->Flush(); + m_pStorage->m_pFile->SeekToEnd(); + } + +// else +// // we are at the end already + + m_info.m_uEntriesNumber = (WORD)m_headers.GetSize(); + m_info.m_uSize = 0; + bool bDontAllowDiskChange = false; + // if there is a disk spanning archive in creation and it is only one-volume, + // (current disk is 0 so far, no bytes has been written so we know they are + // all in the buffer) make sure that it will be after writing central dir + // and make it a non disk spanning archive + if (m_pStorage->IsSpanMode() && m_pStorage->GetCurrentDisk() == 0) + { + DWORD uVolumeFree = m_pStorage->VolumeLeft(); + // calculate the size of data descriptors already in the buffer or on the disk + // (they will be removed in the non disk spanning archive): + // multi span signature at the beginnig (4 bytes) + the size of the data + // descr. for each file (multi span signature + 12 bytes data) + // the number of bytes to add: central dir size - total to remove; + DWORD uToGrow = GetSize(true) - (4 + m_info.m_uEntriesNumber * (4 + 12)); + if (uVolumeFree >= uToGrow) + // lets make sure it will be one-disk archive + { + // can the operation be done only in the buffer? + if (!m_pStorage->m_iBytesWritten && // no bytes on the disk yet + (m_pStorage->GetFreeInBuffer() >= uToGrow)) // is the buffer big enough? + { + RemoveDataDescr(true); + bDontAllowDiskChange = true; // if the disk change occurs somehow, we'll throw an error later + } + else + { + m_pStorage->Flush(); + if (RemoveDataDescr(false)) + bDontAllowDiskChange = true; // if the disk change occurs somehow, we'll throw an error later + } + } + } + + try + { + WriteHeaders(pCallback, bDontAllowDiskChange || !m_pStorage->IsSpanMode()); + + m_info.m_uThisDisk = (WORD)m_pStorage->GetCurrentDisk(); + DWORD uSize = WriteCentralEnd(); + if (bDontAllowDiskChange) + { + if (m_pStorage->GetCurrentDisk() != 0) + ThrowError(CZipException::badZipFile); + } + // if after adding a central directory there is a disk change, + // update the information and write it again + if (m_info.m_uThisDisk != m_pStorage->GetCurrentDisk()) + { + m_info.DiskChange(m_pStorage->GetCurrentDisk()); + + if (m_pStorage->m_uBytesInWriteBuffer >= uSize) + // if the data is still in the buffer, simply remove it + m_pStorage->m_uBytesInWriteBuffer -= uSize; + else + { + m_pStorage->Flush(); + m_pStorage->m_iBytesWritten -= uSize; + m_pStorage->m_pFile->SeekToBegin(); + } + + WriteCentralEnd(); + } + } + catch (...) + { + if (bDontAllowDiskChange) + { + m_pStorage->FinalizeSpan(); + m_info.m_uThisDisk = 0; + } + throw; + } + m_info.m_bOnDisk = true; +} + +void CZipCentralDir::WriteHeaders(CZipActionCallback* pCallback, bool bOneDisk) +{ + m_info.m_uDiskEntriesNo = 0; + m_info.m_uDiskWithCD = (WORD)m_pStorage->GetCurrentDisk(); + m_info.m_uOffset = m_pStorage->GetPosition() - m_info.m_uBytesBeforeZip; + if (!m_info.m_uEntriesNumber) + return; + + WORD iDisk = m_info.m_uDiskWithCD; + int iStep = 0; // for the compiler + + if (pCallback) + { + pCallback->Init(); + pCallback->SetTotal(m_info.m_uEntriesNumber); + iStep = CZipActionCallback::m_iStep;// we don't want to wait forever + } + + int iAborted = 0; + for (int i = 0; i < m_info.m_uEntriesNumber; i++) + { + CZipFileHeader* pHeader = (*this)[i]; + + + CZipString szRemember; + if (m_bConvertAfterOpen) + // if CZipArchive::Flush is called we will be still using the archive, so restore changed name + szRemember = pHeader->GetFileName(); + + ConvertFileName(false, true, pHeader); + m_info.m_uSize += pHeader->Write(m_pStorage); + + if (m_bConvertAfterOpen) + pHeader->SetFileName(szRemember); + + if (m_pStorage->GetCurrentDisk() != iDisk) + { + m_info.m_uDiskEntriesNo = 1; + iDisk = (WORD)m_pStorage->GetCurrentDisk(); + // update the information about the offset and starting disk if the + // first header was written on the new disk + if (i == 0) + { + m_info.m_uOffset = 0; + m_info.m_uDiskWithCD = iDisk; + } + } + else + m_info.m_uDiskEntriesNo++; + if (pCallback && !(i%iStep)) + if (!pCallback->Callback(iStep)) + { + + if (bOneDisk) + { + if (!m_pStorage->IsSpanMode()) + m_pStorage->EmptyWriteBuffer(); + else + m_pStorage->Flush(); // must be flush before - flush was not called in span mode + + // remove saved part from the disk + m_pStorage->m_pFile->SetLength(m_info.m_uBytesBeforeZip + m_info.m_uOffset); +// We can now abort safely + iAborted = CZipException::abortedSafely; + } + else + iAborted = CZipException::abortedAction; + break; + } + } + + if (pCallback) + pCallback->CallbackEnd(); + + if (iAborted) + ThrowError(iAborted); +} + +DWORD CZipCentralDir::WriteCentralEnd() +{ + DWORD uSize = GetSize(); + CZipAutoBuffer buf(uSize); + WORD uCommentSize = (WORD)m_pszComment.GetSize(); + memcpy(buf, m_gszSignature, 4); + memcpy(buf + 4, &m_info.m_uThisDisk, 2); + memcpy(buf + 6, &m_info.m_uDiskWithCD, 2); + memcpy(buf + 8, &m_info.m_uDiskEntriesNo, 2); + memcpy(buf + 10, &m_info.m_uEntriesNumber, 2); + memcpy(buf + 12, &m_info.m_uSize, 4); + memcpy(buf + 16, &m_info.m_uOffset, 4); + memcpy(buf + 20, &uCommentSize, 2); + memcpy(buf + 22, m_pszComment, uCommentSize); + m_pStorage->Write(buf, uSize, true); + return uSize; +} + +void CZipCentralDir::RemoveAll() +{ + m_findarray.RemoveAll(); + RemoveHeaders(); +} + +void CZipCentralDir::RemoveFile(CZipFileHeader* pHeader, int iIndex, bool bShift) +{ + + if (iIndex == -1) + { + int iCount = m_headers.GetSize(); + for (int i = 0; i < iCount; i++) + if (pHeader == m_headers[i]) + { + iIndex = i; + break; + } + } + ASSERT(iIndex != -1 || pHeader); + if (!pHeader) + pHeader = m_headers[iIndex]; + + if (m_bFindFastEnabled) + { + int i = FindFileNameIndex(pHeader->GetFileName()); + ASSERT(i != -1); + int uIndex = m_findarray[i].m_uIndex; + m_findarray.RemoveAt(i); + // shift down the indexes + + if (bShift) + { + int iSize = m_findarray.GetSize(); + for (int j = 0; j < iSize; j++) + { + if (m_findarray[j].m_uIndex > uIndex) + m_findarray[j].m_uIndex--; + } + } + } + + + + if (iIndex != -1) + { + delete pHeader; + m_headers.RemoveAt(iIndex); + } + +} + + +DWORD CZipCentralDir::GetSize(bool bWhole) const +{ + DWORD uHeaders = 0; + int iCount = m_headers.GetSize(); + if (bWhole) + { + for (int i = 0; i < iCount; i++) + { + const CZipFileHeader* pHeader = m_headers[i]; + uHeaders += pHeader->GetSize(); + } + } + return CENTRALDIRSIZE + m_pszComment.GetSize() + uHeaders; +} + +bool CZipCentralDir::RemoveDataDescr(bool bFromBuffer) +{ + ziparchv::CZipFileMapping fm; + char* pFile; + DWORD uSize; + if (bFromBuffer) + { + uSize = m_pStorage->m_uBytesInWriteBuffer; + pFile = m_pStorage->m_pWriteBuffer; + } + else + { + uSize = m_pStorage->m_pFile->GetLength(); + // we cannot use CZipMemFile in multidisk archive + // so it MUST be CZipFile + if (!fm.CreateMapping(static_cast(m_pStorage->m_pFile))) + return false; + pFile = fm.GetMappedMemory(); + } + + DWORD uOffsetToChange = 4; + DWORD uPosInBuffer = 0; + DWORD uExtraHeaderLen; + int iCount = m_headers.GetSize(); + for (int i = 0; i < iCount; i++) + { + // update the flag value in the local and central header +// int uDataDescr = (m_headers[i]->m_uFlag & 8) ? (4 + 12) : 0; + + CZipFileHeader* pHeader = m_headers[i]; + + + char* pSour = pFile + pHeader->m_uOffset; + + if (!pHeader->IsEncrypted()) + { + // removing data descriptor + pHeader->m_uFlag &= ~8; + // update local header: + // write modified flag in the local header + memcpy(pSour + 6, &pHeader->m_uFlag, 2); + uExtraHeaderLen = 4/*ext. header signature*/ + 12/*data descriptor*/; + } + else + // do not remove data descriptors from encrypted files + uExtraHeaderLen = 0; + + // update crc32 and sizes' values + pHeader->GetCrcAndSizes(pSour+ 14); + + DWORD uToCopy = (i == (iCount - 1) ? uSize : m_headers[i + 1]->m_uOffset) + - pHeader->m_uOffset - uExtraHeaderLen; + + memmove(pFile + uPosInBuffer, pSour, uToCopy); + + uPosInBuffer += uToCopy; + pHeader->m_uOffset -= uOffsetToChange; + uOffsetToChange += uExtraHeaderLen; + } + + if (bFromBuffer) + m_pStorage->m_uBytesInWriteBuffer = uPosInBuffer; + else + { + m_pStorage->m_iBytesWritten = uPosInBuffer; + fm.RemoveMapping(); + m_pStorage->m_pFile->SetLength(uPosInBuffer); + } + return true; +} + +void CZipCentralDir::RemoveHeaders() +{ + int iCount = m_headers.GetSize(); + for (int i = 0; i < iCount; i++) + delete m_headers[i]; + m_headers.RemoveAll(); +} + + + +void CZipCentralDir::ConvertAll() +{ + ASSERT(!m_bConvertAfterOpen); + int iCount = m_headers.GetSize(); + for (int i = 0; i < iCount; i++) + ConvertFileName(true, false, m_headers[i]); + m_bConvertAfterOpen = true; +} + +void CZipCentralDir::BuildFindFastArray( bool bCaseSensitive ) +{ + m_findarray.RemoveAll(); + m_bCaseSensitive = bCaseSensitive; + m_pCompare = GetCZipStrCompFunc(bCaseSensitive); + int iCount = m_headers.GetSize(); + if (!m_bConvertAfterOpen) + { + for (int i = 0; i < iCount; i++) + { + CZipFileHeader fh = *m_headers[i]; + ConvertFileName(true, false, &fh); + InsertFindFastElement(&fh, i); // this method requires the name to be already converted + } + } + else + for (int i = 0; i < iCount; i++) + InsertFindFastElement(m_headers[i], i); +} + +void CZipCentralDir::EnableFindFast(bool bEnable, bool bCaseSensitive) +{ + if (m_bFindFastEnabled == bEnable) + return; + m_bFindFastEnabled = bEnable; + if (bEnable) + BuildFindFastArray(bCaseSensitive); + else + m_findarray.RemoveAll(); +} + +int CZipCentralDir::FindFile(LPCTSTR lpszFileName, bool bCaseSensitive, bool bSporadically, bool bFileNameOnly) +{ + // this is required for fast finding and is done only once + if (!m_bConvertAfterOpen) + { + TRACE(_T("%s(%i) : Converting all the filenames.\n"),__FILE__,__LINE__); + ConvertAll(); + } + if (!m_bFindFastEnabled) + EnableFindFast(true, bSporadically ? !bCaseSensitive : bCaseSensitive); + int iResult = -1; + if (bFileNameOnly) + { + // a non-effective search (treat an array as unsorted) + int iSize = m_findarray.GetSize(); + for (int i = 0; i < iSize; i++) + { + CZipString sz = GetProperHeaderFileName(m_findarray[i].m_pHeader); + CZipPathComponent::RemoveSeparators(sz); // to find a dir + CZipPathComponent zpc(sz); + sz = zpc.GetFileName(); + if ((sz.*m_pCompare)(lpszFileName) == 0) + { + iResult = i; + break; + } + } + } + else if (bCaseSensitive == m_bCaseSensitive) + iResult = FindFileNameIndex(lpszFileName); + else + { + if (bSporadically) + { + // a non-effective search (treat an array as unsorted) + int iSize = m_findarray.GetSize(); + for (int i = 0; i < iSize; i++) + if (CompareElement(lpszFileName, (WORD)i) == 0) + { + iResult = i; + break; + } + } + else + { + BuildFindFastArray(bCaseSensitive); + iResult = FindFileNameIndex(lpszFileName); + } + } + return iResult == -1 ? -1 : m_findarray[iResult].m_uIndex; +} + +void CZipCentralDir::InsertFindFastElement(CZipFileHeader* pHeader, WORD uIndex) +{ + CZipString fileName = pHeader->GetFileName(); + int iSize = m_findarray.GetSize(); + + // Our initial binary search range encompasses the entire array of filenames: + int start = 0; + int end = iSize; + + // Keep halving our search range until we find the right place + // to insert the new element: + while ( start < end ) + { + // Find the midpoint of the search range: + int midpoint = ( start + end ) / 2; + + // Compare the filename with the filename at the midpoint of the current search range: + int result = CompareElement(fileName, (WORD)midpoint); + + // If our filename is larger, it must fall in the first half of the search range: + if ( result > 0 ) + { + end = midpoint; + } + + // If it's smaller, it must fall in the last half: + else if ( result < 0 ) + { + start = midpoint + 1; + } + + // If they're equal, we can go ahead and insert here: + else + { + start = midpoint; break; + } + } + m_findarray.InsertAt(start, CZipFindFast(pHeader, WORD(uIndex == WORD(-1) ? iSize : uIndex /* just in case */))); +} + +int CZipCentralDir::FindFileNameIndex(LPCTSTR lpszFileName) const +{ + int start = 0; + int end = m_findarray.GetUpperBound(); + + // Keep halving our search range until we find the given element: + while ( start <= end ) + { + // Find the midpoint of the search range: + int midpoint = ( start + end ) / 2; + + // Compare the given filename with the filename at the midpoint of the search range: + int result = CompareElement(lpszFileName, (WORD)midpoint); + + // If our filename is smaller, it must fall in the first half of the search range: + if ( result > 0 ) + { + end = midpoint - 1; + } + + // If it's larger, it must fall in the last half: + else if ( result < 0 ) + { + start = midpoint + 1; + } + + // If they're equal, return the result: + else + { + return midpoint; + } + } + + // Signal failure: + return -1; +} + +void CZipCentralDir::RenameFile(WORD uIndex, LPCTSTR lpszNewName) +{ + CZipFileHeader* pHeader = m_headers[uIndex]; + pHeader->SetFileName(lpszNewName); + if (!m_bConvertAfterOpen) + ZipCompatibility::FileNameUpdate(*pHeader, false); + if (m_bFindFastEnabled) + BuildFindFastArray(m_bCaseSensitive); +} \ No newline at end of file diff --git a/harbour/contrib/hbzlib/zipcompatibility.cpp b/harbour/contrib/hbzlib/zipcompatibility.cpp new file mode 100644 index 0000000000..4fee9a7ff6 --- /dev/null +++ b/harbour/contrib/hbzlib/zipcompatibility.cpp @@ -0,0 +1,214 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipCompatibility.cpp $ +// $Archive: /ZipArchive/ZipCompatibility.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "zipcompatibility.h" +#include "zipplatform.h" +#include "zipexception.h" +#include "zipautobuffer.h" +#include "zipfileheader.h" +enum iInternalAttr +{ + attROnly = 0x01, + attHidd = 0x02, + attSys = 0x04, + attDir = 0x10, + attArch = 0x20 +}; +// *********************** WINDOWS ************************** +#ifndef _WIN32 + #define FILE_ATTRIBUTE_READONLY 0x00000001 + #define FILE_ATTRIBUTE_HIDDEN 0x00000002 + #define FILE_ATTRIBUTE_SYSTEM 0x00000004 + #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 + #define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#endif +// *********************** UINX ************************** +#define USER_PERMISSIONS_MASK 0x01C00000 +#define EXTRACT_USER_PERMISSIONS(x) ((x & USER_PERMISSIONS_MASK) >> 22) +#define CREATE_USER_PERMISSIONS(x) ((x & 0x0007) << 22) + +#define GROUP_PERMISSIONS_MASK 0x00380000 +#define EXTRACT_GROUP_PERMISSIONS ((x & GROUP_PERMISSIONS_MASK) >> 19) +#define CREATE_GROUP_PERMISSIONS(x) ((x & 0x0007) << 19) + +#define OTHER_PERMISSIONS_MASK 0x00070000 +#define EXTRACT_OTHER_PERMISSIONS ((x & OTHER_PERMISSIONS_MASK) >> 16) +#define CREATE_OTHER_PERMISSIONS(x) ((x & 0x0007) << 16) + +#define UNIX_DIRECTORY_ATTRIBUTE 0x40000000 +#define UNIX_FILE_ATTRIBUTE 0x80000000 + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +using namespace ZipCompatibility; + +typedef DWORD(*conv_func)(DWORD , bool ); + +DWORD AttrDos(DWORD , bool ); +DWORD AttrUnix(DWORD, bool); +DWORD AttrMac(DWORD , bool ); + +// more to come... +conv_func conv_funcs[11] = {AttrDos, + NULL, + NULL, + AttrUnix, + NULL, + NULL, + AttrDos, + AttrMac, + NULL, + NULL, + AttrDos +}; + + + +DWORD ZipCompatibility::ConvertToSystem(DWORD uAttr, int iFromSystem, int iToSystem) +{ + + if (iToSystem != iFromSystem && iFromSystem < 11 && iToSystem < 11) + { + conv_func p = conv_funcs[iFromSystem], q = conv_funcs[iToSystem]; + if (p && q) + uAttr = q( p(uAttr, true), false); + else + CZipException::Throw(CZipException::platfNotSupp); + } + return uAttr; +} + + +DWORD AttrDos(DWORD uAttr, bool ) +{ + return uAttr; +} + + + +DWORD AttrUnix(DWORD uAttr, bool bFrom) +{ + DWORD uNewAttr = 0; + if (bFrom) + { + if (uAttr & UNIX_DIRECTORY_ATTRIBUTE) + uNewAttr = attDir; + + uAttr = EXTRACT_USER_PERMISSIONS (uAttr); + + // we may set archive attribute if the file hasn't the execute permissions + // + if (!(uAttr & 1)) + uNewAttr |= attArch ; + + if (!(uAttr & 2)) + uNewAttr |= attROnly; + + if (!(uAttr & 4)) + uNewAttr |= attHidd; + } + else + { + + uNewAttr = 0; // we cannot assume that if the file hasn't the archive attribute set + + //then it is executable and set execute permissions + + if (!(uAttr & attHidd)) + uNewAttr |= (CREATE_OTHER_PERMISSIONS (4) | + CREATE_GROUP_PERMISSIONS (4)) + | CREATE_USER_PERMISSIONS (4); + + if (!(uAttr & attROnly)) + uNewAttr |= (CREATE_GROUP_PERMISSIONS (2) | + CREATE_USER_PERMISSIONS (2)); + if (uAttr & attDir) + uNewAttr |= UNIX_DIRECTORY_ATTRIBUTE | attDir; + else + uNewAttr |= UNIX_FILE_ATTRIBUTE; + + } + + return uNewAttr; +} + +DWORD AttrMac(DWORD uAttr, bool ) +{ + DWORD uNewAttr = uAttr & (attDir | attROnly); +// if (bFrom) +// { +// +// } +// else +// { +// +// } + return uNewAttr; +} + +// ************************************************************************ +ZIPINLINE bool ZipCompatibility::IsPlatformSupported(int iCode) +{ + return iCode == zcDosFat || iCode == zcUnix || iCode == zcMacintosh + || iCode == zcNtfs || iCode == zcOs2Hpfs; +} + + +void ZipCompatibility::FileNameUpdate(CZipFileHeader& header, bool bFromZip) +{ + int iSysHeader = header.GetSystemCompatibility(); + int iCurSystem = ZipPlatform::GetSystemID(); + if (bFromZip) + { + if (iCurSystem == zcDosFat) + SlashBackslashChg(header.m_pszFileName, true); + if (iSysHeader == zcDosFat) + ZipPlatform::AnsiOem(header.m_pszFileName, false); + } + else + { + if (iSysHeader == zcDosFat) + { + ZipPlatform::AnsiOem(header.m_pszFileName, true); + + + } + SlashBackslashChg(header.m_pszFileName, false); + } +} + +void ZipCompatibility::SlashBackslashChg(CZipAutoBuffer& buffer, bool bReplaceSlash) +{ + char t1 = '\\' /*backslash*/, t2 = '/', c1, c2; + if (bReplaceSlash) + { + c1 = t1; + c2 = t2; + } + else + { + c1 = t2; + c2 = t1; + } + for (DWORD i = 0; i < buffer.GetSize(); i++) + { + if (buffer[i] == c2) + buffer[i] = c1; + } + +} diff --git a/harbour/contrib/hbzlib/zipexception.cpp b/harbour/contrib/hbzlib/zipexception.cpp new file mode 100644 index 0000000000..575f047354 --- /dev/null +++ b/harbour/contrib/hbzlib/zipexception.cpp @@ -0,0 +1,280 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipException.cpp $ +// $Archive: /ZipArchive/ZipException.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "zipexception.h" +#include + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +#ifdef _MFC_VER + IMPLEMENT_DYNAMIC( CZipException, CException) +#endif + +CZipException::CZipException(int iCause, LPCTSTR lpszZipName) +#ifdef _MFC_VER + :CException(TRUE) +#endif +{ + m_iCause = iCause; + + if (lpszZipName) + m_szFileName = lpszZipName; +} + +//CZipException::~CZipException() +#if __GNUC__ >=3 + CZipException::~CZipException() throw() +#else + CZipException::~CZipException() +#endif + +{ + +} + +// inline void CZipException::Throw(int iZipError, LPCTSTR lpszZipName) +// { +// #ifdef _MFC_VER +// throw new CZipException(iZipError, lpszZipName); +// #else +// CZipException e(iZipError, lpszZipName); +// throw e; +// #endif +// MSVC++: ignore "Unreachable code" warning here, it's due to +// optimizations +// } + +int CZipException::ZlibErrToZip(int iZlibError) +{ + switch (iZlibError) + { + case 2://Z_NEED_DICT: + return CZipException::needDict; + case 1://Z_STREAM_END: + return CZipException::streamEnd; + case -1://Z_ERRNO: + return CZipException::errNo; + case -2://Z_STREAM_ERROR: + return CZipException::streamError; + case -3://Z_DATA_ERROR: + return CZipException::dataError; + case -4://Z_MEM_ERROR: + return CZipException::memError; + case -5://Z_BUF_ERROR: + return CZipException::bufError; + case -6://Z_VERSION_ERROR: + return CZipException::versionError; + default: + return CZipException::generic; + } + +} + +#ifdef ZIP_ENABLE_ERROR_DESCRIPTION + +BOOL CZipException::GetErrorMessage(LPTSTR lpszError, UINT nMaxError, + UINT* ) + +{ + if (!lpszError || !nMaxError) + return FALSE; + CZipString sz = GetErrorDescription(); + if (sz.IsEmpty()) + return FALSE; + UINT iLen = sz.GetLength(); + if (nMaxError - 1 < iLen) + iLen = nMaxError - 1; + LPTSTR lpsz = sz.GetBuffer(iLen); +#ifdef _UNICODE + wcsncpy(lpszError, lpsz, iLen); +#else + strncpy(lpszError, lpsz, iLen); +#endif + lpszError[iLen] = _T('\0'); + return TRUE; +} + + +CZipString CZipException::GetErrorDescription() +{ + return GetInternalErrorDescription(m_iCause); +} + + +CZipString CZipException::GetSystemErrorDescription() +{ +#ifdef WIN32 + DWORD x = GetLastError(); + if (x) + { + LPVOID lpMsgBuf; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, x, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL); + CZipString sz = (LPCTSTR)lpMsgBuf; + LocalFree(lpMsgBuf); + return sz; + } +#endif + return GetInternalErrorDescription(errno == 0 ? generic : errno, true); +} + +CZipString CZipException::GetInternalErrorDescription(int iCause, bool bNoLoop) +{ + CZipString sz; + switch (iCause) + { + case EROFS: + sz = _T("Read-only file system"); + break; + case ESPIPE: + sz = _T("Illegal seek"); + break; + case ENOSPC: + sz = _T("No space left on device"); + break; + case EFBIG: + sz = _T("File too large"); + break; + case EMFILE: + sz = _T("Too many open files"); + break; + case ENFILE: + sz = _T("File table overflow"); + break; + case EINVAL: + sz = _T("Invalid argument"); + break; + case EISDIR: + sz = _T("Is a directory"); + break; + case ENOTDIR: + sz = _T("Not a directory"); + break; + case ENODEV: + sz = _T("No such device"); + break; + case EXDEV: + sz = _T("Cross-device link"); + break; + case EEXIST: + sz = _T("File exists"); + break; + case EFAULT: + sz = _T("Bad address"); + break; + case EACCES: + sz = _T("Permission denied"); + break; + case ENOMEM: + sz = _T("Not enough space"); + break; + case EBADF: + sz = _T("Bad file number"); + break; + case ENXIO: + sz = _T("No such device or address"); + break; + case EIO: + sz = _T("I/O error"); + break; + case EINTR: + sz = _T("Interrupted system call"); + break; + case ENOENT: + sz = _T("No such file or directory"); + break; + case EPERM: + sz = _T("Not super-user"); + break; + case badZipFile: + sz = _T("Damaged or not a zip file"); + break; + case badCrc: + sz = _T("Crc mismatched"); + break; + case noCallback: + sz = _T("No disk-spanning callback functor set"); + break; + case aborted: + sz = _T("Disk change aborted"); + break; + case abortedAction: + sz = _T("Action aborted"); + break; + case abortedSafely: + sz = _T("Action aborted safely"); + break; + case nonRemovable: + sz = _T("The device selected for the disk spanning archive is non removable"); + break; + case tooManyVolumes: + sz = _T("Limit of the maximum volumes reached (999)"); + break; + case tooLongFileName: + sz = _T("The filename of the file being added to the archive is too long"); + break; + case badPassword: + sz = _T("Incorrect password set for the file being decrypted"); + break; + case dirWithSize: + sz = _T("During testing found the directory with the size greater than 0"); + break; + case internal: + sz = _T("Internal error"); + break; + case notRemoved: + sz.Format(_T("%s (%s)"), _T("Error while removing a file"), (LPCTSTR)GetSystemErrorDescription()); + break; + case notRenamed: + sz.Format(_T("%s (%s)"), _T("Error while renaming a file"), (LPCTSTR)GetSystemErrorDescription()); + break; + case platfNotSupp: + sz = _T("Cannot create the file for the specified platform"); + break; + case cdirNotFound: + sz = _T("The central directory was not found in the archive (or you were trying to open not the last disk of a multi-disk archive)"); + break; + case streamEnd: + sz = _T("Zlib Library error (end of stream)"); + break; + case errNo: + sz = GetInternalErrorDescription(errno != errNo ? errno : generic); + break; + case streamError: + sz = _T("Zlib library error (stream error)"); + break; + case dataError: + sz = _T("Zlib library error (data error)"); + break; + case memError: + sz = _T("Not enough memory"); + break; + case bufError: + sz = _T("Zlib library error (buffer error)"); + break; + case versionError: + sz = _T("Zlib library error (version error)"); + break; + default: + sz = bNoLoop ? _T("Unknown error") :(LPCTSTR) GetSystemErrorDescription(); + } + return sz; +} + +#endif //ZIP_ENABLE_ERROR_DESCRIPTION diff --git a/harbour/contrib/hbzlib/zipfile.cpp b/harbour/contrib/hbzlib/zipfile.cpp new file mode 100644 index 0000000000..cf9dc841a4 --- /dev/null +++ b/harbour/contrib/hbzlib/zipfile.cpp @@ -0,0 +1,114 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipFile.cpp $ +// $Archive: /ZipArchive_STL/ZipFile.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "zipfile.h" +#include "zipexception.h" +#include "zipplatform.h" + +#include + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CZipFile::CZipFile() +{ + m_hFile = -1; +} + + + + +void CZipFile::ThrowError() const +{ + CZipException::Throw(errno, m_szFileName); +} + + +ZIP_ULONGLONG CZipFile::GetLength() const +{ +// cannot use Seek here, Seek is not const + long lLen, lCur; + lCur = lseek(m_hFile, 0, current); + if (lCur == -1) + ThrowError(); + lLen = lseek(m_hFile, 0, end); + // first get back + lseek(m_hFile, lCur, begin); + if (lLen == -1) + ThrowError(); + return lLen; + +} + + +bool CZipFile::Open(LPCTSTR lpszFileName, UINT openFlags, bool bThrow) +{ + if (!IsClosed()) + Close(); +#ifndef __GNUC__ + UINT iNewFlags = O_BINARY; +#else + UINT iNewFlags = 0; +#endif + bool bReadOnly = false; + if (openFlags & CZipFile::modeCreate) + iNewFlags |= O_CREAT; + if ((openFlags & CZipFile::modeReadWrite) == CZipFile::modeReadWrite) + iNewFlags |= O_RDWR; + else if (openFlags & CZipFile::modeRead) + { + // O_RDONLY is defined as 0 + bReadOnly = true; + iNewFlags |= O_RDONLY; + } + else if (openFlags & CZipFile::modeWrite) + iNewFlags |= O_WRONLY; + + if (!(openFlags & CZipFile::modeNoTruncate) && !bReadOnly) + iNewFlags |= O_TRUNC; + + m_hFile = ZipPlatform::OpenFile(lpszFileName, iNewFlags, openFlags & 0x1C); + if (m_hFile == -1) + if (bThrow) + ThrowError(); + else + return false; + m_szFileName = lpszFileName; + return true; +} + + +void CZipFile::SetLength(ZIP_ULONGLONG nNewLen) +{ + ZipPlatform::TruncateFile(m_hFile, (DWORD)nNewLen); +} + + +void CZipFile::Flush() +{ + if (!ZipPlatform::FlushFile(m_hFile)) + ThrowError(); +} + +CZipFile::operator HANDLE() +{ + int fh = ZipPlatform::GetFileSystemHandle(m_hFile); + if (fh == -1) + ThrowError(); + return (HANDLE)fh; +} diff --git a/harbour/contrib/hbzlib/zipfileheader.cpp b/harbour/contrib/hbzlib/zipfileheader.cpp new file mode 100644 index 0000000000..fb810f8784 --- /dev/null +++ b/harbour/contrib/hbzlib/zipfileheader.cpp @@ -0,0 +1,340 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipFileHeader.cpp $ +// $Archive: /ZipArchive_STL/ZipFileHeader.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#include "zipfileheader.h" +#include "zipautobuffer.h" +#include "ziparchive.h" +#include "zipplatform.h" +#include "zipcompatibility.h" +#include + +#define FILEHEADERSIZE 46 +#define LOCALFILEHEADERSIZE 30 + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +char CZipFileHeader::m_gszSignature[] = {0x50, 0x4b, 0x01, 0x02}; +char CZipFileHeader::m_gszLocalSignature[] = {0x50, 0x4b, 0x03, 0x04}; +CZipFileHeader::CZipFileHeader() +{ + m_uExternalAttr = 0;//ZipPlatform::GetDefaultAttributes(); + m_uModDate = m_uModTime = 0; + m_uMethod = Z_DEFLATED; +// SetSystemCompatibility(ZipPlatform::m_sSystemID); +} + +CZipFileHeader::~CZipFileHeader() +{ + +} + +// read the header from the central dir +bool CZipFileHeader::Read(CZipStorage *pStorage) +{ +// // just in case +// m_pszComment.Release(); +// m_pszFileName.Release(); + WORD uFileNameSize, uCommentSize, uExtraFieldSize; + CZipAutoBuffer buf(FILEHEADERSIZE); + pStorage->Read(buf, FILEHEADERSIZE, true); + memcpy(&m_szSignature, buf, 4); + memcpy(&m_uVersionMadeBy, buf + 4, 2); + memcpy(&m_uVersionNeeded, buf + 6, 2); + memcpy(&m_uFlag, buf + 8, 2); + memcpy(&m_uMethod, buf + 10, 2); + memcpy(&m_uModTime, buf + 12, 2); + memcpy(&m_uModDate, buf + 14, 2); + memcpy(&m_uCrc32, buf + 16, 4); + memcpy(&m_uComprSize, buf + 20, 4); + memcpy(&m_uUncomprSize, buf + 24, 4); + memcpy(&uFileNameSize, buf + 28, 2); + memcpy(&uExtraFieldSize, buf + 30, 2); + memcpy(&uCommentSize, buf + 32, 2); + memcpy(&m_uDiskStart, buf + 34, 2); + memcpy(&m_uInternalAttr, buf + 36, 2); + memcpy(&m_uExternalAttr, buf + 38, 4); + memcpy(&m_uOffset, buf + 42, 4); + buf.Release(); + + if (memcmp(m_szSignature, m_gszSignature, 4) != 0) + return false; + + int iCurDsk = pStorage->GetCurrentDisk(); + m_pszFileName.Allocate(uFileNameSize); // don't add NULL at the end + pStorage->Read(m_pszFileName, uFileNameSize, true); + if (uExtraFieldSize) + { + ASSERT(!m_pExtraField.IsAllocated()); + m_pExtraField.Allocate(uExtraFieldSize); + pStorage->Read(m_pExtraField, uExtraFieldSize, true); + } + if (uCommentSize) + { + m_pszComment.Allocate(uCommentSize); + pStorage->Read(m_pszComment, uCommentSize, true); + } + + return pStorage->GetCurrentDisk() == iCurDsk; // check that the whole header is on the one disk +} + +time_t CZipFileHeader::GetTime()const +{ + struct tm atm; + atm.tm_sec = (m_uModTime & ~0xFFE0) << 1; + atm.tm_min = (m_uModTime & ~0xF800) >> 5; + atm.tm_hour = m_uModTime >> 11; + + atm.tm_mday = m_uModDate & ~0xFFE0; + atm.tm_mon = ((m_uModDate & ~0xFE00) >> 5) - 1; + atm.tm_year = (m_uModDate >> 9) + 80; + atm.tm_isdst = -1; + return mktime(&atm); +} + +// write the header to the central dir +DWORD CZipFileHeader::Write(CZipStorage *pStorage) +{ + WORD uFileNameSize = GetFileNameSize(), uCommentSize = GetCommentSize(), + uExtraFieldSize = GetExtraFieldSize(); + DWORD iSize = FILEHEADERSIZE + uFileNameSize + uCommentSize + uExtraFieldSize; + CZipAutoBuffer buf(iSize); + memcpy(buf, &m_szSignature, 4); + memcpy(buf + 4, &m_uVersionMadeBy, 2); + memcpy(buf + 6, &m_uVersionNeeded, 2); + memcpy(buf + 8, &m_uFlag, 2); + memcpy(buf + 10, &m_uMethod, 2); + memcpy(buf + 12, &m_uModTime, 2); + memcpy(buf + 14, &m_uModDate, 2); + memcpy(buf + 16, &m_uCrc32, 4); + memcpy(buf + 20, &m_uComprSize, 4); + memcpy(buf + 24, &m_uUncomprSize, 4); + memcpy(buf + 28, &uFileNameSize, 2); + memcpy(buf + 30, &uExtraFieldSize, 2); + memcpy(buf + 32, &uCommentSize, 2); + memcpy(buf + 34, &m_uDiskStart, 2); + memcpy(buf + 36, &m_uInternalAttr, 2); + memcpy(buf + 38, &m_uExternalAttr, 4); + memcpy(buf + 42, &m_uOffset, 4); + + memcpy(buf + 46, m_pszFileName, uFileNameSize); + + if (uExtraFieldSize) + memcpy(buf + 46 + uFileNameSize, m_pExtraField, uExtraFieldSize); + + if (uCommentSize) + memcpy(buf + 46 + uFileNameSize + uExtraFieldSize, m_pszComment, uCommentSize); + + pStorage->Write(buf, iSize, true); + return iSize; +} + +bool CZipFileHeader::ReadLocal(CZipStorage *pStorage, WORD& iLocExtrFieldSize) +{ + char buf[LOCALFILEHEADERSIZE]; + pStorage->Read(buf, LOCALFILEHEADERSIZE, true); + if (memcmp(buf, m_gszLocalSignature, 4) != 0) + return false; + + bool bIsDataDescr = (((WORD)*(buf + 6)) & 8) != 0; + + WORD uFileNameSize = GetFileNameSize(); + WORD uTemp; + memcpy(&uTemp, buf+6, 2); // give the priority to the local flag + if ((uTemp & 0xf) != (m_uFlag & 0xf)) + m_uFlag = uTemp; + if ((memcmp(buf + 8, &m_uMethod, 2) != 0) + || (m_uMethod && (m_uMethod != Z_DEFLATED)) + || (memcmp(buf + 26, &uFileNameSize, 2) != 0)) + return false; + +// jeszcze możnaby porównać nazwy plików + + if (!bIsDataDescr/* || !pStorage->IsSpanMode()*/) + if (!CheckCrcAndSizes(buf + 14)) + return false; + + memcpy(&iLocExtrFieldSize, buf + 28, 2); + pStorage->m_pFile->Seek(uFileNameSize, CZipAbstractFile::current); + + return true; +} + +void CZipFileHeader::SetTime(const time_t & ttime) +{ + tm* gt = localtime(&ttime); + WORD year = (WORD)(gt->tm_year + 1900); + if (year <= 1980) + year = 0; + else + year -= 1980; + m_uModDate = (WORD) (gt->tm_mday + ((gt->tm_mon + 1)<< 5) + (year << 9)); + m_uModTime = (WORD) ((gt->tm_sec >> 1) + (gt->tm_min << 5) + + (gt->tm_hour << 11)); +} +// the buffer contains crc32, compressed and uncompressed sizes to be compared +// with the actual values +bool CZipFileHeader::CheckCrcAndSizes(char *pBuf) const +{ + return (memcmp(pBuf, &m_uCrc32, 4) == 0) && (memcmp(pBuf + 4, &m_uComprSize, 4) == 0) + && (memcmp(pBuf + 8, &m_uUncomprSize, 4) == 0); +} + +// write the local header +void CZipFileHeader::WriteLocal(CZipStorage& storage) +{ + // extra field is local by now + WORD uFileNameSize = GetFileNameSize(), uExtraFieldSize = GetExtraFieldSize(); + DWORD iLocalSize = LOCALFILEHEADERSIZE + uExtraFieldSize + uFileNameSize; + CZipAutoBuffer buf(iLocalSize); + memcpy(buf, m_gszLocalSignature, 4); + memcpy(buf + 4, &m_uVersionNeeded, 2); + memcpy(buf + 6, &m_uFlag, 2); + memcpy(buf + 8, &m_uMethod, 2); + memcpy(buf + 10, &m_uModTime, 2); + memcpy(buf + 12, &m_uModDate, 2); + memcpy(buf + 14, &m_uCrc32, 4); + memcpy(buf + 18, &m_uComprSize, 4); + memcpy(buf + 22, &m_uUncomprSize, 4); + memcpy(buf + 26, &uFileNameSize, 2); + memcpy(buf + 28, &uExtraFieldSize, 2); + memcpy(buf + 30, m_pszFileName, uFileNameSize); + memcpy(buf + 30 + uFileNameSize, m_pExtraField, uExtraFieldSize); + + // possible disk change before writing to the file in the disk spanning mode + // so write the local header first + storage.Write(buf, iLocalSize, true); + // it was only local information, use CZipArchive::SetExtraField to set the file extra field in the central directory + m_pExtraField.Release(); + + m_uDiskStart = (WORD)storage.GetCurrentDisk(); + m_uOffset = storage.GetPosition() - iLocalSize; +} + +// prepare the data before adding a new file +bool CZipFileHeader::PrepareData(int iLevel, bool bSpan, bool bEncrypted) +{ + memcpy(m_szSignature, m_gszSignature, 4); + m_uInternalAttr = 0; + m_uVersionNeeded = IsDirectory() ? 0xa : 0x14; // 1.0 or 2.0 + SetVersion((WORD)(0x14)); + + m_uCrc32 = 0; + m_uComprSize = 0; + m_uUncomprSize = 0; + if (iLevel == 0) + m_uMethod = 0; + + if ((m_uMethod != Z_DEFLATED) && (m_uMethod != 0)) + m_uMethod = Z_DEFLATED; + + m_uFlag = 0; + if (m_uMethod == Z_DEFLATED) + switch (iLevel) + { + case 1: + m_uFlag |= 6; + break; + case 2: + m_uFlag |= 4; + break; + case 8: + case 9: + m_uFlag |= 2; + break; + } + + if (bSpan || bEncrypted) + m_uFlag |= 8; // data descriptor present + + if (bEncrypted) + { + m_uComprSize = ZIPARCHIVE_ENCR_HEADER_LEN; // encrypted header + m_uFlag |= 1; // encrypted file + } + + return !(m_pszComment.GetSize() > USHRT_MAX || m_pszFileName.GetSize() > USHRT_MAX + || m_pExtraField.GetSize() > USHRT_MAX); +} + +void CZipFileHeader::GetCrcAndSizes(char * pBuffer)const +{ + memcpy(pBuffer, &m_uCrc32, 4); + memcpy(pBuffer + 4, &m_uComprSize, 4); + memcpy(pBuffer + 8, &m_uUncomprSize, 4); +} + +DWORD CZipFileHeader::GetSize(bool bLocal)const +{ + if (bLocal) + return LOCALFILEHEADERSIZE + GetExtraFieldSize() + GetFileNameSize(); + else + return FILEHEADERSIZE + GetExtraFieldSize() + GetFileNameSize() + GetCommentSize(); +} + + +bool CZipFileHeader::SetComment(LPCTSTR lpszComment) +{ + return CZipArchive::WideToSingle(lpszComment, m_pszComment) != -1; +} + +CZipString CZipFileHeader::GetComment() const +{ + CZipString temp; + CZipArchive::SingleToWide(m_pszComment, temp); + return temp; + +} + +bool CZipFileHeader::SetFileName(LPCTSTR lpszFileName) +{ + + return CZipArchive::WideToSingle(lpszFileName, m_pszFileName) != -1; +} + +CZipString CZipFileHeader::GetFileName()const +{ + CZipString temp; + CZipArchive::SingleToWide(m_pszFileName, temp); + return temp; +} + +bool CZipFileHeader::IsDirectory()const +{ + return ZipPlatform::IsDirectory(GetSystemAttr()); +} + +DWORD CZipFileHeader::GetSystemAttr()const +{ + int iSystemComp = GetSystemCompatibility(); + if (ZipCompatibility::IsPlatformSupported(iSystemComp)) + { + if (!m_uExternalAttr && CZipPathComponent::HasEndingSeparator(GetFileName())) + return ZipPlatform::GetDefaultDirAttributes(); // can happen + else + return ZipCompatibility::ConvertToSystem(m_uExternalAttr, iSystemComp, ZipPlatform::GetSystemID()); + } + else + return CZipPathComponent::HasEndingSeparator(GetFileName()) ? ZipPlatform::GetDefaultDirAttributes() : ZipPlatform::GetDefaultAttributes(); +} + + +void CZipFileHeader::SetSystemAttr(DWORD uAttr) +{ + m_uExternalAttr = ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), GetSystemCompatibility()); +} diff --git a/harbour/contrib/hbzlib/zipmemfile.cpp b/harbour/contrib/hbzlib/zipmemfile.cpp new file mode 100644 index 0000000000..a25fb3f7ba --- /dev/null +++ b/harbour/contrib/hbzlib/zipmemfile.cpp @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipMemFile.cpp $ +// $Archive: /ZipArchive/ZipMemFile.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "zipmemfile.h" +#include "zipexception.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +void CZipMemFile::Grow(size_t nGrowTo) +{ + if (m_nBufSize < (UINT)nGrowTo) + { + if (m_nGrowBy == 0) + CZipException::Throw(CZipException::memError); + size_t nNewSize = m_nBufSize; + while (nNewSize < nGrowTo) + nNewSize += m_nGrowBy; + BYTE* lpNew; + if (m_lpBuf) + lpNew = (BYTE*)hb_xrealloc((void*) m_lpBuf, nNewSize); + else + lpNew = (BYTE*)hb_xalloc(nNewSize); + + if (!lpNew) + CZipException::Throw(CZipException::memError); + m_nBufSize = nNewSize; + m_lpBuf = lpNew; + } +} + +void CZipMemFile::SetLength(ZIP_ULONGLONG nNewLen) +{ + if (m_nBufSize < (UINT)nNewLen) + Grow((size_t)nNewLen); + else + m_nPos = (size_t)nNewLen; + m_nDataSize = (size_t)nNewLen; +} + +UINT CZipMemFile::Read(void *lpBuf, UINT nCount) +{ + if (m_nPos > m_nDataSize) + return 0; + UINT nToRead = (m_nPos + nCount > m_nDataSize) ? m_nDataSize - m_nPos : nCount; + memcpy(lpBuf, m_lpBuf + m_nPos, nToRead); + m_nPos += nToRead; + return nToRead; + +} + +void CZipMemFile::Write(const void *lpBuf, UINT nCount) +{ + if (!nCount) + return; + + if (m_nPos + nCount > m_nBufSize) + Grow(m_nPos + nCount); + memcpy(m_lpBuf + m_nPos, lpBuf, nCount); + m_nPos += nCount; + if (m_nPos > m_nDataSize) + m_nDataSize = m_nPos; +} + +ZIP_ULONGLONG CZipMemFile::Seek(ZIP_LONGLONG lOff, int nFrom) +{ + ZIP_ULONGLONG lNew = m_nPos; + + if (nFrom == CZipAbstractFile::begin) + lNew = lOff; + else if (nFrom == CZipAbstractFile::current) + lNew += lOff; + else if (nFrom == CZipAbstractFile::end) + lNew = m_nDataSize + lOff; + else + return lNew; + + if (lNew< 0) + CZipException::Throw(CZipException::memError); + + m_nPos = (size_t)lNew; + return lNew; +} diff --git a/harbour/contrib/hbzlib/zippathcomponent.cpp b/harbour/contrib/hbzlib/zippathcomponent.cpp new file mode 100644 index 0000000000..79cb90560b --- /dev/null +++ b/harbour/contrib/hbzlib/zippathcomponent.cpp @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipPathComponent.cpp $ +// $Archive: /ZipArchive/ZipPathComponent.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "zippathcomponent.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +CZipPathComponent::~CZipPathComponent() +{ + +} + +void CZipPathComponent::SetFullPath(LPCTSTR lpszFullPath) +{ + + TCHAR szDrive[_MAX_DRIVE]; + TCHAR szDir[_MAX_DIR]; + TCHAR szFname[_MAX_FNAME]; + TCHAR szExt[_MAX_EXT]; + + + CZipString szTempPath(lpszFullPath); + const CZipString szPrefix = _T("\\\\?\\unc\\"); + int i = -1, iLen = szPrefix.GetLength(); + if (iLen > szTempPath.GetLength()) + iLen = szTempPath.GetLength(); + CZipString szPossiblePrefix = szTempPath.Left(iLen); + szPossiblePrefix.MakeLower(); // must perform case insensitive comparison + while (++i < iLen && szPossiblePrefix[i] == szPrefix[i]); + if (i == 2 || i == 4 || i == 8) // unc path, unicode path or unc path meeting windows file name conventions + { + m_szPrefix = szTempPath.Left(i); + szTempPath = szTempPath.Mid(i); + } + else + m_szPrefix.Empty(); + + _tsplitpath(szTempPath, szDrive , szDir, szFname, szExt); + m_szDrive = szDrive; + m_szDirectory = szDir; + + m_szDirectory.TrimLeft(m_cSeparator); + m_szDirectory.TrimRight(m_cSeparator); + SetExtension(szExt); + m_szFileTitle = szFname; +} + + +CZipString CZipPathComponent::GetNoDrive() const +{ + CZipString szPath = m_szDirectory; + CZipString szFileName = GetFileName(); + if (!szFileName.IsEmpty() && !szPath.IsEmpty()) + szPath += m_cSeparator; + + szPath += szFileName; + return szPath; +} + diff --git a/harbour/contrib/hbzlib/zipplatform.cpp b/harbour/contrib/hbzlib/zipplatform.cpp new file mode 100644 index 0000000000..df56778e29 --- /dev/null +++ b/harbour/contrib/hbzlib/zipplatform.cpp @@ -0,0 +1,351 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipPlatform.cpp $ +// $Archive: /ZipArchive/ZipPlatform.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#include "zipplatform.h" +#include "zipfileheader.h" +#include "zipexception.h" +#include "zipautobuffer.h" +#include + +#if defined _MSC_VER && !defined __BORLANDC__ /*_MSC_VER may be defined in Borland after converting the VC project */ + #include +#else + #include +#endif + +#include +#include +#include +#include "zippathcomponent.h" +#include "zipcompatibility.h" + +const TCHAR CZipPathComponent::m_cSeparator = _T('\\'); + + +#ifndef _UTIMBUF_DEFINED +#define _utimbuf utimbuf +#endif + +DWORD ZipPlatform::GetDeviceFreeSpace(LPCTSTR lpszPath) +{ + DWORD SectorsPerCluster, BytesPerSector, NumberOfFreeClusters, TotalNumberOfClusters; + CZipPathComponent zpc (lpszPath); + CZipString szDrive = zpc.GetFileDrive(); + if (!GetDiskFreeSpace( + szDrive, + &SectorsPerCluster, + &BytesPerSector, + &NumberOfFreeClusters, + &TotalNumberOfClusters)) + { + CZipPathComponent::AppendSeparator(szDrive); // in spite of what is written in MSDN it is sometimes needed (on fixed disks) + if (!GetDiskFreeSpace( + szDrive, + &SectorsPerCluster, + &BytesPerSector, + &NumberOfFreeClusters, + &TotalNumberOfClusters)) + + return 0; + } + __int64 total = SectorsPerCluster * BytesPerSector * NumberOfFreeClusters; + return (DWORD)total; +} + +bool ZipPlatform::GetFileSize(LPCTSTR lpszFileName, DWORD& dSize) +{ + HANDLE f = CreateFile(lpszFileName, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL); + if (!f) + return false; + DWORD dwSize; + dwSize = ::GetFileSize(f, NULL); + CloseHandle(f); + if (dwSize == 0xFFFFFFFF) + return false; + dSize = dwSize; + return true; +} + +CZipString ZipPlatform::GetTmpFileName(LPCTSTR lpszPath, DWORD iSizeNeeded) +{ + TCHAR empty[] = _T(""); + CZipString tempPath; + bool bCheckTemp = true; + if (lpszPath) + { + tempPath = lpszPath; + bCheckTemp = GetDeviceFreeSpace(tempPath) < iSizeNeeded; + + } + if (bCheckTemp) + { + DWORD size = GetTempPath(0, NULL); + if (size == 0) + return empty; + + GetTempPath(size, tempPath.GetBuffer(size)); + tempPath.ReleaseBuffer(); + if (GetDeviceFreeSpace(tempPath) < iSizeNeeded) + { + if (!GetCurrentDirectory(tempPath) || GetDeviceFreeSpace(tempPath) < iSizeNeeded) + return empty; + } + } + CZipString tempName; + if (!GetTempFileName(tempPath, _T("ZAR"), 0, tempName.GetBuffer(_MAX_PATH))) + return empty; + tempName.ReleaseBuffer(); + return tempName; +} + + +bool ZipPlatform::GetCurrentDirectory(CZipString& sz) +{ + DWORD i = ::GetCurrentDirectory(0, NULL); + if (!i) + return false; + TCHAR* pBuf = new TCHAR[i]; + bool b = true; + if (!::GetCurrentDirectory(i, pBuf)) + b = false; + else + sz = pBuf; + delete[] pBuf; + return b; +} + +bool ZipPlatform::SetFileAttr(LPCTSTR lpFileName, DWORD uAttr) +{ + return ::SetFileAttributes(lpFileName, uAttr) != 0; +} + +bool ZipPlatform::GetFileAttr(LPCTSTR lpFileName, DWORD& uAttr) +{ + // not using MFC due to MFC bug (attr is one byte there) + DWORD temp = ::GetFileAttributes(lpFileName); + if (temp == ( DWORD )-1) + return false; + uAttr = temp; + return true; + +} + +bool ZipPlatform::GetFileModTime(LPCTSTR lpFileName, time_t & ttime) +{ +#if defined _MSC_VER && !defined __BORLANDC__ /*_MSC_VER may be defined in Borland after converting the VC project */ + struct _stat st; + if (_tstat(lpFileName, &st) != 0) +#else + struct stat st; + if (stat(lpFileName, &st) != 0) +#endif + return false; + + ttime = st.st_mtime; + return ttime != -1; +} + +bool ZipPlatform::SetFileModTime(LPCTSTR lpFileName, time_t ttime) +{ + struct _utimbuf ub; + ub.actime = time(NULL); + ub.modtime = ttime == -1 ? time(NULL) : ttime; // if wrong file time, set it to the current + return _tutime(lpFileName, &ub) == 0; +} + + +bool ZipPlatform::ChangeDirectory(LPCTSTR lpDirectory) +{ + return _tchdir(lpDirectory) == 0; // returns 0 if ok +} +int ZipPlatform::FileExists(LPCTSTR lpszName) +{ + if (_taccess(lpszName, 0) == 0) + { + if (DirectoryExists(lpszName)) + return -1; + return 1; + } + else + return 0; + +} + +ZIPINLINE bool ZipPlatform::IsDriveRemovable(LPCTSTR lpszFilePath) +{ + CZipPathComponent zpc(lpszFilePath); + return ::GetDriveType(zpc.GetFileDrive()) == DRIVE_REMOVABLE; +} + +ZIPINLINE bool ZipPlatform::SetVolLabel(LPCTSTR lpszPath, LPCTSTR lpszLabel) +{ + CZipPathComponent zpc(lpszPath); + CZipString szDrive = zpc.GetFileDrive(); + CZipPathComponent::AppendSeparator(szDrive); + return ::SetVolumeLabel(szDrive, lpszLabel) != 0; +} + +ZIPINLINE void ZipPlatform::AnsiOem(CZipAutoBuffer& buffer, bool bAnsiToOem) +{ + if (bAnsiToOem) + CharToOemBuffA(buffer, buffer, buffer.GetSize()); + else + OemToCharBuffA(buffer, buffer, buffer.GetSize()); +} + +ZIPINLINE bool ZipPlatform::RemoveFile(LPCTSTR lpszFileName, bool bThrow) +{ + if (!::DeleteFile((LPTSTR)lpszFileName)) + if (bThrow) + CZipException::Throw(CZipException::notRemoved, lpszFileName); + else + return false; + return true; + +} +ZIPINLINE bool ZipPlatform::RenameFile( LPCTSTR lpszOldName, LPCTSTR lpszNewName, bool bThrow) +{ + if (!::MoveFile((LPTSTR)lpszOldName, (LPTSTR)lpszNewName)) + if (bThrow) + CZipException::Throw(CZipException::notRenamed, lpszOldName); + else + return false; + return true; + +} +ZIPINLINE bool ZipPlatform::IsDirectory(DWORD uAttr) +{ + return (uAttr & FILE_ATTRIBUTE_DIRECTORY) != 0; +} +ZIPINLINE bool ZipPlatform::CreateDirectory(LPCTSTR lpDirectory) +{ + return ::CreateDirectory(lpDirectory, NULL) != 0; +} + +ZIPINLINE DWORD ZipPlatform::GetDefaultAttributes() +{ + return 0x81a40020; // make it readable under Unix +} + +ZIPINLINE DWORD ZipPlatform::GetDefaultDirAttributes() +{ + return 0x41ff0010; // make it readable under Unix +} + +ZIPINLINE int ZipPlatform::GetSystemID() +{ + return ZipCompatibility::zcDosFat; +} + +ZIPINLINE bool ZipPlatform::GetSystemCaseSensitivity() +{ + return false; +} + +#ifdef _UNICODE +int ZipPlatform::WideToSingle(LPCTSTR lpWide, CZipAutoBuffer &szSingle) +{ + size_t wideLen = wcslen(lpWide); + if (wideLen == 0) + { + szSingle.Release(); + return 0; + } + + // iLen does not include terminating character + int iLen = WideCharToMultiByte(CP_ACP,0, lpWide, wideLen, szSingle, + 0, NULL, NULL); + if (iLen > 0) + { + szSingle.Allocate(iLen, true); + iLen = WideCharToMultiByte(CP_ACP,0, lpWide , wideLen, szSingle, + iLen, NULL, NULL); + ASSERT(iLen != 0); + } + else // here it means error + { + szSingle.Release(); + iLen --; + } + return iLen; + +} +int ZipPlatform::SingleToWide(const CZipAutoBuffer &szSingle, CZipString& szWide) +{ + int singleLen = szSingle.GetSize(); + // iLen doesn't include terminating character + int iLen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szSingle.GetBuffer(), singleLen, NULL, 0); + if (iLen > 0) + { + iLen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szSingle.GetBuffer(), singleLen, + szWide.GetBuffer(iLen) , iLen); + szWide.ReleaseBuffer(iLen); + ASSERT(iLen != 0); + } + else + { + szWide.Empty(); + iLen --; // return -1 + } + return iLen; + +} +#endif + +#ifndef _MFC_VER +#include +#include +bool ZipPlatform::TruncateFile(int iDes, DWORD iSize) +{ + int ret = chsize(iDes, iSize); + return ret != -1; + +} + +int ZipPlatform::OpenFile(LPCTSTR lpszFileName, UINT iMode, int iShareMode) +{ + switch (iShareMode) + { + case (CZipFile::shareDenyWrite & CZipFile::shareDenyRead): + iShareMode = SH_DENYRW; + break; + case (CZipFile::shareDenyRead): + iShareMode = SH_DENYRD; + break; + case (CZipFile::shareDenyWrite): + iShareMode = SH_DENYWR; + break; + default: + iShareMode = SH_DENYNO; + } + return _tsopen(lpszFileName, iMode, iShareMode, S_IREAD | S_IWRITE /*required only when O_CREAT mode*/); +} + +bool ZipPlatform::FlushFile(int iDes) +{ + return _commit(iDes) == 0; +} + +int ZipPlatform::GetFileSystemHandle(int iDes) +{ + return _get_osfhandle(iDes); +} + + +#endif //_MFC_VER diff --git a/harbour/contrib/hbzlib/zipplatformcomm.cpp b/harbour/contrib/hbzlib/zipplatformcomm.cpp new file mode 100644 index 0000000000..2f0624735b --- /dev/null +++ b/harbour/contrib/hbzlib/zipplatformcomm.cpp @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipPlatformComm.cpp $ +// $Archive: /ZipArchive/ZipPlatformComm.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#include "zipplatform.h" + +using namespace ZipPlatform; + +bool ZipPlatform::DirectoryExists(LPCTSTR lpszDir) +{ + CZipString sz; + if (!GetCurrentDirectory(sz)) + return false; + if (!ChangeDirectory(lpszDir)) + return false; + ChangeDirectory(sz); + return true; +} + +bool ZipPlatform::ForceDirectory(LPCTSTR lpDirectory) +{ + ASSERT(lpDirectory); + CZipString szDirectory = lpDirectory; + szDirectory.TrimRight(CZipPathComponent::m_cSeparator); + CZipPathComponent zpc(szDirectory); + if ((zpc.GetFilePath() == szDirectory) || + (FileExists(szDirectory) == -1)) + return true; + if (!ForceDirectory(zpc.GetFilePath())) + return false; + if (!CreateDirectory(szDirectory)) + return false; + return true; +} diff --git a/harbour/contrib/hbzlib/zipstorage.cpp b/harbour/contrib/hbzlib/zipstorage.cpp new file mode 100644 index 0000000000..8cc094b613 --- /dev/null +++ b/harbour/contrib/hbzlib/zipstorage.cpp @@ -0,0 +1,461 @@ +//////////////////////////////////////////////////////////////////////////////// +// $Workfile: ZipStorage.cpp $ +// $Archive: /ZipArchive/ZipStorage.cpp $ +// $Date$ $Author$ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/) +// +// 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 see the file License.txt +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "zipstorage.h" +#include "ziparchive.h" +// #include "ZipPathComponent.h" +#include "zipplatform.h" + +////////////////////////////////////////////////////////////////////// +// disk spanning objectives: +// - sinature at the first disk at the beginning +// - headers and central dir records not divided between disks +// - each file has a data descriptor preceded by the signature +// (bit 3 set in flag); + +int CZipActionCallback::m_iStep = 256; + +char CZipStorage::m_gszExtHeaderSignat[] = {0x50, 0x4b, 0x07, 0x08}; +CZipStorage::CZipStorage() +{ + m_pChangeDiskFunc = NULL; + m_iWriteBufferSize = 65536; + m_iCurrentDisk = -1; + m_pFile = NULL; +} + +CZipStorage::~CZipStorage() +{ + +} + +DWORD CZipStorage::Read(void *pBuf, DWORD iSize, bool bAtOnce) +{ + if (iSize == 0) + return 0; + DWORD iRead = 0; + while (!iRead) + { + iRead = m_pFile->Read(pBuf, iSize); + if (!iRead) + if (IsSpanMode()) + ChangeDisk(m_iCurrentDisk + 1); + else + ThrowError(CZipException::badZipFile); + } + + if (iRead == iSize) + return iRead; + else if (bAtOnce || !IsSpanMode()) + ThrowError(CZipException::badZipFile); + + while (iRead < iSize) + { + ChangeDisk(m_iCurrentDisk + 1); + UINT iNewRead = m_pFile->Read((char*)pBuf + iRead, iSize - iRead); + if (!iNewRead && iRead < iSize) + ThrowError(CZipException::badZipFile); + iRead += iNewRead; + } + + return iRead; +} + +void CZipStorage::Open(LPCTSTR szPathName, int iMode, int iVolumeSize) +{ + m_pWriteBuffer.Allocate(m_iWriteBufferSize); + m_uBytesInWriteBuffer = 0; + m_bNewSpan = false; + m_pFile = &m_internalfile; + m_bInMemory = false; + + if ((iMode == CZipArchive::zipCreate) ||(iMode == CZipArchive::zipCreateSpan)) // create new archive + { + m_bReadOnly = false; + m_iCurrentDisk = 0; + if (iMode == CZipArchive::zipCreate) + { + m_iSpanMode = noSpan; + OpenFile(szPathName, CZipFile::modeCreate | CZipFile::modeReadWrite); + } + else // create disk spanning archive + { + m_bNewSpan = true; + m_iBytesWritten = 0; + if (iVolumeSize <= 0) // pkzip span + { + if (!m_pChangeDiskFunc) + ThrowError(CZipException::noCallback); + if (!ZipPlatform::IsDriveRemovable(szPathName)) + ThrowError(CZipException::nonRemovable); + m_iSpanMode = pkzipSpan; + } + else + { + m_iTdSpanData = iVolumeSize; + m_iSpanMode = tdSpan; + } + + NextDisk(4, szPathName); + Write(m_gszExtHeaderSignat, 4, true); + } + } + else // open existing + { + m_bReadOnly = iMode == CZipArchive::zipOpenReadOnly; + OpenFile(szPathName, CZipFile::modeNoTruncate | + (m_bReadOnly ? CZipFile::modeRead : CZipFile::modeReadWrite)); + // m_uData, i m_iSpanMode are automatically set during reading the central dir + m_iSpanMode = iVolumeSize == 0 ? suggestedAuto : suggestedTd; + } + +} + + +void CZipStorage::Open(CZipMemFile& mf, int iMode) +{ + m_pWriteBuffer.Allocate(m_iWriteBufferSize); + m_uBytesInWriteBuffer = 0; + m_bNewSpan = false; + m_pFile = &mf; + m_bInMemory = true; + + if (iMode == CZipArchive::zipCreate) + { + m_iCurrentDisk = 0; + m_iSpanMode = noSpan; + mf.SetLength(0); + } + else // open existing + { + mf.SeekToBegin(); + m_iSpanMode = suggestedAuto; + } +} + + +void CZipStorage::ChangeDisk(int iNumber) +{ + if (iNumber == m_iCurrentDisk) + return; + + ASSERT(m_iSpanMode != noSpan); + m_iCurrentDisk = iNumber; + OpenFile(m_iSpanMode == pkzipSpan ? ChangePkzipRead() : ChangeTdRead(), + CZipFile::modeNoTruncate | CZipFile::modeRead); +} + +void CZipStorage::ThrowError(int err) +{ + CZipException::Throw(err, m_pFile->GetFilePath()); +} + +bool CZipStorage::OpenFile(LPCTSTR lpszName, UINT uFlags, bool bThrow) +{ + return m_pFile->Open(lpszName, uFlags | CZipFile::shareDenyWrite, bThrow); +} + + +CZipString CZipStorage::ChangePkzipRead() +{ + CZipString szTemp = m_pFile->GetFilePath(); + m_pFile->Close(); + CallCallback(-1 , szTemp); + return szTemp; +} + +CZipString CZipStorage::ChangeTdRead() +{ + CZipString szTemp = GetTdVolumeName(m_iCurrentDisk == m_iTdSpanData); + m_pFile->Close(); + return szTemp; +} + +CZipString CZipStorage::RenameLastFileInTDSpan() +{ + ASSERT(m_iSpanMode == tdSpan); + // give to the last volume the zip extension + CZipString szFileName = m_pFile->GetFilePath(); + CZipString szNewFileName = GetTdVolumeName(true); + if (!m_bInMemory) + { + m_pFile->Flush(); + m_pFile->Close(); + } + if (ZipPlatform::FileExists(szNewFileName)) + ZipPlatform::RemoveFile(szNewFileName); + ZipPlatform::RenameFile(szFileName, szNewFileName); + return szNewFileName; +} + +CZipString CZipStorage::Close(bool bAfterException) +{ + bool bClose = true; + CZipString sz; + if (!bAfterException) + { + Flush(); + if ((m_iSpanMode == tdSpan) && (m_bNewSpan)) + { + sz = RenameLastFileInTDSpan(); + bClose = false;// already closed in RenameLastFileInTDSpan + } + } + if (sz.IsEmpty()) + sz = m_pFile->GetFilePath(); + if (bClose && !m_bInMemory) + { + FlushFile(); + m_pFile->Close(); + } + + + + m_pWriteBuffer.Release(); + m_iCurrentDisk = -1; + m_iSpanMode = noSpan; + m_pFile = NULL; + return sz; +} + +CZipString CZipStorage::GetTdVolumeName(bool bLast, LPCTSTR lpszZipName) const +{ + CZipString szFilePath = lpszZipName ? lpszZipName : (LPCTSTR)m_pFile->GetFilePath(); + CZipPathComponent zpc(szFilePath); + CZipString szExt; + if (bLast) + szExt = m_szSpanExtension; + else + szExt.Format(_T("%.3d"), m_iCurrentDisk); + zpc.SetExtension(szExt); + return zpc.GetFullPath(); +} + +void CZipStorage::NextDisk(int iNeeded, LPCTSTR lpszFileName) +{ + Flush(); + ASSERT(m_iSpanMode != noSpan); + if (m_iBytesWritten) + { + m_iBytesWritten = 0; + m_iCurrentDisk++; + if (m_iCurrentDisk >= 999) + ThrowError(CZipException::tooManyVolumes); + } + CZipString szFileName; + bool bPkSpan = (m_iSpanMode == pkzipSpan); + if (bPkSpan) + szFileName = lpszFileName ? lpszFileName : (LPCTSTR)m_pFile->GetFilePath(); + else + szFileName = GetTdVolumeName(false, lpszFileName); + + if (!m_pFile->IsClosed()) + { + m_pFile->Flush(); + m_pFile->Close(); + } + + if (bPkSpan) + { + int iCode = iNeeded; + while (true) + { + CallCallback(iCode, szFileName); + if (ZipPlatform::FileExists(szFileName)) + iCode = -2; + else + { + CZipString label; + label.Format(_T("pkback# %.3d"), m_iCurrentDisk + 1); + if (!ZipPlatform::SetVolLabel(szFileName, label)) + iCode = -3; + else if (!OpenFile(szFileName, CZipFile::modeCreate | CZipFile::modeReadWrite, false)) + iCode = -4; + else + break; + } + + } + m_uCurrentVolSize = GetFreeVolumeSpace(); + } + else + { + m_uCurrentVolSize = m_iTdSpanData; + OpenFile(szFileName, CZipFile::modeCreate | CZipFile::modeReadWrite); + } +} + +void CZipStorage::CallCallback(int iCode, CZipString szTemp) +{ + ASSERT(m_pChangeDiskFunc); + m_pChangeDiskFunc->m_szExternalFile = szTemp; + m_pChangeDiskFunc->m_uDiskNeeded = m_iCurrentDisk + 1; + if (!m_pChangeDiskFunc->Callback(iCode)) + CZipException::Throw(CZipException::aborted, szTemp); +} + +DWORD CZipStorage::GetFreeVolumeSpace() const +{ + ASSERT (m_iSpanMode == pkzipSpan); + CZipString szTemp = m_pFile->GetFilePath(); + if (szTemp.IsEmpty()) // called once when creating a disk spanning archive + return 0; + else + { + CZipPathComponent zpc(szTemp); + return ZipPlatform::GetDeviceFreeSpace(zpc.GetFilePath()); + } +} + + +void CZipStorage::UpdateSpanMode(WORD uLastDisk) +{ + m_iCurrentDisk = uLastDisk; + if (uLastDisk) + { + // disk spanning detected + CZipString szFilePath = m_pFile->GetFilePath(); + if (m_iSpanMode == suggestedAuto) + m_iSpanMode = ZipPlatform::IsDriveRemovable(szFilePath) ? + pkzipSpan : tdSpan; + else + { + ASSERT(m_iSpanMode == suggestedTd); + m_iSpanMode = tdSpan; + } + + if (m_iSpanMode == pkzipSpan) + { + if (!m_pChangeDiskFunc) + ThrowError(CZipException::noCallback); + } + else /*if (m_iSpanMode == tdSpan)*/ + m_iTdSpanData = uLastDisk; // disk with .zip extension ( the last one) + CZipPathComponent zpc(szFilePath); + m_szSpanExtension = zpc.GetFileExt(); + m_pWriteBuffer.Release(); // no need for this in this case + } + else + m_iSpanMode = noSpan; + +} + +void CZipStorage::Write(const void *pBuf, DWORD iSize, bool bAtOnce) +{ + if (!IsSpanMode()) + WriteInternalBuffer((char*)pBuf, iSize); + else + { + // if not at once, one byte is enough free space + DWORD iNeeded = bAtOnce ? iSize : 1; + DWORD uTotal = 0; + + while (uTotal < iSize) + { + DWORD uFree; + while ((uFree = VolumeLeft()) < iNeeded) + { + if ((m_iSpanMode == tdSpan) && !m_iBytesWritten && !m_uBytesInWriteBuffer) + // in the tdSpan mode, if the size of the archive is less + // than the size of the packet to be written at once, + // increase once the size of the volume + m_uCurrentVolSize = iNeeded; + else + NextDisk(iNeeded); + } + + DWORD uLeftToWrite = iSize - uTotal; + DWORD uToWrite = uFree < uLeftToWrite ? uFree : uLeftToWrite; + WriteInternalBuffer((char*)pBuf + uTotal, uToWrite); + if (bAtOnce) + return; + else + uTotal += uToWrite; + } + + } +} + + +void CZipStorage::WriteInternalBuffer(const char *pBuf, DWORD uSize) +{ + DWORD uWritten = 0; + while (uWritten < uSize) + { + DWORD uFreeInBuffer = GetFreeInBuffer(); + if (uFreeInBuffer == 0) + { + Flush(); + uFreeInBuffer = m_pWriteBuffer.GetSize(); + } + DWORD uLeftToWrite = uSize - uWritten; + DWORD uToCopy = uLeftToWrite < uFreeInBuffer ? uLeftToWrite : uFreeInBuffer; + memcpy(m_pWriteBuffer + m_uBytesInWriteBuffer, pBuf + uWritten, uToCopy); + uWritten += uToCopy; + m_uBytesInWriteBuffer += uToCopy; + } +} + +DWORD CZipStorage::VolumeLeft() const +{ + // for pkzip span m_uCurrentVolSize is updated after each flush() + return m_uCurrentVolSize - m_uBytesInWriteBuffer - ((m_iSpanMode == pkzipSpan) ? 0 : m_iBytesWritten); +} + +void CZipStorage::Flush() +{ + if (m_iSpanMode != noSpan) + m_iBytesWritten += m_uBytesInWriteBuffer; + if (m_uBytesInWriteBuffer) + { + m_pFile->Write(m_pWriteBuffer, m_uBytesInWriteBuffer); + m_uBytesInWriteBuffer = 0; + } + if (m_iSpanMode == pkzipSpan) + // after writing it is difficult to predict the free space due to + // not completly written clusters, write operation may start from + // the new cluster + m_uCurrentVolSize = GetFreeVolumeSpace(); + +} + + + +void CZipStorage::FinalizeSpan() +{ + ASSERT(IsSpanMode() == 1); // span in creation + ASSERT(!m_bInMemory); + + CZipString szFileName; + if ((m_iSpanMode == tdSpan) && (m_bNewSpan)) + szFileName = RenameLastFileInTDSpan(); + else + { + szFileName = m_pFile->GetFilePath(); + // the file is already closed + m_pFile->Close(); + } + m_bNewSpan = false; + if (m_iCurrentDisk == 0) // one-disk span was converted to normal archive + m_iSpanMode = noSpan; + else + m_iTdSpanData = m_iCurrentDisk; + + OpenFile(szFileName, CZipFile::modeNoTruncate | (m_iSpanMode == noSpan ? CZipFile::modeReadWrite : CZipFile::modeRead)); + +} +