Merge commit 'tony/master'
[rox-filer/translations.git] / ROX-Filer / src / run.c
blob922ca4d8589c12c0cff1969fae288355ef057b2f
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"
41 #include "choices.h"
43 /* Static prototypes */
44 static void write_data(gpointer data, gint fd, GdkInputCondition cond);
45 static gboolean follow_symlink(const char *full_path,
46 FilerWindow *filer_window,
47 FilerWindow *src_window);
48 static gboolean open_file(const guchar *path, MIME_type *type);
49 static void open_mountpoint(const guchar *full_path, DirItem *item,
50 FilerWindow *filer_window, FilerWindow *src_window,
51 gboolean edit);
52 static gboolean run_desktop(const char *full_path,
53 const char **args, const char *dir);
54 static gboolean type_open(const char *path, MIME_type *type);
56 typedef struct _PipedData PipedData;
58 struct _PipedData
60 guchar *data;
61 gint tag;
62 gulong sent;
63 gulong length;
67 /****************************************************************
68 * EXTERNAL INTERFACE *
69 ****************************************************************/
72 /* An application has been double-clicked (or run in some other way) */
73 void run_app(const char *path)
75 GString *apprun;
76 const char *argv[] = {NULL, NULL};
78 apprun = g_string_new(path);
79 argv[0] = g_string_append(apprun, "/AppRun")->str;
81 rox_spawn(home_dir, argv);
83 g_string_free(apprun, TRUE);
86 /* Execute this program, passing all the URIs in the list as arguments.
87 * URIs that are files on the local machine will be passed as simple
88 * pathnames. The uri_list should be freed after this function returns.
90 void run_with_files(const char *path, GList *uri_list)
92 const char **argv;
93 int argc = 0, i;
94 struct stat info;
95 MIME_type *type;
97 if (stat(path, &info))
99 delayed_error(_("Program %s not found - deleted?"), path);
100 return;
103 argv = g_malloc(sizeof(char *) * (g_list_length(uri_list) + 2));
105 if (S_ISDIR(info.st_mode))
106 argv[argc++] = make_path(path, "AppRun");
107 else
108 argv[argc++] = path;
110 while (uri_list)
112 const EscapedPath *uri = uri_list->data;
113 char *local;
115 local = get_local_path(uri);
116 if (local)
117 argv[argc++] = local;
118 else
119 argv[argc++] = unescape_uri(uri);
120 uri_list = uri_list->next;
123 argv[argc++] = NULL;
125 type = type_from_path(argv[0]);
126 if (type && type == application_x_desktop)
128 run_desktop(argv[0], argv + 1, home_dir);
130 else
132 rox_spawn(home_dir, argv);
135 for (i = 1; i < argc; i++)
136 g_free((gchar *) argv[i]);
137 g_free(argv);
140 /* Run the program as '<path> -', piping the data to it via stdin.
141 * You can g_free() the data as soon as this returns.
143 void run_with_data(const char *path, gpointer data, gulong length)
145 const char *argv[] = {NULL, "-", NULL};
146 struct stat info;
147 int fds[2];
148 PipedData *pd;
150 if (stat(path, &info))
152 delayed_error(_("Program %s not found - deleted?"), path);
153 return;
156 if (S_ISDIR(info.st_mode))
157 argv[0] = make_path(path, "AppRun");
158 else
159 argv[0] = path;
161 if (pipe(fds))
163 delayed_error("pipe: %s", g_strerror(errno));
164 return;
166 close_on_exec(fds[1], TRUE);
167 close_on_exec(fds[0], TRUE);
169 switch (fork())
171 case -1:
172 delayed_error("fork: %s", g_strerror(errno));
173 close(fds[1]);
174 break;
175 case 0:
176 /* We are the child */
177 chdir(home_dir);
178 if (dup2(fds[0], 0) == -1)
179 g_warning("dup2() failed: %s\n",
180 g_strerror(errno));
181 else
183 close_on_exec(0, FALSE);
184 if (execv(argv[0], (char **) argv))
185 g_warning("execv(%s) failed: %s\n",
186 argv[0], g_strerror(errno));
188 _exit(1);
189 default:
190 /* We are the parent */
191 set_blocking(fds[1], FALSE);
192 pd = g_new(PipedData, 1);
193 pd->data = g_malloc(length);
194 memcpy(pd->data, data, length);
195 pd->length = length;
196 pd->sent = 0;
197 pd->tag = gdk_input_add_full(fds[1], GDK_INPUT_WRITE,
198 write_data, pd, NULL);
199 break;
202 close(fds[0]);
205 /* Splits args into an argument vector, and runs the program. Must be
206 * executable.
208 void run_with_args(const char *path, DirItem *item, const char *args)
210 GError *error = NULL;
211 gchar **argv = NULL;
212 int n_args = 0;
214 if (item->base_type != TYPE_DIRECTORY && item->base_type != TYPE_FILE)
216 delayed_error("Arguments (%s) given for non-executable item %s",
217 args, path);
218 return;
221 if (!g_shell_parse_argv(args, &n_args, &argv, &error))
223 delayed_error("Failed to parse argument string '%s':\n%s",
224 args, error->message);
225 g_error_free(error);
226 return;
229 g_return_if_fail(argv != NULL);
230 g_return_if_fail(error == NULL);
232 argv = g_realloc(argv, (n_args + 2) * sizeof(gchar *));
233 memmove(argv + 1, argv, (n_args + 1) * sizeof(gchar *));
235 if (item->base_type == TYPE_DIRECTORY)
236 argv[0] = g_strconcat(path, "/AppRun", NULL);
237 else
238 argv[0] = g_strdup(path);
240 rox_spawn(home_dir, (const gchar **) argv);
242 g_strfreev(argv);
245 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
246 * edit a file, open an application, follow a symlink or mount a device.
248 * filer_window is the window to use for displaying a directory.
249 * NULL will always use a new directory when needed.
250 * src_window is the window to copy options from, or NULL.
252 * Returns TRUE on success.
254 gboolean run_diritem(const guchar *full_path,
255 DirItem *item,
256 FilerWindow *filer_window,
257 FilerWindow *src_window,
258 gboolean edit)
260 if (item->flags & ITEM_FLAG_SYMLINK && edit)
261 return follow_symlink(full_path, filer_window, src_window);
263 switch (item->base_type)
265 case TYPE_DIRECTORY:
266 if (item->flags & ITEM_FLAG_APPDIR && !edit)
268 run_app(full_path);
269 return TRUE;
272 if (item->flags & ITEM_FLAG_MOUNT_POINT)
274 open_mountpoint(full_path, item,
275 filer_window, src_window, edit);
277 else if (filer_window)
278 filer_change_to(filer_window, full_path, NULL);
279 else
280 filer_opendir(full_path, src_window, NULL);
281 return TRUE;
282 case TYPE_FILE:
283 if (EXECUTABLE_FILE(item) && !edit)
285 const char *argv[] = {NULL, NULL};
286 guchar *dir = filer_window
287 ? filer_window->sym_path
288 : NULL;
290 if (item->mime_type == application_x_desktop)
291 return run_desktop(full_path,
292 NULL, dir);
293 else
294 argv[0] = full_path;
296 return rox_spawn(dir, argv) != 0;
299 return open_file(full_path, edit ? text_plain
300 : item->mime_type);
301 case TYPE_ERROR:
302 delayed_error(_("File doesn't exist, or I can't "
303 "access it: %s"), full_path);
304 return FALSE;
305 default:
306 delayed_error(
307 _("I don't know how to open '%s'"), full_path);
308 return FALSE;
312 /* Attempt to open this item */
313 gboolean run_by_path(const guchar *full_path)
315 gboolean retval;
316 DirItem *item;
318 /* XXX: Loads an image - wasteful */
319 item = diritem_new("");
320 diritem_restat(full_path, item, NULL);
321 retval = run_diritem(full_path, item, NULL, NULL, FALSE);
322 diritem_free(item);
324 return retval;
327 /* Convert uri to path and call run_by_path() */
328 gboolean run_by_uri(const gchar *uri, gchar **errmsg)
330 gboolean retval;
331 gchar *tmp, *tmp2;
332 gchar *scheme;
333 gchar *cmd;
335 scheme=get_uri_scheme((EscapedPath *) uri);
336 if(!scheme)
338 *errmsg=g_strdup_printf(_("'%s' is not a valid URI"),
339 uri);
340 return FALSE;
343 if(strcmp(scheme, "file")==0) {
344 tmp=get_local_path((EscapedPath *) uri);
345 if(tmp) {
346 tmp2=pathdup(tmp);
347 retval=run_by_path(tmp2);
348 if(!retval)
349 *errmsg=g_strdup_printf(_("%s not accessable"),
350 tmp);
352 g_free(tmp2);
353 g_free(tmp);
355 } else {
356 retval=FALSE;
357 *errmsg=g_strdup_printf(_("Non-local URL %s"), uri);
360 } else if((cmd=choices_find_xdg_path_load(scheme, "URI", SITE))) {
361 DirItem *item;
363 item=diritem_new(scheme);
364 diritem_restat(cmd, item, NULL);
366 run_with_args(cmd, item, uri);
367 retval=TRUE; /* we hope... */
369 diritem_free(item);
370 g_free(cmd);
372 } else {
373 retval=FALSE;
374 *errmsg=g_strdup_printf(_("%s: no handler for %s"),
375 uri, scheme);
378 g_free(scheme);
380 return retval;
383 /* Open dir/Help, or show a message if missing */
384 void show_help_files(const char *dir)
386 const char *help_dir;
388 help_dir = make_path(dir, "Help");
390 if (file_exists(help_dir))
391 filer_opendir(help_dir, NULL, NULL);
392 else
393 info_message(
394 _("Application:\n"
395 "This is an application directory - you can "
396 "run it as a program, or open it (hold down "
397 "Shift while you open it). Most applications provide "
398 "their own help here, but this one doesn't."));
401 /* Open a directory viewer showing this file, and wink it */
402 void open_to_show(const guchar *path)
404 FilerWindow *new;
405 guchar *dir, *slash;
407 g_return_if_fail(path != NULL);
409 dir = g_strdup(path);
410 slash = strrchr(dir, '/');
411 if (slash == dir || !slash)
413 /* Item in the root (or root itself!) */
414 new = filer_opendir("/", NULL, NULL);
415 if (new && dir[1])
416 display_set_autoselect(new, dir + 1);
418 else
420 *slash = '\0';
421 new = filer_opendir(dir, NULL, NULL);
422 if (new)
424 if (slash[1] == '.')
425 display_set_hidden(new, TRUE);
426 display_set_autoselect(new, slash + 1);
430 g_free(dir);
433 /* Invoked using -x, this indicates that the filesystem has been modified
434 * and we should look at this item again.
436 void examine(const guchar *path)
438 struct stat info;
440 if (mc_stat(path, &info) != 0)
442 /* Deleted? Do a paranoid update of everything... */
443 filer_check_mounted(path);
445 else
447 /* Update directory containing this item... */
448 dir_check_this(path);
450 /* If this is itself a directory then rescan its contents... */
451 if (S_ISDIR(info.st_mode))
452 refresh_dirs(path);
454 /* If it's on the pinboard or a panel, update the icon... */
455 icons_may_update(path);
459 /****************************************************************
460 * INTERNAL FUNCTIONS *
461 ****************************************************************/
464 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
466 PipedData *pd = (PipedData *) data;
468 while (pd->sent < pd->length)
470 int sent;
472 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
474 if (sent < 0)
476 if (errno == EAGAIN)
477 return;
478 delayed_error(_("Could not send data to program: %s"),
479 g_strerror(errno));
480 goto finish;
483 pd->sent += sent;
486 finish:
487 g_source_remove(pd->tag);
488 g_free(pd->data);
489 g_free(pd);
490 close(fd);
493 /* Follow the link 'full_path' and display it in filer_window, or a
494 * new window if that is NULL.
496 static gboolean follow_symlink(const char *full_path,
497 FilerWindow *filer_window,
498 FilerWindow *src_window)
500 char *real, *slash;
501 char *new_dir;
502 char path[MAXPATHLEN + 1];
503 int got;
505 got = readlink(full_path, path, MAXPATHLEN);
506 if (got < 0)
508 delayed_error(_("Could not read link: %s"),
509 g_strerror(errno));
510 return FALSE;
513 g_return_val_if_fail(got <= MAXPATHLEN, FALSE);
514 path[got] = '\0';
516 /* Make a relative path absolute */
517 if (path[0] != '/')
519 guchar *tmp;
520 slash = strrchr(full_path, '/');
521 g_return_val_if_fail(slash != NULL, FALSE);
523 tmp = g_strndup(full_path, slash - full_path);
524 real = pathdup(make_path(tmp, path));
525 /* NB: full_path may be invalid here... */
526 g_free(tmp);
528 else
529 real = pathdup(path);
531 slash = strrchr(real, '/');
532 if (!slash)
534 g_free(real);
535 delayed_error(
536 _("Broken symlink (or you don't have permission "
537 "to follow it): %s"), full_path);
538 return FALSE;
541 *slash = '\0';
543 if (*real)
544 new_dir = real;
545 else
546 new_dir = "/";
548 if (filer_window)
549 filer_change_to(filer_window, new_dir, slash + 1);
550 else
552 FilerWindow *new;
554 new = filer_opendir(new_dir, src_window, NULL);
555 if (new)
556 display_set_autoselect(new, slash + 1);
559 g_free(real);
561 return TRUE;
564 /* Load this file into an appropriate editor */
565 static gboolean open_file(const guchar *path, MIME_type *type)
567 g_return_val_if_fail(type != NULL, FALSE);
569 if (type_open(path, type))
570 return TRUE;
572 report_error(
573 _("No run action specified for files of this type (%s/%s) - "
574 "you can set a run action by choosing `Set Run Action' "
575 "from the File menu, or you can just drag the file to an "
576 "application.%s"),
577 type->media_type,
578 type->subtype,
579 type->executable ? _("\n\nNote: If this is a computer program which "
580 "you want to run, you need to set the execute bit "
581 "by choosing Permissions from the File menu.")
582 : "");
584 return FALSE;
587 /* Called like run_diritem, when a mount-point is opened */
588 static void open_mountpoint(const guchar *full_path, DirItem *item,
589 FilerWindow *filer_window, FilerWindow *src_window,
590 gboolean edit)
592 gboolean mounted = (item->flags & ITEM_FLAG_MOUNTED) != 0;
594 if (mounted == edit)
596 GList *paths;
598 paths = g_list_prepend(NULL, (gpointer) full_path);
599 action_mount(paths, filer_window == NULL, !mounted, -1);
600 g_list_free(paths);
601 if (filer_window && !mounted)
602 filer_change_to(filer_window, full_path, NULL);
604 else
606 if (filer_window)
607 filer_change_to(filer_window, full_path, NULL);
608 else
609 filer_opendir(full_path, src_window, NULL);
613 /* full_path is a .desktop file. Execute the application, using the Exec line
614 * from the file.
615 * Returns TRUE on success.
617 static gboolean run_desktop(const char *full_path,
618 const char **args,
619 const char *dir)
621 GError *error = NULL;
622 char *exec = NULL;
623 char *terminal = NULL;
624 gint argc = 0;
625 gchar **argv = NULL;
626 GPtrArray *expanded = NULL;
627 gboolean inserted_args = FALSE;
628 int i;
629 gboolean success = FALSE;
631 get_values_from_desktop_file(full_path,
632 &error,
633 "Desktop Entry", "Exec", &exec,
634 "Desktop Entry", "Terminal", &terminal,
635 NULL);
636 if (error)
638 delayed_error("Failed to parse .desktop file '%s':\n%s",
639 full_path, error->message);
640 goto err;
643 if (!exec)
645 delayed_error("Can't find Exec command in .desktop file '%s'",
646 full_path);
647 goto err;
650 if (!g_shell_parse_argv(exec, &argc, &argv, &error))
652 delayed_error("Failed to parse '%s' from '%s':\n%s",
653 exec, full_path, error->message);
654 goto err;
657 expanded = g_ptr_array_new();
659 if (terminal && g_strcasecmp(terminal, "true") == 0) {
660 g_ptr_array_add(expanded, g_strdup("xterm"));
661 g_ptr_array_add(expanded, g_strdup("-e"));
664 for (i = 0; i < argc; i++)
666 const char *src = argv[i];
668 if (src[0] == '%' && src[1] != '\0' && src[2] == '\0')
670 /* We should treat these four differently. */
671 if (src[1] == 'f' || src[1] == 'F' ||
672 src[1] == 'u' || src[1] == 'U')
674 int j;
675 for (j = 0; args && args[j]; j++)
676 g_ptr_array_add(expanded, g_strdup(args[j]));
677 inserted_args = TRUE;
679 else
681 delayed_error("Unsupported escape character in '%s' in '%s'",
682 exec, full_path);
683 goto err;
686 else
688 g_ptr_array_add(expanded, g_strdup(src));
691 if (!inserted_args)
693 /* Many .desktop files don't include a % expansion. In that case
694 * add the arguments here.
696 int j;
697 for (j = 0; args && args[j]; j++)
698 g_ptr_array_add(expanded, g_strdup(args[j]));
700 g_ptr_array_add(expanded, NULL);
702 success = rox_spawn(dir, (const gchar **) expanded->pdata);
703 err:
704 if (error != NULL)
705 g_error_free(error);
706 if (exec != NULL)
707 g_free(exec);
708 if (terminal != NULL)
709 g_free(terminal);
710 if (argv != NULL)
711 g_strfreev(argv);
712 if (expanded != NULL)
714 g_ptr_array_foreach(expanded, (GFunc) g_free, NULL);
715 g_ptr_array_free(expanded, TRUE);
718 return success;
721 /* Returns FALSE is no run action is set for this type. */
722 static gboolean type_open(const char *path, MIME_type *type)
724 gchar *argv[] = {NULL, NULL, NULL};
725 char *open;
726 struct stat info;
728 argv[1] = (char *) path;
730 open = handler_for(type);
731 if (!open)
732 return FALSE;
734 if (stat(open, &info))
736 report_error("stat(%s): %s", open, g_strerror(errno));
737 g_free(open);
738 return TRUE;
741 if (info.st_mode & S_IWOTH)
743 gchar *choices_dir;
744 GList *paths;
746 report_error(_("Executable '%s' is world-writeable! Refusing "
747 "to run. Please change the permissions now (this "
748 "problem may have been caused by a bug in earlier "
749 "versions of the filer).\n\n"
750 "Having (non-symlink) run actions world-writeable "
751 "means that other people who use your computer can "
752 "replace your run actions with malicious versions.\n\n"
753 "If you trust everyone who could write to these files "
754 "then you needn't worry. Otherwise, you should check, "
755 "or even just delete, all the existing run actions."),
756 open);
757 choices_dir = g_path_get_dirname(open);
758 paths = g_list_append(NULL, choices_dir);
759 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
760 g_free(choices_dir);
761 g_list_free(paths);
762 g_free(open);
763 return TRUE;
766 if (S_ISDIR(info.st_mode))
768 argv[0] = g_strconcat(open, "/AppRun", NULL);
769 rox_spawn(home_dir, (const gchar **) argv);
771 else if (type_get_type(open) == application_x_desktop)
773 argv[0] = open;
774 run_desktop(open, (const char **) (argv + 1), home_dir);
776 else
778 argv[0] = open;
779 rox_spawn(home_dir, (const gchar **) argv);
782 if (argv[0] != open)
783 g_free(argv[0]);
785 g_free(open);
787 return TRUE;