summaryrefslogblamecommitdiffstats
path: root/src/server/net/client.cpp
blob: 9422e81165cbfed5d33c5be0deaa42202cafdba9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                  
                                 
                                                                                
                                    
                                                                                                              
                                                                                          


                                 


                                                          







                                                                     



                                               



                                                  

                                                      
                                                                               
                                                                             

 
                                                                                



                            
                                                              




                                                            
                                                                                

                                           









                                                                             





                                                                     















                                                                                             

 
                                                                                










                                                                                             
                                                                                






                                                                                   




                                                     

 
                                                                                

                            



















                                                                                                
                                            

                                            



                 
                                                                                

                        
                                                                             





                                                                         
                                                





















                                                                                           
                                                                                                                                    

                                                                        



                                                                                                    




                                                                                  
                                                                                            







                                                                                                        
                                                                                              
 
                                                                                             
 
                                              
                         

                                                                      
                                                             

                                                                                                                     
                            
                         

                                                                      
                                                                 

                                                                                                                       

                                                              



                       

                                                         























                                                                          


                                                       




                                                 
                                     








                                                                                      


                                                                                     
                                                   


                                                            



                                                                                                   







                                                                                
                                                                                


                             



                                      

 
                                                                                

                            



                                       

 
                                                                                

                            
                                 


                                      

 
                                                                                


                                                                                          







                                                      




                                                                                



                                                       


                                                                                

                                  



                                             


                          
                                                                                

                         
                              
         
                                               


                                                                       
                                    

         
/*
 * 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

int Client::_clientIdCounter = 0;
/******************************************************************************/
Client::Client(QSslSocket* socket) :
	_socket(socket), _authed(0), _timerDelete(0), _desiredProjectionSource(0), _isProjectionSource(false),
	_currentProjectionSource(0), _vncPort(0), _activeVncClient(false), _isTutor(false)
{
	assert(socket != NULL);
	_id = ++_clientIdCounter;
	_ip = _socket->peerAddress().toString();
	qDebug("*** Client %s created.", qPrintable(_ip));
	// Connect important signals
	connect(_socket, SIGNAL(disconnected()),
					this, SLOT(disconnect()));
	connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)),
					this, SLOT(disconnect()));
	connect(_socket, SIGNAL(sslErrors(const QList<QSslError> &)),
					this, SLOT(disconnect()));
	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;
	NetworkMessage msgChlng;
	msgChlng.setField(_ID, _CHALLENGE);
	msgChlng.setField(_CHALLENGE, _challenge);
	msgChlng.writeMessage(_socket);
	// give client 3 seconds to complete handshake
	_timerIdAuthTimeout = startTimer(3000);
	_timerPingTimeout = startTimer(600000); // for debugging purposes 10min
	_pingTimeout = QDateTime::currentMSecsSinceEpoch() + PING_TIMEOUT_MS;
}

/******************************************************************************/
Client::~Client()
{
	if (_socket != NULL)
	{
		qCritical("**** SOCKET DELETE IN DESTRUCTOR");
		_socket->deleteLater();
	}
	qDebug("*** Client %s destroyed.", qPrintable(_ip));
}

/******************************************************************************/
void Client::timerEvent(QTimerEvent* event)
{
	if (event->timerId() == _timerPingTimeout)
	{
		if (_pingTimeout < QDateTime::currentMSecsSinceEpoch())
		{
			qDebug() << "Client" << _ip << "has a ping timeout.";
			killTimer(_timerPingTimeout);
			this->disconnect();
		}
	}
	else if (event->timerId() == _timerIdAuthTimeout)
	{
		// Client did not send login request within 3 seconds
		killTimer(_timerIdAuthTimeout);
		_timerIdAuthTimeout = 0;
		this->disconnect();
	}
	else if (event->timerId() == _timerDelete)
	{
		if (_socket == NULL || _socket->state() == QAbstractSocket::UnconnectedState)
		{
			if (_socket != NULL)
				_socket->deleteLater();
			_socket = NULL;
			killTimer(_timerDelete);
			this->deleteLater();
			return;
		}
		_socket->abort();
		qDebug("A socket is still pending...");
	}
	else
		killTimer(event->timerId());
}

