r4523: Updated headers:
[rox-filer/dt.git] / ROX-Filer / src / run.c
blob9d06a7b16d365984ab312342d06ede3b6e0997f7
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* run.c */
22 #include "config.h"
24 #include <errno.h>
25 #include <string.h>
26 #include <sys/param.h>
28 #include "global.h"
30 #include "run.h"
31 #include "support.h"
32 #include "gui_support.h"
33 #include "filer.h"
34 #include "display.h"
35 #include "main.h"
36 #include "type.h"
37 #include "dir.h"
38 #include "diritem.h"
39 #include "action.h"
40 #include "icon.h"
42 /* Static prototypes */
43 static void write_data(gpointer data, gint fd, GdkInputCondition cond);
44 static gboolean follow_symlink(const char *full_path,
45 FilerWindow *filer_window,
46 FilerWindow *src_window);
47 static gboolean open_file(const guchar *path, MIME_type *type);
48 static void open_mountpoint(const guchar *full_path, DirItem *item,
49 FilerWindow *filer_window, FilerWindow *src_window,
50 gboolean edit);
51 static gboolean run_desktop(const char *full_path,
52 const char **args, const char *dir);
53 static gboolean type_open(const char *path, MIME_type *type);
55 typedef struct _PipedData PipedData;
57 struct _PipedData
59 guchar *data;
60 gint tag;
61 gulong sent;
62 gulong length;
66 /****************************************************************
67 * EXTERNAL INTERFACE *
68 ****************************************************************/
71 /* An application has been double-clicked (or run in some other way) */
72 void run_app(const char *path)
74 GString *apprun;
75 const char *argv[] = {NULL, NULL};
77 apprun = g_string_new(path);
78 argv[0] = g_string_append(apprun, "/AppRun")->str;
80 rox_spawn(home_dir, argv);
82 g_string_free(apprun, TRUE);
85 /* Execute this program, passing all the URIs in the list as arguments.
86 * URIs that are files on the local machine will be passed as simple
87 * pathnames. The uri_list should be freed after this function returns.
89 void run_with_files(const char *path, GList *uri_list)
91 const char **argv;
92 int argc = 0, i;
93 struct stat info;
94 MIME_type *type;
96 if (stat(path, &info))
98 delayed_error(_("Program %s not found - deleted?"), path);
99 return;
102 argv = g_malloc(sizeof(char *) * (g_list_length(uri_list) + 2));
104 if (S_ISDIR(info.st_mode))
105 argv[argc++] = make_path(path, "AppRun");
106 else
107 argv[argc++] = path;
109 while (uri_list)
111 const EscapedPath *uri = uri_list->data;
112 char *local;
114 local = get_local_path(uri);
115 if (local)
116 argv[argc++] = local;
117 else
118 argv[argc++] = unescape_uri(uri);
119 uri_list = uri_list->next;
122 argv[argc++] = NULL;
124 type = type_from_path(argv[0]);
125 if (type && type == application_x_desktop)
127 run_desktop(argv[0], argv + 1, home_dir);
129 else
131 rox_spawn(home_dir, argv);
134 for (i = 1; i < argc; i++)
135 g_free((gchar *) argv[i]);
136 g_free(argv);
139 /* Run the program as '<path> -', piping the data to it via stdin.
140 * You can g_free() the data as soon as this returns.
142 void run_with_data(const char *path, gpointer data, gulong length)
144 const char *argv[] = {NULL, "-", NULL};
145 struct stat info;
146 int fds[2];
147 PipedData *pd;
149 if (stat(path, &info))
151 delayed_error(_("Program %s not found - deleted?"), path);
152 return;
155 if (S_ISDIR(info.st_mode))
156 argv[0] = make_path(path, "AppRun");
157 else
158 argv[0] = path;
160 if (pipe(fds))
162 delayed_error("pipe: %s", g_strerror(errno));
163 return;
165 close_on_exec(fds[1], TRUE);
166 close_on_exec(fds[0], TRUE);
168 switch (fork())
170 case -1:
171 delayed_error("fork: %s", g_strerror(errno));
172 close(fds[1]);
173 break;
174 case 0:
175 /* We are the child */
176 chdir(home_dir);
177 if (dup2(fds[0], 0) == -1)
178 g_warning("dup2() failed: %s\n",
179 g_strerror(errno));
180 else
182 close_on_exec(0, FALSE);
183 if (execv(argv[0], (char **) argv))
184 g_warning("execv(%s) failed: %s\n",
185 argv[0], g_strerror(errno));
187 _exit(1);
188 default:
189 /* We are the parent */
190 set_blocking(fds[1], FALSE);
191 pd = g_new(PipedData, 1);
192 pd->data = g_malloc(length);
193 memcpy(pd->data, data, length);
194 pd->length = length;
195 pd->sent = 0;
196 pd->tag = gdk_input_add_full(fds[1], GDK_INPUT_WRITE,
197 write_data, pd, NULL);
198 break;
201 close(fds[0]);
204 /* Splits args into an argument vector, and runs the program. Must be
205 * executable.
207 void run_with_args(const char *path, DirItem *item, const char *args)
209 GError *error = NULL;
210 gchar **argv = NULL;
211 int n_args = 0;
213 if (item->base_type != TYPE_DIRECTORY && item->base_type != TYPE_FILE)
215 delayed_error("Arguments (%s) given for non-executable item %s",
216 args, path);
217 return;
220 if (!g_shell_parse_argv(args, &n_args, &argv, &error))
222 delayed_error("Failed to parse argument string '%s':\n%s",
223 args, error->message);
224 g_error_free(error);
225 return;
228 g_return_if_fail(argv != NULL);
229 g_return_if_fail(error == NULL);
231 argv = g_realloc(argv, (n_args + 2) * sizeof(gchar *));
232 memmove(argv + 1, argv, (n_args + 1) * sizeof(gchar *));
234 if (item->base_type == TYPE_DIRECTORY)
235 argv[0] = g_strconcat(path, "/AppRun", NULL);
236 else
237 argv[0] = g_strdup(path);
239 rox_spawn(home_dir, (const gchar **) argv);
241 g_strfreev(argv);
244 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
245 * edit a file, open an application, follow a symlink or mount a device.
247 * filer_window is the window to use for displaying a directory.
248 * NULL will always use a new directory when needed.
249 * src_window is the window to copy options from, or NULL.
251 * Returns TRUE on success.
253 gboolean run_diritem(const guchar *full_path,
254 DirItem *item,
255 FilerWindow *filer_window,
256 FilerWindow *src_window,
257 gboolean edit)
259 if (item->flags & ITEM_FLAG_SYMLINK && edit)
260 return follow_symlink(full_path, filer_window, src_window);
262 switch (item->base_type)
264 case TYPE_DIRECTORY:
265 if (item->flags & ITEM_FLAG_APPDIR && !edit)
267 run_app(full_path);
268 return TRUE;
271 if (item->flags & ITEM_FLAG_MOUNT_POINT)
273 open_mountpoint(full_path, item,
274 filer_window, src_window, edit);
276 else if (filer_window)
277 filer_change_to(filer_window, full_path, NULL);
278 else
279 filer_opendir(full_path, src_window, NULL);
280 return TRUE;
281 case TYPE_FILE:
282 if (EXECUTABLE_FILE(item) && !edit)
284 const char *argv[] = {NULL, NULL};
285 guchar *dir = filer_window
286 ? filer_window->sym_path
287 : NULL;
289 if (item->mime_type == application_x_desktop)
290 return run_desktop(full_path,
291 NULL, dir);
292 else
293 argv[0] = full_path;
295 return rox_spawn(dir, argv) != 0;
298 return open_file(full_path, edit ? text_plain
299 : item->mime_type);
300 case TYPE_ERROR:
301 delayed_error(_("File doesn't exist, or I can't "
302 "access it: %s"), full_path);
303 return FALSE;
304 default:
305 delayed_error(
306 _("I don't know how to open '%s'"), full_path);
307 return FALSE;
311 /* Attempt to open this item */
312 gboolean run_by_path(const guchar *full_path)
314 gboolean retval;
315 DirItem *item;
317 /* XXX: Loads an image - wasteful */
318 item = diritem_new("");
319 diritem_restat(full_path, item, NULL);
320 retval = run_diritem(full_path, item, NULL, NULL, FALSE);
321 diritem_free(item);
323 return retval;
326 /* Open dir/Help, or show a message if missing */
327 void show_help_files(const char *dir)
329 const char *help_dir;
331 help_dir = make_path(dir, "Help");
333 if (file_exists(help_dir))
334 filer_opendir(help_dir, NULL, NULL);
335 else
336 info_message(
337 _("Application:\n"
338 "This is an application directory - you can "
339 "run it as a program, or open it (hold down "
340 "Shift while you open it). Most applications provide "
341 "their own help here, but this one doesn't."));
344 /* Open a directory viewer showing this file, and wink it */
345 void open_to_show(const guchar *path)
347 FilerWindow *new;
348 guchar *dir, *slash;
350 g_return_if_fail(path != NULL);
352 dir = g_strdup(path);
353 slash = strrchr(dir, '/');
354 if (slash == dir || !slash)
356 /* Item in the root (or root itself!) */
357 new = filer_opendir("/", NULL, NULL);
358 if (new && dir[1])
359 display_set_autoselect(new, dir + 1);
361 else
363 *slash = '\0';
364 new = filer_opendir(dir, NULL, NULL);
365 if (new)
367 if (slash[1] == '.')
368 display_set_hidden(new, TRUE);
369 display_set_autoselect(new, slash + 1);
373 g_free(dir);
376 /* Invoked using -x, this indicates that the filesystem has been modified
377 * and we should look at this item again.
379 void examine(const guchar *path)
381 struct stat info;
383 if (mc_stat(path, &info) != 0)
385 /* Deleted? Do a paranoid update of everything... */
386 filer_check_mounted(path);
388 else
390 /* Update directory containing this item... */
391 dir_check_this(path);
393 /* If this is itself a directory then rescan its contents... */
394 if (S_ISDIR(info.st_mode))
395 refresh_dirs(path);
397 /* If it's on the pinboard, update the icon... */
398 icons_may_update(path);
402 /****************************************************************
403 * INTERNAL FUNCTIONS *
404 ****************************************************************/
407 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
409 PipedData *pd = (PipedData *) data;
411 while (pd->sent < pd->length)
413 int sent;
415 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
417 if (sent < 0)
419 if (errno == EAGAIN)
420 return;
421 delayed_error(_("Could not send data to program: %s"),
422 g_strerror(errno));
423 goto finish;
426 pd->sent += sent;
429 finish:
430 g_source_remove(pd->tag);
431 g_free(pd->data);
432 g_free(pd);
433 close(fd);
436 /* Follow the link 'full_path' and display it in filer_window, or a
437 * new window if that is NULL.
439 static gboolean follow_symlink(const char *full_path,
440 FilerWindow *filer_window,
441 FilerWindow *src_window)
443 char *real, *slash;
444 char *new_dir;
445 char path[MAXPATHLEN + 1];
446 int got;
448 got = readlink(full_path, path, MAXPATHLEN);
449 if (got < 0)
451 delayed_error(_("Could not read link: %s"),
452 g_strerror(errno));
453 return FALSE;
456 g_return_val_if_fail(got <= MAXPATHLEN, FALSE);
457 path[got] = '\0';
459 /* Make a relative path absolute */
460 if (path[0] != '/')
462 guchar *tmp;
463 slash = strrchr(full_path, '/');
464 g_return_val_if_fail(slash != NULL, FALSE);
466 tmp = g_strndup(full_path, slash - full_path);
467 real = pathdup(make_path(tmp, path));
468 /* NB: full_path may be invalid here... */
469 g_free(tmp);
471 else
472 real = pathdup(path);
474 slash = strrchr(real, '/');
475 if (!slash)
477 g_free(real);
478 delayed_error(
479 _("Broken symlink (or you don't have permission "
480 "to follow it): %s"), full_path);
481 return FALSE;
484 *slash = '\0';
486 if (*real)
487 new_dir = real;
488 else
489 new_dir = "/";
491 if (filer_window)
492 filer_change_to(filer_window, new_dir, slash + 1);
493 else
495 FilerWindow *new;
497 new = filer_opendir(new_dir, src_window, NULL);
498 if (new)
499 display_set_autoselect(new, slash + 1);
502 g_free(real);
504 return TRUE;
507 /* Load this file into an appropriate editor */
508 static gboolean open_file(const guchar *path, MIME_type *type)
510 g_return_val_if_fail(type != NULL, FALSE);
512 if (type_open(path, type))
513 return TRUE;
515 report_error(
516 _("No run action specified for files of this type (%s/%s) - "
517 "you can set a run action by choosing `Set Run Action' "
518 "from the File menu, or you can just drag the file to an "
519 "application.%s"),
520 type->media_type,
521 type->subtype,
522 type->executable ? _("\n\nNote: If this is a computer program which "
523 "you want to run, you need to set the execute bit "
524 "by choosing Permissions from the File menu.")
525 : "");
527 return FALSE;
530 /* Called like run_diritem, when a mount-point is opened */
531 static void open_mountpoint(const guchar *full_path, DirItem *item,
532 FilerWindow *filer_window, FilerWindow *src_window,
533 gboolean edit)
535 gboolean mounted = (item->flags & ITEM_FLAG_MOUNTED) != 0;
537 if (mounted == edit)
539 GList *paths;
541 paths = g_list_prepend(NULL, (gpointer) full_path);
542 action_mount(paths, filer_window == NULL, !mounted, -1);
543 g_list_free(paths);
544 if (filer_window && !mounted)
545 filer_change_to(filer_window, full_path, NULL);
547 else
549 if (filer_window)
550 filer_change_to(filer_window, full_path, NULL);
551 else
552 filer_opendir(full_path, src_window, NULL);
556 /* full_path is a .desktop file. Execute the application, using the Exec line
557 * from the file.
558 * Returns TRUE on success.
560 static gboolean run_desktop(const char *full_path,
561 const char **args,
562 const char *dir)
564 GError *error = NULL;
565 char *exec = NULL;
566 gint argc = 0;
567 gchar **argv = NULL;
568 GPtrArray *expanded = NULL;
569 int i;
570 gboolean success = FALSE;
572 exec = get_value_from_desktop_file(full_path, "Desktop Entry", "Exec",
573 &error);
574 if (error)
576 delayed_error("Failed to parse .desktop file '%s':\n%s",
577 full_path, error->message);
578 goto err;
581 if (!exec)
583 delayed_error("Can't find Exec command in .desktop file '%s'",
584 full_path);
585 goto err;
588 if (!g_shell_parse_argv(exec, &argc, &argv, &error))
590 delayed_error("Failed to parse '%s' from '%s':\n%s",
591 exec, full_path, error->message);
592 goto err;
595 expanded = g_ptr_array_new();
596 for (i = 0; i < argc; i++)
598 const char *src = argv[i];
600 if (src[0] == '%' && src[1] != '\0' && src[2] == '\0')
602 /* We should treat these four differently. */
603 if (src[1] == 'f' || src[1] == 'F' ||
604 src[1] == 'u' || src[1] == 'U')
606 int j;
607 for (j = 0; args && args[j]; j++)
608 g_ptr_array_add(expanded, g_strdup(args[j]));
610 else
612 delayed_error("Unsupported escape character in '%s' in '%s'",
613 exec, full_path);
614 goto err;
617 else
619 g_ptr_array_add(expanded, g_strdup(src));
622 g_ptr_array_add(expanded, NULL);
624 success = rox_spawn(dir, (const gchar **) expanded->pdata);
625 err:
626 if (error != NULL)
627 g_error_free(error);
628 if (exec != NULL)
629 g_free(exec);
630 if (argv != NULL)
631 g_strfreev(argv);
632 if (expanded != NULL)
634 g_ptr_array_foreach(expanded, (GFunc) g_free, NULL);
635 g_ptr_array_free(expanded, TRUE);
638 return success;
641 /* Returns FALSE is no run action is set for this type. */
642 static gboolean type_open(const char *path, MIME_type *type)
644 gchar *argv[] = {NULL, NULL, NULL};
645 char *open;
646 struct stat info;
648 argv[1] = (char *) path;
650 open = handler_for(type);
651 if (!open)
652 return FALSE;
654 if (stat(open, &info))
656 report_error("stat(%s): %s", open, g_strerror(errno));
657 g_free(open);
658 return TRUE;
661 if (info.st_mode & S_IWOTH)
663 gchar *choices_dir;
664 GList *paths;
666 report_error(_("Executable '%s' is world-writeable! Refusing "
667 "to run. Please change the permissions now (this "
668 "problem may have been caused by a bug in earlier "
669 "versions of the filer).\n\n"
670 "Having (non-symlink) run actions world-writeable "
671 "means that other people who use your computer can "
672 "replace your run actions with malicious versions.\n\n"
673 "If you trust everyone who could write to these files "
674 "then you needn't worry. Otherwise, you should check, "
675 "or even just delete, all the existing run actions."),
676 open);
677 choices_dir = g_path_get_dirname(open);
678 paths = g_list_append(NULL, choices_dir);
679 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
680 g_free(choices_dir);
681 g_list_free(paths);
682 g_free(open);
683 return TRUE;
686 if (S_ISDIR(info.st_mode))
688 argv[0] = g_strconcat(open, "/AppRun", NULL);
689 rox_spawn(home_dir, (const gchar **) argv) != 0;
691 else if (type_get_type(open) == application_x_desktop)
693 argv[0] = open;
694 run_desktop(open, (const char **) (argv + 1), home_dir);
696 else
698 argv[0] = open;
699 rox_spawn(home_dir, (const gchar **) argv) != 0;
702 if (argv[0] != open)
703 g_free(argv[0]);
705 g_free(open);
707 return TRUE;