Logo Search packages:      
Sourcecode: netapplet version File versions  Download package

netapplet.c

/*
 * src/netapplet.c - the network control applet
 *
 * Copyright (C) 2004 Novell, Inc.
 *
 * Licensed under the GNU GPL v2.  See COPYING
 */
#define _GNU_SOURCE
#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <errno.h>
#include <gnome.h>
#include <glade/glade-xml.h>
#include <gnome-keyring.h>

#include "eggtrayicon.h"
#include "netcommon.h"

#ifdef ENABLE_NLS
# include <libintl.h>
# define _(String) gettext (String)
# ifdef gettext_noop
#   define N_(String) gettext_noop (String)
# else
#   define N_(String) (String)
# endif
#else
# define _(String)  (String)
# define N_(String) (String)
#endif

#define ETHERNET_ICON         "gnome-dev-ethernet"
#define DIALUP_ICON           "gnome-dev-modem"
#define WIRELESS_ICON_0         "wireless-tower-0"
#define WIRELESS_ICON_1         "wireless-tower-1"
#define WIRELESS_ICON_2         "wireless-tower-2"
#define WIRELESS_ICON_3         "wireless-tower-3"
#define WIRELESS_ICON_4         "wireless-tower-4"
#define DISCONNECT_ICON       "stock_calc-cancel" // should be stock_calc-cancel
#define BROKEN_ICON           "stock_calc-cancel"
#define UNKNOWN_ICON            "stock_unknown" // shoulb be stock_unknown

#define GLADE_APPLET          GLADEDIR "/netapplet.glade"

#define YAST2_ARGV { "/opt/kde3/bin/kdesu", "-n", "/sbin/yast2", "lan", NULL }
#define NET_ADMIN_ARGV { "network-admin", NULL }

typedef struct {
      char *interface;  /* interface name, e.g. eth0 */
      const char *type; /* TYPE_{DIALUP,ETHERNET,WIRELESS} */
      const char *icon; /* icon id */
} Connection;

typedef struct {
      double strength;  /* wireless quality, should be [0.0, 1.0) */
      char *essid;            /* ESSID name */
      gboolean is_encrypted;  /* is this network encrypted ? */
} Accesspoint;

typedef struct {
      GIOChannel *channel;          /* our GIO to the daemon */
      GHashTable *handlers;         /* hash of msg -> handler */
      gboolean authorized;
      gboolean active_scanning;

      EggTrayIcon *tray_icon;
      GtkWidget *button;
      GtkWidget *icon;
      GtkTooltips *tooltips;
      GtkWidget *popup_menu;
      const char *default_keyring;
      const char *icon_name;
      int last_panel_h;

      GSList *connections;          /* of Connection */
      Connection *active;           /* active connection */

      GSList *accesspoints;         /* of Accesspoints */
      Accesspoint *active_ap;         /* Active access point */
      double link_quality;          /* global link quality [0,1] */
} NetApplet;

static NetApplet *netapplet;

static guint update_id = 0;
static gboolean window_shown = FALSE;

static void populate_popup_menu (void);

static void
netapplet_set_icon (const char *icon_name)
{
      GdkPixbuf *pixbuf;
      char *tip;
      int panel_w;
      int panel_h;
      int icon_size;

      gtk_window_get_size (
            GTK_WINDOW (gtk_widget_get_toplevel (netapplet->button)),
            &panel_w, &panel_h);

      if (netapplet->icon_name != NULL &&
          strcmp (netapplet->icon_name, icon_name) == 0 &&
          panel_h == netapplet->last_panel_h)
            return;

      netapplet->icon_name = icon_name;
      netapplet->last_panel_h = panel_h;

      if (panel_h < 30)
            icon_size = 16;
      else
            icon_size = 24;

      pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
                                 icon_name, icon_size, 0, NULL);
      
      /*
       * Scale the icon rather than clip it if our allocation just isn't
       * what we want it to be.
       */
      if (GTK_WIDGET_REALIZED (netapplet->icon) &&
          netapplet->icon->allocation.height < icon_size) {
            int new_size = netapplet->icon->allocation.height;
            GdkPixbuf *new_pixbuf;

            new_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
                                          new_size, new_size,
                                          GDK_INTERP_BILINEAR);

            g_object_unref (pixbuf);
            pixbuf = new_pixbuf;
      }

      gtk_image_set_from_pixbuf (GTK_IMAGE (netapplet->icon), pixbuf);
      g_object_unref (pixbuf);

      if (netapplet->tooltips == NULL)
            netapplet->tooltips = gtk_tooltips_new ();

      if (netapplet->active == NULL)
            tip = g_strdup (_("Disconnected"));
      else if (strcmp (netapplet->active->type, "Ethernet") == 0)
            tip = g_strdup (_("Ethernet connection"));
      else if (strcmp (netapplet->active->type, "Dialup") == 0)
            tip = g_strdup (_("Dial-up connection"));
      else if (strcmp (netapplet->active->type, "Wireless") == 0) {
            if (netapplet->active_ap == NULL)
                  tip = g_strdup ("Wireless connection");
            else if (!netapplet->active_ap->is_encrypted) {
                  tip = g_strdup_printf ("Wireless connection: %d%%",
                        (int)(netapplet->active_ap->strength * 100));
            } else
                  tip = g_strdup_printf ("Wireless connection (secure): "
                   "%d%%", (int)(netapplet->active_ap->strength * 100));
      } else {
            g_warning ("icon_name=%s\n", icon_name);
            g_assert_not_reached ();
            return; /* bleh */
      }

      gtk_tooltips_set_tip (netapplet->tooltips, netapplet->button,
                        tip, NULL);
      g_free (tip);
}

static void
netapplet_set_connection (Connection *conn)
{
      netcommon_send_message (netapplet->channel, "change", conn->interface,
                        NULL);
}

static void
netapplet_set_essid (const char *essid, const char *key)
{
      if (netapplet->active) {
            char *escaped_essid;

            escaped_essid = netcommon_escape_argument (essid);

            netcommon_send_message (netapplet->channel, "essid",
                              netapplet->active->interface,
                              escaped_essid, key, NULL);

            g_free (escaped_essid);
      }
}

static void
netapplet_get_connections (void)
{
      netcommon_send_message (netapplet->channel, "list_interfaces", NULL);
}

static void
netapplet_get_active (void)
{
      netcommon_send_message (netapplet->channel, "get_active", NULL);
}

static void
netapplet_get_wireless (const char *interface)
{
      netcommon_send_message (netapplet->channel, "wireless", interface,
                        NULL);
}

static void
netapplet_get_accesspoints (const char *interface)
{
      netcommon_send_message (netapplet->channel, "accesspoints", interface,
                        NULL);
}

static void
conn_free (Connection *conn)
{
      g_free (conn->interface);
      g_free (conn);
}

