diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
commit | a6d58bb6052ac8cb01805a48c4ad2f129126116f (patch) | |
tree | dd867a099fcbb263a8009a9fb22695b87855dad6 /src/kvilib/file/kvi_packagefile.cpp | |
download | kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.tar.gz kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.zip |
Added KDE3 version of kvirc
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/kvilib/file/kvi_packagefile.cpp')
-rw-r--r-- | src/kvilib/file/kvi_packagefile.cpp | 1028 |
1 files changed, 1028 insertions, 0 deletions
diff --git a/src/kvilib/file/kvi_packagefile.cpp b/src/kvilib/file/kvi_packagefile.cpp new file mode 100644 index 00000000..3e7bcc17 --- /dev/null +++ b/src/kvilib/file/kvi_packagefile.cpp @@ -0,0 +1,1028 @@ +//============================================================================= +// +// File : kvi_packagefile.cpp +// Created on Tue 26 Dec 2006 05:33:33 by Szymon Stefanek +// +// This file is part of the KVIrc IRC Client distribution +// Copyright (C) 2006 Szymon Stefanek <pragma at kvirc dot net> +// +// 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 opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#define __KVILIB__ +#include "kvi_packagefile.h" + +#include "kvi_file.h" +#include "kvi_fileutils.h" +#include "kvi_locale.h" +#include "kvi_inttypes.h" + +#include <qprogressdialog.h> +#include <qlabel.h> + +#include <qdir.h> + +#ifdef COMPILE_ZLIB_SUPPORT + #include <zlib.h> +#endif + +// +// A KVIrc Package File is basically a simple zip file with some additional meta-data. +// The package file has the following format +// + +// Field Type Bytes Description +//------------------------------------------------------------------------------- +// Package: +// PackageHeader +// PackageInfo +// PackageData + +// PackageHeader: +// Magic Bytes 4 'KVPF': Signature for the Kvirc Package File +// Version uint32 4 0x00000001: Version of this package file +// Flags uint32 4 0x00000000: Flags, in version 1 is reserved and must be zero +// + +// PackageInfo: +// InfoFieldCount uint32 4 Number of package info fields +// InfoField InfoField Variable A list of informational name-value pairs +// InfoField InfoField Variable A list of informational name-value pairs +// InfoField InfoField Variable A list of informational name-value pairs +// .... .... .... + +// PackageData: +// DataField DataField Variable A list of data fields with format defined below +// DataField DataField Variable A list of data fields with format defined below +// DataField DataField Variable A list of data fields with format defined below +// .... .... .... + +// InfoField: +// Name UniString Variable The "name" element of the info field +// ValueType uint32 4 The type of the following ValueData field +// ValueData ValueData Variable + +// ValueData for ValueType 1 (string field) +// Value UniString Variable The value element of type string of the the info field + +// ValueData for ValueType 2 (binary buffer field) +// BufferLen uint32 4 The length of the binary buffer +// BufferData Bytes Variable The data for the binary buffer + + +// UniString: +// StringLen uint32 4 The length of the string data in BYTES (null terminator NOT included) +// StringData Bytes StringLen An utf8 encoded string (do NOT write the NULL terminator) + +// Bytes: +// Byte uint8 1 A byte +// Byte uint8 1 A byte +// .... .... .... + +// DataField: +// FieldType uint32 4 The type of the field, see below for defined values +// FieldLen uint32 4 FieldData length in bytes (useful for skipping a field if unsupported) +// FieldData Variable FieldLen The data of the field, see below for defined values + +// FieldData for FieldType 1 (file field) +// Flags uint32 4 Bitmask. Bits: 1=FileIsDeflated +// Path UniString Variable A relative path expressed as utf8 string. \ AND / are considered to be separators +// Size uint32 4 Size of the following file data +// FilePayload Bytes Variable + +// Everything is stored in LITTLE ENDIAN byte order. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Da Base Engine + +KviPackageIOEngine::KviPackageIOEngine() +{ + m_pProgressDialog = 0; + m_pStringInfoFields = new KviPointerHashTable<QString,QString>(); + m_pStringInfoFields->setAutoDelete(true); + m_pBinaryInfoFields = new KviPointerHashTable<QString,QByteArray>(); + m_pBinaryInfoFields->setAutoDelete(true); +} + +KviPackageIOEngine::~KviPackageIOEngine() +{ + if(m_pProgressDialog)delete m_pProgressDialog; + delete m_pStringInfoFields; + delete m_pBinaryInfoFields; +} + + +bool KviPackageIOEngine::updateProgress(int iProgress,const QString &szLabel) +{ + if(!m_pProgressDialog)return true; +#ifdef COMPILE_USE_QT4 + m_pProgressDialog->setValue(iProgress); +#else + m_pProgressDialog->setProgress(iProgress); +#endif + m_pProgressDialogLabel->setText(szLabel); + qApp->processEvents(); + if(m_pProgressDialog->wasCanceled()) + { + setLastError(__tr2qs("Operation cancelled")); + return false; + } + return true; +} + +void KviPackageIOEngine::showProgressDialog(const QString &szCaption,int iTotalSteps) +{ +#ifdef COMPILE_USE_QT4 + m_pProgressDialog = new QProgressDialog(QString(""),__tr2qs("Cancel"),0,iTotalSteps,0); + m_pProgressDialog->setModal(true); + m_pProgressDialog->setWindowTitle(szCaption); +#else + m_pProgressDialog = new QProgressDialog(QString(""),__tr2qs("Cancel"),iTotalSteps,0,"",true); + m_pProgressDialog->setCaption(szCaption); +#endif + m_pProgressDialogLabel = new QLabel(m_pProgressDialog); + m_pProgressDialogLabel->setMaximumSize(500,300); + m_pProgressDialog->setLabel(m_pProgressDialogLabel); +} + +void KviPackageIOEngine::hideProgressDialog() +{ + if(!m_pProgressDialog)return; + delete m_pProgressDialog; + m_pProgressDialog = 0; +} + +bool KviPackageIOEngine::writeError() +{ + setLastError(__tr2qs("File write error")); + return false; +} + +bool KviPackageIOEngine::readError() +{ + setLastError(__tr2qs("File read error")); + return false; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Da Writer + + +KviPackageWriter::KviPackageWriter() +: KviPackageIOEngine() +{ + m_pDataFields = new KviPointerList<DataField>(); + m_pDataFields->setAutoDelete(true); +} + +KviPackageWriter::~KviPackageWriter() +{ + delete m_pDataFields; +} + +void KviPackageWriter::addInfoField(const QString &szName,const QString &szValue) +{ + m_pStringInfoFields->replace(szName,new QString(szValue)); +} + +void KviPackageWriter::addInfoField(const QString &szName,QByteArray * pValue) +{ + m_pBinaryInfoFields->replace(szName,pValue); +} + +bool KviPackageWriter::addFile(const QString &szLocalFileName,const QString &szTargetFileName,kvi_u32_t uAddFileFlags) +{ + QFileInfo fi(szLocalFileName); + return addFileInternal(&fi,szLocalFileName,szTargetFileName,uAddFileFlags); +} + +bool KviPackageWriter::addFileInternal(const QFileInfo * fi,const QString &szLocalFileName,const QString &szTargetFileName,kvi_u32_t uAddFileFlags) +{ + if(!(fi->isFile() && fi->isReadable())) + return false; + + if(!(uAddFileFlags & FollowSymLinks)) + { + if(fi->isSymLink()) + return true; // do NOT add a symlink + } + + DataField * f = new DataField(); + f->m_uType = KVI_PACKAGE_DATAFIELD_TYPE_FILE; + f->m_bFileAllowCompression = !(uAddFileFlags & NoCompression); + f->m_szFileLocalName = szLocalFileName; + f->m_szFileTargetName = szTargetFileName; + m_pDataFields->append(f); + + return true; +} + +bool KviPackageWriter::addDirectory(const QString &szLocalDirectoryName,const QString &szTargetDirectoryPrefix,kvi_u32_t uAddFileFlags) +{ + QDir d(szLocalDirectoryName); +#ifdef COMPILE_USE_QT4 + QDir::Filters iFlags; +#else + int iFlags; +#endif + iFlags = QDir::Files | QDir::Readable; + if(!(uAddFileFlags & FollowSymLinks)) + iFlags |= QDir::NoSymLinks; + + // QT4SUX: Because the QDir::entryInfoList() breaks really a lot of code by returning an object that behaves in a _totally_ different way.. it's also much slower + +#ifdef COMPILE_USE_QT4 + int j; + QFileInfoList sl = d.entryInfoList(iFlags); + for(j=0;j<sl.size();j++) + { +#else + const QFileInfoList * sl = d.entryInfoList(iFlags); + if(!sl)return false; + QFileInfoListIterator it(*sl); + while(QFileInfo * fi = it.current()) + { +#endif + QString szSFileName = szLocalDirectoryName; + KviQString::ensureLastCharIs(szSFileName,QChar(KVI_PATH_SEPARATOR_CHAR)); +#ifdef COMPILE_USE_QT4 + QFileInfo slowCopy = sl.at(j); + szSFileName += slowCopy.fileName(); +#else + szSFileName += fi->fileName(); +#endif + QString szDFileName = szTargetDirectoryPrefix; + KviQString::ensureLastCharIs(szDFileName,QChar(KVI_PATH_SEPARATOR_CHAR)); +#ifdef COMPILE_USE_QT4 + szDFileName += slowCopy.fileName(); + if(!addFileInternal(&slowCopy,szSFileName,szDFileName,uAddFileFlags)) + return false; +#else + szDFileName += fi->fileName(); + if(!addFileInternal(fi,szSFileName,szDFileName,uAddFileFlags)) + return false; +#endif +#ifndef COMPILE_USE_QT4 + ++it; +#endif + } + iFlags = QDir::Dirs | QDir::Readable; + if(!(uAddFileFlags & FollowSymLinks)) + iFlags |= QDir::NoSymLinks; + sl = d.entryInfoList(iFlags); +#ifdef COMPILE_USE_QT4 + for(j=0;j<sl.size();j++) + { + QString szDir = sl.at(j).fileName(); +#else + if(!sl)return false; + QFileInfoListIterator it2(*sl); + while(QFileInfo * fi2 = it2.current()) + { + QString szDir = fi2->fileName(); +#endif + if(!KviQString::equalCS(szDir,"..") && !KviQString::equalCS(szDir,".")) + { + QString szSDirName = szLocalDirectoryName; + KviQString::ensureLastCharIs(szSDirName,QChar(KVI_PATH_SEPARATOR_CHAR)); + szSDirName += szDir; + QString szDDirName = szTargetDirectoryPrefix; + KviQString::ensureLastCharIs(szDDirName,QChar(KVI_PATH_SEPARATOR_CHAR)); + szDDirName += szDir; + if(!addDirectory(szSDirName,szDDirName,uAddFileFlags)) + return false; + } +#ifndef COMPILE_USE_QT4 + ++it2; +#endif + } + + return true; +} + + + +#define BUFFER_SIZE 32768 + +bool KviPackageWriter::packFile(KviFile * pFile,DataField * pDataField) +{ + QString szProgressText; + KviQString::sprintf(szProgressText,__tr2qs("Packaging file %Q"),&(pDataField->m_szFileLocalName)); + if(!updateProgress(m_iCurrentProgress,szProgressText)) + return false; // aborted + + + KviFile source(pDataField->m_szFileLocalName); + if(!source.openForReading()) + { + setLastError(__tr2qs("Failed to open a source file for reading")); + return false; + } + + kvi_u32_t uSize = source.size(); + + // Flags +#ifdef COMPILE_ZLIB_SUPPORT + kvi_u32_t uFlags = pDataField->m_bFileAllowCompression ? + (uSize > 64 ? KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE : 0) + : 0; +#else + kvi_u32_t uFlags = 0; +#endif + + if(!pFile->save(uFlags))return writeError(); + + KviQCString szTargetFileName = KviQString::toUtf8(pDataField->m_szFileTargetName); + + // Path + if(!pFile->save(szTargetFileName))return writeError(); + + kvi_file_offset_t savedSizeOffset = pFile->pos(); + + // Size : will update it if compression is requested + if(!pFile->save(uSize))return writeError(); + + pDataField->m_uWrittenFieldLength = 4 + 4 + 4 + szTargetFileName.length(); // sizeof(flags + uncompressed size + path len + path) + + // FilePayload +#ifdef COMPILE_ZLIB_SUPPORT + if(uFlags & KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE) + { + unsigned char ibuffer[BUFFER_SIZE]; + unsigned char obuffer[BUFFER_SIZE]; + + kvi_i32_t iReaded = source.readBlock((char *)ibuffer,BUFFER_SIZE); + if(iReaded < 0) + return readError(); + + z_stream zstr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.next_in = ibuffer; + zstr.avail_in = iReaded; + zstr.next_out = obuffer; + zstr.avail_out = BUFFER_SIZE; + + if(deflateInit(&zstr,9) != Z_OK) + { + setLastError(__tr2qs("Compression library initialization error")); + return false; + } + + while(iReaded > 0) + { + zstr.next_out = obuffer; + zstr.avail_out = BUFFER_SIZE; + + if(deflate(&zstr,Z_NO_FLUSH) != Z_OK) + { + setLastError(__tr2qs("Compression library error")); + return false; + } + + if(zstr.avail_out < BUFFER_SIZE) + { + int iCompressed = zstr.next_out - obuffer; + pDataField->m_uWrittenFieldLength += iCompressed; + if(pFile->writeBlock((char *)obuffer,iCompressed) != iCompressed) + { + deflateEnd(&zstr); + return writeError(); + } + } + + if(zstr.avail_in < BUFFER_SIZE) + { + int iDataToRead = BUFFER_SIZE - zstr.avail_in; + if(iDataToRead < BUFFER_SIZE) + { + if(ibuffer != zstr.next_in) + { + // hum, there is still some data in the buffer to be readed + // and it is not at the beginning...move it to the beginning of ibuffer + memmove(ibuffer,zstr.next_in,zstr.avail_in); + } + } + iReaded = source.readBlock((char *)(ibuffer + zstr.avail_in),iDataToRead); + if(iReaded < 0) + { + deflateEnd(&zstr); + return readError(); + } + zstr.avail_in += iReaded; + zstr.next_in = ibuffer; + + if((zstr.total_in % 2000000) == 0) + { + QString szTmp; + KviQString::sprintf(szTmp,QString(" (%d of %d bytes)"),zstr.total_in,uSize); + QString szPrg = szProgressText + szTmp; + if(!updateProgress(m_iCurrentProgress,szPrg)) + return false; // aborted + } + + + } + } + + // flush pending output + zstr.next_out = obuffer; + zstr.avail_out = BUFFER_SIZE; + + int ret; + do + { + ret = deflate(&zstr,Z_FINISH); + + if((ret == Z_OK) || (ret == Z_STREAM_END)) + { + if(zstr.avail_out < BUFFER_SIZE) + { + int iCompressed = zstr.next_out - obuffer; + pDataField->m_uWrittenFieldLength += iCompressed; + if(pFile->writeBlock((char *)obuffer,iCompressed) != iCompressed) + { + deflateEnd(&zstr); + return writeError(); + } + } else { + deflateEnd(&zstr); + setLastError(__tr2qs("Compression library internal error")); + return false; + } + + zstr.next_out = obuffer; + zstr.avail_out = BUFFER_SIZE; + } + + } while(ret == Z_OK); + + // store the compressed data size + kvi_file_offset_t here = pFile->pos(); + pFile->seek(savedSizeOffset); + uSize = zstr.total_out; + + deflateEnd(&zstr); + if(!pFile->save(uSize))return writeError(); + + if(ret != Z_STREAM_END) + { + setLastError(__tr2qs("Error while compressing a file stream")); + return false; + } + + pFile->seek(here); + } else { +#endif + unsigned char buffer[BUFFER_SIZE]; + int iTotalFileSize = 0; + kvi_i32_t iReaded = source.readBlock((char *)buffer,BUFFER_SIZE); + if(iReaded < 0) + return readError(); + while(iReaded > 0) + { + iTotalFileSize += iReaded; + if((iTotalFileSize % 1000000) == 0) + { + QString szTmp; + KviQString::sprintf(szTmp,QString(" (%d of %d bytes)"),iTotalFileSize,uSize); + QString szPrg = szProgressText + szTmp; + if(!updateProgress(m_iCurrentProgress,szPrg)) + return false; // aborted + } + pDataField->m_uWrittenFieldLength += iReaded; + if(pFile->writeBlock((char *)buffer,iReaded) != iReaded) + return writeError(); + iReaded = source.readBlock((char *)buffer,BUFFER_SIZE); + } +#ifdef COMPILE_ZLIB_SUPPORT + } +#endif + source.close(); + + return true; +} + +bool KviPackageWriter::pack(const QString &szFileName,kvi_u32_t uPackFlags) +{ + m_iCurrentProgress = 0; + if(!(uPackFlags & NoProgressDialog)) + { + showProgressDialog(__tr2qs("Creating package..."),100); + updateProgress(m_iCurrentProgress,__tr2qs("Writing package header")); + } + + bool bRet = packInternal(szFileName,uPackFlags); + + hideProgressDialog(); + return bRet; +} + +bool KviPackageWriter::packInternal(const QString &szFileName,kvi_u32_t uPackFlags) +{ + + KviFile f(szFileName); + if(!f.openForWriting()) + { + setLastError(__tr2qs("Can't open file for writing")); + return false; + } + + // write the PackageHeader + + // Magic + char magic[4]; + magic[0] = 'K'; + magic[1] = 'V'; + magic[2] = 'P'; + magic[3] = 'F'; + if(f.writeBlock(magic,4) != 4)return writeError(); + + // Version + kvi_u32_t uVersion = 0x1; + if(!f.save(uVersion))return writeError(); + + // Flags + kvi_u32_t uFlags = 0x0; + if(!f.save(uFlags))return writeError(); + + // write PackageInfo + + // InfoFieldCount + kvi_u32_t uCount = m_pStringInfoFields->count() + m_pBinaryInfoFields->count(); + if(!f.save(uCount))return writeError(); + + m_iCurrentProgress = 5; + if(!updateProgress(m_iCurrentProgress,__tr2qs("Writing informational fields"))) + return false; // aborted + + // InfoFields (string) + KviPointerHashTableIterator<QString,QString> it(*m_pStringInfoFields); + while(QString * s = it.current()) + { + if(!f.save(it.currentKey()))return writeError(); + kvi_u32_t uType = KVI_PACKAGE_INFOFIELD_TYPE_STRING; + if(!f.save(uType))return writeError(); + if(!f.save(*s))return writeError(); + ++it; + } + + // InfoFields (binary) + KviPointerHashTableIterator<QString,QByteArray> it2(*m_pBinaryInfoFields); + while(QByteArray * b = it2.current()) + { + if(!f.save(it2.currentKey()))return writeError(); + kvi_u32_t uType = KVI_PACKAGE_INFOFIELD_TYPE_BINARYBUFFER; + if(!f.save(uType))return writeError(); + if(!f.save(*b))return writeError(); + ++it2; + } + + m_iCurrentProgress = 10; + if(!updateProgress(m_iCurrentProgress,__tr2qs("Writing package data"))) + return false; // aborted + + // write PackageData + int iIdx = 0; + for(DataField * pDataField = m_pDataFields->first();pDataField;pDataField = m_pDataFields->next()) + { + kvi_u32_t uDataFieldType = pDataField->m_uType; + if(!f.save(uDataFieldType))return writeError(); + + kvi_file_offset_t savedLenOffset = f.pos(); + // here we will store the length of the field once it's written + if(!f.save(uDataFieldType))return writeError(); + + m_iCurrentProgress = 10 + ((90 * iIdx) / m_pDataFields->count()); + + switch(pDataField->m_uType) + { + case KVI_PACKAGE_DATAFIELD_TYPE_FILE: + if(!packFile(&f,pDataField)) + return false; + break; + default: + setLastError(__tr2qs("Internal error")); + return false; + break; + } + + kvi_file_offset_t savedEndOffset = f.pos(); + f.seek(savedLenOffset); + if(!f.save(pDataField->m_uWrittenFieldLength)) + return writeError(); + + f.seek(savedEndOffset); + iIdx++; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Da Reader + +KviPackageReader::KviPackageReader() +: KviPackageIOEngine() +{ +} + +KviPackageReader::~KviPackageReader() +{ +} + +bool KviPackageReader::readHeaderInternal(KviFile * pFile,const QString &szLocalFileName) +{ + // read the PackageHeader + + // Magic + char magic[4]; + + if(pFile->readBlock(magic,4) != 4)return readError(); + if((magic[0] != 'K') || (magic[1] != 'V') || (magic[2] != 'P') || (magic[3] != 'F')) + { + setLastError(__tr2qs("The file specified is not a valid KVIrc package")); + return false; + } + + // Version + kvi_u32_t uVersion; + if(!pFile->load(uVersion))return readError(); + if(uVersion != 0x1) + { + setLastError(__tr2qs("The package has an invalid version number, it might have been created by a newer KVIrc")); + return false; + } + + // Flags + kvi_u32_t uFlags; + if(!pFile->load(uFlags))return readError(); + // we ignore them at the moment + + // read PackageInfo + + // InfoFieldCount + kvi_u32_t uCount; + if(!pFile->load(uCount))return writeError(); + + m_pStringInfoFields->clear(); + m_pBinaryInfoFields->clear(); + + kvi_u32_t uIdx = 0; + while(uIdx < uCount) + { + QString szKey; + if(!pFile->load(szKey))return readError(); + kvi_u32_t uFieldType; + if(!pFile->load(uFieldType))return readError(); + switch(uFieldType) + { + case KVI_PACKAGE_INFOFIELD_TYPE_STRING: + { + QString szValue; + if(!pFile->load(szValue))return readError(); + m_pStringInfoFields->replace(szKey,new QString(szValue)); + } + break; + case KVI_PACKAGE_INFOFIELD_TYPE_BINARYBUFFER: + { + QByteArray * pbValue = new QByteArray(); + if(!pFile->load(*pbValue)) + { + delete pbValue; + return readError(); + } + m_pBinaryInfoFields->replace(szKey,pbValue); + } + break; + default: + setLastError(__tr2qs("Invalid info field: the package is probably corrupt")); + break; + } + uIdx++; + } + + return true; +} + + +bool KviPackageReader::readHeader(const QString &szLocalFileName) +{ + KviFile f(szLocalFileName); + if(!f.openForReading()) + { + setLastError(__tr2qs("Can't open file for reading")); + return false; + } + + return readHeaderInternal(&f,szLocalFileName); +} + +bool KviPackageReader::unpackFile(KviFile * pFile,const QString &szUnpackPath) +{ + // Flags + kvi_u32_t uFlags; + if(!pFile->load(uFlags))return readError(); + +#ifndef COMPILE_ZLIB_SUPPORT + if(uFlags & KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE) + { + setLastError(__tr2qs("The package contains compressed data but this executable does not support compression")); + return false; + } +#endif + + // Path + QString szPath; + if(!pFile->load(szPath))return readError(); + + QString szFileName = szUnpackPath; + KviQString::ensureLastCharIs(szFileName,QChar(KVI_PATH_SEPARATOR_CHAR)); + + szFileName += szPath; + + // no attacks please :) + szFileName.replace(QString("..\\"),QString("")); + szFileName.replace(QString("..//"),QString("")); + + KviFileUtils::adjustFilePath(szFileName); + + int idx = KviQString::findRev(szFileName,QChar(KVI_PATH_SEPARATOR_CHAR)); + if(idx != -1) + { + QString szPrefixPath = szFileName.left(idx); + if(!KviFileUtils::makeDir(szPrefixPath)) + { + setLastError(__tr2qs("Failed to create the target directory")); + return false; + } + } + + KviFile dest(szFileName); + if(!dest.openForWriting()) + { + setLastError(__tr2qs("Failed to open a source file for reading")); + return false; + } + + QString szProgressText; + KviQString::sprintf(szProgressText,__tr2qs("Unpacking file %Q"),&szFileName); + if(!updateProgress(pFile->pos(),szProgressText)) + return false; // aborted + + // Size + kvi_u32_t uSize; + if(!pFile->load(uSize))return readError(); + + + // FilePayload +#ifdef COMPILE_ZLIB_SUPPORT + if(uFlags & KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE) + { + int iRemainingSize = uSize; + unsigned char ibuffer[BUFFER_SIZE]; + unsigned char obuffer[BUFFER_SIZE]; + + int iToRead = iRemainingSize; + if(iToRead > BUFFER_SIZE)iToRead = BUFFER_SIZE; + int iReaded = pFile->readBlock((char *)ibuffer,iToRead); + iRemainingSize -= iReaded; + + z_stream zstr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.next_in = ibuffer; + zstr.avail_in = iReaded; + zstr.next_out = obuffer; + zstr.avail_out = BUFFER_SIZE; + + if(inflateInit(&zstr) != Z_OK) + { + setLastError(__tr2qs("Compression library initialization error")); + return false; + } + + while((iReaded > 0) && (iRemainingSize > 0)) + { + zstr.next_out = obuffer; + zstr.avail_out = BUFFER_SIZE; + + if(inflate(&zstr,Z_NO_FLUSH) != Z_OK) + { + setLastError(__tr2qs("Compression library error")); + return false; + } + + if(zstr.avail_out < BUFFER_SIZE) + { + int iDecompressed = zstr.next_out - obuffer; + if(dest.writeBlock((char *)obuffer,iDecompressed) != iDecompressed) + { + inflateEnd(&zstr); + return writeError(); + } + } + + if(zstr.avail_in < BUFFER_SIZE) + { + int iDataToRead = BUFFER_SIZE - zstr.avail_in; + if(iDataToRead < BUFFER_SIZE) + { + if(ibuffer != zstr.next_in) + { + // hum, there is still some data in the buffer to be readed + // and it is not at the beginning...move it to the beginning of ibuffer + memmove(ibuffer,zstr.next_in,zstr.avail_in); + } + } + + if(iDataToRead > iRemainingSize) + iDataToRead = iRemainingSize; + + iReaded = pFile->readBlock((char *)(ibuffer + zstr.avail_in),iDataToRead); + if(iReaded < 0) + { + inflateEnd(&zstr); + return readError(); + } + + iRemainingSize -= iReaded; + zstr.avail_in += iReaded; + zstr.next_in = ibuffer; + + if((zstr.total_in % 2000000) == 0) + { + QString szTmp; + KviQString::sprintf(szTmp,QString(" (%d of %d bytes)"),zstr.total_in,uSize); + QString szPrg = szProgressText + szTmp; + if(!updateProgress(pFile->pos(),szPrg)) + return false; // aborted + } + } + } + + // flush pending output + zstr.next_out = obuffer; + zstr.avail_out = BUFFER_SIZE; + + int ret; + + do { + ret = inflate(&zstr,Z_FINISH); + + if((ret == Z_OK) || (ret == Z_STREAM_END) || (ret == Z_BUF_ERROR)) + { + if(zstr.avail_out < BUFFER_SIZE) + { + int iDecompressed = zstr.next_out - obuffer; + if(dest.writeBlock((char *)obuffer,iDecompressed) != iDecompressed) + { + inflateEnd(&zstr); + return writeError(); + } + } /* else { THIS HAPPENS FOR ZERO SIZE FILES + debug("hum.... internal, rEWq (ret = %d) (avail_out = %d)",ret,zstr.avail_out); + + inflateEnd(&zstr); + setLastError(__tr2qs("Compression library internal error")); + return false; + } */ + zstr.next_out = obuffer; + zstr.avail_out = BUFFER_SIZE; + } + + } while((ret == Z_OK) || (ret == Z_BUF_ERROR)); + + inflateEnd(&zstr); + + if(ret != Z_STREAM_END) + { + setLastError(__tr2qs("Error in compressed file stream")); + return false; + } + + } else { +#endif + unsigned char buffer[BUFFER_SIZE]; + int iTotalFileSize = 0; + int iRemainingData = uSize; + int iToRead = iRemainingData; + if(iToRead > BUFFER_SIZE)iToRead = BUFFER_SIZE; + int iReaded = 1; + + while((iReaded > 0) && (iToRead > 0)) + { + iReaded = pFile->readBlock((char *)buffer,iToRead); + if(iReaded > 0) + { + iTotalFileSize += iReaded; + iRemainingData -= iReaded; + + if((iTotalFileSize % 3000000) == 0) + { + QString szTmp; + KviQString::sprintf(szTmp,QString(" (%d of %d bytes)"),iTotalFileSize,uSize); + QString szPrg = szProgressText + szTmp; + if(!updateProgress(pFile->pos(),szPrg)) + return false; // aborted + } + + if(dest.writeBlock((char *)buffer,iReaded) != iReaded) + return writeError(); + } + + int iToRead = iRemainingData; + if(iToRead > BUFFER_SIZE)iToRead = BUFFER_SIZE; + } +#ifdef COMPILE_ZLIB_SUPPORT + } +#endif + dest.close(); + + return true; +} + +bool KviPackageReader::getStringInfoField(const QString &szName,QString &szBuffer) +{ + QString * pVal = m_pStringInfoFields->find(szName); + if(!pVal)return false; + szBuffer = *pVal; + return true; +} + +bool KviPackageReader::unpack(const QString &szLocalFileName,const QString &szUnpackPath,kvi_u32_t uUnpackFlags) +{ + bool bRet = unpackInternal(szLocalFileName,szUnpackPath,uUnpackFlags); + hideProgressDialog(); + return bRet; +} + +bool KviPackageReader::unpackInternal(const QString &szLocalFileName,const QString &szUnpackPath,kvi_u32_t uUnpackFlags) +{ + + KviFile f(szLocalFileName); + if(!f.openForReading()) + { + setLastError(__tr2qs("Can't open file for reading")); + return false; + } + + kvi_file_offset_t size = f.size(); + + if(!(uUnpackFlags & NoProgressDialog)) + { + showProgressDialog(__tr2qs("Reading package..."),size); + updateProgress(0,__tr2qs("Reading package header")); + } + + + if(!readHeaderInternal(&f,szLocalFileName)) + return false; + + if(!updateProgress(f.pos(),__tr2qs("Reading package data"))) + return false; // aborted + + while(!f.atEnd()) + { + // DataFieldType + kvi_u32_t uDataFieldType; + if(!f.load(uDataFieldType))return readError(); + // DataFieldLen + kvi_u32_t uDataFieldLen; + if(!f.load(uDataFieldLen))return readError(); + + switch(uDataFieldType) + { + case KVI_PACKAGE_DATAFIELD_TYPE_FILE: + if(!unpackFile(&f,szUnpackPath)) + return false; + break; + default: + setLastError(__tr2qs("Invalid data field: the package is probably corrupt")); + return false; + break; + } + + } + + return true; +} + + + |