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_disabled;
57 Gdk.Pixbuf icon_connecting;
58 Gdk.Pixbuf icon_connected;
60 // ConIc, GConf and Osso context
63 ConIc.Connection conic;
75 * Update status area icon and status menu button value
77 private void update_status () {
78 Gtk.IconTheme icon_theme;
81 if (tor_enabled && tor_connected && icon_connected == null) try {
82 icon_theme = Gtk.IconTheme.get_default ();
83 pixbuf = icon_theme.load_icon ("tor_status_connected",
84 STATUS_AREA_ICON_SIZE,
85 Gtk.IconLookupFlags.NO_SVG);
86 icon_connected = pixbuf;
90 if (!tor_enabled && icon_disabled == null) try {
91 icon_theme = Gtk.IconTheme.get_default ();
92 pixbuf = icon_theme.load_icon ("tor_status_disabled",
93 STATUS_AREA_ICON_SIZE,
94 Gtk.IconLookupFlags.NO_SVG);
95 icon_disabled = pixbuf;
99 if (tor_enabled && !tor_connected && icon_connecting == null) try {
100 icon_theme = Gtk.IconTheme.get_default ();
101 pixbuf = icon_theme.load_icon ("tor_status_connecting",
102 STATUS_AREA_ICON_SIZE,
103 Gtk.IconLookupFlags.NO_SVG);
104 icon_connecting = pixbuf;
109 if (conic_connected && tor_enabled) {
110 pixbuf = tor_connected ? icon_connected : icon_connecting;
111 button.set_value (tor_connected ? "Connected" : "Connecting ...");
113 pixbuf = conic_connected ? icon_disabled : null;
114 button.set_value (tor_enabled ? "Disconnected" : "Disabled");
116 set_status_area_icon (pixbuf);
120 * Callback for Tor daemon line output
122 private bool tor_io_func (IOChannel source, IOCondition condition) {
124 if ((condition & (IOCondition.IN | IOCondition.PRI)) != 0) {
128 /* var status = */ source.read_line (out line, out length, null);
130 if ("[notice]" in line) {
131 if ("Bootstrapped 100%" in line) {
132 tor_connected = true;
138 Hildon.Banner.show_information (null, null, "DEBUG: %s".printf (line));
142 Hildon.Banner.show_information (null, null, "Error: %s".printf (e.message));
145 if ((condition & (IOCondition.ERR | IOCondition.HUP | IOCondition.NVAL)) != 0) {
152 * Start Tor and setup proxy settings
154 private void start_tor () {
156 if (tor_pid == (Pid) 0) {
157 Process.spawn_async_with_pipes ("/tmp",
160 SpawnFlags.SEARCH_PATH,
166 var channel = new IOChannel.unix_new (tor_stdout);
167 channel.add_watch (IOCondition.IN | IOCondition.PRI | IOCondition.ERR | IOCondition.HUP | IOCondition.NVAL, tor_io_func);
169 if (polipo_pid == (Pid) 0) {
170 Process.spawn_async_with_pipes ("/tmp",
171 { "/usr/bin/polipo" },
173 SpawnFlags.SEARCH_PATH,
178 /* --> proxy settings and will be set up and tor_connected will
179 * be set to true once Tor signals 100%
181 } catch (SpawnError e) {
182 error ("Failed to spawn polipo and tor: %s", e.message);
190 * Stop Tor and revert proxy settings
192 private void stop_tor () {
194 tor_connected = false;
195 if (polipo_pid != (Pid) 0) {
196 Process.close_pid (polipo_pid);
197 Posix.kill ((Posix.pid_t) polipo_pid, Posix.SIGKILL);
198 polipo_pid = (Pid) 0;
200 if (tor_pid != (Pid) 0) {
201 Process.close_pid (tor_pid);
202 Posix.kill ((Posix.pid_t) tor_pid, Posix.SIGKILL);
210 * Setup proxy settings to route through the Tor network
212 private void proxy_setup () {
213 if (backup == null) try {
214 backup = new ProxyBackup ();
215 backup.use_http_proxy = gconf.get_bool (GCONF_KEY_PROXY_HTTP_ENABLED);
217 backup.http_host = gconf.get_string (GCONF_KEY_PROXY_HTTP_HOST);
218 backup.socks_host = gconf.get_string (GCONF_KEY_PROXY_SOCKS_HOST);
219 backup.secure_host = gconf.get_string (GCONF_KEY_PROXY_SECURE_HOST);
220 backup.http_port = gconf.get_int (GCONF_KEY_PROXY_HTTP_PORT);
221 backup.socks_port = gconf.get_int (GCONF_KEY_PROXY_SOCKS_PORT);
222 backup.secure_port = gconf.get_int (GCONF_KEY_PROXY_SECURE_PORT);
224 backup.mode = gconf.get_string (GCONF_KEY_PROXY_MODE);
226 error ("Error saving proxy settings: %s", e.message);
227 backup = new ProxyBackup ();
228 backup.use_http_proxy = false;
230 backup.http_host = "";
231 backup.socks_host = "";
232 backup.secure_host = "";
233 backup.http_port = 8080;
234 backup.socks_port = 0;
235 backup.secure_port = 0;
237 backup.mode = "none";
240 // Hildon.Banner.show_information (null, null, "DEBUG: Proxy setup");
241 gconf.set_bool (GCONF_KEY_PROXY_HTTP_ENABLED, true);
243 gconf.set_string (GCONF_KEY_PROXY_HTTP_HOST, "127.0.0.1");
244 gconf.set_string (GCONF_KEY_PROXY_SOCKS_HOST, "127.0.0.1");
245 gconf.set_string (GCONF_KEY_PROXY_SECURE_HOST, "127.0.0.1");
246 gconf.set_int (GCONF_KEY_PROXY_HTTP_PORT, 8118);
247 gconf.set_int (GCONF_KEY_PROXY_SOCKS_PORT, 9050);
248 gconf.set_int (GCONF_KEY_PROXY_SECURE_PORT, 8118);
250 gconf.set_string (GCONF_KEY_PROXY_MODE, "manual");
252 error ("Error changing proxy settings: %s", e.message);
257 * Revert proxy settings
259 private void proxy_restore () {
260 if (backup != null) try {
261 // Hildon.Banner.show_information (null, null, "DEBUG: Restoring proxy settings");
262 gconf.set_bool (GCONF_KEY_PROXY_HTTP_ENABLED, backup.use_http_proxy);
264 gconf.set_string (GCONF_KEY_PROXY_HTTP_HOST, backup.http_host);
265 gconf.set_string (GCONF_KEY_PROXY_SOCKS_HOST, backup.socks_host);
266 gconf.set_string (GCONF_KEY_PROXY_SECURE_HOST, backup.secure_host);
267 gconf.set_int (GCONF_KEY_PROXY_HTTP_PORT, backup.http_port);
268 gconf.set_int (GCONF_KEY_PROXY_SOCKS_PORT, backup.socks_port);
269 gconf.set_int (GCONF_KEY_PROXY_SECURE_PORT, backup.secure_port);
271 gconf.set_string (GCONF_KEY_PROXY_MODE, backup.mode);
274 error ("Error restoring proxy: %s", e.message);
279 * Callback for the status menu button clicked signal
281 private void button_clicked_cb () {
282 var dialog = new Gtk.Dialog ();
283 var content = (Gtk.VBox) dialog.get_content_area ();
285 dialog.set_title ("The Onion Router");
287 var check = new Hildon.CheckButton (Hildon.SizeType.FINGER_HEIGHT);
288 check.set_label ("Enable onion routing");
289 check.set_active (tor_enabled);
290 content.pack_start (check, true, true, 0);
292 dialog.add_button ("Save", Gtk.ResponseType.ACCEPT);
293 dialog.response.connect ((response_id) => {
294 if (response_id == Gtk.ResponseType.ACCEPT) {
295 if (!tor_enabled && check.get_active ()) {
298 if (conic_connected) {
301 conic.connect (ConIc.ConnectFlags.NONE);
303 } else if (tor_enabled && !check.get_active ()) {
318 * Callback for the ConIc connection-event signal
320 private void conic_connection_event_cb (ConIc.Connection conic, ConIc.ConnectionEvent event) {
321 var status = event.get_status ();
323 case ConIc.ConnectionStatus.CONNECTED:
324 conic_connected = true;
331 case ConIc.ConnectionStatus.DISCONNECTING:
332 conic_connected = false;
335 case ConIc.ConnectionStatus.DISCONNECTED:
336 case ConIc.ConnectionStatus.NETWORK_UP:
341 var error = event.get_error ();
343 case ConIc.ConnectionError.CONNECTION_FAILED:
344 Hildon.Banner.show_information (null, null, "DEBUG: ConIc connection failed");
346 case ConIc.ConnectionError.USER_CANCELED:
347 Hildon.Banner.show_information (null, null, "DEBUG: ConIc user canceled");
349 case ConIc.ConnectionError.NONE:
350 case ConIc.ConnectionError.INVALID_IAP:
356 private void create_widgets () {
357 Gtk.IconTheme icon_theme;
361 // Status menu button
362 button = new Hildon.Button.with_text (Hildon.SizeType.FINGER_HEIGHT,
363 Hildon.ButtonArrangement.VERTICAL,
365 tor_enabled ? "Enabled" : "Disabled");
366 icon_theme = Gtk.IconTheme.get_default();
368 pixbuf = icon_theme.load_icon ("tor_onion",
369 STATUS_MENU_ICON_SIZE,
370 Gtk.IconLookupFlags.NO_SVG);
371 image = new Gtk.Image.from_pixbuf (pixbuf);
372 button.set_image (image);
376 button.set_alignment (0.0f, 0.5f, 1.0f, 1.0f);
377 button.clicked.connect (button_clicked_cb);
389 gconf = GConf.Client.get_default ();
391 tor_enabled = gconf.get_bool (GCONF_KEY_TOR_ENABLED);
393 error ("Failed to get GConf setting: %s", e.message);
395 tor_connected = false;
398 conic = new ConIc.Connection ();
400 Hildon.Banner.show_information (null, null, "DEBUG: ConIc hook-up failed");
402 conic_connected = false;
403 conic.automatic_connection_events = true;
405 conic.connect (ConIc.ConnectFlags.AUTOMATICALLY_TRIGGERED);
406 conic.connection_event.connect (conic_connection_event_cb);
409 osso = new Osso.Context (STATUSMENU_TOR_LIBOSSO_SERVICE_NAME,
419 * Vala code can't use the HD_DEFINE_PLUGIN_MODULE macro, but it handles
420 * most of the class registration issues itself. Only this code from
421 * HD_PLUGIN_MODULE_SYMBOLS_CODE has to be has to be included manually
422 * to register with hildon-desktop:
425 public void hd_plugin_module_load (TypeModule plugin) {
426 // [ModuleInit] registers types automatically
427 ((HD.PluginModule) plugin).add_type (typeof (TorStatusMenuItem));
430 public void hd_plugin_module_unload (HD.PluginModule plugin) {