static void
netapplet_free_connections (void)
{
      g_slist_foreach (netapplet->connections, (GFunc) conn_free, NULL);
      g_slist_free (netapplet->connections);
      netapplet->connections = NULL;
      netapplet->active = NULL;
}

static void
ap_free (Accesspoint *ap)
{
      g_free (ap->essid);
      g_free (ap);
}

static void
netapplet_free_accesspoints (void)
{
      g_slist_foreach (netapplet->accesspoints, (GFunc) ap_free, NULL);
      g_slist_free (netapplet->accesspoints);
      netapplet->accesspoints = NULL;
      netapplet->active_ap = NULL;
}

static Connection *
netapplet_get_connection_by_interface (const char *interface)
{
      GSList *iter;

      for (iter = netapplet->connections; iter != NULL; iter = iter->next) {
            Connection *conn = iter->data;

            if (strcmp (conn->interface, interface) == 0)
                  return conn;
      }

      return NULL;
}

static Accesspoint *
netapplet_get_accesspoint_by_essid (const char *essid)
{
      GSList *iter;

      for (iter = netapplet->accesspoints; iter != NULL; iter = iter->next) {
            Accesspoint *ap = iter->data;

            if (strcmp (ap->essid, essid) == 0)
                  return ap;
      }

      return NULL;
}

static gboolean
daemon_disappeared_cb (GIOChannel *channel G_GNUC_UNUSED,
                   GIOCondition cond G_GNUC_UNUSED,
                   gpointer user_data G_GNUC_UNUSED)
{
      netapplet_set_icon (BROKEN_ICON);
      netapplet_free_connections ();
      netapplet_free_accesspoints ();
      populate_popup_menu ();

      return FALSE;
}

static void
netapplet_connect (void)
{
      int sockfd;
      struct sockaddr_un sa_un;

      g_return_if_fail (netapplet != NULL);
      g_return_if_fail (netapplet->tray_icon != NULL);

      sockfd = socket (PF_UNIX, SOCK_STREAM, 0);
      if (sockfd < 0) {
            GtkWidget *dialog;

            dialog = gtk_message_dialog_new_with_markup (
                  NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                  _("<span weight=\"bold\" size=\"larger\">"
                    "Network switching is currently unavailable"
                  "</span>\n\n"
                  "The \"netdaemon\" service is not running"));
            gtk_dialog_run (GTK_DIALOG (dialog));
            gtk_widget_destroy (dialog);

            netapplet_set_icon (BROKEN_ICON);

            return;
      }

      memset (&sa_un, '0', sizeof (sa_un));
      sa_un.sun_family = AF_UNIX;
      snprintf (sa_un.sun_path, sizeof (sa_un.sun_path), NETDAEMON_SOCKET);

      if (connect (sockfd, (struct sockaddr *) &sa_un, sizeof (sa_un)) < 0) {
            GtkWidget *dialog;

            dialog = gtk_message_dialog_new_with_markup (
                  NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                  _("<span weight=\"bold\" size=\"larger\">"
                    "Network switching is currently unavailable"
                    "</span>\n\n"
                    "The \"netdaemon\" service is not running"));
            gtk_dialog_run (GTK_DIALOG (dialog));
            gtk_widget_destroy (dialog);

            netapplet_set_icon (BROKEN_ICON);

            return;
      }

      netapplet->channel = g_io_channel_unix_new (sockfd);
      g_io_channel_set_encoding (netapplet->channel, NULL, NULL);

      netcommon_watch_channel (netapplet->channel, netapplet->handlers);

      g_io_add_watch (netapplet->channel, G_IO_HUP,
                  daemon_disappeared_cb, NULL);

      netapplet->authorized = TRUE;

      netapplet_get_connections ();
      netapplet_get_active ();
}

static void
connection_activate_cb (GtkMenuItem *mi G_GNUC_UNUSED,
                  gpointer user_data G_GNUC_UNUSED)
{
      const char *interface;
      Connection *conn;

      interface = g_object_get_data (G_OBJECT (mi), "interface");
      conn = netapplet_get_connection_by_interface (interface);
      g_assert (conn != NULL);

      netapplet_set_connection (conn);
}

