diff options
Diffstat (limited to 'installerbuilder/common/fileutils.cpp')
-rw-r--r-- | installerbuilder/common/fileutils.cpp | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/installerbuilder/common/fileutils.cpp b/installerbuilder/common/fileutils.cpp new file mode 100644 index 000000000..04fa9ae94 --- /dev/null +++ b/installerbuilder/common/fileutils.cpp @@ -0,0 +1,468 @@ +/************************************************************************** +** +** 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 "fileutils.h" +#include <common/errors.h> +#include <common/utils.h> + +#include <QDateTime> +#include <QDir> +#include <QDirIterator> +#include <QEventLoop> +#include <QFileInfo> +#include <QString> +#include <QTemporaryFile> +#include <QThread> +#include <QUrl> + +#include <cassert> +#include <cerrno> + +using namespace QInstaller; + +TempDirDeleter::TempDirDeleter( const QString& path ) + : m_paths( QStringList() << path ) +{ +} + +TempDirDeleter::TempDirDeleter( const QStringList& paths_ ) + : m_paths( paths_ ) +{ +} + +TempDirDeleter::~TempDirDeleter() +{ + for( QStringList::const_iterator it = m_paths.constBegin(); it != m_paths.constEnd(); ++it ) + { + const QString& path = *it; + if( !path.isEmpty() ) + try { + removeDirectory( path ); + } catch ( const Error& e ) { + qCritical() << Q_FUNC_INFO << "Exception caught:" << e.message(); + } catch ( ... ) { + qCritical() << Q_FUNC_INFO << "Unknown exception caught."; + } + } +} + +void TempDirDeleter::passAndReleaseAll( TempDirDeleter& tdd ) { + tdd.m_paths = m_paths; + releaseAll(); +} + +void TempDirDeleter::passAndRelease( TempDirDeleter& tdd, const QString& path ) { + tdd.add( path ); + release( path ); +} + +void TempDirDeleter::releaseAll() { + m_paths.clear(); +} + +void TempDirDeleter::release( const QString& path ) { + m_paths.removeAll( path ); +} + +QStringList TempDirDeleter::paths() const { + return m_paths; +} + +void TempDirDeleter::add( const QString& path ) +{ + if( !m_paths.contains( path ) ) + m_paths.push_back( path ); +} + +void TempDirDeleter::add( const QStringList& paths ) +{ + for( QStringList::const_iterator it = paths.begin(); it != paths.end(); ++it ) + add( *it ); +} + +bool QInstaller::isLocalUrl( const QUrl& url ) { + return url.scheme().isEmpty() || url.scheme().toLower() == QLatin1String("file"); +} + +QString QInstaller::pathFromUrl( const QUrl& url ) +{ + if( isLocalUrl( url ) ) + return url.toLocalFile(); + const QString str = url.toString(); + if ( url.scheme() == QLatin1String("resource") ) + return str.mid( QString::fromLatin1("resource").length() ); + return str; +} + + +void QInstaller::openForWrite(QIODevice* dev, const QString& name) +{ + assert( dev ); + if (!dev->open(QIODevice::WriteOnly)) + throw Error(QObject::tr("Cannot open file %1 for writing: %2").arg( name, dev->errorString() ) ); +} + +void QInstaller::openForRead(QIODevice* dev, const QString& name) +{ + assert( dev ); + if (!dev->open(QIODevice::ReadOnly)) + throw Error(QObject::tr( "Cannot open file %1 for reading: %2" ).arg( name, dev->errorString() ) ); +} + +qint64 QInstaller::blockingWrite(QIODevice *out, const char *buffer, qint64 size) +{ + qint64 left = size; + while (left > 0) { + const qint64 n = out->write(buffer, left); + if (n < 0) + throw Error( QObject::tr("Write failed after %1 bytes: %2").arg( QString::number(size-left), out->errorString() ) ); + left -= n; + } + return size; +} + +qint64 QInstaller::blockingWrite(QIODevice *out, const QByteArray& ba) +{ + return blockingWrite( out, ba.constData(), ba.size() ); +} + +qint64 QInstaller::blockingRead(QIODevice *in, char *buffer, qint64 size) +{ + if ( in->atEnd() ) + return 0; + qint64 left = size; + while (left > 0) { + const qint64 n = in->read(buffer, left); + if ( n < 0 ) + throw Error( QObject::tr("Read failed after %1 bytes: %2").arg( QString::number(size-left), in->errorString() ) ); + + left -= n; + buffer += n; + } + return size; +} + +void QInstaller::blockingCopy( QIODevice* in, QIODevice* out, qint64 size ) +{ + static const qint64 blockSize = 4096; + QByteArray ba( blockSize, '\0' ); + qint64 actual = qMin( blockSize, size ); + while( actual > 0 ) + { + blockingRead( in, ba.data(), actual ); + blockingWrite( out, ba.constData(), actual ); + size -= actual; + actual = qMin( blockSize, size ); + } +} + +void QInstaller::removeDirectory( const QString& path, bool ignoreErrors ) +{ + if ( path.isEmpty() ) // QDir( "" ) points to the working directory! We never want to remove that one. + return; + + QDirIterator it( path, QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden ); + while( it.hasNext() ) + { + it.next(); + const QFileInfo fi = it.fileInfo(); + + if( fi.isDir() && !fi.isSymLink() ) + { + removeDirectory( fi.filePath(), ignoreErrors ); + } + else + { + QFile f( fi.filePath() ); + if( !f.remove() && !ignoreErrors ) + throw Error( QObject::tr("Could not remove file %1: %2").arg( f.fileName(), f.errorString() ) ); + } + } + + errno = 0; + if ( !QDir().rmdir( path ) && !ignoreErrors ) + throw Error( QObject::tr("Could not remove folder %1: %2").arg( path, QLatin1String(strerror(errno)) ) ); +} + +/*! + \internal + */ +class RemoveDirectoryThread : public QThread +{ +public: + explicit RemoveDirectoryThread( const QString& path, bool ignoreErrors = false, QObject* parent = 0 ) + : QThread( parent ), + p( path ), + ignore( ignoreErrors ) + { + } + + const QString& error() const + { + return err; + } + +protected: + /*! + \reimp + */ + void run() + { + try + { + removeDirectory( p, ignore ); + } + catch( const Error& e ) + { + err = e.message(); + } + } + +private: + QString err; + const QString p; + const bool ignore; +}; + +void QInstaller::removeDirectoryThreaded( const QString& path, bool ignoreErrors ) +{ + RemoveDirectoryThread thread( path, ignoreErrors ); + QEventLoop loop; + QObject::connect( &thread, SIGNAL( finished() ), &loop, SLOT( quit() ) ); + thread.start(); + loop.exec(); + if( !thread.error().isEmpty() ) + throw Error( thread.error() ); +} + +void QInstaller::copyDirectoryContents( const QString& sourceDir, const QString& targetDir ) { + verbose() << "Copying " << sourceDir << " to " << targetDir << std::endl; + Q_ASSERT( QFileInfo( sourceDir ).isDir() ); + Q_ASSERT( !QFileInfo( targetDir ).exists() || QFileInfo( targetDir ).isDir() ); + if ( !QDir().mkpath( targetDir ) ) + throw Error( QObject::tr("Could not create folder %1").arg( targetDir ) ); + + QDirIterator it( sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries ); + while( it.hasNext() ) + { + const QFileInfo i( it.next() ); + if( i.isDir() ) + { + copyDirectoryContents( QDir( sourceDir ).absoluteFilePath( i.fileName() ), QDir( targetDir ).absoluteFilePath( i.fileName() ) ); + } + else + { + QFile f( i.filePath() ); + const QString target = QDir( targetDir ).absoluteFilePath( i.fileName() ); + if( !f.copy( target ) ) + throw Error( QObject::tr("Could not copy file from %1 to %2: %3").arg( f.fileName(), target, f.errorString() ) ); + } + } +} + +void QInstaller::moveDirectoryContents( const QString& sourceDir, const QString& targetDir ) { + verbose() << "Moving " << sourceDir << " to " << targetDir << std::endl; + Q_ASSERT( QFileInfo( sourceDir ).isDir() ); + Q_ASSERT( !QFileInfo( targetDir ).exists() || QFileInfo( targetDir ).isDir() ); + if ( !QDir().mkpath( targetDir ) ) + throw Error( QObject::tr("Could not create folder %1").arg( targetDir ) ); + + QDirIterator it( sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries ); + while( it.hasNext() ) + { + const QFileInfo i( it.next() ); + if( i.isDir() ) + { + moveDirectoryContents( QDir( sourceDir ).absoluteFilePath( i.fileName() ), QDir( targetDir ).absoluteFilePath( i.fileName() ) ); + } + else + { + QFile f( i.filePath() ); + const QString target = QDir( targetDir ).absoluteFilePath( i.fileName() ); + if( !f.rename( target ) ) + throw Error( QObject::tr("Could not move file from %1 to %2: %3").arg( f.fileName(), target, f.errorString() ) ); + } + } +} + +void QInstaller::mkdir( const QString& path ) { + errno = 0; + if ( !QDir().mkdir( QFileInfo( path ).absoluteFilePath() ) ) + throw Error( QObject::tr("Could not create folder %1: %2" ).arg( path, QString::fromLocal8Bit( strerror( errno ) ) ) ); +} + +void QInstaller::mkpath( const QString& path ) { + errno = 0; + if ( !QDir().mkpath( QFileInfo( path ).absoluteFilePath() ) ) + throw Error( QObject::tr("Could not create folder %1: %2" ).arg( path, QString::fromLocal8Bit( strerror( errno ) ) ) ); +} + +QString QInstaller::generateTemporaryFileName( const QString& templ ) +{ + if ( templ.isEmpty() ) + { + QTemporaryFile f; + if ( !f.open() ) + throw Error( QObject::tr("Could not open temporary file: %1").arg( f.errorString() ) ); + return f.fileName(); + } + else + { + static const QString characters = QLatin1String( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" ); +/* const QFileInfo fi( path ); + const QString dir = fi.absolutePath(); + const QString file = fi.fileName();*/ + QString suffix; + qsrand( qrand() * QDateTime::currentDateTime().toTime_t() ); + for( int i = 0; i < 5; ++i ) + suffix += characters[ qrand() % characters.length() ]; + const QString tmp = QLatin1String( "%1.tmp.%2.%3" ); + int count = 1; + while ( QFile::exists( tmp.arg( templ, suffix ).arg( count ) ) ) + ++count; + QFile f( tmp.arg( templ, suffix ).arg( count ) ); + if( !f.open( QIODevice::WriteOnly ) ) + throw Error( QObject::tr("Could not open temporary file for template %1: %2").arg( templ, f.errorString() ) ); + f.remove(); + return f.fileName(); + } +} + +QString QInstaller::createTemporaryDirectory( const QString& templ ) { + const QString t = QDir::tempPath() + QLatin1String("/") + templ + QLatin1String("XXXXXX"); + QTemporaryFile f( t ); + if ( !f.open() ) + throw Error( QObject::tr("Could not create temporary folder for template %1: %2").arg( t, f.errorString() ) ); + const QString path = f.fileName() + QString::fromLatin1( "meta" ); + verbose() << "Creating meta data directory at " << path << std::endl; + + QInstaller::mkpath( path ); + return path; +} + +#ifdef Q_WS_WIN +#include <windows.h> + +#pragma pack( push ) +#pragma pack(2) + +typedef struct +{ +BYTE bWidth; // Width, in pixels, of the image +BYTE bHeight; // Height, in pixels, of the image +BYTE bColorCount; // Number of colors in image (0 if >=8bpp) +BYTE bReserved; // Reserved +WORD wPlanes; // Color Planes +WORD wBitCount; // Bits per pixel +DWORD dwBytesInRes; // how many bytes in this resource? +DWORD dwImageOffset; // the ID +} ICONDIRENTRY; + +typedef struct +{ +WORD idReserved; // Reserved (must be 0) +WORD idType; // Resource type (1 for icons) +WORD idCount; // How many images? +ICONDIRENTRY idEntries[1]; // The entries for each image +} ICONDIR; + +typedef struct +{ +BYTE bWidth; // Width, in pixels, of the image +BYTE bHeight; // Height, in pixels, of the image +BYTE bColorCount; // Number of colors in image (0 if >=8bpp) +BYTE bReserved; // Reserved +WORD wPlanes; // Color Planes +WORD wBitCount; // Bits per pixel +DWORD dwBytesInRes; // how many bytes in this resource? +WORD nID; // the ID +} GRPICONDIRENTRY, *LPGRPICONDIRENTRY; + +typedef struct +{ +WORD idReserved; // Reserved (must be 0) +WORD idType; // Resource type (1 for icons) +WORD idCount; // How many images? +GRPICONDIRENTRY idEntries[1]; // The entries for each image +} GRPICONDIR, *LPGRPICONDIR; + + +#pragma pack( pop ) + +void QInstaller::setApplicationIcon( const QString& application, const QString& icon ) +{ + wchar_t* const path = new wchar_t[ application.length() + 1 ]; + QDir::toNativeSeparators( application ).toWCharArray( path ); + path[ application.length() ] = 0; + + HANDLE updateRes = BeginUpdateResource( path, false ); + delete[] path; + + QFile iconFile( icon ); + if( !iconFile.open( QIODevice::ReadOnly ) ) + return; + + QByteArray temp = iconFile.readAll(); + + ICONDIR* ig = reinterpret_cast< ICONDIR* >( temp.data() ); + + DWORD newSize = sizeof( GRPICONDIR ) + sizeof( GRPICONDIRENTRY ) * ( ig->idCount - 1 ); + GRPICONDIR* newDir = reinterpret_cast< GRPICONDIR* >( new char[ newSize ] ); + newDir->idReserved = ig->idReserved; + newDir->idType = ig->idType; + newDir->idCount = ig->idCount; + + for( int i = 0; i < ig->idCount; ++i ) + { + char* temp1 = temp.data() + ig->idEntries[ i ].dwImageOffset; + DWORD size1 = ig->idEntries[ i ].dwBytesInRes; + + newDir->idEntries[ i ].bWidth = ig->idEntries[ i ].bWidth; + newDir->idEntries[ i ].bHeight = ig->idEntries[ i ].bHeight; + newDir->idEntries[ i ].bColorCount = ig->idEntries[ i ].bColorCount; + newDir->idEntries[ i ].bReserved = ig->idEntries[ i ].bReserved; + newDir->idEntries[ i ].wPlanes = ig->idEntries[ i ].wPlanes; + newDir->idEntries[ i ].wBitCount = ig->idEntries[ i ].wBitCount; + newDir->idEntries[ i ].dwBytesInRes = ig->idEntries[ i ].dwBytesInRes; + newDir->idEntries[ i ].nID = i + 1; + + UpdateResource( updateRes, RT_ICON, MAKEINTRESOURCE( i + 1 ), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), temp1, size1 ); + } + + UpdateResource( updateRes, RT_GROUP_ICON, L"IDI_ICON1", MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), newDir, newSize ); + + delete[] newDir; + + EndUpdateResource( updateRes, false ); +} +#endif |