877 lines
29 KiB
C
877 lines
29 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright (C) 1998, 1999, 2000 Martin K, Petersen <mkp@mkp.net>
|
|
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
#ifdef HAVE_SYS_SOCKIO_H
|
|
#include <sys/sockio.h>
|
|
#endif
|
|
|
|
#ifdef ENABLE_X11_SUPPORT
|
|
#include <X11/Xmd.h>
|
|
#include <X11/Xdmcp.h>
|
|
#endif
|
|
|
|
#include <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib-object.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "gdm-address.h"
|
|
#include "gdm-chooser-host.h"
|
|
#include "gdm-host-chooser-widget.h"
|
|
|
|
struct _GdmHostChooserWidget
|
|
{
|
|
GtkBox parent;
|
|
|
|
GtkWidget *treeview;
|
|
|
|
int kind_mask;
|
|
|
|
char **hosts;
|
|
|
|
XdmcpBuffer broadcast_buf;
|
|
XdmcpBuffer query_buf;
|
|
gboolean have_ipv6;
|
|
int socket_fd;
|
|
guint io_watch_id;
|
|
guint scan_time_id;
|
|
guint ping_try_id;
|
|
|
|
int ping_tries;
|
|
|
|
GSList *broadcast_addresses;
|
|
GSList *query_addresses;
|
|
GSList *chooser_hosts;
|
|
|
|
GdmChooserHost *current_host;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_KIND_MASK,
|
|
};
|
|
|
|
enum {
|
|
HOST_ACTIVATED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals [LAST_SIGNAL] = { 0, };
|
|
|
|
static void gdm_host_chooser_widget_class_init (GdmHostChooserWidgetClass *klass);
|
|
static void gdm_host_chooser_widget_init (GdmHostChooserWidget *host_chooser_widget);
|
|
|
|
G_DEFINE_TYPE (GdmHostChooserWidget, gdm_host_chooser_widget, GTK_TYPE_BOX)
|
|
|
|
#define GDM_XDMCP_PROTOCOL_VERSION 1001
|
|
#define SCAN_TIMEOUT 30
|
|
#define PING_TIMEOUT 2
|
|
#define PING_TRIES 3
|
|
|
|
enum {
|
|
CHOOSER_LIST_ICON_COLUMN = 0,
|
|
CHOOSER_LIST_LABEL_COLUMN,
|
|
CHOOSER_LIST_HOST_COLUMN
|
|
};
|
|
|
|
static void
|
|
chooser_host_add (GdmHostChooserWidget *widget,
|
|
GdmChooserHost *host)
|
|
{
|
|
widget->chooser_hosts = g_slist_prepend (widget->chooser_hosts, host);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
chooser_host_remove (GdmHostChooserWidget *widget,
|
|
GdmChooserHost *host)
|
|
{
|
|
widget->chooser_hosts = g_slist_remove (widget->chooser_hosts, host);
|
|
}
|
|
#endif
|
|
|
|
static gboolean
|
|
address_hostnames_equal (GdmAddress *address,
|
|
GdmAddress *other_address)
|
|
{
|
|
char *hostname, *other_hostname;
|
|
gboolean are_equal;
|
|
|
|
if (gdm_address_equal (address, other_address)) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!gdm_address_get_hostname (address, &hostname)) {
|
|
gdm_address_get_numeric_info (address, &hostname, NULL);
|
|
}
|
|
|
|
if (!gdm_address_get_hostname (other_address, &other_hostname)) {
|
|
gdm_address_get_numeric_info (other_address, &other_hostname, NULL);
|
|
}
|
|
|
|
are_equal = g_strcmp0 (hostname, other_hostname) == 0;
|
|
|
|
g_free (hostname);
|
|
g_free (other_hostname);
|
|
|
|
return are_equal;
|
|
}
|
|
|
|
static GdmChooserHost *
|
|
find_known_host (GdmHostChooserWidget *widget,
|
|
GdmAddress *address)
|
|
{
|
|
GSList *li;
|
|
GdmChooserHost *host;
|
|
|
|
for (li = widget->chooser_hosts; li != NULL; li = li->next) {
|
|
GdmAddress *other_address;
|
|
|
|
host = li->data;
|
|
|
|
other_address = gdm_chooser_host_get_address (host);
|
|
|
|
if (address_hostnames_equal (address, other_address)) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
host = NULL;
|
|
out:
|
|
|
|
return host;
|
|
}
|
|
|
|
static void
|
|
browser_add_host (GdmHostChooserWidget *widget,
|
|
GdmChooserHost *host)
|
|
{
|
|
char *hostname;
|
|
char *name;
|
|
char *desc;
|
|
char *label;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
gboolean res;
|
|
|
|
GtkTreeSelection *selection;
|
|
|
|
g_assert (host != NULL);
|
|
|
|
if (! gdm_chooser_host_get_willing (host)) {
|
|
gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE);
|
|
return;
|
|
}
|
|
|
|
res = gdm_address_get_hostname (gdm_chooser_host_get_address (host), &hostname);
|
|
if (! res) {
|
|
gdm_address_get_numeric_info (gdm_chooser_host_get_address (host), &hostname, NULL);
|
|
}
|
|
|
|
name = g_markup_escape_text (hostname, -1);
|
|
desc = g_markup_escape_text (gdm_chooser_host_get_description (host), -1);
|
|
label = g_strdup_printf ("<b>%s</b>\n%s", name, desc);
|
|
g_free (name);
|
|
g_free (desc);
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->treeview));
|
|
|
|
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
|
|
gtk_list_store_set (GTK_LIST_STORE (model),
|
|
&iter,
|
|
CHOOSER_LIST_ICON_COLUMN, NULL,
|
|
CHOOSER_LIST_LABEL_COLUMN, label,
|
|
CHOOSER_LIST_HOST_COLUMN, host,
|
|
-1);
|
|
g_free (label);
|
|
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->treeview));
|
|
if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) {
|
|
gtk_tree_selection_select_iter (selection, &iter);
|
|
}
|
|
|
|
}
|
|
|
|
static gboolean
|
|
decode_packet (GIOChannel *source,
|
|
GIOCondition condition,
|
|
GdmHostChooserWidget *widget)
|
|
{
|
|
struct sockaddr_storage clnt_ss;
|
|
GdmAddress *address;
|
|
int ss_len;
|
|
XdmcpHeader header;
|
|
int res;
|
|
static XdmcpBuffer buf;
|
|
ARRAY8 auth = {0};
|
|
ARRAY8 host = {0};
|
|
ARRAY8 stat = {0};
|
|
char *status;
|
|
GdmChooserHost *chooser_host;
|
|
|
|
status = NULL;
|
|
address = NULL;
|
|
|
|
g_debug ("decode_packet: GIOCondition %d", (int)condition);
|
|
|
|
if ( ! (condition & G_IO_IN)) {
|
|
return TRUE;
|
|
}
|
|
|
|
ss_len = (int) sizeof (clnt_ss);
|
|
|
|
res = XdmcpFill (widget->socket_fd, &buf, (XdmcpNetaddr)&clnt_ss, &ss_len);
|
|
if G_UNLIKELY (! res) {
|
|
g_debug (_("XDMCP: Could not create XDMCP buffer!"));
|
|
return TRUE;
|
|
}
|
|
|
|
res = XdmcpReadHeader (&buf, &header);
|
|
if G_UNLIKELY (! res) {
|
|
g_warning (_("XDMCP: Could not read XDMCP header!"));
|
|
return TRUE;
|
|
}
|
|
|
|
if G_UNLIKELY (header.version != XDM_PROTOCOL_VERSION &&
|
|
header.version != GDM_XDMCP_PROTOCOL_VERSION) {
|
|
g_warning (_("XDMCP: Incorrect XDMCP version!"));
|
|
return TRUE;
|
|
}
|
|
|
|
address = gdm_address_new_from_sockaddr ((struct sockaddr *) &clnt_ss, ss_len);
|
|
if (address == NULL) {
|
|
g_warning (_("XDMCP: Unable to parse address"));
|
|
return TRUE;
|
|
}
|
|
|
|
gdm_address_debug (address);
|
|
|
|
if (header.opcode == WILLING) {
|
|
if (! XdmcpReadARRAY8 (&buf, &auth)) {
|
|
goto done;
|
|
}
|
|
|
|
if (! XdmcpReadARRAY8 (&buf, &host)) {
|
|
goto done;
|
|
}
|
|
|
|
if (! XdmcpReadARRAY8 (&buf, &stat)) {
|
|
goto done;
|
|
}
|
|
|
|
status = g_strndup ((char *) stat.data, MIN (stat.length, 256));
|
|
} else if (header.opcode == UNWILLING) {
|
|
/* immaterial, will not be shown */
|
|
status = NULL;
|
|
} else {
|
|
goto done;
|
|
}
|
|
|
|
g_debug ("STATUS: %s", status);
|
|
|
|
chooser_host = find_known_host (widget, address);
|
|
if (chooser_host == NULL) {
|
|
chooser_host = g_object_new (GDM_TYPE_CHOOSER_HOST,
|
|
"address", address,
|
|
"description", status,
|
|
"willing", (header.opcode == WILLING),
|
|
"kind", GDM_CHOOSER_HOST_KIND_XDMCP,
|
|
NULL);
|
|
chooser_host_add (widget, chooser_host);
|
|
browser_add_host (widget, chooser_host);
|
|
} else {
|
|
/* server changed it's mind */
|
|
if (header.opcode == WILLING
|
|
&& ! gdm_chooser_host_get_willing (chooser_host)) {
|
|
browser_add_host (widget, chooser_host);
|
|
g_object_set (chooser_host, "willing", TRUE, NULL);
|
|
}
|
|
/* FIXME: handle unwilling? */
|
|
}
|
|
|
|
done:
|
|
if (header.opcode == WILLING) {
|
|
XdmcpDisposeARRAY8 (&auth);
|
|
XdmcpDisposeARRAY8 (&host);
|
|
XdmcpDisposeARRAY8 (&stat);
|
|
}
|
|
|
|
g_free (status);
|
|
gdm_address_free (address);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
do_ping (GdmHostChooserWidget *widget,
|
|
gboolean full)
|
|
{
|
|
GSList *l;
|
|
|
|
g_debug ("do ping full:%d", full);
|
|
|
|
for (l = widget->broadcast_addresses; l != NULL; l = l->next) {
|
|
GdmAddress *address;
|
|
int res;
|
|
|
|
address = l->data;
|
|
|
|
gdm_address_debug (address);
|
|
errno = 0;
|
|
g_debug ("fd:%d", widget->socket_fd);
|
|
res = XdmcpFlush (widget->socket_fd,
|
|
&widget->broadcast_buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
|
|
if (! res) {
|
|
g_warning ("Unable to flush the XDMCP broadcast packet: %s", g_strerror (errno));
|
|
}
|
|
}
|
|
|
|
if (full) {
|
|
for (l = widget->query_addresses; l != NULL; l = l->next) {
|
|
GdmAddress *address;
|
|
int res;
|
|
|
|
address = l->data;
|
|
|
|
gdm_address_debug (address);
|
|
res = XdmcpFlush (widget->socket_fd,
|
|
&widget->query_buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
if (! res) {
|
|
g_warning ("Unable to flush the XDMCP query packet");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
ping_try (GdmHostChooserWidget *widget)
|
|
{
|
|
do_ping (widget, FALSE);
|
|
|
|
widget->ping_tries --;
|
|
|
|
if (widget->ping_tries <= 0) {
|
|
widget->ping_try_id = 0;
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
xdmcp_discover (GdmHostChooserWidget *widget)
|
|
{
|
|
#if 0
|
|
gtk_widget_set_sensitive (GTK_WIDGET (manage), FALSE);
|
|
gtk_widget_set_sensitive (GTK_WIDGET (rescan), FALSE);
|
|
gtk_list_store_clear (GTK_LIST_STORE (browser_model));
|
|
gtk_widget_set_sensitive (GTK_WIDGET (browser), FALSE);
|
|
gtk_label_set_label (GTK_LABEL (status_label),
|
|
_(scanning_message));
|
|
|
|
while (hl) {
|
|
gdm_chooser_host_dispose ((GdmChooserHost *) hl->data);
|
|
hl = hl->next;
|
|
}
|
|
|
|
g_list_free (chooser_hosts);
|
|
chooser_hosts = NULL;
|
|
#endif
|
|
|
|
do_ping (widget, TRUE);
|
|
|
|
#if 0
|
|
g_clear_handle_id (&widget->scan_time_id, g_source_remove);
|
|
|
|
widget->scan_time_id = g_timeout_add_seconds (SCAN_TIMEOUT,
|
|
chooser_scan_time_update,
|
|
widget);
|
|
#endif
|
|
/* Note we already used up one try */
|
|
widget->ping_tries = PING_TRIES - 1;
|
|
g_clear_handle_id (&widget->ping_try_id, g_source_remove);
|
|
|
|
widget->ping_try_id = g_timeout_add_seconds (PING_TIMEOUT,
|
|
(GSourceFunc)ping_try,
|
|
widget);
|
|
}
|
|
|
|
/* Find broadcast address for all active, non pointopoint interfaces */
|
|
static void
|
|
find_broadcast_addresses (GdmHostChooserWidget *widget)
|
|
{
|
|
int i;
|
|
int num;
|
|
int sock;
|
|
struct ifconf ifc;
|
|
char *buf;
|
|
struct ifreq *ifr;
|
|
|
|
g_debug ("Finding broadcast addresses");
|
|
|
|
sock = socket (AF_INET, SOCK_DGRAM, 0);
|
|
#ifdef SIOCGIFNUM
|
|
if (ioctl (sock, SIOCGIFNUM, &num) < 0) {
|
|
num = 64;
|
|
}
|
|
#else
|
|
num = 64;
|
|
#endif
|
|
|
|
ifc.ifc_len = sizeof (struct ifreq) * num;
|
|
ifc.ifc_buf = buf = g_malloc0 (ifc.ifc_len);
|
|
if (ioctl (sock, SIOCGIFCONF, &ifc) < 0) {
|
|
g_warning ("Could not get local addresses!");
|
|
goto out;
|
|
}
|
|
|
|
ifr = ifc.ifc_req;
|
|
num = ifc.ifc_len / sizeof (struct ifreq);
|
|
for (i = 0 ; i < num ; i++) {
|
|
const char *name;
|
|
|
|
name = ifr[i].ifr_name;
|
|
g_debug ("Checking if %s", name);
|
|
if (name != NULL && name[0] != '\0') {
|
|
struct ifreq ifreq;
|
|
GdmAddress *address;
|
|
struct sockaddr_in sin;
|
|
|
|
memset (&ifreq, 0, sizeof (ifreq));
|
|
|
|
strncpy (ifreq.ifr_name,
|
|
ifr[i].ifr_name,
|
|
sizeof (ifreq.ifr_name));
|
|
/* paranoia */
|
|
ifreq.ifr_name[sizeof (ifreq.ifr_name) - 1] = '\0';
|
|
|
|
if ((ioctl (sock, SIOCGIFFLAGS, &ifreq) < 0) && (errno != ENXIO)) {
|
|
g_warning ("Could not get SIOCGIFFLAGS for %s", ifr[i].ifr_name);
|
|
}
|
|
|
|
if ((ifreq.ifr_flags & IFF_UP) == 0 ||
|
|
(ifreq.ifr_flags & IFF_BROADCAST) == 0 ||
|
|
ioctl (sock, SIOCGIFBRDADDR, &ifreq) < 0) {
|
|
g_debug ("Skipping if %s", name);
|
|
continue;
|
|
}
|
|
|
|
memcpy (&sin, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
|
|
sin.sin_port = htons (XDM_UDP_PORT);
|
|
address = gdm_address_new_from_sockaddr ((struct sockaddr *) &sin, sizeof (sin));
|
|
if (address != NULL) {
|
|
g_debug ("Adding if %s", name);
|
|
gdm_address_debug (address);
|
|
|
|
widget->broadcast_addresses = g_slist_append (widget->broadcast_addresses, address);
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
g_free (buf);
|
|
close (sock);
|
|
}
|
|
|
|
static void
|
|
add_hosts (GdmHostChooserWidget *widget)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; widget->hosts != NULL && widget->hosts[i] != NULL; i++) {
|
|
struct addrinfo hints;
|
|
struct addrinfo *result;
|
|
struct addrinfo *ai;
|
|
int gaierr;
|
|
const char *name;
|
|
char serv_buf [NI_MAXSERV];
|
|
const char *serv;
|
|
|
|
name = widget->hosts[i];
|
|
|
|
if (strcmp (name, "BROADCAST") == 0) {
|
|
find_broadcast_addresses (widget);
|
|
continue;
|
|
}
|
|
|
|
if (strcmp (name, "MULTICAST") == 0) {
|
|
/*gdm_chooser_find_mcaddr ();*/
|
|
continue;
|
|
}
|
|
|
|
result = NULL;
|
|
memset (&hints, 0, sizeof (hints));
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
snprintf (serv_buf, sizeof (serv_buf), "%u", XDM_UDP_PORT);
|
|
serv = serv_buf;
|
|
|
|
gaierr = getaddrinfo (name, serv, &hints, &result);
|
|
if (gaierr != 0) {
|
|
g_warning ("Unable to get address info for name %s: %s", name, gai_strerror (gaierr));
|
|
continue;
|
|
}
|
|
|
|
for (ai = result; ai != NULL; ai = ai->ai_next) {
|
|
GdmAddress *address;
|
|
|
|
address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
|
|
if (address != NULL) {
|
|
widget->query_addresses = g_slist_append (widget->query_addresses, address);
|
|
}
|
|
}
|
|
|
|
freeaddrinfo (result);
|
|
}
|
|
|
|
if (widget->broadcast_addresses == NULL && widget->query_addresses == NULL) {
|
|
find_broadcast_addresses (widget);
|
|
}
|
|
}
|
|
|
|
static void
|
|
xdmcp_init (GdmHostChooserWidget *widget)
|
|
{
|
|
XdmcpHeader header;
|
|
int sockopts;
|
|
int res;
|
|
GIOChannel *ioc;
|
|
ARRAYofARRAY8 aanames;
|
|
|
|
sockopts = 1;
|
|
|
|
widget->socket_fd = -1;
|
|
|
|
/* Open socket for communication */
|
|
#ifdef ENABLE_IPV6
|
|
widget->socket_fd = socket (AF_INET6, SOCK_DGRAM, 0);
|
|
if (widget->socket_fd != -1) {
|
|
widget->have_ipv6 = TRUE;
|
|
#ifdef IPV6_V6ONLY
|
|
{
|
|
int zero = 0;
|
|
if (setsockopt(widget->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) < 0)
|
|
g_warning("setsockopt(IPV6_V6ONLY): %s", g_strerror(errno));
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
if (! widget->have_ipv6) {
|
|
widget->socket_fd = socket (AF_INET, SOCK_DGRAM, 0);
|
|
if (widget->socket_fd == -1) {
|
|
g_critical ("Could not create socket!");
|
|
}
|
|
}
|
|
|
|
res = setsockopt (widget->socket_fd,
|
|
SOL_SOCKET,
|
|
SO_BROADCAST,
|
|
(char *) &sockopts,
|
|
sizeof (sockopts));
|
|
if (res < 0) {
|
|
g_critical ("Could not set socket options!");
|
|
}
|
|
|
|
/* Assemble XDMCP BROADCAST_QUERY packet in static buffer */
|
|
memset (&header, 0, sizeof (XdmcpHeader));
|
|
header.opcode = (CARD16) BROADCAST_QUERY;
|
|
header.length = 1;
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
aanames.length = 0;
|
|
XdmcpWriteHeader (&widget->broadcast_buf, &header);
|
|
XdmcpWriteARRAYofARRAY8 (&widget->broadcast_buf, &aanames);
|
|
|
|
/* Assemble XDMCP QUERY packet in static buffer */
|
|
memset (&header, 0, sizeof (XdmcpHeader));
|
|
header.opcode = (CARD16) QUERY;
|
|
header.length = 1;
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
memset (&widget->query_buf, 0, sizeof (XdmcpBuffer));
|
|
XdmcpWriteHeader (&widget->query_buf, &header);
|
|
XdmcpWriteARRAYofARRAY8 (&widget->query_buf, &aanames);
|
|
|
|
add_hosts (widget);
|
|
|
|
ioc = g_io_channel_unix_new (widget->socket_fd);
|
|
g_io_channel_set_encoding (ioc, NULL, NULL);
|
|
g_io_channel_set_buffered (ioc, FALSE);
|
|
widget->io_watch_id = g_io_add_watch (ioc,
|
|
G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
|
|
(GIOFunc)decode_packet,
|
|
widget);
|
|
g_io_channel_unref (ioc);
|
|
}
|
|
|
|
void
|
|
gdm_host_chooser_widget_refresh (GdmHostChooserWidget *widget)
|
|
{
|
|
g_return_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (widget));
|
|
|
|
xdmcp_discover (widget);
|
|
}
|
|
|
|
GdmChooserHost *
|
|
gdm_host_chooser_widget_get_host (GdmHostChooserWidget *widget)
|
|
{
|
|
GdmChooserHost *host;
|
|
|
|
g_return_val_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (widget), NULL);
|
|
|
|
host = NULL;
|
|
if (widget->current_host != NULL) {
|
|
host = g_object_ref (widget->current_host);
|
|
}
|
|
|
|
return host;
|
|
}
|
|
|
|
static void
|
|
_gdm_host_chooser_widget_set_kind_mask (GdmHostChooserWidget *widget,
|
|
int kind_mask)
|
|
{
|
|
if (widget->kind_mask != kind_mask) {
|
|
widget->kind_mask = kind_mask;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdm_host_chooser_widget_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdmHostChooserWidget *self;
|
|
|
|
self = GDM_HOST_CHOOSER_WIDGET (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_KIND_MASK:
|
|
_gdm_host_chooser_widget_set_kind_mask (self, g_value_get_int (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdm_host_chooser_widget_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (prop_id) {
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GObject *
|
|
gdm_host_chooser_widget_constructor (GType type,
|
|
guint n_construct_properties,
|
|
GObjectConstructParam *construct_properties)
|
|
{
|
|
GdmHostChooserWidget *widget;
|
|
|
|
widget = GDM_HOST_CHOOSER_WIDGET (G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->constructor (type,
|
|
n_construct_properties,
|
|
construct_properties));
|
|
|
|
xdmcp_init (widget);
|
|
xdmcp_discover (widget);
|
|
|
|
return G_OBJECT (widget);
|
|
}
|
|
|
|
static void
|
|
gdm_host_chooser_widget_dispose (GObject *object)
|
|
{
|
|
GdmHostChooserWidget *widget;
|
|
|
|
widget = GDM_HOST_CHOOSER_WIDGET (object);
|
|
|
|
g_debug ("Disposing host_chooser_widget");
|
|
|
|
if (widget->broadcast_addresses != NULL) {
|
|
g_slist_foreach (widget->broadcast_addresses,
|
|
(GFunc)gdm_address_free,
|
|
NULL);
|
|
g_slist_free (widget->broadcast_addresses);
|
|
widget->broadcast_addresses = NULL;
|
|
}
|
|
if (widget->query_addresses != NULL) {
|
|
g_slist_foreach (widget->query_addresses,
|
|
(GFunc)gdm_address_free,
|
|
NULL);
|
|
g_slist_free (widget->query_addresses);
|
|
widget->query_addresses = NULL;
|
|
}
|
|
if (widget->chooser_hosts != NULL) {
|
|
g_slist_foreach (widget->chooser_hosts,
|
|
(GFunc)g_object_unref,
|
|
NULL);
|
|
g_slist_free (widget->chooser_hosts);
|
|
widget->chooser_hosts = NULL;
|
|
}
|
|
|
|
widget->current_host = NULL;
|
|
|
|
G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gdm_host_chooser_widget_class_init (GdmHostChooserWidgetClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->get_property = gdm_host_chooser_widget_get_property;
|
|
object_class->set_property = gdm_host_chooser_widget_set_property;
|
|
object_class->constructor = gdm_host_chooser_widget_constructor;
|
|
object_class->dispose = gdm_host_chooser_widget_dispose;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_KIND_MASK,
|
|
g_param_spec_int ("kind-mask",
|
|
"kind mask",
|
|
"kind mask",
|
|
0,
|
|
G_MAXINT,
|
|
0,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
|
|
signals [HOST_ACTIVATED] = g_signal_new ("host-activated",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE,
|
|
0);
|
|
}
|
|
|
|
static void
|
|
on_host_selected (GtkTreeSelection *selection,
|
|
GdmHostChooserWidget *widget)
|
|
{
|
|
GtkTreeModel *model = NULL;
|
|
GtkTreeIter iter = {0};
|
|
GdmChooserHost *curhost;
|
|
|
|
curhost = NULL;
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
|
|
gtk_tree_model_get (model, &iter, CHOOSER_LIST_HOST_COLUMN, &curhost, -1);
|
|
}
|
|
|
|
widget->current_host = curhost;
|
|
}
|
|
|
|
static void
|
|
on_row_activated (GtkTreeView *tree_view,
|
|
GtkTreePath *tree_path,
|
|
GtkTreeViewColumn *tree_column,
|
|
GdmHostChooserWidget *widget)
|
|
{
|
|
g_signal_emit (widget, signals[HOST_ACTIVATED], 0);
|
|
}
|
|
|
|
static void
|
|
gdm_host_chooser_widget_init (GdmHostChooserWidget *widget)
|
|
{
|
|
GtkWidget *scrolled;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeViewColumn *column;
|
|
GtkTreeModel *model;
|
|
|
|
scrolled = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
|
|
GTK_SHADOW_IN);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
|
|
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
|
gtk_box_pack_start (GTK_BOX (widget), scrolled, TRUE, TRUE, 0);
|
|
|
|
widget->treeview = gtk_tree_view_new ();
|
|
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget->treeview), FALSE);
|
|
g_signal_connect (widget->treeview,
|
|
"row-activated",
|
|
G_CALLBACK (on_row_activated),
|
|
widget);
|
|
gtk_container_add (GTK_CONTAINER (scrolled), widget->treeview);
|
|
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->treeview));
|
|
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
|
|
g_signal_connect (selection, "changed",
|
|
G_CALLBACK (on_host_selected),
|
|
widget);
|
|
|
|
model = (GtkTreeModel *)gtk_list_store_new (3,
|
|
GDK_TYPE_PIXBUF,
|
|
G_TYPE_STRING,
|
|
G_TYPE_POINTER);
|
|
gtk_tree_view_set_model (GTK_TREE_VIEW (widget->treeview), model);
|
|
|
|
column = gtk_tree_view_column_new_with_attributes ("Icon",
|
|
gtk_cell_renderer_pixbuf_new (),
|
|
"pixbuf", CHOOSER_LIST_ICON_COLUMN,
|
|
NULL);
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (widget->treeview), column);
|
|
|
|
column = gtk_tree_view_column_new_with_attributes ("Hostname",
|
|
gtk_cell_renderer_text_new (),
|
|
"markup", CHOOSER_LIST_LABEL_COLUMN,
|
|
NULL);
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (widget->treeview), column);
|
|
|
|
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
|
|
CHOOSER_LIST_LABEL_COLUMN,
|
|
GTK_SORT_ASCENDING);
|
|
}
|
|
|
|
GtkWidget *
|
|
gdm_host_chooser_widget_new (int kind_mask)
|
|
{
|
|
GObject *object;
|
|
|
|
object = g_object_new (GDM_TYPE_HOST_CHOOSER_WIDGET,
|
|
"orientation", GTK_ORIENTATION_VERTICAL,
|
|
"kind-mask", kind_mask,
|
|
NULL);
|
|
|
|
return GTK_WIDGET (object);
|
|
}
|