/* * 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 #include #include #include #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), _isTutor(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 &)), this, SLOT(onSslErrors(const QList &))); 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 & 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; } // Not authed yet, only care about login requests if (_authed == 1) { 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; } // Did not pass challenge yet 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, __TRUE); sendMessage(_toClient); } void Client::stopVncServer() { _toClient.reset(); _toClient.setField(_ID, _VNCSERVER); _toClient.setField(_ENABLE, __FALSE); sendMessage(_toClient); } void Client::stopVncClient() { _toClient.reset(); _toClient.setField(_ID, _VNCCLIENT); sendMessage(_toClient); } void Client::setTutor(bool enable) { _toClient.reset(); _toClient.setField(_ID, _TUTOR); _toClient.setField(_ENABLE, _BOOL(enable)); sendMessage(_toClient); _isTutor = enable; } void Client::disconnect() { if (_socket != NULL) { qDebug("*** Client %s disconnected.", qPrintable(_ip)); _socket->blockSignals(true); _socket->abort(); _socket->deleteLater(); _socket = NULL; this->deleteLater(); } }