489 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			489 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
Copyright (C) 2005-2011 Sergey A. Tachenov
 | 
						|
 | 
						|
This program is free software; you can redistribute it and/or modify it
 | 
						|
under the terms of the GNU Lesser General Public License as published by
 | 
						|
the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
your option) any later version.
 | 
						|
 | 
						|
This program is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU Lesser General Public License
 | 
						|
along with this program; if not, write to the Free Software Foundation,
 | 
						|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | 
						|
 | 
						|
See COPYING file for the full LGPL text.
 | 
						|
 | 
						|
Original ZIP package is copyrighted by Gilles Vollant, see
 | 
						|
quazip/(un)zip.h files for details, basically it's zlib license.
 | 
						|
 **/
 | 
						|
 | 
						|
#include "quazipfile.h"
 | 
						|
 | 
						|
using namespace std;
 | 
						|
 | 
						|
/// The implementation class for QuaZip.
 | 
						|
/**
 | 
						|
\internal
 | 
						|
 | 
						|
This class contains all the private stuff for the QuaZipFile class, thus
 | 
						|
allowing to preserve binary compatibility between releases, the
 | 
						|
technique known as the Pimpl (private implementation) idiom.
 | 
						|
*/
 | 
						|
class QuaZipFilePrivate {
 | 
						|
  friend class QuaZipFile;
 | 
						|
  private:
 | 
						|
    /// The pointer to the associated QuaZipFile instance.
 | 
						|
    QuaZipFile *q;
 | 
						|
    /// The QuaZip object to work with.
 | 
						|
    QuaZip *zip;
 | 
						|
    /// The file name.
 | 
						|
    QString fileName;
 | 
						|
    /// Case sensitivity mode.
 | 
						|
    QuaZip::CaseSensitivity caseSensitivity;
 | 
						|
    /// Whether this file is opened in the raw mode.
 | 
						|
    bool raw;
 | 
						|
    /// Write position to keep track of.
 | 
						|
    /**
 | 
						|
      QIODevice::pos() is broken for non-seekable devices, so we need
 | 
						|
      our own position.
 | 
						|
      */
 | 
						|
    qint64 writePos;
 | 
						|
    /// Uncompressed size to write along with a raw file.
 | 
						|
    ulong uncompressedSize;
 | 
						|
    /// CRC to write along with a raw file.
 | 
						|
    quint32 crc;
 | 
						|
    /// Whether \ref zip points to an internal QuaZip instance.
 | 
						|
    /**
 | 
						|
      This is true if the archive was opened by name, rather than by
 | 
						|
      supplying an existing QuaZip instance.
 | 
						|
      */
 | 
						|
    bool internal;
 | 
						|
    /// The last error.
 | 
						|
    int zipError;
 | 
						|
    /// Resets \ref zipError.
 | 
						|
    inline void resetZipError() const {setZipError(UNZ_OK);}
 | 
						|
    /// Sets the zip error.
 | 
						|
    /**
 | 
						|
      This function is marked as const although it changes one field.
 | 
						|
      This allows to call it from const functions that don't change
 | 
						|
      anything by themselves.
 | 
						|
      */
 | 
						|
    void setZipError(int zipError) const;
 | 
						|
    /// The constructor for the corresponding QuaZipFile constructor.
 | 
						|
    inline QuaZipFilePrivate(QuaZipFile *q):
 | 
						|
      q(q), zip(NULL), internal(true), zipError(UNZ_OK) {}
 | 
						|
    /// The constructor for the corresponding QuaZipFile constructor.
 | 
						|
    inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName):
 | 
						|
      q(q), internal(true), zipError(UNZ_OK)
 | 
						|
      {
 | 
						|
        zip=new QuaZip(zipName);
 | 
						|
      }
 | 
						|
    /// The constructor for the corresponding QuaZipFile constructor.
 | 
						|
    inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName,
 | 
						|
        QuaZip::CaseSensitivity cs):
 | 
						|
      q(q), internal(true), zipError(UNZ_OK)
 | 
						|
      {
 | 
						|
        zip=new QuaZip(zipName);
 | 
						|
        this->fileName=fileName;
 | 
						|
        if (this->fileName.startsWith('/'))
 | 
						|
            this->fileName = this->fileName.mid(1);
 | 
						|
        this->caseSensitivity=cs;
 | 
						|
      }
 | 
						|
    /// The constructor for the QuaZipFile constructor accepting a file name.
 | 
						|
    inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip):
 | 
						|
      q(q), zip(zip), internal(false), zipError(UNZ_OK) {}
 | 
						|
    /// The destructor.
 | 
						|
    inline ~QuaZipFilePrivate()
 | 
						|
    {
 | 
						|
      if (internal)
 | 
						|
        delete zip;
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
QuaZipFile::QuaZipFile():
 | 
						|
  p(new QuaZipFilePrivate(this))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
QuaZipFile::QuaZipFile(QObject *parent):
 | 
						|
  QIODevice(parent),
 | 
						|
  p(new QuaZipFilePrivate(this))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent):
 | 
						|
  QIODevice(parent),
 | 
						|
  p(new QuaZipFilePrivate(this, zipName))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName,
 | 
						|
    QuaZip::CaseSensitivity cs, QObject *parent):
 | 
						|
  QIODevice(parent),
 | 
						|
  p(new QuaZipFilePrivate(this, zipName, fileName, cs))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent):
 | 
						|
  QIODevice(parent),
 | 
						|
  p(new QuaZipFilePrivate(this, zip))
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
QuaZipFile::~QuaZipFile()
 | 
						|
{
 | 
						|
  if (isOpen())
 | 
						|
    close();
 | 
						|
  delete p;
 | 
						|
}
 | 
						|
 | 
						|
