You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sddm/sddm-0.2.0-0.11.20130914git...

1662 lines
54 KiB

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@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@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@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..2898381
--- /dev/null
+++ b/src/daemon/xdmcp/Server.h
@@ -0,0 +1,118 @@
+/*
+ * Server implementation for X Display Control Protocol
+ * Copyright (C) 2013 Martin Bříza <mbriza@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>
+
+// the same as in <X11/Xdmcp.h>
+#define XDM_UDP_PORT 177
+
+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@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@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