[sddm] Added XDMCP support
Martin Briza
mbriza at fedoraproject.org
Tue Oct 15 15:49:22 UTC 2013
commit 288ff57820dcfaa66134f5609167ec2e08cca784
Author: Martin Briza <mbriza at redhat.com>
Date: Tue Oct 15 17:49:06 2013 +0200
Added XDMCP support
sddm-0.2.0-0.11.20130914git50ca5b20-xdmcp.patch | 1660 +++++++++++++++++++++++
sddm.conf | 1 +
sddm.spec | 9 +-
3 files changed, 1669 insertions(+), 1 deletions(-)
---
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 <mbriza at redhat.com>
++ *
++ * 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 <mbriza at redhat.com>
++ *
++ * 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 <QtCore/QByteArray>
++#include <QtNetwork/QHostAddress>
++
++#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<QByteArray> 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<QByteArray> m_authenticationNames;
++ };
++
++ class Packet::IndirectQuery : public Packet {
++ public:
++ IndirectQuery(const QHostAddress& host, quint16 port, Reader& r);
++ virtual QByteArray encode() const;
++ private:
++ QVector<QByteArray> 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<QByteArray> 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<uint16_t> m_connectionTypes;
++ QVector<QByteArray> m_connectionAddresses;
++ QByteArray m_authenticationName;
++ QByteArray m_authenticationData;
++ QVector<QByteArray> 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 <mbriza at redhat.com>
++ *
++ * 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 <QtNetwork/QHostInfo>
++
++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<Display*>(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 <mbriza at redhat.com>
++ *
++ * 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 <QtCore/QObject>
++#include <QtCore/QMap>
++#include <QtCore/QTimer>
++#include <QtNetwork/QUdpSocket>
++
++#include <X11/Xdmcp.h>
++
++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<int, Display*> m_displays;
++ QMap<int, QTimer*> 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 <mbriza at redhat.com>
++ *
++ * 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 <QtCore/QVector>
++
++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 <mbriza at redhat.com>
++ *
++ * 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 <QtCore/QByteArray>
++#include <QtCore/QDataStream>
++
++#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<uint16_t>& wordArray);
++ Reader& operator>>(QVector<QByteArray>& 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<uint16_t>& wordArray);
++ Writer& operator<<(const QVector<QByteArray>& 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 <mbriza at redhat.com> - 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 <rdieter at fedoraproject.org> - 0.2.0-0.14.20130914git50ca5b20
- sddm.conf: CurrentTheme=fedora
More information about the scm-commits
mailing list