/************************************************************************** ** ** Copyright (C) 2012-2013 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 "fsengineserver.h" #include "utils.h" #include #include #include #include #include #include #if QT_VERSION < 0x050000 # include #else # include #endif typedef int descriptor_t; #ifdef Q_OS_WIN # include #endif bool startDetached(const QString &program, const QStringList &args, const QString &workingDirectory, qint64 *pid) { #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 }; const QString arguments = QInstaller::createCommandline(program, args); const bool success = CreateProcess(0, (wchar_t*)arguments.utf16(), 0, 0, false, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0, (wchar_t*)workingDirectory.utf16(), &startupInfo, &pinfo); if (success) { CloseHandle(pinfo.hThread); CloseHandle(pinfo.hProcess); if (pid) *pid = pinfo.dwProcessId; } return success; #else return QProcess::startDetached(program, args, workingDirectory, pid); #endif } class QProcessSignalReceiver : public QObject { Q_OBJECT public: QProcessSignalReceiver(QObject *parent = 0) : QObject(parent) { connect(parent, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus))); connect(parent, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); connect(parent, SIGNAL(readyRead()), this, SLOT(processReadyRead())); connect(parent, SIGNAL(started()), this, SLOT(processStarted())); connect(parent, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState))); } QList receivedSignals; private Q_SLOTS: void processError(QProcess::ProcessError error); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); void processReadyRead(); void processStarted(); void processStateChanged(QProcess::ProcessState newState); }; /*! \internal */ class FSEngineConnectionThread : public QThread { Q_OBJECT public: FSEngineConnectionThread(descriptor_t socketDescriptor, QObject *parent) : QThread(parent) , descriptor(socketDescriptor) , settings(0) , process(0) , signalReceiver(0) {} protected: void run(); private: QByteArray handleCommand(const QString &command); QFSFileEngine engine; const descriptor_t descriptor; QDataStream receivedStream; QSettings *settings; QProcess *process; QProcessSignalReceiver *signalReceiver; }; FSEngineServer::FSEngineServer(quint16 port, QObject *parent) : QTcpServer(parent) { listen(QHostAddress::LocalHost, port); connect(&watchdog, SIGNAL(timeout()), qApp, SLOT(quit())); watchdog.setSingleShot(true); watchdog.setInterval(30000); watchdog.start(); } FSEngineServer::FSEngineServer(const QHostAddress &address, quint16 port, QObject *parent) : QTcpServer(parent) { listen(address, port); connect(&watchdog, SIGNAL(timeout()), qApp, SLOT(quit())); watchdog.setSingleShot(true); watchdog.setInterval(30000); watchdog.start(); } /*! Destroys the FSEngineServer. */ FSEngineServer::~FSEngineServer() { const QList threads = findChildren(); foreach (QThread *thread, threads) thread->wait(); } /*! \reimp */ void FSEngineServer::incomingConnection(int socketDescriptor) { qApp->processEvents(); QThread *const thread = new FSEngineConnectionThread(socketDescriptor, this); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); watchdog.start(); } void FSEngineServer::enableTestMode() { setAuthorizationKey(QLatin1String("testAuthorizationKey")); //we don't want to kill the server, //maybe we should introduce a call where the client can kill the server watchdog.disconnect(); } /*! Sets the authorization key this server is asking the clients for to \a authorizationKey. */ void FSEngineServer::setAuthorizationKey(const QString &authorizationKey) { key = authorizationKey; } QString FSEngineServer::authorizationKey() const { return key; } void QProcessSignalReceiver::processError(QProcess::ProcessError error) { receivedSignals.push_back(QLatin1String("error")); receivedSignals.push_back(static_cast (error)); } void QProcessSignalReceiver::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { receivedSignals.push_back(QLatin1String("finished")); receivedSignals.push_back(exitCode); receivedSignals.push_back(static_cast (exitStatus)); } void QProcessSignalReceiver::processStarted() { receivedSignals.push_back(QLatin1String("started")); } void QProcessSignalReceiver::processReadyRead() { receivedSignals.push_back(QLatin1String("readyRead")); } void QProcessSignalReceiver::processStateChanged(QProcess::ProcessState newState) { receivedSignals.push_back(QLatin1String("stateChanged")); receivedSignals.push_back(static_cast(newState)); } /*! \reimp */ void FSEngineConnectionThread::run() { QTcpSocket socket; socket.setSocketDescriptor(descriptor); receivedStream.setDevice(&socket); receivedStream.setVersion(QDataStream::Qt_4_2); bool authorized = false; while (static_cast(socket.state()) == QAbstractSocket::ConnectedState) { if (!socket.bytesAvailable() && !socket.waitForReadyRead(250)) continue; QString command; receivedStream >> command; if (authorized && command == QLatin1String("shutdown")) { // this is a graceful shutdown socket.close(); parent()->deleteLater(); return; } else if (command == QLatin1String("authorize")) { QString k; receivedStream >> k; if (k != dynamic_cast (parent())->authorizationKey()) { // this is closing the connection... auth failed socket.close(); return; } authorized = true; } else if (authorized) { if (command.isEmpty()) continue; const QByteArray result = handleCommand(command); receivedStream << static_cast (result.size()); if (!result.isEmpty()) receivedStream.writeRawData(result.data(), result.size()); } else { // authorization failed, connection not wanted socket.close(); return; } } } static QDataStream &operator<<(QDataStream &stream, const QSettings::Status &status) { return stream << static_cast(status); } /*! Handles \a command and returns a QByteArray which has the result streamed into it. */ QByteArray FSEngineConnectionThread::handleCommand(const QString &command) { QByteArray block; QDataStream returnStream(&block, QIODevice::WriteOnly); returnStream.setVersion(QDataStream::Qt_4_2); // first, QSettings handling if (command == QLatin1String("createQSettings")) { QString fileName; receivedStream >> fileName; settings = new QSettings(fileName, QSettings::NativeFormat); } else if (command == QLatin1String("destroyQSettings")) { delete settings; settings = 0; } else if (command == QLatin1String("QSettings::allKeys")) { returnStream << settings->allKeys(); } else if (command == QLatin1String("QSettings::beginGroup")) { QString prefix; receivedStream >> prefix; settings->beginGroup(prefix); } else if (command == QLatin1String("QSettings::beginReadArray")) { QString prefix; int size; receivedStream >> prefix; receivedStream >> size; settings->beginWriteArray(prefix, size); } else if (command == QLatin1String("QSettings::beginWriteArray")) { QString prefix; receivedStream >> prefix; returnStream << settings->beginReadArray(prefix); } else if (command == QLatin1String("QSettings::childGroups")) { returnStream << settings->childGroups(); } else if (command == QLatin1String("QSettings::childKeys")) { returnStream << settings->childKeys(); } else if (command == QLatin1String("QSettings::clear")) { settings->clear(); } else if (command == QLatin1String("QSettings::contains")) { QString key; receivedStream >> key; returnStream << settings->contains(key); } else if (command == QLatin1String("QSettings::endArray")) { settings->endArray(); } else if (command == QLatin1String("QSettings::endGroup")) { settings->endGroup(); } else if (command == QLatin1String("QSettings::fallbacksEnabled")) { returnStream << settings->fallbacksEnabled(); } else if (command == QLatin1String("QSettings::fileName")) { returnStream << settings->fileName(); } else if (command == QLatin1String("QSettings::group")) { returnStream << settings->group(); } else if (command == QLatin1String("QSettings::isWritable")) { returnStream << settings->isWritable(); } else if (command == QLatin1String("QSettings::remove")) { QString key; receivedStream >> key; settings->remove(key); } else if (command == QLatin1String("QSettings::setArrayIndex")) { int i; receivedStream >> i; settings->setArrayIndex(i); } else if (command == QLatin1String("QSettings::setFallbacksEnabled")) { bool b; receivedStream >> b; settings->setFallbacksEnabled(b); } else if (command == QLatin1String("QSettings::status")) { returnStream << settings->status(); } else if (command == QLatin1String("QSettings::sync")) { settings->sync(); } else if (command == QLatin1String("QSettings::setValue")) { QString key; QVariant value; receivedStream >> key; receivedStream >> value; settings->setValue(key, value); } else if (command == QLatin1String("QSettings::value")) { QString key; QVariant defaultValue; receivedStream >> key; receivedStream >> defaultValue; returnStream << settings->value(key, defaultValue); } // from here, QProcess handling else if (command == QLatin1String("createQProcess")) { process = new QProcess; signalReceiver = new QProcessSignalReceiver(process); } else if (command == QLatin1String("destroyQProcess")) { signalReceiver->receivedSignals.clear(); process->deleteLater(); process = 0; } else if (command == QLatin1String("getQProcessSignals")) { returnStream << signalReceiver->receivedSignals; signalReceiver->receivedSignals.clear(); qApp->processEvents(); } else if (command == QLatin1String("QProcess::closeWriteChannel")) { process->closeWriteChannel(); } else if (command == QLatin1String("QProcess::exitCode")) { returnStream << process->exitCode(); } else if (command == QLatin1String("QProcess::exitStatus")) { returnStream << static_cast (process->exitStatus()); } else if (command == QLatin1String("QProcess::kill")) { process->kill(); } else if (command == QLatin1String("QProcess::readAll")) { returnStream << process->readAll(); } else if (command == QLatin1String("QProcess::readAllStandardOutput")) { returnStream << process->readAllStandardOutput(); } else if (command == QLatin1String("QProcess::readAllStandardError")) { returnStream << process->readAllStandardError(); } else if (command == QLatin1String("QProcess::startDetached")) { QString program; QStringList arguments; QString workingDirectory; receivedStream >> program; receivedStream >> arguments; receivedStream >> workingDirectory; qint64 pid; const bool result = startDetached(program, arguments, workingDirectory, &pid); returnStream << qMakePair< bool, qint64> (result, pid); } else if (command == QLatin1String("QProcess::setWorkingDirectory")) { QString dir; receivedStream >> dir; process->setWorkingDirectory(dir); } else if (command == QLatin1String("QProcess::setEnvironment")) { QStringList env; receivedStream >> env; process->setEnvironment(env); } else if (command == QLatin1String("QProcess::start")) { QString program; QStringList arguments; int mode; receivedStream >> program; receivedStream >> arguments; receivedStream >> mode; process->start(program, arguments, static_cast (mode)); } else if (command == QLatin1String("QProcess::state")) { returnStream << static_cast (process->state()); } else if (command == QLatin1String("QProcess::terminate")) { process->terminate(); } else if (command == QLatin1String("QProcess::waitForFinished")) { int msecs; receivedStream >> msecs; returnStream << process->waitForFinished(msecs); } else if (command == QLatin1String("QProcess::waitForStarted")) { int msecs; receivedStream >> msecs; returnStream << process->waitForStarted(msecs); } else if (command == QLatin1String("QProcess::workingDirectory")) { returnStream << process->workingDirectory(); } else if (command == QLatin1String("QProcess::errorString")) { returnStream << process->errorString(); } else if (command == QLatin1String("QProcess::write")) { QByteArray byteArray; receivedStream >> byteArray; returnStream << process->write(byteArray); } else if (command == QLatin1String("QProcess::readChannel")) { returnStream << static_cast (process->readChannel()); } else if (command == QLatin1String("QProcess::setReadChannel")) { int processChannel; receivedStream >> processChannel; process->setReadChannel(static_cast(processChannel)); } else if (command == QLatin1String("QProcess::write")) { QByteArray byteArray; receivedStream >> byteArray; returnStream << process->write(byteArray); } // from here, QFSEngine handling else if (command == QLatin1String("QFSFileEngine::atEnd")) { returnStream << engine.atEnd(); } else if (command == QLatin1String("QFSFileEngine::caseSensitive")) { returnStream << engine.caseSensitive(); } else if (command == QLatin1String("QFSFileEngine::close")) { returnStream << engine.close(); } else if (command == QLatin1String("QFSFileEngine::copy")) { QString newName; receivedStream >> newName; returnStream << engine.copy(newName); } else if (command == QLatin1String("QFSFileEngine::entryList")) { int filters; QStringList filterNames; receivedStream >> filters; receivedStream >> filterNames; returnStream << engine.entryList(static_cast (filters), filterNames); } else if (command == QLatin1String("QFSFileEngine::error")) { returnStream << static_cast (engine.error()); } else if (command == QLatin1String("QFSFileEngine::errorString")) { returnStream << engine.errorString(); } // extension else if (command == QLatin1String("QFSFileEngine::fileFlags")) { int flags; receivedStream >> flags; returnStream << static_cast(engine.fileFlags(static_cast(flags))); } else if (command == QLatin1String("QFSFileEngine::fileName")) { int file; receivedStream >> file; returnStream << engine.fileName(static_cast (file)); } else if (command == QLatin1String("QFSFileEngine::flush")) { returnStream << engine.flush(); } else if (command == QLatin1String("QFSFileEngine::handle")) { returnStream << engine.handle(); } else if (command == QLatin1String("QFSFileEngine::isRelativePath")) { returnStream << engine.isRelativePath(); } else if (command == QLatin1String("QFSFileEngine::isSequential")) { returnStream << engine.isSequential(); } else if (command == QLatin1String("QFSFileEngine::link")) { QString newName; receivedStream >> newName; returnStream << engine.link(newName); } else if (command == QLatin1String("QFSFileEngine::mkdir")) { QString dirName; bool createParentDirectories; receivedStream >> dirName; receivedStream >> createParentDirectories; returnStream << engine.mkdir(dirName, createParentDirectories); } else if (command == QLatin1String("QFSFileEngine::open")) { int openMode; receivedStream >> openMode; returnStream << engine.open(static_cast (openMode)); } else if (command == QLatin1String("QFSFileEngine::owner")) { int owner; receivedStream >> owner; returnStream << engine.owner(static_cast (owner)); } else if (command == QLatin1String("QFSFileEngine::ownerId")) { int owner; receivedStream >> owner; returnStream << engine.ownerId(static_cast (owner)); } else if (command == QLatin1String("QFSFileEngine::pos")) { returnStream << engine.pos(); } else if (command == QLatin1String("QFSFileEngine::read")) { qint64 maxlen; receivedStream >> maxlen; QByteArray ba(maxlen, '\0'); const qint64 result = engine.read(ba.data(), maxlen); returnStream << result; int written = 0; while (written < result) written += returnStream.writeRawData(ba.data() + written, result - written); } else if (command == QLatin1String("QFSFileEngine::readLine")) { qint64 maxlen; receivedStream >> maxlen; QByteArray ba(maxlen, '\0'); const qint64 result = engine.readLine(ba.data(), maxlen); returnStream << result; int written = 0; while (written < result) written += returnStream.writeRawData(ba.data() + written, result - written); } else if (command == QLatin1String("QFSFileEngine::remove")) { returnStream << engine.remove(); } else if (command == QLatin1String("QFSFileEngine::rename")) { QString newName; receivedStream >> newName; returnStream << engine.rename(newName); } else if (command == QLatin1String("QFSFileEngine::rmdir")) { QString dirName; bool recurseParentDirectories; receivedStream >> dirName; receivedStream >> recurseParentDirectories; returnStream << engine.rmdir(dirName, recurseParentDirectories); } else if (command == QLatin1String("QFSFileEngine::seek")) { quint64 offset; receivedStream >> offset; returnStream << engine.seek(offset); } else if (command == QLatin1String("QFSFileEngine::setFileName")) { QString fileName; receivedStream >> fileName; engine.setFileName(fileName); } else if (command == QLatin1String("QFSFileEngine::setPermissions")) { uint perms; receivedStream >> perms; returnStream << engine.setPermissions(perms); } else if (command == QLatin1String("QFSFileEngine::setSize")) { qint64 size; receivedStream >> size; returnStream << engine.setSize(size); } else if (command == QLatin1String("QFSFileEngine::size")) { returnStream << engine.size(); } else if (command == QLatin1String("QFSFileEngine::supportsExtension")) { int extension; receivedStream >> extension; //returnStream << engine.supportsExtension(static_cast (extension)); returnStream << false; } else if (command == QLatin1String("QFSFileEngine::write")) { qint64 length; receivedStream >> length; qint64 read = 0; qint64 written = 0; QByteArray buffer(65536, '\0'); while (read < length) { if (!receivedStream.device()->bytesAvailable()) receivedStream.device()->waitForReadyRead(-1); const qint64 r = receivedStream.readRawData(buffer.data(), qMin(length - read, static_cast (buffer.length()))); read += r; qint64 w = 0; while (w < r) w += engine.write(buffer.data(), r); written += r; } returnStream << written; } else if (!command.isEmpty()) { qDebug() << "unknown command:" << command; } return block; } #include "fsengineserver.moc" #include "moc_fsengineserver.cpp"