summaryrefslogblamecommitdiffstats
path: root/src/core/pvsConnectionManager.cpp
blob: 7aa72336fcb3a6f031d5d8d980065bc0f8159c67 (plain) (tree)










































                                                                               





                                                                                                     



















































































































































                                                                                                                 


                                                                   












                                                                                                
                                                                                           











                                                                                                            




                                                    

                                                
                 






















                                                                                                                                                                           

                 




























                                                                                                                                                                  
         
                                            


                                                         
                                                                                                         






                                                                


















                                                                     






































































































































































































































































                                                                                                                                 
/*
# Copyright (c) 2009 - 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/core/pvsConnectionManager.cpp
# -----------------------------------------------------------------------------
*/

#include "pvsConnectionManager.h"
#include "src/gui/mainWindow.h"
#include <QApplication>
#include "src/setup.h"
#include "src/util/CertManager.h"
#include "src/util/serviceDiscoveryUtil.h"

PVSConnectionManager* PVSConnectionManager::singleCM;


PVSConnectionManager* PVSConnectionManager::getManager()
{
    if (!singleCM)
        singleCM = new PVSConnectionManager();
    return singleCM;
}

PVSConnectionManager::PVSConnectionManager()
{
    _busy = false;
    _needPassword = false;
    loadCommands();
    ConsoleLog writeLine(QString("Connection Manager created."));
    ConsoleLog writeChat("Log for chat is now running");

    if (!(_pvsServer.startListen(SERVER_PORT_INT)))
        ConsoleLog writeError(QString("Server listening failed!"));
    setUpdateRate(500);

    QByteArray fingerprint = CertManager::getCertificate("manager").digest(QCryptographicHash::Sha1);
    _sdBroadcaster.setFingerprint(fingerprint);
    qDebug() << "Fingerprint: " << fingerprint.toHex();
    qDebug() << "Sessionname: " << getSessionName();

    _timerId = startTimer(1000);
}

PVSConnectionManager::~PVSConnectionManager()
{
	killTimer(_timerId);
}

void PVSConnectionManager::setUpdateRate(int newRate)
{
	// TODO: Iterate over list of clients and set the updateRate on each vncConnection
	// Object. The timer does not influence the update rate of the thumbs, thus the code
	// here was removed.
}

void PVSConnectionManager::timerEvent(QTimerEvent *event)
{
    update();
}

void PVSConnectionManager::onClientNew(PVSClientConnection* newConnection)
{
    PVSClient* tmp = getClientFromConnection(newConnection);
    if (tmp == NULL)
    {
        PVSClient* newCon  = new PVSClient(newConnection);
        _listClients.push_back(newCon);
        sendEventToClients(QString("addedConnection"), newConnection, "");
    }
    else
    {
        // already got that one... strange, eh?
    }
}

void PVSConnectionManager::onClientRemove(PVSClientConnection* removedConnection)
{
    PVSClient* tmp = getClientFromConnection(removedConnection);
    if (tmp == NULL) return;
    MainWindow::getWindow()->removeConnection(tmp);
    removeConnection(tmp);
    if(tmp->getUserName().length() > 0)
    {
    	ConsoleLog writeChat(tmp->getUserName()+" has left the chat.");
        sendEventToClients("removedClient", removedConnection , tmp->getUserName()+":"+tmp->getIp());
    }
}

// Inform all chat clients about a new event.
void PVSConnectionManager::sendEventToClients(QString event, PVSClientConnection* connection, QString clientName)
{
    if(event.compare("addedConnection") == 0)
    {
        // send the client list to new Connection.
        for (std::list<PVSClient*>::iterator it = _listClients.begin(); it != _listClients.end(); it++)
        {
            QString name = (**it).getUserName();
            QString ip = (**it).getIp();
            if (name.length() == 0) continue;
            connection->push_back_send(PVSMsg(PVSMESSAGE, "clientToAdd", ":" + name + ":" + ip, 0));
        }
    }
    if(event.compare("addedClient") == 0)
    {
        // send name to everybody to all connected clients.
        _pvsServer.sendToAll(PVSMsg(PVSMESSAGE, "clientToAdd", ":" + clientName, 0));
        connection->push_back_send(PVSMsg(PVSMESSAGE, "assignedName", ":" + clientName, 0));
        connection->push_back_send(PVSMsg(PVSMESSAGE, "clientToAdd", ":PVSMGR:local", 0));
    }
    if(event.compare("removedClient") == 0)
    {
        // send name of removed client to connected clients.
        _pvsServer.sendToAll(PVSMsg(PVSMESSAGE, "clientToRemove", ":"+ clientName, 0));
    }
}