/******************************************************************************/
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;
	}
	NetworkMessage msgTmb;
	msgTmb.setField(_ID, _THUMB);
	msgTmb.setField(_X, QString::number(width));
	msgTmb.setField(_Y, QString::number(height));
	msgTmb.writeMessage(_socket);
}

/******************************************************************************/
void Client::onDataArrival()
{
	//
	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();
			_fromClient.reset();
			if (_socket == NULL)
				return;
		}
	}
}

/******************************************************************************/
void Client::handleMsg()
{
	_pingTimeout = QDateTime::currentMSecsSinceEpoch() + PING_TIMEOUT_MS;
	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+_vncPort << ") failed.";
					// TODO: Show message on manager
				}
				else
				{
					qDebug() << "Client " << _name << " stopped its VNC server";
				}
			}
			else
			{
				_vncRoPass = _fromClient.getFieldString("ROPASS");
				_vncRwPass = _fromClient.getFieldString("RWPASS");
				qDebug() << "Client " << _name << " started its VNC server";
			}
			_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 int other = (int)_fromClient.getFieldString("CLIENTID").toInt();

			const int last = _activeVncClient ? _currentProjectionSource : other;

			if (!_activeVncClient)
			{
				if (other == _desiredProjectionSource)
					_desiredProjectionSource = 0;
				_currentProjectionSource = 0;
				qDebug() << "Client " << _name << " stopped its VNC client (watched " << last << ")";
			}
			else
			{
				if (other == _desiredProjectionSource)
					_desiredProjectionSource = 0;
				_currentProjectionSource = other;
				qDebug() << "Client " << _name << " started its VNC client (watching " << other << ")";
			}

			emit vncClientStateChange(this, last);
		}
		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;
			NetworkMessage msgLogin;
			msgLogin.setField(_ID, _LOGIN);
			msgLogin.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
				NetworkMessage msgErr;
				msgErr.buildErrorMessage("Challenge reply invalid.");
				msgErr.writeMessage(_socket);
				this->disconnect();
				return;
			}
			// Now answer to challenge by client
			NetworkMessage msgChlng;
			msgChlng.setField(_ID, _CHALLENGE);
			msgChlng.setField(_HASH, genSha1(&Global::sessionNameArray(), &challenge));
			msgChlng.writeMessage(_socket);
			_authed = 1;
			qDebug("client's challenge reply was valid, step <- 1");
		}
		return;
	}

}

/******************************************************************************/
void Client::startVncServer()
{
	_vncPort = 0;
	NetworkMessage msg;
	msg.setField(_ID, _VNCSERVER);
	msg.setField(_ENABLE, __TRUE);
	sendMessage(msg);
}

/******************************************************************************/
void Client::stopVncServer()
{
	NetworkMessage msg;
	msg.setField(_ID, _VNCSERVER);
	msg.setField(_ENABLE, __FALSE);
	sendMessage(msg);
}

/******************************************************************************/
void Client::stopVncClient()
{
	_activeVncClient = false;
	NetworkMessage msg;
	msg.setField(_ID, _VNCCLIENT);
	sendMessage(msg);
}

/******************************************************************************/
void Client::startVncClient(QString host, int port, QString pass, int id, QString caption)
{
	_activeVncClient = false;
	NetworkMessage msg;
	msg.setField(_ID, _VNCCLIENT);
	msg.setField("HOST", host);
	msg.setField("PORT", QString::number(port));
	msg.setField("ROPASS", pass);
	msg.setField("CLIENTID", QString::number(id));
	msg.setField("CAPTION", caption);
	sendMessage(msg);
}

/******************************************************************************/
void Client::lockScreen(bool lock)
{
	NetworkMessage msg;
	msg.setField(_ID, _LOCK);
	msg.setField(_ENABLE, lock ? __TRUE : __FALSE);
	sendMessage(msg);
}

/******************************************************************************/
void Client::setTutor(bool enable)
{
	NetworkMessage msg;
	msg.setField(_ID, _TUTOR);
	msg.setField(_ENABLE, _BOOL(enable));
	sendMessage(msg);
	_isTutor = enable;
}

/******************************************************************************/
void Client::disconnect()
{
	if (_timerDelete == 0)
	{
		_timerDelete = startTimer(500);
		qDebug("*** Client %s disconnected.", qPrintable(_ip));
		_socket->blockSignals(true);
		_socket->abort();
		emit disconnected();
	}
}