summaryrefslogtreecommitdiffstats
path: root/src/server/net/client.cpp
diff options
context:
space:
mode:
authorsr2013-02-04 19:50:31 +0100
committersr2013-02-04 19:50:31 +0100
commit1a5709501f94014d41987b956338bb6424b9f90c (patch)
treed3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/server/net/client.cpp
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src/server/net/client.cpp')
-rw-r--r--src/server/net/client.cpp288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/server/net/client.cpp b/src/server/net/client.cpp
new file mode 100644
index 0000000..ca91e76
--- /dev/null
+++ b/src/server/net/client.cpp
@@ -0,0 +1,288 @@
+/*
+ * client.cpp
+ *
+ * Created on: 18.01.2013
+ * Author: sr
+ */
+
+#include "client.h"
+#include "../util/global.h"
+#include "../../shared/settings.h"
+#include "../../shared/util.h"
+#include <QSslSocket>
+#include <QHostAddress>
+#include <QPixmap>
+#include <cassert>
+
+#define CHALLENGE_LEN 20
+
+ClientId Client::_clientIdCounter = 0;
+
+Client::Client(QSslSocket* socket) :
+ _socket(socket), _authed(0), _desiredProjectionSource(0), _isProjectionSource(false),
+ _currentProjectionSource(0), _vncPort(0), _activeVncClient(false)
+{
+ assert(socket != NULL);
+ _id = ++_clientIdCounter;
+ _pingTimeout = QDateTime::currentMSecsSinceEpoch() + PING_TIMEOUT_MS;
+ _ip = _socket->peerAddress().toString();
+ qDebug("*** Client %s created.", qPrintable(_ip));
+ // Connect important signals
+ connect(_socket, SIGNAL(disconnected()), this, SLOT(onClosed()));
+ connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
+ connect(_socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(onSslErrors(const QList<QSslError> &)));
+ connect(_socket, SIGNAL(readyRead()), this, SLOT(onDataArrival()));
+ // Send challenge
+ _challenge.resize(CHALLENGE_LEN);
+ for (int i = 0; i < CHALLENGE_LEN; ++i)
+ _challenge[i] = qrand() & 0xff;
+ _toClient.reset();
+ _toClient.setField(_ID, _CHALLENGE);
+ _toClient.setField(_CHALLENGE, _challenge);
+ _toClient.writeMessage(_socket);
+ // give client 3 seconds to complete handshake
+ _timerIdAuthTimeout = startTimer(3000);
+}
+
+Client::~Client()
+{
+ if (_socket != NULL)
+ {
+ qDebug("**** DELETE IN DESTRUCTOR");
+ _socket->deleteLater();
+ }
+ qDebug("*** Client %s destroyed.", qPrintable(_ip));
+}
+
+void Client::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == _timerIdAuthTimeout)
+ {
+ // Client did not send login request within 3 seconds
+ killTimer(_timerIdAuthTimeout);
+ _timerIdAuthTimeout = 0;
+ this->disconnect();
+ }
+}
+
+void Client::sendMessage(NetworkMessage& message)
+{
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ return;
+ message.writeMessage(_socket);
+ if (!message.writeComplete())
+ {
+ qCritical() << "SendMessage to client " << _name << "@" << _ip << " failed!";
+ }
+}
+
+void Client::requestThumb(const int width, const int height)
+{
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ {
+ qDebug("requestThumb called in bad state");
+ return;
+ }
+ _toClient.reset();
+ _toClient.setField(_ID, _THUMB);
+ _toClient.setField(_X, QString::number(width));
+ _toClient.setField(_Y, QString::number(height));
+ _toClient.writeMessage(_socket);
+}
+
+void Client::onDataArrival()
+{
+ _pingTimeout = QDateTime::currentMSecsSinceEpoch() + PING_TIMEOUT_MS;
+ //
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ {
+ qDebug("dataArrival called in bad state");
+ return;
+ }
+
+ //qDebug() << _socket->bytesAvailable() << " bytes to read";
+ bool ret;
+ while (_socket->bytesAvailable())
+ {
+ ret = _fromClient.readMessage(_socket); // let the message read data from socket
+ if (!ret) // error parsing msg, disconnect client!
+ {
+ this->disconnect();
+ return;
+ }
+ if (_fromClient.readComplete()) // message is complete
+ {
+ this->handleMsg();
+ if (_socket == NULL)
+ return;
+ _fromClient.reset();
+ }
+ }
+}
+
+void Client::onSslErrors(const QList<QSslError> & errors)
+{
+ this->disconnect();
+}
+
+void Client::onClosed()
+{
+ this->disconnect();
+}
+
+void Client::onError(QAbstractSocket::SocketError errcode)
+{
+ this->disconnect();
+}
+
+void Client::handleMsg()
+{
+ const QString &id = _fromClient.getFieldString(_ID);
+ if (id.isEmpty())
+ {
+ qDebug("Received message with empty ID field. ignored.");
+ return;
+ }
+ qDebug() << "Received message " << id;
+
+ if (_authed == 2)
+ {
+ // Following messages are only valid of the client is already authenticated
+ if (id == _THUMB)
+ {
+ QPixmap pixmap;
+ if (!pixmap.loadFromData(_fromClient.getFieldBytes("IMG")))
+ {
+ qDebug("Could not decode thumbnail image from client.");
+ return;
+ }
+ emit thumbUpdated(this, pixmap);
+ }
+ else if (id == _VNCSERVER)
+ {
+ // Client tells about startup of vnc server
+ const int port = _fromClient.getFieldString("PORT").toInt();
+ if (port <= 0)
+ {
+ if (_vncPort <= 0)
+ {
+ qDebug() << "Starting VNC server on client" << _name << " (" << _ip << ") failed.";
+ // TODO: Show message on manager
+ }
+ }
+ else
+ {
+ _vncRoPass = _fromClient.getFieldString("ROPASS");
+ _vncRwPass = _fromClient.getFieldString("RWPASS");
+ }
+ _vncPort = port;
+ emit vncServerStateChange(this);
+ }
+ else if (id == _VNCCLIENT)
+ {
+ // Client tells us that it started or stopped displaying a remote screen via VNC
+ _activeVncClient = (_fromClient.getFieldString("ENABLED").toInt() != 0);
+ const ClientId other = (ClientId)_fromClient.getFieldString("CLIENTID").toInt();
+
+ if (!_activeVncClient && other == 0)
+ _desiredProjectionSource = 0;
+
+ emit vncClientStateChange(this);
+
+ if (!_activeVncClient)
+ _currentProjectionSource = 0;
+ else
+ _currentProjectionSource = other;
+ }
+ return;
+ }
+
+ if (_authed == 1) // Not authed yet, only care about login requests
+ {
+ if (id == _LOGIN)
+ {
+ killTimer(_timerIdAuthTimeout);
+ _timerIdAuthTimeout = 0;
+ ClientLogin request;
+ request.accept = true;
+ request.host = _fromClient.getFieldString("HOST");
+ request.name = _fromClient.getFieldString("NAME");
+ request.ip = _socket->peerAddress().toString();
+ qDebug() << "Login request by " << request.name;
+ // emit event, see if request is accepted
+ emit authenticating(this, &request);
+ if (!request.accept)
+ {
+ qDebug("Request denied.");
+ this->disconnect(); // Nope
+ return;
+ }
+ // Accepted
+ _authed = 2;
+ qDebug("valid, step <- 2");
+ _name = request.name;
+ _host = request.host;
+ _toClient.reset();
+ _toClient.setField(_ID, _LOGIN);
+ _toClient.writeMessage(_socket);
+ emit authenticated(this);
+ }
+ return;
+ }
+
+ if (_authed == 0)
+ {
+ // Waiting for challenge reply by client
+ if (id == _CHALLENGE)
+ {
+ QByteArray hash(_fromClient.getFieldBytes(_HASH));
+ QByteArray challenge(_fromClient.getFieldBytes(_CHALLENGE));
+ if (genSha1(&Global::sessionNameArray(), &_challenge) != hash)
+ { // Challenge reply is invalid, drop client
+ _toClient.buildErrorMessage("Challenge reply invalid.");
+ _toClient.writeMessage(_socket);
+ this->deleteLater();
+ return;
+ }
+ // Now answer to challenge by client
+ _toClient.reset();
+ _toClient.setField(_ID, _CHALLENGE);
+ _toClient.setField(_HASH, genSha1(&Global::sessionNameArray(), &challenge));
+ _toClient.writeMessage(_socket);
+ _authed = 1;
+ qDebug("client's challenge reply was valid, step <- 1");
+ }
+ return;
+ }
+
+}
+
+void Client::startVncServer()
+{
+ _vncPort = 0;
+ _toClient.reset();
+ _toClient.setField(_ID, _VNCSERVER);
+ _toClient.setField("ENABLE", QByteArray("1"));
+ sendMessage(_toClient);
+}
+
+void Client::stopVncServer()
+{
+ _toClient.reset();
+ _toClient.setField(_ID, _VNCSERVER);
+ _toClient.setField("ENABLE", QByteArray("0"));
+ sendMessage(_toClient);
+}
+
+void Client::disconnect()
+{
+ if (_socket != NULL)
+ {
+ qDebug("*** Client %s disconnected.", qPrintable(_ip));
+ _socket->blockSignals(true);
+ _socket->abort();
+ _socket->deleteLater();
+ _socket = NULL;
+ this->deleteLater();
+ }
+}