1 /*
2 * Copyright (C) 2003 Sun Microsystems, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 *
19 * Authors:
20 * Mark McLoughlin <mark@skynet.ie>
21 */
22
23 #include <config.h>
24
25 #include "vino-prompt.h"
26
27 #include <gtk/gtk.h>
28 #include <libnotify/notify.h>
29 #include "vino-util.h"
30 #include "vino-enums.h"
31 #include "vino-marshal.h"
32
33 struct _VinoPromptPrivate
34 {
35 GdkScreen *screen;
36 NotifyNotification *notification;
37 rfbClientPtr current_client;
38 GSList *pending_clients;
39 };
40
41 enum
42 {
43 PROP_0,
44 PROP_SCREEN
45 };
46
47 enum
48 {
49 RESPONSE,
50 LAST_SIGNAL
51 };
52
53 static gboolean vino_prompt_display (VinoPrompt *prompt,
54 rfbClientPtr rfb_client);
55
56 static guint prompt_signals [LAST_SIGNAL] = { 0 };
57
58 G_DEFINE_TYPE (VinoPrompt, vino_prompt, G_TYPE_OBJECT);
59
60 static void
clear_notification(VinoPrompt * prompt)61 clear_notification (VinoPrompt *prompt)
62 {
63 if (prompt->priv->notification)
64 notify_notification_close (prompt->priv->notification, NULL);
65 g_clear_object (&prompt->priv->notification);
66 }
67
68 static void
vino_prompt_finalize(GObject * object)69 vino_prompt_finalize (GObject *object)
70 {
71 VinoPrompt *prompt = VINO_PROMPT (object);
72
73 g_slist_free (prompt->priv->pending_clients);
74 prompt->priv->pending_clients = NULL;
75
76 clear_notification (prompt);
77
78 g_free (prompt->priv);
79 prompt->priv = NULL;
80
81 if (G_OBJECT_CLASS (vino_prompt_parent_class)->finalize)
82 G_OBJECT_CLASS (vino_prompt_parent_class)->finalize (object);
83 }
84
85 static void
vino_prompt_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)86 vino_prompt_set_property (GObject *object,
87 guint prop_id,
88 const GValue *value,
89 GParamSpec *pspec)
90 {
91 VinoPrompt *prompt = VINO_PROMPT (object);
92
93 switch (prop_id)
94 {
95 case PROP_SCREEN:
96 vino_prompt_set_screen (prompt, g_value_get_object (value));
97 break;
98 default:
99 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
100 break;
101 }
102 }
103
104 static void
vino_prompt_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)105 vino_prompt_get_property (GObject *object,
106 guint prop_id,
107 GValue *value,
108 GParamSpec *pspec)
109 {
110 VinoPrompt *prompt = VINO_PROMPT (object);
111
112 switch (prop_id)
113 {
114 case PROP_SCREEN:
115 g_value_set_object (value, prompt->priv->screen);
116 break;
117 default:
118 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
119 break;
120 }
121 }
122
123 static void
vino_prompt_init(VinoPrompt * prompt)124 vino_prompt_init (VinoPrompt *prompt)
125 {
126 prompt->priv = g_new0 (VinoPromptPrivate, 1);
127 }
128
129 static void
vino_prompt_class_init(VinoPromptClass * klass)130 vino_prompt_class_init (VinoPromptClass *klass)
131 {
132 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
133 VinoPromptClass *prompt_class = VINO_PROMPT_CLASS (klass);
134
135 gobject_class->finalize = vino_prompt_finalize;
136 gobject_class->set_property = vino_prompt_set_property;
137 gobject_class->get_property = vino_prompt_get_property;
138
139 prompt_class->response = NULL;
140
141 g_object_class_install_property (gobject_class,
142 PROP_SCREEN,
143 g_param_spec_object ("screen",
144 _("Screen"),
145 _("The screen on which to display the prompt"),
146 GDK_TYPE_SCREEN,
147 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
148
149 prompt_signals [RESPONSE] =
150 g_signal_new ("response",
151 G_OBJECT_CLASS_TYPE (gobject_class),
152 G_SIGNAL_RUN_LAST,
153 G_STRUCT_OFFSET (VinoPromptClass, response),
154 NULL, NULL,
155 vino_marshal_VOID__POINTER_ENUM,
156 G_TYPE_NONE,
157 2,
158 G_TYPE_POINTER,
159 VINO_TYPE_PROMPT_RESPONSE);
160
161 vino_init_stock_items ();
162 }
163
164 VinoPrompt *
vino_prompt_new(GdkScreen * screen)165 vino_prompt_new (GdkScreen *screen)
166 {
167 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
168
169 return g_object_new (VINO_TYPE_PROMPT,
170 "screen", screen,
171 NULL);
172 }
173
174 GdkScreen *
vino_prompt_get_screen(VinoPrompt * prompt)175 vino_prompt_get_screen (VinoPrompt *prompt)
176 {
177 g_return_val_if_fail (VINO_IS_PROMPT (prompt), NULL);
178
179 return prompt->priv->screen;
180 }
181
182 void
vino_prompt_set_screen(VinoPrompt * prompt,GdkScreen * screen)183 vino_prompt_set_screen (VinoPrompt *prompt,
184 GdkScreen *screen)
185 {
186 g_return_if_fail (VINO_IS_PROMPT (prompt));
187
188 if (prompt->priv->screen != screen)
189 {
190 prompt->priv->screen = screen;
191
192 g_object_notify (G_OBJECT (prompt), "screen");
193 }
194 }
195
196 static void
vino_prompt_process_pending_clients(VinoPrompt * prompt)197 vino_prompt_process_pending_clients (VinoPrompt *prompt)
198 {
199 if (prompt->priv->pending_clients)
200 {
201 rfbClientPtr rfb_client = (rfbClientPtr) prompt->priv->pending_clients->data;
202
203 prompt->priv->pending_clients =
204 g_slist_delete_link (prompt->priv->pending_clients,
205 prompt->priv->pending_clients);
206
207 vino_prompt_display (prompt, rfb_client);
208 }
209 }
210
211 static void
emit_response_signal(VinoPrompt * prompt,rfbClientPtr rfb_client,int response)212 emit_response_signal (VinoPrompt *prompt,
213 rfbClientPtr rfb_client,
214 int response)
215 {
216 dprintf (PROMPT, "Emiting response signal for %p: %s\n",
217 rfb_client,
218 response == VINO_RESPONSE_ACCEPT ? "accept" : "reject");
219
220 g_signal_emit (prompt,
221 prompt_signals [RESPONSE],
222 0,
223 rfb_client,
224 response);
225 }
226
227 static void
vino_prompt_handle_response(NotifyNotification * notification,char * response,gpointer user_data)228 vino_prompt_handle_response (NotifyNotification *notification,
229 char *response,
230 gpointer user_data)
231 {
232 VinoPrompt *prompt = user_data;
233 rfbClientPtr rfb_client;
234 int prompt_response = VINO_RESPONSE_INVALID;
235
236 dprintf (PROMPT, "Got a response for client %p: %s\n",
237 prompt->priv->current_client,
238 response);
239
240 if (g_strcmp0 (response, "accept") == 0)
241 prompt_response = VINO_RESPONSE_ACCEPT;
242 else
243 prompt_response = VINO_RESPONSE_REJECT;
244
245 rfb_client = prompt->priv->current_client;
246 prompt->priv->current_client = NULL;
247
248 clear_notification (prompt);
249
250 if (rfb_client != NULL)
251 {
252 emit_response_signal (prompt, rfb_client, prompt_response);
253 }
254
255 vino_prompt_process_pending_clients (prompt);
256 }
257
258 static void
vino_prompt_handle_close(NotifyNotification * notification,gpointer user_data)259 vino_prompt_handle_close (NotifyNotification *notification,
260 gpointer user_data)
261 {
262 vino_prompt_handle_response (notification, "close", user_data);
263 }
264
265 static gboolean
vino_prompt_setup_dialog(VinoPrompt * prompt)266 vino_prompt_setup_dialog (VinoPrompt *prompt)
267 {
268 if (!notify_is_initted () && !notify_init (g_get_application_name ()))
269 {
270 g_printerr (_("Error initializing libnotify\n"));
271 return FALSE;
272 }
273
274 return TRUE;
275 }
276
277 static gboolean
vino_prompt_display(VinoPrompt * prompt,rfbClientPtr rfb_client)278 vino_prompt_display (VinoPrompt *prompt,
279 rfbClientPtr rfb_client)
280 {
281 char *host_label;
282
283 if (prompt->priv->current_client)
284 return prompt->priv->current_client == rfb_client;
285
286 if (!vino_prompt_setup_dialog (prompt))
287 return FALSE;
288
289 host_label = g_strdup_printf (_("A user on the computer '%s' is trying to remotely view or control your desktop."),
290 rfb_client->host);
291
292 prompt->priv->notification = notify_notification_new (_("Another user is trying to view your desktop."),
293 host_label,
294 "preferences-desktop-remote-desktop");
295 notify_notification_set_hint_string (prompt->priv->notification, "desktop-entry", "vino-server");
296 notify_notification_add_action (prompt->priv->notification,
297 "refuse",
298 _("Refuse"),
299 vino_prompt_handle_response,
300 prompt,
301 NULL);
302 notify_notification_add_action (prompt->priv->notification,
303 "accept",
304 _("Accept"),
305 vino_prompt_handle_response,
306 prompt,
307 NULL);
308 g_signal_connect (prompt->priv->notification, "closed",
309 G_CALLBACK (vino_prompt_handle_close), prompt);
310
311
312 g_free (host_label);
313
314 prompt->priv->current_client = rfb_client;
315
316 notify_notification_show (prompt->priv->notification, NULL);
317
318 dprintf (PROMPT, "Prompting for client %p\n", rfb_client);
319
320 return TRUE;
321 }
322
323 void
vino_prompt_add_client(VinoPrompt * prompt,rfbClientPtr rfb_client)324 vino_prompt_add_client (VinoPrompt *prompt,
325 rfbClientPtr rfb_client)
326 {
327 g_return_if_fail (VINO_IS_PROMPT (prompt));
328 g_return_if_fail (rfb_client != NULL);
329
330 if (!vino_prompt_display (prompt, rfb_client))
331 {
332 dprintf (PROMPT, "Prompt in progress for %p: queueing %p\n",
333 prompt->priv->current_client, rfb_client);
334 prompt->priv->pending_clients =
335 g_slist_append (prompt->priv->pending_clients, rfb_client);
336 }
337 }
338
339 void
vino_prompt_remove_client(VinoPrompt * prompt,rfbClientPtr rfb_client)340 vino_prompt_remove_client (VinoPrompt *prompt,
341 rfbClientPtr rfb_client)
342 {
343 g_return_if_fail (VINO_IS_PROMPT (prompt));
344 g_return_if_fail (rfb_client != NULL);
345
346 if (prompt->priv->current_client == rfb_client)
347 {
348 clear_notification (prompt);
349 }
350 else
351 {
352 prompt->priv->pending_clients =
353 g_slist_remove (prompt->priv->pending_clients, rfb_client);
354 }
355 }
356