PVSClient* PVSConnectionManager::getClientFromConnection(PVSClientConnection* client)
{
    if (!_listClients.empty())
    {

        for (std::list<PVSClient*>::iterator it = _listClients.begin(); it != _listClients.end(); it++)
        {
            if ((*it)->getPVSClientConnection())
            {
                if ((*it)->getPVSClientConnection() == client)
                {
                    return (*it);
                }
            }
        }
    }
    return NULL;
}

PVSClient* PVSConnectionManager::getClientFromConnectionId(int id)
{
    if (_listClients.empty()) return NULL;
    for (std::list<PVSClient*>::iterator it = _listClients.begin(); it != _listClients.end(); it++)
    {
        if ((*it)->getConnectionId() == id) return (*it);
    }
    return NULL;
}

PVSClient* PVSConnectionManager::getClientFromVNCConnection(VNCConnection* client)
{
    if (!_listClients.empty())
    {

        for (std::list<PVSClient*>::iterator it = _listClients.begin(); it != _listClients.end(); it++)
        {
            if ((*it)->getVNCConnection())
            {
                if ((*it)->getVNCConnection() == client)
                {
                    return (*it);
                }
            }
        }
    }
    return NULL;
}

PVSClient* PVSConnectionManager::getClientFromIp(QString ip)
{
    if (!_listClients.empty())
    {
        for (std::list<PVSClient*>::iterator it = _listClients.begin(); it != _listClients.end(); it++)
        {
            // ok, probably this could be handled better inside the PVSConnection itself
            // but makeshift for now
            if ((*it)->getPVSClientConnection())
            {
                if ((*it)->getIp() == ip)
                {
                    return (*it);
                }
            }
        }
    }
    return NULL;
}

void PVSConnectionManager::onCommand(PVSMsg command)
{
    QString message = command.getMessage();
    QString ident = command.getIdent();

    PVSClient* tmp = getClientFromConnectionId(command.getSndID());

    if (ident.compare("VNCSRVRESULT") == 0)
    {
        int e = string2Int(message);
        QString id = int2String(command.getSndID());
        switch (e)
        {
        case 0:
            ConsoleLog writeLine("[Client: " + id + ", VNCSRV] Server should be running");
            break;
        case 1:
            ConsoleLog writeError("[Client: " + id + ", VNCSRV] Server returned general error");
            break;
        case 2:
            ConsoleLog writeError("[Client: " + id + ", VNCSRV] /tmp/vncpasswd not found");
            break;
        case 3:
            ConsoleLog writeError("[Client: " + id + ", VNCSRV] VNC-Script not found");
            break;
        case 127:
            ConsoleLog writeError("[Client: " + id + ", VNCSRV] command not found (vnc-server installed?)");
            break;
        default:
            ConsoleLog writeError("[Client: " + id + ", VNCSRV] unknown error");
            break;
        }
    }
    if (ident.compare("PROCESSES") == 0)
    {
    	QString id = int2String(command.getSndID());
    	if (message.startsWith("START"))
    	{
    		message.remove(0,6);
    		if (message.startsWith("ERROR"))
    		{
    	    	int e = string2Int(message.remove(0,6));
    	    	message.remove(0,2);
    	        switch (e)
    	        {
					case 0:
						ConsoleLog writeError("[Client: " + id + ", PROCESS] could not start: "+message+" is missing or insufficient permissions");
						break;
					case 1:
						ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+" crashed");
						break;
					case 2:
						ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+" timed out");
						break;
					case 3:
						ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+" read error");
						break;
					case 4:
						ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+" write error");
						break;
					default:
						ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+": unknown error");
						break;
    	        }
    		}
    	}
    	else if (message.startsWith("STOP"))
		{
        	message.remove(0,5);
        	if (message.startsWith("ERROR"))
        	{
        	    int e = string2Int(message.remove(0,6));
        	    message.remove(0,2);
        	    switch (e)
        	    {
    				case 0:
    					ConsoleLog writeError("[Client: " + id + ", PROCESS] could not stop: "+message+" is missing or insufficient permissions");
    					break;
    				case 1:
    					ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+" crashed");
    					break;
    				case 2:
    					ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+" timed out");
    					break;
    				case 3:
    					ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+" read error");
    					break;
    				case 4:
    					ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+" write error");
    					break;
    				default:
    					ConsoleLog writeError("[Client: " + id + ", PROCESS] "+message+": unknown error");
    					break;
        		}
        	}
    	}
    	else if (message.startsWith("SHOW"))
    	{
    		QString msgcontent = message.remove(0,5);
    		if (msgcontent.startsWith("ERROR"))
    			ConsoleLog writeError("[Client: " + id + ", PROCESS] could not show processes.");
    		else if (msgcontent.startsWith("clear"))
    			tmp->clearProcessesVector();
    		else if (msgcontent.startsWith("finished"))
    			tmp->processesVectorAdd("vector ready");
    		else tmp->processesVectorAdd(message);
    	}
    }
    else if (ident == "MCASTFTANNOUNCE")
    {
    	_pvsServer.sendToAll(command);
    }
    else if (ident == "MCASTFTRETRY")
    {
        QStringList fields = message.split(':');
        if (!fields.size() == 2)
        {
            qDebug() << "Malformed MCASTFTRETRY message:" << message;
            return;
        }

        PVSClient* client = getClientFromUsername(fields[0]);
        if (client)
        {
            client->sendMessage(command);
        }
    }
}
void PVSConnectionManager::onChat(PVSMsg chatMsg)
{

    QString nick_to = chatMsg.getIdent();
    int sndID = chatMsg.getSndID();
    PVSClientConnection* clientConnection = NULL;
    PVSClient* client;
    QStringList l     = chatMsg.getMessage().split(":");
    QString nick_from = l[0];
    QString msg       = chatMsg.getMessage().remove(0, l[0].size()+1);

    if (nick_to == "@all") // public message.
    {
        _pvsServer.sendToAll(chatMsg);
        MainWindow::getWindow()->receiveChatMsg(nick_from, nick_to, msg);// get Msg on PVSMgr's Chat-GUI.
        ConsoleLog writeChat(nick_from +" wrote '"+msg+"' in @all-Channel.");
    }
    else // private message.
    {
    	if(nick_from == "PVSMGR") //Msg from Server
		{
			MainWindow::getWindow()->receiveChatMsg(nick_from, nick_to, msg); // get Msg on PVSMgr's Chat-GUI.
			ConsoleLog writeChat(nick_from+" wrote '" + msg + "' to " + nick_to); // write the event to the chat log.
		}
		else
		{
			clientConnection = _pvsServer.getConnectionFromId(sndID); // become your own message.
			if (clientConnection)
			{
				clientConnection->push_back_send(chatMsg);
			}
			else
			{
				ConsoleLog writeError("Got Chat Message from unknown ID");
			}
		}
        if(nick_to == "PVSMGR") //Msg to Server
        {
        	MainWindow::getWindow()->receiveChatMsg(nick_from, nick_to, msg); // get Msg on PVSMgr's Chat-GUI.
        	ConsoleLog writeChat(nick_from+" wrote '" + msg + "' to " + nick_to);// write the event to the chat log.
        }
        else
        {
        	client = getClientFromUsername(nick_to); // private receiver become the message.
        	if (client)
        	{
        		client->sendMessage(chatMsg);
        		ConsoleLog writeChat(nick_from+" wrote '" + msg + "' to " + nick_to);// write the event to the chat log.
        	}
        	else
			{
				ConsoleLog writeError("[CHAT] There is no client with username: " + nick_to);
			}
        }

    }
}

