diff --git a/sddm-0.2.0-0.11.20130914git50ca5b20-xdmcp.patch b/sddm-0.2.0-0.11.20130914git50ca5b20-xdmcp.patch new file mode 100644 index 0000000..ab6ce8a --- /dev/null +++ b/sddm-0.2.0-0.11.20130914git50ca5b20-xdmcp.patch @@ -0,0 +1,1660 @@ +diff --git a/data/sddm.conf.in b/data/sddm.conf.in +index 9522ffd..204dc52 100644 +--- a/data/sddm.conf.in ++++ b/data/sddm.conf.in +@@ -89,3 +89,6 @@ MinimumVT=7 + # Valid values: on|off|none + # If property is set to none, numlock won't be changed + Numlock=none ++ ++# Enables the XDMCP Server ++XDMCPServer=false +\ No newline at end of file +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index a82ae45..1d6d117 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -20,6 +20,9 @@ set(DAEMON_SOURCES + daemon/Session.cpp + daemon/SignalHandler.cpp + daemon/SocketServer.cpp ++ daemon/xdmcp/Packet.cpp ++ daemon/xdmcp/Server.cpp ++ daemon/xdmcp/Utils.cpp + ) + + if(USE_QT5) +diff --git a/src/common/Configuration.cpp b/src/common/Configuration.cpp +index 2095b05..4b780b3 100644 +--- a/src/common/Configuration.cpp ++++ b/src/common/Configuration.cpp +@@ -63,6 +63,7 @@ namespace SDDM { + bool autoRelogin { false }; + + Configuration::NumState numlock { Configuration::NUM_NONE }; ++ bool xdmcpServerEnabled { false }; + }; + + Configuration::Configuration(const QString &configPath, QObject *parent) : QObject(parent), d(new ConfigurationPrivate()) { +@@ -122,6 +123,7 @@ namespace SDDM { + } else { + d->numlock = Configuration::NUM_NONE; + } ++ d->xdmcpServerEnabled = settings.value("XDMCPServer", d->xdmcpServerEnabled).toBool(); + } + + void Configuration::save() { +@@ -158,6 +160,8 @@ namespace SDDM { + settings.setValue("Numlock", "on"); + else if (d->numlock == NUM_SET_OFF) + settings.setValue("Numlock", "off"); ++ ++ settings.setValue("XDMCPServer", d->xdmcpServerEnabled); + } + + Configuration *Configuration::instance() { +@@ -261,4 +265,9 @@ namespace SDDM { + const Configuration::NumState Configuration::numlock() const { + return d->numlock; + } ++ ++ bool Configuration::xdmcpServerEnabled() const { ++ return d->xdmcpServerEnabled; ++ } ++ + } +diff --git a/src/common/Configuration.h b/src/common/Configuration.h +index cbef261..4b610d8 100644 +--- a/src/common/Configuration.h ++++ b/src/common/Configuration.h +@@ -79,6 +79,8 @@ namespace SDDM { + enum NumState { NUM_NONE, NUM_SET_ON, NUM_SET_OFF }; + const NumState numlock() const; + ++ bool xdmcpServerEnabled() const; ++ + bool first { true }; + + bool testing { false }; +diff --git a/src/daemon/DaemonApp.cpp b/src/daemon/DaemonApp.cpp +index c40cdea..616282e 100644 +--- a/src/daemon/DaemonApp.cpp ++++ b/src/daemon/DaemonApp.cpp +@@ -25,6 +25,7 @@ + #include "PowerManager.h" + #include "SeatManager.h" + #include "SignalHandler.h" ++#include "xdmcp/Server.h" + + #ifdef USE_QT5 + #include "MessageHandler.h" +@@ -65,6 +66,12 @@ namespace SDDM { + // create seat manager + m_seatManager = new SeatManager(this); + ++ // start the XDMCP server ++ if (configuration()->xdmcpServerEnabled()) { ++ m_xdmcpServer = XDMCP::Server::instance(this); ++ m_xdmcpServer->start(); ++ } ++ + // connect with display manager + connect(m_seatManager, SIGNAL(seatCreated(QString)), m_displayManager, SLOT(AddSeat(QString))); + connect(m_seatManager, SIGNAL(seatRemoved(QString)), m_displayManager, SLOT(RemoveSeat(QString))); +diff --git a/src/daemon/DaemonApp.h b/src/daemon/DaemonApp.h +index 81f955c..2088010 100644 +--- a/src/daemon/DaemonApp.h ++++ b/src/daemon/DaemonApp.h +@@ -29,6 +29,9 @@ namespace SDDM { + class DisplayManager; + class PowerManager; + class SeatManager; ++ namespace XDMCP { ++ class Server; ++ } + + class DaemonApp : public QCoreApplication { + Q_OBJECT +@@ -57,6 +60,7 @@ namespace SDDM { + DisplayManager *m_displayManager { nullptr }; + PowerManager *m_powerManager { nullptr }; + SeatManager *m_seatManager { nullptr }; ++ XDMCP::Server *m_xdmcpServer { nullptr }; + }; + } + +diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp +index 8517124..80aa95a 100644 +--- a/src/daemon/Display.cpp ++++ b/src/daemon/Display.cpp +@@ -53,6 +53,19 @@ namespace SDDM { + return name; + } + ++ Display::Display(const QString& hostname, const int displayId, QObject* parent) ++ : QObject(parent) ++ , m_displayId(displayId) ++ , m_authenticator(new Authenticator(this)) ++ , m_displayServer(nullptr) ++ , m_socketServer(new SocketServer(this)) ++ , m_greeter(new Greeter(this)) ++ { ++ m_display = QString("%1:%2").arg(hostname).arg(displayId); ++ ++ init(); ++ } ++ + Display::Display(const int displayId, const int terminalId, QObject *parent) : QObject(parent), + m_displayId(displayId), m_terminalId(terminalId), + m_authenticator(new Authenticator(this)), +@@ -62,12 +75,17 @@ namespace SDDM { + + m_display = QString(":%1").arg(m_displayId); + +- // restart display after user session ended +- connect(m_authenticator, SIGNAL(stopped()), this, SLOT(stop())); +- + // restart display after display server ended + connect(m_displayServer, SIGNAL(stopped()), this, SLOT(stop())); + ++ init(); ++ } ++ ++ void Display::init() ++ { ++ // restart display after user session ended ++ connect(m_authenticator, SIGNAL(stopped()), this, SLOT(stop())); ++ + // connect login signal + connect(m_socketServer, SIGNAL(login(QLocalSocket*,QString,QString,QString)), this, SLOT(login(QLocalSocket*,QString,QString,QString))); + +@@ -90,6 +108,22 @@ namespace SDDM { + + // set socket name + m_socket = QString("sddm-%1-%2").arg(m_display).arg(generateName(6)); ++ ++ // generate cookie ++ std::random_device rd; ++ std::mt19937 gen(rd()); ++ std::uniform_int_distribution<> dis(0, 15); ++ ++ // resever 32 bytes ++ m_cookie.reserve(32); ++ ++ // create a random hexadecimal number ++ const char *digits = "0123456789abcdef"; ++ for (int i = 0; i < 32; ++i) ++ m_cookie[i] = digits[dis(gen)]; ++ ++ // generate auth file ++ addCookie(m_authPath); + } + + Display::~Display() { +@@ -112,6 +146,16 @@ namespace SDDM { + return m_cookie; + } + ++ const QByteArray Display::rawCookie() const { ++ QByteArray cookie; ++ for (int i = 0; i < m_cookie.length() / 2; i++) { ++ // horrible, just horrible ++ quint8 byte = QString("%1%2").arg(m_cookie[i*2]).arg(m_cookie[i*2+1]).toUInt(nullptr, 16); ++ cookie.append(byte); ++ } ++ return cookie; ++ } ++ + void Display::addCookie(const QString &file) { + // log message + qDebug() << " DAEMON: Adding cookie to" << file; +@@ -139,28 +183,14 @@ namespace SDDM { + if (m_started) + return; + +- // generate cookie +- std::random_device rd; +- std::mt19937 gen(rd()); +- std::uniform_int_distribution<> dis(0, 15); +- +- // resever 32 bytes +- m_cookie.reserve(32); ++ if (m_displayServer != nullptr) { ++ // set display server params ++ m_displayServer->setDisplay(m_display); ++ m_displayServer->setAuthPath(m_authPath); + +- // create a random hexadecimal number +- const char *digits = "0123456789abcdef"; +- for (int i = 0; i < 32; ++i) +- m_cookie[i] = digits[dis(gen)]; +- +- // generate auth file +- addCookie(m_authPath); +- +- // set display server params +- m_displayServer->setDisplay(m_display); +- m_displayServer->setAuthPath(m_authPath); +- +- // start display server +- m_displayServer->start(); ++ // start display server ++ m_displayServer->start(); ++ } + + if ((daemonApp->configuration()->first || daemonApp->configuration()->autoRelogin()) && + !daemonApp->configuration()->autoUser().isEmpty() && !daemonApp->configuration()->lastSession().isEmpty()) { +@@ -216,9 +246,11 @@ namespace SDDM { + m_socketServer->stop(); + + // stop display server +- m_displayServer->blockSignals(true); +- m_displayServer->stop(); +- m_displayServer->blockSignals(false); ++ if (m_displayServer != nullptr) { ++ m_displayServer->blockSignals(true); ++ m_displayServer->stop(); ++ m_displayServer->blockSignals(false); ++ } + + // remove authority file + QFile::remove(m_authPath); +diff --git a/src/daemon/Display.h b/src/daemon/Display.h +index 9d82678..9c475a9 100644 +--- a/src/daemon/Display.h ++++ b/src/daemon/Display.h +@@ -34,6 +34,7 @@ namespace SDDM { + Q_OBJECT + Q_DISABLE_COPY(Display) + public: ++ explicit Display(const QString& hostname, const int displayId, QObject *parent = 0); + explicit Display(const int displayId, const int terminalId, QObject *parent = 0); + ~Display(); + +@@ -43,6 +44,7 @@ namespace SDDM { + const QString &name() const; + + const QString &cookie() const; ++ const QByteArray rawCookie() const; + void addCookie(const QString &file); + + public slots: +@@ -58,6 +60,8 @@ namespace SDDM { + void loginSucceeded(QLocalSocket *socket); + + private: ++ void init(); ++ + bool m_relogin { true }; + bool m_started { false }; + +diff --git a/src/daemon/xdmcp/Packet.cpp b/src/daemon/xdmcp/Packet.cpp +new file mode 100644 +index 0000000..90688d0 +--- /dev/null ++++ b/src/daemon/xdmcp/Packet.cpp +@@ -0,0 +1,435 @@ ++/* ++ * Packet type handling for X Display Control Protocol ++ * Copyright (C) 2013 Martin Bříza ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#include "Packet.h" ++#include "Server.h" ++#include "../Display.h" ++ ++namespace SDDM { ++namespace XDMCP { ++ ++/******************************************************************************* ++* PLUMBING ++ ******************************************************************************/ ++ ++ Packet::Packet(const QHostAddress& host, quint16 port) ++ : m_host(host) ++ , m_port(port) ++ , m_valid(true) { ++ ++ } ++ ++ Packet::Packet(const QHostAddress& host, quint16 port, Reader& r) ++ : m_host(host) ++ , m_port(port) ++ , m_valid(false) { ++ ++ } ++ ++ Packet::~Packet() { ++ ++ } ++ ++ bool Packet::isValid() const { ++ return m_valid; ++ } ++ ++ // static ++ Packet *Packet::decode(const QByteArray& data, const QHostAddress& host, quint16 port) { ++ Reader reader(data); ++ uint16_t version, opcode, length; ++ ++ reader >> version >> opcode >> length; ++ ++ if (version != 1) ++ return nullptr; ++ if (length != data.size() - 6) ++ return nullptr; ++ ++ switch (opcode) { ++ case _Query: ++ return new Query(host, port, reader); ++ case _BroadcastQuery: ++ return new BroadcastQuery(host, port, reader); ++ case _IndirectQuery: ++ return new IndirectQuery(host, port, reader); ++ case _ForwardQuery: ++ return new ForwardQuery(host, port, reader); ++ case _Willing: ++ return new Willing(host, port, reader); ++ case _Unwilling: ++ return new Unwilling(host, port, reader); ++ case _Request: ++ return new Request(host, port, reader); ++ case _Accept: ++ return new Accept(host, port, reader); ++ case _Decline: ++ return new Decline(host, port, reader); ++ case _Manage: ++ return new Manage(host, port, reader); ++ case _Refuse: ++ return new Refuse(host, port, reader); ++ case _Failed: ++ return new Failed(host, port, reader); ++ case _KeepAlive: ++ return new KeepAlive(host, port, reader); ++ case _Alive: ++ return new Alive(host, port, reader); ++ default: ++ qDebug() << " XDMCP: Got packet of an unknown type" << opcode; ++ return nullptr; ++ } ++ } ++ ++ void Packet::setHost(const QHostAddress host) { ++ m_host = QHostAddress(host); ++ } ++ ++ QHostAddress Packet::host() const { ++ return m_host; ++ } ++ ++ void Packet::setPort(quint16 port) { ++ m_port = port; ++ } ++ ++ quint16 Packet::port() const { ++ return m_port; ++ } ++ ++ QByteArray Packet::encode() const { ++ return QByteArray(); ++ } ++ ++ Packet *Packet::onClientReceived() const { ++ qDebug() << " XDMCP: Client received a wrong packet type (no action assigned)"; ++ return nullptr; ++ } ++ ++ Packet *Packet::onServerReceived() const { ++ qDebug() << " XDMCP: Server received a wrong packet type (no action assigned)"; ++ return nullptr; ++ } ++ ++ Packet::Query::Query(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_authenticationNames; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Query::encode() const { ++ Writer w; ++ w << m_authenticationNames; ++ return w.finalize(Packet::_Query); ++ } ++ ++ Packet::BroadcastQuery::BroadcastQuery(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_authenticationNames; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::BroadcastQuery::encode() const { ++ Writer w; ++ w << m_authenticationNames; ++ return w.finalize(Packet::_BroadcastQuery); ++ } ++ ++ Packet::IndirectQuery::IndirectQuery(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_authenticationNames; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::IndirectQuery::encode() const { ++ Writer w; ++ w << m_authenticationNames; ++ return w.finalize(Packet::_IndirectQuery); ++ } ++ ++ Packet::ForwardQuery::ForwardQuery(const QHostAddress& host, quint16 port, Reader& r) : Packet(host, port, r) { ++ r >> m_clientAddress >> m_clientPort >> m_authenticationNames; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::ForwardQuery::encode() const { ++ Writer w; ++ w << m_clientAddress << m_clientPort << m_authenticationNames; ++ return w.finalize(Packet::_ForwardQuery); ++ } ++ ++ Packet::Willing::Willing(const QHostAddress& host, quint16 port, const QString& authenticationName, const QString& hostname, const QString& status) ++ : Packet(host, port) ++ , m_authenticationName(authenticationName.toLatin1()) ++ , m_hostname(hostname.toLatin1()) ++ , m_status(status.toLatin1()) { ++ qDebug() << " XDMCP: Prepared Willing reply for" << host << port << "with contents" << authenticationName << hostname << status; ++ } ++ ++ Packet::Willing::Willing(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_authenticationName >> m_hostname >> m_status; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Willing::encode() const { ++ Writer w; ++ w << m_authenticationName << m_hostname << m_status; ++ return w.finalize(Packet::_Willing); ++ } ++ ++ Packet::Unwilling::Unwilling(const QHostAddress& host, quint16 port, const QString& hostname, const QString& status) ++ : Packet(host, port) ++ , m_hostname(hostname.toLatin1()) ++ , m_status(status.toLatin1()) { ++ qDebug() << " XDMCP: Prepared Unwilling reply for" << host << port << "with contents" << hostname << status; ++ } ++ ++ Packet::Unwilling::Unwilling(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_hostname >> m_status; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Unwilling::encode() const { ++ Writer w; ++ w << m_hostname << m_status; ++ return w.finalize(Packet::_Unwilling); ++ } ++ ++ Packet::Request::Request(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_displayNumber >> m_connectionTypes >> m_connectionAddresses ++ >> m_authenticationName >> m_authenticationData >> m_authorizationNames ++ >> m_manufacturerDisplayID; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Request::encode() const { ++ Writer w; ++ w << m_displayNumber << m_connectionTypes << m_connectionAddresses ++ << m_authenticationName << m_authenticationData << m_authorizationNames ++ << m_manufacturerDisplayID; ++ return w.finalize(Packet::_Request); ++ } ++ ++ Packet::Accept::Accept(const QHostAddress& host, quint16 port, uint32_t sessionId, const QString authenticationName, const QByteArray authenticationData, const QString authorizationName, const QByteArray authorizationData) ++ : Packet(host, port) ++ , m_sessionID(sessionId) ++ , m_authenticationName(authenticationName.toLatin1()) ++ , m_authenticationData(authenticationData) ++ , m_authorizationName(authorizationName.toLatin1()) ++ , m_authorizationData(authorizationData) { ++ qDebug() << " XDMCP: Prepared Accept reply for" << host << port << "with contents" << sessionId << authenticationName << authenticationData << authorizationName << authorizationData; ++ } ++ ++ Packet::Accept::Accept(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_sessionID >> m_authenticationName >> m_authenticationData ++ >> m_authorizationName >> m_authorizationData; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Accept::encode() const { ++ Writer w; ++ w << m_sessionID << m_authenticationName << m_authenticationData ++ << m_authorizationName << m_authorizationData; ++ return w.finalize(Packet::_Accept); ++ } ++ ++ Packet::Decline::Decline(const QHostAddress& host, quint16 port, const QString status, const QString authenticationName, const QByteArray authenticationData) ++ : Packet(host, port) ++ , m_status(status.toLatin1()) ++ , m_authenticationName(authenticationName.toLatin1()) ++ , m_authenticationData(authenticationData) { ++ qDebug() << " XDMCP: Prepared Decline reply for" << host << port << "with contents" << status << authenticationName << authenticationData; ++ } ++ ++ Packet::Decline::Decline(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_status >> m_authenticationName >> m_authenticationData; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Decline::encode() const { ++ Writer w; ++ w << m_status << m_authenticationName << m_authenticationData; ++ return w.finalize(Packet::_Decline); ++ } ++ ++ Packet::Manage::Manage(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_sessionID >> m_displayNumber >> m_displayClass; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Manage::encode() const { ++ Writer w; ++ w << m_sessionID << m_displayNumber << m_displayClass; ++ return w.finalize(Packet::_Manage); ++ } ++ ++ Packet::Refuse::Refuse(const QHostAddress& host, quint16 port, uint32_t sessionID) ++ : Packet(host, port) ++ , m_sessionID(sessionID) { ++ qDebug() << " XDMCP: Prepared Refuse reply for" << host << port << "with contents" << sessionID; ++ } ++ ++ Packet::Refuse::Refuse(const QHostAddress& host, quint16 port, Reader& r) : Packet(host, port, r) { ++ r >> m_sessionID; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Refuse::encode() const { ++ Writer w; ++ w << m_sessionID; ++ return w.finalize(Packet::_Refuse); ++ } ++ ++ Packet::Failed::Failed(const QHostAddress& host, quint16 port, uint32_t sessionID, const QString& status) ++ : Packet(host, port) ++ , m_sessionID(sessionID) ++ , m_status(status.toLatin1()) { ++ qDebug() << " XDMCP: Prepared Failed reply for" << host << port << "with contents" << sessionID << status; ++ } ++ ++ Packet::Failed::Failed(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_sessionID >> m_status; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Failed::encode() const { ++ Writer w; ++ w << m_sessionID << m_status; ++ return w.finalize(Packet::_Failed); ++ } ++ ++ Packet::KeepAlive::KeepAlive(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_displayNumber >> m_sessionID; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::KeepAlive::encode() const { ++ Writer w; ++ w << m_displayNumber << m_sessionID; ++ return w.finalize(Packet::_KeepAlive); ++ } ++ ++ Packet::Alive::Alive(const QHostAddress& host, quint16 port, uint8_t sessionRunning, uint32_t sessionID) ++ : Packet(host, port) ++ , m_sessionRunning(sessionRunning) ++ , m_sessionID(sessionID) { ++ qDebug() << " XDMCP: Prepared Alive reply for" << host << port << "with contents" << sessionRunning << sessionID; ++ } ++ ++ Packet::Alive::Alive(const QHostAddress& host, quint16 port, Reader& r) ++ : Packet(host, port, r) { ++ r >> m_sessionRunning >> m_sessionID; ++ if (r.isFinished()) { ++ m_valid = true; ++ } ++ } ++ ++ QByteArray Packet::Alive::encode() const { ++ Writer w; ++ w << m_sessionRunning << m_sessionID; ++ return w.finalize(Packet::_Alive); ++ } ++ ++/******************************************************************************* ++ * SERVER IMPLEMENTATIONS ++ ******************************************************************************/ ++ ++ Packet *Packet::Query::onServerReceived() const { ++ if (m_authenticationNames.isEmpty()) { ++ return new Willing(m_host, m_port, "", Server::instance()->hostname(), Server::instance()->status()); ++ } ++ else { ++ return new Unwilling(m_host, m_port, Server::instance()->hostname(), "Server does not support authentication"); ++ } ++ } ++ ++ Packet* Packet::Request::onServerReceived() const { ++ qDebug() << " XDMCP: Server: Received Request" << m_displayNumber << m_connectionTypes << m_connectionAddresses << m_authenticationName << m_authenticationData << m_authorizationNames << m_manufacturerDisplayID; ++ if (m_authorizationNames.contains("MIT-MAGIC-COOKIE-1")) { ++ uint32_t sessionId = Server::instance()->newSessionId(); ++ QHostAddress addr(QString("%1.%2.%3.%4").arg((uint) m_connectionAddresses.first()[0]).arg((uint) m_connectionAddresses.first()[1]).arg((uint) m_connectionAddresses.first()[2]).arg((uint) m_connectionAddresses.first()[3])); ++ Display *display = Server::instance()->newDisplay(sessionId, addr.toString(), m_displayNumber); ++ return new Accept(m_host, m_port, sessionId, m_authenticationName, m_authenticationData, "MIT-MAGIC-COOKIE-1", display->rawCookie()); ++ } else { ++ return new Decline(m_host, m_port, Server::instance()->status(), m_authenticationName, m_authenticationData); ++ } ++ } ++ ++ Packet* Packet::Manage::onServerReceived() const { ++ Display *display = Server::instance()->getDisplay(m_sessionID); ++ if (display != nullptr) { ++ display->start(); ++ return nullptr; ++ } else { ++ return new Refuse(m_host, m_port, m_sessionID); ++ } ++ } ++ ++ Packet* Packet::KeepAlive::onServerReceived() const { ++ Display *display = Server::instance()->getDisplay(m_sessionID); ++ if (display == nullptr) ++ return new Alive(m_host, m_port, 0, m_sessionID); ++ else if (display->displayId() != m_displayNumber) ++ return new Alive(m_host, m_port, 0, m_sessionID); ++ else { ++ return new Alive(m_host, m_port, 1, m_sessionID); ++ } ++ } ++ ++/******************************************************************************* ++ * CLIENT IMPLEMENTATIONS ++ ******************************************************************************/ ++ ++}; ++}; +diff --git a/src/daemon/xdmcp/Packet.h b/src/daemon/xdmcp/Packet.h +new file mode 100644 +index 0000000..507b1b4 +--- /dev/null ++++ b/src/daemon/xdmcp/Packet.h +@@ -0,0 +1,394 @@ ++/* ++ * Packet type handling for X Display Control Protocol ++ * Copyright (C) 2013 Martin Bříza ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#ifndef SDDM_XDMCP_PACKET_H ++#define SDDM_XDMCP_PACKET_H ++ ++#include ++#include ++ ++#include "Utils.h" ++ ++namespace SDDM { ++namespace XDMCP { ++ class Reader; ++ /** ++ * XDMCP Packet main class ++ * ++ * Defines interface and static methods to work with the protocol data ++ */ ++ class Packet { ++ public: ++ virtual ~Packet(); ++ ++ /** ++ * Get the packet's validity (especially for responses) ++ * ++ * \return validity ++ */ ++ bool isValid() const; ++ ++ /** ++ * Defines server behavior on receiving this packet ++ */ ++ virtual Packet *onServerReceived() const; ++ ++ /** ++ * Defines client behavior on receiving this packet ++ */ ++ virtual Packet *onClientReceived() const; ++ ++ /** ++ * Encode the packet to raw data according to the protocol ++ * ++ * \return Data byte array ++ */ ++ virtual QByteArray encode() const; ++ ++ /** ++ * Decode raw packet data and create a packet for further processing ++ * ++ * \param data Raw data from the socket ++ * \param host Source host of the packet ++ * \param port Source port of the packet ++ * \return Parsed packet ++ */ ++ static Packet *decode(const QByteArray& data, const QHostAddress& host = QHostAddress(), quint16 port = 0); ++ ++ /** ++ * Set the packet's source/destination host ++ * ++ * \param host The host ++ */ ++ void setHost(const QHostAddress host); ++ ++ /** ++ * Get the packet's source/destination host ++ * ++ * \return The host ++ */ ++ QHostAddress host() const; ++ ++ /** ++ * Set the packet's source/destination host ++ * ++ * \param port The port ++ */ ++ void setPort(quint16 port); ++ ++ /** ++ * Get the packet's source/destination host ++ * ++ * \return The port ++ */ ++ quint16 port() const; ++ ++ /** ++ * Redundancy for everyone! ++ */ ++ enum Opcode { ++ _None = 0, ++ _BroadcastQuery = 1, ++ _Query, ++ _IndirectQuery, ++ _ForwardQuery, ++ _Willing, ++ _Unwilling, ++ _Request, ++ _Accept, ++ _Decline, ++ _Manage, ++ _Refuse, ++ _Failed, ++ _KeepAlive, ++ _Alive, ++ }; ++ ++ class BroadcastQuery; ++ class Query; ++ class IndirectQuery; ++ class ForwardQuery; ++ class Willing; ++ class Unwilling; ++ class Request; ++ class Accept; ++ class Decline; ++ class Manage; ++ class Refuse; ++ class Failed; ++ class KeepAlive; ++ class Alive; ++ ++ protected: ++ /** ++ * C'tor targetted for creating response packets ++ * ++ * Automatically sets the packet to be valid ++ * \param host Destination host for the response ++ * \param port Destination port for the response ++ */ ++ Packet(const QHostAddress& host, quint16 port); ++ /** ++ * C'tor targetted for parsing raw data ++ * ++ * \param host Destination host for the response ++ * \param port Destination port for the response ++ * \param r Reader containing the packet's raw data ++ */ ++ Packet(const QHostAddress& host, quint16 port, Reader& r); ++ ++ QHostAddress m_host; ++ quint16 m_port { 0 }; ++ bool m_valid { false }; ++ }; ++ ++ class Packet::BroadcastQuery : public Packet { ++ public: ++ BroadcastQuery(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ private: ++ QVector m_authenticationNames; ++ }; ++ ++ class Packet::Query : public Packet { ++ public: ++ Query(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ /** ++ * Server side handling of Query packet ++ * ++ * Client sends a list of authorization names ++ * ++ * If the list is empty (and we are willing to continue without ++ * authorization), we reply with \ref Willing with empty ++ * authenticationName ++ * ++ * Otherwise, we choose the one name that complies to the supported ones ++ * in the server and use it as authenticationName for the \ref Willing ++ * packet ++ * ++ * If none of the names complies and/or we don't want to continue ++ * without authorization, the reply is \ref Unwilling ++ * ++ * \return Response ++ */ ++ virtual Packet *onServerReceived() const; ++ private: ++ QVector m_authenticationNames; ++ }; ++ ++ class Packet::IndirectQuery : public Packet { ++ public: ++ IndirectQuery(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ private: ++ QVector m_authenticationNames; ++ }; ++ ++ class Packet::ForwardQuery : public Packet { ++ public: ++ ForwardQuery(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ private: ++ QByteArray m_clientAddress; ++ QByteArray m_clientPort; ++ QVector m_authenticationNames; ++ }; ++ ++ class Packet::Willing : public Packet { ++ public: ++ Willing(const QHostAddress& host, quint16 port, ++ const QString& authenticationName, const QString& hostname, ++ const QString& status); ++ Willing(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ /** ++ * Client side handling of Willing packet ++ * ++ * Description TBD ++ * ++ * Reply on success is \ref Request ++ * ++ * \return Response ++ */ ++// virtual Packet *onClientReceived() const; ++ private: ++ QByteArray m_authenticationName; ++ QByteArray m_hostname; ++ QByteArray m_status; ++ }; ++ ++ class Packet::Unwilling : public Packet { ++ public: ++ Unwilling(const QHostAddress& host, quint16 port, ++ const QString& hostname, const QString& status); ++ Unwilling(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ private: ++ QByteArray m_hostname; ++ QByteArray m_status; ++ }; ++ ++ class Packet::Request : public Packet { ++ public: ++ Request(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ /** ++ * Server side handling of Request packet ++ * ++ * Client informs there will be displey displayNumber running on ++ * connectionAddresses accessible via connectionTypes. ++ * It also authorizes with the authenticationName and authenticationData. ++ * It sends a list of propsed authorizationNames for the server to choose ++ * one of them and reply using the \ref Accept packet where the chosen ++ * authorizationName will be stored along with the authorizationData for ++ * the client and the new sessionID. ++ * ++ * If the display cannot be used, the server replies using the \ref Decline ++ * packet with its status. ++ * ++ * \return Response ++ */ ++ virtual Packet *onServerReceived() const; ++ private: ++ uint16_t m_displayNumber; ++ QVector m_connectionTypes; ++ QVector m_connectionAddresses; ++ QByteArray m_authenticationName; ++ QByteArray m_authenticationData; ++ QVector m_authorizationNames; ++ QByteArray m_manufacturerDisplayID; ++ }; ++ ++ class Packet::Accept : public Packet { ++ public: ++ Accept(const QHostAddress& host, quint16 port, uint32_t sessionId, ++ const QString authenticationName, const QByteArray authenticationData, ++ const QString authorizationName, const QByteArray authorizationData); ++ Accept(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ /** ++ * Client side handling of Accept packet ++ * ++ * Description TBD ++ * ++ * Reply on succes is \ref Manage ++ * ++ * \return Response ++ */ ++// virtual Packet *onClientReceived() const; ++ private: ++ uint32_t m_sessionID; ++ QByteArray m_authenticationName; ++ QByteArray m_authenticationData; ++ QByteArray m_authorizationName; ++ QByteArray m_authorizationData; ++ }; ++ ++ class Packet::Decline : public Packet { ++ public: ++ Decline(const QHostAddress& host, quint16 port, const QString status, ++ const QString authenticationName, const QByteArray authenticationData); ++ Decline(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ private: ++ QByteArray m_status; ++ QByteArray m_authenticationName; ++ QByteArray m_authenticationData; ++ }; ++ ++ class Packet::Manage : public Packet { ++ public: ++ Manage(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ /** ++ * Server side handling of Manage packet ++ * ++ * Client asks the server to open a connection to its opened display ++ * specified in the previous Request packet. ++ * ++ * There is no answer on success, just opening a connection. ++ * ++ * If the connection is specified wrong (erroneous sessionID, etc.), then ++ * the server replies with a \ref Refuse packet. ++ * ++ * If the connection cannot be opened due to an internal error, ++ * \ref Failed packet is sent to the client. ++ * ++ * \return Response ++ */ ++ virtual Packet *onServerReceived() const; ++ private: ++ uint32_t m_sessionID; ++ uint16_t m_displayNumber; ++ QByteArray m_displayClass; ++ }; ++ ++ class Packet::Refuse : public Packet { ++ public: ++ Refuse(const QHostAddress& host, quint16 port, uint32_t sessionID); ++ Refuse(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ private: ++ uint32_t m_sessionID; ++ }; ++ ++ class Packet::Failed : public Packet { ++ public: ++ Failed(const QHostAddress& host, quint16 port, uint32_t sessionID, const QString& status); ++ Failed(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ private: ++ uint32_t m_sessionID; ++ QByteArray m_status; ++ }; ++ ++ class Packet::KeepAlive : public Packet { ++ public: ++ KeepAlive(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ /** ++ * Server side handling of KeepAlive packet ++ * ++ * Clients asks the server if the session is still alive. ++ * ++ * Server replies with \ref Alive packet with either sessionRunning == 0 ++ * for a dead session or sessionRunning != 0 for a live one ++ */ ++ virtual Packet *onServerReceived() const; ++ private: ++ uint16_t m_displayNumber; ++ uint32_t m_sessionID; ++ }; ++ ++ class Packet::Alive : public Packet { ++ public: ++ Alive(const QHostAddress& host, quint16 port, uint8_t sessionRunning, uint32_t sessionID); ++ Alive(const QHostAddress& host, quint16 port, Reader& r); ++ virtual QByteArray encode() const; ++ private: ++ uint8_t m_sessionRunning; ++ uint32_t m_sessionID; ++ }; ++ ++} // namespace XDMCP ++} // namespace SDDM ++ ++#endif // SDDM_XDMCP_PACKET_H +diff --git a/src/daemon/xdmcp/Server.cpp b/src/daemon/xdmcp/Server.cpp +new file mode 100644 +index 0000000..edfdff4 +--- /dev/null ++++ b/src/daemon/xdmcp/Server.cpp +@@ -0,0 +1,149 @@ ++/* ++ * Server implementation for X Display Control Protocol ++ * Copyright (C) 2013 Martin Bříza ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#include "Server.h" ++#include "Packet.h" ++#include "../DaemonApp.h" ++#include "../Display.h" ++ ++#include ++ ++namespace SDDM { ++namespace XDMCP { ++ ++ Server *Server::self = nullptr; ++ ++ Server* Server::instance(DaemonApp* parent) { ++ if (self == nullptr) { ++ self = new Server(parent); ++ } ++ return self; ++ } ++ ++ Server::Server(DaemonApp* parent) ++ : QUdpSocket(parent) ++ , m_hostname(QHostInfo::localHostName()) { ++ ++ } ++ ++ Server::~Server() { ++ ++ } ++ ++ bool Server::start() { ++ qDebug() << " XDMCP: Server: Starting..."; ++ connect(this, SIGNAL(readyRead()), this, SLOT(newData())); ++ bool result = bind(m_address, m_port); ++ if (!result) { ++ qDebug() << " XDMCP: Server: Cannot bind" << m_address << m_port << errorString(); ++ } ++ else { ++ m_started = true; ++ m_status = "online"; ++ qDebug() << " XDMCP: Server: Started and listening on" << m_address << ":" << m_port; ++ } ++ return result; ++ } ++ ++ void Server::socketError(QAbstractSocket::SocketError socketError) { ++ qDebug() << " XDMCP: Error:" << errorString(); ++ // TODO: error recovery ++ m_started = false; ++ m_status = "error"; ++ } ++ ++ QString Server::hostname() const { ++ return m_hostname; ++ } ++ ++ QString Server::status() const { ++ return m_status; ++ } ++ ++ bool Server::isStarted() const { ++ return m_started; ++ } ++ ++ uint32_t Server::newSessionId() { ++ // realistically, can this serve more than 4 billion clients to actually cause trouble in removeDisplay? ++ while (m_displays.keys().contains(m_lastSession)) ++ m_lastSession++; ++ return m_lastSession++; ++ } ++ ++ Display* Server::newDisplay(uint32_t sessionId, QString hostName, uint32_t displayNumber) { ++ if (m_displays.contains(sessionId)) ++ return nullptr; ++ Display *display = new Display(hostName, displayNumber, this); ++ connect(display, SIGNAL(destroyed(QObject*)), SLOT(removeDisplay(QObject*))); ++ m_displays[sessionId] = display; ++ return display; ++ } ++ ++ Display* Server::getDisplay(uint32_t id) { ++ if (m_displays.contains(id)) ++ return m_displays[id]; ++ else ++ return nullptr; ++ } ++ ++ void Server::removeDisplay(QObject* obj) { ++ int key = m_displays.key(qobject_cast(obj), -1); ++ if (key == -1) ++ return; ++ ++ m_displays.remove(key); ++ } ++ ++ void Server::setAddress(QHostAddress address) { ++ m_address = address; ++ } ++ ++ void Server::setPort(int port) { ++ m_port = port; ++ } ++ ++ void Server::newData() { ++ while (hasPendingDatagrams()) { ++ QByteArray data; ++ QHostAddress sender; ++ quint16 port; ++ data.resize(pendingDatagramSize()); ++ ++ readDatagram(data.data(), data.size(), &sender, &port); ++ ++ Packet *toProcess = Packet::decode(data, sender, port); ++ if (toProcess && toProcess->isValid()) { ++ Packet *response = toProcess->onServerReceived(); ++ if (response && response->isValid()) { ++ writeDatagram(response->encode(), response->host(), response->port()); ++ } ++ delete response; ++ } else { ++ qDebug() << " XDMCP: Server: Received packet wasn't decoded as valid"; ++ } ++ delete toProcess; ++ } ++ } ++ ++} // namespace XDMCP ++} // namespace SDDM ++ ++#include "Server.moc" +diff --git a/src/daemon/xdmcp/Server.h b/src/daemon/xdmcp/Server.h +new file mode 100644 +index 0000000..2860bf9 +--- /dev/null ++++ b/src/daemon/xdmcp/Server.h +@@ -0,0 +1,117 @@ ++/* ++ * Server implementation for X Display Control Protocol ++ * Copyright (C) 2013 Martin Bříza ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#ifndef SDDM_XDMCP_SERVER_H ++#define SDDM_XDMCP_SERVER_H ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++namespace SDDM { ++ ++class Display; ++ class DaemonApp; ++namespace XDMCP { ++ ++ class Server : protected QUdpSocket ++ { ++ Q_OBJECT ++ public: ++ /** ++ * Get an instance of the XDMCP server. If there isn't any, construct a ++ * new one ++ * ++ * \param parent Parent for the eventual construction ++ * \return Singleton XDMCP Server instance ++ */ ++ static Server *instance(DaemonApp *parent = nullptr); ++ /** ++ * D'tor ++ */ ++ virtual ~Server(); ++ ++ /** ++ * Set port to listen on ++ * ++ * \param port The port ++ */ ++ void setPort(int port); ++ ++ /** ++ * Set address to listen on ++ * ++ * \param address The address ++ */ ++ void setAddress(QHostAddress address); ++ ++ /** ++ * Start the server ++ * ++ * \return True if successful ++ */ ++ bool start(); ++ ++ /** ++ * Get server online status ++ * ++ * \return True if running ++ */ ++ bool isStarted() const; ++ ++ ++ /** ++ * Returns a new session ID for incoming requests ++ */ ++ uint32_t newSessionId(); ++ ++ /** ++ * Create a new display ++ */ ++ Display *newDisplay(uint32_t sessionId, QString hostName, uint32_t displayNumber); ++ Display *getDisplay(uint32_t id); ++ QString status() const; ++ QString hostname() const; ++ ++ private slots: ++ void newData(); ++ void socketError(QAbstractSocket::SocketError socketError); ++ void removeDisplay(QObject *obj); ++ ++ private: ++ static Server *self; ++ explicit Server(DaemonApp *parent = nullptr); ++ ++ QString m_status { "offline" }; ++ QString m_hostname { "localhost" }; ++ QHostAddress m_address { QHostAddress::Any }; ++ quint16 m_port { XDM_UDP_PORT }; ++ bool m_started { false }; ++ uint32_t m_lastSession { 0 }; ++ QMap m_displays; ++ QMap m_timers; ++ }; ++} ++} ++ ++#endif // SDDM_XDMCP_SERVER_H +diff --git a/src/daemon/xdmcp/Utils.cpp b/src/daemon/xdmcp/Utils.cpp +new file mode 100644 +index 0000000..92e1d6a +--- /dev/null ++++ b/src/daemon/xdmcp/Utils.cpp +@@ -0,0 +1,145 @@ ++/* ++ * Utilities for X Display Control Protocol ++ * Copyright (C) 2013 Martin Bříza ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#include "Utils.h" ++ ++#include ++ ++namespace SDDM { ++namespace XDMCP { ++ ++ Reader::Reader(const QByteArray& data) ++ : m_data(data) ++ , m_stream(&m_data, QIODevice::ReadOnly | QIODevice::Unbuffered) { ++ m_stream.setByteOrder(QDataStream::BigEndian); ++ } ++ ++ Reader& Reader::operator>>(uint8_t& byte) { ++ m_stream >> byte; ++ return *this; ++ } ++ ++ Reader& Reader::operator>>(uint16_t& word) { ++ m_stream >> word; ++ return *this; ++ } ++ ++ Reader& Reader::operator>>(uint32_t& doubleword) { ++ m_stream >> doubleword; ++ return *this; ++ } ++ ++ Reader& Reader::operator>>(QByteArray& array) { ++ uint16_t arrayLen; ++ *this >> arrayLen; ++ while (arrayLen--) { ++ uint8_t byte; ++ *this >> byte; ++ array.append(byte); ++ } ++ return *this; ++ } ++ ++ Reader& Reader::operator>>(QVector< uint16_t >& wordArray) { ++ uint8_t arrayLen; ++ *this >> arrayLen; ++ while (arrayLen--) { ++ uint16_t word; ++ *this >> word; ++ wordArray.append(word); ++ } ++ return *this; ++ } ++ ++ Reader& Reader::operator>>(QVector< QByteArray >& arrayOfArrays) { ++ uint8_t arrayCount; ++ *this >> arrayCount; ++ while (arrayCount--) { ++ QByteArray array; ++ *this >> array; ++ arrayOfArrays.append(array); ++ } ++ return *this; ++ } ++ ++ bool Reader::isFinished() const { ++ if ((m_stream.status() == QDataStream::Ok) && m_stream.atEnd()) ++ return true; ++ else ++ return false; ++ } ++ ++ Writer::Writer() ++ : m_data() ++ , m_stream(&m_data, QIODevice::WriteOnly | QIODevice::Unbuffered) { ++ m_stream.setByteOrder(QDataStream::BigEndian); ++ } ++ ++ Writer& Writer::operator<<(const uint8_t byte) { ++ qDebug() << "Appending:" << byte << QChar(byte); ++ m_stream << byte; ++ return *this; ++ } ++ ++ Writer& Writer::operator<<(const uint16_t word) { ++ m_stream << word; ++ return *this; ++ } ++ ++ Writer& Writer::operator<<(const uint32_t doubleword) { ++ m_stream << doubleword; ++ return *this; ++ } ++ ++ Writer& Writer::operator<<(const QByteArray& array) { ++ *this << (uint16_t) array.count(); ++ for (uint8_t c : array) ++ m_stream << c; ++ return *this; ++ } ++ ++ Writer& Writer::operator<<(const QVector< uint16_t >& wordArray) { ++ *this << (uint8_t) wordArray.count(); ++ for (const uint16_t& i : wordArray) ++ *this << i; ++ return *this; ++ } ++ ++ Writer& Writer::operator<<(const QVector< QByteArray >& arrayOfArrays) { ++ *this << (uint16_t) arrayOfArrays.count(); ++ for (const QByteArray& i : arrayOfArrays) ++ *this << i; ++ return *this; ++ } ++ ++ QByteArray Writer::finalize(uint16_t opcode) { ++ QByteArray result; ++ QDataStream finalStream(&result, QIODevice::WriteOnly | QIODevice::Unbuffered); ++ finalStream.setByteOrder(QDataStream::BigEndian); ++ finalStream << (uint16_t) 1; ++ finalStream << (uint16_t) opcode; ++ finalStream << (uint16_t) m_data.size(); ++ for (uint8_t c : m_data) ++ finalStream << c; ++ return result; ++ } ++ ++} // namespace XDMCP ++} // namespace SDDM +\ No newline at end of file +diff --git a/src/daemon/xdmcp/Utils.h b/src/daemon/xdmcp/Utils.h +new file mode 100644 +index 0000000..9457ef1 +--- /dev/null ++++ b/src/daemon/xdmcp/Utils.h +@@ -0,0 +1,93 @@ ++/* ++ * Utilities for X Display Control Protocol ++ * Copyright (C) 2013 Martin Bříza ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#ifndef SDDM_XDMCP_UTILS_H ++#define SDDM_XDMCP_UTILS_H ++ ++#include ++#include ++ ++#include "Packet.h" ++ ++namespace SDDM { ++namespace XDMCP { ++ ++ /** ++ * Class for reading information from raw packets and setting the right byte order ++ * ++ * Workflow is as follows: ++ * * Construct Reader from the data received ++ * * Using the stream operator extract all required variables ++ * * Check if the stream is at its end by isFinished() ++ */ ++ class Reader { ++ public: ++ Reader(const QByteArray &data); ++ ~Reader() {} ++ Reader& operator>>(uint8_t& byte); ++ Reader& operator>>(uint16_t& word); ++ Reader& operator>>(uint32_t& doubleword); ++ Reader& operator>>(QByteArray& array); ++ Reader& operator>>(QVector& wordArray); ++ Reader& operator>>(QVector& arrayOfArrays); ++ /** ++ * Returns true if the stream is at its end and no errors occured ++ * ++ * \return Finished status ++ */ ++ bool isFinished() const; ++ private: ++ QByteArray m_data; ++ QDataStream m_stream; ++ }; ++ ++ /** ++ * Class for writing information to raw packets and setting the right byte order ++ * ++ * Workflow is as follows: ++ * * Construct empty writer ++ * * Using the stream operator insert all contained variables ++ * * Get a complete packet by the finalize(opcode) method ++ */ ++ class Writer { ++ public: ++ Writer(); ++ Writer& operator<<(const uint8_t byte); ++ Writer& operator<<(const uint16_t word); ++ Writer& operator<<(const uint32_t doubleword); ++ Writer& operator<<(const QByteArray& array); ++ Writer& operator<<(const QVector& wordArray); ++ Writer& operator<<(const QVector& arrayOfArrays); ++ /** ++ * Finalizes building of the packet ++ * ++ * \param opcode XDMCP protocol code of the packet type ++ * \return Raw packet data ++ */ ++ QByteArray finalize(uint16_t opcode); ++ private: ++ QByteArray m_data; ++ QDataStream m_stream; ++ }; ++ ++} // namespace XDMCP ++} // namespace SDDM ++ ++#endif // SDDM_XDMCP_UTILS_H diff --git a/sddm.conf b/sddm.conf index f1852dd..84914f1 100644 --- a/sddm.conf +++ b/sddm.conf @@ -23,3 +23,4 @@ HideShells= AutoRelogin=false MinimumVT=1 Numlock=on +XDMCPServer=false diff --git a/sddm.spec b/sddm.spec index da3b4e3..0e0a4f2 100644 --- a/sddm.spec +++ b/sddm.spec @@ -3,7 +3,7 @@ Name: sddm Version: 0.2.0 -Release: 0.14.20130914git%(echo %{sddm_commit} | cut -c-8)%{?dist} +Release: 0.15.20130914git%(echo %{sddm_commit} | cut -c-8)%{?dist} # code GPLv2+, fedora theme CC-BY-SA License: GPLv2+ and CC-BY-SA Summary: QML based X11 desktop manager @@ -28,6 +28,8 @@ Source23: fedora-theme.conf # Patch setting a better order of the xsessions and hiding the custom one Patch2: sddm-git.e707e229-session-list.patch +Patch3: sddm-0.2.0-0.11.20130914git50ca5b20-xdmcp.patch + Provides: service(graphical-login) = sddm BuildRequires: cmake @@ -68,6 +70,7 @@ A collection of sddm themes, including: circles, elarun, maldives, maui. %setup -q -n %{name}-%{sddm_commit} %patch2 -p1 -b .session-list +%patch3 -p1 -b .xdmcp # get rid of the architecture flag sed -i "s/-march=native//" CMakeLists.txt @@ -137,6 +140,10 @@ install -Dpm 644 %{SOURCE23} %{buildroot}%{_datadir}/apps/sddm/themes/fedora/the %{_datadir}/apps/sddm/themes/maui/ %changelog +* Tue Oct 15 2013 Martin Briza - 0.2.0-0.15.20130914git50ca5b20 +- Added XDMCP support patch +- Modified the config to reflect the added XDMCP support (disabled by default) + * Tue Oct 15 2013 Rex Dieter - 0.2.0-0.14.20130914git50ca5b20 - sddm.conf: CurrentTheme=fedora