// Zipper.cpp: implementation of the VMZipper class.
//
//////////////////////////////////////////////////////////////////////
#include "../../../stdafx.h"
#include "../../DataCompression/zlib.core.library/zip.h"
#include "../../DataCompression/zlib.core.library/iowin32.h"
#include "VMZipper.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
const UINT BUFFERSIZE = 2048;
VMZipper::VMZipper(LPCTSTR szFilePath, LPCTSTR szRootFolder, bool bAppend) : m_uzFile(0)
{
CloseZip();
ZeroMemory(&m_info, sizeof(m_info));
if (szFilePath)
OpenZip(szFilePath, szRootFolder, bAppend);
}
VMZipper::~VMZipper()
{
CloseZip();
}
bool VMZipper::CloseZip()
{
int nRet = m_uzFile ? zipClose(m_uzFile, NULL) : ZIP_OK;
m_uzFile = NULL;
m_szRootFolder[0] = 0;
// note: we don't clear m_info until the object is re-used or deleted
return (nRet == ZIP_OK);
}
void VMZipper::GetFileInfo(VMZipFileInfo& info)
{
info = m_info;
}
// simple interface
bool VMZipper::ZipFile(LPCTSTR szFilePath)
{
// make zip path
char szDrive[_MAX_DRIVE], szFolder[MAX_PATH], szName[_MAX_FNAME];
_splitpath(szFilePath, szDrive, szFolder, szName, NULL);
char szZipPath[MAX_PATH];
_makepath(szZipPath, szDrive, szFolder, szName, "zip");
VMZipper zip;
if (zip.OpenZip(szZipPath, false))
return zip.AddFileToZip(szFilePath, false);
return FALSE;
}
bool VMZipper::ZipFolder(LPCTSTR szFilePath, bool bIgnoreFilePath)
{
// make zip path
char szDrive[_MAX_DRIVE], szFolder[MAX_PATH], szName[_MAX_FNAME], szExt[_MAX_EXT];
_splitpath(szFilePath, szDrive, szFolder, szName, szExt);
lstrcat(szName, szExt); // save extension if any
// set root path to include the folder name
char szRootPath[MAX_PATH];
_makepath(szRootPath, szDrive, szFolder, szName, NULL);
char szZipPath[MAX_PATH];
_makepath(szZipPath, szDrive, szFolder, szName, "zip");
VMZipper zip;
if (zip.OpenZip(szZipPath, szRootPath, FALSE))
return zip.AddFolderToZip(szFilePath, bIgnoreFilePath);
return FALSE;
}
// works with prior opened zip
bool VMZipper::AddFileToZip(LPCTSTR szFilePath, bool bIgnoreFilePath)
{
if (!m_uzFile)
return FALSE;
// we don't allow paths beginning with '..\' because this would be outside
// the root folder
if (!bIgnoreFilePath && strstr(szFilePath, "..\\") == szFilePath)
return false;
bool bFullPath = (strchr(szFilePath, ':') != NULL);
// if the file is relative then we need to append the root before opening
char szFullFilePath[MAX_PATH];
lstrcpy(szFullFilePath, szFilePath);
PrepareSourcePath(szFullFilePath);
// if the file is a fullpath then remove the root path bit
char szFileName[MAX_PATH] = "";
if (bIgnoreFilePath)
{
char szName[_MAX_FNAME], szExt[_MAX_EXT];
_splitpath(szFilePath, NULL, NULL, szName, szExt);
_makepath(szFileName, NULL, NULL, szName, szExt);
}
else if (bFullPath)
{
// check the root can be found
if (0 != _strnicmp(szFilePath, m_szRootFolder, lstrlen(m_szRootFolder)))
return false;
// else
lstrcpy(szFileName, szFilePath + lstrlen(m_szRootFolder));
}
else // relative path
{
// if the path begins with '.\' then remove it
if (strstr(szFilePath, ".\\") == szFilePath)
lstrcpy(szFileName, szFilePath + 2);
else
lstrcpy(szFileName, szFilePath);
}
PrepareZipPath(szFileName);
// save file attributes
zip_fileinfo zfi;
zfi.internal_fa = 0;
zfi.external_fa = ::GetFileAttributes(szFilePath);
// save file time
SYSTEMTIME st;
GetLastModified(szFullFilePath, st);
zfi.dosDate = 0;
zfi.tmz_date.tm_year = st.wYear;
zfi.tmz_date.tm_mon = st.wMonth - 1;
zfi.tmz_date.tm_mday = st.wDay;
zfi.tmz_date.tm_hour = st.wHour;
zfi.tmz_date.tm_min = st.wMinute;
zfi.tmz_date.tm_sec = st.wSecond;
// load input file
HANDLE hInputFile = ::CreateFile(szFullFilePath,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if (hInputFile == INVALID_HANDLE_VALUE)
return FALSE;
int nRet = zipOpenNewFileInZip(m_uzFile,
szFileName,
&zfi,
NULL,
0,
NULL,
0,
NULL,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
if (nRet == ZIP_OK)
{
m_info.nFileCount++;
// read the file and output to zip
char pBuffer[BUFFERSIZE];
DWORD dwBytesRead = 0, dwFileSize = 0;
while (nRet == ZIP_OK && ::ReadFile(hInputFile, pBuffer, BUFFERSIZE, &dwBytesRead, NULL))
{
dwFileSize += dwBytesRead;
if (dwBytesRead)
nRet = zipWriteInFileInZip(m_uzFile, pBuffer, dwBytesRead);
else
break;
}
m_info.dwUncompressedSize += dwFileSize;
}
zipCloseFileInZip(m_uzFile);
::CloseHandle(hInputFile);
return (nRet == ZIP_OK);
}
bool VMZipper::AddFileToZip(LPCTSTR szFilePath, LPCTSTR szRelFolderPath)
{
if (!m_uzFile)
return FALSE;
// szRelFolderPath cannot contain drive info
if (szRelFolderPath && strchr(szRelFolderPath, ':'))
return FALSE;
// if the file is relative then we need to append the root before opening
char szFullFilePath[MAX_PATH];
lstrcpy(szFullFilePath, szFilePath);
PrepareSourcePath(szFullFilePath);
// save file attributes and time
zip_fileinfo zfi;
zfi.internal_fa = 0;
zfi.external_fa = ::GetFileAttributes(szFilePath);
// save file time
SYSTEMTIME st;
GetLastModified(szFullFilePath, st);
zfi.dosDate = 0;
zfi.tmz_date.tm_year = st.wYear;
zfi.tmz_date.tm_mon = st.wMonth - 1;
zfi.tmz_date.tm_mday = st.wDay;
zfi.tmz_date.tm_hour = st.wHour;
zfi.tmz_date.tm_min = st.wMinute;
zfi.tmz_date.tm_sec = st.wSecond;
// load input file
HANDLE hInputFile = ::CreateFile(szFullFilePath,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if (hInputFile == INVALID_HANDLE_VALUE)
return FALSE;
// strip drive info off filepath
char szName[_MAX_FNAME], szExt[_MAX_EXT];
_splitpath(szFilePath, NULL, NULL, szName, szExt);
// prepend new folder path
char szFileName[MAX_PATH];
_makepath(szFileName, NULL, szRelFolderPath, szName, szExt);
PrepareZipPath(szFileName);
// open the file in the zip making sure we remove any leading '\'
int nRet = zipOpenNewFileInZip(m_uzFile,
szFileName + (szFileName[0] == '\\' ? 1 : 0),
&zfi,
NULL,
0,
NULL,
0,
NULL,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
if (nRet == ZIP_OK)
{
m_info.nFileCount++;
// read the file and output to zip
char pBuffer[BUFFERSIZE];
DWORD dwBytesRead = 0, dwFileSize = 0;
while (nRet == ZIP_OK && ::ReadFile(hInputFile, pBuffer, BUFFERSIZE, &dwBytesRead, NULL))
{
dwFileSize += dwBytesRead;
if (dwBytesRead)
nRet = zipWriteInFileInZip(m_uzFile, pBuffer, dwBytesRead);
else
break;
}
m_info.dwUncompressedSize += dwFileSize;
}
zipCloseFileInZip(m_uzFile);
::CloseHandle(hInputFile);
return (nRet == ZIP_OK);
}
bool VMZipper::AddFolderToZip(LPCTSTR szFolderPath, bool bIgnoreFilePath)
{
if (!m_uzFile)
return FALSE;
m_info.nFolderCount++;
// if the path is relative then we need to append the root before opening
char szFullPath[MAX_PATH];
lstrcpy(szFullPath, szFolderPath);
PrepareSourcePath(szFullPath);
// always add folder first
// save file attributes
zip_fileinfo zfi;
zfi.internal_fa = 0;
zfi.external_fa = ::GetFileAttributes(szFullPath);
SYSTEMTIME st;
GetLastModified(szFullPath, st);
zfi.dosDate = 0;
zfi.tmz_date.tm_year = st.wYear;
zfi.tmz_date.tm_mon = st.wMonth - 1;
zfi.tmz_date.tm_mday = st.wDay;
zfi.tmz_date.tm_hour = st.wHour;
zfi.tmz_date.tm_min = st.wMinute;
zfi.tmz_date.tm_sec = st.wSecond;
// if the folder is a fullpath then remove the root path bit
char szFolderName[MAX_PATH] = "";
if (bIgnoreFilePath)
{
char szExt[_MAX_EXT];
_splitpath(szFullPath, NULL, NULL, szFolderName, szExt);
lstrcat(szFolderName, szExt);
}
else
{
// check the root can be found
if (0 != _strnicmp(szFullPath, m_szRootFolder, lstrlen(m_szRootFolder)))
return false;
// else
lstrcpy(szFolderName, szFullPath + lstrlen(m_szRootFolder));
}
// open the file in the zip making sure we remove any leading '\'
// provided that the folder name is not empty.
// note: its ok for it to be empty if this folder coincides with the root folder
PrepareZipPath(szFolderName);
if (lstrlen(szFolderName))
{
int nRet = zipOpenNewFileInZip(m_uzFile,
szFolderName,
&zfi,
NULL,
0,
NULL,
0,
NULL,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
zipCloseFileInZip(m_uzFile);
}
// build searchspec
char szDrive[_MAX_DRIVE], szFolder[MAX_PATH], szName[_MAX_FNAME], szExt[_MAX_EXT];
_splitpath(szFullPath, szDrive, szFolder, szName, szExt);
lstrcat(szFolder, szName);
lstrcat(szFolder, szExt);
char szSearchSpec[MAX_PATH];
_makepath(szSearchSpec, szDrive, szFolder, "*", "*");
WIN32_FIND_DATA finfo;
HANDLE hSearch = FindFirstFile(szSearchSpec, &finfo);
if (hSearch != INVALID_HANDLE_VALUE)
{
do
{
if (finfo.cFileName[0] != '.')
{
char szItem[MAX_PATH];
_makepath(szItem, szDrive, szFolder, finfo.cFileName, NULL);
if (finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
AddFolderToZip(szItem, bIgnoreFilePath);
}
else
AddFileToZip(szItem, bIgnoreFilePath);
}
}
while (FindNextFile(hSearch, &finfo));
FindClose(hSearch);
}
return TRUE;
}
// extended interface
bool VMZipper::OpenZip(LPCTSTR szFilePath, LPCTSTR szRootFolder, bool bAppend)
{
CloseZip();
ZeroMemory(&m_info, sizeof(m_info));
if (!szFilePath || !lstrlen(szFilePath))
return false;
// convert szFilePath to fully qualified path
char szFullPath[MAX_PATH];
if (!GetFullPathName(szFilePath, MAX_PATH, szFullPath, NULL))
return false;
// zipOpen will fail if bAppend is TRUE and zip does not exist
if (bAppend && ::GetFileAttributes(szFullPath) == 0xffffffff)
bAppend = false;
m_uzFile = zipOpen(szFullPath, bAppend ? 1 : 0);
if (m_uzFile)
{
if (!szRootFolder)
{
char szDrive[_MAX_DRIVE], szFolder[MAX_PATH];
_splitpath(szFullPath, szDrive, szFolder, NULL, NULL);
_makepath(m_szRootFolder, szDrive, szFolder, NULL, NULL);
}
else if (lstrlen(szRootFolder))
{
_makepath(m_szRootFolder, NULL, szRootFolder, NULL, NULL);
}
// remove any trailing whitespace and '\'
UnterminatePath(m_szRootFolder);
}
return (m_uzFile != NULL);
}
void VMZipper::UnterminatePath(LPTSTR szPath)
{
int nEnd = lstrlen(szPath) - 1;
while (isspace(szPath[nEnd]))
nEnd--;
while (szPath[nEnd] == '\\')
nEnd--;
szPath[nEnd + 1] = 0;
}
void VMZipper::PrepareZipPath(LPTSTR szPath)
{
UnterminatePath(szPath);
// remove leading whitespace and '\'
char szTemp[MAX_PATH];
lstrcpy(szTemp, szPath);
int nStart = 0;
while (isspace(szTemp[nStart]))
nStart++;
while (szTemp[nStart] == '\\')
nStart++;
if (nStart)
lstrcpy(szPath, szTemp + nStart);
}
void VMZipper::PrepareSourcePath(LPTSTR szPath)
{
bool bFullPath = (strchr(szPath, ':') != NULL);
// if the file is relative then we need to append the root before opening
if (!bFullPath)
{
char szTemp[MAX_PATH];
lstrcpy(szTemp, szPath);
_makepath(szPath, NULL, m_szRootFolder, szTemp, NULL);
}
}
bool VMZipper::GetLastModified(const char* szPath, SYSTEMTIME& sysTime)
{
ZeroMemory(&sysTime, sizeof(SYSTEMTIME));
DWORD dwAttr = ::GetFileAttributes(szPath);
// files only
if (dwAttr == 0xFFFFFFFF)
return false;
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile((LPTSTR)szPath, &findFileData);
if (hFind == INVALID_HANDLE_VALUE)
return FALSE;
FindClose(hFind);
FILETIME ft = findFileData.ftLastWriteTime;
FileTimeToLocalFileTime(&findFileData.ftLastWriteTime, &ft);
FileTimeToSystemTime(&ft, &sysTime);
return true;
}