static void
disconnect_cb (GtkMenuItem *mi G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
{
      GtkWidget *dialog;

      dialog = gtk_message_dialog_new_with_markup (
                  NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
                  _("<span weight=\"bold\" size=\"larger\">"
                    "Do you want to disconnect all network connections?"
                    "</span>\n\n"
                    "Applications which use the network, such as web "
                    "browsers and email programs, will likely stop "
                    "working while you are disconnected."));
      gtk_dialog_add_buttons (GTK_DIALOG (dialog),
                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                        "_Disconnect", GTK_RESPONSE_OK,
                        NULL);

      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
            netcommon_send_message (netapplet->channel, "disconnect", NULL);

      gtk_widget_destroy (dialog);
}

typedef void (*GDICallback) (gpointer user_data);

typedef struct {
      gboolean create_if_needed;
      GDICallback callback;
      gpointer user_data;
} GetDefaultInfo;

static void
set_default_keyring_cb (GnomeKeyringResult result,
                  gpointer data)
{
      GetDefaultInfo *gdi = data;

      if (result != GNOME_KEYRING_RESULT_OK) {
            g_warning (_("Error trying to set default keyring: %d"),
                     result);
            g_free (gdi);
            return;
      }

      netapplet->default_keyring = "default";

      gdi->callback (gdi->user_data);
      g_free (gdi);
}


static void
create_keyring_cb (GnomeKeyringResult result, gpointer data)
{
      GetDefaultInfo *gdi = data;

      if (result != GNOME_KEYRING_RESULT_OK) {
            g_warning (_("Error trying to create keyring: %d"),
                     result);
            g_free (gdi);
            return;
      }

      gnome_keyring_set_default_keyring ("default", set_default_keyring_cb,
                                 gdi, NULL);
}

static void
get_default_keyring_cb (GnomeKeyringResult result, const char *string,
                  gpointer data)
{
      GetDefaultInfo *gdi = data;
      
      if (result != GNOME_KEYRING_RESULT_OK) {
            g_warning (_("Error trying to get default keyring: %d"),
                     result);
            g_free (gdi);
            return;
      }

      /*
       * Ugh.  If there isn't a default keyring we have to create one
       * ourselves, which is incredibly inconvenient.
       */
      if (string == NULL && gdi->create_if_needed) {
            gnome_keyring_create ("default",
                              NULL, /* ask user */
                              create_keyring_cb,
                              gdi, NULL);
      } else {
            /* Note! "string" could still be NULL */
            netapplet->default_keyring = g_strdup (string);
            gdi->callback (gdi->user_data);
            g_free (gdi);
      }
}

static void
get_default_keyring (gboolean create_if_needed,
                 GDICallback callback,
                 gpointer user_data)
{
      GetDefaultInfo *gdi = g_new0 (GetDefaultInfo, 1);

      gdi->create_if_needed = create_if_needed;
      gdi->callback = callback;
      gdi->user_data = user_data;

      /*
       * Ugh.  We have to try to get the default keyring and stick it
       * in some ugly-ass global variable because some keyring functions 
       * (notably gnome_keyring_unlock() won't take NULL to mean the
       * default keyring.
       *
       * This is fixed in 0.3.x keyring, so hopefully will be
       * temporary.
       */
      gnome_keyring_get_default_keyring (get_default_keyring_cb, gdi, NULL);
}

static void
create_item_cb (GnomeKeyringResult result,
            guint32 val G_GNUC_UNUSED,
            gpointer data G_GNUC_UNUSED)
{
      if (result != GNOME_KEYRING_RESULT_OK)
            g_warning (_("Unable to save to keyring!  Err: %d"), result);
}

static void
create_item (const char *essid, const char *key)
{
      GnomeKeyringAttributeList *attributes;
      GnomeKeyringAttribute attr;
      char *name;

      attributes = gnome_keyring_attribute_list_new ();

      attr.name = g_strdup ("essid");
      attr.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attr.value.string = g_strdup (essid);
      g_array_append_val (attributes, attr);

      name = g_strdup_printf (_("Password for network \"%s\""), essid);

      gnome_keyring_item_create (netapplet->default_keyring,
                           GNOME_KEYRING_ITEM_GENERIC_SECRET,
                           name,
                           attributes,
                           key,
                           TRUE,
                           create_item_cb, NULL, NULL);

      g_free (name);
}

typedef struct {
      char *essid;
      char *key;
} SaveInfo;

static void
queue_create (gpointer user_data)
{
      SaveInfo *sinfo = user_data;

      if (netapplet->default_keyring != NULL)
            create_item (sinfo->essid, sinfo->key);

      g_free (sinfo->essid);
      g_free (sinfo->key);
      g_free (sinfo);
}

static void
keyring_save (const char *essid, const char *key)
{
      SaveInfo *sinfo;

      sinfo = g_new0 (SaveInfo, 1);
      sinfo->essid = g_strdup (essid);
      sinfo->key = g_strdup (key);

      if (netapplet->default_keyring == NULL)
            get_default_keyring (TRUE, queue_create, sinfo);
      else
            create_item (essid, key);
}

static void
entry_changed_cb (GtkEntry *entry, gpointer user_data)
{
      GtkWidget *widget = GTK_WIDGET (user_data);
      const char *text;

      text = gtk_entry_get_text (entry);

      if (text == NULL || *text == '\0')
            gtk_widget_set_sensitive (widget, FALSE);
      else
            gtk_widget_set_sensitive (widget, TRUE);
}

static void
show_key_dialog (const char *essid)
{
      GladeXML *xml;
      GtkWidget *dialog, *entry_key, *checkbox_keyring;
      char *old_essid;

      xml = glade_xml_new (GLADE_APPLET, "dialog-key", NULL);
      dialog = glade_xml_get_widget (xml, "dialog-key");
      
      checkbox_keyring = glade_xml_get_widget (xml, "checkbutton-keyring");
      gtk_widget_set_sensitive (checkbox_keyring, FALSE);
            
      entry_key = glade_xml_get_widget (xml, "entry-key");
      g_signal_connect (entry_key, "changed",
                    G_CALLBACK (entry_changed_cb),
                    checkbox_keyring);

      old_essid = g_strdup (essid);

      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
            const char *key;

            key = gtk_entry_get_text (GTK_ENTRY (entry_key));
            key = netcommon_verify_string (key);
            if (!key) {
                  GtkWidget *error_dialog;

                  error_dialog = gtk_message_dialog_new_with_markup (
                        NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                        _("<span weight=\"bold\" size=\"larger\">"
                          "Invalid Encryption Key: "
                          "</span>\n\n"
                          "Key contains illegal characters!"));
                  gtk_dialog_run (GTK_DIALOG (error_dialog));
                  gtk_widget_destroy (error_dialog);
            } else {
                  netapplet_set_essid (old_essid, key);
                  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
                              (checkbox_keyring)))
                  keyring_save (old_essid, key);
            }
      }

      g_free (old_essid);
      gtk_widget_destroy (dialog);
      g_object_unref (xml);
}

static void
get_item_cb (GnomeKeyringResult result, GList *list, gpointer data)
{
      char *essid = data;
      GnomeKeyringFound *found;

      if (result != GNOME_KEYRING_RESULT_OK) {
            show_key_dialog (essid);
            return;
      }

      /* Not found in keyring */
      if (list == NULL) {
            show_key_dialog (essid);
            return;
      }

      found = (GnomeKeyringFound *) list->data;

      netapplet_set_essid (essid, found->secret);

      g_free (essid);
}

static void
get_item (const char *essid)
{
      gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
                           get_item_cb,
                           g_strdup (essid),
                           NULL,
                           "essid",
                           GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
                           essid,
                           NULL);
}

static void
queue_get (gpointer user_data)
{
      const char *essid = user_data;

      if (netapplet->default_keyring != NULL)
            get_item (essid);
      else
            show_key_dialog (essid);
}

static void
keyring_load (const char *essid)
{
      if (netapplet->default_keyring == NULL)
            get_default_keyring (FALSE, queue_get, (gpointer) essid);
      else
            get_item (essid);
}

static void
essid_activate_cb (GtkMenuItem *mi G_GNUC_UNUSED,
               gpointer user_data G_GNUC_UNUSED)

{
      const char *essid;
      Accesspoint *ap;

      essid = g_object_get_data (G_OBJECT (mi), "essid");
      ap = netapplet_get_accesspoint_by_essid (essid);
      g_assert (ap);

      if (ap->is_encrypted)
            keyring_load (ap->essid);
      else
            netapplet_set_essid (ap->essid, "");
}

