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