Remove vpn_name from struct openconnect_info. It's only used by the auth-dialog
[platform/upstream/openconnect.git] / nm-auth-dialog.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2010 Intel Corporation.
5  *
6  * Authors: Jussi Kukkonen <jku@linux.intel.com>
7  *          David Woodhouse <dwmw2@infradead.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1, as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to:
20  *
21  *   Free Software Foundation, Inc.
22  *   51 Franklin Street, Fifth Floor,
23  *   Boston, MA 02110-1301 USA
24  */
25
26 #include <string.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #define _GNU_SOURCE
30 #include <getopt.h>
31
32 #include <libxml/parser.h>
33 #include <libxml/tree.h>
34
35 #include <gconf/gconf-client.h>
36
37 #include <gtk/gtk.h>
38
39 #include "auth-dlg-settings.h"
40
41 #include "openconnect-internal.h"
42
43 #include <openssl/bio.h>
44 #include <openssl/ui.h>
45
46 static GConfClient *gcl;
47 static char *config_path;
48
49 static char *last_message;
50
51 static char *lasthost;
52
53 typedef struct vpnhost {
54         char *hostname;
55         char *hostaddress;
56         char *usergroup;
57         struct vpnhost *next;
58 } vpnhost;
59
60 vpnhost *vpnhosts;
61
62 enum certificate_response{
63         CERT_DENIED = -1,
64         CERT_USER_NOT_READY,
65         CERT_ACCEPTED,
66 };
67
68 struct gconf_key {
69         char *key;
70         char *value;
71         struct gconf_key *next;
72 };
73
74 typedef struct auth_ui_data {
75         char *vpn_name;
76         struct openconnect_info *vpninfo;
77         struct gconf_key *success_keys;
78         GtkWidget *dialog;
79         GtkWidget *combo;
80         GtkWidget *connect_button;
81         GtkWidget *no_form_label;
82         GtkWidget *getting_form_label;
83         GtkWidget *ssl_box;
84         GtkWidget *cancel_button;
85         GtkWidget *login_button;
86         GtkTextBuffer *log;
87
88         int retval;
89         int cookie_retval;
90
91         gboolean cancelled; /* fully cancel the whole challenge-response series */
92         gboolean getting_cookie;
93
94         int form_grabbed;
95         GQueue *form_entries; /* modified from worker thread */
96         GMutex *form_mutex;
97
98         GCond *form_retval_changed;
99         gpointer form_retval;
100
101         GCond *form_shown_changed;
102         gboolean form_shown;
103
104         GCond *cert_response_changed;
105         enum certificate_response cert_response;
106 } auth_ui_data;
107
108 enum {
109         AUTH_DIALOG_RESPONSE_LOGIN = 1,
110         AUTH_DIALOG_RESPONSE_CANCEL,
111 } auth_dialog_response;
112
113
114
115 /* this is here because ssl ui (*opener) does not have a userdata pointer... */
116 static auth_ui_data *ui_data;
117
118 static void connect_host(auth_ui_data *ui_data);
119
120 static void container_child_remove(GtkWidget *widget, gpointer data)
121 {
122         GtkContainer *container = GTK_CONTAINER(data);
123
124         gtk_container_remove(container, widget);
125 }
126
127 static void ssl_box_add_error(auth_ui_data *ui_data, const char *msg)
128 {
129         GtkWidget *hbox, *text, *image;
130         int width;
131
132         hbox = gtk_hbox_new(FALSE, 8);
133         gtk_box_pack_start(GTK_BOX(ui_data->ssl_box), hbox, FALSE, FALSE, 0);
134
135         image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_ERROR,
136                                          GTK_ICON_SIZE_DIALOG);
137         gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
138
139         text = gtk_label_new(msg);
140         gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
141         gtk_window_get_size(GTK_WINDOW(ui_data->dialog), &width, NULL);
142         /* FIXME: this is not very nice -- can't make the window thinner after this */
143         gtk_widget_set_size_request(text, width - 100, -1);
144         gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 0);
145 }
146
147 static void ssl_box_add_info(auth_ui_data *ui_data, const char *msg)
148 {
149         GtkWidget *text;
150         int width;
151
152         text = gtk_label_new(msg);
153         gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
154         gtk_window_get_size(GTK_WINDOW(ui_data->dialog), &width, NULL);
155         /* FIXME: this is not very nice -- can't make the window thinner after this */
156         gtk_widget_set_size_request(text, width - 40, -1);
157         gtk_box_pack_start(GTK_BOX(ui_data->ssl_box), text, FALSE, FALSE, 0);
158 }
159
160 static void ssl_box_clear(auth_ui_data *ui_data)
161 {
162         gtk_widget_hide(ui_data->no_form_label);
163         gtk_widget_hide(ui_data->getting_form_label);
164         gtk_container_foreach(GTK_CONTAINER(ui_data->ssl_box),
165                               container_child_remove, ui_data->ssl_box);
166         gtk_widget_set_sensitive (ui_data->login_button, FALSE);
167         gtk_widget_set_sensitive (ui_data->cancel_button, FALSE);
168 }
169
170 typedef struct ui_fragment_data {
171         GtkWidget *widget;
172         auth_ui_data *ui_data;
173         UI_STRING *uis;
174         struct oc_form_opt *opt;
175         char *entry_text;
176         int grab_focus;
177 } ui_fragment_data;
178
179 static void entry_activate_cb(GtkWidget *widget, auth_ui_data *ui_data)
180 {
181         gtk_dialog_response(GTK_DIALOG(ui_data->dialog), AUTH_DIALOG_RESPONSE_LOGIN);
182 }
183
184 static void do_check_visibility(ui_fragment_data *data, gboolean *visible)
185 {
186         int min_len;
187
188         if (!data->uis)
189                 return;
190
191         min_len = UI_get_result_minsize(data->uis);
192
193         if (min_len && (!data->entry_text || strlen(data->entry_text) < min_len))
194                 *visible = FALSE;
195 }
196
197 static void evaluate_login_visibility(auth_ui_data *ui_data)
198 {
199         gboolean visible = TRUE;
200         g_queue_foreach(ui_data->form_entries, (GFunc)do_check_visibility,
201                         &visible);
202
203         gtk_widget_set_sensitive (ui_data->login_button, visible);
204 }
205
206 static void entry_changed(GtkEntry *entry, ui_fragment_data *data)
207 {
208         g_free (data->entry_text);
209         data->entry_text = g_strdup(gtk_entry_get_text(entry));
210         evaluate_login_visibility(data->ui_data);
211 }
212
213 static void do_override_label(ui_fragment_data *data, struct oc_choice *choice)
214 {
215         const char *new_label = data->opt->label;
216
217         if (!data->widget)
218                 return;
219
220         if (choice->override_name && !strcmp(choice->override_name, data->opt->name))
221                     new_label = choice->override_label;
222
223         gtk_label_set_text(GTK_LABEL(data->widget), new_label);
224
225 }
226 static void combo_changed(GtkComboBox *combo, ui_fragment_data *data)
227 {
228         struct oc_form_opt_select *sopt = (void *)data->opt;
229         int entry = gtk_combo_box_get_active(combo);
230         if (entry < 0)
231                 return;
232
233         data->entry_text = sopt->choices[entry].name;
234
235         g_queue_foreach(data->ui_data->form_entries, (GFunc)do_override_label,
236                         &sopt->choices[entry]);
237 }
238
239 static gboolean ui_write_error (ui_fragment_data *data)
240 {
241         ssl_box_add_error(data->ui_data, UI_get0_output_string(data->uis));
242
243         g_slice_free (ui_fragment_data, data);
244
245         return FALSE;
246 }
247
248 static gboolean ui_write_info (ui_fragment_data *data)
249 {
250         ssl_box_add_info(data->ui_data, UI_get0_output_string(data->uis));
251
252         g_slice_free (ui_fragment_data, data);
253
254         return FALSE;
255 }
256
257 static gboolean ui_write_prompt (ui_fragment_data *data)
258 {
259         GtkWidget *hbox, *text, *entry;
260         int visible;
261         const char *label;
262
263         if (data->uis) {
264                 label = UI_get0_output_string(data->uis);
265                 visible = UI_get_input_flags(data->uis) & UI_INPUT_FLAG_ECHO;
266         } else {
267                 label = data->opt->label;
268                 visible = (data->opt->type == OC_FORM_OPT_TEXT);
269         }
270
271         hbox = gtk_hbox_new(FALSE, 0);
272         gtk_box_pack_start(GTK_BOX(data->ui_data->ssl_box), hbox, FALSE, FALSE, 0);
273
274         text = gtk_label_new(label);
275         gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 0);
276         data->widget = text;
277
278         entry = gtk_entry_new();
279         gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
280         if (!visible)
281                 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
282         if (data->entry_text)
283                 gtk_entry_set_text(GTK_ENTRY(entry), data->entry_text);
284         if (!data->entry_text && !data->ui_data->form_grabbed) {
285                 data->ui_data->form_grabbed = 1;
286                 gtk_widget_grab_focus (entry);
287         }
288         g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(entry_changed), data);
289         g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(entry_activate_cb), ui_data);
290
291         /* data is freed in ui_flush in worker thread */
292
293         return FALSE;
294 }
295
296 static gboolean ui_add_select (ui_fragment_data *data)
297 {
298         GtkWidget *hbox, *text, *combo;
299         struct oc_form_opt_select *sopt = (void *)data->opt;
300         int i;
301
302         hbox = gtk_hbox_new(FALSE, 0);
303         gtk_box_pack_start(GTK_BOX(data->ui_data->ssl_box), hbox, FALSE, FALSE, 0);
304
305         text = gtk_label_new(data->opt->label);
306         gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 0);
307
308         combo = gtk_combo_box_new_text();
309         gtk_box_pack_end(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
310         for (i = 0; i < sopt->nr_choices; i++) {
311                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), sopt->choices[i].label);
312                 if (data->entry_text && 
313                     !strcmp(data->entry_text, sopt->choices[i].name)) {
314                         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
315                         g_free(data->entry_text);
316                         data->entry_text = sopt->choices[i].name;
317                 }
318         }
319         if (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) {
320                 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); 
321                 data->entry_text = sopt->choices[0].name;
322         }
323
324         if (g_queue_peek_tail(ui_data->form_entries) == data)
325                 gtk_widget_grab_focus (combo);
326         g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(combo_changed), data);
327         /* Hook up the 'show' signal to ensure that we override prompts on 
328            UI elements which may be coming later. */
329         g_signal_connect(G_OBJECT(combo), "show", G_CALLBACK(combo_changed), data);
330
331         /* data is freed in ui_flush in worker thread */
332
333         return FALSE;
334 }
335
336 static gboolean ui_show (auth_ui_data *ui_data)
337 {
338         gtk_widget_hide (ui_data->getting_form_label);
339         gtk_widget_show_all (ui_data->ssl_box);
340         gtk_widget_set_sensitive (ui_data->cancel_button, TRUE);
341         g_mutex_lock (ui_data->form_mutex);
342         evaluate_login_visibility(ui_data);
343         ui_data->form_shown = TRUE;
344         g_cond_signal (ui_data->form_shown_changed);
345         g_mutex_unlock (ui_data->form_mutex);
346
347         return FALSE;
348 }
349
350 /* runs in worker thread */
351 static int ui_open(UI *ui)
352 {
353         UI_add_user_data(ui, ui_data);
354
355         return 1;
356 }
357
358 /* runs in worker thread */
359 static int ui_write(UI *ui, UI_STRING *uis)
360 {
361         auth_ui_data *ui_data;
362         ui_fragment_data *data;
363
364         ui_data = UI_get0_user_data(ui);
365
366         /* return if a new host has been selected */
367         if (ui_data->cancelled) {
368                 return 1;
369         }
370
371         data = g_slice_new0 (ui_fragment_data);
372         data->ui_data = ui_data;
373         data->uis = uis;
374
375         switch(UI_get_string_type(uis)) {
376         case UIT_ERROR:
377                 g_idle_add ((GSourceFunc)ui_write_error, data);
378                 break;
379
380         case UIT_INFO:
381                 g_idle_add ((GSourceFunc)ui_write_info, data);
382                 break;
383
384         case UIT_PROMPT:
385         case UIT_VERIFY:
386                 g_mutex_lock (ui_data->form_mutex);
387                 g_queue_push_head(ui_data->form_entries, data);
388                 g_mutex_unlock (ui_data->form_mutex);
389
390                 g_idle_add ((GSourceFunc)ui_write_prompt, data);
391                 break;
392
393         case UIT_BOOLEAN:
394                 /* FIXME */
395         case UIT_NONE:
396         default:
397                 g_slice_free (ui_fragment_data, data);
398         }
399         return 1;
400 }
401
402 /* runs in worker thread */
403 static int ui_flush(UI* ui)
404 {
405         auth_ui_data *ui_data;
406         int response;
407
408         ui_data = UI_get0_user_data(ui);
409
410         g_idle_add((GSourceFunc)ui_show, ui_data);
411         g_mutex_lock(ui_data->form_mutex);
412         /* wait for ui to show */
413         while (!ui_data->form_shown) {
414                 g_cond_wait(ui_data->form_shown_changed, ui_data->form_mutex);
415         }
416         ui_data->form_shown = FALSE;
417
418         if (!ui_data->cancelled) {
419                 /* wait for form submission or cancel */
420                 while (!ui_data->form_retval) {
421                         g_cond_wait(ui_data->form_retval_changed, ui_data->form_mutex);
422                 }
423                 response = GPOINTER_TO_INT (ui_data->form_retval);
424                 ui_data->form_retval = NULL;
425         } else
426                 response = AUTH_DIALOG_RESPONSE_CANCEL;
427
428         /* set entry results and free temporary data structures */
429         while (!g_queue_is_empty (ui_data->form_entries)) {
430                 ui_fragment_data *data;
431                 data = g_queue_pop_tail (ui_data->form_entries);
432                 if (data->entry_text) {
433                         UI_set_result(ui, data->uis, data->entry_text);
434                 }
435                 g_slice_free (ui_fragment_data, data);
436         }
437         ui_data->form_grabbed = 0;
438         g_mutex_unlock(ui_data->form_mutex);
439
440         /* -1 = cancel,
441          *  0 = failure,
442          *  1 = success */
443         return (response == AUTH_DIALOG_RESPONSE_LOGIN ? 1 : -1);
444 }
445
446 /* runs in worker thread */
447 static int ui_close(UI *ui)
448 {
449         return 1;
450 }
451
452 int init_openssl_ui(void)
453 {
454         UI_METHOD *ui_method = UI_create_method("OpenConnect VPN UI (gtk)");
455
456         UI_method_set_opener(ui_method, ui_open);
457         UI_method_set_flusher(ui_method, ui_flush);
458         UI_method_set_writer(ui_method, ui_write);
459         UI_method_set_closer(ui_method, ui_close);
460
461         UI_set_default_method(ui_method);
462         return 0;
463 }
464
465 void remember_gconf_key(auth_ui_data *ui_data, char *key, char *value)
466 {
467         struct gconf_key *k = g_malloc(sizeof(*k));
468
469         if (!k)
470                 return;
471
472         k->next = ui_data->success_keys;
473         k->key = key;
474         k->value = value;
475
476         ui_data->success_keys = k;
477         while (k->next) {
478                 if (!strcmp(k->next->key, key)) {
479                         struct gconf_key *old = k->next;
480                         k->next = old->next;
481                         g_free(old->key);
482                         g_free(old->value);
483                         g_free(old);
484                         break;
485                 }
486                 k = k->next;
487         }
488 }
489
490 char *find_form_answer(struct oc_auth_form *form, struct oc_form_opt *opt)
491 {
492         char *key, *result;
493         key = g_strdup_printf("%s/vpn/form:%s:%s", config_path,
494                               form->auth_id, opt->name);
495         result = gconf_client_get_string(gcl, key, NULL);
496         g_free(key);
497         return result;
498 }
499
500 /* This part for processing forms from openconnect directly, rather than
501    through the SSL UI abstraction (which doesn't allow 'select' options) */
502
503 static gboolean ui_form (struct oc_auth_form *form)
504 {
505         struct oc_form_opt *opt;
506
507         ssl_box_clear(ui_data);
508
509         g_mutex_lock(ui_data->form_mutex);
510         while (!g_queue_is_empty (ui_data->form_entries)) {
511                 ui_fragment_data *data;
512                 data = g_queue_pop_tail (ui_data->form_entries);
513                 g_slice_free (ui_fragment_data, data);
514         }
515         g_mutex_unlock(ui_data->form_mutex);
516
517         if (form->banner)
518                 ssl_box_add_info(ui_data, form->banner);
519         if (form->error)
520                 ssl_box_add_error(ui_data, form->error);
521         if (form->message)
522                 ssl_box_add_info(ui_data, form->message);
523
524         for (opt = form->opts; opt; opt = opt->next) {
525                 ui_fragment_data *data;
526
527                 if (opt->type == OC_FORM_OPT_HIDDEN)
528                         continue;
529
530                 data = g_slice_new0 (ui_fragment_data);
531                 data->ui_data = ui_data;
532                 data->opt = opt;
533                 
534                 if (opt->type == OC_FORM_OPT_PASSWORD ||
535                     opt->type == OC_FORM_OPT_TEXT) {
536                         g_mutex_lock (ui_data->form_mutex);
537                         g_queue_push_head(ui_data->form_entries, data);
538                         g_mutex_unlock (ui_data->form_mutex);
539                         if (opt->type != OC_FORM_OPT_PASSWORD)
540                                 data->entry_text = find_form_answer(form, opt);
541
542                         ui_write_prompt(data);
543                 } else if (opt->type == OC_FORM_OPT_SELECT) {
544                         g_mutex_lock (ui_data->form_mutex);
545                         g_queue_push_head(ui_data->form_entries, data);
546                         g_mutex_unlock (ui_data->form_mutex);
547                         data->entry_text = find_form_answer(form, opt);
548
549                         ui_add_select(data);
550                 } else
551                         g_slice_free (ui_fragment_data, data);
552         }
553         
554         return ui_show(ui_data);
555 }
556
557 int nm_process_auth_form (struct openconnect_info *vpninfo,
558                           struct oc_auth_form *form)
559 {
560         int response;
561
562         g_idle_add((GSourceFunc)ui_form, form);
563
564         g_mutex_lock(ui_data->form_mutex);
565         /* wait for ui to show */
566         while (!ui_data->form_shown) {
567                 g_cond_wait(ui_data->form_shown_changed, ui_data->form_mutex);
568         }
569         ui_data->form_shown = FALSE;
570
571         if (!ui_data->cancelled) {
572                 /* wait for form submission or cancel */
573                 while (!ui_data->form_retval) {
574                         g_cond_wait(ui_data->form_retval_changed, ui_data->form_mutex);
575                 }
576                 response = GPOINTER_TO_INT (ui_data->form_retval);
577                 ui_data->form_retval = NULL;
578         } else
579                 response = AUTH_DIALOG_RESPONSE_CANCEL;
580
581         if (response == AUTH_DIALOG_RESPONSE_LOGIN) {
582                 /* set entry results and free temporary data structures */
583                 while (!g_queue_is_empty (ui_data->form_entries)) {
584                         ui_fragment_data *data;
585                         data = g_queue_pop_tail (ui_data->form_entries);
586                         if (data->entry_text) {
587                                 data->opt->value = data->entry_text;
588
589                                 if (data->opt->type == OC_FORM_OPT_TEXT ||
590                                     data->opt->type == OC_FORM_OPT_SELECT) {
591                                         char *keyname;
592                                         keyname = g_strdup_printf("form:%s:%s", form->auth_id, data->opt->name);
593                                         remember_gconf_key(ui_data, keyname, strdup(data->entry_text));
594                                 }
595                         }
596                         g_slice_free (ui_fragment_data, data);
597                 }
598         }
599
600
601         g_mutex_unlock(ui_data->form_mutex);
602         
603         /* -1 = cancel,
604          *  0 = failure,
605          *  1 = success */
606         return (response == AUTH_DIALOG_RESPONSE_LOGIN ? 0 : 1);
607
608 }
609
610 static char* get_title(const char *vpn_name)
611 {
612         if (vpn_name)
613                 return g_strdup_printf("Connect to VPN '%s'", vpn_name);
614         else
615                 return g_strdup("Connect to VPN");
616 }
617
618 typedef struct cert_data {
619         auth_ui_data *ui_data;
620         X509 *peer_cert;
621         const char *reason;
622 } cert_data;
623
624
625 static gboolean user_validate_cert(cert_data *data)
626 {
627         BIO *bp = BIO_new(BIO_s_mem());
628         char *msg, *title;
629         BUF_MEM *certinfo;
630         char zero = 0;
631         GtkWidget *dlg, *text, *scroll;
632         GtkTextBuffer *buffer;
633         int result;
634
635         /* There are probably better ways to do this -- getting individual
636            elements of the cert info and formatting it nicely in the dialog
637            box. But this will do for now... */
638         X509_print_ex(bp, data->peer_cert, 0, 0);
639         BIO_write(bp, &zero, 1);
640         BIO_get_mem_ptr(bp, &certinfo);
641
642         title = get_title(data->ui_data->vpn_name);
643         msg = g_strdup_printf("Certificate from VPN server \"%s\" failed verification.\n"
644                               "Reason: %s\nDo you want to accept it?",
645                               data->ui_data->vpninfo->hostname, data->reason);
646
647         dlg = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_QUESTION,
648                                      GTK_BUTTONS_OK_CANCEL,
649                                      msg);
650         gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dlg), FALSE);
651         gtk_window_set_skip_pager_hint(GTK_WINDOW(dlg), FALSE);
652         gtk_window_set_title(GTK_WINDOW(dlg), title);
653         gtk_window_set_default_size(GTK_WINDOW(dlg), 550, 600);
654         gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE);
655         gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_CANCEL);
656
657         g_free(title);
658         g_free(msg);
659
660         scroll = gtk_scrolled_window_new(NULL, NULL);
661         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->vbox), scroll, TRUE, TRUE, 0);
662         gtk_widget_show(scroll);
663
664         text = gtk_text_view_new();
665         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
666         gtk_text_buffer_set_text(buffer, certinfo->data, -1);
667         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), 0);
668         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
669         gtk_container_add(GTK_CONTAINER(scroll), text);
670         gtk_widget_show(text);
671
672         result = gtk_dialog_run(GTK_DIALOG(dlg));
673
674         BIO_free(bp);
675         gtk_widget_destroy(dlg);
676
677         g_mutex_lock (ui_data->form_mutex);
678         if (result == GTK_RESPONSE_OK)
679                 data->ui_data->cert_response = CERT_ACCEPTED;
680         else
681                 data->ui_data->cert_response = CERT_DENIED;
682         g_cond_signal (ui_data->cert_response_changed);
683         g_mutex_unlock (ui_data->form_mutex);
684
685         return FALSE;
686 }
687
688 /* runs in worker thread */
689 static int validate_peer_cert(struct openconnect_info *vpninfo,
690                               X509 *peer_cert, const char *reason)
691 {
692         char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
693         char *certs_data;
694         char *key;
695         int ret = 0;
696         cert_data *data;
697
698         ret = get_cert_sha1_fingerprint(vpninfo, peer_cert, fingerprint);
699         if (ret)
700                 return ret;
701
702         key = g_strdup_printf("%s/vpn/%s", config_path, "certsigs");
703         certs_data = gconf_client_get_string(gcl, key, NULL);
704         if (certs_data) {
705                 char **certs = g_strsplit_set(certs_data, "\t", 0);
706                 char **this = certs;
707
708                 while (*this) {
709                         if (!strcmp(*this, fingerprint)) {
710                                 g_strfreev(certs);
711                                 goto out;
712                         }
713                         this++;
714                 }
715                 g_strfreev(certs);
716         }
717
718         data = g_slice_new(cert_data);
719         data->ui_data = ui_data; /* FIXME uses global */
720         data->peer_cert = peer_cert;
721         data->reason = reason;
722
723         g_mutex_lock(ui_data->form_mutex);
724
725         ui_data->cert_response = CERT_USER_NOT_READY;
726         g_idle_add((GSourceFunc)user_validate_cert, data);
727
728         /* wait for user to accept or cancel */
729         while (ui_data->cert_response == CERT_USER_NOT_READY) {
730                 g_cond_wait(ui_data->cert_response_changed, ui_data->form_mutex);
731         }
732         if (ui_data->cert_response == CERT_ACCEPTED) {
733                 if (certs_data) {
734                         char *new = g_strdup_printf("%s\t%s", certs_data, fingerprint);
735                         gconf_client_set_string(gcl, key, new, NULL);
736                         g_free(new);
737                 } else {
738                         gconf_client_set_string(gcl, key, fingerprint, NULL);
739                 }
740                 ret = 0;
741         } else {
742                 ret = -EINVAL;
743         }
744         g_mutex_unlock (ui_data->form_mutex);
745
746         g_slice_free(cert_data, data);
747
748  out:
749         g_free(certs_data);
750         g_free(key);
751         return ret;
752 }
753
754 static char *get_config_path(GConfClient *gcl, const char *vpn_uuid)
755 {
756         GSList *connections, *this;
757         char *key, *val;
758         char *config_path = NULL;
759
760         connections = gconf_client_all_dirs(gcl,
761                                             "/system/networking/connections",
762                                             NULL);
763
764         for (this = connections; this; this = this->next) {
765                 const char *path = (const char *) this->data;
766
767                 key = g_strdup_printf("%s/connection/type", path);
768                 val = gconf_client_get_string(gcl, key, NULL);
769                 g_free(key);
770
771                 if (!val || strcmp(val, "vpn")) {
772                         g_free(val);
773                         continue;
774                 }
775                 g_free(val);
776
777                 key = g_strdup_printf("%s/connection/uuid", path);
778                 val = gconf_client_get_string(gcl, key, NULL);
779                 g_free(key);
780
781                 if (!val || strcmp(val, vpn_uuid)) {
782                         g_free(val);
783                         continue;
784                 }
785                 g_free(val);
786
787                 config_path = g_strdup(path);
788                 break;
789         }
790         g_slist_foreach(connections, (GFunc)g_free, NULL);
791         g_slist_free(connections);
792
793         return config_path;
794 }
795
796 static char *get_gconf_setting(GConfClient *gcl, char *config_path,
797                                char *setting)
798 {
799         char *result;
800         char *key = g_strdup_printf("%s/vpn/%s", config_path, setting);
801         result = gconf_client_get_string(gcl, key, NULL);
802         g_free(key);
803         return result;
804 }
805
806 static int get_gconf_autoconnect(GConfClient *gcl, char *config_path)
807 {
808         char *autoconnect = get_gconf_setting(gcl, config_path, "autoconnect");
809         int ret = 0;
810
811         if (autoconnect) {
812                 if (!strcmp(autoconnect, "yes"))
813                         ret = 1;
814                 g_free(autoconnect);
815         }
816         return ret;
817 }
818
819 static int parse_xmlconfig(char *xmlconfig)
820 {
821         xmlDocPtr xml_doc;
822         xmlNode *xml_node, *xml_node2;
823         struct vpnhost *newhost, **list_end;
824
825         list_end = &vpnhosts->next;
826         /* gateway may be there already */
827         while (*list_end) {
828                 list_end = &(*list_end)->next;
829         }
830
831         xml_doc = xmlReadMemory(xmlconfig, strlen(xmlconfig), "noname.xml", NULL, 0);
832
833         xml_node = xmlDocGetRootElement(xml_doc);
834         for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
835                 if (xml_node->type == XML_ELEMENT_NODE &&
836                     !strcmp((char *)xml_node->name, "ServerList")) {
837
838                         for (xml_node = xml_node->children; xml_node;
839                              xml_node = xml_node->next) {
840
841                                 if (xml_node->type == XML_ELEMENT_NODE &&
842                                     !strcmp((char *)xml_node->name, "HostEntry")) {
843                                         int match = 0;
844
845                                         newhost = malloc(sizeof(*newhost));
846                                         if (!newhost)
847                                                 return -ENOMEM;
848
849                                         memset(newhost, 0, sizeof(*newhost));
850                                         for (xml_node2 = xml_node->children;
851                                              match >= 0 && xml_node2; xml_node2 = xml_node2->next) {
852
853                                                 if (xml_node2->type != XML_ELEMENT_NODE)
854                                                         continue;
855
856                                                 if (!strcmp((char *)xml_node2->name, "HostName")) {
857                                                         char *content = (char *)xmlNodeGetContent(xml_node2);
858                                                         newhost->hostname = content;
859                                                 } else if (!strcmp((char *)xml_node2->name, "HostAddress")) {
860                                                         char *content = (char *)xmlNodeGetContent(xml_node2);
861                                                         newhost->hostaddress = content;
862                                                 } else if (!strcmp((char *)xml_node2->name, "UserGroup")) {
863                                                         char *content = (char *)xmlNodeGetContent(xml_node2);
864                                                         newhost->usergroup = content;
865                                                 }
866                                         }
867                                         if (newhost->hostname && newhost->hostaddress) {
868                                                 *list_end = newhost;
869                                                 list_end = &newhost->next;
870
871                                                 if (!strcasecmp(newhost->hostaddress, vpnhosts->hostaddress) &&
872                                                     !strcasecmp(newhost->usergroup ?: "", vpnhosts->usergroup ?: "")) {
873                                                         /* Remove originally configured host if it's in the list */
874                                                         struct vpnhost *tmp = vpnhosts->next;
875                                                         free(vpnhosts);
876                                                         vpnhosts = tmp;
877                                                 }
878
879                                         } else
880                                                 free(newhost);
881                                 }
882                         }
883                         break;
884                 }
885         }
886         xmlFreeDoc(xml_doc);
887         return 0;
888 }
889
890 static int get_config(char *vpn_uuid, struct openconnect_info *vpninfo)
891 {
892         char *proxy;
893         char *xmlconfig;
894         char *hostname;
895         char *group;
896         char *csd;
897         char *csd_wrapper;
898         char *pem_passphrase_fsid;
899
900         gcl = gconf_client_get_default();
901         config_path = get_config_path(gcl, vpn_uuid);
902
903         if (!config_path)
904                 return -EINVAL;
905
906         hostname = get_gconf_setting(gcl, config_path,
907                                      NM_OPENCONNECT_KEY_GATEWAY);
908         if (!hostname) {
909                 fprintf(stderr, "No gateway configured\n");
910                 return -EINVAL;
911         }
912
913         /* add gateway to host list */
914         vpnhosts = malloc(sizeof(*vpnhosts));
915         if (!vpnhosts)
916                 return -ENOMEM;
917         vpnhosts->hostname = g_strdup(hostname);
918         group = strchr(hostname, '/');
919         if (group) {
920                 *(group++) = 0;
921                 vpnhosts->usergroup = g_strdup(group);
922         } else
923                 vpnhosts->usergroup = NULL;
924         vpnhosts->hostaddress = hostname;
925         vpnhosts->next = NULL;
926
927 if (0) {
928 /* DEBUG add another copy of gateway to host list */
929         vpnhost *tmphost;
930         tmphost = malloc(sizeof(tmphost));
931         if (!tmphost)
932                 return -ENOMEM;
933         tmphost->hostname = g_strdup("VPN Gateway 2");
934         tmphost->hostaddress = hostname;
935         tmphost->usergroup = NULL;
936         tmphost->next = NULL;
937         vpnhosts->next = tmphost;
938 }
939         lasthost = get_gconf_setting(gcl, config_path, "lasthost");
940
941         xmlconfig = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_XMLCONFIG);
942         if (xmlconfig) {
943                 unsigned char sha1[SHA_DIGEST_LENGTH];
944                 EVP_MD_CTX c;
945                 int i;
946
947                 EVP_MD_CTX_init(&c);
948                 EVP_Digest(xmlconfig, strlen(xmlconfig), sha1, NULL, EVP_sha1(), NULL);
949                 EVP_MD_CTX_cleanup(&c);
950
951                 for (i = 0; i < SHA_DIGEST_LENGTH; i++)
952                         sprintf(&vpninfo->xmlsha1[i*2], "%02x", sha1[i]);
953
954                 parse_xmlconfig(xmlconfig);
955                 g_free(xmlconfig);
956         }
957
958         vpninfo->cafile = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_CACERT);
959
960         csd = get_gconf_setting(gcl, config_path, "enable_csd_trojan");
961         if (csd && !strcmp(csd, "yes")) {
962                 /* We're not running as root; we can't setuid(). */
963                 vpninfo->uid_csd = getuid();
964                 vpninfo->uid_csd_given = 2;
965
966                 csd_wrapper = get_gconf_setting(gcl, config_path, "csd_wrapper");
967                 if (csd_wrapper && csd_wrapper[0] )
968                         vpninfo->csd_wrapper = csd_wrapper;
969                 else
970                         g_free(csd_wrapper);
971         }
972         g_free(csd);
973
974         proxy = get_gconf_setting(gcl, config_path, "proxy");
975         if (proxy && proxy[0] && openconnect_set_http_proxy(vpninfo, proxy))
976                 return -EINVAL;
977
978         vpninfo->cert = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_USERCERT);
979         vpninfo->sslkey = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_PRIVKEY);
980         if (!vpninfo->sslkey)
981                 vpninfo->sslkey = vpninfo->cert;
982
983         pem_passphrase_fsid = get_gconf_setting(gcl, config_path, "pem_passphrase_fsid");
984         if (pem_passphrase_fsid && vpninfo->sslkey && !strcmp(pem_passphrase_fsid, "yes"))
985                 openconnect_passphrase_from_fsid(vpninfo);
986         g_free(pem_passphrase_fsid);
987                                     
988         return 0;
989 }
990
991 static void populate_vpnhost_combo(auth_ui_data *ui_data)
992 {
993         struct vpnhost *host;
994         int i = 0;
995         GtkComboBox *combo = GTK_COMBO_BOX(ui_data->combo);
996
997         for (host = vpnhosts; host; host = host->next) {
998                 gtk_combo_box_append_text(combo, host->hostname);
999
1000                 if (i == 0 ||
1001                     (lasthost && !strcmp(host->hostname, lasthost)))
1002                         gtk_combo_box_set_active(combo, i);
1003                 i++;
1004
1005         }
1006 }
1007
1008 int write_new_config(struct openconnect_info *vpninfo, char *buf, int buflen)
1009 {
1010         char *key = g_strdup_printf("%s/vpn/%s", config_path,
1011                                     NM_OPENCONNECT_KEY_XMLCONFIG);
1012         gconf_client_set_string(gcl, key, buf, NULL);
1013         return 0;
1014 }
1015
1016 static void autocon_toggled(GtkWidget *widget)
1017 {
1018         int enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
1019         char *key = g_strdup_printf("%s/vpn/autoconnect", config_path);
1020
1021         gconf_client_set_string(gcl, key, enabled ? "yes" : "no", NULL);
1022 }
1023
1024 static void scroll_log(GtkTextBuffer *log, GtkTextView *view)
1025 {
1026         GtkTextMark *mark;
1027
1028         g_return_if_fail(GTK_IS_TEXT_VIEW(view));
1029
1030         mark = gtk_text_buffer_get_insert(log);
1031         gtk_text_view_scroll_to_mark(view, mark, 0.0, FALSE, 0.0, 0.0);
1032 }
1033
1034 /* NOTE: write_progress_real() will free the given string */
1035 static gboolean write_progress_real(char *message)
1036 {
1037         GtkTextIter iter;
1038
1039         g_return_val_if_fail(message, FALSE);
1040
1041         gtk_text_buffer_get_end_iter(ui_data->log, &iter);
1042         gtk_text_buffer_insert(ui_data->log, &iter, message, -1);
1043
1044         g_free(message);
1045
1046         return FALSE;
1047 }
1048
1049 /* runs in worker thread */
1050 void write_progress(struct openconnect_info *info, int level, const char *fmt, ...)
1051 {
1052         va_list args;
1053         char *msg;
1054
1055         if (last_message) {
1056                 g_free(last_message);
1057                 last_message = NULL;
1058         }
1059
1060         va_start(args, fmt);
1061         msg = g_strdup_vprintf(fmt, args);
1062         va_end(args);
1063
1064         if (level <= PRG_DEBUG) {
1065                 g_idle_add((GSourceFunc)write_progress_real, g_strdup(msg));
1066         }
1067
1068         if (level <= PRG_ERR) {
1069                 last_message = msg;
1070                 return;
1071         }
1072         g_free(msg);
1073 }
1074
1075 static void print_peer_cert(struct openconnect_info *vpninfo)
1076 {
1077         char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
1078         X509 *cert = SSL_get_peer_certificate(vpninfo->https_ssl);
1079
1080         if (cert && !get_cert_sha1_fingerprint(vpninfo, cert, fingerprint))
1081                 printf("gwcert\n%s\n", fingerprint);
1082 }
1083
1084 static gboolean cookie_obtained(auth_ui_data *ui_data)
1085 {
1086         ui_data->getting_cookie = FALSE;
1087         gtk_widget_hide (ui_data->getting_form_label);
1088
1089         if (ui_data->cancelled) {
1090                 /* user has chosen a new host, start from beginning */
1091                 while (ui_data->success_keys) {
1092                         struct gconf_key *k = ui_data->success_keys;
1093                         
1094                         ui_data->success_keys = k->next;
1095                         g_free(k->key);
1096                         g_free(k->value);
1097                         g_free(k);
1098                 }                       
1099                 connect_host(ui_data);
1100                 return FALSE;
1101         }
1102
1103         if (ui_data->cookie_retval < 0) {
1104                 /* error while getting cookie */
1105                 if (last_message) {
1106                         ssl_box_add_error(ui_data, last_message);
1107                         gtk_widget_show_all(ui_data->ssl_box);
1108                         gtk_widget_set_sensitive(ui_data->cancel_button, TRUE);
1109                 }
1110                 ui_data->retval = 1;
1111         } else if (!ui_data->cookie_retval) {
1112                 /* got cookie */
1113                 while (ui_data->success_keys) {
1114                         struct gconf_key *k = ui_data->success_keys;
1115                         char *key = g_strdup_printf("%s/vpn/%s", config_path, k->key);
1116
1117                         gconf_client_set_string(gcl, key, k->value, NULL);
1118                         g_free(key);
1119
1120                         ui_data->success_keys = k->next;
1121                         g_free(k->key);
1122                         g_free(k->value);
1123                         g_free(k);
1124                 }
1125
1126                 printf("%s\n%s:%d\n", NM_OPENCONNECT_KEY_GATEWAY,
1127                        ui_data->vpninfo->hostname, ui_data->vpninfo->port);
1128                 printf("%s\n%s\n", NM_OPENCONNECT_KEY_COOKIE,
1129                        ui_data->vpninfo->cookie);
1130                 print_peer_cert(ui_data->vpninfo);
1131                 memset((void *)ui_data->vpninfo->cookie, 0, strlen(ui_data->vpninfo->cookie));
1132                 printf("\n\n");
1133                 fflush(stdout);
1134                 ui_data->retval = 0;
1135
1136                 gtk_main_quit();
1137         } else {
1138                 /* no cookie; user cancellation */
1139                 gtk_widget_show (ui_data->no_form_label);
1140                 ui_data->retval = 1;
1141         }
1142
1143         while (ui_data->success_keys) {
1144                 struct gconf_key *k = ui_data->success_keys;
1145
1146                 ui_data->success_keys = k->next;
1147                 g_free(k->key);
1148                 g_free(k->value);
1149                 g_free(k);
1150         }                       
1151
1152         return FALSE;
1153 }
1154
1155 gpointer obtain_cookie (auth_ui_data *ui_data)
1156 {
1157         int ret;
1158
1159         ret = openconnect_obtain_cookie(ui_data->vpninfo);
1160
1161         ui_data->cookie_retval = ret;
1162         g_idle_add ((GSourceFunc)cookie_obtained, ui_data);
1163
1164         return NULL;
1165 }
1166
1167 static void connect_host(auth_ui_data *ui_data)
1168 {
1169         GThread *thread;
1170         vpnhost *host;
1171         int i;
1172         int host_nr;
1173
1174         ui_data->cancelled = FALSE;
1175         ui_data->getting_cookie = TRUE;
1176
1177         g_mutex_lock (ui_data->form_mutex);
1178         ui_data->form_retval = NULL;
1179         g_mutex_unlock (ui_data->form_mutex);
1180
1181         ssl_box_clear(ui_data);
1182         gtk_widget_show(ui_data->getting_form_label);
1183
1184         /* reset ssl context.
1185          * TODO: this is probably not the way to go... */
1186         if (ui_data->vpninfo->https_ssl) {
1187                 free(ui_data->vpninfo->peer_addr);
1188                 ui_data->vpninfo->peer_addr = NULL;
1189                 openconnect_close_https(ui_data->vpninfo);
1190         }
1191         if (ui_data->vpninfo->https_ctx) {
1192                 SSL_CTX_free(ui_data->vpninfo->https_ctx);
1193                 ui_data->vpninfo->https_ctx = NULL;
1194         }
1195
1196         host_nr = gtk_combo_box_get_active(GTK_COMBO_BOX(ui_data->combo));
1197         host = vpnhosts;
1198         for (i = 0; i < host_nr; i++)
1199                 host = host->next;
1200
1201         if (internal_parse_url(host->hostaddress, NULL,
1202                       &ui_data->vpninfo->hostname, &ui_data->vpninfo->port,
1203                       &ui_data->vpninfo->urlpath, 443)) {
1204                 fprintf(stderr, "Failed to parse server URL '%s'\n",
1205                         host->hostaddress);
1206                 ui_data->vpninfo->hostname = g_strdup(host->hostaddress);
1207         }
1208
1209         if (!ui_data->vpninfo->urlpath && host->usergroup)
1210                 ui_data->vpninfo->urlpath = g_strdup(host->usergroup);
1211
1212         remember_gconf_key(ui_data, g_strdup("lasthost"), g_strdup(host->hostname));
1213
1214         thread = g_thread_create((GThreadFunc)obtain_cookie, ui_data,
1215                                  FALSE, NULL);
1216 }
1217
1218
1219 static void queue_connect_host(auth_ui_data *ui_data)
1220 {
1221         ssl_box_clear(ui_data);
1222         gtk_widget_show(ui_data->getting_form_label);
1223         gtk_widget_hide(ui_data->no_form_label);
1224
1225         if (!ui_data->getting_cookie) {
1226                 connect_host(ui_data);
1227         } else if (!ui_data->cancelled) {
1228                 /* set state to cancelled. Current challenge-response-
1229                  * conversation will not be shown to user, and cookie_obtained()
1230                  * will start a new one conversation */
1231                 ui_data->cancelled = TRUE;
1232                 gtk_dialog_response(GTK_DIALOG(ui_data->dialog), AUTH_DIALOG_RESPONSE_CANCEL);
1233         }
1234 }
1235
1236 static void dialog_response (GtkDialog *dialog, int response, auth_ui_data *ui_data)
1237 {
1238         switch (response) {
1239         case AUTH_DIALOG_RESPONSE_CANCEL:
1240         case AUTH_DIALOG_RESPONSE_LOGIN:
1241                 ssl_box_clear(ui_data);
1242                 if (ui_data->getting_cookie)
1243                         gtk_widget_show (ui_data->getting_form_label);
1244                 g_mutex_lock (ui_data->form_mutex);
1245                 ui_data->form_retval = GINT_TO_POINTER(response);
1246                 g_cond_signal (ui_data->form_retval_changed);
1247                 g_mutex_unlock (ui_data->form_mutex);
1248                 break;
1249         case GTK_RESPONSE_CLOSE:
1250                 gtk_main_quit();
1251                 break;
1252         default:
1253                 ;
1254         }
1255 }
1256
1257 static void cancel_clicked (GtkButton *btn, auth_ui_data *ui_data)
1258 {
1259         gtk_dialog_response (GTK_DIALOG(ui_data->dialog), AUTH_DIALOG_RESPONSE_CANCEL);
1260 }
1261
1262 static void login_clicked (GtkButton *btn, auth_ui_data *ui_data)
1263 {
1264         gtk_dialog_response (GTK_DIALOG(ui_data->dialog), AUTH_DIALOG_RESPONSE_LOGIN);
1265 }
1266
1267 static void build_main_dialog(auth_ui_data *ui_data)
1268 {
1269         char *title;
1270         GtkWidget *vbox, *hbox, *label, *frame, *image, *frame_box;
1271         GtkWidget *exp, *scrolled, *view, *autocon;
1272
1273         gtk_window_set_default_icon_name(GTK_STOCK_DIALOG_AUTHENTICATION);
1274
1275         title = get_title(ui_data->vpn_name);
1276         ui_data->dialog = gtk_dialog_new_with_buttons(title, NULL, GTK_DIALOG_MODAL,
1277                                                       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1278                                                       NULL);
1279         g_signal_connect (ui_data->dialog, "response", G_CALLBACK(dialog_response), ui_data);
1280         gtk_window_set_default_size(GTK_WINDOW(ui_data->dialog), 350, 300);
1281         g_signal_connect_swapped(ui_data->dialog, "destroy",
1282                                  G_CALLBACK(gtk_main_quit), NULL);
1283         g_free(title);
1284
1285         vbox = gtk_vbox_new(FALSE, 8);
1286         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(ui_data->dialog)->vbox), vbox, TRUE, TRUE, 0);
1287         gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1288         gtk_widget_show(vbox);
1289
1290         hbox = gtk_hbox_new(FALSE, 4);
1291         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1292         gtk_widget_show(hbox);
1293
1294         label = gtk_label_new("VPN host");
1295         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1296         gtk_widget_show(label);
1297
1298         ui_data->combo = gtk_combo_box_new_text();
1299         populate_vpnhost_combo(ui_data);
1300         gtk_box_pack_start(GTK_BOX(hbox), ui_data->combo, TRUE, TRUE, 0);
1301         g_signal_connect_swapped(ui_data->combo, "changed",
1302                                  G_CALLBACK(queue_connect_host), ui_data);
1303         gtk_widget_show(ui_data->combo);
1304
1305         ui_data->connect_button = gtk_button_new();
1306         gtk_box_pack_end(GTK_BOX(hbox), ui_data->connect_button, FALSE, FALSE, 0);
1307         image = gtk_image_new_from_stock(GTK_STOCK_CONNECT, GTK_ICON_SIZE_BUTTON);
1308         gtk_button_set_image (GTK_BUTTON(ui_data->connect_button), image);
1309         gtk_widget_grab_focus(ui_data->connect_button);
1310         g_signal_connect_swapped(ui_data->connect_button, "clicked",
1311                                  G_CALLBACK(queue_connect_host), ui_data);
1312         gtk_widget_show(ui_data->connect_button);
1313
1314         autocon = gtk_check_button_new_with_label("Automatically start connecting next time");
1315         gtk_box_pack_start(GTK_BOX(vbox), autocon, FALSE, FALSE, 0);
1316         if (get_gconf_autoconnect(gcl, config_path))
1317                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(autocon), 1);
1318         g_signal_connect(autocon, "toggled", G_CALLBACK(autocon_toggled), NULL);
1319         gtk_widget_show(autocon);
1320
1321         frame = gtk_frame_new(NULL);
1322         gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
1323         gtk_widget_set_size_request(frame, -1, -1);
1324         gtk_widget_show(frame);
1325
1326         frame_box = gtk_vbox_new(FALSE, 4);
1327         gtk_container_set_border_width(GTK_CONTAINER(frame_box), 8);
1328         gtk_container_add(GTK_CONTAINER(frame), frame_box);
1329         gtk_widget_show(frame_box);
1330
1331         ui_data->no_form_label = gtk_label_new("Select a host to fetch the login form");
1332         gtk_widget_set_sensitive(ui_data->no_form_label, FALSE);
1333         gtk_box_pack_start(GTK_BOX(frame_box), ui_data->no_form_label, FALSE, FALSE, 0);
1334         gtk_widget_show(ui_data->no_form_label);
1335
1336         ui_data->getting_form_label = gtk_label_new("Contacting host, please wait...");
1337         gtk_widget_set_sensitive(ui_data->getting_form_label, FALSE);
1338         gtk_box_pack_start(GTK_BOX(frame_box), ui_data->getting_form_label, FALSE, FALSE, 0);
1339
1340         ui_data->ssl_box = gtk_vbox_new(FALSE, 4);
1341         gtk_box_pack_start(GTK_BOX(frame_box), ui_data->ssl_box, FALSE, FALSE, 0);
1342         gtk_widget_show(ui_data->ssl_box);
1343
1344         hbox = gtk_hbox_new (FALSE, 6);
1345         gtk_box_pack_end(GTK_BOX(frame_box), hbox, FALSE, FALSE, 0);
1346         gtk_widget_show(hbox);
1347
1348         ui_data->login_button = gtk_button_new_with_mnemonic("_Login");
1349         image = gtk_image_new_from_stock(GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
1350         gtk_button_set_image (GTK_BUTTON(ui_data->login_button), image);
1351         gtk_box_pack_end(GTK_BOX(hbox), ui_data->login_button, FALSE, FALSE, 0);
1352         g_signal_connect (ui_data->login_button, "clicked", G_CALLBACK(login_clicked), ui_data);
1353         gtk_widget_set_sensitive (ui_data->login_button, FALSE);
1354         gtk_widget_show(ui_data->login_button);
1355
1356         ui_data->cancel_button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1357         gtk_box_pack_end(GTK_BOX(hbox), ui_data->cancel_button, FALSE, FALSE, 0);
1358         g_signal_connect (ui_data->cancel_button, "clicked", G_CALLBACK(cancel_clicked), ui_data);
1359         gtk_widget_set_sensitive (ui_data->cancel_button, FALSE);
1360         gtk_widget_show(ui_data->cancel_button);
1361
1362         exp = gtk_expander_new("Log");
1363         gtk_box_pack_end(GTK_BOX(vbox), exp, FALSE, FALSE, 0);
1364         gtk_widget_show(exp);
1365
1366         scrolled = gtk_scrolled_window_new(NULL, NULL);
1367         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1368                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1369         gtk_widget_set_size_request(scrolled, -1, 75);
1370         gtk_container_add(GTK_CONTAINER(exp), scrolled);
1371         gtk_widget_show(scrolled);
1372
1373         view = gtk_text_view_new();
1374         gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
1375         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
1376         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR);
1377         gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 5);
1378         gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 5);
1379         gtk_text_view_set_indent(GTK_TEXT_VIEW(view), -10);
1380         gtk_container_add(GTK_CONTAINER(scrolled), view);
1381         gtk_widget_show(view);
1382
1383         ui_data->log = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
1384         g_signal_connect(ui_data->log, "changed", G_CALLBACK(scroll_log), view);
1385 }
1386
1387 static auth_ui_data *init_ui_data (char *vpn_name)
1388 {
1389         auth_ui_data *ui_data;
1390
1391         ui_data = g_slice_new0(auth_ui_data);
1392         ui_data->retval = 1;
1393
1394         ui_data->form_entries = g_queue_new();
1395         ui_data->form_mutex = g_mutex_new();
1396         ui_data->form_retval_changed = g_cond_new();
1397         ui_data->form_shown_changed = g_cond_new();
1398         ui_data->cert_response_changed = g_cond_new();
1399         ui_data->vpn_name = vpn_name;
1400
1401         ui_data->vpninfo = g_slice_new0(struct openconnect_info);
1402         ui_data->vpninfo->mtu = 1406;
1403         ui_data->vpninfo->useragent = openconnect_create_useragent("OpenConnect VPN Agent (NetworkManager)");
1404         ui_data->vpninfo->ssl_fd = -1;
1405         ui_data->vpninfo->write_new_config = write_new_config;
1406         ui_data->vpninfo->progress = write_progress;
1407         ui_data->vpninfo->validate_peer_cert = validate_peer_cert;
1408         ui_data->vpninfo->process_auth_form = nm_process_auth_form;
1409 #if 0
1410         ui_data->vpninfo->proxy_factory = px_proxy_factory_new();
1411 #endif
1412
1413         return ui_data;
1414 }
1415
1416 static struct option long_options[] = {
1417         {"reprompt", 0, 0, 'r'},
1418         {"uuid", 1, 0, 'u'},
1419         {"name", 1, 0, 'n'},
1420         {"service", 1, 0, 's'},
1421         {NULL, 0, 0, 0},
1422 };
1423
1424 int main (int argc, char **argv)
1425 {
1426         char *vpn_name = NULL, *vpn_uuid = NULL, *vpn_service = NULL;
1427         int reprompt;
1428         int opt;
1429
1430         while ((opt = getopt_long(argc, argv, "ru:n:s:", long_options, NULL))) {
1431                 if (opt < 0)
1432                         break;
1433
1434                 switch(opt) {
1435                 case 'r':
1436                         reprompt = 1;
1437                         break;
1438
1439                 case 'u':
1440                         vpn_uuid = optarg;
1441                         break;
1442
1443                 case 'n':
1444                         vpn_name = optarg;
1445                         break;
1446
1447                 case 's':
1448                         vpn_service = optarg;
1449                         break;
1450
1451                 default:
1452                         fprintf(stderr, "Unknown option\n");
1453                         return 1;
1454                 }
1455         }
1456
1457         if (optind != argc) {
1458                 fprintf(stderr, "Superfluous command line options\n");
1459                 return 1;
1460         }
1461
1462         if (!vpn_uuid || !vpn_name || !vpn_service) {
1463                 fprintf (stderr, "Have to supply UUID, name, and service\n");
1464                 return 1;
1465         }
1466
1467         if (strcmp(vpn_service, NM_DBUS_SERVICE_OPENCONNECT) != 0) {
1468                 fprintf (stderr, "This dialog only works with the '%s' service\n",
1469                          NM_DBUS_SERVICE_OPENCONNECT);
1470                 return 1;
1471         }
1472
1473         g_thread_init (NULL);
1474         gtk_init(0, NULL);
1475
1476         ui_data = init_ui_data(vpn_name);
1477         if (get_config(vpn_uuid, ui_data->vpninfo)) {
1478                 fprintf(stderr, "Failed to find VPN UUID %s in gconf\n", vpn_uuid);
1479                 return 1;
1480         }
1481         build_main_dialog(ui_data);
1482
1483         init_openssl_ui();
1484         openconnect_init_openssl();
1485
1486         if (get_gconf_autoconnect(gcl, config_path))
1487                 queue_connect_host(ui_data);
1488
1489         gtk_window_present(GTK_WINDOW(ui_data->dialog));
1490         gtk_main();
1491
1492         return ui_data->retval;
1493 }