static void
other_essid_cb (GtkMenuItem *mi G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
{
      GtkWidget *dialog, *entry_key, *checkbox_keyring;
      GladeXML *xml;

      xml = glade_xml_new (GLADE_APPLET, "dialog-essid", NULL);
      dialog = glade_xml_get_widget (xml, "dialog-essid");

      checkbox_keyring = glade_xml_get_widget (xml, "checkbutton-keyring");
      gtk_widget_set_sensitive (checkbox_keyring, FALSE);

      entry_key = glade_xml_get_widget (xml, "entry-key");
      g_signal_connect (entry_key, "changed",
                    G_CALLBACK (entry_changed_cb), checkbox_keyring);

      if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
            GtkWidget *entry_essid;
            const char *essid, *key;

            entry_essid = glade_xml_get_widget (xml, "entry-essid");

            essid = gtk_entry_get_text (GTK_ENTRY (entry_essid));
            key = gtk_entry_get_text (GTK_ENTRY (entry_key));

            essid = netcommon_verify_string (essid);
            if (!essid || *essid == '\0') {
                  GtkWidget *error_dialog;

                  error_dialog = gtk_message_dialog_new_with_markup (
                        NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                        _("<span weight=\"bold\" size=\"larger\">"
                          "Invalid ESSID: "
                          "</span>\n\n"
                          "ESSID is blank or "
                          "contains illegal characters!"));
                  gtk_dialog_run (GTK_DIALOG (error_dialog));
                  gtk_widget_destroy (error_dialog);
                  goto out;
            }

            key = netcommon_verify_string (key);
            if (!key) {
                  GtkWidget *error_dialog;

                  error_dialog = gtk_message_dialog_new_with_markup (
                        NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                        _("<span weight=\"bold\" size=\"larger\">"
                        "Invalid Encryption Key:</span>\n\n"
                        "Key contains illegal characters!"));
                  gtk_dialog_run (GTK_DIALOG (error_dialog));
                  gtk_widget_destroy (error_dialog);
                  goto out;
            }

            if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
                              (checkbox_keyring)))
                  keyring_save (essid, key);
            netapplet_set_essid (essid, key);
      }

out:
      gtk_widget_destroy (dialog);
      g_object_unref (xml);
}

static GtkWidget *
get_label (GtkWidget *info_dialog, GladeXML *xml, const char *name)
{
      GtkWidget *label;

      if (xml != NULL) {
            label = glade_xml_get_widget (xml, name);
            g_object_set_data (G_OBJECT (info_dialog), name, label);
      } else
            label = g_object_get_data (G_OBJECT (info_dialog), name);

      return label;
}

static gboolean
update_info(GladeXML *xml)
{
      GtkWidget *info_dialog = NULL;
      char *addr = NULL, *mask = NULL, *broadcast = NULL;
      char *dest = NULL, *mac = NULL;
      GtkWidget *label;
      Connection *conn;
      struct ifreq ifr;
      int fd, flags;
      gboolean ret_val = TRUE;

      info_dialog = glade_xml_get_widget (xml, "dialog-information");

      conn = netapplet->active;
      if (!conn) {
            GtkWidget *error_dialog;

            error_dialog = gtk_message_dialog_new_with_markup (
                        window_shown ? GTK_WINDOW (info_dialog) : NULL,
                        0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                        _("<span weight=\"bold\" size=\"larger\">"
                          "Error displaying connection information: "
                          "</span>\n\n"
                          "No active connection!"));
            gtk_dialog_run (GTK_DIALOG (error_dialog));
            gtk_widget_destroy (error_dialog);
            return FALSE;
      }

      fd = socket (AF_INET, SOCK_DGRAM, 0);
      if (fd < 0) {
            GtkWidget *error_dialog;

            error_dialog = gtk_message_dialog_new_with_markup (
                        window_shown ? GTK_WINDOW (info_dialog) : NULL,
                        0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                        _("<span weight=\"bold\" size=\"larger\">"
                          "Error displaying connection information: "
                        "</span>\n\nUnable to open socket!"));
            gtk_dialog_run (GTK_DIALOG (error_dialog));
            gtk_widget_destroy (error_dialog);
            return FALSE;
      }

      ifr.ifr_addr.sa_family = AF_INET;

      g_strlcpy (ifr.ifr_name, conn->interface, sizeof (ifr.ifr_name));
      if (ioctl (fd, SIOCGIFADDR, &ifr) == 0)
            addr = g_strdup (inet_ntoa (((struct sockaddr_in *)
                              &ifr.ifr_addr)->sin_addr));

      g_strlcpy (ifr.ifr_name, conn->interface, sizeof (ifr.ifr_name));
      if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0) {
            GtkWidget *error_dialog;

            error_dialog = gtk_message_dialog_new_with_markup (
                        window_shown ? GTK_WINDOW (info_dialog) : NULL,
                        0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                        _("<span weight=\"bold\" size=\"larger\">"
                          "Error displaying information: "
                          "</span>\n\n"
                          "SIOCGIFFLAGS failed on socket!"));
            gtk_dialog_run (GTK_DIALOG (error_dialog));
            gtk_widget_destroy (error_dialog);
            ret_val = FALSE;
            goto out;
      }
      flags = ifr.ifr_flags;

      g_strlcpy (ifr.ifr_name, conn->interface, sizeof (ifr.ifr_name));
      if (flags & IFF_BROADCAST && ioctl (fd, SIOCGIFBRDADDR, &ifr) == 0)
            broadcast = g_strdup (inet_ntoa (((struct sockaddr_in *)
                              &ifr.ifr_broadaddr)->sin_addr));

      g_strlcpy (ifr.ifr_name, conn->interface, sizeof (ifr.ifr_name));
      if (ioctl (fd, SIOCGIFNETMASK, &ifr) == 0)
            mask = g_strdup (inet_ntoa (((struct sockaddr_in *)
                              &ifr.ifr_addr)->sin_addr));

      g_strlcpy (ifr.ifr_name, conn->interface, sizeof (ifr.ifr_name));
      if (flags & IFF_POINTOPOINT && ioctl (fd, SIOCGIFDSTADDR, &ifr) == 0)
            dest = g_strdup (inet_ntoa (((struct sockaddr_in *)
                              &ifr.ifr_dstaddr)->sin_addr));

      g_strlcpy (ifr.ifr_name, conn->interface, sizeof (ifr.ifr_name));
      if (ioctl (fd, SIOCGIFHWADDR, &ifr) == 0)
            mac = g_strdup_printf ("%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
                   (unsigned char) ifr.ifr_hwaddr.sa_data[0],
                   (unsigned char) ifr.ifr_hwaddr.sa_data[1],
                   (unsigned char) ifr.ifr_hwaddr.sa_data[2],
                   (unsigned char) ifr.ifr_hwaddr.sa_data[3],
                   (unsigned char) ifr.ifr_hwaddr.sa_data[4],
                   (unsigned char) ifr.ifr_hwaddr.sa_data[5]);

      label = get_label (info_dialog, xml, "label-interface");
      gtk_label_set_text (GTK_LABEL (label), conn->interface);

      label = get_label (info_dialog, xml, "label-type");
      gtk_label_set_text (GTK_LABEL (label), conn->type);

      label = get_label (info_dialog, xml, "label-ip-address");
      gtk_label_set_text (GTK_LABEL (label), addr);

      label = get_label (info_dialog, xml, "label-destination-address");
      if (flags & IFF_POINTOPOINT) {
            gtk_label_set_text (GTK_LABEL (label), dest);
            gtk_widget_show (label);
      } else
            gtk_widget_hide (label);

      label = get_label (info_dialog, xml,
                     "label-destination-address-label");
      if (flags & IFF_POINTOPOINT) {
            gtk_label_set_text (GTK_LABEL (label), dest);
            gtk_widget_show (label);
      } else
            gtk_widget_hide (label);

      label = get_label (info_dialog, xml, "label-broadcast-address");
      gtk_label_set_text (GTK_LABEL (label), broadcast);

      label = get_label (info_dialog, xml, "label-subnet-mask");
      gtk_label_set_text (GTK_LABEL (label), mask);

      label = get_label (info_dialog, xml, "label-hardware-address");
      gtk_label_set_text (GTK_LABEL (label), mac);

