meos-2024/code/zip.cpp
2017-08-30 08:57:46 +02:00

461 lines
11 KiB
C++

/*
Code is based on mini unzip, demo of unzip package.
Ported to C++ and modified to suite MeOS.
*/
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <vector>
#include <direct.h>
#include <io.h>
#include "meos_util.h"
#include "minizip/unzip.h"
#include "minizip/zip.h"
#define CASESENSITIVITY (0)
#define WRITEBUFFERSIZE (1024 * 256)
#define MAXFILENAME (260)
#define USEWIN32IOAPI
#include "minizip/iowin32.h"
const char *zipError = "Error processing zip-file";
/* change_file_date : change the date/time of a file
filename : the filename of the file where date/time must be modified
dosdate : the new date at the MSDos format (4 bytes)
tmu_date : the SAME new date at the tm_unz format */
void change_file_date(const char *filename, uLong dosdate, tm_unz tmu_date) {
HANDLE hFile;
FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
0,NULL,OPEN_EXISTING,0,NULL);
GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
LocalFileTimeToFileTime(&ftLocal,&ftm);
SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
CloseHandle(hFile);
}
/* mymkdir and change_file_date are not 100 % portable
As I don't know well Unix, I wait feedback for the unix portion */
int mymkdir(const char *dirname)
{
int ret=0;
ret = _mkdir(dirname);
return ret;
}
int makedir (const char *newdir)
{
int len = (int)strlen(newdir);
if (len <= 0)
return 0;
string buffer = newdir;
if (buffer[len-1] == '/') {
buffer = buffer.substr(0, len-1);
}
if (mymkdir(buffer.c_str()) == 0) {
return 1;
}
size_t index = 1;
while (index<buffer.length()) {
while(index<buffer.length() && buffer[index] != '\\' && buffer[index] != '/')
index++;
char hold = buffer[index];
string sub = buffer.substr(0, index);
if ((mymkdir(sub.c_str()) == -1) && (errno == ENOENT)) {
throw std::exception("Error creating directories");
}
index++;
if (hold == 0)
break;
}
return 1;
}
string do_extract_currentfile(unzFile uf, const string &baseDir, const char* password)
{
string write_filename;
char filename_inzip[256];
char* filename_withoutpath;
char* p;
int err=UNZ_OK;
FILE *fout=NULL;
void* buf;
uInt size_buf;
bool createSubdir = false;
unz_file_info64 file_info;
err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
if (err!=UNZ_OK)
throw std::exception(zipError);
size_buf = WRITEBUFFERSIZE;
vector<BYTE> byteBuff;
byteBuff.resize(size_buf);
buf = (void *)&byteBuff[0];
p = filename_withoutpath = filename_inzip;
while ((*p) != '\0') {
if (((*p)=='/') || ((*p)=='\\'))
filename_withoutpath = p+1;
p++;
}
if ((*filename_withoutpath)=='\0') {
if (createSubdir)
mymkdir(filename_inzip);
}
else {
if (createSubdir)
write_filename = baseDir + filename_inzip;
else
write_filename = baseDir + filename_withoutpath;
err = unzOpenCurrentFilePassword(uf,password);
if (err!=UNZ_OK)
throw std::exception(zipError);
fout = fopen64(write_filename.c_str(),"wb");
// some zipfile doesn't contain directory alone before file
if ((fout==NULL) && createSubdir &&
(filename_withoutpath!=(char*)filename_inzip)) {
char c=*(filename_withoutpath-1);
*(filename_withoutpath-1)='\0';
makedir(write_filename.c_str());
*(filename_withoutpath-1)=c;
fout=fopen64(write_filename.c_str(),"wb");
}
if (fout==NULL) {
string err = "Error opening" + write_filename;
throw std::exception(err.c_str());
}
do {
err = unzReadCurrentFile(uf,buf,size_buf);
if (err<0)
throw std::exception(zipError);
if (err>0)
if (fwrite(buf,err,1,fout)!=1) {
throw std::exception("Error writing extracted file.");
}
}
while (err>0);
if (fout)
fclose(fout);
change_file_date(write_filename.c_str(),file_info.dosDate, file_info.tmu_date);
err = unzCloseCurrentFile (uf);
if (err != UNZ_OK)
throw std::exception(zipError);
}
return write_filename;
}
void do_extract(unzFile uf, const char *basePath, const char* password, vector<string> &extractedFiles)
{
uLong i;
unz_global_info64 gi;
int err;
err = unzGetGlobalInfo64(uf,&gi);
if (err != UNZ_OK)
throw std::exception(zipError);
for (i=0;i<gi.number_entry;i++) {
string name = do_extract_currentfile(uf, basePath, password);
registerTempFile(name);
extractedFiles.push_back(name);
if ((i+1)<gi.number_entry) {
err = unzGoToNextFile(uf);
if (err != UNZ_OK) {
throw std::exception(zipError);
break;
}
}
}
}
void unzip(const char *zipfilename, const char *password, vector<string> &extractedFiles)
{
extractedFiles.clear();
zlib_filefunc64_def ffunc;
fill_win32_filefunc64A(&ffunc);
unzFile uf = unzOpen2_64(zipfilename,&ffunc);
if (uf==NULL)
throw std::exception("Cannot open zip file");
string base = getTempPath();
char end = base[base.length()-1];
if (end != '\\' && end != '/')
base += "\\";
int id = rand();
string target;
do {
target = base + "zip" + itos(id) + "\\";
id++;
}
while ( access( target.c_str(), 0 ) == 0 );
if (CreateDirectory(target.c_str(), NULL) == 0)
throw std::exception("Failed to create temporary folder");
registerTempFile(target);
do_extract(uf, target.c_str(), password, extractedFiles);
unzClose(uf);
}
uLong filetime(const char *f, uLong *dt) {
int ret = 0;
FILETIME ftLocal;
HANDLE hFind;
WIN32_FIND_DATAA ff32;
hFind = FindFirstFileA(f,&ff32);
if (hFind != INVALID_HANDLE_VALUE)
{
FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
FindClose(hFind);
ret = 1;
}
return ret;
}
int check_exist_file(const char* filename)
{
FILE* ftestexist;
int ret = 1;
ftestexist = fopen64(filename,"rb");
if (ftestexist==NULL)
ret = 0;
else
fclose(ftestexist);
return ret;
}
/* calculate the CRC32 of a file, because to encrypt a file, we need known the CRC32 of the file before */
/*int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc)
{
unsigned long calculate_crc=0;
int err=ZIP_OK;
FILE * fin = fopen64(filenameinzip,"rb");
unsigned long size_read = 0;
unsigned long total_read = 0;
if (fin==NULL)
{
err = ZIP_ERRNO;
}
if (err == ZIP_OK)
do
{
err = ZIP_OK;
size_read = (int)fread(buf,1,size_buf,fin);
if (size_read < size_buf)
if (feof(fin)==0)
{
printf("error in reading %s\n",filenameinzip);
err = ZIP_ERRNO;
}
if (size_read>0)
calculate_crc = crc32(calculate_crc,buf,size_read);
total_read += size_read;
} while ((err == ZIP_OK) && (size_read>0));
if (fin)
fclose(fin);
*result_crc=calculate_crc;
printf("file %s crc %lx\n", filenameinzip, calculate_crc);
return err;
}*/
int isLargeFile(const char* filename)
{
int largeFile = 0;
ZPOS64_T pos = 0;
FILE* pFile = fopen64(filename, "rb");
if (pFile != NULL) {
fseeko64(pFile, 0, SEEK_END);
pos = ftello64(pFile);
if (pos >= 0xffffffff)
largeFile = 1;
fclose(pFile);
}
return largeFile;
}
int zip(const char *zipfilename, const char *password, const vector<string> &files) {
int opt_compress_level=Z_DEFAULT_COMPRESSION;
const int opt_exclude_path = 1;
char filename_try[MAXFILENAME+16];
int err=0;
int size_buf=0;
char eb[256];
size_buf = WRITEBUFFERSIZE;
vector<BYTE> vbuff(size_buf, 0);
void * buf = (void*)&vbuff[0];
zipFile zf;
int errclose;
zlib_filefunc64_def ffunc;
fill_win32_filefunc64A(&ffunc);
strcpy(filename_try, zipfilename);
zf = zipOpen2_64(filename_try, 0,NULL, &ffunc);
if (zf == NULL) {
sprintf_s(eb, "Error opening %s.",filename_try);
throw std::exception(eb);
}
for (size_t i=0; i < files.size(); i++) {
FILE * fin;
int size_read;
const char* filenameinzip = files[i].c_str();
const char *savefilenameinzip;
zip_fileinfo zi;
unsigned long crcFile=0;
int zip64 = 0;
zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
zi.dosDate = 0;
zi.internal_fa = 0;
zi.external_fa = 0;
filetime(filenameinzip, &zi.dosDate);
/*
if ((password != NULL) && (err==ZIP_OK))
err = getFileCrc(filenameinzip,buf,size_buf,&crcFile);
*/
zip64 = isLargeFile(filenameinzip);
/* The path name saved, should not include a leading slash. */
/*if it did, windows/xp and dynazip couldn't read the zip file. */
savefilenameinzip = filenameinzip;
while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' ) {
savefilenameinzip++;
}
/*should the zip file contain any path at all?*/
if ( opt_exclude_path ) {
const char *tmpptr;
const char *lastslash = 0;
for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) {
if ( *tmpptr == '\\' || *tmpptr == '/')
lastslash = tmpptr;
}
if ( lastslash != NULL ) {
savefilenameinzip = lastslash+1; // base filename follows last slash.
}
}
/**/
err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi,
NULL,0,NULL,0,NULL /* comment*/,
(opt_compress_level != 0) ? Z_DEFLATED : 0,
opt_compress_level,0,
/* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
password,crcFile, zip64);
if (err != ZIP_OK) {
sprintf_s(eb, "Error opening %s in zipfile",filenameinzip);
throw std::exception(eb);
}
else {
fin = fopen64(filenameinzip,"rb");
if (fin==NULL) {
sprintf_s(eb, "Error opening %s for reading",filenameinzip);
throw std::exception(eb);
}
}
if (err == ZIP_OK)
do {
err = ZIP_OK;
size_read = (int)fread(buf,1,size_buf,fin);
if (size_read < size_buf) {
if (feof(fin)==0) {
sprintf_s(eb, "Error reading %s",filenameinzip);
throw std::exception(eb);
}
}
if (size_read > 0) {
err = zipWriteInFileInZip (zf,buf,size_read);
if (err<0) {
sprintf_s(eb, "Error in writing %s in the zipfile",filenameinzip);
throw std::exception(eb);
}
}
} while ((err == ZIP_OK) && (size_read>0));
if (fin)
fclose(fin);
if (err<0)
err=ZIP_ERRNO;
else {
err = zipCloseFileInZip(zf);
if (err!=ZIP_OK) {
sprintf_s(eb, "Error closing %s in the zipfile", filenameinzip);
throw std::exception(eb);
}
}
}
errclose = zipClose(zf,NULL);
if (errclose != ZIP_OK) {
sprintf_s(eb, "Error closing %s",filename_try);
throw std::exception(eb);
}
return 0;
}