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;
59 // ConIc, GConf and Osso context
62 ConIc.Connection conic;
74 * Update status area icon and status menu button value
76 private void update_status () {
77 Gtk.IconTheme icon_theme;
80 if (tor_enabled && tor_connected && icon_connected == null) try {
81 icon_theme = Gtk.IconTheme.get_default ();
82 pixbuf = icon_theme.load_icon ("statusarea_tor_connected",
83 STATUS_AREA_ICON_SIZE,
84 Gtk.IconLookupFlags.NO_SVG);
85 icon_connected = pixbuf;
89 if (tor_enabled && !tor_connected && icon_connecting == null) try {
90 icon_theme = Gtk.IconTheme.get_default ();
91 pixbuf = icon_theme.load_icon ("statusarea_tor_connecting",
92 STATUS_AREA_ICON_SIZE,
93 Gtk.IconLookupFlags.NO_SVG);
94 icon_connecting = pixbuf;
99 if (conic_connected && tor_enabled) {
100 pixbuf = tor_connected ? icon_connected : icon_connecting;
101 button.set_value (tor_connected ? _("Connected") : _("Connecting ..."));
104 button.set_value (tor_enabled ? _("Disconnected") : _("Disabled"));
106 set_status_area_icon (pixbuf);
110 * Callback for Tor daemon line output
112 private bool tor_io_func (IOChannel source, IOCondition condition) {
114 if ((condition & (IOCondition.IN | IOCondition.PRI)) != 0) {
118 /* var status = */ source.read_line (out line, out length, null);
120 if ("[notice]" in line) {
121 if ("Bootstrapped 100%" in line) {
122 tor_connected = true;
128 Hildon.Banner.show_information (null, null, "DEBUG: %s".printf (line));
132 Hildon.Banner.show_information (null, null, "Error: %s".printf (e.message));
135 if ((condition & (IOCondition.ERR | IOCondition.HUP | IOCondition.NVAL)) != 0) {
142 * Start Tor and setup proxy settings
144 private void start_tor () {
146 if (tor_pid == (Pid) 0) {
147 Process.spawn_async_with_pipes ("/tmp",
150 SpawnFlags.SEARCH_PATH,
156 var channel = new IOChannel.unix_new (tor_stdout);
157 channel.add_watch (IOCondition.IN | IOCondition.PRI | IOCondition.ERR | IOCondition.HUP | IOCondition.NVAL, tor_io_func);
159 if (polipo_pid == (Pid) 0) {
160 Process.spawn_async_with_pipes ("/tmp",
161 { "/usr/bin/polipo" },
163 SpawnFlags.SEARCH_PATH,
168 /* --> proxy settings and will be set up and tor_connected will
169 * be set to true once Tor signals 100%
171 } catch (SpawnError e) {
172 error ("Failed to spawn polipo and tor: %s", e.message);
180 * Stop Tor and revert proxy settings
182 private void stop_tor () {
184 tor_connected = false;
185 if (polipo_pid != (Pid) 0) {
186 Process.close_pid (polipo_pid);
187 Posix.kill ((Posix.pid_t) polipo_pid, Posix.SIGKILL);
188 polipo_pid = (Pid) 0;
190 if (tor_pid != (Pid) 0) {
191 Process.close_pid (tor_pid);
192 Posix.kill ((Posix.pid_t) tor_pid, Posix.SIGKILL);
200 * Setup proxy settings to route through the Tor network
202 private void proxy_setup () {
203 if (backup == null) try {
204 backup = new ProxyBackup ();
205 backup.use_http_proxy = gconf.get_bool (GCONF_KEY_PROXY_HTTP_ENABLED);
207 backup.http_host = gconf.get_string (GCONF_KEY_PROXY_HTTP_HOST);
208 backup.socks_host = gconf.get_string (GCONF_KEY_PROXY_SOCKS_HOST);
209 backup.secure_host = gconf.get_string (GCONF_KEY_PROXY_SECURE_HOST);
210 backup.http_port = gconf.get_int (GCONF_KEY_PROXY_HTTP_PORT);
211 backup.socks_port = gconf.get_int (GCONF_KEY_PROXY_SOCKS_PORT);
212 backup.secure_port = gconf.get_int (GCONF_KEY_PROXY_SECURE_PORT);
214 backup.mode = gconf.get_string (GCONF_KEY_PROXY_MODE);
216 error ("Error saving proxy settings: %s", e.message);
217 backup = new ProxyBackup ();
218 backup.use_http_proxy = false;
220 backup.http_host = "";
221 backup.socks_host = "";
222 backup.secure_host = "";
223 backup.http_port = 8080;
224 backup.socks_port = 0;
225 backup.secure_port = 0;
227 backup.mode = "none";
230 // Hildon.Banner.show_information (null, null, "DEBUG: Proxy setup");
231 gconf.set_bool (GCONF_KEY_PROXY_HTTP_ENABLED, true);
233 gconf.set_string (GCONF_KEY_PROXY_HTTP_HOST, "127.0.0.1");
234 gconf.set_string (GCONF_KEY_PROXY_SOCKS_HOST, "127.0.0.1");
235 gconf.set_string (GCONF_KEY_PROXY_SECURE_HOST, "127.0.0.1");
236 gconf.set_int (GCONF_KEY_PROXY_HTTP_PORT, 8118);
237 gconf.set_int (GCONF_KEY_PROXY_SOCKS_PORT, 9050);
238 gconf.set_int (GCONF_KEY_PROXY_SECURE_PORT, 8118);
240 gconf.set_string (GCONF_KEY_PROXY_MODE, "manual");
242 error ("Error changing proxy settings: %s", e.message);
247 * Revert proxy settings
249 private void proxy_restore () {
250 if (backup != null) try {
251 // Hildon.Banner.show_information (null, null, "DEBUG: Restoring proxy settings");
252 gconf.set_bool (GCONF_KEY_PROXY_HTTP_ENABLED, backup.use_http_proxy);
254 gconf.set_string (GCONF_KEY_PROXY_HTTP_HOST, backup.http_host);
255 gconf.set_string (GCONF_KEY_PROXY_SOCKS_HOST, backup.socks_host);
256 gconf.set_string (GCONF_KEY_PROXY_SECURE_HOST, backup.secure_host);
257 gconf.set_int (GCONF_KEY_PROXY_HTTP_PORT, backup.http_port);
258 gconf.set_int (GCONF_KEY_PROXY_SOCKS_PORT, backup.socks_port);
259 gconf.set_int (GCONF_KEY_PROXY_SECURE_PORT, backup.secure_port);
261 gconf.set_string (GCONF_KEY_PROXY_MODE, backup.mode);
264 error ("Error restoring proxy: %s", e.message);
269 * Callback for the status menu button clicked signal
271 private void button_clicked_cb () {
272 var dialog = new Gtk.Dialog ();
273 var content = (Gtk.VBox) dialog.get_content_area ();
275 dialog.set_title (_("Tor: anonymity online"));
277 var check = new Hildon.CheckButton (Hildon.SizeType.FINGER_HEIGHT);
278 check.set_label (_("Enable onion routing"));
279 check.set_active (tor_enabled);
280 content.pack_start (check, true, true, 0);
282 dialog.add_button (_("Save"), Gtk.ResponseType.ACCEPT);
283 dialog.response.connect ((response_id) => {
284 if (response_id == Gtk.ResponseType.ACCEPT) {
285 if (!tor_enabled && check.get_active ()) {
288 if (conic_connected) {
291 conic.connect (ConIc.ConnectFlags.NONE);
293 } else if (tor_enabled && !check.get_active ()) {
308 * Callback for the ConIc connection-event signal
310 private void conic_connection_event_cb (ConIc.Connection conic, ConIc.ConnectionEvent event) {
311 var status = event.get_status ();
313 case ConIc.ConnectionStatus.CONNECTED:
314 conic_connected = true;
321 case ConIc.ConnectionStatus.DISCONNECTING:
322 conic_connected = false;
325 case ConIc.ConnectionStatus.DISCONNECTED:
326 case ConIc.ConnectionStatus.NETWORK_UP:
331 var error = event.get_error ();
333 case ConIc.ConnectionError.CONNECTION_FAILED:
334 Hildon.Banner.show_information (null, null, "DEBUG: ConIc connection failed");
336 case ConIc.ConnectionError.USER_CANCELED:
337 Hildon.Banner.show_information (null, null, "DEBUG: ConIc user canceled");
339 case ConIc.ConnectionError.NONE:
340 case ConIc.ConnectionError.INVALID_IAP:
346 private void create_widgets () {
347 Gtk.IconTheme icon_theme;
351 // Status menu button
352 button = new Hildon.Button.with_text (Hildon.SizeType.FINGER_HEIGHT,
353 Hildon.ButtonArrangement.VERTICAL,
354 _("The Onion Router"),
355 tor_enabled ? _("Enabled") : _("Disabled"));
356 icon_theme = Gtk.IconTheme.get_default();
358 pixbuf = icon_theme.load_icon ("tor_onion",
359 STATUS_MENU_ICON_SIZE,
360 Gtk.IconLookupFlags.NO_SVG);
361 image = new Gtk.Image.from_pixbuf (pixbuf);
362 button.set_image (image);
366 button.set_alignment (0.0f, 0.5f, 1.0f, 1.0f);
367 button.set_style (Hildon.ButtonStyle.PICKER);
368 button.clicked.connect (button_clicked_cb);
380 Intl.setlocale (LocaleCategory.ALL, "");
381 Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
382 Intl.textdomain (Config.GETTEXT_PACKAGE);
385 gconf = GConf.Client.get_default ();
387 tor_enabled = gconf.get_bool (GCONF_KEY_TOR_ENABLED);
389 error ("Failed to get GConf setting: %s", e.message);
391 tor_connected = false;
394 conic = new ConIc.Connection ();
396 Hildon.Banner.show_information (null, null, "DEBUG: ConIc hook-up failed");
398 conic_connected = false;
399 conic.automatic_connection_events = true;
401 conic.connect (ConIc.ConnectFlags.AUTOMATICALLY_TRIGGERED);
402 conic.connection_event.connect (conic_connection_event_cb);
405 osso = new Osso.Context (STATUSMENU_TOR_LIBOSSO_SERVICE_NAME,
415 * Vala code can't use the HD_DEFINE_PLUGIN_MODULE macro, but it handles
416 * most of the class registration issues itself. Only this code from
417 * HD_PLUGIN_MODULE_SYMBOLS_CODE has to be has to be included manually
418 * to register with hildon-desktop:
421 public void hd_plugin_module_load (TypeModule plugin) {
422 // [ModuleInit] registers types automatically
423 ((HD.PluginModule) plugin).add_type (typeof (TorStatusMenuItem));
426 public void hd_plugin_module_unload (HD.PluginModule plugin) {