Listen to changes of the /apps/maemo/tor/enabled GConf key
[tor-status] / src / status-area-applet-tor.vala
1 /* This file is part of status-area-applet-tor.
2  *
3  * Copyright (C) 2010-2011 Philipp Zabel
4  *
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.
9  *
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.
14  *
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/>.
17  */
18
19 [Compact]
20 class ProxyBackup {
21         public bool use_http_proxy;
22         public string http_host;
23         public string socks_host;
24         public string secure_host;
25         public int http_port;
26         public int socks_port;
27         public int secure_port;
28         public string mode;
29 }
30
31 class TorStatusMenuItem : HD.StatusMenuItem {
32         private const string STATUSMENU_TOR_LIBOSSO_SERVICE_NAME = "tor_status_menu_item";
33
34         private const int STATUS_MENU_ICON_SIZE = 48;
35         private const int STATUS_AREA_ICON_SIZE = 18;
36
37         private const string GCONF_DIR_TOR         = "/apps/maemo/tor";
38         private const string GCONF_KEY_TOR_ENABLED = GCONF_DIR_TOR + "/enabled";
39         private const string GCONF_KEY_BRIDGES     = GCONF_DIR_TOR + "/bridges";
40         private const string GCONF_KEY_EXITNODES   = GCONF_DIR_TOR + "/exit_nodes";
41
42         private const string GCONF_DIR_PROXY_HTTP         = "/system/http_proxy";
43         private const string GCONF_KEY_PROXY_HTTP_ENABLED = GCONF_DIR_PROXY_HTTP + "/use_http_proxy";
44         private const string GCONF_KEY_PROXY_HTTP_HOST    = GCONF_DIR_PROXY_HTTP + "/host";
45         private const string GCONF_KEY_PROXY_HTTP_PORT    = GCONF_DIR_PROXY_HTTP + "/port";
46
47         private const string GCONF_DIR_PROXY             = "/system/proxy";
48         private const string GCONF_KEY_PROXY_MODE        = GCONF_DIR_PROXY + "/mode";
49         private const string GCONF_KEY_PROXY_SOCKS_HOST  = GCONF_DIR_PROXY + "/socks_host";
50         private const string GCONF_KEY_PROXY_SOCKS_PORT  = GCONF_DIR_PROXY + "/socks_port";
51         private const string GCONF_KEY_PROXY_SECURE_HOST = GCONF_DIR_PROXY + "/secure_host";
52         private const string GCONF_KEY_PROXY_SECURE_PORT = GCONF_DIR_PROXY + "/secure_port";
53
54         // Widgets
55         Hildon.Button button;
56         Gtk.Label log_label;
57
58         // Icons
59         Gdk.Pixbuf icon_connecting;
60         Gdk.Pixbuf icon_connected;
61         Gtk.Image icon_enabled;
62         Gtk.Image icon_disabled;
63
64         // ConIc, GConf and Osso context
65         Osso.Context osso;
66         GConf.Client gconf;
67         ConIc.Connection conic;
68         bool conic_connected;
69
70         // Internal state
71         bool tor_enabled;
72         bool tor_connected;
73         Pid tor_pid;
74         int tor_stdout;
75         Pid polipo_pid;
76         ProxyBackup backup;
77         string tor_log;
78         TorControl.Connection tor_control;
79         string password;
80
81         /**
82          * Update status area icon and status menu button value
83          */
84         private bool update_status () {
85                 try {
86                         if (tor_enabled && tor_connected && icon_connected == null) {
87                                 var icon_theme = Gtk.IconTheme.get_default ();
88                                 var pixbuf = icon_theme.load_icon ("statusarea_tor_connected",
89                                                                    STATUS_AREA_ICON_SIZE,
90                                                                    Gtk.IconLookupFlags.NO_SVG);
91                                 icon_connected = pixbuf;
92                         }
93                         if (tor_enabled && !tor_connected && icon_connecting == null) {
94                                 var icon_theme = Gtk.IconTheme.get_default ();
95                                 var pixbuf = icon_theme.load_icon ("statusarea_tor_connecting",
96                                                                    STATUS_AREA_ICON_SIZE,
97                                                                    Gtk.IconLookupFlags.NO_SVG);
98                                 icon_connecting = pixbuf;
99                         }
100                         if (tor_enabled && icon_enabled == null) {
101                                 var icon_theme = Gtk.IconTheme.get_default();
102                                 var pixbuf = icon_theme.load_icon ("statusarea_tor_enabled",
103                                                                    STATUS_MENU_ICON_SIZE,
104                                                                    Gtk.IconLookupFlags.NO_SVG);
105                                 icon_enabled = new Gtk.Image.from_pixbuf (pixbuf);
106                         }
107                         if (!tor_enabled && icon_disabled == null) {
108                                 var icon_theme = Gtk.IconTheme.get_default();
109                                 var pixbuf = icon_theme.load_icon ("statusarea_tor_disabled",
110                                                                    STATUS_MENU_ICON_SIZE,
111                                                                    Gtk.IconLookupFlags.NO_SVG);
112                                 icon_disabled = new Gtk.Image.from_pixbuf (pixbuf);
113                         }
114                 } catch (Error e) {
115                         critical (e.message);
116                         var icon_theme = Gtk.IconTheme.get_default ();
117                         icon_theme.rescan_if_needed ();
118                         Timeout.add_seconds (1, update_status);
119                         return false;
120                 }
121
122                 if (conic_connected && tor_enabled) {
123                         set_status_area_icon (tor_connected ? icon_connected : icon_connecting);
124                         button.set_value (tor_connected ? _("Connected") : _("Connecting ..."));
125                 } else {
126                         set_status_area_icon (null);
127                         button.set_value (tor_enabled ? _("Disconnected") : _("Disabled"));
128                 }
129                 button.set_image (tor_enabled ? icon_enabled : icon_disabled);
130
131                 return false;
132         }
133
134         /**
135          * Callback for Tor daemon line output
136          */
137         private bool tor_io_func (IOChannel source, IOCondition condition) {
138
139                 if ((condition & (IOCondition.IN | IOCondition.PRI)) != 0) {
140                         string line = null;
141                         size_t length;
142                         try {
143                                 /* var status = */ source.read_line (out line, out length, null);
144
145                                 tor_log += line;
146                                 if (log_label != null)
147                                         log_label.label = tor_log;
148
149                                 if ("[notice]" in line) {
150                                         if ("Bootstrapped 100%" in line) {
151                                                 tor_connected = true;
152                                                 proxy_setup ();
153                                                 update_status ();
154                                         }
155                                         if ("Opening Control listener on 127.0.0.1:9051" in line) {
156                                                 tor_control = new TorControl.Connection ();
157                                                 tor_control_auth.begin ();
158                                         }
159                                 } else {
160                                         // FIXME
161                                         Hildon.Banner.show_information (null, null, "DEBUG: %s".printf (line));
162                                 }
163                         } catch (Error e) {
164                                 // FIXME
165                                 Hildon.Banner.show_information (null, null, "Error: %s".printf (e.message));
166                         }
167                 }
168                 if ((condition & (IOCondition.ERR | IOCondition.HUP | IOCondition.NVAL)) != 0) {
169                         return false;
170                 }
171                 return true;
172         }
173
174         /**
175          * Authenticate with Tor on the control channel
176          */
177         private async void tor_control_auth () throws Error {
178                 yield tor_control.authenticate_async (password);
179
180                 var bridges = gconf.get_list (GCONF_KEY_BRIDGES, GConf.ValueType.STRING);
181
182                 if (bridges.length () > 0) {
183                         // Enable bridge relays
184                         tor_control.set_conf_list ("Bridge", bridges);
185                         tor_control.set_conf_bool ("UseBridges", true);
186
187                         bool use = yield tor_control.get_conf_bool_async ("UseBridges");
188                         if (!use) {
189                                 Hildon.Banner.show_information (null, null,
190                                                                 "Failed to set up bridge relays");
191                         }
192                 }
193
194                 var exits = gconf.get_list (GCONF_KEY_EXITNODES, GConf.ValueType.STRING);
195
196                 if (exits.length () > 0) {
197                         // Enable strict exit nodes
198                         tor_control.set_conf_list ("ExitNodes", exits);
199                         tor_control.set_conf_bool ("StrictExitNodes", true);
200
201                         bool strict = yield tor_control.get_conf_bool_async ("StrictExitNodes");
202                         if (!strict) {
203                                 Hildon.Banner.show_information (null, null,
204                                                                 "Failed to set up strict exit nodes");
205                         }
206                 }
207         }
208
209         /**
210          * Start Tor and setup proxy settings
211          */
212         private void start_tor () {
213                 try {
214                         if (tor_pid == (Pid) 0) {
215                                 string[] tor_hash_argv = {
216                                         "/usr/sbin/tor",
217                                         "--hash-password", "",
218                                         null
219                                 };
220                                 var tv = TimeVal ();
221                                 Random.set_seed ((uint32) tv.tv_usec);
222                                 password = "tor-status-%8x".printf (Random.next_int ());
223                                 tor_hash_argv[2] = password;
224                                 string hash;
225                                 Process.spawn_sync ("/tmp", tor_hash_argv, null, 0, null, out hash);
226                                 hash = hash.str ("\n16:").offset (1).replace ("\n", "");
227
228                                 if (hash == null) {
229                                         Hildon.Banner.show_information (null, null,
230                                                                         "Failed to get hash");
231                                         return;
232                                 }
233
234                                 string[] tor_argv = {
235                                         "/usr/sbin/tor",
236                                         "--ControlPort", "9051",
237                                         "--HashedControlPassword", "",
238                                         null
239                                 };
240                                 tor_argv[4] = hash;
241                                 Process.spawn_async_with_pipes ("/tmp",
242                                                                 tor_argv,
243                                                                 null,
244                                                                 SpawnFlags.SEARCH_PATH,
245                                                                 null,
246                                                                 out tor_pid,
247                                                                 null,
248                                                                 out tor_stdout);
249
250                                 var channel = new IOChannel.unix_new (tor_stdout);
251                                 channel.add_watch (IOCondition.IN | IOCondition.PRI | IOCondition.ERR | IOCondition.HUP | IOCondition.NVAL, tor_io_func);
252                         }
253                         if (polipo_pid == (Pid) 0) {
254                                 Process.spawn_async_with_pipes ("/tmp",
255                                                                 { "/usr/bin/polipo" },
256                                                                 null,
257                                                                 SpawnFlags.SEARCH_PATH,
258                                                                 null,
259                                                                 out polipo_pid);
260                         }
261
262                         /* --> proxy settings and will be set up and tor_connected will
263                          * be set to true once Tor signals 100%
264                          */
265                 } catch (SpawnError e) {
266                         Hildon.Banner.show_information (null, null, "DEBUG: Failed to spawn polipo and tor: %s".printf (e.message));
267                         return;
268                 }
269
270                 tor_log = "";
271                 if (log_label != null)
272                         log_label.label = tor_log;
273                 update_status ();
274         }
275
276         /**
277          * Stop Tor and revert proxy settings
278          */
279         private void stop_tor () {
280                 proxy_restore ();
281                 tor_connected = false;
282                 if (polipo_pid != (Pid) 0) {
283                         Process.close_pid (polipo_pid);
284                         Posix.kill ((Posix.pid_t) polipo_pid, Posix.SIGKILL);
285                         polipo_pid = (Pid) 0;
286                 }
287                 if (tor_pid != (Pid) 0) {
288                         Process.close_pid (tor_pid);
289                         Posix.kill ((Posix.pid_t) tor_pid, Posix.SIGKILL);
290                         tor_pid = (Pid) 0;
291                 }
292
293                 update_status ();
294         }
295
296         /**
297          * Setup proxy settings to route through the Tor network
298          */
299         private void proxy_setup () {
300                 if (backup == null) try {
301                         backup = new ProxyBackup ();
302                         backup.use_http_proxy = gconf.get_bool (GCONF_KEY_PROXY_HTTP_ENABLED);
303
304                         backup.http_host = gconf.get_string (GCONF_KEY_PROXY_HTTP_HOST);
305                         backup.socks_host = gconf.get_string (GCONF_KEY_PROXY_SOCKS_HOST);
306                         backup.secure_host = gconf.get_string (GCONF_KEY_PROXY_SECURE_HOST);
307                         backup.http_port = gconf.get_int (GCONF_KEY_PROXY_HTTP_PORT);
308                         backup.socks_port = gconf.get_int (GCONF_KEY_PROXY_SOCKS_PORT);
309                         backup.secure_port = gconf.get_int (GCONF_KEY_PROXY_SECURE_PORT);
310
311                         backup.mode = gconf.get_string (GCONF_KEY_PROXY_MODE);
312                 } catch (Error e) {
313                         critical ("Error saving proxy settings: %s", e.message);
314                         backup = new ProxyBackup ();
315                         backup.use_http_proxy = false;
316
317                         backup.http_host = "";
318                         backup.socks_host = "";
319                         backup.secure_host = "";
320                         backup.http_port = 8080;
321                         backup.socks_port = 0;
322                         backup.secure_port = 0;
323
324                         backup.mode = "none";
325                 }
326                 try {
327                 //      Hildon.Banner.show_information (null, null, "DEBUG: Proxy setup");
328                         gconf.set_bool (GCONF_KEY_PROXY_HTTP_ENABLED, true);
329
330                         gconf.set_string (GCONF_KEY_PROXY_HTTP_HOST, "127.0.0.1");
331                         gconf.set_string (GCONF_KEY_PROXY_SOCKS_HOST, "127.0.0.1");
332                         gconf.set_string (GCONF_KEY_PROXY_SECURE_HOST, "127.0.0.1");
333                         gconf.set_int (GCONF_KEY_PROXY_HTTP_PORT, 8118);
334                         gconf.set_int (GCONF_KEY_PROXY_SOCKS_PORT, 9050);
335                         gconf.set_int (GCONF_KEY_PROXY_SECURE_PORT, 8118);
336
337                         gconf.set_string (GCONF_KEY_PROXY_MODE, "manual");
338                 } catch (Error e) {
339                         critical ("Error changing proxy settings: %s", e.message);
340                 }
341         }
342
343         /**
344          * Revert proxy settings
345          */
346         private void proxy_restore () {
347                 if (backup != null) try {
348                 //      Hildon.Banner.show_information (null, null, "DEBUG: Restoring proxy settings");
349                         gconf.set_bool (GCONF_KEY_PROXY_HTTP_ENABLED, backup.use_http_proxy);
350
351                         gconf.set_string (GCONF_KEY_PROXY_HTTP_HOST, backup.http_host);
352                         gconf.set_string (GCONF_KEY_PROXY_SOCKS_HOST, backup.socks_host);
353                         gconf.set_string (GCONF_KEY_PROXY_SECURE_HOST, backup.secure_host);
354                         gconf.set_int (GCONF_KEY_PROXY_HTTP_PORT, backup.http_port);
355                         gconf.set_int (GCONF_KEY_PROXY_SOCKS_PORT, backup.socks_port);
356                         gconf.set_int (GCONF_KEY_PROXY_SECURE_PORT, backup.secure_port);
357
358                         gconf.set_string (GCONF_KEY_PROXY_MODE, backup.mode);
359                         backup = null;
360                 } catch (Error e) {
361                         critical ("Error restoring proxy: %s", e.message);
362                 }
363         }
364
365         /**
366          * Show the bridge relay configuration dialog
367          */
368         private void bridges_clicked_cb () {
369                 var dialog = new BridgeDialog ();
370                 dialog.show ();
371         }
372
373         /**
374          * Show the exit node configuration dialog
375          */
376         private void exit_nodes_clicked_cb () {
377                 var dialog = new ExitNodeDialog (tor_control);
378                 dialog.show ();
379         }
380
381         /**
382          * Check whether the IP address consists of four numbers in the 0..255 range
383          */
384         bool is_valid_ip_address (string address) {
385                 string[] ip = address.split (".");
386
387                 if (ip.length != 4)
388                         return false;
389
390                 for (int i = 0; i < ip.length; i++) {
391                         int n = ip[i].to_int ();
392                         if (n < 0 || n > 255)
393                                 return false;
394                 }
395
396                 return true;
397         }
398
399         /**
400          * Show the Tor log dialog
401          */
402         private void show_tor_log () {
403                 var dialog = new Gtk.Dialog ();
404                 var content = (Gtk.VBox) dialog.get_content_area ();
405                 content.set_size_request (-1, 5*70);
406
407                 dialog.set_title (_("Log"));
408
409                 var pannable = new Hildon.PannableArea ();
410                 pannable.mov_mode = Hildon.MovementMode.BOTH;
411                 log_label = new Gtk.Label (tor_log);
412                 log_label.set_alignment (0, 0);
413                 pannable.add_with_viewport (log_label);
414                 content.pack_start (pannable, true, true, 0);
415
416                 dialog.response.connect (() => {
417                         log_label = null;
418                 });
419
420                 dialog.show_all ();
421         }
422
423         /**
424          * Callback for the status menu button clicked signal
425          */
426         private const int RESPONSE_LOG = 1;
427         private void button_clicked_cb () {
428                 var dialog = new Gtk.Dialog ();
429                 var content = (Gtk.VBox) dialog.get_content_area ();
430                 content.set_size_request (-1, 3*70);
431
432                 dialog.set_title (_("Tor: anonymity online"));
433
434                 var check = new Hildon.CheckButton (Hildon.SizeType.FINGER_HEIGHT);
435                 check.set_label (_("Enable onion routing"));
436                 check.set_active (tor_enabled);
437                 content.pack_start (check, true, true, 0);
438
439                 var button = new Hildon.Button.with_text (Hildon.SizeType.FINGER_HEIGHT,
440                                                           Hildon.ButtonArrangement.VERTICAL,
441                                                           _("Bridge relays"),
442                                                           get_bridge_list ());
443                 button.set_style (Hildon.ButtonStyle.PICKER);
444                 button.set_alignment (0, 0.5f, 0, 0.5f);
445                 button.clicked.connect (bridges_clicked_cb);
446                 content.pack_start (button, true, true, 0);
447
448                 button = new Hildon.Button.with_text (Hildon.SizeType.FINGER_HEIGHT,
449                                                       Hildon.ButtonArrangement.VERTICAL,
450                                                       _("Restrict exit nodes"),
451                                                       get_exit_node_list ());
452                 button.set_style (Hildon.ButtonStyle.PICKER);
453                 button.set_alignment (0, 0.5f, 0, 0.5f);
454                 button.clicked.connect (exit_nodes_clicked_cb);
455                 content.pack_start (button, true, true, 0);
456
457                 dialog.add_button (_("Log"), RESPONSE_LOG);
458
459                 dialog.add_button (_("Save"), Gtk.ResponseType.ACCEPT);
460                 dialog.response.connect ((response_id) => {
461                         if (response_id == RESPONSE_LOG) {
462                                 show_tor_log ();
463                                 return;
464                         }
465                         if (response_id == Gtk.ResponseType.ACCEPT) {
466                                 if (!tor_enabled && check.get_active ()) try {
467                                         gconf.set_bool (GCONF_KEY_TOR_ENABLED, true);
468
469                                         // Enabled by user interaction, so connect if needed
470                                         if (!conic_connected)
471                                                 conic.connect (ConIc.ConnectFlags.NONE);
472                                 } catch (Error e) {
473                                         Hildon.Banner.show_information (null, null, "Failed to enable GConf key");
474                                 } else if (tor_enabled && !check.get_active ()) try {
475                                         gconf.set_bool (GCONF_KEY_TOR_ENABLED, false);
476                                 } catch (Error e) {
477                                         Hildon.Banner.show_information (null, null, "Failed to disable GConf key");
478                                 }
479                         }
480                         dialog.destroy ();
481                 });
482
483                 dialog.show_all ();
484         }
485
486         private string get_bridge_list () {
487                 string list = null;
488                 var bridges = new SList<string> ();
489                 try {
490                         bridges = gconf.get_list (GCONF_KEY_BRIDGES, GConf.ValueType.STRING);
491                 } catch (Error e) {
492                         critical ("Error loading bridges: %s", e.message);
493                 }
494                 foreach (string bridge in bridges) {
495                         if (list == null)
496                                 list = bridge;
497                         else
498                                 list += ", " + bridge;
499                 }
500                 if (list == null)
501                         list = _("None");
502
503                 return list;
504         }
505
506         private string get_exit_node_list () {
507                 string list = null;
508                 var exits = new SList<string> ();
509                 try {
510                         exits = gconf.get_list (GCONF_KEY_EXITNODES, GConf.ValueType.STRING);
511                 } catch (Error e) {
512                         error ("Error loading exit nodes: %s", e.message);
513                 }
514                 foreach (string exit in exits) {
515                         if (list == null)
516                                 list = exit;
517                         else
518                                 list += ", " + exit;
519                 }
520                 if (list == null)
521                         list = _("None");
522
523                 return list;
524         }
525
526         /**
527          * Callback for GConf change notification on the tor_enabled key
528          */
529         private void tor_enabled_changed_cb (GConf.Client gc, uint cxnid, GConf.Entry entry) {
530                 if (entry.key == GCONF_KEY_TOR_ENABLED) {
531                         bool old_tor_enabled = tor_enabled;
532                         tor_enabled = entry.get_value ().get_bool ();
533                         if (old_tor_enabled == tor_enabled)
534                                 return;
535
536                         if (tor_enabled) {
537                                 // Start Tor immediately if a connection is already available
538                                 if (conic_connected)
539                                         start_tor ();
540                         } else {
541                                 stop_tor ();
542                                 if (conic_connected)
543                                         conic.disconnect ();
544                         }
545                 }
546         }
547
548         /**
549          * Callback for the ConIc connection-event signal
550          */
551         private void conic_connection_event_cb (ConIc.Connection conic, ConIc.ConnectionEvent event) {
552                 var status = event.get_status ();
553                 switch (status) {
554                 case ConIc.ConnectionStatus.CONNECTED:
555                         conic_connected = true;
556                         if (tor_enabled) {
557                                 start_tor ();
558                         } else {
559                                 update_status ();
560                         }
561                         break;
562                 case ConIc.ConnectionStatus.DISCONNECTING:
563                         conic_connected = false;
564                         stop_tor ();
565                         break;
566                 case ConIc.ConnectionStatus.DISCONNECTED:
567                 case ConIc.ConnectionStatus.NETWORK_UP:
568                         // ignore
569                         break;
570                 }
571
572                 var error = event.get_error ();
573                 switch (error) {
574                 case ConIc.ConnectionError.CONNECTION_FAILED:
575                         Hildon.Banner.show_information (null, null, "DEBUG: ConIc connection failed");
576                         break;
577                 case ConIc.ConnectionError.USER_CANCELED:
578                         Hildon.Banner.show_information (null, null, "DEBUG: ConIc user canceled");
579                         break;
580                 case ConIc.ConnectionError.NONE:
581                 case ConIc.ConnectionError.INVALID_IAP:
582                         // ignore
583                         break;
584                 }
585         }
586
587         private void create_widgets () {
588                 // Status menu button
589                 button = new Hildon.Button.with_text (Hildon.SizeType.FINGER_HEIGHT,
590                                                       Hildon.ButtonArrangement.VERTICAL,
591                                                       _("The Onion Router"),
592                                                       tor_enabled ? _("Enabled") : _("Disabled"));
593                 button.set_alignment (0.0f, 0.5f, 1.0f, 1.0f);
594                 button.set_style (Hildon.ButtonStyle.PICKER);
595                 button.clicked.connect (button_clicked_cb);
596
597                 add (button);
598
599                 log_label = null;
600
601                 // Status area icon
602                 update_status ();
603
604                 show_all ();
605         }
606
607         construct {
608                 // Gettext hook-up
609                 Intl.setlocale (LocaleCategory.ALL, "");
610                 Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
611                 Intl.textdomain (Config.GETTEXT_PACKAGE);
612
613                 // GConf hook-up
614                 gconf = GConf.Client.get_default ();
615                 try {
616                         tor_enabled = gconf.get_bool (GCONF_KEY_TOR_ENABLED);
617
618                         // Request change notifications for the tor_enabled key
619                         gconf.add_dir (GCONF_DIR_TOR, GConf.ClientPreloadType.ONELEVEL);
620                         gconf.notify_add (GCONF_KEY_TOR_ENABLED, tor_enabled_changed_cb);
621                 } catch (Error e) {
622                         critical ("Failed to get GConf setting: %s", e.message);
623                 }
624                 tor_connected = false;
625
626                 // ConIc hook-up
627                 conic = new ConIc.Connection ();
628                 if (conic == null) {
629                         Hildon.Banner.show_information (null, null, "DEBUG: ConIc hook-up failed");
630                 }
631                 conic_connected = false;
632                 conic.automatic_connection_events = true;
633                 if (tor_enabled)
634                         conic.connect (ConIc.ConnectFlags.AUTOMATICALLY_TRIGGERED);
635                 conic.connection_event.connect (conic_connection_event_cb);
636
637                 // Osso hook-up
638                 osso = new Osso.Context (STATUSMENU_TOR_LIBOSSO_SERVICE_NAME,
639                                          Config.VERSION,
640                                          true,
641                                          null);
642
643                 create_widgets ();
644         }
645 }
646
647 /**
648  * Vala code can't use the HD_DEFINE_PLUGIN_MODULE macro, but it handles
649  * most of the class registration issues itself. Only this code from
650  * HD_PLUGIN_MODULE_SYMBOLS_CODE has to be has to be included manually
651  * to register with hildon-desktop:
652  */
653 [ModuleInit]
654 public void hd_plugin_module_load (TypeModule plugin) {
655         // [ModuleInit] registers types automatically
656         ((HD.PluginModule) plugin).add_type (typeof (TorStatusMenuItem));
657 }
658
659 public void hd_plugin_module_unload (HD.PluginModule plugin) {
660 }