maemo: Reload icons on icon theme change.
[strongswan.git] / src / frontends / maemo / src / strongswan-status.c
1 /*
2 * Copyright (C) 2010 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <hildon/hildon.h>
17 #include <libosso.h>
18
19 #include <string.h>
20
21 #include "strongswan-status.h"
22 #include "strongswan-connections.h"
23
24 #define STRONGSWAN_STATUS_GET_PRIVATE(object) \
25 (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
26 STRONGSWAN_TYPE_STATUS, \
27 StrongswanStatusPrivate))
28
29 #define OSSO_STATUS_NAME "status"
30 #define OSSO_STATUS_SERVICE "org.strongswan."OSSO_STATUS_NAME
31 #define OSSO_STATUS_OBJECT "/org/strongswan/"OSSO_STATUS_NAME
32 #define OSSO_STATUS_IFACE "org.strongswan."OSSO_STATUS_NAME
33
34 #define OSSO_CHARON_NAME "charon"
35 #define OSSO_CHARON_SERVICE "org.strongswan."OSSO_CHARON_NAME
36 #define OSSO_CHARON_OBJECT "/org/strongswan/"OSSO_CHARON_NAME
37 #define OSSO_CHARON_IFACE "org.strongswan."OSSO_CHARON_NAME
38
39 #define ICON_SIZE_STATUS 18
40 #define ICON_SIZE_BUTTON 48
41
42 #define UNREF_IF(obj) do { \
43 if (obj) { obj = (g_object_unref (obj), NULL); } \
44 } while(0)
45
46 typedef enum
47 {
48 STATUS_DISCONNECTED,
49 STATUS_CONNECTING,
50 STATUS_CONNECTED,
51 STATUS_AUTH_FAILED,
52 STATUS_CONNECTION_FAILED,
53 } StrongswanConnectionStatus;
54
55 struct _StrongswanStatusPrivate
56 {
57 struct {
58 GdkPixbuf *status_open;
59 GdkPixbuf *status_close;
60 GdkPixbuf *button_open;
61 GdkPixbuf *button_close;
62 } icons;
63
64 GtkWidget *dialog;
65 GtkWidget *button;
66 GtkWidget *image;
67 GtkWidget *selector;
68 GtkWidget *box;
69
70 osso_context_t *context;
71
72 StrongswanConnections *conns;
73
74 StrongswanConnectionStatus status;
75 gchar *current;
76 };
77
78 HD_DEFINE_PLUGIN_MODULE_EXTENDED (StrongswanStatus, strongswan_status, \
79 HD_TYPE_STATUS_MENU_ITEM, {}, { \
80 strongswan_connection_register (G_TYPE_MODULE (plugin)); \
81 strongswan_connections_register (G_TYPE_MODULE (plugin)); }, {});
82
83 static void
84 update_status_menu (StrongswanStatus *plugin)
85 {
86 StrongswanStatusPrivate *priv = plugin->priv;
87 switch (priv->status)
88 {
89 default:
90 case STATUS_DISCONNECTED:
91 {
92 hildon_button_set_value (HILDON_BUTTON (priv->button),
93 "Not connected");
94 hd_status_plugin_item_set_status_area_icon (
95 HD_STATUS_PLUGIN_ITEM (plugin),
96 priv->icons.status_open);
97 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image),
98 priv->icons.button_open);
99 break;
100 }
101 case STATUS_CONNECTING:
102 {
103 gchar *msg = g_strdup_printf ("Connecting to %s...", priv->current);
104 hildon_button_set_value (HILDON_BUTTON (priv->button), msg);
105 g_free (msg);
106 break;
107 }
108 case STATUS_CONNECTED:
109 {
110 gchar *msg = g_strdup_printf ("Connected to %s", priv->current);
111 hildon_button_set_value (HILDON_BUTTON (priv->button), msg);
112 g_free (msg);
113 hd_status_plugin_item_set_status_area_icon (
114 HD_STATUS_PLUGIN_ITEM (plugin),
115 priv->icons.status_close);
116 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image),
117 priv->icons.button_close);
118 break;
119 }
120 }
121 }
122
123 static void
124 update_dialog_connecting (StrongswanStatus *plugin)
125 {
126 StrongswanStatusPrivate *priv = plugin->priv;
127
128 gtk_widget_set_sensitive (priv->box, FALSE);
129 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (priv->dialog), 1);
130 }
131
132 static void
133 update_dialog_default (StrongswanStatus *plugin)
134 {
135 StrongswanStatusPrivate *priv = plugin->priv;
136
137 gtk_widget_set_sensitive (priv->box, TRUE);
138 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (priv->dialog), 0);
139 }
140
141 static void
142 dialog_response (GtkDialog *dialog, gint response_id, StrongswanStatus *plugin)
143 {
144 StrongswanStatusPrivate *priv = plugin->priv;
145 gtk_widget_destroy (priv->dialog);
146 priv->dialog = NULL;
147 }
148
149 static void
150 handle_status_change (StrongswanStatus *plugin,
151 StrongswanConnectionStatus status,
152 gchar *message)
153 {
154 StrongswanStatusPrivate *priv = plugin->priv;
155 gchar *msg = NULL;
156
157 switch (status)
158 {
159 case STATUS_CONNECTION_FAILED:
160 {
161 if (priv->status == STATUS_CONNECTED)
162 {
163 msg = g_strdup_printf ("Lost connection to %s", priv->current);
164 }
165 else if (message)
166 {
167 msg = g_strdup_printf ("Failed to connect to %s: %s",
168 priv->current, message);
169 }
170 else
171 {
172 msg = g_strdup_printf ("Failed to connect to %s",
173 priv->current);
174 }
175 status = STATUS_DISCONNECTED;
176 break;
177 }
178 case STATUS_AUTH_FAILED:
179 {
180 msg = g_strdup_printf ("Failed to connect to %s: authentication "
181 "failed", priv->current);
182 status = STATUS_DISCONNECTED;
183 /* TODO: show password dialog again? */
184 break;
185 }
186 case STATUS_CONNECTED:
187 {
188 msg = g_strdup_printf ("Successfully connected to %s",
189 priv->current);
190 break;
191 }
192 default:
193 case STATUS_DISCONNECTED:
194 {
195 msg = g_strdup_printf ("Disconnected from %s", priv->current);
196 break;
197 }
198 }
199
200 priv->status = status;
201
202 if (status == STATUS_DISCONNECTED)
203 {
204 priv->current = (g_free (priv->current), NULL);
205 }
206
207 hildon_banner_show_information (NULL, NULL, msg);
208 g_free (msg);
209
210 update_status_menu (plugin);
211
212 if (priv->dialog)
213 {
214 update_dialog_default (plugin);
215 if (status == STATUS_CONNECTED)
216 {
217 gtk_dialog_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_OK);
218 }
219 }
220 }
221
222 static gboolean
223 get_password (StrongswanStatus *plugin, gchar **password)
224 {
225 StrongswanStatusPrivate *priv = plugin->priv;
226 gboolean result = FALSE;
227
228 GtkWidget *dialog = gtk_dialog_new_with_buttons (
229 "Connecting...",
230 GTK_WINDOW(priv->dialog),
231 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR,
232 GTK_STOCK_CANCEL,
233 GTK_RESPONSE_CANCEL,
234 GTK_STOCK_OK,
235 GTK_RESPONSE_OK,
236 NULL);
237 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
238 GtkWidget *vbox = GTK_DIALOG (dialog)->vbox;
239 GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
240
241 GtkWidget *pass = hildon_entry_new (HILDON_SIZE_AUTO);
242 hildon_gtk_entry_set_placeholder_text (GTK_ENTRY (pass), "Password");
243 hildon_gtk_entry_set_input_mode (GTK_ENTRY (pass),
244 HILDON_GTK_INPUT_MODE_FULL |
245 HILDON_GTK_INPUT_MODE_INVISIBLE);
246 GtkWidget *pcap = hildon_caption_new (group,
247 "Password",
248 pass,
249 NULL,
250 HILDON_CAPTION_OPTIONAL);
251 gtk_box_pack_start (GTK_BOX (vbox), pcap, TRUE, TRUE, 0);
252 gtk_widget_show_all (dialog);
253
254 gint retval = gtk_dialog_run (GTK_DIALOG (dialog));
255 if (retval == GTK_RESPONSE_OK)
256 {
257 *password = g_strdup (gtk_entry_get_text (GTK_ENTRY (pass)));
258 result = TRUE;
259 }
260 gtk_widget_destroy (dialog);
261 return result;
262 }
263
264 static void
265 connect_clicked (HildonButton *button, StrongswanStatus *plugin)
266 {
267 StrongswanStatusPrivate *priv = plugin->priv;
268 osso_return_t result;
269 osso_rpc_t retval;
270
271 priv->current = hildon_touch_selector_get_current_text (
272 HILDON_TOUCH_SELECTOR (priv->selector));
273 priv->status = STATUS_CONNECTING;
274 update_dialog_connecting (plugin);
275 update_status_menu (plugin);
276
277 StrongswanConnection *conn = strongswan_connections_get_connection (
278 priv->conns,
279 priv->current);
280 if (!conn)
281 {
282 handle_status_change (plugin, STATUS_CONNECTION_FAILED, "not found");
283 return;
284 }
285
286 /* this call on the system bus is only needed to start charon as root */
287 result = osso_rpc_run_system (priv->context,
288 OSSO_CHARON_SERVICE,
289 OSSO_CHARON_OBJECT,
290 OSSO_CHARON_IFACE,
291 "Start",
292 &retval,
293 DBUS_TYPE_INVALID);
294 osso_rpc_free_val (&retval);
295 if (result != OSSO_OK)
296 {
297 handle_status_change (plugin, STATUS_CONNECTION_FAILED,
298 "couldn't connect to charon");
299 return;
300 }
301
302 gchar *c_host, *c_cert, *c_user, *c_pass;
303
304 if (!get_password (plugin, &c_pass))
305 {
306 update_dialog_default (plugin);
307 return;
308 }
309
310 g_object_get (conn,
311 "host", &c_host,
312 "cert", &c_cert,
313 "user", &c_user,
314 NULL);
315
316 result = osso_rpc_run (priv->context,
317 OSSO_CHARON_SERVICE,
318 OSSO_CHARON_OBJECT,
319 OSSO_CHARON_IFACE,
320 "Connect",
321 &retval,
322 DBUS_TYPE_STRING, priv->current,
323 DBUS_TYPE_STRING, c_host,
324 DBUS_TYPE_STRING, c_cert,
325 DBUS_TYPE_STRING, c_user,
326 DBUS_TYPE_STRING, c_pass,
327 DBUS_TYPE_INVALID);
328
329 g_free (c_host);
330 g_free (c_cert);
331 g_free (c_user);
332 g_free (c_pass);
333
334 if (result != OSSO_OK || !retval.value.b)
335 {
336 handle_status_change (plugin, STATUS_CONNECTION_FAILED,
337 "initiation failed");
338 osso_rpc_free_val (&retval);
339 return;
340 }
341 osso_rpc_free_val (&retval);
342 }
343
344 static void
345 disconnect_clicked (HildonButton *button, StrongswanStatus *plugin)
346 {
347 osso_return_t result;
348 osso_rpc_t retval;
349 gchar *msg;
350 StrongswanStatusPrivate *priv = plugin->priv;
351
352 gtk_widget_set_sensitive (priv->box, FALSE);
353 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (priv->dialog), 1);
354
355 result = osso_rpc_run_system (priv->context,
356 OSSO_CHARON_SERVICE,
357 OSSO_CHARON_OBJECT,
358 OSSO_CHARON_IFACE,
359 "Disconnect",
360 &retval,
361 DBUS_TYPE_INVALID);
362
363 gtk_widget_set_sensitive (priv->box, TRUE);
364 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (priv->dialog), 0);
365
366 if (result == OSSO_OK)
367 {
368 msg = g_strdup_printf ("Successfully disconnected from %s",
369 priv->current);
370 }
371 else
372 {
373 msg = g_strdup_printf ("Failed to disconnect from %s", priv->current);
374 }
375 hildon_banner_show_information (NULL, NULL, msg);
376 g_free (msg);
377
378 priv->current = (g_free (priv->current), NULL);
379 priv->status = STATUS_DISCONNECTED;
380
381 update_status_menu (plugin);
382
383 gtk_dialog_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_OK);
384 }
385
386 static void
387 setup_dialog_disconnected (StrongswanStatus *plugin)
388 {
389 StrongswanStatusPrivate *priv = plugin->priv;
390
391 GtkWidget *vbox = GTK_DIALOG (priv->dialog)->vbox;
392 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
393 priv->box = hbox;
394 GtkWidget *button = hildon_picker_button_new (
395 HILDON_SIZE_FINGER_HEIGHT |
396 HILDON_SIZE_AUTO_WIDTH,
397 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
398 hildon_button_set_title (HILDON_BUTTON (button), "Connection:");
399 gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
400
401 GtkWidget *selector = hildon_touch_selector_new ();
402 priv->selector = selector;
403 GtkTreeModel *model = strongswan_connections_get_model (priv->conns);
404 hildon_touch_selector_append_text_column (
405 HILDON_TOUCH_SELECTOR (selector),
406 model,
407 TRUE);
408 hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (button),
409 HILDON_TOUCH_SELECTOR (selector));
410
411 button = hildon_button_new_with_text (
412 HILDON_SIZE_FINGER_HEIGHT |
413 HILDON_SIZE_AUTO_WIDTH,
414 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
415 "Connect", NULL);
416 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
417 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
418 g_signal_connect (button, "clicked", G_CALLBACK (connect_clicked),
419 plugin);
420 }
421
422 static void
423 setup_dialog_connected (StrongswanStatus *plugin)
424 {
425 StrongswanStatusPrivate *priv = plugin->priv;
426
427 GtkWidget *vbox = GTK_DIALOG (priv->dialog)->vbox;
428 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
429 priv->box = hbox;
430 GtkWidget *button = hildon_button_new_with_text (
431 HILDON_SIZE_FINGER_HEIGHT |
432 HILDON_SIZE_AUTO_WIDTH,
433 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
434 "Disconnect", priv->current);
435 hildon_button_set_style (HILDON_BUTTON (button),
436 HILDON_BUTTON_STYLE_PICKER);
437 g_signal_connect (button, "clicked", G_CALLBACK (disconnect_clicked),
438 plugin);
439 gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
440 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
441 }
442
443 static void
444 button_clicked (HildonButton *button, StrongswanStatus *plugin)
445 {
446 StrongswanStatusPrivate *priv = plugin->priv;
447
448 priv->dialog = gtk_dialog_new ();
449 gtk_window_set_title (GTK_WINDOW (priv->dialog), "strongSwan VPN");
450 g_signal_connect (priv->dialog, "response",
451 G_CALLBACK (dialog_response), plugin);
452
453 switch (priv->status)
454 {
455 default:
456 case STATUS_DISCONNECTED:
457 setup_dialog_disconnected (plugin);
458 break;
459 case STATUS_CONNECTING:
460 setup_dialog_disconnected (plugin);
461 update_dialog_connecting (plugin);
462 break;
463 case STATUS_CONNECTED:
464 setup_dialog_connected (plugin);
465 break;
466 }
467
468 gtk_widget_show_all (priv->dialog);
469 }
470
471 static gint
472 dbus_req_handler(const gchar *interface, const gchar *method,
473 GArray *arguments, StrongswanStatus *plugin,
474 osso_rpc_t *retval)
475 {
476 if (!strcmp (method, "StatusChanged") && arguments->len == 1)
477 {
478 int status = 0;
479 osso_rpc_t *arg = &g_array_index(arguments, osso_rpc_t, 0);
480 if (arg->type == DBUS_TYPE_INT32)
481 {
482 status = arg->value.i;
483 }
484 handle_status_change (plugin, status, NULL);
485 }
486 return OSSO_OK;
487 }
488
489 static GdkPixbuf*
490 load_icon (GtkIconTheme *theme, const gchar *name, gint size)
491 {
492 GdkPixbuf *icon = gtk_icon_theme_load_icon (theme, name, size,
493 GTK_ICON_LOOKUP_NO_SVG, NULL);
494 return icon;
495 }
496
497 static void
498 load_icons (StrongswanStatusPrivate *priv)
499 {
500 GtkIconTheme *theme = gtk_icon_theme_get_default ();
501 priv->icons.status_open = load_icon (theme, "strongswan_lock_open",
502 ICON_SIZE_STATUS);
503 priv->icons.status_close = load_icon (theme, "strongswan_lock_close",
504 ICON_SIZE_STATUS);
505 priv->icons.button_open = load_icon (theme, "strongswan_lock_open",
506 ICON_SIZE_BUTTON);
507 priv->icons.button_close = load_icon (theme, "strongswan_lock_close",
508 ICON_SIZE_BUTTON);
509 }
510
511 static void
512 icon_theme_changed (GtkIconTheme *theme, StrongswanStatus *plugin)
513 {
514 StrongswanStatusPrivate *priv = plugin->priv;
515 UNREF_IF(priv->icons.status_open);
516 UNREF_IF(priv->icons.status_close);
517 UNREF_IF(priv->icons.button_open);
518 UNREF_IF(priv->icons.button_close);
519 load_icons (priv);
520 update_status_menu (plugin);
521 }
522
523 static void
524 strongswan_status_init (StrongswanStatus *plugin)
525 {
526 StrongswanStatusPrivate *priv = STRONGSWAN_STATUS_GET_PRIVATE (plugin);
527 plugin->priv = priv;
528
529 priv->context = osso_initialize (OSSO_STATUS_SERVICE, "0.0.1", TRUE, NULL);
530 if (!priv->context)
531 {
532 return;
533 }
534 osso_return_t result;
535 result = osso_rpc_set_cb_f (priv->context,
536 OSSO_STATUS_SERVICE,
537 OSSO_STATUS_OBJECT,
538 OSSO_STATUS_IFACE,
539 (osso_rpc_cb_f*)dbus_req_handler,
540 plugin);
541 if (result != OSSO_OK)
542 {
543 return;
544 }
545
546 priv->conns = strongswan_connections_new ();
547
548 g_signal_connect_object (gtk_icon_theme_get_default (), "changed",
549 G_CALLBACK (icon_theme_changed), plugin, 0);
550
551 load_icons(priv);
552
553 hd_status_plugin_item_set_status_area_icon (HD_STATUS_PLUGIN_ITEM (plugin),
554 priv->icons.status_open);
555
556 GtkWidget *button = hildon_button_new_with_text (
557 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
558 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
559 "strongSwan VPN", "Not connected");
560 hildon_button_set_style (HILDON_BUTTON (button),
561 HILDON_BUTTON_STYLE_PICKER);
562 priv->button = button;
563 gtk_container_add (GTK_CONTAINER (plugin), button);
564
565 GtkWidget *image = gtk_image_new_from_pixbuf (priv->icons.button_open);
566 priv->image = image;
567 hildon_button_set_image (HILDON_BUTTON (button), image);
568
569 gtk_button_set_alignment (GTK_BUTTON (button), 0.0, 0.5);
570
571 g_signal_connect (button, "clicked", G_CALLBACK (button_clicked), plugin);
572
573 gtk_widget_show_all (GTK_WIDGET (plugin));
574 }
575
576 static void
577 strongswan_status_dispose (GObject *object)
578 {
579 StrongswanStatusPrivate *priv = STRONGSWAN_STATUS (object)->priv;
580 if (priv->context)
581 {
582 osso_rpc_unset_cb_f (priv->context,
583 OSSO_STATUS_SERVICE,
584 OSSO_STATUS_OBJECT,
585 OSSO_STATUS_IFACE,
586 (osso_rpc_cb_f*)dbus_req_handler,
587 STRONGSWAN_STATUS (object));
588 osso_deinitialize (priv->context);
589 priv->context = NULL;
590 }
591 UNREF_IF(priv->conns);
592 UNREF_IF(priv->icons.status_open);
593 UNREF_IF(priv->icons.status_close);
594 UNREF_IF(priv->icons.button_open);
595 UNREF_IF(priv->icons.button_close);
596 G_OBJECT_CLASS (strongswan_status_parent_class)->dispose (object);
597 }
598
599 static void
600 strongswan_status_finalize (GObject *object)
601 {
602 StrongswanStatusPrivate *priv = STRONGSWAN_STATUS (object)->priv;
603 priv->current = (g_free (priv->current), NULL);
604 G_OBJECT_CLASS (strongswan_status_parent_class)->finalize (object);
605 }
606
607 static void
608 strongswan_status_class_finalize (StrongswanStatusClass *klass)
609 {
610 }
611
612 static void
613 strongswan_status_class_init (StrongswanStatusClass *klass)
614 {
615 GObjectClass *object_class = G_OBJECT_CLASS (klass);
616
617 object_class->dispose = strongswan_status_dispose;
618 object_class->finalize = strongswan_status_finalize;
619
620 g_type_class_add_private (klass, sizeof (StrongswanStatusPrivate));
621 }