PVSClient* PVSConnectionManager::getClientFromUsername(QString name)
{
    if (!(_listClients.empty()))
    {
        // Check for ping timeout
        for (std::list<PVSClient*>::iterator it = _listClients.begin(); it!= _listClients.end(); it++)
        {
            PVSClient *client = *it;
            if (client)
                if (client->getUserName() == name)
                    return client;
        }
    }
    return NULL;
}

void PVSConnectionManager::onLoginPassword(PVSMsg command)
{
	if (!_needPassword) return; // No password required
	if (_password == command.getMessage()) return; // No password set
	PVSClient* tmp = getClientFromConnectionId(command.getSndID());
	tmp->sendMessage(
			PVSMsg(PVSLOGIN, "FAILED", "Wrong password.")
	);
	tmp->shutDownClient();
}

// Set usernamen and loginname a client.
void PVSConnectionManager::onLoginUsername(PVSMsg command)
{
	QStringList l = command.getMessage().split(",");
	PVSClient* tmp = getClientFromConnectionId(command.getSndID());
	if (tmp->getUserName().length() > 0) return;
    ConsoleLog writeError("(PVSConnectionManager::onUsername) was fired.");
    if(l[0] == "PVSMGR")
    {
    	l[0] = "fake_PVSMGR";
    }
    int counter = 0;
    QString uname = l[0];
    while (getClientFromUsername(uname) != NULL)
    {
    	uname = l[0].append(QString::number(counter++, 16));
    }
    if (tmp)
    {
        tmp->setUsername(uname);
        tmp->setLoginName(l[1]);
        MainWindow::getWindow()->addConnection(tmp);
        ConsoleLog writeChat(tmp->getUserName()+" has joined the chat.");
        sendEventToClients(QString("addedClient"), tmp->getPVSClientConnection(), tmp->getUserName() +":"+ tmp->getIp());
    }
    else
        ConsoleLog writeError("(PVSConnectionManager::onUsername) Couldnt find connection to user-id");
}

