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