diff options
Diffstat (limited to 'installerbuilder/common/kd7zengine.cpp')
-rw-r--r-- | installerbuilder/common/kd7zengine.cpp | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/installerbuilder/common/kd7zengine.cpp b/installerbuilder/common/kd7zengine.cpp new file mode 100644 index 000000000..71298b326 --- /dev/null +++ b/installerbuilder/common/kd7zengine.cpp @@ -0,0 +1,679 @@ +/************************************************************************** +** +** This file is part of Qt SDK** +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).* +** +** Contact: Nokia Corporation qt-info@nokia.com** +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception version +** 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you are unsure which license is appropriate for your use, please contact +** (qt-info@nokia.com). +** +**************************************************************************/ +#include "kd7zengine.h" + +#include <QBuffer> +#include <QDateTime> +#include <QDebug> +#include <QDir> +#include <QMap> +#include <QSet> +#include <QTextCodec> + +#include "lib7z_facade.h" + +static QMap< QString, QFile* > archives; +static QMap< QString, QVector< Lib7z::File > > currentContents; +static QMap< QString, QHash< QString, const Lib7z::File* > > currentFiles; + +namespace +{ + +static bool matches( const QList< QRegExp >& regexps, QDir::Filters filters, const Lib7z::File& n ) +{ + const QString fileName = n.path.section( QChar::fromLatin1( '/' ), -1, -1, QString::SectionSkipEmpty ); + + static QLatin1Char sep( '/' ); + + // check for directories filter + if( ( filters & QDir::Dirs ) == 0 && n.isDirectory ) + return false; + + // check for files filter + if( ( filters & QDir::Files ) == 0 && !n.isDirectory ) + return false; + + // check for name filter + if( !regexps.isEmpty() ) + { + bool matched = false; + for( QList< QRegExp >::const_iterator it = regexps.begin(); it != regexps.end(); ++it ) + { + matched = it->exactMatch( fileName ); + if ( matched ) + break; + } + return matched; + } + return true; +} +} + +class KD7zEngine::Private +{ +public: + Private( KD7zEngine* q ) + : q( q ), + flags( 0 ), + mode( QIODevice::NotOpen ) + { + } + +private: + KD7zEngine* const q; + +public: + void setFileName( const QString& file ) + { + const QString oldZipFileName = zipFileName; + providedFileName = file; + + static const QLatin1Char sep( '/' ); + + static const QString prefix = QString::fromLatin1( "7z://" ); + Q_ASSERT( file.toLower().startsWith( prefix ) ); + zipFileName = file.mid( prefix.length() ); + zipFileName.replace( QLatin1Char( '\\' ), sep ); + + QDir dir( zipFileName ); + int i = -1; + while( !dir.exists() ) + dir = QDir( zipFileName.section( sep, 0, i-- ) ); + + zipFileName = zipFileName.section( sep, 0, i + 2 ); + fileName = file.mid( prefix.length() + zipFileName.length() ); + + while( fileName.endsWith( sep ) ) + fileName.chop( 1 ); + while( fileName.startsWith( sep ) ) + fileName.remove( 0, 1 ); + + flags = 0; + q->close(); + currentFile = Lib7z::File(); + } + + /** + * Taken from qfsfileengine.cpp + */ + static QString canonicalized(const QString &path) + { + if (path.isEmpty()) + return path; + + QFileInfo fi; + const QChar slash(QLatin1Char('/')); + QString tmpPath = path; + int separatorPos = 0; + QSet<QString> nonSymlinks; + QSet<QString> known; + + known.insert(path); + do { +#ifdef Q_OS_WIN + // UNC, skip past the first two elements + if (separatorPos == 0 && tmpPath.startsWith(QLatin1String("//"))) + separatorPos = tmpPath.indexOf(slash, 2); + if (separatorPos != -1) +#endif + separatorPos = tmpPath.indexOf(slash, separatorPos + 1); + QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos); + if (!nonSymlinks.contains(prefix)) { + fi.setFile(prefix); + if (fi.isSymLink()) { + QString target = fi.symLinkTarget(); + if (separatorPos != -1) { + if (fi.isDir() && !target.endsWith(slash)) + target.append(slash); + target.append(tmpPath.mid(separatorPos)); + } + tmpPath = QDir::cleanPath(target); + separatorPos = 0; + + if (known.contains(tmpPath)) + return QString(); + known.insert(tmpPath); + } else { + nonSymlinks.insert(prefix); + } + } + } while (separatorPos != -1); + + return QDir::cleanPath(tmpPath); + } + + QFile* archive() const + { + if( archives[ zipFileName ] == 0 ) + archives[ zipFileName ] = new QFile( zipFileName ); + return archives[ zipFileName ]; + } + + const Lib7z::File& file() const + { + if( currentFile.path.isEmpty() ) + currentFile = file( fileName ); + return currentFile; + } + + const QVector< Lib7z::File >& list() const + { + if( currentContents[ zipFileName ].isEmpty() ) + { + QFile* const archive = this->archive(); + try { + if( !archive->open( QIODevice::ReadOnly ) ) { + qWarning("Could not open %s for reading", qPrintable(zipFileName) ); + return currentContents[ zipFileName ]; + } + currentContents[ zipFileName ] = Lib7z::listArchive( archive ); + } catch ( const Lib7z::SevenZipException& e ) { + qWarning() << e.message(); + } + archive->close(); + } + return currentContents[ zipFileName ]; + } + + Lib7z::File file( const QString& path ) const + { + QString p = QDir::cleanPath( path ).toLower(); + const QLatin1Char sep( '/' ); + while( p.endsWith( sep ) ) + p.chop( 1 ); + while( p.startsWith( sep ) ) + p.remove( 0, 1 ); + + const Lib7z::File* const f = currentFiles[ zipFileName ][ p ]; + if( f != 0 ) + return *f; + + const QVector<Lib7z::File>& files = list(); + for( QVector< Lib7z::File >::const_iterator it = files.begin(); it != files.end(); ++it ) + { + QString n = it->path.toLower(); + while( n.endsWith( sep ) ) + n.chop( 1 ); + + currentFiles[ zipFileName ][ n ] = &(*it); + + if( p == n ) + return *it; + } + + return Lib7z::File(); + } + + QString providedFileName; + QString zipFileName; + QString fileName; + + mutable FileFlags flags; + + Lib7z::File openFile; + QIODevice::OpenMode mode; + QBuffer buffer; + + mutable Lib7z::File currentFile; +}; + +class KD7zEngineIterator : public QAbstractFileEngineIterator +{ +public: + KD7zEngineIterator( const QStringList& entries, QDir::Filters filters, const QStringList& nameFilters ) + : QAbstractFileEngineIterator( filters, nameFilters ), + list( entries ), + index( -1 ) + { + } + + ~KD7zEngineIterator() + { + } + + /** + * \reimp + */ + bool hasNext() const + { + return index < list.size() - 1; + } + + /** + * \reimp + */ + QString next() + { + if( !hasNext() ) + return QString(); + ++index; + return currentFilePath(); + } + + /** + * \reimp + */ + QString currentFileName() const + { + return index < 0 ? QString() : list[ index ]; + } + +private: + const QStringList list; + int index; +}; + +KD7zEngine::KD7zEngine( const QString& fileName ) + : d( new Private( this ) ) +{ + d->setFileName( fileName ); +} + +KD7zEngine::~KD7zEngine() +{ + //delete d; +} + +/** + * \reimp + */ +QStringList KD7zEngine::entryList( QDir::Filters filters, const QStringList& filterNames ) const +{ + QStringList result; + + const QVector<Lib7z::File>& files = d->list(); + + static QLatin1Char sep( '/' ); + + QString p = QDir::cleanPath( d->fileName ).toLower(); + if( !p.endsWith( sep ) ) + p += sep; + while( p.startsWith( sep ) ) + p.remove( 0, 1 ); + + const int slashes = p.count( sep ); + + QList< QRegExp > regexps; + for( QStringList::const_iterator it = filterNames.begin(); it != filterNames.end(); ++it ) + regexps.push_back( QRegExp( *it, Qt::CaseInsensitive, QRegExp::Wildcard ) ); + + for( QVector< Lib7z::File >::const_iterator it = files.begin(); it != files.end(); ++it ) + { + const QString n = it->path; + if( !n.toLower().startsWith( p ) || n == p || ( n.count( sep ) != slashes ) ) + continue; + + if( slashes != n.left( n.length() - 1 ).count( sep ) ) + continue; + + if( matches( regexps, filters, *it ) ) + result.push_back( n.section( sep, -1, -1, QString::SectionSkipEmpty ) ); + } + + return result; +} + +/** + * \reimp + */ +QAbstractFileEngine::Iterator* KD7zEngine::beginEntryList( QDir::Filters filters, const QStringList& filterNames ) +{ + return new KD7zEngineIterator( entryList( filters, filterNames ), filters, filterNames ); +} + +/** + * \reimp + */ +bool KD7zEngine::caseSensitive() const +{ + return false; +} + +/** + * \reimp + */ +void KD7zEngine::setFileName( const QString& file ) +{ + d->setFileName( file ); +} + +/** + * \reimp + */ +QString KD7zEngine::fileName( FileName file ) const +{ + static const QLatin1Char sep( '/' ); + switch( file ) + { + case DefaultName: + return d->providedFileName; + case BaseName: + return QString::fromLatin1( "7z://%1%2" ).arg( d->zipFileName ).arg( d->fileName ).section( QChar::fromLatin1( '/' ), -1 ); + case PathName: + return QString::fromLatin1( "7z://%1%2" ).arg( d->zipFileName ).arg( d->fileName ).section( QChar::fromLatin1( '/' ), 0, -2 ); + case AbsoluteName: + { + if( d->zipFileName.startsWith( sep ) ) + return QDir::cleanPath( d->providedFileName ); + return QDir::cleanPath( QString::fromLatin1( "%1/%2" ).arg( QDir::currentPath(), d->providedFileName ) ); + } + case AbsolutePathName: + return fileName( AbsoluteName ).section( sep, 0, -2 ); + case CanonicalName: + return Private::canonicalized( fileName( AbsoluteName ) ); + case CanonicalPathName: + return fileName( CanonicalName ).section( sep, 0, -2 ); + case BundleName: + case LinkName: + default: + return QString(); + } +} + +/** + * \reimp + */ +bool KD7zEngine::open( QIODevice::OpenMode mode ) +{ + //FIXME: should be unused cause we are using KDUpdater operations for extracting now + + if( d->mode != QIODevice::NotOpen ) + return false; + + const Lib7z::File file = d->file(); + if( file.isDirectory ) + return false; // can't open a directory, dude... + + if( mode & QIODevice::WriteOnly ) + return false; // no write support yet + + d->openFile = file; + d->mode = mode; + + d->buffer.setData( QByteArray() ); + d->buffer.open( QIODevice::WriteOnly ); + + QFile* f = d->archive(); + if( !f->open( QIODevice::ReadOnly ) ) + return false; + + bool error = true; + try { + Lib7z::extractArchive( f, d->openFile, &d->buffer ); + error = false; + d->buffer.close(); + d->buffer.open( mode ); + f->close(); + } catch( const Lib7z::SevenZipException& e ) { + qWarning() << e.message(); + d->buffer.close(); + f->close(); + } + + return !error; +} + +/** + * \reimp + */ +bool KD7zEngine::flush() +{ +#ifdef SEVENZ_PORT + return d->openFileWrite != 0; +#else + return false; +#endif +} + +/** + * \reimp + */ +bool KD7zEngine::close() +{ + if( d->mode == QIODevice::NotOpen ) + return false; + + flush(); + d->mode = QIODevice::NotOpen; + d->openFile = Lib7z::File(); + d->buffer.close(); + d->buffer.setData( QByteArray() ); + return true; +} + +/** + * \reimp + */ +bool KD7zEngine::atEnd() const +{ + return d->buffer.atEnd(); +} + +/** + * \reimp + */ +bool KD7zEngine::seek( qint64 offset ) +{ + return d->buffer.seek( offset ); +} + +/** + * \reimp + */ +qint64 KD7zEngine::pos() const +{ + return d->buffer.pos(); +} + +/** + * \reimp + */ +bool KD7zEngine::copy( const QString& newName ) +{ + //FIXME: should be unused cause we are using KDUpdater operations for extracting now + + QFile* f = d->archive(); + if( !f->open( QIODevice::ReadOnly ) ) + return false; + + QFile target( newName ); + if( !target.open( QIODevice::WriteOnly ) ) + { + f->close(); + return false; + } + + try + { + qDebug()<<"Extract from"<<d->zipFileName<<"to"<<newName; + Lib7z::extractArchive( f, d->file(), &target ); + f->close(); + return true; + } + catch( const Lib7z::SevenZipException& e ) + { + qWarning() << e.message(); + f->close(); + return false; + } +} + +/** + * \reimp + */ +qint64 KD7zEngine::read( char* data, qint64 maxlen ) +{ + return d->buffer.read( data, maxlen ); +} + +/** + * \reimp + */ +qint64 KD7zEngine::write( const char* data, qint64 len ) +{ +#ifdef SEVENZ_PORT + const unsigned int l = static_cast< unsigned int >( qMin( len, 0x7fffffffLL ) ); + return zipWriteInFileInZip( d->openFileWrite, data, l ) == 0 ? l : -1; +#else + Q_UNUSED(data); + Q_UNUSED(len); + return -1; +#endif +} + +/** + * \reimp + */ +qint64 KD7zEngine::size() const +{ + const Lib7z::File file = d->file(); + return file.uncompressedSize; +} + +/** + * \reimpl + */ +QDateTime KD7zEngine::fileTime( FileTime time ) const +{ + const Lib7z::File file = d->file(); + + switch( time ) + { + case CreationTime: + if( d->fileName.isEmpty() ) + return QFileInfo( d->zipFileName ).created(); + case ModificationTime: + return d->fileName.isEmpty() ? QFileInfo( d->zipFileName ).lastModified() : file.mtime; + case AccessTime: + return QFileInfo( d->zipFileName ).lastRead(); + default: + return QDateTime(); + } +} + +/** + * \reimpl + */ +bool KD7zEngine::mkdir( const QString& dirName, bool createParentDirectories ) const +{ +#ifdef SEVENZ_PORT + Q_UNUSED( createParentDirectories ); + static const QChar sep = QChar::fromLatin1( '/' ); + QString path; + const QString root = QString::fromLatin1( "zip://%1" ).arg( d->zipFileName ); + const QString dir = dirName.mid( root.length() ); + + for( int i = 0; i <= dir.count( sep ); ++i ) + { + path += sep + dir.section( sep, i, i ); + + if( !QDir( root + path ).exists() ) + { + QString fileName = QString::fromLatin1( "%1/" ).arg( path ); + QFile file( d->zipFileName ); + + zipFile openFileWrite = zipOpen( QFile::encodeName( d->zipFileName ).data(), file.exists() && file.size() > 0 ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE ); + zip_fileinfo zi; + const QDateTime dt = QDateTime::currentDateTime(); + zi.tmz_date.tm_sec = dt.time().second(); + zi.tmz_date.tm_min = dt.time().minute(); + zi.tmz_date.tm_hour = dt.time().hour(); + zi.tmz_date.tm_mday = dt.date().day(); + zi.tmz_date.tm_mon = dt.date().month() - 1; + zi.tmz_date.tm_year = dt.date().year(); + zi.dosDate = 0; + zi.internal_fa = 0; + zi.external_fa = 0; + while( fileName.startsWith( sep ) ) + fileName.remove( 0, 1 ); + + if( fileName.isEmpty() ) + continue; + + const bool result = zipOpenNewFileInZip( openFileWrite, QFile::encodeName( fileName ).data(), + &zi, 0, 0, 0, 0, 0, Z_DEFLATED, Z_DEFAULT_COMPRESSION ) == ZIP_OK; + if( result ) + zipCloseFileInZip( openFileWrite ); + zipClose( openFileWrite, 0 ); + if( !result ) + return false; + } + } + return true; +#else + Q_UNUSED(dirName); + Q_UNUSED(createParentDirectories); + return false; +#endif +} + +/** + * \reimp + */ +QAbstractFileEngine::FileFlags KD7zEngine::fileFlags( FileFlags type ) const +{ + FileFlags result; + + static const QLatin1Char sep( '/' ); + static const QString sepString( sep ); + + if( !QFile::exists( d->zipFileName ) ) + return result; + + if( d->flags != 0 && ( type & Refresh ) == 0 ) + return d->flags; + + const Lib7z::File file = d->file(); + const QString name = file.path; + + if( type & ExistsFlag ) + { + if( d->fileName.isEmpty() || d->fileName == sepString ) + result |= ExistsFlag; + else if( !name.isEmpty() ) + result |= ExistsFlag; + } + if( ( type & FileType ) && !name.isEmpty() && !file.isDirectory ) + result |= FileType; + if( ( ( type & DirectoryType ) && file.isDirectory ) || ( name.isEmpty() && ( result & ExistsFlag ) ) ) + result |= DirectoryType; + if( ( type & DirectoryType ) && ( d->fileName.isEmpty() || d->fileName == sepString ) ) + result |= DirectoryType; + + if( ( type & 0x7777 ) ) + result |= ( static_cast< QAbstractFileEngine::FileFlags >( static_cast< quint32 >( file.permissions ) ) & type ); + + d->flags = result; + + return result; +} |