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