Initial implementation for zip-file contents scanning.
authorMikko Keinänen <mikko.keinanen@gmail.com>
Sat, 5 Jun 2010 21:43:29 +0000 (00:43 +0300)
committerMikko Keinänen <mikko.keinanen@gmail.com>
Sat, 5 Jun 2010 21:43:29 +0000 (00:43 +0300)
src/dialogs/mediaimagepathmaindialog.cpp
src/emufront.pro
src/utils/OSDaB-Zip/unzip.cpp [new file with mode: 0644]
src/utils/OSDaB-Zip/unzip.h [new file with mode: 0644]
src/utils/fileutil.cpp [new file with mode: 0644]
src/utils/fileutil.h [new file with mode: 0644]
src/utils/ziputil.cpp [new file with mode: 0644]
src/utils/ziputil.h [new file with mode: 0644]

index e7f1836..707594e 100644 (file)
@@ -20,7 +20,9 @@
 #include <QtGui>
 
 #include "../dataobjects/filepathobject.h"
+#include "../dataobjects/emufrontfileobject.h"
 #include "../db/dbfilepath.h"
+#include "../utils/fileutil.h"
 #include "mediaimagepathmaindialog.h"
 #include "mediaimagepathdialog.h"
 
@@ -54,6 +56,7 @@ void MediaImagePathMainDialog::beginScanFilePath()
 {
     qDebug() << "Scan file path requested";
     QModelIndex index = objectList->currentIndex();
+    FileUtil fileUtil(this);
     if (!index.isValid()) return;
     EmuFrontObject *ob = dbManager->getDataObjectFromModel(&index);
     if (!ob) return;
@@ -61,7 +64,9 @@ void MediaImagePathMainDialog::beginScanFilePath()
     try
     {
         QStringList l;
-        scanFilePath(fpo->getName(), l); // TODO implement file filters
+        l << "*.zip"; // TODO set filters in a global constant class
+
+        QList<EmuFrontFileObject*> files = fileUtil.scanFilePath(fpo, l);
     }
     catch (QString s)
     {
index 6693392..11d9382 100644 (file)
@@ -32,7 +32,10 @@ HEADERS += mainwindow.h \
     dialogs/mediaimagepathdialog.h \
     dialogs/mediaimagepathmaindialog.h \
     dialogs/dataobjecteditdialog.h \
-    db/dbfilepath.h
+    db/dbfilepath.h \
+    utils/ziputil.h \
+    utils/fileutil.h \
+    utils/OSDaB-Zip/unzip.h
 SOURCES += main.cpp \
     mainwindow.cpp \
     db/databasemanager.cpp \
@@ -54,4 +57,7 @@ SOURCES += main.cpp \
     dialogs/mediaimagepathdialog.cpp \
     dialogs/mediaimagepathmaindialog.cpp \
     dialogs/dataobjecteditdialog.cpp \
-    db/dbfilepath.cpp
+    db/dbfilepath.cpp \
+    utils/ziputil.cpp \
+    utils/fileutil.cpp \
+    utils/OSDaB-Zip/unzip.cpp
diff --git a/src/utils/OSDaB-Zip/unzip.cpp b/src/utils/OSDaB-Zip/unzip.cpp
new file mode 100644 (file)
index 0000000..1eb896c
--- /dev/null
@@ -0,0 +1,1361 @@
+/****************************************************************************\r
+** Filename: unzip.cpp\r
+** Last updated [dd/mm/yyyy]: 07/09/2008\r
+**\r
+** pkzip 2.0 decompression.\r
+**\r
+** Some of the code has been inspired by other open source projects,\r
+** (mainly Info-Zip and Gilles Vollant's minizip).\r
+** Compression and decompression actually uses the zlib library.\r
+**\r
+** Copyright (C) 2007-2008 Angius Fabrizio. All rights reserved.\r
+**\r
+** This file is part of the OSDaB project (http://osdab.sourceforge.net/).\r
+**\r
+** This file may be distributed and/or modified under the terms of the\r
+** GNU General Public License version 2 as published by the Free Software\r
+** Foundation and appearing in the file LICENSE.GPL included in the\r
+** packaging of this file.\r
+**\r
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE\r
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.\r
+**\r
+** See the file LICENSE.GPL that came with this software distribution or\r
+** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.\r
+**\r
+**********************************************************************/\r
+\r
+#include "unzip.h"\r
+#include "unzip_p.h"\r
+#include "zipentry_p.h"\r
+\r
+#include <QString>\r
+#include <QStringList>\r
+#include <QDir>\r
+#include <QFile>\r
+#include <QCoreApplication>\r
+\r
+// You can remove this #include if you replace the qDebug() statements.\r
+#include <QtDebug>\r
+\r
+/*!\r
+       \class UnZip unzip.h\r
+\r
+       \brief PKZip 2.0 file decompression.\r
+       Compatibility with later versions is not ensured as they may use\r
+       unsupported compression algorithms.\r
+       Versions after 2.7 may have an incompatible header format and thus be\r
+       completely incompatible.\r
+*/\r
+\r
+/*! \enum UnZip::ErrorCode The result of a decompression operation.\r
+       \value UnZip::Ok No error occurred.\r
+       \value UnZip::ZlibInit Failed to init or load the zlib library.\r
+       \value UnZip::ZlibError The zlib library returned some error.\r
+       \value UnZip::OpenFailed Unable to create or open a device.\r
+       \value UnZip::PartiallyCorrupted Corrupted zip archive - some files could be extracted.\r
+       \value UnZip::Corrupted Corrupted or invalid zip archive.\r
+       \value UnZip::WrongPassword Unable to decrypt a password protected file.\r
+       \value UnZip::NoOpenArchive No archive has been opened yet.\r
+       \value UnZip::FileNotFound Unable to find the requested file in the archive.\r
+       \value UnZip::ReadFailed Reading of a file failed.\r
+       \value UnZip::WriteFailed Writing of a file failed.\r
+       \value UnZip::SeekFailed Seek failed.\r
+       \value UnZip::CreateDirFailed Could not create a directory.\r
+       \value UnZip::InvalidDevice A null device has been passed as parameter.\r
+       \value UnZip::InvalidArchive This is not a valid (or supported) ZIP archive.\r
+       \value UnZip::HeaderConsistencyError Local header record info does not match with the central directory record info. The archive may be corrupted.\r
+\r
+       \value UnZip::Skip Internal use only.\r
+       \value UnZip::SkipAll Internal use only.\r
+*/\r
+\r
+/*! \enum UnZip::ExtractionOptions Some options for the file extraction methods.\r
+       \value UnZip::ExtractPaths Default. Does not ignore the path of the zipped files.\r
+       \value UnZip::SkipPaths Default. Ignores the path of the zipped files and extracts them all to the same root directory.\r
+*/\r
+\r
+//! Local header size (excluding signature, excluding variable length fields)\r
+#define UNZIP_LOCAL_HEADER_SIZE 26\r
+//! Central Directory file entry size (excluding signature, excluding variable length fields)\r
+#define UNZIP_CD_ENTRY_SIZE_NS 42\r
+//! Data descriptor size (excluding signature)\r
+#define UNZIP_DD_SIZE 12\r
+//! End Of Central Directory size (including signature, excluding variable length fields)\r
+#define UNZIP_EOCD_SIZE 22\r
+//! Local header entry encryption header size\r
+#define UNZIP_LOCAL_ENC_HEADER_SIZE 12\r
+\r
+// Some offsets inside a CD record (excluding signature)\r
+#define UNZIP_CD_OFF_VERSION 0\r
+#define UNZIP_CD_OFF_GPFLAG 4\r
+#define UNZIP_CD_OFF_CMETHOD 6\r
+#define UNZIP_CD_OFF_MODT 8\r
+#define UNZIP_CD_OFF_MODD 10\r
+#define UNZIP_CD_OFF_CRC32 12\r
+#define UNZIP_CD_OFF_CSIZE 16\r
+#define UNZIP_CD_OFF_USIZE 20\r
+#define UNZIP_CD_OFF_NAMELEN 24\r
+#define UNZIP_CD_OFF_XLEN 26\r
+#define UNZIP_CD_OFF_COMMLEN 28\r
+#define UNZIP_CD_OFF_LHOFFSET 38\r
+\r
+// Some offsets inside a local header record (excluding signature)\r
+#define UNZIP_LH_OFF_VERSION 0\r
+#define UNZIP_LH_OFF_GPFLAG 2\r
+#define UNZIP_LH_OFF_CMETHOD 4\r
+#define UNZIP_LH_OFF_MODT 6\r
+#define UNZIP_LH_OFF_MODD 8\r
+#define UNZIP_LH_OFF_CRC32 10\r
+#define UNZIP_LH_OFF_CSIZE 14\r
+#define UNZIP_LH_OFF_USIZE 18\r
+#define UNZIP_LH_OFF_NAMELEN 22\r
+#define UNZIP_LH_OFF_XLEN 24\r
+\r
+// Some offsets inside a data descriptor record (excluding signature)\r
+#define UNZIP_DD_OFF_CRC32 0\r
+#define UNZIP_DD_OFF_CSIZE 4\r
+#define UNZIP_DD_OFF_USIZE 8\r
+\r
+// Some offsets inside a EOCD record\r
+#define UNZIP_EOCD_OFF_ENTRIES 6\r
+#define UNZIP_EOCD_OFF_CDOFF 12\r
+#define UNZIP_EOCD_OFF_COMMLEN 16\r
+\r
+/*!\r
+       Max version handled by this API.\r
+       0x1B = 2.7 --> full compatibility only up to version 2.0 (0x14)\r
+       versions from 2.1 to 2.7 may use unsupported compression methods\r
+       versions after 2.7 may have an incompatible header format\r
+*/\r
+#define UNZIP_VERSION 0x1B\r
+//! Full compatibility granted until this version\r
+#define UNZIP_VERSION_STRICT 0x14\r
+\r
+//! CRC32 routine\r
+#define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8)\r
+\r
+//! Checks if some file has been already extracted.\r
+#define UNZIP_CHECK_FOR_VALID_DATA \\r
+       {\\r
+               if (headers != 0)\\r
+               {\\r
+                       qDebug() << "Corrupted zip archive. Some files might be extracted.";\\r
+                       ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted;\\r
+                       break;\\r
+               }\\r
+               else\\r
+               {\\r
+                       delete device;\\r
+                       device = 0;\\r
+                       qDebug() << "Corrupted or invalid zip archive";\\r
+                       ec = UnZip::Corrupted;\\r
+                       break;\\r
+               }\\r
+       }\r
+\r
+\r
+/************************************************************************\r
+ Public interface\r
+*************************************************************************/\r
+\r
+/*!\r
+       Creates a new Zip file decompressor.\r
+*/\r
+UnZip::UnZip()\r
+{\r
+       d = new UnzipPrivate;\r
+}\r
+\r
+/*!\r
+       Closes any open archive and releases used resources.\r
+*/\r
+UnZip::~UnZip()\r
+{\r
+       closeArchive();\r
+       delete d;\r
+}\r
+\r
+/*!\r
+       Returns true if there is an open archive.\r
+*/\r
+bool UnZip::isOpen() const\r
+{\r
+       return d->device != 0;\r
+}\r
+\r
+/*!\r
+       Opens a zip archive and reads the files list. Closes any previously opened archive.\r
+*/\r
+UnZip::ErrorCode UnZip::openArchive(const QString& filename)\r
+{\r
+       QFile* file = new QFile(filename);\r
+\r
+       if (!file->exists()) {\r
+               delete file;\r
+               return UnZip::FileNotFound;\r
+       }\r
+\r
+       if (!file->open(QIODevice::ReadOnly)) {\r
+               delete file;\r
+               return UnZip::OpenFailed;\r
+       }\r
+\r
+       return openArchive(file);\r
+}\r
+\r
+/*!\r
+       Opens a zip archive and reads the entries list.\r
+       Closes any previously opened archive.\r
+       \warning The class takes ownership of the device so don't delete it!\r
+*/\r
+UnZip::ErrorCode UnZip::openArchive(QIODevice* device)\r
+{\r
+       if (device == 0)\r
+       {\r
+               qDebug() << "Invalid device.";\r
+               return UnZip::InvalidDevice;\r
+       }\r
+\r
+       return d->openArchive(device);\r
+}\r
+\r
+/*!\r
+       Closes the archive and releases all the used resources (like cached passwords).\r
+*/\r
+void UnZip::closeArchive()\r
+{\r
+       d->closeArchive();\r
+}\r
+\r
+QString UnZip::archiveComment() const\r
+{\r
+       if (d->device == 0)\r
+               return QString();\r
+       return d->comment;\r
+}\r
+\r
+/*!\r
+       Returns a locale translated error string for a given error code.\r
+*/\r
+QString UnZip::formatError(UnZip::ErrorCode c) const\r
+{\r
+       switch (c)\r
+       {\r
+       case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break;\r
+       case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break;\r
+       case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break;\r
+       case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break;\r
+       case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break;\r
+       case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break;\r
+       case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break;\r
+       case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break;\r
+       case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break;\r
+       case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break;\r
+       case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break;\r
+       case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break;\r
+       case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break;\r
+       case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break;\r
+       case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break;\r
+       case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break;\r
+       default: ;\r
+       }\r
+\r
+       return QCoreApplication::translate("UnZip", "Unknown error.");\r
+}\r
+\r
+/*!\r
+       Returns true if the archive contains a file with the given path and name.\r
+*/\r
+bool UnZip::contains(const QString& file) const\r
+{\r
+       if (d->headers == 0)\r
+               return false;\r
+\r
+       return d->headers->contains(file);\r
+}\r
+\r
+/*!\r
+       Returns complete paths of files and directories in this archive.\r
+*/\r
+QStringList UnZip::fileList() const\r
+{\r
+       return d->headers == 0 ? QStringList() : d->headers->keys();\r
+}\r
+\r
+/*!\r
+       Returns information for each (correctly parsed) entry of this archive.\r
+*/\r
+QList<UnZip::ZipEntry> UnZip::entryList() const\r
+{\r
+    QList<UnZip::ZipEntry> list;\r
+\r
+       if (d->headers != 0)\r
+       {\r
+               for (QMap<QString,ZipEntryP*>::ConstIterator it = d->headers->constBegin(); it != d->headers->constEnd(); ++it)\r
+               {\r
+                       const ZipEntryP* entry = it.value();\r
+                       Q_ASSERT(entry != 0);\r
+\r
+                       ZipEntry z;\r
+\r
+                       z.filename = it.key();\r
+                       if (!entry->comment.isEmpty())\r
+                               z.comment = entry->comment;\r
+                       z.compressedSize = entry->szComp;\r
+                       z.uncompressedSize = entry->szUncomp;\r
+                       z.crc32 = entry->crc;\r
+                       z.lastModified = d->convertDateTime(entry->modDate, entry->modTime);\r
+\r
+                       z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression;\r
+                       z.type = z.filename.endsWith("/") ? Directory : File;\r
+\r
+                       z.encrypted = entry->isEncrypted();\r
+\r
+                       list.append(z);\r
+               }\r
+       }\r
+\r
+       return list;\r
+}\r
+\r
+/*!\r
+       Extracts the whole archive to a directory.\r
+*/\r
+UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options)\r
+{\r
+       return extractAll(QDir(dirname), options);\r
+}\r
+\r
+/*!\r
+       Extracts the whole archive to a directory.\r
+*/\r
+UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options)\r
+{\r
+       // this should only happen if we didn't call openArchive() yet\r
+       if (d->device == 0)\r
+               return NoOpenArchive;\r
+\r
+       if (d->headers == 0)\r
+               return Ok;\r
+\r
+       bool end = false;\r
+       for (QMap<QString,ZipEntryP*>::Iterator itr = d->headers->begin(); itr != d->headers->end(); ++itr)\r
+       {\r
+               ZipEntryP* entry = itr.value();\r
+               Q_ASSERT(entry != 0);\r
+\r
+               if ((entry->isEncrypted()) && d->skipAllEncrypted)\r
+                       continue;\r
+\r
+               switch (d->extractFile(itr.key(), *entry, dir, options))\r
+               {\r
+               case Corrupted:\r
+                       qDebug() << "Removing corrupted entry" << itr.key();\r
+                       d->headers->erase(itr++);\r
+                       if (itr == d->headers->end())\r
+                               end = true;\r
+                       break;\r
+               case CreateDirFailed:\r
+                       break;\r
+               case Skip:\r
+                       break;\r
+               case SkipAll:\r
+                       d->skipAllEncrypted = true;\r
+                       break;\r
+               default:\r
+                       ;\r
+               }\r
+\r
+               if (end)\r
+                       break;\r
+       }\r
+\r
+       return Ok;\r
+}\r
+\r
+/*!\r
+       Extracts a single file to a directory.\r
+*/\r
+UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options)\r
+{\r
+       return extractFile(filename, QDir(dirname), options);\r
+}\r
+\r
+/*!\r
+       Extracts a single file to a directory.\r
+*/\r
+UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options)\r
+{\r
+       QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);\r
+       if (itr != d->headers->end())\r
+       {\r
+               ZipEntryP* entry = itr.value();\r
+               Q_ASSERT(entry != 0);\r
+               return d->extractFile(itr.key(), *entry, dir, options);\r
+       }\r
+\r
+       return FileNotFound;\r
+}\r
+\r
+/*!\r
+       Extracts a single file to a directory.\r
+*/\r
+UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* dev, ExtractionOptions options)\r
+{\r
+       if (dev == 0)\r
+               return InvalidDevice;\r
+\r
+       QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);\r
+       if (itr != d->headers->end()) {\r
+               ZipEntryP* entry = itr.value();\r
+               Q_ASSERT(entry != 0);\r
+               return d->extractFile(itr.key(), *entry, dev, options);\r
+       }\r
+\r
+       return FileNotFound;\r
+}\r
+\r
+/*!\r
+       Extracts a list of files.\r
+       Stops extraction at the first error (but continues if a file does not exist in the archive).\r
+ */\r
+UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options)\r
+{\r
+       QDir dir(dirname);\r
+       ErrorCode ec;\r
+\r
+       for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)\r
+       {\r
+               ec = extractFile(*itr, dir, options);\r
+               if (ec == FileNotFound)\r
+                       continue;\r
+               if (ec != Ok)\r
+                       return ec;\r
+       }\r
+\r
+       return Ok;\r
+}\r
+\r
+/*!\r
+       Extracts a list of files.\r
+       Stops extraction at the first error (but continues if a file does not exist in the archive).\r
+ */\r
+UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options)\r
+{\r
+       ErrorCode ec;\r
+\r
+       for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)\r
+       {\r
+               ec = extractFile(*itr, dir, options);\r
+               if (ec == FileNotFound)\r
+                       continue;\r
+               if (ec != Ok)\r
+                       return ec;\r
+       }\r
+\r
+       return Ok;\r
+}\r
+\r
+/*!\r
+       Remove/replace this method to add your own password retrieval routine.\r
+*/\r
+void UnZip::setPassword(const QString& pwd)\r
+{\r
+       d->password = pwd;\r
+}\r
+\r
+/*!\r
+       ZipEntry constructor - initialize data. Type is set to File.\r
+*/\r
+UnZip::ZipEntry::ZipEntry()\r
+{\r
+       compressedSize = uncompressedSize = crc32 = 0;\r
+       compression = NoCompression;\r
+       type = File;\r
+       encrypted = false;\r
+}\r
+\r
+\r
+/************************************************************************\r
+ Private interface\r
+*************************************************************************/\r
+\r
+//! \internal\r
+UnzipPrivate::UnzipPrivate()\r
+{\r
+       skipAllEncrypted = false;\r
+       headers = 0;\r
+       device = 0;\r
+\r
+       uBuffer = (unsigned char*) buffer1;\r
+       crcTable = (quint32*) get_crc_table();\r
+\r
+       cdOffset = eocdOffset = 0;\r
+       cdEntryCount = 0;\r
+       unsupportedEntryCount = 0;\r
+}\r
+\r
+//! \internal Parses a Zip archive.\r
+UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev)\r
+{\r
+       Q_ASSERT(dev != 0);\r
+\r
+       if (device != 0)\r
+               closeArchive();\r
+\r
+       device = dev;\r
+\r
+       if (!(device->isOpen() || device->open(QIODevice::ReadOnly)))\r
+       {\r
+               delete device;\r
+               device = 0;\r
+\r
+               qDebug() << "Unable to open device for reading";\r
+               return UnZip::OpenFailed;\r
+       }\r
+\r
+       UnZip::ErrorCode ec;\r
+\r
+       ec = seekToCentralDirectory();\r
+       if (ec != UnZip::Ok)\r
+       {\r
+               closeArchive();\r
+               return ec;\r
+       }\r
+\r
+       //! \todo Ignore CD entry count? CD may be corrupted.\r
+       if (cdEntryCount == 0)\r
+       {\r
+               return UnZip::Ok;\r
+       }\r
+\r
+       bool continueParsing = true;\r
+\r
+       while (continueParsing)\r
+       {\r
+               if (device->read(buffer1, 4) != 4)\r
+                       UNZIP_CHECK_FOR_VALID_DATA\r
+\r
+               if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01  && buffer1[3] == 0x02) )\r
+                       break;\r
+\r
+               if ( (ec = parseCentralDirectoryRecord()) != UnZip::Ok )\r
+                       break;\r
+       }\r
+\r
+       if (ec != UnZip::Ok)\r
+               closeArchive();\r
+\r
+       return ec;\r
+}\r
+\r
+/*\r
+       \internal Parses a local header record and makes some consistency check\r
+       with the information stored in the Central Directory record for this entry\r
+       that has been previously parsed.\r
+       \todo Optional consistency check (as a ExtractionOptions flag)\r
+\r
+       local file header signature     4 bytes  (0x04034b50)\r
+       version needed to extract       2 bytes\r
+       general purpose bit flag        2 bytes\r
+       compression method              2 bytes\r
+       last mod file time              2 bytes\r
+       last mod file date              2 bytes\r
+       crc-32                          4 bytes\r
+       compressed size                 4 bytes\r
+       uncompressed size               4 bytes\r
+       file name length                2 bytes\r
+       extra field length              2 bytes\r
+\r
+       file name (variable size)\r
+       extra field (variable size)\r
+*/\r
+UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, ZipEntryP& entry)\r
+{\r
+       if (!device->seek(entry.lhOffset))\r
+               return UnZip::SeekFailed;\r
+\r
+       // Test signature\r
+       if (device->read(buffer1, 4) != 4)\r
+               return UnZip::ReadFailed;\r
+\r
+       if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04))\r
+               return UnZip::InvalidArchive;\r
+\r
+       if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE)\r
+               return UnZip::ReadFailed;\r
+\r
+       /*\r
+               Check 3rd general purpose bit flag.\r
+\r
+               "bit 3: If this bit is set, the fields crc-32, compressed size\r
+               and uncompressed size are set to zero in the local\r
+               header.  The correct values are put in the data descriptor\r
+               immediately following the compressed data."\r
+       */\r
+       bool hasDataDescriptor = entry.hasDataDescriptor();\r
+\r
+       bool checkFailed = false;\r
+\r
+       if (!checkFailed)\r
+               checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD);\r
+       if (!checkFailed)\r
+               checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG];\r
+       if (!checkFailed)\r
+               checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1];\r
+       if (!checkFailed)\r
+               checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT];\r
+       if (!checkFailed)\r
+               checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1];\r
+       if (!checkFailed)\r
+               checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD];\r
+       if (!checkFailed)\r
+               checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1];\r
+       if (!hasDataDescriptor)\r
+       {\r
+               if (!checkFailed)\r
+                       checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32);\r
+               if (!checkFailed)\r
+                       checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE);\r
+               if (!checkFailed)\r
+                       checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE);\r
+       }\r
+\r
+       if (checkFailed)\r
+               return UnZip::HeaderConsistencyError;\r
+\r
+       // Check filename\r
+       quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN);\r
+       if (szName == 0)\r
+               return UnZip::HeaderConsistencyError;\r
+\r
+       if (device->read(buffer2, szName) != szName)\r
+               return UnZip::ReadFailed;\r
+\r
+       QString filename = QString::fromAscii(buffer2, szName);\r
+       if (filename != path)\r
+       {\r
+               qDebug() << "Filename in local header mismatches.";\r
+               return UnZip::HeaderConsistencyError;\r
+       }\r
+\r
+       // Skip extra field\r
+       quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN);\r
+       if (szExtra != 0)\r
+       {\r
+               if (!device->seek(device->pos() + szExtra))\r
+                       return UnZip::SeekFailed;\r
+       }\r
+\r
+       entry.dataOffset = device->pos();\r
+\r
+       if (hasDataDescriptor)\r
+       {\r
+               /*\r
+                       The data descriptor has this OPTIONAL signature: PK\7\8\r
+                       We try to skip the compressed data relying on the size set in the\r
+                       Central Directory record.\r
+               */\r
+               if (!device->seek(device->pos() + entry.szComp))\r
+                       return UnZip::SeekFailed;\r
+\r
+               // Read 4 bytes and check if there is a data descriptor signature\r
+               if (device->read(buffer2, 4) != 4)\r
+                       return UnZip::ReadFailed;\r
+\r
+               bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08;\r
+               if (hasSignature)\r
+               {\r
+                       if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE)\r
+                               return UnZip::ReadFailed;\r
+               }\r
+               else\r
+               {\r
+                       if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4)\r
+                               return UnZip::ReadFailed;\r
+               }\r
+\r
+               // DD: crc, compressed size, uncompressed size\r
+               if (\r
+                       entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) ||\r
+                       entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) ||\r
+                       entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE)\r
+                       )\r
+                       return UnZip::HeaderConsistencyError;\r
+       }\r
+\r
+       return UnZip::Ok;\r
+}\r
+\r
+/*! \internal Attempts to find the start of the central directory record.\r
+\r
+       We seek the file back until we reach the "End Of Central Directory"\r
+       signature PK\5\6.\r
+\r
+       end of central dir signature    4 bytes  (0x06054b50)\r
+       number of this disk             2 bytes\r
+       number of the disk with the\r
+       start of the central directory  2 bytes\r
+       total number of entries in the\r
+       central directory on this disk  2 bytes\r
+       total number of entries in\r
+       the central directory           2 bytes\r
+       size of the central directory   4 bytes\r
+       offset of start of central\r
+       directory with respect to\r
+       the starting disk number        4 bytes\r
+       .ZIP file comment length        2 bytes\r
+       --- SIZE UNTIL HERE: UNZIP_EOCD_SIZE ---\r
+       .ZIP file comment       (variable size)\r
+*/\r
+UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory()\r
+{\r
+       qint64 length = device->size();\r
+       qint64 offset = length - UNZIP_EOCD_SIZE;\r
+\r
+       if (length < UNZIP_EOCD_SIZE)\r
+               return UnZip::InvalidArchive;\r
+\r
+       if (!device->seek( offset ))\r
+               return UnZip::SeekFailed;\r
+\r
+       if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)\r
+               return UnZip::ReadFailed;\r
+\r
+       bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06);\r
+\r
+       if (eocdFound)\r
+       {\r
+               // Zip file has no comment (the only variable length field in the EOCD record)\r
+               eocdOffset = offset;\r
+       }\r
+       else\r
+       {\r
+               qint64 read;\r
+               char* p = 0;\r
+\r
+               offset -= UNZIP_EOCD_SIZE;\r
+\r
+               if (offset <= 0)\r
+                       return UnZip::InvalidArchive;\r
+\r
+               if (!device->seek( offset ))\r
+                       return UnZip::SeekFailed;\r
+\r
+               while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0)\r
+               {\r
+                       if ( (p = strstr(buffer1, "PK\5\6")) != 0)\r
+                       {\r
+                               // Seek to the start of the EOCD record so we can read it fully\r
+                               // Yes... we could simply read the missing bytes and append them to the buffer\r
+                               // but this is far easier so heck it!\r
+                               device->seek( offset + (p - buffer1) );\r
+                               eocdFound = true;\r
+                               eocdOffset = offset + (p - buffer1);\r
+\r
+                               // Read EOCD record\r
+                               if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)\r
+                                       return UnZip::ReadFailed;\r
+\r
+                               break;\r
+                       }\r
+\r
+                       // TODO: This is very slow and only a temporary bug fix. Need some pattern matching algorithm here.\r
+                       offset -= 1 /*UNZIP_EOCD_SIZE*/;\r
+                       if (offset <= 0)\r
+                               return UnZip::InvalidArchive;\r
+\r
+                       if (!device->seek( offset ))\r
+                               return UnZip::SeekFailed;\r
+               }\r
+       }\r
+\r
+       if (!eocdFound)\r
+               return UnZip::InvalidArchive;\r
+\r
+       // Parse EOCD to locate CD offset\r
+       offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4);\r
+\r
+       cdOffset = offset;\r
+\r
+       cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4);\r
+\r
+       quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4);\r
+       if (commentLength != 0)\r
+       {\r
+               QByteArray c = device->read(commentLength);\r
+               if (c.count() != commentLength)\r
+                       return UnZip::ReadFailed;\r
+\r
+               comment = c;\r
+       }\r
+\r
+       // Seek to the start of the CD record\r
+       if (!device->seek( cdOffset ))\r
+               return UnZip::SeekFailed;\r
+\r
+       return UnZip::Ok;\r
+}\r
+\r
+/*!\r
+       \internal Parses a central directory record.\r
+\r
+       Central Directory record structure:\r
+\r
+       [file header 1]\r
+       .\r
+       .\r
+       .\r
+       [file header n]\r
+       [digital signature] // PKZip 6.2 or later only\r
+\r
+       File header:\r
+\r
+       central file header signature   4 bytes  (0x02014b50)\r
+       version made by                 2 bytes\r
+       version needed to extract       2 bytes\r
+       general purpose bit flag        2 bytes\r
+       compression method              2 bytes\r
+       last mod file time              2 bytes\r
+       last mod file date              2 bytes\r
+       crc-32                          4 bytes\r
+       compressed size                 4 bytes\r
+       uncompressed size               4 bytes\r
+       file name length                2 bytes\r
+       extra field length              2 bytes\r
+       file comment length             2 bytes\r
+       disk number start               2 bytes\r
+       internal file attributes        2 bytes\r
+       external file attributes        4 bytes\r
+       relative offset of local header 4 bytes\r
+\r
+       file name (variable size)\r
+       extra field (variable size)\r
+       file comment (variable size)\r
+*/\r
+UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord()\r
+{\r
+       // Read CD record\r
+       if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS)\r
+               return UnZip::ReadFailed;\r
+\r
+       bool skipEntry = false;\r
+\r
+       // Get compression type so we can skip non compatible algorithms\r
+       quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD);\r
+\r
+       // Get variable size fields length so we can skip the whole record\r
+       // if necessary\r
+       quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN);\r
+       quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN);\r
+       quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN);\r
+\r
+       quint32 skipLength = szName + szExtra + szComment;\r
+\r
+       UnZip::ErrorCode ec = UnZip::Ok;\r
+\r
+       if ((compMethod != 0) && (compMethod != 8))\r
+       {\r
+               qDebug() << "Unsupported compression method. Skipping file.";\r
+               skipEntry = true;\r
+       }\r
+\r
+       // Header parsing may be a problem if version is bigger than UNZIP_VERSION\r
+       if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION)\r
+       {\r
+               qDebug() << "Unsupported PKZip version. Skipping file.";\r
+               skipEntry = true;\r
+       }\r
+\r
+       if (!skipEntry && szName == 0)\r
+       {\r
+               qDebug() << "Skipping file with no name.";\r
+               skipEntry = true;\r
+       }\r
+\r
+       if (!skipEntry && device->read(buffer2, szName) != szName)\r
+       {\r
+               ec = UnZip::ReadFailed;\r
+               skipEntry = true;\r
+       }\r
+\r
+       if (skipEntry)\r
+       {\r
+               if (ec == UnZip::Ok)\r
+               {\r
+                       if (!device->seek( device->pos() + skipLength ))\r
+                               ec = UnZip::SeekFailed;\r
+\r
+                       unsupportedEntryCount++;\r
+               }\r
+\r
+               return ec;\r
+       }\r
+\r
+       QString filename = QString::fromAscii(buffer2, szName);\r
+\r
+       ZipEntryP* h = new ZipEntryP;\r
+       h->compMethod = compMethod;\r
+\r
+       h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG];\r
+       h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1];\r
+\r
+       h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT];\r
+       h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1];\r
+\r
+       h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD];\r
+       h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1];\r
+\r
+       h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32);\r
+       h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE);\r
+       h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE);\r
+\r
+       // Skip extra field (if any)\r
+       if (szExtra != 0)\r
+       {\r
+               if (!device->seek( device->pos() + szExtra ))\r
+               {\r
+                       delete h;\r
+                       return UnZip::SeekFailed;\r
+               }\r
+       }\r
+\r
+       // Read comment field (if any)\r
+       if (szComment != 0)\r
+       {\r
+               if (device->read(buffer2, szComment) != szComment)\r
+               {\r
+                       delete h;\r
+                       return UnZip::ReadFailed;\r
+               }\r
+\r
+               h->comment = QString::fromAscii(buffer2, szComment);\r
+       }\r
+\r
+       h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET);\r
+\r
+       if (headers == 0)\r
+               headers = new QMap<QString, ZipEntryP*>();\r
+       headers->insert(filename, h);\r
+\r
+       return UnZip::Ok;\r
+}\r
+\r
+//! \internal Closes the archive and resets the internal status.\r
+void UnzipPrivate::closeArchive()\r
+{\r
+       if (device == 0)\r
+               return;\r
+\r
+       skipAllEncrypted = false;\r
+\r
+       if (headers != 0)\r
+       {\r
+               qDeleteAll(*headers);\r
+               delete headers;\r
+               headers = 0;\r
+       }\r
+\r
+       delete device; device = 0;\r
+\r
+       cdOffset = eocdOffset = 0;\r
+       cdEntryCount = 0;\r
+       unsupportedEntryCount = 0;\r
+\r
+       comment.clear();\r
+}\r
+\r
+//! \internal\r
+UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options)\r
+{\r
+       QString name(path);\r
+       QString dirname;\r
+       QString directory;\r
+\r
+       int pos = name.lastIndexOf('/');\r
+\r
+       // This entry is for a directory\r
+       if (pos == name.length() - 1)\r
+       {\r
+               if (options.testFlag(UnZip::SkipPaths))\r
+                       return UnZip::Ok;\r
+\r
+               directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name));\r
+               if (!createDirectory(directory))\r
+               {\r
+                       qDebug() << QString("Unable to create directory: %1").arg(directory);\r
+                       return UnZip::CreateDirFailed;\r
+               }\r
+\r
+               return UnZip::Ok;\r
+       }\r
+\r
+       // Extract path from entry\r
+       if (pos > 0)\r
+       {\r
+               // get directory part\r
+               dirname = name.left(pos);\r
+               if (options.testFlag(UnZip::SkipPaths))\r
+               {\r
+                       directory = dir.absolutePath();\r
+               }\r
+               else\r
+               {\r
+                       directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname));\r
+                       if (!createDirectory(directory))\r
+                       {\r
+                               qDebug() << QString("Unable to create directory: %1").arg(directory);\r
+                               return UnZip::CreateDirFailed;\r
+                       }\r
+               }\r
+               name = name.right(name.length() - pos - 1);\r
+       } else directory = dir.absolutePath();\r
+\r
+       name = QString("%1/%2").arg(directory).arg(name);\r
+\r
+       QFile outFile(name);\r
+\r
+       if (!outFile.open(QIODevice::WriteOnly))\r
+       {\r
+               qDebug() << QString("Unable to open %1 for writing").arg(name);\r
+               return UnZip::OpenFailed;\r
+       }\r
+\r
+       //! \todo Set creation/last_modified date/time\r
+\r
+       UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options);\r
+\r
+       outFile.close();\r
+\r
+       if (ec != UnZip::Ok)\r
+       {\r
+               if (!outFile.remove())\r
+                       qDebug() << QString("Unable to remove corrupted file: %1").arg(name);\r
+       }\r
+\r
+       return ec;\r
+}\r
+\r
+//! \internal\r
+UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, QIODevice* dev, UnZip::ExtractionOptions options)\r
+{\r
+       Q_UNUSED(options);\r
+       Q_ASSERT(dev != 0);\r
+\r
+       if (!entry.lhEntryChecked)\r
+       {\r
+               UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry);\r
+               entry.lhEntryChecked = true;\r
+\r
+               if (ec != UnZip::Ok)\r
+                       return ec;\r
+       }\r
+\r
+       if (!device->seek(entry.dataOffset))\r
+               return UnZip::SeekFailed;\r
+\r
+       // Encryption keys\r
+       quint32 keys[3];\r
+\r
+       if (entry.isEncrypted())\r
+       {\r
+               UnZip::ErrorCode e = testPassword(keys, path, entry);\r
+               if (e != UnZip::Ok)\r
+               {\r
+                       qDebug() << QString("Unable to decrypt %1").arg(path);\r
+                       return e;\r
+               }//! Encryption header size\r
+               entry.szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE; // remove encryption header size\r
+       }\r
+\r
+       if (entry.szComp == 0)\r
+       {\r
+               if (entry.crc != 0)\r
+                       return UnZip::Corrupted;\r
+\r
+               return UnZip::Ok;\r
+       }\r
+\r
+       uInt rep = entry.szComp / UNZIP_READ_BUFFER;\r
+       uInt rem = entry.szComp % UNZIP_READ_BUFFER;\r
+       uInt cur = 0;\r
+\r
+       // extract data\r
+       qint64 read;\r
+       quint64 tot = 0;\r
+\r
+       quint32 myCRC = crc32(0L, Z_NULL, 0);\r
+\r
+       if (entry.compMethod == 0)\r
+       {\r
+               while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 )\r
+               {\r
+                       if (entry.isEncrypted())\r
+                               decryptBytes(keys, buffer1, read);\r
+\r
+                       myCRC = crc32(myCRC, uBuffer, read);\r
+\r
+                       if (dev->write(buffer1, read) != read)\r
+                               return UnZip::WriteFailed;\r
+\r
+                       cur++;\r
+                       tot += read;\r
+\r
+                       if (tot == entry.szComp)\r
+                               break;\r
+               }\r
+\r
+               if (read < 0)\r
+                       return UnZip::ReadFailed;\r
+       }\r
+       else if (entry.compMethod == 8)\r
+       {\r
+               /* Allocate inflate state */\r
+               z_stream zstr;\r
+               zstr.zalloc = Z_NULL;\r
+               zstr.zfree = Z_NULL;\r
+               zstr.opaque = Z_NULL;\r
+               zstr.next_in = Z_NULL;\r
+               zstr.avail_in = 0;\r
+\r
+               int zret;\r
+\r
+               // Use inflateInit2 with negative windowBits to get raw decompression\r
+               if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK )\r
+                       return UnZip::ZlibError;\r
+\r
+               int szDecomp;\r
+\r
+               // Decompress until deflate stream ends or end of file\r
+               do\r
+               {\r
+                       read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem);\r
+                       if (read == 0)\r
+                               break;\r
+                       if (read < 0)\r
+                       {\r
+                               (void)inflateEnd(&zstr);\r
+                               return UnZip::ReadFailed;\r
+                       }\r
+\r
+                       if (entry.isEncrypted())\r
+                               decryptBytes(keys, buffer1, read);\r
+\r
+                       cur++;\r
+                       tot += read;\r
+\r
+                       zstr.avail_in = (uInt) read;\r
+                       zstr.next_in = (Bytef*) buffer1;\r
+\r
+\r
+                       // Run inflate() on input until output buffer not full\r
+                       do {\r
+                               zstr.avail_out = UNZIP_READ_BUFFER;\r
+                               zstr.next_out = (Bytef*) buffer2;;\r
+\r
+                               zret = inflate(&zstr, Z_NO_FLUSH);\r
+\r
+                               switch (zret) {\r
+                                       case Z_NEED_DICT:\r
+                                       case Z_DATA_ERROR:\r
+                                       case Z_MEM_ERROR:\r
+                                               inflateEnd(&zstr);\r
+                                               return UnZip::WriteFailed;\r
+                                       default:\r
+                                               ;\r
+                               }\r
+\r
+                               szDecomp = UNZIP_READ_BUFFER - zstr.avail_out;\r
+                               if (dev->write(buffer2, szDecomp) != szDecomp)\r
+                               {\r
+                                       inflateEnd(&zstr);\r
+                                       return UnZip::ZlibError;\r
+                               }\r
+\r
+                               myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp);\r
+\r
+                       } while (zstr.avail_out == 0);\r
+\r
+               }\r
+               while (zret != Z_STREAM_END);\r
+\r
+               inflateEnd(&zstr);\r
+       }\r
+\r
+       if (myCRC != entry.crc)\r
+               return UnZip::Corrupted;\r
+\r
+       return UnZip::Ok;\r
+}\r
+\r
+//! \internal Creates a new directory and all the needed parent directories.\r
+bool UnzipPrivate::createDirectory(const QString& path)\r
+{\r
+       QDir d(path);\r
+       if (!d.exists())\r
+       {\r
+               int sep = path.lastIndexOf("/");\r
+               if (sep <= 0) return true;\r
+\r
+               if (!createDirectory(path.left(sep)))\r
+                       return false;\r
+\r
+               if (!d.mkdir(path))\r
+               {\r
+                       qDebug() << QString("Unable to create directory: %1").arg(path);\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+/*!\r
+       \internal Reads an quint32 (4 bytes) from a byte array starting at given offset.\r
+*/\r
+quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const\r
+{\r
+       quint32 res = (quint32) data[offset];\r
+       res |= (((quint32)data[offset+1]) << 8);\r
+       res |= (((quint32)data[offset+2]) << 16);\r
+       res |= (((quint32)data[offset+3]) << 24);\r
+\r
+       return res;\r
+}\r
+\r
+/*!\r
+       \internal Reads an quint64 (8 bytes) from a byte array starting at given offset.\r
+*/\r
+quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const\r
+{\r
+       quint64 res = (quint64) data[offset];\r
+       res |= (((quint64)data[offset+1]) << 8);\r
+       res |= (((quint64)data[offset+2]) << 16);\r
+       res |= (((quint64)data[offset+3]) << 24);\r
+       res |= (((quint64)data[offset+1]) << 32);\r
+       res |= (((quint64)data[offset+2]) << 40);\r
+       res |= (((quint64)data[offset+3]) << 48);\r
+       res |= (((quint64)data[offset+3]) << 56);\r
+\r
+       return res;\r
+}\r
+\r
+/*!\r
+       \internal Reads an quint16 (2 bytes) from a byte array starting at given offset.\r
+*/\r
+quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const\r
+{\r
+       return (quint16) data[offset] | (((quint16)data[offset+1]) << 8);\r
+}\r
+\r
+/*!\r
+       \internal Return the next byte in the pseudo-random sequence\r
+ */\r
+int UnzipPrivate::decryptByte(quint32 key2) const\r
+{\r
+       quint16 temp = ((quint16)(key2) & 0xffff) | 2;\r
+       return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);\r
+}\r
+\r
+/*!\r
+       \internal Update the encryption keys with the next byte of plain text\r
+ */\r
+void UnzipPrivate::updateKeys(quint32* keys, int c) const\r
+{\r
+       keys[0] = CRC32(keys[0], c);\r
+       keys[1] += keys[0] & 0xff;\r
+       keys[1] = keys[1] * 134775813L + 1;\r
+       keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24);\r
+}\r
+\r
+/*!\r
+       \internal Initialize the encryption keys and the random header according to\r
+       the given password.\r
+ */\r
+void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const\r
+{\r
+       keys[0] = 305419896L;\r
+       keys[1] = 591751049L;\r
+       keys[2] = 878082192L;\r
+\r
+       QByteArray pwdBytes = pwd.toAscii();\r
+       int sz = pwdBytes.size();\r
+       const char* ascii = pwdBytes.data();\r
+\r
+       for (int i=0; i<sz; ++i)\r
+               updateKeys(keys, (int)ascii[i]);\r
+}\r
+\r
+/*!\r
+       \internal Attempts to test a password without actually extracting a file.\r
+       The \p file parameter can be used in the user interface or for debugging purposes\r
+       as it is the name of the encrypted file for wich the password is being tested.\r
+*/\r
+UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header)\r
+{\r
+       Q_UNUSED(file);\r
+\r
+       // read encryption keys\r
+       if (device->read(buffer1, 12) != 12)\r
+               return UnZip::Corrupted;\r
+\r
+       // Replace this code if you want to i.e. call some dialog and ask the user for a password\r
+       initKeys(password, keys);\r
+       if (testKeys(header, keys))\r
+               return UnZip::Ok;\r
+\r
+       return UnZip::Skip;\r
+}\r
+\r
+/*!\r
+       \internal Tests a set of keys on the encryption header.\r
+*/\r
+bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys)\r
+{\r
+       char lastByte;\r
+\r
+       // decrypt encryption header\r
+       for (int i=0; i<11; ++i)\r
+               updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2]));\r
+       updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2]));\r
+\r
+       // if there is an extended header (bit in the gp flag) buffer[11] is a byte from the file time\r
+       // with no extended header we have to check the crc high-order byte\r
+       char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24;\r
+\r
+       return (lastByte == c);\r
+}\r
+\r
+/*!\r
+       \internal Decrypts an array of bytes long \p read.\r
+*/\r
+void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read)\r
+{\r
+       for (int i=0; i<(int)read; ++i)\r
+               updateKeys(keys, buffer[i] ^= decryptByte(keys[2]));\r
+}\r
+\r
+/*!\r
+       \internal Converts date and time values from ZIP format to a QDateTime object.\r
+*/\r
+QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const\r
+{\r
+       QDateTime dt;\r
+\r
+       // Usual PKZip low-byte to high-byte order\r
+\r
+       // Date: 7 bits = years from 1980, 4 bits = month, 5 bits = day\r
+       quint16 year = (date[1] >> 1) & 127;\r
+       quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7);\r
+       quint16 day = date[0] & 31;\r
+\r
+       // Time: 5 bits hour, 6 bits minutes, 5 bits seconds with a 2sec precision\r
+       quint16 hour = (time[1] >> 3) & 31;\r
+       quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7);\r
+       quint16 seconds = (time[0] & 31) * 2;\r
+\r
+       dt.setDate(QDate(1980 + year, month, day));\r
+       dt.setTime(QTime(hour, minutes, seconds));\r
+       return dt;\r
+}\r
diff --git a/src/utils/OSDaB-Zip/unzip.h b/src/utils/OSDaB-Zip/unzip.h
new file mode 100644 (file)
index 0000000..fa141b1
--- /dev/null
@@ -0,0 +1,144 @@
+/****************************************************************************\r
+** Filename: unzip.h\r
+** Last updated [dd/mm/yyyy]: 28/01/2007\r
+**\r
+** pkzip 2.0 decompression.\r
+**\r
+** Some of the code has been inspired by other open source projects,\r
+** (mainly Info-Zip and Gilles Vollant's minizip).\r
+** Compression and decompression actually uses the zlib library.\r
+**\r
+** Copyright (C) 2007-2008 Angius Fabrizio. All rights reserved.\r
+**\r
+** This file is part of the OSDaB project (http://osdab.sourceforge.net/).\r
+**\r
+** This file may be distributed and/or modified under the terms of the\r
+** GNU General Public License version 2 as published by the Free Software\r
+** Foundation and appearing in the file LICENSE.GPL included in the\r
+** packaging of this file.\r
+**\r
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE\r
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.\r
+**\r
+** See the file LICENSE.GPL that came with this software distribution or\r
+** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.\r
+**\r
+**********************************************************************/\r
+\r
+#ifndef OSDAB_UNZIP__H\r
+#define OSDAB_UNZIP__H\r
+\r
+#include <QtGlobal>\r
+#include <QMap>\r
+#include <QDateTime>\r
+\r
+#include <zlib.h>\r
+\r
+class UnzipPrivate;\r
+class QIODevice;\r
+class QFile;\r
+class QDir;\r
+class QStringList;\r
+class QString;\r
+\r
+\r
+class UnZip\r
+{\r
+public:\r
+       enum ErrorCode\r
+       {\r
+               Ok,\r
+               ZlibInit,\r
+               ZlibError,\r
+               OpenFailed,\r
+               PartiallyCorrupted,\r
+               Corrupted,\r
+               WrongPassword,\r
+               NoOpenArchive,\r
+               FileNotFound,\r
+               ReadFailed,\r
+               WriteFailed,\r
+               SeekFailed,\r
+               CreateDirFailed,\r
+               InvalidDevice,\r
+               InvalidArchive,\r
+               HeaderConsistencyError,\r
+\r
+               Skip, SkipAll // internal use only\r
+       };\r
+\r
+       enum ExtractionOption\r
+       {\r
+               //! Extracts paths (default)\r
+               ExtractPaths = 0x0001,\r
+               //! Ignores paths and extracts all the files to the same directory\r
+               SkipPaths = 0x0002\r
+       };\r
+       Q_DECLARE_FLAGS(ExtractionOptions, ExtractionOption)\r
+\r
+       enum CompressionMethod\r
+       {\r
+               NoCompression, Deflated, UnknownCompression\r
+       };\r
+\r
+       enum FileType\r
+       {\r
+               File, Directory\r
+       };\r
+\r
+    struct ZipEntry\r
+       {\r
+               ZipEntry();\r
+\r
+               QString filename;\r
+               QString comment;\r
+\r
+               quint32 compressedSize;\r
+               quint32 uncompressedSize;\r
+               quint32 crc32;\r
+\r
+               QDateTime lastModified;\r
+\r
+               CompressionMethod compression;\r
+               FileType type;\r
+\r
+               bool encrypted;\r
+       };\r
+\r
+       UnZip();\r
+       virtual ~UnZip();\r
+\r
+       bool isOpen() const;\r
+\r
+       ErrorCode openArchive(const QString& filename);\r
+       ErrorCode openArchive(QIODevice* device);\r
+       void closeArchive();\r
+\r
+       QString archiveComment() const;\r
+\r
+       QString formatError(UnZip::ErrorCode c) const;\r
+\r
+       bool contains(const QString& file) const;\r
+\r
+       QStringList fileList() const;\r
+       QList<ZipEntry> entryList() const;\r
+\r
+       ErrorCode extractAll(const QString& dirname, ExtractionOptions options = ExtractPaths);\r
+       ErrorCode extractAll(const QDir& dir, ExtractionOptions options = ExtractPaths);\r
+\r
+       ErrorCode extractFile(const QString& filename, const QString& dirname, ExtractionOptions options = ExtractPaths);\r
+       ErrorCode extractFile(const QString& filename, const QDir& dir, ExtractionOptions options = ExtractPaths);\r
+       ErrorCode extractFile(const QString& filename, QIODevice* device, ExtractionOptions options = ExtractPaths);\r
+\r
+       ErrorCode extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options = ExtractPaths);\r
+       ErrorCode extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options = ExtractPaths);\r
+\r
+       void setPassword(const QString& pwd);\r
+\r
+private:\r
+       UnzipPrivate* d;\r
+};\r
+\r
+Q_DECLARE_OPERATORS_FOR_FLAGS(UnZip::ExtractionOptions)\r
+\r
+#endif // OSDAB_UNZIP__H\r
diff --git a/src/utils/fileutil.cpp b/src/utils/fileutil.cpp
new file mode 100644 (file)
index 0000000..95d92f8
--- /dev/null
@@ -0,0 +1,52 @@
+#include <QDir>
+#include <QDebug>
+#include "fileutil.h"
+#include "OSDaB-Zip/unzip.h"
+
+FileUtil::FileUtil(QObject *parent) : QObject(parent)
+{}
+
+QList<EmuFrontFileObject*> FileUtil::scanFilePath(const FilePathObject *fp, QStringList filters)
+{
+    QList<EmuFrontFileObject*> files;
+    qDebug() << "Will scan file path " << fp->getName();
+    QDir dir(fp->getName());
+    if (!dir.exists() || !dir.isReadable())
+        throw QString(tr("Directory %1 doesn't exists or isn't readable!").arg(fp->getName()));
+
+    dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Readable);
+
+    if (filters.count() > 0) dir.setNameFilters(filters);
+
+    QFileInfoList list = dir.entryInfoList();
+    for (int i = 0; i < list.size(); ++i)
+    {
+        QFileInfo fileInfo = list.at(i);
+        qDebug() << QString("%1 %2").arg(fileInfo.size(), 10).arg(fileInfo.absoluteFilePath());
+
+        QList<EmuFrontFileObject*> files = listContents(fileInfo.absoluteFilePath(), fp);
+
+        foreach (EmuFrontFileObject *o, files)
+        {
+            qDebug() << o->getFilename();
+        }
+    }
+    return files;
+}
+
+QList<EmuFrontFileObject*> FileUtil::listContents(const QString filePath, const FilePathObject *fp)
+{
+    UnZip uz;
+    UnZip::ErrorCode ec = uz.openArchive(filePath);
+    if (ec != UnZip::Ok)
+        throw QString("Error while opening zip-file %1, error code %2").arg(filePath).arg(ec);
+    QList<UnZip::ZipEntry> list = uz.entryList();
+    foreach(UnZip::ZipEntry entry, list)
+    {
+        qDebug() << "We have an entry " << entry.filename;
+    }
+    QList<EmuFrontFileObject*>  fileList;
+
+    return fileList;
+
+}
diff --git a/src/utils/fileutil.h b/src/utils/fileutil.h
new file mode 100644 (file)
index 0000000..3362b53
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef FILEUTIL_H
+#define FILEUTIL_H
+
+#include <QObject>
+#include "../dataobjects/emufrontfileobject.h"
+#include "../dataobjects/filepathobject.h"
+
+class QStringList;
+
+class FileUtil : QObject
+{
+
+
+public:
+    FileUtil(QObject *parent);
+
+    QList<EmuFrontFileObject*> scanFilePath(const FilePathObject *fpo, const QStringList filters);
+private:
+    QList<EmuFrontFileObject*>  listContents(const QString filePath, const FilePathObject *fp);
+};
+
+#endif // FILEUTIL_H
diff --git a/src/utils/ziputil.cpp b/src/utils/ziputil.cpp
new file mode 100644 (file)
index 0000000..89c868b
--- /dev/null
@@ -0,0 +1,31 @@
+#include <zlib.h>
+#include "ziputil.h"
+
+
+ZipUtil::ZipUtil()
+{
+}
+
+void ZipUtil::zipContents(string filePath)
+{
+    /*unzFile uf = unzOpen(zipfilename);
+    do_list(uf)
+    unzClose(uf);*/
+}
+
+/*unzFile unzOpen(zipfilename)
+{
+
+}
+
+void do_list(unzFile)
+{
+
+}
+
+void unzClose(unzFile)
+{
+
+}
+
+*/
diff --git a/src/utils/ziputil.h b/src/utils/ziputil.h
new file mode 100644 (file)
index 0000000..0e133d1
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef ZIPUTIL_H
+#define ZIPUTIL_H
+#include <iostream>
+#include <string>
+
+using namespace std;
+
+class ZipUtil
+{
+public:
+    ZipUtil();
+    static void zipContents(string filePath);
+};
+
+#endif // ZIPUTIL_H