1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /* nemo-program-choosing.c - functions for selecting and activating
4  				 programs for opening/viewing particular files.
5 
6    Copyright (C) 2000 Eazel, Inc.
7 
8    The Gnome Library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Library General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    The Gnome Library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17 
18    You should have received a copy of the GNU Library General Public
19    License along with the Gnome Library; see the file COPYING.LIB.  If not,
20    write to the Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
21    Boston, MA 02110-1335, USA.
22 
23    Author: John Sullivan <sullivan@eazel.com>
24 */
25 
26 #include <config.h>
27 #include "nemo-program-choosing.h"
28 
29 #include "nemo-global-preferences.h"
30 #include "nemo-icon-info.h"
31 #include "nemo-recent.h"
32 #include "nemo-desktop-icon-file.h"
33 #include <eel/eel-gnome-extensions.h>
34 #include <eel/eel-stock-dialogs.h>
35 #include <gtk/gtk.h>
36 #include <glib/gi18n.h>
37 #include <gio/gio.h>
38 #include <gio/gdesktopappinfo.h>
39 #include <stdlib.h>
40 
41 #include <gdk/gdk.h>
42 #include <gdk/gdkx.h>
43 
44 void
nemo_launch_application_for_mount(GAppInfo * app_info,GMount * mount,GtkWindow * parent_window)45 nemo_launch_application_for_mount (GAppInfo *app_info,
46 				       GMount *mount,
47 				       GtkWindow *parent_window)
48 {
49 	GFile *root;
50 	NemoFile *file;
51 	GList *files;
52 
53 	root = g_mount_get_root (mount);
54 	file = nemo_file_get (root);
55 	g_object_unref (root);
56 
57 	files = g_list_append (NULL, file);
58 	nemo_launch_application (app_info,
59 				     files,
60 				     parent_window);
61 
62 	g_list_free_full (files, (GDestroyNotify) nemo_file_unref);
63 }
64 
65 /**
66  * nemo_launch_application:
67  *
68  * Fork off a process to launch an application with a given file as a
69  * parameter. Provide a parent window for error dialogs.
70  *
71  * @application: The application to be launched.
72  * @uris: The files whose locations should be passed as a parameter to the application.
73  * @parent_window: A window to use as the parent for any error dialogs.
74  */
75 void
nemo_launch_application(GAppInfo * application,GList * files,GtkWindow * parent_window)76 nemo_launch_application (GAppInfo *application,
77 			     GList *files,
78 			     GtkWindow *parent_window)
79 {
80 	GList *uris, *l;
81 
82 	uris = NULL;
83 	for (l = files; l != NULL; l = l->next) {
84 		uris = g_list_prepend (uris, nemo_file_get_activation_uri (l->data));
85 	}
86 	uris = g_list_reverse (uris);
87 	nemo_launch_application_by_uri (application, uris,
88 					    parent_window);
89 	g_list_free_full (uris, g_free);
90 }
91 
92 static void
dummy_child_watch(GPid pid,gint status,gpointer user_data)93 dummy_child_watch (GPid     pid,
94                    gint     status,
95                    gpointer user_data)
96 {
97   /* Nothing, this is just to ensure we don't double fork
98    * and break pkexec:
99    * https://bugzilla.gnome.org/show_bug.cgi?id=675789
100    */
101 }
102 
103 static void
gather_pid_callback(GDesktopAppInfo * appinfo,GPid pid,gpointer data)104 gather_pid_callback (GDesktopAppInfo *appinfo,
105                      GPid            pid,
106                      gpointer        data)
107 {
108     g_child_watch_add(pid, dummy_child_watch, NULL);
109 }
110 
111 void
nemo_launch_application_by_uri(GAppInfo * application,GList * uris,GtkWindow * parent_window)112 nemo_launch_application_by_uri (GAppInfo *application,
113 				    GList *uris,
114 				    GtkWindow *parent_window)
115 {
116 	char *uri;
117 	GList *locations, *l;
118 	GFile *location;
119 	NemoFile *file;
120 	gboolean result;
121 	GError *error;
122 	GdkDisplay *display;
123 	GdkAppLaunchContext *launch_context;
124 	NemoIconInfo *icon;
125 	int count;
126 
127 	g_assert (uris != NULL);
128 
129 	/* count the number of uris with local paths */
130 	count = 0;
131 	locations = NULL;
132 	for (l = uris; l != NULL; l = l->next) {
133 		uri = l->data;
134 
135 		location = g_file_new_for_uri (uri);
136 		if (g_file_is_native (location)) {
137 			count++;
138 		}
139 		locations = g_list_prepend (locations, location);
140 	}
141 	locations = g_list_reverse (locations);
142 
143 	if (parent_window != NULL) {
144 		display = gtk_widget_get_display (GTK_WIDGET (parent_window));
145 	} else {
146 		display = gdk_display_get_default ();
147 	}
148 
149 	launch_context = gdk_display_get_app_launch_context (display);
150 
151 	if (parent_window != NULL) {
152 		gdk_app_launch_context_set_screen (launch_context,
153 						   gtk_window_get_screen (parent_window));
154 	}
155 
156 	file = nemo_file_get_by_uri (uris->data);
157 	icon = nemo_file_get_icon (file,
158                                48, 0,
159                                gtk_widget_get_scale_factor (GTK_WIDGET (parent_window)),
160                                0);
161 	nemo_file_unref (file);
162 	if (icon) {
163 		gdk_app_launch_context_set_icon_name (launch_context,
164 							nemo_icon_info_get_used_name (icon));
165 		nemo_icon_info_unref (icon);
166 	}
167 
168 	error = NULL;
169 
170     result = g_desktop_app_info_launch_uris_as_manager (G_DESKTOP_APP_INFO (application),
171                                                         uris,
172                                                         G_APP_LAUNCH_CONTEXT (launch_context),
173                                                         G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
174                                                         NULL, NULL,
175                                                         gather_pid_callback, application,
176                                                         &error);
177 
178     g_object_unref (launch_context);
179 
180 	if (result) {
181 		for (l = uris; l != NULL; l = l->next) {
182 			file = nemo_file_get_by_uri (l->data);
183 			nemo_recent_add_file (file, application);
184 			nemo_file_unref (file);
185 		}
186 	}
187 
188 	g_list_free_full (locations, g_object_unref);
189 }
190 
191 static void
launch_application_from_command_internal(const gchar * full_command,GdkScreen * screen,gboolean use_terminal)192 launch_application_from_command_internal (const gchar *full_command,
193 					  GdkScreen *screen,
194 					  gboolean use_terminal)
195 {
196 	GAppInfo *app;
197 	GdkAppLaunchContext *ctx;
198 	GdkDisplay *display;
199 
200 	if (use_terminal) {
201 		eel_gnome_open_terminal_on_screen (full_command, screen);
202 	} else {
203 		app = g_app_info_create_from_commandline (full_command, NULL, 0, NULL);
204 
205 		if (app != NULL) {
206 			display = gdk_screen_get_display (screen);
207 			ctx = gdk_display_get_app_launch_context (display);
208 			gdk_app_launch_context_set_screen (ctx, screen);
209 
210 			g_app_info_launch (app, NULL, G_APP_LAUNCH_CONTEXT (ctx), NULL);
211 
212 			g_object_unref (app);
213 			g_object_unref (ctx);
214 		}
215 	}
216 }
217 
218 /**
219  * nemo_launch_application_from_command:
220  *
221  * Fork off a process to launch an application with a given uri as
222  * a parameter.
223  *
224  * @command_string: The application to be launched, with any desired
225  * command-line options.
226  * @...: Passed as parameters to the application after quoting each of them.
227  */
228 void
nemo_launch_application_from_command(GdkScreen * screen,const char * command_string,gboolean use_terminal,...)229 nemo_launch_application_from_command (GdkScreen  *screen,
230 					  const char *command_string,
231 					  gboolean use_terminal,
232 					  ...)
233 {
234 	char *full_command, *tmp;
235 	char *quoted_parameter;
236 	char *parameter;
237 	va_list ap;
238 
239 	full_command = g_strdup (command_string);
240 
241 	va_start (ap, use_terminal);
242 
243 	while ((parameter = va_arg (ap, char *)) != NULL) {
244 		quoted_parameter = g_shell_quote (parameter);
245 		tmp = g_strconcat (full_command, " ", quoted_parameter, NULL);
246 		g_free (quoted_parameter);
247 
248 		g_free (full_command);
249 		full_command = tmp;
250 
251 	}
252 
253 	va_end (ap);
254 
255 	launch_application_from_command_internal (full_command, screen, use_terminal);
256 
257 	g_free (full_command);
258 }
259 
260 /**
261  * nemo_launch_application_from_command:
262  *
263  * Fork off a process to launch an application with a given uri as
264  * a parameter.
265  *
266  * @command_string: The application to be launched, with any desired
267  * command-line options.
268  * @parameters: Passed as parameters to the application after quoting each of them.
269  */
270 void
nemo_launch_application_from_command_array(GdkScreen * screen,const char * command_string,gboolean use_terminal,const char * const * parameters)271 nemo_launch_application_from_command_array (GdkScreen  *screen,
272 						const char *command_string,
273 						gboolean use_terminal,
274 						const char * const * parameters)
275 {
276 	char *full_command, *tmp;
277 	char *quoted_parameter;
278 	const char * const *p;
279 
280 	full_command = g_strdup (command_string);
281 
282 	if (parameters != NULL) {
283 		for (p = parameters; *p != NULL; p++) {
284 			quoted_parameter = g_shell_quote (*p);
285 			tmp = g_strconcat (full_command, " ", quoted_parameter, NULL);
286 			g_free (quoted_parameter);
287 
288 			g_free (full_command);
289 			full_command = tmp;
290 		}
291 	}
292 
293 	launch_application_from_command_internal (full_command, screen, use_terminal);
294 
295 	g_free (full_command);
296 }
297 
298 void
nemo_launch_desktop_file(GdkScreen * screen,const char * desktop_file_uri,const GList * parameter_uris,GtkWindow * parent_window)299 nemo_launch_desktop_file (GdkScreen   *screen,
300 			      const char  *desktop_file_uri,
301 			      const GList *parameter_uris,
302 			      GtkWindow   *parent_window)
303 {
304 	GError *error;
305 	char *message, *desktop_file_path;
306 	const GList *p;
307 	GList *files;
308 	int total, count;
309 	GFile *file, *desktop_file;
310 	GDesktopAppInfo *app_info;
311 	GdkAppLaunchContext *context;
312 
313 	/* Don't allow command execution from remote locations
314 	 * to partially mitigate the security
315 	 * risk of executing arbitrary commands.
316 	 */
317 	desktop_file = g_file_new_for_uri (desktop_file_uri);
318 	desktop_file_path = g_file_get_path (desktop_file);
319 	if (!g_file_is_native (desktop_file)) {
320 		g_free (desktop_file_path);
321 		g_object_unref (desktop_file);
322 		eel_show_error_dialog
323 			(_("Sorry, but you cannot execute commands from "
324 			   "a remote site."),
325 			 _("This is disabled due to security considerations."),
326 			 parent_window);
327 
328 		return;
329 	}
330 	g_object_unref (desktop_file);
331 
332 	app_info = g_desktop_app_info_new_from_filename (desktop_file_path);
333 	g_free (desktop_file_path);
334 	if (app_info == NULL) {
335 		eel_show_error_dialog
336 			(_("There was an error launching the application."),
337 			 NULL,
338 			 parent_window);
339 		return;
340 	}
341 
342 	/* count the number of uris with local paths */
343 	count = 0;
344 	total = g_list_length ((GList *) parameter_uris);
345 	files = NULL;
346 	for (p = parameter_uris; p != NULL; p = p->next) {
347 		file = g_file_new_for_uri ((const char *) p->data);
348 		if (g_file_is_native (file)) {
349 			count++;
350 		}
351 		files = g_list_prepend (files, file);
352 	}
353 
354 	/* check if this app only supports local files */
355 	if (g_app_info_supports_files (G_APP_INFO (app_info)) &&
356 	    !g_app_info_supports_uris (G_APP_INFO (app_info)) &&
357 	    parameter_uris != NULL) {
358 		if (count == 0) {
359 			/* all files are non-local */
360 			eel_show_error_dialog
361 				(_("This drop target only supports local files."),
362 				 _("To open non-local files copy them to a local folder and then"
363 				   " drop them again."),
364 				 parent_window);
365 
366 			g_list_free_full (files, g_object_unref);
367 			g_object_unref (app_info);
368 			return;
369 		} else if (count != total) {
370 			/* some files are non-local */
371 			eel_show_warning_dialog
372 				(_("This drop target only supports local files."),
373 				 _("To open non-local files copy them to a local folder and then"
374 				   " drop them again. The local files you dropped have already been opened."),
375 				 parent_window);
376 		}
377 	}
378 
379 	error = NULL;
380 	context = gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (parent_window)));
381 	/* TODO: Ideally we should accept a timestamp here instead of using GDK_CURRENT_TIME */
382 	gdk_app_launch_context_set_timestamp (context, GDK_CURRENT_TIME);
383 	gdk_app_launch_context_set_screen (context,
384 					   gtk_window_get_screen (parent_window));
385 
386     g_desktop_app_info_launch_uris_as_manager (app_info,
387                                                (GList *) parameter_uris,
388                                                G_APP_LAUNCH_CONTEXT (context),
389                                                G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
390                                                NULL, NULL,
391                                                gather_pid_callback, app_info,
392                                                &error);
393 
394 	if (error != NULL) {
395 		message = g_strconcat (_("Details: "), error->message, NULL);
396 		eel_show_error_dialog
397 			(_("There was an error launching the application."),
398 			 message,
399 			 parent_window);
400 
401 		g_error_free (error);
402 		g_free (message);
403 	}
404 
405 	g_list_free_full (files, g_object_unref);
406 	g_object_unref (context);
407 	g_object_unref (app_info);
408 }
409