//////////////////////////////////////////////////////////////////////////////// // This source file is part of the ZipArchive library source distribution and // is Copyrighted 2000 - 2006 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 #define TABLE_SIZE 64 BYTE KEY_TABLES[TABLE_SIZE] = { 0xcd, 0xd1, 0x1e, 0x9d, 0x9c, 0x16, 0x72, 0x72, 0xf6, 0x1d, 0xf0, 0x84, 0x4f, 0x4a, 0x77, 0x02, 0xd7, 0xf8, 0x39, 0x2c, 0x53, 0xcb, 0xc9, 0x12, 0x1e, 0x33, 0x74, 0x9e, 0x0c, 0xf4, 0xd5, 0xd4, 0x9f, 0xd4, 0xa4, 0x59, 0x7e, 0x35, 0xcf, 0x32, 0x22, 0xf4, 0xcc, 0xcf, 0xd3, 0x90, 0x2d, 0x48, 0xd3, 0x8f, 0x75, 0xf6, 0xd9, 0x1d, 0x2a, 0xf5, 0xc0, 0xf7, 0x2b, 0x78, 0x81, 0x87, 0x44, 0x0e }; BYTE MIX_TABLES[TABLE_SIZE] = { 54, 50, 59, 20, 25, 13, 19, 13, 48, 4, 58, 52, 38, 18, 19, 51, 11, 41, 54, 2, 58, 22, 59, 17, 45, 46, 36, 8, 57, 62, 18, 27, 37, 3, 37, 60, 52, 5, 19, 24, 40, 14, 11, 53, 5, 32, 25, 61, 23, 39, 10, 25, 54, 45, 16, 2, 9, 13, 40, 20, 9, 42, 57, 37 }; ////////////////////////////////////////////////////////////////////// // 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; m_uVersionMadeBy = 0; // initialize to 0, because on 64 bit platform unsigned long is 8 byte and we are copying only 4 bytes in Read() m_uCrc32 = m_uComprSize = m_uUncomprSize = m_uOffset = 0; m_uLocalExtraFieldSize = m_uLocalFileNameSize = 0; // SetSystemCompatibility(ZipPlatform::m_sSystemID); } void CZipFileHeader::Init() { m_uExternalAttr = 0;//ZipPlatform::GetDefaultAttributes(); m_uModDate = m_uModTime = 0; m_uMethod = Z_DEFLATED; m_uVersionMadeBy = 0; // initialize to 0, because on 64 bit platform unsigned long is 8 byte and we are copying only 4 bytes in Read() m_uCrc32 = m_uComprSize = m_uUncomprSize = m_uOffset = 0; m_uLocalExtraFieldSize = m_uLocalFileNameSize = 0; // 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); CZipArchive::ReadBytes(&m_uVersionMadeBy, buf + 4, 2); CZipArchive::ReadBytes(&m_uVersionNeeded, buf + 6, 2); CZipArchive::ReadBytes(&m_uFlag, buf + 8, 2); CZipArchive::ReadBytes(&m_uMethod, buf + 10, 2); CZipArchive::ReadBytes(&m_uModTime, buf + 12, 2); CZipArchive::ReadBytes(&m_uModDate, buf + 14, 2); CZipArchive::ReadBytes(&m_uCrc32, buf + 16, 4); CZipArchive::ReadBytes(&m_uComprSize, buf + 20, 4); CZipArchive::ReadBytes(&m_uUncomprSize, buf + 24, 4); CZipArchive::ReadBytes(&uFileNameSize, buf + 28, 2); CZipArchive::ReadBytes(&uExtraFieldSize, buf + 30, 2); CZipArchive::ReadBytes(&uCommentSize, buf + 32, 2); CZipArchive::ReadBytes(&m_uDiskStart, buf + 34, 2); CZipArchive::ReadBytes(&m_uInternalAttr, buf + 36, 2); CZipArchive::ReadBytes(&m_uExternalAttr, buf + 38, 4); CZipArchive::ReadBytes(&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 } bool CZipFileHeader::ReadEx(CZipStorage *pStorage) { // // just in case // m_pszComment.Release(); // m_pszFileName.Release(); WORD uFileNameSize, uCommentSize, uExtraFieldSize; CZipAutoBuffer buf(FILEHEADERSIZE); pStorage->Read(buf, FILEHEADERSIZE, true); // ÇØ´õ ¸¶½ºÅ· [4/3/2009 Jo_2]/////////////////////////////////////////// char *pBuf = buf; for( unsigned int cnt = 0; cnt < FILEHEADERSIZE; cnt++, pBuf++ ) { unsigned short keyIndex = MIX_TABLES[ cnt % TABLE_SIZE ]; *pBuf = *pBuf ^ KEY_TABLES[keyIndex]; } ///////////////////////////////////////////////////////////////////////// memcpy(&m_szSignature, buf, 4); CZipArchive::ReadBytes(&m_uVersionMadeBy, buf + 4, 2); CZipArchive::ReadBytes(&m_uVersionNeeded, buf + 6, 2); CZipArchive::ReadBytes(&m_uFlag, buf + 8, 2); CZipArchive::ReadBytes(&m_uMethod, buf + 10, 2); CZipArchive::ReadBytes(&m_uModTime, buf + 12, 2); CZipArchive::ReadBytes(&m_uModDate, buf + 14, 2); CZipArchive::ReadBytes(&m_uCrc32, buf + 16, 4); CZipArchive::ReadBytes(&m_uComprSize, buf + 20, 4); CZipArchive::ReadBytes(&m_uUncomprSize, buf + 24, 4); CZipArchive::ReadBytes(&uFileNameSize, buf + 28, 2); CZipArchive::ReadBytes(&uExtraFieldSize, buf + 30, 2); CZipArchive::ReadBytes(&uCommentSize, buf + 32, 2); CZipArchive::ReadBytes(&m_uDiskStart, buf + 34, 2); CZipArchive::ReadBytes(&m_uInternalAttr, buf + 36, 2); CZipArchive::ReadBytes(&m_uExternalAttr, buf + 38, 4); CZipArchive::ReadBytes(&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); // ÆÄÀÏ À̸§ ¸¶½ºÅ· [4/3/2009 Jo_2]/////////////////////////////////////// char *pFileName = m_pszFileName; for( unsigned int idx = 0; idx < uFileNameSize; idx++, pFileName++ ) { unsigned short keyIndex = MIX_TABLES[ idx % TABLE_SIZE ]; *pFileName = *pFileName ^ KEY_TABLES[keyIndex]; } ////////////////////////////////////////////////////////////////////////// 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); CZipArchive::WriteBytes(buf + 4, &m_uVersionMadeBy, 2); CZipArchive::WriteBytes(buf + 6, &m_uVersionNeeded, 2); CZipArchive::WriteBytes(buf + 8, &m_uFlag, 2); CZipArchive::WriteBytes(buf + 10, &m_uMethod, 2); CZipArchive::WriteBytes(buf + 12, &m_uModTime, 2); CZipArchive::WriteBytes(buf + 14, &m_uModDate, 2); CZipArchive::WriteBytes(buf + 16, &m_uCrc32, 4); CZipArchive::WriteBytes(buf + 20, &m_uComprSize, 4); CZipArchive::WriteBytes(buf + 24, &m_uUncomprSize, 4); CZipArchive::WriteBytes(buf + 28, &uFileNameSize, 2); CZipArchive::WriteBytes(buf + 30, &uExtraFieldSize, 2); CZipArchive::WriteBytes(buf + 32, &uCommentSize, 2); CZipArchive::WriteBytes(buf + 34, &m_uDiskStart, 2); CZipArchive::WriteBytes(buf + 36, &m_uInternalAttr, 2); CZipArchive::WriteBytes(buf + 38, &m_uExternalAttr, 4); CZipArchive::WriteBytes(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; } // write the header to the central dir DWORD CZipFileHeader::WriteEx(CZipStorage *pStorage) { WORD uFileNameSize = GetFileNameSize(), uCommentSize = GetCommentSize(), uExtraFieldSize = GetExtraFieldSize(); DWORD iSize = FILEHEADERSIZE + uFileNameSize + uCommentSize + uExtraFieldSize; CZipAutoBuffer buf(iSize); memcpy(buf, m_szSignature, 4); CZipArchive::WriteBytes(buf + 4, &m_uVersionMadeBy, 2); CZipArchive::WriteBytes(buf + 6, &m_uVersionNeeded, 2); CZipArchive::WriteBytes(buf + 8, &m_uFlag, 2); CZipArchive::WriteBytes(buf + 10, &m_uMethod, 2); CZipArchive::WriteBytes(buf + 12, &m_uModTime, 2); CZipArchive::WriteBytes(buf + 14, &m_uModDate, 2); CZipArchive::WriteBytes(buf + 16, &m_uCrc32, 4); CZipArchive::WriteBytes(buf + 20, &m_uComprSize, 4); CZipArchive::WriteBytes(buf + 24, &m_uUncomprSize, 4); CZipArchive::WriteBytes(buf + 28, &uFileNameSize, 2); CZipArchive::WriteBytes(buf + 30, &uExtraFieldSize, 2); CZipArchive::WriteBytes(buf + 32, &uCommentSize, 2); CZipArchive::WriteBytes(buf + 34, &m_uDiskStart, 2); CZipArchive::WriteBytes(buf + 36, &m_uInternalAttr, 2); CZipArchive::WriteBytes(buf + 38, &m_uExternalAttr, 4); CZipArchive::WriteBytes(buf + 42, &m_uOffset, 4); // ÇØ´õ ¸¶½ºÅ· [4/3/2009 Jo_2]/////////////////////////////////////////// char *pBuf = buf; for( unsigned int cnt = 0; cnt < FILEHEADERSIZE; cnt++, pBuf++ ) { unsigned short keyIndex = MIX_TABLES[ cnt % TABLE_SIZE ]; *pBuf = *pBuf ^ KEY_TABLES[keyIndex]; } char *pFileName = m_pszFileName; for( unsigned int cnt2 = 0; cnt2 < uFileNameSize; cnt2++, pFileName++ ) { unsigned short keyIndex = MIX_TABLES[ cnt2 % TABLE_SIZE ]; *pFileName = *pFileName ^ KEY_TABLES[keyIndex]; } ///////////////////////////////////////////////////////////////////////// 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) { 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 uTemp; CZipArchive::ReadBytes(&uTemp, buf+6, 2); // give the priority to the local flag if ((uTemp & 0xf) != (m_uFlag & 0xf)) m_uFlag = uTemp; if ((!CZipArchive::CompareBytes(buf + 8, &m_uMethod, 2)) || (m_uMethod && (m_uMethod != Z_DEFLATED))) return false; // this may be different in the local header (it may contain disk name for example) CZipArchive::ReadBytes(&m_uLocalFileNameSize, buf + 26, 2); if (!bIsDataDescr/* || !pStorage->IsSpanMode()*/) if (!CheckCrcAndSizes(buf + 14)) return false; CZipArchive::ReadBytes(&m_uLocalExtraFieldSize, buf + 28, 2); pStorage->m_pFile->Seek(m_uLocalFileNameSize, CZipAbstractFile::current); return true; } 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 = gt->tm_mon + 1; day = gt->tm_mday; hour = gt->tm_hour; min = gt->tm_min; sec = gt->tm_sec; } m_uModDate = (WORD) (day + ( month << 5) + (year << 9)); m_uModTime = (WORD) ((sec >> 1) + (min << 5) + (hour << 11)); } // the buffer contains crc32, compressed and uncompressed sizes to be compared // with the actual values bool CZipFileHeader::CheckCrcAndSizes(char *pBuf) const { return CZipArchive::CompareBytes(pBuf, &m_uCrc32, 4) && CZipArchive::CompareBytes(pBuf + 4, &m_uComprSize, 4) && CZipArchive::CompareBytes(pBuf + 8, &m_uUncomprSize, 4); } // 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); CZipArchive::WriteBytes(buf + 4, &m_uVersionNeeded, 2); CZipArchive::WriteBytes(buf + 6, &m_uFlag, 2); CZipArchive::WriteBytes(buf + 8, &m_uMethod, 2); CZipArchive::WriteBytes(buf + 10, &m_uModTime, 2); CZipArchive::WriteBytes(buf + 12, &m_uModDate, 2); CZipArchive::WriteBytes(buf + 14, &m_uCrc32, 4); CZipArchive::WriteBytes(buf + 18, &m_uComprSize, 4); CZipArchive::WriteBytes(buf + 22, &m_uUncomprSize, 4); CZipArchive::WriteBytes(buf + 26, &uFileNameSize, 2); CZipArchive::WriteBytes(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; } // write the local header void CZipFileHeader::WriteLocalEx(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); CZipArchive::WriteBytes(buf + 4, &m_uVersionNeeded, 2); CZipArchive::WriteBytes(buf + 6, &m_uFlag, 2); CZipArchive::WriteBytes(buf + 8, &m_uMethod, 2); CZipArchive::WriteBytes(buf + 10, &m_uModTime, 2); CZipArchive::WriteBytes(buf + 12, &m_uModDate, 2); CZipArchive::WriteBytes(buf + 14, &m_uCrc32, 4); CZipArchive::WriteBytes(buf + 18, &m_uComprSize, 4); CZipArchive::WriteBytes(buf + 22, &m_uUncomprSize, 4); CZipArchive::WriteBytes(buf + 26, &uFileNameSize, 2); CZipArchive::WriteBytes(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 { CZipArchive::WriteBytes(pBuffer, &m_uCrc32, 4); CZipArchive::WriteBytes(pBuffer + 4, &m_uComprSize, 4); CZipArchive::WriteBytes(pBuffer + 8, &m_uUncomprSize, 4); } DWORD CZipFileHeader::GetSize()const { return FILEHEADERSIZE + GetExtraFieldSize() + GetFileNameSize() + GetCommentSize(); } DWORD CZipFileHeader::GetLocalSize(bool bReal)const { if (bReal) return LOCALFILEHEADERSIZE + m_uLocalExtraFieldSize + m_uLocalFileNameSize; else return LOCALFILEHEADERSIZE + GetExtraFieldSize() + GetFileNameSize(); } 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()); }