/* * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * Author: Christian Neumair * */ #ifdef HAVE_CONFIG_H #include /* for GETTEXT_PACKAGE */ #endif #include "caja-open-terminal.h" #include "eel-mate-extensions.h" #include #include #include #include #include #include #include #include #include /* for atoi */ #include /* for strcmp */ #include /* for chdir */ #include 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 GSettings *settings_open = NULL; static GSettings *settings_preferences = NULL; static GSettings *settings_lockdown = NULL; static inline gboolean desktop_opens_home_dir (void) { return g_settings_get_boolean (settings_open, "desktop-opens-home-dir"); } static inline gboolean display_mc_item (void) { return g_settings_get_boolean (settings_open, "display-mc-items"); } static inline gboolean desktop_is_home_dir () { return g_settings_get_boolean (settings_preferences, "desktop-is-home-dir"); } /* 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 g_settings_get_boolean (settings_lockdown, "disable-command-line"); } /* 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 (settings_open == NULL); settings_open = g_settings_new ("org.mate.caja-open-terminal"); g_assert (settings_preferences == NULL); settings_preferences = g_settings_new ("org.mate.caja.preferences"); g_assert (settings_lockdown == NULL); settings_lockdown = g_settings_new ("org.mate.lockdown"); } static void caja_open_terminal_class_finalize (CajaOpenTerminalClass *class) { g_assert (settings_open != NULL); g_object_unref (settings_open); g_assert (settings_preferences != NULL); g_object_unref (settings_preferences); g_assert (settings_lockdown != NULL); g_object_unref (settings_lockdown); } 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); }