diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro index 7a679d55..18b88dd3 100644 --- a/src/libs/installer/installer.pro +++ b/src/libs/installer/installer.pro @@ -112,9 +112,18 @@ HEADERS += packagemanagercore.h \ metadatajob.h \ metadatajob_p.h \ installer_global.h \ - scriptengine_p.h + scriptengine_p.h \ + protocol.h \ + remoteobject.h \ + remoteclient.h \ + remoteserver.h \ + remoteclient_p.h \ + remoteserver_p.h \ + remotefileengine.h \ + remoteserverconnection.h \ + remoteserverconnection_p.h - SOURCES += packagemanagercore.cpp \ +SOURCES += packagemanagercore.cpp \ packagemanagercore_p.cpp \ packagemanagergui.cpp \ binaryformat.cpp \ @@ -180,7 +189,12 @@ HEADERS += packagemanagercore.h \ downloadfiletask.cpp \ unziptask.cpp \ observer.cpp \ - metadatajob.cpp + metadatajob.cpp \ + remoteobject.cpp \ + remoteclient.cpp \ + remoteserver.cpp \ + remotefileengine.cpp \ + remoteserverconnection.cpp RESOURCES += resources/patch_file_lists.qrc \ resources/installer.qrc diff --git a/src/libs/installer/installer_global.h b/src/libs/installer/installer_global.h index 635dbcf4..e615eefc 100644 --- a/src/libs/installer/installer_global.h +++ b/src/libs/installer/installer_global.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012-2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. diff --git a/src/libs/installer/protocol.h b/src/libs/installer/protocol.h new file mode 100644 index 00000000..cc084c22 --- /dev/null +++ b/src/libs/installer/protocol.h @@ -0,0 +1,160 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef PROTOCOL_H +#define PROTOCOL_H + +namespace QInstaller { +namespace Protocol { + +const char Create[] = "Create"; +const char Destroy[] = "Destroy"; +const char Shutdown[] = "Shutdown"; +const char Authorize[] = "Authorize"; +const char DebugAuthorizationKey[] = "DebugAuthorizationKey"; + +// QProcessWrapper +const char QProcess[] = "QProcess"; +const char QProcessCloseWriteChannel[] = "QProcess::closeWriteChannel"; +const char QProcessExitCode[] = "QProcess::exitCode"; +const char QProcessExitStatus[] = "QProcess::exitStatus"; +const char QProcessKill[] = "QProcess::kill"; +const char QProcessReadAll[] = "QProcess::readAll"; +const char QProcessReadAllStandardOutput[] = "QProcess::readAllStandardOutput"; +const char QProcessReadAllStandardError[] = "QProcess::readAllStandardError"; +const char QProcessStartDetached[] = "QProcess::startDetached"; +const char QProcessSetWorkingDirectory[] = "QProcess::setWorkingDirectory"; +const char QProcessSetEnvironment[] = "QProcess::setEnvironment"; +const char QProcessEnvironment[] = "QProcess::environment"; +const char QProcessStart3Arg[] = "QProcess::start3"; +const char QProcessStart2Arg[] = "QProcess::start2"; +const char QProcessState[] = "QProcess::state"; +const char QProcessTerminate[] = "QProcess::terminate"; +const char QProcessWaitForFinished[] = "QProcess::waitForFinished"; +const char QProcessWaitForStarted[] = "QProcess::waitForStarted"; +const char QProcessWorkingDirectory[] = "QProcess::workingDirectory"; +const char QProcessErrorString[] = "QProcess::errorString"; +const char QProcessReadChannel[] = "QProcess::readChannel"; +const char QProcessSetReadChannel[] = "QProcess::setReadChannel"; +const char QProcessWrite[] = "QProcess::write"; +const char QProcessProcessChannelMode[] = "QProcess::processChannelMode"; +const char QProcessSetProcessChannelMode[] = "QProcess::setProcessChannelMode"; +const char QProcessSetNativeArguments[] = "QProcess::setNativeArguments"; + +const char GetQProcessSignals[] = "GetQProcessSignals"; +const char QProcessSignalBytesWritten[] = "QProcess::bytesWritten"; +const char QProcessSignalAboutToClose[] = "QProcess::aboutToClose"; +const char QProcessSignalReadChannelFinished[] = "QProcess::readChannelFinished"; +const char QProcessSignalError[] = "QProcess::error"; +const char QProcessSignalReadyReadStandardOutput[] = "QProcess::readyReadStandardOutput"; +const char QProcessSignalReadyReadStandardError[] = "QProcess::readyReadStandardError"; +const char QProcessSignalStarted[] = "QProcess::started"; +const char QProcessSignalReadyRead[] = "QProcess::readyRead"; +const char QProcessSignalStateChanged[] = "QProcess::stateChanged"; +const char QProcessSignalFinished[] = "QProcess::finished"; + + +// QSettingsWrapper +const char QSettings[] = "QSettings"; +const char QSettingsAllKeys[] = "QSettings::allKeys"; +const char QSettingsBeginGroup[] = "QSettings::beginGroup"; +const char QSettingsBeginWriteArray[] = "QSettings::beginWriteArray"; +const char QSettingsBeginReadArray[] = "QSettings::beginReadArray"; +const char QSettingsChildGroups[] = "QSettings::childGroups"; +const char QSettingsChildKeys[] = "QSettings::childKeys"; +const char QSettingsClear[] = "QSettings::clear"; +const char QSettingsContains[] = "QSettings::contains"; +const char QSettingsEndArray[] = "QSettings::endArray"; +const char QSettingsEndGroup[] = "QSettings::endGroup"; +const char QSettingsFallbacksEnabled[] = "QSettings::fallbacksEnabled"; +const char QSettingsFileName[] = "QSettings::fileName"; +const char QSettingsGroup[] = "QSettings::group"; +const char QSettingsIsWritable[] = "QSettings::isWritable"; +const char QSettingsRemove[] = "QSettings::remove"; +const char QSettingsSetArrayIndex[] = "QSettings::setArrayIndex"; +const char QSettingsSetFallbacksEnabled[] = "QSettings::setFallbacksEnabled"; +const char QSettingsStatus[] = "QSettings::status"; +const char QSettingsSync[] = "QSettings::sync"; +const char QSettingsSetValue[] = "QSettings::setValue"; +const char QSettingsValue[] = "QSettings::value"; +const char QSettingsOrganizationName[] = "QSettings::organizationName"; +const char QSettingsApplicationName[] = "QSettings::applicationName"; + + +// RemoteFileEngine +const char QAbstractFileEngine[] = "QAbstractFileEngine"; +const char QAbstractFileEngineAtEnd[] = "QAbstractFileEngine::atEnd"; +const char QAbstractFileEngineCaseSensitive[] = "QAbstractFileEngine::caseSensitive"; +const char QAbstractFileEngineClose[] = "QAbstractFileEngine::close"; +const char QAbstractFileEngineCopy[] = "QAbstractFileEngine::copy"; +const char QAbstractFileEngineEntryList[] = "QAbstractFileEngine::entryList"; +const char QAbstractFileEngineError[] = "QAbstractFileEngine::error"; +const char QAbstractFileEngineErrorString[] = "QAbstractFileEngine::errorString"; +const char QAbstractFileEngineFileFlags[] = "QAbstractFileEngine::fileFlags"; +const char QAbstractFileEngineFileName[] = "QAbstractFileEngine::fileName"; +const char QAbstractFileEngineFlush[] = "QAbstractFileEngine::flush"; +const char QAbstractFileEngineHandle[] = "QAbstractFileEngine::handle"; +const char QAbstractFileEngineIsRelativePath[] = "QAbstractFileEngine::isRelativePath"; +const char QAbstractFileEngineIsSequential[] = "QAbstractFileEngine::isSequential"; +const char QAbstractFileEngineLink[] = "QAbstractFileEngine::link"; +const char QAbstractFileEngineMkdir[] = "QAbstractFileEngine::mkdir"; +const char QAbstractFileEngineOpen[] = "QAbstractFileEngine::open"; +const char QAbstractFileEngineOwner[] = "QAbstractFileEngine::owner"; +const char QAbstractFileEngineOwnerId[] = "QAbstractFileEngine::ownerId"; +const char QAbstractFileEnginePos[] = "QAbstractFileEngine::pos"; +const char QAbstractFileEngineRead[] = "QAbstractFileEngine::read"; +const char QAbstractFileEngineReadLine[] = "QAbstractFileEngine::readLine"; +const char QAbstractFileEngineRemove[] = "QAbstractFileEngine::remove"; +const char QAbstractFileEngineRename[] = "QAbstractFileEngine::rename"; +const char QAbstractFileEngineRmdir[] = "QAbstractFileEngine::rmdir"; +const char QAbstractFileEngineSeek[] = "QAbstractFileEngine::seek"; +const char QAbstractFileEngineSetFileName[] = "QAbstractFileEngine::setFileName"; +const char QAbstractFileEngineSetPermissions[] = "QAbstractFileEngine::setPermissions"; +const char QAbstractFileEngineSetSize[] = "QAbstractFileEngine::setSize"; +const char QAbstractFileEngineSize[] = "QAbstractFileEngine::size"; +const char QAbstractFileEngineSupportsExtension[] = "QAbstractFileEngine::supportsExtension"; +const char QAbstractFileEngineExtension[] = "QAbstractFileEngine::extension"; +const char QAbstractFileEngineWrite[] = "QAbstractFileEngine::write"; + +} // namespace Protocol +} // namespace QInstaller + +#endif // PROTOCOL_H diff --git a/src/libs/installer/remoteclient.cpp b/src/libs/installer/remoteclient.cpp new file mode 100644 index 00000000..60ccb340 --- /dev/null +++ b/src/libs/installer/remoteclient.cpp @@ -0,0 +1,160 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "remoteclient.h" + +#include "protocol.h" +#include "remoteclient_p.h" + +#include +#include + +namespace QInstaller { + +RemoteClient::RemoteClient() + : d_ptr(new RemoteClientPrivate(this)) +{ + Q_D(RemoteClient); + d->m_key = QUuid::createUuid().toString(); +} + +RemoteClient::~RemoteClient() +{ + Q_D(RemoteClient); + d->m_quit = true; +} + +RemoteClient &RemoteClient::instance() +{ + static RemoteClient instance; + return instance; +} + +QString RemoteClient::authorizationKey() const +{ + Q_D(const RemoteClient); + return d->m_key; +} + +void RemoteClient::setAuthorizationKey(const QString &key) +{ + Q_D(RemoteClient); + if (d->m_serverStarted) + return; + d->m_key = key; +} + +void RemoteClient::init(quint16 port, const QHostAddress &address, Mode mode) +{ + Q_D(RemoteClient); + d->init(port, address, mode); +} + +QTcpSocket *RemoteClient::connect() const +{ + Q_D(const RemoteClient); + if (d->m_quit) + return 0; + + int tries = 3; + QScopedPointer socket(new QTcpSocket); + while ((tries > 0) && (!d->m_quit)) { + socket->connectToHost(d->m_address, d->m_port); + + QElapsedTimer stopWatch; + stopWatch.start(); + while ((socket->state() == QAbstractSocket::ConnectingState) + && (stopWatch.elapsed() < 10000) && (!d->m_quit)) { + --tries; + qApp->processEvents(); + continue; + } + if ((socket->state() != QAbstractSocket::ConnectedState) || d->m_quit) + return 0; + + QDataStream stream; + stream.setDevice(socket.data()); + stream << QString::fromLatin1(Protocol::Authorize) << d->m_key; + + socket->waitForBytesWritten(-1); + if (!socket->bytesAvailable()) + socket->waitForReadyRead(-1); + + quint32 size; stream >> size; + bool authorized; stream >> authorized; + if (authorized && (!d->m_quit)) + return socket.take(); + } + return 0; +} + +bool RemoteClient::isActive() const +{ + Q_D(const RemoteClient); + return d->m_active; +} + +void RemoteClient::setActive(bool active) +{ + Q_D(RemoteClient); + d->m_active = active; + if (d->m_active) { + d->maybeStartServer(); + d->m_active = d->m_serverStarted; + } +} + +void RemoteClient::setStartServerCommand(const QString &command, StartAs startAs) +{ + setStartServerCommand(command, QStringList(), startAs); +} + +void RemoteClient::setStartServerCommand(const QString &command, const QStringList &arguments, + StartAs startAs) +{ + Q_D(RemoteClient); + d->maybeStopServer(); + d->m_serverCommand = command; + d->m_serverArguments = arguments; + d->m_startServerAsAdmin = startAs; +} + +} // namespace QInstaller diff --git a/src/libs/installer/remoteclient.h b/src/libs/installer/remoteclient.h new file mode 100644 index 00000000..ff92a3dd --- /dev/null +++ b/src/libs/installer/remoteclient.h @@ -0,0 +1,98 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef REMOTECLIENT_H +#define REMOTECLIENT_H + +#include "installer_global.h" + +#include + +QT_BEGIN_NAMESPACE +class QHostAddress; +class QTcpSocket; +QT_END_NAMESPACE + +namespace QInstaller { + +class RemoteClientPrivate; + +class INSTALLER_EXPORT RemoteClient : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(RemoteClient) + Q_DECLARE_PRIVATE(RemoteClient) + +public: + enum Mode { + Debug, + Release + }; + + enum StartAs { + User, + Administrator + }; + static RemoteClient &instance(); + + QTcpSocket *connect() const; + void init(quint16 port, const QHostAddress &address, Mode mode); + + QString authorizationKey() const; + void setAuthorizationKey(const QString &key); + + bool isActive() const; + void setActive(bool active); + + void setStartServerCommand(const QString &command, StartAs startAs); + void setStartServerCommand(const QString &command, const QStringList &arguments, StartAs start); + +private: + RemoteClient(); + ~RemoteClient(); + +private: + QScopedPointer d_ptr; +}; + +} // namespace QInstaller + +#endif // REMOTECLIENT_H diff --git a/src/libs/installer/remoteclient_p.h b/src/libs/installer/remoteclient_p.h new file mode 100644 index 00000000..5f7dc0be --- /dev/null +++ b/src/libs/installer/remoteclient_p.h @@ -0,0 +1,240 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef REMOTECLIENT_P_H +#define REMOTECLIENT_P_H + +#include "adminauthorization.h" +#include "messageboxhandler.h" +#include "protocol.h" +#include "remoteclient.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QInstaller { + +class KeepAliveObject : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(KeepAliveObject) + +public: + KeepAliveObject(RemoteClient *client) + : m_timer(0) + , m_client(client) + { + } + +public slots: + void run() + { + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); + m_timer->start(1000); + } + +private slots: + void onTimeout() + { + m_timer->stop(); + + if (!m_client) + return; + + { + // Try to connect to the server. If we succeed the server side running watchdog gets + // restarted and the server keeps running for another 30 seconds. + QScopedPointer socket(m_client->connect()); + } + + m_timer->start(1000); + } + +private: + QTimer *m_timer; + QPointer m_client; +}; + +class RemoteClientPrivate +{ + Q_DECLARE_PUBLIC(RemoteClient) + Q_DISABLE_COPY(RemoteClientPrivate) + +public: + RemoteClientPrivate(RemoteClient *parent) + : q_ptr(parent) + , m_mutex(QMutex::Recursive) + , m_port(0) + , m_startServerAsAdmin(false) + , m_serverStarted(false) + , m_serverStarting(false) + , m_active(false) + , m_quit(false) + { + } + + ~RemoteClientPrivate() + { + m_thread.quit(); + m_thread.wait(); + } + + void init(quint16 port, const QHostAddress &address, RemoteClient::Mode mode) + { + m_port = port; + m_mode = mode; + m_address = address; + + if (m_mode == RemoteClient::Release) { + QObject *const object = new KeepAliveObject(q_ptr); + object->moveToThread(&m_thread); + QObject::connect(&m_thread, SIGNAL(finished()), object, SLOT(deleteLater())); + m_thread.start(); + QTimer::singleShot(0, object, SLOT(run())); + } else if (mode == RemoteClient::Debug) { + m_active = true; + m_serverStarted = true; + m_key = QLatin1String(Protocol::DebugAuthorizationKey); + } else { + Q_ASSERT_X(false, Q_FUNC_INFO, "RemoteClient mode not set properly."); + } + } + + void maybeStartServer() { + if (m_serverStarted || m_serverCommand.isEmpty()) + return; + + const QMutexLocker ml(&m_mutex); + if (m_serverStarted) + return; + + m_serverStarting = true; + + if (m_startServerAsAdmin) { + AdminAuthorization auth; + m_serverStarted = auth.authorize() && auth.execute(0, m_serverCommand, m_serverArguments); + + if (!m_serverStarted) { + // something went wrong with authorizing, either user pressed cancel or entered + // wrong password + const QString fallback = m_serverCommand + QLatin1String(" ") + m_serverArguments + .join(QLatin1String(" ")); + + const QMessageBox::Button res = + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), + QObject::tr("Authorization Error"), QObject::tr("Could not get authorization."), + QObject::tr("Could not get authorization that is needed for continuing the " + "installation.\n Either abort the installation or use the fallback " + "solution by running\n%1\nas root and then clicking ok.").arg(fallback), + QMessageBox::Abort | QMessageBox::Ok, QMessageBox::Ok); + + if (res == QMessageBox::Ok) + m_serverStarted = true; + } + } else { + m_serverStarted = QInstaller::startDetached(m_serverCommand, m_serverArguments, + QCoreApplication::applicationDirPath()); + } + + if (m_serverStarted) { + QElapsedTimer t; + t.start(); + // 30 seconds ought to be enough for the app to start + while (m_serverStarting && m_serverStarted && t.elapsed() < 30000) { + Q_Q(RemoteClient); + QScopedPointer socket(q->connect()); + if (socket) + m_serverStarting = false; + } + } + m_serverStarting = false; + } + + void maybeStopServer() + { + if (!m_serverStarted) + return; + + const QMutexLocker ml(&m_mutex); + if (!m_serverStarted) + return; + + Q_Q(RemoteClient); + QScopedPointer socket(q->connect()); + if (socket) { + QDataStream stream(socket.data()); + stream << QString::fromLatin1(Protocol::Authorize); + stream << m_key; + stream << QString::fromLatin1(Protocol::Shutdown); + socket->flush(); + } + m_serverStarted = false; + } + +private: + RemoteClient *q_ptr; + QMutex m_mutex; + QHostAddress m_address; + quint16 m_port; + QString m_socket; + bool m_startServerAsAdmin; + bool m_serverStarted; + bool m_serverStarting; + bool m_active; + QString m_serverCommand; + QStringList m_serverArguments; + QString m_key; + QThread m_thread; + RemoteClient::Mode m_mode; + volatile bool m_quit; +}; + +} // namespace QInstaller + +#endif // REMOTECLIENT_P_H diff --git a/src/libs/installer/remotefileengine.cpp b/src/libs/installer/remotefileengine.cpp new file mode 100644 index 00000000..776f8301 --- /dev/null +++ b/src/libs/installer/remotefileengine.cpp @@ -0,0 +1,519 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "remotefileengine.h" + +#include "protocol.h" +#include "remoteclient.h" + +#include + +namespace QInstaller { + + +// -- RemoteFileEngineHandler + +QAbstractFileEngine* RemoteFileEngineHandler::create(const QString &fileName) const +{ + if (!RemoteClient::instance().isActive()) + return 0; + + static QRegExp re(QLatin1String("^[a-z0-9]*://.*$")); + if (re.exactMatch(fileName)) // stuff like installer:// + return 0; + + if (fileName.isEmpty() || fileName.startsWith(QLatin1String(":"))) + return 0; // empty filename or Qt resource + + QScopedPointer client(new RemoteFileEngine()); + client->setFileName(fileName); + if (client->isConnectedToServer()) + return client.take(); + return 0; +} + + +// -- RemoteFileEngineIterator + +class RemoteFileEngineIterator : public QAbstractFileEngineIterator +{ +public: + RemoteFileEngineIterator(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; +}; + + +// -- RemoteFileEngine + +RemoteFileEngine::RemoteFileEngine() + : RemoteObject(QLatin1String(Protocol::QAbstractFileEngine)) +{ +} + +RemoteFileEngine::~RemoteFileEngine() +{ +} + +/*! + \reimp +*/ +bool RemoteFileEngine::atEnd() const +{ + if ((const_cast(this))->connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineAtEnd)); + return m_fileEngine.atEnd(); +} + +/*! + \reimp +*/ +QAbstractFileEngine::Iterator* RemoteFileEngine::beginEntryList(QDir::Filters filters, + const QStringList &filterNames) +{ + QStringList entries = entryList(filters, filterNames); + entries.removeAll(QString()); + return new RemoteFileEngineIterator(filters, filterNames, entries); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::caseSensitive() const +{ + if ((const_cast(this))->connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineCaseSensitive)); + return m_fileEngine.caseSensitive(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::close() +{ + if (connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineClose)); + return m_fileEngine.close(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::copy(const QString &newName) +{ + if (connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineCopy), newName); + return m_fileEngine.copy(newName); +} + +/*! + \reimp +*/ +QStringList RemoteFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + if ((const_cast(this))->connectToServer()) { + return callRemoteMethod + (QString::fromLatin1(Protocol::QAbstractFileEngineEntryList), static_cast(filters), + filterNames); + } + return m_fileEngine.entryList(filters, filterNames); +} + +/*! + \reimp +*/ +QFile::FileError RemoteFileEngine::error() const +{ + if ((const_cast(this))->connectToServer()) { + return static_cast + (callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineError))); + } + return m_fileEngine.error(); +} + +/*! + \reimp +*/ +QString RemoteFileEngine::errorString() const +{ + if ((const_cast(this))->connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineErrorString)); + return m_fileEngine.errorString(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::extension(Extension extension, const ExtensionOption *eo, ExtensionReturn *er) +{ + return false; + if (extension == UnMapExtension || extension == MapExtension) + return false; + + if (connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineExtension), + static_cast (extension)); + } + return m_fileEngine.extension(extension, eo, er); +} + +/*! + \reimp +*/ +QAbstractFileEngine::FileFlags RemoteFileEngine::fileFlags(FileFlags type) const +{ + if ((const_cast(this))->connectToServer()) { + return static_cast + (callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineFileFlags), + static_cast(type))); + } + return m_fileEngine.fileFlags(type); +} + +/*! + \reimp +*/ +QString RemoteFileEngine::fileName(FileName file) const +{ + if ((const_cast(this))->connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineFileName), + static_cast(file)); + } + return m_fileEngine.fileName(file); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::flush() +{ + if (connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineFlush)); + return m_fileEngine.flush(); +} + +/*! + \reimp +*/ +int RemoteFileEngine::handle() const +{ + if ((const_cast(this))->connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineHandle)); + return m_fileEngine.handle(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::isRelativePath() const +{ + if ((const_cast(this))->connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineIsRelativePath)); + return m_fileEngine.isRelativePath(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::isSequential() const +{ + if ((const_cast(this))->connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineIsSequential)); + return m_fileEngine.isSequential(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::link(const QString &newName) +{ + if (connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineLink), + newName); + } + return m_fileEngine.link(newName); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const +{ + if ((const_cast(this))->connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineMkdir), + dirName, createParentDirectories); + } + return m_fileEngine.mkdir(dirName, createParentDirectories); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::open(QIODevice::OpenMode mode) +{ + if (connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineOpen), + static_cast(mode)); + } + return m_fileEngine.open(mode); +} + +/*! + \reimp +*/ +QString RemoteFileEngine::owner(FileOwner owner) const +{ + if ((const_cast(this))->connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineOwner), + static_cast(owner)); + } + return m_fileEngine.owner(owner); +} + +/*! + \reimp +*/ +uint RemoteFileEngine::ownerId(FileOwner owner) const +{ + if ((const_cast(this))->connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineOwnerId), + static_cast(owner)); + } + return m_fileEngine.ownerId(owner); +} + +/*! + \reimp +*/ +qint64 RemoteFileEngine::pos() const +{ + if ((const_cast(this))->connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEnginePos)); + return m_fileEngine.pos(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::remove() +{ + if (connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineRemove)); + return m_fileEngine.remove(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::rename(const QString &newName) +{ + if (connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineRename), + newName); + } + return m_fileEngine.rename(newName); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const +{ + if ((const_cast(this))->connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineRmdir), + dirName, recurseParentDirectories); + } + return m_fileEngine.rmdir(dirName, recurseParentDirectories); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::seek(qint64 offset) +{ + if (connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSeek), offset); + return m_fileEngine.seek(offset); +} + +/*! + \reimp +*/ +void RemoteFileEngine::setFileName(const QString &fileName) +{ + if (connectToServer()) { + callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName, + dummy); + } + m_fileEngine.setFileName(fileName); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::setPermissions(uint perms) +{ + if (connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSetPermissions), + perms); + } + return m_fileEngine.setPermissions(perms); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::setSize(qint64 size) +{ + if (connectToServer()) { + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSetSize), + size); + } + return m_fileEngine.setSize(size); +} + +/*! + \reimp +*/ +qint64 RemoteFileEngine::size() const +{ + if ((const_cast(this))->connectToServer()) + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSize)); + return m_fileEngine.size(); +} + +/*! + \reimp +*/ +bool RemoteFileEngine::supportsExtension(Extension extension) const +{ + return false; + if (extension == UnMapExtension || extension == MapExtension) + return false; + return m_fileEngine.supportsExtension(extension); +} + +/*! + \reimp +*/ +qint64 RemoteFileEngine::read(char *data, qint64 maxlen) +{ + if (!connectToServer()) + return m_fileEngine.read(data, maxlen); + + QPair result = callRemoteMethod > + (QString::fromLatin1(Protocol::QAbstractFileEngineRead), maxlen); + + if (result.first <= 0) + return result.first; + + QDataStream dataStream(result.second); + dataStream.readRawData(data, result.first); + return result.first; +} + +/*! + \reimp +*/ +qint64 RemoteFileEngine::readLine(char *data, qint64 maxlen) +{ + if (!connectToServer()) + return m_fileEngine.readLine(data, maxlen); + + QPair result = callRemoteMethod > + (QString::fromLatin1(Protocol::QAbstractFileEngineReadLine), maxlen); + + if (result.first <= 0) + return result.first; + + QDataStream dataStream(result.second); + dataStream.readRawData(data, result.first); + return result.first; +} + +/*! + \reimp +*/ +qint64 RemoteFileEngine::write(const char *data, qint64 len) +{ + if (!connectToServer()) + return m_fileEngine.write(data, len); + + QByteArray ba(data, len); + return callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineWrite), ba); +} + +} // namespace QInstaller diff --git a/src/libs/installer/remotefileengine.h b/src/libs/installer/remotefileengine.h new file mode 100644 index 00000000..3e57d963 --- /dev/null +++ b/src/libs/installer/remotefileengine.h @@ -0,0 +1,109 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef REMOTEFILEENGINE_H +#define REMOTEFILEENGINE_H + +#include "remoteobject.h" + +#include +#include + +namespace QInstaller { + +class INSTALLER_EXPORT RemoteFileEngineHandler : public QAbstractFileEngineHandler +{ + Q_DISABLE_COPY(RemoteFileEngineHandler) + +public: + RemoteFileEngineHandler() : QAbstractFileEngineHandler() {} + QAbstractFileEngine* create(const QString &fileName) const Q_DECL_OVERRIDE; +}; + +class RemoteFileEngine : public RemoteObject, public QAbstractFileEngine +{ + Q_DISABLE_COPY(RemoteFileEngine) + +public: + RemoteFileEngine(); + ~RemoteFileEngine(); + + 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: + QFSFileEngine m_fileEngine; +}; + +} // namespace QInstaller + +#endif // REMOTEFILEENGINE_H diff --git a/src/libs/installer/remoteobject.cpp b/src/libs/installer/remoteobject.cpp new file mode 100644 index 00000000..2c1aa275 --- /dev/null +++ b/src/libs/installer/remoteobject.cpp @@ -0,0 +1,105 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "remoteobject.h" + +#include "protocol.h" +#include "remoteclient.h" + +namespace QInstaller { + +RemoteObject::RemoteObject(const QString &wrappedType, QObject *parent) + : QObject(parent) + , m_socket(0) + , dummy(0) + , m_type(wrappedType) +{ + Q_ASSERT_X(!m_type.isEmpty(), Q_FUNC_INFO, "The wrapped Qt type needs to be passed as " + "argument and cannot be empty."); +} + +RemoteObject::~RemoteObject() +{ + if (m_socket) { + m_stream << QString::fromLatin1(Protocol::Destroy) << m_type; + m_socket->waitForBytesWritten(-1); + } +} + +bool RemoteObject::connectToServer(const QVariantList &arguments) +{ + if (!RemoteClient::instance().isActive()) + return false; + + if (m_socket && (m_socket->state() == QAbstractSocket::ConnectedState)) + return true; + + if (m_socket) + m_socket->deleteLater(); + + m_socket = RemoteClient::instance().connect(); + if (!m_socket) + return false; + + m_stream.setDevice(m_socket); + m_stream << QString::fromLatin1(Protocol::Create) << m_type; + foreach (const QVariant &arg, arguments) + m_stream << arg; + m_socket->waitForBytesWritten(-1); + + return true; +} + +bool RemoteObject::isConnectedToServer() const +{ + if ((!m_socket) || (!RemoteClient::instance().isActive())) + return false; + if (m_socket && (m_socket->state() == QAbstractSocket::ConnectedState)) + return true; + return false; +} + +void RemoteObject::callRemoteMethod(const QString &name) +{ + writeData(name, dummy, dummy, dummy); +} + +} // namespace QInstaller diff --git a/src/libs/installer/remoteobject.h b/src/libs/installer/remoteobject.h new file mode 100644 index 00000000..4a0a2173 --- /dev/null +++ b/src/libs/installer/remoteobject.h @@ -0,0 +1,166 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef REMOTEOBJECT_H +#define REMOTEOBJECT_H + +#include "errors.h" +#include "installer_global.h" + +#include +#include +#include + +namespace QInstaller { + +class INSTALLER_EXPORT RemoteObject : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(RemoteObject) + +public: + RemoteObject(const QString &wrappedType, QObject *parent = 0); + virtual ~RemoteObject() = 0; + + bool isConnectedToServer() const; + void callRemoteMethod(const QString &name); + + template + void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2) + { + writeData(name, arg, arg2, dummy); + } + + template + void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 & arg3) + { + writeData(name, arg, arg2, arg3); + } + + template + T callRemoteMethod(const QString &name) const + { + return callRemoteMethod(name, dummy, dummy, dummy); + } + + template + T callRemoteMethod(const QString &name, const T1 &arg) const + { + return callRemoteMethod(name, arg, dummy, dummy); + } + + template + T callRemoteMethod(const QString &name, const T1 & arg, const T2 &arg2) const + { + return callRemoteMethod(name, arg, arg2, dummy); + } + + template + T callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const + { + writeData(name, arg, arg2, arg3); + if (!m_socket->bytesAvailable()) + m_socket->waitForReadyRead(-1); + + quint32 size; m_stream >> size; + while (m_socket->bytesAvailable() < size) { + if (!m_socket->waitForReadyRead(30000)) { + throw Error(tr("Could not read all data after sending command: %1. " + "Bytes expected: %2, Bytes received: %3. Error: %3").arg(name).arg(size) + .arg(m_socket->bytesAvailable()).arg(m_socket->errorString())); + } + } + + T result; + m_stream >> result; + return result; + } + +protected: + bool connectToServer(const QVariantList &arguments = QVariantList()); + + // Use this structure to allow derived classes to manipulate the template + // function signature of the callRemoteMethod templates, since most of the + // generated functions will differ in return type rather given arguments. + struct Dummy {}; Dummy *dummy; + +private: + template bool isValueType(T) const + { + return true; + } + + template bool isValueType(T *dummy) const + { + // Force compiler error while passing anything different then Dummy* to the function. + // It really doesn't make sense to send any pointer over to the server, so bail early. + static_cast (dummy); + return false; + } + + template + void writeData(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const + { + QByteArray data; + QDataStream out(&data, QIODevice::WriteOnly); + + if (isValueType(arg)) + out << arg; + if (isValueType(arg2)) + out << arg2; + if (isValueType(arg3)) + out << arg3; + + m_stream << name; + m_stream << quint32(data.size()); + m_stream << data; + m_socket->waitForBytesWritten(-1); + } + +private: + QString m_type; + QTcpSocket *m_socket; + mutable QDataStream m_stream; +}; + +} // namespace QInstaller + +#endif // REMOTEOBJECT_H diff --git a/src/libs/installer/remoteserver.cpp b/src/libs/installer/remoteserver.cpp new file mode 100644 index 00000000..2bd74c03 --- /dev/null +++ b/src/libs/installer/remoteserver.cpp @@ -0,0 +1,122 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "remoteserver.h" + +#include "protocol.h" +#include "remoteserver_p.h" + +namespace QInstaller { + +RemoteServer::RemoteServer(QObject *parent) + : QObject(parent) + , d_ptr(new RemoteServerPrivate(this)) +{ +} + +RemoteServer::~RemoteServer() +{ + Q_D(RemoteServer); + d->m_thread.quit(); + d->m_thread.wait(); +} + +/*! + Starts the server. If started in debug mode, the watchdog that kills the server after + 30 seconds without usage is not started. The authorization key is set to "DebugMode". +*/ +void RemoteServer::start() +{ + Q_D(RemoteServer); + if (d->m_tcpServer) + return; + + d->m_tcpServer = new TcpServer(d->m_port, d->m_address, this); + d->m_tcpServer->moveToThread(&d->m_thread); + connect(&d->m_thread, SIGNAL(finished()), d->m_tcpServer, SLOT(deleteLater())); + connect (d->m_tcpServer, SIGNAL(newIncommingConnection()), this, SLOT(restartWatchdog())); + d->m_thread.start(); + + if (d->m_mode == RemoteServer::Release) { + connect(d->m_watchdog.data(), SIGNAL(timeout()), this, SLOT(deleteLater())); + d->m_watchdog->start(); + } else { + setAuthorizationKey(QLatin1String(Protocol::DebugAuthorizationKey)); + } +} + +/*! + Returns the authorization key. +*/ +QString RemoteServer::authorizationKey() const +{ + Q_D(const RemoteServer); + return d->m_key; +} + +/*! + Sets the authorization key \a authorizationKey this server is asking the clients for. +*/ +void RemoteServer::setAuthorizationKey(const QString &authorizationKey) +{ + Q_D(RemoteServer); + d->m_key = authorizationKey; +} + +void RemoteServer::init(quint16 port, const QHostAddress &address, Mode mode) +{ + Q_D(RemoteServer); + d->m_port = port; + d->m_address = address; + d->m_mode = mode; +} + +/*! + Restarts the watchdog that tries to kill the server. +*/ +void RemoteServer::restartWatchdog() +{ + Q_D(RemoteServer); + if (d->m_watchdog) + d->m_watchdog->start(); +} + +} // namespace QInstaller diff --git a/src/libs/installer/remoteserver.h b/src/libs/installer/remoteserver.h new file mode 100644 index 00000000..e1158dd6 --- /dev/null +++ b/src/libs/installer/remoteserver.h @@ -0,0 +1,87 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef REMOTESERVER_H +#define REMOTESERVER_H + +#include "installer_global.h" + +#include + +QT_BEGIN_NAMESPACE +class QHostAddress; +QT_END_NAMESPACE + +namespace QInstaller { + +class RemoteServerPrivate; + +class INSTALLER_EXPORT RemoteServer : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(RemoteServer) + Q_DECLARE_PRIVATE(RemoteServer) + +public: + enum Mode { + Debug, + Release + }; + + explicit RemoteServer(QObject *parent = 0); + ~RemoteServer(); + + void start(); + void init(quint16 port, const QHostAddress &address, Mode mode); + + QString authorizationKey() const; + void setAuthorizationKey(const QString &key); + +private slots: + void restartWatchdog(); + +private: + QScopedPointer d_ptr; +}; + +} // namespace QInstaller + +#endif // REMOTESERVER_H diff --git a/src/libs/installer/remoteserver_p.h b/src/libs/installer/remoteserver_p.h new file mode 100644 index 00000000..72ee0267 --- /dev/null +++ b/src/libs/installer/remoteserver_p.h @@ -0,0 +1,123 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef REMOTESERVER_P_H +#define REMOTESERVER_P_H + +#include "remoteserver.h" +#include "remoteserverconnection.h" + +#include +#include +#include + +namespace QInstaller { + +class TcpServer : public QTcpServer +{ + Q_OBJECT + Q_DISABLE_COPY(TcpServer) + +public: + TcpServer(quint16 port, const QHostAddress &address, RemoteServer *server) + : QTcpServer(0) + , m_port(port) + , m_address(address) + , m_server(server) + { + listen(m_address, m_port); + } + + ~TcpServer() { + const QList threads = findChildren(); + foreach (QThread *thread, threads) { + thread->quit(); + thread->wait(); + } + } + +signals: + void newIncommingConnection(); + +private: + void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE { + QThread *const thread = new RemoteServerConnection(socketDescriptor, m_server); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); + emit newIncommingConnection(); + } + +private: + quint16 m_port; + QHostAddress m_address; + QPointer m_server; +}; + +class RemoteServerPrivate +{ + Q_DECLARE_PUBLIC(RemoteServer) + Q_DISABLE_COPY(RemoteServerPrivate) + +public: + explicit RemoteServerPrivate(RemoteServer *server) + : q_ptr(server) + , m_tcpServer(0) + , m_watchdog(new QTimer) + { + m_watchdog->setInterval(30000); + m_watchdog->setSingleShot(true); + } + +private: + RemoteServer *q_ptr; + TcpServer *m_tcpServer; + + QString m_key; + quint16 m_port; + QThread m_thread; + QHostAddress m_address; + RemoteServer::Mode m_mode; + QScopedPointer m_watchdog; +}; + +} // namespace QInstaller + +#endif // REMOTESERVER_P_H diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp new file mode 100644 index 00000000..a3c3eda5 --- /dev/null +++ b/src/libs/installer/remoteserverconnection.cpp @@ -0,0 +1,525 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "remoteserverconnection.h" + +#include "errors.h" +#include "protocol.h" +#include "remoteserver.h" +#include "remoteserverconnection_p.h" +#include "utils.h" + +#include +#include + +namespace QInstaller { + +RemoteServerConnection::RemoteServerConnection(qintptr socketDescriptor, RemoteServer *parent) + : m_socketDescriptor(socketDescriptor) + , m_process(0) + , m_settings(0) + , m_engine(0) + , m_server(parent) + , m_signalReceiver(0) +{ +} + +void RemoteServerConnection::run() +{ + QTcpSocket socket; + socket.setSocketDescriptor(m_socketDescriptor); + + QDataStream stream; + stream.setDevice(&socket); + + bool authorized = false; + while (socket.state() == QAbstractSocket::ConnectedState) { + // Use a polling approach here to kill the thread as soon as the connections + // closes. This seems to be related to the fact that the keep alive thread connects + // every second and immediately throws away the socket and therefor the connection. + if (!socket.bytesAvailable() && !socket.waitForReadyRead(250)) + continue; + + QString command; + stream >> command; + + if (authorized && command == QLatin1String(Protocol::Shutdown)) { + // this is a graceful shutdown + socket.close(); + if (m_server) + m_server->deleteLater(); + return; + } else if (command == QLatin1String(Protocol::Authorize)) { + QString key; + stream >> key; + sendData(stream, (authorized = (m_server && (key == m_server->authorizationKey())))); + if (!authorized) + socket.close(); + } else if (authorized) { + if (command.isEmpty()) + continue; + + if (command == QLatin1String(Protocol::Create)) { + QString type; + stream >> type; + if (type == QLatin1String(Protocol::QSettings)) { + QVariant application; + QVariant organization; + QVariant scope, format; + QVariant fileName; + stream >> application; stream >> organization; stream >> scope; stream >> format; + stream >> fileName; + + if (m_settings) + m_settings->deleteLater(); + if (fileName.toString().isEmpty()) { + m_settings = new QSettings(QSettings::Format(format.toInt()), + QSettings::Scope(scope.toInt()), organization.toString(), application + .toString()); + } else { + m_settings = new QSettings(fileName.toString(), QSettings::NativeFormat); + } + } else if (type == QLatin1String(Protocol::QProcess)) { + if (m_process) + m_process->deleteLater(); + m_process = new QProcess; + m_signalReceiver = new QProcessSignalReceiver(m_process); + } else if (type == QLatin1String(Protocol::QAbstractFileEngine)) { + if (m_engine) + delete m_engine; + m_engine = new QFSFileEngine; + } + continue; + } + + if (command == QLatin1String(Protocol::Destroy)) { + QString type; + stream >> type; + if (type == QLatin1String(Protocol::QSettings)) { + m_settings->deleteLater(); + m_settings = 0; + } else if (command == QLatin1String(Protocol::QProcess)) { + m_signalReceiver->m_receivedSignals.clear(); + m_process->deleteLater(); + m_process = 0; + } else if (command == QLatin1String(Protocol::QAbstractFileEngine)) { + delete m_engine; + m_engine = 0; + } + continue; + } + + if (command == QLatin1String(Protocol::GetQProcessSignals)) { + if (m_signalReceiver) { + QMutexLocker _(&m_signalReceiver->m_lock); + sendData(stream, m_signalReceiver->m_receivedSignals); + m_signalReceiver->m_receivedSignals.clear(); + } + continue; + } + + if (command.startsWith(QLatin1String(Protocol::QProcess))) { + handleQProcess(command, stream); + } else if (command.startsWith(QLatin1String(Protocol::QSettings))) { + handleQSettings(command, stream); + } else if (command.startsWith(QLatin1String(Protocol::QAbstractFileEngine))) { + handleQFSFileEngine(command, stream); + } else { + qDebug() << "Unknown command:" << command; + } + } else { + // authorization failed, connection not wanted + socket.close(); + qDebug() << "Unknown command:" << command; + return; + } + } +} + +template +void RemoteServerConnection::sendData(QDataStream &stream, const T &data) +{ + QByteArray result; + QDataStream returnStream(&result, QIODevice::WriteOnly); + returnStream << data; + + stream << static_cast (result.size()); + if (!result.isEmpty()) + stream.writeRawData(result.data(), result.size()); +} + +void RemoteServerConnection::handleQProcess(const QString &command, QDataStream &stream) +{ + quint32 size; + stream >> size; + while (stream.device()->bytesAvailable() < size) { + if (!stream.device()->waitForReadyRead(30000)) { + throw Error(tr("Could not read all data after sending command: %1. " + "Bytes expected: %2, Bytes received: %3. Error: %3").arg(command).arg(size) + .arg(stream.device()->bytesAvailable()).arg(stream.device()->errorString())); + } + } + + QByteArray ba; + stream >> ba; + QDataStream data(ba); + + if (command == QLatin1String(Protocol::QProcessCloseWriteChannel)) { + m_process->closeWriteChannel(); + } else if (command == QLatin1String(Protocol::QProcessExitCode)) { + sendData(stream, m_process->exitCode()); + } else if (command == QLatin1String(Protocol::QProcessExitStatus)) { + sendData(stream, static_cast (m_process->exitStatus())); + } else if (command == QLatin1String(Protocol::QProcessKill)) { + m_process->kill(); + } else if (command == QLatin1String(Protocol::QProcessReadAll)) { + sendData(stream, m_process->readAll()); + } else if (command == QLatin1String(Protocol::QProcessReadAllStandardOutput)) { + sendData(stream, m_process->readAllStandardOutput()); + } else if (command == QLatin1String(Protocol::QProcessReadAllStandardError)) { + sendData(stream, m_process->readAllStandardError()); + } else if (command == QLatin1String(Protocol::QProcessStartDetached)) { + QString program; + QStringList arguments; + QString workingDirectory; + data >> program; + data >> arguments; + data >> workingDirectory; + + qint64 pid = -1; + bool success = QInstaller::startDetached(program, arguments, workingDirectory, &pid); + sendData(stream, qMakePair< bool, qint64>(success, pid)); + } else if (command == QLatin1String(Protocol::QProcessSetWorkingDirectory)) { + QString dir; + data >> dir; + m_process->setWorkingDirectory(dir); + } else if (command == QLatin1String(Protocol::QProcessSetEnvironment)) { + QStringList env; + data >> env; + m_process->setEnvironment(env); + } else if (command == QLatin1String(Protocol::QProcessEnvironment)) { + sendData(stream, m_process->environment()); + } else if (command == QLatin1String(Protocol::QProcessStart3Arg)) { + QString program; + QStringList arguments; + int mode; + data >> program; + data >> arguments; + data >> mode; + m_process->start(program, arguments, static_cast (mode)); + } else if (command == QLatin1String(Protocol::QProcessStart2Arg)) { + QString program; + int mode; + data >> program; + data >> mode; + m_process->start(program, static_cast (mode)); + } else if (command == QLatin1String(Protocol::QProcessState)) { + sendData(stream, static_cast (m_process->state())); + } else if (command == QLatin1String(Protocol::QProcessTerminate)) { + m_process->terminate(); + } else if (command == QLatin1String(Protocol::QProcessWaitForFinished)) { + int msecs; + data >> msecs; + sendData(stream, m_process->waitForFinished(msecs)); + } else if (command == QLatin1String(Protocol::QProcessWaitForStarted)) { + int msecs; + data >> msecs; + sendData(stream, m_process->waitForStarted(msecs)); + } else if (command == QLatin1String(Protocol::QProcessWorkingDirectory)) { + sendData(stream, m_process->workingDirectory()); + } else if (command == QLatin1String(Protocol::QProcessErrorString)) { + sendData(stream, m_process->errorString()); + } else if (command == QLatin1String(Protocol::QProcessReadChannel)) { + sendData(stream, static_cast (m_process->readChannel())); + } else if (command == QLatin1String(Protocol::QProcessSetReadChannel)) { + int processChannel; + data >> processChannel; + m_process->setReadChannel(static_cast(processChannel)); + } else if (command == QLatin1String(Protocol::QProcessWrite)) { + QByteArray byteArray; + data >> byteArray; + sendData(stream, m_process->write(byteArray)); + } else if (command == QLatin1String(Protocol::QProcessProcessChannelMode)) { + sendData(stream, static_cast (m_process->processChannelMode())); + } else if (command == QLatin1String(Protocol::QProcessSetProcessChannelMode)) { + int processChannel; + data >> processChannel; + m_process->setProcessChannelMode(static_cast(processChannel)); + } +#ifdef Q_OS_WIN + else if (command == QLatin1String(Protocol::QProcessSetNativeArguments)) { + QString arguments; + data >> arguments; + m_process->setNativeArguments(arguments); + } +#endif + else if (!command.isEmpty()) { + qDebug() << "Unknown QProcess command:" << command; + } +} + +void RemoteServerConnection::handleQSettings(const QString &command, QDataStream &stream) +{ + quint32 size; + stream >> size; + while (stream.device()->bytesAvailable() < size) { + if (!stream.device()->waitForReadyRead(30000)) { + throw Error(tr("Could not read all data after sending command: %1. " + "Bytes expected: %2, Bytes received: %3. Error: %3").arg(command).arg(size) + .arg(stream.device()->bytesAvailable()).arg(stream.device()->errorString())); + } + } + + QByteArray ba; + stream >> ba; + QDataStream data(ba); + + if (command == QLatin1String(Protocol::QSettingsAllKeys)) { + sendData(stream, m_settings->allKeys()); + } else if (command == QLatin1String(Protocol::QSettingsBeginGroup)) { + QString prefix; + data >> prefix; + m_settings->beginGroup(prefix); + } else if (command == QLatin1String(Protocol::QSettingsBeginWriteArray)) { + QString prefix; + data >> prefix; + int size; + data >> size; + m_settings->beginWriteArray(prefix, size); + } else if (command == QLatin1String(Protocol::QSettingsBeginReadArray)) { + QString prefix; + data >> prefix; + sendData(stream, m_settings->beginReadArray(prefix)); + } else if (command == QLatin1String(Protocol::QSettingsChildGroups)) { + sendData(stream, m_settings->childGroups()); + } else if (command == QLatin1String(Protocol::QSettingsChildKeys)) { + sendData(stream, m_settings->childKeys()); + } else if (command == QLatin1String(Protocol::QSettingsClear)) { + m_settings->clear(); + } else if (command == QLatin1String(Protocol::QSettingsContains)) { + QString key; + data >> key; + sendData(stream, m_settings->contains(key)); + } else if (command == QLatin1String(Protocol::QSettingsEndArray)) { + m_settings->endArray(); + } else if (command == QLatin1String(Protocol::QSettingsEndGroup)) { + m_settings->endGroup(); + } else if (command == QLatin1String(Protocol::QSettingsFallbacksEnabled)) { + sendData(stream, m_settings->fallbacksEnabled()); + } else if (command == QLatin1String(Protocol::QSettingsFileName)) { + sendData(stream, m_settings->fileName()); + } else if (command == QLatin1String(Protocol::QSettingsGroup)) { + sendData(stream, m_settings->group()); + } else if (command == QLatin1String(Protocol::QSettingsIsWritable)) { + sendData(stream, m_settings->isWritable()); + } else if (command == QLatin1String(Protocol::QSettingsRemove)) { + QString key; + data >> key; + m_settings->remove(key); + } else if (command == QLatin1String(Protocol::QSettingsSetArrayIndex)) { + int i; + data >> i; + m_settings->setArrayIndex(i); + } else if (command == QLatin1String(Protocol::QSettingsSetFallbacksEnabled)) { + bool b; + data >> b; + m_settings->setFallbacksEnabled(b); + } else if (command == QLatin1String(Protocol::QSettingsStatus)) { + sendData(stream, m_settings->status()); + } else if (command == QLatin1String(Protocol::QSettingsSync)) { + m_settings->sync(); + } else if (command == QLatin1String(Protocol::QSettingsSetValue)) { + QString key; + QVariant value; + data >> key; + data >> value; + m_settings->setValue(key, value); + } else if (command == QLatin1String(Protocol::QSettingsValue)) { + QString key; + QVariant defaultValue; + data >> key; + data >> defaultValue; + sendData(stream, m_settings->value(key, defaultValue)); + } else if (command == QLatin1String(Protocol::QSettingsOrganizationName)) { + sendData(stream, m_settings->organizationName()); + } else if (command == QLatin1String(Protocol::QSettingsApplicationName)) { + sendData(stream, m_settings->applicationName()); + } else if (!command.isEmpty()) { + qDebug() << "Unknown QSettings command:" << command; + } +} + +void RemoteServerConnection::handleQFSFileEngine(const QString &command, QDataStream &stream) +{ + quint32 size; + stream >> size; + while (stream.device()->bytesAvailable() < size) { + if (!stream.device()->waitForReadyRead(30000)) { + throw Error(tr("Could not read all data after sending command: %1. " + "Bytes expected: %2, Bytes received: %3. Error: %3").arg(command).arg(size) + .arg(stream.device()->bytesAvailable()).arg(stream.device()->errorString())); + } + } + + QByteArray ba; + stream >> ba; + QDataStream data(ba); + + if (command == QLatin1String(Protocol::QAbstractFileEngineAtEnd)) { + sendData(stream, m_engine->atEnd()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineCaseSensitive)) { + sendData(stream, m_engine->caseSensitive()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineClose)) { + sendData(stream, m_engine->close()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineCopy)) { + QString newName; + data >>newName; + sendData(stream, m_engine->copy(newName)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineEntryList)) { + int filters; + QStringList filterNames; + data >>filters; + data >>filterNames; + sendData(stream, m_engine->entryList(static_cast (filters), filterNames)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineError)) { + sendData(stream, static_cast (m_engine->error())); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineErrorString)) { + sendData(stream, m_engine->errorString()); + } + else if (command == QLatin1String(Protocol::QAbstractFileEngineFileFlags)) { + int flags; + data >>flags; + sendData(stream, + static_cast(m_engine->fileFlags(static_cast(flags)))); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineFileName)) { + int file; + data >>file; + sendData(stream, m_engine->fileName(static_cast (file))); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineFlush)) { + sendData(stream, m_engine->flush()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineHandle)) { + sendData(stream, m_engine->handle()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineIsRelativePath)) { + sendData(stream, m_engine->isRelativePath()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineIsSequential)) { + sendData(stream, m_engine->isSequential()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineLink)) { + QString newName; + data >>newName; + sendData(stream, m_engine->link(newName)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineMkdir)) { + QString dirName; + bool createParentDirectories; + data >>dirName; + data >>createParentDirectories; + sendData(stream, m_engine->mkdir(dirName, createParentDirectories)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineOpen)) { + int openMode; + data >>openMode; + sendData(stream, m_engine->open(static_cast (openMode))); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineOwner)) { + int owner; + data >>owner; + sendData(stream, m_engine->owner(static_cast (owner))); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineOwnerId)) { + int owner; + data >>owner; + sendData(stream, m_engine->ownerId(static_cast (owner))); + } else if (command == QLatin1String(Protocol::QAbstractFileEnginePos)) { + sendData(stream, m_engine->pos()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineRead)) { + qint64 maxlen; + data >> maxlen; + QByteArray byteArray(maxlen, '\0'); + const qint64 r = m_engine->read(byteArray.data(), maxlen); + sendData(stream, qMakePair(r, byteArray)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineReadLine)) { + qint64 maxlen; + data >> maxlen; + QByteArray byteArray(maxlen, '\0'); + const qint64 r = m_engine->readLine(byteArray.data(), maxlen); + sendData(stream, qMakePair(r, byteArray)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineRemove)) { + sendData(stream, m_engine->remove()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineRename)) { + QString newName; + data >>newName; + sendData(stream, m_engine->rename(newName)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineRmdir)) { + QString dirName; + bool recurseParentDirectories; + data >>dirName; + data >>recurseParentDirectories; + sendData(stream, m_engine->rmdir(dirName, recurseParentDirectories)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineSeek)) { + quint64 offset; + data >>offset; + sendData(stream, m_engine->seek(offset)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineSetFileName)) { + QString fileName; + data >>fileName; + m_engine->setFileName(fileName); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineSetPermissions)) { + uint perms; + data >>perms; + sendData(stream, m_engine->setPermissions(perms)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineSetSize)) { + qint64 size; + data >>size; + sendData(stream, m_engine->setSize(size)); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineSize)) { + sendData(stream, m_engine->size()); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineSupportsExtension)) { + // Implemented client side + } else if (command == QLatin1String(Protocol::QAbstractFileEngineExtension)) { + qint32 extension; + data >>extension; + sendData(stream, m_engine->extension(static_cast (extension))); + } else if (command == QLatin1String(Protocol::QAbstractFileEngineWrite)) { + QByteArray content; + data >> content; + sendData(stream, m_engine->write(content.data(), content.size())); + } else if (!command.isEmpty()) { + qDebug() << "Unknown QAbstractFileEngine command:" << command; + } +} + +} // namespace QInstaller diff --git a/src/libs/installer/remoteserverconnection.h b/src/libs/installer/remoteserverconnection.h new file mode 100644 index 00000000..65f24717 --- /dev/null +++ b/src/libs/installer/remoteserverconnection.h @@ -0,0 +1,89 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef REMOTESERVERCONNECTION_H +#define REMOTESERVERCONNECTION_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QProcess; +class QSettings; +QT_END_NAMESPACE + +namespace QInstaller { + +class QProcessSignalReceiver; +class RemoteServer; + +class RemoteServerConnection : public QThread +{ + Q_OBJECT + Q_DISABLE_COPY(RemoteServerConnection) + +public: + RemoteServerConnection(qintptr socketDescriptor, RemoteServer *parent); + + void run() Q_DECL_OVERRIDE; + +private: + template + void sendData(QDataStream &stream, const T &arg); + void handleQProcess(const QString &command, QDataStream &receivedStream); + void handleQSettings(const QString &command, QDataStream &receivedStream); + void handleQFSFileEngine(const QString &command, QDataStream &receivedStream); + +private: + qintptr m_socketDescriptor; + + QProcess *m_process; + QSettings *m_settings; + QFSFileEngine *m_engine; + QPointer m_server; + QProcessSignalReceiver *m_signalReceiver; +}; + +} // namespace QInstaller + +#endif // REMOTESERVERCONNECTION_H diff --git a/src/libs/installer/remoteserverconnection_p.h b/src/libs/installer/remoteserverconnection_p.h new file mode 100644 index 00000000..6c975377 --- /dev/null +++ b/src/libs/installer/remoteserverconnection_p.h @@ -0,0 +1,140 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef REMOTESERVERCONNECTION_P_H +#define REMOTESERVERCONNECTION_P_H + +#include "protocol.h" + +#include +#include +#include + +namespace QInstaller { + +class QProcessSignalReceiver : public QObject +{ + Q_OBJECT + friend class RemoteServerConnection; + +private: + explicit QProcessSignalReceiver(QProcess *process) + : QObject(process) + { + connect(process, SIGNAL(bytesWritten(qint64)), SLOT(onBytesWritten(qint64))); + connect(process, SIGNAL(aboutToClose()), SLOT(onAboutToClose())); + connect(process, SIGNAL(readChannelFinished()), SLOT(onReadChannelFinished())); + connect(process, SIGNAL(error(QProcess::ProcessError)), + SLOT(onError(QProcess::ProcessError))); + connect(process, SIGNAL(readyReadStandardOutput()), SLOT(onReadyReadStandardOutput())); + connect(process, SIGNAL(readyReadStandardError()), SLOT(onReadyReadStandardError())); + connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), + SLOT(onFinished(int, QProcess::ExitStatus))); + connect(process, SIGNAL(readyRead()), SLOT(onReadyRead())); + connect(process, SIGNAL(started()), SLOT(onStarted())); + connect(process, SIGNAL(stateChanged(QProcess::ProcessState)), + SLOT(onStateChanged(QProcess::ProcessState))); + } + +private Q_SLOTS: + void onBytesWritten(qint64 count) { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalBytesWritten)); + m_receivedSignals.append(count); + } + + void onAboutToClose() { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalAboutToClose)); + } + + void onReadChannelFinished() { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalReadChannelFinished)); + } + + void onError(QProcess::ProcessError error) { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalError)); + m_receivedSignals.append(static_cast (error)); + } + + void onReadyReadStandardOutput() { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalReadyReadStandardOutput)); + } + + void onReadyReadStandardError() { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalReadyReadStandardError)); + } + + void onFinished(int exitCode, QProcess::ExitStatus exitStatus) { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalFinished)); + m_receivedSignals.append(exitCode); + m_receivedSignals.append(static_cast (exitStatus)); + } + + void onReadyRead() { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalReadyRead)); + } + + void onStarted() { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalStarted)); + } + + void onStateChanged(QProcess::ProcessState newState) { + QMutexLocker _(&m_lock); + m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalStateChanged)); + m_receivedSignals.append(static_cast(newState)); + } + +private: + QMutex m_lock; + QVariantList m_receivedSignals; +}; + +} // namespace QInstaller + +#endif // REMOTESERVERCONNECTION_P_H diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp index 61af9ae2..b348da8c 100644 --- a/src/libs/installer/utils.cpp +++ b/src/libs/installer/utils.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. @@ -84,6 +84,55 @@ void QInstaller::uiDetachedWait(int ms) } while (timer.elapsed() < ms); } +/*! + Starts the program \a program with the arguments \a arguments in a new process, and detaches + from it. Returns true on success; otherwise returns false. If the calling process exits, the + detached process will continue to live. + + Note that arguments that contain spaces are not passed to the process as separate arguments. + + Unix: The started process will run in its own session and act like a daemon. + Windows: Arguments that contain spaces are wrapped in quotes. The started process will run as + a regular standalone process. + + The process will be started in the directory \a workingDirectory. + + If the function is successful then \a *pid is set to the process identifier of the started + process. + + Additional note: The difference in using this function over its equivalent from QProcess + appears on Windows. While this function will truly detach and not show a GUI + window for the started process, the QProcess version will. +*/ +bool QInstaller::startDetached(const QString &program, const QStringList &arguments, + const QString &workingDirectory, qint64 *pid) +{ + bool success = false; +#ifdef Q_OS_WIN + PROCESS_INFORMATION pinfo; + STARTUPINFOW startupInfo = { sizeof(STARTUPINFO), 0, 0, 0, + static_cast(CW_USEDEFAULT), static_cast(CW_USEDEFAULT), + static_cast(CW_USEDEFAULT), static_cast(CW_USEDEFAULT), + 0, 0, 0, STARTF_USESHOWWINDOW, SW_HIDE, 0, 0, 0, 0, 0 + }; // That's the difference over QProcess::startDetached(): STARTF_USESHOWWINDOW, SW_HIDE. + + const QString commandline = QInstaller::createCommandline(program, arguments); + if (CreateProcessW(0, (wchar_t*) commandline.utf16(), + 0, 0, false, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, + 0, workingDirectory.isEmpty() ? 0 : (wchar_t*) workingDirectory.utf16(), + &startupInfo, &pinfo)) { + success = true; + CloseHandle(pinfo.hThread); + CloseHandle(pinfo.hProcess); + if (pid) + *pid = pinfo.dwProcessId; + } +#else + success = QProcess::startDetached(program, arguments, workingDirectory, pid); +#endif + return success; +} + static bool verb = false; void QInstaller::setVerbose(bool v) diff --git a/src/libs/installer/utils.h b/src/libs/installer/utils.h index 2bc7a180..879b29af 100644 --- a/src/libs/installer/utils.h +++ b/src/libs/installer/utils.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. @@ -58,6 +58,8 @@ QT_END_NAMESPACE namespace QInstaller { void INSTALLER_EXPORT uiDetachedWait(int ms); + bool INSTALLER_EXPORT startDetached(const QString &program, const QStringList &arguments, + const QString &workingDirectory, qint64 *pid = 0); QByteArray INSTALLER_EXPORT calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo); QByteArray INSTALLER_EXPORT calculateHash(const QString &path, QCryptographicHash::Algorithm algo); @@ -65,6 +67,7 @@ namespace QInstaller { QString INSTALLER_EXPORT replaceVariables(const QHash &vars, const QString &str); QString INSTALLER_EXPORT replaceWindowsEnvironmentVariables(const QString &str); QStringList INSTALLER_EXPORT parseCommandLineArgs(int argc, char **argv); + #ifdef Q_OS_WIN QString createCommandline(const QString &program, const QStringList &arguments); #endif