#include "pvsServerConnection.h" #include "src/util/consoleLogger.h" #include "src/util/util.h" #include "pvsMsg.h" #include "src/pvs.h" #include #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 &)), this, SLOT(sslErrors(const QList &)) ); _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 & errors ) { for (QList::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(); } }