summaryrefslogblamecommitdiffstats
path: root/src/client/vnc/vncserver.cpp
blob: a016f08f96d4b71dde7af30ea721bacc8ceb10c3 (plain) (tree)
1
2
3
4
5
6
7
8
9






                          

                       
                   
                         


                         
                                   
 



                                                                                

                                
                          



                                     




                                                                                

                                         
                                            
                                     

                                                  

 


                                                                                
                                 
                                                                           

  


                                                                                
                                                                    
 


                                                                                
                          
 











                                                                           


                                                                                


                            
                                  







                                                                                                                         





                                                                                        


                                                  





                                                                       









                                                                                                              
                                                                         

                          
                                                      

                                     

                                                           




                                                   


                                                            

                     
                                              
                                                             

 


                                                                                

                      
                            


                                    
                                




                                                                                        
                           










                                                                                
 
  


            


                                                                                
                     
               
   
                                                      





                                                                
 
  


        



                                                                                

                          
                                  




                                                                     
                         
                                                         
                                



                                                                                                         
                                                         






                                                    


                                                                                

                          
                                  





                                                                     



                                                                                
                                                            




                                          



                                                                                
                                               



                                          
/*
 * vncserver.cpp
 *
 *  Created on: 24.01.2013
 *      Author: sr
 */


#include <QApplication>
#include <QProcess>
#include <QDesktopWidget>
#include "vncserver.h"
#include "../util/util.h"

VncServer* VncServer::me = nullptr;

/***************************************************************************//**
 * @brief VncServer::instance
 * @return
 */
VncServer* VncServer::instance()
{
	if (me == nullptr)
		me = new VncServer();
	return me;
}

/***************************************************************************//**
 * @brief makePassword
 * @param len
 * @return
 */
static QString makePassword(int len = 10)
{
	QString ret(len, Qt::Uninitialized);
	for (int i = 0; i < len; ++i)
		ret[i] = QChar(43 + qrand() % 80);
	return ret;
}

/***************************************************************************//**
 * @brief Ugly hack to get an el-cheapo platform independent sleep
 */
struct Sleeper : public QThread {
	static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
};

/***************************************************************************//**
 * @brief VncServer::VncServer
 */
VncServer::VncServer() : _process(nullptr), _port(0), _timerId(0) {}

/***************************************************************************//**
 * @brief VncServer::~VncServer
 */
VncServer::~VncServer() {}

QSharedPointer<QFile> VncServer::createPwFile(const QDir& dir)
{
	QDir::root().mkpath(dir.absolutePath());
	QString str(dir.absoluteFilePath("vncpass"));
	QSharedPointer<QFile> file = QSharedPointer<QFile>(new QFile(str));
	if (file->exists()) {
		file->remove();
	}
	file->open(QIODevice::WriteOnly);
	return file;
}

/***************************************************************************//**
 * @brief VncServer::start
 */
void VncServer::start()
{
	// Keep things clean
	if (_process != nullptr) {
		disconnect(_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
		disconnect(_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
	}
	this->stop();
	// Generate passwords
	_rwpass = makePassword();
	_ropass = makePassword();
	// Create new password file
	QSharedPointer<QFile> pwhandle = createPwFile(Util::settingsDir());
	if (!pwhandle->isOpen()) {
		pwhandle = createPwFile(QDir("/tmp"));
	}
	if (!pwhandle->isOpen()) {
		qDebug() << "Could not open " << pwhandle->fileName() << " for writing";
		emit started(0, _ropass, _rwpass);
		return;
	}
	pwhandle->setPermissions(QFile::ReadOwner | QFile::WriteOwner);
	pwhandle->write(_rwpass.toUtf8().constData());
	pwhandle->write("\n");
	pwhandle->write(_ropass.toUtf8().constData());
	pwhandle->write("\n");
	pwhandle->close();
	// Create new process
	_process = new QProcess(this);
	connect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStdOut()));
	connect(_process, SIGNAL(readyReadStandardError()), this, SLOT(onStdErr()));
	connect(_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
	connect(_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
	_timerId = startTimer(4000);
	QStringList args;
	args << "-forever";
	args << "-display" << ":0";
	args << "-passwdfile" << (QString("rm:" + pwhandle->fileName()));
	args << "-shared";
	args << "-repeat";
	args << "-autoport" << QString::number(54112);

	// Get rect of primary screen
	const QDesktopWidget desktop;
	const QRect primaryRect = desktop.screenGeometry();

	// Tell x11vnc to just use primary screen
	args << "-clip";
	QString rect;
	rect = QString::number(primaryRect.width())
	       + "x" + QString::number(primaryRect.height())
	       + "+" + QString::number(primaryRect.x())
	       + "+" + QString::number(primaryRect.y());
	args << rect;

	qDebug() << "Arguments are: " << args;
	_process->start("x11vnc", args, QIODevice::ReadOnly);
}

/***************************************************************************//**
 * @brief VncServer::stop
 */
void VncServer::stop()
{
	if (_timerId != 0) {
		killTimer(_timerId);
		_timerId = 0;
	}
	if (_process == nullptr)
		return;
	qDebug("Stopping old VNC server.");
	disconnect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStdOut()));
	disconnect(_process, SIGNAL(readyReadStandardError()), this, SLOT(onStdErr()));
	QProcess *process = _process;
	_process = nullptr;
	_port = 0;
	process->terminate();
	for (int i = 0; i < 10 && process->state() != QProcess::NotRunning; ++i)
		Sleeper::msleep(10);
	if (process->state() == QProcess::Running)
		process->kill();
	for (int i = 0; i < 10 && process->state() != QProcess::NotRunning; ++i)
		Sleeper::msleep(10);
	process->deleteLater();
}


/*
 * Overrides
 */


/***************************************************************************//**
 * @brief Timer event, currently only used to assume VNC server setup failed
 * after 3 seconds...
 * @param event
 */
void VncServer::timerEvent(QTimerEvent * /* event */ )
{
	// Error timeout (3s), tell server that vnc setup failed
	this->stop();
	emit started(0, _ropass, _rwpass);
}


/*
 * Slots
 */


/***************************************************************************//**
 * @brief VncServer::onStdOut
 */
void VncServer::onStdOut()
{
	if (_process == nullptr) {
		qDebug("VncServer::onStdOut() called in bad state.");
		return;
	}
	QByteArray data(_process->readAllStandardOutput());
	qDebug() << "x11vnc: " << data;
	if (_port <= 0) {
		const int pos = data.indexOf("PORT=", 0);
		if (pos != -1) {
			_port = atoi(data.constData() + pos + 5);
			qDebug() << "Got VNC port " << _port << ", ro " << _ropass << ", rw " << _rwpass;
			emit started(_port, _ropass, _rwpass);
			// Kill error timer, but only if port seemed valid
			if (_timerId != 0 && _port > 0) {
				killTimer(_timerId);
				_timerId = 0;
			}
		}
	}
}

/***************************************************************************//**
 * @brief VncServer::onStdErr
 */
void VncServer::onStdErr()
{
	if (_process == nullptr) {
		qDebug("VncServer::onStdErr() called in bad state.");
		return;
	}
	QByteArray data(_process->readAllStandardError());
}

/***************************************************************************//**
 * @brief VncServer::onError
 * @param error
 */
void VncServer::onError(QProcess::ProcessError /* error */ )
{
	this->stop();
	emit started(0, _ropass, _rwpass);
}

/***************************************************************************//**
 * @brief VncServer::onFinished
 * @param exitCode
 */
void VncServer::onFinished(int /* exitCode */ )
{
	this->stop();
	emit started(0, _ropass, _rwpass);
}