/* GVFS plugin for Tux Commander * Copyright (C) 2008-2009 Tomas Bzatek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library 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 Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include "tuxcmd-vfs.h" #define VERSION "0.2.1" #define BUILD_DATE "2009-12-09" #define DEFAULT_BLOCK_SIZE 0x10000 /* 64kB */ #define CONST_DEFAULT_QUERY_INFO_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," \ G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," \ G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET "," \ G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_ACCESS "," G_FILE_ATTRIBUTE_TIME_CHANGED "," \ G_FILE_ATTRIBUTE_UNIX_MODE "," G_FILE_ATTRIBUTE_UNIX_UID "," \ G_FILE_ATTRIBUTE_UNIX_GID "," G_FILE_ATTRIBUTE_UNIX_INODE struct TVFSGlobs { TVFSLogFunc log_func; GFile *file; GCancellable *cancellable; GFileEnumerator *enumerator; GFile *enumerated_file; gboolean enum_follow_symlinks; gboolean enum_add_full_path; GMainLoop *mount_main_loop; GError *mount_error; int mount_try; gboolean ftp_anonymous; gboolean break_get_dir_size; guint32 block_size; TVFSAskQuestionCallback callback_ask_question; TVFSAskPasswordCallback callback_ask_password; TVFSProgressCallback callback_progress; void *callback_data; }; static void ask_password_cb (GMountOperation *op, const char *message, const char *default_user, const char *default_domain, GAskPasswordFlags flags, gpointer user_data) { struct TVFSGlobs *globs; char *username; char *password; gboolean anonymous; char *domain; TVFSPasswordSave password_save; gboolean result; globs = (struct TVFSGlobs*) user_data; g_assert (globs != NULL); globs->mount_try++; /* First pass, look if we have a password to supply */ if (globs->mount_try == 1) { if ((flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED) && globs->ftp_anonymous) { printf ("(WW) ask_password_cb: mount_try = %d, trying FTP anonymous login...\n", globs->mount_try); g_mount_operation_set_anonymous (op, TRUE); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); return; } } /* Ask user for password */ g_print ("(WW) ask_password_cb: mount_try = %d, message = '%s'\n", globs->mount_try, message); /* Handle abort message from certain backends properly */ /* - e.g. SMB backends use this to mask multiple auth callbacks from smbclient */ if (default_user && strcmp (default_user, "ABORT") == 0) { g_print ("(WW) default_user == \"ABORT\", aborting\n"); g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); return; } username = NULL; domain = NULL; password = NULL; anonymous = FALSE; password_save = VFS_PASSWORD_SAVE_NEVER; if (globs->callback_ask_password) { fprintf (stderr, " (II) Spawning callback_ask_password (%p)...\n", globs->callback_ask_password); result = globs->callback_ask_password (message, default_user, default_domain, NULL, flags, &username, &password, &anonymous, &domain, &password_save, globs->callback_data); fprintf (stderr, " (II) Received result = %d\n", result); if (result) { if (flags & G_ASK_PASSWORD_NEED_USERNAME) g_mount_operation_set_username (op, username); if (flags & G_ASK_PASSWORD_NEED_DOMAIN) g_mount_operation_set_domain (op, domain); if (flags & G_ASK_PASSWORD_NEED_PASSWORD) g_mount_operation_set_password (op, password); if (flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED) g_mount_operation_set_anonymous (op, anonymous); if (flags & G_ASK_PASSWORD_SAVING_SUPPORTED) g_mount_operation_set_password_save (op, password_save); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); } else g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); return; } /* Unhandled, abort */ g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); } static void ask_question_cb (GMountOperation *op, const gchar *message, const gchar *choices[], gpointer user_data) { struct TVFSGlobs *globs; int len; int choice; globs = (struct TVFSGlobs*) user_data; g_assert (globs != NULL); g_print ("(WW) ask_question_cb: message = '%s'\n", message); len = 0; while (choices[len] != NULL) { g_print ("(WW) ask_question_cb: choice[%d] = '%s'\n", len, choices[len]); len++; } choice = -1; if (globs->callback_ask_question) { fprintf (stderr, " (II) Spawning callback_ask_question (%p)...\n", globs->callback_ask_question); /* At this moment, only SFTP uses ask_question and the second button is cancellation */ globs->callback_ask_question (message, choices, &choice, 1, globs->callback_data); fprintf (stderr, " (II) Received choice = %d\n", choice); if (choice >= 0) { g_mount_operation_set_choice (op, choice); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); } else { g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); } return; } g_mount_operation_reply (op, G_MOUNT_OPERATION_UNHANDLED); } static void mount_done_cb (GObject *object, GAsyncResult *res, gpointer user_data) { struct TVFSGlobs *globs; gboolean succeeded; globs = (struct TVFSGlobs*) user_data; g_assert (globs != NULL); succeeded = g_file_mount_enclosing_volume_finish (G_FILE (object), res, &globs->mount_error); if (! succeeded) g_print ("(EE) Error mounting location: %s\n", globs->mount_error->message); else g_print ("(II) Mount successful.\n"); g_main_loop_quit (globs->mount_main_loop); } static GError * vfs_handle_mount (struct TVFSGlobs *globs, GFile *file) { GMountOperation *op; g_print ("(II) Mounting location...\n"); op = g_mount_operation_new (); g_signal_connect (op, "ask-password", (GCallback)ask_password_cb, globs); g_signal_connect (op, "ask-question", (GCallback)ask_question_cb, globs); globs->mount_error = NULL; globs->mount_try = 0; /* Inspiration taken from Bastien Nocera's http://svn.gnome.org/viewvc/totem-pl-parser/trunk/plparse/totem-disc.c?view=markup */ globs->mount_main_loop = g_main_loop_new (NULL, FALSE); g_file_mount_enclosing_volume (file, G_MOUNT_MOUNT_NONE, op, NULL, mount_done_cb, globs); g_main_loop_run (globs->mount_main_loop); g_main_loop_unref (globs->mount_main_loop); globs->mount_main_loop = NULL; g_object_unref (op); return globs->mount_error; } struct TVFSGlobs * VFSNew (TVFSLogFunc log_func) { struct TVFSGlobs * globs; globs = (struct TVFSGlobs *) malloc (sizeof (struct TVFSGlobs)); memset (globs, 0, sizeof (struct TVFSGlobs)); globs->log_func = log_func; globs->log_func ("GVFS plugin: VFSInit"); globs->file = NULL; globs->enumerator = NULL; globs->enumerated_file = NULL; globs->cancellable = NULL; globs->break_get_dir_size = FALSE; globs->block_size = DEFAULT_BLOCK_SIZE; globs->callback_data = NULL; globs->callback_ask_question = NULL; globs->callback_ask_password = NULL; globs->callback_progress = NULL; return globs; } void VFSSetCallbacks (struct TVFSGlobs *globs, TVFSAskQuestionCallback ask_question_callback, TVFSAskPasswordCallback ask_password_callback, TVFSProgressCallback progress_func, void *data) { globs->callback_ask_question = ask_question_callback; globs->callback_ask_password = ask_password_callback; globs->callback_progress = progress_func; globs->callback_data = data; } void VFSFree (struct TVFSGlobs *globs) { globs->log_func ("GVFS plugin: VFSDestroy"); g_free (globs); } int VFSVersion () { return cVFSVersion; } struct TVFSInfo * VFSGetInfo () { struct TVFSInfo *module_info = g_malloc0 (sizeof (struct TVFSInfo)); module_info->ID = g_strdup ("gvfs_plugin"); module_info->Name = g_strdup ("GVFS plugin"); module_info->About = g_strdup_printf ("version %s, build date: %s", VERSION, BUILD_DATE); module_info->Copyright = g_strdup ("Copyright (C) 2008-2009 Tomáš Bžatek"); return module_info; } guint32 VFSGetCapabilities () { return VFS_CAP_HANDLES_MULTIPLE_REQUESTS | VFS_CAP_CAN_BROWSE_NETWORK; } #if 0 char * VFSGetArchiveExts () { return NULL; } #endif char * VFSGetNetworkServices () { GVfs *gvfs; const gchar* const * schemes; char *l = NULL; char *s; gvfs = g_vfs_get_default (); g_print ("(II) GVFS: is_active = %d\n", g_vfs_is_active (gvfs)); schemes = g_vfs_get_supported_uri_schemes (gvfs); for (; *schemes; schemes++) { if (l) { s = g_strdup_printf ("%s;%s", l, *schemes); g_free (l); l = s; } else l = g_strdup (*schemes); } g_print ("(II) GVFS: supported schemes: %s\n", l); return l; } gboolean VFSOpenURI (struct TVFSGlobs *globs, const char *sURI, GError **error) { GFile *f, *f2; GFileInfo *info; GError *local_error; globs->file = NULL; globs->ftp_anonymous = FALSE; if (strstr (sURI, "@") == NULL) { /* test for FTP protocol (we only enable anonymous here) */ globs->ftp_anonymous = strcasestr (sURI, "ftp://") == sURI; } g_print ("(II) VFSOpenURI: opening URI '%s'\n", sURI); f = g_file_new_for_commandline_arg (sURI); while (1) { local_error = NULL; info = g_file_query_info (f, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error); /* Fallback to parent directory if specified path doesn't exist */ if (local_error && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { f2 = g_file_get_parent (f); if (f2) { g_object_unref (f); f = f2; g_error_free (local_error); continue; } } /* Mount the target */ if (local_error && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) { g_error_free (local_error); local_error = vfs_handle_mount (globs, f); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } else continue; } /* Any other errors --> report */ if (local_error) { g_print ("(EE) VFSOpenURI: g_file_query_info() error: %s\n", local_error->message); g_propagate_error (error, local_error); g_object_unref (f); return FALSE; } /* everything ok? */ break; } globs->file = f; return TRUE; } gboolean VFSClose (struct TVFSGlobs *globs, GError **error) { g_print ("(II) VFSClose\n"); if (globs->file) g_object_unref (globs->file); globs->file = NULL; return TRUE; } static char * get_absolute_path (GFile *file) { GFile *root; char *path, *s; if (file) { root = g_file_resolve_relative_path (file, "/"); if (root == NULL) return NULL; path = g_file_get_relative_path (root, file); if (path == NULL) { g_object_unref (root); return NULL; } if (! g_path_is_absolute (path)) s = g_strdup_printf ("/%s", path); else s = g_strdup (path); g_free (path); g_object_unref (root); return s; } else return NULL; } char * VFSGetPath (struct TVFSGlobs *globs) { char *s; s = get_absolute_path (globs->file); g_print ("(II) VFSGetPath: '%s'\n", s); return s; } char * VFSGetPathURI (struct TVFSGlobs *globs) { if (globs->file) return g_file_get_uri (globs->file); else return NULL; } void VFSGetFileSystemInfo (struct TVFSGlobs *globs, const char *APath, gint64 *FSSize, gint64 *FSFree, char **FSLabel) { GFileInfo *info; GError *error; if (FSSize) *FSSize = -1; if (FSFree) *FSFree = -1; if (FSLabel) *FSLabel = NULL; if (globs->file == NULL) return; error = NULL; info = g_file_query_filesystem_info (globs->file, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE "," G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, &error); if (error) { g_print ("(EE) VFSGetFileSystemInfo: %s\n", error->message); g_error_free (error); } else { if (FSSize) *FSSize = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); if (FSFree) *FSFree = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); } if (info) g_object_unref (info); } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ gboolean VFSChangeDir (struct TVFSGlobs *globs, const char *NewPath, GError **error) { GFile *f; GFileEnumerator *en; GError *local_error; GFileInfo *info; gchar *target_uri; if (globs->file == NULL) { g_print ("(EE) VFSChangeDir: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } g_print ("(II) VFSChangeDir: changing dir to '%s'\n", NewPath); f = g_file_resolve_relative_path (globs->file, NewPath); if (f == NULL) { g_print ("(EE) VFSChangeDir: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return FALSE; } while (1) { local_error = NULL; en = g_file_enumerate_children (f, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error); /* if the target is shortcut, change the URI */ if (local_error && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY)) { info = g_file_query_info (f, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); if (info) { target_uri = g_strdup (g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI)); g_object_unref (info); if (target_uri) { g_print ("(WW) VFSChangeDir: following shortcut, changing URI to '%s'\n", target_uri); g_object_unref (f); f = g_file_new_for_uri (target_uri); g_free (target_uri); g_error_free (local_error); continue; } } } /* Mount the target */ if (local_error && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) { g_error_free (local_error); local_error = vfs_handle_mount (globs, f); if (local_error != NULL) { g_object_unref (f); g_propagate_error (error, local_error); return FALSE; } else continue; } /* Any other errors --> report */ if (local_error) { g_print ("(EE) VFSChangeDir: g_file_enumerate_children() error: %s\n", local_error->message); g_propagate_error (error, local_error); g_object_unref (f); return FALSE; } /* everything ok? */ break; } globs->enumerator = en; globs->enumerated_file = g_file_dup (f); g_object_unref (globs->file); globs->file = f; return TRUE; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ static struct TVFSItem * g_file_info_to_TVFSItem (GFileInfo *info, GFile *reference_file, gboolean follow_symlinks, gboolean add_full_path) { GFileInfo *symlink_info = NULL; GError *error; struct TVFSItem *Item; g_assert (info != NULL); Item = g_malloc0 (sizeof (struct TVFSItem)); if (add_full_path) { Item->FName = get_absolute_path (reference_file); if (Item->FName == NULL) Item->FName = g_strdup (g_file_info_get_name (info)); Item->FDisplayName = g_filename_display_name (Item->FName); } else { Item->FName = g_strdup (g_file_info_get_name (info)); Item->FDisplayName = g_strdup (g_file_info_get_display_name (info)); } Item->sLinkTo = g_strdup (g_file_info_get_symlink_target (info)); Item->m_time = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); Item->a_time = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS); Item->c_time = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED); Item->iUID = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID); Item->iGID = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID); Item->inode_no = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE); // g_print ("(II) g_file_info_to_TVFSItem: type = %d\n", g_file_info_get_file_type (info)); // g_print ("(II) g_file_info_to_TVFSItem: UNIX_MODE = %d\n", Item->iMode); Item->IsLink = g_file_info_get_is_symlink (info); if (Item->IsLink && follow_symlinks) { error = NULL; symlink_info = g_file_query_info (reference_file, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NONE, NULL, &error); if (error) { g_print ("(EE) g_file_info_to_TVFSItem: g_file_query_info() error: %s\n", error->message); g_error_free (error); } } Item->iSize = g_file_info_get_size (symlink_info ? symlink_info : info); Item->iPackedSize = -1; Item->iMode = g_file_info_get_attribute_uint32 (symlink_info ? symlink_info : info, G_FILE_ATTRIBUTE_UNIX_MODE); switch (g_file_info_get_file_type (symlink_info ? symlink_info : info)) { case G_FILE_TYPE_REGULAR: Item->ItemType = vRegular; break; case G_FILE_TYPE_DIRECTORY: case G_FILE_TYPE_SHORTCUT: /* Used in network:/// */ case G_FILE_TYPE_MOUNTABLE: Item->ItemType = vDirectory; break; case G_FILE_TYPE_SPECIAL: /* socket, fifo, blockdev, chardev */ Item->ItemType = vBlockdev; break; case G_FILE_TYPE_UNKNOWN: default: Item->ItemType = vRegular; } /* fallback to default file mode if read fails */ if (Item->iMode == 0) { if (Item->ItemType == vDirectory) Item->iMode = S_IFDIR + S_IRWXU + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH; else Item->iMode = S_IRUSR + S_IWUSR + S_IRGRP + S_IROTH; } if (symlink_info) g_object_unref (symlink_info); return Item; } struct TVFSItem * VFSListNext (struct TVFSGlobs *globs, GError **error) { GFileInfo *info; GFile *f; GError *local_error; struct TVFSItem *Item; if (globs->file == NULL) { g_print ("(EE) VFSListNext: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return NULL; } if (globs->enumerator == NULL) { g_print ("(EE) VFSListNext: globs->enumerator == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->enumerator == NULL"); return NULL; } local_error = NULL; info = g_file_enumerator_next_file (globs->enumerator, NULL, &local_error); if (local_error) { g_print ("(EE) VFSListNext: g_file_enumerator_next_file() error: %s\n", local_error->message); g_propagate_error (error, local_error); return FALSE; } if (! info) return NULL; f = g_file_get_child (globs->enumerated_file, g_file_info_get_name (info)); Item = g_file_info_to_TVFSItem (info, f, globs->enum_follow_symlinks, globs->enum_add_full_path); g_object_unref (f); g_object_unref (info); return Item; } struct TVFSItem * VFSListFirst (struct TVFSGlobs *globs, const char *sDir, gboolean FollowSymlinks, gboolean AddFullPath, GError **error) { GFile *list_file; GError *local_error; list_file = g_file_resolve_relative_path (globs->file, sDir); if (! list_file) { g_print ("(EE) VFSListFirst: list_file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return NULL; } if (globs->enumerator == NULL || ! g_file_equal (globs->file, list_file)) { g_print ("(WW) VFSListFirst: resetting enumerator\n"); if (globs->enumerator) { g_file_enumerator_close (globs->enumerator, NULL, NULL); g_object_unref (globs->enumerator); } if (globs->enumerated_file) g_object_unref (globs->enumerated_file); globs->enumerated_file = NULL; local_error = NULL; globs->enumerator = g_file_enumerate_children (list_file, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error); if (local_error) { g_print ("(EE) VFSListFirst: g_file_enumerate_children() error: %s\n", local_error->message); g_propagate_error (error, local_error); g_object_unref (list_file); return NULL; } globs->enumerated_file = g_object_ref (list_file); } globs->enum_follow_symlinks = FollowSymlinks; globs->enum_add_full_path = AddFullPath; g_object_unref (list_file); return VFSListNext (globs, error); } gboolean VFSListClose (struct TVFSGlobs *globs, GError **error) { GError *local_error; if (globs->enumerator == NULL) { g_print ("(EE) VFSListClose: globs->enumerator == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->enumerator == NULL"); return FALSE; } g_print ("(II) VFSListClose\n"); local_error = NULL; g_file_enumerator_close (globs->enumerator, NULL, &local_error); g_object_unref (globs->enumerator); globs->enumerator = NULL; g_object_unref (globs->enumerated_file); globs->enumerated_file = NULL; if (local_error) { g_print ("(EE) VFSListClose: g_file_enumerator_close() error: %s\n", local_error->message); g_propagate_error (error, local_error); return FALSE; } return TRUE; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ struct TVFSItem * VFSFileInfo (struct TVFSGlobs *globs, const char *AFileName, gboolean FollowSymlinks, gboolean AddFullPath, GError **error) { GFile *f; GError *local_error; GFileInfo *info; struct TVFSItem *Item; if (globs->file == NULL) { g_print ("(EE) VFSFileInfo: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return NULL; } f = g_file_resolve_relative_path (globs->file, AFileName); if (f == NULL) { g_print ("(EE) VFSMkDir: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return NULL; } local_error = NULL; info = g_file_query_info (f, CONST_DEFAULT_QUERY_INFO_ATTRIBUTES, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error); if (local_error) { g_print ("(EE) VFSFileInfo: g_file_query_info() error: %s\n", local_error->message); g_propagate_error (error, local_error); g_object_unref (f); return NULL; } Item = g_file_info_to_TVFSItem (info, f, FollowSymlinks, AddFullPath); g_object_unref (info); g_object_unref (f); return Item; } gboolean VFSMkDir (struct TVFSGlobs *globs, const char *sDirName, GError **error) { GFile *f; GError *local_error; if (globs->file == NULL) { g_print ("(EE) VFSMkDir: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } f = g_file_resolve_relative_path (globs->file, sDirName); if (f == NULL) { g_print ("(EE) VFSMkDir: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return FALSE; } local_error = NULL; g_file_make_directory (f, NULL, &local_error); g_object_unref (f); if (local_error) { g_print ("(EE) VFSMkDir: g_file_make_directory() error: %s\n", local_error->message); g_propagate_error (error, local_error); return FALSE; } return TRUE; } gboolean VFSRemove (struct TVFSGlobs *globs, const char *APath, GError **error) { GFile *f; GError *local_error; if (globs->file == NULL) { g_print ("(EE) VFSRemove: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } f = g_file_resolve_relative_path (globs->file, APath); if (f == NULL) { g_print ("(EE) VFSRemove: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return FALSE; } local_error = NULL; g_file_delete (f, NULL, &local_error); g_object_unref (f); if (local_error) { g_print ("(EE) VFSRemove: g_file_delete() error: %s\n", local_error->message); g_propagate_error (error, local_error); return FALSE; } return TRUE; } gboolean VFSRename (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, GError **error) { GFile *src, *dst; GError *local_error; if (globs->file == NULL) { g_print ("(EE) VFSRename: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } src = g_file_resolve_relative_path (globs->file, sSrcName); if (src == NULL) { g_print ("(EE) VFSRename: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return FALSE; } g_print ("VFSRename: '%s' --> '%s'\n", sSrcName, sDstName); local_error = NULL; g_file_set_display_name (src, sDstName, NULL, &local_error); if (local_error) { g_print ("(WW) VFSRename: g_file_set_display_name() failed (\"%s\"), using fallback g_file_move()\n", local_error->message); g_error_free (local_error); dst = g_file_resolve_relative_path (src, sDstName); if (dst == NULL) { g_print ("(EE) VFSRename: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path for target file."); g_object_unref (src); return FALSE; } local_error = NULL; g_file_move (src, dst, G_FILE_COPY_NO_FALLBACK_FOR_MOVE, NULL, NULL, NULL, &local_error); if (local_error) { g_print ("(EE) VFSRename: g_file_move() error: %s\n", local_error->message); g_propagate_error (error, local_error); g_object_unref (src); g_object_unref (dst); return FALSE; } g_object_unref (dst); } g_object_unref (src); return TRUE; } gboolean VFSMakeSymLink (struct TVFSGlobs *globs, const char *NewFileName, const char *PointTo, GError **error) { GFile *f; GError *local_error; if (globs->file == NULL) { g_print ("(EE) VFSMakeSymLink: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } f = g_file_resolve_relative_path (globs->file, NewFileName); if (f == NULL) { g_print ("(EE) VFSMakeSymLink: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return FALSE; } local_error = NULL; g_file_make_symbolic_link (f, PointTo, NULL, &local_error); g_object_unref (f); if (local_error) { g_print ("(EE) VFSMakeSymLink: g_file_make_symbolic_link() error: %s\n", local_error->message); g_propagate_error (error, local_error); return FALSE; } return TRUE; } gboolean VFSChmod (struct TVFSGlobs *globs, const char *FileName, guint32 Mode, GError **error) { GFile *f; GError *local_error; if (globs->file == NULL) { g_print ("(EE) VFSChmod: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } f = g_file_resolve_relative_path (globs->file, FileName); if (f == NULL) { g_print ("(EE) VFSChmod: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return FALSE; } local_error = NULL; g_file_set_attribute_uint32 (f, G_FILE_ATTRIBUTE_UNIX_MODE, Mode, G_FILE_QUERY_INFO_NONE, NULL, &local_error); g_object_unref (f); if (local_error) { g_print ("(EE) VFSChmod: g_file_set_attribute_uint32() error: %s\n", local_error->message); g_propagate_error (error, local_error); return FALSE; } return TRUE; } gboolean VFSChown (struct TVFSGlobs *globs, const char *FileName, guint32 UID, guint32 GID, GError **error) { GFile *f; GError *local_error; if (globs->file == NULL) { g_print ("(EE) VFSChown: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } f = g_file_resolve_relative_path (globs->file, FileName); if (f == NULL) { g_print ("(EE) VFSChown: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return FALSE; } local_error = NULL; g_file_set_attribute_uint32 (f, G_FILE_ATTRIBUTE_UNIX_UID, UID, G_FILE_QUERY_INFO_NONE, NULL, &local_error); if (local_error) { g_print ("(EE) VFSChown: g_file_set_attribute_uint32() error: %s\n", local_error->message); g_propagate_error (error, local_error); g_object_unref (f); return FALSE; } local_error = NULL; g_file_set_attribute_uint32 (f, G_FILE_ATTRIBUTE_UNIX_GID, GID, G_FILE_QUERY_INFO_NONE, NULL, &local_error); if (local_error) { g_print ("(EE) VFSChown: g_file_set_attribute_uint32() error: %s\n", local_error->message); g_propagate_error (error, local_error); g_object_unref (f); return FALSE; } g_object_unref (f); return TRUE; } gboolean VFSChangeTimes (struct TVFSGlobs *globs, const char *APath, guint32 mtime, guint32 atime, GError **error) { GFile *f; GError *local_error; if (globs->file == NULL) { g_print ("(EE) VFSChangeTimes: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } f = g_file_resolve_relative_path (globs->file, APath); if (f == NULL) { g_print ("(EE) VFSChangeTimes: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path."); return FALSE; } /* TODO: add nanoseconds */ /* TODO: change to g_file_set_attributes_from_info () */ local_error = NULL; g_file_set_attribute_uint64 (f, G_FILE_ATTRIBUTE_TIME_MODIFIED, mtime, G_FILE_QUERY_INFO_NONE, NULL, &local_error); if (local_error) { g_print ("(EE) VFSChangeTimes: g_file_set_attribute_uint64() error: %s\n", local_error->message); g_propagate_error (error, local_error); g_object_unref (f); return FALSE; } local_error = NULL; g_file_set_attribute_uint64 (f, G_FILE_ATTRIBUTE_TIME_ACCESS, atime, G_FILE_QUERY_INFO_NONE, NULL, &local_error); if (local_error) { g_print ("(EE) VFSChangeTimes: g_file_set_attribute_uint64() error: %s\n", local_error->message); g_error_free (local_error); /* Silently drop the error, atime is not commonly supported on most systems */ } g_object_unref (f); return TRUE; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ static void VFSGetDirSize_recurse (struct TVFSGlobs *globs, GFile *file, guint64 *size) { GFile *f; GFileEnumerator *en; GError *error; GFileInfo *info; if (globs->break_get_dir_size) return; error = NULL; en = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); if (error) { g_print ("(EE) VFSGetDirSize_recurse: g_file_enumerate_children() error: %s\n", error->message); g_error_free (error); return; } error = NULL; while ((info = g_file_enumerator_next_file (en, NULL, &error))) { if (globs->break_get_dir_size) break; if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { f = g_file_resolve_relative_path (file, g_file_info_get_name (info)); if (f == NULL) { g_print ("(EE) VFSGetDirSize_recurse: g_file_resolve_relative_path() failed.\n"); return; } VFSGetDirSize_recurse (globs, f, size); g_object_unref (f); } else *size += g_file_info_get_size (info); g_object_unref (info); } if (error) { g_print ("(EE) VFSGetDirSize_recurse: g_file_enumerator_next_file() error: %s\n", error->message); g_error_free (error); } g_file_enumerator_close (en, NULL, &error); g_object_unref (en); if (error) { g_print ("(EE) VFSGetDirSize_recurse: g_file_enumerator_close() error: %s\n", error->message); g_error_free (error); } } guint64 VFSGetDirSize (struct TVFSGlobs *globs, const char *APath) { GFile *f; guint64 size; if (globs == NULL) return 0; if (globs->file == NULL) { g_print ("(EE) VFSGetDirSize: globs->file == NULL !\n"); return 0; } f = g_file_resolve_relative_path (globs->file, APath); if (f == NULL) { g_print ("(EE) VFSGetDirSize: g_file_resolve_relative_path() failed.\n"); return 0; } size = 0; globs->break_get_dir_size = FALSE; VFSGetDirSize_recurse (globs, f, &size); globs->break_get_dir_size = FALSE; g_object_unref (f); return size; } void VFSBreakGetDirSize (struct TVFSGlobs *globs) { g_print ("(WW) VFSBreakGetDirSize: ################################### calling Break\n"); if (globs) globs->break_get_dir_size = TRUE; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ #if 0 TVFSFileDes VFSOpenFile (struct TVFSGlobs *globs, const char *APath, int Mode, int *Error) { printf("(WW) VFSOpenFile: Not supported in GVFS plugin.\n"); return NULL; } TVFSResult VFSCloseFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor) { printf("(WW) VFSCloseFile: Not supported in GVFS plugin.\n"); return cVFS_Not_Supported; } guint64 VFSFileSeek (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, guint64 AbsoluteOffset, int *Error) { printf("(WW) VFSFileSeek: Not supported in GVFS plugin.\n"); return 0; } int VFSReadFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, gpointer Buffer, int ABlockSize, int *Error) { printf("(WW) VFSReadFile: Not supported in GVFS plugin.\n"); return cVFS_Not_Supported; } int VFSWriteFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, gpointer Buffer, int BytesCount, int *Error) { printf("(WW) VFSWriteFile: Not supported in GVFS plugin.\n"); return cVFS_Not_Supported; } #endif void VFSSetBlockSize (struct TVFSGlobs *globs, guint32 Value) { if (globs) globs->block_size = Value; } gboolean VFSIsOnSameFS (struct TVFSGlobs *globs, const char *Path1, const char *Path2, gboolean FollowSymlinks) { GFile *file1, *file2; GFileInfo *info1, *info2; GError *error; gboolean res; if (globs->file == NULL) { g_print ("(EE) VFSIsOnSameFS: globs->file == NULL !\n"); return FALSE; } file1 = g_file_resolve_relative_path (globs->file, Path1); file2 = g_file_resolve_relative_path (globs->file, Path2); if (file1 == NULL) { g_print ("(EE) VFSIsOnSameFS: g_file_resolve_relative_path() failed.\n"); return FALSE; } if (file2 == NULL) { g_print ("(EE) VFSIsOnSameFS: g_file_resolve_relative_path() failed.\n"); return FALSE; } error = NULL; info1 = g_file_query_info (file1, G_FILE_ATTRIBUTE_ID_FILESYSTEM, FollowSymlinks ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); if (error) { g_print ("(EE) VFSIsOnSameFS: g_file_query_info() error: %s\n", error->message); g_error_free (error); g_object_unref (file1); g_object_unref (file2); return FALSE; } info2 = g_file_query_info (file2, G_FILE_ATTRIBUTE_ID_FILESYSTEM, FollowSymlinks ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error); if (error) { g_print ("(EE) VFSIsOnSameFS: g_file_query_info() error: %s\n", error->message); g_error_free (error); g_object_unref (info1); g_object_unref (file1); g_object_unref (file2); return FALSE; } g_print ("(II) VFSIsOnSameFS: '%s' vs. '%s'\n", g_file_info_get_attribute_string (info1, G_FILE_ATTRIBUTE_ID_FILESYSTEM), g_file_info_get_attribute_string (info2, G_FILE_ATTRIBUTE_ID_FILESYSTEM)); res = strcmp (g_file_info_get_attribute_string (info1, G_FILE_ATTRIBUTE_ID_FILESYSTEM), g_file_info_get_attribute_string (info2, G_FILE_ATTRIBUTE_ID_FILESYSTEM)) == 0; g_object_unref (file1); g_object_unref (file2); g_object_unref (info1); g_object_unref (info2); return res; } gboolean VFSTwoSameFiles (struct TVFSGlobs *globs, const char *Path1, const char *Path2, gboolean FollowSymlinks) { GFile *file1, *file2; gboolean res; if (globs->file == NULL) { g_print ("(EE) VFSTwoSameFiles: globs->file == NULL !\n"); return FALSE; } file1 = g_file_resolve_relative_path (globs->file, Path1); file2 = g_file_resolve_relative_path (globs->file, Path2); if (file1 == NULL) { g_print ("(EE) VFSTwoSameFiles: g_file_resolve_relative_path() failed.\n"); return FALSE; } if (file2 == NULL) { g_print ("(EE) VFSTwoSameFiles: g_file_resolve_relative_path() failed.\n"); g_object_unref (file1); return FALSE; } /* FIXME: we should do some I/O test here, we're esentially comparing strings at the moment */ res = g_file_equal (file1, file2); g_object_unref (file1); g_object_unref (file2); return res; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ static void vfs_copy_progress_callback (goffset current_num_bytes, goffset total_num_bytes, gpointer user_data) { struct TVFSGlobs *globs; // g_print ("(II) vfs_copy_progress_callback spawned: current_num_bytes = %lu, total_num_bytes = %lu\n", current_num_bytes, total_num_bytes); if (! user_data) return; globs = (struct TVFSGlobs *)user_data; if (globs->callback_progress) { if (! globs->callback_progress (current_num_bytes, NULL, globs->callback_data)) g_cancellable_cancel (globs->cancellable); } } #define TUXCMD_DEFAULT_COPY_FLAGS G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA gboolean VFSCopyToLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append, GError **error) { GFile *src, *dst; GError *local_error; gboolean res; if (globs->file == NULL) { g_print ("(EE) VFSCopyToLocal: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } g_print ("(II) VFSCopyToLocal: '%s' --> '%s'\n", sSrcName, sDstName); src = g_file_resolve_relative_path (globs->file, sSrcName); if (src == NULL) { g_print ("(EE) VFSCopyToLocal: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path for source file."); return FALSE; } dst = g_file_new_for_path (sDstName); if (dst == NULL) { g_print ("(EE) VFSCopyToLocal: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot get target file."); return FALSE; } res = TRUE; local_error = NULL; globs->cancellable = g_cancellable_new (); g_file_copy (src, dst, TUXCMD_DEFAULT_COPY_FLAGS, globs->cancellable, vfs_copy_progress_callback, globs, &local_error); if (local_error) { g_print ("(EE) VFSCopyToLocal: g_file_copy() error: %s\n", local_error->message); g_propagate_error (error, local_error); res = FALSE; } g_object_unref (globs->cancellable); g_object_unref (src); g_object_unref (dst); return res; } gboolean VFSCopyFromLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append, GError **error) { GFile *src, *dst; GError *local_error; gboolean res; if (globs->file == NULL) { g_print ("(EE) VFSCopyFromLocal: globs->file == NULL !\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "globs->file == NULL"); return FALSE; } g_print ("(II) VFSCopyFromLocal: '%s' --> '%s'\n", sSrcName, sDstName); src = g_file_new_for_path (sSrcName); if (src == NULL) { g_print ("(EE) VFSCopyFromLocal: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot get source file."); return FALSE; } dst = g_file_resolve_relative_path (globs->file, sDstName); if (dst == NULL) { g_print ("(EE) VFSCopyFromLocal: g_file_resolve_relative_path() failed.\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Cannot resolve relative path for target file."); return FALSE; } globs->cancellable = g_cancellable_new (); res = TRUE; local_error = NULL; globs->cancellable = g_cancellable_new (); /* FIXME: Appending not supported */ g_file_copy (src, dst, TUXCMD_DEFAULT_COPY_FLAGS, globs->cancellable, vfs_copy_progress_callback, globs, &local_error); if (local_error) { g_print ("(EE) VFSCopyFromLocal: g_file_copy() error: %s\n", local_error->message); g_propagate_error (error, local_error); res = FALSE; } g_object_unref (globs->cancellable); g_object_unref (src); g_object_unref (dst); return res; } /********** * TODO: * NOT NEEDED - block size settings for GIO subsystem * NOT NEEDED - variable block size for different protocols? * - support for appending in VFSCopyFromLocal * DONE- authentication improvements (needs new VFS API) * ***/