diff options
Diffstat (limited to 'src/kvilib/file')
-rw-r--r-- | src/kvilib/file/Makefile.am | 5 | ||||
-rw-r--r-- | src/kvilib/file/kvi_file.cpp | 256 | ||||
-rw-r--r-- | src/kvilib/file/kvi_file.h | 120 | ||||
-rw-r--r-- | src/kvilib/file/kvi_fileutils.cpp | 505 | ||||
-rw-r--r-- | src/kvilib/file/kvi_fileutils.h | 112 | ||||
-rw-r--r-- | src/kvilib/file/kvi_packagefile.cpp | 1028 | ||||
-rw-r--r-- | src/kvilib/file/kvi_packagefile.h | 142 |
7 files changed, 2168 insertions, 0 deletions
diff --git a/src/kvilib/file/Makefile.am b/src/kvilib/file/Makefile.am new file mode 100644 index 00000000..c84487eb --- /dev/null +++ b/src/kvilib/file/Makefile.am @@ -0,0 +1,5 @@ +############################################################################### +# KVirc IRC client Makefile - 16.12.98 Szymon Stefanek <stefanek@tin.it> +############################################################################### + +EXTRA_DIST = *.cpp *.h diff --git a/src/kvilib/file/kvi_file.cpp b/src/kvilib/file/kvi_file.cpp new file mode 100644 index 00000000..8ab1e739 --- /dev/null +++ b/src/kvilib/file/kvi_file.cpp @@ -0,0 +1,256 @@ +//============================================================================= +// +// File : kvi_file.cpp +// Creation date : Mon Dec 17 2001 00:04:12 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2001-2007 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_file.h" +#include "kvi_byteorder.h" + + +KviFile::KviFile() +: QFile() +{ +} + +KviFile::KviFile(const QString &name) +: QFile(name) +{ +} + +KviFile::~KviFile() +{ +} + +bool KviFile::openForReading() +{ +#ifdef COMPILE_USE_QT4 + return open(QFile::ReadOnly); +#else + return open(IO_ReadOnly); +#endif +} + +bool KviFile::openForWriting(bool bAppend) +{ +#ifdef COMPILE_USE_QT4 + return open(QFile::WriteOnly | (bAppend ? QFile::Append : QFile::Truncate)); +#else + return open(IO_WriteOnly | (bAppend ? IO_Append : IO_Truncate)); +#endif +} + + +bool KviFile::save(const QByteArray &bData) +{ + if(!save((kvi_u32_t)(bData.size())))return false; + return (writeBlock(bData.data(),bData.size()) == ((int)(bData.size()))); +} + +bool KviFile::load(QByteArray &bData) +{ + kvi_u32_t iLen; + if(!load(iLen))return false; + bData.resize(iLen); // it is automatically null terminated in Qt 4.x... BLEAH :D + if(readBlock((char *)(bData.data()),iLen) != iLen)return false; + return true; +} + +#ifndef COMPILE_USE_QT4 + +bool KviFile::save(const KviQCString &szData) +{ + if(!save((kvi_u32_t)(szData.length())))return false; + return (writeBlock(szData.data(),szData.length()) == ((int)(szData.length()))); +} + +bool KviFile::load(KviQCString &szData) +{ + kvi_u32_t iLen; + if(!load(iLen))return false; + szData.resize(iLen + 1); // this would allocate one extra byte with Qt 4.x... + if(readBlock((char *)(szData.data()),iLen) != iLen)return false; + *(szData.data() + iLen) = 0; + return true; +} + +#endif + + +bool KviFile::save(const QString &szData) +{ + KviQCString c = KviQString::toUtf8(szData); + if(!save((kvi_u32_t)(c.length())))return false; + return (writeBlock(c.data(),c.length()) == ((int)(c.length()))); +} + +bool KviFile::load(QString &szData) +{ + kvi_u32_t iLen; + if(!load(iLen))return false; + KviQCString tmp; + tmp.resize(iLen + 1); + if(readBlock((char *)(tmp.data()),iLen) != iLen)return false; + *(tmp.data() + iLen) = 0; + szData = QString::fromUtf8(tmp.data()); + return true; +} + +bool KviFile::save(const KviStr &szData) +{ + if(!save((kvi_u32_t)(szData.len())))return false; + return (writeBlock(szData.ptr(),szData.len()) == (int) szData.len()); +} + +bool KviFile::load(KviStr &szData) +{ + kvi_u32_t iLen; + if(!load(iLen))return false; + szData.setLength(iLen); + return (readBlock((char *)(szData.ptr()),iLen) == iLen); +} + +bool KviFile::save(kvi_u32_t t) +{ +#ifndef LOCAL_CPU_LITTLE_ENDIAN + t = kvi_localCpuToLittleEndian32(t); +#endif + return (writeBlock((const char *)(&t),sizeof(kvi_u32_t)) == sizeof(kvi_u32_t)); +} + +bool KviFile::load(kvi_u32_t &t) +{ + if(!(readBlock((char *)(&t),sizeof(kvi_u32_t)) == sizeof(kvi_u32_t)))return false; +#ifndef LOCAL_CPU_LITTLE_ENDIAN + t = kvi_littleEndianToLocalCpu32(t); +#endif + return true; +} + +bool KviFile::save(kvi_u64_t t) +{ +#ifndef LOCAL_CPU_LITTLE_ENDIAN + t = kvi_localCpuToLittleEndian64(t); +#endif + return (writeBlock((const char *)(&t),sizeof(kvi_u64_t)) == sizeof(kvi_u64_t)); +} + +bool KviFile::load(kvi_u64_t &t) +{ + if(!(readBlock((char *)(&t),sizeof(kvi_u32_t)) == sizeof(kvi_u32_t)))return false; +#ifndef LOCAL_CPU_LITTLE_ENDIAN + t = kvi_littleEndianToLocalCpu32(t); +#endif + return true; +} + + +bool KviFile::save(kvi_u16_t t) +{ +#ifndef LOCAL_CPU_LITTLE_ENDIAN + t = kvi_localCpuToLittleEndian16(t); +#endif + return (writeBlock((const char *)(&t),sizeof(kvi_u16_t)) == sizeof(kvi_u16_t)); +} + +bool KviFile::load(kvi_u16_t &t) +{ + if(!(readBlock((char *)(&t),sizeof(kvi_u16_t)) == sizeof(kvi_u16_t)))return false; +#ifndef LOCAL_CPU_LITTLE_ENDIAN + t = kvi_littleEndianToLocalCpu16(t); +#endif + return true; +} + +bool KviFile::save(kvi_u8_t t) +{ + return (writeBlock((const char *)(&t),sizeof(kvi_u8_t)) == sizeof(kvi_u8_t)); +} + +bool KviFile::load(kvi_u8_t &t) +{ + return (readBlock((char *)(&t),sizeof(kvi_u8_t)) == sizeof(kvi_u8_t)); +} + + +bool KviFile::save(KviPointerList<KviStr> * pData) +{ + if(!save((int)(pData->count())))return false; + for(KviStr * s = pData->first();s;s = pData->next()) + { + if(!save(*s))return false; + } + return true; +} + +bool KviFile::load(KviPointerList<KviStr> * pData) +{ + pData->clear(); + int iCount; + if(!load(iCount))return false; + for(int i=0;i<iCount;i++) + { + KviStr * s = new KviStr(); + if(!load(*s)) + { + delete s; + s = 0; + return false; + } + pData->append(s); + } + return true; +} + +bool KviFile::skipFirst(char t,unsigned int maxdist) +{ + while(maxdist > 0) + { + char c; + if(!getChar(&c))return false; + if(((char)c) == t)return true; + maxdist--; + } + return false; +} + +bool KviFile::skipFirst(const KviStr &t,unsigned int maxdist) +{ + char * ptr = t.ptr(); + while(maxdist > 0) + { + char c; + if(!getChar(&c))return false; + if(c == *ptr) + { + ptr++; + if(!*ptr)return true; + } else { + ptr = t.ptr(); + } + maxdist--; + } + return false; +} + diff --git a/src/kvilib/file/kvi_file.h b/src/kvilib/file/kvi_file.h new file mode 100644 index 00000000..188a9dad --- /dev/null +++ b/src/kvilib/file/kvi_file.h @@ -0,0 +1,120 @@ +#ifndef _KVI_FILE_H_ +#define _KVI_FILE_H_ + +//============================================================================= +// +// File : kvi_file.h +// Creation date : Mon Dec 17 2001 00:05:04 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2001-2007 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. +// +//============================================================================= + +#include "kvi_settings.h" +#include "kvi_heapobject.h" +#include "kvi_qstring.h" +#include "kvi_string.h" +#include "kvi_pointerlist.h" +#include "kvi_inttypes.h" +#include "kvi_qcstring.h" + +#include <qfile.h> +#include <time.h> + +#ifdef COMPILE_USE_QT4 + #define kvi_file_offset_t qlonglong +#else + #define kvi_file_offset_t QFile::Offset +#endif + + +class KVILIB_API KviFile : public QFile, public KviHeapObject +{ +public: + KviFile(); + KviFile(const QString &name); + ~KviFile(); +public: + // Wrappers portable across Qt 3.x and Qt 4.x + bool openForReading(); + bool openForWriting(bool bAppend = false); + +#ifndef COMPILE_USE_QT4 + // Functions present in Qt 4.x but not Qt 3.x + bool putChar(char c){ return putch(c) != -1; }; + bool ungetChar(char c){ return ungetch(c) != -1; }; + bool getChar(char * c){ *c = getch(); return *c != -1; }; + bool seek(kvi_file_offset_t o){ return at(o); }; + kvi_file_offset_t pos(){ return at(); }; +#endif + +#ifdef COMPILE_USE_QT4 + // Missing functions in Qt 4.x + quint64 writeBlock(const char * data,quint64 uLen){ return write(data,uLen); }; + quint64 readBlock(char * data,quint64 uLen){ return read(data,uLen); }; +#endif + + // This stuff loads and saves LITTLE ENDIAN DATA! + bool save(kvi_u64_t t); + bool load(kvi_u64_t &t); + + bool save(kvi_i64_t t){ return save((kvi_u64_t)t); }; + bool load(kvi_i64_t &t){ return load((kvi_u64_t &)t); }; + + bool save(kvi_u32_t t); + bool load(kvi_u32_t &t); + + bool save(kvi_i32_t t){ return save((kvi_u32_t)t); }; + bool load(kvi_i32_t &t){ return load((kvi_u32_t &)t); }; + + bool save(kvi_u16_t t); + bool load(kvi_u16_t &t); + + bool save(kvi_i16_t t){ return save((kvi_u16_t)t); }; + bool load(kvi_i16_t &t){ return load((kvi_u16_t &)t); }; + + bool save(kvi_u8_t t); + bool load(kvi_u8_t &t); + + bool save(kvi_i8_t t){ return save((kvi_u8_t)t); }; + bool load(kvi_i8_t &t){ return load((kvi_u8_t &)t); };; + + bool save(const KviStr &szData); + bool load(KviStr &szData); + +#ifndef COMPILE_USE_QT4 + // Under Qt 4.x these collide with QByteArray + bool save(const KviQCString &szData); + bool load(KviQCString &szData); +#endif + + bool save(const QByteArray &bData); + bool load(QByteArray &bData); + + bool save(const QString &szData); + bool load(QString &szData); + + bool skipFirst(char t,unsigned int maxdist = 0xffffffff); + bool skipFirst(const KviStr &t,unsigned int maxdist = 0xffffffff); + + bool save(KviPointerList<KviStr> * pData); + bool load(KviPointerList<KviStr> * pData); +}; + + +#endif //_KVI_FILE_H_ diff --git a/src/kvilib/file/kvi_fileutils.cpp b/src/kvilib/file/kvi_fileutils.cpp new file mode 100644 index 00000000..648d9125 --- /dev/null +++ b/src/kvilib/file/kvi_fileutils.cpp @@ -0,0 +1,505 @@ +//============================================================================= +// +// File : kvi_fileutils.cpp +// Creation date : Fri Dec 25 1998 18:26:48 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1998-2007 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__ + + +#define _KVI_FILEUTLIS_CPP_ +#include "kvi_fileutils.h" +#include "kvi_qstring.h" +#include "kvi_file.h" +#include "kvi_malloc.h" + +#include <qdir.h> +#include <qfileinfo.h> +#include <qglobal.h> +#include <qtextcodec.h> +#include <qtextstream.h> + + +namespace KviFileUtils +{ + /* + WORKING CODE BUT UNUSED FOR NOW + bool readLine(QFile * f,QString &szBuffer,bool bClearBuffer) + { + // FIXME: Should this assume UTF8 encoding ? + char tmp_buf[256]; + int cur_len = 0; + //char *cur_ptr = tmp_buf; + if(bClearBuffer)szBuffer = ""; + int ch = f->getch(); + + while((ch != -1)&&(ch != '\n')&&(ch != 0)) + { + tmp_buf[cur_len] = ch; + cur_len++; + if(cur_len > 255) + { + if(tmp_buf[255] == '\r')cur_len--; //Ignore CR... + int lastlen = szBuffer.length(); + szBuffer.setLength(lastlen + cur_len); + QChar *p1 = szBuffer.unicode() + lastlen; + char * p2 = tmp_buf; + for(int i=0;i<cur_len;i++)*p1++ = *p2++; + cur_len = 0; + } + ch = f->getch(); + } + if(ch == 0) + { + debug("Warning : %s is not an ascii file",f->name().latin1()); + } + if(cur_len > 0) + { + if(tmp_buf[cur_len - 1] == '\r')cur_len--; //Ignore CR... + int lastlen = szBuffer.length(); + szBuffer.setLength(lastlen + cur_len); + QChar *p1 = szBuffer.unicode() + lastlen; + char * p2 = tmp_buf; + for(int i=0;i<cur_len;i++)*p1++ = *p2++; + } + return (ch == '\n'); //more data to read else a broken file or EOF + } + + bool loadFileStripCR(const QString &szPath,QString &szBuffer) + { + QFile f(szPath); + if(!f.open(IO_ReadOnly))return false; + szBuffer = ""; + while(readLine(&f,szBuffer,false)) + { + szBuffer.append('\n'); // readLine returned true...last char was a newline + } + // readLine returned false , no ending newline encountered + return true; + } + */ + + bool makeDir(const QString &szPath) + { + QDir d; + QString dir = KviQString::trimmed(szPath); + adjustFilePath(dir); + QString createdDir; + +#ifdef COMPILE_ON_WINDOWS +#ifdef COMPILE_USE_QT4 + int idx = dir.indexOf(':'); +#else + int idx = dir.find(':'); +#endif + if(idx == 1) + { + createdDir = dir.left(2); + dir.remove(0,2); + } +#endif + + KviQString::stripLeft(dir,KVI_PATH_SEPARATOR_CHAR); + while(!dir.isEmpty()) + { + createdDir += KVI_PATH_SEPARATOR; + createdDir += KviQString::getToken(dir,KVI_PATH_SEPARATOR_CHAR); + if(!directoryExists(createdDir)) + { + if(!d.mkdir(createdDir)) + { + debug("Can't create directory %s",KviQString::toUtf8(createdDir).data()); + return false; + } + } + KviQString::stripLeft(dir,KVI_PATH_SEPARATOR_CHAR); + } + return true; + } + + bool makeDir(const char* path) + { + QString szPath=QString::fromUtf8(path); + return makeDir(szPath); + } + + bool renameFile(const QString &szSrc,const QString &szDst) + { + QDir d; + return d.rename(szSrc,szDst); + } + + bool renameFile(const char* path,const char* path2) + { + QString szPath=QString::fromUtf8(path); + QString szPath2=QString::fromUtf8(path2); + return renameFile(szPath,szPath2); + } + + bool copyFile(const QString &szSrc,const QString &szDst) + { + KviFile f1(szSrc); + if(!f1.openForReading())return false; + KviFile f2(szDst); + if(!f2.openForWriting()) + { + f1.close(); + return false; + } + char buffer[1024]; + while(!f1.atEnd()) + { + int len = f1.readBlock(buffer,1024); + if(len <= 0) + { + f1.close(); + f2.close(); + return false; //"serious error" + } + f2.writeBlock(buffer,len); + } + f1.close(); + f2.close(); + return true; + } + + bool copyFile(const char* path,const char* path2) + { + QString szPath=QString::fromUtf8(path); + QString szPath2=QString::fromUtf8(path2); + return copyFile(szPath,szPath2); + } + + bool loadFile(const QString &szPath,QString &szBuffer,bool bUtf8) + { + KviFile f(szPath); + if(!f.openForReading())return false; + if(bUtf8) + { + QByteArray ba = f.readAll(); + szBuffer = QString::fromUtf8(ba.data(),ba.size()); + //debug("BUFFERLEN: %d",szBuffer.length()); + } else { + szBuffer = QString(f.readAll()); + } + return true; + } + + bool loadFile(const char* path,QString &szBuffer,bool bUtf8) + { + QString szPath=QString::fromUtf8(path); + return loadFile(szPath,szBuffer,bUtf8); + } + + void adjustFilePath(QString &szPath) + { +#ifdef COMPILE_ON_WINDOWS + szPath.replace('/',"\\"); +#ifdef COMPILE_USE_QT4 + szPath.replace("\\\\","\\"); +#else + while(szPath.find("\\\\") != -1)szPath.replace("\\\\","\\"); +#endif + // FIXME: Use the default drive here ? + if(szPath.startsWith("\\"))szPath.prepend("C:"); +#else + szPath.replace('\\',"/"); +#ifdef COMPILE_USE_QT4 + szPath.replace("//","/"); +#else + while(KviQString::find(szPath,"//") != -1)szPath.replace("//","/"); +#endif + // deal with windows paths + if((szPath.length() > 2) && (szPath.at(0) != QChar('/'))) + { + if((szPath.at(1) == QChar(':')) && (szPath.at(2) == QChar('/'))) + { + szPath.remove(0,2); + } + } +#ifdef COMPILE_USE_QT4 + szPath=QDir::cleanPath(szPath); +#else + szPath=QDir::cleanDirPath(szPath); +#endif +#endif + + } + + bool directoryExists(const QString &szPath) + { + QFileInfo f(szPath); + return (f.exists() && f.isDir()); + } + + bool directoryExists(const char* path) + { + QString szPath=QString::fromUtf8(path); + QFileInfo f(szPath); + return (f.exists() && f.isDir()); + } + + bool fileExists(const QString &szPath) + { + QFileInfo f(szPath); + return (f.exists() && f.isFile()); + } + + bool fileExists(const char* path) + { + QString szPath=QString::fromUtf8(path); + return fileExists(szPath); + } + + bool removeFile(const QString &szPath) + { + QDir d; + return d.remove(szPath); + } + + bool removeFile(const char* path) + { + QString szPath=QString::fromUtf8(path); + return removeFile(szPath); + } + + bool removeDir(const QString &szPath) + { + QDir d; + return d.rmdir(szPath); + } + + bool removeDir(const char* path) + { + QString szPath=QString::fromUtf8(path); + return removeDir(szPath); + } + + bool deleteDir(const QString &szPath) + { + QDir d(szPath); + QStringList sl = d.entryList(QDir::Dirs); + QStringList::Iterator it; + for(it=sl.begin();it != sl.end();it++) + { + QString szSubdir = *it; + if(!(KviQString::equalCS(szSubdir,"..") || KviQString::equalCS(szSubdir,"."))) + { + QString szSubPath = szPath; + KviQString::ensureLastCharIs(szSubPath,QChar(KVI_PATH_SEPARATOR_CHAR)); + szSubPath += szSubdir; + if(!KviFileUtils::deleteDir(szSubPath)) + return false; + } + } + + sl = d.entryList(QDir::Files); + for(it=sl.begin();it != sl.end();it++) + { + QString szFilePath = szPath; + KviQString::ensureLastCharIs(szFilePath,QChar(KVI_PATH_SEPARATOR_CHAR)); + szFilePath += *it; + if(!KviFileUtils::removeFile(szFilePath)) + return false; + } + + return KviFileUtils::removeDir(szPath); + } + + bool writeFile(const QString &szPath,const QString &szData,bool bAppend) + { + KviFile f(szPath); + if(!f.openForWriting(bAppend))return false; + KviQCString szTmp = KviQString::toUtf8(szData); + if(!szTmp.data())return true; + if(f.writeBlock(szTmp.data(),szTmp.length()) != ((int)(szTmp.length())))return false; + return true; + } + + bool writeFile(const char* path,const QString &szData,bool bAppend) + { + QString szPath=QString::fromUtf8(path); + return writeFile(szPath,szData,bAppend); + } + + bool writeFileLocal8Bit(const QString &szPath,const QString &szData,bool bAppend) + { + KviFile f(szPath); + if(!f.openForWriting(bAppend))return false; + KviQCString szTmp = QTextCodec::codecForLocale()->fromUnicode(szData); + if(!szTmp.data())return true; + if(f.writeBlock(szTmp.data(),szTmp.length()) != ((int)(szTmp.length())))return false; + return true; + } + + bool writeFileLocal8Bit(const char* path,const QString &szData,bool bAppend) + { + QString szPath=QString::fromUtf8(path); + return writeFileLocal8Bit(szPath,szData,bAppend); + } + + bool readFile(const QString &szPath,QString &szBuffer,unsigned int uMaxSize) + { + KviFile f(szPath); + if(!f.openForReading())return false; + if(f.size() < 1) + { + szBuffer = ""; + f.close(); + return true; + } + if(f.size() > uMaxSize)return false; + char * buf = new char[f.size() + 1]; + if(f.readBlock(buf,f.size()) != ((long int)f.size())) + { + delete buf; + buf = 0; + return false; + } + buf[f.size()] = '\0'; + szBuffer = QString::fromUtf8(buf); + delete[] buf; + return true; + } + + bool readFile(const char* path,QString &szBuffer,unsigned int uMaxSize) + { + QString szPath=QString::fromUtf8(path); + return readFile(szPath,szBuffer,uMaxSize); + } + + + QString extractFileName(const QString &szFileNameWithPath) + { + return QFileInfo(szFileNameWithPath).fileName(); + } + + QString extractFilePath(const QString &szFileNameWithPath) + { + return QFileInfo(szFileNameWithPath).dirPath(true); + } + + bool readLine(QFile * f,QString &szBuffer,bool bUtf8) + { + QTextStream stream(f); + stream.setEncoding(bUtf8 ? QTextStream::UnicodeUTF8 : QTextStream::Locale); + szBuffer=stream.readLine(); + return !szBuffer.isNull(); + } + + bool readLines(QFile * f,QStringList &buffer,int iStartLine, int iCount, bool bUtf8) + { + QTextStream stream( f ); + stream.setEncoding(bUtf8 ? QTextStream::UnicodeUTF8 : QTextStream::Locale); + for(int i=0;i<iStartLine;i++) + stream.readLine(); + + if(iCount>0) + { + for(; (iCount>0 && !stream.atEnd()) ; iCount-- ) + buffer.append(stream.readLine()); + } else { + while(!stream.atEnd()) { + buffer.append(stream.readLine()); + } + } + return buffer.count()!= 0; + } + + bool isReadable(const QString &szFname) + { + QFileInfo f(szFname); + return (f.exists() && f.isFile() && f.isReadable()); + } + + bool isReadable(const char* path) + { + QString szPath=QString::fromUtf8(path); + return isReadable(szPath); + } + + bool isAbsolutePath(const QString &szPath) + { + QFileInfo f(szPath); + return !f.isRelative(); + } +}; + +static char hexchars[16] = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' }; + + +void kvi_encodeFileName(KviStr & path) +{ + QString szPath(path.ptr()); + kvi_encodeFileName(szPath); + path=szPath; +} + +void kvi_encodeFileName(QString & path) +{ + QString src(path); + path=""; + for(int i=0;i<src.length();i++) + { + QChar cur=src[i]; + if( ! (cur.isLetter() || cur.isDigit() || cur==' ' || cur=='_' || cur=='.' || cur=='#' || cur=='%') ) + { + if(cur.row()!=0) + { + path+='%'; + path+=hexchars[cur.row() >> 4]; + path+=hexchars[cur.row() & 15]; + } + path+='%'; + path+=hexchars[cur.cell() >> 4]; + path+=hexchars[cur.cell() & 15]; + } else if (cur=='%') + { + path+="%%"; + } else { + path+=cur; + } + } +} + +//================ kvi_isAbsolutePath ===============// + +bool kvi_isAbsolutePath(const char *path) +{ + if(*path == '/')return true; + if(isalpha(*path)) + { + if((*(path + 1)) == ':')return true; + } + return false; +} + +//=================== kvi_readLine =====================// + +bool kvi_readLine(QFile *f,KviStr &str) +{ + QTextStream stream(f); + QString szBuff=stream.readLine(); + str=szBuff; + return szBuff.isNull() ? 1 : 0; +} + + diff --git a/src/kvilib/file/kvi_fileutils.h b/src/kvilib/file/kvi_fileutils.h new file mode 100644 index 00000000..dcead8ae --- /dev/null +++ b/src/kvilib/file/kvi_fileutils.h @@ -0,0 +1,112 @@ +#ifndef _KVI_FILEUTILS_H_ +#define _KVI_FILEUTILS_H_ + +// +// File : kvi_fileutils.h +// Creation date : Fri Dec 25 1998 18:27:04 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2000 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. +// + +#include "kvi_settings.h" +#include "kvi_string.h" + +#include <qfile.h> +#include <qstringlist.h> + +#include <time.h> + + +#ifdef COMPILE_ON_WINDOWS + #define KVI_PATH_SEPARATOR "\\" + #define KVI_PATH_SEPARATOR_CHAR '\\' +#else + #define KVI_PATH_SEPARATOR "/" + #define KVI_PATH_SEPARATOR_CHAR '/' +#endif + +// #warning "Add kvi_trashFile(const char * path) ? - is it needed in the whole app" +// #warning "or should it be availible only for dirbrowser module ?" +namespace KviFileUtils +{ + //extern KVILIB_API bool readLine(QFile * f,QString &szBuffer,bool bClearBuffer = true); + //extern KVILIB_API bool loadFileStripCR(const QString &szPath,QString &szBuffer); + + // loads the file at szPath to szBuffer eventually converting from utf8 + extern KVILIB_API bool loadFile(const QString &szPath,QString &szBuffer,bool bUtf8 = true); + extern KVILIB_API bool loadFile(const char* szPath,QString &szBuffer,bool bUtf8 = true); + // adjusts the file path to the current platform + extern KVILIB_API void adjustFilePath(QString &szPath); + // returns true if szPath points to an existing directory + extern KVILIB_API bool directoryExists(const QString &szPath); + extern KVILIB_API bool directoryExists(const char* path); + // returns true if szPath points to an existing file + extern KVILIB_API bool fileExists(const QString &szPath); + extern KVILIB_API bool fileExists(const char* szPath); + // removes a file + extern KVILIB_API bool removeFile(const QString &szPath); + extern KVILIB_API bool removeFile(const char* path); + // removes a dir (must be empty) + extern KVILIB_API bool removeDir(const QString &szPath); + extern KVILIB_API bool removeDir(const char* path); + // removes a dir recursively + extern KVILIB_API bool deleteDir(const QString &szPath); + // writes a complete file (utf8 version) + extern KVILIB_API bool writeFile(const QString &szPath,const QString &szData,bool bAppend = false); + extern KVILIB_API bool writeFile(const char* path,const QString &szData,bool bAppend = false); + // writes a complete file (local 8 bit version) + extern KVILIB_API bool writeFileLocal8Bit(const QString &szPath,const QString &szData,bool bAppend = false); + extern KVILIB_API bool writeFileLocal8Bit(const char* path,const QString &szData,bool bAppend = false); + // reads a complete file and puts it in the string szBuffer, if the file is smaller than uMaxSize bytes + extern KVILIB_API bool readFile(const QString &szPath,QString &szBuffer,unsigned int uMaxSize = 65535); + extern KVILIB_API bool readFile(const char* path,QString &szBuffer,unsigned int uMaxSize = 65535); + // extracts the filename from a complete path (strips leading path) + extern KVILIB_API QString extractFileName(const QString &szFileNameWithPath); + + extern KVILIB_API QString extractFilePath(const QString &szFileNameWithPath); + // cp -f + extern KVILIB_API bool copyFile(const QString &szSrc,const QString &szDst); + extern KVILIB_API bool copyFile(const char* src,const char* dst); + // mv + extern KVILIB_API bool renameFile(const QString &szSrc,const QString &szDst); + extern KVILIB_API bool renameFile(const char* src,const char* dst); + // mkdir + extern KVILIB_API bool makeDir(const QString &szPath); + extern KVILIB_API bool makeDir(const char* path); + // reads a text line, returns false if EOF is reached + extern KVILIB_API bool readLine(QFile * f,QString &szBuffer,bool bUtf8 = true); + extern KVILIB_API bool readLines(QFile * f,QStringList &buffer,int iStartLine = 0, int iCount = -1, bool bUtf8 = true); + extern KVILIB_API bool isReadable(const QString &szFname); + extern KVILIB_API bool isAbsolutePath(const QString &szPath); +}; + +// ALL THIS STUFF BELOW SHOULD DIE: IF YOU SEE IT, REPLACE WITH THE FUNCTIONS IN THE NAMESPACE ABOVE + +// Returns true if the path begins with '/' +KVILIB_API extern bool kvi_isAbsolutePath(const char *path); +// Translates ANY string into a valid filename (with no path!) +// There is NO way to come back to the original string +// the algo is one-way only +KVILIB_API extern void kvi_encodeFileName(KviStr & path); +KVILIB_API extern void kvi_encodeFileName(QString & path); + +// Reads a single line from the file and returns false if EOF was encountered. +KVILIB_API extern bool kvi_readLine(QFile *f,KviStr &str); +// Removes a file + +#endif //_KVI_FILEUTILS_H_INCLUDED_ 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; +} + + + diff --git a/src/kvilib/file/kvi_packagefile.h b/src/kvilib/file/kvi_packagefile.h new file mode 100644 index 00000000..3e330554 --- /dev/null +++ b/src/kvilib/file/kvi_packagefile.h @@ -0,0 +1,142 @@ +#ifndef _KVI_PACKAGEFILE_H_ +#define _KVI_PACKAGEFILE_H_ +//============================================================================= +// +// File : kvi_packagefile.h +// 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. +// +//============================================================================= + +#include "kvi_settings.h" +#include "kvi_qstring.h" +#include "kvi_pointerhashtable.h" +#include "kvi_qcstring.h" // QByteArray anyway +#include <qobject.h> +#include "kvi_pointerlist.h" + +class KviFile; +class QProgressDialog; +class QLabel; +class QFileInfo; + +// +// This class is used for creating KVIrc package files. +// You simply instantiate it, add some info fields, add some files and then call pack(). +// + +class KVILIB_API KviPackageIOEngine +{ +public: + KviPackageIOEngine(); + virtual ~KviPackageIOEngine(); +protected: + QString m_szLastError; + KviPointerHashTable<QString,QString> * m_pStringInfoFields; + KviPointerHashTable<QString,QByteArray> * m_pBinaryInfoFields; + QProgressDialog * m_pProgressDialog; + QLabel * m_pProgressDialogLabel; +public: + const QString & lastError(){ return m_szLastError; }; + void setLastError(const QString &szLastError){ m_szLastError = szLastError; }; + KviPointerHashTable<QString,QString> * stringInfoFields(){ return m_pStringInfoFields; }; + KviPointerHashTable<QString,QByteArray> * binaryInfoFields(){ return m_pBinaryInfoFields; }; +protected: + void showProgressDialog(const QString &szCaption,int iTotalSteps); + void hideProgressDialog(); + bool updateProgress(int iProgress,const QString &szLabel); + bool writeError(); + bool readError(); +}; + +#define KVI_PACKAGE_INFOFIELD_TYPE_STRING 1 +#define KVI_PACKAGE_INFOFIELD_TYPE_BINARYBUFFER 2 + +#define KVI_PACKAGE_DATAFIELD_TYPE_FILE 1 + +#define KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE 1 + +class KVILIB_API KviPackageWriter : public KviPackageIOEngine +{ +public: + KviPackageWriter(); + virtual ~KviPackageWriter(); +protected: + + class DataField + { + public: + kvi_u32_t m_uType; + // output length of the field + kvi_u32_t m_uWrittenFieldLength; + // data fields for the File DataFieldType + bool m_bFileAllowCompression; + QString m_szFileLocalName; + QString m_szFileTargetName; + }; + + KviPointerList<DataField> * m_pDataFields; + int m_iCurrentProgress; +public: + // Adds a file to the package. The file must be specified as absolute local + // path and as target path relative to the KVIrc local directory. + // ... more ? + enum AddFileFlags { + NoCompression = 1, + FollowSymLinks = 2 + }; + bool addFile(const QString &szLocalFileName,const QString &szTargetFileName,kvi_u32_t uAddFileFlags = 0); + bool addDirectory(const QString &szLocalDirectoryName,const QString &szTargetDirectoryPrefix,kvi_u32_t uAddFileFlags = 0); + // Adds an info field as a name=value pair + void addInfoField(const QString &szName,const QString &szValue); + void addInfoField(const QString &szName,QByteArray * pArray); + // Attempts to pack everything and store it as the specified file. + // There is no mandatory extension but you *should* use KVI_FILEEXTENSION_THEMEPACKAGE for themes + // and KVI_FILEEXTENSION_ADDONPACKAGE for addons. See kvi_fileextension.h + enum PackFlags { + NoProgressDialog = 1 + }; + bool pack(const QString &szFileName,kvi_u32_t uPackFlags = 0); +private: + bool packInternal(const QString &szFileName,kvi_u32_t uPackFlags = 0); + bool packFile(KviFile * pFile,DataField * pDataField); + bool addFileInternal(const QFileInfo * fi,const QString &szLocalFileName,const QString &szTargetFileName,kvi_u32_t uAddFileFlags = 0); +}; + +class KVILIB_API KviPackageReader : public KviPackageIOEngine +{ +public: + KviPackageReader(); + virtual ~KviPackageReader(); +public: + bool readHeader(const QString &szLocalFileName); + enum UnpackFlags { + NoProgressDialog = 1 + }; + bool getStringInfoField(const QString &szName,QString &szBuffer); + bool unpack(const QString &szLocalFileName,const QString &szUnpackPath,kvi_u32_t uUnpackFlags = 0); +private: + bool unpackInternal(const QString &szLocalFileName,const QString &szUnpackPath,kvi_u32_t uUnpackFlags = 0); + bool unpackFile(KviFile * pFile,const QString &szUnpackPath); + bool readHeaderInternal(KviFile * pFile,const QString &szLocalFileName); +}; + + + +#endif //!_KVI_PACKAGEFILE_H_ |