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
26 #include <sys/param.h>
32 #include "gui_support.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
,
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
;
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
)
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
)
96 if (stat(path
, &info
))
98 delayed_error(_("Program %s not found - deleted?"), path
);
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");
111 const EscapedPath
*uri
= uri_list
->data
;
114 local
= get_local_path(uri
);
116 argv
[argc
++] = local
;
118 argv
[argc
++] = unescape_uri(uri
);
119 uri_list
= uri_list
->next
;
124 type
= type_from_path(argv
[0]);
125 if (type
&& type
== application_x_desktop
)
127 run_desktop(argv
[0], argv
+ 1, home_dir
);
131 rox_spawn(home_dir
, argv
);
134 for (i
= 1; i
< argc
; i
++)
135 g_free((gchar
*) argv
[i
]);
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
};
149 if (stat(path
, &info
))
151 delayed_error(_("Program %s not found - deleted?"), path
);
155 if (S_ISDIR(info
.st_mode
))
156 argv
[0] = make_path(path
, "AppRun");
162 delayed_error("pipe: %s", g_strerror(errno
));
165 close_on_exec(fds
[1], TRUE
);
166 close_on_exec(fds
[0], TRUE
);
171 delayed_error("fork: %s", g_strerror(errno
));
175 /* We are the child */
177 if (dup2(fds
[0], 0) == -1)
178 g_warning("dup2() failed: %s\n",
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
));
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
);
196 pd
->tag
= gdk_input_add_full(fds
[1], GDK_INPUT_WRITE
,
197 write_data
, pd
, NULL
);
204 /* Splits args into an argument vector, and runs the program. Must be
207 void run_with_args(const char *path
, DirItem
*item
, const char *args
)
209 GError
*error
= NULL
;
213 if (item
->base_type
!= TYPE_DIRECTORY
&& item
->base_type
!= TYPE_FILE
)
215 delayed_error("Arguments (%s) given for non-executable item %s",
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
);
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
);
237 argv
[0] = g_strdup(path
);
239 rox_spawn(home_dir
, (const gchar
**) 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
,
255 FilerWindow
*filer_window
,
256 FilerWindow
*src_window
,
259 if (item
->flags
& ITEM_FLAG_SYMLINK
&& edit
)
260 return follow_symlink(full_path
, filer_window
, src_window
);
262 switch (item
->base_type
)
265 if (item
->flags
& ITEM_FLAG_APPDIR
&& !edit
)
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
);
279 filer_opendir(full_path
, src_window
, NULL
);
282 if (EXECUTABLE_FILE(item
) && !edit
)
284 const char *argv
[] = {NULL
, NULL
};
285 guchar
*dir
= filer_window
286 ? filer_window
->sym_path
289 if (item
->mime_type
== application_x_desktop
)
290 return run_desktop(full_path
,
295 return rox_spawn(dir
, argv
) != 0;
298 return open_file(full_path
, edit
? text_plain
301 delayed_error(_("File doesn't exist, or I can't "
302 "access it: %s"), full_path
);
306 _("I don't know how to open '%s'"), full_path
);
311 /* Attempt to open this item */
312 gboolean
run_by_path(const guchar
*full_path
)
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
);
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
);
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
)
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
);
359 display_set_autoselect(new, dir
+ 1);
364 new = filer_opendir(dir
, NULL
, NULL
);
368 display_set_hidden(new, TRUE
);
369 display_set_autoselect(new, slash
+ 1);
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
)
383 if (mc_stat(path
, &info
) != 0)
385 /* Deleted? Do a paranoid update of everything... */
386 filer_check_mounted(path
);
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
))
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
)
415 sent
= write(fd
, pd
->data
+ pd
->sent
, pd
->length
- pd
->sent
);
421 delayed_error(_("Could not send data to program: %s"),
430 g_source_remove(pd
->tag
);
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
)
445 char path
[MAXPATHLEN
+ 1];
448 got
= readlink(full_path
, path
, MAXPATHLEN
);
451 delayed_error(_("Could not read link: %s"),
456 g_return_val_if_fail(got
<= MAXPATHLEN
, FALSE
);
459 /* Make a relative path absolute */
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... */
472 real
= pathdup(path
);
474 slash
= strrchr(real
, '/');
479 _("Broken symlink (or you don't have permission "
480 "to follow it): %s"), full_path
);
492 filer_change_to(filer_window
, new_dir
, slash
+ 1);
497 new = filer_opendir(new_dir
, src_window
, NULL
);
499 display_set_autoselect(new, slash
+ 1);
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
))
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 "
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.")
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
,
535 gboolean mounted
= (item
->flags
& ITEM_FLAG_MOUNTED
) != 0;
541 paths
= g_list_prepend(NULL
, (gpointer
) full_path
);
542 action_mount(paths
, filer_window
== NULL
, !mounted
, -1);
544 if (filer_window
&& !mounted
)
545 filer_change_to(filer_window
, full_path
, NULL
);
550 filer_change_to(filer_window
, full_path
, NULL
);
552 filer_opendir(full_path
, src_window
, NULL
);
556 /* full_path is a .desktop file. Execute the application, using the Exec line
558 * Returns TRUE on success.
560 static gboolean
run_desktop(const char *full_path
,
564 GError
*error
= NULL
;
568 GPtrArray
*expanded
= NULL
;
570 gboolean success
= FALSE
;
572 exec
= get_value_from_desktop_file(full_path
, "Desktop Entry", "Exec",
576 delayed_error("Failed to parse .desktop file '%s':\n%s",
577 full_path
, error
->message
);
583 delayed_error("Can't find Exec command in .desktop file '%s'",
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
);
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')
607 for (j
= 0; args
&& args
[j
]; j
++)
608 g_ptr_array_add(expanded
, g_strdup(args
[j
]));
612 delayed_error("Unsupported escape character in '%s' in '%s'",
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
);
632 if (expanded
!= NULL
)
634 g_ptr_array_foreach(expanded
, (GFunc
) g_free
, NULL
);
635 g_ptr_array_free(expanded
, TRUE
);
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
};
648 argv
[1] = (char *) path
;
650 open
= handler_for(type
);
654 if (stat(open
, &info
))
656 report_error("stat(%s): %s", open
, g_strerror(errno
));
661 if (info
.st_mode
& S_IWOTH
)
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."),
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)"));
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
)
694 run_desktop(open
, (const char **) (argv
+ 1), home_dir
);
699 rox_spawn(home_dir
, (const gchar
**) argv
) != 0;