summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore2
-rw-r--r--src/seat-xvnc.c2
-rw-r--r--src/xserver-xvnc.c30
-rw-r--r--src/xserver-xvnc.h4
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/scripts/vnc-login.conf61
-rw-r--r--tests/src/Makefile.am26
-rw-r--r--tests/src/X.c960
-rw-r--r--tests/src/Xvnc.c286
-rw-r--r--tests/src/status.c9
-rw-r--r--tests/src/test-runner.c23
-rw-r--r--tests/src/vnc-client.c98
-rw-r--r--tests/src/x-authority.c229
-rw-r--r--tests/src/x-authority.h71
-rw-r--r--tests/src/x-common.c160
-rw-r--r--tests/src/x-common.h48
-rw-r--r--tests/src/x-server.c783
-rw-r--r--tests/src/x-server.h190
-rw-r--r--tests/src/xdmcp-client.c456
-rw-r--r--tests/src/xdmcp-client.h81
-rwxr-xr-xtests/test-vnc-login2
21 files changed, 2683 insertions, 840 deletions
diff --git a/.bzrignore b/.bzrignore
index b17ab126..8e0228f6 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -65,6 +65,8 @@ tests/src/test-qt-greeter
tests/src/test-runner
tests/src/test-script-hook
tests/src/test-session
+tests/src/vnc-client
tests/src/X
+tests/src/Xvnc
utils/lightdm-set-defaults
utils/dm-tool
diff --git a/src/seat-xvnc.c b/src/seat-xvnc.c
index d1e16965..5b95aa82 100644
--- a/src/seat-xvnc.c
+++ b/src/seat-xvnc.c
@@ -37,7 +37,7 @@ seat_xvnc_create_display_server (Seat *seat)
XServerXVNC *xserver;
xserver = xserver_xvnc_new ();
- xserver_xvnc_set_stdin (xserver, g_socket_get_fd (SEAT_XVNC (seat)->priv->connection));
+ xserver_xvnc_set_socket (xserver, g_socket_get_fd (SEAT_XVNC (seat)->priv->connection));
return DISPLAY_SERVER (xserver);
}
diff --git a/src/xserver-xvnc.c b/src/xserver-xvnc.c
index 9712f344..6e867b3f 100644
--- a/src/xserver-xvnc.c
+++ b/src/xserver-xvnc.c
@@ -33,7 +33,7 @@ struct XServerXVNCPrivate
GFile *authority_file;
/* File descriptor to use for standard input */
- gint stdin_fd;
+ gint socket_fd;
/* Geometry and colour depth */
gint width, height, depth;
@@ -60,17 +60,17 @@ xserver_xvnc_new (void)
}
void
-xserver_xvnc_set_stdin (XServerXVNC *server, int fd)
+xserver_xvnc_set_socket (XServerXVNC *server, int fd)
{
g_return_if_fail (server != NULL);
- server->priv->stdin_fd = fd;
+ server->priv->socket_fd = fd;
}
int
-xserver_xvnc_get_stdin (XServerXVNC *server)
+xserver_xvnc_get_socket (XServerXVNC *server)
{
g_return_val_if_fail (server != NULL, 0);
- return server->priv->stdin_fd;
+ return server->priv->socket_fd;
}
void
@@ -123,7 +123,9 @@ static void
run_cb (Process *process, XServerXVNC *server)
{
/* Connect input */
- dup2 (server->priv->stdin_fd, STDIN_FILENO);
+ dup2 (server->priv->socket_fd, STDIN_FILENO);
+ dup2 (server->priv->socket_fd, STDOUT_FILENO);
+ close (server->priv->socket_fd);
/* Redirect output to logfile */
if (server->priv->log_file)
@@ -135,7 +137,6 @@ run_cb (Process *process, XServerXVNC *server)
g_warning ("Failed to open log file %s: %s", server->priv->log_file, g_strerror (errno));
else
{
- dup2 (fd, STDOUT_FILENO);
dup2 (fd, STDERR_FILENO);
close (fd);
}
@@ -250,19 +251,28 @@ xserver_xvnc_start (DisplayServer *display_server)
g_free (absolute_command);
g_string_append_printf (command, " :%d", xserver_get_display_number (XSERVER (server)));
- g_string_append_printf (command, " -auth %s ", path);
+ g_string_append_printf (command, " -auth %s", path);
g_free (path);
g_string_append (command, " -inetd -nolisten tcp");
if (server->priv->width > 0 && server->priv->height > 0)
- g_string_append_printf (command, " -geometry %dx%d ", server->priv->width, server->priv->height);
+ g_string_append_printf (command, " -geometry %dx%d", server->priv->width, server->priv->height);
if (server->priv->depth > 0)
- g_string_append_printf (command, " -depth %d ", server->priv->depth);
+ g_string_append_printf (command, " -depth %d", server->priv->depth);
process_set_command (server->priv->xserver_process, command->str);
g_string_free (command, TRUE);
g_debug ("Launching Xvnc server");
+ /* Variable required for regression tests */
+ if (g_getenv ("LIGHTDM_TEST_STATUS_SOCKET"))
+ {
+ process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_STATUS_SOCKET", g_getenv ("LIGHTDM_TEST_STATUS_SOCKET"));
+ process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_CONFIG", g_getenv ("LIGHTDM_TEST_CONFIG"));
+ process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_HOME_DIR", g_getenv ("LIGHTDM_TEST_HOME_DIR"));
+ process_set_env (server->priv->xserver_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
+ }
+
result = process_start (server->priv->xserver_process);
if (result)
diff --git a/src/xserver-xvnc.h b/src/xserver-xvnc.h
index 12cc5a59..b7893630 100644
--- a/src/xserver-xvnc.h
+++ b/src/xserver-xvnc.h
@@ -41,9 +41,9 @@ gboolean xserver_xvnc_check_available (void);
XServerXVNC *xserver_xvnc_new (void);
-void xserver_xvnc_set_stdin (XServerXVNC *server, int fd);
+void xserver_xvnc_set_socket (XServerXVNC *server, int fd);
-int xserver_xvnc_get_stdin (XServerXVNC *server);
+int xserver_xvnc_get_socket (XServerXVNC *server);
void xserver_xvnc_set_geometry (XServerXVNC *server, gint width, gint height);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 88514363..a20a3bfb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -46,6 +46,7 @@ TESTS = \
test-switch-to-user \
test-switch-to-user-existing-session \
test-switch-to-user-no-password \
+ test-vnc-login \
test-xdmcp-login
# test-session-exit-error
@@ -123,5 +124,6 @@ EXTRA_DIST = \
scripts/switch-to-user.conf \
scripts/switch-to-user-existing-session.conf \
scripts/switch-to-user-no-password.conf \
+ scripts/vnc-login.conf \
scripts/xdmcp-login.conf \
scripts/xserver-fail-start.conf
diff --git a/tests/scripts/vnc-login.conf b/tests/scripts/vnc-login.conf
new file mode 100644
index 00000000..b401ce48
--- /dev/null
+++ b/tests/scripts/vnc-login.conf
@@ -0,0 +1,61 @@
+#
+# Check that LightDM correctly negotiates VNC and starts the session to the remote server.
+#
+
+[LightDM]
+minimum-display-number=50
+start-default-seat=false
+
+[VNCServer]
+enabled=true
+port=9999
+
+[test-greeter-config]
+username=alice
+password=password
+
+#?RUNNER DAEMON-START
+#?*WAIT 1
+
+# Start a VNC client
+#?*START-VNC-CLIENT ARGS="::9999"
+#?VNC-CLIENT START
+#?VNC-CLIENT CONNECT SERVER=::9999
+
+# Xvnc server starts
+#?XSERVER :50 START GEOMETRY=1024x768 DEPTH=8
+
+# Negotiate with Xvnc
+#?VNC-CLIENT CONNECTED VERSION="RFB 003.007"
+
+#?XSERVER :50 INDICATE-READY
+
+#?XSERVER :50 VNC-CLIENT-CONNECT VERSION="RFB 003.003"
+
+# LightDM connects to X server
+#?XSERVER :50 ACCEPT-CONNECT
+
+# Greeter starts and connects to remote X server
+#?GREETER START
+#?XSERVER :50 ACCEPT-CONNECT
+#?GREETER CONNECT-XSERVER :50
+#?GREETER CONNECT-TO-DAEMON
+#?GREETER CONNECTED-TO-DAEMON
+
+# Log in
+#?GREETER AUTHENTICATE USERNAME=alice
+#?GREETER SHOW-PROMPT TEXT="Password:"
+#?GREETER RESPOND TEXT="password"
+#?GREETER AUTHENTICATION-COMPLETE USERNAME=alice AUTHENTICATED=TRUE
+#?GREETER TERMINATE SIGNAL=15
+
+# Session starts
+#?SESSION START USER=alice
+#?XSERVER :50 ACCEPT-CONNECT
+#?SESSION CONNECT-XSERVER
+
+# Clean up
+#?*STOP-DAEMON
+#?(SESSION TERMINATE SIGNAL=15|XSERVER :50 TERMINATE SIGNAL=15)
+#?(SESSION TERMINATE SIGNAL=15|XSERVER :50 TERMINATE SIGNAL=15)
+#?RUNNER DAEMON-EXIT STATUS=0
diff --git a/tests/src/Makefile.am b/tests/src/Makefile.am
index d6a93d75..be8b8ecb 100644
--- a/tests/src/Makefile.am
+++ b/tests/src/Makefile.am
@@ -1,4 +1,4 @@
-noinst_PROGRAMS = test-runner X test-gobject-greeter test-session test-script-hook guest-account initctl plymouth
+noinst_PROGRAMS = test-runner X Xvnc test-gobject-greeter test-session test-script-hook guest-account initctl plymouth vnc-client
noinst_SCRIPTS = lightdm-session
lib_LTLIBRARIES = libsystem.la
@@ -20,7 +20,7 @@ test_runner_LDADD = \
$(GLIB_LIBS) \
$(GIO_LIBS)
-X_SOURCES = X.c status.c status.h
+X_SOURCES = X.c x-authority.c x-authority.h x-common.c x-common.h x-server.c x-server.h xdmcp-client.c xdmcp-client.h status.c status.h
X_CFLAGS = \
$(WARN_CFLAGS) \
$(GOBJECT_CFLAGS) \
@@ -33,6 +33,19 @@ X_LDADD = \
$(GIO_LIBS) \
$(GIO_UNIX_LIBS)
+Xvnc_SOURCES = Xvnc.c x-authority.c x-authority.h x-common.c x-common.h x-server.c x-server.h status.c status.h
+Xvnc_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(GOBJECT_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GIO_CFLAGS) \
+ $(GIO_UNIX_CFLAGS)
+Xvnc_LDADD = \
+ $(GOBJECT_LIBS) \
+ $(GLIB_LIBS) \
+ $(GIO_LIBS) \
+ $(GIO_UNIX_LIBS)
+
test_gobject_greeter_SOURCES = test-gobject-greeter.c status.c status.h
test_gobject_greeter_CFLAGS = \
-I$(top_srcdir)/liblightdm-gobject \
@@ -97,6 +110,15 @@ plymouth_CFLAGS = \
plymouth_LDADD = \
$(GLIB_LIBS)
+vnc_client_SOURCES = vnc-client.c status.c status.h
+vnc_client_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GIO_CFLAGS)
+vnc_client_LDADD = \
+ $(GLIB_LIBS) \
+ $(GIO_LIBS)
+
CLEANFILES = \
test-qt-greeter_moc.cpp
diff --git a/tests/src/X.c b/tests/src/X.c
index bfb041fa..014b683a 100644
--- a/tests/src/X.c
+++ b/tests/src/X.c
@@ -1,113 +1,48 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <errno.h>
-#include <signal.h>
+#include <sys/types.h>
#include <unistd.h>
+#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <glib.h>
-#include <gio/gio.h>
-#include <gio/gunixsocketaddress.h>
+#include <errno.h>
#include "status.h"
+#include "x-server.h"
+#include "x-authority.h"
+#include "xdmcp-client.h"
static GKeyFile *config;
-static gchar *socket_path = NULL;
+/* Path to lock file */
static gchar *lock_path = NULL;
-static gchar *auth_path = NULL;
-typedef struct
-{
- GIOChannel *parent_channel;
- GIOChannel *channel;
- guint8 byte_order;
- gboolean connected;
-} Connection;
-static GHashTable *connections;
+/* Path to authority database to use */
+static gchar *auth_path = NULL;
+/* Display number being served */
static int display_number = 0;
-static gboolean listen_unix = TRUE;
-static gboolean listen_tcp = TRUE;
-static GSocket *unix_socket = NULL;
-static GIOChannel *unix_channel = NULL;
-static GSocket *tcp_socket = NULL;
-static GIOChannel *tcp_channel = NULL;
-
-static int xdmcp_port = 177;
-static gboolean do_xdmcp = FALSE;
-static gchar *xdmcp_host = NULL;
-static GSocket *xdmcp_socket = NULL;
-static guint xdmcp_query_timer = 0;
-static gchar *xdmcp_authorization_name = NULL;
-static gint xdmcp_authorization_data_length = 0;
-static guint8 *xdmcp_authorization_data = NULL;
+/* X server */
+static XServer *xserver = NULL;
-#define BYTE_ORDER_MSB 'B'
-#define BYTE_ORDER_LSB 'l'
+/* XDMCP client */
+static XDMCPClient *xdmcp_client = NULL;
-#define PROTOCOL_MAJOR_VERSION 11
-#define PROTOCOL_MINOR_VERSION 0
-
-#define RELEASE_NUMBER 0
-#define RESOURCE_ID_BASE 0x04e00000
-#define RESOURCE_ID_MASK 0x001fffff
-#define MOTION_BUFFER_SIZE 256
-#define MAXIMUM_REQUEST_LENGTH 65535
-#define BITMAP_FORMAT_SCANLINE_UNIT 32
-#define BITMAP_FORMAT_SCANLINE_PAD 32
-#define MIN_KEYCODE 8
-#define MAX_KEYCODE 255
-#define VENDOR "LightDM"
-
-#define XAUTH_FAMILY_INTERNET 0
-#define XAUTH_FAMILY_DECNET 1
-#define XAUTH_FAMILY_CHAOS 2
-#define XAUTH_FAMILY_SERVER_INTERPRETED 5
-#define XAUTH_FAMILY_INTERNET6 6
-#define XAUTH_FAMILY_LOCALHOST 252
-#define XAUTH_FAMILY_KRB5_PRINCIPAL 253
-#define XAUTH_FAMILY_NETNAME 254
-#define XAUTH_FAMILY_LOCAL 256
-#define XAUTH_FAMILY_WILD 65535
-
-enum
-{
- Failed = 0,
- Success = 1,
- Authenticate = 2
-};
-
-typedef enum
-{
- XDMCP_BroadcastQuery = 1,
- XDMCP_Query = 2,
- XDMCP_IndirectQuery = 3,
- XDMCP_ForwardQuery = 4,
- XDMCP_Willing = 5,
- XDMCP_Unwilling = 6,
- XDMCP_Request = 7,
- XDMCP_Accept = 8,
- XDMCP_Decline = 9,
- XDMCP_Manage = 10,
- XDMCP_Refuse = 11,
- XDMCP_Failed = 12,
- XDMCP_KeepAlive = 13,
- XDMCP_Alive = 14
-} XDMCPOpcode;
+/* Authorization provided by XDMCP server */
+static guint16 xdmcp_cookie_length = 0;
+static guint8 *xdmcp_cookie = NULL;
static void
cleanup ()
{
if (lock_path)
unlink (lock_path);
- if (socket_path)
- unlink (socket_path);
+ if (xserver)
+ g_object_unref (xserver);
+ if (xdmcp_client)
+ g_object_unref (xdmcp_client);
}
static void
@@ -117,427 +52,169 @@ quit (int status)
exit (status);
}
-static gsize
-pad (gsize length)
-{
- if (length % 4 == 0)
- return 0;
- return 4 - length % 4;
-}
-
static void
-read_padding (gsize length, gsize *offset)
-{
- *offset += length;
-}
-
-static guint8
-read_card8 (const guint8 *buffer, gsize buffer_length, gsize *offset)
-{
- if (*offset >= buffer_length)
- return 0;
- (*offset)++;
- return buffer[*offset - 1];
-}
-
-static guint16
-read_card16 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset)
-{
- guint8 a, b;
-
- a = read_card8 (buffer, buffer_length, offset);
- b = read_card8 (buffer, buffer_length, offset);
- if (byte_order == BYTE_ORDER_MSB)
- return a << 8 | b;
- else
- return b << 8 | a;
-}
-
-static guint32
-read_card32 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset)
-{
- guint8 a, b, c, d;
-
- a = read_card8 (buffer, buffer_length, offset);
- b = read_card8 (buffer, buffer_length, offset);
- c = read_card8 (buffer, buffer_length, offset);
- d = read_card8 (buffer, buffer_length, offset);
- if (byte_order == BYTE_ORDER_MSB)
- return a << 24 | b << 16 | c << 8 | d;
- else
- return d << 24 | c << 16 | b << 8 | a;
-}
-
-static guint8 *
-read_string8 (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset)
-{
- guint8 *string;
- int i;
-
- string = g_malloc (string_length + 1);
- for (i = 0; i < string_length; i++)
- string[i] = read_card8 (buffer, buffer_length, offset);
- string[i] = '\0';
- return string;
-}
-
-static gchar *
-read_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset)
-{
- return (gchar *) read_string8 (buffer, buffer_length, string_length, offset);
-}
-
-static gchar *
-read_padded_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset)
-{
- guint8 *value;
- value = read_string8 (buffer, buffer_length, string_length, offset);
- read_padding (pad (string_length), offset);
- return (gchar *) value;
-}
-
-static void
-write_card8 (guint8 *buffer, gsize buffer_length, guint8 value, gsize *offset)
-{
- if (*offset >= buffer_length)
- return;
- buffer[*offset] = value;
- (*offset)++;
-}
-
-static void
-write_padding (guint8 *buffer, gsize buffer_length, gsize length, gsize *offset)
-{
- gsize i;
- for (i = 0; i < length; i++)
- write_card8 (buffer, buffer_length, 0, offset);
-}
-
-static void
-write_card16 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint16 value, gsize *offset)
+indicate_ready ()
{
- if (byte_order == BYTE_ORDER_MSB)
- {
- write_card8 (buffer, buffer_length, value >> 8, offset);
- write_card8 (buffer, buffer_length, value & 0xFF, offset);
- }
- else
+ void *handler;
+ handler = signal (SIGUSR1, SIG_IGN);
+ if (handler == SIG_IGN)
{
- write_card8 (buffer, buffer_length, value & 0xFF, offset);
- write_card8 (buffer, buffer_length, value >> 8, offset);
+ notify_status ("XSERVER :%d INDICATE-READY", display_number);
+ kill (getppid (), SIGUSR1);
}
+ signal (SIGUSR1, handler);
}
static void
-write_card32 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint32 value, gsize *offset)
+signal_cb (int signum)
{
- if (byte_order == BYTE_ORDER_MSB)
+ if (signum == SIGHUP)
{
- write_card8 (buffer, buffer_length, value >> 24, offset);
- write_card8 (buffer, buffer_length, (value >> 16) & 0xFF, offset);
- write_card8 (buffer, buffer_length, (value >> 8) & 0xFF, offset);
- write_card8 (buffer, buffer_length, value & 0xFF, offset);
+ notify_status ("XSERVER :%d DISCONNECT-CLIENTS", display_number);
+ indicate_ready ();
}
else
{
- write_card8 (buffer, buffer_length, value & 0xFF, offset);
- write_card8 (buffer, buffer_length, (value >> 8) & 0xFF, offset);
- write_card8 (buffer, buffer_length, (value >> 16) & 0xFF, offset);
- write_card8 (buffer, buffer_length, value >> 24, offset);
+ notify_status ("XSERVER :%d TERMINATE SIGNAL=%d", display_number, signum);
+ quit (EXIT_SUCCESS);
}
}
static void
-write_string8 (guint8 *buffer, gsize buffer_length, const guint8 *value, gsize value_length, gsize *offset)
-{
- gsize i;
- for (i = 0; i < value_length; i++)
- write_card8 (buffer, buffer_length, value[i], offset);
-}
-
-static gsize
-padded_string_length (const gchar *value)
-{
- return (strlen (value) + pad (strlen (value))) / 4;
-}
-
-static void
-write_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset)
+xdmcp_query_cb (XDMCPClient *client)
{
- write_string8 (buffer, buffer_length, (guint8 *) value, strlen (value), offset);
-}
+ static gboolean notified_query = FALSE;
-static void
-write_padded_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset)
-{
- write_string8 (buffer, buffer_length, (guint8 *) value, strlen (value), offset);
- write_padding (buffer, buffer_length, pad (strlen (value)), offset);
+ if (!notified_query)
+ {
+ notify_status ("XSERVER :%d SEND-QUERY", display_number);
+ notified_query = TRUE;
+ }
}
static void
-decode_connect (const guint8 *buffer, gsize buffer_length,
- guint8 *byte_order,
- guint16 *protocol_major_version, guint16 *protocol_minor_version,
- gchar **authorization_protocol_name,
- guint8 **authorization_protocol_data, guint16 *authorization_protocol_data_length)
-{
- gsize offset = 0;
- guint16 n;
-
- *byte_order = read_card8 (buffer, buffer_length, &offset);
- read_padding (1, &offset);
- *protocol_major_version = read_card16 (buffer, buffer_length, *byte_order, &offset);
- *protocol_minor_version = read_card16 (buffer, buffer_length, *byte_order, &offset);
- n = read_card16 (buffer, buffer_length, *byte_order, &offset);
- *authorization_protocol_data_length = read_card16 (buffer, buffer_length, *byte_order, &offset);
- read_padding (2, &offset);
- *authorization_protocol_name = read_padded_string (buffer, buffer_length, n, &offset);
- *authorization_protocol_data = read_string8 (buffer, buffer_length, *authorization_protocol_data_length, &offset);
- read_padding (pad (*authorization_protocol_data_length), &offset);
-}
-
-static gsize
-encode_failed (guint8 *buffer, gsize buffer_length,
- guint8 byte_order, const gchar *reason)
+xdmcp_willing_cb (XDMCPClient *client, XDMCPWilling *message)
{
- gsize offset = 0;
- guint8 additional_data_length;
+ gchar **authorization_names;
+ GInetAddress *addresses[2];
- write_card8 (buffer, buffer_length, Failed, &offset);
- write_card8 (buffer, buffer_length, strlen (reason), &offset);
- write_card16 (buffer, buffer_length, byte_order, PROTOCOL_MAJOR_VERSION, &offset);
- write_card16 (buffer, buffer_length, byte_order, PROTOCOL_MINOR_VERSION, &offset);
- additional_data_length = padded_string_length (reason);
- write_card16 (buffer, buffer_length, byte_order, additional_data_length, &offset);
+ notify_status ("XSERVER :%d GOT-WILLING AUTHENTICATION-NAME=\"%s\" HOSTNAME=\"%s\" STATUS=\"%s\"", display_number, message->authentication_name, message->hostname, message->status);
- /* Additional data */
- write_padded_string (buffer, buffer_length, reason, &offset);
+ notify_status ("XSERVER :%d SEND-REQUEST DISPLAY-NUMBER=%d AUTHORIZATION-NAME=\"%s\" MFID=\"%s\"", display_number, display_number, "MIT-MAGIC-COOKIE-1", "TEST XSERVER");
- return offset;
+ authorization_names = g_strsplit ("MIT-MAGIC-COOKIE-1", " ", -1);
+ addresses[0] = xdmcp_client_get_local_address (client);
+ addresses[1] = NULL;
+ xdmcp_client_send_request (client, display_number,
+ addresses,
+ "", NULL, 0,
+ authorization_names, "TEST XSERVER");
+ g_strfreev (authorization_names);
}
-static gsize
-encode_accept (guint8 *buffer, gsize buffer_length,
- guint8 byte_order)
+static void
+xdmcp_accept_cb (XDMCPClient *client, XDMCPAccept *message)
{
- gsize offset = 0;
- guint8 additional_data_length;
+ notify_status ("XSERVER :%d GOT-ACCEPT SESSION-ID=%d AUTHENTICATION-NAME=\"%s\" AUTHORIZATION-NAME=\"%s\"", display_number, message->session_id, message->authentication_name, message->authorization_name);
- write_card8 (buffer, buffer_length, Success, &offset);
- write_padding (buffer, buffer_length, 1, &offset);
- write_card16 (buffer, buffer_length, byte_order, PROTOCOL_MAJOR_VERSION, &offset);
- write_card16 (buffer, buffer_length, byte_order, PROTOCOL_MINOR_VERSION, &offset);
- additional_data_length = 8 + padded_string_length (VENDOR);
- write_card16 (buffer, buffer_length, byte_order, additional_data_length, &offset);
+ /* Ignore if haven't picked a valid authorization */
+ if (strcmp (message->authorization_name, "MIT-MAGIC-COOKIE-1") != 0)
+ return;
- /* Additional data */
- write_card32 (buffer, buffer_length, byte_order, RELEASE_NUMBER, &offset);
- write_card32 (buffer, buffer_length, byte_order, RESOURCE_ID_BASE, &offset);
- write_card32 (buffer, buffer_length, byte_order, RESOURCE_ID_MASK, &offset);
- write_card32 (buffer, buffer_length, byte_order, MOTION_BUFFER_SIZE, &offset);
- write_card16 (buffer, buffer_length, byte_order, strlen (VENDOR), &offset);
- write_card16 (buffer, buffer_length, byte_order, MAXIMUM_REQUEST_LENGTH, &offset);
- write_card8 (buffer, buffer_length, 0, &offset); // number of screens
- write_card8 (buffer, buffer_length, 0, &offset); // number of pixmap formats
- write_card8 (buffer, buffer_length, 0, &offset); // image-byte-order
- write_card8 (buffer, buffer_length, 0, &offset); // bitmap-format-bit-order
- write_card8 (buffer, buffer_length, BITMAP_FORMAT_SCANLINE_UNIT, &offset);
- write_card8 (buffer, buffer_length, BITMAP_FORMAT_SCANLINE_PAD, &offset);
- write_card8 (buffer, buffer_length, MIN_KEYCODE, &offset);
- write_card8 (buffer, buffer_length, MAX_KEYCODE, &offset);
- write_padding (buffer, buffer_length, 4, &offset);
- write_padded_string (buffer, buffer_length, VENDOR, &offset);
- // pixmap formats
- // screens
+ g_free (xdmcp_cookie);
+ xdmcp_cookie_length = message->authorization_data_length;
+ xdmcp_cookie = g_malloc (message->authorization_data_length);
+ memcpy (xdmcp_cookie, message->authorization_data, message->authorization_data_length);
- return offset;
+ notify_status ("XSERVER :%d SEND-MANAGE SESSION-ID=%d DISPLAY-NUMBER=%d DISPLAY-CLASS=\"%s\"", display_number, message->session_id, display_number, "DISPLAY CLASS");
+ xdmcp_client_send_manage (client, message->session_id, display_number, "DISPLAY CLASS");
}
-static gchar *
-make_hex_string (const guint8 *buffer, gsize buffer_length)
+static void
+xdmcp_decline_cb (XDMCPClient *client, XDMCPDecline *message)
{
- GString *text;
- gsize i;
- gchar *result;
-
- text = g_string_new ("");
- for (i = 0; i < buffer_length; i++)
- {
- if (i > 0)
- g_string_append (text, " ");
- g_string_append_printf (text, "%02X", buffer[i]);
- }
-
- result = text->str;
- g_string_free (text, FALSE);
- return result;
+ notify_status ("XSERVER :%d GOT-DECLINE STATUS=\"%s\" AUTHENTICATION-NAME=\"%s\"", display_number, message->status, message->authentication_name);
}
static void
-log_buffer (const gchar *text, const guint8 *buffer, gsize buffer_length)
+xdmcp_failed_cb (XDMCPClient *client, XDMCPFailed *message)
{
- gchar *hex;
-
- hex = make_hex_string (buffer, buffer_length);
- printf ("%s %s\n", text, hex);
- g_free (hex);
+ notify_status ("XSERVER :%d GOT-FAILED SESSION-ID=%d STATUS=\"%s\"", display_number, message->session_id, message->status);
}
static void
-decode_connection_request (Connection *connection, const guint8 *buffer, gssize buffer_length)
+x_client_connect_cb (XClient *client, XConnect *message)
{
- guint8 byte_order;
- guint16 protocol_major_version, protocol_minor_version;
- gchar *authorization_protocol_name;
- guint8 *authorization_protocol_data;
- guint16 authorization_protocol_data_length;
- gchar *hex;
gchar *auth_error = NULL;
- guint8 response_buffer[MAXIMUM_REQUEST_LENGTH];
- gsize n_written;
- decode_connect (buffer, buffer_length,
- &byte_order,
- &protocol_major_version, &protocol_minor_version,
- &authorization_protocol_name,
- &authorization_protocol_data, &authorization_protocol_data_length);
- hex = make_hex_string (authorization_protocol_data, authorization_protocol_data_length);
- g_debug ("Got connect request using protocol %d.%d and authorization '%s' with data '%s'", protocol_major_version, protocol_minor_version, authorization_protocol_name, hex);
- g_free (hex);
-
- if (connection->parent_channel == tcp_channel)
+ if (x_client_get_address (client))
notify_status ("XSERVER :%d TCP-ACCEPT-CONNECT", display_number);
else
notify_status ("XSERVER :%d ACCEPT-CONNECT", display_number);
- if (do_xdmcp)
+ if (xdmcp_client)
{
- if (strcmp (xdmcp_authorization_name, "") == 0)
- {
- if (strcmp (authorization_protocol_name, "") != 0)
- auth_error = g_strdup ("Authorization provided but none needed");
- }
- else if (strcmp (xdmcp_authorization_name, "MIT-MAGIC-COOKIE-1") == 0)
+ if (!xdmcp_cookie)
+ auth_error = g_strdup ("Need to authenticate with XDMCP");
+ else
{
gboolean matches = TRUE;
- if (authorization_protocol_data_length == xdmcp_authorization_data_length)
+ if (message->authorization_protocol_data_length == xdmcp_cookie_length)
{
guint16 i;
- for (i = 0; i < xdmcp_authorization_data_length && authorization_protocol_data[i] == xdmcp_authorization_data[i]; i++);
- matches = i == xdmcp_authorization_data_length;
+ for (i = 0; i < xdmcp_cookie_length && message->authorization_protocol_data[i] == xdmcp_cookie[i]; i++);
+ matches = i == xdmcp_cookie_length;
}
else
matches = FALSE;
- if (strcmp (authorization_protocol_name, "MIT-MAGIC-COOKIE-1") != 0)
+ if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") != 0)
auth_error = g_strdup ("Authorization required");
else if (!matches)
auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key");
}
- else
- auth_error = g_strdup_printf ("Unknown authorization: '%s'", authorization_protocol_name);
}
else if (auth_path)
{
- gchar *xauth_data;
- gsize xauth_length;
+ XAuthority *authority;
+ XAuthorityRecord *record = NULL;
GError *error = NULL;
- if (g_file_get_contents (auth_path, &xauth_data, &xauth_length, &error))
- {
- gsize offset = 0;
- guint16 family, length, data_length;
- gchar *address, *number, *name;
- guint8 *data;
-
- family = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset);
- length = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset);
- address = (gchar *) read_string8 ((guint8 *) xauth_data, xauth_length, length, &offset);
- length = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset);
- number = (gchar *) read_string8 ((guint8 *) xauth_data, xauth_length, length, &offset);
- length = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset);
- name = (gchar *) read_string8 ((guint8 *) xauth_data, xauth_length, length, &offset);
- data_length = read_card16 ((guint8 *) xauth_data, xauth_length, BYTE_ORDER_MSB, &offset);
- data = read_string8 ((guint8 *) xauth_data, xauth_length, data_length, &offset);
+ authority = x_authority_new ();
+ x_authority_load (authority, auth_path, &error);
+ if (error)
+ g_warning ("Error reading auth file: %s", error->message);
+ g_clear_error (&error);
- if (connection->parent_channel == unix_channel && !(family == XAUTH_FAMILY_LOCAL || family == XAUTH_FAMILY_WILD))
- auth_error = g_strdup ("Authorization not valid for Unix connection");
- else if (connection->parent_channel == tcp_channel && !(family == XAUTH_FAMILY_INTERNET || family == XAUTH_FAMILY_WILD))
- auth_error = g_strdup ("Authorization not valid for TCP/IP connection");
- else if (strcmp (authorization_protocol_name, "") == 0)
- auth_error = g_strdup ("Authorization required");
- else if (strcmp (authorization_protocol_name, "MIT-MAGIC-COOKIE-1") == 0)
+ if (x_client_get_address (client))
+ record = x_authority_match_localhost (authority, message->authorization_protocol_name); // FIXME: Should check if remote
+ else
+ record = x_authority_match_local (authority, message->authorization_protocol_name);
+ if (record)
+ {
+ if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") == 0)
{
- gboolean matches = TRUE;
- if (authorization_protocol_data_length == data_length)
- {
- guint16 i;
- for (i = 0; i < data_length && authorization_protocol_data[i] == data[i]; i++);
- matches = i == data_length;
- }
- else
- matches = FALSE;
- if (!matches)
- {
- gchar *hex1, *hex2;
- hex1 = make_hex_string (authorization_protocol_data, authorization_protocol_data_length);
- hex2 = make_hex_string (data, data_length);
- g_debug ("MIT-MAGIC-COOKIE mismatch, got '%s', expect '%s'", hex1, hex2);
- g_free (hex1);
- g_free (hex2);
+ if (!x_authority_record_check_cookie (record, message->authorization_protocol_data, message->authorization_protocol_data_length))
auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key");
- }
}
else
- auth_error = g_strdup_printf ("Unknown authorization: '%s'", authorization_protocol_name);
-
- g_free (address);
- g_free (number);
- g_free (name);
- g_free (data);
+ auth_error = g_strdup_printf ("Unknown authorization: '%s'", message->authorization_protocol_name);
}
else
- {
- g_warning ("Error reading auth file: %s", error->message);
- auth_error = g_strdup ("No authorization database");
- }
- g_clear_error (&error);
+ auth_error = g_strdup ("No authorization record");
}
if (auth_error)
- {
- n_written = encode_failed (response_buffer, MAXIMUM_REQUEST_LENGTH, byte_order, auth_error);
- g_debug ("Sending Failed: %s", auth_error);
- g_free (auth_error);
- }
+ x_client_send_failed (client, auth_error);
else
- {
- g_debug ("Sending Success");
- connection->connected = TRUE;
- connection->byte_order = byte_order;
- n_written = encode_accept (response_buffer, MAXIMUM_REQUEST_LENGTH, byte_order);
- }
-
- send (g_io_channel_unix_get_fd (connection->channel), response_buffer, n_written, 0);
- log_buffer ("Wrote X", response_buffer, n_written);
+ x_client_send_success (client);
+ g_free (auth_error);
}
static void
-decode_intern_atom (Connection *connection, const guint8 *buffer, gssize buffer_length, gsize *offset)
+x_client_intern_atom_cb (XClient *client, XInternAtom *message)
{
- gboolean only_if_exists;
- guint16 name_length;
- gchar *name;
-
- only_if_exists = read_card8 (buffer, buffer_length, offset) != 0;
- read_padding (2, offset);
- name_length = read_card16 (buffer, buffer_length, connection->byte_order, offset);
- read_padding (2, offset);
- name = read_padded_string (buffer, buffer_length, name_length, offset);
-
- g_debug ("InternAtom only-if-exits=%s name=%s", only_if_exists ? "True" : "False", name);
-
- if (strcmp (name, "SIGSEGV") == 0)
+ if (strcmp (message->name, "SIGSEGV") == 0)
{
notify_status ("XSERVER :%d CRASH", display_number);
cleanup ();
@@ -546,347 +223,18 @@ decode_intern_atom (Connection *connection, const guint8 *buffer, gssize buffer_
}
static void
-decode_request (Connection *connection, const guint8 *buffer, gssize buffer_length)
+client_connected_cb (XServer *server, XClient *client)
{
- int opcode;
- gsize offset = 0;
-
- opcode = read_card8 (buffer, buffer_length, &offset);
- switch (opcode)
- {
- case 16:
- decode_intern_atom (connection, buffer, buffer_length, &offset);
- break;
- default:
- g_debug ("Ignoring unknown opcode %d", opcode);
- break;
- }
+ g_signal_connect (client, "connect", G_CALLBACK (x_client_connect_cb), NULL);
+ g_signal_connect (client, "intern-atom", G_CALLBACK (x_client_intern_atom_cb), NULL);
}
static void
-indicate_ready ()
-{
- void *handler;
- handler = signal (SIGUSR1, SIG_IGN);
- if (handler == SIG_IGN)
- {
- notify_status ("XSERVER :%d INDICATE-READY", display_number);
- kill (getppid (), SIGUSR1);
- }
- signal (SIGUSR1, handler);
-}
-
-static gboolean
-socket_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
-{
- Connection *connection;
- guint8 buffer[MAXIMUM_REQUEST_LENGTH];
- gssize n_read;
-
- connection = g_hash_table_lookup (connections, channel);
-
- n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0);
- if (n_read < 0)
- g_warning ("Error reading from socket: %s", strerror (errno));
- else if (n_read == 0)
- {
- if (connection->connected)
- {
- g_debug ("Client disconnected");
- g_hash_table_remove (connections, connection->channel);
- if (g_hash_table_size (connections) == 0)
- indicate_ready ();
- }
- else
- g_debug ("EOF before connection made");
- return FALSE;
- }
- else
- {
- log_buffer ("Read X", buffer, n_read);
-
- if (connection->connected)
- decode_request (connection, buffer, n_read);
- else
- decode_connection_request (connection, buffer, n_read);
- }
-
- return TRUE;
-}
-
-static gboolean
-socket_connect_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
-{
- int s, data_socket;
-
- g_debug ("Got connection");
-
- s = g_io_channel_unix_get_fd (channel);
- data_socket = accept (s, NULL, NULL);
- if (data_socket < 0)
- g_warning ("Error accepting connection: %s", strerror (errno));
- else if (do_xdmcp && !xdmcp_authorization_name)
- {
- /* Decline if haven't connected yet */
- close (data_socket);
- }
- else
- {
- Connection *connection;
-
- connection = g_malloc0 (sizeof (Connection));
- connection->parent_channel = g_io_channel_ref (channel);
- connection->channel = g_io_channel_unix_new (data_socket);
- g_hash_table_insert (connections, connection->channel, connection);
- g_io_add_watch (connection->channel, G_IO_IN, socket_data_cb, NULL);
- }
-
- return TRUE;
-}
-
-static void
-signal_cb (int signum)
-{
- if (signum == SIGHUP)
- {
- notify_status ("XSERVER :%d DISCONNECT-CLIENTS", display_number);
+client_disconnected_cb (XServer *server, XClient *client)
+{
+ g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
+ if (x_server_get_n_clients (server) == 0)
indicate_ready ();
- }
- else
- {
- notify_status ("XSERVER :%d TERMINATE SIGNAL=%d", display_number, signum);
- quit (EXIT_SUCCESS);
- }
-}
-
-static void
-xdmcp_write (const guint8 *buffer, gssize buffer_length)
-{
- gssize n_written;
- GError *error = NULL;
-
- n_written = g_socket_send (xdmcp_socket, (const gchar *) buffer, buffer_length, NULL, &error);
- if (n_written < 0)
- g_warning ("Failed to send XDMCP request: %s", error->message);
- else if (n_written != buffer_length)
- g_warning ("Partial write for XDMCP request, wrote %zi, expected %zi", n_written, buffer_length);
- g_clear_error (&error);
-
- log_buffer ("Wrote XDMCP", buffer, buffer_length);
-}
-
-static void
-decode_willing (const guint8 *buffer, gssize buffer_length)
-{
- gsize offset = 0;
- guint16 length;
- gchar *authentication_name, *hostname, *status;
- GSocketAddress *local_address;
- const guint8 *native_address;
- gssize native_address_length;
- guint8 response[MAXIMUM_REQUEST_LENGTH];
-
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- authentication_name = read_string (buffer, buffer_length, length, &offset);
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- hostname = read_string (buffer, buffer_length, length, &offset);
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- status = read_string (buffer, buffer_length, length, &offset);
-
- if (xdmcp_query_timer == 0)
- {
- g_debug ("Ignoring XDMCP unrequested/duplicate Willing");
- return;
- }
-
- notify_status ("XSERVER :%d GOT-WILLING AUTHENTICATION-NAME=\"%s\" HOSTNAME=\"%s\" STATUS=\"%s\"", display_number, authentication_name, hostname, status);
-
- /* Stop sending queries */
- g_source_remove (xdmcp_query_timer);
- xdmcp_query_timer = 0;
-
- local_address = g_socket_get_local_address (xdmcp_socket, NULL);
- GInetAddress *inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (local_address));
- native_address_length = g_inet_address_get_native_size (inet_address);
- native_address = g_inet_address_to_bytes (inet_address);
-
- offset = 0;
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 1, &offset); /* version = 1 */
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, XDMCP_Request, &offset);
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 17 + native_address_length + strlen ("") + strlen ("MIT-MAGIC-COOKIE-1") + strlen ("TEST XSERVER"), &offset);
-
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, display_number, &offset);
- write_card8 (response, MAXIMUM_REQUEST_LENGTH, 1, &offset); /* 1 address */
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 0, &offset); /* FamilyInternet */
- write_card8 (response, MAXIMUM_REQUEST_LENGTH, 1, &offset); /* 1 address */
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, native_address_length, &offset);
- write_string8 (response, MAXIMUM_REQUEST_LENGTH, native_address, native_address_length, &offset);
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, strlen (""), &offset);
- write_string (response, MAXIMUM_REQUEST_LENGTH, "", &offset);
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 0, &offset); /* No authentication data */
- write_card8 (response, MAXIMUM_REQUEST_LENGTH, 1, &offset); /* 1 authorization */
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, strlen ("MIT-MAGIC-COOKIE-1"), &offset);
- write_string (response, MAXIMUM_REQUEST_LENGTH, "MIT-MAGIC-COOKIE-1", &offset);
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, strlen ("TEST XSERVER"), &offset);
- write_string (response, MAXIMUM_REQUEST_LENGTH, "TEST XSERVER", &offset);
-
- notify_status ("XSERVER :%d SEND-REQUEST DISPLAY-NUMBER=%d AUTHORIZATION-NAME=\"%s\" MFID=\"%s\"", display_number, display_number, "MIT-MAGIC-COOKIE-1", "TEST XSERVER");
-
- xdmcp_write (response, offset);
-}
-
-static void
-decode_accept (const guint8 *buffer, gssize buffer_length)
-{
- gsize offset = 0;
- guint16 length;
- guint32 session_id;
- gchar *authentication_name;
- guint8 response[MAXIMUM_REQUEST_LENGTH];
-
- session_id = read_card32 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- authentication_name = read_string (buffer, buffer_length, length, &offset);
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- read_string8 (buffer, buffer_length, length, &offset);
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- xdmcp_authorization_name = read_string (buffer, buffer_length, length, &offset);
- xdmcp_authorization_data_length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- xdmcp_authorization_data = read_string8 (buffer, buffer_length, length, &offset);
-
- notify_status ("XSERVER :%d GOT-ACCEPT SESSION-ID=%d AUTHENTICATION-NAME=\"%s\" AUTHORIZATION-NAME=\"%s\"", display_number, session_id, authentication_name, xdmcp_authorization_name);
-
- offset = 0;
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 1, &offset); /* version = 1 */
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, XDMCP_Manage, &offset);
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 8 + strlen ("DISPLAY CLASS"), &offset);
-
- write_card32 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, session_id, &offset);
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, display_number, &offset);
- write_card16 (response, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, strlen ("DISPLAY CLASS"), &offset);
- write_string (response, MAXIMUM_REQUEST_LENGTH, "DISPLAY CLASS", &offset);
-
- notify_status ("XSERVER :%d SEND-MANAGE SESSION-ID=%d DISPLAY-NUMBER=%d DISPLAY-CLASS=\"%s\"", display_number, session_id, display_number, "DISPLAY CLASS");
-
- xdmcp_write (response, offset);
-}
-
-static void
-decode_decline (const guint8 *buffer, gssize buffer_length)
-{
- gsize offset = 0;
- guint16 length;
- gchar *authentication_name, *status;
-
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- status = read_string (buffer, buffer_length, length, &offset);
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- authentication_name = read_string (buffer, buffer_length, length, &offset);
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- read_string8 (buffer, buffer_length, length, &offset);
-
- notify_status ("XSERVER :%d GOT-DECLINE STATUS=\"%s\" AUTHENTICATION-NAME=\"%s\"", display_number, status, authentication_name);
-}
-
-static void
-decode_failed (const guint8 *buffer, gssize buffer_length)
-{
- gsize offset = 0;
- guint16 length;
- guint32 session_id;
- gchar *status;
-
- session_id = read_card32 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- length = read_card16 (buffer, buffer_length, BYTE_ORDER_MSB, &offset);
- status = read_string (buffer, buffer_length, length, &offset);
-
- notify_status ("XSERVER :%d GOT-FAILED SESSION-ID=%d STATUS=\"%s\"", display_number, session_id, status);
-}
-
-static gboolean
-xdmcp_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
-{
- guint8 buffer[MAXIMUM_REQUEST_LENGTH];
- gssize n_read;
-
- n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0);
- if (n_read < 0)
- g_warning ("Error reading from XDMCP socket: %s", strerror (errno));
- else if (n_read == 0)
- {
- g_debug ("EOF");
- return FALSE;
- }
- else
- {
- gsize offset = 0;
- guint16 version, opcode, length;
-
- log_buffer ("Read XDMCP", buffer, n_read);
-
- version = read_card16 (buffer, n_read, BYTE_ORDER_MSB, &offset);
- opcode = read_card16 (buffer, n_read, BYTE_ORDER_MSB, &offset);
- length = read_card16 (buffer, n_read, BYTE_ORDER_MSB, &offset);
-
- if (version != 1)
- {
- g_debug ("Ignoring XDMCP version %d message", version);
- return TRUE;
- }
- if (6 + length > n_read)
- {
- g_debug ("Ignoring XDMCP message of length %zi with invalid length field %d", n_read, length);
- return TRUE;
- }
- switch (opcode)
- {
- case XDMCP_Willing:
- decode_willing (buffer + offset, n_read - offset);
- break;
-
- case XDMCP_Accept:
- decode_accept (buffer + offset, n_read - offset);
- break;
-
- case XDMCP_Decline:
- decode_decline (buffer + offset, n_read - offset);
- break;
-
- case XDMCP_Failed:
- decode_failed (buffer + offset, n_read - offset);
- break;
-
- default:
- g_debug ("Ignoring unknown XDMCP opcode %d", opcode);
- break;
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-xdmcp_query_cb (gpointer data)
-{
- guint8 buffer[MAXIMUM_REQUEST_LENGTH];
- gsize offset = 0;
- static gboolean notified_query = FALSE;
-
- g_debug ("Send XDMCP Query");
- if (!notified_query)
- {
- notify_status ("XSERVER :%d SEND-QUERY", display_number);
- notified_query = TRUE;
- }
-
- write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 1, &offset); /* version = 1 */
- write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, XDMCP_Query, &offset);
- write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, BYTE_ORDER_MSB, 1, &offset);
- write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &offset);
-
- xdmcp_write (buffer, offset);
-
- return TRUE;
}
int
@@ -895,16 +243,20 @@ main (int argc, char **argv)
int i;
char *pid_string, *return_lock;
GMainLoop *loop;
+ gboolean listen_tcp = TRUE;
+ gboolean listen_unix = TRUE;
+ gboolean do_xdmcp = FALSE;
+ guint xdmcp_port = 0;
+ gchar *xdmcp_host = NULL;
int lock_file;
FILE *f;
- GError *error = NULL;
signal (SIGINT, signal_cb);
signal (SIGTERM, signal_cb);
signal (SIGHUP, signal_cb);
g_type_init ();
-
+
for (i = 1; i < argc; i++)
{
char *arg = argv[i];
@@ -916,7 +268,6 @@ main (int argc, char **argv)
else if (strcmp (arg, "-auth") == 0)
{
auth_path = argv[i+1];
- g_debug ("Loading authorization from %s", auth_path);
i++;
}
else if (strcmp (arg, "-nolisten") == 0)
@@ -964,7 +315,7 @@ main (int argc, char **argv)
g_printerr ("Unrecognized option: %s\n"
"Use: %s [:<display>] [option]\n"
"-auth file Select authorization file\n"
- "-nolisten string Don't listen on protocol\n"
+ "-nolisten protocol Don't listen on protocol\n"
"-background [none] Create root window with no background\n"
"-nr (Ubuntu-specific) Synonym for -background none\n"
"-query host-name Contact named host for XDMCP\n"
@@ -976,6 +327,12 @@ main (int argc, char **argv)
}
}
+ xserver = x_server_new (display_number);
+ g_signal_connect (xserver, "client-connected", G_CALLBACK (client_connected_cb), NULL);
+ g_signal_connect (xserver, "client-disconnected", G_CALLBACK (client_disconnected_cb), NULL);
+ x_server_set_listen_unix (xserver, listen_unix);
+ x_server_set_listen_tcp (xserver, listen_tcp);
+
notify_status ("XSERVER :%d START", display_number);
config = g_key_file_new ();
@@ -1021,73 +378,30 @@ main (int argc, char **argv)
}
g_free (pid_string);
- connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) g_io_channel_unref, g_free);
-
- if (listen_unix)
- {
- socket_path = g_strdup_printf ("/tmp/.X11-unix/X%d", display_number);
- g_debug ("Opening Unix socket %s", socket_path);
-
- unix_socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
- if (!unix_socket ||
- !g_socket_bind (unix_socket, g_unix_socket_address_new (socket_path), TRUE, &error) ||
- !g_socket_listen (unix_socket, &error))
- {
- g_warning ("Error creating Unix X socket: %s", error->message);
- quit (EXIT_FAILURE);
- }
- unix_channel = g_io_channel_unix_new (g_socket_get_fd (unix_socket));
- g_io_add_watch (unix_channel, G_IO_IN, socket_connect_cb, NULL);
- }
-
- if (listen_tcp)
- {
- gint port = 6000 + display_number;
-
- g_debug ("Opening TCP/IP socket on port %d", port);
-
- tcp_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error);
- if (!tcp_socket ||
- !g_socket_bind (tcp_socket, g_inet_socket_address_new (g_inet_address_new_any (G_SOCKET_FAMILY_IPV4), port), TRUE, &error) ||
- !g_socket_listen (tcp_socket, &error))
- {
- g_warning ("Error creating TCP/IP X socket: %s", error->message);
- quit (EXIT_FAILURE);
- }
- tcp_channel = g_io_channel_unix_new (g_socket_get_fd (tcp_socket));
- g_io_add_watch (tcp_channel, G_IO_IN, socket_connect_cb, NULL);
- }
+ if (!x_server_start (xserver))
+ quit (EXIT_FAILURE);
/* Enable XDMCP */
if (do_xdmcp)
{
- GSocketConnectable *address;
- GSocketAddress *socket_address;
-
- xdmcp_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error);
-
- address = g_network_address_new (xdmcp_host, xdmcp_port);
- socket_address = g_socket_address_enumerator_next (g_socket_connectable_enumerate (address), NULL, NULL);
-
- if (!xdmcp_socket ||
- !g_socket_connect (xdmcp_socket, socket_address, NULL, NULL) ||
- !g_io_add_watch (g_io_channel_unix_new (g_socket_get_fd (xdmcp_socket)), G_IO_IN, xdmcp_data_cb, &error))
- {
- g_warning ("Error creating XDMCP socket: %s", error->message);
+ xdmcp_client = xdmcp_client_new ();
+ if (xdmcp_host > 0)
+ xdmcp_client_set_hostname (xdmcp_client, xdmcp_host);
+ if (xdmcp_port > 0)
+ xdmcp_client_set_port (xdmcp_client, xdmcp_port);
+ g_signal_connect (xdmcp_client, "query", G_CALLBACK (xdmcp_query_cb), NULL);
+ g_signal_connect (xdmcp_client, "willing", G_CALLBACK (xdmcp_willing_cb), NULL);
+ g_signal_connect (xdmcp_client, "accept", G_CALLBACK (xdmcp_accept_cb), NULL);
+ g_signal_connect (xdmcp_client, "decline", G_CALLBACK (xdmcp_decline_cb), NULL);
+ g_signal_connect (xdmcp_client, "failed", G_CALLBACK (xdmcp_failed_cb), NULL);
+
+ if (!xdmcp_client_start (xdmcp_client))
quit (EXIT_FAILURE);
- }
}
/* Indicate ready if parent process has requested it */
indicate_ready ();
- /* Send first query */
- if (do_xdmcp)
- {
- xdmcp_query_timer = g_timeout_add (2000, xdmcp_query_cb, NULL);
- xdmcp_query_cb (NULL);
- }
-
g_main_loop_run (loop);
return EXIT_SUCCESS;
diff --git a/tests/src/Xvnc.c b/tests/src/Xvnc.c
new file mode 100644
index 00000000..2098b52a
--- /dev/null
+++ b/tests/src/Xvnc.c
@@ -0,0 +1,286 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <gio/gio.h>
+
+#include "status.h"
+#include "x-server.h"
+#include "x-authority.h"
+
+static GKeyFile *config;
+
+/* Path to lock file */
+static gchar *lock_path = NULL;
+
+/* Path to authority database to use */
+static gchar *auth_path = NULL;
+
+/* Display number being served */
+static int display_number = 0;
+
+/* X server */
+static XServer *xserver = NULL;
+
+static void
+indicate_ready ()
+{
+ void *handler;
+ handler = signal (SIGUSR1, SIG_IGN);
+ if (handler == SIG_IGN)
+ {
+ notify_status ("XSERVER :%d INDICATE-READY", display_number);
+ kill (getppid (), SIGUSR1);
+ }
+ signal (SIGUSR1, handler);
+}
+
+static void
+cleanup ()
+{
+ if (lock_path)
+ unlink (lock_path);
+ if (xserver)
+ g_object_unref (xserver);
+}
+
+static void
+quit (int status)
+{
+ cleanup ();
+ exit (status);
+}
+
+static void
+signal_cb (int signum)
+{
+ if (signum == SIGHUP)
+ {
+ notify_status ("XSERVER :%d DISCONNECT-CLIENTS", display_number);
+ indicate_ready ();
+ }
+ else
+ {
+ notify_status ("XSERVER :%d TERMINATE SIGNAL=%d", display_number, signum);
+ quit (EXIT_SUCCESS);
+ }
+}
+
+static void
+x_client_connect_cb (XClient *client, XConnect *message)
+{
+ gchar *auth_error = NULL;
+
+ if (x_client_get_address (client))
+ notify_status ("XSERVER :%d TCP-ACCEPT-CONNECT", display_number);
+ else
+ notify_status ("XSERVER :%d ACCEPT-CONNECT", display_number);
+
+ if (auth_path)
+ {
+ XAuthority *authority;
+ XAuthorityRecord *record = NULL;
+ GError *error = NULL;
+
+ authority = x_authority_new ();
+ x_authority_load (authority, auth_path, &error);
+ if (error)
+ g_warning ("Error reading auth file: %s", error->message);
+ g_clear_error (&error);
+
+ if (x_client_get_address (client))
+ record = x_authority_match_localhost (authority, message->authorization_protocol_name); // FIXME: Should check if remote
+ else
+ record = x_authority_match_local (authority, message->authorization_protocol_name);
+ if (record)
+ {
+ if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") == 0)
+ {
+ if (!x_authority_record_check_cookie (record, message->authorization_protocol_data, message->authorization_protocol_data_length))
+ auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key");
+ }
+ else
+ auth_error = g_strdup_printf ("Unknown authorization: '%s'", message->authorization_protocol_name);
+ }
+ else
+ auth_error = g_strdup ("No authorization record");
+ }
+
+ if (auth_error)
+ x_client_send_failed (client, auth_error);
+ else
+ x_client_send_success (client);
+ g_free (auth_error);
+}
+
+static void
+client_connected_cb (XServer *server, XClient *client)
+{
+ g_signal_connect (client, "connect", G_CALLBACK (x_client_connect_cb), NULL);
+}
+
+static void
+client_disconnected_cb (XServer *server, XClient *client)
+{
+ g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
+ if (x_server_get_n_clients (server) == 0)
+ indicate_ready ();
+}
+
+static gboolean
+vnc_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+ gchar buffer[1024];
+ gsize n_read;
+ GIOStatus status;
+ GError *error = NULL;
+
+ status = g_io_channel_read_chars (channel, buffer, 1023, &n_read, &error);
+ if (error)
+ g_warning ("Error reading from VNC client: %s", error->message);
+ g_clear_error (&error);
+
+ if (status == G_IO_STATUS_NORMAL)
+ {
+ buffer[n_read] = '\0';
+ if (g_str_has_suffix (buffer, "\n"))
+ buffer[n_read-1] = '\0';
+ notify_status ("XSERVER :%d VNC-CLIENT-CONNECT VERSION=\"%s\"", display_number, buffer);
+ }
+
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ char *pid_string;
+ gboolean listen_tcp = TRUE;
+ gboolean listen_unix = TRUE;
+ gboolean use_inetd = FALSE;
+ gchar *geometry = g_strdup ("640x480");
+ gint depth = 8;
+ int lock_file;
+ int i;
+
+ signal (SIGINT, signal_cb);
+ signal (SIGTERM, signal_cb);
+ signal (SIGHUP, signal_cb);
+
+ g_type_init ();
+
+ for (i = 1; i < argc; i++)
+ {
+ char *arg = argv[i];
+
+ if (arg[0] == ':')
+ {
+ display_number = atoi (arg + 1);
+ }
+ else if (strcmp (arg, "-auth") == 0)
+ {
+ auth_path = argv[i+1];
+ i++;
+ }
+ else if (strcmp (arg, "-nolisten") == 0)
+ {
+ char *protocol = argv[i+1];
+ i++;
+ if (strcmp (protocol, "tcp") == 0)
+ listen_tcp = FALSE;
+ else if (strcmp (protocol, "unix") == 0)
+ listen_unix = FALSE;
+ }
+ else if (strcmp (arg, "-geometry") == 0)
+ {
+ g_free (geometry);
+ geometry = g_strdup (argv[i+1]);
+ i++;
+ }
+ else if (strcmp (arg, "-depth") == 0)
+ {
+ depth = atoi (argv[i+1]);
+ i++;
+ }
+ else if (strcmp (arg, "-inetd") == 0)
+ {
+ use_inetd = TRUE;
+ }
+ else
+ {
+ g_printerr ("Unrecognized option: %s\n"
+ "Use: %s [:<display>] [option]\n"
+ "-auth file Select authorization file\n"
+ "-nolisten protocol Don't listen on protocol\n"
+ "-geometry WxH Set framebuffer width & height\n"
+ "-depth D Set framebuffer depth\n"
+ "-inetd Xvnc is launched by inetd\n",
+ arg, argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ xserver = x_server_new (display_number);
+ g_signal_connect (xserver, "client-connected", G_CALLBACK (client_connected_cb), NULL);
+ g_signal_connect (xserver, "client-disconnected", G_CALLBACK (client_disconnected_cb), NULL);
+ x_server_set_listen_unix (xserver, listen_unix);
+ x_server_set_listen_tcp (xserver, listen_tcp);
+
+ notify_status ("XSERVER :%d START GEOMETRY=%s DEPTH=%d", display_number, geometry, depth);
+
+ config = g_key_file_new ();
+ if (g_getenv ("LIGHTDM_TEST_CONFIG"))
+ g_key_file_load_from_file (config, g_getenv ("LIGHTDM_TEST_CONFIG"), G_KEY_FILE_NONE, NULL);
+
+ if (use_inetd)
+ {
+ /* Send server protocol version to client */
+ g_print ("RFB 003.007\n");
+
+ if (!g_io_add_watch (g_io_channel_unix_new (STDIN_FILENO), G_IO_IN, vnc_data_cb, NULL))
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ g_printerr ("Only supported in -inetd mode\n");
+ return EXIT_FAILURE;
+ }
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ lock_path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
+ lock_file = open (lock_path, O_CREAT | O_EXCL | O_WRONLY, 0444);
+ if (lock_file < 0)
+ {
+ fprintf (stderr,
+ "Fatal server error:\n"
+ "Server is already active for display %d\n"
+ " If this server is no longer running, remove %s\n"
+ " and start again.\n", display_number, lock_path);
+ g_free (lock_path);
+ lock_path = NULL;
+ quit (EXIT_FAILURE);
+ }
+ pid_string = g_strdup_printf ("%10ld", (long) getpid ());
+ if (write (lock_file, pid_string, strlen (pid_string)) < 0)
+ {
+ g_warning ("Error writing PID file: %s", strerror (errno));
+ quit (EXIT_FAILURE);
+ }
+ g_free (pid_string);
+
+ if (!x_server_start (xserver))
+ quit (EXIT_FAILURE);
+
+ /* Indicate ready if parent process has requested it */
+ indicate_ready ();
+
+ g_main_loop_run (loop);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/src/status.c b/tests/src/status.c
index 0beee6e6..2822a605 100644
--- a/tests/src/status.c
+++ b/tests/src/status.c
@@ -36,7 +36,14 @@ notify_status (const char *format, ...)
socket_name = getenv ("LIGHTDM_TEST_STATUS_SOCKET");
if (!socket_name)
{
- fprintf (stderr, "LIGHTDM_TEST_STATUS_SOCKET not defined\n");
+ static int warned = 0;
+
+ if (!warned)
+ {
+ fprintf (stderr, "LIGHTDM_TEST_STATUS_SOCKET not defined\n");
+ warned = 1;
+ }
+
fprintf (stderr, "%s", status);
fprintf (stderr, "\n");
return;
diff --git a/tests/src/test-runner.c b/tests/src/test-runner.c
index c6325f43..a917dc66 100644
--- a/tests/src/test-runner.c
+++ b/tests/src/test-runner.c
@@ -277,6 +277,7 @@ run_commands ()
expect_exit = TRUE;
stop_daemon ();
}
+ // FIXME: Make generic RUN-COMMAND
else if (strcmp (name, "START-XSERVER") == 0)
{
gchar *xserver_args, *command_line;
@@ -289,7 +290,6 @@ run_commands ()
xserver_args = "";
command_line = g_strdup_printf ("%s/tests/src/X %s", BUILDDIR, xserver_args);
- g_debug ("Run %s", command_line);
if (!g_shell_parse_argv (command_line, NULL, &argv, &error) ||
!g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, &pid, &error))
{
@@ -299,6 +299,27 @@ run_commands ()
}
children = g_list_append (children, GINT_TO_POINTER (pid));
}
+ else if (strcmp (name, "START-VNC-CLIENT") == 0)
+ {
+ gchar *vnc_client_args, *command_line;
+ gchar **argv;
+ GPid pid;
+ GError *error = NULL;
+
+ vnc_client_args = g_hash_table_lookup (params, "ARGS");
+ if (!vnc_client_args)
+ vnc_client_args = "";
+ command_line = g_strdup_printf ("%s/tests/src/vnc-client %s", BUILDDIR, vnc_client_args);
+
+ if (!g_shell_parse_argv (command_line, NULL, &argv, &error) ||
+ !g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, &pid, &error))
+ {
+ g_printerr ("Error starting VNC client: %s", error->message);
+ quit (EXIT_FAILURE);
+ return;
+ }
+ children = g_list_append (children, GINT_TO_POINTER (pid));
+ }
else
{
g_printerr ("Unknown command '%s'\n", name);
diff --git a/tests/src/vnc-client.c b/tests/src/vnc-client.c
new file mode 100644
index 00000000..fc1ec74c
--- /dev/null
+++ b/tests/src/vnc-client.c
@@ -0,0 +1,98 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "status.h"
+
+static GKeyFile *config;
+
+int
+main (int argc, char **argv)
+{
+ gchar *server_address;
+ gchar *hostname, *c;
+ gint port;
+ GError *error = NULL;
+ GSocket *socket;
+ GSocketConnectable *address;
+ GSocketAddress *socket_address;
+ gboolean result;
+ gchar buffer[1024];
+ gssize n_read, n_sent;
+
+ g_type_init ();
+
+ notify_status ("VNC-CLIENT START");
+
+ config = g_key_file_new ();
+ if (g_getenv ("LIGHTDM_TEST_CONFIG"))
+ g_key_file_load_from_file (config, g_getenv ("LIGHTDM_TEST_CONFIG"), G_KEY_FILE_NONE, NULL);
+
+ if (argc > 1)
+ server_address = g_strdup (argv[1]);
+ else
+ server_address = g_strdup (":0");
+
+ notify_status ("VNC-CLIENT CONNECT SERVER=%s", server_address);
+
+ socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error);
+ if (error)
+ g_warning ("Unable to make VNC socket: %s", error->message);
+ g_clear_error (&error);
+ if (!socket)
+ return EXIT_FAILURE;
+
+ hostname = g_strdup (server_address);
+ c = strchr (hostname, ':');
+ if (c)
+ {
+ *c = '\0';
+ gchar *port_string = c + 1;
+ if (g_str_has_prefix (port_string, ":"))
+ port = atoi (port_string + 1);
+ else
+ port = 5900 + atoi (port_string);
+ }
+ else
+ port = 5900;
+ if (strcmp (hostname, "") == 0)
+ {
+ g_free (hostname);
+ hostname = g_strdup ("localhost");
+ }
+
+ address = g_network_address_new (hostname, port);
+ socket_address = g_socket_address_enumerator_next (g_socket_connectable_enumerate (address), NULL, NULL);
+
+ result = g_socket_connect (socket, socket_address, NULL, &error);
+ if (error)
+ g_warning ("Unable to connect VNC socket: %s", error->message);
+ g_clear_error (&error);
+ if (!result)
+ return EXIT_FAILURE;
+
+ n_read = g_socket_receive (socket, buffer, 1023, NULL, &error);
+ if (error)
+ g_warning ("Unable to receive on VNC socket: %s", error->message);
+ g_clear_error (&error);
+ if (n_read < 0)
+ return EXIT_FAILURE;
+
+ buffer[n_read] = '\0';
+ if (g_str_has_suffix (buffer, "\n"))
+ buffer[n_read-1] = '\0';
+ notify_status ("VNC-CLIENT CONNECTED VERSION=\"%s\"", buffer);
+
+ snprintf (buffer, 1024, "RFB 003.003\n");
+ n_sent = g_socket_send (socket, buffer, strlen (buffer), NULL, &error);
+ if (error)
+ g_warning ("Unable to send on VNC socket: %s", error->message);
+ g_clear_error (&error);
+ if (n_sent != strlen (buffer))
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/src/x-authority.c b/tests/src/x-authority.c
new file mode 100644
index 00000000..5f692fe0
--- /dev/null
+++ b/tests/src/x-authority.c
@@ -0,0 +1,229 @@
+#include <string.h>
+
+#include "x-authority.h"
+#include "x-common.h"
+
+struct XAuthorityPrivate
+{
+ GList *records;
+};
+
+struct XAuthorityRecordPrivate
+{
+ guint16 family;
+ guint16 address_length;
+ guint8 *address;
+ gchar *number;
+ gchar *authorization_name;
+ guint16 authorization_data_length;
+ guint8 *authorization_data;
+};
+
+G_DEFINE_TYPE (XAuthority, x_authority, G_TYPE_OBJECT);
+G_DEFINE_TYPE (XAuthorityRecord, x_authority_record, G_TYPE_OBJECT);
+
+XAuthority *
+x_authority_new (void)
+{
+ return g_object_new (x_authority_get_type (), NULL);
+}
+
+gboolean
+x_authority_load (XAuthority *authority, const gchar *filename, GError **error)
+{
+ guint8 *xauth_data;
+ gsize xauth_length;
+ gsize offset = 0;
+
+ if (!g_file_get_contents (filename, (gchar **) &xauth_data, &xauth_length, error))
+ return FALSE;
+
+ while (offset < xauth_length)
+ {
+ XAuthorityRecord *record;
+ guint16 length;
+
+ record = g_object_new (x_authority_record_get_type (), NULL);
+ record->priv->family = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset);
+ record->priv->address_length = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset);
+ record->priv->address = read_string8 (xauth_data, xauth_length, record->priv->address_length, &offset);
+ length = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset);
+ record->priv->number = (gchar *) read_string8 (xauth_data, xauth_length, length, &offset);
+ length = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset);
+ record->priv->authorization_name = (gchar *) read_string8 (xauth_data, xauth_length, length, &offset);
+ record->priv->authorization_data_length = read_card16 (xauth_data, xauth_length, X_BYTE_ORDER_MSB, &offset);
+ record->priv->authorization_data = read_string8 (xauth_data, xauth_length, record->priv->authorization_data_length, &offset);
+
+ authority->priv->records = g_list_append (authority->priv->records, record);
+ }
+
+ return TRUE;
+}
+
+XAuthorityRecord *
+x_authority_match_local (XAuthority *authority, const gchar *authorization_name)
+{
+ GList *link;
+
+ for (link = authority->priv->records; link; link = link->next)
+ {
+ XAuthorityRecord *record = link->data;
+
+ if (strcmp (record->priv->authorization_name, authorization_name) != 0)
+ continue;
+
+ if (record->priv->family == XAUTH_FAMILY_WILD || record->priv->family == XAUTH_FAMILY_LOCAL)
+ return record;
+ }
+
+ return NULL;
+}
+
+XAuthorityRecord *
+x_authority_match_localhost (XAuthority *authority, const gchar *authorization_name)
+{
+ GList *link;
+
+ for (link = authority->priv->records; link; link = link->next)
+ {
+ XAuthorityRecord *record = link->data;
+
+ if (strcmp (record->priv->authorization_name, authorization_name) != 0)
+ continue;
+
+ if (record->priv->family == XAUTH_FAMILY_WILD || record->priv->family == XAUTH_FAMILY_LOCALHOST)
+ return record;
+ }
+
+ return NULL;
+}
+
+XAuthorityRecord *
+x_authority_match_inet (XAuthority *authority, GInetAddress *address, const gchar *authorization_name)
+{
+ GList *link;
+ guint16 family;
+ gssize address_data_length;
+ const guint8 *address_data;
+
+ switch (g_inet_address_get_family (address))
+ {
+ case G_SOCKET_FAMILY_IPV4:
+ family = XAUTH_FAMILY_INTERNET;
+ break;
+ case G_SOCKET_FAMILY_IPV6:
+ family = XAUTH_FAMILY_INTERNET6;
+ break;
+ default:
+ return NULL;
+ }
+
+ address_data_length = g_inet_address_get_native_size (address);
+ address_data = g_inet_address_to_bytes (address);
+ for (link = authority->priv->records; link; link = link->next)
+ {
+ XAuthorityRecord *record = link->data;
+ int i;
+ gboolean matches = TRUE;
+
+ if (strcmp (record->priv->authorization_name, authorization_name) != 0)
+ continue;
+
+ if (record->priv->family == XAUTH_FAMILY_WILD)
+ return record;
+
+ if (record->priv->family != family)
+ continue;
+
+ if (record->priv->address_length != address_data_length)
+ continue;
+
+ for (i = 0; i < address_data_length; i++)
+ {
+ if (address_data[i] != record->priv->address[i])
+ {
+ matches = FALSE;
+ break;
+ }
+ }
+ if (matches)
+ return record;
+ }
+
+ return NULL;
+}
+
+static void
+x_authority_init (XAuthority *authority)
+{
+ authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority, x_authority_get_type (), XAuthorityPrivate);
+}
+
+static void
+x_authority_finalize (GObject *object)
+{
+ XAuthority *authority = (XAuthority *) object;
+ g_list_free_full (authority->priv->records, g_object_unref);
+}
+
+static void
+x_authority_class_init (XAuthorityClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = x_authority_finalize;
+ g_type_class_add_private (klass, sizeof (XAuthorityPrivate));
+}
+
+guint16
+x_authority_record_get_authorization_data_length (XAuthorityRecord *record)
+{
+ return record->priv->authorization_data_length;
+}
+
+const guint8 *
+x_authority_record_get_authorization_data (XAuthorityRecord *record)
+{
+ return record->priv->authorization_data;
+}
+
+gboolean
+x_authority_record_check_cookie (XAuthorityRecord *record, const guint8 *cookie_data, guint16 cookie_data_length)
+{
+ guint16 i;
+
+ if (strcmp (record->priv->authorization_name, "MIT-MAGIC-COOKIE-1") != 0)
+ return FALSE;
+
+ if (cookie_data_length != record->priv->authorization_data_length)
+ return FALSE;
+
+ for (i = 0; i < cookie_data_length; i++)
+ if (cookie_data[i] != record->priv->authorization_data[i])
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+x_authority_record_init (XAuthorityRecord *record)
+{
+ record->priv = G_TYPE_INSTANCE_GET_PRIVATE (record, x_authority_record_get_type (), XAuthorityRecordPrivate);
+}
+
+static void
+x_authority_record_finalize (GObject *object)
+{
+ XAuthorityRecord *record = (XAuthorityRecord *) object;
+ g_free (record->priv->address);
+ g_free (record->priv->number);
+ g_free (record->priv->authorization_name);
+ g_free (record->priv->authorization_data);
+}
+
+static void
+x_authority_record_class_init (XAuthorityRecordClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = x_authority_record_finalize;
+ g_type_class_add_private (klass, sizeof (XAuthorityRecordPrivate));
+}
diff --git a/tests/src/x-authority.h b/tests/src/x-authority.h
new file mode 100644
index 00000000..56497fb3
--- /dev/null
+++ b/tests/src/x-authority.h
@@ -0,0 +1,71 @@
+#ifndef _X_AUTHORITY_H_
+#define _X_AUTHORITY_H_
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+enum
+{
+ XAUTH_FAMILY_INTERNET = 0,
+ XAUTH_FAMILY_DECNET = 1,
+ XAUTH_FAMILY_CHAOS = 2,
+ XAUTH_FAMILY_SERVER_INTERPRETED = 5,
+ XAUTH_FAMILY_INTERNET6 = 6,
+ XAUTH_FAMILY_LOCALHOST = 252,
+ XAUTH_FAMILY_KRB5_PRINCIPAL = 253,
+ XAUTH_FAMILY_NETNAME = 254,
+ XAUTH_FAMILY_LOCAL = 256,
+ XAUTH_FAMILY_WILD = 65535
+};
+
+typedef struct XAuthorityPrivate XAuthorityPrivate;
+
+typedef struct
+{
+ GObjectClass parent_instance;
+ XAuthorityPrivate *priv;
+} XAuthority;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} XAuthorityClass;
+
+typedef struct XAuthorityRecordPrivate XAuthorityRecordPrivate;
+
+typedef struct
+{
+ GObjectClass parent_instance;
+ XAuthorityRecordPrivate *priv;
+} XAuthorityRecord;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} XAuthorityRecordClass;
+
+GType x_authority_get_type (void);
+
+GType x_authority_record_get_type (void);
+
+XAuthority *x_authority_new (void);
+
+gboolean x_authority_load (XAuthority *authority, const gchar *filename, GError **error);
+
+XAuthorityRecord *x_authority_match_local (XAuthority *authority, const gchar *authorization_name);
+
+XAuthorityRecord *x_authority_match_localhost (XAuthority *authority, const gchar *authorization_name);
+
+XAuthorityRecord *x_authority_match_inet (XAuthority *authority, GInetAddress *address, const gchar *authorization_name);
+
+guint16 x_authority_record_get_authorization_data_length (XAuthorityRecord *record);
+
+const guint8 *x_authority_record_get_authorization_data (XAuthorityRecord *record);
+
+gboolean x_authority_record_check_cookie (XAuthorityRecord *record, const guint8 *cookie_data, guint16 cookie_data_length);
+
+G_END_DECLS
+
+#endif /* _X_AUTHORITY_H_ */
diff --git a/tests/src/x-common.c b/tests/src/x-common.c
new file mode 100644
index 00000000..82249cd5
--- /dev/null
+++ b/tests/src/x-common.c
@@ -0,0 +1,160 @@
+#include <string.h>
+
+#include "x-common.h"
+
+gsize
+pad (gsize length)
+{
+ if (length % 4 == 0)
+ return 0;
+ return 4 - length % 4;
+}
+
+void
+read_padding (gsize length, gsize *offset)
+{
+ *offset += length;
+}
+
+guint8
+read_card8 (const guint8 *buffer, gsize buffer_length, gsize *offset)
+{
+ if (*offset >= buffer_length)
+ return 0;
+ (*offset)++;
+ return buffer[*offset - 1];
+}
+
+guint16
+read_card16 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset)
+{
+ guint8 a, b;
+
+ a = read_card8 (buffer, buffer_length, offset);
+ b = read_card8 (buffer, buffer_length, offset);
+ if (byte_order == X_BYTE_ORDER_MSB)
+ return a << 8 | b;
+ else
+ return b << 8 | a;
+}
+
+guint32
+read_card32 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset)
+{
+ guint8 a, b, c, d;
+
+ a = read_card8 (buffer, buffer_length, offset);
+ b = read_card8 (buffer, buffer_length, offset);
+ c = read_card8 (buffer, buffer_length, offset);
+ d = read_card8 (buffer, buffer_length, offset);
+ if (byte_order == X_BYTE_ORDER_MSB)
+ return a << 24 | b << 16 | c << 8 | d;
+ else
+ return d << 24 | c << 16 | b << 8 | a;
+}
+
+guint8 *
+read_string8 (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset)
+{
+ guint8 *string;
+ int i;
+
+ string = g_malloc (string_length + 1);
+ for (i = 0; i < string_length; i++)
+ string[i] = read_card8 (buffer, buffer_length, offset);
+ string[i] = '\0';
+ return string;
+}
+
+gchar *
+read_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset)
+{
+ return (gchar *) read_string8 (buffer, buffer_length, string_length, offset);
+}
+
+gchar *
+read_padded_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset)
+{
+ guint8 *value;
+ value = read_string8 (buffer, buffer_length, string_length, offset);
+ read_padding (pad (string_length), offset);
+ return (gchar *) value;
+}
+
+void
+write_card8 (guint8 *buffer, gsize buffer_length, guint8 value, gsize *offset)
+{
+ if (*offset >= buffer_length)
+ return;
+ buffer[*offset] = value;
+ (*offset)++;
+}
+
+void
+write_padding (guint8 *buffer, gsize buffer_length, gsize length, gsize *offset)
+{
+ gsize i;
+ for (i = 0; i < length; i++)
+ write_card8 (buffer, buffer_length, 0, offset);
+}
+
+void
+write_card16 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint16 value, gsize *offset)
+{
+ if (byte_order == X_BYTE_ORDER_MSB)
+ {
+ write_card8 (buffer, buffer_length, value >> 8, offset);
+ write_card8 (buffer, buffer_length, value & 0xFF, offset);
+ }
+ else
+ {
+ write_card8 (buffer, buffer_length, value & 0xFF, offset);
+ write_card8 (buffer, buffer_length, value >> 8, offset);
+ }
+}
+
+void
+write_card32 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint32 value, gsize *offset)
+{
+ if (byte_order == X_BYTE_ORDER_MSB)
+ {
+ write_card8 (buffer, buffer_length, value >> 24, offset);
+ write_card8 (buffer, buffer_length, (value >> 16) & 0xFF, offset);
+ write_card8 (buffer, buffer_length, (value >> 8) & 0xFF, offset);
+ write_card8 (buffer, buffer_length, value & 0xFF, offset);
+ }
+ else
+ {
+ write_card8 (buffer, buffer_length, value & 0xFF, offset);
+ write_card8 (buffer, buffer_length, (value >> 8) & 0xFF, offset);
+ write_card8 (buffer, buffer_length, (value >> 16) & 0xFF, offset);
+ write_card8 (buffer, buffer_length, value >> 24, offset);
+ }
+}
+
+void
+write_string8 (guint8 *buffer, gsize buffer_length, const guint8 *value, gsize value_length, gsize *offset)
+{
+ gsize i;
+ for (i = 0; i < value_length; i++)
+ write_card8 (buffer, buffer_length, value[i], offset);
+}
+
+gsize
+padded_string_length (const gchar *value)
+{
+ return (strlen (value) + pad (strlen (value))) / 4;
+}
+
+void
+write_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset)
+{
+ write_string8 (buffer, buffer_length, (guint8 *) value, strlen (value), offset);
+}
+
+void
+write_padded_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset)
+{
+ write_string8 (buffer, buffer_length, (guint8 *) value, strlen (value), offset);
+ write_padding (buffer, buffer_length, pad (strlen (value)), offset);
+}
diff --git a/tests/src/x-common.h b/tests/src/x-common.h
new file mode 100644
index 00000000..2904b254
--- /dev/null
+++ b/tests/src/x-common.h
@@ -0,0 +1,48 @@
+#ifndef _X_COMMON_H_
+#define _X_COMMON_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+enum
+{
+ X_BYTE_ORDER_MSB,
+ X_BYTE_ORDER_LSB
+};
+
+gsize pad (gsize length);
+
+void read_padding (gsize length, gsize *offset);
+
+guint8 read_card8 (const guint8 *buffer, gsize buffer_length, gsize *offset);
+
+guint16 read_card16 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset);
+
+guint32 read_card32 (const guint8 *buffer, gsize buffer_length, guint8 byte_order, gsize *offset);
+
+guint8 *read_string8 (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset);
+
+gchar *read_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset);
+
+gchar *read_padded_string (const guint8 *buffer, gsize buffer_length, gsize string_length, gsize *offset);
+
+void write_card8 (guint8 *buffer, gsize buffer_length, guint8 value, gsize *offset);
+
+void write_padding (guint8 *buffer, gsize buffer_length, gsize length, gsize *offset);
+
+void write_card16 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint16 value, gsize *offset);
+
+void write_card32 (guint8 *buffer, gsize buffer_length, guint8 byte_order, guint32 value, gsize *offset);
+
+void write_string8 (guint8 *buffer, gsize buffer_length, const guint8 *value, gsize value_length, gsize *offset);
+
+gsize padded_string_length (const gchar *value);
+
+void write_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset);
+
+void write_padded_string (guint8 *buffer, gsize buffer_length, const gchar *value, gsize *offset);
+
+G_END_DECLS
+
+#endif /* _X_COMMON_H_ */
diff --git a/tests/src/x-server.c b/tests/src/x-server.c
new file mode 100644
index 00000000..c773c21b
--- /dev/null
+++ b/tests/src/x-server.c
@@ -0,0 +1,783 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <gio/gio.h>
+#include <gio/gunixsocketaddress.h>
+
+#include "x-common.h"
+#include "x-server.h"
+
+G_DEFINE_TYPE (XServer, x_server, G_TYPE_OBJECT);
+G_DEFINE_TYPE (XClient, x_client, G_TYPE_OBJECT);
+
+#define MAXIMUM_REQUEST_LENGTH 65535
+#define VENDOR "LightDM"
+
+enum
+{
+ Failed = 0,
+ Success = 1,
+ Authenticate = 2
+};
+
+enum
+{
+ Reply = 1,
+};
+
+enum {
+ X_SERVER_CLIENT_CONNECTED,
+ X_SERVER_CLIENT_DISCONNECTED,
+ X_SERVER_LAST_SIGNAL
+};
+static guint x_server_signals[X_SERVER_LAST_SIGNAL] = { 0 };
+
+struct XServerPrivate
+{
+ gint display_number;
+ gboolean listen_unix;
+ gboolean listen_tcp;
+ gint tcp_port;
+ gchar *socket_path;
+ GSocket *unix_socket;
+ GIOChannel *unix_channel;
+ GSocket *tcp_socket;
+ GIOChannel *tcp_channel;
+ GHashTable *clients;
+
+ GList *screens;
+};
+
+struct XClientPrivate
+{
+ GSocket *socket;
+ GIOChannel *channel;
+ guint8 byte_order;
+ gboolean connected;
+ guint16 sequence_number;
+};
+
+enum
+{
+ X_CLIENT_CONNECT,
+ X_CLIENT_INTERN_ATOM,
+ X_CLIENT_GET_PROPERTY,
+ X_CLIENT_CREATE_GC,
+ X_CLIENT_QUERY_EXTENSION,
+ X_CLIENT_DISCONNECTED,
+ X_CLIENT_LAST_SIGNAL
+};
+static guint x_client_signals[X_CLIENT_LAST_SIGNAL] = { 0 };
+
+GInetAddress *
+x_client_get_address (XClient *client)
+{
+ GSocketAddress *socket_address;
+ GError *error = NULL;
+
+ socket_address = g_socket_get_remote_address (client->priv->socket, &error);
+ if (error)
+ g_warning ("Error getting remote socket address");
+ g_clear_error (&error);
+ if (!socket_address)
+ return NULL;
+
+ if (G_IS_INET_SOCKET_ADDRESS (socket_address))
+ return g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_address));
+ else
+ return NULL;
+}
+
+void
+x_client_send_failed (XClient *client, const gchar *reason)
+{
+ guint8 buffer[MAXIMUM_REQUEST_LENGTH];
+ gsize n_written = 0, length_offset;
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, Failed, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, strlen (reason), &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_PROTOCOL_MAJOR_VERSION, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_PROTOCOL_MINOR_VERSION, &n_written);
+ length_offset = n_written;
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written);
+ write_padded_string (buffer, MAXIMUM_REQUEST_LENGTH, reason, &n_written);
+
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, (n_written - length_offset) / 4, &length_offset);
+
+ send (g_io_channel_unix_get_fd (client->priv->channel), buffer, n_written, 0);
+}
+
+void
+x_client_send_success (XClient *client)
+{
+ guint8 buffer[MAXIMUM_REQUEST_LENGTH];
+ gsize n_written = 0, length_offset;
+ int i;
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, Success, &n_written);
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_PROTOCOL_MAJOR_VERSION, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_PROTOCOL_MINOR_VERSION, &n_written);
+ length_offset = n_written;
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written);
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_RELEASE_NUMBER, &n_written);
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x00a00000, &n_written); // resource-id-base
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x001fffff, &n_written); // resource-id-mask
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // motion-buffer-size
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, strlen (VENDOR), &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, MAXIMUM_REQUEST_LENGTH, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); // number of screens
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 7, &n_written); // number of pixmap formats
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &n_written); // image-byte-order
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &n_written); // bitmap-format-bit-order
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // bitmap-format-scanline-unit
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // bitmap-format-scanline-pad
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // min-keycode
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 255, &n_written); // max-keycode
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+ write_padded_string (buffer, MAXIMUM_REQUEST_LENGTH, VENDOR, &n_written);
+
+ // LISTofFORMAT
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); // depth
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); // bits-per-pixel
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); // depth
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // bits-per-pixel
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // depth
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // bits-per-pixel
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 15, &n_written); // depth
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 16, &n_written); // bits-per-pixel
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 16, &n_written); // depth
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 16, &n_written); // bits-per-pixel
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 24, &n_written); // depth
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // bits-per-pixel
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // depth
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // bits-per-pixel
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // scanline-pad
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 5, &n_written);
+
+ // LISTofSCREEN
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 87, &n_written); // root
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 32, &n_written); // default-colormap
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x00FFFFFF, &n_written); // white-pixel
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x00000000, &n_written); // black-pixel
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, X_EVENT_StructureNotify | X_EVENT_SubstructureNotify | X_EVENT_SubstructureRedirect, &n_written); // SETofEVENT
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1680, &n_written); // width-in-pixels
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1050, &n_written); // height-in-pixels
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 569, &n_written); // width-in-millimeters
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 356, &n_written); // height-in-millimeters
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1, &n_written); // min-installed-maps
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1, &n_written); // max-installed-maps
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 34, &n_written); // root-visual
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &n_written); // backing-stores
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &n_written); // save-unders
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 24, &n_written); // root-depth
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 7, &n_written); // number of depths
+
+ // LISTofDEPTH
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 24, &n_written); // depth
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 32, &n_written); // number of VISUALTYPES in visuals
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+
+ // LISTofVISUALTYPE
+ for (i = 0; i < 32; i++)
+ {
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 34 + i, &n_written); // visual-id
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); // class
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // bits-per-rgb-value
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 1, &n_written); // colormap-entries
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x00FF0000, &n_written); // red-mask
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x0000FF00, &n_written); // green-mask
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0x000000FF, &n_written); // blue-mask
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+ }
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written); // depth
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written); // depth
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 8, &n_written); // depth
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 15, &n_written); // depth
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 16, &n_written); // depth
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 32, &n_written); // depth
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written); // number of VISUALTYPES in visuals
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 4, &n_written);
+
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, (n_written - length_offset) / 4, &length_offset);
+
+ send (g_io_channel_unix_get_fd (client->priv->channel), buffer, n_written, 0);
+}
+
+void
+x_client_send_query_extension_response (XClient *client, guint16 sequence_number, gboolean present, guint8 major_opcode, guint8 first_event, guint8 first_error)
+{
+ guint8 buffer[MAXIMUM_REQUEST_LENGTH];
+ gsize n_written = 0;
+
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, Reply, &n_written);
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 1, &n_written);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, sequence_number, &n_written); // sequence-number
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, client->priv->byte_order, 0, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, present ? 1 : 0, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, major_opcode, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, first_event, &n_written);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, first_error, &n_written);
+ write_padding (buffer, MAXIMUM_REQUEST_LENGTH, 20, &n_written);
+
+ send (g_io_channel_unix_get_fd (client->priv->channel), buffer, n_written, 0);
+}
+
+void
+x_client_disconnect (XClient *client)
+{
+ g_io_channel_shutdown (client->priv->channel, TRUE, NULL);
+}
+
+static void
+x_client_init (XClient *client)
+{
+ client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, x_client_get_type (), XClientPrivate);
+ client->priv->sequence_number = 1;
+}
+
+static void
+x_client_finalize (GObject *object)
+{
+}
+
+static void
+x_client_class_init (XClientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = x_client_finalize;
+ g_type_class_add_private (klass, sizeof (XClientPrivate));
+
+ x_client_signals[X_CLIENT_CONNECT] =
+ g_signal_new ("connect",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XClientClass, connect),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ x_client_signals[X_CLIENT_INTERN_ATOM] =
+ g_signal_new ("intern-atom",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XClientClass, intern_atom),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ x_client_signals[X_CLIENT_GET_PROPERTY] =
+ g_signal_new ("get_property",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XClientClass, get_property),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ x_client_signals[X_CLIENT_CREATE_GC] =
+ g_signal_new ("create-gc",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XClientClass, create_gc),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ x_client_signals[X_CLIENT_QUERY_EXTENSION] =
+ g_signal_new ("query-extension",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XClientClass, query_extension),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ x_client_signals[X_CLIENT_DISCONNECTED] =
+ g_signal_new ("disconnected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XClientClass, disconnected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+XServer *
+x_server_new (gint display_number)
+{
+ XServer *server = g_object_new (x_server_get_type (), NULL);
+ server->priv->display_number = display_number;
+ server->priv->tcp_port = 6000 + display_number;
+ return server;
+}
+
+void
+x_server_set_listen_unix (XServer *server, gboolean listen_unix)
+{
+ server->priv->listen_unix = listen_unix;
+}
+
+void
+x_server_set_listen_tcp (XServer *server, gboolean listen_tcp)
+{
+ server->priv->listen_tcp = listen_tcp;
+}
+
+static void
+decode_connection_request (XClient *client, const guint8 *buffer, gssize buffer_length)
+{
+ guint8 byte_order;
+ gsize offset = 0;
+ guint16 n;
+ XConnect *message;
+
+ byte_order = read_card8 (buffer, buffer_length, &offset);
+ if (!(byte_order == 'B' || byte_order == 'l'))
+ {
+ g_warning ("Invalid byte order");
+ return;
+ }
+
+ message = g_malloc0 (sizeof (XConnect));
+
+ message->byte_order = byte_order == 'B' ? X_BYTE_ORDER_MSB : X_BYTE_ORDER_LSB;
+ read_padding (1, &offset);
+ message->protocol_major_version = read_card16 (buffer, buffer_length, message->byte_order, &offset);
+ message->protocol_minor_version = read_card16 (buffer, buffer_length, message->byte_order, &offset);
+ n = read_card16 (buffer, buffer_length, message->byte_order, &offset);
+ message->authorization_protocol_data_length = read_card16 (buffer, buffer_length, message->byte_order, &offset);
+ read_padding (2, &offset);
+ message->authorization_protocol_name = read_padded_string (buffer, buffer_length, n, &offset);
+ message->authorization_protocol_data = read_string8 (buffer, buffer_length, message->authorization_protocol_data_length, &offset);
+ read_padding (pad (message->authorization_protocol_data_length), &offset);
+
+ /* Store information about the client */
+ client->priv->byte_order = message->byte_order;
+ client->priv->connected = TRUE;
+
+ g_signal_emit (client, x_client_signals[X_CLIENT_CONNECT], 0, message);
+
+ g_free (message->authorization_protocol_name);
+ g_free (message->authorization_protocol_data);
+ g_free (message);
+}
+
+static void
+decode_intern_atom (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset)
+{
+ XInternAtom *message;
+ guint16 name_length;
+
+ message = g_malloc0 (sizeof (XInternAtom));
+
+ message->only_if_exists = data != 0;
+ name_length = read_card16 (buffer, buffer_length, client->priv->byte_order, offset);
+ read_padding (2, offset);
+ message->name = read_padded_string (buffer, buffer_length, name_length, offset);
+
+ g_signal_emit (client, x_client_signals[X_CLIENT_INTERN_ATOM], 0, message);
+
+ g_free (message->name);
+ g_free (message);
+}
+
+static void
+decode_get_property (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset)
+{
+ XGetProperty *message;
+
+ message = g_malloc0 (sizeof (XGetProperty));
+
+ message->delete = data != 0;
+ message->window = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ message->property = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ message->type = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ message->long_offset = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ message->long_length = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+
+ g_signal_emit (client, x_client_signals[X_CLIENT_GET_PROPERTY], 0, message);
+
+ g_free (message);
+}
+
+static void
+decode_create_gc (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset)
+{
+ XCreateGC *message;
+
+ message = g_malloc0 (sizeof (XCreateGC));
+
+ message->cid = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ message->drawable = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ message->value_mask = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ if ((message->value_mask & X_GC_VALUE_MASK_function) != 0)
+ {
+ message->function = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_plane_mask) != 0)
+ message->plane_mask = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ if ((message->value_mask & X_GC_VALUE_MASK_foreground) != 0)
+ message->foreground = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ if ((message->value_mask & X_GC_VALUE_MASK_background) != 0)
+ message->background = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ if ((message->value_mask & X_GC_VALUE_MASK_line_width) != 0)
+ {
+ message->line_width = read_card16 (buffer, buffer_length, client->priv->byte_order, offset);
+ read_padding (2, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_line_style) != 0)
+ {
+ message->line_style = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_cap_style) != 0)
+ {
+ message->cap_style = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_join_style) != 0)
+ {
+ message->join_style = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_fill_style) != 0)
+ {
+ message->fill_style = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_fill_rule) != 0)
+ {
+ message->fill_rule = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_tile) != 0)
+ message->tile = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ if ((message->value_mask & X_GC_VALUE_MASK_stipple) != 0)
+ message->stipple = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ if ((message->value_mask & X_GC_VALUE_MASK_tile_stipple_x_origin) != 0)
+ {
+ message->tile_stipple_x_origin = read_card16 (buffer, buffer_length, client->priv->byte_order, offset);
+ read_padding (2, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_tile_stipple_y_origin) != 0)
+ {
+ message->tile_stipple_y_origin = read_card16 (buffer, buffer_length, client->priv->byte_order, offset);
+ read_padding (2, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_font) != 0)
+ message->font = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ if ((message->value_mask & X_GC_VALUE_MASK_subwindow_mode) != 0)
+ {
+ message->subwindow_mode = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_graphics_exposures) != 0)
+ {
+ message->graphics_exposures = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_clip_x_origin) != 0)
+ {
+ message->clip_x_origin = read_card16 (buffer, buffer_length, client->priv->byte_order, offset);
+ read_padding (2, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_clip_y_origin) != 0)
+ {
+ message->clip_y_origin = read_card16 (buffer, buffer_length, client->priv->byte_order, offset);
+ read_padding (2, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_clip_mask) != 0)
+ message->clip_mask = read_card32 (buffer, buffer_length, client->priv->byte_order, offset);
+ if ((message->value_mask & X_GC_VALUE_MASK_dash_offset) != 0)
+ {
+ message->dash_offset = read_card16 (buffer, buffer_length, client->priv->byte_order, offset);
+ read_padding (2, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_dashes) != 0)
+ {
+ message->dashes = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+ if ((message->value_mask & X_GC_VALUE_MASK_arc_mode) != 0)
+ {
+ message->arc_mode = read_card8 (buffer, buffer_length, offset);
+ read_padding (3, offset);
+ }
+
+ g_signal_emit (client, x_client_signals[X_CLIENT_CREATE_GC], 0, message);
+
+ g_free (message);
+}
+
+static void
+decode_query_extension (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset)
+{
+ XQueryExtension *message;
+ guint16 name_length;
+
+ message = g_malloc0 (sizeof (XQueryExtension));
+
+ name_length = read_card16 (buffer, buffer_length, client->priv->byte_order, offset);
+ read_padding (2, offset);
+ message->name = read_padded_string (buffer, buffer_length, name_length, offset);
+
+ g_signal_emit (client, x_client_signals[X_CLIENT_QUERY_EXTENSION], 0, message);
+
+ g_free (message->name);
+ g_free (message);
+}
+
+static void
+decode_big_req_enable (XClient *client, guint16 sequence_number, guint8 data, const guint8 *buffer, gssize buffer_length, gsize *offset)
+{
+}
+
+static void
+decode_request (XClient *client, guint16 sequence_number, const guint8 *buffer, gssize buffer_length)
+{
+ int opcode;
+ gsize offset = 0;
+
+ while (offset < buffer_length)
+ {
+ guint8 data;
+ guint16 length, remaining;
+ gsize start_offset;
+
+ start_offset = offset;
+ opcode = read_card8 (buffer, buffer_length, &offset);
+ data = read_card8 (buffer, buffer_length, &offset);
+ length = read_card16 (buffer, buffer_length, client->priv->byte_order, &offset) * 4;
+ remaining = start_offset + length;
+
+ g_debug ("Got opcode=%d length=%d", opcode, length);
+
+ switch (opcode)
+ {
+ /*case 1:
+ decode_create_window (client, sequence_number, data, buffer, remaining, &offset);
+ break;*/
+ /*case 4:
+ decode_destroy_window (client, sequence_number, data, buffer, remaining, &offset);
+ break;*/
+ /*case 8:
+ decode_map_window (client, sequence_number, data, buffer, remaining, &offset);
+ break;*/
+ /*case 10:
+ decode_unmap_window (client, sequence_number, data, buffer, remaining, &offset);
+ break;*/
+ /*case 12:
+ decode_configure_window (client, sequence_number, data, buffer, remaining, &offset);
+ break;*/
+ case 16:
+ decode_intern_atom (client, sequence_number, data, buffer, remaining, &offset);
+ break;
+ case 20:
+ decode_get_property (client, sequence_number, data, buffer, remaining, &offset);
+ break;
+ case 55:
+ decode_create_gc (client, sequence_number, data, buffer, remaining, &offset);
+ break;
+ case 98:
+ decode_query_extension (client, sequence_number, data, buffer, remaining, &offset);
+ break;
+ case 135:
+ decode_big_req_enable (client, sequence_number, data, buffer, remaining, &offset);
+ break;
+ default:
+ g_debug ("Ignoring unknown opcode %d", opcode);
+ break;
+ }
+
+ offset = start_offset + length;
+ }
+}
+
+static gboolean
+socket_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+ XClient *client = data;
+ guint8 buffer[MAXIMUM_REQUEST_LENGTH];
+ gssize n_read;
+
+ n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0);
+ if (n_read < 0)
+ g_warning ("Error reading from socket: %s", strerror (errno));
+ else if (n_read == 0)
+ {
+ g_signal_emit (client, x_client_signals[X_CLIENT_DISCONNECTED], 0);
+ return FALSE;
+ }
+ else
+ {
+ if (client->priv->connected)
+ {
+ decode_request (client, client->priv->sequence_number, buffer, n_read);
+ client->priv->sequence_number++;
+ }
+ else
+ decode_connection_request (client, buffer, n_read);
+ }
+
+ return TRUE;
+}
+
+static void
+x_client_disconnected_cb (XClient *client, XServer *server)
+{
+ g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, server);
+ g_hash_table_remove (server->priv->clients, client->priv->channel);
+ g_signal_emit (server, x_server_signals[X_SERVER_CLIENT_DISCONNECTED], 0, client);
+}
+
+static gboolean
+socket_connect_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+ XServer *server = data;
+ GSocket *data_socket;
+ XClient *client;
+ GError *error = NULL;
+
+ if (channel == server->priv->unix_channel)
+ data_socket = g_socket_accept (server->priv->unix_socket, NULL, &error);
+ else
+ data_socket = g_socket_accept (server->priv->tcp_socket, NULL, &error);
+ if (error)
+ g_warning ("Error accepting connection: %s", strerror (errno));
+ g_clear_error (&error);
+ if (!data_socket)
+ return FALSE;
+
+ client = g_object_new (x_client_get_type (), NULL);
+ g_signal_connect (client, "disconnected", G_CALLBACK (x_client_disconnected_cb), server);
+ client->priv->socket = data_socket;
+ client->priv->channel = g_io_channel_unix_new (g_socket_get_fd (data_socket));
+ g_hash_table_insert (server->priv->clients, client->priv->channel, client);
+ g_io_add_watch (client->priv->channel, G_IO_IN, socket_data_cb, client);
+
+ g_signal_emit (server, x_server_signals[X_SERVER_CLIENT_CONNECTED], 0, client);
+
+ return TRUE;
+}
+
+gboolean
+x_server_start (XServer *server)
+{
+ if (server->priv->listen_unix)
+ {
+ GError *error = NULL;
+
+ server->priv->socket_path = g_strdup_printf ("/tmp/.X11-unix/X%d", server->priv->display_number);
+
+ server->priv->unix_socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
+ if (!server->priv->unix_socket ||
+ !g_socket_bind (server->priv->unix_socket, g_unix_socket_address_new (server->priv->socket_path), TRUE, &error) ||
+ !g_socket_listen (server->priv->unix_socket, &error))
+ {
+ g_warning ("Error creating Unix X socket: %s", error->message);
+ return FALSE;
+ }
+ server->priv->unix_channel = g_io_channel_unix_new (g_socket_get_fd (server->priv->unix_socket));
+ g_io_add_watch (server->priv->unix_channel, G_IO_IN, socket_connect_cb, server);
+ }
+
+ if (server->priv->listen_tcp)
+ {
+ GError *error = NULL;
+
+ server->priv->tcp_socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &error);
+ if (!server->priv->tcp_socket ||
+ !g_socket_bind (server->priv->tcp_socket, g_inet_socket_address_new (g_inet_address_new_any (G_SOCKET_FAMILY_IPV4), server->priv->tcp_port), TRUE, &error) ||
+ !g_socket_listen (server->priv->tcp_socket, &error))
+ {
+ g_warning ("Error creating TCP/IP X socket: %s", error->message);
+ return FALSE;
+ }
+ server->priv->tcp_channel = g_io_channel_unix_new (g_socket_get_fd (server->priv->tcp_socket));
+ g_io_add_watch (server->priv->tcp_channel, G_IO_IN, socket_connect_cb, server);
+ }
+
+ return TRUE;
+}
+
+gsize
+x_server_get_n_clients (XServer *server)
+{
+ return g_hash_table_size (server->priv->clients);
+}
+
+static void
+x_server_init (XServer *server)
+{
+ server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, x_server_get_type (), XServerPrivate);
+ server->priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) g_io_channel_unref, g_object_unref);
+ server->priv->listen_unix = TRUE;
+ server->priv->listen_tcp = TRUE;
+}
+
+static void
+x_server_finalize (GObject *object)
+{
+ XServer *server = (XServer *) object;
+ if (server->priv->socket_path)
+ unlink (server->priv->socket_path);
+}
+
+static void
+x_server_class_init (XServerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = x_server_finalize;
+ g_type_class_add_private (klass, sizeof (XServerPrivate));
+ x_server_signals[X_SERVER_CLIENT_CONNECTED] =
+ g_signal_new ("client-connected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XServerClass, client_connected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, x_client_get_type ());
+ x_server_signals[X_SERVER_CLIENT_DISCONNECTED] =
+ g_signal_new ("client-disconnected",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XServerClass, client_disconnected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, x_client_get_type ());
+}
diff --git a/tests/src/x-server.h b/tests/src/x-server.h
new file mode 100644
index 00000000..955a8d52
--- /dev/null
+++ b/tests/src/x-server.h
@@ -0,0 +1,190 @@
+#ifndef _X_SERVER_H_
+#define _X_SERVER_H_
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define X_PROTOCOL_MAJOR_VERSION 11
+#define X_PROTOCOL_MINOR_VERSION 0
+
+#define X_RELEASE_NUMBER 0
+
+typedef enum
+{
+ X_EVENT_KeyPress = 0x00000001,
+ X_EVENT_KeyRelease = 0x00000002,
+ X_EVENT_ButtonPress = 0x00000004,
+ X_EVENT_ButtonRelease = 0x00000008,
+ X_EVENT_EnterWindow = 0x00000010,
+ X_EVENT_LeaveWindow = 0x00000020,
+ X_EVENT_PointerMotion = 0x00000040,
+ X_EVENT_PointerMotionHint = 0x00000080,
+ X_EVENT_Button1Motion = 0x00000100,
+ X_EVENT_Button2Motion = 0x00000200,
+ X_EVENT_Button3Motion = 0x00000400,
+ X_EVENT_Button4Motion = 0x00000800,
+ X_EVENT_Button5Motion = 0x00001000,
+ X_EVENT_ButtonMotion = 0x00002000,
+ X_EVENT_KeymapState = 0x00004000,
+ X_EVENT_Exposure = 0x00008000,
+ X_EVENT_VisibilityChange = 0x00010000,
+ X_EVENT_StructureNotify = 0x00020000,
+ X_EVENT_ResizeRedirect = 0x00040000,
+ X_EVENT_SubstructureNotify = 0x00080000,
+ X_EVENT_SubstructureRedirect = 0x00100000,
+ X_EVENT_FocusChange = 0x00200000,
+ X_EVENT_PropertyChange = 0x00400000,
+ X_EVENT_ColormapChange = 0x00800000,
+ X_EVENT_OwnerGrabButton = 0x01000000
+} XEvent;
+
+enum
+{
+ X_GC_VALUE_MASK_function = 0x00000001,
+ X_GC_VALUE_MASK_plane_mask = 0x00000002,
+ X_GC_VALUE_MASK_foreground = 0x00000004,
+ X_GC_VALUE_MASK_background = 0x00000008,
+ X_GC_VALUE_MASK_line_width = 0x00000010,
+ X_GC_VALUE_MASK_line_style = 0x00000020,
+ X_GC_VALUE_MASK_cap_style = 0x00000040,
+ X_GC_VALUE_MASK_join_style = 0x00000080,
+ X_GC_VALUE_MASK_fill_style = 0x00000100,
+ X_GC_VALUE_MASK_fill_rule = 0x00000200,
+ X_GC_VALUE_MASK_tile = 0x00000400,
+ X_GC_VALUE_MASK_stipple = 0x00000800,
+ X_GC_VALUE_MASK_tile_stipple_x_origin = 0x00001000,
+ X_GC_VALUE_MASK_tile_stipple_y_origin = 0x00002000,
+ X_GC_VALUE_MASK_font = 0x00004000,
+ X_GC_VALUE_MASK_subwindow_mode = 0x00008000,
+ X_GC_VALUE_MASK_graphics_exposures = 0x00010000,
+ X_GC_VALUE_MASK_clip_x_origin = 0x00020000,
+ X_GC_VALUE_MASK_clip_y_origin = 0x00040000,
+ X_GC_VALUE_MASK_clip_mask = 0x00080000,
+ X_GC_VALUE_MASK_dash_offset = 0x00100000,
+ X_GC_VALUE_MASK_dashes = 0x00200000,
+ X_GC_VALUE_MASK_arc_mode = 0x00400000
+} XGCValueMask;
+
+typedef struct
+{
+ guint8 byte_order;
+ guint16 protocol_major_version, protocol_minor_version;
+ gchar *authorization_protocol_name;
+ guint8 *authorization_protocol_data;
+ guint16 authorization_protocol_data_length;
+} XConnect;
+
+typedef struct
+{
+ gboolean only_if_exists;
+ gchar *name;
+} XInternAtom;
+
+typedef struct
+{
+ gboolean delete;
+ guint32 window;
+ guint32 property;
+ guint32 type;
+ guint32 long_offset;
+ guint32 long_length;
+} XGetProperty;
+
+typedef struct
+{
+ guint32 cid;
+ guint32 drawable;
+ guint32 value_mask;
+ guint8 function;
+ guint32 plane_mask;
+ guint32 foreground;
+ guint32 background;
+ guint16 line_width;
+ guint8 line_style;
+ guint8 cap_style;
+ guint8 join_style;
+ guint8 fill_style;
+ guint8 fill_rule;
+ guint32 tile;
+ guint32 stipple;
+ guint16 tile_stipple_x_origin;
+ guint16 tile_stipple_y_origin;
+ guint32 font;
+ guint8 subwindow_mode;
+ gboolean graphics_exposures;
+ guint16 clip_x_origin;
+ guint16 clip_y_origin;
+ guint32 clip_mask;
+ guint16 dash_offset;
+ guint8 dashes;
+ guint8 arc_mode;
+} XCreateGC;
+
+typedef struct
+{
+ gchar *name;
+} XQueryExtension;
+
+typedef struct XClientPrivate XClientPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ XClientPrivate *priv;
+} XClient;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ void (*connect)(XClient *client, XConnect *message);
+ void (*intern_atom)(XClient *client, XInternAtom *message);
+ void (*get_property)(XClient *client, XGetProperty *message);
+ void (*create_gc)(XClient *client, XCreateGC *message);
+ void (*query_extension)(XClient *client, XQueryExtension *message);
+ void (*disconnected)(XClient *client);
+} XClientClass;
+
+typedef struct XServerPrivate XServerPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ XServerPrivate *priv;
+} XServer;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ void (*client_connected)(XServer *server, XClient *client);
+ void (*client_disconnected)(XServer *server, XClient *client);
+} XServerClass;
+
+GType x_server_get_type (void);
+
+GType x_client_get_type (void);
+
+XServer *x_server_new (gint display_number);
+
+void x_server_set_listen_unix (XServer *server, gboolean listen_unix);
+
+void x_server_set_listen_tcp (XServer *server, gboolean listen_tcp);
+
+gboolean x_server_start (XServer *server);
+
+gsize x_server_get_n_clients (XServer *server);
+
+GInetAddress *x_client_get_address (XClient *client);
+
+void x_client_send_failed (XClient *client, const gchar *reason);
+
+void x_client_send_success (XClient *client);
+
+void x_client_send_query_extension_response (XClient *client, guint16 sequence_number, gboolean present, guint8 major_opcode, guint8 first_event, guint8 first_error);
+
+void x_client_disconnect (XClient *client);
+
+G_END_DECLS
+
+#endif /* _X_SERVER_H_ */
diff --git a/tests/src/xdmcp-client.c b/tests/src/xdmcp-client.c
new file mode 100644
index 00000000..dd352c8b
--- /dev/null
+++ b/tests/src/xdmcp-client.c
@@ -0,0 +1,456 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <gio/gio.h>
+
+#include "x-common.h"
+#include "xdmcp-client.h"
+
+G_DEFINE_TYPE (XDMCPClient, xdmcp_client, G_TYPE_OBJECT);
+
+#define MAXIMUM_REQUEST_LENGTH 65535
+
+typedef enum
+{
+ XDMCP_BroadcastQuery = 1,
+ XDMCP_Query = 2,
+ XDMCP_IndirectQuery = 3,
+ XDMCP_ForwardQuery = 4,
+ XDMCP_Willing = 5,
+ XDMCP_Unwilling = 6,
+ XDMCP_Request = 7,
+ XDMCP_Accept = 8,
+ XDMCP_Decline = 9,
+ XDMCP_Manage = 10,
+ XDMCP_Refuse = 11,
+ XDMCP_Failed = 12,
+ XDMCP_KeepAlive = 13,
+ XDMCP_Alive = 14
+} XDMCPOpcode;
+
+struct XDMCPClientPrivate
+{
+ gchar *host;
+ gint port;
+ GSocket *socket;
+ guint query_timer;
+ gchar *authorization_name;
+ gint authorization_data_length;
+ guint8 *authorization_data;
+};
+
+enum {
+ XDMCP_CLIENT_QUERY,
+ XDMCP_CLIENT_WILLING,
+ XDMCP_CLIENT_ACCEPT,
+ XDMCP_CLIENT_DECLINE,
+ XDMCP_CLIENT_FAILED,
+ XDMCP_CLIENT_LAST_SIGNAL
+};
+static guint xdmcp_client_signals[XDMCP_CLIENT_LAST_SIGNAL] = { 0 };
+
+static void
+xdmcp_write (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
+{
+ gssize n_written;
+ GError *error = NULL;
+
+ n_written = g_socket_send (client->priv->socket, (const gchar *) buffer, buffer_length, NULL, &error);
+ if (n_written < 0)
+ g_warning ("Failed to send XDMCP request: %s", error->message);
+ else if (n_written != buffer_length)
+ g_warning ("Partial write for XDMCP request, wrote %zi, expected %zi", n_written, buffer_length);
+ g_clear_error (&error);
+}
+
+static void
+decode_willing (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
+{
+ XDMCPWilling *message;
+ gsize offset = 0;
+ guint16 length;
+
+ if (client->priv->query_timer == 0)
+ {
+ g_debug ("Ignoring XDMCP unrequested/duplicate Willing");
+ return;
+ }
+
+ /* Stop sending queries */
+ g_source_remove (client->priv->query_timer);
+ client->priv->query_timer = 0;
+
+ message = g_malloc0 (sizeof (XDMCPWilling));
+
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->authentication_name = read_string (buffer, buffer_length, length, &offset);
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->hostname = read_string (buffer, buffer_length, length, &offset);
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->status = read_string (buffer, buffer_length, length, &offset);
+
+ g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_WILLING], 0, message);
+
+ g_free (message->authentication_name);
+ g_free (message->hostname);
+ g_free (message->status);
+ g_free (message);
+}
+
+static void
+decode_accept (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
+{
+ XDMCPAccept *message;
+ gsize offset = 0;
+ guint16 length;
+
+ message = g_malloc (sizeof (XDMCPAccept));
+
+ message->session_id = read_card32 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->authentication_name = read_string (buffer, buffer_length, length, &offset);
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ read_string8 (buffer, buffer_length, length, &offset);
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->authorization_name = read_string (buffer, buffer_length, length, &offset);
+ message->authorization_data_length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->authorization_data = read_string8 (buffer, buffer_length, length, &offset);
+
+ g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_ACCEPT], 0, message);
+
+ g_free (message->authentication_name);
+ g_free (message->authorization_name);
+ g_free (message->authorization_data);
+ g_free (message);
+}
+
+static void
+decode_decline (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
+{
+ XDMCPDecline *message;
+ gsize offset = 0;
+ guint16 length;
+
+ message = g_malloc0 (sizeof (XDMCPDecline));
+
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->status = read_string (buffer, buffer_length, length, &offset);
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->authentication_name = read_string (buffer, buffer_length, length, &offset);
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ read_string8 (buffer, buffer_length, length, &offset);
+
+ g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_DECLINE], 0, message);
+
+ g_free (message->status);
+ g_free (message->authentication_name);
+ g_free (message);
+}
+
+static void
+decode_failed (XDMCPClient *client, const guint8 *buffer, gssize buffer_length)
+{
+ XDMCPFailed *message;
+ gsize offset = 0;
+ guint16 length;
+
+ message = g_malloc0 (sizeof (XDMCPFailed));
+
+ message->session_id = read_card32 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ length = read_card16 (buffer, buffer_length, X_BYTE_ORDER_MSB, &offset);
+ message->status = read_string (buffer, buffer_length, length, &offset);
+
+ g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_FAILED], 0, message);
+
+ g_free (message->status);
+ g_free (message);
+}
+
+static gboolean
+xdmcp_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+ XDMCPClient *client = data;
+ guint8 buffer[MAXIMUM_REQUEST_LENGTH];
+ gssize n_read;
+
+ n_read = recv (g_io_channel_unix_get_fd (channel), buffer, MAXIMUM_REQUEST_LENGTH, 0);
+ if (n_read < 0)
+ g_warning ("Error reading from XDMCP socket: %s", strerror (errno));
+ else if (n_read == 0)
+ {
+ g_debug ("EOF");
+ return FALSE;
+ }
+ else
+ {
+ gsize offset = 0;
+ guint16 version, opcode, length;
+
+ version = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset);
+ opcode = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset);
+ length = read_card16 (buffer, n_read, X_BYTE_ORDER_MSB, &offset);
+
+ if (version != 1)
+ {
+ g_debug ("Ignoring XDMCP version %d message", version);
+ return TRUE;
+ }
+ if (6 + length > n_read)
+ {
+ g_debug ("Ignoring XDMCP message of length %zi with invalid length field %d", n_read, length);
+ return TRUE;
+ }
+ switch (opcode)
+ {
+ case XDMCP_Willing:
+ decode_willing (client, buffer + offset, n_read - offset);
+ break;
+
+ case XDMCP_Accept:
+ decode_accept (client, buffer + offset, n_read - offset);
+ break;
+
+ case XDMCP_Decline:
+ decode_decline (client, buffer + offset, n_read - offset);
+ break;
+
+ case XDMCP_Failed:
+ decode_failed (client, buffer + offset, n_read - offset);
+ break;
+
+ default:
+ g_debug ("Ignoring unknown XDMCP opcode %d", opcode);
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+xdmcp_query_cb (gpointer data)
+{
+ XDMCPClient *client = data;
+ g_signal_emit (client, xdmcp_client_signals[XDMCP_CLIENT_QUERY], 0);
+ xdmcp_client_send_query (client);
+ return TRUE;
+}
+
+XDMCPClient *
+xdmcp_client_new (void)
+{
+ return g_object_new (xdmcp_client_get_type (), NULL);
+}
+
+void
+xdmcp_client_set_hostname (XDMCPClient *client, const gchar *hostname)
+{
+ g_free (client->priv->host);
+ client->priv->host = g_strdup (hostname);
+}
+
+void
+xdmcp_client_set_port (XDMCPClient *client, guint16 port)
+{
+ client->priv->port = port;
+}
+
+gboolean
+xdmcp_client_start (XDMCPClient *client)
+{
+ GSocketConnectable *address;
+ GSocketAddress *socket_address;
+ GError *error = NULL;
+
+ client->priv->socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error);
+
+ address = g_network_address_new (client->priv->host, client->priv->port);
+ socket_address = g_socket_address_enumerator_next (g_socket_connectable_enumerate (address), NULL, NULL);
+
+ if (!client->priv->socket ||
+ !g_socket_connect (client->priv->socket, socket_address, NULL, &error) ||
+ !g_io_add_watch (g_io_channel_unix_new (g_socket_get_fd (client->priv->socket)), G_IO_IN, xdmcp_data_cb, client))
+ {
+ g_warning ("Error creating XDMCP socket: %s", error->message);
+ return FALSE;
+ }
+
+ client->priv->query_timer = g_timeout_add (2000, xdmcp_query_cb, client);
+ xdmcp_query_cb (client);
+
+ return TRUE;
+}
+
+GInetAddress *
+xdmcp_client_get_local_address (XDMCPClient *client)
+{
+ GSocketAddress *socket_address;
+
+ if (!client->priv->socket)
+ return NULL;
+
+ socket_address = g_socket_get_local_address (client->priv->socket, NULL);
+ return g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_address));
+}
+
+static void
+xdmcp_client_init (XDMCPClient *client)
+{
+ client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, xdmcp_client_get_type (), XDMCPClientPrivate);
+ client->priv->port = XDMCP_PORT;
+}
+
+void
+xdmcp_client_send_query (XDMCPClient *client)
+{
+ guint8 buffer[MAXIMUM_REQUEST_LENGTH];
+ gsize offset = 0;
+
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Query, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 1, &offset);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, 0, &offset);
+
+ xdmcp_write (client, buffer, offset);
+}
+
+void
+xdmcp_client_send_request (XDMCPClient *client,
+ guint16 display_number,
+ GInetAddress **addresses,
+ const gchar *authentication_name,
+ const guint8 *authentication_data, guint16 authentication_data_length,
+ gchar **authorization_names, const gchar *mfid)
+{
+ guint8 buffer[MAXIMUM_REQUEST_LENGTH];
+ gsize length = 0, offset = 0, n_addresses = 0, n_names = 0;
+ GInetAddress **address;
+ gchar **name;
+
+ length = 11 + strlen (authentication_name) + authentication_data_length + strlen (mfid);
+ for (address = addresses; *address; address++)
+ {
+ gssize native_address_length = g_inet_address_get_native_size (*address);
+ length += 4 + native_address_length;
+ n_addresses++;
+ }
+ for (name = authorization_names; *name; name++)
+ {
+ length += 2 + strlen (*name);
+ n_names++;
+ }
+
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Request, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, length, &offset);
+
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, display_number, &offset);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_addresses, &offset);
+ for (address = addresses; *address; address++)
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 0, &offset); /* FamilyInternet */
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_addresses, &offset);
+ for (address = addresses; *address; address++)
+ {
+ gssize native_address_length;
+ const guint8 *native_address;
+
+ native_address_length = g_inet_address_get_native_size (*address);
+ native_address = g_inet_address_to_bytes (*address);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, native_address_length, &offset);
+ write_string8 (buffer, MAXIMUM_REQUEST_LENGTH, native_address, native_address_length, &offset);
+ }
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (authentication_name), &offset);
+ write_string (buffer, MAXIMUM_REQUEST_LENGTH, authentication_name, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, authentication_data_length, &offset);
+ write_string8 (buffer, MAXIMUM_REQUEST_LENGTH, authentication_data, authentication_data_length, &offset);
+ write_card8 (buffer, MAXIMUM_REQUEST_LENGTH, n_names, &offset);
+ for (name = authorization_names; *name; name++)
+ {
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (*name), &offset);
+ write_string (buffer, MAXIMUM_REQUEST_LENGTH, *name, &offset);
+ }
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (mfid), &offset);
+ write_string (buffer, MAXIMUM_REQUEST_LENGTH, mfid, &offset);
+
+ xdmcp_write (client, buffer, offset);
+}
+
+void
+xdmcp_client_send_manage (XDMCPClient *client, guint32 session_id, guint16 display_number, gchar *display_class)
+{
+ guint8 buffer[MAXIMUM_REQUEST_LENGTH];
+ gsize offset = 0;
+
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_VERSION, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, XDMCP_Manage, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, 8 + strlen (display_class), &offset);
+
+ write_card32 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, session_id, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, display_number, &offset);
+ write_card16 (buffer, MAXIMUM_REQUEST_LENGTH, X_BYTE_ORDER_MSB, strlen (display_class), &offset);
+ write_string (buffer, MAXIMUM_REQUEST_LENGTH, display_class, &offset);
+
+ xdmcp_write (client, buffer, offset);
+}
+
+static void
+xdmcp_client_finalize (GObject *object)
+{
+ XDMCPClient *client = (XDMCPClient *) object;
+ g_free (client->priv->host);
+ if (client->priv->socket)
+ g_object_unref (client->priv->socket);
+ g_free (client->priv->authorization_name);
+ g_free (client->priv->authorization_data);
+}
+
+static void
+xdmcp_client_class_init (XDMCPClientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = xdmcp_client_finalize;
+ g_type_class_add_private (klass, sizeof (XDMCPClientPrivate));
+ xdmcp_client_signals[XDMCP_CLIENT_QUERY] =
+ g_signal_new ("query",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XDMCPClientClass, query),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ xdmcp_client_signals[XDMCP_CLIENT_WILLING] =
+ g_signal_new ("willing",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XDMCPClientClass, willing),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ xdmcp_client_signals[XDMCP_CLIENT_ACCEPT] =
+ g_signal_new ("accept",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XDMCPClientClass, accept),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ xdmcp_client_signals[XDMCP_CLIENT_DECLINE] =
+ g_signal_new ("decline",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XDMCPClientClass, decline),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ xdmcp_client_signals[XDMCP_CLIENT_FAILED] =
+ g_signal_new ("failed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (XDMCPClientClass, failed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
diff --git a/tests/src/xdmcp-client.h b/tests/src/xdmcp-client.h
new file mode 100644
index 00000000..8cacbf83
--- /dev/null
+++ b/tests/src/xdmcp-client.h
@@ -0,0 +1,81 @@
+#ifndef _XDMCP_CLIENT_H_
+#define _XDMCP_CLIENT_H_
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define XDMCP_VERSION 1
+#define XDMCP_PORT 177
+
+typedef struct
+{
+ gchar *authentication_name;
+ gchar *hostname;
+ gchar *status;
+} XDMCPWilling;
+
+typedef struct
+{
+ guint32 session_id;
+ gchar *authentication_name;
+ gchar *authorization_name;
+ guint16 authorization_data_length;
+ guint8 *authorization_data;
+} XDMCPAccept;
+
+typedef struct
+{
+ gchar *status;
+ gchar *authentication_name;
+} XDMCPDecline;
+
+typedef struct
+{
+ guint32 session_id;
+ gchar *status;
+} XDMCPFailed;
+
+typedef struct XDMCPClientPrivate XDMCPClientPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ XDMCPClientPrivate *priv;
+} XDMCPClient;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ void (*query)(XDMCPClient *client);
+ void (*willing)(XDMCPClient *client, XDMCPWilling *message);
+ void (*accept)(XDMCPClient *client, XDMCPAccept *message);
+ void (*decline)(XDMCPClient *client, XDMCPDecline *message);
+ void (*failed)(XDMCPClient *client, XDMCPFailed *message);
+} XDMCPClientClass;
+
+GType xdmcp_client_get_type (void);
+
+XDMCPClient *xdmcp_client_new (void);
+
+void xdmcp_client_set_hostname (XDMCPClient *client, const gchar *hostname);
+
+void xdmcp_client_set_port (XDMCPClient *client, guint16 port);
+
+gboolean xdmcp_client_start (XDMCPClient *client);
+
+GInetAddress *xdmcp_client_get_local_address (XDMCPClient *client);
+
+void xdmcp_client_send_query (XDMCPClient *client);
+
+void xdmcp_client_send_request (XDMCPClient *client,
+ guint16 display_number,
+ GInetAddress **addresses,
+ const gchar *authentication_name,
+ const guint8 *authentication_data, guint16 authentication_data_length,
+ gchar **authorization_names, const gchar *mfid);
+
+void xdmcp_client_send_manage (XDMCPClient *client, guint32 session_id, guint16 display_number, gchar *display_class);
+
+G_END_DECLS
+
+#endif /* _XDMCP_CLIENT_H_ */
diff --git a/tests/test-vnc-login b/tests/test-vnc-login
new file mode 100755
index 00000000..243b7d45
--- /dev/null
+++ b/tests/test-vnc-login
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/test-runner vnc-login test-gobject-greeter