1 /*
2 * vinagre-spice-plugin.c
3 * This file is part of vinagre
4 *
5 * Copyright (C) 2011 Red Hat, Inc.
6 * Author: Marc-Andre Lureau <marcandre.lureau@redhat.com>
7 *
8 * vinagre-spice-plugin.c is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * vinagre-spice-plugin.c is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib/gi18n-lib.h>
27 #include <gmodule.h>
28
29 #include <vinagre/vinagre-cache-prefs.h>
30 #include <vinagre/vinagre-protocol.h>
31
32 #include "vinagre-spice-plugin.h"
33 #include "vinagre-spice-connection.h"
34 #include "vinagre-spice-tab.h"
35
36 void spice_register_types (void);
37 static void vinagre_spice_protocol_iface_init (VinagreProtocolInterface *iface);
38
39 G_DEFINE_TYPE_EXTENDED (VinagreSpicePlugin,
40 vinagre_spice_plugin,
41 VINAGRE_TYPE_STATIC_EXTENSION,
42 0,
43 G_IMPLEMENT_INTERFACE (VINAGRE_TYPE_PROTOCOL,
44 vinagre_spice_protocol_iface_init))
45
46 static const gchar *
impl_get_protocol(VinagreProtocol * plugin)47 impl_get_protocol (VinagreProtocol *plugin)
48 {
49 return "spice";
50 }
51
52 static const gchar *
impl_get_mdns_service(VinagreProtocol * plugin)53 impl_get_mdns_service (VinagreProtocol *plugin)
54 {
55 return "_spice._tcp";
56 }
57
58 static VinagreConnection *
impl_new_connection(VinagreProtocol * plugin)59 impl_new_connection (VinagreProtocol *plugin)
60 {
61 VinagreConnection *conn;
62
63 conn = vinagre_spice_connection_new ();
64
65 return conn;
66 }
67
68 static VinagreConnection *
impl_new_connection_from_file(VinagreProtocol * plugin,const gchar * data,gboolean use_bookmarks,gchar ** error_msg)69 impl_new_connection_from_file (VinagreProtocol *plugin,
70 const gchar *data,
71 gboolean use_bookmarks,
72 gchar **error_msg)
73 {
74 GKeyFile *file;
75 GError *error;
76 gboolean loaded;
77 gchar *host, *actual_host, *protocol;
78 gint port;
79 VinagreConnection *conn;
80
81 *error_msg = NULL;
82 conn = NULL;
83 host = NULL;
84 protocol = NULL;
85 error = NULL;
86
87 file = g_key_file_new ();
88 loaded = g_key_file_load_from_data (file,
89 data,
90 -1,
91 0,
92 &error);
93 if (!loaded)
94 {
95 if (error)
96 {
97 *error_msg = g_strdup (error->message);
98 g_error_free (error);
99 }
100 else
101 *error_msg = g_strdup (_("Could not parse the file."));
102
103 goto the_end;
104 }
105 if (!g_key_file_has_group (file, "connection"))
106 {
107 /* Translators: Do not translate "connection". It's the name of a group in the .spice (.ini like) file. */
108 *error_msg = g_strdup (_("The file is not a Spice one: Missing the group \"connection\"."));
109 goto the_end;
110 }
111
112 if (!g_key_file_has_key (file, "connection", "host", NULL))
113 {
114 /* Translators: Do not translate "host". It's the name of a key in the .spice (.ini like) file. */
115 *error_msg = g_strdup (_("The file is not a Spice one: Missing the key \"host\"."));
116 goto the_end;
117 }
118
119 host = g_key_file_get_string (file, "connection", "host", NULL);
120 port = g_key_file_get_integer (file, "connection", "port", NULL);
121 if (!port)
122 {
123 if (!vinagre_connection_split_string (host, "spice", &protocol, &actual_host, &port, error_msg))
124 goto the_end;
125
126 g_free (host);
127 host = actual_host;
128 }
129
130 if (use_bookmarks)
131 conn = vinagre_bookmarks_exists (vinagre_bookmarks_get_default (), "spice", host, port);
132 if (!conn)
133 {
134 gchar *s_value;
135
136 conn = vinagre_spice_connection_new ();
137 vinagre_connection_set_host (conn, host);
138 vinagre_connection_set_port (conn, port);
139
140 s_value = g_key_file_get_string (file, "connection", "password", NULL);
141 vinagre_connection_set_password (conn, s_value);
142 g_free (s_value);
143 }
144
145 the_end:
146
147 g_free (host);
148 g_free (protocol);
149 g_key_file_free (file);
150 return conn;
151
152 }
153
154 static gboolean
impl_recognize_file(VinagreProtocol * plugin,GFile * file)155 impl_recognize_file (VinagreProtocol *plugin, GFile *file)
156 {
157 gboolean result = FALSE;
158 gchar *filename = g_file_get_basename (file);
159
160 if (filename)
161 {
162 result = g_str_has_suffix (filename, ".spice");
163 g_free (filename);
164 }
165
166 return result;
167 }
168
169 static gchar **
impl_get_public_description(VinagreProtocol * plugin)170 impl_get_public_description (VinagreProtocol *plugin)
171 {
172 gchar **result = g_new (gchar *, 3);
173
174 result[0] = g_strdup (_("SPICE"));
175 /* Translators: This is a description of the SPICE protocol. It appears at Connect dialog. */
176 result[1] = g_strdup (_("Access Spice desktop server"));
177 result[2] = NULL;
178
179 return result;
180 }
181
182 static gint
impl_get_default_port(VinagreProtocol * plugin)183 impl_get_default_port (VinagreProtocol *plugin)
184 {
185 return 5900;
186 }
187
188 static void
vinagre_spice_plugin_init(VinagreSpicePlugin * plugin)189 vinagre_spice_plugin_init (VinagreSpicePlugin *plugin)
190 {
191 }
192
193 static GtkWidget *
impl_new_tab(VinagreProtocol * plugin,VinagreConnection * conn,VinagreWindow * window)194 impl_new_tab (VinagreProtocol *plugin,
195 VinagreConnection *conn,
196 VinagreWindow *window)
197 {
198 return vinagre_spice_tab_new (conn, window);
199 }
200
201 static void
ssh_tunnel_check_toggled_cb(GtkToggleButton * button,GObject * box)202 ssh_tunnel_check_toggled_cb (GtkToggleButton *button, GObject *box)
203 {
204 gboolean active = gtk_toggle_button_get_active (button);
205 GtkWidget *ssh_host_entry = g_object_get_data (G_OBJECT (box), "ssh_host");
206
207 gtk_widget_set_sensitive (ssh_host_entry, active);
208
209 if (active)
210 gtk_widget_grab_focus (ssh_host_entry);
211 else
212 gtk_entry_set_text (GTK_ENTRY (ssh_host_entry), "");
213 }
214
215 static GtkWidget *
impl_get_connect_widget(VinagreProtocol * plugin,VinagreConnection * conn)216 impl_get_connect_widget (VinagreProtocol *plugin, VinagreConnection *conn)
217 {
218 GtkWidget *box, *label, *check, *p_entry, *box2, *ssh_host_entry, *grid;
219 gchar *str, *ssh_host;
220 gboolean has_conn = VINAGRE_IS_SPICE_CONNECTION (conn);
221
222 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
223
224 str = g_strdup_printf ("<b>%s</b>", _("SPICE Options"));
225 label = gtk_label_new (str);
226 g_free (str);
227 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
228 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
229 gtk_misc_set_padding (GTK_MISC (label), 0, 6);
230 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
231
232 grid = gtk_grid_new ();
233 label = gtk_label_new (" ");
234 gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
235
236 /* View only check button - not fully ready on spice-gtk side */
237 check = gtk_check_button_new_with_mnemonic (_("_View only"));
238 g_object_set_data (G_OBJECT (box), "view_only", check);
239 /* gtk_table_attach_defaults (table, check, 1, 2, 0, 1); */
240 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
241 has_conn ? vinagre_spice_connection_get_view_only (VINAGRE_SPICE_CONNECTION (conn))
242 : vinagre_cache_prefs_get_boolean ("spice-connection", "view-only", FALSE));
243
244 /* Resize guest check button */
245 check = gtk_check_button_new_with_mnemonic (_("_Resize guest"));
246 g_object_set_data (G_OBJECT (box), "resize_guest", check);
247 gtk_grid_attach (GTK_GRID (grid), check, 1, 0, 1, 1);
248 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
249 has_conn ? vinagre_spice_connection_get_resize_guest (VINAGRE_SPICE_CONNECTION (conn))
250 : vinagre_cache_prefs_get_boolean ("spice-connection", "resize-guest", TRUE));
251
252 /* Clipboard sharing check button */
253 check = gtk_check_button_new_with_mnemonic (_("_Share clipboard"));
254 g_object_set_data (G_OBJECT (box), "auto_clipboard", check);
255 gtk_grid_attach (GTK_GRID (grid), check, 1, 1, 1, 1);
256 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
257 has_conn ? vinagre_spice_connection_get_auto_clipboard (VINAGRE_SPICE_CONNECTION (conn))
258 : vinagre_cache_prefs_get_boolean ("spice-connection", "auto-clipboard", TRUE));
259
260 /* Scaling check button */
261 check = gtk_check_button_new_with_mnemonic (_("_Scaling"));
262 g_object_set_data (G_OBJECT (box), "scaling", check);
263 gtk_grid_attach (GTK_GRID (grid), check, 1, 2, 1, 1);
264 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
265 has_conn ? vinagre_spice_connection_get_scaling (VINAGRE_SPICE_CONNECTION (conn))
266 : vinagre_cache_prefs_get_boolean ("spice-connection", "scaling", FALSE));
267
268 /* SSH Tunneling */
269 box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
270
271 /* Translators: the whole sentence will be: Use host <hostname> as a SSH tunnel*/
272 check = gtk_check_button_new_with_mnemonic (_("Use h_ost"));
273 g_object_set_data (G_OBJECT (box), "use_ssh", check);
274 gtk_box_pack_start (GTK_BOX (box2), check, FALSE, FALSE, 0);
275
276 ssh_host_entry = gtk_entry_new ();
277 gtk_widget_set_sensitive (ssh_host_entry, FALSE);
278 g_object_set_data (G_OBJECT (box), "ssh_host", ssh_host_entry);
279 /* Translators: This is the tooltip of the SSH tunneling entry */
280 str = g_strdup_printf ("%s\n%s\n%s",
281 _("hostname or user@hostname"),
282 _("Supply an alternative port using colon"),
283 _("For instance: joe@example.com:5022"));
284 gtk_widget_set_tooltip_text (ssh_host_entry, str);
285 g_free (str);
286 gtk_box_pack_start (GTK_BOX (box2), ssh_host_entry, FALSE, FALSE, 0);
287
288 /* Translators: the whole sentence will be: Use host <hostname> as a SSH tunnel*/
289 label = gtk_label_new_with_mnemonic (_("as a SSH tunnel"));
290 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
291 gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
292
293 g_signal_connect (check,
294 "toggled",
295 G_CALLBACK (ssh_tunnel_check_toggled_cb),
296 box);
297
298 ssh_host = has_conn ? g_strdup (vinagre_spice_connection_get_ssh_tunnel_host (VINAGRE_SPICE_CONNECTION (conn)))
299 : vinagre_cache_prefs_get_string ("spice-connection", "ssh-tunnel-host", NULL);
300 if (ssh_host)
301 gtk_entry_set_text (GTK_ENTRY (ssh_host_entry), ssh_host);
302 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), ssh_host && *ssh_host);
303 g_free (ssh_host);
304
305 gtk_grid_attach (GTK_GRID (grid), box2, 1, 3, 1, 1);
306
307 /* Password */
308 box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
309
310 label = gtk_label_new_with_mnemonic (_("_Password:"));
311 gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
312
313 p_entry = gtk_entry_new ();
314 /* Translators: This is the tooltip for the password field in a SPICE connection */
315 gtk_widget_set_tooltip_text (p_entry, _("Optional"));
316 g_object_set_data (G_OBJECT (box), "password_entry", p_entry);
317 gtk_box_pack_start (GTK_BOX (box2), p_entry, FALSE, FALSE, 0);
318 gtk_label_set_mnemonic_widget (GTK_LABEL (label), p_entry);
319 str = g_strdup (vinagre_cache_prefs_get_string ("spice-connection", "password", ""));
320 gtk_entry_set_text (GTK_ENTRY (p_entry), str);
321 gtk_entry_set_activates_default (GTK_ENTRY (p_entry), TRUE);
322 g_free (str);
323 /* FIXME: how do you set the password here? gtk_table_attach_defaults (table, box2, 1, 2, 4, 5); */
324
325 gtk_box_pack_start (GTK_BOX (box), grid, FALSE, FALSE, 0);
326 return box;
327 }
328
329 static GtkFileFilter *
impl_get_file_filter(VinagreProtocol * plugin)330 impl_get_file_filter (VinagreProtocol *plugin)
331 {
332 GtkFileFilter *filter;
333
334 filter = gtk_file_filter_new ();
335 /* Translators: this is a pattern to open *.spice files in a open dialog. */
336 gtk_file_filter_set_name (filter, _("Spice Files"));
337 gtk_file_filter_add_pattern (filter, "*.spice");
338
339 return filter;
340 }
341
342 static void
vinagre_spice_protocol_iface_init(VinagreProtocolInterface * iface)343 vinagre_spice_protocol_iface_init (VinagreProtocolInterface *iface)
344 {
345 iface->get_protocol = impl_get_protocol;
346 iface->get_public_description = impl_get_public_description;
347 iface->get_default_port = impl_get_default_port;
348 iface->get_connect_widget = impl_get_connect_widget;
349 iface->get_mdns_service = impl_get_mdns_service;
350 iface->get_file_filter = impl_get_file_filter;
351 iface->new_connection = impl_new_connection;
352 iface->new_tab = impl_new_tab;
353 iface->new_connection_from_file = impl_new_connection_from_file;
354 iface->recognize_file = impl_recognize_file;
355 }
356
357 static void
vinagre_spice_plugin_class_finalize(VinagreSpicePluginClass * klass)358 vinagre_spice_plugin_class_finalize (VinagreSpicePluginClass *klass)
359 {
360 }
361
362 static void
vinagre_spice_plugin_class_init(VinagreSpicePluginClass * klass)363 vinagre_spice_plugin_class_init (VinagreSpicePluginClass *klass)
364 {
365 }
366
367 __attribute__((constructor)) void
spice_register_types(void)368 spice_register_types (void)
369 {
370 g_type_init ();
371 volatile dontoptimiseaway = vinagre_spice_plugin_get_type ();
372 }
373
374 /* vim: set ts=8: */
375