QString QuaZipFile::getZipName() const
 | 
						|
{
 | 
						|
  return p->zip==NULL ? QString() : p->zip->getZipName();
 | 
						|
}
 | 
						|
 | 
						|
QuaZip *QuaZipFile::getZip() const
 | 
						|
{
 | 
						|
    return p->internal ? NULL : p->zip;
 | 
						|
}
 | 
						|
 | 
						|
QString QuaZipFile::getActualFileName()const
 | 
						|
{
 | 
						|
  p->setZipError(UNZ_OK);
 | 
						|
  if (p->zip == NULL || (openMode() & WriteOnly))
 | 
						|
    return QString();
 | 
						|
  QString name=p->zip->getCurrentFileName();
 | 
						|
  if(name.isNull())
 | 
						|
    p->setZipError(p->zip->getZipError());
 | 
						|
  return name;
 | 
						|
}
 | 
						|
 | 
						|
void QuaZipFile::setZipName(const QString& zipName)
 | 
						|
{
 | 
						|
  if(isOpen()) {
 | 
						|
    qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if(p->zip!=NULL && p->internal)
 | 
						|
    delete p->zip;
 | 
						|
  p->zip=new QuaZip(zipName);
 | 
						|
  p->internal=true;
 | 
						|
}
 | 
						|
 | 
						|
void QuaZipFile::setZip(QuaZip *zip)
 | 
						|
{
 | 
						|
  if(isOpen()) {
 | 
						|
    qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if(p->zip!=NULL && p->internal)
 | 
						|
    delete p->zip;
 | 
						|
  p->zip=zip;
 | 
						|
  p->fileName=QString();
 | 
						|
  p->internal=false;
 | 
						|
}
 | 
						|
 | 
						|
void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs)
 | 
						|
{
 | 
						|
  if(p->zip==NULL) {
 | 
						|
    qWarning("QuaZipFile::setFileName(): call setZipName() first");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if(!p->internal) {
 | 
						|
    qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if(isOpen()) {
 | 
						|
    qWarning("QuaZipFile::setFileName(): can not set file name for already opened file");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  p->fileName=fileName;
 | 
						|
  if (p->fileName.startsWith('/'))
 | 
						|
      p->fileName = p->fileName.mid(1);
 | 
						|
  p->caseSensitivity=cs;
 | 
						|
}
 | 
						|
 | 
						|
void QuaZipFilePrivate::setZipError(int zipError) const
 | 
						|
{
 | 
						|
  QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const
 | 
						|
  fakeThis->zipError=zipError;
 | 
						|
  if(zipError==UNZ_OK)
 | 
						|
    q->setErrorString(QString());
 | 
						|
  else
 | 
						|
    q->setErrorString(q->tr("ZIP/UNZIP API error %1").arg(zipError));
 | 
						|
}
 | 
						|
 | 
						|
bool QuaZipFile::open(OpenMode mode)
 | 
						|
{
 | 
						|
  return open(mode, NULL);
 | 
						|
}
 | 
						|
 | 
						|
bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password)
 | 
						|
{
 | 
						|
  p->resetZipError();
 | 
						|
  if(isOpen()) {
 | 
						|
    qWarning("QuaZipFile::open(): already opened");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if(mode&Unbuffered) {
 | 
						|
    qWarning("QuaZipFile::open(): Unbuffered mode is not supported");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if((mode&ReadOnly)&&!(mode&WriteOnly)) {
 | 
						|
    if(p->internal) {
 | 
						|
      if(!p->zip->open(QuaZip::mdUnzip)) {
 | 
						|
        p->setZipError(p->zip->getZipError());
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) {
 | 
						|
        p->setZipError(p->zip->getZipError());
 | 
						|
        p->zip->close();
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      if(p->zip==NULL) {
 | 
						|
        qWarning("QuaZipFile::open(): zip is NULL");
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      if(p->zip->getMode()!=QuaZip::mdUnzip) {
 | 
						|
        qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
 | 
						|
            (int)mode, (int)p->zip->getMode());
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      if(!p->zip->hasCurrentFile()) {
 | 
						|
        qWarning("QuaZipFile::open(): zip does not have current file");
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password));
 | 
						|
    if(p->zipError==UNZ_OK) {
 | 
						|
      setOpenMode(mode);
 | 
						|
      p->raw=raw;
 | 
						|
      return true;
 | 
						|
    } else
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info,
 | 
						|
    const char *password, quint32 crc,
 | 
						|
    int method, int level, bool raw,
 | 
						|
    int windowBits, int memLevel, int strategy)
 | 
						|
{
 | 
						|
  zip_fileinfo info_z;
 | 
						|
  p->resetZipError();
 | 
						|
  if(isOpen()) {
 | 
						|
    qWarning("QuaZipFile::open(): already opened");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if((mode&WriteOnly)&&!(mode&ReadOnly)) {
 | 
						|
    if(p->internal) {
 | 
						|
      qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    if(p->zip==NULL) {
 | 
						|
      qWarning("QuaZipFile::open(): zip is NULL");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) {
 | 
						|
      qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
 | 
						|
          (int)mode, (int)p->zip->getMode());
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    info_z.tmz_date.tm_year=info.dateTime.date().year();
 | 
						|
    info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1;
 | 
						|
    info_z.tmz_date.tm_mday=info.dateTime.date().day();
 | 
						|
    info_z.tmz_date.tm_hour=info.dateTime.time().hour();
 | 
						|
    info_z.tmz_date.tm_min=info.dateTime.time().minute();
 | 
						|
    info_z.tmz_date.tm_sec=info.dateTime.time().second();
 | 
						|
    info_z.dosDate = 0;
 | 
						|
    info_z.internal_fa=(uLong)info.internalAttr;
 | 
						|
    info_z.external_fa=(uLong)info.externalAttr;
 | 
						|
    if (!p->zip->isDataDescriptorWritingEnabled())
 | 
						|
        zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR);
 | 
						|
    p->setZipError(zipOpenNewFileInZip3(p->zip->getZipFile(),
 | 
						|
          p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z,
 | 
						|
          info.extraLocal.constData(), info.extraLocal.length(),
 | 
						|
          info.extraGlobal.constData(), info.extraGlobal.length(),
 | 
						|
          p->zip->getCommentCodec()->fromUnicode(info.comment).constData(),
 | 
						|
          method, level, (int)raw,
 | 
						|
          windowBits, memLevel, strategy,
 | 
						|
          password, (uLong)crc));
 | 
						|
    if(p->zipError==UNZ_OK) {
 | 
						|
      p->writePos=0;
 | 
						|
      setOpenMode(mode);
 | 
						|
      p->raw=raw;
 | 
						|
      if(raw) {
 | 
						|
        p->crc=crc;
 | 
						|
        p->uncompressedSize=info.uncompressedSize;
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    } else
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool QuaZipFile::isSequential()const
 | 
						|
{
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
qint64 QuaZipFile::pos()const
 | 
						|
{
 | 
						|
  if(p->zip==NULL) {
 | 
						|
    qWarning("QuaZipFile::pos(): call setZipName() or setZip() first");
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  if(!isOpen()) {
 | 
						|
    qWarning("QuaZipFile::pos(): file is not open");
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  if(openMode()&ReadOnly)
 | 
						|
      // QIODevice::pos() is broken for sequential devices,
 | 
						|
      // but thankfully bytesAvailable() returns the number of
 | 
						|
      // bytes buffered, so we know how far ahead we are.
 | 
						|
    return unztell(p->zip->getUnzFile()) - QIODevice::bytesAvailable();
 | 
						|
  else
 | 
						|
    return p->writePos;
 | 
						|
}
 | 
						|
 | 
						|
bool QuaZipFile::atEnd()const
 | 
						|
{
 | 
						|
  if(p->zip==NULL) {
 | 
						|
    qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if(!isOpen()) {
 | 
						|
    qWarning("QuaZipFile::atEnd(): file is not open");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if(openMode()&ReadOnly)
 | 
						|
      // the same problem as with pos()
 | 
						|
    return QIODevice::bytesAvailable() == 0
 | 
						|
        && unzeof(p->zip->getUnzFile())==1;
 | 
						|
  else
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
qint64 QuaZipFile::size()const
 | 
						|
{
 | 
						|
  if(!isOpen()) {
 | 
						|
    qWarning("QuaZipFile::atEnd(): file is not open");
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  if(openMode()&ReadOnly)
 | 
						|
    return p->raw?csize():usize();
 | 
						|
  else
 | 
						|
    return p->writePos;
 | 
						|
}
 | 
						|
 | 
						|
qint64 QuaZipFile::csize()const
 | 
						|
{
 | 
						|
  unz_file_info info_z;
 | 
						|
  p->setZipError(UNZ_OK);
 | 
						|
  if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
 | 
						|
  p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
 | 
						|
  if(p->zipError!=UNZ_OK)
 | 
						|
    return -1;
 | 
						|
  return info_z.compressed_size;
 | 
						|
}
 | 
						|
 | 
						|
qint64 QuaZipFile::usize()const
 | 
						|
{
 | 
						|
  unz_file_info info_z;
 | 
						|
  p->setZipError(UNZ_OK);
 | 
						|
  if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
 | 
						|
  p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
 | 
						|
  if(p->zipError!=UNZ_OK)
 | 
						|
    return -1;
 | 
						|
  return info_z.uncompressed_size;
 | 
						|
}
 | 
						|
 | 
						|
bool QuaZipFile::getFileInfo(QuaZipFileInfo *info)
 | 
						|
{
 | 
						|
  if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false;
 | 
						|
  p->zip->getCurrentFileInfo(info);
 | 
						|
  p->setZipError(p->zip->getZipError());
 | 
						|
  return p->zipError==UNZ_OK;
 | 
						|
}
 | 
						|
 | 
						|
void QuaZipFile::close()
 | 
						|
{
 | 
						|
  p->resetZipError();
 | 
						|
  if(p->zip==NULL||!p->zip->isOpen()) return;
 | 
						|
  if(!isOpen()) {
 | 
						|
    qWarning("QuaZipFile::close(): file isn't open");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if(openMode()&ReadOnly)
 | 
						|
    p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile()));
 | 
						|
  else if(openMode()&WriteOnly)
 | 
						|
    if(isRaw()) p->setZipError(zipCloseFileInZipRaw(p->zip->getZipFile(), p->uncompressedSize, p->crc));
 | 
						|
    else p->setZipError(zipCloseFileInZip(p->zip->getZipFile()));
 | 
						|
  else {
 | 
						|
    qWarning("Wrong open mode: %d", (int)openMode());
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen);
 | 
						|
  else return;
 | 
						|
  if(p->internal) {
 | 
						|
    p->zip->close();
 | 
						|
    p->setZipError(p->zip->getZipError());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
qint64 QuaZipFile::readData(char *data, qint64 maxSize)
 | 
						|
{
 | 
						|
  p->setZipError(UNZ_OK);
 | 
						|
  qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize);
 | 
						|
  if (bytesRead < 0) {
 | 
						|
    p->setZipError((int) bytesRead);
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  return bytesRead;
 | 
						|
}
 | 
						|
 | 
						|
qint64 QuaZipFile::writeData(const char* data, qint64 maxSize)
 | 
						|
{
 | 
						|
  p->setZipError(ZIP_OK);
 | 
						|
  p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize));
 | 
						|
  if(p->zipError!=ZIP_OK) return -1;
 | 
						|
  else {
 | 
						|
    p->writePos+=maxSize;
 | 
						|
    return maxSize;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
QString QuaZipFile::getFileName() const
 | 
						|
{
 | 
						|
  return p->fileName;
 | 
						|
}
 | 
						|
 | 
						|
QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const
 | 
						|
{
 | 
						|
  return p->caseSensitivity;
 | 
						|
}
 | 
						|
 | 
						|
bool QuaZipFile::isRaw() const
 | 
						|
{
 | 
						|
  return p->raw;
 | 
						|
}
 | 
						|
 | 
						|
int QuaZipFile::getZipError() const
 | 
						|
{
 | 
						|
  return p->zipError;
 | 
						|
}
 | 
						|
 | 
						|
qint64 QuaZipFile::bytesAvailable() const
 | 
						|
{
 | 
						|
    return size() - pos();
 | 
						|
}
 |