/*
# 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] ~/.pvs/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));
}