/** * @file gnome-cmd-con.cc * @copyright (C) 2001-2006 Marcus Bjurman\n * @copyright (C) 2007-2012 Piotr Eljasiak\n * @copyright (C) 2013-2023 Uwe Scholz\n * * @copyright This program 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. * * @copyright This program 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 General Public License for more details. * * @copyright You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "gnome-cmd-includes.h" #include "gnome-cmd-con.h" #include "gnome-cmd-plain-path.h" #include "gnome-cmd-main-win.h" #include "gnome-cmd-con-list.h" using namespace std; struct GnomeCmdConPrivate { GnomeCmdDir *default_dir; // the start dir of this connection History *dir_history; GnomeCmdBookmarkGroup *bookmarks; GList *all_dirs; GHashTable *all_dirs_map; }; enum { UPDATED, CLOSE, OPEN_DONE, OPEN_FAILED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; static GtkObjectClass *parent_class = nullptr; // Keep this in sync with enum ConnectionMethodID in gnome-cmd-con.h const gchar *icon_name[] = {"folder-remote", // CON_SSH "folder-remote", // CON_FTP "folder-remote", // CON_ANON_FTP #ifdef HAVE_SAMBA "folder-remote", // CON_SMB #endif "folder-remote", // CON_DAV "folder-remote", // CON_DAVS "network-workgroup", // CON_URI "folder"}; // CON_FILE static void on_open_done (GnomeCmdCon *con) { gnome_cmd_con_updated (con); } static void on_open_failed (GnomeCmdCon *con) { // gnome_cmd_con_updated (con); // Free the error because the error handling is done now. (Logging happened already.) g_error_free(con->open_failed_error); con->open_failed_msg = nullptr; } /******************************* * Gtk class implementation *******************************/ static void destroy (GtkObject *object) { GnomeCmdCon *con = GNOME_CMD_CON (object); g_free (con->alias); g_free (con->uri); g_free (con->scheme); delete con->base_path; g_string_free (con->root_path, TRUE); g_free (con->open_text); g_free (con->open_tooltip); gnome_cmd_pixmap_free (con->open_pixmap); g_free (con->close_text); g_free (con->close_tooltip); g_object_unref(con->base_gFileInfo); g_error_free(con->open_failed_error); con->open_failed_msg = nullptr; gnome_cmd_pixmap_free (con->close_pixmap); gnome_cmd_pixmap_free (con->go_pixmap); if (con->priv->default_dir) gnome_cmd_dir_unref (con->priv->default_dir); delete con->priv->dir_history; g_free (con->priv); if (GTK_OBJECT_CLASS (parent_class)->destroy) (*GTK_OBJECT_CLASS (parent_class)->destroy) (object); } static void class_init (GnomeCmdConClass *klass) { GtkObjectClass *object_class; object_class = GTK_OBJECT_CLASS (klass); parent_class = (GtkObjectClass *) gtk_type_class (gtk_object_get_type ()); signals[UPDATED] = gtk_signal_new ("updated", GTK_RUN_LAST, G_OBJECT_CLASS_TYPE (object_class), GTK_SIGNAL_OFFSET (GnomeCmdConClass, updated), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); signals[CLOSE] = gtk_signal_new ("close", GTK_RUN_LAST, G_OBJECT_CLASS_TYPE (object_class), GTK_SIGNAL_OFFSET (GnomeCmdConClass, close), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); signals[OPEN_DONE] = gtk_signal_new ("open-done", GTK_RUN_LAST, G_OBJECT_CLASS_TYPE (object_class), GTK_SIGNAL_OFFSET (GnomeCmdConClass, open_done), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); signals[OPEN_FAILED] = gtk_signal_new ("open-failed", GTK_RUN_LAST, G_OBJECT_CLASS_TYPE (object_class), GTK_SIGNAL_OFFSET (GnomeCmdConClass, open_failed), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); object_class->destroy = destroy; klass->updated = nullptr; klass->open_done = on_open_done; klass->open_failed = on_open_failed; klass->open = nullptr; klass->close = nullptr; klass->cancel_open = nullptr; klass->open_is_needed = nullptr; klass->create_gfile = nullptr; klass->create_path = nullptr; } static void init (GnomeCmdCon *con) { con->alias = nullptr; con->uri = nullptr; con->scheme = nullptr; con->method = CON_URI; con->base_path = nullptr; con->root_path = g_string_sized_new (128); con->open_msg = nullptr; con->should_remember_dir = FALSE; con->needs_open_visprog = FALSE; con->needs_list_visprog = FALSE; con->can_show_free_space = FALSE; con->is_local = FALSE; con->is_closeable = FALSE; con->go_text = nullptr; con->go_tooltip = nullptr; con->go_pixmap = nullptr; con->open_text = nullptr; con->open_tooltip = nullptr; con->open_pixmap = nullptr; con->close_text = nullptr; con->close_tooltip = nullptr; con->close_pixmap = nullptr; con->state = GnomeCmdCon::STATE_CLOSED; con->open_result = GnomeCmdCon::OPEN_NOT_STARTED; con->open_failed_msg = nullptr; con->open_failed_error = nullptr; con->priv = g_new0 (GnomeCmdConPrivate, 1); con->priv->default_dir = nullptr; con->priv->dir_history = new History(20); con->priv->bookmarks = g_new0 (GnomeCmdBookmarkGroup, 1); con->priv->bookmarks->con = con; // con->priv->bookmarks->bookmarks = nullptr; // con->priv->bookmarks->data = nullptr; con->priv->all_dirs = nullptr; con->priv->all_dirs_map = nullptr; } /*********************************** * Public functions ***********************************/ GtkType gnome_cmd_con_get_type () { static GtkType type = 0; if (type == 0) { GtkTypeInfo info = { (gchar*) "GnomeCmdCon", sizeof (GnomeCmdCon), sizeof (GnomeCmdConClass), (GtkClassInitFunc) class_init, (GtkObjectInitFunc) init, /* reserved_1 */ nullptr, /* reserved_2 */ nullptr, (GtkClassInitFunc) nullptr }; type = gtk_type_unique (gtk_object_get_type (), &info); } return type; } static gboolean check_con_open_progress (GnomeCmdCon *con) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), FALSE); g_return_val_if_fail (con->open_result != GnomeCmdCon::OPEN_NOT_STARTED, FALSE); #if defined (__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" #endif switch (con->open_result) { case GnomeCmdCon::OPEN_IN_PROGRESS: return TRUE; case GnomeCmdCon::OPEN_OK: { DEBUG('m', "GnomeCmdCon::OPEN_OK detected\n"); GnomeCmdDir *dir = gnome_cmd_dir_new_with_con (con); gnome_cmd_con_set_default_dir (con, dir); DEBUG ('m', "Emitting 'open-done' signal\n"); gtk_signal_emit (GTK_OBJECT (con), signals[OPEN_DONE]); } return FALSE; case GnomeCmdCon::OPEN_FAILED: { DEBUG ('m', "GnomeCmdCon::OPEN_FAILED detected\n"); DEBUG ('m', "Emitting 'open-failed' signal\n"); gtk_signal_emit (GTK_OBJECT (con), signals[OPEN_FAILED]); } return FALSE; default: return FALSE; } #if defined (__GNUC__) #pragma GCC diagnostic pop #endif } void gnome_cmd_con_set_base_path(GnomeCmdCon *con, GnomeCmdPath *path) { g_return_if_fail (con != nullptr); g_return_if_fail (GNOME_CMD_IS_CON (con)); g_return_if_fail (path != nullptr); if (con->base_path) delete con->base_path; con->base_path = path; } void set_con_base_path_for_gmount(GnomeCmdCon *con, GMount *gMount) { g_return_if_fail (con != nullptr); g_return_if_fail (GNOME_CMD_IS_CON (con)); g_return_if_fail (gMount != nullptr); g_return_if_fail (G_IS_MOUNT(gMount)); auto gFile = g_mount_get_default_location(gMount); auto pathString = g_file_get_path(gFile); g_object_unref(gFile); gnome_cmd_con_set_base_path(con, new GnomeCmdPlainPath(pathString)); g_free(pathString); } gboolean set_con_base_gfileinfo(GnomeCmdCon *con) { g_return_val_if_fail (con != nullptr, FALSE); g_return_val_if_fail (GNOME_CMD_IS_CON (con), FALSE); GError *error = nullptr; if (con->base_gFileInfo) { g_object_unref(con->base_gFileInfo); con->base_gFileInfo = nullptr; } auto gFile = con->is_local ? gnome_cmd_con_create_gfile (con, con->base_path) : gnome_cmd_con_create_gfile (con, nullptr); con->base_gFileInfo = g_file_query_info(gFile, "*", G_FILE_QUERY_INFO_NONE, nullptr, &error); g_object_unref(gFile); if (error) { g_critical("set_con_base_gfileinfo: error: %s", error->message); g_error_free(error); return FALSE; } return TRUE; } void gnome_cmd_con_open (GnomeCmdCon *con) { g_return_if_fail (GNOME_CMD_IS_CON (con)); DEBUG ('m', "Opening connection\n"); GnomeCmdConClass *klass = GNOME_CMD_CON_GET_CLASS (con); if (con->state != GnomeCmdCon::STATE_OPEN) klass->open (con); g_timeout_add (gnome_cmd_data.gui_update_rate, (GSourceFunc) check_con_open_progress, con); } void gnome_cmd_con_cancel_open (GnomeCmdCon *con) { g_return_if_fail (GNOME_CMD_IS_CON (con)); if (con->state == GnomeCmdCon::STATE_OPENING) { GnomeCmdConClass *klass = GNOME_CMD_CON_GET_CLASS (con); klass->cancel_open (con); } } gboolean gnome_cmd_con_close (GnomeCmdCon *con) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), FALSE); if (gnome_cmd_con_is_closeable (con) && gnome_cmd_con_is_open(con)) { gtk_signal_emit (GTK_OBJECT (con), signals[CLOSE]); gtk_signal_emit (GTK_OBJECT (con), signals[UPDATED]); } return TRUE; } GFile *gnome_cmd_con_create_gfile (GnomeCmdCon *con, GnomeCmdPath *path) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), nullptr); GnomeCmdConClass *klass = GNOME_CMD_CON_GET_CLASS (con); return klass->create_gfile (con, path); } GnomeCmdPath *gnome_cmd_con_create_path (GnomeCmdCon *con, const gchar *path_str) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), nullptr); GnomeCmdConClass *klass = GNOME_CMD_CON_GET_CLASS (con); return klass->create_path (con, path_str); } void gnome_cmd_con_set_default_dir (GnomeCmdCon *con, GnomeCmdDir *dir) { g_return_if_fail (GNOME_CMD_IS_CON (con)); if (dir == con->priv->default_dir) return; if (dir) gnome_cmd_dir_ref (dir); if (con->priv->default_dir) gnome_cmd_dir_unref (con->priv->default_dir); con->priv->default_dir = dir; } GnomeCmdDir *gnome_cmd_con_get_default_dir (GnomeCmdCon *con) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), nullptr); return con->priv->default_dir; } History *gnome_cmd_con_get_dir_history (GnomeCmdCon *con) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), nullptr); return con->priv->dir_history; } GnomeCmdBookmarkGroup *gnome_cmd_con_get_bookmarks (GnomeCmdCon *con) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), nullptr); return con->priv->bookmarks; } void gnome_cmd_con_add_bookmark (GnomeCmdCon *con, gchar *name, gchar *path) { GnomeCmdBookmarkGroup *group = gnome_cmd_con_get_bookmarks (con); GnomeCmdBookmark *bookmark = g_new (GnomeCmdBookmark, 1); bookmark->name = name; bookmark->path = path; bookmark->group = group; group->bookmarks = g_list_append (group->bookmarks, bookmark); } void gnome_cmd_con_erase_bookmark (GnomeCmdCon *con) { if (!con) return; g_return_if_fail (GNOME_CMD_IS_CON (con)); GnomeCmdBookmarkGroup *group = con->priv->bookmarks; for(GList *l = group->bookmarks; l; l = l->next) { auto bookmark = static_cast (l->data); g_free (bookmark->name); g_free (bookmark->path); g_free (bookmark); } g_list_free(group->bookmarks); con->priv->bookmarks = g_new0 (GnomeCmdBookmarkGroup, 1); con->priv->bookmarks->con = con; } void gnome_cmd_con_updated (GnomeCmdCon *con) { g_return_if_fail (GNOME_CMD_IS_CON (con)); gtk_signal_emit (GTK_OBJECT (con), signals[UPDATED]); } /** * Get the type of the file at the specified path. * If the file does not exists, the function will return false. * If the operation succeeds, true is returned and type is set. */ gboolean gnome_cmd_con_get_path_target_type (GnomeCmdCon *con, const gchar *path_str, GFileType *gFileType) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), false); g_return_val_if_fail (path_str != nullptr, false); GnomeCmdPath *path = gnome_cmd_con_create_path (con, path_str); auto gFile = gnome_cmd_con_create_gfile(con, path); if (!gFile || !g_file_query_exists(gFile, nullptr)) { if (gFile) g_object_unref(gFile); *gFileType = G_FILE_TYPE_UNKNOWN; delete path; return false; } *gFileType = g_file_query_file_type(gFile, G_FILE_QUERY_INFO_NONE, nullptr); g_object_unref(gFile); delete path; return true; } gboolean gnome_cmd_con_mkdir (GnomeCmdCon *con, const gchar *path_str, GError *error) { GError *tmpError = nullptr; g_return_val_if_fail (GNOME_CMD_IS_CON (con), false); g_return_val_if_fail (path_str != nullptr, false); auto path = gnome_cmd_con_create_path (con, path_str); auto gFile = gnome_cmd_con_create_gfile (con, path); if (!g_file_make_directory (gFile, nullptr, &tmpError)) { g_warning("g_file_make_directory error: %s\n", tmpError ? tmpError->message : "unknown error"); g_propagate_error(&error, tmpError); return false; } g_object_unref (gFile); delete path; return true; } void gnome_cmd_con_add_to_cache (GnomeCmdCon *con, GnomeCmdDir *dir, gchar *uri_str) { g_return_if_fail (GNOME_CMD_IS_CON (con)); g_return_if_fail (GNOME_CMD_IS_DIR (dir)); if (!uri_str) { uri_str = GNOME_CMD_FILE (dir)->get_uri_str(); } if (!uri_str) { return; } if (!con->priv->all_dirs_map) con->priv->all_dirs_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nullptr); DEBUG ('k', "ADDING %p %s to the cache\n", dir, uri_str); g_hash_table_insert (con->priv->all_dirs_map, uri_str, dir); } void gnome_cmd_con_remove_from_cache (GnomeCmdCon *con, GnomeCmdDir *dir) { g_return_if_fail (GNOME_CMD_IS_CON (con)); g_return_if_fail (GNOME_CMD_IS_DIR (dir)); gchar *uri_str = GNOME_CMD_FILE (dir)->get_uri_str(); DEBUG ('k', "REMOVING %p %s from the cache\n", dir, uri_str); g_hash_table_remove (con->priv->all_dirs_map, uri_str); g_free (uri_str); } void gnome_cmd_con_remove_from_cache (GnomeCmdCon *con, const gchar *uri_str) { g_return_if_fail (GNOME_CMD_IS_CON (con)); g_return_if_fail (uri_str != nullptr); DEBUG ('k', "REMOVING %s from the cache\n", uri_str); g_hash_table_remove (con->priv->all_dirs_map, uri_str); } GnomeCmdDir *gnome_cmd_con_cache_lookup (GnomeCmdCon *con, const gchar *uri_str) { g_return_val_if_fail (GNOME_CMD_IS_CON (con), nullptr); g_return_val_if_fail (uri_str != nullptr, nullptr); GnomeCmdDir *dir = nullptr; if (con->priv->all_dirs_map) { dir = static_cast (g_hash_table_lookup (con->priv->all_dirs_map, uri_str)); } if (dir) DEBUG ('k', "FOUND %p %s in the hash-table, reusing it!\n", dir, uri_str); else DEBUG ('k', "FAILED to find %s in the hash-table\n", uri_str); return dir; } const gchar *gnome_cmd_con_get_icon_name (ConnectionMethodID method) { return icon_name[method]; } string &__gnome_cmd_con_make_uri (string &s, const gchar *method, string &server, string &port, string &folder) { if (!folder.empty()) { // remove initial '/' characters size_t len = 0; while (folder[len] == '/') ++len; folder.erase(0, len); } folder = stringify (g_uri_escape_string (folder.c_str(), nullptr, true)); s = method; s += server; if (!port.empty()) s += ':' + port; if (!folder.empty()) s += '/' + folder; return s; } #ifdef HAVE_SAMBA std::string &gnome_cmd_con_make_smb_uri (std::string &uriString, std::string &server, std::string &folder, std::string &domain) { if (!folder.empty()) { // remove initial '/' characters size_t len = 0; while (folder[len] == '/') ++len; folder.erase(0, len); } uriString = "smb://"; if (!domain.empty()) uriString += domain + ';'; uriString += server; if (!folder.empty()) uriString += '/' + folder; return uriString; } #endif /** * This function checks if the active or inactive file pane is showing * files from the given GMount. If yes, close the connection to this GMount * and open the home connection instead. */ void gnome_cmd_con_close_active_or_inactive_connection (GMount *gMount) { g_return_if_fail(gMount != nullptr); g_return_if_fail(G_IS_MOUNT(gMount)); auto gFile = g_mount_get_root(gMount); auto uriString = g_file_get_uri(gFile); auto activeConUri = gnome_cmd_con_get_uri(main_win->fs(ACTIVE)->get_connection()); auto activeConGFile = activeConUri ? g_file_new_for_uri(activeConUri) : nullptr; auto inactiveConUri = gnome_cmd_con_get_uri(main_win->fs(INACTIVE)->get_connection()); auto inactiveConGFile = inactiveConUri ? g_file_new_for_uri(inactiveConUri) : nullptr; if (activeConUri && g_file_equal(gFile, activeConGFile)) { gnome_cmd_con_close (main_win->fs(ACTIVE)->get_connection()); main_win->fs(ACTIVE)->set_connection(get_home_con()); } if (inactiveConUri && g_file_equal(gFile, inactiveConGFile)) { gnome_cmd_con_close (main_win->fs(INACTIVE)->get_connection()); main_win->fs(INACTIVE)->set_connection(get_home_con()); } g_free(uriString); g_object_unref(gFile); }