summaryrefslogblamecommitdiffstats
path: root/src/net/pvsServerConnection.cpp
blob: bc4b48220331787aa3a91c5e5be872d90912b024 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                          
                




























                                                                                      

                                                                       























                                                                                                                        
                                                           








                                                                                            
                                                                         















                                                                                                                                                    
                                         

                                                        







                                                                                           











                                                                                       


























                                                                          
                               







































































































                                                                                                                                                                    
#include "pvsServerConnection.h"
#include "src/util/consoleLogger.h"
#include "src/util/util.h"
#include "pvsMsg.h"
#include "src/pvs.h"
#include <QtNetwork/QHostAddress>
#include "src/core/pvsChatClient.h"
#include "src/util/serviceDiscoveryUtil.h"
#include "src/net/pvsDiscoveredServer.h"
#include <QTime>
//#define verbose

PVSServerConnection::PVSServerConnection(PVS *parent) : QObject(parent)
{
    _client = parent;
    _incomplete = NULL;
    _socket = NULL;
    _timerId = 0;
    _timer = 0;
    _wasConnected = false;
    loadCommands();
}

inline void PVSServerConnection::timerStop()
{
    if (_timer == 0) return;
    killTimer(_timer);
    _timer = 0;
}

bool PVSServerConnection::connectToServer(PVSDiscoveredServer* server, QString passwd)
{
	QHostAddress host = server->getHost();
    if (_socket != NULL && _socket->state() == QAbstractSocket::ConnectedState
            && _socket->peerAddress() == host) return false;
    int port = server->getPort();
    if (port < 1 || port > 65535) port = SERVER_PORT_INT;
    ConsoleLog writeNetwork("Connecting to host:");
    ConsoleLog writeNetwork(host.toString().toUtf8().data());
    ConsoleLog writeNetwork("Fingerprint is:");
    ConsoleLog writeNetwork(QString(server->getFingerprint().toHex()));
    timerStop();
    if (_socket != NULL)
    {
        _socket->blockSignals(true);
        _socket->abort();
        _socket->deleteLater();
    }
    _name = server->getName();
    _expectedFingerprint = server->getFingerprint();
    _passwd = passwd;
    _socket = new QSslSocket(this);
    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)));
    char tex[2000];
    snprintf(tex, 2000, "Connecting to %s on port %d", host.toString().toUtf8().data(), (int)port);
    ConsoleLog writeNetwork(tex);
    connect(_socket,
    		SIGNAL(sslErrors(const QList<QSslError> &)),
    		this,
    		SLOT(sslErrors(const QList<QSslError> &))
    		);
    _socket->connectToHostEncrypted(host.toString(), port);
    if (_timer == 0) _timer = startTimer(SERVER_KEEPALIVE);

    return true; // we really don't know yet if it will succeed since its async
}

void PVSServerConnection::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
		ConsoleLog writeNetwork(err.errorString().toUtf8().data());
		ConsoleLog writeNetwork("***** SSL ERROR, ABORTING *****");
		return;
	}
	_socket->ignoreSslErrors();
}

void PVSServerConnection::disconnectFromServer()
{
    if (_socket == NULL) return;
    _socket->disconnectFromHost();
    handleDisconnectInternal();
}

// timer event, SERVER_KEEPALIVE interval
void PVSServerConnection::timerEvent(QTimerEvent *event)
{
	if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) return;
    time_t refval = time(NULL) - (SERVER_TIMEOUT/1000);
	if (_lastData < refval)
	{ // ping timeout
		qDebug("DEBUG: Ping timeout for server!");
		qDebug("DEBUG: Timeout was %i last response was %i", refval, _lastData);
		disconnectFromServer();
	}
}

void PVSServerConnection::sendMessage(PVSMsg newMessage)
{
    if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState) return;
    char *data;
    int len;
    newMessage.getBinaryData(data, len);
    QByteArray qba(data, len);
    _socket->write(qba);
}

QString PVSServerConnection::getServerName()
{
	if (!isConnected()) return QString();
    return _name;
}

void PVSServerConnection::loadCommands()
{
    addLoginHandler("ID", this, &PVSServerConnection::onID);
    addCommandHandler("PING", this, &PVSServerConnection::onPing);
}

