summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsr2013-02-04 19:50:31 +0100
committersr2013-02-04 19:50:31 +0100
commit1a5709501f94014d41987b956338bb6424b9f90c (patch)
treed3b93fe8dc406bca56aff147ef5cc4cbf9ed6be0 /src
parentTest (diff)
downloadpvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.gz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.tar.xz
pvs2-1a5709501f94014d41987b956338bb6424b9f90c.zip
Initial commit
Diffstat (limited to 'src')
-rw-r--r--src/client/connectwindow/connectwindow.cpp284
-rw-r--r--src/client/connectwindow/connectwindow.h95
-rw-r--r--src/client/main.cpp41
-rw-r--r--src/client/net/serverconnection.cpp348
-rw-r--r--src/client/net/serverconnection.h62
-rw-r--r--src/client/toolbar/toolbar.cpp200
-rw-r--r--src/client/toolbar/toolbar.h78
-rwxr-xr-xsrc/client/util/platform/blankscreen.h25
-rwxr-xr-xsrc/client/util/platform/blankscreen_Win32.cpp41
-rwxr-xr-xsrc/client/util/platform/blankscreen_X11.cpp75
-rw-r--r--src/client/util/util.cpp25
-rw-r--r--src/client/util/util.h23
-rw-r--r--src/client/vnc/vncserver.cpp191
-rw-r--r--src/client/vnc/vncserver.h52
-rw-r--r--src/client/vnc/vncthread.cpp314
-rw-r--r--src/client/vnc/vncthread.h141
-rw-r--r--src/client/vnc/vncwindow.cpp277
-rw-r--r--src/client/vnc/vncwindow.h73
-rw-r--r--src/server/clicklabel/clicklabel.cpp14
-rw-r--r--src/server/clicklabel/clicklabel.h25
-rw-r--r--src/server/connectionframe/connectionframe.cpp278
-rw-r--r--src/server/connectionframe/connectionframe.h77
-rw-r--r--src/server/main.cpp41
-rw-r--r--src/server/mainwindow/mainwindow.cpp633
-rw-r--r--src/server/mainwindow/mainwindow.h76
-rw-r--r--src/server/net/certmanager.cpp92
-rw-r--r--src/server/net/certmanager.h29
-rw-r--r--src/server/net/client.cpp288
-rw-r--r--src/server/net/client.h104
-rw-r--r--src/server/net/discoverylistener.cpp165
-rw-r--r--src/server/net/discoverylistener.h40
-rw-r--r--src/server/net/listenserver.cpp40
-rw-r--r--src/server/net/listenserver.h30
-rw-r--r--src/server/net/sslserver.cpp113
-rw-r--r--src/server/net/sslserver.h50
-rw-r--r--src/server/sessionnamewindow/sessionnamewindow.cpp68
-rw-r--r--src/server/sessionnamewindow/sessionnamewindow.h38
-rw-r--r--src/server/util/global.cpp17
-rw-r--r--src/server/util/global.h29
-rw-r--r--src/server/util/util.cpp14
-rw-r--r--src/server/util/util.h17
-rw-r--r--src/shared/network.cpp50
-rw-r--r--src/shared/network.h25
-rw-r--r--src/shared/networkmessage.cpp296
-rw-r--r--src/shared/networkmessage.h69
-rw-r--r--src/shared/settings.h12
-rw-r--r--src/shared/util.cpp22
-rw-r--r--src/shared/util.h15
48 files changed, 5112 insertions, 0 deletions
diff --git a/src/client/connectwindow/connectwindow.cpp b/src/client/connectwindow/connectwindow.cpp
new file mode 100644
index 0000000..bc4e9b2
--- /dev/null
+++ b/src/client/connectwindow/connectwindow.cpp
@@ -0,0 +1,284 @@
+/*
+ * connectwindow.cpp
+ *
+ * Created on: 28.01.2013
+ * Author: sr
+ */
+
+#include "connectwindow.h"
+#include "../../shared/settings.h"
+#include "../../shared/network.h"
+#include "../../shared/util.h"
+
+#include "../net/serverconnection.h"
+
+#include <QNetworkInterface>
+
+#define UDPBUFSIZ 9000
+#define SALT_LEN 18
+
+ConnectWindow::ConnectWindow(QWidget *parent) :
+ QDialog(parent), _connected(false), _timerDiscover(0), _timerHide(0), _connection(NULL), _state(Idle),
+ _hashErrorCount(0), _hashSslErrorCount(0), _certErrorCount(0), _ipErrorCount(0), _discoveryInterval(800)
+{
+ setupUi(this);
+ //
+ connect(cmdOK, SIGNAL(clicked()), this, SLOT(onOkClick()));
+ connect(cmdCancel, SIGNAL(clicked()), this, SLOT(onCancelClick()));
+ int tries = 10;
+ while (tries-- != 0)
+ {
+ const quint16 port = (quint16)(qrand() % 10000) + 10000;
+ if (_discoverySocket.bind(QHostAddress::Any, port))
+ break;
+ if (tries == 0)
+ qFatal("Could not bind to any UDP port for server discovery.");
+ }
+ connect(&_discoverySocket, SIGNAL(readyRead()), this, SLOT(onUdpReadyRead()));
+ this->setState(Idle);
+}
+
+ConnectWindow::~ConnectWindow()
+{
+
+}
+
+void ConnectWindow::setConnected(const bool connected)
+{
+ _connected = connected;
+ this->updateState();
+ if (_state == Scanning)
+ {
+ killTimer(_timerDiscover);
+ _discoveryInterval = 1000;
+ _timerDiscover = startTimer(_discoveryInterval);
+ }
+}
+
+void ConnectWindow::setState(const ConnectionState state)
+{
+ if (_state != state)
+ {
+ _state = state;
+ this->updateState();
+ }
+}
+
+void ConnectWindow::updateState()
+{
+ txtName->setEnabled(_state == Idle && !_connected);
+
+ if (_connected)
+ {
+ cmdOK->setEnabled(true);
+ cmdOK->setText(tr("&Disconnect"));
+ lblStatus->setText(tr("Connected."));
+ txtName->setEnabled(false);
+ return;
+ }
+
+ if (_state != Idle)
+ cmdOK->setText(tr("&Stop"));
+ else
+ cmdOK->setText(tr("&Connect"));
+
+ switch (_state)
+ {
+ case Idle:
+ lblStatus->setText(tr("Ready to connect; please enter session name."));
+ break;
+ case Scanning:
+ lblStatus->setText(tr("Scanning for session %1.").arg(txtName->text()));
+ _timerDiscover = startTimer(_discoveryInterval);
+ break;
+ case Connecting:
+ lblStatus->setText(tr("Found session, connecting..."));
+ break;
+ case AwaitingChallenge:
+ lblStatus->setText(tr("Waiting for server challenge..."));
+ break;
+ case AwaitingChallengeResponse:
+ lblStatus->setText(tr("Replied to challenge, sent own..."));
+ break;
+ case LoggingIn:
+ lblStatus->setText(tr("Logging in..."));
+ break;
+ case Connected:
+ lblStatus->setText(tr("Connection established!"));
+ break;
+ case InvalidIpList:
+ case InvalidHash:
+ case InvalidCert:
+ case InvalidSslHash:
+ lblError->setText(tr("Invalid hash: %1; invalid cert: %2; invalid iplist: %3; invalid sslhash: %4")
+ .arg(_hashErrorCount).arg(_certErrorCount).arg(_ipErrorCount).arg(_hashSslErrorCount));
+ break;
+ }
+}
+
+/**
+ * Overrides
+ */
+
+void ConnectWindow::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == _timerDiscover)
+ {
+ killTimer(_timerDiscover);
+ if (_connected || _state != Scanning) // Not scanning, bail out
+ return;
+ if (_discoveryInterval < 30000)
+ _discoveryInterval += 100;
+ _timerDiscover = startTimer(_discoveryInterval);
+ // Don't send packet if we're trying to connect
+ if (_connection != NULL)
+ return;
+ // Send discovery
+ _packet.reset();
+ QByteArray iplist(Network::interfaceAddressesToString().toUtf8());
+ QByteArray salt1(SALT_LEN, 0);
+ if (_salt2.size() < SALT_LEN)
+ _salt2.resize(SALT_LEN);
+ for (int i = 0; i < SALT_LEN; ++i)
+ {
+ salt1[i] = qrand() & 0xff;
+ _salt2[i] = qrand() & 0xff;
+ }
+ _packet.reset();
+ _packet.setField(_HASH, genSha1(&_nameBytes, &salt1, &iplist));
+ _packet.setField(_SALT1, salt1);
+ _packet.setField(_SALT2, _salt2);
+ _packet.setField(_IPLIST, iplist);
+ foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces())
+ {
+ foreach (QNetworkAddressEntry entry, interface.addressEntries())
+ {
+ if (!entry.broadcast().isNull() && entry.ip() != QHostAddress::LocalHost && entry.ip() != QHostAddress::LocalHostIPv6)
+ {
+ qDebug() << "Broadcasting to " << entry.broadcast().toString();
+ if (!_packet.writeMessage(&_discoverySocket, entry.broadcast(), SERVICE_DISCOVERY_PORT))
+ qDebug("FAILED");
+ }
+ }
+ }
+ qDebug("Broadcasting to 255.255.255.255");
+ if (!_packet.writeMessage(&_discoverySocket, QHostAddress::Broadcast, SERVICE_DISCOVERY_PORT))
+ qDebug("FAILED");
+ // End send discovery
+ }
+ else if(event->timerId() == _timerHide)
+ {
+ killTimer(_timerHide);
+ _timerHide = 0;
+ this->hide();
+ }
+ else
+ // Unknown/Old timer id, kill it
+ killTimer(event->timerId());
+}
+
+void ConnectWindow::closeEvent(QCloseEvent *e)
+{
+ e->ignore();
+ this->hide();
+}
+
+/**
+ * Slots
+ */
+
+void ConnectWindow::onOkClick()
+{
+ if (_timerHide)
+ killTimer(_timerHide);
+ _timerHide = 0;
+ if (_timerDiscover)
+ killTimer(_timerDiscover);
+ if (_connected || _state != Idle)
+ {
+ // Stop or disconnect
+ _timerDiscover = 0;
+ emit disconnect();
+ this->setState(Idle);
+ }
+ else
+ {
+ // Connect (scan for session)
+ _discoveryInterval = 800;
+ _nameBytes = txtName->text().toUtf8();
+ _timerDiscover = startTimer(_discoveryInterval);
+ _hashErrorCount = _hashSslErrorCount = _certErrorCount = _ipErrorCount = 0;
+ this->setState(Scanning);
+ }
+}
+
+void ConnectWindow::onCancelClick()
+{
+ this->hide();
+}
+
+void ConnectWindow::onUdpReadyRead()
+{
+ char data[UDPBUFSIZ];
+ QHostAddress addr;
+ quint16 port;
+ while (_discoverySocket.hasPendingDatagrams())
+ {
+ const qint64 size = _discoverySocket.readDatagram(data, UDPBUFSIZ, &addr, &port);
+ if (size <= 0 || _connection != NULL)
+ continue;
+
+ _packet.reset();
+ if (!_packet.readMessage(data, (quint32)size))
+ continue;
+ // Valid packet, process it:
+ const QByteArray hash(_packet.getFieldBytes(_HASH));
+ const QByteArray iplist(_packet.getFieldBytes(_IPLIST));
+ const QByteArray port(_packet.getFieldBytes(_PORT));
+ const QByteArray cert(_packet.getFieldBytes(_CERT));
+ // Check if the source IP of the packet matches any of the addresses given in the IP list
+ if (!Network::isAddressInList(QString::fromUtf8(iplist), addr.toString()))
+ {
+ ++_ipErrorCount;
+ this->setState(InvalidIpList);
+ this->setState(Scanning);
+ continue;
+ }
+ // If so, check if the submitted hash seems valid
+ if (genSha1(&_nameBytes, &_salt2, &iplist, &port, &cert) != hash)
+ {
+ // did not match local session name, or other data was spoofed
+ ++_hashErrorCount;
+ this->setState(InvalidHash);
+ this->setState(Scanning);
+ continue;
+ }
+ // Otherwise it's a valid reply, try to connect
+ _connection = new ServerConnection(addr.toString(), (quint16)QString::fromUtf8(port).toInt(), _nameBytes, cert);
+ connect(_connection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState)));
+ connect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*)));
+ }
+}
+
+void ConnectWindow::onConnectionStateChange(ConnectWindow::ConnectionState state)
+{
+ bool reset = (_state == Scanning);
+ if (state == InvalidSslHash)
+ ++_hashSslErrorCount;
+ this->setState(state);
+ if (reset)
+ _state = Scanning;
+ if (state == Connected)
+ {
+ QObject::disconnect(_connection, SIGNAL(stateChange(ConnectWindow::ConnectionState)), this, SLOT(onConnectionStateChange(ConnectWindow::ConnectionState)));
+ QObject::disconnect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onConnectionClosed(QObject*)));
+ emit connected(_connection);
+ _connection = NULL;
+ _timerHide = startTimer(2000);
+ }
+}
+
+void ConnectWindow::onConnectionClosed(QObject* connection)
+{
+ _connection = NULL;
+}
diff --git a/src/client/connectwindow/connectwindow.h b/src/client/connectwindow/connectwindow.h
new file mode 100644
index 0000000..d8f9ab9
--- /dev/null
+++ b/src/client/connectwindow/connectwindow.h
@@ -0,0 +1,95 @@
+/*
+ # Copyright (c) 2013 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # ---------------------------------------------------------------------
+ # - Allow user to connect/disconnect to/from server
+ */
+
+#ifndef PVSCONNECTWINDOW_H_
+#define PVSCONNECTWINDOW_H_
+
+#include <QtGui>
+#include <QUdpSocket>
+#include <QSslSocket>
+#include "ui_connect.h"
+
+#include "../../shared/networkmessage.h"
+
+class ServerConnection;
+
+class ConnectWindow : public QDialog, private Ui_Dialog
+{
+Q_OBJECT
+
+public:
+ enum ConnectionState
+ {
+ Idle,
+ Scanning,
+ Connecting,
+ AwaitingChallenge,
+ AwaitingChallengeResponse,
+ LoggingIn,
+ InvalidIpList,
+ InvalidHash, // Hash of UDP reply invalid
+ InvalidSslHash, // Hash of challenge inside SSL connection invalid
+ InvalidCert,
+ Connected
+ };
+
+private:
+ bool _connected;
+ int _timerDiscover, _timerHide;
+ ServerConnection *_connection;
+ ConnectionState _state;
+ int _hashErrorCount, _hashSslErrorCount, _certErrorCount, _ipErrorCount;
+ int _discoveryInterval;
+
+ QByteArray _nameBytes;
+ QByteArray _salt2;
+ QUdpSocket _discoverySocket;
+ NetworkMessage _packet;
+
+ void setState(const ConnectionState state);
+ void updateState();
+
+public:
+ ConnectWindow(QWidget *parent = NULL);
+ virtual ~ConnectWindow();
+
+ void setConnected(const bool connected);
+
+protected:
+ /*
+ void enterEvent(QEvent *e);
+ void leaveEvent(QEvent *e);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ */
+ void timerEvent(QTimerEvent* event);
+ void closeEvent(QCloseEvent *e);
+
+protected slots:
+ void onOkClick();
+ void onCancelClick();
+ void onUdpReadyRead();
+ void onConnectionStateChange(ConnectWindow::ConnectionState state);
+ void onConnectionClosed(QObject* connection);
+ //void onSsl
+
+signals:
+ void disconnect();
+ void connected(ServerConnection* connection);
+
+};
+
+#endif
diff --git a/src/client/main.cpp b/src/client/main.cpp
new file mode 100644
index 0000000..cea9ae8
--- /dev/null
+++ b/src/client/main.cpp
@@ -0,0 +1,41 @@
+#include "toolbar/toolbar.h"
+#include "util/util.h"
+
+int main(int argc, char** argv)
+{
+ QApplication app(argc, argv);
+ app.setOrganizationName("openslx");
+ app.setOrganizationDomain("openslx.org");
+ app.setApplicationName("pvsclient");
+
+ qsrand((uint)QDateTime::currentMSecsSinceEpoch());
+
+ // Make sure settings directory exists
+ do {
+ USER_SETTINGS(settings);
+ QFileInfo fi(settings.fileName());
+ QDir path(fi.path());
+ qDebug() << "Settings directory is " << fi.path();
+ if (!path.exists())
+ path.mkpath(path.absolutePath());
+ // Now check if settings file exists. If not, copy system default (if available)
+ if (!fi.exists())
+ {
+ SYSTEM_SETTINGS(sys);
+ QFileInfo sysfi(sys.fileName());
+ if (sysfi.exists())
+ {
+ if (!QFile::copy(sys.fileName(), settings.fileName()))
+ qDebug() << "Copying default settings from " << sys.fileName() << " to " << settings.fileName() << " failed.";
+ }
+ }
+ } while (0);
+
+ // use system locale as language to translate gui
+ QTranslator translator;
+ translator.load(":pvsclient");
+ app.installTranslator(&translator);
+
+ Toolbar pvsclient;
+ return app.exec();
+}
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);
+}
diff --git a/src/client/net/serverconnection.h b/src/client/net/serverconnection.h
new file mode 100644
index 0000000..afb8204
--- /dev/null
+++ b/src/client/net/serverconnection.h
@@ -0,0 +1,62 @@
+#ifndef _PVSSERVERCONNECTION_H_
+#define _PVSSERVERCONNECTION_H_
+
+#include <QSslSocket>
+#include "../../shared/networkmessage.h"
+#include "../connectwindow/connectwindow.h"
+
+class BlankScreen;
+
+class ServerConnection : public QObject
+{
+Q_OBJECT
+
+private:
+ QSslSocket *_socket;
+ BlankScreen *_blank;
+ int _timerId;
+ int _jpegQuality;
+ int _authed;
+
+ NetworkMessage _fromServer, _toServer;
+ QByteArray _expectedFingerprint;
+ QString _passwd;
+ QByteArray _myChallenge;
+ QByteArray _sessionName;
+ QByteArray _certHash;
+
+ void handleMsg();
+
+public:
+ ServerConnection(const QString& host, const quint16 port, const QByteArray& sessionName, const QByteArray& certHash);
+ void disconnectFromServer();
+ ~ServerConnection();
+ const inline bool isConnected() const
+ {
+ return _socket != NULL && _socket->state() == QAbstractSocket::ConnectedState;
+ }
+
+ void sendMessage(NetworkMessage& message);
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+private slots:
+ void sslErrors(const QList<QSslError> & errors); // triggered for errors that occur during SSL negotiation
+ void sock_dataArrival(); // triggered if data is available for reading
+ void sock_closed(); // triggered if the socket is closed
+ void sock_error(QAbstractSocket::SocketError errcode); // triggered if an error occurs on the socket
+ void sock_connected(); // triggered if the connection is established and ready to use
+
+ void onVncServerStartStop(int port, QString& ropass, QString& rwpass); // triggered if the local vnc server was started
+
+ void onVncViewerStartStop(const bool started, const int clientId);
+
+signals:
+ void openVnc(const QString& host, int port, const QString& passwd, bool ro, bool fullscreen, const QString& caption, const int clientId);
+ void closeVnc();
+ void stateChange(ConnectWindow::ConnectionState state);
+
+};
+
+#endif
diff --git a/src/client/toolbar/toolbar.cpp b/src/client/toolbar/toolbar.cpp
new file mode 100644
index 0000000..416a68f
--- /dev/null
+++ b/src/client/toolbar/toolbar.cpp
@@ -0,0 +1,200 @@
+/*
+ * toolbar.cpp
+ *
+ * Created on: 21.01.2013
+ * Author: sr
+ */
+
+#include "toolbar.h"
+#include "../../shared/settings.h"
+#include "../net/serverconnection.h"
+#include "../vnc/vncwindow.h"
+#include "../vnc/vncserver.h"
+
+Toolbar::Toolbar(QWidget *parent) :
+ QWidget(parent), _location(POSITION_TOP_CENTER), _hideTimer(0), _connection(NULL)
+{
+ setupUi(this);
+ setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint);
+ setAttribute(Qt::WA_AlwaysShowToolTips);
+ setAttribute(Qt::WA_QuitOnClose);
+ setVisible(true);
+ // VNC Window
+ _vnc = new VncWindow(NULL);
+ // Connect window
+ _connectWindow = new ConnectWindow(NULL);
+ connect(_connectWindow, SIGNAL(disconnect()), this, SLOT(onDoDisconnect()));
+ connect(_connectWindow, SIGNAL(connected(ServerConnection*)), this, SLOT(onConnected(ServerConnection*)));
+ //
+ setupMenu();
+ setLocation();
+ hideBar();
+}
+
+void Toolbar::setupMenu()
+{
+ _menu = new QMenu(this);
+ // setup actions
+ _acnDisconnect = new QAction(tr("&Connect"), this);
+ //_acnDisconnect->setEnabled(false);
+ _acnQuit = new QAction(tr("&Quit"), this);
+
+ // setup menu
+ _menu->addAction(_acnDisconnect);
+ _menu->addSeparator();
+ _menu->addAction(_acnQuit);
+
+ cmdMenu->setMenu(_menu);
+
+ connect(_acnQuit, SIGNAL(triggered()), this, SLOT(onQuit()));
+ connect(_acnDisconnect, SIGNAL(triggered()), _connectWindow, SLOT(show()));
+}
+
+Toolbar::~Toolbar()
+{
+ VncServer::instance()->stop();
+ _vnc->deleteLater();
+ _connectWindow->deleteLater();
+}
+
+//###########\\/\/
+
+void Toolbar::setLocation()
+{
+ const QDesktopWidget desktop;
+ const QRect primaryScreen = desktop.screenGeometry();
+ switch (_location)
+ {
+ case POSITION_TOP_LEFT:
+ move(primaryScreen.left(), primaryScreen.top());
+ break;
+ case POSITION_TOP_CENTER:
+ move((primaryScreen.width() - this->width()) / 2 + primaryScreen.left(), primaryScreen.top());
+ break;
+ case POSITION_TOP_RIGHT:
+ move(primaryScreen.right() - width(), primaryScreen.top());
+ break;
+ case POSITION_BOTTOM_LEFT:
+ move(primaryScreen.left(), primaryScreen.bottom() - height());
+ break;
+ case POSITION_BOTTOM_CENTER:
+ move((primaryScreen.width() - this->width()) / 2 + primaryScreen.left(), primaryScreen.bottom() - height());
+ break;
+ case POSITION_BOTTOM_RIGHT:
+ move(primaryScreen.right() - width(), primaryScreen.bottom() - height());
+ break;
+ default:
+ break;
+ }
+}
+
+void Toolbar::setBarVisible(bool shown)
+{
+ const QDesktopWidget desktop;
+ const QRect primaryScreen = desktop.screenGeometry();
+ if (!shown)
+ {
+ if (_location <= POSITION_TOP_RIGHT)
+ move(x(), primaryScreen.top() + 2 - height());
+ else
+ move(x(), primaryScreen.bottom() - 2);
+ }
+ else
+ {
+ if (_location <= POSITION_TOP_RIGHT)
+ move(x(), primaryScreen.top());
+ else
+ move(x(), primaryScreen.bottom() - height());
+ }
+}
+
+bool Toolbar::hideBar()
+{
+ if (_menu->isVisible()) // Don't hide window if any menu is open
+ return false;
+ setBarVisible(false);
+ return true;
+}
+
+/**
+ * Override
+ */
+
+void Toolbar::leaveEvent(QEvent* e)
+{
+ if (_hideTimer == 0)
+ _hideTimer = startTimer(100);
+ _hideDelay = 6;
+ QWidget::leaveEvent(e);
+}
+
+void Toolbar::enterEvent(QEvent* e)
+{
+ if (_hideTimer != 0)
+ {
+ killTimer(_hideTimer);
+ _hideTimer = 0;
+ }
+ setBarVisible(true);
+ QWidget::enterEvent(e);
+}
+
+void Toolbar::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == _hideTimer)
+ {
+ if (--_hideDelay <= 0)
+ {
+ if (hideBar())
+ {
+ killTimer(_hideTimer);
+ _hideTimer = 0;
+ }
+ }
+ }
+}
+
+/**
+ * Slots
+ */
+
+void Toolbar::onDisconnected(QObject* connection)
+{
+ if (connection != _connection)
+ qDebug("onDisconnect pointer mismatch!");
+ _connectWindow->setConnected(false);
+ _connection = NULL;
+ lblStatus->setStyleSheet("color:red");
+ lblStatus->setText(tr("Offline"));
+}
+
+void Toolbar::onConnected(ServerConnection* connection)
+{
+ lblStatus->setStyleSheet("color:green");
+ lblStatus->setText(tr("Online"));
+ //
+ if (_connection != NULL)
+ {
+ disconnect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onDisconnected(QObject*)));
+ _connection->blockSignals(true);
+ _connection->disconnectFromServer();
+ }
+ _connection = connection;
+ connect(_connection, SIGNAL(destroyed(QObject*)), this, SLOT(onDisconnected(QObject*)));
+ connect(_connection, SIGNAL(openVnc(const QString&, int, const QString&, bool, bool, const QString&, const int)),
+ _vnc, SLOT(open(const QString&, int, const QString&, bool, bool, const QString&, const int)));
+ connect(_connection, SIGNAL(closeVnc()), _vnc, SLOT(close()));
+ connect(_vnc, SIGNAL(running(const bool, const int)), _connection, SLOT(onVncViewerStartStop(const bool, const int)));
+ _connectWindow->setConnected(true);
+}
+
+void Toolbar::onDoDisconnect()
+{
+ if (_connection != NULL)
+ _connection->disconnectFromServer();
+}
+
+void Toolbar::onQuit()
+{
+ QApplication::exit(0);
+}
diff --git a/src/client/toolbar/toolbar.h b/src/client/toolbar/toolbar.h
new file mode 100644
index 0000000..0ce4d2b
--- /dev/null
+++ b/src/client/toolbar/toolbar.h
@@ -0,0 +1,78 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ */
+
+#ifndef PVSCLIENTGUI_H_
+#define PVSCLIENTGUI_H_
+
+#include <QtGui>
+#include "ui_toolbar.h"
+
+class ServerConnection;
+class VncWindow;
+class ConnectWindow;
+class BlankScreen;
+
+class Toolbar : public QWidget, private Ui_ToolbarClass
+{
+Q_OBJECT
+
+private:
+ int _location;
+ int _hideTimer;
+ int _hideDelay;
+ QMenu *_menu;
+ QAction *_acnDisconnect;
+ QAction *_acnQuit;
+ ServerConnection *_connection;
+
+ ConnectWindow *_connectWindow;
+ VncWindow *_vnc;
+
+ void setLocation();
+ bool hideBar();
+ void setBarVisible(bool shown);
+ void setupMenu();
+
+ void leaveEvent(QEvent* e);
+ void enterEvent(QEvent* e);
+ void timerEvent(QTimerEvent* event);
+
+public:
+ Toolbar(QWidget *parent = NULL);
+ virtual ~Toolbar();
+
+ int const static POSITION_TOP_LEFT = 0;
+ int const static POSITION_TOP_CENTER = 1;
+ int const static POSITION_TOP_RIGHT = 2;
+ int const static POSITION_BOTTOM_LEFT = 3;
+ int const static POSITION_BOTTOM_CENTER = 4;
+ int const static POSITION_BOTTOM_RIGHT = 5;
+
+protected:
+ /*
+ void enterEvent(QEvent *e);
+ void leaveEvent(QEvent *e);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ */
+
+private slots:
+ void onDisconnected(QObject* connection);
+ void onConnected(ServerConnection* connection);
+ void onDoDisconnect();
+ void onQuit();
+
+};
+
+#endif /* PVSCLIENTGUI_H_ */
diff --git a/src/client/util/platform/blankscreen.h b/src/client/util/platform/blankscreen.h
new file mode 100755
index 0000000..8ed4c04
--- /dev/null
+++ b/src/client/util/platform/blankscreen.h
@@ -0,0 +1,25 @@
+#ifndef _BLANKSCREEN_H_
+#define _BLANKSCREEN_H_
+
+#include <QString>
+#include <QDialog>
+
+class BlankScreen_Sysdep;
+
+class BlankScreen : public QDialog
+{
+ Q_OBJECT
+public:
+ BlankScreen();
+ virtual ~BlankScreen();
+ void draw(bool force = false);
+ bool lock(const QString& message);
+ bool unlock();
+
+private:
+ bool _locked;
+ QString _message;
+ BlankScreen_Sysdep* _sysdep;
+};
+
+#endif
diff --git a/src/client/util/platform/blankscreen_Win32.cpp b/src/client/util/platform/blankscreen_Win32.cpp
new file mode 100755
index 0000000..2bba1cf
--- /dev/null
+++ b/src/client/util/platform/blankscreen_Win32.cpp
@@ -0,0 +1,41 @@
+
+#include "blankscreen.h"
+#include <qwidget.h>
+
+struct BlankScreen_Sysdep {
+
+ bool locked;
+ QWidget* blankwin;
+
+ QString lockMsg;
+ int blackColor, whiteColor;
+ int offX, offY;
+};
+
+BlankScreen::BlankScreen()
+{
+ _sysdep = new BlankScreen_Sysdep;
+ _sysdep->blankwin = new QWidget(0, Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
+ _sysdep->blankwin->setWindowState(Qt::WindowFullScreen);
+ _sysdep->blankwin->show();
+}
+
+BlankScreen::~BlankScreen()
+{
+ delete _sysdep;
+}
+
+void BlankScreen::draw(bool force)
+{
+
+}
+
+bool BlankScreen::lock(const QString& message)
+{
+ return true;
+}
+
+bool BlankScreen::unlock()
+{
+ return true;
+}
diff --git a/src/client/util/platform/blankscreen_X11.cpp b/src/client/util/platform/blankscreen_X11.cpp
new file mode 100755
index 0000000..69b6d8c
--- /dev/null
+++ b/src/client/util/platform/blankscreen_X11.cpp
@@ -0,0 +1,75 @@
+#include "blankscreen.h"
+
+#include <QApplication>
+#include <QDesktopWidget>
+
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+
+#include <cassert>
+#include <cstring>
+
+struct BlankScreen_Sysdep
+{
+ Display *dpy;
+};
+
+BlankScreen::BlankScreen() : QDialog(NULL)
+{
+ _sysdep = new BlankScreen_Sysdep;
+ _sysdep->dpy = XOpenDisplay(NULL);
+ if (_sysdep->dpy == NULL)
+ return;
+ assert(_sysdep->dpy);
+
+ setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint);
+ setStyleSheet("background-color:#000");
+
+ _locked = false;
+}
+
+BlankScreen::~BlankScreen()
+{
+ unlock();
+ delete _sysdep;
+}
+
+bool BlankScreen::lock(const QString& message)
+{
+ if (_locked)
+ return true;
+ if (_sysdep->dpy == NULL)
+ return false;
+
+ _message = message;
+
+ this->setGeometry(QApplication::desktop()->geometry());
+ this->showFullScreen();
+ this->activateWindow();
+ this->raise();
+
+ // grabbing of keyboard and mouse
+ XGrabKeyboard(_sysdep->dpy, DefaultRootWindow(_sysdep->dpy) , false, GrabModeAsync, GrabModeAsync, CurrentTime);
+ XGrabPointer(_sysdep->dpy, DefaultRootWindow(_sysdep->dpy) , false, 0, GrabModeAsync, GrabModeAsync, None, 0, CurrentTime);
+
+ _locked = true;
+ return true;
+}
+
+bool BlankScreen::unlock()
+{
+ this->hide();
+ if (!_locked)
+ return true;
+ if (_sysdep->dpy == NULL)
+ return false;
+
+ // ungrabbing of keyboard and mouse
+ XUngrabPointer(_sysdep->dpy, CurrentTime);
+ XUngrabKeyboard(_sysdep->dpy, CurrentTime);
+
+ XFlush(_sysdep->dpy);
+
+ _locked = false;
+ return true;
+}
diff --git a/src/client/util/util.cpp b/src/client/util/util.cpp
new file mode 100644
index 0000000..9dcbebb
--- /dev/null
+++ b/src/client/util/util.cpp
@@ -0,0 +1,25 @@
+/*
+ * Util.cpp
+ *
+ * Created on: 18.01.2013
+ * Author: sr
+ */
+
+#include "util.h"
+#include <QSettings>
+
+namespace Util
+{
+//#
+//#
+QDir settingsDir()
+{
+ USER_SETTINGS(settings);
+ QFileInfo fi(settings.fileName());
+ QDir path(fi.path());
+ return path;
+}
+//#
+//#
+}
+
diff --git a/src/client/util/util.h b/src/client/util/util.h
new file mode 100644
index 0000000..82248c0
--- /dev/null
+++ b/src/client/util/util.h
@@ -0,0 +1,23 @@
+#ifndef UTIL_H_
+#define UTIL_H_
+
+// Helper for getting a settings object in various places, so if you ever change the organization, location,
+// file format or anything, you won't have to edit in 100 places.
+// Use like this:
+// USER_SETTINGS(settings)
+// settings.value("somekey")
+#define USER_SETTINGS(name) QSettings name (QSettings::IniFormat, QSettings::UserScope, "openslx", "pvs2client")
+#define SYSTEM_SETTINGS(name) QSettings name (QSettings::IniFormat, QSettings::SystemScope, "openslx", "pvs2client")
+
+#include <QDir>
+
+namespace Util
+{
+//#
+//#
+QDir settingsDir();
+//#
+//#
+}
+
+#endif /* UTIL_H_ */
diff --git a/src/client/vnc/vncserver.cpp b/src/client/vnc/vncserver.cpp
new file mode 100644
index 0000000..2b49b8e
--- /dev/null
+++ b/src/client/vnc/vncserver.cpp
@@ -0,0 +1,191 @@
+/*
+ * vncserver.cpp
+ *
+ * Created on: 24.01.2013
+ * Author: sr
+ */
+
+#include <QProcess>
+#include "vncserver.h"
+#include "../util/util.h"
+
+/*******************************************
+ * STATIC
+ *******************************************/
+
+VncServer* VncServer::me = NULL;
+
+VncServer* VncServer::instance()
+{
+ if (me == NULL)
+ me = new VncServer();
+ return me;
+}
+
+//
+static QString makePassword(int len = 10)
+{
+ char pass[len];
+ for (int i = 0; i < len; ++i)
+ pass[i] = 43 + qrand() % 80;
+ return QString::fromUtf8(pass, len);
+}
+
+// Ugly hack to get an el-cheapo platform independent sleep
+struct Sleeper : public QThread
+{
+static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
+};
+
+/*******************************************
+ * INSTANCE
+ *******************************************/
+
+VncServer::VncServer() :
+ _process(NULL), _port(0), _timerId(0)
+{
+ // TODO Auto-generated constructor stub
+}
+
+VncServer::~VncServer()
+{
+ // TODO Auto-generated destructor stub
+}
+
+void VncServer::start()
+{
+ // Keep things clean
+ if (_process != NULL)
+ {
+ disconnect(_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
+ disconnect(_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
+ }
+ this->stop();
+ // Generate passwords
+ _rwpass = makePassword();
+ _ropass = makePassword();
+ // Create new password file
+ QDir path = Util::settingsDir();
+ QString pwfile(path.absoluteFilePath("vncpass"));
+ QFile pwhandle(pwfile);
+ if (pwhandle.exists())
+ pwhandle.remove();
+ if (!pwhandle.open(QIODevice::WriteOnly))
+ {
+ qDebug() << "Could not open " << pwfile << " for writing";
+ emit started(0, _ropass, _rwpass);
+ return;
+ }
+ pwhandle.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
+ pwhandle.write(_rwpass.toUtf8().constData());
+ pwhandle.write("\n");
+ pwhandle.write(_ropass.toUtf8().constData());
+ pwhandle.write("\n");
+ pwhandle.close();
+ // Create new process
+ _process = new QProcess(this);
+ connect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStdOut()));
+ connect(_process, SIGNAL(readyReadStandardError()), this, SLOT(onStdErr()));
+ connect(_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
+ connect(_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
+ _timerId = startTimer(4000);
+ QStringList args;
+ args << "-forever";
+ args << "-display" << ":0";
+ args << "-passwdfile" << (QString("rm:" + pwfile));
+ args << "-shared";
+ args << "-autoport" << QString::number(54112);
+ qDebug() << "Arguments are: " << args;
+ _process->start("x11vnc",
+ args,
+ QIODevice::ReadOnly);
+}
+
+void VncServer::stop()
+{
+ if (_timerId != 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ }
+ if (_process == NULL)
+ return;
+ qDebug("Stopping old VNC server.");
+ disconnect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStdOut()));
+ disconnect(_process, SIGNAL(readyReadStandardError()), this, SLOT(onStdErr()));
+ QProcess *process = _process;
+ _process = NULL;
+ _port = 0;
+ process->terminate();
+ for (int i = 0; i < 10 && process->state() != QProcess::NotRunning; ++i)
+ Sleeper::msleep(10);
+ if (process->state() == QProcess::Running)
+ process->kill();
+ for (int i = 0; i < 10 && process->state() != QProcess::NotRunning; ++i)
+ Sleeper::msleep(10);
+ process->deleteLater();
+}
+
+/**
+ * Overrides
+ */
+
+void VncServer::timerEvent(QTimerEvent *event)
+{
+ // Error timeout (3s), tell server that vnc setup failed
+ this->stop();
+ emit started(0, _ropass, _rwpass);
+}
+
+/**
+ * Slots
+ */
+
+void VncServer::onStdOut()
+{
+ if (_process == NULL)
+ {
+ qDebug("VncServer::onStdOut() called in bad state.");
+ return;
+ }
+ QByteArray data(_process->readAllStandardOutput());
+ qDebug() << "x11vnc: " << data;
+ if (_port <= 0)
+ {
+ const int pos = data.indexOf("PORT=", 0);
+ if (pos != -1)
+ {
+ _port = atoi(data.constData() + pos + 5);
+ qDebug() << "Got VNC port " << _port << ", ro " << _ropass << ", rw " << _rwpass;
+ emit started(_port, _ropass, _rwpass);
+ // Kill error timer, but only if port seemed valid
+ if (_timerId != 0 && _port > 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ }
+ }
+ }
+}
+
+void VncServer::onStdErr()
+{
+ if (_process == NULL)
+ {
+ qDebug("VncServer::onStdErr() called in bad state.");
+ return;
+ }
+ QByteArray data(_process->readAllStandardError());
+}
+
+void VncServer::onError(QProcess::ProcessError error)
+{
+ this->stop();
+ emit started(0, _ropass, _rwpass);
+}
+
+void VncServer::onFinished(int exitCode)
+{
+ this->stop();
+ emit started(0, _ropass, _rwpass);
+}
diff --git a/src/client/vnc/vncserver.h b/src/client/vnc/vncserver.h
new file mode 100644
index 0000000..2aae49c
--- /dev/null
+++ b/src/client/vnc/vncserver.h
@@ -0,0 +1,52 @@
+/*
+ * vncserver.h
+ *
+ * Created on: 24.01.2013
+ * Author: sr
+ */
+
+#ifndef VNCSERVER_H_
+#define VNCSERVER_H_
+
+#include <QtCore>
+
+class VncServer;
+
+class VncServer : public QObject
+{
+ Q_OBJECT
+
+private:
+ QProcess *_process;
+ QString _ropass;
+ QString _rwpass;
+ int _port;
+ int _timerId;
+
+ VncServer();
+ virtual ~VncServer();
+
+ static VncServer *me;
+
+public:
+ static VncServer *instance();
+
+ void start();
+ void stop();
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+signals:
+ // Emited when started succesfully, or if startup failed. port will be <= 0 if it failed.
+ void started(int port, QString& ropass, QString& rwpass);
+
+private slots:
+ void onStdOut();
+ void onStdErr();
+ void onFinished(int exitCode);
+ void onError(QProcess::ProcessError error);
+
+};
+
+#endif /* VNCSERVER_H_ */
diff --git a/src/client/vnc/vncthread.cpp b/src/client/vnc/vncthread.cpp
new file mode 100644
index 0000000..492f970
--- /dev/null
+++ b/src/client/vnc/vncthread.cpp
@@ -0,0 +1,314 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # vncClientThread.cpp
+ # - Connection to remove vnc server
+ # - Emits Qt signal on framebuffer updates
+ # -----------------------------------------------------------------------------
+ */
+
+#include "vncthread.h"
+#include <QPainter>
+
+#include <netinet/tcp.h>
+
+// greatest common divisor
+static int gcd(int a, int b)
+{
+ if (b == 0)
+ return a;
+ return gcd(b, a % b);
+}
+
+VncThread::VncThread(QString host, int port, QString passwd, int quality) :
+ QThread(), _frameBuffer(NULL), _painter(NULL), _hasNewLocalSize(false), _run(true)
+{
+ _srcStepX = _srcStepY = _dstStepX = _dstStepY = 0;
+ _host = host;
+ _port = port;
+ _passwd = passwd;
+ _quality = quality;
+ _client = NULL;
+ _connected = false;
+ moveToThread(this);
+}
+
+// ALWAYS delete this class from another thread using delete, not deleteLater, or you will deadlock the thread
+VncThread::~VncThread()
+{
+ qDebug("VNC worker destructor called, waiting for thread finishing...");
+ _run = false;
+ while (this->isRunning())
+ this->msleep(10);
+ qDebug("Thread ended.");
+ if (_frameBuffer)
+ delete[] _frameBuffer;
+ if (_client != NULL)
+ {
+ if (_client->sock != -1)
+ ::close(_client->sock);
+ _client->frameBuffer = NULL;
+ rfbClientCleanup(_client);
+ }
+ if (_painter != NULL)
+ delete _painter;
+}
+
+// Calc matching pixel borders for both resolutions to prevent artifacts through bilinear scaling
+void VncThread::calcScaling()
+{
+ if (_localSize.isEmpty() || _localSize.width() == 0 || _localSize.height() == 0)
+ return;
+ if (_clientSize.isEmpty() || _clientSize.width() == 0 || _clientSize.height() == 0)
+ return;
+ const int gcdX = gcd(_localSize.width(), _clientSize.width());
+ const int gcdY = gcd(_localSize.height(), _clientSize.height());
+ _srcStepX = _clientSize.width() / gcdX;
+ _srcStepY = _clientSize.height() / gcdY;
+ _dstStepX = _localSize.width() / gcdX;
+ _dstStepY = _localSize.height() / gcdY;
+ qDebug() << "Scaling updated to " << _clientSize << " -> " << _localSize;
+ emit imageUpdated(0, 0, _localSize.width(), _localSize.height());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Public
+
+void VncThread::setTargetSize(const QSize size)
+{
+ if (_localSize == size)
+ return;
+ qDebug() << "Setting target size to " << size;
+ QMutexLocker lock(&_mutex);
+ _newLocalSize = size;
+ _hasNewLocalSize = true;
+}
+
+void VncThread::run()
+{
+ qDebug("[%s] VNC client started.", metaObject()->className());
+ qDebug("[%s] Host: '%s' Port: %i Passwd: '%s' Quality: %i", metaObject()->className(), qPrintable(_host), _port,
+ qPrintable(_passwd), _quality);
+
+ // setup network
+ _client = rfbGetClient(8, 3, 4);
+ _client->MallocFrameBuffer = &frameBufferHandler;
+ _client->canHandleNewFBSize = true;
+ free(_client->serverHost); // in rfbGetClient, serverHost is assigned strdup(""), so free that first.
+ _client->serverHost = strdup(_host.toUtf8().constData());
+ _client->desktopName = NULL;
+ _client->serverPort = _port;
+ _client->GetPassword = &passwdHandler;
+ _client->GotFrameBufferUpdate = &updateImage;
+ _client->frameBuffer = NULL;
+
+ // save this instance in vnc-struct for callbacks
+ rfbClientSetClientData(_client, 0, this);
+
+ // start client
+ if (!rfbInitClient(_client, NULL, NULL))
+ {
+ _client = NULL; // !!! <- if you don't do this you will get a segfault later when you try to clean up _client, as rfbInitClient already did so
+ this->stop();
+ return;
+ }
+
+ qDebug("[%s] Connection successful!", metaObject()->className());
+ int one = 1;
+ setsockopt(_client->sock, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
+ one = 1;
+ setsockopt(_client->sock, SOL_TCP, TCP_QUICKACK, &one, sizeof(one));
+
+ // Main VNC event loop
+ emit projectionStarted();
+ while (_run)
+ {
+ _connected = true;
+ const int i = WaitForMessage(_client, 100 * 1000); // wait 100ms for message. returns -1 on error/disconnect, 0 if nothing happened, 1 if new data arrived
+ if (i < 0)
+ break;
+ if (i > 0 && !HandleRFBServerMessage(_client))
+ break;
+
+ if (_hasNewLocalSize)
+ {
+ QMutexLocker lock(&_mutex);
+ _hasNewLocalSize = false;
+ _localSize = _newLocalSize;
+ if (_painter != NULL)
+ delete _painter;
+ _imgScaled = QImage(_localSize, QImage::Format_RGB32);
+ _painter = new QPainter(&_imgScaled);
+ this->calcScaling();
+ }
+
+ /*
+ //work yourself through event queue and fire every event...
+ while (!_eventQueue.isEmpty()) {
+ SomeEvent* event = _eventQueue.dequeue();
+ event->fire(_client);
+ delete event;
+ }*/
+ }
+
+ _connected = false;
+ qDebug("[%s] VNC client stopped.", metaObject()->className());
+}
+
+const QString VncThread::getDesktopName() const
+{
+ if (_client == NULL || _client->desktopName == NULL)
+ return QString();
+ return QString(_client->desktopName);
+}
+
+void VncThread::mouseEvent(int x, int y, int buttonMask)
+{
+ //QMutexLocker lock(&mutex);
+ if (!_run)
+ return;
+
+ _eventQueue.enqueue(new PointerEvent(x, y, buttonMask));
+}
+
+void VncThread::keyEvent(int key, bool pressed)
+{
+ //QMutexLocker lock(&mutex);
+ if (!_run)
+ return;
+
+ _eventQueue.enqueue(new KeyEvent(key, pressed));
+}
+
+void VncThread::processImageUpdate(const int x, const int y, const int w, const int h)
+{
+ if (_srcStepX > 1 || _srcStepY > 1)
+ {
+ // Scaling is required as vnc server and client are using different resolutions
+ // Calc section offsets first
+ const int startX = x / _srcStepX;
+ const int startY = y / _srcStepY;
+ const int endX = (x + w - 1) / _srcStepX + 1;
+ const int endY = (y + h - 1) / _srcStepY + 1;
+ // Now pixel offsets for source
+ const int srcX = startX * _srcStepX;
+ const int srcY = startY * _srcStepY;
+ const int srcW = endX * _srcStepX - srcX;
+ const int srcH = endY * _srcStepY - srcY;
+ // Pixel offsets for destination
+ const int dstX = startX * _dstStepX;
+ const int dstY = startY * _dstStepY;
+ const int dstW = endX * _dstStepX - dstX;
+ const int dstH = endY * _dstStepY - dstY;
+ // Rescale
+ if (_painter != NULL)
+ {
+ QImage scaled(
+ _img.copy(srcX, srcY, srcW, srcH).scaled(dstW, dstH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ _painter->drawImage(dstX, dstY, scaled, 0, 0, dstW, dstH);
+ emit imageUpdated(dstX, dstY, dstW, dstH);
+ }
+ }
+ else
+ {
+ // Same resolution, nothing to do
+ emit imageUpdated(x, y, w, h);
+ }
+}
+
+// *** callback stuff ***
+
+// the vnc lib is requesting the connection password
+char* VncThread::passwdHandler(rfbClient *client)
+{
+ VncThread* t = (VncThread*)rfbClientGetClientData(client, 0);
+ return strdup(t->_passwd.toUtf8());
+}
+
+// the vnc lib is telling us the size and bit depth of the remote screen
+rfbBool VncThread::frameBufferHandler(rfbClient *client)
+{
+ VncThread *t = (VncThread*)rfbClientGetClientData(client, 0);
+ const int width = client->width, height = client->height, depth = client->format.bitsPerPixel;
+ const int size = width * height * (depth / 8);
+ qDebug("[%s] Remote desktop: %ix%ix%i", t->metaObject()->className(), width, height, depth);
+
+ QMutexLocker lock(&(t->_mutex));
+
+ if (t->_frameBuffer)
+ delete[] t->_frameBuffer;
+
+ t->_frameBuffer = new uint8_t[size];
+ client->frameBuffer = t->_frameBuffer;
+ memset(client->frameBuffer, '\0', size);
+ client->format.bitsPerPixel = 32;
+ client->format.redShift = 16;
+ client->format.greenShift = 8;
+ client->format.blueShift = 0;
+ client->format.redMax = 0xff;
+ client->format.greenMax = 0xff;
+ client->format.blueMax = 0xff;
+
+ const int quality = t->_quality;
+ switch (quality)
+ {
+ case VncThread::HIGH:
+ client->appData.useBGR233 = 0;
+ client->appData.encodingsString = "copyrect zlib hextile raw";
+ client->appData.compressLevel = 4;
+ client->appData.qualityLevel = 9;
+ client->appData.scaleSetting = 0;
+ break;
+ case VncThread::MEDIUM:
+ client->appData.useBGR233 = 0;
+ client->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
+ client->appData.compressLevel = 7;
+ client->appData.qualityLevel = 8;
+ client->appData.scaleSetting = 0;
+ break;
+ case VncThread::LOW:
+ default:
+ client->appData.useBGR233 = 1;
+ client->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
+ client->appData.compressLevel = 9;
+ client->appData.qualityLevel = 1;
+ client->appData.scaleSetting = 0;
+ break;
+ }
+ SetFormatAndEncodings(client);
+
+ t->_clientSize = QSize(width, height);
+
+ t->_img = QImage(client->frameBuffer, client->width, client->height, QImage::Format_RGB32);
+
+ t->calcScaling();
+
+ return true;
+}
+
+void VncThread::updateImage(rfbClient* client, int x, int y, int w, int h)
+{
+ VncThread* t = (VncThread*)rfbClientGetClientData(client, 0);
+ t->processImageUpdate(x, y, w, h);
+}
+
+// *** event stuff ***
+
+void PointerEvent::fire(rfbClient* cl)
+{
+ SendPointerEvent(cl, _x, _y, _buttonMask);
+}
+
+void KeyEvent::fire(rfbClient* cl)
+{
+ SendKeyEvent(cl, _key, _pressed);
+}
diff --git a/src/client/vnc/vncthread.h b/src/client/vnc/vncthread.h
new file mode 100644
index 0000000..b6679b3
--- /dev/null
+++ b/src/client/vnc/vncthread.h
@@ -0,0 +1,141 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ */
+
+#ifndef VNCCLIENTTHREAD_H_
+#define VNCCLIENTTHREAD_H_
+
+#include <QtCore>
+#include <QImage>
+#include <QThread>
+
+class QPainter;
+
+extern "C"
+{
+#include <rfb/rfbclient.h>
+}
+
+/**
+ * - START -
+ * Event classes. Not my code. Might be useful to implement remote assistance via VNC later.
+ * Code might come from the KDE VNC client.
+ */
+class SomeEvent
+{
+public:
+ virtual ~SomeEvent(){}
+ virtual void fire(rfbClient*) = 0;
+};
+
+class KeyEvent : public SomeEvent
+{
+public:
+ KeyEvent(int key, int pressed) :
+ _key(key), _pressed(pressed)
+ {
+ }
+
+ void fire(rfbClient*);
+
+private:
+ int _key;
+ int _pressed;
+};
+
+class PointerEvent : public SomeEvent
+{
+public:
+ PointerEvent(int x, int y, int buttonMask) :
+ _x(x), _y(y), _buttonMask(buttonMask)
+ {
+ }
+
+ void fire(rfbClient*);
+
+private:
+ int _x;
+ int _y;
+ int _buttonMask;
+};
+/** - END - **/
+
+/**
+ * VncThread - communicate with VNC server, scale image if necessary.
+ *
+ * As this class is derived from QThread and overrides the run() method,
+ * it does not have an event-loop. Do NOT try to add any slots to it.
+ * It will NOT be able to receive signals. Emitting signals is fine though.
+ */
+class VncThread : public QThread
+{
+Q_OBJECT
+
+private:
+ rfbClient *_client;
+ quint8 *_frameBuffer;
+
+ QString _host;
+ int _port;
+ QString _passwd;
+ int _quality;
+ QQueue<SomeEvent*> _eventQueue;
+
+ QPainter *_painter;
+ QImage _img;
+ QImage _imgScaled;
+ QSize _clientSize;
+ QSize _localSize;
+ QMutex _mutex;
+
+ QSize _newLocalSize;
+ volatile bool _hasNewLocalSize;
+
+ int _srcStepX, _srcStepY, _dstStepX, _dstStepY;
+
+ bool _connected;
+ volatile bool _run;
+
+ void calcScaling();
+
+ // Callbacks for rfb lib. make them class members so the callbacks can access private members of the class.
+ static void updateImage(rfbClient *client, int x, int y, int w, int h);
+ static char* passwdHandler(rfbClient *client);
+ static rfbBool frameBufferHandler(rfbClient *client);
+
+public:
+ VncThread(QString host, int port, QString passwd, int quality);
+ // Do NOT delete this class directly. use VncThread::destroy() instead!
+ ~VncThread();
+
+ const QImage& getImage() const { if (_srcStepX > 1 || _srcStepY > 1) return _imgScaled; return _img; }
+ const QSize& getSourceSize() const { return _clientSize; }
+ const QString getDesktopName() const;
+ void processImageUpdate(const int x, const int y, const int w, const int h);
+ void mouseEvent(int x, int y, int buttonMask);
+ void keyEvent(int key, bool pressed);
+ const bool isConnected() const { return _connected; }
+ void stop() { _run = false; }
+ void setTargetSize(const QSize size);
+ void run();
+
+ int const static HIGH = 0;
+ int const static MEDIUM = 1;
+ int const static LOW = 2;
+
+signals:
+ void imageUpdated(const int x, const int y, const int w, const int h);
+ void projectionStarted();
+
+};
+
+#endif /* VNCCLIENTTHREAD_H_ */
diff --git a/src/client/vnc/vncwindow.cpp b/src/client/vnc/vncwindow.cpp
new file mode 100644
index 0000000..d4f6d40
--- /dev/null
+++ b/src/client/vnc/vncwindow.cpp
@@ -0,0 +1,277 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # clientVNCViewer.cpp
+ # - connetct to vnc server and show remote screen (window/full)
+ # -----------------------------------------------------------------------------
+ */
+
+#include "vncwindow.h"
+#include "vncthread.h"
+
+VncWindow::VncWindow(QWidget *parent) :
+ QDialog(parent), _vncWorker(NULL), _viewOnly(true), _buttonMask(0), _clientId(0)
+{
+ //
+}
+
+VncWindow::~VncWindow()
+{
+ close();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Public
+
+void VncWindow::open(const QString& host, int port, const QString& passwd, bool ro, bool fullscreen, const QString& caption, const int clientId)
+{
+ // start thread for vnc-updates
+ this->onThreadFinished();
+ _clientId = clientId;
+ _vncWorker = new VncThread(host, port, passwd, 1);
+ connect(_vncWorker, SIGNAL(finished()), this, SLOT(onThreadFinished()), Qt::QueuedConnection);
+ connect(_vncWorker, SIGNAL(projectionStarted()), this, SLOT(onProjectionStarted()), Qt::QueuedConnection);
+ _vncWorker->start(QThread::LowPriority);
+ //_rfbclient = _thread->getRfbClient();
+ //installEventFilter(this);
+ setMouseTracking(true); // get mouse events even when there is no mousebutton pressed
+ setFocusPolicy(Qt::WheelFocus); //needed?!?
+
+ setWindowTitle(caption);
+
+ setAttribute(Qt::WA_OpaquePaintEvent);
+
+ if (fullscreen)
+ {
+ setWindowFlags(Qt::WindowStaysOnTopHint);
+ showFullScreen();
+ activateWindow();
+ raise();
+ }
+ else
+ {
+ resize(800, 600);
+ showNormal();
+ }
+
+ this->show();
+ _vncWorker->setTargetSize(this->size());
+
+ connect(_vncWorker, SIGNAL(imageUpdated(const int, const int, const int, const int)), this,
+ SLOT(onUpdateImage(const int, const int, const int, const int)),
+ Qt::QueuedConnection);
+}
+
+void VncWindow::closeEvent(QCloseEvent *e)
+{
+ e->ignore();
+ qDebug("Closing VNC viewer window.");
+ this->setVisible(false);
+ this->onThreadFinished();
+ emit running(false, _clientId);
+}
+
+void VncWindow::onUpdateImage(const int x, const int y, const int w, const int h)
+{
+ this->repaint(x, y, w, h);
+}
+
+/**
+ * Thread finished, clean up and close window
+ */
+void VncWindow::onThreadFinished()
+{
+ if (_vncWorker)
+ {
+ disconnect(_vncWorker, SIGNAL(imageUpdated(const int, const int, const int, const int)), this,
+ SLOT(onUpdateImage(const int, const int, const int, const int)));
+ disconnect(_vncWorker, SIGNAL(finished()), this, SLOT(onThreadFinished()));
+ _vncWorker->stop();
+ delete _vncWorker;
+ _vncWorker = NULL;
+ this->close();
+ }
+}
+
+/**
+ * VNC Thread successfully connected to remote end - projection will start
+ */
+void VncWindow::onProjectionStarted()
+{
+ emit running(true, _clientId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Protected
+
+void VncWindow::paintEvent(QPaintEvent *event)
+{
+ const QRect &r = event->rect();
+ this->draw(r.left(), r.top(), r.width(), r.height());
+ event->accept();
+}
+
+void VncWindow::resizeEvent(QResizeEvent* event)
+{
+ _vncWorker->setTargetSize(event->size());
+}
+
+void VncWindow::draw(const int x, const int y, const int w, const int h)
+{
+ if (_vncWorker == NULL)
+ return;
+ QPainter painter(this);
+ painter.drawImage(x, y, _vncWorker->getImage(), x, y, w, h);
+ //painter.drawRect(x,y,w,h); // for debugging updated area
+}
+
+//returns true if event was processed
+/*bool VncWindow::event(QEvent *event)
+ {
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+
+ keyEventHandler(static_cast<QKeyEvent*>(event));
+ return true;
+ break;
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+ mouseEventHandler(static_cast<QMouseEvent*>(event));
+ return true;
+ break;
+ case QEvent::Wheel:
+ wheelEventHandler(static_cast<QWheelEvent*>(event));
+ return true;
+ break;
+ default:
+ return false;
+ }
+ }*/
+
+//handles mouseevents
+void VncWindow::mouseEventHandler(QMouseEvent *e)
+{
+ if (e->type() != QEvent::MouseMove)
+ {
+ if ((e->type() == QEvent::MouseButtonPress) || (e->type() == QEvent::MouseButtonDblClick))
+ {
+ if (e->button() & Qt::LeftButton)
+ _buttonMask |= 0x01;
+ if (e->button() & Qt::MidButton)
+ _buttonMask |= 0x02;
+ if (e->button() & Qt::RightButton)
+ _buttonMask |= 0x04;
+ }
+ else if (e->type() == QEvent::MouseButtonRelease)
+ {
+ if (e->button() & Qt::LeftButton)
+ _buttonMask &= 0xfe;
+ if (e->button() & Qt::MidButton)
+ _buttonMask &= 0xfd;
+ if (e->button() & Qt::RightButton)
+ _buttonMask &= 0xfb;
+ }
+ }
+ _vncWorker->mouseEvent(e->x(), e->y(), _buttonMask);
+}
+
+//handles mousewheel
+void VncWindow::wheelEventHandler(QWheelEvent *event)
+{
+ int eb = 0;
+ if (event->delta() < 0)
+ eb |= 0x10;
+ else
+ eb |= 0x8;
+
+ const int x = event->x();
+ const int y = event->y();
+
+ _vncWorker->mouseEvent(x, y, eb | _buttonMask);
+ _vncWorker->mouseEvent(x, y, _buttonMask);
+}
+
+//Handles keypress
+void VncWindow::keyEventHandler(QKeyEvent *e)
+{
+ rfbKeySym k = e->nativeVirtualKey();
+
+ // do not handle Key_Backtab separately because the Shift-modifier
+ // is already enabled
+ if (e->key() == Qt::Key_Backtab)
+ {
+ k = XK_Tab;
+ }
+
+ const bool pressed = (e->type() == QEvent::KeyPress);
+
+ // handle modifiers
+ if (k == XK_Shift_L || k == XK_Control_L || k == XK_Meta_L || k == XK_Alt_L)
+ {
+ if (pressed)
+ {
+ _modkeys[k] = true;
+ }
+ else if (_modkeys.contains(k))
+ {
+ _modkeys.remove(k);
+ }
+ else
+ {
+ unpressModifiers();
+ }
+ }
+
+ if (k)
+ {
+ _vncWorker->keyEvent(k, pressed);
+ }
+}
+
+void VncWindow::keyPressEvent(QKeyEvent* event)
+{
+ if (event->key() == Qt::Key_Escape)
+ this->close();
+}
+
+//removes modifier keys which have been pressed
+void VncWindow::unpressModifiers()
+{
+ const QList<unsigned int> keys = _modkeys.keys();
+ QList<unsigned int>::const_iterator it = keys.constBegin();
+ while (it != keys.end())
+ {
+ _vncWorker->keyEvent(*it, false);
+ it++;
+ }
+ _modkeys.clear();
+}
+
+//(QT Function) Filters events, if _viewOnly is set, true is returned and the event is ignored
+//TODO use this function when implementing viewonly switch
+bool VncWindow::eventFilter(QObject *obj, QEvent *event)
+{
+ if (_viewOnly)
+ {
+ if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease
+ || event->type() == QEvent::MouseButtonDblClick || event->type() == QEvent::MouseButtonPress
+ || event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::Wheel
+ || event->type() == QEvent::MouseMove)
+ return true;
+ }
+
+ return false;
+ //return RemoteView::eventFilter(obj, event);
+}
diff --git a/src/client/vnc/vncwindow.h b/src/client/vnc/vncwindow.h
new file mode 100644
index 0000000..3a73a8e
--- /dev/null
+++ b/src/client/vnc/vncwindow.h
@@ -0,0 +1,73 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ */
+
+#ifndef CLIENTVNCVIEWER_H_
+#define CLIENTVNCVIEWER_H_
+
+#include <QtGui>
+#include <QMouseEvent>
+
+class VncThread;
+class QPainter;
+
+// Definition of key modifier mask constants
+#define KMOD_Alt_R 0x01
+#define KMOD_Alt_L 0x02
+#define KMOD_Meta_L 0x04
+#define KMOD_Control_L 0x08
+#define KMOD_Shift_L 0x10
+
+class VncWindow : public QDialog
+{
+Q_OBJECT
+
+public:
+ VncWindow(QWidget *parent = 0);
+ virtual ~VncWindow();
+
+protected slots:
+ void onUpdateImage(const int x, const int y, const int w, const int h);
+ void onThreadFinished();
+ void onProjectionStarted();
+
+ void open(const QString& host, int port, const QString& passwd, bool ro, bool fullscreen, const QString& caption, const int clientId);
+
+signals:
+ void running(const bool isRunning, const int clientId);
+
+
+protected:
+ void draw(const int x, const int y, const int w, const int h);
+ void paintEvent(QPaintEvent *event);
+ void resizeEvent(QResizeEvent* event);
+ void closeEvent(QCloseEvent *e);
+ //bool event(QEvent *event);
+ //bool eventFilter(QObject *obj, QEvent *event);
+
+private:
+ VncThread *_vncWorker;
+ bool _viewOnly;
+ int _buttonMask;
+ QMap<unsigned int, bool> _modkeys;
+ int _clientId;
+
+ bool eventFilter(QObject *obj, QEvent *event);
+ void keyPressEvent(QKeyEvent* event);
+ void keyEventHandler(QKeyEvent *e);
+ void unpressModifiers();
+ void wheelEventHandler(QWheelEvent *event);
+ void mouseEventHandler(QMouseEvent *event);
+
+};
+
+#endif /* CLIENTVNCVIEWER_H_ */
diff --git a/src/server/clicklabel/clicklabel.cpp b/src/server/clicklabel/clicklabel.cpp
new file mode 100644
index 0000000..2989542
--- /dev/null
+++ b/src/server/clicklabel/clicklabel.cpp
@@ -0,0 +1,14 @@
+#include "clicklabel.h"
+
+ClickLabel::ClickLabel(QWidget *parent) : QLabel(parent)
+{
+ QFont f(this->font());
+ f.setPixelSize(20);
+ this->setFont(f);
+ this->setMaximumHeight(22);
+}
+
+void ClickLabel::mouseReleaseEvent(QMouseEvent* e)
+{
+ emit clicked();
+}
diff --git a/src/server/clicklabel/clicklabel.h b/src/server/clicklabel/clicklabel.h
new file mode 100644
index 0000000..6a05152
--- /dev/null
+++ b/src/server/clicklabel/clicklabel.h
@@ -0,0 +1,25 @@
+#ifndef _CLICKLABEL_H_
+#define _CLICKLABEL_H_
+
+#include <QtGui>
+#include <QLabel>
+
+
+class ClickLabel : public QLabel
+{
+ Q_OBJECT
+
+public:
+ ClickLabel(QWidget *parent);
+
+protected:
+ void mouseReleaseEvent(QMouseEvent* e);
+
+
+signals:
+ void clicked();
+
+};
+
+
+#endif
diff --git a/src/server/connectionframe/connectionframe.cpp b/src/server/connectionframe/connectionframe.cpp
new file mode 100644
index 0000000..1544c76
--- /dev/null
+++ b/src/server/connectionframe/connectionframe.cpp
@@ -0,0 +1,278 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/gui/connectionFrame.cpp
+# -----------------------------------------------------------------------------
+*/
+
+
+#include "connectionframe.h"
+#include "../net/client.h"
+#include <QPixmap>
+#include <QImage>
+#include <cassert>
+
+static QIcon *term = NULL, *cam = NULL, *eye = NULL;
+
+static QString backgroundStyle("background-color: %1; margin: 1px; border: 1px solid black; border-radius: 6px");
+
+ConnectionFrame::ConnectionFrame(QWidget *parent, int width, int height) :
+ QGroupBox(parent), _client(NULL), _timerId(0), _timerCounter(0), _selected(false), _isTutor(false)
+{
+
+ //defines the ui-stuff
+ // load icons first
+ if (term == NULL)
+ {
+ term = new QIcon(":terminal");
+ cam = new QIcon(":cam32");
+ eye = new QIcon(":eye");
+ }
+
+ //this->setAttribute(Qt::WA_OpaquePaintEvent);
+
+ _mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
+ _mainLayout->setSpacing(1);
+ _mainLayout->setMargin(3);
+ _mainLayout->setAlignment(Qt::AlignHCenter);
+
+ _iconLayout = new QBoxLayout(QBoxLayout::RightToLeft, NULL);
+ _iconLayout->setSpacing(1);
+ _iconLayout->setMargin(3);
+
+ _lblUserName = new QLabel("Test", this);
+ _lblUserName->setStyleSheet(QString::fromUtf8("background-color: white; color: black;"));
+ _lblUserName->setAlignment(Qt::AlignHCenter);
+ //_lblUsername->resize(_lblUsername->width(), _lblUsername->font().pixelSize());
+
+ _lblHostName = new QLabel("PC", this);
+ _lblHostName->setStyleSheet(QString::fromUtf8("background-color: white; color: black;"));
+ _lblHostName->setAlignment(Qt::AlignHCenter);
+
+ _icoCam = addIcon(cam);
+ _icoEye = addIcon(eye);
+
+ _iconLayout->addWidget(_icoCam);
+ _iconLayout->addWidget(_icoEye);
+ _iconLayout->addStretch();
+
+ //_layout->addWidget(_imgScreen);
+ _mainLayout->addLayout(_iconLayout);
+ _mainLayout->addStretch();
+ _mainLayout->addWidget(_lblUserName);
+ _mainLayout->addWidget(_lblHostName);
+ this->setLayout(_mainLayout);
+ this->setSize(width, height);
+ this->updateColor();
+}
+
+ConnectionFrame::~ConnectionFrame()
+{
+ //
+}
+
+QLabel* ConnectionFrame::addIcon(const QIcon* icon)
+{
+ QLabel *label = new QLabel(this);
+ label->setPixmap(icon->pixmap(24, 24, QIcon::Normal, QIcon::On));
+ label->setAttribute(Qt::WA_TranslucentBackground);
+ label->hide();
+ _icons.append(label);
+ return label;
+}
+
+void ConnectionFrame::setSize(int width, int height)
+{
+ this->resize(width, height);
+}
+
+void ConnectionFrame::assignClient(Client* client)
+{
+ assert(_client == NULL);
+ connect(client, SIGNAL(destroyed(QObject*)), this, SLOT(onClientDisconnected(QObject*)));
+ connect(client, SIGNAL(thumbUpdated(Client*, const QPixmap&)), this, SLOT(onThumbUpdated(Client*, const QPixmap&)));
+ connect(client, SIGNAL(vncServerStateChange(Client*)), this, SLOT(onVncServerStateChange(Client*)));
+ connect(client, SIGNAL(vncClientStateChange(Client*)), this, SLOT(onVncClientStateChange(Client*)));
+ _client = client;
+ _computerId = client->computerId();
+ _lblHostName->setText(client->ip());
+ _lblHostName->setToolTip(client->host());
+ _lblUserName->setText(client->name());
+ showDefaultThumb();
+ if (_timerId == 0)
+ _timerId = startTimer(1000 + qrand() % 150);
+ this->updateColor();
+}
+
+void ConnectionFrame::showDefaultThumb()
+{
+ const int width = this->width() - 6;
+ const int height = this->height() - 8 - _lblHostName->height() - _lblUserName->height();
+ _remoteScreen = term->pixmap(width, height, QIcon::Normal, QIcon::On);
+ this->repaint();
+ //_imgScreen->setPixmap(_remoteScreen);
+}
+
+void ConnectionFrame::mouseReleaseEvent(QMouseEvent* event)
+{
+ event->accept();
+ if (event->button() == Qt::LeftButton)
+ {
+ QApplication::setOverrideCursor(QCursor(Qt::OpenHandCursor));
+ if (this->pos() != _previousPosition) {
+ qDebug("Moved");
+ emit frameMoved(this);
+ }
+ else
+ {
+ qDebug("Clicked");
+ emit clicked(this);
+ }
+ }
+}
+
+void ConnectionFrame::enterEvent(QEvent* event)
+{
+ QApplication::setOverrideCursor(QCursor(Qt::OpenHandCursor));
+ event->accept();
+}
+
+void ConnectionFrame::leaveEvent(QEvent* event)
+{
+ QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
+ event->accept();
+}
+
+void ConnectionFrame::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::RightButton)
+ {
+ // Menu...
+ }
+ else
+ {
+ _clickPoint = event->pos();
+ _previousPosition = this->pos();
+ QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
+ }
+ // On click, the window has to be on the top-level.
+ activateWindow();
+ raise();
+ update();
+ event->accept();
+}
+
+void ConnectionFrame::mouseMoveEvent(QMouseEvent *event)
+{
+ QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
+ move(mapToParent(event->pos()-_clickPoint));
+ event->accept();
+}
+
+void ConnectionFrame::mouseDoubleClickEvent(QMouseEvent* event)
+{
+ emit doubleClicked(this);
+ event->accept();
+}
+
+void ConnectionFrame::paintEvent(QPaintEvent *event)
+{
+ QGroupBox::paintEvent(event);
+ if (_remoteScreen.isNull())
+ return;
+ QPainter painter(this);
+ painter.drawPixmap((this->width() - _remoteScreen.width()) / 2, 4, _remoteScreen);
+ event->accept();
+}
+
+void ConnectionFrame::timerEvent(QTimerEvent* event)
+{
+ if (_client == NULL)
+ return;
+ ++_timerCounter;
+ if (_client->isActiveVncServer() && _timerCounter % 5 != 0)
+ return;
+ const int width = this->width() - 8;
+ const int height = this->height() - 9 - _lblHostName->height() - _lblUserName->height();
+ _client->requestThumb(width, height);
+}
+
+void ConnectionFrame::setSelection(bool selected)
+{
+ if (_selected == selected)
+ return;
+ _selected = selected;
+ this->updateColor();
+}
+
+void ConnectionFrame::updateColor()
+{
+ if (_client == NULL)
+ {
+ // Unconnected Frame
+ if (_selected)
+ this->setStyleSheet(backgroundStyle.arg("rgb(140, 140, 170)"));
+ else
+ this->setStyleSheet(backgroundStyle.arg("rgb(140, 140, 140)"));
+ for (QList<QLabel*>::iterator it(_icons.begin()); it != _icons.end(); ++it)
+ (**it).hide();
+ return;
+ }
+ _icoCam->setVisible(_client->isActiveVncServer());
+ _icoEye->setVisible(_client->isActiveVncClient());
+
+ // Normal client, no special stuff active
+
+ if (_selected && _isTutor)
+ this->setStyleSheet(backgroundStyle.arg("rgb(255, 220, 180)"));
+ else if (_isTutor)
+ this->setStyleSheet(backgroundStyle.arg("rgb(255, 180, 180)"));
+ else if (_selected)
+ this->setStyleSheet(backgroundStyle.arg("rgb(180, 180, 210)"));
+ else
+ this->setStyleSheet(backgroundStyle.arg("rgb(180, 180, 180)"));
+}
+
+/**
+ * Slots
+ */
+
+void ConnectionFrame::onClientDisconnected(QObject* client)
+{
+ assert(client == _client);
+ if (_timerId != 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ }
+ _client = NULL;
+ _lblUserName->setText(QString());
+ showDefaultThumb();
+ this->updateColor();
+}
+
+void ConnectionFrame::onThumbUpdated(Client* client, const QPixmap& thumb)
+{
+ assert(client == _client);
+ _remoteScreen = thumb;
+ //_imgScreen->setPixmap(_remoteScreen);
+ this->repaint();
+}
+
+void ConnectionFrame::onVncServerStateChange(Client* client)
+{
+ this->updateColor();
+}
+
+void ConnectionFrame::onVncClientStateChange(Client* client)
+{
+ this->updateColor();
+}
diff --git a/src/server/connectionframe/connectionframe.h b/src/server/connectionframe/connectionframe.h
new file mode 100644
index 0000000..2b78056
--- /dev/null
+++ b/src/server/connectionframe/connectionframe.h
@@ -0,0 +1,77 @@
+#ifndef _CONNECTIONFRAME_H_
+#define _CONNECTIONFRAME_H_
+#include <QtGui>
+
+class Client;
+
+class ConnectionFrame : public QGroupBox
+{
+
+Q_OBJECT
+
+private:
+
+ QString _computerId;
+
+ QBoxLayout *_mainLayout;
+ QBoxLayout *_iconLayout;
+ QLabel *_lblUserName;
+ QLabel *_lblHostName;
+
+ QLabel *_icoCam, *_icoEye;
+ QList<QLabel*> _icons;
+
+ QPixmap _remoteScreen;
+
+ QPoint _clickPoint;
+ QPoint _previousPosition;
+
+ Client *_client;
+
+ int _timerId, _timerCounter;
+ bool _selected;
+ bool _isTutor;
+
+ void showDefaultThumb();
+ void updateColor();
+ QLabel* addIcon(const QIcon* icon);
+
+public:
+ ConnectionFrame(QWidget* parent, int width, int height);
+ virtual ~ConnectionFrame();
+
+ const QPixmap& getFramePixmap() const { return _remoteScreen; }
+ void setSize(int width, int height);
+ void assignClient(Client *client);
+ void setSelection(bool selected);
+ const inline bool selected() const { return _selected; }
+
+ const QString& computerId() const { return _computerId; }
+ Client* client() const { return _client; }
+
+ inline const bool isTutor() const { return _isTutor; }
+ inline void setTutor(bool b) { _isTutor = b; }
+
+protected:
+ void mouseDoubleClickEvent(QMouseEvent* event);
+ void mouseReleaseEvent(QMouseEvent* e);
+ void enterEvent(QEvent* event);
+ void leaveEvent(QEvent* event);
+ void mousePressEvent(QMouseEvent* event);
+ void mouseMoveEvent(QMouseEvent* event);
+ void paintEvent(QPaintEvent *event);
+ void timerEvent(QTimerEvent* event);
+
+signals:
+ void frameMoved(ConnectionFrame* frame);
+ void doubleClicked(ConnectionFrame* frame);
+ void clicked(ConnectionFrame* frame);
+
+private slots:
+ void onClientDisconnected(QObject* client);
+ void onThumbUpdated(Client* client, const QPixmap& thumb);
+ void onVncServerStateChange(Client* client);
+ void onVncClientStateChange(Client* client);
+};
+
+#endif
diff --git a/src/server/main.cpp b/src/server/main.cpp
new file mode 100644
index 0000000..e7c709a
--- /dev/null
+++ b/src/server/main.cpp
@@ -0,0 +1,41 @@
+#include "mainwindow/mainwindow.h"
+#include "util/util.h"
+
+int main(int argc, char** argv)
+{
+ QApplication app(argc, argv);
+ app.setOrganizationName("openslx");
+ app.setOrganizationDomain("openslx.org");
+ app.setApplicationName("pvsmgr");
+
+ qsrand((uint)QDateTime::currentMSecsSinceEpoch());
+
+ // Make sure settings directory exists
+ do {
+ USER_SETTINGS(settings);
+ QFileInfo fi(settings.fileName());
+ QDir path(fi.path());
+ qDebug() << "Settings directory is " << fi.path();
+ if (!path.exists())
+ path.mkpath(path.absolutePath());
+ // Now check if settings file exists. If not, copy system default (if available)
+ if (!fi.exists())
+ {
+ SYSTEM_SETTINGS(sys);
+ QFileInfo sysfi(sys.fileName());
+ if (sysfi.exists())
+ {
+ if (!QFile::copy(sys.fileName(), settings.fileName()))
+ qDebug() << "Copying default settings from " << sys.fileName() << " to " << settings.fileName() << " failed.";
+ }
+ }
+ } while (0);
+
+ // use system locale as language to translate gui
+ QTranslator translator;
+ translator.load(":pvsmgr");
+ app.installTranslator(&translator);
+
+ MainWindow pvsmgr;
+ return app.exec();
+}
diff --git a/src/server/mainwindow/mainwindow.cpp b/src/server/mainwindow/mainwindow.cpp
new file mode 100644
index 0000000..8c57b91
--- /dev/null
+++ b/src/server/mainwindow/mainwindow.cpp
@@ -0,0 +1,633 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # mainWindow.cpp
+ This is the Main class for the pvsManager. The GUI is constructed here.
+ # -----------------------------------------------------------------------------
+ */
+// Self
+#include "mainwindow.h"
+// QT stuff
+#include <QtGui>
+#include <QFileDialog>
+// Other custom UI elements
+#include "../clicklabel/clicklabel.h"
+#include "../sessionnamewindow/sessionnamewindow.h"
+#include "../connectionframe/connectionframe.h"
+// Network
+#include "../net/listenserver.h"
+#include "../net/client.h"
+#include "../net/discoverylistener.h"
+#include "../../shared/networkmessage.h"
+// Others
+#include "../../shared/settings.h"
+#include "../util/util.h"
+#include "../util/global.h"
+// Auto-generated ui class
+#include "ui_mainwindow.h"
+
+MainWindow::MainWindow(QWidget* parent) :
+ QMainWindow(parent), ui(new Ui::MainWindow), _tbIconSize(24), _tbArea(Qt::LeftToolBarArea),
+ _buttonBlockTime(0)
+
+{
+ _sessionNameWindow = new SessionNameWindow(this);
+ ui->setupUi(this);
+
+ Global::setSessionName("1234");
+
+ //conWin = new ConnectionWindow(ui->widget);
+ //ui->VconWinLayout->addWidget(conWin);
+ //conList = new ConnectionList(ui->ClWidget);
+ //ui->ClientGLayout->addWidget(conList);
+
+ // Show only session name label, not textbox
+ _sessionNameLabel = new ClickLabel(this);
+ ui->mainLayout->addWidget(_sessionNameLabel);
+
+ ui->frmRoom->setStyleSheet("background-color:#444");
+
+ // toolbar and actions in pvsmgr
+ ui->action_Exit->setStatusTip(tr("Exit"));
+ ui->action_Lock->setStatusTip(tr("Lock or Unlock all Clients"));
+
+ // Close button in tool bar
+ connect(ui->action_Exit, SIGNAL(triggered()), this, SLOT(onButtonExit()));
+ connect(ui->action_BroadcastScreen, SIGNAL(triggered()), this, SLOT(onButtonBroadcast()));
+ connect(ui->action_Lock, SIGNAL(toggled(bool)), this, SLOT(onButtonLock(bool)));
+ // Clicking the session name label shows the edit field for it
+ connect(_sessionNameLabel, SIGNAL(clicked()), this, SLOT(onSessionNameClick()));
+ // Listen to updates to the session name through the session name window
+ connect(_sessionNameWindow, SIGNAL(updateSessionName()), this,
+ SLOT(onSessionNameUpdate()));
+
+ setAttribute(Qt::WA_QuitOnClose);
+ setUnifiedTitleAndToolBarOnMac(true);
+ this->showMaximized(); // show the Mainwindow maximized
+
+ _tilesX = 9;
+ _tilesY = 7;
+ _tileWidth = 10;
+ _tileHeight = 10;
+
+ _timerId = 0;
+ _timerTimeout = 0;
+
+ _listenServer = new ListenServer(CLIENT_PORT);
+ connect(_listenServer, SIGNAL(newClient(Client*)), this, SLOT(onClientConnected(Client*)));
+ _discoveryListener = new DiscoveryListener();
+
+ // Finally
+ this->onSessionNameUpdate();
+ _timerId = startTimer(10);
+ _timerTimeout = 8;
+}
+
+MainWindow::~MainWindow()
+{
+ _sessionNameLabel->deleteLater();
+ delete ui;
+}
+
+void MainWindow::placeFrameInFreeSlot(ConnectionFrame* frame)
+{
+ // Get occupied cell of each frame and store status in an array
+ const int elems = _tilesX * _tilesY;
+ int currentIndex = 0;
+ bool grid[elems];
+ memset(grid, 0, sizeof(bool) * elems); // set everything to false
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ const QPoint &oldpos = (*it)->frameGeometry().topLeft();
+ const int tx = oldpos.x() / _tileWidth;
+ const int ty = oldpos.y() / _tileHeight;
+ const int index = tx + ty * _tilesX;
+ if (frame == *it)
+ {
+ // Current frame is the frame to place, remember its old index
+ currentIndex = index;
+ continue;
+ }
+ if (index < 0 || index >= elems)
+ {
+ // Current frame is out of bounds, place in top left corner
+ qDebug("Move A");
+ (*it)->move(0, 0);
+ grid[0] = true;
+ continue;
+ }
+ // Just mark cell as occupied in array
+ grid[index] = true;
+ }
+ // Safety
+ if (currentIndex < 0 || currentIndex >= elems)
+ currentIndex = 0;
+ // Now place in next free cell, start looking at current position of frame
+ for (int i = 0; i < elems; ++i)
+ {
+ const int index = (i + currentIndex) % elems;
+ if (grid[index])
+ continue;
+ const int x = (index % _tilesX) * _tileWidth;
+ const int y = (index / _tilesX) * _tileHeight;
+ //qDebug() << "Index=" << index << " i=" << i << " cI=" << currentIndex << " elems=" << elems << " x=" << x << " y=" << y;
+ qDebug("Move B");
+ frame->move(x, y);
+ break;
+ }
+}
+
+ConnectionFrame* MainWindow::createFrame()
+{
+ // Allocate and resize
+ ConnectionFrame *cf = new ConnectionFrame(ui->frmRoom, _tileWidth, _tileHeight);
+ _clientFrames.append(cf);
+ cf->show();
+ connect(cf, SIGNAL(frameMoved(ConnectionFrame *)), this, SLOT(onPlaceFrame(ConnectionFrame *)));
+ connect(cf, SIGNAL(clicked(ConnectionFrame *)), this, SLOT(onFrameClicked(ConnectionFrame *)));
+ return cf;
+}
+
+bool MainWindow::loadPosition(QSettings& settings, const QString& id, int& x, int& y)
+{
+ settings.beginGroup("client_position");
+ const QVariant retval(settings.value(id));
+ settings.endGroup();
+ if (retval.type() != QVariant::Point)
+ return false;
+ const QPoint point(retval.toPoint());
+ x = point.x();
+ y = point.y();
+ return true;
+}
+
+void MainWindow::savePosition(ConnectionFrame *cf)
+{
+ Client *client = cf->client();
+ if (client == NULL)
+ return;
+ QPoint pos(cf->pos().x() / _tileWidth, cf->pos().y() / _tileHeight);
+ USER_SETTINGS(settings);
+ settings.beginGroup("client_position");
+ settings.setValue(client->computerId(), pos);
+ settings.endGroup();
+}
+
+/*
+ * Overridden methods
+ */
+
+void MainWindow::closeEvent(QCloseEvent* e)
+{
+ int ret = QMessageBox::question(this, "Test", "Beenden?", 0, 1, 2);
+ if (ret == 1)
+ QApplication::exit(0);
+ else
+ e->ignore();
+}
+
+void MainWindow::changeEvent(QEvent* e)
+{
+ QMainWindow::changeEvent(e);
+}
+
+void MainWindow::resizeEvent(QResizeEvent* e)
+{
+ QMainWindow::resizeEvent(e);
+ if (_timerId == 0)
+ {
+ _timerId = startTimer(5);
+ }
+ _timerTimeout = 8;
+}
+
+void MainWindow::mouseReleaseEvent(QMouseEvent* e)
+{
+ // Try to figure out if the user clicked inside the "room" frame
+ // No idea if there is a more elegant way to do this without
+ // sub-classing that frame and overriding its mouseReleaseEvent
+ const QPoint pos(ui->frmRoom->mapFrom(this, e->pos()));
+ if (pos.x() < 0 || pos.y() < 0)
+ return;
+ const QSize frame(ui->frmRoom->size());
+ if (frame.width() > pos.x() && frame.height() > pos.y())
+ {
+ for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it)
+ {
+ (**it).setSelection(false);
+ }
+ }
+}
+
+void MainWindow::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == _timerId)
+ {
+ _timerTimeout -= 1;
+ if (_timerTimeout == 1)
+ {
+ // Move toolbar if necessary
+ const int iconSize = this->size().width() / 30 + 14;
+ const int barSpace = ui->toolBar->actions().size() * (iconSize + 8);
+ const Qt::ToolBarArea area =
+ (barSpace < this->size().height() ? Qt::LeftToolBarArea : Qt::TopToolBarArea);
+ if (_tbIconSize != iconSize)
+ {
+ _tbIconSize = iconSize;
+ ui->toolBar->setIconSize(QSize(iconSize, iconSize));
+ }
+ if (_tbArea != area)
+ {
+ _tbArea = area;
+ this->addToolBar(area, ui->toolBar);
+ }
+ return;
+ }
+ // Kill timer when done
+ if (_timerTimeout <= 0)
+ {
+ killTimer(_timerId);
+ _timerId = 0;
+ // Resize all connection windows
+ if (ui->frmRoom->size().width() < 100 || ui->frmRoom->size().height() < 100 || _tilesX <= 0 || _tilesY <= 0)
+ return;
+ const int nw = ui->frmRoom->size().width() / _tilesX;
+ const int nh = ui->frmRoom->size().height() / _tilesY;
+ for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it)
+ {
+ const QPoint &oldpos = (*it)->frameGeometry().topLeft();
+ qDebug("Move C");
+ (*it)->move((oldpos.x() / _tileWidth) * nw, (oldpos.y() / _tileHeight) * nh);
+ (*it)->setSize(nw, nh);
+ }
+ _tileWidth = nw;
+ _tileHeight = nh;
+ }
+ }
+ else
+ {
+ // Unknown timer ID, kill
+ killTimer(event->timerId());
+ }
+}
+
+/*
+ * Slots
+ */
+
+void MainWindow::onPlaceFrame(ConnectionFrame* frame)
+{
+ if (_tilesX <= 0 || _tilesY <= 0)
+ return;
+ const QPoint &p = frame->frameGeometry().center();
+ const QSize &s = ui->frmRoom->geometry().size();
+ int x = (p.x() / _tileWidth) * _tileWidth;
+ int y = (p.y() / _tileHeight) * _tileHeight;
+ if (x < 0)
+ x = 0;
+ else if (x > s.width() - _tileWidth)
+ x = (_tilesX - 1) * _tileWidth;
+ if (y < 0)
+ y = 0;
+ else if (y > s.height() - _tileHeight)
+ y = (_tilesY - 1) * _tileHeight;
+ qDebug("Move D");
+ frame->move(x, y);
+ savePosition(frame);
+ const QPoint &newpos = frame->frameGeometry().topLeft();
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ if (*it == frame)
+ continue;
+ if ((*it)->frameGeometry().topLeft() == newpos)
+ {
+ placeFrameInFreeSlot(*it);
+ }
+ }
+}
+
+void MainWindow::onFrameClicked(ConnectionFrame* frame)
+{
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ if (*it == frame)
+ frame->setSelection(true);
+ else
+ (**it).setSelection(false);
+ }
+}
+
+void MainWindow::onSessionNameClick()
+{
+ _sessionNameWindow->show(Global::sessionName());
+}
+
+void MainWindow::onSessionNameUpdate()
+{
+ _sessionNameLabel->setText(tr("Session Name: %1 [click to edit]").arg(Global::sessionName()));
+}
+
+void MainWindow::onButtonSettings()
+{
+ // Move to any free tile
+ placeFrameInFreeSlot(createFrame());
+}
+
+void MainWindow::onButtonChat()
+{
+ /*
+ for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it) {
+ (*it)->setSize(100 + qrand() % 200);
+ }
+ */
+}
+
+void MainWindow::onButtonBroadcast()
+{
+ const qint64 now = QDateTime::currentMSecsSinceEpoch();
+ if (now < _buttonBlockTime)
+ return;
+ _buttonBlockTime = now + 3000;
+ Client *source = NULL; // the desired source (selected frame)
+ Client *oldSource = NULL; // if there is a client that is already broadcaster, save it here
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if ((**it).selected())
+ source = c;
+ if (c != NULL && c->isProjectionSource())
+ oldSource = c;
+ }
+ if (source == NULL && oldSource == NULL)
+ {
+ QMessageBox::information(this, tr("No Projection Source selected"), tr("You did not select any active client as the connection source."));
+ return;
+ }
+ if (oldSource != NULL)
+ {
+ // Is already broadcast source, disable
+ oldSource->setProjectionSource(false);
+ oldSource->stopVncServer();
+ if (source == NULL || source->id() == oldSource->id())
+ return;
+ }
+ source->setProjectionSource(true);
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL || c->id() == source->id())
+ continue;
+ //c->setDesiredProjectionSource(source->id());
+ c->setProjectionSource(false);
+ }
+ source->startVncServer();
+}
+
+void MainWindow::onButtonLock(bool checked)
+{
+ NetworkMessage msg;
+ msg.setField(_ID, _LOCK);
+ if (checked)
+ msg.setField("ENABLE", QByteArray("1"));
+ else
+ msg.setField("ENABLE", QByteArray("0"));
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL || (**it).isTutor())
+ continue; // Don't lock the tutor
+ c->sendMessage(msg);
+ }
+}
+
+void MainWindow::onButtonExit()
+{
+ this->close();
+}
+
+void MainWindow::onClientConnected(Client* client)
+{
+ qDebug("ListenServer told MainWindow about new connection");
+ connect(client, SIGNAL(authenticating(Client*, ClientLogin*)), this, SLOT(onClientAuthenticating(Client*, ClientLogin*)));
+ connect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*)));
+ connect(client, SIGNAL(vncClientStateChange(Client*)), this, SLOT(onVncClientStateChange(Client*)));
+}
+
+void MainWindow::onClientAuthenticating(Client* client, ClientLogin* request)
+{
+ disconnect(client, SIGNAL(authenticating(Client*, ClientLogin*)), this, SLOT(onClientAuthenticating(Client*, ClientLogin*)));
+ if (!request->accept) // Another receiver of that signal already rejected the client, so nothing to do
+ return;
+ bool inuse;
+ QString check = request->name;
+ int addnum = 1;
+ do
+ {
+ inuse = false;
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL)
+ continue;
+ if (!c->isAuthed())
+ continue;
+ if (c->ip() == request->ip)
+ {
+ request->accept = false;
+ return;
+ }
+ if (c->name() == check)
+ {
+ inuse = true;
+ check = request->name + " (" + QString::number(++addnum) + ")";
+ break;
+ }
+ }
+ } while (inuse && addnum < 100);
+ if (inuse)
+ request->accept = false;
+ else
+ request->name = check;
+}
+
+void MainWindow::onClientAuthenticated(Client* client)
+{
+ disconnect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*)));
+ connect(client, SIGNAL(vncServerStateChange(Client*)), this, SLOT(onVncServerStateChange(Client*)));
+ bool hasActiveTutor = false;
+ ConnectionFrame *deadTutor = NULL;
+ bool anyClient = false;
+ ConnectionFrame *existing = NULL;
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ if ((*it)->computerId() == client->computerId())
+ existing = *it;
+ if ((*it)->client() != NULL)
+ anyClient = true;
+ if ((*it)->isTutor())
+ {
+ if ((*it)->client() == NULL)
+ deadTutor = (*it)->client();
+ else
+ hasActiveTutor = true;
+ }
+ }
+ if (!anyClient && deadTutor != NULL)
+ deadTutor->setTutor(false);
+ if (existing != NULL)
+ {
+ if (!anyClient && !hasActiveTutor)
+ existing->setTutor(true);
+ existing->assignClient(client);
+ return;
+ }
+ // New one, create
+ ConnectionFrame *cf = createFrame();
+ // Try to load last known position
+ int x, y;
+ bool ok;
+ USER_SETTINGS(usr);
+ ok = loadPosition(usr, client->computerId(), x, y);
+ if (!ok)
+ {
+ SYSTEM_SETTINGS(sys);
+ ok = loadPosition(sys, client->computerId(), x, y);
+ }
+ if (x >= _tilesX || y >= _tilesY)
+ ok = false;
+ if (ok)
+ {
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+ qDebug("Move E");
+ cf->move(x * _tileWidth, y * _tileHeight);
+ onPlaceFrame(cf);
+ }
+ else
+ {
+ // Move to any free tile
+ placeFrameInFreeSlot(cf);
+ }
+ // Assign client instance
+ cf->assignClient(client);
+ // Make first active client tutor
+ if (!anyClient && !hasActiveTutor)
+ cf->setTutor(true);
+ // ################
+ NetworkMessage msg;
+ // If clients are currently locked, tell this new client
+ if (ui->action_Lock->isChecked())
+ {
+ msg.reset();
+ msg.setField(_ID, _LOCK);
+ msg.setField("ENABLE", QByteArray("1"));
+ client->sendMessage(msg);
+ }
+ // Same for VNC projections
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c != NULL && c != client && c->isActiveVncServer() && c->isProjectionSource())
+ {
+ msg.reset();
+ msg.setField(_ID, _VNCCLIENT);
+ msg.setField("HOST", c->ip());
+ msg.setField("PORT", QString::number(c->vncPort()));
+ msg.setField("ROPASS", c->vncRoPass());
+ msg.setField("CAPTION", c->name() + " @ " + c->host());
+ client->sendMessage(msg);
+ break;
+ }
+ }
+}
+
+void MainWindow::onVncServerStateChange(Client* client)
+{
+ if (client->vncPort() > 0)
+ {
+ // VNC Server started on some client - start projection on all clients interested in that client's screen
+ NetworkMessage msg;
+ msg.setField(_ID, _VNCCLIENT);
+ msg.setField("HOST", client->ip());
+ msg.setField("PORT", QString::number(client->vncPort()));
+ msg.setField("ROPASS", client->vncRoPass());
+ msg.setField("CAPTION", client->name() + " @ " + client->host());
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL)
+ continue; // Offline
+ if (c->id() == client->id() ||
+ (c->desiredProjectionSource() != client->id() && !client->isProjectionSource()))
+ continue; // Not interested
+ c->sendMessage(msg);
+ }
+ }
+ else
+ {
+ // VNC server stopped on some client or failed to start - reset local pending projection information
+ // If at least one other client seemed to be waiting for this client's screen, pop up a message
+ // on the console
+ bool wasInterested = client->isProjectionSource();
+ client->setProjectionSource(true);
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL)
+ continue;
+ if (c->desiredProjectionSource() == client->id())
+ {
+ wasInterested = true;
+ c->setDesiredProjectionSource(0);
+ }
+ }
+ if (wasInterested)
+ {
+ QMessageBox::information(this,
+ tr("Projection Error"),
+ tr("Could not send screen contents of %1 to other clients: VNC Startup failed.").arg(client->name())
+ );
+ }
+ }
+}
+
+void MainWindow::onVncClientStateChange(Client* client)
+{
+ // If the client started projection, ignore event. Also if
+ // the source is not known (anymore), we cannot do anything meaningful here
+ if (client->isActiveVncClient() || client->currentProjectionSource() == 0)
+ return;
+ // Client is not a vnc client anymore. Check if the VNC server it was (supposedly) connected
+ // to is still running, and kill it if there are no more clients connected to it.
+ // (client->currentProjectionSource() will still contain the id of the server it was connected to during this event)
+ // 1. check if vnc server is still running
+ // 2. check if there are any other clients "using" that server
+ bool inUse = false;
+ Client *server = NULL;
+ for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
+ {
+ Client *c = (**it).client();
+ if (c == NULL || c == client)
+ continue;
+ if (c->currentProjectionSource() == client->currentProjectionSource())
+ {
+ inUse = true;
+ break;
+ }
+ if (c->id() == client->currentProjectionSource())
+ server = c;
+ }
+ // 3. kill server if applicable
+ if (!inUse && server != NULL)
+ server->stopVncServer();
+}
diff --git a/src/server/mainwindow/mainwindow.h b/src/server/mainwindow/mainwindow.h
new file mode 100644
index 0000000..4f9d142
--- /dev/null
+++ b/src/server/mainwindow/mainwindow.h
@@ -0,0 +1,76 @@
+#ifndef _MAINWINDOW_H_
+#define _MAINWINDOW_H_
+
+#include <QtGui>
+#include <QMainWindow>
+
+class SessionNameWindow;
+class ConnectionFrame;
+class ListenServer;
+class DiscoveryListener;
+class Client;
+class ClientLogin;
+
+namespace Ui
+{
+ class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+private:
+ Ui::MainWindow *ui;
+ SessionNameWindow *_sessionNameWindow;
+ QLabel *_sessionNameLabel;
+ QList<ConnectionFrame*> _clientFrames;
+ int _tileWidth, _tileHeight;
+ int _tilesX, _tilesY;
+ int _tbIconSize;
+ Qt::ToolBarArea _tbArea;
+ int _timerId, _timerTimeout;
+ ListenServer *_listenServer;
+ DiscoveryListener *_discoveryListener;
+ qint64 _buttonBlockTime;
+
+ void placeFrameInFreeSlot(ConnectionFrame* frame);
+ ConnectionFrame* createFrame();
+ bool loadPosition(QSettings& settings, const QString& id, int& x, int& y);
+ void savePosition(ConnectionFrame *cf);
+
+public:
+ MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+protected:
+ void closeEvent(QCloseEvent *e);
+ void changeEvent(QEvent *e);
+ void resizeEvent(QResizeEvent *e);
+ void mouseReleaseEvent(QMouseEvent* e);
+ void timerEvent(QTimerEvent* event);
+
+
+protected slots:
+ // This
+ void onSessionNameClick();
+ void onSessionNameUpdate();
+ void onButtonSettings();
+ void onButtonChat();
+ void onButtonBroadcast();
+ void onButtonLock(bool checked);
+ void onButtonExit();
+ // connection frame
+ void onPlaceFrame(ConnectionFrame* frame);
+ void onFrameClicked(ConnectionFrame* frame);
+ // Net
+ void onClientConnected(Client* client);
+ void onClientAuthenticating(Client* client, ClientLogin* request);
+ void onClientAuthenticated(Client* client);
+ void onVncServerStateChange(Client* client);
+ void onVncClientStateChange(Client* client);
+
+};
+
+
+#endif
diff --git a/src/server/net/certmanager.cpp b/src/server/net/certmanager.cpp
new file mode 100644
index 0000000..e661a70
--- /dev/null
+++ b/src/server/net/certmanager.cpp
@@ -0,0 +1,92 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # src/util/CertManager.cpp
+ # - Manage SSL certificates
+ # - provide access by name
+ # -----------------------------------------------------------------------------
+ */
+
+#include "certmanager.h"
+#include "../util/util.h"
+#include <QMap>
+#include <QFileInfo>
+#include <QSettings>
+#include <cstdlib>
+
+namespace CertManager
+{
+static QMap<QString, QSslCertificate> _certs;
+static QMap<QString, QSslKey> _keys;
+
+static void generateFiles(QString& key, QString& cert);
+static bool loadFiles(QString& keyFile, QString& certFile, QSslKey &key, QSslCertificate &cert);
+
+bool getPrivateKeyAndCert(const QString &name, QSslKey &key, QSslCertificate &cert)
+{
+ if (_keys.contains(name))
+ {
+ key = _keys[name];
+ cert = _certs[name];
+ return true;
+ }
+ USER_SETTINGS(settings);
+ QString certFile = settings.fileName().append(".").append(name);
+ QString keyFile = certFile;
+ keyFile.append(".rsa");
+ certFile.append(".crt");
+ //
+ if (!loadFiles(keyFile, certFile, key, cert))
+ {
+ generateFiles(keyFile, certFile);
+ if (!loadFiles(keyFile, certFile, key, cert))
+ return false;
+ }
+ _certs.insert(name, cert);
+ _keys.insert(name, key);
+ return true;
+}
+
+static bool loadFiles(QString& keyFile, QString& certFile, QSslKey &key, QSslCertificate &cert)
+{
+ QFileInfo keyInfo(keyFile);
+ QFileInfo certInfo(certFile);
+ if (keyInfo.exists() && certInfo.exists())
+ { // Both files exist, see if they're valid and return
+ QFile kf(keyFile);
+ kf.open(QFile::ReadOnly);
+ key = QSslKey(&kf, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QList<QSslCertificate> certlist = QSslCertificate::fromPath(certFile);
+ if (!key.isNull() && !certlist.empty())
+ {
+ cert = certlist.first();
+ if (!cert.isNull())
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void generateFiles(QString& key, QString& cert)
+{
+ char tmp[1000];
+ unlink(key.toLocal8Bit().data());
+ unlink(cert.toLocal8Bit().data());
+ snprintf(tmp, 1000,
+ "openssl req -x509 -nodes -days 3650 -newkey rsa:1024 -subj '/C=DE/ST=BaWue/L=Freiburg/CN=openslx.org' -keyout \"%s\" -out \"%s\"",
+ key.toLocal8Bit().data(), cert.toLocal8Bit().data());
+ system(tmp);
+ snprintf(tmp, 1000, "chmod 0600 \"%s\" \"%s\"", key.toLocal8Bit().data(), cert.toLocal8Bit().data());
+ system(tmp);
+}
+}
diff --git a/src/server/net/certmanager.h b/src/server/net/certmanager.h
new file mode 100644
index 0000000..c69bc23
--- /dev/null
+++ b/src/server/net/certmanager.h
@@ -0,0 +1,29 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/util/CertManager.cpp
+# - Manage SSL certificates
+# - provide access by name
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef CERTMANAGER_H_
+#define CERTMANAGER_H_
+
+#include <QtNetwork/QSslCertificate>
+#include <QtNetwork/QSslKey>
+
+namespace CertManager
+{
+ bool getPrivateKeyAndCert(const QString &name, QSslKey &key, QSslCertificate &cert);
+}
+
+#endif /* CERTMANAGER_H_ */
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();
+ }
+}
diff --git a/src/server/net/client.h b/src/server/net/client.h
new file mode 100644
index 0000000..8004ebb
--- /dev/null
+++ b/src/server/net/client.h
@@ -0,0 +1,104 @@
+#ifndef CLIENT_H_
+#define CLIENT_H_
+
+#include <QtCore>
+#include <QSslError>
+#include <QAbstractSocket>
+#include "../../shared/networkmessage.h"
+
+class QSslSocket;
+
+struct ClientLogin
+{
+ bool accept;
+ QString name;
+ QString host;
+ QString ip;
+};
+
+typedef int ClientId;
+
+class Client : public QObject
+{
+ Q_OBJECT
+
+private:
+ static ClientId _clientIdCounter;
+
+ QSslSocket *_socket;
+ int _authed; // 0 = challenge sent, awaiting reply 1 = challenge ok, client challenge replied, awaiting login, 2 = ESTABLISHED
+ QString _name;
+ QString _host;
+ QString _ip;
+ QByteArray _challenge;
+ qint64 _pingTimeout;
+ NetworkMessage _toClient, _fromClient;
+ int _timerIdAuthTimeout;
+
+ ClientId _id; // this client's unique id
+
+ // If this client should be projected to from another client, the other client's id is set here. 0 otherwise.
+ // This is not currently used and it is questionable if this makes sense, as it might just be confusing if
+ // several groups students watch different other students.
+ // Also, visualizing such a situation in the GUI in a meaningful way would be hard.
+ ClientId _desiredProjectionSource;
+ // This boolean tells whether this client is currently the VNC broadcast source. This
+ // version only allows "one to all others" setups
+ bool _isProjectionSource;
+
+ ClientId _currentProjectionSource;
+
+ QString _vncRwPass, _vncRoPass;
+ int _vncPort;
+ bool _activeVncClient;
+
+ void handleMsg();
+ void disconnect();
+
+protected:
+ void timerEvent(QTimerEvent* event);
+
+public:
+ explicit Client(QSslSocket* socket);
+ ~Client();
+ void requestThumb(const int width, const int height);
+ void sendMessage(NetworkMessage& message);
+ //void acceptData();
+ const inline bool isAuthed() const { return _authed == 2; }
+ const inline QString& name() const { return _name; }
+ const inline QString& host() const { return _host; }
+ const inline QString& ip() const { return _ip; }
+ // The computer ID (used eg. for saving the frame positions) is currently the IP, but this is an extra method for easier modification later on
+ const inline QString& computerId() const { return _ip; }
+ const inline ClientId id() const { return _id; }
+
+ inline const QString& vncRwPass() const { return _vncRwPass; }
+ inline const QString& vncRoPass() const { return _vncRoPass; }
+ inline const int vncPort() const { return _vncPort; }
+ inline const bool isActiveVncClient() const { return _activeVncClient; }
+ inline const bool isActiveVncServer() const { return _vncPort > 0; }
+ inline const ClientId desiredProjectionSource() const { return _desiredProjectionSource; }
+ inline void setDesiredProjectionSource(ClientId source) { _desiredProjectionSource = source; }
+ inline const bool isProjectionSource() const { return _isProjectionSource; }
+ inline void setProjectionSource(bool enable) { _isProjectionSource = enable; }
+ inline const ClientId currentProjectionSource() const { return _currentProjectionSource; }
+ inline void setCurrentProjectionSource(ClientId source) { _currentProjectionSource = source; }
+ void startVncServer();
+ void stopVncServer();
+
+signals:
+ void authenticating(Client* client, ClientLogin* request);
+ void authenticated(Client* client);
+ void thumbUpdated(Client* client, const QPixmap& thumb);
+ void vncServerStateChange(Client* client);
+ void vncClientStateChange(Client* client);
+
+private slots:
+ void onSslErrors(const QList<QSslError> & errors); // triggered for errors that occur during SSL negotiation
+ void onDataArrival(); // triggered if data is available for reading
+ void onClosed(); // triggered if the socket is closed
+ void onError(QAbstractSocket::SocketError errcode); // triggered if an error occurs on the socket
+
+};
+
+#endif /* CLIENT_H_ */
diff --git a/src/server/net/discoverylistener.cpp b/src/server/net/discoverylistener.cpp
new file mode 100644
index 0000000..051a972
--- /dev/null
+++ b/src/server/net/discoverylistener.cpp
@@ -0,0 +1,165 @@
+/*
+ * discoverylistener.cpp
+ *
+ * Created on: 25.01.2013
+ * Author: sr
+ */
+
+#include "discoverylistener.h"
+#include "certmanager.h"
+#include "../util/global.h"
+#include "../../shared/settings.h"
+#include "../../shared/network.h"
+#include "../../shared/util.h"
+#include <QCryptographicHash>
+#include <QSslCertificate>
+#include <QSslKey>
+
+#define UDPBUFSIZ 9000
+#define SPAM_CUTOFF 50
+#define SPAM_MODERATE_INTERVAL 6787
+#define SPAM_MODERATE_AT_ONCE 100
+
+// static objects
+
+
+// +++ static ++++ hash ip address +++
+
+static quint16 hash(const QHostAddress& host)
+{
+ static quint16 seed1 = 0, seed2 = 0;
+ while (seed1 == 0) // Make sure the algorithm uses different seeds each time the program is
+ { // run to prevent hash collision attacks
+ seed1 = (quint16)(qrand() & 0xffff);
+ seed2 = (quint16)(qrand() & 0xffff);
+ }
+ quint8 data[16], len;
+ if (host.protocol() == QAbstractSocket::IPv4Protocol)
+ {
+ // IPv4
+ quint32 addr = host.toIPv4Address();
+ len = 4;
+ memcpy(data, &addr, len);
+ }
+ else if (QAbstractSocket::IPv6Protocol)
+ {
+ // IPv6
+ len = 16;
+ // Fast version (might break with future qt versions)
+ memcpy(data, host.toIPv6Address().c, len);
+ /* // --- Safe version (but better try to figure out a new fast way if above stops working ;))
+ Q_IPV6ADDR addr = host.toIPv6Address();
+ for (int i = 0; i < len; ++i)
+ data[i] = addr[i];
+ */
+ }
+ else
+ {
+ // Durr?
+ len = 2;
+ data[0] = (quint16)qrand();
+ data[1] = (quint16)qrand();
+ }
+ quint16 result = 0;
+ quint16 mod = seed1;
+ for (quint8 i = 0; i < len; ++i)
+ {
+ result = ((result << 1) + data[i]) ^ mod; // because of the shift this algo is not suitable for len(input) > 8
+ mod += seed2 + data[i];
+ }
+ return result;
+}
+
+// +++++++++++++++++++++++++++++++++++
+
+DiscoveryListener::DiscoveryListener() :
+ _socket(this), _counterResetPos(0)
+{
+ if (!_socket.bind(SERVICE_DISCOVERY_PORT))
+ qFatal("Could not bind to service discovery port %d", (int)SERVICE_DISCOVERY_PORT);
+ connect(&_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
+ for (int i = 0; i < SD_PACKET_TABLE_SIZE; ++i)
+ _packetCounter[i] = 0;
+ startTimer((SPAM_MODERATE_AT_ONCE * SPAM_MODERATE_INTERVAL) / SD_PACKET_TABLE_SIZE + 1);
+}
+
+DiscoveryListener::~DiscoveryListener()
+{
+ // TODO Auto-generated destructor stub
+}
+
+/**
+ * Overrides
+ */
+
+/**
+ * Decrease packet counters
+ */
+void DiscoveryListener::timerEvent(QTimerEvent* event)
+{
+ for (int i = 0; i < SPAM_MODERATE_AT_ONCE; ++i)
+ {
+ if (++_counterResetPos >= SD_PACKET_TABLE_SIZE)
+ _counterResetPos = 0;
+ if (_packetCounter[_counterResetPos] > 10)
+ _packetCounter[_counterResetPos] -= 10;
+ else if (_packetCounter[_counterResetPos] != 0)
+ _packetCounter[_counterResetPos] = 0;
+ }
+}
+
+/**
+ * Slots
+ */
+
+void DiscoveryListener::onReadyRead()
+{
+ char data[UDPBUFSIZ];
+ QHostAddress addr;
+ quint16 port;
+ while (_socket.hasPendingDatagrams())
+ {
+ const qint64 size = _socket.readDatagram(data, UDPBUFSIZ, &addr, &port);
+ if (size <= 0)
+ continue;
+ const quint16 bucket = hash(addr) % SD_PACKET_TABLE_SIZE;
+ if (_packetCounter[bucket] > SPAM_CUTOFF)
+ {
+ qDebug() << "SD: Potential (D)DoS from " << _socket.peerAddress().toString();
+ // emit some signal and pop up a big warning that someone is flooding/ddosing the PVS SD
+ // ... on the other hand, will the user understand? ;)
+ continue;
+ }
+ ++_packetCounter[bucket];
+ _packet.reset();
+ if (!_packet.readMessage(data, (quint32)size))
+ continue;
+ // Valid packet, process it:
+ const QByteArray iplist(_packet.getFieldBytes(_IPLIST));
+ const QByteArray hash(_packet.getFieldBytes(_HASH));
+ const QByteArray salt1(_packet.getFieldBytes(_SALT1));
+ const QByteArray salt2(_packet.getFieldBytes(_SALT2));
+ // For security, the salt has to be at least 16 bytes long
+ if (salt1.size() < 16 || salt2.size() < 16)
+ continue; // To make this more secure, you could remember the last X salts used, and ignore new packets using the same
+ // Check if the source IP of the packet matches any of the addresses given in the IP list
+ if (!Network::isAddressInList(QString::fromUtf8(iplist), addr.toString()))
+ continue;
+ // If so, check if the submitted hash seems valid
+ if (genSha1(&Global::sessionNameArray(), &salt1, &iplist) != hash)
+ continue; // did not match local session name
+ qDebug("Got matching discovery request...");
+ QByteArray myiplist(Network::interfaceAddressesToString().toUtf8());
+ QSslKey key;
+ QSslCertificate cert;
+ CertManager::getPrivateKeyAndCert("manager", key, cert);
+ QByteArray certhash(cert.digest(QCryptographicHash::Sha1));
+ // Reply to client
+ _packet.reset();
+ _packet.setField(_HASH, genSha1(&Global::sessionNameArray(), &salt2, &myiplist, &CLIENT_PORT_ARRAY, &certhash));
+ _packet.setField(_IPLIST, myiplist);
+ _packet.setField(_PORT, CLIENT_PORT_ARRAY);
+ _packet.setField(_CERT, certhash);
+ _packet.writeMessage(&_socket, addr, port);
+ }
+}
diff --git a/src/server/net/discoverylistener.h b/src/server/net/discoverylistener.h
new file mode 100644
index 0000000..64d4351
--- /dev/null
+++ b/src/server/net/discoverylistener.h
@@ -0,0 +1,40 @@
+/*
+ * discoverylistener.h
+ *
+ * Created on: 25.01.2013
+ * Author: sr
+ */
+
+#ifndef DISCOVERYLISTENER_H_
+#define DISCOVERYLISTENER_H_
+
+#include <QtCore>
+#include <QUdpSocket>
+#include "../../shared/networkmessage.h"
+
+#define SD_PACKET_TABLE_SIZE 20000
+
+class DiscoveryListener : public QObject
+{
+ Q_OBJECT
+
+private:
+ QUdpSocket _socket;
+ NetworkMessage _packet;
+ int _counterResetPos;
+
+ quint8 _packetCounter[SD_PACKET_TABLE_SIZE]; // count packets per source address to ignore spammers
+
+protected:
+ void timerEvent(QTimerEvent* event);
+
+public:
+ DiscoveryListener();
+ virtual ~DiscoveryListener();
+
+private slots:
+ void onReadyRead();
+
+};
+
+#endif /* DISCOVERYLISTENER_H_ */
diff --git a/src/server/net/listenserver.cpp b/src/server/net/listenserver.cpp
new file mode 100644
index 0000000..706962d
--- /dev/null
+++ b/src/server/net/listenserver.cpp
@@ -0,0 +1,40 @@
+#include "listenserver.h"
+#include "client.h"
+#include <QSslSocket>
+
+#define MAX_CLIENTS 50
+
+ListenServer::ListenServer(quint16 port)
+{
+ if (!_server.listen(QHostAddress::Any, port) || !_server.isListening())
+ qFatal("Cannot bind to TCP port %d (incoming SSL clients)", (int)port);
+ connect(&_server, SIGNAL(newConnection()), this, SLOT(newClientConnection()));
+}
+
+ListenServer::~ListenServer()
+{
+ _server.close();
+ for (int i = 0; i < _clients.size(); ++i)
+ _clients[i]->deleteLater();
+}
+
+/**
+ * Slots
+ */
+
+void ListenServer::newClientConnection()
+{
+ QSslSocket* sock;
+ while ((sock = (QSslSocket*)_server.nextPendingConnection()) != NULL)
+ {
+ if (_clients.size() >= MAX_CLIENTS)
+ {
+ sock->abort();
+ sock->deleteLater();
+ continue;
+ }
+ Client* client = new Client(sock);
+ _clients.append(client); // create new client class and add to list
+ emit newClient(client);
+ }
+}
diff --git a/src/server/net/listenserver.h b/src/server/net/listenserver.h
new file mode 100644
index 0000000..f237703
--- /dev/null
+++ b/src/server/net/listenserver.h
@@ -0,0 +1,30 @@
+#ifndef LISTENSERVER_H_
+#define LISTENSERVER_H_
+
+#include <QtCore>
+#include <QList>
+#include "sslserver.h"
+
+class Client;
+
+class ListenServer : public QObject
+{
+ Q_OBJECT
+
+private:
+ SslServer _server;
+ QList<Client*> _clients;
+
+public:
+ explicit ListenServer(quint16 port);
+ virtual ~ListenServer();
+
+private slots:
+ void newClientConnection();
+
+signals:
+ void newClient(Client* client);
+
+};
+
+#endif /* LISTENSERVER_H_ */
diff --git a/src/server/net/sslserver.cpp b/src/server/net/sslserver.cpp
new file mode 100644
index 0000000..70daea4
--- /dev/null
+++ b/src/server/net/sslserver.cpp
@@ -0,0 +1,113 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # src/net/SslServer.cpp
+ # - provide QTcpServer-like behaviour for SSL
+ # -----------------------------------------------------------------------------
+ */
+
+#include "sslserver.h"
+#include <QtNetwork/QSslCipher>
+#include <QtNetwork/QSslSocket>
+#include "certmanager.h"
+
+SslServer::SslServer()
+{
+ _tmr = startTimer(5123);
+ //QSslSocket::setDefaultCiphers(QSslSocket::supportedCiphers());
+}
+
+SslServer::~SslServer()
+{
+ killTimer((_tmr));
+}
+
+void SslServer::incomingConnection(int socketDescriptor)
+{
+ QSslSocket *serverSocket = new QSslSocket(NULL);
+ connect(serverSocket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(sslErrors(const QList<QSslError> &)));
+ QSslKey key;
+ QSslCertificate cert;
+ CertManager::getPrivateKeyAndCert("manager", key, cert);
+ serverSocket->setPrivateKey(key);
+ serverSocket->setLocalCertificate(cert);
+ serverSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
+ serverSocket->setProtocol(QSsl::SslV3);
+ //printf("Keylen %d\n", serverSocket->privateKey().length());
+ if (serverSocket->setSocketDescriptor(socketDescriptor))
+ {
+ // Once the connection is successfully encrypted, raise our newConnection event
+ connect(serverSocket, SIGNAL(encrypted()), this, SIGNAL(newConnection()));
+ serverSocket->startServerEncryption();
+ _pending.push_back(serverSocket);
+ }
+ else
+ {
+ serverSocket->deleteLater();
+ }
+}
+
+void SslServer::sslErrors(const QList<QSslError> & errors)
+{
+ //qDebug("FIXME: SSL ERRORS on SERVER: %s", qPrintable(errors.begin()->errorString()));
+}
+
+void SslServer::timerEvent(QTimerEvent* event)
+{
+ // Remove all sockets marked for deletion
+ while (!_delete.isEmpty())
+ {
+ QSslSocket *sock = _delete.takeFirst();
+ sock->blockSignals(true);
+ sock->deleteLater();
+ }
+ _delete = _pending;
+ _pending.clear();
+}
+
+bool SslServer::hasPendingConnections()
+{
+ for (QList<QSslSocket*>::iterator it(_pending.begin()); it != _pending.end(); it++)
+ {
+ qDebug("State: %d - Encrypted: %d", (int)(*it)->state(), (*it)->isEncrypted());
+ if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted())
+ return true;
+ }
+ return false;
+}
+
+QTcpSocket* SslServer::nextPendingConnection()
+{
+ for (QList<QSslSocket*>::iterator it(_pending.begin()); it != _pending.end(); it++)
+ {
+ if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted())
+ {
+ QSslSocket *sock = *it;
+ QObject::disconnect(sock, SIGNAL(encrypted()), this, SIGNAL(newConnection()));
+ _pending.removeAll(sock);
+ _delete.removeAll(sock);
+ return sock;
+ }
+ }
+ for (QList<QSslSocket*>::iterator it(_delete.begin()); it != _delete.end(); it++)
+ {
+ if ((*it)->state() == QAbstractSocket::ConnectedState && (*it)->isEncrypted())
+ {
+ QSslSocket *sock = *it;
+ QObject::disconnect(sock, SIGNAL(encrypted()), this, SIGNAL(newConnection()));
+ _pending.removeAll(sock);
+ _delete.removeAll(sock);
+ return sock;
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/server/net/sslserver.h b/src/server/net/sslserver.h
new file mode 100644
index 0000000..400bf6a
--- /dev/null
+++ b/src/server/net/sslserver.h
@@ -0,0 +1,50 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/SslServer.cpp
+# - provide QTcpServer-like behaviour for SSL
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef SSLSERVER_H_
+#define SSLSERVER_H_
+
+#include <QtCore/QList>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QSslError>
+
+class QSslSocket;
+
+class SslServer : public QTcpServer
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void sslErrors ( const QList<QSslError> & errors );
+
+public:
+ SslServer();
+ virtual ~SslServer();
+
+ bool hasPendingConnections ();
+ // This one has to return a TcpSocket as we're overwriting from the base class
+ // just cast it to QSslSocket later
+ virtual QTcpSocket* nextPendingConnection();
+
+protected:
+ void incomingConnection(int socketDescriptor);
+ void timerEvent (QTimerEvent* event);
+ QList<QSslSocket*> _pending;
+ QList<QSslSocket*> _delete;
+ int _tmr;
+};
+
+#endif /* SSLSERVER_H_ */
diff --git a/src/server/sessionnamewindow/sessionnamewindow.cpp b/src/server/sessionnamewindow/sessionnamewindow.cpp
new file mode 100644
index 0000000..1d8c699
--- /dev/null
+++ b/src/server/sessionnamewindow/sessionnamewindow.cpp
@@ -0,0 +1,68 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # mainWindow.cpp
+ This is the Main class for the pvsManager. The GUI is contructed here.
+ # -----------------------------------------------------------------------------
+ */
+
+#include <QtGui>
+#include "sessionnamewindow.h"
+#include "../util/global.h"
+
+#include "ui_sessionname.h"
+
+SessionNameWindow::SessionNameWindow(QWidget *parent) :
+ QDialog(parent), ui(new Ui::SessionName)
+
+{
+ ui->setupUi(this);
+ connect(ui->bboxOkCancel, SIGNAL(accepted()), this, SLOT(onOkClicked()));
+ connect(ui->bboxOkCancel, SIGNAL(rejected()), this, SLOT(close()));
+ connect(ui->cmdRandom, SIGNAL(clicked(bool)), this, SLOT(onGenerateRandomName()));
+}
+
+SessionNameWindow::~SessionNameWindow()
+{
+ delete ui;
+}
+
+void SessionNameWindow::show(const QString& name)
+{
+ ui->txtName->setText(name);
+ this->showNormal();
+}
+
+/*
+ * Overridden methods
+ */
+
+void SessionNameWindow::closeEvent(QCloseEvent *e)
+{
+ e->ignore();
+ this->hide();
+}
+
+/**
+ * Slots
+ */
+
+void SessionNameWindow::onOkClicked()
+{
+ Global::setSessionName(ui->txtName->text());
+ emit updateSessionName();
+ this->hide();
+}
+
+void SessionNameWindow::onGenerateRandomName()
+{
+ ui->txtName->setText(QString::number(qrand() % 9000 + 1000));
+}
diff --git a/src/server/sessionnamewindow/sessionnamewindow.h b/src/server/sessionnamewindow/sessionnamewindow.h
new file mode 100644
index 0000000..ed944c1
--- /dev/null
+++ b/src/server/sessionnamewindow/sessionnamewindow.h
@@ -0,0 +1,38 @@
+#ifndef _SESSIONNAMEWINDOW_H_
+#define _SESSIONNAMEWINDOW_H_
+
+#include <QtGui>
+
+
+namespace Ui
+{
+class SessionName;
+}
+
+class SessionNameWindow : public QDialog
+{
+ Q_OBJECT
+
+private:
+ Ui::SessionName *ui;
+
+public:
+ SessionNameWindow(QWidget *parent = 0);
+ ~SessionNameWindow();
+
+ void show(const QString& name);
+
+protected:
+ void closeEvent(QCloseEvent *e);
+
+private slots:
+ void onOkClicked();
+ void onGenerateRandomName();
+
+signals:
+ void updateSessionName();
+
+};
+
+
+#endif
diff --git a/src/server/util/global.cpp b/src/server/util/global.cpp
new file mode 100644
index 0000000..9b7b4f2
--- /dev/null
+++ b/src/server/util/global.cpp
@@ -0,0 +1,17 @@
+/*
+ * global.cpp
+ *
+ * Created on: 29.01.2013
+ * Author: sr
+ */
+
+#include "global.h"
+
+QString Global::_sessionName = QString();
+QByteArray Global::_sessionNameArray = QByteArray();
+
+void Global::setSessionName(const QString& name)
+{
+ Global::_sessionName = name;
+ Global::_sessionNameArray = name.toUtf8();
+}
diff --git a/src/server/util/global.h b/src/server/util/global.h
new file mode 100644
index 0000000..38eec6d
--- /dev/null
+++ b/src/server/util/global.h
@@ -0,0 +1,29 @@
+/*
+ * global.h
+ *
+ * Created on: 29.01.2013
+ * Author: sr
+ */
+
+#ifndef GLOBAL_H_
+#define GLOBAL_H_
+
+#include <QString>
+#include <QByteArray>
+
+class Global
+{
+private:
+ Global(){}
+ ~Global(){}
+
+ static QString _sessionName;
+ static QByteArray _sessionNameArray;
+
+public:
+ static const QString& sessionName() { return Global::_sessionName; }
+ static const QByteArray& sessionNameArray() { return Global::_sessionNameArray; }
+ static void setSessionName(const QString& name);
+};
+
+#endif /* GLOBAL_H_ */
diff --git a/src/server/util/util.cpp b/src/server/util/util.cpp
new file mode 100644
index 0000000..7ff9404
--- /dev/null
+++ b/src/server/util/util.cpp
@@ -0,0 +1,14 @@
+/*
+ * Util.cpp
+ *
+ * Created on: 18.01.2013
+ * Author: sr
+ */
+
+#include "util.h"
+
+namespace Util
+{
+
+}
+
diff --git a/src/server/util/util.h b/src/server/util/util.h
new file mode 100644
index 0000000..e2d6a62
--- /dev/null
+++ b/src/server/util/util.h
@@ -0,0 +1,17 @@
+#ifndef UTIL_H_
+#define UTIL_H_
+
+// Helper for getting a settings object in various places, so if you ever change the organization, location,
+// file format or anything, you won't have to edit in 100 places.
+// Use like this:
+// USER_SETTINGS(settings)
+// settings.value("somekey")
+#define USER_SETTINGS(name) QSettings name (QSettings::IniFormat, QSettings::UserScope, "openslx", "pvs2mgr")
+#define SYSTEM_SETTINGS(name) QSettings name (QSettings::IniFormat, QSettings::SystemScope, "openslx", "pvs2mgr")
+
+namespace Util
+{
+
+}
+
+#endif /* UTIL_H_ */
diff --git a/src/shared/network.cpp b/src/shared/network.cpp
new file mode 100644
index 0000000..6dd6a73
--- /dev/null
+++ b/src/shared/network.cpp
@@ -0,0 +1,50 @@
+/*
+ * network.cpp
+ *
+ * Created on: 28.01.2013
+ * Author: sr
+ */
+
+#include "network.h"
+#include <QNetworkInterface>
+#include <QHostAddress>
+#include <QList>
+
+namespace Network
+{
+
+
+/**
+ * Returns list of all addresses assigned to the interfaces of this machine.
+ * Every address is surrounded by '|', eg:
+ * |1.2.3.4|3.66.77.88|123:34::1|
+ */
+QString interfaceAddressesToString()
+{
+ QList<QHostAddress> list(QNetworkInterface::allAddresses());
+ return interfaceAddressesToString(list);
+}
+
+QString interfaceAddressesToString(const QList<QHostAddress>& list)
+{
+ QString ret;
+ ret.reserve(500);
+ for (QList<QHostAddress>::const_iterator it(list.begin()); it != list.end(); ++it)
+ {
+ if (*it == QHostAddress::LocalHost || *it == QHostAddress::LocalHostIPv6)
+ continue; // TODO: Filter other addresses/ranges?
+ ret.append("|");
+ ret.append(it->toString());
+ }
+ ret.append("|");
+ return ret;
+}
+
+bool isAddressInList(const QString& list, const QString& address)
+{
+ static const QString find("|%1|");
+ return list.contains(find.arg(address), Qt::CaseSensitive);
+}
+
+
+}
diff --git a/src/shared/network.h b/src/shared/network.h
new file mode 100644
index 0000000..4a763be
--- /dev/null
+++ b/src/shared/network.h
@@ -0,0 +1,25 @@
+/*
+ * network.h
+ *
+ * Created on: 28.01.2013
+ * Author: sr
+ */
+
+#ifndef NETWORK_H_
+#define NETWORK_H_
+
+#include <QString>
+#include <QHostAddress>
+
+namespace Network
+{
+
+
+QString interfaceAddressesToString();
+QString interfaceAddressesToString(const QList<QHostAddress>& list);
+bool isAddressInList(const QString& list, const QString& address);
+
+
+}
+
+#endif /* NETWORK_H_ */
diff --git a/src/shared/networkmessage.cpp b/src/shared/networkmessage.cpp
new file mode 100644
index 0000000..9a7ba9f
--- /dev/null
+++ b/src/shared/networkmessage.cpp
@@ -0,0 +1,296 @@
+/*
+ * NetworkMessage.cpp
+ *
+ * Created on: 18.01.2013
+ * Author: sr
+ */
+
+#include <QtCore>
+#include <QtNetwork>
+#include "networkmessage.h"
+
+#define HEADER_LEN 8
+#define MAX_MSG_LEN 60000
+
+#define BYTE_SWAP4(x) \
+ (((x & 0xFF000000) >> 24) | \
+ ((x & 0x00FF0000) >> 8) | \
+ ((x & 0x0000FF00) << 8) | \
+ ((x & 0x000000FF) << 24))
+
+#define BYTE_SWAP2(x) \
+ (((x & 0xFF00) >> 8) | \
+ ((x & 0x00FF) << 8))
+
+static quint16 _htons(quint16 x)
+{
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ return x;
+ return BYTE_SWAP2(x);
+}
+
+static quint16 _ntohs(quint16 x)
+{
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ return x;
+ return BYTE_SWAP2(x);
+}
+
+static quint32 _htonl(quint32 x)
+{
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ return x;
+ return BYTE_SWAP4(x);
+}
+
+static quint32 _ntohl(quint32 x)
+{
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ return x;
+ return BYTE_SWAP4(x);
+}
+
+/*
+ // ####################################################### \\ ° . °
+ \\ ####################################################### // \___/
+ */
+
+NetworkMessage::NetworkMessage() :
+ _buffer(NULL), _bufferSize(0), _bufferPos(0), _lastBufferSize(0), _mode(0)
+{
+ //
+}
+
+NetworkMessage::~NetworkMessage()
+{
+ if (_buffer)
+ delete[] _buffer;
+}
+
+inline void NetworkMessage::allocBuffer()
+{
+ if (_lastBufferSize < _bufferSize || _buffer == NULL)
+ {
+ if (_buffer)
+ delete[] _buffer;
+ _lastBufferSize = _bufferSize;
+ _buffer = new char[_lastBufferSize];
+ }
+}
+
+bool NetworkMessage::readMessage(QAbstractSocket* socket)
+{
+ // Check/Set the _mode variable, so read and write calls are not mixed
+ if (_mode != 1)
+ {
+ if (_mode != 0)
+ {
+ qDebug("NetworkMessage::readMessage(TCP) called when class was in mode %d!", _mode);
+ return false;
+ }
+ _mode = 1;
+ }
+ // buffer size == 0 means the header hasn't been received yet. do so and set things up
+ if (_bufferSize == 0)
+ {
+ if (socket->bytesAvailable() < HEADER_LEN)
+ return true;
+ char header[HEADER_LEN];
+ if (socket->read(header, HEADER_LEN) != HEADER_LEN)
+ {
+ qDebug("FIXME: Socket said 8 bytes available, but could not read 8...");
+ return false;
+ }
+ if (!this->parseHeader(header))
+ return false;
+ //qDebug() << "Expecting message of " << _bufferSize << " bytes";
+ allocBuffer();
+ }
+ if (_bufferSize > _bufferPos)
+ {
+ while (_bufferSize > _bufferPos && socket->bytesAvailable() > 0)
+ {
+ const qint64 ret = socket->read(_buffer + _bufferPos, _bufferSize - _bufferPos);
+ //qDebug() << "Read " << ret << " bytes";
+ if (ret < 0)
+ return false;
+ _bufferPos += ret;
+ //qDebug() << "Buffer has now " << _bufferPos << " of " << _bufferSize << " bytes";
+ }
+ if (_bufferSize == _bufferPos)
+ {
+ if (!this->parseMessage(_buffer))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool NetworkMessage::readMessage(char* data, quint32 len)
+{
+ // Check/Set the _mode variable, so read and write calls are not mixed
+ if (_mode != 1)
+ {
+ if (_mode != 0)
+ {
+ qDebug("NetworkMessage::readMessage(UDP) called when class was in mode %d!", _mode);
+ return false;
+ }
+ _mode = 1;
+ }
+ if (len < HEADER_LEN)
+ {
+ qDebug("UDP message shorter than 8 bytes. ignored.");
+ return false;
+ }
+ if (!this->parseHeader(data))
+ return false;
+ if (len != _bufferSize + HEADER_LEN)
+ {
+ qDebug("UDP packet has wrong size. Is %d, expected %d", (int)_bufferSize, len - HEADER_LEN);
+ return false;
+ }
+ return this->parseMessage(data + HEADER_LEN);
+}
+
+bool NetworkMessage::parseHeader(char *header)
+{
+ if (header[0] != 'P' || header[1] != 'V' || header[2] != 'S' || header[3] != '2')
+ {
+ qDebug("Protocol magic wrong.");
+ return false;
+ }
+ _bufferPos = 0;
+ _bufferSize = _ntohl(*(quint32*)(header + 4));
+ if (_bufferSize > MAX_MSG_LEN)
+ {
+ qDebug("Disconnecting Client: MAX_MSG_LEN exceeded.");
+ return false;
+ }
+ if (_bufferSize < 4) // TODO: magic number. msg needs to be at least 4 bytes for 1 key/value pair of length 0 each.
+ {
+ qDebug("A Client sent an empty message.");
+ return false;
+ }
+ return true;
+}
+
+bool NetworkMessage::parseMessage(char *buffer)
+{
+ char *ptr = buffer;
+ while (_bufferSize - (ptr - buffer) >= 4)
+ {
+ const quint16 keyLen = _ntohs(*(quint16*)(ptr));
+ ptr += 2;
+ const quint16 valLen = _ntohs(*(quint16*)(ptr));
+ ptr += 2;
+ if (_bufferSize - (ptr - buffer) < keyLen + valLen)
+ {
+ qDebug() << "Warning: Error parsing message. key(" << keyLen << ")+value(" << valLen
+ << ") length > total remaining bytes (" << (_bufferSize - (ptr - buffer)) << ")";
+ return false;
+ }
+ _fields.insert(QString::fromUtf8(ptr, keyLen), QByteArray(ptr + keyLen, valLen));
+ //qDebug() << "Got " << QString::fromUtf8(ptr, keyLen) << " -> " << QString::fromUtf8(ptr + keyLen, valLen);
+ ptr += keyLen + valLen;
+ }
+ _mode = 3;
+ return true;
+}
+
+bool NetworkMessage::writeMessage(QAbstractSocket* socket)
+{
+ if (_mode != 2)
+ {
+ if (_mode == 1)
+ {
+ qDebug("NetworkMessage::writeMessage called when class was in mode %d!", _mode);
+ return false;
+ }
+ _mode = 1;
+ _bufferPos = 0;
+ }
+ // key/value pairs have not been serialized yet...
+ if (_bufferSize == 0)
+ {
+ this->serializeMessage();
+ }
+ const qint64 ret = socket->write(_buffer + _bufferPos, _bufferSize - _bufferPos);
+ if (ret == 0)
+ return true;
+ if (ret < 0)
+ return false;
+ _bufferPos += ret;
+ if (_bufferPos == _bufferSize)
+ {
+ _bufferPos = 0;
+ _mode = 4;
+ }
+ return true;
+}
+
+bool NetworkMessage::writeMessage(QUdpSocket* socket, const QHostAddress& address, quint16 port)
+{
+ if (_mode != 4)
+ {
+ if (_mode == 1)
+ {
+ qDebug("NetworkMessage::writeMessage called when class was in mode %d!", _mode);
+ return false;
+ }
+ _mode = 4;
+ _bufferPos = 0;
+ }
+ // key/value pairs have not been serialized yet...
+ if (_bufferSize == 0)
+ {
+ this->serializeMessage();
+ }
+ const qint64 ret = socket->writeDatagram(_buffer, _bufferSize, address, port);
+ if (ret != _bufferSize)
+ return false;
+ if (ret < 0)
+ return false;
+ _bufferPos = 0;
+ return true;
+}
+
+void NetworkMessage::serializeMessage()
+{
+ QByteArray buf;
+ //qDebug() << "Default size: " << buf.capacity();
+ buf.reserve(_lastBufferSize > 0 ? _lastBufferSize : 200);
+ for (QHash<QString, QByteArray>::const_iterator it = _fields.begin(); it != _fields.end(); ++it)
+ {
+ const QByteArray &ba = it.key().toUtf8();
+ const QByteArray &val = it.value();
+ quint16 keyLen = _htons((quint16)ba.size());
+ quint16 valLen = _htons((quint16)val.size());
+ //qDebug() << "Adding to msg(" << ba.size() << "/" << val.size() << "): " << ba << " -> " << val;
+ buf.append((char*)&keyLen, 2);
+ buf.append((char*)&valLen, 2);
+ buf.append(ba);
+ buf.append(val);
+ }
+ _bufferSize = buf.length() + HEADER_LEN;
+ allocBuffer();
+ memcpy(_buffer + HEADER_LEN, buf.data(), buf.size());
+ _buffer[0] = 'P';
+ _buffer[1] = 'V';
+ _buffer[2] = 'S';
+ _buffer[3] = '2';
+ const quint32 flipped = _htonl(_bufferSize - HEADER_LEN);
+ memcpy(_buffer + 4, &flipped, 4);
+}
+
+void NetworkMessage::buildErrorMessage(const QString& error)
+{
+ this->reset();
+ this->setField(_ID, _ERROR);
+ this->setField(_ERROR, error);
+}
+
+void NetworkMessage::buildErrorMessage(const char* error)
+{
+ this->buildErrorMessage(QString::fromUtf8(error));
+}
diff --git a/src/shared/networkmessage.h b/src/shared/networkmessage.h
new file mode 100644
index 0000000..5a8b2c2
--- /dev/null
+++ b/src/shared/networkmessage.h
@@ -0,0 +1,69 @@
+/*
+ * NetworkMessage.h
+ *
+ * Created on: 18.01.2013
+ * Author: sr
+ */
+
+#ifndef NETWORKMESSAGE_H_
+#define NETWORKMESSAGE_H_
+
+#include <QtCore>
+
+class QAbstractSocket;
+class QUdpSocket;
+
+// define qstrings for message ids. this prevents implicit instanciation of the same qstrings over and over again
+#define MSGTYPE(name) static const QString _ ## name ( #name )
+MSGTYPE(ID);
+MSGTYPE(IMG);
+MSGTYPE(LOGIN);
+MSGTYPE(THUMB);
+MSGTYPE(X);
+MSGTYPE(Y);
+MSGTYPE(VNCSERVER);
+MSGTYPE(VNCCLIENT);
+MSGTYPE(LOCK);
+MSGTYPE(HASH);
+MSGTYPE(SALT1);
+MSGTYPE(SALT2);
+MSGTYPE(IPLIST);
+MSGTYPE(PORT);
+MSGTYPE(CERT);
+MSGTYPE(CHALLENGE);
+MSGTYPE(ERROR);
+
+class NetworkMessage
+{
+private:
+ char *_buffer;
+ quint32 _bufferSize, _bufferPos, _lastBufferSize;
+ QHash<QString, QByteArray> _fields;
+ int _mode; // 0 = none, 1 = reading, 2 = writing, 3 = read complete, 4 = write complete
+
+ void allocBuffer();
+ bool parseHeader(char *header);
+ bool parseMessage(char *buffer);
+ void serializeMessage();
+
+public:
+ NetworkMessage();
+ virtual ~NetworkMessage();
+ bool readMessage(QAbstractSocket* socket);
+ bool readMessage(char* data, quint32 len);
+ bool writeMessage(QAbstractSocket* socket);
+ bool writeMessage(QUdpSocket* socket, const QHostAddress& address, quint16 port);
+ void reset() { _fields.clear(); _bufferSize = 0; _mode = 0; }
+ const bool readComplete() const { return _mode == 3; }
+ const bool writeComplete() const { return _mode == 4; }
+ const bool hasField(const QString& key) const { return _fields.contains(key); }
+ const QString getFieldString(const QString& key) const { return QString::fromUtf8(_fields.value(key)); }
+ const QByteArray getFieldBytes(const QString& key) const { return _fields.value(key); }
+ void setField(const QString& key, const QByteArray& value) { if (_mode == 1 || _mode == 2) qFatal("setField called in bad state."); _fields.insert(key, value); _mode = 0; }
+ void setField(const QString& key, const QString& value) { setField(key, value.toUtf8()); }
+ // Convenience
+ void buildErrorMessage(const QString& error);
+ void buildErrorMessage(const char* error);
+};
+
+#endif /* NETWORKMESSAGE_H_ */
diff --git a/src/shared/settings.h b/src/shared/settings.h
new file mode 100644
index 0000000..c1c45c1
--- /dev/null
+++ b/src/shared/settings.h
@@ -0,0 +1,12 @@
+#ifndef _SETTINGS_H_
+#define _SETTINGS_H_
+
+#define CLIENT_PORT 5194
+static const QString CLIENT_PORT_STR(QString::number(CLIENT_PORT));
+static const QByteArray CLIENT_PORT_ARRAY(QString::number(CLIENT_PORT).toUtf8());
+
+#define SERVICE_DISCOVERY_PORT 3492
+
+#define PING_TIMEOUT_MS 30000
+
+#endif
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
new file mode 100644
index 0000000..d5c101e
--- /dev/null
+++ b/src/shared/util.cpp
@@ -0,0 +1,22 @@
+/*
+ * util.cpp
+ *
+ * Created on: 30.01.2013
+ * Author: sr
+ */
+
+#include "util.h"
+#include <QCryptographicHash>
+
+static QCryptographicHash sha1(QCryptographicHash::Sha1);
+
+QByteArray genSha1(const QByteArray* a, const QByteArray* b, const QByteArray* c, const QByteArray* d, const QByteArray* e)
+{
+ sha1.reset();
+ sha1.addData(*a);
+ if (b) sha1.addData(*b);
+ if (c) sha1.addData(*c);
+ if (d) sha1.addData(*d);
+ if (e) sha1.addData(*e);
+ return sha1.result();
+}
diff --git a/src/shared/util.h b/src/shared/util.h
new file mode 100644
index 0000000..c2e5145
--- /dev/null
+++ b/src/shared/util.h
@@ -0,0 +1,15 @@
+/*
+ * util.h
+ *
+ * Created on: 30.01.2013
+ * Author: sr
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <QByteArray>
+
+QByteArray genSha1(const QByteArray* a, const QByteArray* b = NULL, const QByteArray* c = NULL, const QByteArray* d = NULL, const QByteArray* e = NULL);
+
+#endif /* UTIL_H_ */