Add exit node configuration dialog
authorPhilipp Zabel <philipp.zabel@gmail.com>
Thu, 22 Apr 2010 13:54:28 +0000 (15:54 +0200)
committerPhilipp Zabel <philipp.zabel@gmail.com>
Wed, 16 Feb 2011 17:12:31 +0000 (18:12 +0100)
Makefile.am
src/exit-node-dialog.vala [new file with mode: 0644]
src/status-area-applet-tor.vala

index 2a86f5d..8e2d3da 100644 (file)
@@ -24,12 +24,14 @@ icon18_DATA = \
 status_area_applet_tor_la_SOURCES = \
        src/status-area-applet-tor.c \
        src/bridge-dialog.c \
+       src/exit-node-dialog.c \
        src/torcontrol.c \
        src/torcontrol-socket.c
 
 status_area_applet_tor_la_VALASOURCES = \
        src/status-area-applet-tor.vala \
        src/bridge-dialog.vala \
+       src/exit-node-dialog.vala \
        src/torcontrol.vala
 
 src/status-area-applet-tor.c: ${status_area_applet_tor_la_VALASOURCES}
@@ -48,4 +50,5 @@ ACLOCAL_AMFLAGS = -Im4
 CLEANFILES = \
         src/status-area-applet-tor.c \
        src/bridge-dialog.c \
+       src/exit-node-dialog.c \
         src/torcontrol.c
