diff options
Diffstat (limited to 'caja/caja-open-terminal.c')
-rw-r--r-- | caja/caja-open-terminal.c | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/caja/caja-open-terminal.c b/caja/caja-open-terminal.c new file mode 100644 index 0000000..378270a --- /dev/null +++ b/caja/caja-open-terminal.c @@ -0,0 +1,664 @@ +/* + * caja-open-terminal.c + * + * Copyright (C) 2004, 2005 Free Software Foundation, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Christian Neumair <[email protected]> + * + */ + +#ifdef HAVE_CONFIG_H + #include <config.h> /* for GETTEXT_PACKAGE */ +#endif + +#include "caja-open-terminal.h" +#include "eel-mate-extensions.h" + +#include <libcaja-extension/caja-menu-provider.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <mateconf/mateconf.h> +#include <mateconf/mateconf-client.h> +#include <libmate/mate-desktop-item.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> /* for atoi */ +#include <string.h> /* for strcmp */ +#include <unistd.h> /* for chdir */ +#include <sys/stat.h> + +static void caja_open_terminal_instance_init (CajaOpenTerminal *cvs); +static void caja_open_terminal_class_init (CajaOpenTerminalClass *class); + +static GType terminal_type = 0; + +typedef enum { + /* local files. Always open "conventionally", i.e. cd and spawn. */ + FILE_INFO_LOCAL, + FILE_INFO_DESKTOP, + /* SFTP: Shell terminals are opened "remote" (i.e. with ssh client), + * commands are executed like *_OTHER */ + FILE_INFO_SFTP, + /* OTHER: Terminals and commands are opened by mapping the URI back + * to ~/.gvfs, i.e. to the GVFS FUSE bridge + */ + FILE_INFO_OTHER +} TerminalFileInfo; + +static TerminalFileInfo +get_terminal_file_info (const char *uri) +{ + TerminalFileInfo ret; + char *uri_scheme; + + uri_scheme = g_uri_parse_scheme (uri); + + if (uri_scheme == NULL) { + ret = FILE_INFO_OTHER; + } else if (strcmp (uri_scheme, "file") == 0) { + ret = FILE_INFO_LOCAL; + } else if (strcmp (uri_scheme, "x-caja-desktop") == 0) { + ret = FILE_INFO_DESKTOP; + } else if (strcmp (uri_scheme, "sftp") == 0 || + strcmp (uri_scheme, "ssh") == 0) { + ret = FILE_INFO_SFTP; + } else { + ret = FILE_INFO_OTHER; + } + + g_free (uri_scheme); + + return ret; +} + +static MateConfClient *mateconf_client = NULL; + +static inline gboolean +desktop_opens_home_dir (void) +{ + return mateconf_client_get_bool (mateconf_client, + "/apps/caja-open-terminal/desktop_opens_home_dir", + NULL); +} + +static inline gboolean +display_mc_item (void) +{ + return mateconf_client_get_bool (mateconf_client, + "/apps/caja-open-terminal/display_mc_item", + NULL); +} + +static inline gboolean +desktop_is_home_dir () +{ + return mateconf_client_get_bool (mateconf_client, + "/apps/caja/preferences/desktop_is_home_dir", + NULL); +} + +/* a very simple URI parsing routine from Launchpad #333462, until GLib supports URI parsing (MATE #489862) */ +#define SFTP_PREFIX "sftp://" +static void +parse_sftp_uri (GFile *file, + char **user, + char **host, + unsigned int *port, + char **path) +{ + char *tmp, *save; + char *uri; + + uri = g_file_get_uri (file); + g_assert (uri != NULL); + save = uri; + + *path = NULL; + *user = NULL; + *host = NULL; + *port = 0; + + /* skip intial 'sftp:// prefix */ + g_assert (!strncmp (uri, SFTP_PREFIX, strlen (SFTP_PREFIX))); + uri += strlen (SFTP_PREFIX); + + /* cut out the path */ + tmp = strchr (uri, '/'); + if (tmp != NULL) { + *path = g_uri_unescape_string (tmp, "/"); + *tmp = '\0'; + } + + /* read the username - it ends with @ */ + tmp = strchr (uri, '@'); + if (tmp != NULL) { + *tmp++ = '\0'; + + *user = strdup (uri); + if (strchr (*user, ':') != NULL) { + /* chop the password */ + *(strchr (*user, ':')) = '\0'; + } + + uri = tmp; + } + + /* now read the port, starts with : */ + tmp = strchr (uri, ':'); + if (tmp != NULL) { + *tmp++ = '\0'; + *port = atoi (tmp); /*FIXME: getservbyname*/ + } + + /* what is left is the host */ + *host = strdup (uri); + g_free (save); +} + +static char * +get_remote_ssh_command (const char *uri, + const char *command_to_run) +{ + GFile *file; + + char *host_name, *path, *user_name; + char *command, *user_host, *unescaped_path; + char *quoted_path; + char *remote_command; + char *quoted_remote_command; + char *port_str; + guint host_port; + + g_assert (uri != NULL); + + file = g_file_new_for_uri (uri); + parse_sftp_uri (file, &user_name, &host_name, &host_port, &path); + g_object_unref (file); + + /* FIXME to we have to consider the remote file encoding? */ + unescaped_path = g_uri_unescape_string (path, NULL); + quoted_path = g_shell_quote (unescaped_path); + + port_str = NULL; + if (host_port != 0) { + port_str = g_strdup_printf (" -p %d", host_port); + } else { + port_str = g_strdup (""); + } + + if (user_name != NULL) { + user_host = g_strdup_printf ("%s@%s", user_name, host_name); + } else { + user_host = g_strdup (host_name); + } + + if (command_to_run != NULL) { + remote_command = g_strdup_printf ("cd %s && exec %s", quoted_path, command_to_run); + } else { + remote_command = g_strdup_printf ("cd %s && exec $SHELL -l", quoted_path); + } + + quoted_remote_command = g_shell_quote (remote_command); + + command = g_strdup_printf ("ssh %s%s -t %s", user_host, port_str, quoted_remote_command); + + g_free (user_name); + g_free (user_host); + g_free (host_name); + g_free (port_str); + + g_free (path); + g_free (unescaped_path); + g_free (quoted_path); + + g_free (remote_command); + g_free (quoted_remote_command); + + return command; +} + +static inline char * +get_gvfs_path_for_uri (const char *uri) +{ + GFile *file; + char *path; + + file = g_file_new_for_uri (uri); + path = g_file_get_path (file); + g_object_unref (file); + + return path; +} + +static char * +get_terminal_command_for_file_info (cajaFileInfo *file_info, + const char *command_to_run, + gboolean remote_terminal) +{ + char *uri, *path, *quoted_path; + char *command; + + uri = caja_file_info_get_activation_uri (file_info); + + path = NULL; + command = NULL; + + switch (get_terminal_file_info (uri)) { + case FILE_INFO_LOCAL: + if (uri != NULL) { + path = g_filename_from_uri (uri, NULL, NULL); + } + break; + + case FILE_INFO_DESKTOP: + if (desktop_is_home_dir () || desktop_opens_home_dir ()) { + path = g_strdup (g_get_home_dir ()); + } else { + path = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)); + } + break; + + case FILE_INFO_SFTP: + if (remote_terminal && uri != NULL) { + command = get_remote_ssh_command (uri, command_to_run); + break; + } + + /* fall through */ + case FILE_INFO_OTHER: + if (uri != NULL) { + /* map back remote URI to local path */ + path = get_gvfs_path_for_uri (uri); + } + break; + + default: + g_assert_not_reached (); + } + + if (command == NULL && path != NULL) { + quoted_path = g_shell_quote (path); + + if (command_to_run != NULL) { + command = g_strdup_printf ("cd %s && exec %s", quoted_path, command_to_run); + } else { + command = g_strdup_printf ("cd %s && exec $SHELL -l", quoted_path); + } + + g_free (quoted_path); + } + + g_free (path); + g_free (uri); + + return command; +} + + +static void +open_terminal (cajaMenuItem *item, + cajaFileInfo *file_info) +{ + char *terminal_command, *command_to_run; + GdkScreen *screen; + gboolean remote_terminal; + + screen = g_object_get_data (G_OBJECT (item), "CajaOpenTerminal::screen"); + command_to_run = g_object_get_data (G_OBJECT (item), "CajaOpenTerminal::command-to-run"); + remote_terminal = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (item), "CajaOpenTerminal::remote-terminal")); + + terminal_command = get_terminal_command_for_file_info (file_info, command_to_run, remote_terminal); + if (terminal_command != NULL) { + _not_eel_mate_open_terminal_on_screen (terminal_command, screen); + } + g_free (terminal_command); +} + +static void +open_terminal_callback (cajaMenuItem *item, + cajaFileInfo *file_info) +{ + open_terminal (item, file_info); +} + +static cajaMenuItem * +open_terminal_menu_item_new (cajaFileInfo *file_info, + TerminalFileInfo terminal_file_info, + GdkScreen *screen, + const char *command_to_run, + gboolean remote_terminal, + gboolean is_file_item) +{ + cajaMenuItem *ret; + char *action_name; + const char *name; + const char *tooltip; + + if (command_to_run == NULL) { + switch (terminal_file_info) { + case FILE_INFO_SFTP: + if (remote_terminal) { + name = _("Open in _Remote Terminal"); + } else { + name = _("Open in _Local Terminal"); + } + + if (is_file_item) { + tooltip = _("Open the currently selected folder in a terminal"); + } else { + tooltip = _("Open the currently open folder in a terminal"); + } + break; + + case FILE_INFO_LOCAL: + case FILE_INFO_OTHER: + name = _("Open in T_erminal"); + + if (is_file_item) { + tooltip = _("Open the currently selected folder in a terminal"); + } else { + tooltip = _("Open the currently open folder in a terminal"); + } + break; + + case FILE_INFO_DESKTOP: + if (desktop_opens_home_dir ()) { + name = _("Open T_erminal"); + tooltip = _("Open a terminal"); + } else { + name = _("Open in T_erminal"); + tooltip = _("Open the currently open folder in a terminal"); + } + break; + + default: + g_assert_not_reached (); + } + } else if (!strcmp (command_to_run, "mc")) { + switch (terminal_file_info) { + case FILE_INFO_LOCAL: + case FILE_INFO_SFTP: + case FILE_INFO_OTHER: + name = _("Open in _Midnight Commander"); + if (is_file_item) { + tooltip = _("Open the currently selected folder in the terminal file manager Midnight Commander"); + } else { + tooltip = _("Open the currently open folder in the terminal file manager Midnight Commander"); + } + break; + + case FILE_INFO_DESKTOP: + if (desktop_opens_home_dir ()) { + name = _("Open _Midnight Commander"); + tooltip = _("Open the terminal file manager Midnight Commander"); + } else { + name = _("Open in _Midnight Commander"); + tooltip = _("Open the currently open folder in the terminal file manager Midnight Commander"); + } + break; + + default: + g_assert_not_reached (); + } + } else { + g_assert_not_reached (); + } + + if (command_to_run != NULL) { + action_name = g_strdup_printf (remote_terminal ? + "CajaOpenTerminal::open_remote_terminal_%s" : + "CajaOpenTerminal::open_terminal_%s", + command_to_run); + } else { + action_name = g_strdup (remote_terminal ? + "CajaOpenTerminal::open_remote_terminal" : + "CajaOpenTerminal::open_terminal"); + } + ret = caja_menu_item_new (action_name, name, tooltip, "utilities-terminal"); + g_free (action_name); + + g_object_set_data (G_OBJECT (ret), + "CajaOpenTerminal::screen", + screen); + g_object_set_data_full (G_OBJECT (ret), "CajaOpenTerminal::command-to-run", + g_strdup (command_to_run), + (GDestroyNotify) g_free); + g_object_set_data (G_OBJECT (ret), "CajaOpenTerminal::remote-terminal", + GUINT_TO_POINTER (remote_terminal)); + + + g_object_set_data_full (G_OBJECT (ret), "file-info", + g_object_ref (file_info), + (GDestroyNotify) g_object_unref); + + + g_signal_connect (ret, "activate", + G_CALLBACK (open_terminal_callback), + file_info); + + + return ret; +} + +static gboolean +terminal_locked_down (void) +{ + return mateconf_client_get_bool (mateconf_client, + "/desktop/mate/lockdown/disable_command_line", + NULL); +} + +/* used to determine for remote URIs whether GVFS is capable of mapping them to ~/.gvfs */ +static gboolean +uri_has_local_path (const char *uri) +{ + GFile *file; + char *path; + gboolean ret; + + file = g_file_new_for_uri (uri); + path = g_file_get_path (file); + + ret = (path != NULL); + + g_free (path); + g_object_unref (file); + + return ret; +} + +static GList * +caja_open_terminal_get_background_items (cajaMenuProvider *provider, + GtkWidget *window, + cajaFileInfo *file_info) +{ + gchar *uri; + GList *items; + cajaMenuItem *item; + TerminalFileInfo terminal_file_info; + + if (terminal_locked_down ()) { + return NULL; + } + + items = NULL; + + uri = caja_file_info_get_activation_uri (file_info); + terminal_file_info = get_terminal_file_info (uri); + + if (terminal_file_info == FILE_INFO_SFTP || + terminal_file_info == FILE_INFO_DESKTOP || + uri_has_local_path (uri)) { + /* local locations or SSH */ + item = open_terminal_menu_item_new (file_info, terminal_file_info, gtk_widget_get_screen (window), + NULL, terminal_file_info == FILE_INFO_SFTP, FALSE); + items = g_list_append (items, item); + } + + if ((terminal_file_info == FILE_INFO_SFTP || + terminal_file_info == FILE_INFO_OTHER) && + uri_has_local_path (uri)) { + /* remote locations that offer local back-mapping */ + item = open_terminal_menu_item_new (file_info, terminal_file_info, gtk_widget_get_screen (window), + NULL, FALSE, FALSE); + items = g_list_append (items, item); + } + + if (display_mc_item () && + g_find_program_in_path ("mc") && + ((terminal_file_info == FILE_INFO_DESKTOP && + (desktop_is_home_dir () || desktop_opens_home_dir ())) || + uri_has_local_path (uri))) { + item = open_terminal_menu_item_new (file_info, terminal_file_info, gtk_widget_get_screen (window), "mc", FALSE, FALSE); + items = g_list_append (items, item); + } + + g_free (uri); + + return items; +} + +GList * +caja_open_terminal_get_file_items (cajaMenuProvider *provider, + GtkWidget *window, + GList *files) +{ + gchar *uri; + GList *items; + cajaMenuItem *item; + TerminalFileInfo terminal_file_info; + + if (terminal_locked_down ()) { + return NULL; + } + + if (g_list_length (files) != 1 || + (!caja_file_info_is_directory (files->data) && + caja_file_info_get_file_type (files->data) != G_FILE_TYPE_SHORTCUT && + caja_file_info_get_file_type (files->data) != G_FILE_TYPE_MOUNTABLE)) { + return NULL; + } + + items = NULL; + + uri = caja_file_info_get_activation_uri (files->data); + terminal_file_info = get_terminal_file_info (uri); + + switch (terminal_file_info) { + case FILE_INFO_LOCAL: + case FILE_INFO_SFTP: + case FILE_INFO_OTHER: + if (terminal_file_info == FILE_INFO_SFTP || uri_has_local_path (uri)) { + item = open_terminal_menu_item_new (files->data, terminal_file_info, gtk_widget_get_screen (window), + NULL, terminal_file_info == FILE_INFO_SFTP, TRUE); + items = g_list_append (items, item); + } + + if (terminal_file_info == FILE_INFO_SFTP && + uri_has_local_path (uri)) { + item = open_terminal_menu_item_new (files->data, terminal_file_info, gtk_widget_get_screen (window), NULL, FALSE, TRUE); + items = g_list_append (items, item); + } + + if (display_mc_item () && + g_find_program_in_path ("mc") && + uri_has_local_path (uri)) { + item = open_terminal_menu_item_new (files->data, terminal_file_info, gtk_widget_get_screen (window), "mc", TRUE, FALSE); + items = g_list_append (items, item); + } + break; + + case FILE_INFO_DESKTOP: + break; + + default: + g_assert_not_reached (); + } + + g_free (uri); + + return items; +} + +static void +caja_open_terminal_menu_provider_iface_init (cajaMenuProviderIface *iface) +{ + iface->get_background_items = caja_open_terminal_get_background_items; + iface->get_file_items = caja_open_terminal_get_file_items; +} + +static void +caja_open_terminal_instance_init (CajaOpenTerminal *cvs) +{ +} + +static void +caja_open_terminal_class_init (CajaOpenTerminalClass *class) +{ + g_assert (mateconf_client == NULL); + mateconf_client = mateconf_client_get_default (); +} + +static void +caja_open_terminal_class_finalize (CajaOpenTerminalClass *class) +{ + g_assert (mateconf_client != NULL); + g_object_unref (mateconf_client); + mateconf_client = NULL; +} + +GType +caja_open_terminal_get_type (void) +{ + return terminal_type; +} + +void +caja_open_terminal_register_type (GTypeModule *module) +{ + static const GTypeInfo info = { + sizeof (CajaOpenTerminalClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) caja_open_terminal_class_init, + (GClassFinalizeFunc) caja_open_terminal_class_finalize, + NULL, + sizeof (CajaOpenTerminal), + 0, + (GInstanceInitFunc) caja_open_terminal_instance_init, + }; + + static const GInterfaceInfo menu_provider_iface_info = { + (GInterfaceInitFunc) caja_open_terminal_menu_provider_iface_init, + NULL, + NULL + }; + + terminal_type = g_type_module_register_type (module, + G_TYPE_OBJECT, + "CajaOpenTerminal", + &info, 0); + + g_type_module_add_interface (module, + terminal_type, + caja_TYPE_MENU_PROVIDER, + &menu_provider_iface_info); +} |