1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 1998, 1999, 2000 Martin K, Petersen <mkp@mkp.net>
4 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 */
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
33 #include <net/if.h>
34 #ifdef HAVE_SYS_SOCKIO_H
35 #include <sys/sockio.h>
36 #endif
37
38 #include <X11/Xmd.h>
39 #include <X11/Xdmcp.h>
40
41 #include <glib.h>
42 #include <glib/gi18n.h>
43 #include <glib-object.h>
44 #include <gtk/gtk.h>
45
46 #include "gdm-address.h"
47 #include "gdm-chooser-host.h"
48 #include "gdm-host-chooser-widget.h"
49
50 #define GDM_HOST_CHOOSER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_HOST_CHOOSER_WIDGET, GdmHostChooserWidgetPrivate))
51
52 struct GdmHostChooserWidgetPrivate
53 {
54 GtkWidget *treeview;
55
56 int kind_mask;
57
58 char **hosts;
59
60 XdmcpBuffer broadcast_buf;
61 XdmcpBuffer query_buf;
62 gboolean have_ipv6;
63 int socket_fd;
64 guint io_watch_id;
65 guint scan_time_id;
66 guint ping_try_id;
67
68 int ping_tries;
69
70 GSList *broadcast_addresses;
71 GSList *query_addresses;
72 GSList *chooser_hosts;
73
74 GdmChooserHost *current_host;
75 };
76
77 enum {
78 PROP_0,
79 PROP_KIND_MASK,
80 };
81
82 enum {
83 HOST_ACTIVATED,
84 LAST_SIGNAL
85 };
86
87 static guint signals [LAST_SIGNAL] = { 0, };
88
89 static void gdm_host_chooser_widget_class_init (GdmHostChooserWidgetClass *klass);
90 static void gdm_host_chooser_widget_init (GdmHostChooserWidget *host_chooser_widget);
91 static void gdm_host_chooser_widget_finalize (GObject *object);
92
93 G_DEFINE_TYPE (GdmHostChooserWidget, gdm_host_chooser_widget, GTK_TYPE_BOX)
94
95 #define GDM_XDMCP_PROTOCOL_VERSION 1001
96 #define SCAN_TIMEOUT 30
97 #define PING_TIMEOUT 2
98 #define PING_TRIES 3
99
100 enum {
101 CHOOSER_LIST_ICON_COLUMN = 0,
102 CHOOSER_LIST_LABEL_COLUMN,
103 CHOOSER_LIST_HOST_COLUMN
104 };
105
106 static void
chooser_host_add(GdmHostChooserWidget * widget,GdmChooserHost * host)107 chooser_host_add (GdmHostChooserWidget *widget,
108 GdmChooserHost *host)
109 {
110 widget->priv->chooser_hosts = g_slist_prepend (widget->priv->chooser_hosts, host);
111 }
112
113 #if 0
114 static void
115 chooser_host_remove (GdmHostChooserWidget *widget,
116 GdmChooserHost *host)
117 {
118 widget->priv->chooser_hosts = g_slist_remove (widget->priv->chooser_hosts, host);
119 }
120 #endif
121
122 static gboolean
address_hostnames_equal(GdmAddress * address,GdmAddress * other_address)123 address_hostnames_equal (GdmAddress *address,
124 GdmAddress *other_address)
125 {
126 char *hostname, *other_hostname;
127 gboolean are_equal;
128
129 if (gdm_address_equal (address, other_address)) {
130 return TRUE;
131 }
132
133 if (!gdm_address_get_hostname (address, &hostname)) {
134 gdm_address_get_numeric_info (address, &hostname, NULL);
135 }
136
137 if (!gdm_address_get_hostname (other_address, &other_hostname)) {
138 gdm_address_get_numeric_info (other_address, &other_hostname, NULL);
139 }
140
141 are_equal = g_strcmp0 (hostname, other_hostname) == 0;
142
143 g_free (hostname);
144 g_free (other_hostname);
145
146 return are_equal;
147 }
148
149 static GdmChooserHost *
find_known_host(GdmHostChooserWidget * widget,GdmAddress * address)150 find_known_host (GdmHostChooserWidget *widget,
151 GdmAddress *address)
152 {
153 GSList *li;
154 GdmChooserHost *host;
155
156 for (li = widget->priv->chooser_hosts; li != NULL; li = li->next) {
157 GdmAddress *other_address;
158
159 host = li->data;
160
161 other_address = gdm_chooser_host_get_address (host);
162
163 if (address_hostnames_equal (address, other_address)) {
164 goto out;
165 }
166 }
167
168 host = NULL;
169 out:
170
171 return host;
172 }
173
174 static void
browser_add_host(GdmHostChooserWidget * widget,GdmChooserHost * host)175 browser_add_host (GdmHostChooserWidget *widget,
176 GdmChooserHost *host)
177 {
178 char *hostname;
179 char *name;
180 char *desc;
181 char *label;
182 GtkTreeModel *model;
183 GtkTreeIter iter;
184 gboolean res;
185
186 GtkTreeSelection *selection;
187
188 g_assert (host != NULL);
189
190 if (! gdm_chooser_host_get_willing (host)) {
191 gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE);
192 return;
193 }
194
195 res = gdm_address_get_hostname (gdm_chooser_host_get_address (host), &hostname);
196 if (! res) {
197 gdm_address_get_numeric_info (gdm_chooser_host_get_address (host), &hostname, NULL);
198 }
199
200 name = g_markup_escape_text (hostname, -1);
201 desc = g_markup_escape_text (gdm_chooser_host_get_description (host), -1);
202 label = g_strdup_printf ("<b>%s</b>\n%s", name, desc);
203 g_free (name);
204 g_free (desc);
205
206 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->priv->treeview));
207
208 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
209 gtk_list_store_set (GTK_LIST_STORE (model),
210 &iter,
211 CHOOSER_LIST_ICON_COLUMN, NULL,
212 CHOOSER_LIST_LABEL_COLUMN, label,
213 CHOOSER_LIST_HOST_COLUMN, host,
214 -1);
215 g_free (label);
216
217 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->treeview));
218 if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) {
219 gtk_tree_selection_select_iter (selection, &iter);
220 }
221
222 }
223
224 static gboolean
decode_packet(GIOChannel * source,GIOCondition condition,GdmHostChooserWidget * widget)225 decode_packet (GIOChannel *source,
226 GIOCondition condition,
227 GdmHostChooserWidget *widget)
228 {
229 struct sockaddr_storage clnt_ss;
230 GdmAddress *address;
231 int ss_len;
232 XdmcpHeader header;
233 int res;
234 static XdmcpBuffer buf;
235 ARRAY8 auth = {0};
236 ARRAY8 host = {0};
237 ARRAY8 stat = {0};
238 char *status;
239 GdmChooserHost *chooser_host;
240
241 status = NULL;
242 address = NULL;
243
244 g_debug ("decode_packet: GIOCondition %d", (int)condition);
245
246 if ( ! (condition & G_IO_IN)) {
247 return TRUE;
248 }
249
250 ss_len = (int) sizeof (clnt_ss);
251
252 res = XdmcpFill (widget->priv->socket_fd, &buf, (XdmcpNetaddr)&clnt_ss, &ss_len);
253 if G_UNLIKELY (! res) {
254 g_debug (_("XDMCP: Could not create XDMCP buffer!"));
255 return TRUE;
256 }
257
258 res = XdmcpReadHeader (&buf, &header);
259 if G_UNLIKELY (! res) {
260 g_warning (_("XDMCP: Could not read XDMCP header!"));
261 return TRUE;
262 }
263
264 if G_UNLIKELY (header.version != XDM_PROTOCOL_VERSION &&
265 header.version != GDM_XDMCP_PROTOCOL_VERSION) {
266 g_warning (_("XDMCP: Incorrect XDMCP version!"));
267 return TRUE;
268 }
269
270 address = gdm_address_new_from_sockaddr ((struct sockaddr *) &clnt_ss, ss_len);
271 if (address == NULL) {
272 g_warning (_("XDMCP: Unable to parse address"));
273 return TRUE;
274 }
275
276 gdm_address_debug (address);
277
278 if (header.opcode == WILLING) {
279 if (! XdmcpReadARRAY8 (&buf, &auth)) {
280 goto done;
281 }
282
283 if (! XdmcpReadARRAY8 (&buf, &host)) {
284 goto done;
285 }
286
287 if (! XdmcpReadARRAY8 (&buf, &stat)) {
288 goto done;
289 }
290
291 status = g_strndup ((char *) stat.data, MIN (stat.length, 256));
292 } else if (header.opcode == UNWILLING) {
293 /* immaterial, will not be shown */
294 status = NULL;
295 } else {
296 goto done;
297 }
298
299 g_debug ("STATUS: %s", status);
300
301 chooser_host = find_known_host (widget, address);
302 if (chooser_host == NULL) {
303 chooser_host = g_object_new (GDM_TYPE_CHOOSER_HOST,
304 "address", address,
305 "description", status,
306 "willing", (header.opcode == WILLING),
307 "kind", GDM_CHOOSER_HOST_KIND_XDMCP,
308 NULL);
309 chooser_host_add (widget, chooser_host);
310 browser_add_host (widget, chooser_host);
311 } else {
312 /* server changed it's mind */
313 if (header.opcode == WILLING
314 && ! gdm_chooser_host_get_willing (chooser_host)) {
315 browser_add_host (widget, chooser_host);
316 g_object_set (chooser_host, "willing", TRUE, NULL);
317 }
318 /* FIXME: handle unwilling? */
319 }
320
321 done:
322 if (header.opcode == WILLING) {
323 XdmcpDisposeARRAY8 (&auth);
324 XdmcpDisposeARRAY8 (&host);
325 XdmcpDisposeARRAY8 (&stat);
326 }
327
328 g_free (status);
329 gdm_address_free (address);
330
331 return TRUE;
332 }
333
334 static void
do_ping(GdmHostChooserWidget * widget,gboolean full)335 do_ping (GdmHostChooserWidget *widget,
336 gboolean full)
337 {
338 GSList *l;
339
340 g_debug ("do ping full:%d", full);
341
342 for (l = widget->priv->broadcast_addresses; l != NULL; l = l->next) {
343 GdmAddress *address;
344 int res;
345
346 address = l->data;
347
348 gdm_address_debug (address);
349 errno = 0;
350 g_debug ("fd:%d", widget->priv->socket_fd);
351 res = XdmcpFlush (widget->priv->socket_fd,
352 &widget->priv->broadcast_buf,
353 (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
354 (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
355
356 if (! res) {
357 g_warning ("Unable to flush the XDMCP broadcast packet: %s", g_strerror (errno));
358 }
359 }
360
361 if (full) {
362 for (l = widget->priv->query_addresses; l != NULL; l = l->next) {
363 GdmAddress *address;
364 int res;
365
366 address = l->data;
367
368 gdm_address_debug (address);
369 res = XdmcpFlush (widget->priv->socket_fd,
370 &widget->priv->query_buf,
371 (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
372 (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
373 if (! res) {
374 g_warning ("Unable to flush the XDMCP query packet");
375 }
376 }
377 }
378 }
379
380 static gboolean
ping_try(GdmHostChooserWidget * widget)381 ping_try (GdmHostChooserWidget *widget)
382 {
383 do_ping (widget, FALSE);
384
385 widget->priv->ping_tries --;
386
387 if (widget->priv->ping_tries <= 0) {
388 widget->priv->ping_try_id = 0;
389 return FALSE;
390 } else {
391 return TRUE;
392 }
393 }
394
395 static void
xdmcp_discover(GdmHostChooserWidget * widget)396 xdmcp_discover (GdmHostChooserWidget *widget)
397 {
398 #if 0
399 gtk_widget_set_sensitive (GTK_WIDGET (manage), FALSE);
400 gtk_widget_set_sensitive (GTK_WIDGET (rescan), FALSE);
401 gtk_list_store_clear (GTK_LIST_STORE (browser_model));
402 gtk_widget_set_sensitive (GTK_WIDGET (browser), FALSE);
403 gtk_label_set_label (GTK_LABEL (status_label),
404 _(scanning_message));
405
406 while (hl) {
407 gdm_chooser_host_dispose ((GdmChooserHost *) hl->data);
408 hl = hl->next;
409 }
410
411 g_list_free (chooser_hosts);
412 chooser_hosts = NULL;
413 #endif
414
415 do_ping (widget, TRUE);
416
417 #if 0
418 if (widget->priv->scan_time_id > 0) {
419 g_source_remove (widget->priv->scan_time_id);
420 }
421
422 widget->priv->scan_time_id = g_timeout_add_seconds (SCAN_TIMEOUT,
423 chooser_scan_time_update,
424 widget);
425 #endif
426 /* Note we already used up one try */
427 widget->priv->ping_tries = PING_TRIES - 1;
428 if (widget->priv->ping_try_id > 0) {
429 g_source_remove (widget->priv->ping_try_id);
430 }
431
432 widget->priv->ping_try_id = g_timeout_add_seconds (PING_TIMEOUT,
433 (GSourceFunc)ping_try,
434 widget);
435 }
436
437 /* Find broadcast address for all active, non pointopoint interfaces */
438 static void
find_broadcast_addresses(GdmHostChooserWidget * widget)439 find_broadcast_addresses (GdmHostChooserWidget *widget)
440 {
441 int i;
442 int num;
443 int sock;
444 struct ifconf ifc;
445 char *buf;
446 struct ifreq *ifr;
447
448 g_debug ("Finding broadcast addresses");
449
450 sock = socket (AF_INET, SOCK_DGRAM, 0);
451 #ifdef SIOCGIFNUM
452 if (ioctl (sock, SIOCGIFNUM, &num) < 0) {
453 num = 64;
454 }
455 #else
456 num = 64;
457 #endif
458
459 ifc.ifc_len = sizeof (struct ifreq) * num;
460 ifc.ifc_buf = buf = g_malloc0 (ifc.ifc_len);
461 if (ioctl (sock, SIOCGIFCONF, &ifc) < 0) {
462 g_warning ("Could not get local addresses!");
463 goto out;
464 }
465
466 ifr = ifc.ifc_req;
467 num = ifc.ifc_len / sizeof (struct ifreq);
468 for (i = 0 ; i < num ; i++) {
469 const char *name;
470
471 name = ifr[i].ifr_name;
472 g_debug ("Checking if %s", name);
473 if (name != NULL && name[0] != '\0') {
474 struct ifreq ifreq;
475 GdmAddress *address;
476 struct sockaddr_in sin;
477
478 memset (&ifreq, 0, sizeof (ifreq));
479
480 strncpy (ifreq.ifr_name,
481 ifr[i].ifr_name,
482 sizeof (ifreq.ifr_name));
483 /* paranoia */
484 ifreq.ifr_name[sizeof (ifreq.ifr_name) - 1] = '\0';
485
486 if ((ioctl (sock, SIOCGIFFLAGS, &ifreq) < 0) && (errno != ENXIO)) {
487 g_warning ("Could not get SIOCGIFFLAGS for %s", ifr[i].ifr_name);
488 }
489
490 if ((ifreq.ifr_flags & IFF_UP) == 0 ||
491 (ifreq.ifr_flags & IFF_BROADCAST) == 0 ||
492 ioctl (sock, SIOCGIFBRDADDR, &ifreq) < 0) {
493 g_debug ("Skipping if %s", name);
494 continue;
495 }
496
497 g_memmove (&sin, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
498 sin.sin_port = htons (XDM_UDP_PORT);
499 address = gdm_address_new_from_sockaddr ((struct sockaddr *) &sin, sizeof (sin));
500 if (address != NULL) {
501 g_debug ("Adding if %s", name);
502 gdm_address_debug (address);
503
504 widget->priv->broadcast_addresses = g_slist_append (widget->priv->broadcast_addresses, address);
505 }
506 }
507 }
508 out:
509 g_free (buf);
510 close (sock);
511 }
512
513 static void
add_hosts(GdmHostChooserWidget * widget)514 add_hosts (GdmHostChooserWidget *widget)
515 {
516 int i;
517
518 for (i = 0; widget->priv->hosts != NULL && widget->priv->hosts[i] != NULL; i++) {
519 struct addrinfo hints;
520 struct addrinfo *result;
521 struct addrinfo *ai;
522 int gaierr;
523 const char *name;
524 char serv_buf [NI_MAXSERV];
525 const char *serv;
526
527 name = widget->priv->hosts[i];
528
529 if (strcmp (name, "BROADCAST") == 0) {
530 find_broadcast_addresses (widget);
531 continue;
532 }
533
534 if (strcmp (name, "MULTICAST") == 0) {
535 /*gdm_chooser_find_mcaddr ();*/
536 continue;
537 }
538
539 result = NULL;
540 memset (&hints, 0, sizeof (hints));
541 hints.ai_socktype = SOCK_STREAM;
542
543 snprintf (serv_buf, sizeof (serv_buf), "%u", XDM_UDP_PORT);
544 serv = serv_buf;
545
546 gaierr = getaddrinfo (name, serv, &hints, &result);
547 if (gaierr != 0) {
548 g_warning ("Unable to get address info for name %s: %s", name, gai_strerror (gaierr));
549 continue;
550 }
551
552 for (ai = result; ai != NULL; ai = ai->ai_next) {
553 GdmAddress *address;
554
555 address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
556 if (address != NULL) {
557 widget->priv->query_addresses = g_slist_append (widget->priv->query_addresses, address);
558 }
559 }
560
561 freeaddrinfo (result);
562 }
563
564 if (widget->priv->broadcast_addresses == NULL && widget->priv->query_addresses == NULL) {
565 find_broadcast_addresses (widget);
566 }
567 }
568
569 static void
xdmcp_init(GdmHostChooserWidget * widget)570 xdmcp_init (GdmHostChooserWidget *widget)
571 {
572 XdmcpHeader header;
573 int sockopts;
574 int res;
575 GIOChannel *ioc;
576 ARRAYofARRAY8 aanames;
577
578 sockopts = 1;
579
580 widget->priv->socket_fd = -1;
581
582 /* Open socket for communication */
583 #ifdef ENABLE_IPV6
584 widget->priv->socket_fd = socket (AF_INET6, SOCK_DGRAM, 0);
585 if (widget->priv->socket_fd != -1) {
586 widget->priv->have_ipv6 = TRUE;
587 #ifdef IPV6_V6ONLY
588 {
589 int zero = 0;
590 if (setsockopt(widget->priv->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) < 0)
591 g_warning("setsockopt(IPV6_V6ONLY): %s", g_strerror(errno));
592 }
593 #endif
594 }
595 #endif
596 if (! widget->priv->have_ipv6) {
597 widget->priv->socket_fd = socket (AF_INET, SOCK_DGRAM, 0);
598 if (widget->priv->socket_fd == -1) {
599 g_critical ("Could not create socket!");
600 }
601 }
602
603 res = setsockopt (widget->priv->socket_fd,
604 SOL_SOCKET,
605 SO_BROADCAST,
606 (char *) &sockopts,
607 sizeof (sockopts));
608 if (res < 0) {
609 g_critical ("Could not set socket options!");
610 }
611
612 /* Assemble XDMCP BROADCAST_QUERY packet in static buffer */
613 memset (&header, 0, sizeof (XdmcpHeader));
614 header.opcode = (CARD16) BROADCAST_QUERY;
615 header.length = 1;
616 header.version = XDM_PROTOCOL_VERSION;
617 aanames.length = 0;
618 XdmcpWriteHeader (&widget->priv->broadcast_buf, &header);
619 XdmcpWriteARRAYofARRAY8 (&widget->priv->broadcast_buf, &aanames);
620
621 /* Assemble XDMCP QUERY packet in static buffer */
622 memset (&header, 0, sizeof (XdmcpHeader));
623 header.opcode = (CARD16) QUERY;
624 header.length = 1;
625 header.version = XDM_PROTOCOL_VERSION;
626 memset (&widget->priv->query_buf, 0, sizeof (XdmcpBuffer));
627 XdmcpWriteHeader (&widget->priv->query_buf, &header);
628 XdmcpWriteARRAYofARRAY8 (&widget->priv->query_buf, &aanames);
629
630 add_hosts (widget);
631
632 ioc = g_io_channel_unix_new (widget->priv->socket_fd);
633 g_io_channel_set_encoding (ioc, NULL, NULL);
634 g_io_channel_set_buffered (ioc, FALSE);
635 widget->priv->io_watch_id = g_io_add_watch (ioc,
636 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
637 (GIOFunc)decode_packet,
638 widget);
639 g_io_channel_unref (ioc);
640 }
641
642 void
gdm_host_chooser_widget_refresh(GdmHostChooserWidget * widget)643 gdm_host_chooser_widget_refresh (GdmHostChooserWidget *widget)
644 {
645 g_return_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (widget));
646
647 xdmcp_discover (widget);
648 }
649
650 GdmChooserHost *
gdm_host_chooser_widget_get_host(GdmHostChooserWidget * widget)651 gdm_host_chooser_widget_get_host (GdmHostChooserWidget *widget)
652 {
653 GdmChooserHost *host;
654
655 g_return_val_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (widget), NULL);
656
657 host = NULL;
658 if (widget->priv->current_host != NULL) {
659 host = g_object_ref (widget->priv->current_host);
660 }
661
662 return host;
663 }
664
665 static void
_gdm_host_chooser_widget_set_kind_mask(GdmHostChooserWidget * widget,int kind_mask)666 _gdm_host_chooser_widget_set_kind_mask (GdmHostChooserWidget *widget,
667 int kind_mask)
668 {
669 if (widget->priv->kind_mask != kind_mask) {
670 widget->priv->kind_mask = kind_mask;
671 }
672 }
673
674 static void
gdm_host_chooser_widget_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)675 gdm_host_chooser_widget_set_property (GObject *object,
676 guint prop_id,
677 const GValue *value,
678 GParamSpec *pspec)
679 {
680 GdmHostChooserWidget *self;
681
682 self = GDM_HOST_CHOOSER_WIDGET (object);
683
684 switch (prop_id) {
685 case PROP_KIND_MASK:
686 _gdm_host_chooser_widget_set_kind_mask (self, g_value_get_int (value));
687 break;
688 default:
689 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
690 break;
691 }
692 }
693
694 static void
gdm_host_chooser_widget_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)695 gdm_host_chooser_widget_get_property (GObject *object,
696 guint prop_id,
697 GValue *value,
698 GParamSpec *pspec)
699 {
700 switch (prop_id) {
701 default:
702 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
703 break;
704 }
705 }
706
707 static GObject *
gdm_host_chooser_widget_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)708 gdm_host_chooser_widget_constructor (GType type,
709 guint n_construct_properties,
710 GObjectConstructParam *construct_properties)
711 {
712 GdmHostChooserWidget *widget;
713
714 widget = GDM_HOST_CHOOSER_WIDGET (G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->constructor (type,
715 n_construct_properties,
716 construct_properties));
717
718 xdmcp_init (widget);
719 xdmcp_discover (widget);
720
721 return G_OBJECT (widget);
722 }
723
724 static void
gdm_host_chooser_widget_dispose(GObject * object)725 gdm_host_chooser_widget_dispose (GObject *object)
726 {
727 GdmHostChooserWidget *widget;
728
729 widget = GDM_HOST_CHOOSER_WIDGET (object);
730
731 g_debug ("Disposing host_chooser_widget");
732
733 if (widget->priv->broadcast_addresses != NULL) {
734 g_slist_foreach (widget->priv->broadcast_addresses,
735 (GFunc)gdm_address_free,
736 NULL);
737 g_slist_free (widget->priv->broadcast_addresses);
738 widget->priv->broadcast_addresses = NULL;
739 }
740 if (widget->priv->query_addresses != NULL) {
741 g_slist_foreach (widget->priv->query_addresses,
742 (GFunc)gdm_address_free,
743 NULL);
744 g_slist_free (widget->priv->query_addresses);
745 widget->priv->query_addresses = NULL;
746 }
747 if (widget->priv->chooser_hosts != NULL) {
748 g_slist_foreach (widget->priv->chooser_hosts,
749 (GFunc)g_object_unref,
750 NULL);
751 g_slist_free (widget->priv->chooser_hosts);
752 widget->priv->chooser_hosts = NULL;
753 }
754
755 widget->priv->current_host = NULL;
756
757 G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->dispose (object);
758 }
759
760 static void
gdm_host_chooser_widget_class_init(GdmHostChooserWidgetClass * klass)761 gdm_host_chooser_widget_class_init (GdmHostChooserWidgetClass *klass)
762 {
763 GObjectClass *object_class = G_OBJECT_CLASS (klass);
764
765 object_class->get_property = gdm_host_chooser_widget_get_property;
766 object_class->set_property = gdm_host_chooser_widget_set_property;
767 object_class->constructor = gdm_host_chooser_widget_constructor;
768 object_class->dispose = gdm_host_chooser_widget_dispose;
769 object_class->finalize = gdm_host_chooser_widget_finalize;
770
771 g_object_class_install_property (object_class,
772 PROP_KIND_MASK,
773 g_param_spec_int ("kind-mask",
774 "kind mask",
775 "kind mask",
776 0,
777 G_MAXINT,
778 0,
779 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
780
781 signals [HOST_ACTIVATED] = g_signal_new ("host-activated",
782 G_TYPE_FROM_CLASS (object_class),
783 G_SIGNAL_RUN_LAST,
784 G_STRUCT_OFFSET (GdmHostChooserWidgetClass, host_activated),
785 NULL,
786 NULL,
787 g_cclosure_marshal_VOID__VOID,
788 G_TYPE_NONE,
789 0);
790
791 g_type_class_add_private (klass, sizeof (GdmHostChooserWidgetPrivate));
792 }
793
794 static void
on_host_selected(GtkTreeSelection * selection,GdmHostChooserWidget * widget)795 on_host_selected (GtkTreeSelection *selection,
796 GdmHostChooserWidget *widget)
797 {
798 GtkTreeModel *model = NULL;
799 GtkTreeIter iter = {0};
800 GdmChooserHost *curhost;
801
802 curhost = NULL;
803
804 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
805 gtk_tree_model_get (model, &iter, CHOOSER_LIST_HOST_COLUMN, &curhost, -1);
806 }
807
808 widget->priv->current_host = curhost;
809 }
810
811 static void
on_row_activated(GtkTreeView * tree_view,GtkTreePath * tree_path,GtkTreeViewColumn * tree_column,GdmHostChooserWidget * widget)812 on_row_activated (GtkTreeView *tree_view,
813 GtkTreePath *tree_path,
814 GtkTreeViewColumn *tree_column,
815 GdmHostChooserWidget *widget)
816 {
817 g_signal_emit (widget, signals[HOST_ACTIVATED], 0);
818 }
819
820 static void
gdm_host_chooser_widget_init(GdmHostChooserWidget * widget)821 gdm_host_chooser_widget_init (GdmHostChooserWidget *widget)
822 {
823 GtkWidget *scrolled;
824 GtkTreeSelection *selection;
825 GtkTreeViewColumn *column;
826 GtkTreeModel *model;
827
828 widget->priv = GDM_HOST_CHOOSER_WIDGET_GET_PRIVATE (widget);
829
830 scrolled = gtk_scrolled_window_new (NULL, NULL);
831 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
832 GTK_SHADOW_IN);
833 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
834 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
835 gtk_box_pack_start (GTK_BOX (widget), scrolled, TRUE, TRUE, 0);
836
837 widget->priv->treeview = gtk_tree_view_new ();
838 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget->priv->treeview), FALSE);
839 g_signal_connect (widget->priv->treeview,
840 "row-activated",
841 G_CALLBACK (on_row_activated),
842 widget);
843 gtk_container_add (GTK_CONTAINER (scrolled), widget->priv->treeview);
844
845 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->treeview));
846 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
847 g_signal_connect (selection, "changed",
848 G_CALLBACK (on_host_selected),
849 widget);
850
851 model = (GtkTreeModel *)gtk_list_store_new (3,
852 GDK_TYPE_PIXBUF,
853 G_TYPE_STRING,
854 G_TYPE_POINTER);
855 gtk_tree_view_set_model (GTK_TREE_VIEW (widget->priv->treeview), model);
856
857 column = gtk_tree_view_column_new_with_attributes ("Icon",
858 gtk_cell_renderer_pixbuf_new (),
859 "pixbuf", CHOOSER_LIST_ICON_COLUMN,
860 NULL);
861 gtk_tree_view_append_column (GTK_TREE_VIEW (widget->priv->treeview), column);
862
863 column = gtk_tree_view_column_new_with_attributes ("Hostname",
864 gtk_cell_renderer_text_new (),
865 "markup", CHOOSER_LIST_LABEL_COLUMN,
866 NULL);
867 gtk_tree_view_append_column (GTK_TREE_VIEW (widget->priv->treeview), column);
868
869 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
870 CHOOSER_LIST_LABEL_COLUMN,
871 GTK_SORT_ASCENDING);
872 }
873
874 static void
gdm_host_chooser_widget_finalize(GObject * object)875 gdm_host_chooser_widget_finalize (GObject *object)
876 {
877 GdmHostChooserWidget *host_chooser_widget;
878
879 g_return_if_fail (object != NULL);
880 g_return_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (object));
881
882 host_chooser_widget = GDM_HOST_CHOOSER_WIDGET (object);
883
884 g_return_if_fail (host_chooser_widget->priv != NULL);
885
886 G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->finalize (object);
887 }
888
889 GtkWidget *
gdm_host_chooser_widget_new(int kind_mask)890 gdm_host_chooser_widget_new (int kind_mask)
891 {
892 GObject *object;
893
894 object = g_object_new (GDM_TYPE_HOST_CHOOSER_WIDGET,
895 "kind-mask", kind_mask,
896 NULL);
897
898 return GTK_WIDGET (object);
899 }
900