/* * vncserver.cpp * * Created on: 24.01.2013 * Author: sr */ #include #include #include #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 VncServer::createPwFile(const QDir& dir) { QDir::root().mkpath(dir.absolutePath()); QString str(dir.absoluteFilePath("vncpass")); QSharedPointer file = QSharedPointer(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 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); }