summaryrefslogtreecommitdiff
path: root/mate-session/gsm-xsmp-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'mate-session/gsm-xsmp-server.c')
-rw-r--r--mate-session/gsm-xsmp-server.c732
1 files changed, 732 insertions, 0 deletions
diff --git a/mate-session/gsm-xsmp-server.c b/mate-session/gsm-xsmp-server.c
new file mode 100644
index 0000000..1f0e045
--- /dev/null
+++ b/mate-session/gsm-xsmp-server.c
@@ -0,0 +1,732 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include <X11/ICE/ICElib.h>
+#include <X11/ICE/ICEutil.h>
+#include <X11/ICE/ICEconn.h>
+#include <X11/SM/SMlib.h>
+
+#ifdef HAVE_X11_XTRANS_XTRANS_H
+/* Get the proto for _IceTransNoListen */
+#define ICE_t
+#define TRANS_SERVER
+#include <X11/Xtrans/Xtrans.h>
+#undef ICE_t
+#undef TRANS_SERVER
+#endif /* HAVE_X11_XTRANS_XTRANS_H */
+
+#include "gsm-xsmp-server.h"
+#include "gsm-xsmp-client.h"
+#include "gsm-util.h"
+
+/* ICEauthority stuff */
+/* Various magic numbers stolen from iceauth.c */
+#define GSM_ICE_AUTH_RETRIES 10
+#define GSM_ICE_AUTH_INTERVAL 2 /* 2 seconds */
+#define GSM_ICE_AUTH_LOCK_TIMEOUT 600 /* 10 minutes */
+
+#define GSM_ICE_MAGIC_COOKIE_AUTH_NAME "MIT-MAGIC-COOKIE-1"
+#define GSM_ICE_MAGIC_COOKIE_LEN 16
+
+#define GSM_XSMP_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_XSMP_SERVER, GsmXsmpServerPrivate))
+
+struct GsmXsmpServerPrivate
+{
+ GsmStore *client_store;
+
+ IceListenObj *xsmp_sockets;
+ int num_xsmp_sockets;
+ int num_local_xsmp_sockets;
+
+};
+
+enum {
+ PROP_0,
+ PROP_CLIENT_STORE
+};
+
+static void gsm_xsmp_server_class_init (GsmXsmpServerClass *klass);
+static void gsm_xsmp_server_init (GsmXsmpServer *xsmp_server);
+static void gsm_xsmp_server_finalize (GObject *object);
+
+static gpointer xsmp_server_object = NULL;
+
+G_DEFINE_TYPE (GsmXsmpServer, gsm_xsmp_server, G_TYPE_OBJECT)
+
+typedef struct {
+ GsmXsmpServer *server;
+ IceListenObj listener;
+} GsmIceConnectionData;
+
+typedef struct {
+ guint watch_id;
+ guint protocol_timeout;
+} GsmIceConnectionWatch;
+
+static void
+disconnect_ice_connection (IceConn ice_conn)
+{
+ IceSetShutdownNegotiation (ice_conn, FALSE);
+ IceCloseConnection (ice_conn);
+}
+
+static void
+free_ice_connection_watch (GsmIceConnectionWatch *data)
+{
+ if (data->watch_id) {
+ g_source_remove (data->watch_id);
+ data->watch_id = 0;
+ }
+
+ if (data->protocol_timeout) {
+ g_source_remove (data->protocol_timeout);
+ data->protocol_timeout = 0;
+ }
+
+ g_free (data);
+}
+
+static gboolean
+ice_protocol_timeout (IceConn ice_conn)
+{
+ GsmIceConnectionWatch *data;
+
+ g_debug ("GsmXsmpServer: ice_protocol_timeout for IceConn %p with status %d",
+ ice_conn, IceConnectionStatus (ice_conn));
+
+ data = ice_conn->context;
+
+ free_ice_connection_watch (data);
+ disconnect_ice_connection (ice_conn);
+
+ return FALSE;
+}
+
+static gboolean
+auth_iochannel_watch (GIOChannel *source,
+ GIOCondition condition,
+ IceConn ice_conn)
+{
+
+ GsmIceConnectionWatch *data;
+ gboolean keep_going;
+
+ data = ice_conn->context;
+
+ switch (IceProcessMessages (ice_conn, NULL, NULL)) {
+ case IceProcessMessagesSuccess:
+ keep_going = TRUE;
+ break;
+ case IceProcessMessagesIOError:
+ g_debug ("GsmXsmpServer: IceProcessMessages returned IceProcessMessagesIOError");
+ free_ice_connection_watch (data);
+ disconnect_ice_connection (ice_conn);
+ keep_going = FALSE;
+ break;
+ case IceProcessMessagesConnectionClosed:
+ g_debug ("GsmXsmpServer: IceProcessMessages returned IceProcessMessagesConnectionClosed");
+ free_ice_connection_watch (data);
+ keep_going = FALSE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return keep_going;
+}
+
+/* IceAcceptConnection returns a new ICE connection that is in a "pending" state,
+ * this is because authentification may be necessary.
+ * So we've to authenticate it, before accept_xsmp_connection() is called.
+ * Then each GsmXSMPClient will have its own IceConn watcher
+ */
+static void
+auth_ice_connection (IceConn ice_conn)
+{
+ GIOChannel *channel;
+ GsmIceConnectionWatch *data;
+ int fd;
+
+ g_debug ("GsmXsmpServer: auth_ice_connection()");
+
+ fd = IceConnectionNumber (ice_conn);
+ fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
+ channel = g_io_channel_unix_new (fd);
+
+ data = g_new0 (GsmIceConnectionWatch, 1);
+ ice_conn->context = data;
+
+ data->protocol_timeout = g_timeout_add_seconds (5,
+ (GSourceFunc)ice_protocol_timeout,
+ ice_conn);
+ data->watch_id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_ERR,
+ (GIOFunc)auth_iochannel_watch,
+ ice_conn);
+ g_io_channel_unref (channel);
+}
+
+/* This is called (by glib via xsmp->ice_connection_watch) when a
+ * connection is first received on the ICE listening socket.
+ */
+static gboolean
+accept_ice_connection (GIOChannel *source,
+ GIOCondition condition,
+ GsmIceConnectionData *data)
+{
+ IceConn ice_conn;
+ IceAcceptStatus status;
+
+ g_debug ("GsmXsmpServer: accept_ice_connection()");
+
+ ice_conn = IceAcceptConnection (data->listener, &status);
+ if (status != IceAcceptSuccess) {
+ g_debug ("GsmXsmpServer: IceAcceptConnection returned %d", status);
+ return TRUE;
+ }
+
+ auth_ice_connection (ice_conn);
+
+ return TRUE;
+}
+
+void
+gsm_xsmp_server_start (GsmXsmpServer *server)
+{
+ GIOChannel *channel;
+ int i;
+
+ for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) {
+ GsmIceConnectionData *data;
+
+ data = g_new0 (GsmIceConnectionData, 1);
+ data->server = server;
+ data->listener = server->priv->xsmp_sockets[i];
+
+ channel = g_io_channel_unix_new (IceGetListenConnectionNumber (server->priv->xsmp_sockets[i]));
+ g_io_add_watch_full (channel,
+ G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_HUP | G_IO_ERR,
+ (GIOFunc)accept_ice_connection,
+ data,
+ (GDestroyNotify)g_free);
+ g_io_channel_unref (channel);
+ }
+}
+
+static void
+gsm_xsmp_server_set_client_store (GsmXsmpServer *xsmp_server,
+ GsmStore *store)
+{
+ g_return_if_fail (GSM_IS_XSMP_SERVER (xsmp_server));
+
+ if (store != NULL) {
+ g_object_ref (store);
+ }
+
+ if (xsmp_server->priv->client_store != NULL) {
+ g_object_unref (xsmp_server->priv->client_store);
+ }
+
+ xsmp_server->priv->client_store = store;
+}
+
+static void
+gsm_xsmp_server_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GsmXsmpServer *self;
+
+ self = GSM_XSMP_SERVER (object);
+
+ switch (prop_id) {
+ case PROP_CLIENT_STORE:
+ gsm_xsmp_server_set_client_store (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gsm_xsmp_server_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GsmXsmpServer *self;
+
+ self = GSM_XSMP_SERVER (object);
+
+ switch (prop_id) {
+ case PROP_CLIENT_STORE:
+ g_value_set_object (value, self->priv->client_store);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* This is called (by libSM) when XSMP is initiated on an ICE
+ * connection that was already accepted by accept_ice_connection.
+ */
+static Status
+accept_xsmp_connection (SmsConn sms_conn,
+ GsmXsmpServer *server,
+ unsigned long *mask_ret,
+ SmsCallbacks *callbacks_ret,
+ char **failure_reason_ret)
+{
+ IceConn ice_conn;
+ GsmClient *client;
+ GsmIceConnectionWatch *data;
+
+ /* FIXME: what about during shutdown but before gsm_xsmp_shutdown? */
+ if (server->priv->xsmp_sockets == NULL) {
+ g_debug ("GsmXsmpServer: In shutdown, rejecting new client");
+
+ *failure_reason_ret = strdup (_("Refusing new client connection because the session is currently being shut down\n"));
+ return FALSE;
+ }
+
+ ice_conn = SmsGetIceConnection (sms_conn);
+ data = ice_conn->context;
+
+ /* Each GsmXSMPClient has its own IceConn watcher */
+ free_ice_connection_watch (data);
+
+ client = gsm_xsmp_client_new (ice_conn);
+
+ gsm_store_add (server->priv->client_store, gsm_client_peek_id (client), G_OBJECT (client));
+ /* the store will own the ref */
+ g_object_unref (client);
+
+ gsm_xsmp_client_connect (GSM_XSMP_CLIENT (client), sms_conn, mask_ret, callbacks_ret);
+
+ return TRUE;
+}
+
+static void
+ice_error_handler (IceConn conn,
+ Bool swap,
+ int offending_minor_opcode,
+ unsigned long offending_sequence,
+ int error_class,
+ int severity,
+ IcePointer values)
+{
+ g_debug ("GsmXsmpServer: ice_error_handler (%p, %s, %d, %lx, %d, %d)",
+ conn, swap ? "TRUE" : "FALSE", offending_minor_opcode,
+ offending_sequence, error_class, severity);
+
+ if (severity == IceCanContinue) {
+ return;
+ }
+
+ /* FIXME: the ICElib docs are completely vague about what we're
+ * supposed to do in this case. Need to verify that calling
+ * IceCloseConnection() here is guaranteed to cause neither
+ * free-memory-reads nor leaks.
+ */
+ IceCloseConnection (conn);
+}
+
+static void
+ice_io_error_handler (IceConn conn)
+{
+ g_debug ("GsmXsmpServer: ice_io_error_handler (%p)", conn);
+
+ /* We don't need to do anything here; the next call to
+ * IceProcessMessages() for this connection will receive
+ * IceProcessMessagesIOError and we can handle the error there.
+ */
+}
+
+static void
+sms_error_handler (SmsConn conn,
+ Bool swap,
+ int offending_minor_opcode,
+ unsigned long offending_sequence_num,
+ int error_class,
+ int severity,
+ IcePointer values)
+{
+ g_debug ("GsmXsmpServer: sms_error_handler (%p, %s, %d, %lx, %d, %d)",
+ conn, swap ? "TRUE" : "FALSE", offending_minor_opcode,
+ offending_sequence_num, error_class, severity);
+
+ /* We don't need to do anything here; if the connection needs to be
+ * closed, libSM will do that itself.
+ */
+}
+
+static IceAuthFileEntry *
+auth_entry_new (const char *protocol,
+ const char *network_id)
+{
+ IceAuthFileEntry *file_entry;
+ IceAuthDataEntry data_entry;
+
+ file_entry = malloc (sizeof (IceAuthFileEntry));
+
+ file_entry->protocol_name = strdup (protocol);
+ file_entry->protocol_data = NULL;
+ file_entry->protocol_data_length = 0;
+ file_entry->network_id = strdup (network_id);
+ file_entry->auth_name = strdup (GSM_ICE_MAGIC_COOKIE_AUTH_NAME);
+ file_entry->auth_data = IceGenerateMagicCookie (GSM_ICE_MAGIC_COOKIE_LEN);
+ file_entry->auth_data_length = GSM_ICE_MAGIC_COOKIE_LEN;
+
+ /* Also create an in-memory copy, which is what the server will
+ * actually use for checking client auth.
+ */
+ data_entry.protocol_name = file_entry->protocol_name;
+ data_entry.network_id = file_entry->network_id;
+ data_entry.auth_name = file_entry->auth_name;
+ data_entry.auth_data = file_entry->auth_data;
+ data_entry.auth_data_length = file_entry->auth_data_length;
+ IceSetPaAuthData (1, &data_entry);
+
+ return file_entry;
+}
+
+static gboolean
+update_iceauthority (GsmXsmpServer *server,
+ gboolean adding)
+{
+ char *filename;
+ char **our_network_ids;
+ FILE *fp;
+ IceAuthFileEntry *auth_entry;
+ GSList *entries;
+ GSList *e;
+ int i;
+ gboolean ok = FALSE;
+
+ filename = IceAuthFileName ();
+ if (IceLockAuthFile (filename,
+ GSM_ICE_AUTH_RETRIES,
+ GSM_ICE_AUTH_INTERVAL,
+ GSM_ICE_AUTH_LOCK_TIMEOUT) != IceAuthLockSuccess) {
+ return FALSE;
+ }
+
+ our_network_ids = g_malloc (server->priv->num_local_xsmp_sockets * sizeof (char *));
+ for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) {
+ our_network_ids[i] = IceGetListenConnectionString (server->priv->xsmp_sockets[i]);
+ }
+
+ entries = NULL;
+
+ fp = fopen (filename, "r+");
+ if (fp != NULL) {
+ while ((auth_entry = IceReadAuthFileEntry (fp)) != NULL) {
+ /* Skip/delete entries with no network ID (invalid), or with
+ * our network ID; if we're starting up, an entry with our
+ * ID must be a stale entry left behind by an old process,
+ * and if we're shutting down, it won't be valid in the
+ * future, so either way we want to remove it from the list.
+ */
+ if (!auth_entry->network_id) {
+ IceFreeAuthFileEntry (auth_entry);
+ continue;
+ }
+
+ for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) {
+ if (!strcmp (auth_entry->network_id, our_network_ids[i])) {
+ IceFreeAuthFileEntry (auth_entry);
+ break;
+ }
+ }
+ if (i != server->priv->num_local_xsmp_sockets) {
+ continue;
+ }
+
+ entries = g_slist_prepend (entries, auth_entry);
+ }
+
+ rewind (fp);
+ } else {
+ int fd;
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ g_warning ("Unable to read ICE authority file: %s", filename);
+ goto cleanup;
+ }
+
+ fd = open (filename, O_CREAT | O_WRONLY, 0600);
+ fp = fdopen (fd, "w");
+ if (!fp) {
+ g_warning ("Unable to write to ICE authority file: %s", filename);
+ if (fd != -1) {
+ close (fd);
+ }
+ goto cleanup;
+ }
+ }
+
+ if (adding) {
+ for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) {
+ entries = g_slist_append (entries,
+ auth_entry_new ("ICE", our_network_ids[i]));
+ entries = g_slist_prepend (entries,
+ auth_entry_new ("XSMP", our_network_ids[i]));
+ }
+ }
+
+ for (e = entries; e; e = e->next) {
+ IceAuthFileEntry *auth_entry = e->data;
+ IceWriteAuthFileEntry (fp, auth_entry);
+ IceFreeAuthFileEntry (auth_entry);
+ }
+ g_slist_free (entries);
+
+ fclose (fp);
+ ok = TRUE;
+
+ cleanup:
+ IceUnlockAuthFile (filename);
+ for (i = 0; i < server->priv->num_local_xsmp_sockets; i++) {
+ free (our_network_ids[i]);
+ }
+ g_free (our_network_ids);
+
+ return ok;
+}
+
+
+static void
+setup_listener (GsmXsmpServer *server)
+{
+ char error[256];
+ mode_t saved_umask;
+ char *network_id_list;
+ int i;
+ int res;
+
+ /* Set up sane error handlers */
+ IceSetErrorHandler (ice_error_handler);
+ IceSetIOErrorHandler (ice_io_error_handler);
+ SmsSetErrorHandler (sms_error_handler);
+
+ /* Initialize libSM; we pass NULL for hostBasedAuthProc to disable
+ * host-based authentication.
+ */
+ res = SmsInitialize (PACKAGE,
+ VERSION,
+ (SmsNewClientProc)accept_xsmp_connection,
+ server,
+ NULL,
+ sizeof (error),
+ error);
+ if (! res) {
+ gsm_util_init_error (TRUE, "Could not initialize libSM: %s", error);
+ }
+
+#ifdef HAVE_X11_XTRANS_XTRANS_H
+ /* By default, IceListenForConnections will open one socket for each
+ * transport type known to X. We don't want connections from remote
+ * hosts, so for security reasons it would be best if ICE didn't
+ * even open any non-local sockets. So we use an internal ICElib
+ * method to disable them here. Unfortunately, there is no way to
+ * ask X what transport types it knows about, so we're forced to
+ * guess.
+ */
+ _IceTransNoListen ("tcp");
+#endif
+
+ /* Create the XSMP socket. Older versions of IceListenForConnections
+ * have a bug which causes the umask to be set to 0 on certain types
+ * of failures. Probably not an issue on any modern systems, but
+ * we'll play it safe.
+ */
+ saved_umask = umask (0);
+ umask (saved_umask);
+ res = IceListenForConnections (&server->priv->num_xsmp_sockets,
+ &server->priv->xsmp_sockets,
+ sizeof (error),
+ error);
+ if (! res) {
+ gsm_util_init_error (TRUE, _("Could not create ICE listening socket: %s"), error);
+ }
+
+ umask (saved_umask);
+
+ /* Find the local sockets in the returned socket list and move them
+ * to the start of the list.
+ */
+ for (i = server->priv->num_local_xsmp_sockets = 0; i < server->priv->num_xsmp_sockets; i++) {
+ char *id = IceGetListenConnectionString (server->priv->xsmp_sockets[i]);
+
+ if (!strncmp (id, "local/", sizeof ("local/") - 1) ||
+ !strncmp (id, "unix/", sizeof ("unix/") - 1)) {
+ if (i > server->priv->num_local_xsmp_sockets) {
+ IceListenObj tmp;
+ tmp = server->priv->xsmp_sockets[i];
+ server->priv->xsmp_sockets[i] = server->priv->xsmp_sockets[server->priv->num_local_xsmp_sockets];
+ server->priv->xsmp_sockets[server->priv->num_local_xsmp_sockets] = tmp;
+ }
+ server->priv->num_local_xsmp_sockets++;
+ }
+ free (id);
+ }
+
+ if (server->priv->num_local_xsmp_sockets == 0) {
+ gsm_util_init_error (TRUE, "IceListenForConnections did not return a local listener!");
+ }
+
+#ifdef HAVE_X11_XTRANS_XTRANS_H
+ if (server->priv->num_local_xsmp_sockets != server->priv->num_xsmp_sockets) {
+ /* Xtrans was apparently compiled with support for some
+ * non-local transport besides TCP (which we disabled above); we
+ * won't create IO watches on those extra sockets, so
+ * connections to them will never be noticed, but they're still
+ * there, which is inelegant.
+ *
+ * If the g_warning below is triggering for you and you want to
+ * stop it, the fix is to add additional _IceTransNoListen()
+ * calls above.
+ */
+ network_id_list = IceComposeNetworkIdList (server->priv->num_xsmp_sockets - server->priv->num_local_xsmp_sockets,
+ server->priv->xsmp_sockets + server->priv->num_local_xsmp_sockets);
+ g_warning ("IceListenForConnections returned %d non-local listeners: %s",
+ server->priv->num_xsmp_sockets - server->priv->num_local_xsmp_sockets,
+ network_id_list);
+ free (network_id_list);
+ }
+#endif
+
+ /* Update .ICEauthority with new auth entries for our socket */
+ if (!update_iceauthority (server, TRUE)) {
+ /* FIXME: is this really fatal? Hm... */
+ gsm_util_init_error (TRUE,
+ "Could not update ICEauthority file %s",
+ IceAuthFileName ());
+ }
+
+ network_id_list = IceComposeNetworkIdList (server->priv->num_local_xsmp_sockets,
+ server->priv->xsmp_sockets);
+
+ gsm_util_setenv ("SESSION_MANAGER", network_id_list);
+ g_debug ("GsmXsmpServer: SESSION_MANAGER=%s\n", network_id_list);
+ free (network_id_list);
+}
+
+static GObject *
+gsm_xsmp_server_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GsmXsmpServer *xsmp_server;
+
+ xsmp_server = GSM_XSMP_SERVER (G_OBJECT_CLASS (gsm_xsmp_server_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+ setup_listener (xsmp_server);
+
+ return G_OBJECT (xsmp_server);
+}
+
+static void
+gsm_xsmp_server_class_init (GsmXsmpServerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gsm_xsmp_server_get_property;
+ object_class->set_property = gsm_xsmp_server_set_property;
+ object_class->constructor = gsm_xsmp_server_constructor;
+ object_class->finalize = gsm_xsmp_server_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_CLIENT_STORE,
+ g_param_spec_object ("client-store",
+ NULL,
+ NULL,
+ GSM_TYPE_STORE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (klass, sizeof (GsmXsmpServerPrivate));
+}
+
+static void
+gsm_xsmp_server_init (GsmXsmpServer *xsmp_server)
+{
+ xsmp_server->priv = GSM_XSMP_SERVER_GET_PRIVATE (xsmp_server);
+
+}
+
+static void
+gsm_xsmp_server_finalize (GObject *object)
+{
+ GsmXsmpServer *xsmp_server;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSM_IS_XSMP_SERVER (object));
+
+ xsmp_server = GSM_XSMP_SERVER (object);
+
+ g_return_if_fail (xsmp_server->priv != NULL);
+
+ IceFreeListenObjs (xsmp_server->priv->num_xsmp_sockets,
+ xsmp_server->priv->xsmp_sockets);
+
+ if (xsmp_server->priv->client_store != NULL) {
+ g_object_unref (xsmp_server->priv->client_store);
+ }
+
+ G_OBJECT_CLASS (gsm_xsmp_server_parent_class)->finalize (object);
+}
+
+GsmXsmpServer *
+gsm_xsmp_server_new (GsmStore *client_store)
+{
+ if (xsmp_server_object != NULL) {
+ g_object_ref (xsmp_server_object);
+ } else {
+ xsmp_server_object = g_object_new (GSM_TYPE_XSMP_SERVER,
+ "client-store", client_store,
+ NULL);
+
+ g_object_add_weak_pointer (xsmp_server_object,
+ (gpointer *) &xsmp_server_object);
+ }
+
+ return GSM_XSMP_SERVER (xsmp_server_object);
+}