out:
      close (fd);
      g_free (addr);
      g_free (broadcast);
      g_free (mask);
      g_free (dest);
      g_free (mac);

      return ret_val;
}

static gboolean
update_cb (gpointer user_data)
{
    GladeXML *xml = user_data;
    if (update_info (xml)) {
      return TRUE;
    } else {
      update_id = 0;
      return FALSE;
    }
}

static void
show_info_cb (GtkMenuItem *mi G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
{
      static GladeXML *xml = NULL;
      GtkWidget *info_dialog = NULL;

      if (!xml) {
            xml = glade_xml_new (GLADE_APPLET, "dialog-information", NULL);
      }

      info_dialog = glade_xml_get_widget (xml, "dialog-information");

      if (window_shown) {
            if (!update_info(xml))
                  return;

            gtk_window_present (GTK_WINDOW (info_dialog));
            if (update_id == 0) {
                  update_id = g_timeout_add (1000, update_cb, xml);
            }
            return;
      }

      if (!update_info(xml))
          return;

      update_id = g_timeout_add (1000, update_cb, xml);

      window_shown = TRUE;
      gtk_window_present (GTK_WINDOW (info_dialog));
      gtk_dialog_run (GTK_DIALOG (info_dialog));
      gtk_widget_hide (GTK_WIDGET (info_dialog));
      window_shown = FALSE;

      if (update_id) {
          g_source_remove (update_id);
      }
      update_id = 0;
}

static void
configure_activate_cb (GtkMenuItem *mi G_GNUC_UNUSED,
                   gpointer user_data G_GNUC_UNUSED)
{
      GError *err = NULL;
      char *argv_suse[] = YAST2_ARGV; 
      char *argv_debian[] = NET_ADMIN_ARGV;
      char **argv;

      if (strcmp (get_platform (), DEBIAN_PLATFORM_NAME) == 0) {
        argv=argv_debian;
      } else {
        argv=argv_suse;
      }
      
      if (!g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err)) {
            GtkWidget *dialog;

            dialog = gtk_message_dialog_new_with_markup (
                  NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                  _("<span weight=\"bold\" size=\"larger\">"
                    "Network configuration could not be run"
                  "</span>\n\n%s"), err->message);
            gtk_dialog_run (GTK_DIALOG (dialog));
            gtk_widget_destroy (dialog);

            g_error_free (err);
      }
}

static void
quit_cb (GtkMenuItem *mi G_GNUC_UNUSED,
       gpointer user_data G_GNUC_UNUSED)
{
      if (netapplet->channel != NULL)
            g_io_channel_shutdown (netapplet->channel, TRUE, NULL);

      gtk_exit (0);
}

static void
remove_old_items (gpointer node, gpointer user_data)
{
      gtk_container_remove (GTK_CONTAINER (user_data), GTK_WIDGET (node));
}

static GtkWidget *
percentage_menu_item_new (const char *text, double percentage, gboolean locked)
{
      GtkWidget *menu_item;
      GtkWidget *box, *label, *progress;
      PangoContext *context;
      PangoFontMetrics *metrics;
      int ascent;

      menu_item = gtk_image_menu_item_new ();

      if (locked) {
            GtkWidget *image;

            image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
                                      GTK_ICON_SIZE_MENU);
            gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
                                     image);
      }

      box = gtk_hbox_new (FALSE, 6);
      gtk_container_add (GTK_CONTAINER (menu_item), box);
      gtk_widget_show (box);

      label = gtk_label_new (text);
      gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
      gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
      gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
      gtk_widget_show (label);

      progress = gtk_progress_bar_new ();
      gtk_progress_set_percentage (GTK_PROGRESS (progress), percentage);

      g_object_set_data (G_OBJECT (menu_item), "progress_widget", progress);

      context = gtk_widget_get_pango_context (progress);
      metrics = pango_context_get_metrics (context,
                  pango_context_get_font_description (context), NULL);

      ascent = pango_font_metrics_get_ascent (metrics) * 1.5 / PANGO_SCALE;
      gtk_widget_set_size_request (progress, ascent * 5, ascent);

      gtk_box_pack_end (GTK_BOX (box), progress, FALSE, TRUE, 0);
      gtk_widget_show (progress);

      return menu_item;
}

