summaryrefslogtreecommitdiffstats
path: root/src/libs/installer/fsengineclient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer/fsengineclient.cpp')
-rw-r--r--src/libs/installer/fsengineclient.cpp818
1 files changed, 818 insertions, 0 deletions
diff --git a/src/libs/installer/fsengineclient.cpp b/src/libs/installer/fsengineclient.cpp
new file mode 100644
index 000000000..1858d7408
--- /dev/null
+++ b/src/libs/installer/fsengineclient.cpp
@@ -0,0 +1,818 @@
+/**************************************************************************
+**
+** 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 "fsengineclient.h"
+
+#include "adminauthorization.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QMutex>
+#include <QtCore/QProcess>
+#include <QtCore/QThread>
+#include <QtCore/QTimer>
+#include <QtCore/QUuid>
+
+#include <QtNetwork/QHostAddress>
+#include <QtNetwork/QTcpSocket>
+
+
+// -- StillAliveThread
+
+/*!
+ This thread convinces the watchdog in the running server that the client has not crashed yet.
+*/
+class StillAliveThread : public QThread
+{
+ Q_OBJECT
+public:
+ void run()
+ {
+ QTimer stillAliveTimer;
+ connect(&stillAliveTimer, SIGNAL(timeout()), this, SLOT(stillAlive()));
+ stillAliveTimer.start(1000);
+ exec();
+ }
+
+public Q_SLOTS:
+ void stillAlive()
+ {
+ if (!FSEngineClientHandler::instance().isServerRunning())
+ return;
+
+ // in case of the server not running, this will simply fail
+ QTcpSocket socket;
+ FSEngineClientHandler::instance().connect(&socket);
+ }
+};
+
+
+// -- FSEngineClient
+
+class FSEngineClient : public QAbstractFileEngine
+{
+public:
+ FSEngineClient();
+ ~FSEngineClient();
+
+ bool atEnd() const;
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames);
+ bool caseSensitive() const;
+ bool close();
+ bool copy(const QString &newName);
+ QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const;
+ QFile::FileError error() const;
+ QString errorString() const;
+ bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0);
+ FileFlags fileFlags(FileFlags type = FileInfoAll) const;
+ QString fileName(FileName file = DefaultName) const;
+ bool flush();
+ int handle() const;
+ bool isRelativePath() const;
+ bool isSequential() const;
+ bool link(const QString &newName);
+ bool mkdir(const QString &dirName, bool createParentDirectories) const;
+ bool open(QIODevice::OpenMode mode);
+ QString owner(FileOwner owner) const;
+ uint ownerId(FileOwner owner) const;
+ qint64 pos() const;
+ qint64 read(char *data, qint64 maxlen);
+ qint64 readLine(char *data, qint64 maxlen);
+ bool remove();
+ bool rename(const QString &newName);
+ bool rmdir(const QString &dirName, bool recurseParentDirectories) const;
+ bool seek(qint64 offset);
+ void setFileName(const QString &fileName);
+ bool setPermissions(uint perms);
+ bool setSize(qint64 size);
+ qint64 size() const;
+ bool supportsExtension(Extension extension) const;
+ qint64 write(const char *data, qint64 len);
+
+private:
+ template<typename T> T returnWithType() const;
+ template<typename T> T returnWithCastedType() const;
+
+private:
+ friend class FSEngineClientHandler;
+
+ mutable QTcpSocket *socket;
+ mutable QDataStream stream;
+};
+
+template<typename T> T FSEngineClient::returnWithType() const
+{
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 test;
+ stream >> test;
+
+ T result;
+ stream >> result;
+ return result;
+}
+
+template<typename T> T FSEngineClient::returnWithCastedType() const
+{
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 test;
+ stream >> test;
+
+ int result;
+ stream >> result;
+ return static_cast<T>(result);
+}
+
+/*!
+ \internal
+*/
+class FSEngineClientIterator : public QAbstractFileEngineIterator
+{
+public:
+ FSEngineClientIterator(QDir::Filters filters, const QStringList &nameFilters, const QStringList &files)
+ : QAbstractFileEngineIterator(filters, nameFilters),
+ entries(files),
+ index(-1)
+ {
+ }
+
+ /*!
+ \reimp
+ */
+ bool hasNext() const
+ {
+ return index < entries.size() - 1;
+ }
+
+ /*!
+ \reimp
+ */
+ QString next()
+ {
+ if (!hasNext())
+ return QString();
+ ++index;
+ return currentFilePath();
+ }
+
+ /*!
+ \reimp
+ */
+ QString currentFileName() const
+ {
+ return entries.at(index);
+ }
+
+private:
+ const QStringList entries;
+ int index;
+};
+
+FSEngineClient::FSEngineClient()
+ : socket(new QTcpSocket)
+{
+ FSEngineClientHandler::instance().connect(socket);
+ stream.setDevice(socket);
+ stream.setVersion(QDataStream::Qt_4_2);
+}
+
+FSEngineClient::~FSEngineClient()
+{
+ if (QThread::currentThread() == socket->thread()) {
+ socket->close();
+ delete socket;
+ } else {
+ socket->deleteLater();
+ }
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::atEnd() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::atEnd");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine::Iterator* FSEngineClient::beginEntryList(QDir::Filters filters,
+ const QStringList &filterNames)
+{
+ QStringList entries = entryList(filters, filterNames);
+ entries.removeAll(QString());
+ return new FSEngineClientIterator(filters, filterNames, entries);
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::caseSensitive() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::caseSensitive");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::close()
+{
+ stream << QString::fromLatin1("QFSFileEngine::close");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::copy(const QString &newName)
+{
+ stream << QString::fromLatin1("QFSFileEngine::copy");
+ stream << newName;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+QStringList FSEngineClient::entryList(QDir::Filters filters, const QStringList &filterNames) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::entryList");
+ stream << static_cast<int>(filters);
+ stream << filterNames;
+ return returnWithType<QStringList>();
+}
+
+/*!
+ \reimp
+*/
+QFile::FileError FSEngineClient::error() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::error");
+ return returnWithCastedType<QFile::FileError>();
+}
+
+/*!
+ \reimp
+*/
+QString FSEngineClient::errorString() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::errorString");
+ return returnWithType<QString>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
+{
+ Q_UNUSED(extension)
+ Q_UNUSED(option)
+ Q_UNUSED(output)
+ return false;
+}
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine::FileFlags FSEngineClient::fileFlags(FileFlags type) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::fileFlags");
+ stream << static_cast<int>(type);
+ return returnWithCastedType<QAbstractFileEngine::FileFlags>();
+}
+
+/*!
+ \reimp
+*/
+QString FSEngineClient::fileName(FileName file) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::fileName");
+ stream << static_cast<int>(file);
+ return returnWithType<QString>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::flush()
+{
+ stream << QString::fromLatin1("QFSFileEngine::flush");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+int FSEngineClient::handle() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::handle");
+ return returnWithType<int>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::isRelativePath() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::isRelativePath");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::isSequential() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::isSequential");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::link(const QString &newName)
+{
+ stream << QString::fromLatin1("QFSFileEngine::link");
+ stream << newName;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::mkdir(const QString &dirName, bool createParentDirectories) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::mkdir");
+ stream << dirName;
+ stream << createParentDirectories;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::open(QIODevice::OpenMode mode)
+{
+ stream << QString::fromLatin1("QFSFileEngine::open");
+ stream << static_cast<int>(mode);
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+QString FSEngineClient::owner(FileOwner owner) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::owner");
+ stream << static_cast<int>(owner);
+ return returnWithType<QString>();
+}
+
+/*!
+ \reimp
+*/
+uint FSEngineClient::ownerId(FileOwner owner) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::ownerId");
+ stream << static_cast<int>(owner);
+ return returnWithType<uint>();
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::pos() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::pos");
+ return returnWithType<qint64>();
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::read(char *data, qint64 maxlen)
+{
+ stream << QString::fromLatin1("QFSFileEngine::read");
+ stream << maxlen;
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 size;
+ stream >> size;
+ qint64 result;
+ stream >> result;
+ qint64 read = 0;
+ while (read < result) {
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ read += socket->read(data + read, result - read);
+ }
+ return result;
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::readLine(char *data, qint64 maxlen)
+{
+ stream << QString::fromLatin1("QFSFileEngine::readLine");
+ stream << maxlen;
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 size;
+ stream >> size;
+ qint64 result;
+ stream >> result;
+ qint64 read = 0;
+ while (read < result) {
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ read += socket->read(data + read, result - read);
+ }
+ return result;
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::remove()
+{
+ stream << QString::fromLatin1("QFSFileEngine::remove");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::rename(const QString &newName)
+{
+ stream << QString::fromLatin1("QFSFileEngine::rename");
+ stream << newName;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::rmdir(const QString &dirName, bool recurseParentDirectories) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::rmdir");
+ stream << dirName;
+ stream << recurseParentDirectories;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::seek(qint64 offset)
+{
+ stream << QString::fromLatin1("QFSFileEngine::seek");
+ stream << offset;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+void FSEngineClient::setFileName(const QString &fileName)
+{
+ stream << QString::fromLatin1("QFSFileEngine::setFileName");
+ stream << fileName;
+
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 test;
+ stream >> test;
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::setPermissions(uint perms)
+{
+ stream << QString::fromLatin1("QFSFileEngine::setPermissions");
+ stream << perms;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::setSize(qint64 size)
+{
+ stream << QString::fromLatin1("QFSFileEngine::setSize");
+ stream << size;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::size() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::size");
+ return returnWithType<qint64>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::supportsExtension(Extension extension) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::supportsExtension");
+ stream << static_cast<int>(extension);
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::write(const char *data, qint64 len)
+{
+ stream << QString::fromLatin1("QFSFileEngine::write");
+ stream << len;
+ qint64 written = 0;
+ while (written < len) {
+ written += socket->write(data, len - written);
+ socket->waitForBytesWritten();
+ }
+ return returnWithType<qint64>();
+}
+
+class FSEngineClientHandler::Private
+{
+public:
+ Private()
+ : mutex(QMutex::Recursive),
+ port(0),
+ startServerAsAdmin(false),
+ serverStarted(false),
+ serverStarting(false),
+ active(false),
+ thread(new StillAliveThread)
+ {
+ thread->moveToThread(thread);
+ }
+
+ void maybeStartServer();
+ void maybeStopServer();
+
+ QMutex mutex;
+ QHostAddress address;
+ quint16 port;
+ QString socket;
+ bool startServerAsAdmin;
+ bool serverStarted;
+ bool serverStarting;
+ bool active;
+ QString serverCommand;
+ QStringList serverArguments;
+ QString key;
+
+ StillAliveThread *const thread;
+};
+
+/*!
+ Creates a new FSEngineClientHandler with no connection.
+*/
+FSEngineClientHandler::FSEngineClientHandler()
+ : d(new Private)
+{
+ //don't do this in the Private ctor as createUuid() accesses QFileEngine, which accesses this
+ // half-constructed handler -> Crash (KDNDK-248)
+ d->key = QUuid::createUuid().toString();
+}
+
+void FSEngineClientHandler::enableTestMode()
+{
+ d->key = QLatin1String("testAuthorizationKey");
+ d->serverStarted = true;
+}
+
+void FSEngineClientHandler::init(quint16 port, const QHostAddress &a)
+{
+ d->address = a;
+ d->port = port;
+ d->thread->start();
+}
+
+bool FSEngineClientHandler::connect(QTcpSocket *socket)
+{
+ int tries = 3;
+ while (tries > 0) {
+ socket->connectToHost(d->address, d->port);
+ if (!socket->waitForConnected(10000)) {
+ if (static_cast<QAbstractSocket::SocketError>(socket->error()) != QAbstractSocket::UnknownSocketError)
+ --tries;
+ qApp->processEvents();
+ continue;
+ }
+
+ QDataStream stream(socket);
+ stream << QString::fromLatin1("authorize");
+ stream << d->key;
+ socket->flush();
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Destroys the FSEngineClientHandler. If the handler started a server instance, it gets shut down.
+*/
+FSEngineClientHandler::~FSEngineClientHandler()
+{
+ QMetaObject::invokeMethod(d->thread, "quit");
+ //d->maybeStopServer();
+ delete d;
+}
+
+/*!
+ Returns a previously created FSEngineClientHandler instance.
+*/
+FSEngineClientHandler &FSEngineClientHandler::instance()
+{
+ static FSEngineClientHandler instance;
+ return instance;
+}
+
+/*!
+ Returns a created authorization key which is sent to the server when connecting via the "authorize"
+ command after the server was started.
+*/
+QString FSEngineClientHandler::authorizationKey() const
+{
+ return d->key;
+}
+
+/*!
+ Sets \a command as the command to be executed to startup the server. If \a startAsAdmin is set,
+ it is executed with admin privilegies.
+*/
+void FSEngineClientHandler::setStartServerCommand(const QString &command, bool startAsAdmin)
+{
+ setStartServerCommand(command, QStringList(), startAsAdmin);
+}
+
+/*!
+ Sets \a command as the command to be executed to startup the server. If \a startAsAdmin is set, it is
+ executed with admin privilegies. A list of \a arguments is passed to the process.
+*/
+void FSEngineClientHandler::setStartServerCommand(const QString &command, const QStringList &arguments,
+ bool startAsAdmin)
+{
+ d->maybeStopServer();
+
+ d->startServerAsAdmin = startAsAdmin;
+ d->serverCommand = command;
+ d->serverArguments = arguments;
+}
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine* FSEngineClientHandler::create(const QString &fileName) const
+{
+ if (d->serverStarting || !d->active)
+ return 0;
+
+ d->maybeStartServer();
+
+ static QRegExp re(QLatin1String("^[a-z0-9]*://.*$"));
+ if (re.exactMatch(fileName)) // stuff like installer:// 7z:// and so on
+ return 0;
+
+ if (fileName.isEmpty() || fileName.startsWith(QLatin1String(":")))
+ return 0; // empty filename or Qt resource
+
+ FSEngineClient *const client = new FSEngineClient;
+ // authorize
+ client->stream << QString::fromLatin1("authorize");
+ client->stream << d->key;
+ client->socket->flush();
+
+ client->setFileName(fileName);
+ return client;
+}
+
+/*!
+ Sets the FSEngineClientHandler to \a active. I.e. to actually return FSEngineClients if asked for.
+*/
+void FSEngineClientHandler::setActive(bool active)
+{
+ d->active = active;
+ if (active) {
+ d->maybeStartServer();
+ d->active = d->serverStarted;
+ }
+}
+
+/*!
+ Returns, wheter this FSEngineClientHandler is active or not.
+*/
+bool FSEngineClientHandler::isActive() const
+{
+ return d->active;
+}
+
+/*!
+ Returns true, when the server already has been started.
+*/
+bool FSEngineClientHandler::isServerRunning() const
+{
+ return d->serverStarted;
+}
+
+/*!
+ \internal
+ Starts the server if a command was set and it isn't already started.
+*/
+void FSEngineClientHandler::Private::maybeStartServer()
+{
+ if (serverStarted || serverCommand.isEmpty())
+ return;
+
+ const QMutexLocker ml(&mutex);
+ if (serverStarted)
+ return;
+
+ serverStarting = true;
+
+ if (startServerAsAdmin) {
+ AdminAuthorization auth;
+ serverStarted = auth.authorize() && auth.execute(0, serverCommand, serverArguments);
+ } else {
+ serverStarted = QProcess::startDetached(serverCommand, serverArguments);
+ }
+
+ // now wait for the socket to arrive
+ QTcpSocket s;
+ while (serverStarting && serverStarted) {
+ if (FSEngineClientHandler::instance().connect(&s))
+ serverStarting = false;
+ }
+ serverStarting = false;
+}
+
+/*!
+ Stops the server if it was started before.
+*/
+void FSEngineClientHandler::Private::maybeStopServer()
+{
+ if (!serverStarted)
+ return;
+
+ const QMutexLocker ml(&mutex);
+ if (!serverStarted)
+ return;
+
+ QTcpSocket s;
+ if (FSEngineClientHandler::instance().connect(&s)) {
+ QDataStream stream(&s);
+ stream.setVersion(QDataStream::Qt_4_2);
+ stream << QString::fromLatin1("authorize");
+ stream << key;
+ stream << QString::fromLatin1("shutdown");
+ s.flush();
+ }
+ serverStarted = false;
+}
+
+#include "fsengineclient.moc"