diff --git a/src/exit-node-dialog.vala b/src/exit-node-dialog.vala
new file mode 100644 (file)
index 0000000..033e94d
--- /dev/null
@@ -0,0 +1,152 @@
+class ExitNodeDialog : Gtk.Dialog {
+       private const string GCONF_DIR_TOR       = "/apps/maemo/tor";
+       private const string GCONF_KEY_EXITNODES = GCONF_DIR_TOR + "/exit_nodes";
+
+       GConf.Client gconf;
+       Gtk.ListStore list_store;
+
+       /**
+        * Show the exit node configuration dialog
+        */
+       private const int RESPONSE_NEW = 1;
+       public ExitNodeDialog (TorControl.Connection? tor_control = null) {
+               var content = (Gtk.VBox) get_content_area ();
+                content.set_size_request (-1, 5*70);
+
+               set_title (_("Exit nodes"));
+
+               gconf = GConf.Client.get_default ();
+               var exit_nodes = new SList<string> ();
+               try {
+                       exit_nodes = gconf.get_list (GCONF_KEY_EXITNODES, GConf.ValueType.STRING);
+               } catch (Error e) {
+                       Hildon.Banner.show_information (this, null, "Error loading exit nodes: %s".printf (e.message));
+               }
+
+               list_store = new Gtk.ListStore (1, typeof (string));
+               Gtk.TreeIter iter;
+               foreach (string exit_node in exit_nodes) {
+                       list_store.append (out iter);
+                       list_store.@set (iter, 0, exit_node);
+               }
+
+               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) => {
+                       exit_node_edit_dialog (list_store, path);
+               });
+
+               add_button (_("New"), RESPONSE_NEW);
+               response.connect ((response_id) => {
+                       if (response_id == RESPONSE_NEW) {
+                               exit_node_edit_dialog (list_store, null);
+                       }
+               });
+
+               content.show_all ();
+       }
+
+       /**
+        * Show the exit node edit dialog
+        */
+       private const int RESPONSE_DELETE = 1;
+       private void exit_node_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 exit node"));
+               else
+                       dialog.set_title (_("Edit exit node"));
+
+               var size_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
+
+               var hbox = new Gtk.HBox (false, Hildon.MARGIN_DOUBLE);
+               var label = new Gtk.Label (_("Name"));
+               label.set_alignment (0, 0.5f);
+               size_group.add_widget (label);
+               hbox.pack_start (label, false, false, 0);
+               var name_entry = new Hildon.Entry (Hildon.SizeType.FINGER_HEIGHT);
+               hbox.pack_start (name_entry, true, true, 0);
+               content.pack_start (hbox, false, false, 0);
+
+               var iter = Gtk.TreeIter ();
+               if (path != null && store.get_iter (out iter, path)) {
+                       string tmp;
+                       store.@get (iter, 0, out tmp);
+                       name_entry.set_text (tmp);
+
+                       dialog.add_button (_("Delete"), RESPONSE_DELETE);
+               }
+               dialog.add_button (_("Save"), Gtk.ResponseType.OK);
+               dialog.response.connect ((response_id) => {
+                       var exit_nodes = new SList<string> ();
+
+                       if (response_id == RESPONSE_DELETE) {
+                               if (path != null) {
+                                       Gtk.TreeIter iter2;
+                                       store.get_iter (out iter2, path);
+                                       store.remove (iter2);
+                                       string exit_node;
+                                       if (store.get_iter_first (out iter2)) do {
+                                               store.@get (iter2, 0, out exit_node);
+                                               exit_nodes.append (exit_node);
+                                       } while (store.iter_next (ref iter2));
+                                       try {
+                                               gconf.set_list (GCONF_KEY_EXITNODES,
+                                                               GConf.ValueType.STRING,
+                                                               exit_nodes);
+                                       } catch (Error e) {
+                                               Hildon.Banner.show_information (dialog, null,
+                                                                               "Failed to save exit node list: %s".printf (e.message));
+                                       }
+                               }
+                               dialog.destroy ();
+                       }
+                       if (response_id == Gtk.ResponseType.OK) {
+                               Gtk.TreeIter iter2;
+                               if (path == null) {
+                                       store.append (out iter2);
+                               } else {
+                                       store.get_iter (out iter2, path);
+                               }
+                               store.@set (iter2, 0, name_entry.get_text ());
+                               try {
+                                       exit_nodes = gconf.get_list (GCONF_KEY_EXITNODES,
+                                                                    GConf.ValueType.STRING);
+                               } catch (Error e) {
+                                       Hildon.Banner.show_information (null, null,
+                                                                       "Error loading exit nodes: %s".printf (e.message));
+                               }
+                               if (path == null) {
+                                       exit_nodes.append (name_entry.get_text ());
+                               } else {
+                                       exit_nodes = null;
+                                       string exit_node;
+                                       if (store.get_iter_first (out iter2)) do {
+                                               store.@get (iter2, 0, out exit_node);
+                                               exit_nodes.append (exit_node);
+                                       } while (store.iter_next (ref iter2));
+                               }
+                               try {
+                                       gconf.set_list (GCONF_KEY_EXITNODES,
+                                                       GConf.ValueType.STRING,
+                                                       exit_nodes);
+                               } catch (Error e) {
+                                               Hildon.Banner.show_information (dialog, null,
+                                                                               "Failed to save exit node list: %s".printf (e.message));
+                               }
+
+                               dialog.destroy ();
+                       }
+               });
+
+               dialog.show_all ();
+       }
+}
index 038f8fb..83225b2 100644 (file)
@@ -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";
@@ -189,6 +190,20 @@ class TorStatusMenuItem : HD.StatusMenuItem {
                                                                "Failed to set up bridge relays");
                        }
                }
+
+               var exits = gconf.get_list (GCONF_KEY_EXITNODES, GConf.ValueType.STRING);
+
+               if (exits.length () > 0) {
+                       // Enable strict exit nodes
+                       tor_control.set_conf_list ("ExitNodes", exits);
+                       tor_control.set_conf_bool ("StrictExitNodes", true);
+
+                       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");
+                       }
+               }
        }
 
        /**
@@ -356,6 +371,14 @@ class TorStatusMenuItem : HD.StatusMenuItem {
        }
 
        /**
+        * Show the exit node configuration dialog
+        */
+       private void exit_nodes_clicked_cb () {
+               var dialog = new ExitNodeDialog (tor_control);
+               dialog.show ();
+       }
+
+       /**
         * Check whether the IP address consists of four numbers in the 0..255 range
         */
        bool is_valid_ip_address (string address) {
@@ -404,7 +427,7 @@ 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);
+               content.set_size_request (-1, 3*70);
 
                dialog.set_title (_("Tor: anonymity online"));
 
@@ -422,6 +445,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);
@@ -473,6 +505,26 @@ 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 the ConIc connection-event signal
         */