added autogen script to run autotools to generate configure and necessary make inputs
[ovirt-viewer.git] / main.c
1 /* ovirt viewer console application
2  * Copyright (C) 2008 Red Hat Inc.
3  * Written by Richard W.M. Jones <rjones@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28 #include <vncdisplay.h>
29
30 #ifdef HAVE_NETDB_H
31 #include <netdb.h>
32 #endif
33
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37
38 #ifdef HAVE_SYS_SOCKET_H
39 #include <sys/socket.h>
40 #endif
41
42 #ifdef HAVE_SYS_UN_H
43 #include <sys/un.h>
44 #endif
45
46 #ifdef HAVE_WINDOWS_H
47 #include <windows.h>
48 #endif
49
50 #include "internal.h"
51
52 /*#define HTTPS "https"*/
53 #define HTTPS "http"
54
55 gboolean debug = 0;
56
57 /* Usually /etc/pki/tls/certs/ca-bundle.crt unless overridden during
58  * configure or on the command line.
59  */
60 const char *cainfo = CAINFO;
61 gboolean check_cert = TRUE;
62
63 /* Private functions. */
64 static void start_ui (void);
65 static GtkWidget *menu_item_new (int which_menu);
66 static void connect_to_wui (GtkWidget *, gpointer);
67 static void login_to_wui (GtkWidget *, gpointer);
68 static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data);
69 static void destroy (GtkWidget *widget, gpointer data);
70 static void clear_connectmenu (void);
71 static void help_about (GtkWidget *menu);
72 static void viewer_shutdown (GtkWidget *src, void *dummy, GtkWidget *vnc);
73 #if 0
74 static void viewer_quit (GtkWidget *src, GtkWidget *vnc);
75 #endif
76 static void viewer_connected (GtkWidget *vnc);
77 static void viewer_initialized (GtkWidget *vnc, GtkWidget *data);
78 static void viewer_disconnected (GtkWidget *vnc);
79 static void viewer_credential (GtkWidget *vnc, GValueArray *credList);
80 static int viewer_open_vnc_socket (const char *vnchost, int vncport);
81
82 /* For any widgets accessed from multiple functions. */
83 static GtkWidget *window;
84 static GtkWidget *connectitem;
85 static GtkWidget *connectmenu;
86 static GtkWidget *no_connections;
87 static GtkWidget *refresh_vmlist;
88 static GtkWidget *refresh_vmlist_separator;
89 static GtkWidget *connection_area;
90 static GtkWidget *ca_hostname;
91 static GtkWidget *ca_button;
92 static GtkWidget *ca_error;
93 static GtkWidget *login_area;
94 static GtkWidget *la_username;
95 static GtkWidget *la_password;
96 static GtkWidget *la_button;
97 static GtkWidget *notebook;
98 static GtkWidget *statusbar;
99 static guint statusbar_ctx;
100 static GdkCursor *busy_cursor;
101
102 /* Menus. */
103 enum menuNums {
104   CONNECT_MENU,
105   VIEW_MENU,
106   SEND_KEY_MENU,
107   WINDOW_MENU,
108   HELP_MENU,
109 };
110
111 struct menuItem {
112   guint menu;
113   GtkWidget *label;
114   const char *ungrabbed_text;
115   const char *grabbed_text;
116 };
117
118 static struct menuItem menuItems[] = {
119   { CONNECT_MENU, NULL, "_Connect", "Connect" },
120   { VIEW_MENU, NULL, "_View", "View" },
121   { SEND_KEY_MENU, NULL, "_Send Key", "Send Key" },
122   { WINDOW_MENU, NULL, "_Window", "Window" },
123   { HELP_MENU, NULL, "_Help", "Help" }
124 };
125
126 /* Window title. */
127 static const char *title = "oVirt Viewer";
128
129 /* Gtk widget styles.  Avoid installation hassles by keeping this
130  * inside the binary.  It can still be overridden by the user (who
131  * will do that?)
132  */
133 static const char *styles =
134   "style \"ovirt-viewer-yellow-box\"\n"
135   "{\n"
136   "  bg[NORMAL] = shade (1.5, \"yellow\")\n"
137   "}\n"
138   "widget \"*.ovirt-viewer-connection-area\" style \"ovirt-viewer-yellow-box\"\n"
139   ;
140
141 /* Command-line arguments. */
142 static int print_version = 0;
143
144 static const char *help_msg =
145   "Use '" PACKAGE " --help' to see a list of available command line options";
146
147 static const GOptionEntry options[] = {
148   { "cainfo", 0, 0, G_OPTION_ARG_STRING, &cainfo,
149     "set the path of the CA certificate bundle", NULL },
150   { "check-certificate", 0, 0, G_OPTION_ARG_NONE, &check_cert,
151     "if --no-check-certificate is passed we don't check the SSL certificate of the server", NULL },
152   { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
153     "turn on debugging messages", NULL },
154   { "version", 'V', 0, G_OPTION_ARG_NONE, &print_version,
155     "display version and exit", NULL },
156   { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
157 };
158
159 int
160 main (int argc, char *argv[])
161 {
162   GOptionContext *context;
163   GError *error = NULL;
164
165   /* Initialize GLib threads before anything else.
166    *
167    * There is one main thread, which is used for all Gtk interactions
168    * and to keep the UI responsive, and one WUI thread.  The WUI
169    * thread is used to connect to the WUI, log in, and maintain the list
170    * of virtual machines.  The WUI thread is the only thread allowed
171    * to use the CURL library.
172    *
173    * The main thread sends instructions to the WUI thread (like "connect",
174    * "disconnect", etc.) using a simple message-passing protocol and
175    * a GAsyncQueue.
176    *
177    * The WUI thread keeps the UI updated by adding idle events which are
178    * processed in the main thread - see:
179    * http://mail.gnome.org/archives/gtk-app-devel-list/2007-March/msg00232.html
180    *
181    * Note that under Win32 you must confine all Gtk/Gdk interactions
182    * to a single thread - see:
183    * http://developer.gimp.org/api/2.0/gdk/gdk-Threads.html
184    */
185   if (!g_thread_supported ()) {
186     g_thread_init (NULL);
187 #ifndef WIN32
188     gdk_threads_init ();
189 #endif
190   } else {
191     fprintf (stderr, "GLib threads not supported or not working.");
192     exit (1);
193   }
194
195   gtk_init (&argc, &argv);
196
197   /* Parse command-line options. */
198   context = g_option_context_new ("oVirt viewer");
199   g_option_context_add_main_entries (context, options, NULL);
200   g_option_context_add_group (context, gtk_get_option_group (TRUE));
201   g_option_context_add_group (context, vnc_display_get_option_group ());
202   g_option_context_parse (context, &argc, &argv, &error);
203
204   if (error) {
205     g_print ("%s\n%s\n", error->message, help_msg);
206     g_error_free (error);
207     exit (1);
208   }
209
210   if (print_version) {
211     printf ("%s %s\n", PACKAGE, VERSION);
212     exit (0);
213   }
214
215   start_wui_thread ();
216   start_ui ();
217
218   DEBUG ("entering the Gtk main loop");
219
220   gtk_main ();
221
222   stop_wui_thread ();
223
224   exit (0);
225 }
226
227 /* Create the viewer window, menus. */
228 static void
229 start_ui (void)
230 {
231   GtkWidget *vbox;
232   GtkWidget *menubar;
233   GtkWidget *view;
234   GtkWidget *viewmenu;
235   GtkWidget *sendkey;
236   GtkWidget *sendkeymenu;
237   GtkWidget *wind;
238   GtkWidget *windmenu;
239   GtkWidget *help;
240   GtkWidget *helpmenu;
241   GtkWidget *about;
242   GtkWidget *ca_vbox;
243   GtkWidget *ca_hbox;
244   GtkWidget *ca_label;
245   GtkWidget *la_hbox;
246
247   DEBUG ("creating viewer windows and menus");
248
249   /* Parse styles. */
250   gtk_rc_parse_string (styles);
251
252   /* Busy cursor, used by main_busy() function.
253    * XXX This cursor is crap - how can we use the Bluecurve/theme cursor?
254    */
255   busy_cursor = gdk_cursor_new (GDK_WATCH);
256
257   /* Window. */
258   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
259   gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
260   gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
261   gtk_window_set_title (GTK_WINDOW (window), title);
262
263   g_signal_connect (G_OBJECT (window), "delete_event",
264                     G_CALLBACK (delete_event), NULL);
265   g_signal_connect (G_OBJECT (window), "destroy",
266                     G_CALLBACK (destroy), NULL);
267
268   /* VBox for layout within the window. */
269   vbox = gtk_vbox_new (FALSE, 0);
270
271   /* Menubar. */
272   menubar = gtk_menu_bar_new ();
273   connectitem = menu_item_new (CONNECT_MENU);
274   connectmenu = gtk_menu_new ();
275   gtk_menu_item_set_submenu (GTK_MENU_ITEM (connectitem), connectmenu);
276
277   no_connections = gtk_menu_item_new_with_label ("Not connected");
278   gtk_menu_append (GTK_MENU (connectmenu), no_connections);
279   gtk_widget_set_sensitive (no_connections, FALSE);
280
281   /* This is not added to the menu yet, but will be when we are
282    * connected.
283    */
284   refresh_vmlist =
285     gtk_menu_item_new_with_label ("Refresh list of virtual machines");
286   g_object_ref (refresh_vmlist);
287   refresh_vmlist_separator = gtk_separator_menu_item_new ();
288   g_object_ref (refresh_vmlist_separator);
289
290 #if 0
291   screenshot = gtk_menu_item_new_with_mnemonic ("_Screenshot");
292   gtk_menu_append (GTK_MENU (filemenu), screenshot);
293   g_signal_connect (screenshot, "activate",
294                     GTK_SIGNAL_FUNC (take_screenshot), NULL);
295 #endif
296
297   view = menu_item_new (VIEW_MENU);
298   viewmenu = gtk_menu_new ();
299   gtk_menu_item_set_submenu (GTK_MENU_ITEM (view), viewmenu);
300
301   sendkey = menu_item_new (SEND_KEY_MENU);
302   sendkeymenu = gtk_menu_new ();
303   gtk_menu_item_set_submenu (GTK_MENU_ITEM (sendkey), sendkeymenu);
304
305   wind = menu_item_new (WINDOW_MENU);
306   windmenu = gtk_menu_new ();
307   gtk_menu_item_set_submenu (GTK_MENU_ITEM (wind), windmenu);
308
309   help = menu_item_new (HELP_MENU);
310   helpmenu = gtk_menu_new ();
311   gtk_menu_item_set_submenu (GTK_MENU_ITEM (help), helpmenu);
312
313   about = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
314   gtk_menu_append(GTK_MENU(helpmenu), about);
315   g_signal_connect(about, "activate", GTK_SIGNAL_FUNC (help_about), NULL);
316
317   gtk_menu_bar_append (GTK_MENU_BAR (menubar), connectitem);
318   gtk_menu_bar_append (GTK_MENU_BAR (menubar), view);
319   gtk_menu_bar_append (GTK_MENU_BAR (menubar), sendkey);
320   gtk_menu_bar_append (GTK_MENU_BAR (menubar), wind);
321   gtk_menu_bar_append (GTK_MENU_BAR (menubar), help);
322
323   /* For login dialogs, etc., usually invisible. */
324   connection_area = gtk_event_box_new ();
325   ca_vbox = gtk_vbox_new (FALSE, 0);
326   ca_label = gtk_label_new ("Give the name of the oVirt management server:");
327   ca_hbox = gtk_hbox_new (FALSE, 0);
328   ca_hostname = gtk_entry_new ();
329   gtk_entry_set_width_chars (GTK_ENTRY (ca_hostname), 24);
330   ca_button = gtk_button_new_with_label ("Connect");
331   ca_error = gtk_label_new (NULL);
332   gtk_box_pack_start (GTK_BOX (ca_hbox), ca_hostname, FALSE, FALSE, 0);
333   gtk_box_pack_start (GTK_BOX (ca_hbox), ca_button,   FALSE, FALSE, 0);
334   gtk_box_pack_start (GTK_BOX (ca_vbox), ca_label,    FALSE, FALSE, 4);
335   gtk_box_pack_start (GTK_BOX (ca_vbox), ca_hbox,     TRUE,  FALSE, 4);
336   gtk_box_pack_start (GTK_BOX (ca_vbox), ca_error,    TRUE,  FALSE, 4);
337   gtk_container_add (GTK_CONTAINER (connection_area), ca_vbox);
338
339   gtk_widget_set_name (connection_area, "ovirt-viewer-connection-area");
340
341   g_signal_connect (G_OBJECT (ca_button), "clicked",
342                     G_CALLBACK (connect_to_wui), NULL);
343
344   login_area = gtk_event_box_new ();
345   la_hbox = gtk_hbox_new (FALSE, 0);
346   la_username = gtk_entry_new ();
347   gtk_entry_set_width_chars (GTK_ENTRY (la_username), 12);
348   la_password = gtk_entry_new ();
349   gtk_entry_set_width_chars (GTK_ENTRY (la_password), 12);
350   gtk_entry_set_visibility (GTK_ENTRY (la_password), FALSE);
351   la_button = gtk_button_new_with_label ("Login");
352   gtk_container_add (GTK_CONTAINER (la_hbox), la_username);
353   gtk_container_add (GTK_CONTAINER (la_hbox), la_password);
354   gtk_container_add (GTK_CONTAINER (la_hbox), la_button);
355   gtk_container_add (GTK_CONTAINER (login_area), la_hbox);
356
357   gtk_widget_set_name (login_area, "ovirt-viewer-login-area");
358
359   g_signal_connect (G_OBJECT (la_button), "clicked",
360                     G_CALLBACK (login_to_wui), NULL);
361
362   /* Tabbed notebook. */
363   notebook = gtk_notebook_new ();
364   /*gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);*/
365   gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), TRUE);
366   gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
367
368   /* Status bar. */
369   statusbar = gtk_statusbar_new ();
370   statusbar_ctx = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar),
371                                                 "context");
372   gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, "");
373
374   /* Packing. */
375   gtk_container_add (GTK_CONTAINER (window), vbox);
376   gtk_container_add_with_properties (GTK_CONTAINER (vbox), menubar,
377                                      "expand", FALSE, NULL);
378   gtk_container_add_with_properties (GTK_CONTAINER (vbox), connection_area,
379                                      "expand", FALSE, "fill", TRUE, NULL);
380   gtk_container_add_with_properties (GTK_CONTAINER (vbox), login_area,
381                                      "expand", FALSE, "fill", TRUE, NULL);
382   gtk_container_add_with_properties (GTK_CONTAINER (vbox), notebook,
383                                      "expand", TRUE, NULL);
384   gtk_container_add_with_properties (GTK_CONTAINER (vbox), statusbar,
385                                      "expand", FALSE, NULL);
386
387   /* Show widgets. */
388   gtk_widget_show_all (window);
389
390   if (wui_thread_is_connected ())
391     gtk_widget_hide (connection_area);
392   if (!wui_thread_is_connected () || wui_thread_is_logged_in ())
393     gtk_widget_hide (login_area);
394 }
395
396 static GtkWidget *
397 menu_item_new (int which_menu)
398 {
399   GtkWidget *widget;
400   GtkWidget *label;
401   const char *text;
402
403   text = menuItems[which_menu].ungrabbed_text;
404
405   widget = gtk_menu_item_new ();
406   label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
407   gtk_label_set_text_with_mnemonic (GTK_LABEL (label), text);
408   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
409
410   gtk_container_add (GTK_CONTAINER (widget), label);
411   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), widget);
412   gtk_widget_show (label);
413
414   menuItems[which_menu].label = label;
415
416   return widget;
417 }
418
419 static gboolean
420 delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
421 {
422   DEBUG ("delete_event");
423   return FALSE;
424 }
425
426 static void
427 destroy (GtkWidget *widget, gpointer data)
428 {
429   DEBUG ("destroy");
430   gtk_main_quit ();
431 }
432
433 static void
434 help_about (GtkWidget *menu)
435 {
436   GtkWidget *about;
437   const char *authors[] = {
438     "Richard W.M. Jones <rjones@redhat.com>",
439     "Daniel P. Berrange <berrange@redhat.com>",
440     NULL
441   };
442
443   about = gtk_about_dialog_new();
444
445   gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), "oVirt Viewer");
446   gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
447   gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://ovirt.org/");
448   gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(about), "oVirt website");
449   gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
450   gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about), 
451  "This program is free software; you can redistribute it and/or modify\n" \
452  "it under the terms of the GNU General Public License as published by\n" \
453  "the Free Software Foundation; either version 2 of the License, or\n" \
454  "(at your option) any later version.\n" \
455  "\n" \
456  "This program is distributed in the hope that it will be useful,\n" \
457  "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
458  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" \
459  "GNU General Public License for more details.\n" \
460  "\n" \
461  "You should have received a copy of the GNU General Public License\n" \
462  "along with this program; if not, write to the Free Software\n" \
463  "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n");
464
465   gtk_dialog_run(GTK_DIALOG(about));
466   gtk_widget_destroy(about);
467 }
468
469 static void
470 connect_to_wui (GtkWidget *widget, gpointer data)
471 {
472   const char *hostname;
473   char *uri;
474   int len;
475
476   hostname = gtk_entry_get_text (GTK_ENTRY (ca_hostname));
477   if (STREQ (hostname, "")) return;
478
479   /* https:// + hostname + /ovirt + \0 */
480   len = 8 + strlen (hostname) + 6 + 1;
481   uri = g_alloca (len);
482   snprintf (uri, len, HTTPS "://%s/ovirt", hostname);
483
484   wui_thread_send_connect (uri);
485 }
486
487 static void
488 login_to_wui (GtkWidget *widget, gpointer data)
489 {
490   const char *username, *password;
491
492   username = gtk_entry_get_text (GTK_ENTRY (la_username));
493   if (STREQ (username, "")) return;
494   password = gtk_entry_get_text (GTK_ENTRY (la_password));
495
496   wui_thread_send_login (username, password);
497 }
498
499 /* Connect to a virtual machine.  This callback is called from the
500  * connect menu.  It searches the notebook of gtk-vnc widgets to see
501  * if we have already connected to this machine, and if not it
502  * makes a new connection.
503  */
504 static void
505 connect_to_vm (GtkWidget *widget, gpointer _vm)
506 {
507   struct vm *vm = (struct vm *) _vm;
508   int n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
509   int i, uuidlen, len, fd;
510   GtkWidget *child;
511   const char *label;
512   const char* hostname;
513   char *label2;
514
515   DEBUG ("searching tabs for uuid %s", vm->uuid);
516
517   uuidlen = strlen (vm->uuid);
518
519   /* Search the tabs for this UUID, and if found, switch to it and return. */
520   for (i = 0; i < n; ++i) {
521     child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
522     label = gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (notebook), child);
523     len = strlen (label);
524     if (len >= uuidlen &&
525         STREQ (label + len - uuidlen, vm->uuid)) {
526       DEBUG ("found on tab %d", i);
527       gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
528       return;
529     }
530   }
531
532   DEBUG ("not found, creating new tab");
533
534   /* This VM isn't in the notebook already, so create a new console. */
535   hostname = gtk_entry_get_text (GTK_ENTRY (ca_hostname));
536   fd = viewer_open_vnc_socket(hostname, vm->forward_vnc_port);
537   if (fd == -1) return;         /* We've already given an error. */
538
539   child = vnc_display_new ();
540   if (! vnc_display_open_fd (VNC_DISPLAY (child), fd)) {
541     main_status_error (g_strdup ("internal error in Gtk-VNC widget"));
542     return;
543   }
544
545   /*
546   gtk_signal_connect(GTK_OBJECT(child), "vnc-pointer-grab",
547                      GTK_SIGNAL_FUNC(viewer_grab), window);
548   gtk_signal_connect(GTK_OBJECT(child), "vnc-pointer-ungrab",
549                      GTK_SIGNAL_FUNC(viewer_ungrab), window);
550   */
551
552   gtk_signal_connect(GTK_OBJECT(child), "delete-event",
553                      GTK_SIGNAL_FUNC(viewer_shutdown), child);
554
555   gtk_signal_connect(GTK_OBJECT(child), "vnc-connected",
556                      GTK_SIGNAL_FUNC(viewer_connected), NULL);
557   gtk_signal_connect(GTK_OBJECT(child), "vnc-initialized",
558                      GTK_SIGNAL_FUNC(viewer_initialized), NULL);
559   gtk_signal_connect(GTK_OBJECT(child), "vnc-disconnected",
560                      GTK_SIGNAL_FUNC(viewer_disconnected), NULL);
561
562   g_signal_connect(GTK_OBJECT(child), "vnc-auth-credential",
563                    GTK_SIGNAL_FUNC(viewer_credential), NULL);
564
565   /* NB. We have to do this before adding it to the notebook. */
566   gtk_widget_show (child);
567
568   /* Choose a tab label, which MUST end with the uuid string, since
569    * we use the tab label to store uuid.
570    */
571   len = strlen (vm->description) + 1 + strlen (vm->uuid) + 1;
572   label2 = g_alloca (len);
573   snprintf (label2, len, "%s %s", vm->description, vm->uuid);
574
575   i = gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child, NULL);
576   gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (notebook), child, label2);
577   gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
578
579   DEBUG ("finished creating new tab");
580 }
581
582 /*
583 static void viewer_grab(GtkWidget *vnc, GtkWidget *window)
584 {
585         int i;
586
587         viewer_set_title(VNC_DISPLAY(vnc), window, TRUE);
588
589         for (i = 0 ; i < LAST_MENU; i++) {
590                 gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].grabbed_text);
591         }
592 }
593
594 static void viewer_ungrab(GtkWidget *vnc, GtkWidget *window)
595 {
596         int i;
597
598         viewer_set_title(VNC_DISPLAY(vnc), window, FALSE);
599
600         for (i = 0 ; i < LAST_MENU; i++) {
601                 gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].ungrabbed_text);
602         }
603 }
604 */
605
606 static void
607 viewer_shutdown (GtkWidget *src, void *dummy, GtkWidget *vnc)
608 {
609   vnc_display_close (VNC_DISPLAY(vnc));
610
611   /* Just close the notebook tab for now. XXX */
612   gtk_notebook_remove_page (GTK_NOTEBOOK (notebook),
613                             gtk_notebook_page_num (GTK_NOTEBOOK (notebook),
614                                                    vnc));
615 }
616
617 #if 0
618 static void
619 viewer_quit (GtkWidget *src, GtkWidget *vnc)
620 {
621   viewer_shutdown (src, NULL, vnc);
622 }
623 #endif
624
625 static void
626 viewer_connected (GtkWidget *vnc)
627 {
628   DEBUG ("Connected to server");
629 }
630
631 static void
632 viewer_initialized (GtkWidget *vnc, GtkWidget *data)
633 {
634   DEBUG ("Connection initialized");
635 }
636
637 static void
638 viewer_disconnected (GtkWidget *vnc)
639 {
640   DEBUG ("Disconnected from server");
641 }
642
643 static void
644 viewer_credential (GtkWidget *vnc, GValueArray *credList)
645 {
646         GtkWidget *dialog = NULL;
647         int response;
648         unsigned int i, prompt = 0;
649         const char **data;
650
651         DEBUG ("Got credential request for %d credential(s)",
652                credList->n_values);
653
654         data = g_new0(const char *, credList->n_values);
655
656         for (i = 0 ; i < credList->n_values ; i++) {
657                 GValue *cred = g_value_array_get_nth(credList, i);
658                 switch (g_value_get_enum(cred)) {
659                 case VNC_DISPLAY_CREDENTIAL_USERNAME:
660                 case VNC_DISPLAY_CREDENTIAL_PASSWORD:
661                         prompt++;
662                         break;
663                 case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
664                         data[i] = "libvirt";
665                 default:
666                         break;
667                 }
668         }
669
670         if (prompt) {
671                 GtkWidget **label, **entry, *box, *vbox;
672                 int row;
673                 dialog = gtk_dialog_new_with_buttons("Authentication required",
674                                                      NULL,
675                                                      0,
676                                                      GTK_STOCK_CANCEL,
677                                                      GTK_RESPONSE_CANCEL,
678                                                      GTK_STOCK_OK,
679                                                      GTK_RESPONSE_OK,
680                                                      NULL);
681                 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
682
683                 box = gtk_table_new(credList->n_values, 2, FALSE);
684                 label = g_new(GtkWidget *, prompt);
685                 entry = g_new(GtkWidget *, prompt);
686
687                 for (i = 0, row =0 ; i < credList->n_values ; i++) {
688                         GValue *cred = g_value_array_get_nth(credList, i);
689                         switch (g_value_get_enum(cred)) {
690                         case VNC_DISPLAY_CREDENTIAL_USERNAME:
691                                 label[row] = gtk_label_new("Username:");
692                                 break;
693                         case VNC_DISPLAY_CREDENTIAL_PASSWORD:
694                                 label[row] = gtk_label_new("Password:");
695                                 break;
696                         default:
697                                 continue;
698                         }
699                         entry[row] = gtk_entry_new();
700                         if (g_value_get_enum(cred) == VNC_DISPLAY_CREDENTIAL_PASSWORD)
701                                 gtk_entry_set_visibility(GTK_ENTRY(entry[row]), FALSE);
702
703                         gtk_table_attach(GTK_TABLE(box), label[i], 0, 1, row, row+1, GTK_SHRINK, GTK_SHRINK, 3, 3);
704                         gtk_table_attach(GTK_TABLE(box), entry[i], 1, 2, row, row+1, GTK_SHRINK, GTK_SHRINK, 3, 3);
705                         row++;
706                 }
707
708                 vbox = gtk_bin_get_child(GTK_BIN(dialog));
709                 gtk_container_add(GTK_CONTAINER(vbox), box);
710
711                 gtk_widget_show_all(dialog);
712                 response = gtk_dialog_run(GTK_DIALOG(dialog));
713                 gtk_widget_hide(GTK_WIDGET(dialog));
714
715                 if (response == GTK_RESPONSE_OK) {
716                         for (i = 0, row = 0 ; i < credList->n_values ; i++) {
717                                 GValue *cred = g_value_array_get_nth(credList, i);
718                                 switch (g_value_get_enum(cred)) {
719                                 case VNC_DISPLAY_CREDENTIAL_USERNAME:
720                                 case VNC_DISPLAY_CREDENTIAL_PASSWORD:
721                                         data[i] = gtk_entry_get_text(GTK_ENTRY(entry[row]));
722                                         break;
723                                 }
724                         }
725                 }
726         }
727
728         for (i = 0 ; i < credList->n_values ; i++) {
729                 GValue *cred = g_value_array_get_nth(credList, i);
730                 if (data[i]) {
731                         if (vnc_display_set_credential(VNC_DISPLAY(vnc),
732                                                        g_value_get_enum(cred),
733                                                        data[i])) {
734                                 DEBUG("Failed to set credential type %d",
735                                       g_value_get_enum(cred));
736                                 vnc_display_close(VNC_DISPLAY(vnc));
737                         }
738                 } else {
739                         DEBUG("Unsupported credential type %d",
740                               g_value_get_enum(cred));
741                         vnc_display_close(VNC_DISPLAY(vnc));
742                 }
743         }
744
745         g_free(data);
746         if (dialog)
747                 gtk_widget_destroy(GTK_WIDGET(dialog));
748 }
749
750 #if defined(HAVE_SOCKET) && defined(HAVE_CONNECT) && defined(HAVE_HTONS) && defined(HAVE_GETHOSTBYNAME)
751
752 static int 
753 viewer_open_vnc_socket(const char* vnchost, int vncport)
754 {
755   int socketfd;
756   struct hostent *serv;
757   struct sockaddr_in serv_addr;
758
759   socketfd = socket(PF_INET, SOCK_STREAM, 0);
760   if(socketfd < 0){
761       return -1;
762   }
763
764   serv = gethostbyname(vnchost);
765   if(serv == NULL){
766       return -1;
767   }
768
769   serv_addr.sin_family = PF_INET;
770   serv_addr.sin_port = htons(vncport);
771   serv_addr.sin_addr.s_addr = ((struct in_addr *)(serv->h_addr))->s_addr; 
772
773   if (connect(socketfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0){
774       return -1;
775   }
776
777   return socketfd;
778 }
779
780 #endif /* defined(HAVE_SOCKET) && defined(HAVE_CONNECT) && defined(HAVE_HTONS) && defined(HAVE_GETHOSTBYNAME) */
781
782 /* Remove all menu items from the Connect menu. */
783 static void
784 remove_menu_item (GtkWidget *menu_item, gpointer data)
785 {
786   gtk_container_remove (GTK_CONTAINER (connectmenu), menu_item);
787 }
788
789 static void
790 clear_connectmenu (void)
791 {
792   DEBUG ("clear Connect menu");
793   gtk_container_foreach (GTK_CONTAINER (connectmenu), remove_menu_item, NULL);
794 }
795
796 /* The WUI thread has changed its state to connected. */
797 gboolean
798 main_connected (gpointer data)
799 {
800   DEBUG ("connected");
801   ASSERT_IS_MAIN_THREAD ();
802
803   gtk_label_set_text (GTK_LABEL (ca_error), NULL);
804
805   gtk_widget_hide (connection_area);
806   if (!wui_thread_is_logged_in ())
807     gtk_widget_show (login_area);
808   return FALSE;
809 }
810
811 /* The WUI thread has changed its state to disconnected. */
812 gboolean
813 main_disconnected (gpointer data)
814 {
815   DEBUG ("disconnected");
816   ASSERT_IS_MAIN_THREAD ();
817
818   gtk_widget_show (connection_area);
819   gtk_widget_hide (login_area);
820
821   clear_connectmenu ();
822   gtk_menu_append (GTK_MENU (connectmenu), no_connections);
823   gtk_widget_show_all (connectmenu);
824
825   return FALSE;
826 }
827
828 /* The WUI thread has changed its state to logged in. */
829 gboolean
830 main_logged_in (gpointer data)
831 {
832   DEBUG ("logged in");
833   ASSERT_IS_MAIN_THREAD ();
834
835   gtk_widget_hide (login_area);
836   return FALSE;
837 }
838
839 /* The WUI thread has changed its state to logged out. */
840 gboolean
841 main_logged_out (gpointer data)
842 {
843   DEBUG ("logged out");
844   ASSERT_IS_MAIN_THREAD ();
845
846   if (wui_thread_is_connected ())
847     gtk_widget_show (login_area);
848   return FALSE;
849 }
850
851 /* The WUI thread has changed its state to busy. */
852 gboolean
853 main_busy (gpointer data)
854 {
855   GdkWindow *gdk_window;
856
857   DEBUG ("busy");
858   ASSERT_IS_MAIN_THREAD ();
859
860   gdk_window = gtk_widget_get_window (window);
861   if (gdk_window) {
862     gdk_window_set_cursor (gdk_window, busy_cursor);
863     gdk_flush ();
864   }
865
866   return FALSE;
867 }
868
869 /* The WUI thread has changed its state to idle. */
870 gboolean
871 main_idle (gpointer data)
872 {
873   GdkWindow *gdk_window;
874
875   DEBUG ("idle");
876   ASSERT_IS_MAIN_THREAD ();
877
878   gdk_window = gtk_widget_get_window (window);
879   if (gdk_window) {
880     gdk_window_set_cursor (gdk_window, NULL);
881     gdk_flush ();
882   }
883
884   return FALSE;
885 }
886
887 /* The WUI thread had a connection error.  This function must
888  * free the string.
889  */
890 gboolean
891 main_connection_error (gpointer _str)
892 {
893   char *str = (char *) _str;
894
895   DEBUG ("connection error: %s", str);
896   ASSERT_IS_MAIN_THREAD ();
897
898   gtk_label_set_text (GTK_LABEL (ca_error), str);
899   g_free (str);
900
901   return FALSE;
902 }
903
904 /* The WUI thread had a login error.  This function must
905  * free the string.
906  */
907 gboolean
908 main_login_error (gpointer _str)
909 {
910   char *str = (char *) _str;
911
912   DEBUG ("login error: %s", str);
913   ASSERT_IS_MAIN_THREAD ();
914
915   /*
916   gtk_label_set_text (GTK_LABEL (la_error), str);
917   */
918   g_free (str);
919
920   return FALSE;
921 }
922
923 /* The WUI thread reports a general status error.  This function
924  * must free the string.
925  */
926 gboolean
927 main_status_error (gpointer _str)
928 {
929   char *str = (char *) _str;
930
931   DEBUG ("status error: %s", str);
932   ASSERT_IS_MAIN_THREAD ();
933
934   gtk_statusbar_pop (GTK_STATUSBAR (statusbar), statusbar_ctx);
935   gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, str);
936   g_free (str);
937
938   return FALSE;
939 }
940
941 /* The WUI thread has updated the vm list.  Here in the main thread
942  * we keep our own copy of the vmlist.
943  */
944 static GSList *vmlist = NULL;
945
946 static void add_vm_to_connectmenu (gpointer _vm, gpointer data);
947
948 gboolean
949 main_vmlist_updated (gpointer data)
950 {
951   GSList *new_vmlist;
952
953   DEBUG ("vmlist updated");
954   ASSERT_IS_MAIN_THREAD ();
955
956   /* Get the new vmlist. */
957   if (wui_thread_get_vmlist (&new_vmlist)) {
958     /* Free the previous vmlist.  This invalidates all the vm pointers
959      * contained in the Connect menu callbacks, but we're going to
960      * delete those callbacks and create news ones in a moment anyway ...
961      */
962     free_vmlist (vmlist);
963
964     vmlist = new_vmlist;
965
966     clear_connectmenu ();
967
968     gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist);
969
970     if (vmlist != NULL) {
971       gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist_separator);
972       g_slist_foreach (vmlist, add_vm_to_connectmenu, NULL);
973     }
974
975     /* Grrrr Gtk is stupid. */
976     gtk_widget_show_all (connectmenu);
977   }
978
979   return FALSE;
980 }
981
982 static void
983 add_vm_to_connectmenu (gpointer _vm, gpointer data)
984 {
985   struct vm *vm = (struct vm *) _vm;
986   GtkWidget *item;
987
988   DEBUG ("adding %s to Connect menu", vm->description);
989
990   item = gtk_menu_item_new_with_label (vm->description);
991   gtk_menu_append (GTK_MENU (connectmenu), item);
992
993   g_signal_connect (G_OBJECT (item), "activate",
994                     G_CALLBACK (connect_to_vm), vm);
995 }