21ca65184fbbd955a41bcde3d6f051920387454b
[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 "strongswan-status.h"
20 #include "strongswan-connections.h"
21
22 #define STRONGSWAN_STATUS_GET_PRIVATE(object) \
23 (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
24 STRONGSWAN_TYPE_STATUS, \
25 StrongswanStatusPrivate))
26
27 #define OSSO_CHARON_NAME "charon"
28 #define OSSO_CHARON_SERVICE "org.strongswan."OSSO_CHARON_NAME
29 #define OSSO_CHARON_OBJECT "/org/strongswan/"OSSO_CHARON_NAME
30 #define OSSO_CHARON_IFACE "org.strongswan."OSSO_CHARON_NAME
31
32 #define ICON_SIZE_STATUS 18
33 #define ICON_SIZE_BUTTON 48
34
35 typedef enum
36 {
37 STATUS_DISCONNECTED,
38 STATUS_CONNECTING,
39 STATUS_CONNECTED,
40 } StrongswanConnectionStatus;
41
42 struct _StrongswanStatusPrivate
43 {
44 struct {
45 GdkPixbuf *status_open;
46 GdkPixbuf *status_close;
47 GdkPixbuf *button_open;
48 GdkPixbuf *button_close;
49 } icons;
50
51 GtkWidget *dialog;
52 GtkWidget *button;
53 GtkWidget *image;
54 GtkWidget *selector;
55 GtkWidget *box;
56
57 osso_context_t *context;
58
59 StrongswanConnections *conns;
60
61 StrongswanConnectionStatus status;
62 gchar *current;
63 };
64
65 HD_DEFINE_PLUGIN_MODULE_EXTENDED (StrongswanStatus, strongswan_status, \
66 HD_TYPE_STATUS_MENU_ITEM, {}, { \
67 strongswan_connection_register (G_TYPE_MODULE (plugin)); \
68 strongswan_connections_register (G_TYPE_MODULE (plugin)); }, {});
69
70 static void
71 update_status_menu (StrongswanStatus *plugin)
72 {
73 StrongswanStatusPrivate *priv = plugin->priv;
74 switch (priv->status)
75 {
76 case STATUS_DISCONNECTED:
77 {
78 hildon_button_set_value (HILDON_BUTTON (priv->button),
79 "Not connected");
80 hd_status_plugin_item_set_status_area_icon (
81 HD_STATUS_PLUGIN_ITEM (plugin),
82 priv->icons.status_open);
83 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image),
84 priv->icons.button_open);
85 break;
86 }
87 case STATUS_CONNECTING:
88 {
89 gchar *msg = g_strdup_printf ("Connecting to %s...", priv->current);
90 hildon_button_set_value (HILDON_BUTTON (priv->button), msg);
91 g_free (msg);
92 break;
93 }
94 case STATUS_CONNECTED:
95 {
96 gchar *msg = g_strdup_printf ("Connected to %s", priv->current);
97 hildon_button_set_value (HILDON_BUTTON (priv->button), msg);
98 g_free (msg);
99 hd_status_plugin_item_set_status_area_icon (
100 HD_STATUS_PLUGIN_ITEM (plugin),
101 priv->icons.status_close);
102 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image),
103 priv->icons.button_close);
104 break;
105 }
106 }
107 }
108
109 static void
110 dialog_response (GtkDialog *dialog, gint response_id, StrongswanStatus *plugin)
111 {
112 StrongswanStatusPrivate *priv = plugin->priv;
113 gtk_widget_destroy (priv->dialog);
114 priv->dialog = NULL;
115 }
116
117 static void
118 connect_callback (const gchar* interface, const gchar* method,
119 osso_rpc_t *retval, StrongswanStatus *plugin)
120 {
121 gchar *msg = NULL;
122 StrongswanStatusPrivate *priv = plugin->priv;
123
124 if (retval->type == DBUS_TYPE_STRING)
125 { /* unfortunately, this is the only indication that an error occured
126 * for asynchronous calls */
127 msg = g_strdup_printf ("Failed to initiate connection: %s",
128 retval->value.s);
129 }
130 else if (retval->type != DBUS_TYPE_BOOLEAN)
131 {
132 msg = g_strdup_printf ("Failed to initiate connection: return type");
133 }
134 else if (!retval->value.b)
135 {
136 msg = g_strdup_printf ("Failed to connect to %s", priv->current);
137 }
138
139 if (msg)
140 {
141 /* connecting failed */
142 priv->current = (g_free (priv->current), NULL);
143 priv->status = STATUS_DISCONNECTED;
144 }
145 else
146 {
147 msg = g_strdup_printf ("Successfully connected to %s", priv->current);
148 priv->status = STATUS_CONNECTED;
149 }
150
151 hildon_banner_show_information (NULL, NULL, msg);
152 g_free (msg);
153
154 update_status_menu (plugin);
155
156 if (priv->dialog)
157 {
158 gtk_widget_set_sensitive (priv->box, TRUE);
159 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (priv->dialog), 0);
160
161 gtk_dialog_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_OK);
162 }
163 }
164
165 static void
166 update_dialog_connecting (StrongswanStatus *plugin)
167 {
168 StrongswanStatusPrivate *priv = plugin->priv;
169
170 gtk_widget_set_sensitive (priv->box, FALSE);
171 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (priv->dialog), 1);
172 }
173
174 static void
175 connect_clicked (HildonButton *button, StrongswanStatus *plugin)
176 {
177 StrongswanStatusPrivate *priv = plugin->priv;
178
179 priv->current = hildon_touch_selector_get_current_text (
180 HILDON_TOUCH_SELECTOR (priv->selector));
181 priv->status = STATUS_CONNECTING;
182 update_dialog_connecting (plugin);
183 update_status_menu (plugin);
184
185 StrongswanConnection *conn = strongswan_connections_get_connection (
186 priv->conns,
187 priv->current);
188 if (!conn)
189 { /* emulate a callback call */
190 osso_rpc_t retval;
191 retval.type = DBUS_TYPE_STRING;
192 retval.value.s = g_strdup ("not found");
193 connect_callback (NULL, NULL, &retval, plugin);
194 osso_rpc_free_val (&retval);
195 return;
196 }
197
198 /* this call on the system bus is only needed to start charon as root */
199 osso_rpc_t retval;
200 osso_return_t result;
201 result = osso_rpc_run_system (priv->context,
202 OSSO_CHARON_SERVICE,
203 OSSO_CHARON_OBJECT,
204 OSSO_CHARON_IFACE,
205 "Start",
206 &retval,
207 DBUS_TYPE_INVALID);
208 osso_rpc_free_val (&retval);
209 if (result != OSSO_OK)
210 {
211 retval.type = DBUS_TYPE_STRING;
212 retval.value.s = g_strdup ("couldn't connect to charon");
213 connect_callback (NULL, NULL, &retval, plugin);
214 osso_rpc_free_val (&retval);
215 return;
216 }
217
218 gchar *c_host, *c_cert, *c_user, *c_pass;
219 g_object_get (conn,
220 "host", &c_host,
221 "cert", &c_cert,
222 "user", &c_user,
223 "pass", &c_pass,
224 NULL);
225
226 osso_rpc_async_run (priv->context,
227 OSSO_CHARON_SERVICE,
228 OSSO_CHARON_OBJECT,
229 OSSO_CHARON_IFACE,
230 "Connect",
231 (osso_rpc_async_f*)connect_callback,
232 plugin,
233 DBUS_TYPE_STRING, priv->current,
234 DBUS_TYPE_STRING, c_host,
235 DBUS_TYPE_STRING, c_cert,
236 DBUS_TYPE_STRING, c_user,
237 DBUS_TYPE_STRING, c_pass,
238 DBUS_TYPE_INVALID);
239
240 g_free (c_host);
241 g_free (c_cert);
242 g_free (c_user);
243 g_free (c_pass);
244 }
245
246 static void
247 disconnect_clicked (HildonButton *button, StrongswanStatus *plugin)
248 {
249 osso_return_t result;
250 osso_rpc_t retval;
251 gchar *msg;
252 StrongswanStatusPrivate *priv = plugin->priv;
253
254 gtk_widget_set_sensitive (priv->box, FALSE);
255 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (priv->dialog), 1);
256
257 result = osso_rpc_run_system (priv->context,
258 OSSO_CHARON_SERVICE,
259 OSSO_CHARON_OBJECT,
260 OSSO_CHARON_IFACE,
261 "Disconnect",
262 &retval,
263 DBUS_TYPE_INVALID);
264
265 gtk_widget_set_sensitive (priv->box, TRUE);
266 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (priv->dialog), 0);
267
268 if (result == OSSO_OK)
269 {
270 msg = g_strdup_printf ("Successfully disconnected from %s",
271 priv->current);
272 }
273 else
274 {
275 msg = g_strdup_printf ("Failed to disconnect from %s", priv->current);
276 }
277 hildon_banner_show_information (NULL, NULL, msg);
278 g_free (msg);
279
280 priv->current = (g_free (priv->current), NULL);
281 priv->status = STATUS_DISCONNECTED;
282
283 update_status_menu (plugin);
284
285 gtk_dialog_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_OK);
286 }
287
288 static void
289 setup_dialog_disconnected (StrongswanStatus *plugin)
290 {
291 StrongswanStatusPrivate *priv = plugin->priv;
292
293 GtkWidget *vbox = GTK_DIALOG (priv->dialog)->vbox;
294 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
295 priv->box = hbox;
296 GtkWidget *button = hildon_picker_button_new (
297 HILDON_SIZE_FINGER_HEIGHT |
298 HILDON_SIZE_AUTO_WIDTH,
299 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
300 hildon_button_set_title (HILDON_BUTTON (button), "Connection:");
301 gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
302
303 GtkWidget *selector = hildon_touch_selector_new ();
304 priv->selector = selector;
305 GtkTreeModel *model = strongswan_connections_get_model (priv->conns);
306 hildon_touch_selector_append_text_column (
307 HILDON_TOUCH_SELECTOR (selector),
308 model,
309 TRUE);
310 hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (button),
311 HILDON_TOUCH_SELECTOR (selector));
312
313 button = hildon_button_new_with_text (
314 HILDON_SIZE_FINGER_HEIGHT |
315 HILDON_SIZE_AUTO_WIDTH,
316 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
317 "Connect", NULL);
318 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
319 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
320 g_signal_connect (button, "clicked", G_CALLBACK (connect_clicked),
321 plugin);
322 }
323
324 static void
325 setup_dialog_connected (StrongswanStatus *plugin)
326 {
327 StrongswanStatusPrivate *priv = plugin->priv;
328
329 GtkWidget *vbox = GTK_DIALOG (priv->dialog)->vbox;
330 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
331 priv->box = hbox;
332 GtkWidget *button = hildon_button_new_with_text (
333 HILDON_SIZE_FINGER_HEIGHT |
334 HILDON_SIZE_AUTO_WIDTH,
335 HILDON_BUTTON_ARRANGEMENT_HORIZONTAL,
336 "Disconnect", priv->current);
337 hildon_button_set_style (HILDON_BUTTON (button),
338 HILDON_BUTTON_STYLE_PICKER);
339 g_signal_connect (button, "clicked", G_CALLBACK (disconnect_clicked),
340 plugin);
341 gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
342 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
343 }
344
345 static void
346 button_clicked (HildonButton *button, StrongswanStatus *plugin)
347 {
348 StrongswanStatusPrivate *priv = plugin->priv;
349
350 priv->dialog = gtk_dialog_new ();
351 gtk_window_set_title (GTK_WINDOW (priv->dialog), "strongSwan VPN");
352 g_signal_connect (priv->dialog, "response",
353 G_CALLBACK (dialog_response), plugin);
354
355 switch (priv->status)
356 {
357 case STATUS_DISCONNECTED:
358 setup_dialog_disconnected (plugin);
359 break;
360 case STATUS_CONNECTING:
361 setup_dialog_disconnected (plugin);
362 update_dialog_connecting (plugin);
363 break;
364 case STATUS_CONNECTED:
365 setup_dialog_connected (plugin);
366 break;
367 }
368
369 gtk_widget_show_all (priv->dialog);
370 }
371
372 static GdkPixbuf*
373 load_icon (GtkIconTheme *theme, const gchar *name, gint size)
374 {
375 GdkPixbuf *icon = NULL;
376 GdkPixbuf *loaded = gtk_icon_theme_load_icon (theme, name, size,
377 GTK_ICON_LOOKUP_NO_SVG, NULL);
378 if (loaded)
379 { /* so we don't have to listen for theme changes, we copy the icon */
380 icon = gdk_pixbuf_copy (loaded);
381 g_object_unref (loaded);
382 }
383 return icon;
384 }
385
386 static void
387 load_icons (StrongswanStatusPrivate *priv)
388 {
389 GtkIconTheme *theme = gtk_icon_theme_get_default ();
390 priv->icons.status_open = load_icon (theme, "strongswan_lock_open",
391 ICON_SIZE_STATUS);
392 priv->icons.status_close = load_icon (theme, "strongswan_lock_close",
393 ICON_SIZE_STATUS);
394 priv->icons.button_open = load_icon (theme, "strongswan_lock_open",
395 ICON_SIZE_BUTTON);
396 priv->icons.button_close = load_icon (theme, "strongswan_lock_close",
397 ICON_SIZE_BUTTON);
398 if (!priv->icons.status_open || !priv->icons.button_open)
399 {
400 hildon_banner_show_information (NULL, NULL, "failed to load icons");
401 }
402 }
403
404 static void
405 strongswan_status_init (StrongswanStatus *plugin)
406 {
407 StrongswanStatusPrivate *priv = STRONGSWAN_STATUS_GET_PRIVATE (plugin);
408 plugin->priv = priv;
409
410 priv->context = osso_initialize (OSSO_STATUS_SERVICE, "0.0.1", TRUE, NULL);
411 if (!priv->context)
412 {
413 return;
414 }
415
416 priv->conns = strongswan_connections_new ();
417
418 load_icons(priv);
419
420 hd_status_plugin_item_set_status_area_icon (HD_STATUS_PLUGIN_ITEM (plugin),
421 priv->icons.status_open);
422
423 GtkWidget *button = hildon_button_new_with_text (
424 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
425 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
426 "strongSwan VPN", "Not connected");
427 hildon_button_set_style (HILDON_BUTTON (button),
428 HILDON_BUTTON_STYLE_PICKER);
429 priv->button = button;
430 gtk_container_add (GTK_CONTAINER (plugin), button);
431
432 GtkWidget *image = gtk_image_new_from_pixbuf (priv->icons.button_open);
433 priv->image = image;
434 hildon_button_set_image (HILDON_BUTTON (button), image);
435
436 gtk_button_set_alignment (GTK_BUTTON (button), 0.0, 0.5);
437
438 g_signal_connect (button, "clicked", G_CALLBACK (button_clicked), plugin);
439
440 gtk_widget_show_all (GTK_WIDGET (plugin));
441 }
442
443 static void
444 strongswan_status_dispose (GObject *object)
445 {
446 StrongswanStatusPrivate *priv = STRONGSWAN_STATUS (object)->priv;
447 if (priv->conns)
448 {
449 priv->conns = (g_object_unref (priv->conns), NULL);
450 }
451 if (priv->icons.status_open)
452 {
453 g_object_unref (priv->icons.status_open);
454 priv->icons.status_open = NULL;
455 }
456 if (priv->icons.status_close)
457 {
458 g_object_unref (priv->icons.status_close);
459 priv->icons.status_close = NULL;
460 }
461 if (priv->icons.button_open)
462 {
463 g_object_unref (priv->icons.button_open);
464 priv->icons.button_open = NULL;
465 }
466 if (priv->icons.button_close)
467 {
468 g_object_unref (priv->icons.button_close);
469 priv->icons.button_close = NULL;
470 }
471 G_OBJECT_CLASS (strongswan_status_parent_class)->dispose (object);
472 }
473
474 static void
475 strongswan_status_finalize (GObject *object)
476 {
477 StrongswanStatusPrivate *priv = STRONGSWAN_STATUS (object)->priv;
478 priv->current = (g_free (priv->current), NULL);
479 G_OBJECT_CLASS (strongswan_status_parent_class)->finalize (object);
480 }
481
482 static void
483 strongswan_status_class_finalize (StrongswanStatusClass *klass)
484 {
485 }
486
487 static void
488 strongswan_status_class_init (StrongswanStatusClass *klass)
489 {
490 GObjectClass *object_class = G_OBJECT_CLASS (klass);
491
492 object_class->dispose = strongswan_status_dispose;
493 object_class->finalize = strongswan_status_finalize;
494
495 g_type_class_add_private (klass, sizeof (StrongswanStatusPrivate));
496 }