summaryrefslogblamecommitdiffstats
path: root/src/net/pvsListenServer.cpp
blob: 1280a92ea113fae657e8093549e4cbf15397f40a (plain) (tree)






















                                                                                


                     


                      
                                     
                











                                                       
                     

















































































                                                                                
                                       




                                                    
                                                           









                                                                                
                                                            

                                                        

                                                                                                             







































                                                                                               
                                                    
















































                                                                               




                                                    

















                                                                      

                                                                       





                                                                   
                                              




























                                                                                                                

                                                                                           



















                                                                                        
















































                                                                            
/*
 # Copyright (c) 2009,2010 - OpenSLX Project, Computer Center University of
 # Freiburg
 #
 # This program is free software distributed under the GPL version 2.
 # See http://openslx.org/COPYING
 #
 # If you have any feedback please consult http://openslx.org/feedback and
 # send your suggestions, praise, or complaints to feedback@openslx.org
 #
 # General information about OpenSLX can be found at http://openslx.org/
 # -----------------------------------------------------------------------------
 # src/net/pvsListenServer.cpp
 #    - handling of clients (server side)
 #    - accepting new connections etc.
 # -----------------------------------------------------------------------------
 */

#include "pvsListenServer.h"
#include "pvsMsg.h"
#include "pvsClientConnection.h"
#include "src/util/consoleLogger.h"
#include <QtNetwork/QSslSocket>
#include <QBuffer>
#include <QByteArray>
#include <QSettings>
#include "SslServer.h"
#include <cassert>
//#define verbose
#include "mcast/McastConfiguration.h"
#include <QTime>

// Create listener
PVSListenServer::PVSListenServer(int port, int clients)
{
    _id = generateID();
    _listenSocket = NULL;
    _timer = 0;
    if (clients < 1)
        _clientsMax = 100;
    else
        _clientsMax = clients;
    _port = port;
    _mcastConfig = 0;
    init();

}

PVSListenServer::~PVSListenServer()
{
    if (_listenSocket != NULL)
    {
        disconnect(_listenSocket, SIGNAL(newConnection()), this, SLOT(
                       server_connectionRequest()));
        _listenSocket->deleteLater();
    }
}

PVSClientConnection* PVSListenServer::getConnectionFromId(int id)
{
    if (!(_clients.empty()))
    {
        // Check for ping timeout
        for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it
                != _clients.end(); it++)
        {
            PVSClientConnection *client = *it;
            if (client)
                if (client->getID() == id)
                    return client;
        }
    }
    return NULL;
}

void PVSListenServer::sendToAll(PVSMsg msg)
{
    if (!(_clients.empty()))
    {
        // Check for ping timeout
        for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it
                != _clients.end(); it++)
        {
            PVSClientConnection *client = *it;
            if (client)
                client->push_back_send(msg);
        }
    }
}

// listen on a specific port
bool PVSListenServer::startListen(int port)
{
    if (_listenSocket != NULL)
    {
        ConsoleLog writeError(
            QString("Server already listening on port :").append(
                int2String(_port)));
        if (shutdown())
        {
            ConsoleLog writeError(QString("Server shutdown failed!"));
            return false;
        }
    }
    _port = port;
    return init();
}

// stop listening and free socket descriptor
bool PVSListenServer::shutdown()
{
    if (_timer != 0)
        killTimer( _timer);
    _timer = 0;
    if (_listenSocket != NULL)
    {
        disconnect(_listenSocket, SIGNAL(newConnection()), this, SLOT(
                       server_connectionRequest()));
        _listenSocket->close();
        _listenSocket->deleteLater();
    }
    _listenSocket = NULL;
    _fresh = true;
    return false;
}

// timer event, CLIENT_TIMEOUT interval
void PVSListenServer::timerEvent(QTimerEvent *event)
{
    if (!(_clients.empty()))
    {
        // Check for ping timeout
    	time_t refval = time(NULL) - (CLIENT_TIMEOUT/1000);
        for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it
                != _clients.end(); it++)
        {
            PVSClientConnection *client = *it;
            client->ping();
            if (!client->isConnected())
            { // disconnected
                if (disconnectClient(client))
                    break; // list was modified, iterator not valid anymore
            }
            qDebug() << client->lastData() << " " << refval;
			if (client->lastData() < refval)
			{ // ping timeout
			    qDebug("DEBUG: Ping timeout for client %s", qPrintable(client->getNameUser()));
			    qDebug("DEBUG: Timeout was %i last response was %i", refval, client->lastData());
				if (disconnectClient(client))
					break; // list was modified, iterator not valid anymore
			}
        }
    }
}

// remove a client
bool PVSListenServer::disconnectClient(PVSClientConnection* delinquent)
{
	assert(delinquent);
	delinquent->deleteLater();
    if (!(_clients.empty()))
    {
        for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it
                != _clients.end(); it++)
        {
            if ((*it) == delinquent)
            {
                (*it)->closeConnection();
                _clients.remove((*it));
                onClientDisconnected(delinquent);
                return true;
            }
        }
    }
    return false;
}

// All sorts of events:

void PVSListenServer::onConnectionRemoved(PVSClientConnection* delinquent)
{

}

void PVSListenServer::onClientConnected(PVSClientConnection* connected)
{
    connected->setServerID(_id);
    connected->setID(generateID());
    connected->push_back_send(mcastConfigMessage());
}

void PVSListenServer::onClientDisconnected(PVSClientConnection* disconnected)
{
    //
}

