From 16f738ecee689c6feb2acb7e4ef4d9bb4144ae7d Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 8 Jun 2008 11:04:43 +0200 Subject: Initial commit --- zip/ZipArchive/ZipFileHeader.cpp | 792 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 792 insertions(+) create mode 100644 zip/ZipArchive/ZipFileHeader.cpp (limited to 'zip/ZipArchive/ZipFileHeader.cpp') diff --git a/zip/ZipArchive/ZipFileHeader.cpp b/zip/ZipArchive/ZipFileHeader.cpp new file mode 100644 index 0000000..e9bb080 --- /dev/null +++ b/zip/ZipArchive/ZipFileHeader.cpp @@ -0,0 +1,792 @@ +//////////////////////////////////////////////////////////////////////////////// +// This source file is part of the ZipArchive library source distribution and +// is Copyrighted 2000 - 2007 by Artpol Software - Tadeusz Dracz +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// For the licensing details refer to the License.txt file. +// +// Web Site: http://www.artpol-software.com +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ZipFileHeader.h" +#include "ZipAutoBuffer.h" +#include "ZipArchive.h" +#include "ZipPlatform.h" +#include "ZipCompatibility.h" +#include + +#include "ZipCrc32Cryptograph.h" +#include "BytesWriter.h" + +#define FILEHEADERSIZE 46 +#define LOCALFILEHEADERSIZE 30 +#define ZIP_EXTRA_ZARCH_NAME_VER 1 + +using namespace ZipArchiveLib; + +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 = CZipCompressor::methodDeflate; + m_uVersionMadeBy = 0; + m_uCrc32 = 0; + // initialize to 0, because on 64 bit platform unsigned long is 8 byte and we are copying only 4 bytes in Read() + m_uComprSize = m_uUncomprSize = m_uOffset = 0; + m_uLocalFileNameSize = 0; + m_uLocalComprSize = m_uLocalUncomprSize = 0; + m_uVolumeStart = 0; + m_pszFileName = NULL; + m_uEncryptionMethod = CZipCryptograph::encNone; + m_bIgnoreCrc32 = false; + m_uFlag = 0; +} + +CZipFileHeader::~CZipFileHeader() +{ + if (m_pszFileName != NULL) + delete m_pszFileName; +} + +bool CZipFileHeader::Read(CZipCentralDir& centralDir, bool bReadSignature) +{ + CZipStorage *pStorage = centralDir.m_pStorage; + WORD uFileNameSize, uCommentSize, uExtraFieldSize; + CZipAutoBuffer buf(FILEHEADERSIZE); + if (bReadSignature) + { + pStorage->Read(buf, FILEHEADERSIZE, true); + if (!VerifySignature(buf)) + return false; + } + else + pStorage->Read((char*)buf + 4, FILEHEADERSIZE - 4, true); + + CBytesWriter::ReadBytes(m_uVersionMadeBy, buf + 4); + CBytesWriter::ReadBytes(m_uVersionNeeded, buf + 6); + CBytesWriter::ReadBytes(m_uFlag, buf + 8); + CBytesWriter::ReadBytes(m_uMethod, buf + 10); + CBytesWriter::ReadBytes(m_uModTime, buf + 12); + CBytesWriter::ReadBytes(m_uModDate, buf + 14); + CBytesWriter::ReadBytes(m_uCrc32, buf + 16); + CBytesWriter::ReadBytes(m_uComprSize, buf + 20, 4); + CBytesWriter::ReadBytes(m_uUncomprSize, buf + 24, 4); + CBytesWriter::ReadBytes(uFileNameSize, buf + 28); + CBytesWriter::ReadBytes(uExtraFieldSize, buf + 30); + CBytesWriter::ReadBytes(uCommentSize, buf + 32); + CBytesWriter::ReadBytes(m_uVolumeStart, buf + 34, 2); + CBytesWriter::ReadBytes(m_uInternalAttr, buf + 36); + CBytesWriter::ReadBytes(m_uExternalAttr, buf + 38); + CBytesWriter::ReadBytes(m_uOffset, buf + 42, 4); + buf.Release(); + + // we may need to modify this later + m_uEncryptionMethod = (BYTE)((m_uFlag & (WORD) 1) != 0 ? CZipCryptograph::encStandard : CZipCryptograph::encNone); + + ZIP_VOLUME_TYPE uCurDsk = pStorage->GetCurrentVolume(); + m_pszFileNameBuffer.Allocate(uFileNameSize); // don't add NULL at the end + pStorage->Read(m_pszFileNameBuffer, uFileNameSize, true); + + if (centralDir.m_pStringSettings->IsStandardNameCodePage()) + m_stringSettings.SetDefaultNameCodePage(GetSystemCompatibility()); + else + m_stringSettings.m_uNameCodePage = centralDir.m_pStringSettings->m_uNameCodePage; + + if (!centralDir.m_pStringSettings->IsStandardCommentCodePage()) + m_stringSettings.m_uCommentCodePage = centralDir.m_pStringSettings->m_uCommentCodePage; + + if (!m_aCentralExtraData.Read(pStorage, uExtraFieldSize)) + return false; + + CZipExtraData* pExtra = m_aCentralExtraData.Lookup(ZIP_EXTRA_ZARCH_NAME); + if (pExtra != NULL) + { + WORD uExtraDataSize = (WORD)pExtra->m_data.GetSize(); + int offset = 1; + if (offset > uExtraDataSize) + return false; + if (pExtra->m_data[0] <= ZIP_EXTRA_ZARCH_NAME_VER) // else don't parse it + { + offset++; + if (offset > uExtraDataSize) + return false; + BYTE flag = pExtra->m_data[1]; + bool bReadCommentCp = (flag & 4) != 0; + if ((flag & 1) != 0) + { + // code page present + if (offset + 4 > uExtraDataSize) + return false; + // avoid warnings + DWORD temp; + CBytesWriter::ReadBytes(temp, pExtra->m_data + offset); + m_stringSettings.m_uNameCodePage = temp; + offset += 4; + } + + if ((flag & 3) == 3) + { + m_stringSettings.m_bStoreNameInExtraData = true; + int iFileNameSize = pExtra->m_data.GetSize() - 2 - 4; + if (bReadCommentCp) + iFileNameSize -= 4; + // code page present + if (offset + iFileNameSize > uExtraDataSize || iFileNameSize <= 0) + return false; + CZipAutoBuffer buffer; + buffer.Allocate(iFileNameSize); + memcpy(buffer, pExtra->m_data + offset, iFileNameSize); + m_pszFileName = new CZipString(_T("")); + ZipCompatibility::ConvertBufferToString(*m_pszFileName, buffer, m_stringSettings.m_uNameCodePage); + offset += iFileNameSize; + m_pszFileNameBuffer.Release(); + } + else + m_stringSettings.m_bStoreNameInExtraData = false; + + if (bReadCommentCp) + { + // code page present + if (offset + 4 > uExtraDataSize) + return false; + DWORD temp; + CBytesWriter::ReadBytes(temp, pExtra->m_data + offset); + m_stringSettings.m_uCommentCodePage = temp; + // offset += 4; + } + } + } + + + if (uCommentSize) + { + m_pszComment.Allocate(uCommentSize); + pStorage->Read(m_pszComment, uCommentSize, true); + } + + return pStorage->GetCurrentVolume() == uCurDsk; // check that the whole header is in one volume +} + + +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) +{ + m_aCentralExtraData.RemoveInternalHeaders(); + + WORD uMethod = m_uMethod; + + if (!CheckLengths(false)) + CZipException::Throw(CZipException::tooLongData); + + PrepareFileName(); + + + if (m_stringSettings.m_bStoreNameInExtraData) + { + if (m_pszFileName == NULL && m_pszFileNameBuffer.IsAllocated()) + GetFileName(false); // don't clear the buffer, it will be needed in a moment + ASSERT(m_pszFileName != NULL); + if (m_pszFileName->GetLength() == 0) + m_stringSettings.m_bStoreNameInExtraData = false; + } + + int iSystemCompatibility = GetSystemCompatibility(); + if (!m_stringSettings.IsStandard(iSystemCompatibility)) + { + CZipExtraData* pExtra = m_aCentralExtraData.CreateNew(ZIP_EXTRA_ZARCH_NAME); + bool bWriteCommentCp = !m_stringSettings.IsStandardCommentCodePage(); + + BYTE flag = 0; + int offset = 2; + char* data = NULL; + if (m_stringSettings.m_bStoreNameInExtraData) + { + CZipAutoBuffer buffer; + // m_pszFileNameBuffer contains CP_ACP page, we don't check if the current page is CP_ACP - too large dependency on PrepareFileName + ZipCompatibility::ConvertStringToBuffer(*m_pszFileName, buffer, m_stringSettings.m_uNameCodePage); + int size = 2 + 4 + buffer.GetSize(); + if (bWriteCommentCp) + size += 4; + pExtra->m_data.Allocate(size); + data = (char*)pExtra->m_data; + CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uNameCodePage); + offset += 4; + memcpy(data + offset, buffer, buffer.GetSize()); + offset += buffer.GetSize(); + flag = 3; + } + else if (!m_stringSettings.IsStandardNameCodePage(iSystemCompatibility)) + { + int size = 2 + 4; + if (bWriteCommentCp) + size += 4; + pExtra->m_data.Allocate(size); + data = (char*)pExtra->m_data; + CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uNameCodePage); + offset += 4; + flag = 1; + } + + if (bWriteCommentCp) + { + if (!pExtra->m_data.IsAllocated()) + { + pExtra->m_data.Allocate(2 + 4); + data = (char*)pExtra->m_data; + } + ASSERT(data); + CBytesWriter::WriteBytes(data + offset, (DWORD)m_stringSettings.m_uCommentCodePage); + flag |= 4; + } + + data[0] = ZIP_EXTRA_ZARCH_NAME_VER; + data[1] = flag; + } + + WORD uFileNameSize = (WORD)m_pszFileNameBuffer.GetSize(), uCommentSize = (WORD)GetCommentSize(), + uExtraFieldSize = (WORD)m_aCentralExtraData.GetTotalSize(); + DWORD uSize = FILEHEADERSIZE + uFileNameSize + uCommentSize + uExtraFieldSize; + CZipAutoBuffer buf(uSize); + char* dest = (char*)buf; + memcpy(dest, m_gszSignature, 4); + CBytesWriter::WriteBytes(dest + 4, m_uVersionMadeBy); + CBytesWriter::WriteBytes(dest + 6, m_uVersionNeeded); + CBytesWriter::WriteBytes(dest + 8, m_uFlag); + CBytesWriter::WriteBytes(dest + 10, uMethod); + CBytesWriter::WriteBytes(dest + 12, m_uModTime); + CBytesWriter::WriteBytes(dest + 14, m_uModDate); + WriteCrc32(dest + 16); + CBytesWriter::WriteBytes(dest + 20, CBytesWriter::WriteSafeU32(m_uComprSize)); + CBytesWriter::WriteBytes(dest + 24, CBytesWriter::WriteSafeU32(m_uUncomprSize)); + CBytesWriter::WriteBytes(dest + 28, uFileNameSize); + CBytesWriter::WriteBytes(dest + 30, uExtraFieldSize); + CBytesWriter::WriteBytes(dest + 32, uCommentSize); + CBytesWriter::WriteBytes(dest + 34, CBytesWriter::WriteSafeU16(m_uVolumeStart)); + CBytesWriter::WriteBytes(dest + 36, m_uInternalAttr); + CBytesWriter::WriteBytes(dest + 38, m_uExternalAttr); + CBytesWriter::WriteBytes(dest + 42, CBytesWriter::WriteSafeU32(m_uOffset)); + + memcpy(dest + 46, m_pszFileNameBuffer, uFileNameSize); + + if (uExtraFieldSize) + m_aCentralExtraData.Write(dest + 46 + uFileNameSize); + + if (uCommentSize) + memcpy(dest + 46 + uFileNameSize + uExtraFieldSize, m_pszComment, uCommentSize); + + pStorage->Write(dest, uSize, true); + + // remove to avoid miscalculations in GetSize() + m_aCentralExtraData.RemoveInternalHeaders(); + ClearFileName(); + return uSize; +} + +bool CZipFileHeader::ReadLocal(CZipCentralDir& centralDir) +{ + char buf[LOCALFILEHEADERSIZE]; + CZipStorage* pStorage = centralDir.m_pStorage; + pStorage->Read(buf, LOCALFILEHEADERSIZE, true); + if (memcmp(buf, m_gszLocalSignature, 4) != 0) + return false; + + bool bIsDataDescr = (((WORD)*(buf + 6)) & 8) != 0; + + WORD uTemp; + CBytesWriter::ReadBytes(uTemp, buf + 6); + // do not compare the whole flag - the bits reserved by PKWARE may differ + // in local and central headers + if (centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalFlag) + && (uTemp & 0xf) != (m_uFlag & 0xf)) + return false; + + // method + WORD uMethod; + CBytesWriter::ReadBytes(uMethod, buf + 8); + + // this may be different in the local header (it may contain disk name for example) + CBytesWriter::ReadBytes(m_uLocalFileNameSize, buf + 26); + WORD uExtraFieldSize; + CBytesWriter::ReadBytes(uExtraFieldSize, buf + 28); + ZIP_VOLUME_TYPE uCurDsk = pStorage->GetCurrentVolume(); + // skip reading the local file name + pStorage->m_pFile->Seek(m_uLocalFileNameSize, CZipAbstractFile::current); + if (!m_aLocalExtraData.Read(pStorage, uExtraFieldSize)) + return false; + + CBytesWriter::ReadBytes(m_uLocalComprSize, buf + 18, 4); + CBytesWriter::ReadBytes(m_uLocalUncomprSize, buf + 22, 4); + if (uMethod == CZipCompressor::methodWinZipAes && IsEncrypted()) + CZipException::Throw(CZipException::noAES); + + if (centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalMethod) + && uMethod != m_uMethod ) + return false; + + if (!bIsDataDescr && centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalCRC | CZipArchive::checkLocalSizes)) + { + // read all at once - probably overally faster than checking and reading separately + DWORD uCrc32; + CBytesWriter::ReadBytes(uCrc32, buf + 14); + if (centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalCRC) + && uCrc32 != m_uCrc32) + return false; + + if (centralDir.IsConsistencyCheckOn( CZipArchive::checkLocalSizes) + // do not check, if local compressed size is 0 - this usually means, that some archiver + // could not update the compressed size after compression + && ( m_uLocalComprSize != 0 && m_uLocalComprSize != m_uComprSize || m_uLocalUncomprSize != m_uUncomprSize)) + return false; + } + return pStorage->GetCurrentVolume() == uCurDsk; // check that the whole header is in one volume +} + +void CZipFileHeader::SetTime(const time_t & ttime) +{ +#if _MSC_VER >= 1400 + tm gts; + tm* gt = >s; + localtime_s(gt, &ttime); +#else + tm* gt = localtime(&ttime); +#endif + WORD year, month, day, hour, min, sec; + if (gt == NULL) + { + year = 0; + month = day = 1; + hour = min = sec = 0; + } + else + { + year = (WORD)(gt->tm_year + 1900); + if (year <= 1980) + year = 0; + else + year -= 1980; + month = (WORD)gt->tm_mon + 1; + day = (WORD)gt->tm_mday; + hour = (WORD)gt->tm_hour; + min = (WORD)gt->tm_min; + sec = (WORD)gt->tm_sec; + } + + m_uModDate = (WORD) (day + ( month << 5) + (year << 9)); + m_uModTime = (WORD) ((sec >> 1) + (min << 5) + (hour << 11)); +} + +void CZipFileHeader::ConvertFileName(CZipAutoBuffer& buffer) const +{ + if (m_pszFileName == NULL) + return; + CZipString temp = *m_pszFileName; + ZipCompatibility::SlashBackslashChg(temp, false); + if (m_stringSettings.m_bStoreNameInExtraData) + ZipCompatibility::ConvertStringToBuffer(temp, buffer, m_stringSettings.GetDefaultNameCodePage(GetSystemCompatibility())); + else + ZipCompatibility::ConvertStringToBuffer(temp, buffer, m_stringSettings.m_uNameCodePage); +} + +void CZipFileHeader::ConvertFileName(CZipString& szFileName) const +{ + if (!m_pszFileNameBuffer.IsAllocated() || m_pszFileNameBuffer.GetSize() == 0) + return; + ZipCompatibility::ConvertBufferToString(szFileName, m_pszFileNameBuffer, m_stringSettings.m_uNameCodePage); + int sc = ZipPlatform::GetSystemID(); + if (sc == ZipCompatibility::zcDosFat || sc == ZipCompatibility::zcNtfs) + ZipCompatibility::SlashBackslashChg(szFileName, true); + else // some archives may have an invalid path separator stored + ZipCompatibility::SlashBackslashChg(szFileName, false); +} + +// write the local header +void CZipFileHeader::WriteLocal(CZipStorage *pStorage) +{ + if (IsDataDescriptor()) + { + m_uLocalComprSize = 0; + // write, if we know it - WinZip 9.0 in segmented mode with AES encryption will + // complain otherwise (this seems like a bug, because the data descriptor is present and + // local descriptor should be discarded) + if (!IsWinZipAesEncryption()) + m_uLocalUncomprSize = 0; + } + else + { + m_uLocalComprSize = GetDataSize(true, false); + } + + WORD uMethod = m_uMethod; + + PrepareFileName(); + m_uLocalFileNameSize = (WORD)m_pszFileNameBuffer.GetSize(); + DWORD uExtraFieldSize = m_aLocalExtraData.GetTotalSize(); + DWORD iLocalSize = LOCALFILEHEADERSIZE + uExtraFieldSize + m_uLocalFileNameSize; + CZipAutoBuffer buf(iLocalSize); + char* dest = (char*) buf; + memcpy(dest, m_gszLocalSignature, 4); + + CBytesWriter::WriteBytes(dest + 4, m_uVersionNeeded); + CBytesWriter::WriteBytes(dest + 6, m_uFlag); + CBytesWriter::WriteBytes(dest + 8, uMethod); + CBytesWriter::WriteBytes(dest + 10, m_uModTime); + CBytesWriter::WriteBytes(dest + 12, m_uModDate); + WriteSmallDataDescriptor(dest + 14); + CBytesWriter::WriteBytes(dest + 26, m_uLocalFileNameSize); + CBytesWriter::WriteBytes(dest + 28, (WORD)uExtraFieldSize); + memcpy(dest + 30, m_pszFileNameBuffer, m_uLocalFileNameSize); + + if (uExtraFieldSize) + m_aLocalExtraData.Write(dest + 30 + m_uLocalFileNameSize); + + // possible volume change before writing to the file in the segmented archive + // so write the local header first + pStorage->Write(dest, iLocalSize, true); + + m_uVolumeStart = pStorage->GetCurrentVolume(); + m_uOffset = pStorage->GetPosition() - iLocalSize; + ClearFileName(); +} + +WORD CZipFileHeader::GetDataDescriptorSize(bool bConsiderSignature) const +{ + if (IsDataDescriptor()) + { + + WORD size = 12; + return (WORD)(bConsiderSignature ? size + 4 : size); + } + else + return 0; +} + +bool CZipFileHeader::NeedsDataDescriptor() const +{ + return m_uEncryptionMethod == CZipCryptograph::encStandard; +} + +void CZipFileHeader::PrepareData(int iLevel, bool bSegm) +{ + // could be == 1, but the way below it works for PredictMaximumFileSizeInArchive when used on an existing segmented archive - for whatever reason + m_uInternalAttr = 0; + + // version made by + + SetVersion((WORD)(0x14)); + + + m_uCrc32 = 0; + m_uComprSize = 0; + m_uUncomprSize = 0; + + ASSERT(CZipCompressor::IsCompressionSupported(m_uMethod) && ((iLevel == 0) == (m_uMethod == CZipCompressor::methodStore))); + + m_uFlag = 0; + if (m_uMethod == CZipCompressor::methodDeflate) + switch (iLevel) + { + case 1: + m_uFlag |= 6; + break; + case 2: + m_uFlag |= 4; + break; + case 8: + case 9: + m_uFlag |= 2; + break; + } + + UpdateFlag(bSegm); + + m_uVersionNeeded = 0; + if (m_uVersionNeeded == 0) + m_uVersionNeeded = IsDirectory() ? 0xa : 0x14; // 1.0 or 2.0 +} + + +void CZipFileHeader::GetCrcAndSizes(char * pBuffer)const +{ + WriteCrc32(pBuffer); + CBytesWriter::WriteBytes(pBuffer + 4, m_uComprSize, 4); + CBytesWriter::WriteBytes(pBuffer + 8, m_uUncomprSize, 4); +} + +bool CZipFileHeader::CheckDataDescriptor(CZipStorage* pStorage) const +{ + if (!IsDataDescriptor()) + return true; + + const int sizeOfSize = 4; + + const int size = 4 + 2 * sizeOfSize; // crc and two sizes + + CZipAutoBuffer buf(size + 4); + pStorage->Read(buf, size, false); + char* pBuf; + + // when an archive is segmented, files that are divided between volume have bit 3 of flag set + // which tell about the presence of the data descriptor after the compressed data + // This signature may be in a segmented archive that is one volume only + // (it is detected as a not segmented archive) + if (memcmp(buf, CZipStorage::m_gszExtHeaderSignat, 4) == 0) // there is a signature + { + pStorage->Read((char*)buf + size, 4, false); + pBuf = (char*)buf + 4; + } + else + pBuf = buf; + + DWORD uCrc32 = 0; + ZIP_SIZE_TYPE uCompressed = 0, uUncompressed = 0; + + CBytesWriter::ReadBytes(uCrc32, pBuf); + CBytesWriter::ReadBytes(uCompressed, pBuf + 4, sizeOfSize); + CBytesWriter::ReadBytes(uUncompressed, pBuf + 4 + sizeOfSize, sizeOfSize); + return uCrc32 == m_uCrc32 && uCompressed == m_uComprSize && uUncompressed == m_uUncomprSize; +} + +DWORD CZipFileHeader::GetSize()const +{ + DWORD uSize = FILEHEADERSIZE + PredictFileNameSize() + GetCommentSize(); + uSize += m_aCentralExtraData.GetTotalSize(); + if (m_stringSettings.m_bStoreNameInExtraData) + { + CZipString temp; + if (m_pszFileName != NULL) + temp = *m_pszFileName; + else + ConvertFileName(temp); + if (temp.GetLength() > 0) + { + uSize += 4 + 2 + 4; // headerID, size + version, flag + filename code page + CZipAutoBuffer buffer; + ZipCompatibility::ConvertStringToBuffer(temp, buffer, m_stringSettings.m_uNameCodePage); + uSize += buffer.GetSize(); + if (!m_stringSettings.IsStandardCommentCodePage()) + uSize += 4; + } + } + return uSize; +} + +DWORD CZipFileHeader::GetLocalSize(bool bReal)const +{ + DWORD uSize = LOCALFILEHEADERSIZE + m_aLocalExtraData.GetTotalSize(); + if (bReal) + uSize += m_uLocalFileNameSize; + else + uSize += PredictFileNameSize(); + return uSize; +} + +void CZipFileHeader::SetComment(LPCTSTR lpszComment) +{ + ZipCompatibility::ConvertStringToBuffer(lpszComment, m_pszComment, m_stringSettings.m_uCommentCodePage); +} + +CZipString CZipFileHeader::GetComment() const +{ + CZipString temp; + ZipCompatibility::ConvertBufferToString(temp, m_pszComment, m_stringSettings.m_uCommentCodePage); + return temp; +} + +int CZipFileHeader::GetCompressionLevel() const +{ + if (m_uMethod == CZipCompressor::methodStore) + return CZipCompressor::levelStore; + else if ((m_uFlag & (WORD) 6) != 0) + return 1; + else if ((m_uFlag & (WORD) 4) != 0) + return 2; + else if ((m_uFlag & (WORD) 2) != 0) + return CZipCompressor::levelBest; + else + return CZipCompressor::levelDefault; +} + +void CZipFileHeader::SetFileName(LPCTSTR lpszFileName) +{ + if (m_pszFileName == NULL) + m_pszFileName = new CZipString(lpszFileName); + else + *m_pszFileName = lpszFileName; + m_pszFileNameBuffer.Release(); +} + +CZipString& CZipFileHeader::GetFileName(bool bClearBuffer) +{ + if (m_pszFileName != NULL) + return *m_pszFileName; + m_pszFileName = new CZipString(_T("")); + ConvertFileName(*m_pszFileName); + // don't keep it in memory + if (bClearBuffer) + m_pszFileNameBuffer.Release(); + return *m_pszFileName; +} + +bool CZipFileHeader::IsDirectory() +{ + return ZipPlatform::IsDirectory(GetSystemAttr()); +} + +DWORD CZipFileHeader::GetSystemAttr() +{ + int iSystemComp = GetSystemCompatibility(); + if (ZipCompatibility::IsPlatformSupported(iSystemComp)) + { + DWORD uAttr = iSystemComp == ZipCompatibility::zcUnix ? (m_uExternalAttr >> 16) : (m_uExternalAttr & 0xFFFF); + if (!uAttr && CZipPathComponent::HasEndingSeparator(GetFileName())) + return ZipPlatform::GetDefaultDirAttributes(); // can happen + else + { + uAttr = ZipCompatibility::ConvertToSystem(uAttr, iSystemComp, ZipPlatform::GetSystemID()); +#ifdef ZIP_ARCHIVE_LNX + // converting from Windows attributes may create a not readable linux directory + if (iSystemComp != ZipCompatibility::zcUnix && ZipPlatform::IsDirectory(uAttr)) + return ZipPlatform::GetDefaultDirAttributes(); +#endif + return uAttr; + } + } + else + return CZipPathComponent::HasEndingSeparator(GetFileName()) ? ZipPlatform::GetDefaultDirAttributes() : ZipPlatform::GetDefaultAttributes(); +} + +void CZipFileHeader::SetSystemAttr(DWORD uAttr) +{ + // make it readable under Unix as well, since it stores its attributes in HIWORD(uAttr) + int iSystemComp = GetSystemCompatibility(); + m_uExternalAttr = ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), iSystemComp); + if (iSystemComp == ZipCompatibility::zcUnix) + { + m_uExternalAttr <<= 16; + if (ZipPlatform::IsDirectory(uAttr)) + m_uExternalAttr |= 0x10; // make it recognizable under other systems (all use 0x10 for directory) + } + else + // make it readable under linux + m_uExternalAttr |= (ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), ZipCompatibility::zcUnix) << 16); +} + +CZipFileHeader& CZipFileHeader::operator=(const CZipFileHeader& header) +{ + m_uVersionMadeBy = header.m_uVersionMadeBy; + m_uVersionNeeded = header.m_uVersionNeeded; + m_uFlag = header.m_uFlag; + m_uMethod = header.m_uMethod; + m_uModTime = header.m_uModTime; + m_uModDate = header.m_uModDate; + m_uCrc32 = header.m_uCrc32; + m_uComprSize = header.m_uComprSize; + m_uUncomprSize = header.m_uUncomprSize; + m_uVolumeStart = header.m_uVolumeStart; + m_uInternalAttr = header.m_uInternalAttr; + m_uLocalComprSize = header.m_uLocalComprSize; + m_uLocalUncomprSize = header.m_uUncomprSize; + m_uExternalAttr = header.m_uExternalAttr; + m_uLocalFileNameSize = header.m_uLocalFileNameSize;; + m_uOffset = header.m_uOffset; + m_aLocalExtraData = header.m_aLocalExtraData; + m_aCentralExtraData = header.m_aCentralExtraData; + m_uEncryptionMethod = header.m_uEncryptionMethod; + if (m_pszFileName) + delete m_pszFileName; + + if (header.m_pszFileName) + m_pszFileName = new CZipString(*header.m_pszFileName); + else + m_pszFileName = NULL; + + m_pszFileNameBuffer = header.m_pszFileNameBuffer; + m_pszComment = header.m_pszComment; + m_stringSettings = header.m_stringSettings; + + return *this; +} + +void CZipFileHeader::WriteSmallDataDescriptor(char* pDest, bool bLocal) +{ + WriteCrc32(pDest); + if (bLocal) + { + CBytesWriter::WriteBytes(pDest + 4, m_uLocalComprSize, 4); + CBytesWriter::WriteBytes(pDest + 8, m_uLocalUncomprSize, 4); + } + else + { + CBytesWriter::WriteBytes(pDest + 4, m_uComprSize, 4); + CBytesWriter::WriteBytes(pDest + 8, m_uUncomprSize, 4); + } +} + + +void CZipFileHeader::WriteDataDescriptor(CZipStorage* pStorage) +{ + if (!IsDataDescriptor()) + return; + bool signature = NeedsSignatureInDataDescriptor(pStorage); + CZipAutoBuffer buf; + buf.Allocate(GetDataDescriptorSize(signature)); + char* pBuf; + if (signature) + { + memcpy(buf, CZipStorage::m_gszExtHeaderSignat, 4); + pBuf = (char*)buf + 4; + } + else + pBuf = buf; + WriteCrc32(pBuf); + CBytesWriter::WriteBytes(pBuf + 4, m_uComprSize, 4); + CBytesWriter::WriteBytes(pBuf + 8, m_uUncomprSize, 4); + pStorage->Write(buf, buf.GetSize(), true); +} + +void CZipFileHeader::UpdateLocalHeader(CZipStorage* pStorage) +{ + if (pStorage->IsSegmented() != 0 || IsDataDescriptor()) + // there is nothing to fix + return; + pStorage->Flush(); + ZIP_FILE_USIZE uPos = pStorage->m_pFile->GetPosition(); + // update crc and sizes, the sizes may already be all right, + // but 8 more bytes won't make a difference, we need to update crc32 anyway + CZipAutoBuffer buf(12); + m_uLocalComprSize = CBytesWriter::WriteSafeU32(m_uComprSize); + m_uLocalUncomprSize = CBytesWriter::WriteSafeU32(m_uUncomprSize); + WriteSmallDataDescriptor(buf); + pStorage->Seek(m_uOffset + 14); + pStorage->m_pFile->Write(buf, 12); + + pStorage->m_pFile->Seek(uPos); +} + +void CZipFileHeader::WriteCrc32(char* pBuf) const +{ + DWORD uCrc = m_bIgnoreCrc32 ? 0 : m_uCrc32; + CBytesWriter::WriteBytes(pBuf, uCrc); +} + -- cgit v1.2.3