Mounting and unmounting works again.
[rox-filer.git] / ROX-Filer / src / run.c
blobea784e73f1d79f5bfb7f8249e204676599487602
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 <gio/gio.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/param.h>
29 #include "global.h"
31 #include "run.h"
32 #include "support.h"
33 #include "gui_support.h"
34 #include "filer.h"
35 #include "display.h"
36 #include "main.h"
37 #include "type.h"
38 #include "dir.h"
39 #include "diritem.h"
40 #include "action.h"
41 #include "icon.h"
42 #include "choices.h"
44 /* Static prototypes */
45 static void write_data(gpointer data, gint fd, GdkInputCondition cond);
46 static gboolean follow_symlink(GFile *path,
47 FilerWindow *filer_window,
48 FilerWindow *src_window);
49 static gboolean open_file(GFile *path, MIME_type *type);
50 static void open_mountpoint(GFile *full_path, DirItem *item,
51 FilerWindow *filer_window, FilerWindow *src_window,
52 gboolean edit);
53 static gboolean run_desktop(const char *full_path,
54 const char **args, const char *dir);
55 static gboolean type_open(GFile *path, MIME_type *type);
57 typedef struct _PipedData PipedData;
59 struct _PipedData
61 guchar *data;
62 gint tag;
63 gulong sent;
64 gulong length;
68 /****************************************************************
69 * EXTERNAL INTERFACE *
70 ****************************************************************/
73 /* An application has been double-clicked (or run in some other way) */
74 void run_app(const char *path)
76 GString *apprun;
77 const char *argv[] = {NULL, NULL};
79 apprun = g_string_new(path);
80 argv[0] = g_string_append(apprun, "/AppRun")->str;
82 rox_spawn(home_dir, argv);
84 g_string_free(apprun, TRUE);
87 /* Execute this program, passing all the URIs in the list as arguments.
88 * URIs that are files on the local machine will be passed as simple
89 * pathnames. The uri_list should be freed after this function returns.
91 void run_with_files(const char *path, GList *uri_list)
93 const char **argv;
94 int argc = 0, i;
95 struct stat info;
96 MIME_type *type;
98 if (stat(path, &info))
100 delayed_error(_("Program %s not found - deleted?"), path);
101 return;
104 argv = g_malloc(sizeof(char *) * (g_list_length(uri_list) + 2));
106 if (S_ISDIR(info.st_mode))
107 argv[argc++] = make_path(path, "AppRun");
108 else
109 argv[argc++] = path;
111 while (uri_list)
113 const EscapedPath *uri = uri_list->data;
114 char *local;
116 local = get_local_path(uri);
117 if (local)
118 argv[argc++] = local;
119 else
120 argv[argc++] = unescape_uri(uri);
121 uri_list = uri_list->next;
124 argv[argc++] = NULL;
126 type = type_from_path(argv[0]);
127 if (type && type == application_x_desktop)
129 run_desktop(argv[0], argv + 1, home_dir);
131 else
133 rox_spawn(home_dir, argv);
136 for (i = 1; i < argc; i++)
137 g_free((gchar *) argv[i]);
138 g_free(argv);
141 /* Run the program as '<path> -', piping the data to it via stdin.
142 * You can g_free() the data as soon as this returns.
144 void run_with_data(const char *path, gpointer data, gulong length)
146 const char *argv[] = {NULL, "-", NULL};
147 struct stat info;
148 int fds[2];
149 PipedData *pd;
151 if (stat(path, &info))
153 delayed_error(_("Program %s not found - deleted?"), path);
154 return;
157 if (S_ISDIR(info.st_mode))
158 argv[0] = make_path(path, "AppRun");
159 else
160 argv[0] = path;
162 if (pipe(fds))
164 delayed_error("pipe: %s", g_strerror(errno));
165 return;
167 close_on_exec(fds[1], TRUE);
168 close_on_exec(fds[0], TRUE);
170 switch (fork())
172 case -1:
173 delayed_error("fork: %s", g_strerror(errno));
174 close(fds[1]);
175 break;
176 case 0:
177 /* We are the child */
178 chdir(home_dir);
179 if (dup2(fds[0], 0) == -1)
180 g_warning("dup2() failed: %s\n",
181 g_strerror(errno));
182 else
184 close_on_exec(0, FALSE);
185 if (execv(argv[0], (char **) argv))
186 g_warning("execv(%s) failed: %s\n",
187 argv[0], g_strerror(errno));
189 _exit(1);
190 default:
191 /* We are the parent */
192 set_blocking(fds[1], FALSE);
193 pd = g_new(PipedData, 1);
194 pd->data = g_malloc(length);
195 memcpy(pd->data, data, length);
196 pd->length = length;
197 pd->sent = 0;
198 pd->tag = gdk_input_add_full(fds[1], GDK_INPUT_WRITE,
199 write_data, pd, NULL);
200 break;
203 close(fds[0]);
206 /* Splits args into an argument vector, and runs the program. Must be
207 * executable.
209 void run_with_args(const char *path, DirItem *item, const char *args)
211 GError *error = NULL;
212 gchar **argv = NULL;
213 int n_args = 0;
215 if (item->base_type != TYPE_DIRECTORY && item->base_type != TYPE_FILE)
217 delayed_error("Arguments (%s) given for non-executable item %s",
218 args, path);
219 return;
222 if (!g_shell_parse_argv(args, &n_args, &argv, &error))
224 delayed_error("Failed to parse argument string '%s':\n%s",
225 args, error->message);
226 g_error_free(error);
227 return;
230 g_return_if_fail(argv != NULL);
231 g_return_if_fail(error == NULL);
233 argv = g_realloc(argv, (n_args + 2) * sizeof(gchar *));
234 memmove(argv + 1, argv, (n_args + 1) * sizeof(gchar *));
236 if (item->base_type == TYPE_DIRECTORY)
237 argv[0] = g_strconcat(path, "/AppRun", NULL);
238 else
239 argv[0] = g_strdup(path);
241 rox_spawn(home_dir, (const gchar **) argv);
243 g_strfreev(argv);
246 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
247 * edit a file, open an application, follow a symlink or mount a device.
249 * filer_window is the window to use for displaying a directory.
250 * NULL will always use a new directory when needed.
251 * src_window is the window to copy options from, or NULL.
253 * Returns TRUE on success.
255 gboolean run_diritem(const guchar *full_path,
256 DirItem *item,
257 FilerWindow *filer_window,
258 FilerWindow *src_window,
259 gboolean edit)
261 GFile *gfile;
262 gboolean retval;
264 gfile = g_file_new_for_path(full_path);
265 retval = run_diritem_gfile(gfile, item, filer_window, src_window, edit);
266 g_object_unref(gfile);
268 return retval;
271 gboolean run_diritem_gfile(GFile *path,
272 DirItem *item,
273 FilerWindow *filer_window,
274 FilerWindow *src_window,
275 gboolean edit)
277 if (item->flags & ITEM_FLAG_SYMLINK && edit)
278 return follow_symlink(path, filer_window, src_window);
280 const char *full_path = "TODO";
282 switch (item->base_type)
284 case TYPE_DIRECTORY:
285 if (item->flags & ITEM_FLAG_APPDIR && !edit)
287 char *pathname;
288 pathname = g_file_get_path(path);
289 if (!pathname)
291 delayed_error(_("Can't run a remote application!"));
292 return FALSE;
294 run_app(pathname);
295 g_free(pathname);
296 return TRUE;
299 if (item->flags & ITEM_FLAG_MOUNT_POINT)
300 open_mountpoint(path, item, filer_window, src_window, edit);
301 else if (filer_window)
302 filer_change_to_gfile(filer_window, path, NULL);
303 else
304 filer_opendir_gfile(path, src_window, NULL);
305 return TRUE;
306 case TYPE_FILE:
307 if (EXECUTABLE_FILE(item) && !edit)
309 gboolean retval;
310 const char *argv[] = {NULL, NULL};
311 char *arg;
312 guchar *dir = filer_window
313 ? filer_window->sym_path
314 : NULL;
316 if (item->mime_type == application_x_desktop)
317 return run_desktop(full_path, NULL, dir);
319 arg = g_file_get_path(path);
320 if (arg == NULL)
321 arg = g_file_get_uri(path);
323 argv[0] = arg;
324 retval = rox_spawn(dir, argv) != 0;
325 g_free(arg);
327 return retval;
330 return open_file(path, edit ? text_plain : item->mime_type);
331 case TYPE_ERROR:
332 delayed_error(_("File doesn't exist, or I can't "
333 "access it: %s"), full_path);
334 return FALSE;
335 default:
336 delayed_error(
337 _("I don't know how to open '%s'"), full_path);
338 return FALSE;
342 /* Attempt to open this item */
343 gboolean run_by_path(const guchar *full_path)
345 gboolean retval;
346 DirItem *item;
348 /* XXX: Loads an image - wasteful */
349 item = diritem_new("");
350 diritem_restat(full_path, item, NULL);
351 retval = run_diritem(full_path, item, NULL, NULL, FALSE);
352 diritem_free(item);
354 return retval;
357 /* Attempt to open this item */
358 gboolean run_by_gfile(GFile *path)
360 gboolean retval;
361 DirItem *item;
363 /* XXX: Loads an image - wasteful */
364 item = diritem_new("");
365 diritem_restat_gfile(path, item);
366 retval = run_diritem_gfile(path, item, NULL, NULL, FALSE);
367 diritem_free(item);
369 return retval;
372 /* Convert uri to path and call run_by_path() */
373 gboolean run_by_uri(const gchar *uri, gchar **errmsg)
375 gboolean retval;
376 gchar *scheme;
377 gchar *cmd;
379 scheme=get_uri_scheme((EscapedPath *) uri);
380 if(!scheme)
382 *errmsg=g_strdup_printf(_("'%s' is not a valid URI"),
383 uri);
384 return FALSE;
387 if(strcmp(scheme, "file")==0 || strcmp(scheme, "trash")==0) {
388 GFile *gfile;
389 gfile = g_file_new_for_uri(uri);
391 retval = run_by_gfile(gfile);
392 g_object_unref(gfile);
394 if(!retval)
395 *errmsg=g_strdup_printf(_("%s not accessable"), uri);
397 } else if((cmd=choices_find_xdg_path_load(scheme, "URI", SITE))) {
398 DirItem *item;
400 item=diritem_new(scheme);
401 diritem_restat(cmd, item, NULL);
403 run_with_args(cmd, item, uri);
404 retval=TRUE; /* we hope... */
406 diritem_free(item);
407 g_free(cmd);
409 } else {
410 retval=FALSE;
411 *errmsg=g_strdup_printf(_("%s: no handler for %s"),
412 uri, scheme);
415 g_free(scheme);
417 return retval;
420 /* Open dir/Help, or show a message if missing */
421 void show_help_files(const char *dir)
423 const char *help_dir;
425 help_dir = make_path(dir, "Help");
427 if (file_exists(help_dir))
428 filer_opendir(help_dir, NULL, NULL);
429 else
430 info_message(
431 _("Application:\n"
432 "This is an application directory - you can "
433 "run it as a program, or open it (hold down "
434 "Shift while you open it). Most applications provide "
435 "their own help here, but this one doesn't."));
438 /* Open a directory viewer showing this file, and wink it */
439 void open_to_show(const guchar *path)
441 FilerWindow *new;
442 guchar *dir, *slash;
444 g_return_if_fail(path != NULL);
446 dir = g_strdup(path);
447 slash = strrchr(dir, '/');
448 if (slash == dir || !slash)
450 /* Item in the root (or root itself!) */
451 new = filer_opendir("/", NULL, NULL);
452 if (new && dir[1])
453 display_set_autoselect(new, dir + 1);
455 else
457 *slash = '\0';
458 new = filer_opendir(dir, NULL, NULL);
459 if (new)
461 if (slash[1] == '.')
462 display_set_hidden(new, TRUE);
463 display_set_autoselect(new, slash + 1);
467 g_free(dir);
470 /* Invoked using -x, this indicates that the filesystem has been modified
471 * and we should look at this item again.
473 void examine(const guchar *path)
475 struct stat info;
477 if (mc_stat(path, &info) != 0)
479 /* Deleted? Do a paranoid update of everything... */
480 filer_check_mounted(path);
482 else
484 /* Update directory containing this item... */
485 dir_check_this(path);
487 /* If this is itself a directory then rescan its contents... */
488 if (S_ISDIR(info.st_mode))
489 refresh_dirs(path);
491 /* If it's on the pinboard or a panel, update the icon... */
492 icons_may_update(path);
496 /****************************************************************
497 * INTERNAL FUNCTIONS *
498 ****************************************************************/
501 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
503 PipedData *pd = (PipedData *) data;
505 while (pd->sent < pd->length)
507 int sent;
509 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
511 if (sent < 0)
513 if (errno == EAGAIN)
514 return;
515 delayed_error(_("Could not send data to program: %s"),
516 g_strerror(errno));
517 goto finish;
520 pd->sent += sent;
523 finish:
524 g_source_remove(pd->tag);
525 g_free(pd->data);
526 g_free(pd);
527 close(fd);
530 static char *g_file_readlink(GFile *symlink, GError **error)
532 GFileInfo *info;
533 char *target;
535 info = g_file_query_info(symlink, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error);
536 if (*error)
537 return NULL;
539 target = (char *) g_file_info_get_symlink_target(info);
540 if (target)
541 target = g_strdup(target);
543 g_object_unref(info);
545 return target;
548 /* Follow the link 'full_path' and display it in filer_window, or a
549 * new window if that is NULL.
551 static gboolean follow_symlink(GFile *path,
552 FilerWindow *filer_window,
553 FilerWindow *src_window)
555 char *target, *leafname;
556 GFile *resolved, *new_dir;
557 GError *error = NULL;
559 target = g_file_readlink(path, &error);
561 if (error)
563 delayed_error(_("Could not read link: %s"), error->message);
564 g_error_free(error);
565 return FALSE;
568 resolved = g_file_resolve_relative_path(path, target);
569 g_free(target);
570 target = NULL;
572 new_dir = g_file_get_parent(resolved);
573 leafname = g_file_get_basename(resolved);
574 g_object_unref(resolved);
576 if (filer_window)
577 filer_change_to(filer_window, g_file_get_path(new_dir), leafname); /* XXX : free */
578 else
580 FilerWindow *new;
582 new = filer_opendir_gfile(new_dir, src_window, NULL);
583 if (new)
584 display_set_autoselect(new, leafname);
587 g_object_unref(new_dir);
588 g_free(leafname);
590 return TRUE;
593 /* Load this file into an appropriate editor */
594 static gboolean open_file(GFile *path, MIME_type *type)
596 g_return_val_if_fail(type != NULL, FALSE);
598 if (type_open(path, type))
599 return TRUE;
601 report_error(
602 _("No run action specified for files of this type (%s/%s) - "
603 "you can set a run action by choosing `Set Run Action' "
604 "from the File menu, or you can just drag the file to an "
605 "application.%s"),
606 type->media_type,
607 type->subtype,
608 type->executable ? _("\n\nNote: If this is a computer program which "
609 "you want to run, you need to set the execute bit "
610 "by choosing Permissions from the File menu.")
611 : "");
613 return FALSE;
616 /* Called like run_diritem, when a mount-point is opened */
617 static void open_mountpoint(GFile *full_path, DirItem *item,
618 FilerWindow *filer_window, FilerWindow *src_window,
619 gboolean edit)
621 gboolean mounted = (item->flags & ITEM_FLAG_MOUNTED) != 0;
623 if (mounted == edit)
625 GList *paths;
626 char *path;
628 path = g_file_get_path(full_path);
629 if (!path)
631 delayed_error(_("Can't mount/unmount on remote filesystems yet; sorry!"));
632 return;
635 paths = g_list_prepend(NULL, (gpointer) path);
636 action_mount(paths, filer_window == NULL, !mounted, -1);
637 g_list_free(paths);
638 if (filer_window && !mounted)
639 filer_change_to_gfile(filer_window, full_path, NULL);
641 else
643 if (filer_window)
644 filer_change_to_gfile(filer_window, full_path, NULL);
645 else
646 filer_opendir_gfile(full_path, src_window, NULL);
650 /* full_path is a .desktop file. Execute the application, using the Exec line
651 * from the file.
652 * Returns TRUE on success.
654 static gboolean run_desktop(const char *full_path,
655 const char **args,
656 const char *dir)
658 GError *error = NULL;
659 char *exec = NULL;
660 char *terminal = NULL;
661 char *req_dir = NULL;
662 gint argc = 0;
663 gchar **argv = NULL;
664 GPtrArray *expanded = NULL;
665 gboolean inserted_args = FALSE;
666 int i;
667 gboolean success = FALSE;
669 get_values_from_desktop_file(full_path,
670 &error,
671 "Desktop Entry", "Exec", &exec,
672 "Desktop Entry", "Terminal", &terminal,
673 "Desktop Entry", "Path", &req_dir,
674 NULL);
675 if (error)
677 delayed_error("Failed to parse .desktop file '%s':\n%s",
678 full_path, error->message);
679 goto err;
682 if (!exec)
684 delayed_error("Can't find Exec command in .desktop file '%s'",
685 full_path);
686 goto err;
689 if (!g_shell_parse_argv(exec, &argc, &argv, &error))
691 delayed_error("Failed to parse '%s' from '%s':\n%s",
692 exec, full_path, error->message);
693 goto err;
696 expanded = g_ptr_array_new();
698 if (terminal && g_strcasecmp(terminal, "true") == 0) {
699 g_ptr_array_add(expanded, g_strdup("xterm"));
700 g_ptr_array_add(expanded, g_strdup("-e"));
703 for (i = 0; i < argc; i++)
705 const char *src = argv[i];
707 if (src[0] == '%' && src[1] != '\0' && src[2] == '\0')
709 /* We should treat these four differently. */
710 if (src[1] == 'f' || src[1] == 'F' ||
711 src[1] == 'u' || src[1] == 'U')
713 int j;
714 for (j = 0; args && args[j]; j++)
715 g_ptr_array_add(expanded, g_strdup(args[j]));
716 inserted_args = TRUE;
718 else
720 delayed_error("Unsupported escape character in '%s' in '%s'",
721 exec, full_path);
722 goto err;
725 else
727 g_ptr_array_add(expanded, g_strdup(src));
730 if (!inserted_args)
732 /* Many .desktop files don't include a % expansion. In that case
733 * add the arguments here.
735 int j;
736 for (j = 0; args && args[j]; j++)
737 g_ptr_array_add(expanded, g_strdup(args[j]));
739 g_ptr_array_add(expanded, NULL);
741 if(req_dir)
742 dir = req_dir;
744 success = rox_spawn(dir, (const gchar **) expanded->pdata);
745 err:
746 if (error != NULL)
747 g_error_free(error);
748 if (exec != NULL)
749 g_free(exec);
750 if (terminal != NULL)
751 g_free(terminal);
752 if (req_dir != NULL)
753 g_free(req_dir);
754 if (argv != NULL)
755 g_strfreev(argv);
756 if (expanded != NULL)
758 g_ptr_array_foreach(expanded, (GFunc) g_free, NULL);
759 g_ptr_array_free(expanded, TRUE);
762 return success;
765 static char *path_or_uri(GFile *path)
767 char *retval;
769 retval = g_file_get_path(path);
770 if (retval == NULL)
771 retval = g_file_get_uri(path);
772 return retval;
775 /* Returns FALSE is no run action is set for this type. */
776 static gboolean type_open(GFile *path, MIME_type *type)
778 gchar *argv[] = {NULL, NULL, NULL};
779 char *open;
780 struct stat info;
782 open = handler_for(type);
783 if (!open)
784 return FALSE;
786 if (stat(open, &info))
788 report_error("stat(%s): %s", open, g_strerror(errno));
789 g_free(open);
790 return TRUE;
793 if (info.st_mode & S_IWOTH)
795 gchar *choices_dir;
796 GList *paths;
798 report_error(_("Executable '%s' is world-writeable! Refusing "
799 "to run. Please change the permissions now (this "
800 "problem may have been caused by a bug in earlier "
801 "versions of the filer).\n\n"
802 "Having (non-symlink) run actions world-writeable "
803 "means that other people who use your computer can "
804 "replace your run actions with malicious versions.\n\n"
805 "If you trust everyone who could write to these files "
806 "then you needn't worry. Otherwise, you should check, "
807 "or even just delete, all the existing run actions."),
808 open);
809 choices_dir = g_path_get_dirname(open);
810 paths = g_list_append(NULL, choices_dir);
811 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
812 g_free(choices_dir);
813 g_list_free(paths);
814 g_free(open);
815 return TRUE;
818 argv[1] = path_or_uri(path);
820 if (S_ISDIR(info.st_mode))
822 argv[0] = g_strconcat(open, "/AppRun", NULL);
823 rox_spawn(home_dir, (const gchar **) argv);
825 else if (type_get_type(open) == application_x_desktop)
827 argv[0] = open;
828 run_desktop(open, (const char **) (argv + 1), home_dir);
830 else
832 argv[0] = open;
833 rox_spawn(home_dir, (const gchar **) argv);
836 g_free(argv[1]);
838 if (argv[0] != open)
839 g_free(argv[0]);
841 g_free(open);
843 return TRUE;