/*
# 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/
*/
#include "pvs.h"
#include "src/util/dispatcher.h"
#include "src/net/pvsMsg.h"
#include "src/net/pvsServiceDiscovery.h"
#include "src/net/pvsDiscoveredServer.h"
// D-Bus
#include "pvsadaptor.h"
extern PVS *mainClient;
// !!STATION LOCK TESTING!!
//#define lock_test_unlock_time 3
// uncomment the following line to test locking in a local context
// it disables the lock of a station #(lock_test_unlock_time) seconds after it has been locked
//#define lock_test
PVS::PVS() :
QObject()
{
_pvsServerConnection = new PVSServerConnection(this);
_locked = false;
_vncAllowed = false;
_vncRequested = false;
readPolicyFiles();
loadCommands();
/*#ifndef __WIN32__*/
_blankScreen = NULL;
/*#endif*/
_vncPort = -1;
// add a notify to the allow file, so we get informed when the file is changed
QString watchPath(getPolicyDir());
watchPath.append(QString(".allow"));
_notify = new QFileSystemWatcher(this);
_notify->addPath(QString(watchPath.toUtf8().data()));
connect(_notify, SIGNAL(fileChanged(const QString &)), this,
SLOT(fileChanged(const QString &)));
// connect to D-Bus
new PvsAdaptor(this);
QDBusConnection dbus = QDBusConnection::sessionBus();
if (!dbus.registerObject("/", this))
qDebug("[ERROR] DBus: Could not register object");
if (!dbus.registerService("org.openslx.pvs"))
qDebug("[ERROR] DBus: Could not register service");
_sdClient = new PVSServiceDiscovery(this);
_chat = new PVSChatClient();
_chat->setServerConnection(_pvsServerConnection);
_chat->getChatMsgHandler().addListener(this, &PVS::chat_receive);
_chat->getChatCommandHandler().addListener(this, &PVS::UpdateChatClients);
_timerLockTest = 0;
_timerLockDelay = 0;
#ifndef __WIN32__
//add signalhandling for sigterm signals
struct sigaction act;
act.sa_handler = &PVS::signalHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
//register signals with actions
sigaction(SIGTERM, &act, 0);
sigaction(SIGHUP, &act, 0);
sigaction(SIGINT, &act, 0);
sigaction(SIGQUIT, &act, 0);
#endif /*__WIN32__*/
}
PVS::~PVS()
{
// make sure we dont leave a locked computer
if (_notify)
disconnect(_notify, SIGNAL(fileChanged(const QString &)), this,
SLOT(fileChanged(const QString &)));
unlock();
delete _sdClient;
}
void PVS::connectToHost(PVSDiscoveredServer* server, QString passwd)
{
_pvsServerConnection->connectToServer(server, passwd);
}
/**
* onCommand Callback method
* handles incoming commands
*/
void PVS::onCommand(PVSMsg cmdMessage)
{
QString message = cmdMessage.getMessage();
QString ident = cmdMessage.getIdent();
if (ident.compare("LOCKSTATION") == 0)
{
if (_blankScreen)
{
return;
}
else
{
_lockMsg = message;
//TODO:@Javier: lock it eith message.
// Sent message.
if(message.size() > 0) // Lock client with message.
{
message = message + "\n \n WARNING: Your screen will be blocked in 10 seconds.";
emit showMessage("Lock Message", message, true);
_timerLockDelay = startTimer(10*1000); // 10 sec.
}
else // lock client without message.
{
lock();
}
}
return;
}
if (ident.compare("UNLOCKSTATION") == 0)
{
_timerLockDelay = 0;
if (!_blankScreen)
{
return;
}
else
{
ConsoleLog writeLine("Unlocked.");
unlock();
}
return;
}
if (ident.compare("VNCREQUEST") == 0)
{
// a vnc connection was requested, so lets check if this is against our policies
_vncRequested = (message.compare("YES") == 0);
if (!_vncRequested) return;
getVNCAllow();
return;
}
if (ident.compare("PROJECT") == 0)
{
std::vector<QString> connDetails;
std::string buf;
std::stringstream ss(message.toStdString());
while (ss >> buf)
connDetails.push_back(QString::fromStdString(buf));
if(connDetails.size() == 4) {
int port, quality;
bool fullscreen, smooth;
fullscreen = smooth = true;
port = connDetails[1].toInt();
quality = connDetails[3].toInt();
if(port > 0) {
emit project(connDetails[0], port, connDetails[2], fullscreen, smooth, quality);
lock_inputs();
ConsoleLog writeNetwork(QString("Received Projection Information: Projsrc: ").append(connDetails[0]).append( " port: ").append(connDetails[1]).append(" pass:").append(connDetails[2]));
}
ConsoleLog writeNetwork(QString("Received illegal port! (").append(port).append(")"));
}
}
if (ident.compare("UNPROJECT") == 0)
{
emit unproject();
unlock();
return;
}
if (ident.compare("SHOWPROCESSES") == 0)
{
// do stuff to show processes
// to test if SHOWPROCESSES command is submitted correct uncomment following lines
// a messagebox will appear on the client
// emit showMessage("Show Processes", "", true);
return;
}
if (ident.compare("KILLPROCESS") == 0)
{
// do stuff to kill a process
return;
}
#ifdef never
// prototype
if (command.compare(<<string>>) == 0)
{
// do stuff
return;
}
#endif
}
/**
* onLogin Callback method
* handles the servers login messages (see PVSListenServer)
*/
void PVS::onLogin(PVSMsg login)
{
if (login.getIdent().compare(QString("ACCEPTED")) == 0)
{
ConsoleLog writeNetwork("Server accepted connection.");
login.getMessage();
}
else if (login.getIdent().compare(QString("DENIED")) == 0)
{
ConsoleLog writeNetwork(QString("Sent Username."));
}
}
void PVS::chat_send(QString nick_to, QString nick_from, QString msg)
{
_chat->send(nick_to, msg);
}
void PVS::UpdateChatClients(PVSChatMsg chatCommand)
{
if(chatCommand.getCommand().compare("clientToAdd") == 0)
{
_chat->addClient(chatCommand.getUsername(), chatCommand.getIp());
emit chat_client_add(chatCommand.getUsername());
}
if(chatCommand.getCommand().compare("clientToRemove") == 0)
{
_chat->removeClient(chatCommand.getUsername());
emit chat_client_remove(chatCommand.getUsername());
}
if(chatCommand.getCommand().compare("assignedName") == 0)
{
_chat->setSource(chatCommand.getUsername());
}
}
void PVS::chat_receive(PVSChatMsg msg)
{
emit chat_receive(msg.getNickFrom(), msg.getNickTo(), msg.getMsg());
}
/**
* onDaemon Callback method
* handles incoming UDS Communicator messages
*/
void PVS::onDaemonCommand(PVSMsg msg)
{
if (msg.getIdent().compare(QString("stop")) == 0)
{
stop();
unlock();
exit(0);
}
}
void PVS::onMessage(PVSMsg msg)
{
QString ident = msg.getIdent();
QString message = msg.getMessage();
if (ident.compare("BROADCAST") == 0)
{
emit showMessage(tr("Message"), message, true);
}
}
/**
* load custom commands by adding callbacks to the dispatchers
*/
void PVS::loadCommands()
{
if (_pvsServerConnection)
{
_pvsServerConnection->addCommandHandler("*", this, &PVS::onCommand);
_pvsServerConnection->addLoginHandler("*", this, &PVS::onLogin);
_pvsServerConnection->addChatHandler("*", this, &PVS::onMessage);
}
}
void PVS::quit()
{
stop();
unlock();
exit(0);
}
/**
* stops the client
*/
void PVS::stop()
{
_pvsServerConnection->disconnectFromServer();
stopVNCScript();
}
/**
* used to unlock the client after 3 seconds
* this a maintainance method
* uncomment the define to use it
*/
void PVS::timerEvent(QTimerEvent *event)
{
#ifdef lock_test
if (_locked && event->timerId()== _timerLockTest) unlock();
#endif
if(_timerLockDelay == event->timerId())
{
if (lock()) ConsoleLog writeLine(QString("Locked."));
_timerLockDelay = 0;
}
killTimer(event->timerId());
}
bool PVS::allowExists()
{
return policyFileExists(QString(".allow"));
}
/**
* check whether we want to allow vnc connections to this client
*/
bool PVS::getVNCAllow()
{
QString value = getConfigValue("Permissions/vnc_lecturer");
_vncAllowed = (value == "rw" || value == "ro");
if (_vncAllowed && _vncRequested)
{
// then boot up the vnc-server and set password and port
int result = bootVNC();
if (result == 0)
{
// now inform the server
_pvsServerConnection->sendMessage(PVSMsg(PVSCOMMAND, "PORT", int2String(_vncPort)));
_pvsServerConnection->sendMessage(PVSMsg(PVSCOMMAND, "PASSWORD", _vncPassword));
_pvsServerConnection->sendMessage(PVSMsg(PVSCOMMAND, "RWPASSWORD", _vncRwPassword));
_pvsServerConnection->sendMessage(PVSMsg(PVSCOMMAND, "VNC","YES"));
ConsoleLog writeNetwork(QString("Sent password, port and granted access."));
// show message on clientgui
emit showMessage(tr("VNC connection"), tr("The host ")
.append(_pvsServerConnection->getServerName()
.append(tr(" requested your screen!"))));
}
_pvsServerConnection->sendMessage(PVSMsg(PVSCOMMAND, "VNCSRVRESULT", int2String(result)));
}
else if (!_vncAllowed)
{
stopVNCScript();
_pvsServerConnection->sendMessage(PVSMsg(PVSCOMMAND, "VNC","NO"));
}
return _vncAllowed;
}
/**
* read the policy files
*/
void PVS::readPolicyFiles()
{
getVNCAllow();
// Add more policyfiles here if any
}
/**
* callback which gets invoked when the watched file is changed
*/
void PVS::fileChanged(const QString& path)
{
getVNCAllow();
}
/**
* lock the client
*/
bool PVS::lock()
{
#ifdef lock_test
_timerLockTest = startTimer(lock_test_unlock_time * 1000);
printf("will auto-unlock in %d secs\n", int(lock_test_unlock_time));
#endif
if (!_blankScreen)
{
_blankScreen = new BlankScreen();
if (_blankScreen)
return _locked = _blankScreen->lock();
else
return false;
}
return true;
}
/**
* lock the client inputs only
*/
bool PVS::lock_inputs()
{
#ifdef lock_test
_timerLockTest = startTimer(lock_test_unlock_time * 1000);
printf("will auto-unlock in %d secs\n", int(lock_test_unlock_time));
#endif
if (!_blankScreen)
{
_blankScreen = new BlankScreen();
if (_blankScreen)
return _locked = _blankScreen->lock_inputs();
else
return false;
}
return true;
}
/**
* unlock the client
*/
bool PVS::unlock()
{
if (_blankScreen)
{
if (_blankScreen->unlock())
{
delete _blankScreen;
_blankScreen = NULL;
}
}
return false;
}
void PVS::setScriptPath(QString path)
{
_vncScriptPath = path;
_vncScriptName = getFilenameFromPath(_vncScriptPath);
}
bool PVS::gotVNCScript()
{
if (!_vncScriptPath.isEmpty() && fileExists(_vncScriptPath))
return true;
if (!_vncScriptName.isEmpty() && policyFileExists(_vncScriptName))
return true;
return false;
}
/**
* setup password and port
* the local_test define is used to enable local testing modes without invoking the vnc-script
*/
int PVS::bootVNC()
{
// #define local_test
stopVNCScript(); // just to be sure
#ifndef local_test
_vncPassword = int2String(getRandom(0, 99999999));
do {
_vncRwPassword = int2String(getRandom(0, 99999999));
} while (_vncRwPassword == _vncPassword);
_vncPort = getRandom(5901, 6000);
return startVNCScript(_vncPort, _vncPassword, _vncRwPassword);
#else
_vncPassword = QString("1234");
_vncPort = 5901;
return startVNCScript(_vncPort, _vncPassword);
#endif
//#undef local_test
}
/**
* launch the vnc script (if present) to start the vnc-server
*/
int PVS::startVNCScript(int port, QString pass, QString rwpass)
{
if (gotVNCScript())
{
ConsoleLog writeLine("Starting VNC-Script");
QString commandLine(_vncScriptPath);
commandLine.append(" ");
commandLine.append("start");
commandLine.append(" ");
commandLine.append(int2String(port));
commandLine.append(" ");
commandLine.append(pass);
commandLine.append(" ");
commandLine.append(rwpass);
if (!system(NULL))
ConsoleLog writeError("No Command processor available");
int result = system(commandLine.toUtf8().data());
#ifndef __WIN32__
//TODO Win32
result = WEXITSTATUS(result);
if (result != 0)
ConsoleLog writeError(QString("VNC-Server is not running, code: ") + int2String(result));
else
ConsoleLog writeLine("VNC-Server should be running");
return result;
#else
/*Code fuer VNC-Aufruf unter Windows einfuegen*/
return 0;
#endif /*__WIN32__*/
}
else
{
ConsoleLog writeError("VNC-Script not found.");
return 3;
}
}
/**
* launch the vnc script (if present) to stop the vnc-server
*/
int PVS::stopVNCScript()
{
if (gotVNCScript())
{
#ifndef __WIN32__
ConsoleLog writeLine("Stopping VNC-Script");
QString commandLine(_vncScriptPath);
commandLine.append(" ");
commandLine.append("stop");
commandLine.append(" ");
commandLine.append("dummy");
commandLine.append(" ");
commandLine.append("dummy");
ConsoleLog writeLine("VNC-Server should be stopped");
int result = system(commandLine.toUtf8().data());
return WEXITSTATUS(result);
#else
/*Code fuer VNC-Server stoppen unter Windows einfuegen*/
return 0;
#endif /*__WIN32__*/
}
else
{
return 3;
}
}
bool PVS::start()
{
_pvsServerConnection->sendMessage(PVSMsg(PVSCOMMAND, "PROJECTING", "YES"));
return true;
}
void PVS::onConnected(QString name)
{
emit connected(name);
}
void PVS::onDisconnected()
{
emit disconnected();
_chat->clearClients();
stopVNCScript();
}
QString PVS::isConnected()
{
return _pvsServerConnection->getServerName();
}
QString PVS::chat_getNickname()
{
return _chat->getSource();
}
QStringList PVS::chat_getNicknames()
{
return _chat->getClients();
}
void PVS::pvsConnect(QString host, QString passwd)
{
_pvsServerConnection->disconnectFromServer();
_sdClient->connectToSession(host, passwd);
}
void PVS::pvsDisconnect()
{
_chat->clearClients();
_sdClient->connectToSession("", "");
stop();
}
QStringList PVS::getAvailableHosts()
{
return _sdClient->getAllServers();
}
void PVS::guiAddHost(QString host)
{
emit addHost(host);
}
void PVS::guiDelHost(QString host)
{
emit delHost(host);
}
QString PVS::getIpByNick(QString nick)
{
return _chat->getIPFromUsername(nick);
}
//Handles signals (SIGTERM and SIGHUP)
void PVS::signalHandler(int signal)
{
ConsoleLog writeLine(QString("Received Signal ").append (int2String(signal)));
#ifndef __WIN32__
switch (signal) {
case SIGHUP:
mainClient->quit();
break;
case SIGTERM:
mainClient->quit();
break;
case SIGINT:
mainClient->quit();
break;
case SIGQUIT:
mainClient->quit();
break;
}
#else
ConsoleLog writeLine("Abfang nicht definiert fuer Windows");
#endif /*__WIN32__*/
}
void PVS::setConfigValue(QString key, QString value)
{
_settings.setValue(key, value);
_settings.sync();
getVNCAllow();
}
QString PVS::getConfigValue(QString key)
{
return _settings.value(key).toString();
}