Fix for portrait mode support in CSSU
[tor-status] / src / status-area-applet-tor.vala
index 9daec7f..285c99c 100644 (file)
@@ -1,6 +1,6 @@
 /* This file is part of status-area-applet-tor.
  *
- * Copyright (C) 2010 Philipp Zabel
+ * Copyright (C) 2010-2011 Philipp Zabel
  *
  * status-area-applet-tor is free software: you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as published
@@ -37,6 +37,7 @@ class TorStatusMenuItem : HD.StatusMenuItem {
        private const string GCONF_DIR_TOR         = "/apps/maemo/tor";
        private const string GCONF_KEY_TOR_ENABLED = GCONF_DIR_TOR + "/enabled";
        private const string GCONF_KEY_BRIDGES     = GCONF_DIR_TOR + "/bridges";
+       private const string GCONF_KEY_EXITNODES   = GCONF_DIR_TOR + "/exit_nodes";
 
        private const string GCONF_DIR_PROXY_HTTP         = "/system/http_proxy";
        private const string GCONF_KEY_PROXY_HTTP_ENABLED = GCONF_DIR_PROXY_HTTP + "/use_http_proxy";
@@ -52,6 +53,7 @@ class TorStatusMenuItem : HD.StatusMenuItem {
 
        // Widgets
        Hildon.Button button;
+       Gtk.Label log_label;
 
        // Icons
        Gdk.Pixbuf icon_connecting;
@@ -79,42 +81,42 @@ class TorStatusMenuItem : HD.StatusMenuItem {
        /**
         * Update status area icon and status menu button value
         */