static void
populate_popup_menu (void)
{
      GtkWidget *mi, *image;
      GSList *iter;

      g_list_foreach (GTK_MENU_SHELL (netapplet->popup_menu)->children,
                  remove_old_items, netapplet->popup_menu);

      /* Build list of network connections ... */
      if (netapplet->connections)
            mi = gtk_menu_item_new_with_label (_("Network Connections"));
      else
            mi = gtk_menu_item_new_with_label (
                              _("No network connections available"));

      gtk_widget_set_sensitive (mi, FALSE);
      gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu), mi);
      gtk_widget_show (mi);

      for (iter = netapplet->connections; iter != NULL; iter = iter->next) {
            Connection *conn = iter->data;
            GdkPixbuf *pixbuf;
            char *display_name;

            if (conn == netapplet->active)
                  display_name = g_strdup_printf (_("%s: %s (active)"),
                                          conn->type,
                                          conn->interface);
            else
                  display_name = g_strdup_printf ("%s: %s", conn->type,
                                          conn->interface);
            mi = gtk_image_menu_item_new_with_label (display_name);
            g_free (display_name);

            pixbuf = gtk_icon_theme_load_icon (
                        gtk_icon_theme_get_default (),
                        conn->icon, 16, 0, NULL);
            image = gtk_image_new_from_pixbuf (pixbuf);
            gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi),
                                     image);
            gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu),
                               mi);
            g_object_set_data_full (G_OBJECT (mi), "interface",
                              g_strdup (conn->interface), g_free);
            g_signal_connect (mi, "activate",
                          G_CALLBACK (connection_activate_cb), NULL);

            gtk_widget_set_sensitive (mi, netapplet->authorized);
            gtk_widget_show (mi);
      }

      if (netapplet->connections) {
            image = gtk_image_new_from_stock (GTK_STOCK_NO,
                                      GTK_ICON_SIZE_MENU);
            mi = gtk_image_menu_item_new_with_label ("Disconnect");
            gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
            gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu),
                               mi);
            g_signal_connect (mi, "activate", G_CALLBACK (disconnect_cb),
                          NULL);
            gtk_widget_set_sensitive (mi, netapplet->authorized);
            gtk_widget_show (mi);
      }

      mi = gtk_separator_menu_item_new ();
      gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu), mi);
      gtk_widget_show (mi);

      /* Build list of wireless accesspoints ... */
      if (netapplet->accesspoints)
            mi = gtk_menu_item_new_with_label (_("Wireless Networks"));
      else if (!(netapplet->active &&
               strcmp (netapplet->active->type, "Wireless") == 0))
            mi = gtk_menu_item_new_with_label (_("Wireless disabled"));
      else
            mi = gtk_menu_item_new_with_label (
                              _("No wireless networks available"));
      gtk_widget_set_sensitive (mi, FALSE);
      gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu), mi);
      gtk_widget_show (mi);

      for (iter = netapplet->accesspoints; iter != NULL; iter = iter->next) {
            Accesspoint *ap = iter->data;
            char *display_name;
            float strength = ap->strength;

            if (ap == netapplet->active_ap) {
                  display_name = g_strdup_printf (_("%s (active)"),
                                          ap->essid);
                  if (strength == 0.0)    /* workaround driver bug */
                        strength = netapplet->link_quality;
            } else
                  display_name = g_strdup_printf ("%s", ap->essid);

            mi = percentage_menu_item_new (display_name, strength,
                                     ap->is_encrypted);
            gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu),
                               mi);
            g_object_set_data_full (G_OBJECT (mi), "essid",
                              g_strdup (ap->essid), g_free);
            g_signal_connect (mi, "activate",
                          G_CALLBACK (essid_activate_cb), NULL);
            gtk_widget_set_sensitive (mi, netapplet->authorized);
            gtk_widget_show (mi);
            g_free (display_name);
      }

      if (netapplet->active &&
          strcmp (netapplet->active->type, "Wireless") == 0) { 
            mi = gtk_menu_item_new_with_label ("Other ...");
            gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu),
                               mi);
            g_signal_connect (mi, "activate", G_CALLBACK (other_essid_cb),
                          NULL);

            gtk_widget_set_sensitive (mi, netapplet->authorized);
            gtk_widget_show (mi);
      }

      mi = gtk_separator_menu_item_new ();
      gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu), mi);
      gtk_widget_show (mi);

      /* 'Connection Information' item */
      image = gtk_image_new_from_stock (GTK_STOCK_PROPERTIES,
                                GTK_ICON_SIZE_MENU);
      mi = gtk_image_menu_item_new_with_mnemonic (
                              _("Connection _Information"));
      gtk_image_menu_item_set_image ((GtkImageMenuItem *) mi, image);
      gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu), mi);
      gtk_widget_show (mi);
      g_signal_connect (mi, "activate", G_CALLBACK (show_info_cb), netapplet);

      /* 'Configure Network Settings' item */
      image = gtk_image_new_from_stock (GTK_STOCK_PREFERENCES,
                                GTK_ICON_SIZE_MENU);
      mi = gtk_image_menu_item_new_with_mnemonic (
                        _("_Configure Network Settings"));
      gtk_image_menu_item_set_image ((GtkImageMenuItem *) mi, image);
      gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu), mi);
      gtk_widget_show (mi);
      g_signal_connect (mi, "activate", G_CALLBACK (configure_activate_cb),
                    netapplet);

      mi = gtk_separator_menu_item_new ();
      gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu), mi);
      gtk_widget_show (mi);

      mi = gtk_image_menu_item_new_with_mnemonic (_("_Remove From Panel"));
      image = gtk_image_new_from_stock (GTK_STOCK_REMOVE,
                                GTK_ICON_SIZE_MENU);
      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
      gtk_menu_shell_append (GTK_MENU_SHELL (netapplet->popup_menu), mi);
      gtk_widget_show (mi);
      g_signal_connect (mi, "activate", G_CALLBACK (quit_cb), NULL);
}

static Accesspoint *
get_accesspoint_for_essid (char *essid)
{
      GSList *iter;

      for (iter = netapplet->accesspoints; iter != NULL; iter = iter->next) {
            Accesspoint *ap = iter->data;

            if (strcmp (ap->essid, essid) == 0)
                  return ap;
      }

      return NULL;
}

static void
update_popup_accesspoint_strengths (void)
{
      GList *iter;

      for (iter = GTK_MENU_SHELL (netapplet->popup_menu)->children;
           iter != NULL; iter = iter->next) {
            GtkWidget *mi = iter->data;
            char      *essid;

            essid = g_object_get_data (G_OBJECT (mi), "essid");
            if (essid) {
                  Accesspoint *ap;
                  GtkWidget *progress;

                  ap = get_accesspoint_for_essid (essid);
                  if (ap == NULL)
                        continue;

                  progress = g_object_get_data (G_OBJECT (mi),
                                          "progress_widget");
                  if (progress == NULL)
                        continue;

                  gtk_progress_set_percentage (GTK_PROGRESS (progress),
                                         ap->strength);
            }
      }
}

static void
menu_position_func (GtkMenu *menu G_GNUC_UNUSED, int *x, int *y,
                gboolean *push_in, gpointer user_data G_GNUC_UNUSED)
{
      GdkScreen *screen;
      int screen_w, screen_h;
      int button_x, button_y;
      int panel_w, panel_h;
      GtkRequisition requisition;

      screen = gtk_widget_get_screen (netapplet->button);
      screen_w = gdk_screen_get_width (screen);
      screen_h = gdk_screen_get_height (screen);

      gdk_window_get_origin (netapplet->button->window,
                         &button_x, &button_y);

      gtk_window_get_size (
            GTK_WINDOW (gtk_widget_get_toplevel (netapplet->button)),
            &panel_w, &panel_h);

      *x = button_x;

      /*
       * Check to see if we would be placing the menu off the
       * end of the screen.
       */
      gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
      
      if (button_y + panel_h + requisition.height >= screen_h)
            *y = button_y - requisition.height;
      else
            *y = button_y + panel_h;

      *push_in = TRUE;
}

static void
active_scan_on (void)
{
      netapplet->active_scanning = TRUE;
      if (!netapplet->active)
            return;
      if (g_str_has_prefix (netapplet->active->interface, "ath")) {
            netapplet_get_accesspoints (netapplet->active->interface);
            netapplet_get_wireless (netapplet->active->interface);
      }
}

static void
active_scan_off (void)
{
      netapplet->active_scanning = FALSE;
}

static void
button_toggled_cb (GtkToggleButton *button G_GNUC_UNUSED,
               gpointer user_data G_GNUC_UNUSED)
{
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (netapplet->button))) {
            active_scan_on ();
            gtk_menu_popup (GTK_MENU (netapplet->popup_menu), NULL, NULL,
                        menu_position_func, NULL, 0,
                        gtk_get_current_event_time ());
      }
}

