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