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)
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
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
27 #include <sys/param.h>
33 #include "gui_support.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
,
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
;
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
)
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
)
98 if (stat(path
, &info
))
100 delayed_error(_("Program %s not found - deleted?"), path
);
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");
113 const EscapedPath
*uri
= uri_list
->data
;
116 local
= get_local_path(uri
);
118 argv
[argc
++] = local
;
120 argv
[argc
++] = unescape_uri(uri
);
121 uri_list
= uri_list
->next
;
126 type
= type_from_path(argv
[0]);
127 if (type
&& type
== application_x_desktop
)
129 run_desktop(argv
[0], argv
+ 1, home_dir
);
133 rox_spawn(home_dir
, argv
);
136 for (i
= 1; i
< argc
; i
++)
137 g_free((gchar
*) argv
[i
]);
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
};
151 if (stat(path
, &info
))
153 delayed_error(_("Program %s not found - deleted?"), path
);
157 if (S_ISDIR(info
.st_mode
))
158 argv
[0] = make_path(path
, "AppRun");
164 delayed_error("pipe: %s", g_strerror(errno
));
167 close_on_exec(fds
[1], TRUE
);
168 close_on_exec(fds
[0], TRUE
);
173 delayed_error("fork: %s", g_strerror(errno
));
177 /* We are the child */
179 if (dup2(fds
[0], 0) == -1)
180 g_warning("dup2() failed: %s\n",
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
));
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
);
198 pd
->tag
= gdk_input_add_full(fds
[1], GDK_INPUT_WRITE
,
199 write_data
, pd
, NULL
);
206 /* Splits args into an argument vector, and runs the program. Must be
209 void run_with_args(const char *path
, DirItem
*item
, const char *args
)
211 GError
*error
= NULL
;
215 if (item
->base_type
!= TYPE_DIRECTORY
&& item
->base_type
!= TYPE_FILE
)
217 delayed_error("Arguments (%s) given for non-executable item %s",
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
);
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
);
239 argv
[0] = g_strdup(path
);
241 rox_spawn(home_dir
, (const gchar
**) 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
,
257 FilerWindow
*filer_window
,
258 FilerWindow
*src_window
,
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
);
271 gboolean
run_diritem_gfile(GFile
*path
,
273 FilerWindow
*filer_window
,
274 FilerWindow
*src_window
,
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
)
285 if (item
->flags
& ITEM_FLAG_APPDIR
&& !edit
)
288 pathname
= g_file_get_path(path
);
291 delayed_error(_("Can't run a remote application!"));
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
);
304 filer_opendir_gfile(path
, src_window
, NULL
);
307 if (EXECUTABLE_FILE(item
) && !edit
)
310 const char *argv
[] = {NULL
, NULL
};
312 guchar
*dir
= filer_window
313 ? filer_window
->sym_path
316 if (item
->mime_type
== application_x_desktop
)
317 return run_desktop(full_path
, NULL
, dir
);
319 arg
= g_file_get_path(path
);
321 arg
= g_file_get_uri(path
);
324 retval
= rox_spawn(dir
, argv
) != 0;
330 return open_file(path
, edit
? text_plain
: item
->mime_type
);
332 delayed_error(_("File doesn't exist, or I can't "
333 "access it: %s"), full_path
);
337 _("I don't know how to open '%s'"), full_path
);
342 /* Attempt to open this item */
343 gboolean
run_by_path(const guchar
*full_path
)
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
);
357 /* Attempt to open this item */
358 gboolean
run_by_gfile(GFile
*path
)
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
);
372 /* Convert uri to path and call run_by_path() */
373 gboolean
run_by_uri(const gchar
*uri
, gchar
**errmsg
)
379 scheme
=get_uri_scheme((EscapedPath
*) uri
);
382 *errmsg
=g_strdup_printf(_("'%s' is not a valid URI"),
387 if(strcmp(scheme
, "file")==0 || strcmp(scheme
, "trash")==0) {
389 gfile
= g_file_new_for_uri(uri
);
391 retval
= run_by_gfile(gfile
);
392 g_object_unref(gfile
);
395 *errmsg
=g_strdup_printf(_("%s not accessable"), uri
);
397 } else if((cmd
=choices_find_xdg_path_load(scheme
, "URI", SITE
))) {
400 item
=diritem_new(scheme
);
401 diritem_restat(cmd
, item
, NULL
);
403 run_with_args(cmd
, item
, uri
);
404 retval
=TRUE
; /* we hope... */
411 *errmsg
=g_strdup_printf(_("%s: no handler for %s"),
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
);
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
)
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
);
453 display_set_autoselect(new, dir
+ 1);
458 new = filer_opendir(dir
, NULL
, NULL
);
462 display_set_hidden(new, TRUE
);
463 display_set_autoselect(new, slash
+ 1);
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
)
477 if (mc_stat(path
, &info
) != 0)
479 /* Deleted? Do a paranoid update of everything... */
480 filer_check_mounted(path
);
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
))
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
)
509 sent
= write(fd
, pd
->data
+ pd
->sent
, pd
->length
- pd
->sent
);
515 delayed_error(_("Could not send data to program: %s"),
524 g_source_remove(pd
->tag
);
530 static char *g_file_readlink(GFile
*symlink
, GError
**error
)
535 info
= g_file_query_info(symlink
, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET
, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
, NULL
, error
);
539 target
= (char *) g_file_info_get_symlink_target(info
);
541 target
= g_strdup(target
);
543 g_object_unref(info
);
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
);
563 delayed_error(_("Could not read link: %s"), error
->message
);
568 resolved
= g_file_resolve_relative_path(path
, target
);
572 new_dir
= g_file_get_parent(resolved
);
573 leafname
= g_file_get_basename(resolved
);
574 g_object_unref(resolved
);
577 filer_change_to(filer_window
, g_file_get_path(new_dir
), leafname
); /* XXX : free */
582 new = filer_opendir_gfile(new_dir
, src_window
, NULL
);
584 display_set_autoselect(new, leafname
);
587 g_object_unref(new_dir
);
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
))
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 "
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.")
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
,
621 gboolean mounted
= (item
->flags
& ITEM_FLAG_MOUNTED
) != 0;
628 path
= g_file_get_path(full_path
);
631 delayed_error(_("Can't mount/unmount on remote filesystems yet; sorry!"));
635 paths
= g_list_prepend(NULL
, (gpointer
) path
);
636 action_mount(paths
, filer_window
== NULL
, !mounted
, -1);
638 if (filer_window
&& !mounted
)
639 filer_change_to_gfile(filer_window
, full_path
, NULL
);
644 filer_change_to_gfile(filer_window
, full_path
, NULL
);
646 filer_opendir_gfile(full_path
, src_window
, NULL
);
650 /* full_path is a .desktop file. Execute the application, using the Exec line
652 * Returns TRUE on success.
654 static gboolean
run_desktop(const char *full_path
,
658 GError
*error
= NULL
;
660 char *terminal
= NULL
;
661 char *req_dir
= NULL
;
664 GPtrArray
*expanded
= NULL
;
665 gboolean inserted_args
= FALSE
;
667 gboolean success
= FALSE
;
669 get_values_from_desktop_file(full_path
,
671 "Desktop Entry", "Exec", &exec
,
672 "Desktop Entry", "Terminal", &terminal
,
673 "Desktop Entry", "Path", &req_dir
,
677 delayed_error("Failed to parse .desktop file '%s':\n%s",
678 full_path
, error
->message
);
684 delayed_error("Can't find Exec command in .desktop file '%s'",
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
);
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')
714 for (j
= 0; args
&& args
[j
]; j
++)
715 g_ptr_array_add(expanded
, g_strdup(args
[j
]));
716 inserted_args
= TRUE
;
720 delayed_error("Unsupported escape character in '%s' in '%s'",
727 g_ptr_array_add(expanded
, g_strdup(src
));
732 /* Many .desktop files don't include a % expansion. In that case
733 * add the arguments here.
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
);
744 success
= rox_spawn(dir
, (const gchar
**) expanded
->pdata
);
750 if (terminal
!= NULL
)
756 if (expanded
!= NULL
)
758 g_ptr_array_foreach(expanded
, (GFunc
) g_free
, NULL
);
759 g_ptr_array_free(expanded
, TRUE
);
765 static char *path_or_uri(GFile
*path
)
769 retval
= g_file_get_path(path
);
771 retval
= g_file_get_uri(path
);
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
};
782 open
= handler_for(type
);
786 if (stat(open
, &info
))
788 report_error("stat(%s): %s", open
, g_strerror(errno
));
793 if (info
.st_mode
& S_IWOTH
)
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."),
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)"));
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
)
828 run_desktop(open
, (const char **) (argv
+ 1), home_dir
);
833 rox_spawn(home_dir
, (const gchar
**) argv
);