1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2016-2021 Antenore Gatta, Giovanni Panozzo
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., 51 Franklin Street, Fifth Floor,
18  * Boston, MA  02110-1301, USA.
19  *
20  *  In addition, as a special exception, the copyright holders give
21  *  permission to link the code of portions of this program with the
22  *  OpenSSL library under certain conditions as described in each
23  *  individual source file, and distribute linked combinations
24  *  including the two.
25  *  You must obey the GNU General Public License in all respects
26  *  for all of the code used other than OpenSSL. *  If you modify
27  *  file(s) with this exception, you may extend this exception to your
28  *  version of the file(s), but you are not obligated to do so. *  If you
29  *  do not wish to do so, delete this exception statement from your
30  *  version. *  If you delete this exception statement from all source
31  *  files in the program, then also delete it here.
32  *
33  */
34 
35 #include "gvnc_plugin_config.h"
36 #include "gvnc_plugin.h"
37 
38 #include <vncdisplay.h>
39 #include <vncutil.h>
40 #include <vncaudiopulse.h>
41 
42 #define GVNC_DEFAULT_PORT 5900
43 
44 #ifndef VNC_CHECK_VERSION
45 # define VNC_CHECK_VERSION(a, b, c) 0
46 #endif
47 #if VNC_CHECK_VERSION(1, 2, 0)
48 # define HAVE_VNC_REMOTE_RESIZE
49 # define HAVE_VNC_POWER_CONTROL
50 #endif
51 
52 enum {
53 	GVNC_PLUGIN_FEATURE_PREF_VIEWONLY = 1,
54 	GVNC_PLUGIN_FEATURE_DYNRESUPDATE,
55 	GVNC_PLUGIN_FEATURE_PREF_DISABLECLIPBOARD,
56 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL,
57 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE,
58 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1,
59 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2,
60 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3,
61 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4,
62 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5,
63 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6,
64 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7,
65 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF8,
66 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9,
67 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10,
68 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11,
69 	GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12,
70 #ifdef HAVE_VNC_POWER_CONTROL
71 	GVNC_PLUGIN_FEATURE_TOOL_REBOOT,
72 	GVNC_PLUGIN_FEATURE_TOOL_RESET,
73 	GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN,
74 #endif
75 	GVNC_PLUGIN_FEATURE_SCALE
76 };
77 
78 
79 static RemminaPluginService *remmina_plugin_service = NULL;
80 #define REMMINA_PLUGIN_DEBUG(fmt, ...) remmina_plugin_service->_remmina_debug(__func__, fmt, ## __VA_ARGS__)
81 
str_replace(const gchar * string,const gchar * search,const gchar * replacement)82 gchar* str_replace(const gchar *string, const gchar *search, const gchar *replacement)
83 {
84 	TRACE_CALL(__func__);
85 	gchar *str, **arr;
86 
87 	g_return_val_if_fail(string != NULL, NULL);
88 	g_return_val_if_fail(search != NULL, NULL);
89 
90 	if (replacement == NULL)
91 		replacement = "";
92 
93 	arr = g_strsplit(string, search, -1);
94 	if (arr != NULL && arr[0] != NULL)
95 		str = g_strjoinv(replacement, arr);
96 	else
97 		str = g_strdup(string);
98 
99 	g_strfreev(arr);
100 	return str;
101 }
102 
103 /* Send a keystroke to the plugin window */
gvnc_plugin_keystroke(RemminaProtocolWidget * gp,const guint keystrokes[],const gint keylen)104 static void gvnc_plugin_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
105 {
106 	TRACE_CALL(__func__);
107 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
108 
109 	if (gpdata->vnc) {
110 		vnc_display_send_keys(VNC_DISPLAY(gpdata->vnc),
111 				      keystrokes,
112 				      keylen);
113 	}
114 }
115 
116 #ifdef HAVE_VNC_POWER_CONTROL
gvnc_plugin_power_ctrl(RemminaProtocolWidget * gp,VncConnectionPowerAction action)117 static void gvnc_plugin_power_ctrl(RemminaProtocolWidget *gp, VncConnectionPowerAction action)
118 {
119 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
120 	VncConnection *conn = vnc_display_get_connection(VNC_DISPLAY(gpdata->vnc));
121 
122 	if (!vnc_connection_get_power_control(conn)) {
123 		REMMINA_PLUGIN_DEBUG("Power control not available on this server");
124 		return;
125 	}
126 	vnc_connection_power_control(conn, action);
127 }
128 #endif
129 
130 
gvnc_plugin_mouse_grab(GtkWidget * vncdisplay,RemminaProtocolWidget * gp)131 static void gvnc_plugin_mouse_grab(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
132 {
133 	TRACE_CALL(__func__);
134 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
135 	VncGrabSequence *seq = vnc_display_get_grab_keys(VNC_DISPLAY(gpdata->vnc));
136 	gchar *seqstr = vnc_grab_sequence_as_string(seq);
137 
138 	REMMINA_PLUGIN_DEBUG("Pointer grabbed: %s", seqstr);
139 }
140 
gvnc_plugin_mouse_ungrab(GtkWidget * vncdisplay,RemminaProtocolWidget * gp)141 static void gvnc_plugin_mouse_ungrab(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
142 {
143 	TRACE_CALL(__func__);
144 	REMMINA_PLUGIN_DEBUG("Pointer ungrabbed");
145 }
146 
gvnc_plugin_on_vnc_error(GtkWidget * vncdisplay G_GNUC_UNUSED,const gchar * msg,RemminaProtocolWidget * gp)147 static void gvnc_plugin_on_vnc_error(GtkWidget *vncdisplay G_GNUC_UNUSED, const gchar *msg, RemminaProtocolWidget *gp)
148 {
149 	TRACE_CALL(__func__);
150 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
151 
152 	REMMINA_PLUGIN_DEBUG("Error: %s\n", msg);
153 	/* "vnc-error" is always followed by "vnc-disconnected",
154 	 * so save the error for that signal */
155 	g_free(gpdata->error_msg);
156 	gpdata->error_msg = g_strdup(msg);
157 }
158 
159 static gboolean gvnc_plugin_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) __attribute__ ((unused));
gvnc_plugin_get_screenshot(RemminaProtocolWidget * gp,RemminaPluginScreenshotData * rpsd)160 static gboolean gvnc_plugin_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
161 {
162 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
163 	//gsize szmem;
164 	const VncPixelFormat *currentFormat;
165 	//GError *err = NULL;
166 
167 	if (!gpdata)
168 		return FALSE;
169 
170 	/* Get current pixel format for server */
171 	currentFormat = vnc_connection_get_pixel_format(gpdata->conn);
172 
173 
174 	GdkPixbuf *pix = vnc_display_get_pixbuf(VNC_DISPLAY(gpdata->vnc));
175 
176 	rpsd->width = gdk_pixbuf_get_width(pix);
177 	rpsd->height = gdk_pixbuf_get_height(pix);
178 	rpsd->bitsPerPixel = currentFormat->bits_per_pixel;
179 	rpsd->bytesPerPixel = rpsd->bitsPerPixel / 8;
180 	//szmem = gdk_pixbuf_get_byte_length(pix);
181 
182 	//szmem = rpsd->width * rpsd->height * rpsd->bytesPerPixel;
183 
184 	//REMMINA_PLUGIN_DEBUG("allocating %zu bytes for a full screenshot", szmem);
185 	//REMMINA_PLUGIN_DEBUG("Calculated screenshot size: %zu", szcalc);
186 	//rpsd->buffer = malloc(szmem);
187 
188 	//memcpy(rpsd->buffer, pix, szmem);
189 	//gdk_pixbuf_save_to_buffer(pix, &rpsd->buffer, &szmem, "jpeg", &err, "quality", "100", NULL);
190 
191 
192 	/* Returning TRUE instruct also the caller to deallocate rpsd->buffer */
193 	return TRUE;
194 }
195 
gvnc_plugin_paste_text(RemminaProtocolWidget * gp,const gchar * text)196 void gvnc_plugin_paste_text(RemminaProtocolWidget *gp, const gchar *text)
197 {
198 	TRACE_CALL(__func__);
199 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
200 	gchar *out;
201 	gsize a, b;
202 	GError *error = NULL;
203 
204 	if (!gpdata) return;
205 
206 	out = g_convert_with_fallback(text, -1, "iso8859-1//TRANSLIT", "utf-8", NULL, &a, &b, &error);
207 	if (out) {
208 		REMMINA_PLUGIN_DEBUG("Pasting text");
209 		vnc_display_client_cut_text(VNC_DISPLAY(gpdata->vnc), out);
210 		g_free(out);
211 	} else {
212 		REMMINA_PLUGIN_DEBUG("Error pasting text: %s", error->message);
213 		g_error_free(error);
214 	}
215 }
216 
gvnc_plugin_clipboard_cb(GtkClipboard * cb,GdkEvent * event,RemminaProtocolWidget * gp)217 static void gvnc_plugin_clipboard_cb(GtkClipboard *cb, GdkEvent *event, RemminaProtocolWidget *gp)
218 {
219 	TRACE_CALL(__func__);
220 	//GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
221 	gchar *text;
222 
223 	REMMINA_PLUGIN_DEBUG("owner-change event received");
224 
225 	if (cb && gtk_clipboard_get_owner(cb) == (GObject *)gp)
226 		return;
227 
228 	text = gtk_clipboard_wait_for_text(cb);
229 	if (!text)
230 		return;
231 
232 	gvnc_plugin_paste_text(gp, text);
233 	g_free(text);
234 }
235 
236 
237 /* text was actually requested */
gvnc_plugin_clipboard_copy(GtkClipboard * clipboard G_GNUC_UNUSED,GtkSelectionData * data,guint info G_GNUC_UNUSED,RemminaProtocolWidget * gp)238 static void gvnc_plugin_clipboard_copy(GtkClipboard *clipboard G_GNUC_UNUSED, GtkSelectionData *data, guint info G_GNUC_UNUSED, RemminaProtocolWidget *gp)
239 {
240 	TRACE_CALL(__func__);
241 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
242 
243 	if (!gpdata) return;
244 	gtk_selection_data_set_text(data, gpdata->clipstr, -1);
245 	REMMINA_PLUGIN_DEBUG("Text copied");
246 }
247 
gvnc_plugin_cut_text(VncDisplay * vnc G_GNUC_UNUSED,const gchar * text,RemminaProtocolWidget * gp)248 static void gvnc_plugin_cut_text(VncDisplay *vnc G_GNUC_UNUSED, const gchar *text, RemminaProtocolWidget *gp)
249 {
250 	TRACE_CALL(__func__);
251 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
252 
253 	REMMINA_PLUGIN_DEBUG("Got clipboard request for \"%s\"", text);
254 
255 	GtkClipboard *cb;
256 	gsize a, b;
257 	GtkTargetEntry targets[] = {
258 		{ g_strdup("UTF8_STRING"),   0, 0 },
259 		{ g_strdup("COMPOUND_TEXT"), 0, 0 },
260 		{ g_strdup("TEXT"),	     0, 0 },
261 		{ g_strdup("STRING"),	     0, 0 },
262 	};
263 
264 	if (!text)
265 		return;
266 	g_free(gpdata->clipstr);
267 	gpdata->clipstr = g_convert(text, -1, "utf-8", "iso8859-1", &a, &b, NULL);
268 
269 	if (gpdata->clipstr) {
270 		cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
271 
272 		REMMINA_PLUGIN_DEBUG("setting clipboard with owner to owner %p", gp);
273 		gtk_clipboard_set_with_owner(cb,
274 					     targets,
275 					     G_N_ELEMENTS(targets),
276 					     (GtkClipboardGetFunc)gvnc_plugin_clipboard_copy,
277 					     NULL,
278 					     G_OBJECT(gp));
279 	}
280 
281 	//g_signal_emit_by_name(session, "session-cut-text", text);
282 }
283 
284 
gvnc_plugin_desktop_resize(GtkWidget * vncdisplay G_GNUC_UNUSED,int width,int height,RemminaProtocolWidget * gp)285 static void gvnc_plugin_desktop_resize(GtkWidget *vncdisplay G_GNUC_UNUSED, int width, int height, RemminaProtocolWidget *gp)
286 {
287 	TRACE_CALL(__func__);
288 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
289 
290 	REMMINA_PLUGIN_DEBUG("Remote desktop size changed to %dx%d\n", width, height);
291 	REMMINA_PLUGIN_DEBUG("Desktop size is set to %dx%d\n", gpdata->width, gpdata->height);
292 	remmina_plugin_service->protocol_plugin_set_width(gp, width);
293 	remmina_plugin_service->protocol_plugin_set_height(gp, height);
294 
295 	remmina_plugin_service->protocol_plugin_desktop_resize(gp);
296 }
297 
gvnc_plugin_on_bell(RemminaProtocolWidget * gp)298 static void gvnc_plugin_on_bell(RemminaProtocolWidget *gp)
299 {
300 	TRACE_CALL(__func__);
301 	REMMINA_PLUGIN_DEBUG("Bell message received");
302 	//GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
303 	RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
304 
305 	if (remmina_plugin_service->file_get_int(remminafile, "disableserverbell", FALSE))
306 		return;
307 	GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(gp));
308 
309 	if (window)
310 		gdk_window_beep(window);
311 	REMMINA_PLUGIN_DEBUG("Beep emitted");
312 }
gvnc_plugin_update_scale_mode(RemminaProtocolWidget * gp)313 static void gvnc_plugin_update_scale_mode(RemminaProtocolWidget *gp)
314 {
315 	TRACE_CALL(__func__);
316 
317 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
318 	RemminaScaleMode scaleMode = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
319 
320 #if 0
321 	g_object_set(gpdata->display,
322 		     "scaling", (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED),
323 		     "resize-guest", (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES),
324 		     NULL);
325 #endif
326 	if (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED || scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES)
327 		vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), TRUE);
328 	else
329 		vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), FALSE);
330 
331 	//width = remmina_plugin_service->protocol_plugin_get_width(gp);
332 	//height = remmina_plugin_service->protocol_plugin_get_height(gp);
333 	gpdata->width = vnc_display_get_width(VNC_DISPLAY(gpdata->vnc));
334 	gpdata->height = vnc_display_get_height(VNC_DISPLAY(gpdata->vnc));
335 
336 	if (scaleMode != REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE) {
337 		/* In scaled mode, the VncDisplay will get its dimensions from its parent */
338 		gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), -1, -1);
339 	} else {
340 		/* In non scaled mode, the plugins forces dimensions of the VncDisplay */
341 #if 0
342 		g_object_get(gpdata->display_channel,
343 			     "width", &width,
344 			     "height", &height,
345 			     NULL);
346 		gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), width, height);
347 #endif
348 		gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), gpdata->width, gpdata->height);
349 	}
350 	remmina_plugin_service->protocol_plugin_update_align(gp);
351 }
352 
gvnc_plugin_query_feature(RemminaProtocolWidget * gp,const RemminaProtocolFeature * feature)353 static gboolean gvnc_plugin_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
354 {
355 	TRACE_CALL(__func__);
356 
357 	return TRUE;
358 }
359 
gvnc_plugin_call_feature(RemminaProtocolWidget * gp,const RemminaProtocolFeature * feature)360 static void gvnc_plugin_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
361 {
362 	TRACE_CALL(__func__);
363 
364 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
365 	RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
366 
367 	guint keys[3];
368 
369 	switch (feature->id) {
370 	case GVNC_PLUGIN_FEATURE_PREF_VIEWONLY:
371 		gpdata->viewonly = remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE);
372 		break;
373 	case GVNC_PLUGIN_FEATURE_DYNRESUPDATE:
374 	case GVNC_PLUGIN_FEATURE_SCALE:
375 		gvnc_plugin_update_scale_mode(gp);
376 		break;
377 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL:
378 		keys[0] = GDK_KEY_Control_L;
379 		keys[1] = GDK_KEY_Alt_L;
380 		keys[2] = GDK_KEY_Delete;
381 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
382 		break;
383 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE:
384 		keys[0] = GDK_KEY_Control_L;
385 		keys[1] = GDK_KEY_Alt_L;
386 		keys[2] = GDK_KEY_BackSpace;
387 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
388 		break;
389 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1:
390 		keys[0] = GDK_KEY_Control_L;
391 		keys[1] = GDK_KEY_Alt_L;
392 		keys[2] = GDK_KEY_F1;
393 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
394 		break;
395 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2:
396 		keys[0] = GDK_KEY_Control_L;
397 		keys[1] = GDK_KEY_Alt_L;
398 		keys[2] = GDK_KEY_F2;
399 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
400 		break;
401 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3:
402 		keys[0] = GDK_KEY_Control_L;
403 		keys[1] = GDK_KEY_Alt_L;
404 		keys[2] = GDK_KEY_F3;
405 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
406 		break;
407 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4:
408 		keys[0] = GDK_KEY_Control_L;
409 		keys[1] = GDK_KEY_Alt_L;
410 		keys[2] = GDK_KEY_F4;
411 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
412 		break;
413 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5:
414 		keys[0] = GDK_KEY_Control_L;
415 		keys[1] = GDK_KEY_Alt_L;
416 		keys[2] = GDK_KEY_F5;
417 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
418 		break;
419 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6:
420 		keys[0] = GDK_KEY_Control_L;
421 		keys[1] = GDK_KEY_Alt_L;
422 		keys[2] = GDK_KEY_F6;
423 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
424 		break;
425 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7:
426 		keys[0] = GDK_KEY_Control_L;
427 		keys[1] = GDK_KEY_Alt_L;
428 		keys[2] = GDK_KEY_F7;
429 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
430 		break;
431 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9:
432 		keys[0] = GDK_KEY_Control_L;
433 		keys[1] = GDK_KEY_Alt_L;
434 		keys[2] = GDK_KEY_F9;
435 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
436 		break;
437 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10:
438 		keys[0] = GDK_KEY_Control_L;
439 		keys[1] = GDK_KEY_Alt_L;
440 		keys[2] = GDK_KEY_F10;
441 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
442 		break;
443 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11:
444 		keys[0] = GDK_KEY_Control_L;
445 		keys[1] = GDK_KEY_Alt_L;
446 		keys[2] = GDK_KEY_F11;
447 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
448 		break;
449 	case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12:
450 		keys[0] = GDK_KEY_Control_L;
451 		keys[1] = GDK_KEY_Alt_L;
452 		keys[2] = GDK_KEY_F12;
453 		gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
454 		break;
455 #ifdef HAVE_VNC_POWER_CONTROL
456 	case GVNC_PLUGIN_FEATURE_TOOL_REBOOT:
457 		gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_REBOOT);
458 		break;
459 	case GVNC_PLUGIN_FEATURE_TOOL_RESET:
460 		gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_RESET);
461 		break;
462 	case GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN:
463 		gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_SHUTDOWN);
464 		break;
465 #endif
466 	default:
467 		break;
468 	}
469 }
470 
gvnc_plugin_auth_unsupported(VncDisplay * vnc G_GNUC_UNUSED,unsigned int authType,RemminaProtocolWidget * gp)471 static void gvnc_plugin_auth_unsupported(VncDisplay *vnc G_GNUC_UNUSED, unsigned int authType, RemminaProtocolWidget *gp)
472 {
473 	TRACE_CALL(__func__);
474 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
475 
476 	g_clear_pointer(&gpdata->error_msg, g_free);
477 	gchar *msg = g_strdup_printf(_("Unsupported authentication type %u"), authType);
478 
479 	remmina_plugin_service->protocol_plugin_set_error(gp, "%s", msg);
480 	g_free(msg);
481 }
482 
gvnc_plugin_auth_failure(VncDisplay * vnc G_GNUC_UNUSED,const gchar * reason,RemminaProtocolWidget * gp)483 static void gvnc_plugin_auth_failure(VncDisplay *vnc G_GNUC_UNUSED, const gchar *reason, RemminaProtocolWidget *gp)
484 {
485 	TRACE_CALL(__func__);
486 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
487 
488 	g_clear_pointer(&gpdata->error_msg, g_free);
489 	gchar *msg = g_strdup_printf(_("Authentication failure: %s"), reason);
490 
491 	remmina_plugin_service->protocol_plugin_set_error(gp, "%s", msg);
492 	g_free(msg);
493 }
494 
gvnc_plugin_ask_auth(GtkWidget * vncdisplay,GValueArray * credList,RemminaProtocolWidget * gp)495 static gboolean gvnc_plugin_ask_auth(GtkWidget *vncdisplay, GValueArray *credList, RemminaProtocolWidget *gp)
496 {
497 	TRACE_CALL(__func__);
498 
499 	gint ret;
500 	gboolean disablepasswordstoring;
501 	gchar *s_username = NULL, *s_password = NULL;
502 	gboolean wantPassword = FALSE, wantUsername = FALSE;
503 	int i;
504 	gboolean save;
505 
506 
507 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
508 	RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
509 
510 
511 	GArray *credArray = g_array_sized_new(FALSE, TRUE, sizeof(GValue), credList->n_values);
512 	g_array_set_clear_func(credArray, (GDestroyNotify) g_value_unset);
513 	g_array_append_vals(credArray, credList->values, credList->n_values);
514 
515 	REMMINA_PLUGIN_DEBUG("Got credential request for %d credential(s)\n", credArray->len);
516 
517 	for (i = 0 ; i < credArray->len ; i++) {
518 		GValue *cred = &g_array_index(credArray, GValue, i);
519 		switch (g_value_get_enum(cred)) {
520 		case VNC_DISPLAY_CREDENTIAL_USERNAME:
521 			wantUsername = TRUE;
522 			break;
523 		case VNC_DISPLAY_CREDENTIAL_PASSWORD:
524 			wantPassword = TRUE;
525 			break;
526 		case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
527 			break;
528 		default:
529 			REMMINA_PLUGIN_DEBUG("Unsupported credential type %d", g_value_get_enum(cred));
530 			vnc_display_close(VNC_DISPLAY(gpdata->vnc));
531 			goto cleanup;
532 		}
533 	}
534 	disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
535 
536 	ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
537 								(disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD)
538 								| (wantUsername ? REMMINA_MESSAGE_PANEL_FLAG_USERNAME : 0),
539 								_("Enter VNC authentication credentials"),
540 								(wantUsername ? remmina_plugin_service->file_get_string(remminafile, "username") : NULL),
541 								(wantPassword ? remmina_plugin_service->file_get_string(remminafile, "password") : NULL),
542 								NULL,
543 								NULL);
544 	if (ret == GTK_RESPONSE_OK) {
545 		s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
546 		s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
547 		remmina_plugin_service->file_set_string(remminafile, "username", s_username);
548 
549 		save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
550 		if (save) {
551 			// User has requested to save credentials. We put the password
552 			// into remminafile->settings. It will be saved later, on successful connection, by
553 			// rcw.c
554 			remmina_plugin_service->file_set_string(remminafile, "password", s_password);
555 		} else {
556 			remmina_plugin_service->file_set_string(remminafile, "password", NULL);
557 		}
558 
559 		for (i = 0 ; i < credArray->len ; i++) {
560 			GValue *cred = &g_array_index(credArray, GValue, i);
561 			switch (g_value_get_enum(cred)) {
562 				case VNC_DISPLAY_CREDENTIAL_USERNAME:
563 					if (!s_username ||
564 							vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
565 								g_value_get_enum(cred),
566 								s_username)) {
567 						g_debug("Failed to set credential type %d", g_value_get_enum(cred));
568 						vnc_display_close(VNC_DISPLAY(gpdata->vnc));
569 					}
570 					break;
571 				case VNC_DISPLAY_CREDENTIAL_PASSWORD:
572 					if (!s_password ||
573 							vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
574 								g_value_get_enum(cred),
575 								s_password)) {
576 					g_debug("Failed to set credential type %d", g_value_get_enum(cred));
577 					vnc_display_close(VNC_DISPLAY(gpdata->vnc));
578 				}
579 				break;
580 			case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
581 				if (vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
582 							       g_value_get_enum(cred),
583 							       "remmina")) {
584 					g_debug("Failed to set credential type %d", g_value_get_enum(cred));
585 					vnc_display_close(VNC_DISPLAY(gpdata->vnc));
586 				}
587 				break;
588 			default:
589 				g_debug("Unsupported credential type %d", g_value_get_enum(cred));
590 				vnc_display_close(VNC_DISPLAY(gpdata->vnc));
591 			}
592 		}
593 
594 		if (s_username) g_free(s_username);
595 		if (s_password) g_free(s_password);
596 
597 		return TRUE;
598 	} else {
599 		return FALSE;
600 	}
601 
602 cleanup:
603 	g_free(s_username);
604 	g_free(s_password);
605 
606 	//g_object_set(gpdata->session, "password", s_password, NULL);
607 	return TRUE;
608 }
609 
gvnc_plugin_initialized(GtkWidget * vncdisplay,RemminaProtocolWidget * gp)610 static void gvnc_plugin_initialized(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
611 {
612 	TRACE_CALL(__func__);
613 
614 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
615 	RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
616 
617 	REMMINA_PLUGIN_DEBUG("Connection initialized");
618 	g_return_if_fail(gpdata != NULL);
619 	REMMINA_PLUGIN_DEBUG("Presenting the window");
620 
621 	VncAudioFormat format = {
622 		VNC_AUDIO_FORMAT_RAW_S32,
623 		2,
624 		44100,
625 	};
626 
627 	REMMINA_PLUGIN_DEBUG("Gathering the VNC connection object");
628 	gpdata->conn = vnc_display_get_connection(VNC_DISPLAY(gpdata->vnc));
629 	g_return_if_fail(gpdata->conn != NULL);
630 
631 	if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE)) {
632 		REMMINA_PLUGIN_DEBUG("Setting up VNC audio channel");
633 		if (vnc_connection_set_audio_format(gpdata->conn, &format))
634 			REMMINA_PLUGIN_DEBUG("VNC audio format set");
635 		else {
636 			REMMINA_PLUGIN_DEBUG("VNC audio format returned an error");
637 			return;
638 		}
639 
640 		if (vnc_connection_set_audio(gpdata->conn, VNC_AUDIO(gpdata->pa)))
641 			REMMINA_PLUGIN_DEBUG("VNC audio channel has been set");
642 		else {
643 			REMMINA_PLUGIN_DEBUG("VNC audio channel cannot be set");
644 			return;
645 		}
646 		REMMINA_PLUGIN_DEBUG("Enabling audio");
647 		if (vnc_connection_audio_enable(gpdata->conn))
648 			REMMINA_PLUGIN_DEBUG("Audio enabled");
649 		else
650 			REMMINA_PLUGIN_DEBUG("Audio cannot be enabled");
651 	}
652 	gpdata->width = vnc_display_get_width(VNC_DISPLAY(gpdata->vnc));
653 	gpdata->width = vnc_display_get_height(VNC_DISPLAY(gpdata->vnc));
654 }
655 
gvnc_plugin_disconnected(VncDisplay * vnc G_GNUC_UNUSED,RemminaProtocolWidget * gp)656 static void gvnc_plugin_disconnected(VncDisplay *vnc G_GNUC_UNUSED, RemminaProtocolWidget *gp)
657 {
658 	TRACE_CALL(__func__);
659 	//GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
660 
661 	REMMINA_PLUGIN_DEBUG("[%s] Plugin disconnected", PLUGIN_NAME);
662 }
gvnc_plugin_close_connection(RemminaProtocolWidget * gp)663 static gboolean gvnc_plugin_close_connection(RemminaProtocolWidget *gp)
664 {
665 	TRACE_CALL(__func__);
666 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
667 
668 	REMMINA_PLUGIN_DEBUG("[%s] Plugin close connection", PLUGIN_NAME);
669 
670 	if (gpdata) {
671 		if (gpdata->error_msg) g_free(gpdata->error_msg);
672 		if (gpdata->vnc)
673 			vnc_display_close(VNC_DISPLAY(gpdata->vnc));
674 		//g_object_unref(gpdata->vnc);
675 	}
676 
677 	/* Remove instance->context from gp object data to avoid double free */
678 	g_object_steal_data(G_OBJECT(gp), "plugin-data");
679 	remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
680 	return FALSE;
681 }
682 
gvnc_plugin_init(RemminaProtocolWidget * gp)683 static void gvnc_plugin_init(RemminaProtocolWidget *gp)
684 {
685 	TRACE_CALL(__func__);
686 	GVncPluginData *gpdata;
687 	//VncGrabSequence *seq;
688 
689 	GtkClipboard *cb;
690 
691 	gpdata = g_new0(GVncPluginData, 1);
692 	g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
693 
694 	gpdata->pa = NULL;
695 
696 	REMMINA_PLUGIN_DEBUG("[%s] Plugin init", PLUGIN_NAME);
697 
698 	RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
699 
700 	if (remmina_plugin_service->file_get_int(remminafile, "gvncdebug", FALSE))
701 		vnc_util_set_debug(TRUE);
702 
703 	gpdata->vnc = vnc_display_new();
704 	if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE))
705 		gpdata->pa = vnc_audio_pulse_new();
706 
707 
708 	g_signal_connect(gpdata->vnc, "vnc-auth-credential",
709 			 G_CALLBACK(gvnc_plugin_ask_auth), gp);
710 	g_signal_connect(gpdata->vnc, "vnc-auth-failure",
711 			 G_CALLBACK(gvnc_plugin_auth_failure), gp);
712 	g_signal_connect(gpdata->vnc, "vnc-auth-unsupported",
713 			 G_CALLBACK(gvnc_plugin_auth_unsupported), gp);
714 	g_signal_connect(gpdata->vnc, "vnc-disconnected",
715 			 G_CALLBACK(gvnc_plugin_disconnected), gp);
716 	g_signal_connect(gpdata->vnc, "vnc-initialized",
717 			 G_CALLBACK(gvnc_plugin_initialized), gp);
718 	g_signal_connect(gpdata->vnc, "vnc-desktop-resize",
719 			 G_CALLBACK(gvnc_plugin_desktop_resize), gp);
720 	g_signal_connect(gpdata->vnc, "vnc-bell",
721 			 G_CALLBACK(gvnc_plugin_on_bell), gp);
722 	g_signal_connect(gpdata->vnc, "vnc-error",
723 			 G_CALLBACK(gvnc_plugin_on_vnc_error), gp);
724 	g_signal_connect(gpdata->vnc, "vnc-pointer-grab",
725 			 G_CALLBACK(gvnc_plugin_mouse_grab), gp);
726 	g_signal_connect(gpdata->vnc, "vnc-pointer-ungrab",
727 			 G_CALLBACK(gvnc_plugin_mouse_ungrab), gp);
728 	g_signal_connect(gpdata->vnc, "vnc-server-cut-text",
729 			 G_CALLBACK(gvnc_plugin_cut_text), gp);
730 	//seq = vnc_grab_sequence_new_from_string ("Control_R");
731 	//vnc_display_set_grab_keys(VNC_DISPLAY(gpdata->vnc), seq);
732 	//vnc_grab_sequence_free(seq);
733 
734 	/* Setup the clipboard */
735 	cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
736 	gpdata->signal_clipboard = g_signal_connect(cb,
737 						    "owner-change",
738 						    G_CALLBACK(gvnc_plugin_clipboard_cb),
739 						    gp);
740 }
741 
gvnc_plugin_open_connection(RemminaProtocolWidget * gp)742 static gboolean gvnc_plugin_open_connection(RemminaProtocolWidget *gp)
743 {
744 	TRACE_CALL(__func__);
745 
746 	gint port;
747 	gchar *host = NULL, *tunnel = NULL;
748 	GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
749 	RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
750 
751 	REMMINA_PLUGIN_DEBUG("[%s] Plugin open connection", PLUGIN_NAME);
752 
753 	gpdata->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
754 	gtk_container_add(GTK_CONTAINER(gp), gpdata->box);
755 	gtk_widget_set_hexpand(GTK_WIDGET(gpdata->vnc), TRUE);
756 	gtk_widget_set_vexpand(GTK_WIDGET(gpdata->vnc), TRUE);
757 	gtk_container_add(GTK_CONTAINER(gpdata->box), gpdata->vnc);
758 
759 	remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->vnc);
760 
761 
762 	const gchar *address = remmina_plugin_service->file_get_string(remminafile, "server");
763 	if(strstr(g_strdup(address), "unix:///") != NULL) {
764 		REMMINA_PLUGIN_DEBUG("address contain unix:// -> %s", address);
765 		gchar *val = str_replace (address, "unix://", "");
766 		REMMINA_PLUGIN_DEBUG("address after cleaning = %s", val);
767 		gint fd = remmina_plugin_service->open_unix_sock(val);
768 		REMMINA_PLUGIN_DEBUG("Unix socket fd: %d", fd);
769 		gpdata->fd = fd;
770 		g_free(val);
771 
772 	} else {
773 		/* Setup SSH tunnel if needed */
774 		tunnel = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, GVNC_DEFAULT_PORT, FALSE);
775 		if (!tunnel)
776 			return FALSE;
777 		remmina_plugin_service->get_server_port(tunnel,
778 				GVNC_DEFAULT_PORT,
779 				&host,
780 				&port);
781 	}
782 
783 
784 
785 	gpdata->depth_profile = remmina_plugin_service->file_get_int(remminafile, "depth_profile", 24);
786 	gpdata->viewonly = remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE);
787 	vnc_display_set_depth(VNC_DISPLAY(gpdata->vnc), gpdata->depth_profile);
788 	if (gpdata->fd > 0)
789 		vnc_display_open_fd(VNC_DISPLAY(gpdata->vnc), gpdata->fd);
790 	else
791 		vnc_display_open_host(VNC_DISPLAY(gpdata->vnc), host, g_strdup_printf("%d", port));
792 	gpdata->lossy_encoding = remmina_plugin_service->file_get_int(remminafile, "lossy_encoding", FALSE);
793 	vnc_display_set_lossy_encoding(VNC_DISPLAY(gpdata->vnc), gpdata->shared);
794 	vnc_display_set_shared_flag(VNC_DISPLAY(gpdata->vnc), gpdata->shared);
795 
796 	if(host) g_free(host);
797 	if(tunnel) g_free(tunnel);
798 
799 	/* TRUE Conflict with remmina? */
800 	vnc_display_set_keyboard_grab(VNC_DISPLAY(gpdata->vnc), FALSE);
801 	vnc_display_set_read_only(VNC_DISPLAY(gpdata->vnc), gpdata->viewonly);
802 	/* TRUE Conflict with remmina? */
803 	vnc_display_set_pointer_grab(VNC_DISPLAY(gpdata->vnc), FALSE);
804 	vnc_display_set_pointer_local(VNC_DISPLAY(gpdata->vnc), TRUE);
805 
806 	vnc_display_set_force_size(VNC_DISPLAY(gpdata->vnc), FALSE);
807 	vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), TRUE);
808 #ifdef HAVE_VNC_REMOTE_RESIZE
809 	vnc_display_set_allow_resize(VNC_DISPLAY(gpdata->vnc), TRUE);
810 	vnc_display_set_zoom_level(VNC_DISPLAY(gpdata->vnc), opt_zoom);
811 #endif
812 	remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
813 	gtk_widget_show_all(gpdata->box);
814 	return TRUE;
815 }
816 
817 /* Array of key/value pairs for color depths */
818 static gpointer colordepth_list[] =
819 {
820 	"0", N_("Use server settings"),
821 	"1", N_("True colour (24 bits)"),
822 	"2", N_("High colour (16 bits)"),
823 	"3", N_("Low colour (8 bits)"),
824 	"4", N_("Ultra low colour (3 bits)"),
825 	NULL
826 };
827 /* Array of RemminaProtocolSetting for basic settings.
828  * Each item is composed by:
829  * a) RemminaProtocolSettingType for setting type
830  * b) Setting name
831  * c) Setting description
832  * d) Compact disposition
833  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
834  * f) Setting Tooltip
835  */
836 static const RemminaProtocolSetting gvnc_plugin_basic_settings[] =
837 {
838 	{ REMMINA_PROTOCOL_SETTING_TYPE_SERVER,	  "server",	    NULL,			FALSE, NULL,		NULL					     },
839 	{ REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password",	    N_("VNC password"),		FALSE, NULL,		NULL					     },
840 	{ REMMINA_PROTOCOL_SETTING_TYPE_SELECT,	  "depth_profile",  N_("Colour depth"),		FALSE, colordepth_list, NULL					     },
841 	{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK,	  "lossy_encoding", N_("Use JPEG Compression"), TRUE,  NULL,		N_("This might not work on all VNC servers") },
842 	{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK,	  "gvncdebug",	    N_("Enable GTK-VNC debug"), FALSE, NULL,		NULL					     },
843 	{ REMMINA_PROTOCOL_SETTING_TYPE_END,	  NULL,		    NULL,			FALSE, NULL,		NULL					     }
844 };
845 
846 /* Array of RemminaProtocolSetting for advanced settings.
847  * Each item is composed by:
848  * a) RemminaProtocolSettingType for setting type
849  * b) Setting name
850  * c) Setting description
851  * d) Compact disposition
852  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
853  * f) Setting Tooltip
854  */
855 static const RemminaProtocolSetting gvnc_plugin_advanced_settings[] =
856 {
857 	{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard",	 N_("No clipboard sync"),	    TRUE,  NULL, NULL											},
858 	{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"),  FALSE, NULL, NULL											},
859 	{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverbell",	 N_("Ignore remote bell messages"), TRUE,  NULL, NULL											},
860 	{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enableaudio",		 N_("Enable audio channel"),	    FALSE, NULL, NULL											},
861 	{ REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly",		 N_("View only"),		    TRUE,  NULL, NULL											},
862 	{ 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") },
863 	{ REMMINA_PROTOCOL_SETTING_TYPE_END,   NULL,			 NULL,				    TRUE,  NULL, NULL											}
864 };
865 
866 /* Array for available features.
867  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
868 static const RemminaProtocolFeature gvnc_plugin_features[] =
869 {
870 	{ REMMINA_PROTOCOL_FEATURE_TYPE_PREF,	      GVNC_PLUGIN_FEATURE_PREF_VIEWONLY,	     GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",	       N_("View only")	       },
871 	{ REMMINA_PROTOCOL_FEATURE_TYPE_PREF,	      GVNC_PLUGIN_FEATURE_PREF_DISABLECLIPBOARD,     GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableclipboard", N_("No clipboard sync") },
872 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL,	     N_("Send Ctrl+Alt+_Del"),				   NULL,	       NULL		       },
873 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE, N_("Send Ctrl+Alt+_Backspace"),			   NULL,	       NULL		       },
874 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1,	     N_("Send Ctrl+Alt+_F1"),				   NULL,	       NULL		       },
875 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2,	     N_("Send Ctrl+Alt+_F2"),				   NULL,	       NULL		       },
876 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3,	     N_("Send Ctrl+Alt+_F3"),				   NULL,	       NULL		       },
877 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4,	     N_("Send Ctrl+Alt+_F4"),				   NULL,	       NULL		       },
878 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5,	     N_("Send Ctrl+Alt+_F5"),				   NULL,	       NULL		       },
879 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6,	     N_("Send Ctrl+Alt+_F6"),				   NULL,	       NULL		       },
880 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7,	     N_("Send Ctrl+Alt+_F7"),				   NULL,	       NULL		       },
881 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF8,	     N_("Send Ctrl+Alt+_F8"),				   NULL,	       NULL		       },
882 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9,	     N_("Send Ctrl+Alt+_F9"),				   NULL,	       NULL		       },
883 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10,	     N_("Send Ctrl+Alt+_F10"),				   NULL,	       NULL		       },
884 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11,	     N_("Send Ctrl+Alt+_F11"),				   NULL,	       NULL		       },
885 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12,	     N_("Send Ctrl+Alt+_F12"),				   NULL,	       NULL		       },
886 #ifdef HAVE_VNC_POWER_CONTROL
887 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_REBOOT,		     N_("Reboot remote host"),				   NULL,	       NULL		       },
888 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_RESET,		     N_("Reset remote host (hard reboot)"),		   NULL,	       NULL		       },
889 	{ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL,	      GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN,	     N_("Shutdown remote host"),			   NULL,	       NULL		       },
890 #endif
891 	{ REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, GVNC_PLUGIN_FEATURE_DYNRESUPDATE,		     NULL,						   NULL,	       NULL		       },
892 	{ REMMINA_PROTOCOL_FEATURE_TYPE_SCALE,	      GVNC_PLUGIN_FEATURE_SCALE,		     NULL,						   NULL,	       NULL		       },
893 	{ REMMINA_PROTOCOL_FEATURE_TYPE_END,	      0,					     NULL,						   NULL,	       NULL		       }
894 };
895 
896 /* Protocol plugin definition and features */
897 static RemminaProtocolPlugin remmina_plugin = {
898 	REMMINA_PLUGIN_TYPE_PROTOCOL,           // Type
899 	PLUGIN_NAME,                            // Name
900 	PLUGIN_DESCRIPTION,                     // Description
901 	GETTEXT_PACKAGE,                        // Translation domain
902 	PLUGIN_VERSION,                         // Version number
903 	PLUGIN_APPICON,                         // Icon for normal connection
904 	PLUGIN_APPICON,                         // Icon for SSH connection
905 	gvnc_plugin_basic_settings,             // Array for basic settings
906 	gvnc_plugin_advanced_settings,          // Array for advanced settings
907 	REMMINA_PROTOCOL_SSH_SETTING_TUNNEL,    // SSH settings type
908 	gvnc_plugin_features,                   // Array for available features
909 	gvnc_plugin_init,                       // Plugin initialization
910 	gvnc_plugin_open_connection,            // Plugin open connection
911 	gvnc_plugin_close_connection,           // Plugin close connection
912 	gvnc_plugin_query_feature,              // Query for available features
913 	gvnc_plugin_call_feature,               // Call a feature
914 	gvnc_plugin_keystroke,                  // Send a keystroke
915 	NULL,                                   // No screenshot support available
916 	//gvnc_plugin_get_screenshot,             // No screenshot support available
917 	NULL,                                   // RCW map event
918 	NULL                                    // RCW unmap event
919 };
920 
remmina_plugin_entry(RemminaPluginService * service)921 G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
922 {
923 	TRACE_CALL(__func__);
924 	remmina_plugin_service = service;
925 
926 	bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
927 	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
928 
929 	if (!service->register_plugin((RemminaPlugin *)&remmina_plugin))
930 		return FALSE;
931 
932 	return TRUE;
933 }
934