// Get client class for a given socket
PVSClientConnection* PVSListenServer::getConnectionFromSocket(QSslSocket* sock)
{
    for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it
            != _clients.end(); it++)
    {
        if ((*it)->getSocket() == sock) // match!
            return (*it);
    }
    return NULL;
}

// generate a random (unique) id
unsigned int PVSListenServer::generateID()
{
    int random_integer;
    do
    {
        random_integer = getRandom(0, 0);
    }
    while (getConnectionFromID(random_integer));

    return (unsigned int) random_integer;
}

// Get client class for a given id
PVSClientConnection* PVSListenServer::getConnectionFromID(int id)
{
    for (std::list<PVSClientConnection*>::iterator it = _clients.begin(); it
            != _clients.end(); it++)
    {
        if ((*it)->getID() == id || id == _id)
        {
            return (*it);
        }
    }
    return NULL;
}

// Initialize listening socket
bool PVSListenServer::init()
{
	if (_mcastConfig)
		delete _mcastConfig;
	_mcastConfig = new McastConfiguration(this);
	loadMcastConfig();

    if (_listenSocket != NULL)
        shutdown();

    if (_port > 0)
    {
        // make a socket, bind it, and listen on it:
        _listenSocket = new SslServer;
        QHostAddress addr(QHostAddress::Any);
        _listenSocket->listen(addr, _port);

        if (!_listenSocket->isListening())
        {
            ConsoleLog writeNetwork(
                QString("Error listening on Port ").append(int2String(
                            _port)));
            return false;
        }
        ConsoleLog writeNetwork(
            QString("Server is listening. Socket: ").append(ptr2String(
                        _listenSocket)));
        connect(_listenSocket, SIGNAL(newConnection()), this, SLOT(
                    server_connectionRequest()));

        _fresh = true;
        if (_timer != 0)
            killTimer( _timer);
        _timer = startTimer(CLIENT_KEEPALIVE);
        return true;
    }
    return false;
}

void PVSListenServer::server_connectionRequest()
{
	//printf("ConnReq\n");
    if (_listenSocket == NULL)
        return; // o.O
    while (_listenSocket->hasPendingConnections())
    {
        QSslSocket* sock = (QSslSocket*)_listenSocket->nextPendingConnection();
        //printf("Got one: %d: %s\n", (int)sock->isEncrypted(), sock->peerAddress().toString().toUtf8().data());
        if (_clients.size() >= _clientsMax)
        {
            sock->abort();
            sock->deleteLater();
            continue;
        }
        PVSClientConnection* client = new PVSClientConnection(this, sock);
        _clients.push_back(client); // create new client class and add to list
        onClientConnected(client); // trigger event
        _fresh = false;
    }
}

void PVSListenServer::handleClientMsg(unsigned int clientID, PVSMsg msg)
{
    qDebug("Got Message for client %ud: [%c][%s][%s]", clientID,
           (char) msg.getType(), qPrintable(msg.getIdent()), qPrintable(msg.getMessage()));
    msg.setSndID(clientID);
    if (msg.getType() == PVSCOMMAND)
        _commandDispatcher.fire(msg.getIdent(), msg);
    else if (msg.getType() == PVSMESSAGE)
        _chatDispatcher.fire(msg.getIdent(), msg);
    else if (msg.getType() == PVSLOGIN)
        _loginDispatcher.fire(msg.getIdent(), msg);
    else
        ConsoleLog
        writeNetwork(
            QString(
                "Could not dispatch Message of unknown Type.\n\t Message was: ").append(
                msg.getIdent().append(QString(":").append(
                                          msg.getMessage()))));
}

bool PVSListenServer::isListening()
{
    return _listenSocket != NULL && _listenSocket->isListening();
}

void PVSListenServer::loadMcastConfig()
{
	QSettings settings;
	_mcastConfig->loadFrom(&settings, "multicast-filetransfer");
}

void PVSListenServer::saveMcastConfig()
{
	QSettings settings;
	_mcastConfig->writeTo(&settings, "multicast-filetransfer");
	settings.sync();
}

PVSMsg PVSListenServer::mcastConfigMessage()
{
	// If anything is changed here, do not forget to
	// 1. assign a new version number
	// 2. adapt PVS::onCommand(PVSMsg) in pvs.cpp
	QByteArray ba;
	QDataStream strm(&ba, QIODevice::WriteOnly);
	strm << (quint16)1 // version
			<< _mcastConfig->multicastAddress()
			<< _mcastConfig->multicastUDPPortBase()
			<< _mcastConfig->multicastDPort()
			<< _mcastConfig->multicastSPort()
			<< _mcastConfig->multicastMTU()
			<< _mcastConfig->multicastWinSize()
			<< _mcastConfig->multicastRate()
			<< _mcastConfig->multicastUseUDP();

	QByteArray b64 = ba.toBase64();
	QString message = QString::fromAscii(b64.constData(), b64.length());
	PVSMsg msg(PVSCOMMAND, "MCASTFTCONFIG", message);
	return msg;
}

void PVSListenServer::multicastReconfigure(McastConfiguration const* source)
{
	_mcastConfig->multicastAddress(source->multicastAddress());
	*_mcastConfig = *source;
	saveMcastConfig();
	sendToAll(mcastConfigMessage());
}

McastConfiguration const* PVSListenServer::getMulticastConfiguration()
{
	return _mcastConfig;
}