/*
* Virt Viewer: A virtual machine console viewer
*
* Copyright (C) 2007-2012 Red Hat, Inc.
* Copyright (C) 2009-2012 Daniel P. Berrange
* Copyright (C) 2010 Marc-André Lureau
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Daniel P. Berrange <
[email protected]>
*/
#include <config.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <locale.h>
#include <glib/gprintf.h>
#include <glib/gi18n.h>
#include <libxml/xpath.h>
#include <libxml/uri.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "virt-gtk-compat.h"
#include "virt-viewer-app.h"
#include "virt-viewer-auth.h"
#include "virt-viewer-window.h"
#include "virt-viewer-session.h"
#ifdef HAVE_GTK_VNC
#include "virt-viewer-session-vnc.h"
#endif
#ifdef HAVE_SPICE_GTK
#include "virt-viewer-session-spice.h"
#endif
gboolean doDebug = FALSE;
/* Signal handlers for about dialog */
void virt_viewer_app_about_close(GtkWidget *dialog, VirtViewerApp *self);
void virt_viewer_app_about_delete(GtkWidget *dialog, void *dummy, VirtViewerApp *self);
/* Internal methods */
static void virt_viewer_app_connected(VirtViewerSession *session,
VirtViewerApp *self);
static void virt_viewer_app_initialized(VirtViewerSession *session,
VirtViewerApp *self);
static void virt_viewer_app_disconnected(VirtViewerSession *session,
VirtViewerApp *self);
static void virt_viewer_app_auth_refused(VirtViewerSession *session,
const char *msg,
VirtViewerApp *self);
static void virt_viewer_app_auth_failed(VirtViewerSession *session,
const char *msg,
VirtViewerApp *self);
static void virt_viewer_app_usb_failed(VirtViewerSession *session,
const char *msg,
VirtViewerApp *self);
static void virt_viewer_app_server_cut_text(VirtViewerSession *session,
const gchar *text,
VirtViewerApp *self);
static void virt_viewer_app_bell(VirtViewerSession *session,
VirtViewerApp *self);
static void virt_viewer_app_cancelled(VirtViewerSession *session,
VirtViewerApp *self);
static void virt_viewer_app_channel_open(VirtViewerSession *session,
VirtViewerSessionChannel *channel,
VirtViewerApp *self);
static void virt_viewer_app_update_pretty_address(VirtViewerApp *self);
static void virt_viewer_app_set_fullscreen(VirtViewerApp *self, gboolean fullscreen);
static void virt_viewer_app_update_menu_displays(VirtViewerApp *self);
static void virt_viewer_update_smartcard_accels(VirtViewerApp *self);
struct _VirtViewerAppPrivate {
VirtViewerWindow *main_window;
GtkWidget *main_notebook;
GHashTable *windows;
GArray *initial_display_map;
gchar *clipboard;
gboolean direct;
gboolean verbose;
gboolean enable_accel;
gboolean authretry;
gboolean started;
gboolean fullscreen;
gboolean attach;
gboolean quitting;
gboolean kiosk;
VirtViewerSession *session;
gboolean active;
gboolean connected;
gboolean cancelled;
guint reconnect_poll; /* source id */
char *unixsock;
char *guri; /* prefered over ghost:gport */
char *ghost;
char *gport;
char *gtlsport;
char *host; /* ssh */
int port;/* ssh */
char *user; /* ssh */
char *transport;
char *pretty_address;
gchar *guest_name;
gboolean grabbed;
char *title;
gint focused;
GKeyFile *config;
gchar *config_file;
guint insert_smartcard_accel_key;
GdkModifierType insert_smartcard_accel_mods;
guint remove_smartcard_accel_key;
GdkModifierType remove_smartcard_accel_mods;
gboolean quit_on_disconnect;
};
G_DEFINE_ABSTRACT_TYPE(VirtViewerApp, virt_viewer_app, G_TYPE_OBJECT)
#define GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), VIRT_VIEWER_TYPE_APP, VirtViewerAppPrivate))
enum {
PROP_0,
PROP_VERBOSE,
PROP_SESSION,
PROP_GUEST_NAME,
PROP_GURI,
PROP_FULLSCREEN,
PROP_TITLE,
PROP_ENABLE_ACCEL,
PROP_HAS_FOCUS,
PROP_KIOSK,
PROP_QUIT_ON_DISCONNECT,
};
enum {
SIGNAL_WINDOW_ADDED,
SIGNAL_WINDOW_REMOVED,
SIGNAL_LAST,
};
static guint signals[SIGNAL_LAST];
void
virt_viewer_app_set_debug(gboolean debug)
{
#if GLIB_CHECK_VERSION(2, 31, 0)
if (debug) {
const gchar *doms = g_getenv("G_MESSAGES_DEBUG");
if (!doms) {
g_setenv("G_MESSAGES_DEBUG", G_LOG_DOMAIN, 1);
} else if (!g_str_equal(doms, "all") &&
!strstr(doms, G_LOG_DOMAIN)) {
gchar *newdoms = g_strdup_printf("%s %s", doms, G_LOG_DOMAIN);
g_setenv("G_MESSAGES_DEBUG", newdoms, 1);
g_free(newdoms);
}
}
#endif
doDebug = debug;
}
void
virt_viewer_app_simple_message_dialog(VirtViewerApp *self,
const char *fmt, ...)
{
g_return_if_fail(VIRT_VIEWER_IS_APP(self));
GtkWindow *window = GTK_WINDOW(virt_viewer_window_get_window(self->priv->main_window));
GtkWidget *dialog;
char *msg;
va_list vargs;
va_start(vargs, fmt);
msg = g_strdup_vprintf(fmt, vargs);
va_end(vargs);
dialog = gtk_message_dialog_new(window,
GTK_DIALOG_MODAL |
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"%s",
msg);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
g_free(msg);
}
static void
virt_viewer_app_save_config(VirtViewerApp *self)
{
VirtViewerAppPrivate *priv = self->priv;
GError *error = NULL;
gchar *dir, *data;
dir = g_path_get_dirname(priv->config_file);
if (g_mkdir_with_parents(dir, S_IRWXU) == -1)
g_warning("failed to create config directory");
g_free(dir);
if ((data = g_key_file_to_data(priv->config, NULL, &error)) == NULL ||
!g_file_set_contents(priv->config_file, data, -1, &error)) {
g_warning("Couldn't save configuration: %s", error->message);
g_clear_error(&error);
}
g_free(data);
}
static void
virt_viewer_app_quit(VirtViewerApp *self)
{
g_return_if_fail(VIRT_VIEWER_IS_APP(self));
VirtViewerAppPrivate *priv = self->priv;
if (self->priv->kiosk) {
g_warning("The app is in kiosk mode and can't quit");
return;
}
virt_viewer_app_save_config(self);
if (priv->session) {
virt_viewer_session_close(VIRT_VIEWER_SESSION(priv->session));
if (priv->connected) {
priv->quittin