1
//=============================================================================
3
// File : KviPackageReader.cpp
4
// Creation date : Tue 26 Dec 2006 05:33:33 by Szymon Stefanek
6
// This file is part of the KVIrc IRC Client distribution
7
// Copyright (C) 2006-2010 Szymon Stefanek <pragma at kvirc dot net>
9
// This program is FREE software. You can redistribute it and/or
10
// modify it under the terms of the GNU General Public License
11
// as published by the Free Software Foundation; either version 2
12
// of the License, or (at your opinion) any later version.
14
// This program is distributed in the HOPE that it will be USEFUL,
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
// See the GNU General Public License for more details.
19
// You should have received a copy of the GNU General Public License
20
// along with this program. If not, write to the Free Software Foundation,
21
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
//=============================================================================
25
#include "KviPackageReader.h"
28
#include "KviFileUtils.h"
29
#include "KviLocale.h"
31
#include "kvi_inttypes.h"
33
#ifdef COMPILE_ZLIB_SUPPORT
38
// See KviPackageIOEngine.cpp for the description of the KVIrc package file
42
KviPackageReader::KviPackageReader()
43
: KviPackageIOEngine()
47
KviPackageReader::~KviPackageReader()
51
bool KviPackageReader::readHeaderInternal(KviFile * pFile,const QString &)
53
// read the PackageHeader
58
if(pFile->read(magic,4) != 4)
61
if((magic[0] != 'K') || (magic[1] != 'V') || (magic[2] != 'P') || (magic[3] != 'F'))
63
setLastError(__tr2qs("The file specified is not a valid KVIrc package"));
69
if(!pFile->load(uVersion))
73
setLastError(__tr2qs("The package has an invalid version number, it might have been created by a newer KVIrc"));
79
if(!pFile->load(uFlags))
81
// we ignore them at the moment
87
if(!pFile->load(uCount))
90
stringInfoFields()->clear();
91
binaryInfoFields()->clear();
97
if(!pFile->load(szKey))
100
if(!pFile->load(uFieldType))
104
case KVI_PACKAGE_INFOFIELD_TYPE_STRING:
107
if(!pFile->load(szValue))return readError();
108
stringInfoFields()->replace(szKey,new QString(szValue));
111
case KVI_PACKAGE_INFOFIELD_TYPE_BINARYBUFFER:
113
QByteArray * pbValue = new QByteArray();
114
if(!pFile->load(*pbValue))
119
binaryInfoFields()->replace(szKey,pbValue);
123
setLastError(__tr2qs("Invalid info field: the package is probably corrupt"));
132
bool KviPackageReader::readHeader(const QString &szLocalFileName)
134
KviFile f(szLocalFileName);
135
if(!f.open(QFile::ReadOnly))
137
setLastError(__tr2qs("Can't open file for reading"));
141
return readHeaderInternal(&f,szLocalFileName);
144
#define BUFFER_SIZE 32768
147
bool KviPackageReader::unpackFile(KviFile * pFile,const QString &szUnpackPath)
151
if(!pFile->load(uFlags))
154
#ifndef COMPILE_ZLIB_SUPPORT
155
if(uFlags & KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE)
157
setLastError(__tr2qs("The package contains compressed data but this executable does not support compression"));
164
if(!pFile->load(szPath))
167
QString szFileName = szUnpackPath;
168
KviQString::ensureLastCharIs(szFileName,QChar(KVI_PATH_SEPARATOR_CHAR));
170
szFileName += szPath;
172
// no attacks please :)
173
szFileName.replace(QString("..\\"),QString(""));
174
szFileName.replace(QString("..//"),QString(""));
176
KviFileUtils::adjustFilePath(szFileName);
178
int idx = szFileName.lastIndexOf(QChar(KVI_PATH_SEPARATOR_CHAR),0,Qt::CaseSensitive);
181
QString szPrefixPath = szFileName.left(idx);
182
if(!KviFileUtils::makeDir(szPrefixPath))
184
setLastError(__tr2qs("Failed to create the target directory"));
189
KviFile dest(szFileName);
190
if(!dest.open(QFile::WriteOnly | QFile::Truncate))
192
setLastError(__tr2qs("Failed to open a source file for reading"));
196
QString szProgressText = QString(__tr2qs("Unpacking file %1")).arg(szFileName);
197
if(!updateProgress(pFile->pos(),szProgressText))
198
return false; // aborted
202
if(!pFile->load(uSize))
206
#ifdef COMPILE_ZLIB_SUPPORT
207
if(uFlags & KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE)
209
// qDebug ("loading compressed data");
210
int iRemainingSize = uSize;
211
unsigned char ibuffer[BUFFER_SIZE];
212
unsigned char obuffer[BUFFER_SIZE];
214
int iToRead = iRemainingSize;
215
if(iToRead > BUFFER_SIZE)iToRead = BUFFER_SIZE;
216
int iReaded = pFile->read((char *)ibuffer,iToRead);
217
iRemainingSize -= iReaded;
220
zstr.zalloc = Z_NULL;
222
zstr.opaque = Z_NULL;
223
zstr.next_in = ibuffer;
224
zstr.avail_in = iReaded;
225
zstr.next_out = obuffer;
226
zstr.avail_out = BUFFER_SIZE;
228
if(inflateInit(&zstr) != Z_OK)
230
setLastError(__tr2qs("Compression library initialization error"));
234
while((iReaded > 0) && (iRemainingSize > 0))
236
zstr.next_out = obuffer;
237
zstr.avail_out = BUFFER_SIZE;
239
if(inflate(&zstr,Z_NO_FLUSH) != Z_OK)
241
setLastError(__tr2qs("Compression library error"));
245
if(zstr.avail_out < BUFFER_SIZE)
247
int iDecompressed = zstr.next_out - obuffer;
248
if(dest.write((char *)obuffer,iDecompressed) != iDecompressed)
255
if(zstr.avail_in < BUFFER_SIZE)
257
int iDataToRead = BUFFER_SIZE - zstr.avail_in;
258
if(iDataToRead < BUFFER_SIZE)
260
if(ibuffer != zstr.next_in)
262
// hum, there is still some data in the buffer to be readed
263
// and it is not at the beginning...move it to the beginning of ibuffer
264
memmove(ibuffer,zstr.next_in,zstr.avail_in);
268
if(iDataToRead > iRemainingSize)
269
iDataToRead = iRemainingSize;
271
iReaded = pFile->read((char *)(ibuffer + zstr.avail_in),iDataToRead);
278
iRemainingSize -= iReaded;
279
zstr.avail_in += iReaded;
280
zstr.next_in = ibuffer;
282
if((zstr.total_in % 2000000) == 0)
284
QString szTmp = QString(" (%1 of %2 bytes)").arg(zstr.total_in,uSize);
285
QString szPrg = szProgressText + szTmp;
286
if(!updateProgress(pFile->pos(),szPrg))
287
return false; // aborted
292
// flush pending output
293
zstr.next_out = obuffer;
294
zstr.avail_out = BUFFER_SIZE;
299
ret = inflate(&zstr,Z_FINISH);
301
if((ret == Z_OK) || (ret == Z_STREAM_END) || (ret == Z_BUF_ERROR))
303
if(zstr.avail_out < BUFFER_SIZE)
305
int iDecompressed = zstr.next_out - obuffer;
306
if(dest.write((char *)obuffer,iDecompressed) != iDecompressed)
312
//THIS HAPPENS FOR ZERO SIZE FILES
313
qDebug("hum.... internal, rEWq (ret = %d) (avail_out = %d)",ret,zstr.avail_out);
316
setLastError(__tr2qs("Compression library internal error"));
319
zstr.next_out = obuffer;
320
zstr.avail_out = BUFFER_SIZE;
323
} while((ret == Z_OK) || (ret == Z_BUF_ERROR));
327
if(ret != Z_STREAM_END)
329
setLastError(__tr2qs("Error in compressed file stream"));
335
// qDebug("Load uncompressed data");
336
unsigned char buffer[BUFFER_SIZE];
337
int iTotalFileSize = 0;
338
int iRemainingData = uSize;
339
int iToRead = iRemainingData;
340
if(iToRead > BUFFER_SIZE)iToRead = BUFFER_SIZE;
342
// qDebug("iReaded %i and iToRead %i and index %i",iReaded,iToRead,pFile->pos());
343
while((iReaded > 0) && (iToRead > 0))
345
iReaded = pFile->read((char *)buffer,iToRead);
348
iTotalFileSize += iReaded;
349
iRemainingData -= iReaded;
351
if((iTotalFileSize % 3000000) == 0)
353
QString szTmp = QString(" (%1 of %2 bytes)").arg(iTotalFileSize).arg(uSize);
354
QString szPrg = szProgressText + szTmp;
355
if(!updateProgress(pFile->pos(),szPrg))
356
return false; // aborted
358
// qDebug("write file with size %i and name %s",iReaded,dest.fileName().toUtf8().data());
359
if(dest.write((char *)buffer,iReaded) != iReaded)
362
//qDebug("Remains %i", iRemainingData);
363
iToRead = iRemainingData;
364
if(iToRead > BUFFER_SIZE)iToRead = BUFFER_SIZE;
366
// qDebug("finish to read %i and index %i",iReaded,pFile->pos());
367
#ifdef COMPILE_ZLIB_SUPPORT
375
bool KviPackageReader::getStringInfoField(const QString &szName,QString &szBuffer)
377
QString * pVal = stringInfoFields()->find(szName);
384
bool KviPackageReader::unpack(const QString &szLocalFileName,const QString &szUnpackPath,kvi_u32_t uUnpackFlags)
386
bool bRet = unpackInternal(szLocalFileName,szUnpackPath,uUnpackFlags);
387
hideProgressDialog();
391
bool KviPackageReader::unpackInternal(const QString &szLocalFileName,const QString &szUnpackPath,kvi_u32_t uUnpackFlags)
394
KviFile f(szLocalFileName);
395
if(!f.open(QFile::ReadOnly))
397
setLastError(__tr2qs("Can't open file for reading"));
401
kvi_file_offset_t size = f.size();
403
if(!(uUnpackFlags & NoProgressDialog))
405
showProgressDialog(__tr2qs("Reading package..."),size);
406
updateProgress(0,__tr2qs("Reading package header"));
409
if(!readHeaderInternal(&f,szLocalFileName))
412
if(!updateProgress(f.pos(),__tr2qs("Reading package data")))
413
return false; // aborted
418
kvi_u32_t uDataFieldType;
419
if(!f.load(uDataFieldType))return readError();
421
kvi_u32_t uDataFieldLen;
422
if(!f.load(uDataFieldLen))return readError();
424
switch(uDataFieldType)
426
case KVI_PACKAGE_DATAFIELD_TYPE_FILE:
427
if(!unpackFile(&f,szUnpackPath))
431
setLastError(__tr2qs("Invalid data field: the package is probably corrupt"));