static void
menu_deactivate_cb (GtkMenuShell *menu_shell G_GNUC_UNUSED,
                gpointer user_data G_GNUC_UNUSED)
{
      active_scan_off ();
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (netapplet->button),
                              FALSE);
      gtk_menu_popdown (GTK_MENU (netapplet->popup_menu));
}

static gboolean
accesspoints_differ (Accesspoint *ap1, Accesspoint *ap2)
{
      if (ap1 == NULL || ap2 == NULL)
            return ap1 != ap2;
            
      if (strcmp (ap1->essid, ap2->essid) != 0)
            return TRUE;

      if (ap1->is_encrypted != ap2->is_encrypted)
            return TRUE;

      return FALSE;
}

static gboolean
accesspoint_in_list (Accesspoint *ap, GSList *list)
{
      GSList *iter;

      for (iter = list; iter != NULL; iter = iter->next) {
            Accesspoint *ap2 = iter->data;

            if (! accesspoints_differ (ap, ap2))
                  return TRUE;
      }

      return FALSE;
}

static gboolean
accesspoint_lists_differ (GSList *aps1, GSList *aps2)
{
      GSList *iter;

      if (g_slist_length (aps1) != g_slist_length (aps2))
            return TRUE;

      for (iter = aps1; iter != NULL; iter = iter->next) {
            Accesspoint *ap = iter->data;

            if (!accesspoint_in_list (ap, aps2))
                  return TRUE;
      }

      return FALSE;
}

static char *
wireless_icon (Connection *conn)
{
      float strength;
      gboolean encrypted;

      if (strcmp (conn->type, "Wireless") != 0 ||
          netapplet->active_ap == NULL)
            return WIRELESS_ICON_0;

      encrypted = netapplet->active_ap->is_encrypted;
      strength = netapplet->link_quality;

      if (strength < 0.25)
            return encrypted ? WIRELESS_ICON_1 : WIRELESS_ICON_1;

      if (strength < 0.50)
            return encrypted ? WIRELESS_ICON_2 : WIRELESS_ICON_2;

      if (strength < 0.75)
            return encrypted ? WIRELESS_ICON_3 : WIRELESS_ICON_3;

      return encrypted ? WIRELESS_ICON_4 : WIRELESS_ICON_4;
}

static void
netapplet_handle_accesspoints (GIOChannel *channel G_GNUC_UNUSED,
                         char **args)
{
      GSList *new_aps = NULL;
      char **iter;
      Accesspoint *new_active = NULL;
      gboolean changed = FALSE;

      for (iter = &args[2]; *iter != NULL; iter += 3) {
            Accesspoint *ap;

            printf("Got %s %s %s\n",*iter, *(iter+1), *(iter+2));

            ap = g_new (Accesspoint, 1);
            ap->essid = g_strdup (*iter);
            ap->strength = g_ascii_strtod (*(iter + 1), NULL);
            ap->is_encrypted = strtol (*(iter + 2), NULL, 10);


            if (netapplet->active_ap != NULL &&
                strcmp (netapplet->active_ap->essid, ap->essid) == 0)
                  new_active = ap;

            new_aps = g_slist_append (new_aps, ap);
      }

      if (accesspoint_lists_differ (netapplet->accesspoints, new_aps) ||
          accesspoints_differ (netapplet->active_ap, new_active))
            changed = TRUE;

      netapplet_free_accesspoints ();
      netapplet->accesspoints = new_aps;
      netapplet->active_ap = new_active;

      netapplet->active->icon = wireless_icon (netapplet->active);
      netapplet_set_icon (netapplet->active->icon);

      if (changed)
            populate_popup_menu ();
      else
            update_popup_accesspoint_strengths ();
}

/*
 * handle the 'wireless' message, which tells us the active ESSID
 *
 * args[0] is 'wireless' (ignored)
 * args[1] is the ESSID
 * args[2] is 1 if encrypted is enabled and 0 otherwise
 * args[3] is the link quality [0, 1]
 */
static void
netapplet_handle_wireless (GIOChannel *channel G_GNUC_UNUSED, char **args)
{
      char *new_icon;
      GSList *iter;
      
      netapplet->link_quality = strtod (args[3], NULL);
      new_icon = wireless_icon (netapplet->active);
      if (netapplet->active->icon != new_icon) {
            netapplet->active->icon = new_icon;
            netapplet_set_icon (netapplet->active->icon);
            goto out;   /* we want to run populate_popup_menu () */
      }

      /*
       * Bail out early if we see that our active essid has not changed.
       * We handle the link quality update above, so there is nothing
       * else to do.  This avoids redrawing the menu, too.
       */
      if (netapplet->active_ap &&
          strcmp (args[1], netapplet->active_ap->essid) == 0)
            return;

      for (iter = netapplet->accesspoints; iter != NULL; iter = iter->next) {
            Accesspoint *ap = iter->data;

            if (strcmp (ap->essid, args[1]) == 0)
                  netapplet->active_ap = ap;
      }

out:
      populate_popup_menu ();
}

static void
netapplet_handle_disconnected (GIOChannel *channel G_GNUC_UNUSED,
                         char **args G_GNUC_UNUSED)
{
      netapplet_set_icon (DISCONNECT_ICON);
      netapplet->active = NULL;
      if (netapplet->accesspoints) {
            /* remove no-longer-valid wireless information */
            netapplet_free_accesspoints ();
      }

      populate_popup_menu ();
}

static gboolean
connection_in_list (Connection *conn, GSList *list)
{
      GSList *iter;

      for (iter = list; iter != NULL; iter = iter->next) {
            Connection *conn2 = iter->data;

            if (strcmp (conn->interface, conn2->interface) == 0)
                  return TRUE;
      }

      return FALSE;
}

static gboolean
connection_lists_differ (GSList *conns1, GSList *conns2)
{
      GSList *iter;

      if (g_slist_length (conns1) != g_slist_length (conns2))
            return TRUE;

      for (iter = conns1; iter != NULL; iter = iter->next) {
            Connection *conn = iter->data;
            if (!connection_in_list (conn, conns2))
                  return TRUE;
      }

      return FALSE;
}

