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