summaryrefslogtreecommitdiffstats
path: root/src/client/net/serverconnection.cpp
diff options
context:
space:
mode:
authorsr2013-02-04 19:50:31 +0100
committersr2013-02-04 19:50:31 +0100
commit1a5709501f94014d41987b956338bb6424b9f90c (patch)
treed3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src/client/net/serverconnection.cpp
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src/client/net/serverconnection.cpp')
-rw-r--r--src/client/net/serverconnection.cpp348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/client/net/serverconnection.cpp b/src/client/net/serverconnection.cpp
new file mode 100644
index 0000000..849f43b
--- /dev/null
+++ b/src/client/net/serverconnection.cpp
@@ -0,0 +1,348 @@
+#include "serverconnection.h"
+#include <QtCore>
+#include <QPixmap>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QHostInfo>
+#include <unistd.h>
+#include <cstdlib>
+//#define verbose
+#include "../vnc/vncserver.h"
+
+#include "../../shared/util.h"
+#include "../util/platform/blankscreen.h"
+
+#define CHALLENGE_LEN 20
+
+ServerConnection::ServerConnection(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash) :
+ QObject(NULL), _jpegQuality(80), _authed(0), _sessionName(sessionName), _certHash(certHash)
+{
+ _socket = new QSslSocket();
+ _blank = new BlankScreen();
+ connect(_socket, SIGNAL(encrypted()), this, SLOT(sock_connected()));
+ connect(_socket, SIGNAL(readyRead()), this, SLOT(sock_dataArrival()));
+ connect(_socket, SIGNAL(disconnected()), this, SLOT(sock_closed()));
+ connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sock_error(QAbstractSocket::SocketError)));
+ connect(_socket,
+ SIGNAL(sslErrors(const QList<QSslError> &)),
+ this,
+ SLOT(sslErrors(const QList<QSslError> &))
+ );
+ qDebug("Connecting to %s on port %d", host.toUtf8().data(), (int)port);
+ _socket->connectToHostEncrypted(host, port);
+ _timerId = startTimer(4000);
+ // Connect the vnc start/stop signal to this class, so we can tell the server about successful vnc server startup
+ connect(VncServer::instance(), SIGNAL(started(int, QString&, QString&)), this, SLOT(onVncServerStartStop(int, QString&, QString&)));
+}
+
+ServerConnection::~ServerConnection()
+{
+ this->disconnectFromServer();
+ _blank->deleteLater();
+ _blank = NULL;
+}
+
+void ServerConnection::sendMessage(NetworkMessage& message)
+{
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ return;
+ message.writeMessage(_socket);
+ if (!message.writeComplete())
+ {
+ qCritical() << "SendMessage to server failed!";
+ }
+}
+
+
+void ServerConnection::disconnectFromServer()
+{
+ if (_socket == NULL)
+ return;
+ qDebug("Closing connection to server");
+ VncServer::instance()->stop();
+ emit closeVnc();
+ _socket->blockSignals(true);
+ _socket->abort();
+ _socket->deleteLater();
+ _socket = NULL;
+ this->deleteLater();
+}
+
+void ServerConnection::handleMsg()
+{
+ const QString &id = _fromServer.getFieldString(_ID);
+
+ if (_authed == 0)
+ {
+ if (id == _CHALLENGE)
+ {
+ // Initial challenge request by server
+ emit stateChange(ConnectWindow::AwaitingChallengeResponse);
+ _myChallenge.resize(CHALLENGE_LEN);
+ for (int i = 0; i < CHALLENGE_LEN; ++i)
+ _myChallenge[i] = qrand() & 0xff;
+ QByteArray serverChallenge(_fromServer.getFieldBytes(_CHALLENGE));
+ _toServer.reset();
+ _toServer.setField(_ID, _CHALLENGE);
+ _toServer.setField(_HASH, genSha1(&_sessionName, &serverChallenge));
+ _toServer.setField(_CHALLENGE, _myChallenge);
+ _toServer.writeMessage(_socket);
+ qDebug("Received challenge, replying, sending own challenge step <- 1");
+ _authed = 1;
+ }
+ return;
+ }
+
+ if (_authed == 1)
+ {
+ if (id == _CHALLENGE)
+ {
+ qDebug("Received challenge reply");
+ if (_timerId != 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ }
+ // Check challenge response
+ QByteArray serverhash(_fromServer.getFieldBytes(_HASH));
+ if (serverhash != genSha1(&_sessionName, &_myChallenge))
+ {
+ qDebug("invalid. STOP.");
+ emit stateChange(ConnectWindow::InvalidSslHash);
+ this->disconnectFromServer();
+ return;
+ }
+ emit stateChange(ConnectWindow::LoggingIn);
+ char *user = getlogin();
+ if (user == NULL || *user == '\0')
+ user = getenv("USER");
+ if (user == NULL || *user == '\0')
+ user = getenv("USERNAME");
+ if (user == NULL || *user == '\0')
+ user = (char*)"Hans Affe";
+ _toServer.reset();
+ _toServer.setField(_ID, _LOGIN);
+ _toServer.setField("HOST", QHostInfo::localHostName());
+ _toServer.setField("NAME", QString(user));
+ qDebug("Sending login request!");
+ if (_toServer.writeMessage(_socket))
+ {
+ _authed = 2;
+ qDebug("valid, step <- 2");
+ }
+ else
+ {
+ this->disconnectFromServer();
+ }
+ }
+ return;
+ }
+
+ if (_authed == 2)
+ {
+ if (id == _LOGIN)
+ {
+ qDebug("login accepted, step <- 3");
+ _authed = 3;
+ emit stateChange(ConnectWindow::Connected);
+ }
+ return;
+ }
+
+ // message THUMB - server requests screenshot as thumbnail
+ if (id == _THUMB)
+ {
+ int x = _fromServer.getFieldString(_X).toInt();
+ int y = _fromServer.getFieldString(_Y).toInt();
+ if (x < 32)
+ x = 32;
+ else if (x > 400)
+ x = 400;
+ if (y < 18)
+ y = 18;
+ else if (y > 300)
+ y = 300;
+ QPixmap desktop(QPixmap::grabWindow(QApplication::desktop()->winId()).scaled(
+ x, y,
+ Qt::KeepAspectRatio,
+ Qt::SmoothTransformation));
+ QByteArray bytes;
+ QBuffer jpgBuffer(&bytes);
+ jpgBuffer.open(QIODevice::WriteOnly);
+ qDebug("JPEG quality is %d", _jpegQuality);
+ if (desktop.save(&jpgBuffer, "JPG", _jpegQuality)) // writes pixmap into bytes in JPG format
+ {
+ // Try to adjust quality so we stay between 3 and 4.5 KB
+ if (_jpegQuality < 90 && bytes.size() < 3000)
+ _jpegQuality += 7;
+ else if (_jpegQuality > 40 && bytes.size() > 4500)
+ _jpegQuality -= 7;
+ }
+ else
+ { // FALLBACK
+ bytes.clear();
+ QBuffer pngBuffer(&bytes);
+ pngBuffer.open(QIODevice::WriteOnly);
+ if (!desktop.save(&pngBuffer, "PNG")) // writes pixmap into bytes in PNG format
+ {
+ qDebug("Could not convert screenshot to PNG nor JPG");
+ return; // Failed :-(
+ }
+ }
+ _toServer.reset();
+ _toServer.setField(_ID, _THUMB);
+ _toServer.setField(_IMG, bytes);
+ sendMessage(_toServer);
+ } // message VNCSERVER - start local vncserver
+ else if (id == _VNCSERVER)
+ {
+ const bool enable = (_fromServer.getFieldString("ENABLE").toInt() != 0);
+ if (enable)
+ {
+ emit closeVnc(); // In case we are watching some other client, stop doing so
+ VncServer::instance()->start();
+ }
+ else
+ {
+ VncServer::instance()->stop();
+ }
+ }
+ else if (id == _VNCCLIENT)
+ {
+ const QString host(_fromServer.getFieldString("HOST"));
+ const int port = _fromServer.getFieldString("PORT").toInt();
+ if (host.isEmpty() || port <= 0)
+ {
+ emit closeVnc();
+ }
+ else
+ {
+ emit openVnc(host, port, _fromServer.getFieldString("ROPASS"), true, true, _fromServer.getFieldString("CAPTION"), _fromServer.getFieldString("CLIENTID").toInt());
+ }
+ }
+ else if (id == _LOCK)
+ {
+ const bool enable = (_fromServer.getFieldString("ENABLE").toInt() != 0);
+ if (enable)
+ _blank->lock(_fromServer.getFieldString("MESSAGE"));
+ else
+ _blank->unlock();
+ }
+}
+
+/**
+ * Override
+ */
+
+void ServerConnection::timerEvent(QTimerEvent *event)
+{
+ if (_timerId == 0)
+ return;
+ killTimer(_timerId);
+ _timerId = 0;
+ this->disconnectFromServer();
+}
+
+/**
+ * Slots
+ */
+
+void ServerConnection::onVncServerStartStop(int port, QString& ropass, QString& rwpass)
+{
+ _toServer.reset();
+ _toServer.setField(_ID, _VNCSERVER);
+ if (port <= 0)
+ {
+ _toServer.setField("PORT", QByteArray("0"));
+ }
+ else
+ {
+ _toServer.setField("PORT", QString::number(port));
+ _toServer.setField("ROPASS", ropass);
+ _toServer.setField("RWPASS", rwpass);
+ }
+ sendMessage(_toServer);
+}
+
+void ServerConnection::onVncViewerStartStop(const bool started, const int clientId)
+{
+ _toServer.reset();
+ _toServer.setField(_ID, _VNCCLIENT);
+ if (started)
+ _toServer.setField("ENABLED", QByteArray("1"));
+ else
+ _toServer.setField("ENABLED", QByteArray("0"));
+ _toServer.setField("CLIENTID", QString::number(clientId));
+ sendMessage(_toServer);
+}
+
+void ServerConnection::sslErrors(const QList<QSslError> & errors)
+{
+ for (QList<QSslError>::const_iterator it = errors.begin(); it != errors.end(); it++)
+ {
+ QSslError err = *it;
+ qDebug("Connect SSL: %s", qPrintable(err.errorString()));
+ if (err.error() == QSslError::HostNameMismatch)
+ continue; // We don't pay attention to hostnames for validation
+ if (err.error() == QSslError::SelfSignedCertificate)
+ continue; // Also, this will always be the case; we check the fingerprint later
+ if (err.error() == QSslError::CertificateNotYetValid || err.error() == QSslError::CertificateExpired)
+ continue;
+ // Some other SSL error - do not ignore
+ return;
+ }
+ _socket->ignoreSslErrors();
+}
+
+void ServerConnection::sock_dataArrival()
+{
+ if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
+ {
+ qDebug("dataArrival called in bad state");
+ return;
+ }
+
+ while (_socket->bytesAvailable())
+ {
+ bool retval;
+ retval = _fromServer.readMessage(_socket); // let the message read data from socket
+ if (!retval) // error parsing msg, disconnect client!
+ {
+ this->disconnectFromServer();
+ return;
+ }
+ if (_fromServer.readComplete()) // message is complete
+ {
+ this->handleMsg();
+ if (_socket == NULL)
+ return;
+ _fromServer.reset();
+ }
+ }
+}
+
+void ServerConnection::sock_closed()
+{
+ // should this be unreliable in some way i suggest using the signal "stateChanged()" instead
+ // and check if the state changed to unconnected.
+ qDebug("Socket was closed... oh well..");
+ this->disconnectFromServer();
+}
+
+void ServerConnection::sock_error(QAbstractSocket::SocketError errcode)
+{
+ qDebug("Connection error: %d", (int)errcode);
+ this->disconnectFromServer();
+}
+
+void ServerConnection::sock_connected()
+{
+ QByteArray cert(_socket->peerCertificate().digest(QCryptographicHash::Sha1));
+ if (_certHash != cert)
+ {
+ emit stateChange(ConnectWindow::InvalidCert);
+ this->disconnectFromServer();
+ return;
+ }
+ emit stateChange(ConnectWindow::AwaitingChallenge);
+}