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