#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"
//#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(10000);
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();
}
void PVSServerConnection::timerEvent(QTimerEvent *event)
{
if (_socket != NULL && _socket->state() == QAbstractSocket::ConnectedState) return;
// TODO: Check for ping timeout
}
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);
}
void PVSServerConnection::ping()
{
sendMessage(PVSMsg(PVSCOMMAND, "PING", "U THERE?"));;
}
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)
{
// FIXME: @SimonR, this line cuase problems with pvs in daemon mode and dbus
//qDebug("Got Message for this client: [%c][%s][%s]\n", (char)receiver.getType(), receiver.getIdent().toUtf8().data(), receiver.getMessage().toUtf8().data());
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();
}
}