From 84f6da103f64396335d693d676344613f3f5c0cd Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Sun, 6 Jan 2019 19:46:18 +0100 Subject: [PATCH] remove all dos line endings (#3489) --- cmake/FindVCredistRuntime.cmake | 72 +- cmake/FindWin32SslRuntime.cmake | 142 +- oracle/src/zip/unzip.cpp | 2854 +++++++++++++-------------- oracle/src/zip/unzip.h | 304 +-- oracle/src/zip/unzip_p.h | 260 +-- oracle/src/zip/zip.cpp | 3238 +++++++++++++++---------------- oracle/src/zip/zip.h | 316 +-- oracle/src/zip/zip_p.h | 266 +-- oracle/src/zip/zipentry_p.h | 182 +- oracle/src/zip/zipglobal.cpp | 304 +-- oracle/src/zip/zipglobal.h | 154 +- 11 files changed, 4046 insertions(+), 4046 deletions(-) diff --git a/cmake/FindVCredistRuntime.cmake b/cmake/FindVCredistRuntime.cmake index 9c3d335b..a9d07445 100644 --- a/cmake/FindVCredistRuntime.cmake +++ b/cmake/FindVCredistRuntime.cmake @@ -1,36 +1,36 @@ -# Find the MS Visual Studio VC redistributable package - -if (WIN32) - set(VCREDISTRUNTIME_FOUND "NO") - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64-bit - set(REDIST_ARCH x64) - else() - set(REDIST_ARCH x86) - endif() - - set(REDIST_FILE vcredist_${REDIST_ARCH}.exe) - - set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) - include(InstallRequiredSystemLibraries) - - # Check if the list contains minimum one element, to get the path from - list(LENGTH CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS libsCount) - if (libsCount GREATER 0) - list(GET CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS 0 _path) - - get_filename_component(_path ${_path} DIRECTORY) - get_filename_component(_path ${_path}/../../ ABSOLUTE) - - if (EXISTS "${_path}/${REDIST_FILE}") # VS 2017 - set(VCREDISTRUNTIME_FOUND "YES") - set(VCREDISTRUNTIME_FILE ${_path}/${REDIST_FILE}) - endif() - endif() - - if(VCREDISTRUNTIME_FOUND) - message(STATUS "Found VCredist ${VCREDISTRUNTIME_FILE}") - else() - message(WARNING "Could not find VCredist package. It's not required for compiling, but needs to be available at runtime.") - endif() -endif() +# Find the MS Visual Studio VC redistributable package + +if (WIN32) + set(VCREDISTRUNTIME_FOUND "NO") + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64-bit + set(REDIST_ARCH x64) + else() + set(REDIST_ARCH x86) + endif() + + set(REDIST_FILE vcredist_${REDIST_ARCH}.exe) + + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) + include(InstallRequiredSystemLibraries) + + # Check if the list contains minimum one element, to get the path from + list(LENGTH CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS libsCount) + if (libsCount GREATER 0) + list(GET CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS 0 _path) + + get_filename_component(_path ${_path} DIRECTORY) + get_filename_component(_path ${_path}/../../ ABSOLUTE) + + if (EXISTS "${_path}/${REDIST_FILE}") # VS 2017 + set(VCREDISTRUNTIME_FOUND "YES") + set(VCREDISTRUNTIME_FILE ${_path}/${REDIST_FILE}) + endif() + endif() + + if(VCREDISTRUNTIME_FOUND) + message(STATUS "Found VCredist ${VCREDISTRUNTIME_FILE}") + else() + message(WARNING "Could not find VCredist package. It's not required for compiling, but needs to be available at runtime.") + endif() +endif() diff --git a/cmake/FindWin32SslRuntime.cmake b/cmake/FindWin32SslRuntime.cmake index 79ba688d..37ffd0b0 100644 --- a/cmake/FindWin32SslRuntime.cmake +++ b/cmake/FindWin32SslRuntime.cmake @@ -1,71 +1,71 @@ -# Find the OpenSSL runtime libraries (.dll) for Windows that -# will be needed by Qt in order to access https urls. - -if (WIN32) - # Get standard installation paths for OpenSSL under Windows - - # http://www.slproweb.com/products/Win32OpenSSL.html - - if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - # target win64 - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ENV OPENSSL_ROOT_DIR - ) - file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "C:/Tools/vcpkg/installed/x64-windows/bin" - "${_programfiles}/OpenSSL-Win64" - "C:/OpenSSL-Win64/" - ) - unset(_programfiles) - else( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - # target win32 - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - ENV OPENSSL_ROOT_DIR - ) - file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "C:/Tools/vcpkg/installed/x86-windows/bin" - "${_programfiles}/OpenSSL" - "${_programfiles}/OpenSSL-Win32" - "C:/OpenSSL/" - "C:/OpenSSL-Win32/" - ) - unset(_programfiles) - endif( CMAKE_SIZEOF_VOID_P EQUAL 8 ) - -else () - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR - ) -endif () - -set(_OPENSSL_ROOT_HINTS_AND_PATHS - HINTS ${_OPENSSL_ROOT_HINTS} - PATHS ${_OPENSSL_ROOT_PATHS} - ) - -# For OpenSSL < 1.1, they are named libeay32 and ssleay32 and even if the dll is 64bit, it's still suffixed as *32.dll -# For OpenSSL >= 1.1, they are named libcrypto and libssl with no suffix -FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libeay32.dll libcrypto.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS}) -FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES ssleay32.dll libssl.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS}) - - -IF(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY) - SET(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}") - SET(WIN32SSLRUNTIME_FOUND "YES") - message(STATUS "Found OpenSSL ${WIN32SSLRUNTIME_LIBRARIES}") -ELSE() - SET(WIN32SSLRUNTIME_FOUND "NO") - message(WARNING "Could not find OpenSSL runtime libraries. They are not required for compiling, but needs to be available at runtime.") -ENDIF() - -MARK_AS_ADVANCED( - WIN32SSLRUNTIME_LIBEAY - WIN32SSLRUNTIME_SSLEAY - ) +# Find the OpenSSL runtime libraries (.dll) for Windows that +# will be needed by Qt in order to access https urls. + +if (WIN32) + # Get standard installation paths for OpenSSL under Windows + + # http://www.slproweb.com/products/Win32OpenSSL.html + + if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + # target win64 + set(_OPENSSL_ROOT_HINTS + ${OPENSSL_ROOT_DIR} + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" + ENV OPENSSL_ROOT_DIR + ) + file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) + set(_OPENSSL_ROOT_PATHS + "C:/Tools/vcpkg/installed/x64-windows/bin" + "${_programfiles}/OpenSSL-Win64" + "C:/OpenSSL-Win64/" + ) + unset(_programfiles) + else( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + # target win32 + set(_OPENSSL_ROOT_HINTS + ${OPENSSL_ROOT_DIR} + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" + ENV OPENSSL_ROOT_DIR + ) + file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) + set(_OPENSSL_ROOT_PATHS + "C:/Tools/vcpkg/installed/x86-windows/bin" + "${_programfiles}/OpenSSL" + "${_programfiles}/OpenSSL-Win32" + "C:/OpenSSL/" + "C:/OpenSSL-Win32/" + ) + unset(_programfiles) + endif( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + +else () + set(_OPENSSL_ROOT_HINTS + ${OPENSSL_ROOT_DIR} + ENV OPENSSL_ROOT_DIR + ) +endif () + +set(_OPENSSL_ROOT_HINTS_AND_PATHS + HINTS ${_OPENSSL_ROOT_HINTS} + PATHS ${_OPENSSL_ROOT_PATHS} + ) + +# For OpenSSL < 1.1, they are named libeay32 and ssleay32 and even if the dll is 64bit, it's still suffixed as *32.dll +# For OpenSSL >= 1.1, they are named libcrypto and libssl with no suffix +FIND_FILE(WIN32SSLRUNTIME_LIBEAY NAMES libeay32.dll libcrypto.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS}) +FIND_FILE(WIN32SSLRUNTIME_SSLEAY NAMES ssleay32.dll libssl.dll ${_OPENSSL_ROOT_HINTS_AND_PATHS}) + + +IF(WIN32SSLRUNTIME_LIBEAY AND WIN32SSLRUNTIME_SSLEAY) + SET(WIN32SSLRUNTIME_LIBRARIES "${WIN32SSLRUNTIME_LIBEAY}" "${WIN32SSLRUNTIME_SSLEAY}") + SET(WIN32SSLRUNTIME_FOUND "YES") + message(STATUS "Found OpenSSL ${WIN32SSLRUNTIME_LIBRARIES}") +ELSE() + SET(WIN32SSLRUNTIME_FOUND "NO") + message(WARNING "Could not find OpenSSL runtime libraries. They are not required for compiling, but needs to be available at runtime.") +ENDIF() + +MARK_AS_ADVANCED( + WIN32SSLRUNTIME_LIBEAY + WIN32SSLRUNTIME_SSLEAY + ) diff --git a/oracle/src/zip/unzip.cpp b/oracle/src/zip/unzip.cpp index 6640e0a9..716952c6 100755 --- a/oracle/src/zip/unzip.cpp +++ b/oracle/src/zip/unzip.cpp @@ -1,1427 +1,1427 @@ -/**************************************************************************** -** Filename: unzip.cpp -** Last updated [dd/mm/yyyy]: 08/07/2010 -** -** pkzip 2.0 decompression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#include "unzip.h" -#include "unzip_p.h" -#include "zipentry_p.h" - -#include -#include -#include -#include -#include - -// You can remove this #include if you replace the qDebug() statements. -#include - -/*! - \class UnZip unzip.h - - \brief PKZip 2.0 file decompression. - Compatibility with later versions is not ensured as they may use - unsupported compression algorithms. - Versions after 2.7 may have an incompatible header format and thus be - completely incompatible. -*/ - -/*! \enum UnZip::ErrorCode The result of a decompression operation. - \value UnZip::Ok No error occurred. - \value UnZip::ZlibInit Failed to init or load the zlib library. - \value UnZip::ZlibError The zlib library returned some error. - \value UnZip::OpenFailed Unable to create or open a device. - \value UnZip::PartiallyCorrupted Corrupted zip archive - some files could be extracted. - \value UnZip::Corrupted Corrupted or invalid zip archive. - \value UnZip::WrongPassword Unable to decrypt a password protected file. - \value UnZip::NoOpenArchive No archive has been opened yet. - \value UnZip::FileNotFound Unable to find the requested file in the archive. - \value UnZip::ReadFailed Reading of a file failed. - \value UnZip::WriteFailed Writing of a file failed. - \value UnZip::SeekFailed Seek failed. - \value UnZip::CreateDirFailed Could not create a directory. - \value UnZip::InvalidDevice A null device has been passed as parameter. - \value UnZip::InvalidArchive This is not a valid (or supported) ZIP archive. - \value UnZip::HeaderConsistencyError Local header record info does not match with the central directory record info. The archive may be corrupted. - - \value UnZip::Skip Internal use only. - \value UnZip::SkipAll Internal use only. -*/ - -/*! \enum UnZip::ExtractionOptions Some options for the file extraction methods. - \value UnZip::ExtractPaths Default. Does not ignore the path of the zipped files. - \value UnZip::SkipPaths Default. Ignores the path of the zipped files and extracts them all to the same root directory. - \value UnZip::VerifyOnly Doesn't actually extract files. - \value UnZip::NoSilentDirectoryCreation Doesn't attempt to silently create missing output directories. -*/ - -//! Local header size (excluding signature, excluding variable length fields) -#define UNZIP_LOCAL_HEADER_SIZE 26 -//! Central Directory file entry size (excluding signature, excluding variable length fields) -#define UNZIP_CD_ENTRY_SIZE_NS 42 -//! Data descriptor size (excluding signature) -#define UNZIP_DD_SIZE 12 -//! End Of Central Directory size (including signature, excluding variable length fields) -#define UNZIP_EOCD_SIZE 22 -//! Local header entry encryption header size -#define UNZIP_LOCAL_ENC_HEADER_SIZE 12 - -// Some offsets inside a CD record (excluding signature) -#define UNZIP_CD_OFF_VERSION_MADE 0 -#define UNZIP_CD_OFF_VERSION 2 -#define UNZIP_CD_OFF_GPFLAG 4 -#define UNZIP_CD_OFF_CMETHOD 6 -#define UNZIP_CD_OFF_MODT 8 -#define UNZIP_CD_OFF_MODD 10 -#define UNZIP_CD_OFF_CRC32 12 -#define UNZIP_CD_OFF_CSIZE 16 -#define UNZIP_CD_OFF_USIZE 20 -#define UNZIP_CD_OFF_NAMELEN 24 -#define UNZIP_CD_OFF_XLEN 26 -#define UNZIP_CD_OFF_COMMLEN 28 -#define UNZIP_CD_OFF_LHOFFSET 38 - -// Some offsets inside a local header record (excluding signature) -#define UNZIP_LH_OFF_VERSION 0 -#define UNZIP_LH_OFF_GPFLAG 2 -#define UNZIP_LH_OFF_CMETHOD 4 -#define UNZIP_LH_OFF_MODT 6 -#define UNZIP_LH_OFF_MODD 8 -#define UNZIP_LH_OFF_CRC32 10 -#define UNZIP_LH_OFF_CSIZE 14 -#define UNZIP_LH_OFF_USIZE 18 -#define UNZIP_LH_OFF_NAMELEN 22 -#define UNZIP_LH_OFF_XLEN 24 - -// Some offsets inside a data descriptor record (excluding signature) -#define UNZIP_DD_OFF_CRC32 0 -#define UNZIP_DD_OFF_CSIZE 4 -#define UNZIP_DD_OFF_USIZE 8 - -// Some offsets inside a EOCD record -#define UNZIP_EOCD_OFF_ENTRIES 6 -#define UNZIP_EOCD_OFF_CDOFF 12 -#define UNZIP_EOCD_OFF_COMMLEN 16 - -/*! - Max version handled by this API. - 0x14 = 2.0 --> full compatibility only up to this version; - later versions use unsupported features -*/ -#define UNZIP_VERSION 0x14 - -//! CRC32 routine -#define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8) - -OSDAB_BEGIN_NAMESPACE(Zip) - - -/************************************************************************ - ZipEntry -*************************************************************************/ - -/*! - ZipEntry constructor - initialize data. Type is set to File. -*/ -UnZip::ZipEntry::ZipEntry() -{ - compressedSize = uncompressedSize = crc32 = 0; - compression = NoCompression; - type = File; - encrypted = false; -} - - -/************************************************************************ - Private interface -*************************************************************************/ - -//! \internal -UnzipPrivate::UnzipPrivate() : - password(), - skipAllEncrypted(false), - headers(0), - device(0), - file(0), - uBuffer(0), - crcTable(0), - cdOffset(0), - eocdOffset(0), - cdEntryCount(0), - unsupportedEntryCount(0), - comment() -{ - uBuffer = (unsigned char*) buffer1; - crcTable = (quint32*) get_crc_table(); -} - -//! \internal -void UnzipPrivate::deviceDestroyed(QObject*) -{ - qDebug("Unexpected device destruction detected."); - do_closeArchive(); -} - -//! \internal Parses a Zip archive. -UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev) -{ - Q_ASSERT(!device); - Q_ASSERT(dev); - - if (!(dev->isOpen() || dev->open(QIODevice::ReadOnly))) { - qDebug() << "Unable to open device for reading"; - return UnZip::OpenFailed; - } - - device = dev; - if (device != file) - connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*))); - - UnZip::ErrorCode ec; - - ec = seekToCentralDirectory(); - if (ec != UnZip::Ok) { - closeArchive(); - return ec; - } - - //! \todo Ignore CD entry count? CD may be corrupted. - if (cdEntryCount == 0) { - return UnZip::Ok; - } - - bool continueParsing = true; - - while (continueParsing) { - if (device->read(buffer1, 4) != 4) { - if (headers) { - qDebug() << "Corrupted zip archive. Some files might be extracted."; - ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted; - break; - } else { - closeArchive(); - qDebug() << "Corrupted or invalid zip archive. Closing."; - ec = UnZip::Corrupted; - break; - } - } - - if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) ) - break; - - if ((ec = parseCentralDirectoryRecord()) != UnZip::Ok) - break; - } - - if (ec != UnZip::Ok) - closeArchive(); - - return ec; -} - -/* - \internal Parses a local header record and makes some consistency check - with the information stored in the Central Directory record for this entry - that has been previously parsed. - \todo Optional consistency check (as a ExtractionOptions flag) - - local file header signature 4 bytes (0x04034b50) - version needed to extract 2 bytes - general purpose bit flag 2 bytes - compression method 2 bytes - last mod file time 2 bytes - last mod file date 2 bytes - crc-32 4 bytes - compressed size 4 bytes - uncompressed size 4 bytes - file name length 2 bytes - extra field length 2 bytes - - file name (variable size) - extra field (variable size) -*/ -UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry) -{ - Q_ASSERT(device); - - if (!device->seek(entry.lhOffset)) - return UnZip::SeekFailed; - - // Test signature - if (device->read(buffer1, 4) != 4) - return UnZip::ReadFailed; - - if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04)) - return UnZip::InvalidArchive; - - if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE) - return UnZip::ReadFailed; - - /* - Check 3rd general purpose bit flag. - - "bit 3: If this bit is set, the fields crc-32, compressed size - and uncompressed size are set to zero in the local - header. The correct values are put in the data descriptor - immediately following the compressed data." - */ - bool hasDataDescriptor = entry.hasDataDescriptor(); - bool checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD); - - if (!checkFailed) - checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG]; - if (!checkFailed) - checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1]; - if (!checkFailed) - checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT]; - if (!checkFailed) - checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1]; - if (!checkFailed) - checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD]; - if (!checkFailed) - checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1]; - if (!hasDataDescriptor) - { - if (!checkFailed) - checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32); - if (!checkFailed) - checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE); - if (!checkFailed) - checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE); - } - - if (checkFailed) - return UnZip::HeaderConsistencyError; - - // Check filename - quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN); - if (szName == 0) - return UnZip::HeaderConsistencyError; - - if (device->read(buffer2, szName) != szName) - return UnZip::ReadFailed; - - QString filename = QString::fromLatin1(buffer2, szName); - if (filename != path) { - qDebug() << "Filename in local header mismatches."; - return UnZip::HeaderConsistencyError; - } - - // Skip extra field - quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN); - if (szExtra != 0) { - if (!device->seek(device->pos() + szExtra)) - return UnZip::SeekFailed; - } - - entry.dataOffset = device->pos(); - - if (hasDataDescriptor) { - /* - The data descriptor has this OPTIONAL signature: PK\7\8 - We try to skip the compressed data relying on the size set in the - Central Directory record. - */ - if (!device->seek(device->pos() + entry.szComp)) - return UnZip::SeekFailed; - - // Read 4 bytes and check if there is a data descriptor signature - if (device->read(buffer2, 4) != 4) - return UnZip::ReadFailed; - - bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08; - if (hasSignature) { - if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE) - return UnZip::ReadFailed; - } else { - if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4) - return UnZip::ReadFailed; - } - - // DD: crc, compressed size, uncompressed size - if ( - entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) || - entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) || - entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE) - ) - return UnZip::HeaderConsistencyError; - } - - return UnZip::Ok; -} - -/*! \internal Attempts to find the start of the central directory record. - - We seek the file back until we reach the "End Of Central Directory" - signature PK\5\6. - - end of central dir signature 4 bytes (0x06054b50) - number of this disk 2 bytes - number of the disk with the - start of the central directory 2 bytes - total number of entries in the - central directory on this disk 2 bytes - total number of entries in - the central directory 2 bytes - size of the central directory 4 bytes - offset of start of central - directory with respect to - the starting disk number 4 bytes - .ZIP file comment length 2 bytes - --- SIZE UNTIL HERE: UNZIP_EOCD_SIZE --- - .ZIP file comment (variable size) -*/ -UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory() -{ - Q_ASSERT(device); - - qint64 length = device->size(); - qint64 offset = length - UNZIP_EOCD_SIZE; - - if (length < UNZIP_EOCD_SIZE) - return UnZip::InvalidArchive; - - if (!device->seek( offset )) - return UnZip::SeekFailed; - - if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE) - return UnZip::ReadFailed; - - bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06); - - if (eocdFound) { - // Zip file has no comment (the only variable length field in the EOCD record) - eocdOffset = offset; - } else { - qint64 read; - char* p = 0; - - offset -= UNZIP_EOCD_SIZE; - - if (offset <= 0) - return UnZip::InvalidArchive; - - if (!device->seek( offset )) - return UnZip::SeekFailed; - - while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0) { - if ( (p = strstr(buffer1, "PK\5\6")) != 0) { - // Seek to the start of the EOCD record so we can read it fully - // Yes... we could simply read the missing bytes and append them to the buffer - // but this is far easier so heck it! - device->seek( offset + (p - buffer1) ); - eocdFound = true; - eocdOffset = offset + (p - buffer1); - - // Read EOCD record - if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE) - return UnZip::ReadFailed; - - break; - } - - // TODO: This is very slow and only a temporary bug fix. Need some pattern matching algorithm here. - offset -= 1 /*UNZIP_EOCD_SIZE*/; - if (offset <= 0) - return UnZip::InvalidArchive; - - if (!device->seek( offset )) - return UnZip::SeekFailed; - } - } - - if (!eocdFound) - return UnZip::InvalidArchive; - - // Parse EOCD to locate CD offset - offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4); - - cdOffset = offset; - - cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4); - - quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4); - if (commentLength != 0) { - QByteArray c = device->read(commentLength); - if (c.count() != commentLength) - return UnZip::ReadFailed; - - comment = c; - } - - // Seek to the start of the CD record - if (!device->seek( cdOffset )) - return UnZip::SeekFailed; - - return UnZip::Ok; -} - -/*! - \internal Parses a central directory record. - - Central Directory record structure: - - [file header 1] - . - . - . - [file header n] - [digital signature] // PKZip 6.2 or later only - - File header: - - central file header signature 4 bytes (0x02014b50) - version made by 2 bytes - version needed to extract 2 bytes - general purpose bit flag 2 bytes - compression method 2 bytes - last mod file time 2 bytes - last mod file date 2 bytes - crc-32 4 bytes - compressed size 4 bytes - uncompressed size 4 bytes - file name length 2 bytes - extra field length 2 bytes - file comment length 2 bytes - disk number start 2 bytes - internal file attributes 2 bytes - external file attributes 4 bytes - relative offset of local header 4 bytes - - file name (variable size) - extra field (variable size) - file comment (variable size) -*/ -UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord() -{ - Q_ASSERT(device); - - // Read CD record - if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS) - return UnZip::ReadFailed; - - bool skipEntry = false; - - // Get compression type so we can skip non compatible algorithms - quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD); - - // Get variable size fields length so we can skip the whole record - // if necessary - quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN); - quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN); - quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN); - - quint32 skipLength = szName + szExtra + szComment; - - UnZip::ErrorCode ec = UnZip::Ok; - - if ((compMethod != 0) && (compMethod != 8)) { - qDebug() << "Unsupported compression method. Skipping file."; - skipEntry = true; - } - - if (!skipEntry && szName == 0) { - qDebug() << "Skipping file with no name."; - skipEntry = true; - } - - QString filename; - if (device->read(buffer2, szName) != szName) { - ec = UnZip::ReadFailed; - skipEntry = true; - } else { - filename = QString::fromLatin1(buffer2, szName); - } - - // Unsupported features if version is bigger than UNZIP_VERSION - if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION) { - QString v = QString::number(buffer1[UNZIP_CD_OFF_VERSION]); - if (v.length() == 2) - v.insert(1, QLatin1Char('.')); - v = QString::fromLatin1("Unsupported PKZip version (%1). Skipping file: %2") - .arg(v, filename.isEmpty() ? QString::fromLatin1("") : filename); - qDebug() << v.toLatin1().constData(); - skipEntry = true; - } - - if (skipEntry) { - if (ec == UnZip::Ok) { - if (!device->seek( device->pos() + skipLength )) - ec = UnZip::SeekFailed; - unsupportedEntryCount++; - } - - return ec; - } - - ZipEntryP* h = new ZipEntryP; - h->compMethod = compMethod; - - h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG]; - h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1]; - - h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT]; - h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1]; - - h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD]; - h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1]; - - h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32); - h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE); - h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE); - - // Skip extra field (if any) - if (szExtra != 0) { - if (!device->seek( device->pos() + szExtra )) { - delete h; - return UnZip::SeekFailed; - } - } - - // Read comment field (if any) - if (szComment != 0) { - if (device->read(buffer2, szComment) != szComment) { - delete h; - return UnZip::ReadFailed; - } - - h->comment = QString::fromLatin1(buffer2, szComment); - } - - h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET); - - if (!headers) - headers = new QMap(); - headers->insert(filename, h); - - return UnZip::Ok; -} - -//! \internal Closes the archive and resets the internal status. -void UnzipPrivate::closeArchive() -{ - if (!device) { - Q_ASSERT(!file); - return; - } - - if (device != file) - disconnect(device, 0, this, 0); - - do_closeArchive(); -} - -//! \internal -void UnzipPrivate::do_closeArchive() -{ - skipAllEncrypted = false; - - if (headers) { - if (headers) - qDeleteAll(*headers); - delete headers; - headers = 0; - } - - device = 0; - - if (file) - delete file; - file = 0; - - cdOffset = eocdOffset = 0; - cdEntryCount = 0; - unsupportedEntryCount = 0; - - comment.clear(); -} - -//! \internal -UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry, - const QDir& dir, UnZip::ExtractionOptions options) -{ - QString name(path); - QString dirname; - QString directory; - - const bool verify = (options & UnZip::VerifyOnly); - const int pos = name.lastIndexOf('/'); - - // This entry is for a directory - if (pos == name.length() - 1) { - if (verify) - return UnZip::Ok; - - if (options & UnZip::SkipPaths) - return UnZip::Ok; - - directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name)); - if (!createDirectory(directory)) { - qDebug() << QString("Unable to create directory: %1").arg(directory); - return UnZip::CreateDirFailed; - } - - return UnZip::Ok; - } - - // Extract path from entry - if (verify) { - return extractFile(path, entry, 0, options); - } - - if (pos > 0) { - // get directory part - dirname = name.left(pos); - if (options & UnZip::SkipPaths) { - directory = dir.absolutePath(); - } else { - directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname)); - if (!createDirectory(directory)) { - qDebug() << QString("Unable to create directory: %1").arg(directory); - return UnZip::CreateDirFailed; - } - } - name = name.right(name.length() - pos - 1); - } else { - directory = dir.absolutePath(); - } - - const bool silentDirectoryCreation = !(options & UnZip::NoSilentDirectoryCreation); - if (silentDirectoryCreation) { - if (!createDirectory(directory)) { - qDebug() << QString("Unable to create output directory %1").arg(directory); - return UnZip::CreateDirFailed; - } - } - - name = QString("%1/%2").arg(directory).arg(name); - - QFile outFile(name); - if (!outFile.open(QIODevice::WriteOnly)) { - qDebug() << QString("Unable to open %1 for writing").arg(name); - return UnZip::OpenFailed; - } - - UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options); - outFile.close(); - - const QDateTime lastModified = convertDateTime(entry.modDate, entry.modTime); - const bool setTimeOk = OSDAB_ZIP_MANGLE(setFileTimestamp)(name, lastModified); - if (!setTimeOk) { - qDebug() << QString("Unable to set last modified time on file: %1").arg(name); - } - - if (ec != UnZip::Ok) { - if (!outFile.remove()) - qDebug() << QString("Unable to remove corrupted file: %1").arg(name); - } - - return ec; -} - -//! \internal -UnZip::ErrorCode UnzipPrivate::extractStoredFile( - const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev, - UnZip::ExtractionOptions options) -{ - const bool verify = (options & UnZip::VerifyOnly); - const bool isEncrypted = keys != 0; - - uInt rep = szComp / UNZIP_READ_BUFFER; - uInt rem = szComp % UNZIP_READ_BUFFER; - uInt cur = 0; - - // extract data - qint64 read; - quint64 tot = 0; - - while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 ) { - if (isEncrypted) - decryptBytes(*keys, buffer1, read); - - myCRC = crc32(myCRC, uBuffer, read); - if (!verify) { - if (outDev->write(buffer1, read) != read) - return UnZip::WriteFailed; - } - - cur++; - tot += read; - if (tot == szComp) - break; - } - - return (read < 0) - ? UnZip::ReadFailed - : UnZip::Ok; -} - -//! \internal -UnZip::ErrorCode UnzipPrivate::inflateFile( - const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev, - UnZip::ExtractionOptions options) -{ - const bool verify = (options & UnZip::VerifyOnly); - const bool isEncrypted = keys != 0; - Q_ASSERT(verify ? true : outDev != 0); - - uInt rep = szComp / UNZIP_READ_BUFFER; - uInt rem = szComp % UNZIP_READ_BUFFER; - uInt cur = 0; - - // extract data - qint64 read; - quint64 tot = 0; - - /* Allocate inflate state */ - z_stream zstr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - zstr.next_in = Z_NULL; - zstr.avail_in = 0; - - int zret; - - // Use inflateInit2 with negative windowBits to get raw decompression - if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK ) - return UnZip::ZlibError; - - int szDecomp; - - // Decompress until deflate stream ends or end of file - do { - read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem); - if (!read) - break; - - if (read < 0) { - (void)inflateEnd(&zstr); - return UnZip::ReadFailed; - } - - if (isEncrypted) - decryptBytes(*keys, buffer1, read); - - cur++; - tot += read; - - zstr.avail_in = (uInt) read; - zstr.next_in = (Bytef*) buffer1; - - // Run inflate() on input until output buffer not full - do { - zstr.avail_out = UNZIP_READ_BUFFER; - zstr.next_out = (Bytef*) buffer2;; - - zret = inflate(&zstr, Z_NO_FLUSH); - - switch (zret) { - case Z_NEED_DICT: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&zstr); - return UnZip::WriteFailed; - default: - ; - } - - szDecomp = UNZIP_READ_BUFFER - zstr.avail_out; - if (!verify) { - if (outDev->write(buffer2, szDecomp) != szDecomp) { - inflateEnd(&zstr); - return UnZip::ZlibError; - } - } - - myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp); - - } while (zstr.avail_out == 0); - - } while (zret != Z_STREAM_END); - - inflateEnd(&zstr); - return UnZip::Ok; -} - -//! \internal \p outDev is null if the VerifyOnly option is set -UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry, - QIODevice* outDev, UnZip::ExtractionOptions options) -{ - const bool verify = (options & UnZip::VerifyOnly); - - Q_UNUSED(options); - Q_ASSERT(device); - Q_ASSERT(verify ? true : outDev != 0); - - if (!entry.lhEntryChecked) { - UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry); - entry.lhEntryChecked = true; - if (ec != UnZip::Ok) - return ec; - } - - if (!device->seek(entry.dataOffset)) - return UnZip::SeekFailed; - - // Encryption keys - quint32 keys[3]; - quint32 szComp = entry.szComp; - if (entry.isEncrypted()) { - UnZip::ErrorCode e = testPassword(keys, path, entry); - if (e != UnZip::Ok) - { - qDebug() << QString("Unable to decrypt %1").arg(path); - return e; - }//! Encryption header size - szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE; // remove encryption header size - } - - if (szComp == 0) { - if (entry.crc != 0) - return UnZip::Corrupted; - return UnZip::Ok; - } - - quint32 myCRC = crc32(0L, Z_NULL, 0); - quint32* k = keys; - - UnZip::ErrorCode ec = UnZip::Ok; - if (entry.compMethod == 0) { - ec = extractStoredFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options); - } else if (entry.compMethod == 8) { - ec = inflateFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options); - } - - if (ec == UnZip::Ok && myCRC != entry.crc) - return UnZip::Corrupted; - - return UnZip::Ok; -} - -//! \internal Creates a new directory and all the needed parent directories. -bool UnzipPrivate::createDirectory(const QString& path) -{ - QDir d(path); - if (!d.exists() && !d.mkpath(path)) { - qDebug() << QString("Unable to create directory: %1").arg(path); - return false; - } - - return true; -} - -/*! - \internal Reads an quint32 (4 bytes) from a byte array starting at given offset. -*/ -quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const -{ - quint32 res = (quint32) data[offset]; - res |= (((quint32)data[offset+1]) << 8); - res |= (((quint32)data[offset+2]) << 16); - res |= (((quint32)data[offset+3]) << 24); - - return res; -} - -/*! - \internal Reads an quint64 (8 bytes) from a byte array starting at given offset. -*/ -quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const -{ - quint64 res = (quint64) data[offset]; - res |= (((quint64)data[offset+1]) << 8); - res |= (((quint64)data[offset+2]) << 16); - res |= (((quint64)data[offset+3]) << 24); - res |= (((quint64)data[offset+1]) << 32); - res |= (((quint64)data[offset+2]) << 40); - res |= (((quint64)data[offset+3]) << 48); - res |= (((quint64)data[offset+3]) << 56); - - return res; -} - -/*! - \internal Reads an quint16 (2 bytes) from a byte array starting at given offset. -*/ -quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const -{ - return (quint16) data[offset] | (((quint16)data[offset+1]) << 8); -} - -/*! - \internal Return the next byte in the pseudo-random sequence - */ -int UnzipPrivate::decryptByte(quint32 key2) const -{ - quint16 temp = ((quint16)(key2) & 0xffff) | 2; - return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); -} - -/*! - \internal Update the encryption keys with the next byte of plain text - */ -void UnzipPrivate::updateKeys(quint32* keys, int c) const -{ - keys[0] = CRC32(keys[0], c); - keys[1] += keys[0] & 0xff; - keys[1] = keys[1] * 134775813L + 1; - keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24); -} - -/*! - \internal Initialize the encryption keys and the random header according to - the given password. - */ -void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const -{ - keys[0] = 305419896L; - keys[1] = 591751049L; - keys[2] = 878082192L; - - QByteArray pwdBytes = pwd.toLatin1(); - int sz = pwdBytes.size(); - const char* ascii = pwdBytes.data(); - - for (int i = 0; i < sz; ++i) - updateKeys(keys, (int)ascii[i]); -} - -/*! - \internal Attempts to test a password without actually extracting a file. - The \p file parameter can be used in the user interface or for debugging purposes - as it is the name of the encrypted file for wich the password is being tested. -*/ -UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header) -{ - Q_UNUSED(file); - Q_ASSERT(device); - - // read encryption keys - if (device->read(buffer1, 12) != 12) - return UnZip::Corrupted; - - // Replace this code if you want to i.e. call some dialog and ask the user for a password - initKeys(password, keys); - if (testKeys(header, keys)) - return UnZip::Ok; - - return UnZip::Skip; -} - -/*! - \internal Tests a set of keys on the encryption header. -*/ -bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys) -{ - char lastByte; - - // decrypt encryption header - for (int i = 0; i < 11; ++i) - updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2])); - updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2])); - - // if there is an extended header (bit in the gp flag) buffer[11] is a byte from the file time - // with no extended header we have to check the crc high-order byte - char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24; - - return (lastByte == c); -} - -/*! - \internal Decrypts an array of bytes long \p read. -*/ -void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read) -{ - for (int i = 0; i < (int)read; ++i) - updateKeys(keys, buffer[i] ^= decryptByte(keys[2])); -} - -/*! - \internal Converts date and time values from ZIP format to a QDateTime object. -*/ -QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const -{ - QDateTime dt; - - // Usual PKZip low-byte to high-byte order - - // Date: 7 bits = years from 1980, 4 bits = month, 5 bits = day - quint16 year = (date[1] >> 1) & 127; - quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7); - quint16 day = date[0] & 31; - - // Time: 5 bits hour, 6 bits minutes, 5 bits seconds with a 2sec precision - quint16 hour = (time[1] >> 3) & 31; - quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7); - quint16 seconds = (time[0] & 31) * 2; - - dt.setDate(QDate(1980 + year, month, day)); - dt.setTime(QTime(hour, minutes, seconds)); - return dt; -} - - -/************************************************************************ - Public interface -*************************************************************************/ - -/*! - Creates a new Zip file decompressor. -*/ -UnZip::UnZip() : d(new UnzipPrivate) -{ -} - -/*! - Closes any open archive and releases used resources. -*/ -UnZip::~UnZip() -{ - closeArchive(); - delete d; -} - -/*! - Returns true if there is an open archive. -*/ -bool UnZip::isOpen() const -{ - return d->device; -} - -/*! - Opens a zip archive and reads the files list. Closes any previously opened archive. -*/ -UnZip::ErrorCode UnZip::openArchive(const QString& filename) -{ - closeArchive(); - - // closeArchive will destroy the file - d->file = new QFile(filename); - - if (!d->file->exists()) { - delete d->file; - d->file = 0; - return UnZip::FileNotFound; - } - - if (!d->file->open(QIODevice::ReadOnly)) { - delete d->file; - d->file = 0; - return UnZip::OpenFailed; - } - - return d->openArchive(d->file); -} - -/*! - Opens a zip archive and reads the entries list. - Closes any previously opened archive. - \warning The class takes DOES NOT take ownership of the device. -*/ -UnZip::ErrorCode UnZip::openArchive(QIODevice* device) -{ - closeArchive(); - - if (!device) { - qDebug() << "Invalid device."; - return UnZip::InvalidDevice; - } - - return d->openArchive(device); -} - -/*! - Closes the archive and releases all the used resources (like cached passwords). -*/ -void UnZip::closeArchive() -{ - d->closeArchive(); -} - -QString UnZip::archiveComment() const -{ - return d->comment; -} - -/*! - Returns a locale translated error string for a given error code. -*/ -QString UnZip::formatError(UnZip::ErrorCode c) const -{ - switch (c) - { - case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break; - case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break; - case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break; - case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break; - case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break; - case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break; - case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break; - case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break; - case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break; - case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break; - case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break; - case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break; - case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break; - case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break; - case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break; - case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break; - default: ; - } - - return QCoreApplication::translate("UnZip", "Unknown error."); -} - -/*! - Returns true if the archive contains a file with the given path and name. -*/ -bool UnZip::contains(const QString& file) const -{ - return d->headers ? d->headers->contains(file) : false; -} - -/*! - Returns complete paths of files and directories in this archive. -*/ -QStringList UnZip::fileList() const -{ - return d->headers ? d->headers->keys() : QStringList(); -} - -/*! - Returns information for each (correctly parsed) entry of this archive. -*/ -QList UnZip::entryList() const -{ - QList list; - if (!d->headers) - return list; - - for (QMap::ConstIterator it = d->headers->constBegin(); - it != d->headers->constEnd(); ++it) { - const ZipEntryP* entry = it.value(); - Q_ASSERT(entry != 0); - - ZipEntry z; - - z.filename = it.key(); - if (!entry->comment.isEmpty()) - z.comment = entry->comment; - z.compressedSize = entry->szComp; - z.uncompressedSize = entry->szUncomp; - z.crc32 = entry->crc; - z.lastModified = d->convertDateTime(entry->modDate, entry->modTime); - - z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression; - z.type = z.filename.endsWith("/") ? Directory : File; - - z.encrypted = entry->isEncrypted(); - - list.append(z); - } - - return list; -} - -/*! - Extracts the whole archive to a directory. -*/ -UnZip::ErrorCode UnZip::verifyArchive() -{ - return extractAll(QDir(), VerifyOnly); -} - -/*! - Extracts the whole archive to a directory. -*/ -UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options) -{ - return extractAll(QDir(dirname), options); -} - -/*! - Extracts the whole archive to a directory. - Stops extraction at the first error. -*/ -UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options) -{ - // this should only happen if we didn't call openArchive() yet - if (!d->device) - return NoOpenArchive; - - if (!d->headers) - return Ok; - - ErrorCode ec = Ok; - - QMap::ConstIterator it = d->headers->constBegin(); - const QMap::ConstIterator end = d->headers->constEnd(); - while (it != end) { - ZipEntryP* entry = it.value(); - Q_ASSERT(entry != 0); - if ((entry->isEncrypted()) && d->skipAllEncrypted) { - ++it; - continue; - } - - bool skip = false; - ec = d->extractFile(it.key(), *entry, dir, options); - switch (ec) { - case Corrupted: - qDebug() << "Corrupted entry" << it.key(); - break; - case CreateDirFailed: - break; - case Skip: - skip = true; - break; - case SkipAll: - skip = true; - d->skipAllEncrypted = true; - break; - default: - ; - } - - if (ec != Ok && !skip) { - break; - } - - ++it; - } - - return ec; -} - -/*! - Extracts a single file to a directory. -*/ -UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options) -{ - return extractFile(filename, QDir(dirname), options); -} - -/*! - Extracts a single file to a directory. -*/ -UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options) -{ - if (!d->device) - return NoOpenArchive; - if (!d->headers) - return FileNotFound; - - QMap::Iterator itr = d->headers->find(filename); - if (itr != d->headers->end()) { - ZipEntryP* entry = itr.value(); - Q_ASSERT(entry != 0); - return d->extractFile(itr.key(), *entry, dir, options); - } - - return FileNotFound; -} - -/*! - Extracts a single file to a directory. -*/ -UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* outDev, ExtractionOptions options) -{ - if (!d->device) - return NoOpenArchive; - if (!d->headers) - return FileNotFound; - if (!outDev) - return InvalidDevice; - - QMap::Iterator itr = d->headers->find(filename); - if (itr != d->headers->end()) { - ZipEntryP* entry = itr.value(); - Q_ASSERT(entry != 0); - return d->extractFile(itr.key(), *entry, outDev, options); - } - - return FileNotFound; -} - -/*! - Extracts a list of files. - Stops extraction at the first error (but continues if a file does not exist in the archive). - */ -UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options) -{ - if (!d->device) - return NoOpenArchive; - if (!d->headers) - return Ok; - - QDir dir(dirname); - ErrorCode ec; - - for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) { - ec = extractFile(*itr, dir, options); - if (ec == FileNotFound) - continue; - if (ec != Ok) - return ec; - } - - return Ok; -} - -/*! - Extracts a list of files. - Stops extraction at the first error (but continues if a file does not exist in the archive). - */ -UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options) -{ - if (!d->device) - return NoOpenArchive; - if (!d->headers) - return Ok; - - ErrorCode ec; - - for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) { - ec = extractFile(*itr, dir, options); - if (ec == FileNotFound) - continue; - if (ec != Ok) - return ec; - } - - return Ok; -} - -/*! - Remove/replace this method to add your own password retrieval routine. -*/ -void UnZip::setPassword(const QString& pwd) -{ - d->password = pwd; -} - -OSDAB_END_NAMESPACE +/**************************************************************************** +** Filename: unzip.cpp +** Last updated [dd/mm/yyyy]: 08/07/2010 +** +** pkzip 2.0 decompression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#include "unzip.h" +#include "unzip_p.h" +#include "zipentry_p.h" + +#include +#include +#include +#include +#include + +// You can remove this #include if you replace the qDebug() statements. +#include + +/*! + \class UnZip unzip.h + + \brief PKZip 2.0 file decompression. + Compatibility with later versions is not ensured as they may use + unsupported compression algorithms. + Versions after 2.7 may have an incompatible header format and thus be + completely incompatible. +*/ + +/*! \enum UnZip::ErrorCode The result of a decompression operation. + \value UnZip::Ok No error occurred. + \value UnZip::ZlibInit Failed to init or load the zlib library. + \value UnZip::ZlibError The zlib library returned some error. + \value UnZip::OpenFailed Unable to create or open a device. + \value UnZip::PartiallyCorrupted Corrupted zip archive - some files could be extracted. + \value UnZip::Corrupted Corrupted or invalid zip archive. + \value UnZip::WrongPassword Unable to decrypt a password protected file. + \value UnZip::NoOpenArchive No archive has been opened yet. + \value UnZip::FileNotFound Unable to find the requested file in the archive. + \value UnZip::ReadFailed Reading of a file failed. + \value UnZip::WriteFailed Writing of a file failed. + \value UnZip::SeekFailed Seek failed. + \value UnZip::CreateDirFailed Could not create a directory. + \value UnZip::InvalidDevice A null device has been passed as parameter. + \value UnZip::InvalidArchive This is not a valid (or supported) ZIP archive. + \value UnZip::HeaderConsistencyError Local header record info does not match with the central directory record info. The archive may be corrupted. + + \value UnZip::Skip Internal use only. + \value UnZip::SkipAll Internal use only. +*/ + +/*! \enum UnZip::ExtractionOptions Some options for the file extraction methods. + \value UnZip::ExtractPaths Default. Does not ignore the path of the zipped files. + \value UnZip::SkipPaths Default. Ignores the path of the zipped files and extracts them all to the same root directory. + \value UnZip::VerifyOnly Doesn't actually extract files. + \value UnZip::NoSilentDirectoryCreation Doesn't attempt to silently create missing output directories. +*/ + +//! Local header size (excluding signature, excluding variable length fields) +#define UNZIP_LOCAL_HEADER_SIZE 26 +//! Central Directory file entry size (excluding signature, excluding variable length fields) +#define UNZIP_CD_ENTRY_SIZE_NS 42 +//! Data descriptor size (excluding signature) +#define UNZIP_DD_SIZE 12 +//! End Of Central Directory size (including signature, excluding variable length fields) +#define UNZIP_EOCD_SIZE 22 +//! Local header entry encryption header size +#define UNZIP_LOCAL_ENC_HEADER_SIZE 12 + +// Some offsets inside a CD record (excluding signature) +#define UNZIP_CD_OFF_VERSION_MADE 0 +#define UNZIP_CD_OFF_VERSION 2 +#define UNZIP_CD_OFF_GPFLAG 4 +#define UNZIP_CD_OFF_CMETHOD 6 +#define UNZIP_CD_OFF_MODT 8 +#define UNZIP_CD_OFF_MODD 10 +#define UNZIP_CD_OFF_CRC32 12 +#define UNZIP_CD_OFF_CSIZE 16 +#define UNZIP_CD_OFF_USIZE 20 +#define UNZIP_CD_OFF_NAMELEN 24 +#define UNZIP_CD_OFF_XLEN 26 +#define UNZIP_CD_OFF_COMMLEN 28 +#define UNZIP_CD_OFF_LHOFFSET 38 + +// Some offsets inside a local header record (excluding signature) +#define UNZIP_LH_OFF_VERSION 0 +#define UNZIP_LH_OFF_GPFLAG 2 +#define UNZIP_LH_OFF_CMETHOD 4 +#define UNZIP_LH_OFF_MODT 6 +#define UNZIP_LH_OFF_MODD 8 +#define UNZIP_LH_OFF_CRC32 10 +#define UNZIP_LH_OFF_CSIZE 14 +#define UNZIP_LH_OFF_USIZE 18 +#define UNZIP_LH_OFF_NAMELEN 22 +#define UNZIP_LH_OFF_XLEN 24 + +// Some offsets inside a data descriptor record (excluding signature) +#define UNZIP_DD_OFF_CRC32 0 +#define UNZIP_DD_OFF_CSIZE 4 +#define UNZIP_DD_OFF_USIZE 8 + +// Some offsets inside a EOCD record +#define UNZIP_EOCD_OFF_ENTRIES 6 +#define UNZIP_EOCD_OFF_CDOFF 12 +#define UNZIP_EOCD_OFF_COMMLEN 16 + +/*! + Max version handled by this API. + 0x14 = 2.0 --> full compatibility only up to this version; + later versions use unsupported features +*/ +#define UNZIP_VERSION 0x14 + +//! CRC32 routine +#define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8) + +OSDAB_BEGIN_NAMESPACE(Zip) + + +/************************************************************************ + ZipEntry +*************************************************************************/ + +/*! + ZipEntry constructor - initialize data. Type is set to File. +*/ +UnZip::ZipEntry::ZipEntry() +{ + compressedSize = uncompressedSize = crc32 = 0; + compression = NoCompression; + type = File; + encrypted = false; +} + + +/************************************************************************ + Private interface +*************************************************************************/ + +//! \internal +UnzipPrivate::UnzipPrivate() : + password(), + skipAllEncrypted(false), + headers(0), + device(0), + file(0), + uBuffer(0), + crcTable(0), + cdOffset(0), + eocdOffset(0), + cdEntryCount(0), + unsupportedEntryCount(0), + comment() +{ + uBuffer = (unsigned char*) buffer1; + crcTable = (quint32*) get_crc_table(); +} + +//! \internal +void UnzipPrivate::deviceDestroyed(QObject*) +{ + qDebug("Unexpected device destruction detected."); + do_closeArchive(); +} + +//! \internal Parses a Zip archive. +UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev) +{ + Q_ASSERT(!device); + Q_ASSERT(dev); + + if (!(dev->isOpen() || dev->open(QIODevice::ReadOnly))) { + qDebug() << "Unable to open device for reading"; + return UnZip::OpenFailed; + } + + device = dev; + if (device != file) + connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*))); + + UnZip::ErrorCode ec; + + ec = seekToCentralDirectory(); + if (ec != UnZip::Ok) { + closeArchive(); + return ec; + } + + //! \todo Ignore CD entry count? CD may be corrupted. + if (cdEntryCount == 0) { + return UnZip::Ok; + } + + bool continueParsing = true; + + while (continueParsing) { + if (device->read(buffer1, 4) != 4) { + if (headers) { + qDebug() << "Corrupted zip archive. Some files might be extracted."; + ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted; + break; + } else { + closeArchive(); + qDebug() << "Corrupted or invalid zip archive. Closing."; + ec = UnZip::Corrupted; + break; + } + } + + if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) ) + break; + + if ((ec = parseCentralDirectoryRecord()) != UnZip::Ok) + break; + } + + if (ec != UnZip::Ok) + closeArchive(); + + return ec; +} + +/* + \internal Parses a local header record and makes some consistency check + with the information stored in the Central Directory record for this entry + that has been previously parsed. + \todo Optional consistency check (as a ExtractionOptions flag) + + local file header signature 4 bytes (0x04034b50) + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + file name length 2 bytes + extra field length 2 bytes + + file name (variable size) + extra field (variable size) +*/ +UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry) +{ + Q_ASSERT(device); + + if (!device->seek(entry.lhOffset)) + return UnZip::SeekFailed; + + // Test signature + if (device->read(buffer1, 4) != 4) + return UnZip::ReadFailed; + + if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04)) + return UnZip::InvalidArchive; + + if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE) + return UnZip::ReadFailed; + + /* + Check 3rd general purpose bit flag. + + "bit 3: If this bit is set, the fields crc-32, compressed size + and uncompressed size are set to zero in the local + header. The correct values are put in the data descriptor + immediately following the compressed data." + */ + bool hasDataDescriptor = entry.hasDataDescriptor(); + bool checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD); + + if (!checkFailed) + checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG]; + if (!checkFailed) + checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1]; + if (!checkFailed) + checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT]; + if (!checkFailed) + checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1]; + if (!checkFailed) + checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD]; + if (!checkFailed) + checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1]; + if (!hasDataDescriptor) + { + if (!checkFailed) + checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32); + if (!checkFailed) + checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE); + if (!checkFailed) + checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE); + } + + if (checkFailed) + return UnZip::HeaderConsistencyError; + + // Check filename + quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN); + if (szName == 0) + return UnZip::HeaderConsistencyError; + + if (device->read(buffer2, szName) != szName) + return UnZip::ReadFailed; + + QString filename = QString::fromLatin1(buffer2, szName); + if (filename != path) { + qDebug() << "Filename in local header mismatches."; + return UnZip::HeaderConsistencyError; + } + + // Skip extra field + quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN); + if (szExtra != 0) { + if (!device->seek(device->pos() + szExtra)) + return UnZip::SeekFailed; + } + + entry.dataOffset = device->pos(); + + if (hasDataDescriptor) { + /* + The data descriptor has this OPTIONAL signature: PK\7\8 + We try to skip the compressed data relying on the size set in the + Central Directory record. + */ + if (!device->seek(device->pos() + entry.szComp)) + return UnZip::SeekFailed; + + // Read 4 bytes and check if there is a data descriptor signature + if (device->read(buffer2, 4) != 4) + return UnZip::ReadFailed; + + bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08; + if (hasSignature) { + if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE) + return UnZip::ReadFailed; + } else { + if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4) + return UnZip::ReadFailed; + } + + // DD: crc, compressed size, uncompressed size + if ( + entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) || + entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) || + entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE) + ) + return UnZip::HeaderConsistencyError; + } + + return UnZip::Ok; +} + +/*! \internal Attempts to find the start of the central directory record. + + We seek the file back until we reach the "End Of Central Directory" + signature PK\5\6. + + end of central dir signature 4 bytes (0x06054b50) + number of this disk 2 bytes + number of the disk with the + start of the central directory 2 bytes + total number of entries in the + central directory on this disk 2 bytes + total number of entries in + the central directory 2 bytes + size of the central directory 4 bytes + offset of start of central + directory with respect to + the starting disk number 4 bytes + .ZIP file comment length 2 bytes + --- SIZE UNTIL HERE: UNZIP_EOCD_SIZE --- + .ZIP file comment (variable size) +*/ +UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory() +{ + Q_ASSERT(device); + + qint64 length = device->size(); + qint64 offset = length - UNZIP_EOCD_SIZE; + + if (length < UNZIP_EOCD_SIZE) + return UnZip::InvalidArchive; + + if (!device->seek( offset )) + return UnZip::SeekFailed; + + if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE) + return UnZip::ReadFailed; + + bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06); + + if (eocdFound) { + // Zip file has no comment (the only variable length field in the EOCD record) + eocdOffset = offset; + } else { + qint64 read; + char* p = 0; + + offset -= UNZIP_EOCD_SIZE; + + if (offset <= 0) + return UnZip::InvalidArchive; + + if (!device->seek( offset )) + return UnZip::SeekFailed; + + while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0) { + if ( (p = strstr(buffer1, "PK\5\6")) != 0) { + // Seek to the start of the EOCD record so we can read it fully + // Yes... we could simply read the missing bytes and append them to the buffer + // but this is far easier so heck it! + device->seek( offset + (p - buffer1) ); + eocdFound = true; + eocdOffset = offset + (p - buffer1); + + // Read EOCD record + if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE) + return UnZip::ReadFailed; + + break; + } + + // TODO: This is very slow and only a temporary bug fix. Need some pattern matching algorithm here. + offset -= 1 /*UNZIP_EOCD_SIZE*/; + if (offset <= 0) + return UnZip::InvalidArchive; + + if (!device->seek( offset )) + return UnZip::SeekFailed; + } + } + + if (!eocdFound) + return UnZip::InvalidArchive; + + // Parse EOCD to locate CD offset + offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4); + + cdOffset = offset; + + cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4); + + quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4); + if (commentLength != 0) { + QByteArray c = device->read(commentLength); + if (c.count() != commentLength) + return UnZip::ReadFailed; + + comment = c; + } + + // Seek to the start of the CD record + if (!device->seek( cdOffset )) + return UnZip::SeekFailed; + + return UnZip::Ok; +} + +/*! + \internal Parses a central directory record. + + Central Directory record structure: + + [file header 1] + . + . + . + [file header n] + [digital signature] // PKZip 6.2 or later only + + File header: + + central file header signature 4 bytes (0x02014b50) + version made by 2 bytes + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + file name length 2 bytes + extra field length 2 bytes + file comment length 2 bytes + disk number start 2 bytes + internal file attributes 2 bytes + external file attributes 4 bytes + relative offset of local header 4 bytes + + file name (variable size) + extra field (variable size) + file comment (variable size) +*/ +UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord() +{ + Q_ASSERT(device); + + // Read CD record + if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS) + return UnZip::ReadFailed; + + bool skipEntry = false; + + // Get compression type so we can skip non compatible algorithms + quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD); + + // Get variable size fields length so we can skip the whole record + // if necessary + quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN); + quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN); + quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN); + + quint32 skipLength = szName + szExtra + szComment; + + UnZip::ErrorCode ec = UnZip::Ok; + + if ((compMethod != 0) && (compMethod != 8)) { + qDebug() << "Unsupported compression method. Skipping file."; + skipEntry = true; + } + + if (!skipEntry && szName == 0) { + qDebug() << "Skipping file with no name."; + skipEntry = true; + } + + QString filename; + if (device->read(buffer2, szName) != szName) { + ec = UnZip::ReadFailed; + skipEntry = true; + } else { + filename = QString::fromLatin1(buffer2, szName); + } + + // Unsupported features if version is bigger than UNZIP_VERSION + if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION) { + QString v = QString::number(buffer1[UNZIP_CD_OFF_VERSION]); + if (v.length() == 2) + v.insert(1, QLatin1Char('.')); + v = QString::fromLatin1("Unsupported PKZip version (%1). Skipping file: %2") + .arg(v, filename.isEmpty() ? QString::fromLatin1("") : filename); + qDebug() << v.toLatin1().constData(); + skipEntry = true; + } + + if (skipEntry) { + if (ec == UnZip::Ok) { + if (!device->seek( device->pos() + skipLength )) + ec = UnZip::SeekFailed; + unsupportedEntryCount++; + } + + return ec; + } + + ZipEntryP* h = new ZipEntryP; + h->compMethod = compMethod; + + h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG]; + h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1]; + + h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT]; + h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1]; + + h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD]; + h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1]; + + h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32); + h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE); + h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE); + + // Skip extra field (if any) + if (szExtra != 0) { + if (!device->seek( device->pos() + szExtra )) { + delete h; + return UnZip::SeekFailed; + } + } + + // Read comment field (if any) + if (szComment != 0) { + if (device->read(buffer2, szComment) != szComment) { + delete h; + return UnZip::ReadFailed; + } + + h->comment = QString::fromLatin1(buffer2, szComment); + } + + h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET); + + if (!headers) + headers = new QMap(); + headers->insert(filename, h); + + return UnZip::Ok; +} + +//! \internal Closes the archive and resets the internal status. +void UnzipPrivate::closeArchive() +{ + if (!device) { + Q_ASSERT(!file); + return; + } + + if (device != file) + disconnect(device, 0, this, 0); + + do_closeArchive(); +} + +//! \internal +void UnzipPrivate::do_closeArchive() +{ + skipAllEncrypted = false; + + if (headers) { + if (headers) + qDeleteAll(*headers); + delete headers; + headers = 0; + } + + device = 0; + + if (file) + delete file; + file = 0; + + cdOffset = eocdOffset = 0; + cdEntryCount = 0; + unsupportedEntryCount = 0; + + comment.clear(); +} + +//! \internal +UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry, + const QDir& dir, UnZip::ExtractionOptions options) +{ + QString name(path); + QString dirname; + QString directory; + + const bool verify = (options & UnZip::VerifyOnly); + const int pos = name.lastIndexOf('/'); + + // This entry is for a directory + if (pos == name.length() - 1) { + if (verify) + return UnZip::Ok; + + if (options & UnZip::SkipPaths) + return UnZip::Ok; + + directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name)); + if (!createDirectory(directory)) { + qDebug() << QString("Unable to create directory: %1").arg(directory); + return UnZip::CreateDirFailed; + } + + return UnZip::Ok; + } + + // Extract path from entry + if (verify) { + return extractFile(path, entry, 0, options); + } + + if (pos > 0) { + // get directory part + dirname = name.left(pos); + if (options & UnZip::SkipPaths) { + directory = dir.absolutePath(); + } else { + directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname)); + if (!createDirectory(directory)) { + qDebug() << QString("Unable to create directory: %1").arg(directory); + return UnZip::CreateDirFailed; + } + } + name = name.right(name.length() - pos - 1); + } else { + directory = dir.absolutePath(); + } + + const bool silentDirectoryCreation = !(options & UnZip::NoSilentDirectoryCreation); + if (silentDirectoryCreation) { + if (!createDirectory(directory)) { + qDebug() << QString("Unable to create output directory %1").arg(directory); + return UnZip::CreateDirFailed; + } + } + + name = QString("%1/%2").arg(directory).arg(name); + + QFile outFile(name); + if (!outFile.open(QIODevice::WriteOnly)) { + qDebug() << QString("Unable to open %1 for writing").arg(name); + return UnZip::OpenFailed; + } + + UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options); + outFile.close(); + + const QDateTime lastModified = convertDateTime(entry.modDate, entry.modTime); + const bool setTimeOk = OSDAB_ZIP_MANGLE(setFileTimestamp)(name, lastModified); + if (!setTimeOk) { + qDebug() << QString("Unable to set last modified time on file: %1").arg(name); + } + + if (ec != UnZip::Ok) { + if (!outFile.remove()) + qDebug() << QString("Unable to remove corrupted file: %1").arg(name); + } + + return ec; +} + +//! \internal +UnZip::ErrorCode UnzipPrivate::extractStoredFile( + const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev, + UnZip::ExtractionOptions options) +{ + const bool verify = (options & UnZip::VerifyOnly); + const bool isEncrypted = keys != 0; + + uInt rep = szComp / UNZIP_READ_BUFFER; + uInt rem = szComp % UNZIP_READ_BUFFER; + uInt cur = 0; + + // extract data + qint64 read; + quint64 tot = 0; + + while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 ) { + if (isEncrypted) + decryptBytes(*keys, buffer1, read); + + myCRC = crc32(myCRC, uBuffer, read); + if (!verify) { + if (outDev->write(buffer1, read) != read) + return UnZip::WriteFailed; + } + + cur++; + tot += read; + if (tot == szComp) + break; + } + + return (read < 0) + ? UnZip::ReadFailed + : UnZip::Ok; +} + +//! \internal +UnZip::ErrorCode UnzipPrivate::inflateFile( + const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev, + UnZip::ExtractionOptions options) +{ + const bool verify = (options & UnZip::VerifyOnly); + const bool isEncrypted = keys != 0; + Q_ASSERT(verify ? true : outDev != 0); + + uInt rep = szComp / UNZIP_READ_BUFFER; + uInt rem = szComp % UNZIP_READ_BUFFER; + uInt cur = 0; + + // extract data + qint64 read; + quint64 tot = 0; + + /* Allocate inflate state */ + z_stream zstr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.next_in = Z_NULL; + zstr.avail_in = 0; + + int zret; + + // Use inflateInit2 with negative windowBits to get raw decompression + if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK ) + return UnZip::ZlibError; + + int szDecomp; + + // Decompress until deflate stream ends or end of file + do { + read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem); + if (!read) + break; + + if (read < 0) { + (void)inflateEnd(&zstr); + return UnZip::ReadFailed; + } + + if (isEncrypted) + decryptBytes(*keys, buffer1, read); + + cur++; + tot += read; + + zstr.avail_in = (uInt) read; + zstr.next_in = (Bytef*) buffer1; + + // Run inflate() on input until output buffer not full + do { + zstr.avail_out = UNZIP_READ_BUFFER; + zstr.next_out = (Bytef*) buffer2;; + + zret = inflate(&zstr, Z_NO_FLUSH); + + switch (zret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&zstr); + return UnZip::WriteFailed; + default: + ; + } + + szDecomp = UNZIP_READ_BUFFER - zstr.avail_out; + if (!verify) { + if (outDev->write(buffer2, szDecomp) != szDecomp) { + inflateEnd(&zstr); + return UnZip::ZlibError; + } + } + + myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp); + + } while (zstr.avail_out == 0); + + } while (zret != Z_STREAM_END); + + inflateEnd(&zstr); + return UnZip::Ok; +} + +//! \internal \p outDev is null if the VerifyOnly option is set +UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry, + QIODevice* outDev, UnZip::ExtractionOptions options) +{ + const bool verify = (options & UnZip::VerifyOnly); + + Q_UNUSED(options); + Q_ASSERT(device); + Q_ASSERT(verify ? true : outDev != 0); + + if (!entry.lhEntryChecked) { + UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry); + entry.lhEntryChecked = true; + if (ec != UnZip::Ok) + return ec; + } + + if (!device->seek(entry.dataOffset)) + return UnZip::SeekFailed; + + // Encryption keys + quint32 keys[3]; + quint32 szComp = entry.szComp; + if (entry.isEncrypted()) { + UnZip::ErrorCode e = testPassword(keys, path, entry); + if (e != UnZip::Ok) + { + qDebug() << QString("Unable to decrypt %1").arg(path); + return e; + }//! Encryption header size + szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE; // remove encryption header size + } + + if (szComp == 0) { + if (entry.crc != 0) + return UnZip::Corrupted; + return UnZip::Ok; + } + + quint32 myCRC = crc32(0L, Z_NULL, 0); + quint32* k = keys; + + UnZip::ErrorCode ec = UnZip::Ok; + if (entry.compMethod == 0) { + ec = extractStoredFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options); + } else if (entry.compMethod == 8) { + ec = inflateFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options); + } + + if (ec == UnZip::Ok && myCRC != entry.crc) + return UnZip::Corrupted; + + return UnZip::Ok; +} + +//! \internal Creates a new directory and all the needed parent directories. +bool UnzipPrivate::createDirectory(const QString& path) +{ + QDir d(path); + if (!d.exists() && !d.mkpath(path)) { + qDebug() << QString("Unable to create directory: %1").arg(path); + return false; + } + + return true; +} + +/*! + \internal Reads an quint32 (4 bytes) from a byte array starting at given offset. +*/ +quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const +{ + quint32 res = (quint32) data[offset]; + res |= (((quint32)data[offset+1]) << 8); + res |= (((quint32)data[offset+2]) << 16); + res |= (((quint32)data[offset+3]) << 24); + + return res; +} + +/*! + \internal Reads an quint64 (8 bytes) from a byte array starting at given offset. +*/ +quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const +{ + quint64 res = (quint64) data[offset]; + res |= (((quint64)data[offset+1]) << 8); + res |= (((quint64)data[offset+2]) << 16); + res |= (((quint64)data[offset+3]) << 24); + res |= (((quint64)data[offset+1]) << 32); + res |= (((quint64)data[offset+2]) << 40); + res |= (((quint64)data[offset+3]) << 48); + res |= (((quint64)data[offset+3]) << 56); + + return res; +} + +/*! + \internal Reads an quint16 (2 bytes) from a byte array starting at given offset. +*/ +quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const +{ + return (quint16) data[offset] | (((quint16)data[offset+1]) << 8); +} + +/*! + \internal Return the next byte in the pseudo-random sequence + */ +int UnzipPrivate::decryptByte(quint32 key2) const +{ + quint16 temp = ((quint16)(key2) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*! + \internal Update the encryption keys with the next byte of plain text + */ +void UnzipPrivate::updateKeys(quint32* keys, int c) const +{ + keys[0] = CRC32(keys[0], c); + keys[1] += keys[0] & 0xff; + keys[1] = keys[1] * 134775813L + 1; + keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24); +} + +/*! + \internal Initialize the encryption keys and the random header according to + the given password. + */ +void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const +{ + keys[0] = 305419896L; + keys[1] = 591751049L; + keys[2] = 878082192L; + + QByteArray pwdBytes = pwd.toLatin1(); + int sz = pwdBytes.size(); + const char* ascii = pwdBytes.data(); + + for (int i = 0; i < sz; ++i) + updateKeys(keys, (int)ascii[i]); +} + +/*! + \internal Attempts to test a password without actually extracting a file. + The \p file parameter can be used in the user interface or for debugging purposes + as it is the name of the encrypted file for wich the password is being tested. +*/ +UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header) +{ + Q_UNUSED(file); + Q_ASSERT(device); + + // read encryption keys + if (device->read(buffer1, 12) != 12) + return UnZip::Corrupted; + + // Replace this code if you want to i.e. call some dialog and ask the user for a password + initKeys(password, keys); + if (testKeys(header, keys)) + return UnZip::Ok; + + return UnZip::Skip; +} + +/*! + \internal Tests a set of keys on the encryption header. +*/ +bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys) +{ + char lastByte; + + // decrypt encryption header + for (int i = 0; i < 11; ++i) + updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2])); + updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2])); + + // if there is an extended header (bit in the gp flag) buffer[11] is a byte from the file time + // with no extended header we have to check the crc high-order byte + char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24; + + return (lastByte == c); +} + +/*! + \internal Decrypts an array of bytes long \p read. +*/ +void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read) +{ + for (int i = 0; i < (int)read; ++i) + updateKeys(keys, buffer[i] ^= decryptByte(keys[2])); +} + +/*! + \internal Converts date and time values from ZIP format to a QDateTime object. +*/ +QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const +{ + QDateTime dt; + + // Usual PKZip low-byte to high-byte order + + // Date: 7 bits = years from 1980, 4 bits = month, 5 bits = day + quint16 year = (date[1] >> 1) & 127; + quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7); + quint16 day = date[0] & 31; + + // Time: 5 bits hour, 6 bits minutes, 5 bits seconds with a 2sec precision + quint16 hour = (time[1] >> 3) & 31; + quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7); + quint16 seconds = (time[0] & 31) * 2; + + dt.setDate(QDate(1980 + year, month, day)); + dt.setTime(QTime(hour, minutes, seconds)); + return dt; +} + + +/************************************************************************ + Public interface +*************************************************************************/ + +/*! + Creates a new Zip file decompressor. +*/ +UnZip::UnZip() : d(new UnzipPrivate) +{ +} + +/*! + Closes any open archive and releases used resources. +*/ +UnZip::~UnZip() +{ + closeArchive(); + delete d; +} + +/*! + Returns true if there is an open archive. +*/ +bool UnZip::isOpen() const +{ + return d->device; +} + +/*! + Opens a zip archive and reads the files list. Closes any previously opened archive. +*/ +UnZip::ErrorCode UnZip::openArchive(const QString& filename) +{ + closeArchive(); + + // closeArchive will destroy the file + d->file = new QFile(filename); + + if (!d->file->exists()) { + delete d->file; + d->file = 0; + return UnZip::FileNotFound; + } + + if (!d->file->open(QIODevice::ReadOnly)) { + delete d->file; + d->file = 0; + return UnZip::OpenFailed; + } + + return d->openArchive(d->file); +} + +/*! + Opens a zip archive and reads the entries list. + Closes any previously opened archive. + \warning The class takes DOES NOT take ownership of the device. +*/ +UnZip::ErrorCode UnZip::openArchive(QIODevice* device) +{ + closeArchive(); + + if (!device) { + qDebug() << "Invalid device."; + return UnZip::InvalidDevice; + } + + return d->openArchive(device); +} + +/*! + Closes the archive and releases all the used resources (like cached passwords). +*/ +void UnZip::closeArchive() +{ + d->closeArchive(); +} + +QString UnZip::archiveComment() const +{ + return d->comment; +} + +/*! + Returns a locale translated error string for a given error code. +*/ +QString UnZip::formatError(UnZip::ErrorCode c) const +{ + switch (c) + { + case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break; + case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break; + case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break; + case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break; + case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break; + case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break; + case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break; + case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break; + case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break; + case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break; + case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break; + case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break; + case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break; + case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break; + case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break; + case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break; + default: ; + } + + return QCoreApplication::translate("UnZip", "Unknown error."); +} + +/*! + Returns true if the archive contains a file with the given path and name. +*/ +bool UnZip::contains(const QString& file) const +{ + return d->headers ? d->headers->contains(file) : false; +} + +/*! + Returns complete paths of files and directories in this archive. +*/ +QStringList UnZip::fileList() const +{ + return d->headers ? d->headers->keys() : QStringList(); +} + +/*! + Returns information for each (correctly parsed) entry of this archive. +*/ +QList UnZip::entryList() const +{ + QList list; + if (!d->headers) + return list; + + for (QMap::ConstIterator it = d->headers->constBegin(); + it != d->headers->constEnd(); ++it) { + const ZipEntryP* entry = it.value(); + Q_ASSERT(entry != 0); + + ZipEntry z; + + z.filename = it.key(); + if (!entry->comment.isEmpty()) + z.comment = entry->comment; + z.compressedSize = entry->szComp; + z.uncompressedSize = entry->szUncomp; + z.crc32 = entry->crc; + z.lastModified = d->convertDateTime(entry->modDate, entry->modTime); + + z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression; + z.type = z.filename.endsWith("/") ? Directory : File; + + z.encrypted = entry->isEncrypted(); + + list.append(z); + } + + return list; +} + +/*! + Extracts the whole archive to a directory. +*/ +UnZip::ErrorCode UnZip::verifyArchive() +{ + return extractAll(QDir(), VerifyOnly); +} + +/*! + Extracts the whole archive to a directory. +*/ +UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options) +{ + return extractAll(QDir(dirname), options); +} + +/*! + Extracts the whole archive to a directory. + Stops extraction at the first error. +*/ +UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options) +{ + // this should only happen if we didn't call openArchive() yet + if (!d->device) + return NoOpenArchive; + + if (!d->headers) + return Ok; + + ErrorCode ec = Ok; + + QMap::ConstIterator it = d->headers->constBegin(); + const QMap::ConstIterator end = d->headers->constEnd(); + while (it != end) { + ZipEntryP* entry = it.value(); + Q_ASSERT(entry != 0); + if ((entry->isEncrypted()) && d->skipAllEncrypted) { + ++it; + continue; + } + + bool skip = false; + ec = d->extractFile(it.key(), *entry, dir, options); + switch (ec) { + case Corrupted: + qDebug() << "Corrupted entry" << it.key(); + break; + case CreateDirFailed: + break; + case Skip: + skip = true; + break; + case SkipAll: + skip = true; + d->skipAllEncrypted = true; + break; + default: + ; + } + + if (ec != Ok && !skip) { + break; + } + + ++it; + } + + return ec; +} + +/*! + Extracts a single file to a directory. +*/ +UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options) +{ + return extractFile(filename, QDir(dirname), options); +} + +/*! + Extracts a single file to a directory. +*/ +UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options) +{ + if (!d->device) + return NoOpenArchive; + if (!d->headers) + return FileNotFound; + + QMap::Iterator itr = d->headers->find(filename); + if (itr != d->headers->end()) { + ZipEntryP* entry = itr.value(); + Q_ASSERT(entry != 0); + return d->extractFile(itr.key(), *entry, dir, options); + } + + return FileNotFound; +} + +/*! + Extracts a single file to a directory. +*/ +UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* outDev, ExtractionOptions options) +{ + if (!d->device) + return NoOpenArchive; + if (!d->headers) + return FileNotFound; + if (!outDev) + return InvalidDevice; + + QMap::Iterator itr = d->headers->find(filename); + if (itr != d->headers->end()) { + ZipEntryP* entry = itr.value(); + Q_ASSERT(entry != 0); + return d->extractFile(itr.key(), *entry, outDev, options); + } + + return FileNotFound; +} + +/*! + Extracts a list of files. + Stops extraction at the first error (but continues if a file does not exist in the archive). + */ +UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options) +{ + if (!d->device) + return NoOpenArchive; + if (!d->headers) + return Ok; + + QDir dir(dirname); + ErrorCode ec; + + for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) { + ec = extractFile(*itr, dir, options); + if (ec == FileNotFound) + continue; + if (ec != Ok) + return ec; + } + + return Ok; +} + +/*! + Extracts a list of files. + Stops extraction at the first error (but continues if a file does not exist in the archive). + */ +UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options) +{ + if (!d->device) + return NoOpenArchive; + if (!d->headers) + return Ok; + + ErrorCode ec; + + for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) { + ec = extractFile(*itr, dir, options); + if (ec == FileNotFound) + continue; + if (ec != Ok) + return ec; + } + + return Ok; +} + +/*! + Remove/replace this method to add your own password retrieval routine. +*/ +void UnZip::setPassword(const QString& pwd) +{ + d->password = pwd; +} + +OSDAB_END_NAMESPACE diff --git a/oracle/src/zip/unzip.h b/oracle/src/zip/unzip.h index e095a228..7171c8b8 100755 --- a/oracle/src/zip/unzip.h +++ b/oracle/src/zip/unzip.h @@ -1,152 +1,152 @@ -/**************************************************************************** -** Filename: unzip.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 decompression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#ifndef OSDAB_UNZIP__H -#define OSDAB_UNZIP__H - -#include "zipglobal.h" - -#include -#include -#include - -#include - -class QDir; -class QFile; -class QIODevice; -class QString; -class QStringList; - -OSDAB_BEGIN_NAMESPACE(Zip) - -class UnzipPrivate; - -class OSDAB_ZIP_EXPORT UnZip -{ -public: - enum ErrorCode - { - Ok, - ZlibInit, - ZlibError, - OpenFailed, - PartiallyCorrupted, - Corrupted, - WrongPassword, - NoOpenArchive, - FileNotFound, - ReadFailed, - WriteFailed, - SeekFailed, - CreateDirFailed, - InvalidDevice, - InvalidArchive, - HeaderConsistencyError, - - Skip, SkipAll // internal use only - }; - - enum ExtractionOption - { - ExtractPaths = 0x0001, - SkipPaths = 0x0002, - VerifyOnly = 0x0004, - NoSilentDirectoryCreation = 0x0008 - }; - Q_DECLARE_FLAGS(ExtractionOptions, ExtractionOption) - - enum CompressionMethod - { - NoCompression, Deflated, UnknownCompression - }; - - enum FileType - { - File, Directory - }; - - struct ZipEntry - { - ZipEntry(); - - QString filename; - QString comment; - - quint32 compressedSize; - quint32 uncompressedSize; - quint32 crc32; - - QDateTime lastModified; - - CompressionMethod compression; - FileType type; - - bool encrypted; - }; - - UnZip(); - virtual ~UnZip(); - - bool isOpen() const; - - ErrorCode openArchive(const QString& filename); - ErrorCode openArchive(QIODevice* device); - void closeArchive(); - - QString archiveComment() const; - - QString formatError(UnZip::ErrorCode c) const; - - bool contains(const QString& file) const; - - QStringList fileList() const; - QList entryList() const; - - ErrorCode verifyArchive(); - - ErrorCode extractAll(const QString& dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractAll(const QDir& dir, ExtractionOptions options = ExtractPaths); - - ErrorCode extractFile(const QString& filename, const QString& dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractFile(const QString& filename, const QDir& dir, ExtractionOptions options = ExtractPaths); - ErrorCode extractFile(const QString& filename, QIODevice* device, ExtractionOptions options = ExtractPaths); - - ErrorCode extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options = ExtractPaths); - - void setPassword(const QString& pwd); - -private: - UnzipPrivate* d; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(UnZip::ExtractionOptions) - -OSDAB_END_NAMESPACE - -#endif // OSDAB_UNZIP__H +/**************************************************************************** +** Filename: unzip.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 decompression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#ifndef OSDAB_UNZIP__H +#define OSDAB_UNZIP__H + +#include "zipglobal.h" + +#include +#include +#include + +#include + +class QDir; +class QFile; +class QIODevice; +class QString; +class QStringList; + +OSDAB_BEGIN_NAMESPACE(Zip) + +class UnzipPrivate; + +class OSDAB_ZIP_EXPORT UnZip +{ +public: + enum ErrorCode + { + Ok, + ZlibInit, + ZlibError, + OpenFailed, + PartiallyCorrupted, + Corrupted, + WrongPassword, + NoOpenArchive, + FileNotFound, + ReadFailed, + WriteFailed, + SeekFailed, + CreateDirFailed, + InvalidDevice, + InvalidArchive, + HeaderConsistencyError, + + Skip, SkipAll // internal use only + }; + + enum ExtractionOption + { + ExtractPaths = 0x0001, + SkipPaths = 0x0002, + VerifyOnly = 0x0004, + NoSilentDirectoryCreation = 0x0008 + }; + Q_DECLARE_FLAGS(ExtractionOptions, ExtractionOption) + + enum CompressionMethod + { + NoCompression, Deflated, UnknownCompression + }; + + enum FileType + { + File, Directory + }; + + struct ZipEntry + { + ZipEntry(); + + QString filename; + QString comment; + + quint32 compressedSize; + quint32 uncompressedSize; + quint32 crc32; + + QDateTime lastModified; + + CompressionMethod compression; + FileType type; + + bool encrypted; + }; + + UnZip(); + virtual ~UnZip(); + + bool isOpen() const; + + ErrorCode openArchive(const QString& filename); + ErrorCode openArchive(QIODevice* device); + void closeArchive(); + + QString archiveComment() const; + + QString formatError(UnZip::ErrorCode c) const; + + bool contains(const QString& file) const; + + QStringList fileList() const; + QList entryList() const; + + ErrorCode verifyArchive(); + + ErrorCode extractAll(const QString& dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractAll(const QDir& dir, ExtractionOptions options = ExtractPaths); + + ErrorCode extractFile(const QString& filename, const QString& dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractFile(const QString& filename, const QDir& dir, ExtractionOptions options = ExtractPaths); + ErrorCode extractFile(const QString& filename, QIODevice* device, ExtractionOptions options = ExtractPaths); + + ErrorCode extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options = ExtractPaths); + + void setPassword(const QString& pwd); + +private: + UnzipPrivate* d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(UnZip::ExtractionOptions) + +OSDAB_END_NAMESPACE + +#endif // OSDAB_UNZIP__H diff --git a/oracle/src/zip/unzip_p.h b/oracle/src/zip/unzip_p.h index 21e9f2a2..c389fcd6 100755 --- a/oracle/src/zip/unzip_p.h +++ b/oracle/src/zip/unzip_p.h @@ -1,130 +1,130 @@ -/**************************************************************************** -** Filename: unzip_p.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 decompression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Zip/UnZip API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef OSDAB_UNZIP_P__H -#define OSDAB_UNZIP_P__H - -#include "unzip.h" -#include "zipentry_p.h" - -#include -#include - -// zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate()) -// we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;) -#define UNZIP_READ_BUFFER (256*1024) - -OSDAB_BEGIN_NAMESPACE(Zip) - -class UnzipPrivate : public QObject -{ - Q_OBJECT - -public: - UnzipPrivate(); - - // Replace this with whatever else you use to store/retrieve the password. - QString password; - - bool skipAllEncrypted; - - QMap* headers; - - QIODevice* device; - QFile* file; - - char buffer1[UNZIP_READ_BUFFER]; - char buffer2[UNZIP_READ_BUFFER]; - - unsigned char* uBuffer; - const quint32* crcTable; - - // Central Directory (CD) offset - quint32 cdOffset; - // End of Central Directory (EOCD) offset - quint32 eocdOffset; - - // Number of entries in the Central Directory (as to the EOCD record) - quint16 cdEntryCount; - - // The number of detected entries that have been skipped because of a non compatible format - quint16 unsupportedEntryCount; - - QString comment; - - UnZip::ErrorCode openArchive(QIODevice* device); - - UnZip::ErrorCode seekToCentralDirectory(); - UnZip::ErrorCode parseCentralDirectoryRecord(); - UnZip::ErrorCode parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry); - - void closeArchive(); - - UnZip::ErrorCode extractFile(const QString& path, const ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options); - UnZip::ErrorCode extractFile(const QString& path, const ZipEntryP& entry, QIODevice* device, UnZip::ExtractionOptions options); - - UnZip::ErrorCode testPassword(quint32* keys, const QString& file, const ZipEntryP& header); - bool testKeys(const ZipEntryP& header, quint32* keys); - - bool createDirectory(const QString& path); - - inline void decryptBytes(quint32* keys, char* buffer, qint64 read); - - inline quint32 getULong(const unsigned char* data, quint32 offset) const; - inline quint64 getULLong(const unsigned char* data, quint32 offset) const; - inline quint16 getUShort(const unsigned char* data, quint32 offset) const; - inline int decryptByte(quint32 key2) const; - inline void updateKeys(quint32* keys, int c) const; - inline void initKeys(const QString& pwd, quint32* keys) const; - - inline QDateTime convertDateTime(const unsigned char date[2], const unsigned char time[2]) const; - -private slots: - void deviceDestroyed(QObject*); - -private: - UnZip::ErrorCode extractStoredFile(const quint32 szComp, quint32** keys, - quint32& myCRC, QIODevice* outDev, UnZip::ExtractionOptions options); - UnZip::ErrorCode inflateFile(const quint32 szComp, quint32** keys, - quint32& myCRC, QIODevice* outDev, UnZip::ExtractionOptions options); - void do_closeArchive(); -}; - -OSDAB_END_NAMESPACE - -#endif // OSDAB_UNZIP_P__H +/**************************************************************************** +** Filename: unzip_p.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 decompression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Zip/UnZip API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef OSDAB_UNZIP_P__H +#define OSDAB_UNZIP_P__H + +#include "unzip.h" +#include "zipentry_p.h" + +#include +#include + +// zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate()) +// we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;) +#define UNZIP_READ_BUFFER (256*1024) + +OSDAB_BEGIN_NAMESPACE(Zip) + +class UnzipPrivate : public QObject +{ + Q_OBJECT + +public: + UnzipPrivate(); + + // Replace this with whatever else you use to store/retrieve the password. + QString password; + + bool skipAllEncrypted; + + QMap* headers; + + QIODevice* device; + QFile* file; + + char buffer1[UNZIP_READ_BUFFER]; + char buffer2[UNZIP_READ_BUFFER]; + + unsigned char* uBuffer; + const quint32* crcTable; + + // Central Directory (CD) offset + quint32 cdOffset; + // End of Central Directory (EOCD) offset + quint32 eocdOffset; + + // Number of entries in the Central Directory (as to the EOCD record) + quint16 cdEntryCount; + + // The number of detected entries that have been skipped because of a non compatible format + quint16 unsupportedEntryCount; + + QString comment; + + UnZip::ErrorCode openArchive(QIODevice* device); + + UnZip::ErrorCode seekToCentralDirectory(); + UnZip::ErrorCode parseCentralDirectoryRecord(); + UnZip::ErrorCode parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry); + + void closeArchive(); + + UnZip::ErrorCode extractFile(const QString& path, const ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options); + UnZip::ErrorCode extractFile(const QString& path, const ZipEntryP& entry, QIODevice* device, UnZip::ExtractionOptions options); + + UnZip::ErrorCode testPassword(quint32* keys, const QString& file, const ZipEntryP& header); + bool testKeys(const ZipEntryP& header, quint32* keys); + + bool createDirectory(const QString& path); + + inline void decryptBytes(quint32* keys, char* buffer, qint64 read); + + inline quint32 getULong(const unsigned char* data, quint32 offset) const; + inline quint64 getULLong(const unsigned char* data, quint32 offset) const; + inline quint16 getUShort(const unsigned char* data, quint32 offset) const; + inline int decryptByte(quint32 key2) const; + inline void updateKeys(quint32* keys, int c) const; + inline void initKeys(const QString& pwd, quint32* keys) const; + + inline QDateTime convertDateTime(const unsigned char date[2], const unsigned char time[2]) const; + +private slots: + void deviceDestroyed(QObject*); + +private: + UnZip::ErrorCode extractStoredFile(const quint32 szComp, quint32** keys, + quint32& myCRC, QIODevice* outDev, UnZip::ExtractionOptions options); + UnZip::ErrorCode inflateFile(const quint32 szComp, quint32** keys, + quint32& myCRC, QIODevice* outDev, UnZip::ExtractionOptions options); + void do_closeArchive(); +}; + +OSDAB_END_NAMESPACE + +#endif // OSDAB_UNZIP_P__H diff --git a/oracle/src/zip/zip.cpp b/oracle/src/zip/zip.cpp index aa33bfc7..5b017729 100755 --- a/oracle/src/zip/zip.cpp +++ b/oracle/src/zip/zip.cpp @@ -1,1619 +1,1619 @@ -/**************************************************************************** -** Filename: zip.cpp -** Last updated [dd/mm/yyyy]: 01/02/2007 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#include "zip.h" -#include "zip_p.h" -#include "zipentry_p.h" - -// we only use this to seed the random number generator -#include - -#include -#include -#include -#include -#include -#include -#include - -// You can remove this #include if you replace the qDebug() statements. -#include - - -/*! #define OSDAB_ZIP_NO_PNG_RLE to disable the use of Z_RLE compression strategy with - PNG files (achieves slightly better compression levels according to the authors). -*/ -// #define OSDAB_ZIP_NO_PNG_RLE - -#define OSDAB_ZIP_NO_DEBUG - -//! Local header size (including signature, excluding variable length fields) -#define ZIP_LOCAL_HEADER_SIZE 30 -//! Encryption header size -#define ZIP_LOCAL_ENC_HEADER_SIZE 12 -//! Data descriptor size (signature included) -#define ZIP_DD_SIZE_WS 16 -//! Central Directory record size (signature included) -#define ZIP_CD_SIZE 46 -//! End of Central Directory record size (signature included) -#define ZIP_EOCD_SIZE 22 - -// Some offsets inside a local header record (signature included) -#define ZIP_LH_OFF_VERS 4 -#define ZIP_LH_OFF_GPFLAG 6 -#define ZIP_LH_OFF_CMET 8 -#define ZIP_LH_OFF_MODT 10 -#define ZIP_LH_OFF_MODD 12 -#define ZIP_LH_OFF_CRC 14 -#define ZIP_LH_OFF_CSIZE 18 -#define ZIP_LH_OFF_USIZE 22 -#define ZIP_LH_OFF_NAMELEN 26 -#define ZIP_LH_OFF_XLEN 28 - -// Some offsets inside a data descriptor record (including signature) -#define ZIP_DD_OFF_CRC32 4 -#define ZIP_DD_OFF_CSIZE 8 -#define ZIP_DD_OFF_USIZE 12 - -// Some offsets inside a Central Directory record (including signature) -#define ZIP_CD_OFF_MADEBY 4 -#define ZIP_CD_OFF_VERSION 6 -#define ZIP_CD_OFF_GPFLAG 8 -#define ZIP_CD_OFF_CMET 10 -#define ZIP_CD_OFF_MODT 12 -#define ZIP_CD_OFF_MODD 14 -#define ZIP_CD_OFF_CRC 16 -#define ZIP_CD_OFF_CSIZE 20 -#define ZIP_CD_OFF_USIZE 24 -#define ZIP_CD_OFF_NAMELEN 28 -#define ZIP_CD_OFF_XLEN 30 -#define ZIP_CD_OFF_COMMLEN 32 -#define ZIP_CD_OFF_DISKSTART 34 -#define ZIP_CD_OFF_IATTR 36 -#define ZIP_CD_OFF_EATTR 38 -#define ZIP_CD_OFF_LHOFF 42 - -// Some offsets inside a EOCD record (including signature) -#define ZIP_EOCD_OFF_DISKNUM 4 -#define ZIP_EOCD_OFF_CDDISKNUM 6 -#define ZIP_EOCD_OFF_ENTRIES 8 -#define ZIP_EOCD_OFF_CDENTRIES 10 -#define ZIP_EOCD_OFF_CDSIZE 12 -#define ZIP_EOCD_OFF_CDOFF 16 -#define ZIP_EOCD_OFF_COMMLEN 20 - -//! PKZip version for archives created by this API -#define ZIP_VERSION 0x14 - -//! Do not store very small files as the compression headers overhead would be to big -#define ZIP_COMPRESSION_THRESHOLD 60 - -/*! - \class Zip zip.h - - \brief Zip file compression. - - Some quick usage examples. - - \verbatim - Suppose you have this directory structure: - - /home/user/dir1/file1.1 - /home/user/dir1/file1.2 - /home/user/dir1/dir1.1/ - /home/user/dir1/dir1.2/file1.2.1 - - EXAMPLE 1: - myZipInstance.addDirectory("/home/user/dir1"); - - RESULT: - Beheaves like any common zip software and creates a zip file with this structure: - - dir1/file1.1 - dir1/file1.2 - dir1/dir1.1/ - dir1/dir1.2/file1.2.1 - - EXAMPLE 2: - myZipInstance.addDirectory("/home/user/dir1", "myRoot/myFolder"); - - RESULT: - Adds a custom root to the paths and creates a zip file with this structure: - - myRoot/myFolder/dir1/file1.1 - myRoot/myFolder/dir1/file1.2 - myRoot/myFolder/dir1/dir1.1/ - myRoot/myFolder/dir1/dir1.2/file1.2.1 - - EXAMPLE 3: - myZipInstance.addDirectory("/home/user/dir1", Zip::AbsolutePaths); - - NOTE: - Same as calling addDirectory(SOME_PATH, PARENT_PATH_of_SOME_PATH). - - RESULT: - Preserves absolute paths and creates a zip file with this structure: - - /home/user/dir1/file1.1 - /home/user/dir1/file1.2 - /home/user/dir1/dir1.1/ - /home/user/dir1/dir1.2/file1.2.1 - - EXAMPLE 4: - myZipInstance.setPassword("hellopass"); - myZipInstance.addDirectory("/home/user/dir1", "/"); - - RESULT: - Adds and encrypts the files in /home/user/dir1, creating the following zip structure: - - /dir1/file1.1 - /dir1/file1.2 - /dir1/dir1.1/ - /dir1/dir1.2/file1.2.1 - - EXAMPLE 5: - myZipInstance.addDirectory("/home/user/dir1", Zip::IgnoreRoot); - - RESULT: - Adds the files in /home/user/dir1 but doesn't create the top level - directory: - - file1.1 - file1.2 - dir1.1/ - dir1.2/file1.2.1 - - EXAMPLE 5: - myZipInstance.addDirectory("/home/user/dir1", "data/backup", Zip::IgnoreRoot); - - RESULT: - Adds the files in /home/user/dir1 but uses "data/backup" as top level - directory instead of "dir1": - - data/backup/file1.1 - data/backup/file1.2 - data/backup/dir1.1/ - data/backup/dir1.2/file1.2.1 - - \endverbatim -*/ - -/*! \enum Zip::ErrorCode The result of a compression operation. - \value Zip::Ok No error occurred. - \value Zip::ZlibInit Failed to init or load the zlib library. - \value Zip::ZlibError The zlib library returned some error. - \value Zip::FileExists The file already exists and will not be overwritten. - \value Zip::OpenFailed Unable to create or open a device. - \value Zip::NoOpenArchive CreateArchive() has not been called yet. - \value Zip::FileNotFound File or directory does not exist. - \value Zip::ReadFailed Reading of a file failed. - \value Zip::WriteFailed Writing of a file failed. - \value Zip::SeekFailed Seek failed. -*/ - -/*! \enum Zip::CompressionLevel Returns the result of a decompression operation. - \value Zip::Store No compression. - \value Zip::Deflate1 Deflate compression level 1(lowest compression). - \value Zip::Deflate1 Deflate compression level 2. - \value Zip::Deflate1 Deflate compression level 3. - \value Zip::Deflate1 Deflate compression level 4. - \value Zip::Deflate1 Deflate compression level 5. - \value Zip::Deflate1 Deflate compression level 6. - \value Zip::Deflate1 Deflate compression level 7. - \value Zip::Deflate1 Deflate compression level 8. - \value Zip::Deflate1 Deflate compression level 9 (maximum compression). - \value Zip::AutoCPU Adapt compression level to CPU speed (faster CPU => better compression). - \value Zip::AutoMIME Adapt compression level to MIME type of the file being compressed. - \value Zip::AutoFull Use both CPU and MIME type detection. -*/ - -namespace { - -struct ZippedDir { - bool init; - QString actualRoot; - int files; - ZippedDir() : init(false), actualRoot(), files(0) {} -}; - -void checkRootPath(QString& path) -{ - const bool isUnixRoot = path.length() == 1 && path.at(0) == QLatin1Char('/'); - if (!path.isEmpty() && !isUnixRoot) { - while (path.endsWith(QLatin1String("\\"))) - path.truncate(path.length() - 1); - - int sepCount = 0; - for (int i = path.length()-1; i >= 0; --i) { - if (path.at(i) == QLatin1Char('/')) - ++sepCount; - else break; - } - - if (sepCount > 1) - path.truncate(path.length() - (sepCount-1)); - else if (sepCount == 0) - path.append(QLatin1String("/")); - } -} - -} - -////////////////////////////////////////////////////////////////////////// - -OSDAB_BEGIN_NAMESPACE(Zip) - -/************************************************************************ - Private interface -*************************************************************************/ - -//! \internal -ZipPrivate::ZipPrivate() : - headers(0), - device(0), - file(0), - uBuffer(0), - crcTable(0), - comment(), - password() -{ - // keep an unsigned pointer so we avoid to over bloat the code with casts - uBuffer = (unsigned char*) buffer1; - crcTable = get_crc_table(); -} - -//! \internal -ZipPrivate::~ZipPrivate() -{ - closeArchive(); -} - -//! \internal -Zip::ErrorCode ZipPrivate::createArchive(QIODevice* dev) -{ - Q_ASSERT(dev); - - if (device) - closeArchive(); - - device = dev; - if (device != file) - connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*))); - - if (!device->isOpen()) { - if (!device->open(QIODevice::ReadOnly)) { - delete device; - device = 0; - qDebug() << "Unable to open device for writing."; - return Zip::OpenFailed; - } - } - - headers = new QMap; - return Zip::Ok; -} - -//! \internal -void ZipPrivate::deviceDestroyed(QObject*) -{ - qDebug("Unexpected device destruction detected."); - do_closeArchive(); -} - -/*! Returns true if an entry for \p info has already been added. - Uses file size and lower case absolute path to compare entries. -*/ -bool ZipPrivate::containsEntry(const QFileInfo& info) const -{ - if (!headers || headers->isEmpty()) - return false; - - const qint64 sz = info.size(); - const QString path = info.absoluteFilePath().toLower(); - - QMap::ConstIterator b = headers->constBegin(); - const QMap::ConstIterator e = headers->constEnd(); - while (b != e) { - const ZipEntryP* e = b.value(); - if (e->fileSize == sz && e->absolutePath == path) - return true; - ++b; - } - - return false; -} - -//! \internal Actual implementation of the addDirectory* methods. -Zip::ErrorCode ZipPrivate::addDirectory(const QString& path, const QString& root, - Zip::CompressionOptions options, Zip::CompressionLevel level, int hierarchyLevel, - int* addedFiles) -{ - if (addedFiles) - ++(*addedFiles); - - // Bad boy didn't call createArchive() yet :) - if (!device) - return Zip::NoOpenArchive; - - QDir dir(path); - if (!dir.exists()) - return Zip::FileNotFound; - - // Remove any trailing separator - QString actualRoot = root.trimmed(); - - // Preserve Unix root but make sure the path ends only with a single - // unix like separator - ::checkRootPath(actualRoot); - - // QDir::cleanPath() fixes some issues with QDir::dirName() - QFileInfo current(QDir::cleanPath(path)); - - const bool path_absolute = options.testFlag(Zip::AbsolutePaths); - const bool path_ignore = options.testFlag(Zip::IgnorePaths); - const bool path_noroot = options.testFlag(Zip::IgnoreRoot); - - if (path_absolute && !path_ignore && !path_noroot) { - QString absolutePath = extractRoot(path, options); - if (!absolutePath.isEmpty() && absolutePath != QLatin1String("/")) - absolutePath.append(QLatin1String("/")); - actualRoot.append(absolutePath); - } - - const bool skipDirName = !hierarchyLevel && path_noroot; - if (!path_ignore && !skipDirName) { - actualRoot.append(QDir(current.absoluteFilePath()).dirName()); - actualRoot.append(QLatin1String("/")); - } - - // actualRoot now contains the path of the file relative to the zip archive - // with a trailing / - - const bool skipBad = options & Zip::SkipBadFiles; - const bool noDups = options & Zip::CheckForDuplicates; - - const QDir::Filters dir_filter = - QDir::Files | - QDir::Dirs | - QDir::NoDotAndDotDot | - QDir::NoSymLinks; - const QDir::SortFlags dir_sort = - QDir::DirsFirst; - QFileInfoList list = dir.entryInfoList(dir_filter, dir_sort); - - Zip::ErrorCode ec = Zip::Ok; - bool filesAdded = false; - - Zip::CompressionOptions recursionOptions; - if (path_ignore) - recursionOptions |= Zip::IgnorePaths; - else recursionOptions |= Zip::RelativePaths; - - for (int i = 0; i < list.size(); ++i) { - QFileInfo info = list.at(i); - const QString absPath = info.absoluteFilePath(); - if (noDups && containsEntry(info)) - continue; - if (info.isDir()) { - // Recursion - ec = addDirectory(absPath, actualRoot, recursionOptions, - level, hierarchyLevel + 1, addedFiles); - } else { - ec = createEntry(info, actualRoot, level); - if (ec == Zip::Ok) { - filesAdded = true; - if (addedFiles) - ++(*addedFiles); - } - } - - if (ec != Zip::Ok && !skipBad) { - break; - } - } - - // We need an explicit record for this dir - // Non-empty directories don't need it because they have a path component in the filename - if (!filesAdded && !path_ignore) - ec = createEntry(current, actualRoot, level); - - return ec; -} - -//! \internal Actual implementation of the addFile methods. -Zip::ErrorCode ZipPrivate::addFiles(const QStringList& files, const QString& root, - Zip::CompressionOptions options, Zip::CompressionLevel level, - int* addedFiles) -{ - if (addedFiles) - *addedFiles = 0; - - const bool skipBad = options & Zip::SkipBadFiles; - const bool noDups = options & Zip::CheckForDuplicates; - - // Bad boy didn't call createArchive() yet :) - if (!device) - return Zip::NoOpenArchive; - - QFileInfoList paths; - paths.reserve(files.size()); - for (int i = 0; i < files.size(); ++i) { - QFileInfo info(files.at(i)); - if (noDups && (paths.contains(info) || containsEntry(info))) - continue; - if (!info.exists() || !info.isReadable()) { - if (skipBad) { - continue; - } else { - return Zip::FileNotFound; - } - } - paths.append(info); - } - - if (paths.isEmpty()) - return Zip::Ok; - - // Remove any trailing separator - QString actualRoot = root.trimmed(); - - // Preserve Unix root but make sure the path ends only with a single - // unix like separator - ::checkRootPath(actualRoot); - - const bool path_absolute = options.testFlag(Zip::AbsolutePaths); - const bool path_ignore = options.testFlag(Zip::IgnorePaths); - const bool path_noroot = options.testFlag(Zip::IgnoreRoot); - - Zip::ErrorCode ec = Zip::Ok; - QHash dirMap; - - for (int i = 0; i < paths.size(); ++i) { - const QFileInfo& info = paths.at(i); - const QString path = QFileInfo(QDir::cleanPath(info.absolutePath())).absolutePath(); - - ZippedDir& zd = dirMap[path]; - if (!zd.init) { - zd.init = true; - zd.actualRoot = actualRoot; - if (path_absolute && !path_ignore && !path_noroot) { - QString absolutePath = extractRoot(path, options); - if (!absolutePath.isEmpty() && absolutePath != QLatin1String("/")) - absolutePath.append(QLatin1String("/")); - zd.actualRoot.append(absolutePath); - } - - if (!path_ignore && !path_noroot) { - zd.actualRoot.append(QDir(path).dirName()); - zd.actualRoot.append(QLatin1String("/")); - } - } - - // zd.actualRoot now contains the path of the file relative to the zip archive - // with a trailing / - - if (info.isDir()) { - // Recursion - ec = addDirectory(info.absoluteFilePath(), actualRoot, options, - level, 1, addedFiles); - } else { - ec = createEntry(info, actualRoot, level); - if (ec == Zip::Ok) { - ++zd.files; - if (addedFiles) - ++(*addedFiles); - } - } - - if (ec != Zip::Ok && !skipBad) { - break; - } - } - - // Create explicit records for empty directories - if (!path_ignore) { - QHash::ConstIterator b = dirMap.constBegin(); - const QHash::ConstIterator e = dirMap.constEnd(); - while (b != e) { - const ZippedDir& zd = b.value(); - if (zd.files <= 0) { - ec = createEntry(b.key(), zd.actualRoot, level); - } - ++b; - } - } - - return ec; -} - -//! \internal \p file must be a file and not a directory. -Zip::ErrorCode ZipPrivate::deflateFile(const QFileInfo& fileInfo, - quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys) -{ - const QString path = fileInfo.absoluteFilePath(); - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - qDebug() << QString("An error occurred while opening %1").arg(path); - return Zip::OpenFailed; - } - - const Zip::ErrorCode ec = (level == Zip::Store) - ? storeFile(path, file, crc, written, keys) - : compressFile(path, file, crc, written, level, keys); - - file.close(); - return ec; -} - -//! \internal -Zip::ErrorCode ZipPrivate::storeFile(const QString& path, QIODevice& file, - quint32& crc, qint64& totalWritten, quint32** keys) -{ - Q_UNUSED(path); - - qint64 read = 0; - qint64 written = 0; - - const bool encrypt = keys != 0; - - totalWritten = 0; - crc = crc32(0L, Z_NULL, 0); - - while ( (read = file.read(buffer1, ZIP_READ_BUFFER)) > 0 ) { - crc = crc32(crc, uBuffer, read); - if (encrypt) - encryptBytes(*keys, buffer1, read); - written = device->write(buffer1, read); - totalWritten += written; - if (written != read) { - return Zip::WriteFailed; - } - } - - return Zip::Ok; -} - -//! \internal -int ZipPrivate::compressionStrategy(const QString& path, QIODevice& file) const -{ - Q_UNUSED(file); - -#ifndef OSDAB_ZIP_NO_PNG_RLE - return Z_DEFAULT_STRATEGY; -#endif - const bool isPng = path.endsWith(QLatin1String("png"), Qt::CaseInsensitive); - return isPng ? Z_RLE : Z_DEFAULT_STRATEGY; -} - -//! \internal -Zip::ErrorCode ZipPrivate::compressFile(const QString& path, QIODevice& file, - quint32& crc, qint64& totalWritten, const Zip::CompressionLevel& level, quint32** keys) -{ - qint64 read = 0; - qint64 written = 0; - - qint64 totRead = 0; - qint64 toRead = file.size(); - - const bool encrypt = keys != 0; - const int strategy = compressionStrategy(path, file); - - totalWritten = 0; - crc = crc32(0L, Z_NULL, 0); - - z_stream zstr; - - // Initialize zalloc, zfree and opaque before calling the init function - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - - int zret; - - // Use deflateInit2 with negative windowBits to get raw compression - if ((zret = deflateInit2_( - &zstr, - (int)level, // compression level - Z_DEFLATED, // method - -MAX_WBITS, // windowBits - 8, // memLevel - strategy, - ZLIB_VERSION, - sizeof(z_stream) - )) != Z_OK ) { - qDebug() << "Could not initialize zlib for compression"; - return Zip::ZlibError; - } - - qint64 compressed; - int flush = Z_NO_FLUSH; - do { - read = file.read(buffer1, ZIP_READ_BUFFER); - totRead += read; - if (!read) - break; - - if (read < 0) { - deflateEnd(&zstr); - qDebug() << QString("Error while reading %1").arg(path); - return Zip::ReadFailed; - } - - crc = crc32(crc, uBuffer, read); - - zstr.next_in = (Bytef*) buffer1; - zstr.avail_in = (uInt)read; - - // Tell zlib if this is the last chunk we want to encode - // by setting the flush parameter to Z_FINISH - flush = (totRead == toRead) ? Z_FINISH : Z_NO_FLUSH; - - // Run deflate() on input until output buffer not full - // finish compression if all of source has been read in - do { - zstr.next_out = (Bytef*) buffer2; - zstr.avail_out = ZIP_READ_BUFFER; - - zret = deflate(&zstr, flush); - // State not clobbered - Q_ASSERT(zret != Z_STREAM_ERROR); - - // Write compressed data to file and empty buffer - compressed = ZIP_READ_BUFFER - zstr.avail_out; - - if (encrypt) - encryptBytes(*keys, buffer2, compressed); - - written = device->write(buffer2, compressed); - totalWritten += written; - - if (written != compressed) { - deflateEnd(&zstr); - qDebug() << QString("Error while writing %1").arg(path); - return Zip::WriteFailed; - } - - } while (zstr.avail_out == 0); - - // All input will be used - Q_ASSERT(zstr.avail_in == 0); - - } while (flush != Z_FINISH); - - // Stream will be complete - Q_ASSERT(zret == Z_STREAM_END); - deflateEnd(&zstr); - - return Zip::Ok; -} - -//! \internal Writes a new entry in the zip file. -Zip::ErrorCode ZipPrivate::createEntry(const QFileInfo& file, const QString& root, - Zip::CompressionLevel level) -{ - const bool dirOnly = file.isDir(); - - // entryName contains the path as it should be written - // in the zip file records - const QString entryName = dirOnly - ? root - : root + file.fileName(); - - // Directory entry - if (dirOnly || file.size() < ZIP_COMPRESSION_THRESHOLD) { - level = Zip::Store; - } else { - switch (level) { - case Zip::AutoCPU: - level = Zip::Deflate5; -#ifndef OSDAB_ZIP_NO_DEBUG - qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); -#endif - break; - case Zip::AutoMIME: - level = detectCompressionByMime(file.completeSuffix().toLower()); -#ifndef OSDAB_ZIP_NO_DEBUG - qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); -#endif - break; - case Zip::AutoFull: - level = detectCompressionByMime(file.completeSuffix().toLower()); -#ifndef OSDAB_ZIP_NO_DEBUG - qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); -#endif - break; - default: ; - } - } - - - - // create header and store it to write a central directory later - QScopedPointer h(new ZipEntryP); - h->absolutePath = file.absoluteFilePath().toLower(); - h->fileSize = file.size(); - - // Set encryption bit and set the data descriptor bit - // so we can use mod time instead of crc for password check - bool encrypt = !dirOnly && !password.isEmpty(); - if (encrypt) - h->gpFlag[0] |= 9; - - QDateTime dt = file.lastModified(); - dt = OSDAB_ZIP_MANGLE(fromFileTimestamp)(dt); - QDate d = dt.date(); - h->modDate[1] = ((d.year() - 1980) << 1) & 254; - h->modDate[1] |= ((d.month() >> 3) & 1); - h->modDate[0] = ((d.month() & 7) << 5) & 224; - h->modDate[0] |= d.day(); - - QTime t = dt.time(); - h->modTime[1] = (t.hour() << 3) & 248; - h->modTime[1] |= ((t.minute() >> 3) & 7); - h->modTime[0] = ((t.minute() & 7) << 5) & 224; - h->modTime[0] |= t.second() / 2; - - h->szUncomp = dirOnly ? 0 : file.size(); - - h->compMethod = (level == Zip::Store) ? 0 : 0x0008; - - // **** Write local file header **** - - // signature - buffer1[0] = 'P'; buffer1[1] = 'K'; - buffer1[2] = 0x3; buffer1[3] = 0x4; - - // version needed to extract - buffer1[ZIP_LH_OFF_VERS] = ZIP_VERSION; - buffer1[ZIP_LH_OFF_VERS + 1] = 0; - - // general purpose flag - buffer1[ZIP_LH_OFF_GPFLAG] = h->gpFlag[0]; - buffer1[ZIP_LH_OFF_GPFLAG + 1] = h->gpFlag[1]; - - // compression method - buffer1[ZIP_LH_OFF_CMET] = h->compMethod & 0xFF; - buffer1[ZIP_LH_OFF_CMET + 1] = (h->compMethod>>8) & 0xFF; - - // last mod file time - buffer1[ZIP_LH_OFF_MODT] = h->modTime[0]; - buffer1[ZIP_LH_OFF_MODT + 1] = h->modTime[1]; - - // last mod file date - buffer1[ZIP_LH_OFF_MODD] = h->modDate[0]; - buffer1[ZIP_LH_OFF_MODD + 1] = h->modDate[1]; - - // skip crc (4bytes) [14,15,16,17] - - // skip compressed size but include evtl. encryption header (4bytes: [18,19,20,21]) - buffer1[ZIP_LH_OFF_CSIZE] = - buffer1[ZIP_LH_OFF_CSIZE + 1] = - buffer1[ZIP_LH_OFF_CSIZE + 2] = - buffer1[ZIP_LH_OFF_CSIZE + 3] = 0; - - h->szComp = encrypt ? ZIP_LOCAL_ENC_HEADER_SIZE : 0; - - // uncompressed size [22,23,24,25] - setULong(h->szUncomp, buffer1, ZIP_LH_OFF_USIZE); - - // filename length - QByteArray entryNameBytes = entryName.toLatin1(); - int sz = entryNameBytes.size(); - - buffer1[ZIP_LH_OFF_NAMELEN] = sz & 0xFF; - buffer1[ZIP_LH_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF; - - // extra field length - buffer1[ZIP_LH_OFF_XLEN] = buffer1[ZIP_LH_OFF_XLEN + 1] = 0; - - // Store offset to write crc and compressed size - h->lhOffset = device->pos(); - quint32 crcOffset = h->lhOffset + ZIP_LH_OFF_CRC; - - if (device->write(buffer1, ZIP_LOCAL_HEADER_SIZE) != ZIP_LOCAL_HEADER_SIZE) { - return Zip::WriteFailed; - } - - // Write out filename - if (device->write(entryNameBytes) != sz) { - return Zip::WriteFailed; - } - - // Encryption keys - quint32 keys[3] = { 0, 0, 0 }; - - if (encrypt) { - // **** encryption header **** - - // XOR with PI to ensure better random numbers - // with poorly implemented rand() as suggested by Info-Zip - srand(time(NULL) ^ 3141592654UL); - int randByte; - - initKeys(keys); - for (int i = 0; i < 10; ++i) { - randByte = (rand() >> 7) & 0xff; - buffer1[i] = decryptByte(keys[2]) ^ randByte; - updateKeys(keys, randByte); - } - - // Encrypt encryption header - initKeys(keys); - for (int i = 0; i < 10; ++i) { - randByte = decryptByte(keys[2]); - updateKeys(keys, buffer1[i]); - buffer1[i] ^= randByte; - } - - // We don't know the CRC at this time, so we use the modification time - // as the last two bytes - randByte = decryptByte(keys[2]); - updateKeys(keys, h->modTime[0]); - buffer1[10] ^= randByte; - - randByte = decryptByte(keys[2]); - updateKeys(keys, h->modTime[1]); - buffer1[11] ^= randByte; - - // Write out encryption header - if (device->write(buffer1, ZIP_LOCAL_ENC_HEADER_SIZE) != ZIP_LOCAL_ENC_HEADER_SIZE) { - return Zip::WriteFailed; - } - } - - quint32 crc = 0; - qint64 written = 0; - - if (!dirOnly) { - quint32* k = keys; - const Zip::ErrorCode ec = deflateFile(file, crc, written, level, encrypt ? &k : 0); - if (ec != Zip::Ok) - return ec; - Q_ASSERT(!h.isNull()); - } - - // Store end of entry offset - quint32 current = device->pos(); - - // Update crc and compressed size in local header - if (!device->seek(crcOffset)) { - return Zip::SeekFailed; - } - - h->crc = dirOnly ? 0 : crc; - h->szComp += written; - - setULong(h->crc, buffer1, 0); - setULong(h->szComp, buffer1, 4); - if ( device->write(buffer1, 8) != 8) { - return Zip::WriteFailed; - } - - // Seek to end of entry - if (!device->seek(current)) { - return Zip::SeekFailed; - } - - if ((h->gpFlag[0] & 8) == 8) { - // Write data descriptor - - // Signature: PK\7\8 - buffer1[0] = 'P'; - buffer1[1] = 'K'; - buffer1[2] = 0x07; - buffer1[3] = 0x08; - - // CRC - setULong(h->crc, buffer1, ZIP_DD_OFF_CRC32); - - // Compressed size - setULong(h->szComp, buffer1, ZIP_DD_OFF_CSIZE); - - // Uncompressed size - setULong(h->szUncomp, buffer1, ZIP_DD_OFF_USIZE); - - if (device->write(buffer1, ZIP_DD_SIZE_WS) != ZIP_DD_SIZE_WS) { - return Zip::WriteFailed; - } - } - - headers->insert(entryName, h.take()); - return Zip::Ok; -} - -//! \internal -int ZipPrivate::decryptByte(quint32 key2) const -{ - quint16 temp = ((quint16)(key2) & 0xffff) | 2; - return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); -} - -//! \internal Writes an quint32 (4 bytes) to a byte array at given offset. -void ZipPrivate::setULong(quint32 v, char* buffer, unsigned int offset) -{ - buffer[offset+3] = ((v >> 24) & 0xFF); - buffer[offset+2] = ((v >> 16) & 0xFF); - buffer[offset+1] = ((v >> 8) & 0xFF); - buffer[offset] = (v & 0xFF); -} - -//! \internal Initializes decryption keys using a password. -void ZipPrivate::initKeys(quint32* keys) const -{ - // Encryption keys initialization constants are taken from the - // PKZip file format specification docs - keys[0] = 305419896L; - keys[1] = 591751049L; - keys[2] = 878082192L; - - QByteArray pwdBytes = password.toLatin1(); - int sz = pwdBytes.size(); - const char* ascii = pwdBytes.data(); - - for (int i = 0; i < sz; ++i) - updateKeys(keys, (int)ascii[i]); -} - -//! Updates a one-char-only CRC; it's the Info-Zip macro re-adapted. -quint32 ZipPrivate::updateChecksum(const quint32& crc, const quint32& val) const -{ - return quint32(crcTable[quint32(crc^val) & 0xff] ^ crc_t(crc >> 8)); -} - -//! \internal Updates encryption keys. -void ZipPrivate::updateKeys(quint32* keys, int c) const -{ - keys[0] = updateChecksum(keys[0], c); - keys[1] += keys[0] & 0xff; - keys[1] = keys[1] * 134775813L + 1; - keys[2] = updateChecksum(keys[2], ((int)keys[1]) >> 24); -} - -//! \internal Encrypts a byte array. -void ZipPrivate::encryptBytes(quint32* keys, char* buffer, qint64 read) -{ - char t; - - for (qint64 i = 0; i < read; ++i) { - t = buffer[i]; - buffer[i] ^= decryptByte(keys[2]); - updateKeys(keys, t); - } -} - -namespace { -struct KeywordHelper { - const QString needle; - inline KeywordHelper(const QString& keyword) : needle(keyword) {} -}; - -bool operator<(const KeywordHelper& helper, const char* keyword) { - return helper.needle.compare(QLatin1String(keyword)) < 0; -} - -bool operator<(const char* keyword, const KeywordHelper& helper) { - return helper.needle.compare(QLatin1String(keyword)) > 0; -} - -bool hasExtension(const QString& ext, const char* const* map, int max) { - const char* const* start = &map[0]; - const char* const* end = &map[max - 1]; - const char* const* kw = qBinaryFind(start, end, KeywordHelper(ext)); - return kw != end; -} -} - -//! \internal Detects the best compression level for a given file extension. -Zip::CompressionLevel ZipPrivate::detectCompressionByMime(const QString& ext) -{ - // NOTE: Keep the MAX_* and the number of strings in the map up to date. - // NOTE: Alphabetically sort the strings in the map -- we use a binary search! - - // Archives or files that will hardly compress - const int MAX_EXT1 = 14; - const char* const ext1[MAX_EXT1] = { - "7z", "bin", "deb", "exe", "gz", "gz2", "jar", "rar", "rpm", "tar", "tgz", "z", "zip", - 0 // # MAX_EXT1 - }; - - // Slow or usually large files that we should not spend to much time with - const int MAX_EXT2 = 24; - const char* const ext2[MAX_EXT2] = { - "asf", - "avi", - "divx", - "doc", - "docx", - "flv", - "gif", - "iso", - "jpg", - "jpeg", - "mka", - "mkv", - "mp3", - "mp4", - "mpeg", - "mpg", - "odt", - "ogg", - "ogm", - "ra", - "rm", - "wma", - "wmv", - 0 // # MAX_EXT2 - }; - - // Files with high compression ratio - const int MAX_EXT3 = 28; - const char* const ext3[MAX_EXT3] = { - "asp", "bat", "c", "conf", "cpp", "cpp", "css", "csv", "cxx", "h", "hpp", "htm", "html", "hxx", - "ini", "js", "php", "pl", "py", "rtf", "sh", "tsv", "txt", "vb", "vbs", "xml", "xst", - 0 // # MAX_EXT3 - }; - - const char* const* map = ext1; - if (hasExtension(ext, map, MAX_EXT1)) - return Zip::Store; - - map = ext2; - if (hasExtension(ext, map, MAX_EXT2)) - return Zip::Deflate2; - - map = ext3; - if (hasExtension(ext, map, MAX_EXT3)) - return Zip::Deflate9; - - return Zip::Deflate5; -} - -/*! - Closes the current archive and writes out pending data. -*/ -Zip::ErrorCode ZipPrivate::closeArchive() -{ - if (!device) { - Q_ASSERT(!file); - return Zip::Ok; - } - - if (device != file) - disconnect(device, 0, this, 0); - - return do_closeArchive(); -} - -//! \internal -Zip::ErrorCode ZipPrivate::do_closeArchive() -{ - // Close current archive by writing out central directory - // and free up resources - - if (!device && !headers) - return Zip::Ok; - - quint32 szCentralDir = 0; - quint32 offCentralDir = device->pos(); - Zip::ErrorCode c = Zip::Ok; - - if (headers && device) { - for (QMap::ConstIterator itr = headers->constBegin(); - itr != headers->constEnd(); ++itr) { - const QString fileName = itr.key(); - const ZipEntryP* h = itr.value(); - c = writeEntry(fileName, h, szCentralDir); - } - } - - if (c == Zip::Ok) - c = writeCentralDir(offCentralDir, szCentralDir); - - if (c != Zip::Ok) { - if (file) { - file->close(); - if (!file->remove()) { - qDebug() << "Failed to delete corrupt archive."; - } - } - } - - return c; -} - -//! \internal -Zip::ErrorCode ZipPrivate::writeEntry(const QString& fileName, const ZipEntryP* h, quint32& szCentralDir) -{ - unsigned int sz; - - Q_ASSERT(h && device && headers); - - // signature - buffer1[0] = 'P'; - buffer1[1] = 'K'; - buffer1[2] = 0x01; - buffer1[3] = 0x02; - - // version made by (currently only MS-DOS/FAT - no symlinks or other stuff supported) - buffer1[ZIP_CD_OFF_MADEBY] = buffer1[ZIP_CD_OFF_MADEBY + 1] = 0; - - // version needed to extract - buffer1[ZIP_CD_OFF_VERSION] = ZIP_VERSION; - buffer1[ZIP_CD_OFF_VERSION + 1] = 0; - - // general purpose flag - buffer1[ZIP_CD_OFF_GPFLAG] = h->gpFlag[0]; - buffer1[ZIP_CD_OFF_GPFLAG + 1] = h->gpFlag[1]; - - // compression method - buffer1[ZIP_CD_OFF_CMET] = h->compMethod & 0xFF; - buffer1[ZIP_CD_OFF_CMET + 1] = (h->compMethod >> 8) & 0xFF; - - // last mod file time - buffer1[ZIP_CD_OFF_MODT] = h->modTime[0]; - buffer1[ZIP_CD_OFF_MODT + 1] = h->modTime[1]; - - // last mod file date - buffer1[ZIP_CD_OFF_MODD] = h->modDate[0]; - buffer1[ZIP_CD_OFF_MODD + 1] = h->modDate[1]; - - // crc (4bytes) [16,17,18,19] - setULong(h->crc, buffer1, ZIP_CD_OFF_CRC); - - // compressed size (4bytes: [20,21,22,23]) - setULong(h->szComp, buffer1, ZIP_CD_OFF_CSIZE); - - // uncompressed size [24,25,26,27] - setULong(h->szUncomp, buffer1, ZIP_CD_OFF_USIZE); - - // filename - QByteArray fileNameBytes = fileName.toLatin1(); - sz = fileNameBytes.size(); - buffer1[ZIP_CD_OFF_NAMELEN] = sz & 0xFF; - buffer1[ZIP_CD_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF; - - // extra field length - buffer1[ZIP_CD_OFF_XLEN] = buffer1[ZIP_CD_OFF_XLEN + 1] = 0; - - // file comment length - buffer1[ZIP_CD_OFF_COMMLEN] = buffer1[ZIP_CD_OFF_COMMLEN + 1] = 0; - - // disk number start - buffer1[ZIP_CD_OFF_DISKSTART] = buffer1[ZIP_CD_OFF_DISKSTART + 1] = 0; - - // internal file attributes - buffer1[ZIP_CD_OFF_IATTR] = buffer1[ZIP_CD_OFF_IATTR + 1] = 0; - - // external file attributes - buffer1[ZIP_CD_OFF_EATTR] = - buffer1[ZIP_CD_OFF_EATTR + 1] = - buffer1[ZIP_CD_OFF_EATTR + 2] = - buffer1[ZIP_CD_OFF_EATTR + 3] = 0; - - // relative offset of local header [42->45] - setULong(h->lhOffset, buffer1, ZIP_CD_OFF_LHOFF); - - if (device->write(buffer1, ZIP_CD_SIZE) != ZIP_CD_SIZE) { - return Zip::WriteFailed; - } - - // Write out filename - if ((unsigned int)device->write(fileNameBytes) != sz) { - return Zip::WriteFailed; - } - - szCentralDir += (ZIP_CD_SIZE + sz); - - return Zip::Ok; -} - -//! \internal -Zip::ErrorCode ZipPrivate::writeCentralDir(quint32 offCentralDir, quint32 szCentralDir) -{ - Q_ASSERT(device && headers); - - unsigned int sz; - - // signature - buffer1[0] = 'P'; - buffer1[1] = 'K'; - buffer1[2] = 0x05; - buffer1[3] = 0x06; - - // number of this disk - buffer1[ZIP_EOCD_OFF_DISKNUM] = buffer1[ZIP_EOCD_OFF_DISKNUM + 1] = 0; - - // number of disk with central directory - buffer1[ZIP_EOCD_OFF_CDDISKNUM] = buffer1[ZIP_EOCD_OFF_CDDISKNUM + 1] = 0; - - // number of entries in this disk - sz = headers->count(); - buffer1[ZIP_EOCD_OFF_ENTRIES] = sz & 0xFF; - buffer1[ZIP_EOCD_OFF_ENTRIES + 1] = (sz >> 8) & 0xFF; - - // total number of entries - buffer1[ZIP_EOCD_OFF_CDENTRIES] = buffer1[ZIP_EOCD_OFF_ENTRIES]; - buffer1[ZIP_EOCD_OFF_CDENTRIES + 1] = buffer1[ZIP_EOCD_OFF_ENTRIES + 1]; - - // size of central directory [12->15] - setULong(szCentralDir, buffer1, ZIP_EOCD_OFF_CDSIZE); - - // central dir offset [16->19] - setULong(offCentralDir, buffer1, ZIP_EOCD_OFF_CDOFF); - - // ZIP file comment length - QByteArray commentBytes = comment.toLatin1(); - quint16 commentLength = commentBytes.size(); - - if (commentLength == 0) { - buffer1[ZIP_EOCD_OFF_COMMLEN] = buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = 0; - } else { - buffer1[ZIP_EOCD_OFF_COMMLEN] = commentLength & 0xFF; - buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = (commentLength >> 8) & 0xFF; - } - - if (device->write(buffer1, ZIP_EOCD_SIZE) != ZIP_EOCD_SIZE) { - return Zip::WriteFailed; - } - - if (commentLength != 0) { - if ((unsigned int)device->write(commentBytes) != commentLength) { - return Zip::WriteFailed; - } - } - - return Zip::Ok; -} - -//! \internal -void ZipPrivate::reset() -{ - comment.clear(); - - if (headers) { - qDeleteAll(*headers); - delete headers; - headers = 0; - } - - device = 0; - - if (file) - delete file; - file = 0; -} - -//! \internal Returns the path of the parent directory -QString ZipPrivate::extractRoot(const QString& p, Zip::CompressionOptions o) -{ - Q_UNUSED(o); - QDir d(QDir::cleanPath(p)); - if (!d.exists()) - return QString(); - - if (!d.cdUp()) - return QString(); - - return d.absolutePath(); -} - - -/************************************************************************ - Public interface -*************************************************************************/ - -/*! - Creates a new Zip file compressor. -*/ -Zip::Zip() : d(new ZipPrivate) -{ -} - -/*! - Closes any open archive and releases used resources. -*/ -Zip::~Zip() -{ - closeArchive(); - delete d; -} - -/*! - Returns true if there is an open archive. -*/ -bool Zip::isOpen() const -{ - return d->device; -} - -/*! - Sets the password to be used for the next files being added! - Files added before calling this method will use the previously - set password (if any). - Closing the archive won't clear the password! -*/ -void Zip::setPassword(const QString& pwd) -{ - d->password = pwd; -} - -//! Convenience method, clears the current password. -void Zip::clearPassword() -{ - d->password.clear(); -} - -//! Returns the currently used password. -QString Zip::password() const -{ - return d->password; -} - -/*! - Attempts to create a new Zip archive. If \p overwrite is true and the file - already exist it will be overwritten. - Any open archive will be closed. - */ -Zip::ErrorCode Zip::createArchive(const QString& filename, bool overwrite) -{ - closeArchive(); - Q_ASSERT(!d->device && !d->file); - - if (filename.isEmpty()) - return Zip::FileNotFound; - - d->file = new QFile(filename); - - if (d->file->exists() && !overwrite) { - delete d->file; - d->file = 0; - return Zip::FileExists; - } - - if (!d->file->open(QIODevice::WriteOnly)) { - delete d->file; - d->file = 0; - return Zip::OpenFailed; - } - - const Zip::ErrorCode ec = createArchive(d->file); - if (ec != Zip::Ok) { - closeArchive(); - } - - return ec; -} - -/*! - Attempts to create a new Zip archive. If there is another open archive this will be closed. - \warning The class takes ownership of the device! - */ -Zip::ErrorCode Zip::createArchive(QIODevice* device) -{ - if (!device) { - qDebug() << "Invalid device."; - return Zip::OpenFailed; - } - - return d->createArchive(device); -} - -/*! - Returns the current archive comment. -*/ -QString Zip::archiveComment() const -{ - return d->comment; -} - -/*! - Sets the comment for this archive. Note: createArchive() should have been - called before. -*/ -void Zip::setArchiveComment(const QString& comment) -{ - d->comment = comment; -} - -/*! - Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::IgnorePaths flag as compression option and an empty \p root parameter. - - The result is that all files found in \p path (and in subdirectories) are - added to the zip file without a directory entry. -*/ -Zip::ErrorCode Zip::addDirectoryContents(const QString& path, CompressionLevel level) -{ - return addDirectory(path, QString(), IgnorePaths, level); -} - -/*! - Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::IgnorePaths flag as compression option. - - The result is that all files found in \p path (and in subdirectories) are - added to the zip file without a directory entry (or within a directory - structure specified by \p root). -*/ -Zip::ErrorCode Zip::addDirectoryContents(const QString& path, const QString& root, CompressionLevel level) -{ - return addDirectory(path, root, IgnorePaths, level); -} - -/*! - Convenience method, same as calling - Zip::addDirectory(const QString&,const QString&,CompressionLevel) - with an empty \p root parameter and Zip::RelativePaths flag as compression option. - */ -Zip::ErrorCode Zip::addDirectory(const QString& path, CompressionLevel level) -{ - return addDirectory(path, QString(), Zip::RelativePaths, level); -} - -/*! - Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::RelativePaths flag as compression option. - */ -Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, CompressionLevel level) -{ - return addDirectory(path, root, Zip::RelativePaths, level); -} - -/*! - Recursively adds files contained in \p dir to the archive, using \p root as name for the root folder. - Stops adding files if some error occurs. - - The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. - This means that the last one overwrites the previous one (if some conflict occurs), i.e. - Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. - - The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / - is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). - - If \p addedFiles is not null it is set to the number of successfully added - files. -*/ -Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, - CompressionOptions options, CompressionLevel level, int* addedFiles) -{ - const int hierarchyLev = 0; - return d->addDirectory(path, root, options, level, hierarchyLev, addedFiles); -} - -/*! - Convenience method, same as calling Zip::addFile(const QString&,const QString&,CompressionOptions,CompressionLevel) - with an empty \p root parameter and Zip::RelativePaths as compression option. - */ -Zip::ErrorCode Zip::addFile(const QString& path, CompressionLevel level) -{ - return addFile(path, QString(), Zip::RelativePaths, level); -} - -/*! - Convenience method, same as calling Zip::addFile(const QString&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::RelativePaths flag as compression option. - */ -Zip::ErrorCode Zip::addFile(const QString& path, const QString& root, - CompressionLevel level) -{ - return addFile(path, root, Zip::RelativePaths, level); -} - -/*! - Adds the file at \p path to the archive, using \p root as name for the root folder. - If \p path points to a directory the behaviour is basically the same as - addDirectory(). - - The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. - This means that the last one overwrites the previous one (if some conflict occurs), i.e. - Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. - - The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / - is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). -*/ -Zip::ErrorCode Zip::addFile(const QString& path, const QString& root, - CompressionOptions options, CompressionLevel level) -{ - if (path.isEmpty()) - return Zip::Ok; - return addFiles(QStringList() << path, root, options, level); -} - -/*! - Convenience method, same as calling Zip::addFiles(const QStringList&,const QString&,CompressionOptions,CompressionLevel) - with an empty \p root parameter and Zip::RelativePaths as compression option. - */ -Zip::ErrorCode Zip::addFiles(const QStringList& paths, CompressionLevel level) -{ - return addFiles(paths, QString(), Zip::RelativePaths, level); -} - -/*! - Convenience method, same as calling Zip::addFiles(const QStringList&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::RelativePaths flag as compression option. - */ -Zip::ErrorCode Zip::addFiles(const QStringList& paths, const QString& root, - CompressionLevel level) -{ - return addFiles(paths, root, Zip::RelativePaths, level); -} - -/*! - Adds the files or directories in \p paths to the archive, using \p root as - name for the root folder. - This is similar to calling addFile or addDirectory for all the entries in - \p paths, except it is slightly faster. - - The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. - This means that the last one overwrites the previous one (if some conflict occurs), i.e. - Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. - - The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / - is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). - - If \p addedFiles is not null it is set to the number of successfully added - files. -*/ -Zip::ErrorCode Zip::addFiles(const QStringList& paths, const QString& root, - CompressionOptions options, CompressionLevel level, int* addedFiles) -{ - return d->addFiles(paths, root, options, level, addedFiles); -} - -/*! - Closes the archive and writes any pending data. -*/ -Zip::ErrorCode Zip::closeArchive() -{ - Zip::ErrorCode ec = d->closeArchive(); - d->reset(); - return ec; -} - -/*! - Returns a locale translated error string for a given error code. -*/ -QString Zip::formatError(Zip::ErrorCode c) const -{ - switch (c) - { - case Ok: return QCoreApplication::translate("Zip", "ZIP operation completed successfully."); break; - case ZlibInit: return QCoreApplication::translate("Zip", "Failed to initialize or load zlib library."); break; - case ZlibError: return QCoreApplication::translate("Zip", "zlib library error."); break; - case OpenFailed: return QCoreApplication::translate("Zip", "Unable to create or open file."); break; - case NoOpenArchive: return QCoreApplication::translate("Zip", "No archive has been created yet."); break; - case FileNotFound: return QCoreApplication::translate("Zip", "File or directory does not exist."); break; - case ReadFailed: return QCoreApplication::translate("Zip", "File read error."); break; - case WriteFailed: return QCoreApplication::translate("Zip", "File write error."); break; - case SeekFailed: return QCoreApplication::translate("Zip", "File seek error."); break; - default: ; - } - - return QCoreApplication::translate("Zip", "Unknown error."); -} - -OSDAB_END_NAMESPACE +/**************************************************************************** +** Filename: zip.cpp +** Last updated [dd/mm/yyyy]: 01/02/2007 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#include "zip.h" +#include "zip_p.h" +#include "zipentry_p.h" + +// we only use this to seed the random number generator +#include + +#include +#include +#include +#include +#include +#include +#include + +// You can remove this #include if you replace the qDebug() statements. +#include + + +/*! #define OSDAB_ZIP_NO_PNG_RLE to disable the use of Z_RLE compression strategy with + PNG files (achieves slightly better compression levels according to the authors). +*/ +// #define OSDAB_ZIP_NO_PNG_RLE + +#define OSDAB_ZIP_NO_DEBUG + +//! Local header size (including signature, excluding variable length fields) +#define ZIP_LOCAL_HEADER_SIZE 30 +//! Encryption header size +#define ZIP_LOCAL_ENC_HEADER_SIZE 12 +//! Data descriptor size (signature included) +#define ZIP_DD_SIZE_WS 16 +//! Central Directory record size (signature included) +#define ZIP_CD_SIZE 46 +//! End of Central Directory record size (signature included) +#define ZIP_EOCD_SIZE 22 + +// Some offsets inside a local header record (signature included) +#define ZIP_LH_OFF_VERS 4 +#define ZIP_LH_OFF_GPFLAG 6 +#define ZIP_LH_OFF_CMET 8 +#define ZIP_LH_OFF_MODT 10 +#define ZIP_LH_OFF_MODD 12 +#define ZIP_LH_OFF_CRC 14 +#define ZIP_LH_OFF_CSIZE 18 +#define ZIP_LH_OFF_USIZE 22 +#define ZIP_LH_OFF_NAMELEN 26 +#define ZIP_LH_OFF_XLEN 28 + +// Some offsets inside a data descriptor record (including signature) +#define ZIP_DD_OFF_CRC32 4 +#define ZIP_DD_OFF_CSIZE 8 +#define ZIP_DD_OFF_USIZE 12 + +// Some offsets inside a Central Directory record (including signature) +#define ZIP_CD_OFF_MADEBY 4 +#define ZIP_CD_OFF_VERSION 6 +#define ZIP_CD_OFF_GPFLAG 8 +#define ZIP_CD_OFF_CMET 10 +#define ZIP_CD_OFF_MODT 12 +#define ZIP_CD_OFF_MODD 14 +#define ZIP_CD_OFF_CRC 16 +#define ZIP_CD_OFF_CSIZE 20 +#define ZIP_CD_OFF_USIZE 24 +#define ZIP_CD_OFF_NAMELEN 28 +#define ZIP_CD_OFF_XLEN 30 +#define ZIP_CD_OFF_COMMLEN 32 +#define ZIP_CD_OFF_DISKSTART 34 +#define ZIP_CD_OFF_IATTR 36 +#define ZIP_CD_OFF_EATTR 38 +#define ZIP_CD_OFF_LHOFF 42 + +// Some offsets inside a EOCD record (including signature) +#define ZIP_EOCD_OFF_DISKNUM 4 +#define ZIP_EOCD_OFF_CDDISKNUM 6 +#define ZIP_EOCD_OFF_ENTRIES 8 +#define ZIP_EOCD_OFF_CDENTRIES 10 +#define ZIP_EOCD_OFF_CDSIZE 12 +#define ZIP_EOCD_OFF_CDOFF 16 +#define ZIP_EOCD_OFF_COMMLEN 20 + +//! PKZip version for archives created by this API +#define ZIP_VERSION 0x14 + +//! Do not store very small files as the compression headers overhead would be to big +#define ZIP_COMPRESSION_THRESHOLD 60 + +/*! + \class Zip zip.h + + \brief Zip file compression. + + Some quick usage examples. + + \verbatim + Suppose you have this directory structure: + + /home/user/dir1/file1.1 + /home/user/dir1/file1.2 + /home/user/dir1/dir1.1/ + /home/user/dir1/dir1.2/file1.2.1 + + EXAMPLE 1: + myZipInstance.addDirectory("/home/user/dir1"); + + RESULT: + Beheaves like any common zip software and creates a zip file with this structure: + + dir1/file1.1 + dir1/file1.2 + dir1/dir1.1/ + dir1/dir1.2/file1.2.1 + + EXAMPLE 2: + myZipInstance.addDirectory("/home/user/dir1", "myRoot/myFolder"); + + RESULT: + Adds a custom root to the paths and creates a zip file with this structure: + + myRoot/myFolder/dir1/file1.1 + myRoot/myFolder/dir1/file1.2 + myRoot/myFolder/dir1/dir1.1/ + myRoot/myFolder/dir1/dir1.2/file1.2.1 + + EXAMPLE 3: + myZipInstance.addDirectory("/home/user/dir1", Zip::AbsolutePaths); + + NOTE: + Same as calling addDirectory(SOME_PATH, PARENT_PATH_of_SOME_PATH). + + RESULT: + Preserves absolute paths and creates a zip file with this structure: + + /home/user/dir1/file1.1 + /home/user/dir1/file1.2 + /home/user/dir1/dir1.1/ + /home/user/dir1/dir1.2/file1.2.1 + + EXAMPLE 4: + myZipInstance.setPassword("hellopass"); + myZipInstance.addDirectory("/home/user/dir1", "/"); + + RESULT: + Adds and encrypts the files in /home/user/dir1, creating the following zip structure: + + /dir1/file1.1 + /dir1/file1.2 + /dir1/dir1.1/ + /dir1/dir1.2/file1.2.1 + + EXAMPLE 5: + myZipInstance.addDirectory("/home/user/dir1", Zip::IgnoreRoot); + + RESULT: + Adds the files in /home/user/dir1 but doesn't create the top level + directory: + + file1.1 + file1.2 + dir1.1/ + dir1.2/file1.2.1 + + EXAMPLE 5: + myZipInstance.addDirectory("/home/user/dir1", "data/backup", Zip::IgnoreRoot); + + RESULT: + Adds the files in /home/user/dir1 but uses "data/backup" as top level + directory instead of "dir1": + + data/backup/file1.1 + data/backup/file1.2 + data/backup/dir1.1/ + data/backup/dir1.2/file1.2.1 + + \endverbatim +*/ + +/*! \enum Zip::ErrorCode The result of a compression operation. + \value Zip::Ok No error occurred. + \value Zip::ZlibInit Failed to init or load the zlib library. + \value Zip::ZlibError The zlib library returned some error. + \value Zip::FileExists The file already exists and will not be overwritten. + \value Zip::OpenFailed Unable to create or open a device. + \value Zip::NoOpenArchive CreateArchive() has not been called yet. + \value Zip::FileNotFound File or directory does not exist. + \value Zip::ReadFailed Reading of a file failed. + \value Zip::WriteFailed Writing of a file failed. + \value Zip::SeekFailed Seek failed. +*/ + +/*! \enum Zip::CompressionLevel Returns the result of a decompression operation. + \value Zip::Store No compression. + \value Zip::Deflate1 Deflate compression level 1(lowest compression). + \value Zip::Deflate1 Deflate compression level 2. + \value Zip::Deflate1 Deflate compression level 3. + \value Zip::Deflate1 Deflate compression level 4. + \value Zip::Deflate1 Deflate compression level 5. + \value Zip::Deflate1 Deflate compression level 6. + \value Zip::Deflate1 Deflate compression level 7. + \value Zip::Deflate1 Deflate compression level 8. + \value Zip::Deflate1 Deflate compression level 9 (maximum compression). + \value Zip::AutoCPU Adapt compression level to CPU speed (faster CPU => better compression). + \value Zip::AutoMIME Adapt compression level to MIME type of the file being compressed. + \value Zip::AutoFull Use both CPU and MIME type detection. +*/ + +namespace { + +struct ZippedDir { + bool init; + QString actualRoot; + int files; + ZippedDir() : init(false), actualRoot(), files(0) {} +}; + +void checkRootPath(QString& path) +{ + const bool isUnixRoot = path.length() == 1 && path.at(0) == QLatin1Char('/'); + if (!path.isEmpty() && !isUnixRoot) { + while (path.endsWith(QLatin1String("\\"))) + path.truncate(path.length() - 1); + + int sepCount = 0; + for (int i = path.length()-1; i >= 0; --i) { + if (path.at(i) == QLatin1Char('/')) + ++sepCount; + else break; + } + + if (sepCount > 1) + path.truncate(path.length() - (sepCount-1)); + else if (sepCount == 0) + path.append(QLatin1String("/")); + } +} + +} + +////////////////////////////////////////////////////////////////////////// + +OSDAB_BEGIN_NAMESPACE(Zip) + +/************************************************************************ + Private interface +*************************************************************************/ + +//! \internal +ZipPrivate::ZipPrivate() : + headers(0), + device(0), + file(0), + uBuffer(0), + crcTable(0), + comment(), + password() +{ + // keep an unsigned pointer so we avoid to over bloat the code with casts + uBuffer = (unsigned char*) buffer1; + crcTable = get_crc_table(); +} + +//! \internal +ZipPrivate::~ZipPrivate() +{ + closeArchive(); +} + +//! \internal +Zip::ErrorCode ZipPrivate::createArchive(QIODevice* dev) +{ + Q_ASSERT(dev); + + if (device) + closeArchive(); + + device = dev; + if (device != file) + connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*))); + + if (!device->isOpen()) { + if (!device->open(QIODevice::ReadOnly)) { + delete device; + device = 0; + qDebug() << "Unable to open device for writing."; + return Zip::OpenFailed; + } + } + + headers = new QMap; + return Zip::Ok; +} + +//! \internal +void ZipPrivate::deviceDestroyed(QObject*) +{ + qDebug("Unexpected device destruction detected."); + do_closeArchive(); +} + +/*! Returns true if an entry for \p info has already been added. + Uses file size and lower case absolute path to compare entries. +*/ +bool ZipPrivate::containsEntry(const QFileInfo& info) const +{ + if (!headers || headers->isEmpty()) + return false; + + const qint64 sz = info.size(); + const QString path = info.absoluteFilePath().toLower(); + + QMap::ConstIterator b = headers->constBegin(); + const QMap::ConstIterator e = headers->constEnd(); + while (b != e) { + const ZipEntryP* e = b.value(); + if (e->fileSize == sz && e->absolutePath == path) + return true; + ++b; + } + + return false; +} + +//! \internal Actual implementation of the addDirectory* methods. +Zip::ErrorCode ZipPrivate::addDirectory(const QString& path, const QString& root, + Zip::CompressionOptions options, Zip::CompressionLevel level, int hierarchyLevel, + int* addedFiles) +{ + if (addedFiles) + ++(*addedFiles); + + // Bad boy didn't call createArchive() yet :) + if (!device) + return Zip::NoOpenArchive; + + QDir dir(path); + if (!dir.exists()) + return Zip::FileNotFound; + + // Remove any trailing separator + QString actualRoot = root.trimmed(); + + // Preserve Unix root but make sure the path ends only with a single + // unix like separator + ::checkRootPath(actualRoot); + + // QDir::cleanPath() fixes some issues with QDir::dirName() + QFileInfo current(QDir::cleanPath(path)); + + const bool path_absolute = options.testFlag(Zip::AbsolutePaths); + const bool path_ignore = options.testFlag(Zip::IgnorePaths); + const bool path_noroot = options.testFlag(Zip::IgnoreRoot); + + if (path_absolute && !path_ignore && !path_noroot) { + QString absolutePath = extractRoot(path, options); + if (!absolutePath.isEmpty() && absolutePath != QLatin1String("/")) + absolutePath.append(QLatin1String("/")); + actualRoot.append(absolutePath); + } + + const bool skipDirName = !hierarchyLevel && path_noroot; + if (!path_ignore && !skipDirName) { + actualRoot.append(QDir(current.absoluteFilePath()).dirName()); + actualRoot.append(QLatin1String("/")); + } + + // actualRoot now contains the path of the file relative to the zip archive + // with a trailing / + + const bool skipBad = options & Zip::SkipBadFiles; + const bool noDups = options & Zip::CheckForDuplicates; + + const QDir::Filters dir_filter = + QDir::Files | + QDir::Dirs | + QDir::NoDotAndDotDot | + QDir::NoSymLinks; + const QDir::SortFlags dir_sort = + QDir::DirsFirst; + QFileInfoList list = dir.entryInfoList(dir_filter, dir_sort); + + Zip::ErrorCode ec = Zip::Ok; + bool filesAdded = false; + + Zip::CompressionOptions recursionOptions; + if (path_ignore) + recursionOptions |= Zip::IgnorePaths; + else recursionOptions |= Zip::RelativePaths; + + for (int i = 0; i < list.size(); ++i) { + QFileInfo info = list.at(i); + const QString absPath = info.absoluteFilePath(); + if (noDups && containsEntry(info)) + continue; + if (info.isDir()) { + // Recursion + ec = addDirectory(absPath, actualRoot, recursionOptions, + level, hierarchyLevel + 1, addedFiles); + } else { + ec = createEntry(info, actualRoot, level); + if (ec == Zip::Ok) { + filesAdded = true; + if (addedFiles) + ++(*addedFiles); + } + } + + if (ec != Zip::Ok && !skipBad) { + break; + } + } + + // We need an explicit record for this dir + // Non-empty directories don't need it because they have a path component in the filename + if (!filesAdded && !path_ignore) + ec = createEntry(current, actualRoot, level); + + return ec; +} + +//! \internal Actual implementation of the addFile methods. +Zip::ErrorCode ZipPrivate::addFiles(const QStringList& files, const QString& root, + Zip::CompressionOptions options, Zip::CompressionLevel level, + int* addedFiles) +{ + if (addedFiles) + *addedFiles = 0; + + const bool skipBad = options & Zip::SkipBadFiles; + const bool noDups = options & Zip::CheckForDuplicates; + + // Bad boy didn't call createArchive() yet :) + if (!device) + return Zip::NoOpenArchive; + + QFileInfoList paths; + paths.reserve(files.size()); + for (int i = 0; i < files.size(); ++i) { + QFileInfo info(files.at(i)); + if (noDups && (paths.contains(info) || containsEntry(info))) + continue; + if (!info.exists() || !info.isReadable()) { + if (skipBad) { + continue; + } else { + return Zip::FileNotFound; + } + } + paths.append(info); + } + + if (paths.isEmpty()) + return Zip::Ok; + + // Remove any trailing separator + QString actualRoot = root.trimmed(); + + // Preserve Unix root but make sure the path ends only with a single + // unix like separator + ::checkRootPath(actualRoot); + + const bool path_absolute = options.testFlag(Zip::AbsolutePaths); + const bool path_ignore = options.testFlag(Zip::IgnorePaths); + const bool path_noroot = options.testFlag(Zip::IgnoreRoot); + + Zip::ErrorCode ec = Zip::Ok; + QHash dirMap; + + for (int i = 0; i < paths.size(); ++i) { + const QFileInfo& info = paths.at(i); + const QString path = QFileInfo(QDir::cleanPath(info.absolutePath())).absolutePath(); + + ZippedDir& zd = dirMap[path]; + if (!zd.init) { + zd.init = true; + zd.actualRoot = actualRoot; + if (path_absolute && !path_ignore && !path_noroot) { + QString absolutePath = extractRoot(path, options); + if (!absolutePath.isEmpty() && absolutePath != QLatin1String("/")) + absolutePath.append(QLatin1String("/")); + zd.actualRoot.append(absolutePath); + } + + if (!path_ignore && !path_noroot) { + zd.actualRoot.append(QDir(path).dirName()); + zd.actualRoot.append(QLatin1String("/")); + } + } + + // zd.actualRoot now contains the path of the file relative to the zip archive + // with a trailing / + + if (info.isDir()) { + // Recursion + ec = addDirectory(info.absoluteFilePath(), actualRoot, options, + level, 1, addedFiles); + } else { + ec = createEntry(info, actualRoot, level); + if (ec == Zip::Ok) { + ++zd.files; + if (addedFiles) + ++(*addedFiles); + } + } + + if (ec != Zip::Ok && !skipBad) { + break; + } + } + + // Create explicit records for empty directories + if (!path_ignore) { + QHash::ConstIterator b = dirMap.constBegin(); + const QHash::ConstIterator e = dirMap.constEnd(); + while (b != e) { + const ZippedDir& zd = b.value(); + if (zd.files <= 0) { + ec = createEntry(b.key(), zd.actualRoot, level); + } + ++b; + } + } + + return ec; +} + +//! \internal \p file must be a file and not a directory. +Zip::ErrorCode ZipPrivate::deflateFile(const QFileInfo& fileInfo, + quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys) +{ + const QString path = fileInfo.absoluteFilePath(); + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qDebug() << QString("An error occurred while opening %1").arg(path); + return Zip::OpenFailed; + } + + const Zip::ErrorCode ec = (level == Zip::Store) + ? storeFile(path, file, crc, written, keys) + : compressFile(path, file, crc, written, level, keys); + + file.close(); + return ec; +} + +//! \internal +Zip::ErrorCode ZipPrivate::storeFile(const QString& path, QIODevice& file, + quint32& crc, qint64& totalWritten, quint32** keys) +{ + Q_UNUSED(path); + + qint64 read = 0; + qint64 written = 0; + + const bool encrypt = keys != 0; + + totalWritten = 0; + crc = crc32(0L, Z_NULL, 0); + + while ( (read = file.read(buffer1, ZIP_READ_BUFFER)) > 0 ) { + crc = crc32(crc, uBuffer, read); + if (encrypt) + encryptBytes(*keys, buffer1, read); + written = device->write(buffer1, read); + totalWritten += written; + if (written != read) { + return Zip::WriteFailed; + } + } + + return Zip::Ok; +} + +//! \internal +int ZipPrivate::compressionStrategy(const QString& path, QIODevice& file) const +{ + Q_UNUSED(file); + +#ifndef OSDAB_ZIP_NO_PNG_RLE + return Z_DEFAULT_STRATEGY; +#endif + const bool isPng = path.endsWith(QLatin1String("png"), Qt::CaseInsensitive); + return isPng ? Z_RLE : Z_DEFAULT_STRATEGY; +} + +//! \internal +Zip::ErrorCode ZipPrivate::compressFile(const QString& path, QIODevice& file, + quint32& crc, qint64& totalWritten, const Zip::CompressionLevel& level, quint32** keys) +{ + qint64 read = 0; + qint64 written = 0; + + qint64 totRead = 0; + qint64 toRead = file.size(); + + const bool encrypt = keys != 0; + const int strategy = compressionStrategy(path, file); + + totalWritten = 0; + crc = crc32(0L, Z_NULL, 0); + + z_stream zstr; + + // Initialize zalloc, zfree and opaque before calling the init function + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + + int zret; + + // Use deflateInit2 with negative windowBits to get raw compression + if ((zret = deflateInit2_( + &zstr, + (int)level, // compression level + Z_DEFLATED, // method + -MAX_WBITS, // windowBits + 8, // memLevel + strategy, + ZLIB_VERSION, + sizeof(z_stream) + )) != Z_OK ) { + qDebug() << "Could not initialize zlib for compression"; + return Zip::ZlibError; + } + + qint64 compressed; + int flush = Z_NO_FLUSH; + do { + read = file.read(buffer1, ZIP_READ_BUFFER); + totRead += read; + if (!read) + break; + + if (read < 0) { + deflateEnd(&zstr); + qDebug() << QString("Error while reading %1").arg(path); + return Zip::ReadFailed; + } + + crc = crc32(crc, uBuffer, read); + + zstr.next_in = (Bytef*) buffer1; + zstr.avail_in = (uInt)read; + + // Tell zlib if this is the last chunk we want to encode + // by setting the flush parameter to Z_FINISH + flush = (totRead == toRead) ? Z_FINISH : Z_NO_FLUSH; + + // Run deflate() on input until output buffer not full + // finish compression if all of source has been read in + do { + zstr.next_out = (Bytef*) buffer2; + zstr.avail_out = ZIP_READ_BUFFER; + + zret = deflate(&zstr, flush); + // State not clobbered + Q_ASSERT(zret != Z_STREAM_ERROR); + + // Write compressed data to file and empty buffer + compressed = ZIP_READ_BUFFER - zstr.avail_out; + + if (encrypt) + encryptBytes(*keys, buffer2, compressed); + + written = device->write(buffer2, compressed); + totalWritten += written; + + if (written != compressed) { + deflateEnd(&zstr); + qDebug() << QString("Error while writing %1").arg(path); + return Zip::WriteFailed; + } + + } while (zstr.avail_out == 0); + + // All input will be used + Q_ASSERT(zstr.avail_in == 0); + + } while (flush != Z_FINISH); + + // Stream will be complete + Q_ASSERT(zret == Z_STREAM_END); + deflateEnd(&zstr); + + return Zip::Ok; +} + +//! \internal Writes a new entry in the zip file. +Zip::ErrorCode ZipPrivate::createEntry(const QFileInfo& file, const QString& root, + Zip::CompressionLevel level) +{ + const bool dirOnly = file.isDir(); + + // entryName contains the path as it should be written + // in the zip file records + const QString entryName = dirOnly + ? root + : root + file.fileName(); + + // Directory entry + if (dirOnly || file.size() < ZIP_COMPRESSION_THRESHOLD) { + level = Zip::Store; + } else { + switch (level) { + case Zip::AutoCPU: + level = Zip::Deflate5; +#ifndef OSDAB_ZIP_NO_DEBUG + qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); +#endif + break; + case Zip::AutoMIME: + level = detectCompressionByMime(file.completeSuffix().toLower()); +#ifndef OSDAB_ZIP_NO_DEBUG + qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); +#endif + break; + case Zip::AutoFull: + level = detectCompressionByMime(file.completeSuffix().toLower()); +#ifndef OSDAB_ZIP_NO_DEBUG + qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); +#endif + break; + default: ; + } + } + + + + // create header and store it to write a central directory later + QScopedPointer h(new ZipEntryP); + h->absolutePath = file.absoluteFilePath().toLower(); + h->fileSize = file.size(); + + // Set encryption bit and set the data descriptor bit + // so we can use mod time instead of crc for password check + bool encrypt = !dirOnly && !password.isEmpty(); + if (encrypt) + h->gpFlag[0] |= 9; + + QDateTime dt = file.lastModified(); + dt = OSDAB_ZIP_MANGLE(fromFileTimestamp)(dt); + QDate d = dt.date(); + h->modDate[1] = ((d.year() - 1980) << 1) & 254; + h->modDate[1] |= ((d.month() >> 3) & 1); + h->modDate[0] = ((d.month() & 7) << 5) & 224; + h->modDate[0] |= d.day(); + + QTime t = dt.time(); + h->modTime[1] = (t.hour() << 3) & 248; + h->modTime[1] |= ((t.minute() >> 3) & 7); + h->modTime[0] = ((t.minute() & 7) << 5) & 224; + h->modTime[0] |= t.second() / 2; + + h->szUncomp = dirOnly ? 0 : file.size(); + + h->compMethod = (level == Zip::Store) ? 0 : 0x0008; + + // **** Write local file header **** + + // signature + buffer1[0] = 'P'; buffer1[1] = 'K'; + buffer1[2] = 0x3; buffer1[3] = 0x4; + + // version needed to extract + buffer1[ZIP_LH_OFF_VERS] = ZIP_VERSION; + buffer1[ZIP_LH_OFF_VERS + 1] = 0; + + // general purpose flag + buffer1[ZIP_LH_OFF_GPFLAG] = h->gpFlag[0]; + buffer1[ZIP_LH_OFF_GPFLAG + 1] = h->gpFlag[1]; + + // compression method + buffer1[ZIP_LH_OFF_CMET] = h->compMethod & 0xFF; + buffer1[ZIP_LH_OFF_CMET + 1] = (h->compMethod>>8) & 0xFF; + + // last mod file time + buffer1[ZIP_LH_OFF_MODT] = h->modTime[0]; + buffer1[ZIP_LH_OFF_MODT + 1] = h->modTime[1]; + + // last mod file date + buffer1[ZIP_LH_OFF_MODD] = h->modDate[0]; + buffer1[ZIP_LH_OFF_MODD + 1] = h->modDate[1]; + + // skip crc (4bytes) [14,15,16,17] + + // skip compressed size but include evtl. encryption header (4bytes: [18,19,20,21]) + buffer1[ZIP_LH_OFF_CSIZE] = + buffer1[ZIP_LH_OFF_CSIZE + 1] = + buffer1[ZIP_LH_OFF_CSIZE + 2] = + buffer1[ZIP_LH_OFF_CSIZE + 3] = 0; + + h->szComp = encrypt ? ZIP_LOCAL_ENC_HEADER_SIZE : 0; + + // uncompressed size [22,23,24,25] + setULong(h->szUncomp, buffer1, ZIP_LH_OFF_USIZE); + + // filename length + QByteArray entryNameBytes = entryName.toLatin1(); + int sz = entryNameBytes.size(); + + buffer1[ZIP_LH_OFF_NAMELEN] = sz & 0xFF; + buffer1[ZIP_LH_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF; + + // extra field length + buffer1[ZIP_LH_OFF_XLEN] = buffer1[ZIP_LH_OFF_XLEN + 1] = 0; + + // Store offset to write crc and compressed size + h->lhOffset = device->pos(); + quint32 crcOffset = h->lhOffset + ZIP_LH_OFF_CRC; + + if (device->write(buffer1, ZIP_LOCAL_HEADER_SIZE) != ZIP_LOCAL_HEADER_SIZE) { + return Zip::WriteFailed; + } + + // Write out filename + if (device->write(entryNameBytes) != sz) { + return Zip::WriteFailed; + } + + // Encryption keys + quint32 keys[3] = { 0, 0, 0 }; + + if (encrypt) { + // **** encryption header **** + + // XOR with PI to ensure better random numbers + // with poorly implemented rand() as suggested by Info-Zip + srand(time(NULL) ^ 3141592654UL); + int randByte; + + initKeys(keys); + for (int i = 0; i < 10; ++i) { + randByte = (rand() >> 7) & 0xff; + buffer1[i] = decryptByte(keys[2]) ^ randByte; + updateKeys(keys, randByte); + } + + // Encrypt encryption header + initKeys(keys); + for (int i = 0; i < 10; ++i) { + randByte = decryptByte(keys[2]); + updateKeys(keys, buffer1[i]); + buffer1[i] ^= randByte; + } + + // We don't know the CRC at this time, so we use the modification time + // as the last two bytes + randByte = decryptByte(keys[2]); + updateKeys(keys, h->modTime[0]); + buffer1[10] ^= randByte; + + randByte = decryptByte(keys[2]); + updateKeys(keys, h->modTime[1]); + buffer1[11] ^= randByte; + + // Write out encryption header + if (device->write(buffer1, ZIP_LOCAL_ENC_HEADER_SIZE) != ZIP_LOCAL_ENC_HEADER_SIZE) { + return Zip::WriteFailed; + } + } + + quint32 crc = 0; + qint64 written = 0; + + if (!dirOnly) { + quint32* k = keys; + const Zip::ErrorCode ec = deflateFile(file, crc, written, level, encrypt ? &k : 0); + if (ec != Zip::Ok) + return ec; + Q_ASSERT(!h.isNull()); + } + + // Store end of entry offset + quint32 current = device->pos(); + + // Update crc and compressed size in local header + if (!device->seek(crcOffset)) { + return Zip::SeekFailed; + } + + h->crc = dirOnly ? 0 : crc; + h->szComp += written; + + setULong(h->crc, buffer1, 0); + setULong(h->szComp, buffer1, 4); + if ( device->write(buffer1, 8) != 8) { + return Zip::WriteFailed; + } + + // Seek to end of entry + if (!device->seek(current)) { + return Zip::SeekFailed; + } + + if ((h->gpFlag[0] & 8) == 8) { + // Write data descriptor + + // Signature: PK\7\8 + buffer1[0] = 'P'; + buffer1[1] = 'K'; + buffer1[2] = 0x07; + buffer1[3] = 0x08; + + // CRC + setULong(h->crc, buffer1, ZIP_DD_OFF_CRC32); + + // Compressed size + setULong(h->szComp, buffer1, ZIP_DD_OFF_CSIZE); + + // Uncompressed size + setULong(h->szUncomp, buffer1, ZIP_DD_OFF_USIZE); + + if (device->write(buffer1, ZIP_DD_SIZE_WS) != ZIP_DD_SIZE_WS) { + return Zip::WriteFailed; + } + } + + headers->insert(entryName, h.take()); + return Zip::Ok; +} + +//! \internal +int ZipPrivate::decryptByte(quint32 key2) const +{ + quint16 temp = ((quint16)(key2) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +//! \internal Writes an quint32 (4 bytes) to a byte array at given offset. +void ZipPrivate::setULong(quint32 v, char* buffer, unsigned int offset) +{ + buffer[offset+3] = ((v >> 24) & 0xFF); + buffer[offset+2] = ((v >> 16) & 0xFF); + buffer[offset+1] = ((v >> 8) & 0xFF); + buffer[offset] = (v & 0xFF); +} + +//! \internal Initializes decryption keys using a password. +void ZipPrivate::initKeys(quint32* keys) const +{ + // Encryption keys initialization constants are taken from the + // PKZip file format specification docs + keys[0] = 305419896L; + keys[1] = 591751049L; + keys[2] = 878082192L; + + QByteArray pwdBytes = password.toLatin1(); + int sz = pwdBytes.size(); + const char* ascii = pwdBytes.data(); + + for (int i = 0; i < sz; ++i) + updateKeys(keys, (int)ascii[i]); +} + +//! Updates a one-char-only CRC; it's the Info-Zip macro re-adapted. +quint32 ZipPrivate::updateChecksum(const quint32& crc, const quint32& val) const +{ + return quint32(crcTable[quint32(crc^val) & 0xff] ^ crc_t(crc >> 8)); +} + +//! \internal Updates encryption keys. +void ZipPrivate::updateKeys(quint32* keys, int c) const +{ + keys[0] = updateChecksum(keys[0], c); + keys[1] += keys[0] & 0xff; + keys[1] = keys[1] * 134775813L + 1; + keys[2] = updateChecksum(keys[2], ((int)keys[1]) >> 24); +} + +//! \internal Encrypts a byte array. +void ZipPrivate::encryptBytes(quint32* keys, char* buffer, qint64 read) +{ + char t; + + for (qint64 i = 0; i < read; ++i) { + t = buffer[i]; + buffer[i] ^= decryptByte(keys[2]); + updateKeys(keys, t); + } +} + +namespace { +struct KeywordHelper { + const QString needle; + inline KeywordHelper(const QString& keyword) : needle(keyword) {} +}; + +bool operator<(const KeywordHelper& helper, const char* keyword) { + return helper.needle.compare(QLatin1String(keyword)) < 0; +} + +bool operator<(const char* keyword, const KeywordHelper& helper) { + return helper.needle.compare(QLatin1String(keyword)) > 0; +} + +bool hasExtension(const QString& ext, const char* const* map, int max) { + const char* const* start = &map[0]; + const char* const* end = &map[max - 1]; + const char* const* kw = qBinaryFind(start, end, KeywordHelper(ext)); + return kw != end; +} +} + +//! \internal Detects the best compression level for a given file extension. +Zip::CompressionLevel ZipPrivate::detectCompressionByMime(const QString& ext) +{ + // NOTE: Keep the MAX_* and the number of strings in the map up to date. + // NOTE: Alphabetically sort the strings in the map -- we use a binary search! + + // Archives or files that will hardly compress + const int MAX_EXT1 = 14; + const char* const ext1[MAX_EXT1] = { + "7z", "bin", "deb", "exe", "gz", "gz2", "jar", "rar", "rpm", "tar", "tgz", "z", "zip", + 0 // # MAX_EXT1 + }; + + // Slow or usually large files that we should not spend to much time with + const int MAX_EXT2 = 24; + const char* const ext2[MAX_EXT2] = { + "asf", + "avi", + "divx", + "doc", + "docx", + "flv", + "gif", + "iso", + "jpg", + "jpeg", + "mka", + "mkv", + "mp3", + "mp4", + "mpeg", + "mpg", + "odt", + "ogg", + "ogm", + "ra", + "rm", + "wma", + "wmv", + 0 // # MAX_EXT2 + }; + + // Files with high compression ratio + const int MAX_EXT3 = 28; + const char* const ext3[MAX_EXT3] = { + "asp", "bat", "c", "conf", "cpp", "cpp", "css", "csv", "cxx", "h", "hpp", "htm", "html", "hxx", + "ini", "js", "php", "pl", "py", "rtf", "sh", "tsv", "txt", "vb", "vbs", "xml", "xst", + 0 // # MAX_EXT3 + }; + + const char* const* map = ext1; + if (hasExtension(ext, map, MAX_EXT1)) + return Zip::Store; + + map = ext2; + if (hasExtension(ext, map, MAX_EXT2)) + return Zip::Deflate2; + + map = ext3; + if (hasExtension(ext, map, MAX_EXT3)) + return Zip::Deflate9; + + return Zip::Deflate5; +} + +/*! + Closes the current archive and writes out pending data. +*/ +Zip::ErrorCode ZipPrivate::closeArchive() +{ + if (!device) { + Q_ASSERT(!file); + return Zip::Ok; + } + + if (device != file) + disconnect(device, 0, this, 0); + + return do_closeArchive(); +} + +//! \internal +Zip::ErrorCode ZipPrivate::do_closeArchive() +{ + // Close current archive by writing out central directory + // and free up resources + + if (!device && !headers) + return Zip::Ok; + + quint32 szCentralDir = 0; + quint32 offCentralDir = device->pos(); + Zip::ErrorCode c = Zip::Ok; + + if (headers && device) { + for (QMap::ConstIterator itr = headers->constBegin(); + itr != headers->constEnd(); ++itr) { + const QString fileName = itr.key(); + const ZipEntryP* h = itr.value(); + c = writeEntry(fileName, h, szCentralDir); + } + } + + if (c == Zip::Ok) + c = writeCentralDir(offCentralDir, szCentralDir); + + if (c != Zip::Ok) { + if (file) { + file->close(); + if (!file->remove()) { + qDebug() << "Failed to delete corrupt archive."; + } + } + } + + return c; +} + +//! \internal +Zip::ErrorCode ZipPrivate::writeEntry(const QString& fileName, const ZipEntryP* h, quint32& szCentralDir) +{ + unsigned int sz; + + Q_ASSERT(h && device && headers); + + // signature + buffer1[0] = 'P'; + buffer1[1] = 'K'; + buffer1[2] = 0x01; + buffer1[3] = 0x02; + + // version made by (currently only MS-DOS/FAT - no symlinks or other stuff supported) + buffer1[ZIP_CD_OFF_MADEBY] = buffer1[ZIP_CD_OFF_MADEBY + 1] = 0; + + // version needed to extract + buffer1[ZIP_CD_OFF_VERSION] = ZIP_VERSION; + buffer1[ZIP_CD_OFF_VERSION + 1] = 0; + + // general purpose flag + buffer1[ZIP_CD_OFF_GPFLAG] = h->gpFlag[0]; + buffer1[ZIP_CD_OFF_GPFLAG + 1] = h->gpFlag[1]; + + // compression method + buffer1[ZIP_CD_OFF_CMET] = h->compMethod & 0xFF; + buffer1[ZIP_CD_OFF_CMET + 1] = (h->compMethod >> 8) & 0xFF; + + // last mod file time + buffer1[ZIP_CD_OFF_MODT] = h->modTime[0]; + buffer1[ZIP_CD_OFF_MODT + 1] = h->modTime[1]; + + // last mod file date + buffer1[ZIP_CD_OFF_MODD] = h->modDate[0]; + buffer1[ZIP_CD_OFF_MODD + 1] = h->modDate[1]; + + // crc (4bytes) [16,17,18,19] + setULong(h->crc, buffer1, ZIP_CD_OFF_CRC); + + // compressed size (4bytes: [20,21,22,23]) + setULong(h->szComp, buffer1, ZIP_CD_OFF_CSIZE); + + // uncompressed size [24,25,26,27] + setULong(h->szUncomp, buffer1, ZIP_CD_OFF_USIZE); + + // filename + QByteArray fileNameBytes = fileName.toLatin1(); + sz = fileNameBytes.size(); + buffer1[ZIP_CD_OFF_NAMELEN] = sz & 0xFF; + buffer1[ZIP_CD_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF; + + // extra field length + buffer1[ZIP_CD_OFF_XLEN] = buffer1[ZIP_CD_OFF_XLEN + 1] = 0; + + // file comment length + buffer1[ZIP_CD_OFF_COMMLEN] = buffer1[ZIP_CD_OFF_COMMLEN + 1] = 0; + + // disk number start + buffer1[ZIP_CD_OFF_DISKSTART] = buffer1[ZIP_CD_OFF_DISKSTART + 1] = 0; + + // internal file attributes + buffer1[ZIP_CD_OFF_IATTR] = buffer1[ZIP_CD_OFF_IATTR + 1] = 0; + + // external file attributes + buffer1[ZIP_CD_OFF_EATTR] = + buffer1[ZIP_CD_OFF_EATTR + 1] = + buffer1[ZIP_CD_OFF_EATTR + 2] = + buffer1[ZIP_CD_OFF_EATTR + 3] = 0; + + // relative offset of local header [42->45] + setULong(h->lhOffset, buffer1, ZIP_CD_OFF_LHOFF); + + if (device->write(buffer1, ZIP_CD_SIZE) != ZIP_CD_SIZE) { + return Zip::WriteFailed; + } + + // Write out filename + if ((unsigned int)device->write(fileNameBytes) != sz) { + return Zip::WriteFailed; + } + + szCentralDir += (ZIP_CD_SIZE + sz); + + return Zip::Ok; +} + +//! \internal +Zip::ErrorCode ZipPrivate::writeCentralDir(quint32 offCentralDir, quint32 szCentralDir) +{ + Q_ASSERT(device && headers); + + unsigned int sz; + + // signature + buffer1[0] = 'P'; + buffer1[1] = 'K'; + buffer1[2] = 0x05; + buffer1[3] = 0x06; + + // number of this disk + buffer1[ZIP_EOCD_OFF_DISKNUM] = buffer1[ZIP_EOCD_OFF_DISKNUM + 1] = 0; + + // number of disk with central directory + buffer1[ZIP_EOCD_OFF_CDDISKNUM] = buffer1[ZIP_EOCD_OFF_CDDISKNUM + 1] = 0; + + // number of entries in this disk + sz = headers->count(); + buffer1[ZIP_EOCD_OFF_ENTRIES] = sz & 0xFF; + buffer1[ZIP_EOCD_OFF_ENTRIES + 1] = (sz >> 8) & 0xFF; + + // total number of entries + buffer1[ZIP_EOCD_OFF_CDENTRIES] = buffer1[ZIP_EOCD_OFF_ENTRIES]; + buffer1[ZIP_EOCD_OFF_CDENTRIES + 1] = buffer1[ZIP_EOCD_OFF_ENTRIES + 1]; + + // size of central directory [12->15] + setULong(szCentralDir, buffer1, ZIP_EOCD_OFF_CDSIZE); + + // central dir offset [16->19] + setULong(offCentralDir, buffer1, ZIP_EOCD_OFF_CDOFF); + + // ZIP file comment length + QByteArray commentBytes = comment.toLatin1(); + quint16 commentLength = commentBytes.size(); + + if (commentLength == 0) { + buffer1[ZIP_EOCD_OFF_COMMLEN] = buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = 0; + } else { + buffer1[ZIP_EOCD_OFF_COMMLEN] = commentLength & 0xFF; + buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = (commentLength >> 8) & 0xFF; + } + + if (device->write(buffer1, ZIP_EOCD_SIZE) != ZIP_EOCD_SIZE) { + return Zip::WriteFailed; + } + + if (commentLength != 0) { + if ((unsigned int)device->write(commentBytes) != commentLength) { + return Zip::WriteFailed; + } + } + + return Zip::Ok; +} + +//! \internal +void ZipPrivate::reset() +{ + comment.clear(); + + if (headers) { + qDeleteAll(*headers); + delete headers; + headers = 0; + } + + device = 0; + + if (file) + delete file; + file = 0; +} + +//! \internal Returns the path of the parent directory +QString ZipPrivate::extractRoot(const QString& p, Zip::CompressionOptions o) +{ + Q_UNUSED(o); + QDir d(QDir::cleanPath(p)); + if (!d.exists()) + return QString(); + + if (!d.cdUp()) + return QString(); + + return d.absolutePath(); +} + + +/************************************************************************ + Public interface +*************************************************************************/ + +/*! + Creates a new Zip file compressor. +*/ +Zip::Zip() : d(new ZipPrivate) +{ +} + +/*! + Closes any open archive and releases used resources. +*/ +Zip::~Zip() +{ + closeArchive(); + delete d; +} + +/*! + Returns true if there is an open archive. +*/ +bool Zip::isOpen() const +{ + return d->device; +} + +/*! + Sets the password to be used for the next files being added! + Files added before calling this method will use the previously + set password (if any). + Closing the archive won't clear the password! +*/ +void Zip::setPassword(const QString& pwd) +{ + d->password = pwd; +} + +//! Convenience method, clears the current password. +void Zip::clearPassword() +{ + d->password.clear(); +} + +//! Returns the currently used password. +QString Zip::password() const +{ + return d->password; +} + +/*! + Attempts to create a new Zip archive. If \p overwrite is true and the file + already exist it will be overwritten. + Any open archive will be closed. + */ +Zip::ErrorCode Zip::createArchive(const QString& filename, bool overwrite) +{ + closeArchive(); + Q_ASSERT(!d->device && !d->file); + + if (filename.isEmpty()) + return Zip::FileNotFound; + + d->file = new QFile(filename); + + if (d->file->exists() && !overwrite) { + delete d->file; + d->file = 0; + return Zip::FileExists; + } + + if (!d->file->open(QIODevice::WriteOnly)) { + delete d->file; + d->file = 0; + return Zip::OpenFailed; + } + + const Zip::ErrorCode ec = createArchive(d->file); + if (ec != Zip::Ok) { + closeArchive(); + } + + return ec; +} + +/*! + Attempts to create a new Zip archive. If there is another open archive this will be closed. + \warning The class takes ownership of the device! + */ +Zip::ErrorCode Zip::createArchive(QIODevice* device) +{ + if (!device) { + qDebug() << "Invalid device."; + return Zip::OpenFailed; + } + + return d->createArchive(device); +} + +/*! + Returns the current archive comment. +*/ +QString Zip::archiveComment() const +{ + return d->comment; +} + +/*! + Sets the comment for this archive. Note: createArchive() should have been + called before. +*/ +void Zip::setArchiveComment(const QString& comment) +{ + d->comment = comment; +} + +/*! + Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::IgnorePaths flag as compression option and an empty \p root parameter. + + The result is that all files found in \p path (and in subdirectories) are + added to the zip file without a directory entry. +*/ +Zip::ErrorCode Zip::addDirectoryContents(const QString& path, CompressionLevel level) +{ + return addDirectory(path, QString(), IgnorePaths, level); +} + +/*! + Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::IgnorePaths flag as compression option. + + The result is that all files found in \p path (and in subdirectories) are + added to the zip file without a directory entry (or within a directory + structure specified by \p root). +*/ +Zip::ErrorCode Zip::addDirectoryContents(const QString& path, const QString& root, CompressionLevel level) +{ + return addDirectory(path, root, IgnorePaths, level); +} + +/*! + Convenience method, same as calling + Zip::addDirectory(const QString&,const QString&,CompressionLevel) + with an empty \p root parameter and Zip::RelativePaths flag as compression option. + */ +Zip::ErrorCode Zip::addDirectory(const QString& path, CompressionLevel level) +{ + return addDirectory(path, QString(), Zip::RelativePaths, level); +} + +/*! + Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::RelativePaths flag as compression option. + */ +Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, CompressionLevel level) +{ + return addDirectory(path, root, Zip::RelativePaths, level); +} + +/*! + Recursively adds files contained in \p dir to the archive, using \p root as name for the root folder. + Stops adding files if some error occurs. + + The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. + This means that the last one overwrites the previous one (if some conflict occurs), i.e. + Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. + + The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / + is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). + + If \p addedFiles is not null it is set to the number of successfully added + files. +*/ +Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, + CompressionOptions options, CompressionLevel level, int* addedFiles) +{ + const int hierarchyLev = 0; + return d->addDirectory(path, root, options, level, hierarchyLev, addedFiles); +} + +/*! + Convenience method, same as calling Zip::addFile(const QString&,const QString&,CompressionOptions,CompressionLevel) + with an empty \p root parameter and Zip::RelativePaths as compression option. + */ +Zip::ErrorCode Zip::addFile(const QString& path, CompressionLevel level) +{ + return addFile(path, QString(), Zip::RelativePaths, level); +} + +/*! + Convenience method, same as calling Zip::addFile(const QString&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::RelativePaths flag as compression option. + */ +Zip::ErrorCode Zip::addFile(const QString& path, const QString& root, + CompressionLevel level) +{ + return addFile(path, root, Zip::RelativePaths, level); +} + +/*! + Adds the file at \p path to the archive, using \p root as name for the root folder. + If \p path points to a directory the behaviour is basically the same as + addDirectory(). + + The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. + This means that the last one overwrites the previous one (if some conflict occurs), i.e. + Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. + + The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / + is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). +*/ +Zip::ErrorCode Zip::addFile(const QString& path, const QString& root, + CompressionOptions options, CompressionLevel level) +{ + if (path.isEmpty()) + return Zip::Ok; + return addFiles(QStringList() << path, root, options, level); +} + +/*! + Convenience method, same as calling Zip::addFiles(const QStringList&,const QString&,CompressionOptions,CompressionLevel) + with an empty \p root parameter and Zip::RelativePaths as compression option. + */ +Zip::ErrorCode Zip::addFiles(const QStringList& paths, CompressionLevel level) +{ + return addFiles(paths, QString(), Zip::RelativePaths, level); +} + +/*! + Convenience method, same as calling Zip::addFiles(const QStringList&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::RelativePaths flag as compression option. + */ +Zip::ErrorCode Zip::addFiles(const QStringList& paths, const QString& root, + CompressionLevel level) +{ + return addFiles(paths, root, Zip::RelativePaths, level); +} + +/*! + Adds the files or directories in \p paths to the archive, using \p root as + name for the root folder. + This is similar to calling addFile or addDirectory for all the entries in + \p paths, except it is slightly faster. + + The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. + This means that the last one overwrites the previous one (if some conflict occurs), i.e. + Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. + + The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / + is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). + + If \p addedFiles is not null it is set to the number of successfully added + files. +*/ +Zip::ErrorCode Zip::addFiles(const QStringList& paths, const QString& root, + CompressionOptions options, CompressionLevel level, int* addedFiles) +{ + return d->addFiles(paths, root, options, level, addedFiles); +} + +/*! + Closes the archive and writes any pending data. +*/ +Zip::ErrorCode Zip::closeArchive() +{ + Zip::ErrorCode ec = d->closeArchive(); + d->reset(); + return ec; +} + +/*! + Returns a locale translated error string for a given error code. +*/ +QString Zip::formatError(Zip::ErrorCode c) const +{ + switch (c) + { + case Ok: return QCoreApplication::translate("Zip", "ZIP operation completed successfully."); break; + case ZlibInit: return QCoreApplication::translate("Zip", "Failed to initialize or load zlib library."); break; + case ZlibError: return QCoreApplication::translate("Zip", "zlib library error."); break; + case OpenFailed: return QCoreApplication::translate("Zip", "Unable to create or open file."); break; + case NoOpenArchive: return QCoreApplication::translate("Zip", "No archive has been created yet."); break; + case FileNotFound: return QCoreApplication::translate("Zip", "File or directory does not exist."); break; + case ReadFailed: return QCoreApplication::translate("Zip", "File read error."); break; + case WriteFailed: return QCoreApplication::translate("Zip", "File write error."); break; + case SeekFailed: return QCoreApplication::translate("Zip", "File seek error."); break; + default: ; + } + + return QCoreApplication::translate("Zip", "Unknown error."); +} + +OSDAB_END_NAMESPACE diff --git a/oracle/src/zip/zip.h b/oracle/src/zip/zip.h index 79c512ce..79f32ada 100755 --- a/oracle/src/zip/zip.h +++ b/oracle/src/zip/zip.h @@ -1,158 +1,158 @@ -/**************************************************************************** -** Filename: zip.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#ifndef OSDAB_ZIP__H -#define OSDAB_ZIP__H - -#include "zipglobal.h" - -#include -#include - -#include - -class QIODevice; -class QFile; -class QDir; -class QStringList; -class QString; - -OSDAB_BEGIN_NAMESPACE(Zip) - -class ZipPrivate; - -class OSDAB_ZIP_EXPORT Zip -{ -public: - enum ErrorCode - { - Ok, - ZlibInit, - ZlibError, - FileExists, - OpenFailed, - NoOpenArchive, - FileNotFound, - ReadFailed, - WriteFailed, - SeekFailed, - InternalError - }; - - enum CompressionLevel - { - Store, - Deflate1 = 1, Deflate2, Deflate3, Deflate4, - Deflate5, Deflate6, Deflate7, Deflate8, Deflate9, - AutoCPU, AutoMIME, AutoFull - }; - - enum CompressionOption - { - /*! Does not preserve absolute paths in the zip file when adding a - file or directory (default) */ - RelativePaths = 0x0001, - /*! Preserve absolute paths */ - AbsolutePaths = 0x0002, - /*! Do not store paths. All the files are put in the (evtl. user defined) - root of the zip file */ - IgnorePaths = 0x0004, - /*! Works only with addDirectory(). Adds the directory's contents, - including subdirectories, but does not add an entry for the root - directory itself. */ - IgnoreRoot = 0x0008, - /*! Used only when compressing a directory or multiple files. - If set invalid or unreadable files are simply skipped. - */ - SkipBadFiles = 0x0020, - /*! Makes sure a file is never added twice to the same zip archive. - This check is only necessary in certain usage scenarios and given - that it slows down processing you need to enable it explicitly with - this flag. - */ - CheckForDuplicates = 0x0040 - }; - Q_DECLARE_FLAGS(CompressionOptions, CompressionOption) - - Zip(); - virtual ~Zip(); - - bool isOpen() const; - - void setPassword(const QString& pwd); - void clearPassword(); - QString password() const; - - ErrorCode createArchive(const QString& file, bool overwrite = true); - ErrorCode createArchive(QIODevice* device); - - QString archiveComment() const; - void setArchiveComment(const QString& comment); - - ErrorCode addDirectoryContents(const QString& path, - CompressionLevel level = AutoFull); - ErrorCode addDirectoryContents(const QString& path, const QString& root, - CompressionLevel level = AutoFull); - - ErrorCode addDirectory(const QString& path, - CompressionLevel level = AutoFull); - ErrorCode addDirectory(const QString& path, const QString& root, - CompressionLevel level = AutoFull); - ErrorCode addDirectory(const QString& path, const QString& root, - CompressionOptions options, CompressionLevel level = AutoFull, - int* addedFiles = 0); - - ErrorCode addFile(const QString& path, - CompressionLevel level = AutoFull); - ErrorCode addFile(const QString& path, const QString& root, - CompressionLevel level = AutoFull); - ErrorCode addFile(const QString& path, const QString& root, - CompressionOptions options, - CompressionLevel level = AutoFull); - - ErrorCode addFiles(const QStringList& paths, - CompressionLevel level = AutoFull); - ErrorCode addFiles(const QStringList& paths, const QString& root, - CompressionLevel level = AutoFull); - ErrorCode addFiles(const QStringList& paths, const QString& root, - CompressionOptions options, - CompressionLevel level = AutoFull, - int* addedFiles = 0); - - ErrorCode closeArchive(); - - QString formatError(ErrorCode c) const; - -private: - ZipPrivate* d; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(Zip::CompressionOptions) - -OSDAB_END_NAMESPACE - -#endif // OSDAB_ZIP__H +/**************************************************************************** +** Filename: zip.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#ifndef OSDAB_ZIP__H +#define OSDAB_ZIP__H + +#include "zipglobal.h" + +#include +#include + +#include + +class QIODevice; +class QFile; +class QDir; +class QStringList; +class QString; + +OSDAB_BEGIN_NAMESPACE(Zip) + +class ZipPrivate; + +class OSDAB_ZIP_EXPORT Zip +{ +public: + enum ErrorCode + { + Ok, + ZlibInit, + ZlibError, + FileExists, + OpenFailed, + NoOpenArchive, + FileNotFound, + ReadFailed, + WriteFailed, + SeekFailed, + InternalError + }; + + enum CompressionLevel + { + Store, + Deflate1 = 1, Deflate2, Deflate3, Deflate4, + Deflate5, Deflate6, Deflate7, Deflate8, Deflate9, + AutoCPU, AutoMIME, AutoFull + }; + + enum CompressionOption + { + /*! Does not preserve absolute paths in the zip file when adding a + file or directory (default) */ + RelativePaths = 0x0001, + /*! Preserve absolute paths */ + AbsolutePaths = 0x0002, + /*! Do not store paths. All the files are put in the (evtl. user defined) + root of the zip file */ + IgnorePaths = 0x0004, + /*! Works only with addDirectory(). Adds the directory's contents, + including subdirectories, but does not add an entry for the root + directory itself. */ + IgnoreRoot = 0x0008, + /*! Used only when compressing a directory or multiple files. + If set invalid or unreadable files are simply skipped. + */ + SkipBadFiles = 0x0020, + /*! Makes sure a file is never added twice to the same zip archive. + This check is only necessary in certain usage scenarios and given + that it slows down processing you need to enable it explicitly with + this flag. + */ + CheckForDuplicates = 0x0040 + }; + Q_DECLARE_FLAGS(CompressionOptions, CompressionOption) + + Zip(); + virtual ~Zip(); + + bool isOpen() const; + + void setPassword(const QString& pwd); + void clearPassword(); + QString password() const; + + ErrorCode createArchive(const QString& file, bool overwrite = true); + ErrorCode createArchive(QIODevice* device); + + QString archiveComment() const; + void setArchiveComment(const QString& comment); + + ErrorCode addDirectoryContents(const QString& path, + CompressionLevel level = AutoFull); + ErrorCode addDirectoryContents(const QString& path, const QString& root, + CompressionLevel level = AutoFull); + + ErrorCode addDirectory(const QString& path, + CompressionLevel level = AutoFull); + ErrorCode addDirectory(const QString& path, const QString& root, + CompressionLevel level = AutoFull); + ErrorCode addDirectory(const QString& path, const QString& root, + CompressionOptions options, CompressionLevel level = AutoFull, + int* addedFiles = 0); + + ErrorCode addFile(const QString& path, + CompressionLevel level = AutoFull); + ErrorCode addFile(const QString& path, const QString& root, + CompressionLevel level = AutoFull); + ErrorCode addFile(const QString& path, const QString& root, + CompressionOptions options, + CompressionLevel level = AutoFull); + + ErrorCode addFiles(const QStringList& paths, + CompressionLevel level = AutoFull); + ErrorCode addFiles(const QStringList& paths, const QString& root, + CompressionLevel level = AutoFull); + ErrorCode addFiles(const QStringList& paths, const QString& root, + CompressionOptions options, + CompressionLevel level = AutoFull, + int* addedFiles = 0); + + ErrorCode closeArchive(); + + QString formatError(ErrorCode c) const; + +private: + ZipPrivate* d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Zip::CompressionOptions) + +OSDAB_END_NAMESPACE + +#endif // OSDAB_ZIP__H diff --git a/oracle/src/zip/zip_p.h b/oracle/src/zip/zip_p.h index 81c2dacd..b8ec6564 100755 --- a/oracle/src/zip/zip_p.h +++ b/oracle/src/zip/zip_p.h @@ -1,133 +1,133 @@ -/**************************************************************************** -** Filename: zip_p.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Zip/UnZip API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef OSDAB_ZIP_P__H -#define OSDAB_ZIP_P__H - -#include "zip.h" -#include "zipentry_p.h" - -#include -#include -#include - -#include - -/*! - zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate()) - we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;) -*/ -#define ZIP_READ_BUFFER (256*1024) - -OSDAB_BEGIN_NAMESPACE(Zip) - -class ZipPrivate : public QObject -{ - Q_OBJECT - -public: - // uLongf from zconf.h - typedef uLongf crc_t; - - ZipPrivate(); - virtual ~ZipPrivate(); - - QMap* headers; - - QIODevice* device; - QFile* file; - - char buffer1[ZIP_READ_BUFFER]; - char buffer2[ZIP_READ_BUFFER]; - - unsigned char* uBuffer; - - const crc_t* crcTable; - - QString comment; - QString password; - - Zip::ErrorCode createArchive(QIODevice* device); - Zip::ErrorCode closeArchive(); - void reset(); - - bool zLibInit(); - - bool containsEntry(const QFileInfo& info) const; - - Zip::ErrorCode addDirectory(const QString& path, const QString& root, - Zip::CompressionOptions options, Zip::CompressionLevel level, - int hierarchyLevel, int* addedFiles = 0); - Zip::ErrorCode addFiles(const QStringList& paths, const QString& root, - Zip::CompressionOptions options, Zip::CompressionLevel level, - int* addedFiles); - - Zip::ErrorCode createEntry(const QFileInfo& file, const QString& root, - Zip::CompressionLevel level); - Zip::CompressionLevel detectCompressionByMime(const QString& ext); - - inline quint32 updateChecksum(const quint32& crc, const quint32& val) const; - - inline void encryptBytes(quint32* keys, char* buffer, qint64 read); - - inline void setULong(quint32 v, char* buffer, unsigned int offset); - inline void updateKeys(quint32* keys, int c) const; - inline void initKeys(quint32* keys) const; - inline int decryptByte(quint32 key2) const; - - inline QString extractRoot(const QString& p, Zip::CompressionOptions o); - -private slots: - void deviceDestroyed(QObject*); - -private: - int compressionStrategy(const QString& path, QIODevice& file) const; - Zip::ErrorCode deflateFile(const QFileInfo& fileInfo, - quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys); - Zip::ErrorCode storeFile(const QString& path, QIODevice& file, - quint32& crc, qint64& written, quint32** keys); - Zip::ErrorCode compressFile(const QString& path, QIODevice& file, - quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys); - Zip::ErrorCode do_closeArchive(); - Zip::ErrorCode writeEntry(const QString& fileName, const ZipEntryP* h, quint32& szCentralDir); - Zip::ErrorCode writeCentralDir(quint32 offCentralDir, quint32 szCentralDir); -}; - -OSDAB_END_NAMESPACE - -#endif // OSDAB_ZIP_P__H +/**************************************************************************** +** Filename: zip_p.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Zip/UnZip API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef OSDAB_ZIP_P__H +#define OSDAB_ZIP_P__H + +#include "zip.h" +#include "zipentry_p.h" + +#include +#include +#include + +#include + +/*! + zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate()) + we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;) +*/ +#define ZIP_READ_BUFFER (256*1024) + +OSDAB_BEGIN_NAMESPACE(Zip) + +class ZipPrivate : public QObject +{ + Q_OBJECT + +public: + // uLongf from zconf.h + typedef uLongf crc_t; + + ZipPrivate(); + virtual ~ZipPrivate(); + + QMap* headers; + + QIODevice* device; + QFile* file; + + char buffer1[ZIP_READ_BUFFER]; + char buffer2[ZIP_READ_BUFFER]; + + unsigned char* uBuffer; + + const crc_t* crcTable; + + QString comment; + QString password; + + Zip::ErrorCode createArchive(QIODevice* device); + Zip::ErrorCode closeArchive(); + void reset(); + + bool zLibInit(); + + bool containsEntry(const QFileInfo& info) const; + + Zip::ErrorCode addDirectory(const QString& path, const QString& root, + Zip::CompressionOptions options, Zip::CompressionLevel level, + int hierarchyLevel, int* addedFiles = 0); + Zip::ErrorCode addFiles(const QStringList& paths, const QString& root, + Zip::CompressionOptions options, Zip::CompressionLevel level, + int* addedFiles); + + Zip::ErrorCode createEntry(const QFileInfo& file, const QString& root, + Zip::CompressionLevel level); + Zip::CompressionLevel detectCompressionByMime(const QString& ext); + + inline quint32 updateChecksum(const quint32& crc, const quint32& val) const; + + inline void encryptBytes(quint32* keys, char* buffer, qint64 read); + + inline void setULong(quint32 v, char* buffer, unsigned int offset); + inline void updateKeys(quint32* keys, int c) const; + inline void initKeys(quint32* keys) const; + inline int decryptByte(quint32 key2) const; + + inline QString extractRoot(const QString& p, Zip::CompressionOptions o); + +private slots: + void deviceDestroyed(QObject*); + +private: + int compressionStrategy(const QString& path, QIODevice& file) const; + Zip::ErrorCode deflateFile(const QFileInfo& fileInfo, + quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys); + Zip::ErrorCode storeFile(const QString& path, QIODevice& file, + quint32& crc, qint64& written, quint32** keys); + Zip::ErrorCode compressFile(const QString& path, QIODevice& file, + quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys); + Zip::ErrorCode do_closeArchive(); + Zip::ErrorCode writeEntry(const QString& fileName, const ZipEntryP* h, quint32& szCentralDir); + Zip::ErrorCode writeCentralDir(quint32 offCentralDir, quint32 szCentralDir); +}; + +OSDAB_END_NAMESPACE + +#endif // OSDAB_ZIP_P__H diff --git a/oracle/src/zip/zipentry_p.h b/oracle/src/zip/zipentry_p.h index 5c355f59..f0675b6b 100755 --- a/oracle/src/zip/zipentry_p.h +++ b/oracle/src/zip/zipentry_p.h @@ -1,91 +1,91 @@ -/**************************************************************************** -** Filename: ZipEntryP.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** Wrapper for a ZIP local header. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Zip/UnZip API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef OSDAB_ZIPENTRY_P__H -#define OSDAB_ZIPENTRY_P__H - -#include -#include - -OSDAB_BEGIN_NAMESPACE(Zip) - -class ZipEntryP -{ -public: - ZipEntryP() : - lhOffset(0), - dataOffset(0), - gpFlag(), - compMethod(0), - modTime(), - modDate(), - crc(0), - szComp(0), - szUncomp(0), - absolutePath(), - fileSize(0), - lhEntryChecked(false) - { - gpFlag[0] = gpFlag[1] = 0; - modTime[0] = modTime[1] = 0; - modDate[0] = modDate[1] = 0; - } - - quint32 lhOffset; // Offset of the local header record for this entry - mutable quint32 dataOffset; // Offset of the file data for this entry - unsigned char gpFlag[2]; // General purpose flag - quint16 compMethod; // Compression method - unsigned char modTime[2]; // Last modified time - unsigned char modDate[2]; // Last modified date - quint32 crc; // CRC32 - quint32 szComp; // Compressed file size - quint32 szUncomp; // Uncompressed file size - QString comment; // File comment - - QString absolutePath; // Internal use - qint64 fileSize; // Internal use - - mutable bool lhEntryChecked; // Is true if the local header record for this entry has been parsed - - inline bool isEncrypted() const { return gpFlag[0] & 0x01; } - inline bool hasDataDescriptor() const { return gpFlag[0] & 0x08; } -}; - -OSDAB_END_NAMESPACE - -#endif // OSDAB_ZIPENTRY_P__H +/**************************************************************************** +** Filename: ZipEntryP.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** Wrapper for a ZIP local header. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Zip/UnZip API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef OSDAB_ZIPENTRY_P__H +#define OSDAB_ZIPENTRY_P__H + +#include +#include + +OSDAB_BEGIN_NAMESPACE(Zip) + +class ZipEntryP +{ +public: + ZipEntryP() : + lhOffset(0), + dataOffset(0), + gpFlag(), + compMethod(0), + modTime(), + modDate(), + crc(0), + szComp(0), + szUncomp(0), + absolutePath(), + fileSize(0), + lhEntryChecked(false) + { + gpFlag[0] = gpFlag[1] = 0; + modTime[0] = modTime[1] = 0; + modDate[0] = modDate[1] = 0; + } + + quint32 lhOffset; // Offset of the local header record for this entry + mutable quint32 dataOffset; // Offset of the file data for this entry + unsigned char gpFlag[2]; // General purpose flag + quint16 compMethod; // Compression method + unsigned char modTime[2]; // Last modified time + unsigned char modDate[2]; // Last modified date + quint32 crc; // CRC32 + quint32 szComp; // Compressed file size + quint32 szUncomp; // Uncompressed file size + QString comment; // File comment + + QString absolutePath; // Internal use + qint64 fileSize; // Internal use + + mutable bool lhEntryChecked; // Is true if the local header record for this entry has been parsed + + inline bool isEncrypted() const { return gpFlag[0] & 0x01; } + inline bool hasDataDescriptor() const { return gpFlag[0] & 0x08; } +}; + +OSDAB_END_NAMESPACE + +#endif // OSDAB_ZIPENTRY_P__H diff --git a/oracle/src/zip/zipglobal.cpp b/oracle/src/zip/zipglobal.cpp index 5abf6a72..aed1ee0e 100755 --- a/oracle/src/zip/zipglobal.cpp +++ b/oracle/src/zip/zipglobal.cpp @@ -1,152 +1,152 @@ -/**************************************************************************** -** Filename: zipglobal.cpp -** Last updated [dd/mm/yyyy]: 06/02/2011 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#include "zipglobal.h" - -#if defined(Q_OS_WIN) || defined(Q_OS_WINCE) || defined(Q_OS_LINUX) || defined (Q_OS_MACX) -#define OSDAB_ZIP_HAS_UTC -#include -#else -#undef OSDAB_ZIP_HAS_UTC -#endif - -#if defined(Q_OS_WIN) -#include -#elif defined(Q_OS_LINUX) || defined(Q_OS_MACX) -#include -#endif - -OSDAB_BEGIN_NAMESPACE(Zip) - -/*! Returns the current UTC offset in seconds unless OSDAB_ZIP_NO_UTC is defined - and method is implemented for the current platform and 0 otherwise. -*/ -int OSDAB_ZIP_MANGLE(currentUtcOffset)() -{ -#if !(!defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC) - return 0; -#else - time_t curr_time_t; - time(&curr_time_t); - -#if defined Q_OS_WIN - struct tm _tm_struct; - struct tm* tm_struct = &_tm_struct; -#else - struct tm* tm_struct = 0; -#endif - -#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) - // use the reentrant version of localtime() where available - tzset(); - tm res; - tm_struct = gmtime_r(&curr_time_t, &res); -#elif defined Q_OS_WIN && !defined Q_CC_MINGW - if (gmtime_s(tm_struct, &curr_time_t)) - return 0; -#else - tm_struct = gmtime(&curr_time_t); -#endif - - if (!tm_struct) - return 0; - - const time_t global_time_t = mktime(tm_struct); - -#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) - // use the reentrant version of localtime() where available - tm_struct = localtime_r(&curr_time_t, &res); -#elif defined Q_OS_WIN && !defined Q_CC_MINGW - if (localtime_s(tm_struct, &curr_time_t)) - return 0; -#else - tm_struct = localtime(&curr_time_t); -#endif - - if (!tm_struct) - return 0; - - const time_t local_time_t = mktime(tm_struct); - - const int utcOffset = - qRound(difftime(global_time_t, local_time_t)); - return tm_struct->tm_isdst > 0 ? utcOffset + 3600 : utcOffset; -#endif // No UTC - - return 0; -} - -QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime) -{ -#if !defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC - const int utc = OSDAB_ZIP_MANGLE(currentUtcOffset)(); - return dateTime.toUTC().addSecs(utc); -#else - return dateTime; -#endif // OSDAB_ZIP_NO_UTC -} - -bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime& dateTime) -{ - if (fileName.isEmpty()) - return true; - -#ifdef Q_OS_WIN - HANDLE hFile = CreateFileW(fileName.toStdWString().c_str(), - GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); - if (hFile == INVALID_HANDLE_VALUE) { - return false; - } - - SYSTEMTIME st; - FILETIME ft, ftLastMod; - const QDate date = dateTime.date(); - const QTime time = dateTime.time(); - st.wYear = date.year(); - st.wMonth = date.month(); - st.wDay = date.day(); - st.wHour = time.hour(); - st.wMinute = time.minute(); - st.wSecond = time.second(); - st.wMilliseconds = time.msec(); - - SystemTimeToFileTime(&st, &ft); - LocalFileTimeToFileTime(&ft, &ftLastMod); - - const bool success = SetFileTime(hFile, NULL, NULL, &ftLastMod); - CloseHandle(hFile); - return success; - -#elif defined(Q_OS_LINUX) || defined(Q_OS_MACX) - - struct utimbuf t_buffer; - t_buffer.actime = t_buffer.modtime = dateTime.toTime_t(); - return utime(fileName.toLocal8Bit().constData(), &t_buffer) == 0; -#endif - - return true; -} -OSDAB_END_NAMESPACE +/**************************************************************************** +** Filename: zipglobal.cpp +** Last updated [dd/mm/yyyy]: 06/02/2011 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#include "zipglobal.h" + +#if defined(Q_OS_WIN) || defined(Q_OS_WINCE) || defined(Q_OS_LINUX) || defined (Q_OS_MACX) +#define OSDAB_ZIP_HAS_UTC +#include +#else +#undef OSDAB_ZIP_HAS_UTC +#endif + +#if defined(Q_OS_WIN) +#include +#elif defined(Q_OS_LINUX) || defined(Q_OS_MACX) +#include +#endif + +OSDAB_BEGIN_NAMESPACE(Zip) + +/*! Returns the current UTC offset in seconds unless OSDAB_ZIP_NO_UTC is defined + and method is implemented for the current platform and 0 otherwise. +*/ +int OSDAB_ZIP_MANGLE(currentUtcOffset)() +{ +#if !(!defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC) + return 0; +#else + time_t curr_time_t; + time(&curr_time_t); + +#if defined Q_OS_WIN + struct tm _tm_struct; + struct tm* tm_struct = &_tm_struct; +#else + struct tm* tm_struct = 0; +#endif + +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of localtime() where available + tzset(); + tm res; + tm_struct = gmtime_r(&curr_time_t, &res); +#elif defined Q_OS_WIN && !defined Q_CC_MINGW + if (gmtime_s(tm_struct, &curr_time_t)) + return 0; +#else + tm_struct = gmtime(&curr_time_t); +#endif + + if (!tm_struct) + return 0; + + const time_t global_time_t = mktime(tm_struct); + +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of localtime() where available + tm_struct = localtime_r(&curr_time_t, &res); +#elif defined Q_OS_WIN && !defined Q_CC_MINGW + if (localtime_s(tm_struct, &curr_time_t)) + return 0; +#else + tm_struct = localtime(&curr_time_t); +#endif + + if (!tm_struct) + return 0; + + const time_t local_time_t = mktime(tm_struct); + + const int utcOffset = - qRound(difftime(global_time_t, local_time_t)); + return tm_struct->tm_isdst > 0 ? utcOffset + 3600 : utcOffset; +#endif // No UTC + + return 0; +} + +QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime) +{ +#if !defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC + const int utc = OSDAB_ZIP_MANGLE(currentUtcOffset)(); + return dateTime.toUTC().addSecs(utc); +#else + return dateTime; +#endif // OSDAB_ZIP_NO_UTC +} + +bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime& dateTime) +{ + if (fileName.isEmpty()) + return true; + +#ifdef Q_OS_WIN + HANDLE hFile = CreateFileW(fileName.toStdWString().c_str(), + GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if (hFile == INVALID_HANDLE_VALUE) { + return false; + } + + SYSTEMTIME st; + FILETIME ft, ftLastMod; + const QDate date = dateTime.date(); + const QTime time = dateTime.time(); + st.wYear = date.year(); + st.wMonth = date.month(); + st.wDay = date.day(); + st.wHour = time.hour(); + st.wMinute = time.minute(); + st.wSecond = time.second(); + st.wMilliseconds = time.msec(); + + SystemTimeToFileTime(&st, &ft); + LocalFileTimeToFileTime(&ft, &ftLastMod); + + const bool success = SetFileTime(hFile, NULL, NULL, &ftLastMod); + CloseHandle(hFile); + return success; + +#elif defined(Q_OS_LINUX) || defined(Q_OS_MACX) + + struct utimbuf t_buffer; + t_buffer.actime = t_buffer.modtime = dateTime.toTime_t(); + return utime(fileName.toLocal8Bit().constData(), &t_buffer) == 0; +#endif + + return true; +} +OSDAB_END_NAMESPACE diff --git a/oracle/src/zip/zipglobal.h b/oracle/src/zip/zipglobal.h index 0b4c94f6..e7ff3310 100755 --- a/oracle/src/zip/zipglobal.h +++ b/oracle/src/zip/zipglobal.h @@ -1,77 +1,77 @@ -/**************************************************************************** -** Filename: zipglobal.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#ifndef OSDAB_ZIPGLOBAL__H -#define OSDAB_ZIPGLOBAL__H - -#include -#include - -/* If you want to build the OSDaB Zip code as - a library, define OSDAB_ZIP_LIB in the library's .pro file and - in the libraries using it OR remove the #ifndef OSDAB_ZIP_LIB - define below and leave the #else body. Also remember to define - OSDAB_ZIP_BUILD_LIB in the library's project). -*/ - -#ifndef OSDAB_ZIP_LIB -# define OSDAB_ZIP_EXPORT -#else -# if defined(OSDAB_ZIP_BUILD_LIB) -# define OSDAB_ZIP_EXPORT Q_DECL_EXPORT -# else -# define OSDAB_ZIP_EXPORT Q_DECL_IMPORT -# endif -#endif - -#ifdef OSDAB_NAMESPACE -#define OSDAB_BEGIN_NAMESPACE(ModuleName) namespace Osdab { namespace ModuleName { -#else -#define OSDAB_BEGIN_NAMESPACE(ModuleName) -#endif - -#ifdef OSDAB_NAMESPACE -#define OSDAB_END_NAMESPACE } } -#else -#define OSDAB_END_NAMESPACE -#endif - -#ifndef OSDAB_NAMESPACE -#define OSDAB_ZIP_MANGLE(x) zip_##x -#else -#define OSDAB_ZIP_MANGLE(x) x -#endif - -OSDAB_BEGIN_NAMESPACE(Zip) - -OSDAB_ZIP_EXPORT int OSDAB_ZIP_MANGLE(currentUtcOffset)(); -OSDAB_ZIP_EXPORT QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime); -OSDAB_ZIP_EXPORT bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime& dateTime); - -OSDAB_END_NAMESPACE - -#endif // OSDAB_ZIPGLOBAL__H +/**************************************************************************** +** Filename: zipglobal.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#ifndef OSDAB_ZIPGLOBAL__H +#define OSDAB_ZIPGLOBAL__H + +#include +#include + +/* If you want to build the OSDaB Zip code as + a library, define OSDAB_ZIP_LIB in the library's .pro file and + in the libraries using it OR remove the #ifndef OSDAB_ZIP_LIB + define below and leave the #else body. Also remember to define + OSDAB_ZIP_BUILD_LIB in the library's project). +*/ + +#ifndef OSDAB_ZIP_LIB +# define OSDAB_ZIP_EXPORT +#else +# if defined(OSDAB_ZIP_BUILD_LIB) +# define OSDAB_ZIP_EXPORT Q_DECL_EXPORT +# else +# define OSDAB_ZIP_EXPORT Q_DECL_IMPORT +# endif +#endif + +#ifdef OSDAB_NAMESPACE +#define OSDAB_BEGIN_NAMESPACE(ModuleName) namespace Osdab { namespace ModuleName { +#else +#define OSDAB_BEGIN_NAMESPACE(ModuleName) +#endif + +#ifdef OSDAB_NAMESPACE +#define OSDAB_END_NAMESPACE } } +#else +#define OSDAB_END_NAMESPACE +#endif + +#ifndef OSDAB_NAMESPACE +#define OSDAB_ZIP_MANGLE(x) zip_##x +#else +#define OSDAB_ZIP_MANGLE(x) x +#endif + +OSDAB_BEGIN_NAMESPACE(Zip) + +OSDAB_ZIP_EXPORT int OSDAB_ZIP_MANGLE(currentUtcOffset)(); +OSDAB_ZIP_EXPORT QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime); +OSDAB_ZIP_EXPORT bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime& dateTime); + +OSDAB_END_NAMESPACE + +#endif // OSDAB_ZIPGLOBAL__H