1 /* This file is part of status-area-applet-tor.
3 * Copyright (C) 2010 Philipp Zabel
5 * status-area-applet-tor is free software: you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * status-area-applet-tor is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with status-area-applet-tor. If not, see <http://www.gnu.org/licenses/>.
21 public bool use_http_proxy;
22 public string http_host;
23 public string socks_host;
24 public string secure_host;
26 public int socks_port;
27 public int secure_port;
31 class TorStatusMenuItem : HD.StatusMenuItem {
32 private const string STATUSMENU_TOR_LIBOSSO_SERVICE_NAME = "tor_status_menu_item";
34 private const int STATUS_MENU_ICON_SIZE = 48;
35 private const int STATUS_AREA_ICON_SIZE = 18;
37 private const string GCONF_DIR_TOR = "/apps/maemo/tor";
38 private const string GCONF_KEY_TOR_ENABLED = GCONF_DIR_TOR + "/enabled";
40 private const string GCONF_DIR_PROXY_HTTP = "/system/http_proxy";
41 private const string GCONF_KEY_PROXY_HTTP_ENABLED = GCONF_DIR_PROXY_HTTP + "/use_http_proxy";
42 private const string GCONF_KEY_PROXY_HTTP_HOST = GCONF_DIR_PROXY_HTTP + "/host";
43 private const string GCONF_KEY_PROXY_HTTP_PORT = GCONF_DIR_PROXY_HTTP + "/port";
45 private const string GCONF_DIR_PROXY = "/system/proxy";
46 private const string GCONF_KEY_PROXY_MODE = GCONF_DIR_PROXY + "/mode";
47 private const string GCONF_KEY_PROXY_SOCKS_HOST = GCONF_DIR_PROXY + "/socks_host";
48 private const string GCONF_KEY_PROXY_SOCKS_PORT = GCONF_DIR_PROXY + "/socks_port";
49 private const string GCONF_KEY_PROXY_SECURE_HOST = GCONF_DIR_PROXY + "/secure_host";
50 private const string GCONF_KEY_PROXY_SECURE_PORT = GCONF_DIR_PROXY + "/secure_port";
56 Gdk.Pixbuf icon_connecting;
57 Gdk.Pixbuf icon_connected;
58 Gtk.Image icon_enabled;
59 Gtk.Image icon_disabled;
61 // ConIc, GConf and Osso context
64 ConIc.Connection conic;
76 * Update status area icon and status menu button value
78 private void update_status () {
79 if (tor_enabled && tor_connected && icon_connected == null) try {
80 var icon_theme = Gtk.IconTheme.get_default ();
81 var pixbuf = icon_theme.load_icon ("statusarea_tor_connected",
82 STATUS_AREA_ICON_SIZE,
83 Gtk.IconLookupFlags.NO_SVG);
84 icon_connected = pixbuf;
88 if (tor_enabled && !tor_connected && icon_connecting == null) try {
89 var icon_theme = Gtk.IconTheme.get_default ();
90 var pixbuf = icon_theme.load_icon ("statusarea_tor_connecting",
91 STATUS_AREA_ICON_SIZE,
92 Gtk.IconLookupFlags.NO_SVG);
93 icon_connecting = pixbuf;
97 if (tor_enabled && icon_enabled == null) try {
98 var icon_theme = Gtk.IconTheme.get_default();
99 var pixbuf = icon_theme.load_icon ("statusarea_tor_enabled",
100 STATUS_MENU_ICON_SIZE,
101 Gtk.IconLookupFlags.NO_SVG);
102 icon_enabled = new Gtk.Image.from_pixbuf (pixbuf);
106 if (!tor_enabled && icon_disabled == null) try {
107 var icon_theme = Gtk.IconTheme.get_default();
108 var pixbuf = icon_theme.load_icon ("statusarea_tor_disabled",
109 STATUS_MENU_ICON_SIZE,
110 Gtk.IconLookupFlags.NO_SVG);
111 icon_disabled = new Gtk.Image.from_pixbuf (pixbuf);
116 if (conic_connected && tor_enabled) {
117 set_status_area_icon (tor_connected ? icon_connected : icon_connecting);
118 button.set_value (tor_connected ? _("Connected") : _("Connecting ..."));
120 set_status_area_icon (null);
121 button.set_value (tor_enabled ? _("Disconnected") : _("Disabled"));
123 button.set_image (tor_enabled ? icon_enabled : icon_disabled);
127 * Callback for Tor daemon line output
129 private bool tor_io_func (IOChannel source, IOCondition condition) {
131 if ((condition & (IOCondition.IN | IOCondition.PRI)) != 0) {
135 /* var status = */ source.read_line (out line, out length, null);
137 if ("[notice]" in line) {
138 if ("Bootstrapped 100%" in line) {
139 tor_connected = true;
145 Hildon.Banner.show_information (null, null, "DEBUG: %s".printf (line));
149 Hildon.Banner.show_information (null, null, "Error: %s".printf (e.message));
152 if ((condition & (IOCondition.ERR | IOCondition.HUP | IOCondition.NVAL)) != 0) {
159 * Start Tor and setup proxy settings
161 private void start_tor () {
163 if (tor_pid == (Pid) 0) {
164 Process.spawn_async_with_pipes ("/tmp",
167 SpawnFlags.SEARCH_PATH,
173 var channel = new IOChannel.unix_new (tor_stdout);
174 channel.add_watch (IOCondition.IN | IOCondition.PRI | IOCondition.ERR | IOCondition.HUP | IOCondition.NVAL, tor_io_func);
176 if (polipo_pid == (Pid) 0) {
177 Process.spawn_async_with_pipes ("/tmp",
178 { "/usr/bin/polipo" },
180 SpawnFlags.SEARCH_PATH,
185 /* --> proxy settings and will be set up and tor_connected will
186 * be set to true once Tor signals 100%
188 } catch (SpawnError e) {
189 error ("Failed to spawn polipo and tor: %s", e.message);
197 * Stop Tor and revert proxy settings
199 private void stop_tor () {
201 tor_connected = false;
202 if (polipo_pid != (Pid) 0) {
203 Process.close_pid (polipo_pid);
204 Posix.kill ((Posix.pid_t) polipo_pid, Posix.SIGKILL);
205 polipo_pid = (Pid) 0;
207 if (tor_pid != (Pid) 0) {
208 Process.close_pid (tor_pid);
209 Posix.kill ((Posix.pid_t) tor_pid, Posix.SIGKILL);
217 * Setup proxy settings to route through the Tor network
219 private void proxy_setup () {
220 if (backup == null) try {
221 backup = new ProxyBackup ();
222 backup.use_http_proxy = gconf.get_bool (GCONF_KEY_PROXY_HTTP_ENABLED);
224 backup.http_host = gconf.get_string (GCONF_KEY_PROXY_HTTP_HOST);
225 backup.socks_host = gconf.get_string (GCONF_KEY_PROXY_SOCKS_HOST);
226 backup.secure_host = gconf.get_string (GCONF_KEY_PROXY_SECURE_HOST);
227 backup.http_port = gconf.get_int (GCONF_KEY_PROXY_HTTP_PORT);
228 backup.socks_port = gconf.get_int (GCONF_KEY_PROXY_SOCKS_PORT);
229 backup.secure_port = gconf.get_int (GCONF_KEY_PROXY_SECURE_PORT);
231 backup.mode = gconf.get_string (GCONF_KEY_PROXY_MODE);
233 error ("Error saving proxy settings: %s", e.message);
234 backup = new ProxyBackup ();
235 backup.use_http_proxy = false;
237 backup.http_host = "";
238 backup.socks_host = "";
239 backup.secure_host = "";
240 backup.http_port = 8080;
241 backup.socks_port = 0;
242 backup.secure_port = 0;
244 backup.mode = "none";
247 // Hildon.Banner.show_information (null, null, "DEBUG: Proxy setup");
248 gconf.set_bool (GCONF_KEY_PROXY_HTTP_ENABLED, true);
250 gconf.set_string (GCONF_KEY_PROXY_HTTP_HOST, "127.0.0.1");
251 gconf.set_string (GCONF_KEY_PROXY_SOCKS_HOST, "127.0.0.1");
252 gconf.set_string (GCONF_KEY_PROXY_SECURE_HOST, "127.0.0.1");
253 gconf.set_int (GCONF_KEY_PROXY_HTTP_PORT, 8118);
254 gconf.set_int (GCONF_KEY_PROXY_SOCKS_PORT, 9050);
255 gconf.set_int (GCONF_KEY_PROXY_SECURE_PORT, 8118);
257 gconf.set_string (GCONF_KEY_PROXY_MODE, "manual");
259 error ("Error changing proxy settings: %s", e.message);
264 * Revert proxy settings
266 private void proxy_restore () {
267 if (backup != null) try {
268 // Hildon.Banner.show_information (null, null, "DEBUG: Restoring proxy settings");
269 gconf.set_bool (GCONF_KEY_PROXY_HTTP_ENABLED, backup.use_http_proxy);
271 gconf.set_string (GCONF_KEY_PROXY_HTTP_HOST, backup.http_host);
272 gconf.set_string (GCONF_KEY_PROXY_SOCKS_HOST, backup.socks_host);
273 gconf.set_string (GCONF_KEY_PROXY_SECURE_HOST, backup.secure_host);
274 gconf.set_int (GCONF_KEY_PROXY_HTTP_PORT, backup.http_port);
275 gconf.set_int (GCONF_KEY_PROXY_SOCKS_PORT, backup.socks_port);
276 gconf.set_int (GCONF_KEY_PROXY_SECURE_PORT, backup.secure_port);
278 gconf.set_string (GCONF_KEY_PROXY_MODE, backup.mode);
281 error ("Error restoring proxy: %s", e.message);
286 * Callback for the status menu button clicked signal
288 private void button_clicked_cb () {
289 var dialog = new Gtk.Dialog ();
290 var content = (Gtk.VBox) dialog.get_content_area ();
292 dialog.set_title (_("Tor: anonymity online"));
294 var check = new Hildon.CheckButton (Hildon.SizeType.FINGER_HEIGHT);
295 check.set_label (_("Enable onion routing"));
296 check.set_active (tor_enabled);
297 content.pack_start (check, true, true, 0);
299 dialog.add_button (_("Save"), Gtk.ResponseType.ACCEPT);
300 dialog.response.connect ((response_id) => {
301 if (response_id == Gtk.ResponseType.ACCEPT) {
302 if (!tor_enabled && check.get_active ()) {
305 if (conic_connected) {
308 conic.connect (ConIc.ConnectFlags.NONE);
310 } else if (tor_enabled && !check.get_active ()) {
325 * Callback for the ConIc connection-event signal
327 private void conic_connection_event_cb (ConIc.Connection conic, ConIc.ConnectionEvent event) {
328 var status = event.get_status ();
330 case ConIc.ConnectionStatus.CONNECTED:
331 conic_connected = true;
338 case ConIc.ConnectionStatus.DISCONNECTING:
339 conic_connected = false;
342 case ConIc.ConnectionStatus.DISCONNECTED:
343 case ConIc.ConnectionStatus.NETWORK_UP:
348 var error = event.get_error ();
350 case ConIc.ConnectionError.CONNECTION_FAILED:
351 Hildon.Banner.show_information (null, null, "DEBUG: ConIc connection failed");
353 case ConIc.ConnectionError.USER_CANCELED:
354 Hildon.Banner.show_information (null, null, "DEBUG: ConIc user canceled");
356 case ConIc.ConnectionError.NONE:
357 case ConIc.ConnectionError.INVALID_IAP:
363 private void create_widgets () {
364 // Status menu button
365 button = new Hildon.Button.with_text (Hildon.SizeType.FINGER_HEIGHT,
366 Hildon.ButtonArrangement.VERTICAL,
367 _("The Onion Router"),
368 tor_enabled ? _("Enabled") : _("Disabled"));
369 button.set_alignment (0.0f, 0.5f, 1.0f, 1.0f);
370 button.set_style (Hildon.ButtonStyle.PICKER);
371 button.clicked.connect (button_clicked_cb);
383 Intl.setlocale (LocaleCategory.ALL, "");
384 Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
385 Intl.textdomain (Config.GETTEXT_PACKAGE);
388 gconf = GConf.Client.get_default ();
390 tor_enabled = gconf.get_bool (GCONF_KEY_TOR_ENABLED);
392 error ("Failed to get GConf setting: %s", e.message);
394 tor_connected = false;
397 conic = new ConIc.Connection ();
399 Hildon.Banner.show_information (null, null, "DEBUG: ConIc hook-up failed");
401 conic_connected = false;
402 conic.automatic_connection_events = true;
404 conic.connect (ConIc.ConnectFlags.AUTOMATICALLY_TRIGGERED);
405 conic.connection_event.connect (conic_connection_event_cb);
408 osso = new Osso.Context (STATUSMENU_TOR_LIBOSSO_SERVICE_NAME,
418 * Vala code can't use the HD_DEFINE_PLUGIN_MODULE macro, but it handles
419 * most of the class registration issues itself. Only this code from
420 * HD_PLUGIN_MODULE_SYMBOLS_CODE has to be has to be included manually
421 * to register with hildon-desktop:
424 public void hd_plugin_module_load (TypeModule plugin) {
425 // [ModuleInit] registers types automatically
426 ((HD.PluginModule) plugin).add_type (typeof (TorStatusMenuItem));
429 public void hd_plugin_module_unload (HD.PluginModule plugin) {