/* GDM - The Gnome Display Manager * Copyright (C) 1998, 1999, 2000 Martin K, Petersen * * 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 */ /* gdmchooser discovers hosts running XDMCP on the local network(s), * presents a list of them and allows the user to choose one. The * selected hostname will be printed on stdout. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #include #include "gdm.h" #include "misc.h" #include "gdmwm.h" #include "gdmcommon.h" static gboolean RUNNING_UNDER_GDM = FALSE; enum { CHOOSER_LIST_ICON_COLUMN = 0, CHOOSER_LIST_LABEL_COLUMN, CHOOSER_LIST_HOST_COLUMN }; typedef struct _GdmChooserHost GdmChooserHost; struct _GdmChooserHost { gchar *name; gchar *desc; #ifdef ENABLE_IPV6 struct in6_addr ia6; #endif struct in_addr ia; gint addrtype; /* Address stored is IPv4 or IPv6 */ GdkPixbuf *picture; gboolean willing; }; static const gchar *scanning_message = N_("Please wait: scanning local network..."); static const gchar *empty_network = N_("No serving hosts were found."); static const gchar *active_network = N_("Choose a ho_st to connect to:"); /* XDM chooser style stuff */ static gchar *xdm_address = NULL; static gchar *client_address = NULL; static gint connection_type = 0; static void gdm_chooser_abort (const gchar *format, ...) G_GNUC_PRINTF (1, 2); static void gdm_chooser_warn (const gchar *format, ...) G_GNUC_PRINTF (1, 2); static void set_background (void); /* Exported for glade */ void gdm_chooser_cancel (void); void gdm_chooser_add_host (void); void gdm_chooser_add_entry_changed (void); void gdm_chooser_manage (GtkButton *button, gpointer data); void gdm_chooser_browser_select (GtkWidget *widget, gint selected, GdkEvent *event); void gdm_chooser_browser_unselect (GtkWidget *widget, gint selected, GdkEvent *event); void gdm_chooser_xdmcp_discover (void); void display_chooser_information (void); #define ADD_TIMEOUT 3000 static guint add_check_handler = 0; /* if this is received, select that host automatically */ #ifdef ENABLE_IPV6 static struct in6_addr *added6_addr = NULL; #endif static struct in_addr *added_addr = NULL; static char *added_host = NULL; static guint scan_time_handler = 0; #define PING_TIMEOUT 2000 #define PING_TRIES 3 static int ping_tries = PING_TRIES; static guint ping_try_handler = 0; /* set in the main function */ static char **stored_argv = NULL; static int stored_argc = 0; /* Fixetyfix */ int XdmcpReallocARRAY8 (ARRAY8Ptr array, int length); typedef struct _XdmAuth { ARRAY8 authentication; ARRAY8 authorization; } XdmAuthRec, *XdmAuthPtr; static XdmAuthRec authlist = { { (CARD16) 0, (CARD8 *) 0 }, { (CARD16) 0, (CARD8 *) 0 } }; static gint sockfd; static XdmcpBuffer bcbuf; static XdmcpBuffer querybuf; static GSList *bcaddr; static GSList *queryaddr; static gint GdmXineramaScreen; static gint GdmIconMaxHeight; static gint GdmIconMaxWidth; static gboolean GdmDebug; static gint GdmScanTime; static gchar *GdmHostIconDir; static gchar *GdmHostDefaultIcon; static gchar *GdmGtkRC; static gchar *GdmGtkTheme; static gchar *GdmHosts; static gchar *GdmHostsOrig; static gboolean GdmBroadcast; static gboolean GdmAllowAdd; static gchar *GdmBackgroundColor; static int GdmBackgroundType; #ifdef ENABLE_IPV6 static gboolean GdmMulticast; static gchar *GdmMulticastAddr; #endif /* HACK: for gdmcommon, else it complains */ /* */ gchar *GdmInfoMsgFile; /* */ gchar *GdmInfoMsgFont; /* */ gchar *GdmSoundProgram; /* */ gboolean GdmSoundOnLogin; /* */ gchar *GdmSoundOnLoginFile; /* */ void /* */ gdm_kill_thingies (void) /* */ { /* */ ; /* nothing */ /* */ } enum { GDM_BACKGROUND_NONE = 0, GDM_BACKGROUND_IMAGE = 1, GDM_BACKGROUND_COLOR = 2 }; static GladeXML *chooser_app; static GtkWidget *chooser, *manage, *rescan, *cancel, *add_entry, *add_button; static GtkWidget *status_label; static GIOChannel *channel; static GList *hosts = NULL; static GdkPixbuf *defhostimg; static GtkWidget *browser; static GtkTreeModel *browser_model; static GdmChooserHost *curhost; static gboolean have_ipv6; /* Socket is IPv4 or IPv6 */ static gboolean find_host_in_list (GdmChooserHost *host, GtkTreeIter *iter) { if (gtk_tree_model_get_iter_first (browser_model, iter)) { do { GdmChooserHost *lhost; gtk_tree_model_get (browser_model, iter, CHOOSER_LIST_HOST_COLUMN, &lhost, -1); if (lhost == host) return TRUE; } while (gtk_tree_model_iter_next (browser_model, iter)); } return FALSE; } static void setup_cursor (GdkCursorType type) { GdkCursor *cursor = gdk_cursor_new (type); gdk_window_set_cursor (gdk_get_default_root_window (), cursor); gdk_cursor_unref (cursor); } static void gdm_chooser_host_dispose (GdmChooserHost *host) { if (!host) return; if (host->picture != NULL) g_object_unref (G_OBJECT (host->picture)); host->picture = NULL; g_free (host->name); host->name = NULL; g_free (host->desc); host->desc = NULL; g_free (host); } static GdmChooserHost * gdm_chooser_host_alloc (const char *hostname, const char *description, char *ia, int family, gboolean willing) { GdmChooserHost *host; GdkPixbuf *img; gchar *hostimg; host = g_new0 (GdmChooserHost, 1); host->name = g_strdup (hostname); host->desc = g_strdup (description); host->willing = willing; #ifdef ENABLE_IPV6 if (family == AF_INET6) memcpy (&host->ia6, (struct in6_addr *)ia, sizeof (struct in6_addr)); else #endif memcpy (&host->ia, (struct in_addr *)ia, sizeof (struct in_addr)); host->addrtype = family; hosts = g_list_prepend (hosts, host); if ( ! willing) return host; hostimg = g_strconcat (GdmHostIconDir, "/", hostname, NULL); if (access (hostimg, R_OK) != 0) { g_free (hostimg); hostimg = g_strconcat (GdmHostIconDir, "/", hostname, ".png", NULL); } if (access (hostimg, R_OK) == 0 && (img = gdk_pixbuf_new_from_file (hostimg, NULL)) != NULL) { gint w, h; w = gdk_pixbuf_get_width (img); h = gdk_pixbuf_get_height (img); if (w>h && w>GdmIconMaxWidth) { h = h * ((gfloat) GdmIconMaxWidth/w); w = GdmIconMaxWidth; } else if (h>GdmIconMaxHeight) { w = w * ((gfloat) GdmIconMaxHeight/h); h = GdmIconMaxHeight; } if (w != gdk_pixbuf_get_width (img) || h != gdk_pixbuf_get_height (img)) host->picture = gdk_pixbuf_scale_simple (img, w, h, GDK_INTERP_BILINEAR); else host->picture = g_object_ref (G_OBJECT (img)); g_object_unref (G_OBJECT (img)); } else if (defhostimg != NULL) { host->picture = (GdkPixbuf *)g_object_ref (G_OBJECT (defhostimg)); } g_free (hostimg); return host; } static void gdm_chooser_browser_add_host (GdmChooserHost *host) { gboolean add_this_host = FALSE; if (host->willing) { GtkTreeIter iter = {0}; const char *addr; char *label; char *name, *desc; #ifdef ENABLE_IPV6 if (host->addrtype == AF_INET6) { /* IPv6 address */ static char buffer6[INET6_ADDRSTRLEN]; addr = inet_ntop (AF_INET6, host->ia6.s6_addr, buffer6, INET6_ADDRSTRLEN); } else /* IPv4 address */ #endif { addr = inet_ntoa (host->ia); } name = g_markup_escape_text (host->name, -1); desc = g_markup_escape_text (host->desc, -1); if (strcmp (addr, host->name) == 0) label = g_strdup_printf ("%s\n%s", name, desc); else label = g_strdup_printf ("%s (%s)\n%s", name, addr, desc); g_free (name); g_free (desc); gtk_list_store_append (GTK_LIST_STORE (browser_model), &iter); gtk_list_store_set (GTK_LIST_STORE (browser_model), &iter, CHOOSER_LIST_ICON_COLUMN, host->picture, CHOOSER_LIST_LABEL_COLUMN, label, CHOOSER_LIST_HOST_COLUMN, host, -1); g_free (label); #ifdef ENABLE_IPV6 if (added6_addr != NULL && memcmp (&host->ia6, added6_addr, sizeof (struct in6_addr)) == 0) { added6_addr = NULL; add_this_host = TRUE; } #else if (added_addr != NULL && memcmp (&host->ia, added_addr, sizeof (struct in_addr)) == 0) { added_addr = NULL; add_this_host = TRUE; } #endif if (add_this_host) { GtkTreeSelection *selection; GtkTreePath *path = gtk_tree_model_get_path (browser_model, &iter); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (browser)); gtk_tree_selection_select_iter (selection, &iter); gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (browser), path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free (path); gtk_widget_grab_focus (manage); } g_free (added_host); added_host = NULL; if (add_check_handler > 0) g_source_remove (add_check_handler); add_check_handler = 0; } gtk_widget_set_sensitive (GTK_WIDGET (browser), TRUE); } static GdmChooserHost * gdm_host_known (char *ia, gint family) { GList *li; for (li = hosts; li != NULL; li = li->next) { GdmChooserHost *host = li->data; #ifdef ENABLE_IPV6 if (family == AF_INET6) { if (host->addrtype != AF_INET6) continue; if ( ! memcmp (&host->ia6, (struct in6_addr *)ia, sizeof (struct in6_addr))) return host; } else #endif if (family == AF_INET) { if (host->addrtype != AF_INET) continue; if ( ! memcmp (&host->ia, (struct in_addr *)ia, sizeof (struct in_addr))) return host; } } return NULL; } static gboolean is_loopback_addr (char *ia, gint family) { const char lo[] = {127,0,0,1}; #ifdef ENABLE_IPV6 if (family == AF_INET6 && IN6_IS_ADDR_LOOPBACK ((struct in6_addr *)ia)) { return TRUE; } else #endif if (((family == AF_INET) && (((struct in_addr *) ia)->s_addr == INADDR_LOOPBACK)) || memcmp (&((struct in_addr *)ia)->s_addr, lo, 4) == 0) { return TRUE; } else { return FALSE; } } static gboolean gdm_addr_known (char *ia, gint family) { GSList *li; for (li = queryaddr; li != NULL; li = li->next) { struct sockaddr *sa = li->data; #ifdef ENABLE_IPV6 if (sa->sa_family == AF_INET6) { if (family != AF_INET6) continue; if (!memcmp (&((struct sockaddr_in6 *)sa)->sin6_addr, (struct in6_addr *)ia , sizeof (struct in6_addr))) return TRUE; } else if (sa->sa_family == AF_INET) { if (family != AF_INET) continue; if (!memcmp (&((struct sockaddr_in *)sa)->sin_addr, (struct in_addr *)ia, sizeof (struct in_addr))) return TRUE; } #else if (memcmp (&((struct sockaddr_in *)sa)->sin_addr, (struct in_addr *)ia, sizeof (struct in_addr)) == 0) return TRUE; #endif } return FALSE; } static gboolean gdm_chooser_decode_packet (GIOChannel *source, GIOCondition condition, gpointer data) { #ifdef ENABLE_IPV6 char hbuf[NI_MAXHOST]; struct sockaddr_in6 clnt6_sa; #endif struct sockaddr_in clnt_sa; gint sa_len; static XdmcpBuffer buf; XdmcpHeader header; struct hostent *he; gchar *hostname = NULL; gchar *status = NULL; ARRAY8 auth = {0}, host = {0}, stat = {0}; GdmChooserHost *gh; int pipe_buf; gboolean host_not_willing = FALSE; if ( ! (condition & G_IO_IN)) return TRUE; #ifdef ENABLE_IPV6 if (have_ipv6) { sa_len = sizeof (struct sockaddr_in6); if (! XdmcpFill (sockfd, &buf, (XdmcpNetaddr) &clnt6_sa, &sa_len)) return TRUE; } else #endif { sa_len = sizeof (struct sockaddr_in); if (! XdmcpFill (sockfd, &buf, (XdmcpNetaddr) &clnt_sa, &sa_len)) return TRUE; } if (! XdmcpReadHeader (&buf, &header)) return TRUE; if (header.version != XDM_PROTOCOL_VERSION) return TRUE; 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 (stat.data, MIN (stat.length, 256)); } else if (header.opcode == UNWILLING) { /* immaterial, will not be shown */ status = NULL; } else { return TRUE; } #ifdef ENABLE_IPV6 /*Since, IPv4 addresses will get extracted as V4 mapped IPv6 address*/ if (have_ipv6 && IN6_IS_ADDR_V4MAPPED (&(clnt6_sa.sin6_addr))) { memset (&clnt_sa, 0, sizeof (clnt_sa)); memcpy (&(clnt_sa.sin_addr), &(clnt6_sa.sin6_addr.s6_addr[12]), 4); clnt_sa.sin_family = AF_INET; clnt_sa.sin_port = clnt6_sa.sin6_port; clnt6_sa.sin6_family = AF_INET; } if (have_ipv6 && ((struct sockaddr *) &clnt6_sa)->sa_family == AF_INET6) { if ( ! is_loopback_addr ((gchar *) &clnt6_sa.sin6_addr, AF_INET6)) { clnt6_sa.sin6_scope_id = 0; getnameinfo ((struct sockaddr *)&clnt6_sa, sizeof (struct sockaddr_in6), hbuf, sizeof(hbuf), NULL, 0, 0); hostname = hbuf; if (strlen (hostname)+1 > PIPE_BUF) goto done; hostname = g_strdup (hostname); } else { hostname = g_new0 (char, 1024); if (gethostname (hostname, 1023) != 0) { g_free (hostname); goto done; } added6_addr = NULL; gh = gdm_host_known ((char *)&clnt6_sa.sin6_addr, AF_INET6); } } else #endif { if ( !is_loopback_addr ((char *)&clnt_sa.sin_addr, AF_INET)) { he = gethostbyaddr ((gchar *) &clnt_sa.sin_addr, sizeof (struct in_addr), AF_INET); hostname = (he && he->h_name) ? he->h_name : inet_ntoa (clnt_sa.sin_addr); if (strlen (hostname)+1 > PIPE_BUF) goto done; hostname = g_strdup (hostname); } else { hostname = g_new0 (char, 1024); if (gethostname (hostname, 1023) != 0) { g_free (hostname); goto done; } } } #ifdef PIPE_BUF pipe_buf = PIPE_BUF; #else /* apparently Hurd doesn't have PIPE_BUF */ pipe_buf = fpathconf (1 /*stdout*/, _PC_PIPE_BUF); /* could return -1 if no limit */ #endif /* We can't pipe hostnames larger than this */ if (pipe_buf > 0 && strlen (hostname)+1 > pipe_buf) { g_free (hostname); goto done; } #ifdef ENABLE_IPV6 if (have_ipv6 && ((struct sockaddr *) &clnt6_sa)->sa_family == AF_INET6) { gh = gdm_host_known ((char *)&clnt6_sa.sin6_addr, AF_INET6); if (gh == NULL) { gh = gdm_chooser_host_alloc (hostname, status, (char *)&clnt6_sa.sin6_addr, AF_INET6, header.opcode == WILLING); gdm_chooser_browser_add_host (gh); } } else #endif { gh = gdm_host_known ((char *)&clnt_sa.sin_addr, AF_INET); if (gh == NULL) { gh = gdm_chooser_host_alloc (hostname, status, (char *)&clnt_sa.sin_addr, AF_INET, header.opcode == WILLING); gdm_chooser_browser_add_host (gh); } } if (gh != NULL) { /* server changed it's mind */ if (header.opcode == WILLING && ! gh->willing) { gh->willing = TRUE; gdm_chooser_browser_add_host (gh); } /* hmmm what about the other change, just ignore for now, it's kind of confusing to just remove servers really */ } #ifdef ENABLE_IPV6 if (have_ipv6 && ((struct sockaddr *) &clnt6_sa)->sa_family == AF_INET6 && ! gh->willing && added6_addr != NULL && memcmp (&gh->ia6, added6_addr, sizeof (struct in6_addr)) == 0) { added6_addr = NULL; host_not_willing = TRUE; } else #endif if (clnt_sa.sin_family == AF_INET && ! gh->willing && added_addr != NULL && memcmp (&gh->ia, added_addr, sizeof (struct in_addr)) == 0) { added_addr = NULL; host_not_willing = TRUE; } if (host_not_willing) { GtkWidget *dialog; if (add_check_handler > 0) g_source_remove (add_check_handler); add_check_handler = 0; dialog = ve_hig_dialog_new (GTK_WINDOW (chooser) /* parent */, GTK_DIALOG_MODAL /* flags */, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, FALSE /* markup */, _("Cannot connect to remote server"), _("The host \"%s\" is not willing " "to support a login session right now. " "Please try again later."), added_host); if (RUNNING_UNDER_GDM) gdm_wm_center_window (GTK_WINDOW (dialog)); gdm_wm_no_login_focus_push (); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); gdm_wm_no_login_focus_pop (); g_free (added_host); added_host = NULL; } g_free (hostname); done: if (header.opcode == WILLING) { XdmcpDisposeARRAY8 (&auth); XdmcpDisposeARRAY8 (&host); XdmcpDisposeARRAY8 (&stat); } g_free (status); return TRUE; } /* Find broadcast address for all active, non pointopoint interfaces */ static void gdm_chooser_find_bcaddr (void) { int i = 0, num; int sock; struct ifconf ifc; char *buf; struct ifreq *ifr; 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_free (buf); gdm_chooser_warn ("Cannot get local addresses!"); close (sock); return; } ifr = ifc.ifc_req; num = ifc.ifc_len / sizeof(struct ifreq); for (i = 0 ; i < num ; i++) { if ( ! ve_string_empty (ifr[i].ifr_name)) { struct ifreq ifreq; struct sockaddr_in *ba = NULL; struct sockaddr_in *sin = NULL; 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) gdm_chooser_warn ("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) continue; ba = (struct sockaddr_in *) &ifreq.ifr_broadaddr; sin = g_new0 (struct sockaddr_in, 1); sin->sin_family = AF_INET; memcpy (&sin->sin_addr, &ba->sin_addr, sizeof (ba->sin_addr)); bcaddr = g_slist_append (bcaddr, sin); } } g_free (buf); } /* Append multicast address into the list */ #ifdef ENABLE_IPV6 static void gdm_chooser_find_mcaddr (void) { struct sockaddr_in6 *sin6; int sock; /* Temporary socket for getting information about available interfaces */ u_char loop = 0; /* Disable multicast for loopback interface */ int i, num; char *buf; /* For interfaces' list */ struct ifconf ifc; struct ifreq *ifr = NULL; 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 = malloc (ifc.ifc_len); if (setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof (loop)) < 0) gdm_chooser_warn ("setsockopt: Can't disable loopback interface for multicasting\n"); if (ioctl (sock, SIOCGIFCONF, &ifc) >= 0) ifr = ifc.ifc_req; num = ifc.ifc_len / sizeof (struct ifreq); /* No of interfaces */ for (i = 0 ; i < num ; i++) { struct ifreq ifreq; int ifindex; memset (&ifreq, 0, sizeof (ifreq)); strncpy (ifreq.ifr_name, ifr[i].ifr_name, sizeof (ifreq.ifr_name)); ifreq.ifr_name[sizeof (ifreq.ifr_name) - 1] = '\0'; if (ioctl (sock, SIOCGIFFLAGS, &ifreq) < 0) gdm_chooser_warn ("Could not get interface flags for %s\n", ifr[i].ifr_name); ifindex = if_nametoindex (ifr[i].ifr_name); if ((!(ifreq.ifr_flags & IFF_UP) || (!(ifreq.ifr_flags & IFF_MULTICAST))) || (ifindex == 0 )) { /* Not a valid interface or Not up */ continue; } sin6 = g_new0 (struct sockaddr_in6, 1); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons (XDM_UDP_PORT); sin6->sin6_scope_id = ifindex; inet_pton (AF_INET6, GdmMulticastAddr, &sin6->sin6_addr); bcaddr = g_slist_append (bcaddr, sin6); /* bcaddr is also serving for multicast address for IPv6 */ } } #endif static gboolean chooser_scan_time_update (gpointer data) { GList *li; scan_time_handler = 0; for (li = hosts; li != NULL; li = li->next) { GdmChooserHost *host = (GdmChooserHost *) li->data; if (host->willing) break; } if (li != NULL /* something was found */) { gtk_label_set_label (GTK_LABEL (status_label), _(active_network)); } else { gtk_label_set_label (GTK_LABEL (status_label), _(empty_network)); } gtk_widget_set_sensitive (GTK_WIDGET (rescan), TRUE); return FALSE; } static void do_ping (gboolean full) { struct sockaddr_in sock; GSList *bl = bcaddr; GSList *ql = queryaddr; struct sockaddr *ia; #ifdef ENABLE_IPV6 struct sockaddr_in6 sock6; memset (&sock6, 0, sizeof (sock6)); sock6.sin6_family = AF_INET6; sock6.sin6_port = htons (XDM_UDP_PORT); #endif sock.sin_family = AF_INET; sock.sin_port = htons (XDM_UDP_PORT); while (bl) { ia = (struct sockaddr *) bl->data; #ifdef ENABLE_IPV6 if (have_ipv6) { /* Convert the IPv4 broadcast address to v4 mapped v6 address.*/ if (ia->sa_family == AF_INET) { char tmpaddr[30]; struct in6_addr in6; sprintf (tmpaddr, "::ffff:%s", inet_ntoa(((struct sockaddr_in *)(ia))->sin_addr)); inet_pton (AF_INET6, tmpaddr, &in6); memcpy (sock6.sin6_addr.s6_addr, in6.s6_addr, sizeof (struct in6_addr)); XdmcpFlush (sockfd, &bcbuf, (XdmcpNetaddr) &sock6, (int) sizeof (struct sockaddr_in6)); } else if (ia->sa_family == AF_INET6) { memcpy (sock6.sin6_addr.s6_addr, ((struct sockaddr_in6 *)ia)->sin6_addr.s6_addr, sizeof (struct in6_addr)); XdmcpFlush (sockfd, &bcbuf, (XdmcpNetaddr) &sock6, (int) sizeof (struct sockaddr_in6)); } } else #endif { if (ia->sa_family == AF_INET) { sock.sin_addr.s_addr = ((struct sockaddr_in *)ia)->sin_addr.s_addr; XdmcpFlush (sockfd, &bcbuf, (XdmcpNetaddr) &sock, (int)sizeof (struct sockaddr_in)); } } bl = bl->next; } while (ql != NULL) { ia = (struct sockaddr *) ql->data; #ifdef ENABLE_IPV6 if (have_ipv6) { if (ia->sa_family == AF_INET) { char tmpaddr[30]; struct in6_addr in6; sprintf (tmpaddr, "::ffff:%s", inet_ntoa(((struct sockaddr_in *)(ia))->sin_addr)); inet_pton (AF_INET6, tmpaddr, &in6); if (full || ! gdm_host_known ((char *)&((struct sockaddr_in6 *)ia)->sin6_addr, AF_INET6)) { memcpy (sock6.sin6_addr.s6_addr, in6.s6_addr, sizeof (struct in6_addr)); XdmcpFlush (sockfd, &bcbuf, (XdmcpNetaddr) &sock6, (int) sizeof (struct sockaddr_in6)); } } if (ia->sa_family == AF_INET6) { if (full || ! gdm_host_known ((char *)&((struct sockaddr_in6 *)ia)->sin6_addr, AF_INET6)) { memcpy (&sock6.sin6_addr, &((struct sockaddr_in6 *)ia)->sin6_addr, sizeof (struct in6_addr)); XdmcpFlush (sockfd, &querybuf, (XdmcpNetaddr) &sock6, (int) sizeof (struct sockaddr_in6)); } } } else #endif { if (full || ! gdm_host_known ((char *)&((struct sockaddr_in *)ia)->sin_addr, AF_INET)) { sock.sin_addr.s_addr = ((struct sockaddr_in *)ia)->sin_addr.s_addr; XdmcpFlush (sockfd, &querybuf, (XdmcpNetaddr) &sock, (int)sizeof (struct sockaddr_in)); } } ql = ql->next; } } static gboolean ping_try (gpointer data) { do_ping (FALSE); ping_tries --; if (ping_tries <= 0) return FALSE; else return TRUE; } void gdm_chooser_xdmcp_discover (void) { GList *hl = hosts; g_free (added_host); added_host = NULL; #ifdef ENABLE_IPV6 added6_addr = NULL; #endif added_addr = NULL; if (add_check_handler > 0) g_source_remove (add_check_handler); add_check_handler = 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 (hosts); hosts = NULL; do_ping (TRUE); if (scan_time_handler > 0) g_source_remove (scan_time_handler); scan_time_handler = g_timeout_add (GdmScanTime * 1000, chooser_scan_time_update, NULL); /* Note we already used up one try */ ping_tries = PING_TRIES - 1; if (ping_try_handler > 0) g_source_remove (ping_try_handler); ping_try_handler = g_timeout_add (PING_TIMEOUT, ping_try, NULL); } #ifndef ishexdigit #define ishexdigit(c) (isdigit(c) || ('a' <= (c) && (c) <= 'f')) #endif #define HexChar(c) ('0' <= (c) && (c) <= '9' ? (c) - '0' : (c) - 'a' + 10) static int from_hex (const char *s, char *d, int len) { int t; while (len >= 2) { if (!ishexdigit(*s)) return 1; t = HexChar (*s) << 4; s++; if (!ishexdigit(*s)) return 1; t += HexChar (*s); s++; *d++ = t; len -= 2; } return len; } static void gdm_chooser_add_hosts (char **hosts) { struct hostent *hostent; #ifdef ENABLE_IPV6 struct sockaddr_in6 *qa6 = NULL; struct addrinfo hints, *result, *res; #endif struct sockaddr_in* qa = NULL; int used_addr = 0; int i; for (i = 0; hosts != NULL && hosts[i] != NULL; i++) { const char *name = hosts[i]; if (strcmp (name, "BROADCAST") == 0) { gdm_chooser_find_bcaddr (); continue; } #ifdef ENABLE_IPV6 if (strcmp (name, "MULTICAST") == 0) { gdm_chooser_find_mcaddr (); continue; } #endif if (used_addr == AF_INET || !qa) { qa = g_new0 (struct sockaddr_in, 1); memset (qa, 0, sizeof (*qa)); qa->sin_family = AF_INET; } #ifdef ENABLE_IPV6 if (used_addr == AF_INET6 || !qa6) { qa6 = g_new0 (struct sockaddr_in6, 1); memset (qa6, 0, sizeof (qa6)); qa6->sin6_family = AF_INET6; } result = NULL; memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; if ((strlen (name) == 32) && from_hex (name, (char *) &qa6->sin6_addr, strlen (name)) == 0) { queryaddr = g_slist_append (queryaddr, qa6); g_free (qa); qa = NULL; used_addr = AF_INET6; } else #endif if ((strlen (name) == 8) && (from_hex (name, (char *) &qa->sin_addr, strlen (name)) == 0)) { queryaddr = g_slist_append (queryaddr, qa); #ifdef ENABLE_IPV6 g_free (qa6); qa6 = NULL; #endif used_addr = AF_INET; } else #ifdef ENABLE_IPV6 if (inet_pton (AF_INET6, name, &qa6->sin6_addr) > 0) { queryaddr = g_slist_append (queryaddr, qa6); g_free (qa); qa = NULL; used_addr = AF_INET6; } else #endif if ((qa->sin_addr.s_addr = inet_addr (name)) != -1) { queryaddr = g_slist_append (queryaddr, qa); #ifdef ENABLE_IPV6 g_free (qa6); qa6 = NULL; #endif used_addr = AF_INET; } else #ifdef ENABLE_IPV6 if (getaddrinfo (name, NULL, &hints, &result) == 0) { for (res = result; res; res = res->ai_next) { if (res && res->ai_family == AF_INET6) { memmove (qa6, res->ai_addr, res->ai_addrlen); queryaddr = g_slist_append (queryaddr, qa6); g_free (qa); qa = NULL; used_addr = AF_INET6; } if (res && res->ai_family == AF_INET) { memmove (qa, res->ai_addr, res->ai_addrlen); queryaddr = g_slist_append (queryaddr, qa); g_free (qa6); qa6 = NULL; used_addr = AF_INET; } } } else #endif if ((hostent = gethostbyname (name)) != NULL && hostent->h_addrtype == AF_INET && hostent->h_length == 4) { qa->sin_family = AF_INET; memmove (&qa->sin_addr, hostent->h_addr, 4); queryaddr = g_slist_append (queryaddr, qa); #ifdef ENABLE_IPV6 g_free (qa6); qa6 = NULL; #endif used_addr = AF_INET; } else { continue; /* not a valid address */ } } if (bcaddr == NULL && queryaddr == NULL) gdm_chooser_find_bcaddr (); } static void gdm_chooser_xdmcp_init (char **hosts) { static XdmcpHeader header; gint sockopts = 1; /* Open socket for communication */ #ifdef ENABLE_IPV6 if ((sockfd = socket (AF_INET6, SOCK_DGRAM, 0)) == -1) have_ipv6 = FALSE; else have_ipv6 = TRUE; #endif if ( ! have_ipv6) { if ((sockfd = socket (AF_INET, SOCK_DGRAM, 0)) == -1) { gdm_chooser_abort ("Could not create socket()!"); } } if (setsockopt (sockfd, SOL_SOCKET, SO_BROADCAST, (char *) &sockopts, sizeof (sockopts)) < 0) gdm_chooser_abort ("Could not set socket options!"); /* Assemble XDMCP BROADCAST_QUERY packet in static buffer */ header.opcode = (CARD16) BROADCAST_QUERY; header.length = 1; header.version = XDM_PROTOCOL_VERSION; XdmcpWriteHeader (&bcbuf, &header); XdmcpWriteARRAY8 (&bcbuf, &authlist.authentication); /* Assemble XDMCP QUERY packet in static buffer */ header.opcode = (CARD16) QUERY; header.length = 1; header.version = XDM_PROTOCOL_VERSION; XdmcpWriteHeader (&querybuf, &header); XdmcpWriteARRAY8 (&querybuf, &authlist.authentication); gdm_chooser_add_hosts (hosts); channel = g_io_channel_unix_new (sockfd); g_io_channel_set_encoding (channel, NULL, NULL); g_io_channel_set_buffered (channel, FALSE); g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, gdm_chooser_decode_packet, GINT_TO_POINTER (sockfd), NULL); g_io_channel_unref (channel); gdm_chooser_xdmcp_discover(); } static void gdm_chooser_choose_host (const char *hostname) { ARRAY8 tmparr; #ifndef ENABLE_IPV6 struct hostent *hentry; #endif printf ("\n%s\n", curhost->name); fflush (stdout); if (xdm_address != NULL) { #ifdef ENABLE_IPV6 int status; struct sockaddr_in6 in6_addr; struct addrinfo hints, *result; #endif struct sockaddr_in in_addr; char xdm_addr[32]; char client_addr[32]; int fd; char buf[1024]; XdmcpBuffer buffer; long family, port, addr; if (strlen (xdm_address) > 64 || from_hex (xdm_address, xdm_addr, strlen (xdm_address)) != 0) gdm_chooser_abort ("gdm_chooser_chooser_host: Invalid xdm address."); family = (xdm_addr[0] << 8) | xdm_addr[1]; port = (xdm_addr[2] << 8) | xdm_addr[3]; #ifdef ENABLE_IPV6 if (family == AF_INET6) { memset (&in6_addr, 0, sizeof (in6_addr)); in6_addr.sin6_port = htons (port); in6_addr.sin6_family = AF_INET6; memcpy (&in6_addr.sin6_addr, &xdm_address[4], 16); if ((fd = socket (PF_INET6, SOCK_STREAM, 0)) == -1) gdm_chooser_abort ("gdm_chooser_choose_host: Couldn't create response socket."); if (connect (fd, (struct sockaddr *) &in6_addr, sizeof (in6_addr)) == -1) gdm_chooser_abort ("gdm_chooser_chooser_host: Couldn't connect to xdm."); } else #endif { addr = (xdm_addr[4] << 24) | (xdm_addr[5] << 16) | (xdm_addr[6] << 8) | xdm_addr[7]; in_addr.sin_family = AF_INET; in_addr.sin_port = htons (port); in_addr.sin_addr.s_addr = htonl (addr); if ((fd = socket (PF_INET, SOCK_STREAM, 0)) == -1) gdm_chooser_abort ("gdm_chooser_chooser_host: Couldn't create response socket."); if (connect (fd, (struct sockaddr *) &in_addr, sizeof (in_addr)) == -1) gdm_chooser_abort ("gdm_chooser_chooser_host: Couldn't connect to xdm."); } buffer.data = (BYTE *) buf; buffer.size = sizeof (buf); buffer.pointer = 0; buffer.count = 0; if (strlen (client_address) > 64 || from_hex (client_address, client_addr, strlen (client_address)) != 0) gdm_chooser_abort ("gdm_chooser_chooser_host: Invalid client address."); tmparr.data = (BYTE *) client_addr; tmparr.length = strlen (client_address) / 2; XdmcpWriteARRAY8 (&buffer, &tmparr); XdmcpWriteCARD16 (&buffer, (CARD16) connection_type); #ifdef ENABLE_IPV6 result = NULL; memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; status = getaddrinfo (hostname, NULL, &hints, &result); if (status != 0) gdm_chooser_abort ("gdm_chooser_chooser_host: Couldn't get host entry for %s", hostname); if (result->ai_family == AF_INET6) tmparr.length = 16; if (result->ai_family == AF_INET) tmparr.length = 4; tmparr.data = (BYTE *) result->ai_addr; #else hentry = gethostbyname (hostname); if (!hentry) gdm_chooser_abort ("gdm_chooser_chooser_host: Couldn't get host entry for %s", hostname); tmparr.data = (BYTE *) hentry->h_addr_list[0]; /* XXX */ tmparr.length = 4; #endif XdmcpWriteARRAY8 (&buffer, &tmparr); write (fd, (char *) buffer.data, buffer.pointer); close (fd); } } static gboolean add_check (gpointer data) { gboolean check = FALSE; #ifdef ENABLE_IPV6 if (have_ipv6 && added6_addr != NULL) check = TRUE; else #endif if ((! have_ipv6) && added_addr != NULL) check = TRUE; if (check) { GtkWidget *dialog; dialog = ve_hig_dialog_new (GTK_WINDOW (chooser) /* parent */, GTK_DIALOG_MODAL /* flags */, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, FALSE /* markup */, _("Did not receive response from server"), _("Did not receive any response from host \"%s\" " "in %d seconds. Perhaps the host is not " "turned on, or is not willing to support a " "login session right now. Please try again " "later."), added_host, ADD_TIMEOUT / 1000); if (RUNNING_UNDER_GDM) gdm_wm_center_window (GTK_WINDOW (dialog)); gdm_wm_no_login_focus_push (); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); gdm_wm_no_login_focus_pop (); } add_check_handler = 0; return FALSE; } void gdm_chooser_add_host (void) { struct hostent *hostent; struct sockaddr_in *qa; GdmChooserHost *host = NULL; struct sockaddr_in sock; gboolean status = FALSE; const char *name; #ifdef ENABLE_IPV6 struct sockaddr_in6 *qa6; struct sockaddr_in6 sock6; struct addrinfo hints, *result; result = NULL; memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_DGRAM; #endif name = gtk_entry_get_text (GTK_ENTRY (add_entry)); if (ve_string_empty (name)) return; qa = g_new0 (struct sockaddr_in, 1); qa->sin_family = AF_INET; #ifdef ENABLE_IPV6 qa6 = g_new0 (struct sockaddr_in6, 1); qa6->sin6_family = AF_INET6; if (have_ipv6 && strlen (name) == 32 && from_hex (name, (char *) &qa6->sin6_addr, strlen (name)) == 0) ; else #endif if (strlen (name) == 8 && from_hex (name, (char *) &qa->sin_addr, strlen (name)) == 0) { #ifdef ENABLE_IPV6 if (have_ipv6) { char tmpaddr[30]; sprintf (tmpaddr, "::ffff:%s", inet_ntoa (qa->sin_addr)); inet_pton (AF_INET6, tmpaddr, &qa6->sin6_addr); } #endif } else #ifdef ENABLE_IPV6 if (have_ipv6 && inet_pton (AF_INET6, name, &qa6->sin6_addr) > 0) ; else #endif if (inet_aton (name, &(qa->sin_addr))) { #ifdef ENABLE_IPV6 if (have_ipv6) { char tmpaddr[30]; sprintf (tmpaddr, "::ffff:%s", inet_ntoa (qa->sin_addr)); inet_pton (AF_INET6, tmpaddr, &qa6->sin6_addr); } #endif } else #ifdef ENABLE_IPV6 if (getaddrinfo (name, NULL, &hints, &result) == 0) { if (result->ai_family == AF_INET6) { memcpy (qa6, (struct sockaddr_in6 *)result->ai_addr, result->ai_addrlen); } else if (result->ai_family == AF_INET) { if (have_ipv6) { char tmpaddr [30]; sprintf (tmpaddr, "::ffff:%s", inet_ntoa(((struct sockaddr_in *)result->ai_addr)->sin_addr)); inet_pton (AF_INET6, tmpaddr, &qa6->sin6_addr); } } } else #endif if ((hostent = gethostbyname (name)) != NULL && hostent->h_addrtype == AF_INET && hostent->h_length == 4) { memmove (&qa->sin_addr, hostent->h_addr, 4); } else { GtkWidget *dialog; dialog = ve_hig_dialog_new (GTK_WINDOW (chooser) /* parent */, GTK_DIALOG_MODAL /* flags */, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, FALSE, _("Cannot find host"), _("I cannot find the host \"%s\", " "perhaps you have mistyped it."), name); if (RUNNING_UNDER_GDM) gdm_wm_center_window (GTK_WINDOW (dialog)); gdm_wm_no_login_focus_push (); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); gdm_wm_no_login_focus_pop (); return; /* not a valid address */ } #ifdef ENABLE_IPV6 if (have_ipv6) { memset (&sock6, 0, sizeof (struct sockaddr_in6)); sock6.sin6_family = AF_INET6; sock6.sin6_port = htons (XDM_UDP_PORT); status = gdm_addr_known ((char *)&qa6->sin6_addr, AF_INET6); if ( ! status) { queryaddr = g_slist_append (queryaddr, qa6); } if (IN6_IS_ADDR_V4MAPPED (&qa6->sin6_addr)) { memcpy (&qa->sin_addr, &(qa6->sin6_addr.s6_addr[12]), 4); host = gdm_host_known ((char *) &qa->sin_addr, AF_INET); } else host = gdm_host_known ((char *) &qa6->sin6_addr, AF_INET6); } else #endif { memset (&sock, 0, sizeof (struct sockaddr_in)); sock.sin_family = AF_INET; sock.sin_port = htons (XDM_UDP_PORT); status = gdm_addr_known ((char *)&qa->sin_addr, AF_INET); if ( ! status) { queryaddr = g_slist_append (queryaddr, qa); } host = gdm_host_known ((char *) &qa->sin_addr, AF_INET); } if (host != NULL) { GtkTreeIter iter = {0}; if (find_host_in_list (host, &iter)) { GtkTreeSelection *selection; GtkTreePath *path = gtk_tree_model_get_path (browser_model, &iter); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (browser)); gtk_tree_selection_select_iter (selection, &iter); gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (browser), path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free (path); gtk_widget_grab_focus (manage); } else { /* hmm, probably not willing, ping the host then for good measure */ #ifdef ENABLE_IPV6 if (have_ipv6) { memcpy (&sock6.sin6_addr, &qa6->sin6_addr, sizeof (qa6->sin6_addr)); XdmcpFlush (sockfd, &querybuf, (XdmcpNetaddr) &sock6, (int) sizeof (struct sockaddr_in6)); added6_addr = &qa6->sin6_addr; if (IN6_IS_ADDR_V4MAPPED (added6_addr)) added_addr = (struct in_addr *)&(qa6->sin6_addr.s6_addr[12]); } else #endif { sock.sin_addr.s_addr = qa->sin_addr.s_addr; XdmcpFlush (sockfd, &querybuf, (XdmcpNetaddr) &sock, (int)sizeof (struct sockaddr_in)); added_addr = &qa->sin_addr; } g_free (added_host); added_host = g_strdup (name); if (add_check_handler > 0) g_source_remove (add_check_handler); add_check_handler = g_timeout_add (ADD_TIMEOUT, add_check, NULL); } /* empty the text entry to indicate success */ gtk_entry_set_text (GTK_ENTRY (add_entry), ""); return; } #ifdef ENABLE_IPV6 if (have_ipv6) { added6_addr = &qa6->sin6_addr; if (IN6_IS_ADDR_V4MAPPED (added6_addr)) added_addr = (struct in_addr *)&(qa6->sin6_addr.s6_addr[12]); memcpy (&sock6.sin6_addr, &qa6->sin6_addr, sizeof (struct in6_addr)); XdmcpFlush (sockfd, &querybuf, (XdmcpNetaddr) &sock6, (int)sizeof (struct sockaddr_in6)); } else #endif { added_addr = &qa->sin_addr; /* and send out the query */ sock.sin_addr.s_addr = qa->sin_addr.s_addr; XdmcpFlush (sockfd, &querybuf, (XdmcpNetaddr) &sock, (int)sizeof (struct sockaddr_in)); } g_free (added_host); added_host = g_strdup (name); if (add_check_handler > 0) g_source_remove (add_check_handler); add_check_handler = g_timeout_add (ADD_TIMEOUT, add_check, NULL); /* empty the text entry to indicate success */ gtk_entry_set_text (GTK_ENTRY (add_entry), ""); if (have_ipv6) g_free (qa); #ifdef ENABLE_IPV6 else g_free (qa6); #endif } void gdm_chooser_add_entry_changed (void) { const char *name; name = gtk_entry_get_text (GTK_ENTRY (add_entry)); gtk_widget_set_sensitive (add_button, ! ve_string_empty (name)); } void gdm_chooser_cancel (void) { if (scan_time_handler > 0) { g_source_remove (scan_time_handler); scan_time_handler = 0; } closelog (); /* exit rather gtk_main_quit, it's just safer this way we don't have to worry about random whackiness happening */ exit (EXIT_SUCCESS); } void gdm_chooser_manage (GtkButton *button, gpointer data) { if (scan_time_handler > 0) { g_source_remove (scan_time_handler); scan_time_handler = 0; } if (curhost) gdm_chooser_choose_host (curhost->name); closelog(); /* exit rather gtk_main_quit, it's just safer this way we don't have to worry about random whackiness happening */ exit (EXIT_SUCCESS); } static void gdm_chooser_abort (const gchar *format, ...) { va_list args; gchar *s; va_start (args, format); s = g_strdup_vprintf (format, args); va_end (args); syslog (LOG_ERR, "%s", s); closelog (); exit (EXIT_FAILURE); } static void gdm_chooser_warn (const gchar *format, ...) { va_list args; gchar *s; va_start (args, format); s = g_strdup_vprintf (format, args); va_end (args); syslog (LOG_ERR, "%s", s); closelog (); } static void gdm_chooser_parse_config (void) { VeConfig *cfg; cfg = ve_config_get (GDM_CONFIG_FILE); GdmXineramaScreen = ve_config_get_int (cfg, GDM_KEY_XINERAMASCREEN); GdmGtkRC = ve_config_get_string (cfg, GDM_KEY_GTKRC); GdmGtkTheme = ve_config_get_string (cfg, GDM_KEY_GTK_THEME); GdmScanTime = ve_config_get_int (cfg, GDM_KEY_SCAN); GdmHostDefaultIcon = ve_config_get_string (cfg, GDM_KEY_HOST); GdmHostIconDir = ve_config_get_string (cfg, GDM_KEY_HOSTDIR); GdmIconMaxWidth = ve_config_get_int (cfg, GDM_KEY_ICONWIDTH); GdmIconMaxHeight = ve_config_get_int (cfg, GDM_KEY_ICONHEIGHT); GdmDebug = ve_config_get_bool (cfg, GDM_KEY_DEBUG); GdmBackgroundColor = ve_config_get_string (cfg, GDM_KEY_BACKGROUNDCOLOR); GdmBackgroundType = ve_config_get_int (cfg, GDM_KEY_BACKGROUNDTYPE); #ifdef ENABLE_IPV6 GdmMulticast = ve_config_get_bool (cfg, GDM_KEY_MULTICAST); GdmMulticastAddr = ve_config_get_string (cfg, GDM_KEY_MULTICAST_ADDR); #endif GdmAllowAdd = ve_config_get_bool (cfg, GDM_KEY_ALLOWADD); /* note that command line arguments will prevail over these */ GdmHosts = ve_config_get_string (cfg, GDM_KEY_HOSTS); GdmHostsOrig = g_strdup (GdmHosts); GdmBroadcast = ve_config_get_bool (cfg, GDM_KEY_BROADCAST); /* if broadcasting, then append BROADCAST to hosts */ if (GdmBroadcast) { if (ve_string_empty (GdmHosts)) { g_free (GdmHosts); GdmHosts = "BROADCAST"; } else { char *tmp = g_strconcat (GdmHosts, ",BROADCAST", NULL); g_free (GdmHosts); GdmHosts = tmp; } } #ifdef ENABLE_IPV6 if (GdmMulticast) { if (ve_string_empty (GdmHosts)) { g_free (GdmHosts); GdmHosts = "MULTICAST"; } else { GdmHosts = g_strconcat (GdmHosts, ",MULTICAST", NULL); } } #endif if (GdmScanTime < 1) GdmScanTime = 1; if (GdmIconMaxWidth < 0) GdmIconMaxWidth = 128; if (GdmIconMaxHeight < 0) GdmIconMaxHeight = 128; } static void host_selected (GtkTreeSelection *selection, gpointer data) { GtkTreeModel *tm = NULL; GtkTreeIter iter = {0}; curhost = NULL; if (gtk_tree_selection_get_selected (selection, &tm, &iter)) { gtk_tree_model_get (tm, &iter, CHOOSER_LIST_HOST_COLUMN, &curhost, -1); } gtk_widget_set_sensitive (manage, curhost != NULL); } static void row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column) { if (curhost != NULL) gdm_chooser_manage (NULL, NULL); } void display_chooser_information (void) { GtkWidget *dialog; /* How to get HIG compliance? */ dialog = gtk_message_dialog_new (GTK_WINDOW (chooser) /* parent */, GTK_DIALOG_MODAL /* flags */, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, _("The main area of this application shows the hosts on " "the local network that have \"XDMCP\" enabled. This " "allows users to login remotely to other machines as " "if they were logged on using the console.\n\n" "You can rescan the network for new hosts by clicking " "\"Refresh\". When you have selected a host click " "\"Connect\" to open a session to that machine.")); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); if (RUNNING_UNDER_GDM) gdm_wm_center_window (GTK_WINDOW (dialog)); gdm_wm_no_login_focus_push (); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); gdm_wm_no_login_focus_pop (); } static void gdm_chooser_gui_init (void) { int width; int height; GtkTreeSelection *selection; GtkTreeViewColumn *column; glade_helper_add_glade_directory (GDM_GLADE_DIR); glade_helper_search_gnome_dirs (FALSE); /* Enable theme */ if (RUNNING_UNDER_GDM) { const char *theme_name; if( ! ve_string_empty (GdmGtkRC)) gtk_rc_parse (GdmGtkRC); theme_name = g_getenv ("GDM_GTK_THEME"); if (ve_string_empty (theme_name)) theme_name = GdmGtkTheme; if ( ! ve_string_empty (theme_name)) { gdm_set_theme (theme_name); } } /* Load default host image */ if (access (GdmHostDefaultIcon, R_OK) != 0) { gdm_chooser_warn (_("Can't open default host icon: %s"), GdmHostDefaultIcon); /* bogus image */ defhostimg = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE /* has_alpha */, 8 /* bits_per_sample */, 48 /* width */, 48 /* height */); } else { defhostimg = gdk_pixbuf_new_from_file (GdmHostDefaultIcon, NULL); } /* Main window */ chooser_app = glade_helper_load ("gdmchooser.glade", "gdmchooser_main", GTK_TYPE_DIALOG, FALSE /* dump_on_destroy */); glade_xml_signal_autoconnect (chooser_app); chooser = glade_helper_get (chooser_app, "gdmchooser_main", GTK_TYPE_DIALOG); manage = glade_helper_get (chooser_app, "connect_button", GTK_TYPE_BUTTON); rescan = glade_helper_get (chooser_app, "rescan_button", GTK_TYPE_BUTTON); cancel = glade_helper_get (chooser_app, "quit_button", GTK_TYPE_BUTTON); status_label = glade_helper_get (chooser_app, "status_label", GTK_TYPE_LABEL); add_entry = glade_helper_get (chooser_app, "add_entry", GTK_TYPE_ENTRY); add_button = glade_helper_get (chooser_app, "add_button", GTK_TYPE_BUTTON); browser = glade_helper_get (chooser_app, "chooser_iconlist", GTK_TYPE_TREE_VIEW); gtk_dialog_set_has_separator (GTK_DIALOG (chooser), FALSE); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (browser), TRUE); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (browser)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); g_signal_connect (selection, "changed", G_CALLBACK (host_selected), NULL); g_signal_connect (browser, "row_activated", G_CALLBACK (row_activated), NULL); browser_model = (GtkTreeModel *)gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); gtk_tree_view_set_model (GTK_TREE_VIEW (browser), browser_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 (browser), 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 (browser), column); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (browser_model), CHOOSER_LIST_LABEL_COLUMN, GTK_SORT_ASCENDING); if ( ! GdmAllowAdd) { GtkWidget *w = glade_helper_get (chooser_app, "add_hbox", GTK_TYPE_HBOX); gtk_widget_hide (w); } gtk_window_get_size (GTK_WINDOW (chooser), &width, &height); if (RUNNING_UNDER_GDM) { if (width > gdm_wm_screen.width) width = gdm_wm_screen.width; if (height > gdm_wm_screen.height) height = gdm_wm_screen.height; } else { if (width > gdk_screen_width ()) width = gdk_screen_width (); if (height > gdk_screen_height ()) height = gdk_screen_height (); } gtk_widget_set_size_request (GTK_WIDGET (chooser), width, height); gtk_window_set_default_size (GTK_WINDOW (chooser), width, height); gtk_window_resize (GTK_WINDOW (chooser), width, height); /* cursor blinking is evil on remote displays, don't do it forever */ gdm_setup_blinking (); gdm_setup_blinking_entry (add_entry); if (RUNNING_UNDER_GDM) { gtk_widget_show_now (chooser); gdm_wm_center_window (GTK_WINDOW (chooser)); } } static gboolean string_same (VeConfig *config, const char *cur, const char *key) { char *val = ve_config_get_string (config, key); if (strcmp (ve_sure_string (cur), ve_sure_string (val)) == 0) { g_free (val); return TRUE; } else { g_free (val); return FALSE; } } static gboolean bool_same (VeConfig *config, gboolean cur, const char *key) { gboolean val = ve_config_get_bool (config, key); if (ve_bool_equal (cur, val)) { return TRUE; } else { return FALSE; } } static gboolean int_same (VeConfig *config, int cur, const char *key) { int val = ve_config_get_int (config, key); if (cur == val) { return TRUE; } else { return FALSE; } } static gboolean gdm_reread_config (int sig, gpointer data) { VeConfig *config; /* reparse config stuff here. At least ones we care about */ config = ve_config_get (GDM_CONFIG_FILE); /* FIXME: The following is evil, we should update on the fly rather * then just restarting */ /* Also we may not need to check ALL those keys but just a few */ if ( ! string_same (config, GdmHostsOrig, GDM_KEY_HOSTS) || ! string_same (config, GdmGtkRC, GDM_KEY_GTKRC) || ! string_same (config, GdmGtkTheme, GDM_KEY_GTK_THEME) || ! string_same (config, GdmHostDefaultIcon, GDM_KEY_HOST) || ! string_same (config, GdmHostIconDir, GDM_KEY_HOSTDIR) || ! int_same (config, GdmXineramaScreen, GDM_KEY_XINERAMASCREEN) || ! int_same (config, GdmIconMaxWidth, GDM_KEY_ICONWIDTH) || ! int_same (config, GdmIconMaxHeight, GDM_KEY_ICONHEIGHT) || ! int_same (config, GdmScanTime, GDM_KEY_SCAN) || ! bool_same (config, GdmDebug, GDM_KEY_DEBUG)) { if (RUNNING_UNDER_GDM) { /* Set busy cursor */ setup_cursor (GDK_WATCH); gdm_wm_save_wm_order (); } /* we don't need to tell the slave that we're restarting it doesn't care about our state. Unlike with the greeter */ execvp (stored_argv[0], stored_argv); _exit (DISPLAY_REMANAGE); } /* we only use the color and do it for all types except NONE */ if ( ! string_same (config, GdmBackgroundColor, GDM_KEY_BACKGROUNDCOLOR) || ! int_same (config, GdmBackgroundType, GDM_KEY_BACKGROUNDTYPE)) { set_background (); } return TRUE; } static void gdm_chooser_signals_init (void) { struct sigaction hup; struct sigaction term; sigset_t mask; ve_signal_add (SIGHUP, gdm_reread_config, NULL); hup.sa_handler = ve_signal_notify; hup.sa_flags = 0; sigemptyset(&hup.sa_mask); sigaddset (&hup.sa_mask, SIGCHLD); term.sa_handler = (void *) gdm_chooser_cancel; term.sa_flags = 0; sigemptyset (&term.sa_mask); if (sigaction (SIGHUP, &hup, NULL) < 0) gdm_chooser_abort (_("%s: Error setting up %s signal handler: %s"), "gdm_signals_init", "HUP", strerror (errno)); if (sigaction (SIGINT, &term, NULL) < 0) gdm_chooser_abort (_("%s: Error setting up %s signal handler: %s"), "gdm_signals_init", "INT", strerror (errno)); if (sigaction (SIGTERM, &term, NULL) < 0) gdm_chooser_abort (_("%s: Error setting up %s signal handler: %s"), "gdm_signals_init", "TERM", strerror (errno)); sigfillset (&mask); sigdelset (&mask, SIGTERM); sigdelset (&mask, SIGHUP); sigdelset (&mask, SIGINT); if (sigprocmask (SIG_SETMASK, &mask, NULL) == -1) gdm_chooser_abort (_("Could not set signal mask!")); } struct poptOption xdm_options [] = { { "xdmaddress", '\0', POPT_ARG_STRING | POPT_ARGFLAG_ONEDASH, &xdm_address, 0, N_("Socket for xdm communication"), N_("SOCKET") }, { "clientaddress", '\0', POPT_ARG_STRING | POPT_ARGFLAG_ONEDASH, &client_address, 0, N_("Client address to return in response to xdm"), N_("ADDRESS") }, { "connectionType", '\0', POPT_ARG_INT|POPT_ARGFLAG_ONEDASH, &connection_type, 0, N_("Connection type to return in response to xdm"), N_("TYPE") }, POPT_AUTOHELP { NULL, 0, 0, NULL, 0} }; static void set_background (void) { if (GdmBackgroundType != GDM_BACKGROUND_NONE) { GdkColor color; GdkColormap *colormap; if (ve_string_empty (GdmBackgroundColor) || ! gdk_color_parse (GdmBackgroundColor, &color)) { gdk_color_parse ("#007777", &color); } colormap = gdk_drawable_get_colormap (gdk_get_default_root_window ()); /* paranoia */ if (colormap != NULL) { gboolean success; gdk_error_trap_push (); gdk_colormap_alloc_colors (colormap, &color, 1, FALSE, TRUE, &success); gdk_window_set_background (gdk_get_default_root_window (), &color); gdk_window_clear (gdk_get_default_root_window ()); gdk_flush (); gdk_error_trap_pop (); } } } static gboolean gdm_event (GSignalInvocationHint *ihint, guint n_param_values, const GValue *param_values, gpointer data) { GdkEvent *event; /* HAAAAAAAAAAAAAAAAACK */ /* Since the user has not logged in yet and may have left/right * mouse buttons switched, we just translate every right mouse click * to a left mouse click */ if (n_param_values != 2 || !G_VALUE_HOLDS (¶m_values[1], GDK_TYPE_EVENT)) return FALSE; event = g_value_get_boxed (¶m_values[1]); if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) && event->button.button == 3) event->button.button = 1; return TRUE; } int main (int argc, char *argv[]) { char **hosts; poptContext ctx; int nextopt; const char *gdm_version; int i; stored_argv = g_new0 (char *, argc + 1); for (i = 0; i < argc; i++) stored_argv[i] = g_strdup (argv[i]); stored_argv[i] = NULL; stored_argc = argc; if (g_getenv ("RUNNING_UNDER_GDM") != NULL) RUNNING_UNDER_GDM = TRUE; openlog ("gdmchooser", LOG_PID, LOG_DAEMON); bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); gtk_init (&argc, &argv); ctx = poptGetContext ("gdm", argc, (const char **) argv, xdm_options, 0); while ((nextopt = poptGetNextOpt (ctx)) > 0 || nextopt == POPT_ERROR_BADOPT) /* do nothing */ ; if (nextopt != -1) { g_print (_("Error on option %s: %s.\nRun '%s --help' to see a full list of available command line options.\n"), poptBadOption (ctx, 0), poptStrerror (nextopt), argv[0]); exit (1); } /* Should be a watch already, but just in case */ if (RUNNING_UNDER_GDM) setup_cursor (GDK_WATCH); glade_init(); gdm_chooser_parse_config(); if (RUNNING_UNDER_GDM) gdm_wm_screen_init (GdmXineramaScreen); gdm_version = g_getenv ("GDM_VERSION"); if (RUNNING_UNDER_GDM && gdm_version != NULL && strcmp (gdm_version, VERSION) != 0) { GtkWidget *dialog; gdm_wm_init (0); gdm_wm_focus_new_windows (TRUE); dialog = ve_hig_dialog_new (NULL /* parent */, GTK_DIALOG_MODAL /* flags */, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, FALSE /* markup */, _("Cannot run chooser"), _("The chooser version (%s) does not match the daemon " "version (%s). " "You have probably just upgraded gdm. " "Please restart the gdm daemon or reboot the computer."), VERSION, gdm_version); gtk_widget_show_all (dialog); gdm_wm_center_window (GTK_WINDOW (dialog)); setup_cursor (GDK_LEFT_PTR); gtk_dialog_run (GTK_DIALOG (dialog)); return EXIT_SUCCESS; } gdm_chooser_gui_init(); gdm_chooser_signals_init(); if (RUNNING_UNDER_GDM) set_background (); hosts = (char **)poptGetArgs (ctx); /* when no hosts on the command line, take them from the config */ if (hosts == NULL || hosts[0] == NULL) { int i; hosts = g_strsplit (GdmHosts, ",", -1); for (i = 0; hosts != NULL && hosts[i] != NULL; i++) { g_strstrip (hosts[i]); } } gdm_chooser_xdmcp_init (hosts); poptFreeContext (ctx); if (RUNNING_UNDER_GDM) { guint sid = g_signal_lookup ("event", GTK_TYPE_WIDGET); g_signal_add_emission_hook (sid, 0 /* detail */, gdm_event, NULL /* data */, NULL /* destroy_notify */); } gtk_widget_queue_resize (chooser); gtk_widget_show_now (chooser); if (RUNNING_UNDER_GDM) gdm_wm_center_window (GTK_WINDOW (chooser)); if (RUNNING_UNDER_GDM && /* can it ever happen that it'd be NULL here ??? */ chooser->window != NULL) { gdm_wm_init (GDK_WINDOW_XWINDOW (chooser->window)); /* Run the focus, note that this will work no matter what * since gdm_wm_init will set the display to the gdk one * if it fails */ gdm_wm_focus_window (GDK_WINDOW_XWINDOW (chooser->window)); } if (GdmAllowAdd) gtk_widget_grab_focus (add_entry); gdm_chooser_add_entry_changed (); if (RUNNING_UNDER_GDM) { gdm_wm_restore_wm_order (); setup_cursor (GDK_LEFT_PTR); } gtk_main(); exit (EXIT_SUCCESS); } /* EOF */