void PVSServerConnection::onID(PVSMsg idmsg)
{
    _id = (unsigned int)string2Int(idmsg.getMessage());
}

void PVSServerConnection::onPing(PVSMsg pingmsg)
{
	if (pingmsg.getMessage().at(pingmsg.getMessage().size()-1) == '?')
	{
		sendMessage(PVSMsg(PVSCOMMAND, "PING", "HI!"));
	}
}

void PVSServerConnection::handleClientMsg(PVSMsg receiver)
{
	_lastData = time(NULL);
    if (receiver.getType() == PVSCOMMAND)
    {
        _commandDispatcher.fire(receiver.getIdent(), receiver);
//      ConsoleLog writeNetwork(QString("Received a command and handled it.").append(receiver.getIdent().append(QString(":").append(receiver.getMessage()))));
    }
    else if (receiver.getType() == PVSMESSAGE)
    {
        _chatDispatcher.fire(receiver.getIdent(), receiver);
        ConsoleLog writeNetwork(QString("Received a chat message and handled it.").append(receiver.getIdent().append(QString(":").append(receiver.getMessage()))));
    }
    else if (receiver.getType() == PVSLOGIN)
    {
        _loginDispatcher.fire(receiver.getIdent(), receiver);
//      ConsoleLog writeNetwork(QString("Received a login and handled it. Ident: ").append(receiver.getIdent().append(QString(":")).append(receiver.getMessage())));
    }
    else if (receiver.getType() == PVSUNKNOWN)
    {
        ConsoleLog writeNetwork(QString("Received an unknown type. Ident : ").append(receiver.getIdent().append(QString(" : ").append(receiver.getMessage()))));
    }
}

void PVSServerConnection::sock_dataArrival()
{
    if (_socket == NULL || _socket->state() != QAbstractSocket::ConnectedState)
    {
        ConsoleLog writeError("dataArrival called in bad state");
        return;
    }

    while (_socket->bytesAvailable())
    {
        int retval = 0;
        do
        {
            if (_incomplete == NULL) _incomplete = new PVSMsg(); // we need a pvsmsg object
            retval = _incomplete->readMessage(_socket); // let the message read data from socket
            if (retval == -1)   // error parsing msg, disconnect client!
            {
                this->disconnectFromServer();
                return;
            }
            if (retval == 1)   // message is complete
            {
                this->handleClientMsg(*_incomplete);
                delete _incomplete; // ...and delete...
                _incomplete = NULL; // ...so the next msg can be parsed
            }
        } while (retval == 1);
    }
}

void PVSServerConnection::sock_closed()
{
    // should this be unreliable in some way i suggest using the signal "stateChanged()" instead
    // and check if the state changed to unconnected.
    ConsoleLog writeNetwork("Socket was closed... oh well..");
    handleDisconnectInternal();
}

void PVSServerConnection::sock_error(QAbstractSocket::SocketError errcode)
{
    char txt[204];
    snprintf(txt, 200, "Connection error: %d", (int)errcode);
    ConsoleLog writeNetwork(txt);
    handleDisconnectInternal();
}

// Send to server username and loginname.
void PVSServerConnection::sock_connected()
{
    QByteArray cert = _socket->peerCertificate().digest(QCryptographicHash::Sha1);
    if (_expectedFingerprint != cert)
    {	// Nich mit mir, Freundchen!
    	disconnectFromServer();
    	// Maybe you want to inform the user that there was a certificate mismatch
    	// and that it is very possible that the CIA is trying to eavesdrop on the connection
    	return;
    }
    ConsoleLog writeNetwork("Connected!");
    _wasConnected = true;
    sendMessage(
        PVSMsg(PVSLOGIN, "USERNAME", getFullUsername()+","+getenv("USER")));
    sendMessage(
        PVSMsg(PVSLOGIN, "PASSWORD", _passwd));
    ConsoleLog writeNetwork("Sent Username and password.");

    _client->onConnected(_name);
}

void PVSServerConnection::handleDisconnectInternal()
{
    _socket->blockSignals(true);
    _socket->abort();
    _socket->deleteLater();
    _socket = NULL;
    _name = QString();
    _client->securityUnlock();
    timerStop();
    if (_wasConnected)
    {
    	_wasConnected = false;
    	_client->onDisconnected();
    }
}