diff options
Diffstat (limited to 'src/libs/installer/fileutils.cpp')
-rw-r--r-- | src/libs/installer/fileutils.cpp | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp new file mode 100644 index 000000000..b07b5b08f --- /dev/null +++ b/src/libs/installer/fileutils.cpp @@ -0,0 +1,507 @@ +/************************************************************************** +** +** This file is part of Installer Framework +** +** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** +** 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. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ +#include "fileutils.h" + +#include <errors.h> + +#include <QtCore/QDateTime> +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <QtCore/QEventLoop> +#include <QtCore/QTemporaryFile> +#include <QtCore/QThread> +#include <QtCore/QUrl> + +#include <errno.h> + +using namespace QInstaller; + + +// -- TempDirDeleter + +TempDirDeleter::TempDirDeleter(const QString &path) +{ + m_paths.insert(path); +} + +TempDirDeleter::TempDirDeleter(const QStringList &paths) + : m_paths(paths.toSet()) +{ +} + +TempDirDeleter::~TempDirDeleter() +{ + releaseAndDeleteAll(); +} + +QStringList TempDirDeleter::paths() const +{ + return m_paths.toList(); +} + +void TempDirDeleter::add(const QString &path) +{ + m_paths.insert(path); +} + +void TempDirDeleter::add(const QStringList &paths) +{ + m_paths += paths.toSet(); +} + +void TempDirDeleter::releaseAll() +{ + m_paths.clear(); +} + +void TempDirDeleter::release(const QString &path) +{ + m_paths.remove(path); +} + +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::releaseAndDeleteAll() +{ + foreach (const QString &path, m_paths) + releaseAndDelete(path); +} + +void TempDirDeleter::releaseAndDelete(const QString &path) +{ + if (m_paths.contains(path)) { + try { + m_paths.remove(path); + removeDirectory(path); + } catch (const Error &e) { + qCritical() << Q_FUNC_INFO << "Exception caught:" << e.message(); + } catch (...) { + qCritical() << Q_FUNC_INFO << "Unknown exception caught."; + } + } +} + + +// -- read, write operations + +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::openForRead(QIODevice *dev, const QString &name) +{ + Q_ASSERT(dev); + if (!dev->open(QIODevice::ReadOnly)) + throw Error(QObject::tr("Cannot open file %1 for reading: %2").arg(name, dev->errorString())); +} + +void QInstaller::openForWrite(QIODevice *dev, const QString &name) +{ + Q_ASSERT(dev); + if (!dev->open(QIODevice::WriteOnly)) + throw Error(QObject::tr("Cannot open file %1 for writing: %2").arg(name, dev->errorString())); +} + +void QInstaller::openForAppend(QIODevice *dev, const QString &name) +{ + Q_ASSERT(dev); + if (!dev->open(QIODevice::ReadWrite | QIODevice::Append)) + throw Error(QObject::tr("Cannot open file %1 for writing: %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::removeFiles(const QString &path, bool ignoreErrors) +{ + const QFileInfoList entries = QDir(path).entryInfoList(QDir::AllEntries | QDir::Hidden); + foreach (const QFileInfo &fi, entries) { + if (fi.isSymLink() || fi.isFile()) { + QFile f(fi.filePath()); + if (!f.remove() && !ignoreErrors) + throw Error(QObject::tr("Could not remove file %1: %2").arg(f.fileName(), f.errorString())); + } + } +} + +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; + + QStringList dirs; + QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Dirs | QDir::NoSymLinks | QDir::Hidden, + QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + dirs.prepend(it.filePath()); + removeFiles(dirs.at(0), ignoreErrors); + } + + QDir d; + dirs.append(path); + removeFiles(path, ignoreErrors); + foreach (const QString &dir, dirs) { + errno = 0; + if (d.exists(path) && !d.rmdir(dir) && !ignoreErrors) + throw Error(QObject::tr("Could not remove folder %1: %2").arg(dir, 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::removeSystemGeneratedFiles(const QString &path) +{ + if (path.isEmpty()) + return; +#if defined Q_WS_MAC + QFile::remove(path + QLatin1String("/.DS_Store")); +#elif defined Q_WS_WIN + QFile::remove(path + QLatin1String("/Thumbs.db")); +#endif +} + +void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &targetDir) +{ + qDebug() << "Copying" << sourceDir << "to" << targetDir; + 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) +{ + qDebug() << "Moving" << sourceDir << "to" << targetDir; + 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(); + } + + static const QString characters = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); + 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() + QLatin1String("meta"); + qDebug() << "Creating meta data directory at" << path; + + 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 |