void PVSConnectionManager::onVncPassword(PVSMsg command)
{
    PVSClient* tmp = getClientFromConnectionId(command.getSndID());

    if (tmp)
    {
        tmp->setVncPassword(command.getMessage());
        return;
    }
    else
        ConsoleLog writeError("couldnt find matching connection to id!");
}

void PVSConnectionManager::onVncRwPassword(PVSMsg command)
{
    PVSClient* tmp = getClientFromConnectionId(command.getSndID());

    if (tmp)
    {
        tmp->setVncRwPassword(command.getMessage());
        return;
    }
    else
        ConsoleLog writeError("couldnt find matching connection to id!");
}

void PVSConnectionManager::onVncAllow(PVSMsg command)
{
    PVSClient* tmp = getClientFromConnectionId(command.getSndID());

    if (tmp)
    {
        if (command.getMessage().compare("YES") == 0)
        {
            tmp->setAllowed(true);
            return;
        }
        tmp->setAllowed(false);
    }
}

void PVSConnectionManager::onVncPort(PVSMsg command)
{
    PVSClient* tmp = getClientFromConnectionId(command.getSndID());
    if (tmp)
    {
        int port = string2Int(command.getMessage());
        if (port > 0)
            tmp->setVncPort(port);
    }

}

void PVSConnectionManager::onVncProjection(PVSMsg command)
{
    if (command.getMessage().compare(QString("YES")) == 0)
        ConsoleLog writeLine("Client is watching.(connected via vnc)");
    if (command.getMessage().compare(QString("NO")) == 0)
        ConsoleLog writeLine("Client failed to connect via vnc.");
}

void PVSConnectionManager::removeConnection(PVSClient* newConnection)
{
    if (newConnection != NULL)
    {
        for (std::list<PVSClient*>::iterator it = _listClients.begin(); it != _listClients.end(); it++)
        {
            if (newConnection == (*it))
            {
                // remove it from the list to keep this method from causing trouble when being called more than
                // once in the process
                _listClients.remove(newConnection);

                // it is important to tell the GUI that the vnc is gone (or ... will be)
                // before we really shut it down, since the removal procedure of the
                // frames shouldnt be messed with from here.
                MainWindow::getWindow()->onConnectionRemoved(newConnection); // to kill vncConnection

                // now that the gui nows about it and does no longer call the vnc-stuff
                newConnection->shutDownVNC();
                // and its gone...

                // clean up on the connection
                newConnection->shutDownClient(); // just to be sure
                newConnection->onClientDisconnected();


                newConnection->deleteLater();
                return;
            }
        }
    }
    else
    {
        // well, not our problem obviously
    }
}

bool PVSConnectionManager::update()
{
    if (_busy) return true;	// already ongoing, so scrap that.

    _busy = true; // lock method

    if (!_listClients.empty())
    {
        // now update the rest and check if they are alive
        // otherwise mark dead connections
        for (std::list<PVSClient*>::iterator it = _listClients.begin(); it != _listClients.end(); it++)
        {
            PVSClient* current = (*it);
            current->tick();
        }
    }

    //QApplication::processEvents(QEventLoop::AllEvents, 5);

    _busy = false; // we're done, so unlock
    return true;
}

void PVSConnectionManager::loadCommands()
{
    _pvsServer.addLoginHandler("USERNAME", this, &PVSConnectionManager::onLoginUsername);
    _pvsServer.addLoginHandler("PASSWORD", this, &PVSConnectionManager::onLoginPassword);
    _pvsServer.addCommandHandler("*", this, &PVSConnectionManager::onCommand);
    _pvsServer.addCommandHandler("VNC", this, &PVSConnectionManager::onVncAllow);
    _pvsServer.addCommandHandler("PORT", this, &PVSConnectionManager::onVncPort);
    _pvsServer.addCommandHandler("PASSWORD", this, &PVSConnectionManager::onVncPassword);
    _pvsServer.addCommandHandler("RWPASSWORD", this, &PVSConnectionManager::onVncRwPassword);
    _pvsServer.addCommandHandler("PROJECTING", this, &PVSConnectionManager::onVncProjection);
    _pvsServer.addChatHandler("*", this, &PVSConnectionManager::onChat);
}

QString PVSConnectionManager::setNeedPassword(bool enabled)
{
	_needPassword = enabled;
    if (enabled && _password.isEmpty())
    {
        _password = QString::number(getRandom(1000, 9999), 10);
    }
    return _password;
}

QString PVSConnectionManager::getSessionName()
{
    return sha1ToReadable(CertManager::getCertificate("manager").digest(QCryptographicHash::Sha1));
}