static void
netapplet_handle_interfaces (GIOChannel *channel G_GNUC_UNUSED, char **args)
{
      char *prev_active = NULL;
      GSList *new_conns = NULL;
      gboolean conns_changed;
      char **iter;
      Connection *new_active = NULL;

      /*
       * This is used to restore netapplet->active to the new Connection
       * value obtained below.  This avoids a race where netapplet->active
       * is an invalid pointer.  It also acts as a cache, pointing to the
       * previous active value until our next active message.
       */
      if (netapplet->active)
            prev_active = g_strdup (netapplet->active->interface);

      for (iter = &args[1]; *iter != NULL; iter += 2) {
            const char *interface = *iter;
            const char *type = *(iter + 1);
            Connection *conn;

            conn = g_new0 (Connection, 1);
            conn->interface = g_strdup (interface);

            if (prev_active && strcmp (interface, prev_active) == 0)
                  new_active = conn;

            if (strcmp (type, TYPE_WIRELESS) == 0) {
                  conn->type = "Wireless";
                  conn->icon = wireless_icon (conn);
            } else if (strcmp (type, TYPE_ETHERNET) == 0) {
                  conn->type = "Ethernet";
                  conn->icon = ETHERNET_ICON;
            } else if (strcmp (type, TYPE_DIALUP) == 0) {
                  conn->type = "Dialup";
                  conn->icon = DIALUP_ICON;
            } else if (strcmp (type, TYPE_UNKNOWN) == 0) {
                  conn->type = "Unknown";
                  conn->icon = UNKNOWN_ICON;
            } else
                  g_assert_not_reached ();

            new_conns = g_slist_prepend (new_conns, conn);
      }

      conns_changed = connection_lists_differ (netapplet->connections, new_conns);

      netapplet_free_connections ();
      netapplet->connections = new_conns;
      netapplet->active = new_active;

      g_free (prev_active);

      if (conns_changed)
            populate_popup_menu ();
}

static void
netapplet_handle_active (GIOChannel *channel G_GNUC_UNUSED, char **args)
{
      GSList *iter;
      gboolean changed;

      changed = FALSE;

      if (!netapplet->active ||
          strcmp (netapplet->active->interface, args[1]) != 0) {

            netapplet->active = NULL;
            for (iter = netapplet->connections; iter != NULL;
                 iter = iter->next) {
                  Connection *conn = iter->data;
                  
                  if (strcmp (conn->interface, args[1]) == 0) {
                        netapplet->active = conn;
                        changed = TRUE;
                  }
            }
      }

      if (!netapplet->active) {
            /* Nothing for this setup.  Fake it. */
            Connection *conn;

            conn = g_new0 (Connection, 1);
            conn->interface = g_strdup (args[1]);
            conn->type = "Unknown";
            conn->icon = UNKNOWN_ICON;

            netapplet->connections =
                  g_slist_prepend (netapplet->connections, conn);
            netapplet->active = conn;
            changed = TRUE;
      }

      if (g_str_has_prefix (netapplet->active->type, "Wireless")) {
            if (netapplet->active_scanning || changed ||
                !g_str_has_prefix (netapplet->active->interface, "ath")) {
                  netapplet_get_accesspoints (netapplet->active->interface);
                  netapplet_get_wireless (netapplet->active->interface);
            }
      } else if (netapplet->accesspoints) {
            /* remove no-longer-valid wireless information */
            netapplet_free_accesspoints ();
      }

      if (changed) {
            netapplet_set_icon (netapplet->active->icon);
            populate_popup_menu ();
      }
}

static void
netapplet_handle_unauthorized (GIOChannel *channel G_GNUC_UNUSED,
                         char **args G_GNUC_UNUSED)
{
      if (netapplet->authorized) {
            GtkWidget *dialog;

            netapplet->authorized = FALSE;

            dialog = gtk_message_dialog_new_with_markup (
                  NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
                  _("<span weight=\"bold\" size=\"larger\">"
                    "Network switching is currently unavailable"
                    "</span>\n\n"
                    "You do not have the permissions to change "
                    "network settings"));
            gtk_dialog_run (GTK_DIALOG (dialog));
            gtk_widget_destroy (dialog);
      
            netapplet_set_icon (BROKEN_ICON);
            netapplet_free_connections ();
            netapplet_free_accesspoints ();
            populate_popup_menu ();
      }
}

static void
size_changed_cb (void)
{
      if (netapplet->icon_name == NULL)
            return;

      /* If the panel changed size, we might need to change the icon. */
      netapplet_set_icon (netapplet->icon_name);
}

static NetApplet *
netapplet_new (void)
{
      NetApplet *applet;

      /* Widgetry */
      applet = g_new0 (NetApplet, 1);
      applet->tray_icon = egg_tray_icon_new ("applet");

      applet->active_scanning = FALSE;

      applet->button = gtk_toggle_button_new ();
      gtk_button_set_relief (GTK_BUTTON (applet->button),
                         GTK_RELIEF_NONE);
      gtk_container_add (GTK_CONTAINER (applet->tray_icon),
                     applet->button);
      gtk_widget_show (applet->button);

      g_signal_connect (applet->button, "toggled",
                    G_CALLBACK (button_toggled_cb), NULL);
      g_signal_connect (applet->button, "size-allocate",
                    G_CALLBACK (size_changed_cb), NULL);

      applet->icon = gtk_image_new ();
      gtk_container_add (GTK_CONTAINER (applet->button), applet->icon);
      gtk_widget_show (applet->icon);

      applet->popup_menu = gtk_menu_new ();
      g_signal_connect (applet->popup_menu, "deactivate",
                    G_CALLBACK (menu_deactivate_cb), NULL);

      /* Protocol handlers */
      applet->handlers = g_hash_table_new (g_str_hash, g_str_equal);

      g_hash_table_insert (applet->handlers, "interfaces",
                       netapplet_handle_interfaces);
      g_hash_table_insert (applet->handlers, "active",
                       netapplet_handle_active);
      g_hash_table_insert (applet->handlers, "wireless",
                       netapplet_handle_wireless);
      g_hash_table_insert (applet->handlers, "accesspoints",
                       netapplet_handle_accesspoints);
      g_hash_table_insert (applet->handlers, "disconnected",
                       netapplet_handle_disconnected);
      g_hash_table_insert (applet->handlers, "unauthorized",
                       netapplet_handle_unauthorized);

      return applet;
}

int
main (int argc, char *argv[])
{
      GnomeClient *client;

      bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
      bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
      textdomain (GETTEXT_PACKAGE);

      gnome_program_init ("netapplet", VERSION,
                      LIBGNOMEUI_MODULE,
                      argc, argv,
                      GNOME_PROGRAM_STANDARD_PROPERTIES,
                      GNOME_CLIENT_PARAM_SM_CONNECT, TRUE,
                      NULL);

      gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
                                 ICONDIR);

      client = gnome_master_client ();
      gnome_client_set_restart_command (client, argc, argv);
      if (netapplet_get_clipboard ())
            gnome_client_set_restart_style (client,
                                    GNOME_RESTART_IF_RUNNING);
      else {
            /* if we are already running, silently exit */
            gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
            return 1;
      }

      netapplet = netapplet_new ();
      netapplet_set_icon (DISCONNECT_ICON);
      populate_popup_menu ();

      netapplet_connect ();

      gtk_widget_show (GTK_WIDGET (netapplet->tray_icon));

      gtk_main ();

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index