/* # 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 #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::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::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::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::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::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::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::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::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)); }