/* 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
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";
// Widgets
Hildon.Button button;
+ Gtk.Label log_label;
// Icons
Gdk.Pixbuf icon_connecting;
/**
* 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) {
button.set_value (tor_enabled ? _("Disconnected") : _("Disabled"));
}
button.set_image (tor_enabled ? icon_enabled : icon_disabled);
+
+ return false;
}
/**
/* 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;
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");
+ }
}
}
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,
}
tor_log = "";
+ if (log_label != null)
+ log_label.label = tor_log;
update_status ();
}
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;
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);
}
}
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 ();
}
/**
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 ();
}
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"));
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);
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 ();
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)
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
*/
add (button);
+ log_label = null;
+
// Status area icon
update_status ();
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;