-       private void update_status () {
-               if (tor_enabled && tor_connected && icon_connected == null) try {
-                       var icon_theme = Gtk.IconTheme.get_default ();
-                       var pixbuf = icon_theme.load_icon ("statusarea_tor_connected",
-                                                          STATUS_AREA_ICON_SIZE,
-                                                          Gtk.IconLookupFlags.NO_SVG);
-                       icon_connected = pixbuf;
+       private bool update_status () {
+               try {
+                       if (tor_enabled && tor_connected && icon_connected == null) {
+                               var icon_theme = Gtk.IconTheme.get_default ();
+                               var pixbuf = icon_theme.load_icon ("statusarea_tor_connected",
+                                                                  STATUS_AREA_ICON_SIZE,
+                                                                  Gtk.IconLookupFlags.NO_SVG);
+                               icon_connected = pixbuf;
+                       }
+                       if (tor_enabled && !tor_connected && icon_connecting == null) {
+                               var icon_theme = Gtk.IconTheme.get_default ();
+                               var pixbuf = icon_theme.load_icon ("statusarea_tor_connecting",
+                                                                  STATUS_AREA_ICON_SIZE,
+                                                                  Gtk.IconLookupFlags.NO_SVG);
+                               icon_connecting = pixbuf;
+                       }
+                       if (tor_enabled && icon_enabled == null) {
+                               var icon_theme = Gtk.IconTheme.get_default();
+                               var pixbuf = icon_theme.load_icon ("statusarea_tor_enabled",
+                                                                  STATUS_MENU_ICON_SIZE,
+                                                                  Gtk.IconLookupFlags.NO_SVG);
+                               icon_enabled = new Gtk.Image.from_pixbuf (pixbuf);
+                       }
+                       if (!tor_enabled && icon_disabled == null) {
+                               var icon_theme = Gtk.IconTheme.get_default();
+                               var pixbuf = icon_theme.load_icon ("statusarea_tor_disabled",
+                                                                  STATUS_MENU_ICON_SIZE,
+                                                                  Gtk.IconLookupFlags.NO_SVG);
+                               icon_disabled = new Gtk.Image.from_pixbuf (pixbuf);
+                       }
                } catch (Error e) {
-                       error (e.message);
-               }
-               if (tor_enabled && !tor_connected && icon_connecting == null) try {
+                       critical (e.message);
                        var icon_theme = Gtk.IconTheme.get_default ();
-                       var pixbuf = icon_theme.load_icon ("statusarea_tor_connecting",
-                                                          STATUS_AREA_ICON_SIZE,
-                                                          Gtk.IconLookupFlags.NO_SVG);
-                       icon_connecting = pixbuf;
-               } catch (Error e) {
-                       error (e.message);
-               }
-               if (tor_enabled && icon_enabled == null) try {
-                       var icon_theme = Gtk.IconTheme.get_default();
-                       var pixbuf = icon_theme.load_icon ("statusarea_tor_enabled",
-                                                          STATUS_MENU_ICON_SIZE,
-                                                          Gtk.IconLookupFlags.NO_SVG);
-                       icon_enabled = new Gtk.Image.from_pixbuf (pixbuf);
-               } catch (Error e) {
-                       error (e.message);
-               }
-               if (!tor_enabled && icon_disabled == null) try {
-                       var icon_theme = Gtk.IconTheme.get_default();
-                       var pixbuf = icon_theme.load_icon ("statusarea_tor_disabled",
-                                                          STATUS_MENU_ICON_SIZE,
-                                                          Gtk.IconLookupFlags.NO_SVG);
-                       icon_disabled = new Gtk.Image.from_pixbuf (pixbuf);
-               } catch (Error e) {
-                       error (e.message);
+                       icon_theme.rescan_if_needed ();
+                       Timeout.add_seconds (1, update_status);
+                       return false;
                }
 
                if (conic_connected && tor_enabled) {
@@ -125,6 +127,8 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                        button.set_value (tor_enabled ? _("Disconnected") : _("Disabled"));
                }
                button.set_image (tor_enabled ? icon_enabled : icon_disabled);
+
+               return false;
        }
 
        /**
@@ -139,6 +143,9 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                                /* var status = */ source.read_line (out line, out length, null);
 
                                tor_log += line;
+                               if (log_label != null)
+                                       log_label.label = tor_log;
+
                                if ("[notice]" in line) {
                                        if ("Bootstrapped 100%" in line) {
                                                tor_connected = true;
@@ -170,25 +177,32 @@ class TorStatusMenuItem : HD.StatusMenuItem {
        private async void tor_control_auth () throws Error {
                yield tor_control.authenticate_async (password);
 
-               var bridges = new SList<string> ();
-               try {
-                       bridges = gconf.get_list (GCONF_KEY_BRIDGES, GConf.ValueType.STRING);
-               } catch (Error e) {
-                       error ("Error loading bridges: %s", e.message);
-                       return;
+               var bridges = gconf.get_list (GCONF_KEY_BRIDGES, GConf.ValueType.STRING);
+
+               if (bridges.length () > 0) {
+                       // Enable bridge relays
+                       tor_control.set_conf_list ("Bridge", bridges);
+                       tor_control.set_conf_bool ("UseBridges", true);
+
+                       bool use = yield tor_control.get_conf_bool_async ("UseBridges");
+                       if (!use) {
+                               Hildon.Banner.show_information (null, null,
+                                                               "Failed to set up bridge relays");
+                       }
                }
 
-               if (bridges.length () <= 0)
-                       return;
+               var exits = gconf.get_list (GCONF_KEY_EXITNODES, GConf.ValueType.STRING);
 
-               // Enable bridge relays
-               tor_control.set_conf_list ("Bridge", bridges);
-               tor_control.set_conf_bool ("UseBridges", true);
+               if (exits.length () > 0) {
+                       // Enable strict exit nodes
+                       tor_control.set_conf_list ("ExitNodes", exits);
+                       tor_control.set_conf_bool ("StrictExitNodes", true);
 
-               bool use = yield tor_control.get_conf_bool_async ("UseBridges");
-               if (!use) {
-                       Hildon.Banner.show_information (null, null,
-                                                       "Failed to set up bridge relays");
+                       bool strict = yield tor_control.get_conf_bool_async ("StrictExitNodes");
+                       if (!strict) {
+                               Hildon.Banner.show_information (null, null,
+                                                               "Failed to set up strict exit nodes");
+                       }
                }
        }
 
@@ -209,7 +223,7 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                                tor_hash_argv[2] = password;
                                string hash;
                                Process.spawn_sync ("/tmp", tor_hash_argv, null, 0, null, out hash);
-                               hash = hash.str ("16:").replace ("\n", "");
+                               hash = hash.str ("\n16:").offset (1).replace ("\n", "");
 
                                if (hash == null) {
                                        Hildon.Banner.show_information (null, null,
@@ -254,6 +268,8 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                }
 
                tor_log = "";
+               if (log_label != null)
+                       log_label.label = tor_log;
                update_status ();
        }
 
@@ -294,7 +310,7 @@ class TorStatusMenuItem : HD.StatusMenuItem {
 
                        backup.mode = gconf.get_string (GCONF_KEY_PROXY_MODE);
                } catch (Error e) {
-                       error ("Error saving proxy settings: %s", e.message);
+                       critical ("Error saving proxy settings: %s", e.message);
                        backup = new ProxyBackup ();
                        backup.use_http_proxy = false;
 
@@ -320,7 +336,7 @@ class TorStatusMenuItem : HD.StatusMenuItem {
 
                        gconf.set_string (GCONF_KEY_PROXY_MODE, "manual");
                } catch (Error e) {
-                       error ("Error changing proxy settings: %s", e.message);
+                       critical ("Error changing proxy settings: %s", e.message);
                }
        }
 
@@ -342,187 +358,24 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                        gconf.set_string (GCONF_KEY_PROXY_MODE, backup.mode);
                        backup = null;
                } catch (Error e) {
-                       error ("Error restoring proxy: %s", e.message);
+                       critical ("Error restoring proxy: %s", e.message);
                }
        }
 
        /**
         * Show the bridge relay configuration dialog
         */
-       private const int RESPONSE_NEW = 1;
        private void bridges_clicked_cb () {
-               var dialog = new Gtk.Dialog ();
-               var content = (Gtk.VBox) dialog.get_content_area ();
-                content.set_size_request (-1, 5*70);
-
-               dialog.set_title (_("Bridge relays"));
-
-               var bridges = new SList<string> ();
-               try {
-                       bridges = gconf.get_list (GCONF_KEY_BRIDGES, GConf.ValueType.STRING);
-               } catch (Error e) {
-                       Hildon.Banner.show_information (null, null, "Error loading bridges: %s".printf (e.message));
-               }
-
-               var list_store = new Gtk.ListStore (1, typeof (string));
-               Gtk.TreeIter iter;
-               foreach (string bridge in bridges) {
-                       list_store.append (out iter);
-                       list_store.@set (iter, 0, bridge);
-               }
-
-               var pannable_area = new Hildon.PannableArea ();
-               var tree_view = new Gtk.TreeView.with_model (list_store);
-               var renderer = new Gtk.CellRendererText ();
-               var column = new Gtk.TreeViewColumn.with_attributes ("IP", renderer, "text", 0);
-               tree_view.append_column (column);
-               pannable_area.add (tree_view);
-               content.pack_start (pannable_area, true, true, 0);
-
-               tree_view.row_activated.connect ((path, column) => {
-                       bridge_edit_dialog (list_store, path);
-               });
-
-               dialog.add_button (_("New"), RESPONSE_NEW);
-               dialog.response.connect ((response_id) => {
-                       if (response_id == RESPONSE_NEW) {
-                               bridge_edit_dialog (list_store, null);
-                       }
-               });
-
-               dialog.show_all ();
+               var dialog = new BridgeDialog ();
+               dialog.show ();
        }
 
        /**
-        * Show the bridge relay edit dialog
+        * Show the exit node configuration dialog
         */
-       private const int RESPONSE_DELETE = 1;
-       private void bridge_edit_dialog (Gtk.ListStore store, Gtk.TreePath? path) {
-               var dialog = new Gtk.Dialog ();
-               var content = (Gtk.VBox) dialog.get_content_area ();
-
-               if (path == null)
-                       dialog.set_title (_("New bridge relay"));
-               else
-                       dialog.set_title (_("Edit bridge relay"));
-
-               var size_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
-
-               var hbox = new Gtk.HBox (false, Hildon.MARGIN_DOUBLE);
-               var label = new Gtk.Label (_("IP address"));
-               label.set_alignment (0, 0.5f);
-               size_group.add_widget (label);
-               hbox.pack_start (label, false, false, 0);
-               var ip_entry = new Hildon.Entry (Hildon.SizeType.FINGER_HEIGHT);
-               ip_entry.set ("hildon-input-mode", Hildon.GtkInputMode.NUMERIC |
-                                                  Hildon.GtkInputMode.SPECIAL);
-               hbox.pack_start (ip_entry, true, true, 0);
-               content.pack_start (hbox, false, false, 0);
-
-               hbox = new Gtk.HBox (false, Hildon.MARGIN_DOUBLE);
-               label = new Gtk.Label (_("Port"));
-               label.set_alignment (0, 0.5f);
-               size_group.add_widget (label);
-               hbox.pack_start (label, false, false, 0);
-               var port_entry = new Hildon.Entry (Hildon.SizeType.FINGER_HEIGHT);
-               port_entry.set ("hildon-input-mode", Hildon.GtkInputMode.NUMERIC);
-               hbox.pack_start (port_entry, true, true, 0);
-               content.pack_start (hbox, true, true, 0);
-
-               hbox = new Gtk.HBox (false, Hildon.MARGIN_DOUBLE);
-               label = new Gtk.Label (_("Fingerprint"));
-               label.set_alignment (0, 0.5f);
-               size_group.add_widget (label);
-               hbox.pack_start (label, false, false, 0);
-               var fingerprint_entry = new Hildon.Entry (Hildon.SizeType.FINGER_HEIGHT);
-               fingerprint_entry.set ("hildon-input-mode", Hildon.GtkInputMode.HEXA);
-               hbox.pack_start (fingerprint_entry, true, true, 0);
-               content.pack_start (hbox, true, true, 0);
-
-               var iter = Gtk.TreeIter ();
-               if (path == null) {
-                       port_entry.set_text ("443");
-               } else if (store.get_iter (out iter, path)) {
-                       string tmp;
-                       store.@get (iter, 0, out tmp);
-                       string[] ip_port = tmp.split (":");
-                       if (ip_port.length == 2) {
-                               ip_entry.set_text (ip_port[0]);
-                               port_entry.set_text (ip_port[1]);
-                       }
-
-                       dialog.add_button (_("Delete"), RESPONSE_DELETE);
-               }
-               dialog.add_button (_("Save"), Gtk.ResponseType.OK);
-               dialog.response.connect ((response_id) => {
-                       var bridges = new SList<string> ();
-
-                       if (response_id == RESPONSE_DELETE) {
-                               if (path != null) {
-                                       store.remove (iter);
-                                       string bridge;
-                                       if (store.get_iter_first (out iter)) do {
-                                               store.@get (iter, 0, out bridge);
-                                               bridges.append (bridge);
-                                       } while (store.iter_next (ref iter));
-                                       try {
-                                               gconf.set_list (GCONF_KEY_BRIDGES,
-                                                               GConf.ValueType.STRING,
-                                                               bridges);
-                                       } catch (Error e) {
-                                               Hildon.Banner.show_information (dialog, null,
-                                                                               "Failed to save bridge relay list: %s".printf (e.message));
-                                       }
-                               }
-                               dialog.destroy ();
-                       }
-                       if (response_id == Gtk.ResponseType.OK) {
-                               if (!is_valid_ip_address (ip_entry.get_text ())) {
-                                       Hildon.Banner.show_information (dialog, null,
-                                                                       _("Invalid IP address"));
-                                       return;
-                               }
-                               int port = port_entry.get_text ().to_int ();
-                               if (port < 0 || port > 65565) {
-                                       Hildon.Banner.show_information (dialog, null,
-                                                                       _("Invalid port number"));
-                                       return;
-                               }
-                               if (path == null) {
-                                       store.append (out iter);
-                               }
-                               store.@set (iter, 0, "%s:%d".printf (ip_entry.get_text (), port));
-                               try {
-                                       bridges = gconf.get_list (GCONF_KEY_BRIDGES,
-                                                                 GConf.ValueType.STRING);
-                               } catch (Error e) {
-                                       Hildon.Banner.show_information (null, null,
-                                                                       "Error loading bridges: %s".printf (e.message));
-                               }
-                               if (path == null) {
-                                       bridges.append ("%s:%d".printf (ip_entry.get_text (), port));
-                               } else {
-                                       bridges = null;
-                                       string bridge;
-                                       if (store.get_iter_first (out iter)) do {
-                                               store.@get (iter, 0, out bridge);
-                                               bridges.append (bridge);
-                                       } while (store.iter_next (ref iter));
-                               }
-                               try {
-                                       gconf.set_list (GCONF_KEY_BRIDGES,
-                                                       GConf.ValueType.STRING,
-                                                       bridges);
-                               } catch (Error e) {
-                                               Hildon.Banner.show_information (dialog, null,
-                                                                               "Failed to save bridge relay list: %s".printf (e.message));
-                               }
-
-                               dialog.destroy ();
-                       }
-               });
-
-               dialog.show_all ();
+       private void exit_nodes_clicked_cb () {
+               var dialog = new ExitNodeDialog (tor_control);
+               dialog.show ();
        }
 
        /**
@@ -554,10 +407,16 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                dialog.set_title (_("Log"));
 
                var pannable = new Hildon.PannableArea ();
-               var label = new Gtk.Label (tor_log);
-               pannable.add_with_viewport (label);
+               pannable.mov_mode = Hildon.MovementMode.BOTH;
+               log_label = new Gtk.Label (tor_log);
+               log_label.set_alignment (0, 0);
+               pannable.add_with_viewport (log_label);
                content.pack_start (pannable, true, true, 0);
 
+               dialog.response.connect (() => {
+                       log_label = null;
+               });
+
                dialog.show_all ();
        }
 
@@ -568,7 +427,6 @@ class TorStatusMenuItem : HD.StatusMenuItem {
        private void button_clicked_cb () {
                var dialog = new Gtk.Dialog ();
                var content = (Gtk.VBox) dialog.get_content_area ();
-               content.set_size_request (-1, 2*70);
 
                dialog.set_title (_("Tor: anonymity online"));
 
@@ -586,6 +444,15 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                button.clicked.connect (bridges_clicked_cb);
                content.pack_start (button, true, true, 0);
 
+               button = new Hildon.Button.with_text (Hildon.SizeType.FINGER_HEIGHT,
+                                                     Hildon.ButtonArrangement.VERTICAL,
+                                                     _("Restrict exit nodes"),
+                                                     get_exit_node_list ());
+               button.set_style (Hildon.ButtonStyle.PICKER);
+               button.set_alignment (0, 0.5f, 0, 0.5f);
+               button.clicked.connect (exit_nodes_clicked_cb);
+               content.pack_start (button, true, true, 0);
+
                dialog.add_button (_("Log"), RESPONSE_LOG);
 
                dialog.add_button (_("Save"), Gtk.ResponseType.ACCEPT);
@@ -595,20 +462,18 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                                return;
                        }
                        if (response_id == Gtk.ResponseType.ACCEPT) {
-                               if (!tor_enabled && check.get_active ()) {
-                                       tor_enabled = true;
+                               if (!tor_enabled && check.get_active ()) try {
+                                       gconf.set_bool (GCONF_KEY_TOR_ENABLED, true);
 
-                                       if (conic_connected) {
-                                               start_tor ();
-                                       } else {
+                                       // Enabled by user interaction, so connect if needed
+                                       if (!conic_connected)
                                                conic.connect (ConIc.ConnectFlags.NONE);
-                                       }
-                               } else if (tor_enabled && !check.get_active ()) {
-                                       tor_enabled = false;
-
-                                       stop_tor ();
-                                       if (conic_connected)
-                                               conic.disconnect ();
+                               } catch (Error e) {
+                                       Hildon.Banner.show_information (null, null, "Failed to enable GConf key");
+                               } else if (tor_enabled && !check.get_active ()) try {
+                                       gconf.set_bool (GCONF_KEY_TOR_ENABLED, false);
+                               } catch (Error e) {
+                                       Hildon.Banner.show_information (null, null, "Failed to disable GConf key");
                                }
                        }
                        dialog.destroy ();
@@ -623,7 +488,7 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                try {
                        bridges = gconf.get_list (GCONF_KEY_BRIDGES, GConf.ValueType.STRING);
                } catch (Error e) {
-                       error ("Error loading bridges: %s", e.message);
+                       critical ("Error loading bridges: %s", e.message);
                }
                foreach (string bridge in bridges) {
                        if (list == null)
@@ -637,6 +502,48 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                return list;
        }
 
+       private string get_exit_node_list () {
+               string list = null;
+               var exits = new SList<string> ();
+               try {
+                       exits = gconf.get_list (GCONF_KEY_EXITNODES, GConf.ValueType.STRING);
+               } catch (Error e) {
+                       error ("Error loading exit nodes: %s", e.message);
+               }
+               foreach (string exit in exits) {
+                       if (list == null)
+                               list = exit;
+                       else
+                               list += ", " + exit;
+               }
+               if (list == null)
+                       list = _("None");
+
+               return list;
+       }
+
+       /**
+        * Callback for GConf change notification on the tor_enabled key
+        */
+       private void tor_enabled_changed_cb (GConf.Client gc, uint cxnid, GConf.Entry entry) {
+               if (entry.key == GCONF_KEY_TOR_ENABLED) {
+                       bool old_tor_enabled = tor_enabled;
+                       tor_enabled = entry.get_value ().get_bool ();
+                       if (old_tor_enabled == tor_enabled)
+                               return;
+
+                       if (tor_enabled) {
+                               // Start Tor immediately if a connection is already available
+                               if (conic_connected)
+                                       start_tor ();
+                       } else {
+                               stop_tor ();
+                               if (conic_connected)
+                                       conic.disconnect ();
+                       }
+               }
+       }
+
        /**
         * Callback for the ConIc connection-event signal
         */
@@ -688,6 +595,8 @@ class TorStatusMenuItem : HD.StatusMenuItem {
 
                add (button);
 
+               log_label = null;
+
                // Status area icon
                update_status ();
 
@@ -704,8 +613,12 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                gconf = GConf.Client.get_default ();
                try {
                        tor_enabled = gconf.get_bool (GCONF_KEY_TOR_ENABLED);
+
+                       // Request change notifications for the tor_enabled key
+                       gconf.add_dir (GCONF_DIR_TOR, GConf.ClientPreloadType.ONELEVEL);
+                       gconf.notify_add (GCONF_KEY_TOR_ENABLED, tor_enabled_changed_cb);
                } catch (Error e) {
-                       error ("Failed to get GConf setting: %s", e.message);
+                       critical ("Failed to get GConf setting: %s", e.message);
                }
                tor_connected = false;