diff options
Diffstat (limited to 'plugins/gvnc/gvnc_plugin.c')
-rw-r--r-- | plugins/gvnc/gvnc_plugin.c | 941 |
1 files changed, 941 insertions, 0 deletions
diff --git a/plugins/gvnc/gvnc_plugin.c b/plugins/gvnc/gvnc_plugin.c new file mode 100644 index 0000000..14cead9 --- /dev/null +++ b/plugins/gvnc/gvnc_plugin.c @@ -0,0 +1,941 @@ +/* + * Remmina - The GTK+ Remote Desktop Client + * Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. * If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. * If you + * do not wish to do so, delete this exception statement from your + * version. * If you delete this exception statement from all source + * files in the program, then also delete it here. + * + */ + +#include "gvnc_plugin_config.h" +#include "gvnc_plugin.h" + +#include <vncdisplay.h> +#include <vncutil.h> +#include <vncaudiopulse.h> + +#define GVNC_DEFAULT_PORT 5900 + +#ifndef VNC_CHECK_VERSION +# define VNC_CHECK_VERSION(a, b, c) 0 +#endif +#if VNC_CHECK_VERSION(1, 2, 0) +# define HAVE_VNC_REMOTE_RESIZE +# define HAVE_VNC_POWER_CONTROL +#endif + +enum { + GVNC_PLUGIN_FEATURE_PREF_VIEWONLY = 1, + GVNC_PLUGIN_FEATURE_DYNRESUPDATE, + GVNC_PLUGIN_FEATURE_PREF_DISABLECLIPBOARD, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF8, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11, + GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12, +#ifdef HAVE_VNC_POWER_CONTROL + GVNC_PLUGIN_FEATURE_TOOL_REBOOT, + GVNC_PLUGIN_FEATURE_TOOL_RESET, + GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN, +#endif + GVNC_PLUGIN_FEATURE_SCALE +}; + + +static RemminaPluginService *remmina_plugin_service = NULL; + +gchar* str_replace(const gchar *string, const gchar *search, const gchar *replacement) +{ + TRACE_CALL(__func__); + gchar *str, **arr; + + g_return_val_if_fail(string != NULL, NULL); + g_return_val_if_fail(search != NULL, NULL); + + if (replacement == NULL) + replacement = ""; + + arr = g_strsplit(string, search, -1); + if (arr != NULL && arr[0] != NULL) + str = g_strjoinv(replacement, arr); + else + str = g_strdup(string); + + g_strfreev(arr); + return str; +} + +/* Send a keystroke to the plugin window */ +static void gvnc_plugin_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + if (gpdata->vnc) { + vnc_display_send_keys(VNC_DISPLAY(gpdata->vnc), + keystrokes, + keylen); + } +} + +#ifdef HAVE_VNC_POWER_CONTROL +static void gvnc_plugin_power_ctrl(RemminaProtocolWidget *gp, VncConnectionPowerAction action) +{ + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + VncConnection *conn = vnc_display_get_connection(VNC_DISPLAY(gpdata->vnc)); + + if (!vnc_connection_get_power_control(conn)) { + REMMINA_PLUGIN_DEBUG("Power control not available on this server"); + return; + } + vnc_connection_power_control(conn, action); +} +#endif + + +static void gvnc_plugin_mouse_grab(GtkWidget *vncdisplay, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + VncGrabSequence *seq = vnc_display_get_grab_keys(VNC_DISPLAY(gpdata->vnc)); + gchar *seqstr = vnc_grab_sequence_as_string(seq); + + REMMINA_PLUGIN_DEBUG("Pointer grabbed: %s", seqstr); +} + +static void gvnc_plugin_mouse_ungrab(GtkWidget *vncdisplay, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + REMMINA_PLUGIN_DEBUG("Pointer ungrabbed"); +} + +static void gvnc_plugin_on_vnc_error(GtkWidget *vncdisplay G_GNUC_UNUSED, const gchar *msg, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + REMMINA_PLUGIN_DEBUG("Error: %s\n", msg); + /* "vnc-error" is always followed by "vnc-disconnected", + * so save the error for that signal */ + g_free(gpdata->error_msg); + gpdata->error_msg = g_strdup(msg); +} + +static gboolean gvnc_plugin_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) __attribute__ ((unused)); +static gboolean gvnc_plugin_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) +{ + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + //gsize szmem; + const VncPixelFormat *currentFormat; + //GError *err = NULL; + + if (!gpdata) + return FALSE; + + /* Get current pixel format for server */ + currentFormat = vnc_connection_get_pixel_format(gpdata->conn); + + + GdkPixbuf *pix = vnc_display_get_pixbuf(VNC_DISPLAY(gpdata->vnc)); + + rpsd->width = gdk_pixbuf_get_width(pix); + rpsd->height = gdk_pixbuf_get_height(pix); + rpsd->bitsPerPixel = currentFormat->bits_per_pixel; + rpsd->bytesPerPixel = rpsd->bitsPerPixel / 8; + //szmem = gdk_pixbuf_get_byte_length(pix); + + //szmem = rpsd->width * rpsd->height * rpsd->bytesPerPixel; + + //REMMINA_PLUGIN_DEBUG("allocating %zu bytes for a full screenshot", szmem); + //REMMINA_PLUGIN_DEBUG("Calculated screenshot size: %zu", szcalc); + //rpsd->buffer = malloc(szmem); + + //memcpy(rpsd->buffer, pix, szmem); + //gdk_pixbuf_save_to_buffer(pix, &rpsd->buffer, &szmem, "jpeg", &err, "quality", "100", NULL); + + + /* Returning TRUE instruct also the caller to deallocate rpsd->buffer */ + return TRUE; +} + +void gvnc_plugin_paste_text(RemminaProtocolWidget *gp, const gchar *text) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + gchar *out; + gsize a, b; + GError *error = NULL; + + if (!gpdata) return; + + out = g_convert_with_fallback(text, -1, "iso8859-1//TRANSLIT", "utf-8", NULL, &a, &b, &error); + if (out) { + REMMINA_PLUGIN_DEBUG("Pasting text"); + vnc_display_client_cut_text(VNC_DISPLAY(gpdata->vnc), out); + g_free(out); + } else { + REMMINA_PLUGIN_DEBUG("Error pasting text: %s", error->message); + g_error_free(error); + } +} + +static void gvnc_plugin_clipboard_cb(GtkClipboard *cb, GdkEvent *event, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + gchar *text; + + REMMINA_PLUGIN_DEBUG("owner-change event received"); + + if (cb && gtk_clipboard_get_owner(cb) == (GObject *)gp) + return; + + text = gtk_clipboard_wait_for_text(cb); + if (!text) + return; + + gvnc_plugin_paste_text(gp, text); + g_free(text); +} + + +/* text was actually requested */ +static void gvnc_plugin_clipboard_copy(GtkClipboard *clipboard G_GNUC_UNUSED, GtkSelectionData *data, guint info G_GNUC_UNUSED, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + if (!gpdata) return; + gtk_selection_data_set_text(data, gpdata->clipstr, -1); + REMMINA_PLUGIN_DEBUG("Text copied"); +} + +static void gvnc_plugin_cut_text(VncDisplay *vnc G_GNUC_UNUSED, const gchar *text, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + REMMINA_PLUGIN_DEBUG("Got clipboard request for ā%sā", text); + + GtkClipboard *cb; + gsize a, b; + GtkTargetEntry targets[] = { + { g_strdup("UTF8_STRING"), 0, 0 }, + { g_strdup("COMPOUND_TEXT"), 0, 0 }, + { g_strdup("TEXT"), 0, 0 }, + { g_strdup("STRING"), 0, 0 }, + }; + + if (!text) + return; + g_free(gpdata->clipstr); + gpdata->clipstr = g_convert(text, -1, "utf-8", "iso8859-1", &a, &b, NULL); + + if (gpdata->clipstr) { + cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + + REMMINA_PLUGIN_DEBUG("setting clipboard with owner to owner %p", gp); + gtk_clipboard_set_with_owner(cb, + targets, + G_N_ELEMENTS(targets), + (GtkClipboardGetFunc)gvnc_plugin_clipboard_copy, + NULL, + G_OBJECT(gp)); + } + + //g_signal_emit_by_name(session, "session-cut-text", text); +} + + +static void gvnc_plugin_desktop_resize(GtkWidget *vncdisplay G_GNUC_UNUSED, int width, int height, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + REMMINA_PLUGIN_DEBUG("Remote desktop size changed to %dx%d\n", width, height); + REMMINA_PLUGIN_DEBUG("Desktop size is set to %dx%d\n", gpdata->width, gpdata->height); + remmina_plugin_service->protocol_plugin_set_width(gp, width); + remmina_plugin_service->protocol_plugin_set_height(gp, height); + + remmina_plugin_service->protocol_plugin_desktop_resize(gp); +} + +static void gvnc_plugin_on_bell(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + REMMINA_PLUGIN_DEBUG("Bell message received"); + //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + if (remmina_plugin_service->file_get_int(remminafile, "disableserverbell", FALSE)) + return; + GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(gp)); + + if (window) + gdk_window_beep(window); + REMMINA_PLUGIN_DEBUG("Beep emitted"); +} +static void gvnc_plugin_update_scale_mode(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + RemminaScaleMode scaleMode = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp); + +#if 0 + g_object_set(gpdata->display, + "scaling", (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED), + "resize-guest", (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES), + NULL); +#endif + if (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED || scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES) + vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), TRUE); + else + vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), FALSE); + + //width = remmina_plugin_service->protocol_plugin_get_width(gp); + //height = remmina_plugin_service->protocol_plugin_get_height(gp); + gpdata->width = vnc_display_get_width(VNC_DISPLAY(gpdata->vnc)); + gpdata->height = vnc_display_get_height(VNC_DISPLAY(gpdata->vnc)); + + if (scaleMode != REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE) { + /* In scaled mode, the VncDisplay will get its dimensions from its parent */ + gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), -1, -1); + } else { + /* In non scaled mode, the plugins forces dimensions of the VncDisplay */ +#if 0 + g_object_get(gpdata->display_channel, + "width", &width, + "height", &height, + NULL); + gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), width, height); +#endif + gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), gpdata->width, gpdata->height); + } + remmina_plugin_service->protocol_plugin_update_align(gp); +} + +static gboolean gvnc_plugin_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) +{ + TRACE_CALL(__func__); + + return TRUE; +} + +static void gvnc_plugin_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature) +{ + TRACE_CALL(__func__); + + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + guint keys[3]; + + switch (feature->id) { + case GVNC_PLUGIN_FEATURE_PREF_VIEWONLY: + gpdata->viewonly = remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE); + break; + case GVNC_PLUGIN_FEATURE_DYNRESUPDATE: + case GVNC_PLUGIN_FEATURE_SCALE: + gvnc_plugin_update_scale_mode(gp); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_Delete; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_BackSpace; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F1; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F2; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F3; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F4; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F5; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F6; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F7; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F9; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F10; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F11; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12: + keys[0] = GDK_KEY_Control_L; + keys[1] = GDK_KEY_Alt_L; + keys[2] = GDK_KEY_F12; + gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys)); + break; +#ifdef HAVE_VNC_POWER_CONTROL + case GVNC_PLUGIN_FEATURE_TOOL_REBOOT: + gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_REBOOT); + break; + case GVNC_PLUGIN_FEATURE_TOOL_RESET: + gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_RESET); + break; + case GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN: + gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_SHUTDOWN); + break; +#endif + default: + break; + } +} + +static void gvnc_plugin_auth_unsupported(VncDisplay *vnc G_GNUC_UNUSED, unsigned int authType, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + g_clear_pointer(&gpdata->error_msg, g_free); + gchar *msg = g_strdup_printf(_("Unsupported authentication type %u"), authType); + + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", msg); + g_free(msg); +} + +static void gvnc_plugin_auth_failure(VncDisplay *vnc G_GNUC_UNUSED, const gchar *reason, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + g_clear_pointer(&gpdata->error_msg, g_free); + gchar *msg = g_strdup_printf(_("Authentication failure: %s"), reason); + + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", msg); + g_free(msg); +} + +static gboolean gvnc_plugin_ask_auth(GtkWidget *vncdisplay, GValueArray *credList, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + + gint ret; + gboolean disablepasswordstoring; + gchar *s_username = NULL, *s_password = NULL; + gboolean wantPassword = FALSE, wantUsername = FALSE; + int i; + gboolean save; + + + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + + GArray *credArray = g_array_sized_new(FALSE, TRUE, sizeof(GValue), credList->n_values); + g_array_set_clear_func(credArray, (GDestroyNotify) g_value_unset); + g_array_append_vals(credArray, credList->values, credList->n_values); + + REMMINA_PLUGIN_DEBUG("Got credential request for %d credential(s)\n", credArray->len); + + for (i = 0 ; i < credArray->len ; i++) { + GValue *cred = &g_array_index(credArray, GValue, i); + switch (g_value_get_enum(cred)) { + case VNC_DISPLAY_CREDENTIAL_USERNAME: + wantUsername = TRUE; + break; + case VNC_DISPLAY_CREDENTIAL_PASSWORD: + wantPassword = TRUE; + break; + case VNC_DISPLAY_CREDENTIAL_CLIENTNAME: + break; + default: + REMMINA_PLUGIN_DEBUG("Unsupported credential type %d", g_value_get_enum(cred)); + vnc_display_close(VNC_DISPLAY(gpdata->vnc)); + goto cleanup; + } + } + disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE); + + ret = remmina_plugin_service->protocol_plugin_init_auth(gp, + (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD) + | (wantUsername ? REMMINA_MESSAGE_PANEL_FLAG_USERNAME : 0), + _("Enter VNC authentication credentials"), + (wantUsername ? remmina_plugin_service->file_get_string(remminafile, "username") : NULL), + (wantPassword ? remmina_plugin_service->file_get_string(remminafile, "password") : NULL), + NULL, + NULL); + if (ret == GTK_RESPONSE_OK) { + s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp); + s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp); + remmina_plugin_service->file_set_string(remminafile, "username", s_username); + + save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp); + if (save) { + // User has requested to save credentials. We put the password + // into remminafile->settings. It will be saved later, on successful connection, by + // rcw.c + remmina_plugin_service->file_set_string(remminafile, "password", s_password); + } else { + remmina_plugin_service->file_set_string(remminafile, "password", NULL); + } + + for (i = 0 ; i < credArray->len ; i++) { + GValue *cred = &g_array_index(credArray, GValue, i); + switch (g_value_get_enum(cred)) { + case VNC_DISPLAY_CREDENTIAL_USERNAME: + if (!s_username || + vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc), + g_value_get_enum(cred), + s_username)) { + g_debug("Failed to set credential type %d", g_value_get_enum(cred)); + vnc_display_close(VNC_DISPLAY(gpdata->vnc)); + } + break; + case VNC_DISPLAY_CREDENTIAL_PASSWORD: + if (!s_password || + vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc), + g_value_get_enum(cred), + s_password)) { + g_debug("Failed to set credential type %d", g_value_get_enum(cred)); + vnc_display_close(VNC_DISPLAY(gpdata->vnc)); + } + break; + case VNC_DISPLAY_CREDENTIAL_CLIENTNAME: + if (vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc), + g_value_get_enum(cred), + "remmina")) { + g_debug("Failed to set credential type %d", g_value_get_enum(cred)); + vnc_display_close(VNC_DISPLAY(gpdata->vnc)); + } + break; + default: + g_debug("Unsupported credential type %d", g_value_get_enum(cred)); + vnc_display_close(VNC_DISPLAY(gpdata->vnc)); + } + } + + if (s_username) g_free(s_username); + if (s_password) g_free(s_password); + + return TRUE; + } else { + return FALSE; + } + +cleanup: + g_free(s_username); + g_free(s_password); + + //g_object_set(gpdata->session, "password", s_password, NULL); + return TRUE; +} + +static void gvnc_plugin_initialized(GtkWidget *vncdisplay, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + REMMINA_PLUGIN_DEBUG("Connection initialized"); + g_return_if_fail(gpdata != NULL); + REMMINA_PLUGIN_DEBUG("Presenting the window"); + + VncAudioFormat format = { + VNC_AUDIO_FORMAT_RAW_S32, + 2, + 44100, + }; + + REMMINA_PLUGIN_DEBUG("Gathering the VNC connection object"); + gpdata->conn = vnc_display_get_connection(VNC_DISPLAY(gpdata->vnc)); + g_return_if_fail(gpdata->conn != NULL); + + if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE)) { + REMMINA_PLUGIN_DEBUG("Setting up VNC audio channel"); + if (vnc_connection_set_audio_format(gpdata->conn, &format)) + REMMINA_PLUGIN_DEBUG("VNC audio format set"); + else { + REMMINA_PLUGIN_DEBUG("VNC audio format returned an error"); + return; + } + + if (vnc_connection_set_audio(gpdata->conn, VNC_AUDIO(gpdata->pa))) + REMMINA_PLUGIN_DEBUG("VNC audio channel has been set"); + else { + REMMINA_PLUGIN_DEBUG("VNC audio channel cannot be set"); + return; + } + REMMINA_PLUGIN_DEBUG("Enabling audio"); + if (vnc_connection_audio_enable(gpdata->conn)) + REMMINA_PLUGIN_DEBUG("Audio enabled"); + else + REMMINA_PLUGIN_DEBUG("Audio cannot be enabled"); + } + gpdata->width = vnc_display_get_width(VNC_DISPLAY(gpdata->vnc)); + gpdata->width = vnc_display_get_height(VNC_DISPLAY(gpdata->vnc)); +} + +static void gvnc_plugin_disconnected(VncDisplay *vnc G_GNUC_UNUSED, RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + REMMINA_PLUGIN_DEBUG("[%s] Plugin disconnected", PLUGIN_NAME); +} +static gboolean gvnc_plugin_close_connection(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + + REMMINA_PLUGIN_DEBUG("[%s] Plugin close connection", PLUGIN_NAME); + + if (gpdata) { + if (gpdata->error_msg) g_free(gpdata->error_msg); + if (gpdata->vnc) + vnc_display_close(VNC_DISPLAY(gpdata->vnc)); + //g_object_unref(gpdata->vnc); + } + + /* Remove instance->context from gp object data to avoid double free */ + g_object_steal_data(G_OBJECT(gp), "plugin-data"); + remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); + return FALSE; +} + +static void gvnc_plugin_init(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + GVncPluginData *gpdata; + //VncGrabSequence *seq; + + GtkClipboard *cb; + + gpdata = g_new0(GVncPluginData, 1); + g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free); + + gpdata->pa = NULL; + + REMMINA_PLUGIN_DEBUG("[%s] Plugin init", PLUGIN_NAME); + + RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + if (remmina_plugin_service->file_get_int(remminafile, "gvncdebug", FALSE)) + vnc_util_set_debug(TRUE); + + gpdata->vnc = vnc_display_new(); + if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE)) + gpdata->pa = vnc_audio_pulse_new(); + + + g_signal_connect(gpdata->vnc, "vnc-auth-credential", + G_CALLBACK(gvnc_plugin_ask_auth), gp); + g_signal_connect(gpdata->vnc, "vnc-auth-failure", + G_CALLBACK(gvnc_plugin_auth_failure), gp); + g_signal_connect(gpdata->vnc, "vnc-auth-unsupported", + G_CALLBACK(gvnc_plugin_auth_unsupported), gp); + g_signal_connect(gpdata->vnc, "vnc-disconnected", + G_CALLBACK(gvnc_plugin_disconnected), gp); + g_signal_connect(gpdata->vnc, "vnc-initialized", + G_CALLBACK(gvnc_plugin_initialized), gp); + g_signal_connect(gpdata->vnc, "vnc-desktop-resize", + G_CALLBACK(gvnc_plugin_desktop_resize), gp); + g_signal_connect(gpdata->vnc, "vnc-bell", + G_CALLBACK(gvnc_plugin_on_bell), gp); + g_signal_connect(gpdata->vnc, "vnc-error", + G_CALLBACK(gvnc_plugin_on_vnc_error), gp); + g_signal_connect(gpdata->vnc, "vnc-pointer-grab", + G_CALLBACK(gvnc_plugin_mouse_grab), gp); + g_signal_connect(gpdata->vnc, "vnc-pointer-ungrab", + G_CALLBACK(gvnc_plugin_mouse_ungrab), gp); + g_signal_connect(gpdata->vnc, "vnc-server-cut-text", + G_CALLBACK(gvnc_plugin_cut_text), gp); + //seq = vnc_grab_sequence_new_from_string ("Control_R"); + //vnc_display_set_grab_keys(VNC_DISPLAY(gpdata->vnc), seq); + //vnc_grab_sequence_free(seq); + + /* Setup the clipboard */ + cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gpdata->signal_clipboard = g_signal_connect(cb, + "owner-change", + G_CALLBACK(gvnc_plugin_clipboard_cb), + gp); +} + +static gboolean gvnc_plugin_open_connection(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + + gint port; + gchar *host = NULL, *tunnel = NULL; + GVncPluginData *gpdata = GET_PLUGIN_DATA(gp); + RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + REMMINA_PLUGIN_DEBUG("[%s] Plugin open connection", PLUGIN_NAME); + + gpdata->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(gp), gpdata->box); + gtk_widget_set_hexpand(GTK_WIDGET(gpdata->vnc), TRUE); + gtk_widget_set_vexpand(GTK_WIDGET(gpdata->vnc), TRUE); + gtk_container_add(GTK_CONTAINER(gpdata->box), gpdata->vnc); + + remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->vnc); + + + const gchar *address = remmina_plugin_service->file_get_string(remminafile, "server"); + if(strstr(g_strdup(address), "unix:///") != NULL) { + REMMINA_PLUGIN_DEBUG("address contain unix:// -> %s", address); + gchar *val = str_replace (address, "unix://", ""); + REMMINA_PLUGIN_DEBUG("address after cleaning = %s", val); + gint fd = remmina_plugin_service->open_unix_sock(val); + REMMINA_PLUGIN_DEBUG("Unix socket fd: %d", fd); + gpdata->fd = fd; + g_free(val); + + } else { + /* Setup SSH tunnel if needed */ + tunnel = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, GVNC_DEFAULT_PORT, FALSE); + if (!tunnel) + return FALSE; + remmina_plugin_service->get_server_port(tunnel, + GVNC_DEFAULT_PORT, + &host, + &port); + } + + + + gpdata->depth_profile = remmina_plugin_service->file_get_int(remminafile, "depth_profile", 24); + gpdata->viewonly = remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE); + vnc_display_set_depth(VNC_DISPLAY(gpdata->vnc), gpdata->depth_profile); + if (gpdata->fd > 0) + vnc_display_open_fd(VNC_DISPLAY(gpdata->vnc), gpdata->fd); + else + vnc_display_open_host(VNC_DISPLAY(gpdata->vnc), host, g_strdup_printf("%d", port)); + gpdata->lossy_encoding = remmina_plugin_service->file_get_int(remminafile, "lossy_encoding", FALSE); + vnc_display_set_lossy_encoding(VNC_DISPLAY(gpdata->vnc), gpdata->lossy_encoding); + vnc_display_set_shared_flag(VNC_DISPLAY(gpdata->vnc), gpdata->shared); + + if(host) g_free(host); + if(tunnel) g_free(tunnel); + + /* TRUE Conflict with remmina? */ + vnc_display_set_keyboard_grab(VNC_DISPLAY(gpdata->vnc), FALSE); + vnc_display_set_read_only(VNC_DISPLAY(gpdata->vnc), gpdata->viewonly); + /* TRUE Conflict with remmina? */ + vnc_display_set_pointer_grab(VNC_DISPLAY(gpdata->vnc), FALSE); + vnc_display_set_pointer_local(VNC_DISPLAY(gpdata->vnc), TRUE); + + vnc_display_set_force_size(VNC_DISPLAY(gpdata->vnc), FALSE); + vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), TRUE); +#ifdef HAVE_VNC_REMOTE_RESIZE + vnc_display_set_allow_resize(VNC_DISPLAY(gpdata->vnc), TRUE); + vnc_display_set_zoom_level(VNC_DISPLAY(gpdata->vnc), opt_zoom); +#endif + remmina_plugin_service->protocol_plugin_signal_connection_opened(gp); + gtk_widget_show_all(gpdata->box); + return TRUE; +} + +/* Array of key/value pairs for color depths */ +static gpointer colordepth_list[] = +{ + "0", N_("Use server settings"), + "1", N_("True colour (24 bits)"), + "2", N_("High colour (16 bits)"), + "3", N_("Low colour (8 bits)"), + "4", N_("Ultra low colour (3 bits)"), + NULL +}; + +/* Array of RemminaProtocolSetting for basic settings. + * Each item is composed by: + * a) RemminaProtocolSettingType for setting type + * b) Setting name + * c) Setting description + * d) Compact disposition + * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO + * f) Setting tooltip + * g) Validation data pointer, will be passed to the validation callback method. + * h) Validation callback method (Can be NULL. Every entry will be valid then.) + * use following prototype: + * gboolean mysetting_validator_method(gpointer key, gpointer value, + * gpointer validator_data); + * gpointer key is a gchar* containing the setting's name, + * gpointer value contains the value which should be validated, + * gpointer validator_data contains your passed data. + */ +static const RemminaProtocolSetting gvnc_plugin_basic_settings[] = +{ + { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("VNC password"), FALSE, NULL, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "depth_profile", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "lossy_encoding", N_("Use JPEG Compression"), TRUE, NULL, N_("This might not work on all VNC servers"), NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "gvncdebug", N_("Enable GTK-VNC debug"), FALSE, NULL, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL } +}; + +/* Array of RemminaProtocolSetting for advanced settings. + * Each item is composed by: + * a) RemminaProtocolSettingType for setting type + * b) Setting name + * c) Setting description + * d) Compact disposition + * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO + * f) Setting Tooltip + */ +static const RemminaProtocolSetting gvnc_plugin_advanced_settings[] = +{ + { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("No clipboard sync"), TRUE, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), FALSE, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverbell", N_("Ignore remote bell messages"), TRUE, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enableaudio", N_("Enable audio channel"), FALSE, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly", N_("View only"), TRUE, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shared", N_("Shared connection"), TRUE, NULL, N_("If the server should try to share the desktop by leaving other clients connected") }, + { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, TRUE, NULL, NULL } +}; + +/* Array for available features. + * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */ +static const RemminaProtocolFeature gvnc_plugin_features[] = +{ + { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, GVNC_PLUGIN_FEATURE_PREF_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly", N_("View only") }, + { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, GVNC_PLUGIN_FEATURE_PREF_DISABLECLIPBOARD, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableclipboard", N_("No clipboard sync") }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+_Del"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE, N_("Send Ctrl+Alt+_Backspace"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1, N_("Send Ctrl+Alt+_F1"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2, N_("Send Ctrl+Alt+_F2"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3, N_("Send Ctrl+Alt+_F3"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4, N_("Send Ctrl+Alt+_F4"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5, N_("Send Ctrl+Alt+_F5"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6, N_("Send Ctrl+Alt+_F6"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7, N_("Send Ctrl+Alt+_F7"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF8, N_("Send Ctrl+Alt+_F8"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9, N_("Send Ctrl+Alt+_F9"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10, N_("Send Ctrl+Alt+_F10"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11, N_("Send Ctrl+Alt+_F11"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12, N_("Send Ctrl+Alt+_F12"), NULL, NULL }, +#ifdef HAVE_VNC_POWER_CONTROL + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_REBOOT, N_("Reboot remote host"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_RESET, N_("Reset remote host (hard reboot)"), NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN, N_("Shutdown remote host"), NULL, NULL }, +#endif + { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, GVNC_PLUGIN_FEATURE_DYNRESUPDATE, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, GVNC_PLUGIN_FEATURE_SCALE, NULL, NULL, NULL }, + { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL } +}; + +/* Protocol plugin definition and features */ +static RemminaProtocolPlugin remmina_plugin = { + REMMINA_PLUGIN_TYPE_PROTOCOL, // Type + PLUGIN_NAME, // Name + PLUGIN_DESCRIPTION, // Description + GETTEXT_PACKAGE, // Translation domain + PLUGIN_VERSION, // Version number + PLUGIN_APPICON, // Icon for normal connection + PLUGIN_APPICON, // Icon for SSH connection + gvnc_plugin_basic_settings, // Array for basic settings + gvnc_plugin_advanced_settings, // Array for advanced settings + REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type + gvnc_plugin_features, // Array for available features + gvnc_plugin_init, // Plugin initialization + gvnc_plugin_open_connection, // Plugin open connection + gvnc_plugin_close_connection, // Plugin close connection + gvnc_plugin_query_feature, // Query for available features + gvnc_plugin_call_feature, // Call a feature + gvnc_plugin_keystroke, // Send a keystroke + NULL, // No screenshot support available + //gvnc_plugin_get_screenshot, // No screenshot support available + NULL, // RCW map event + NULL // RCW unmap event +}; + +G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service) +{ + TRACE_CALL(__func__); + remmina_plugin_service = service; + + bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + + if (!service->register_plugin((RemminaPlugin *)&remmina_plugin)) + return FALSE; + + return TRUE; +} |