[file-roller] Bring back support for file caching when gvfs-fuse-daemon is not available



commit 33c21fc3647af32de5a9c9d49e33765a526910f5
Author: Tomas Bzatek <tbzatek redhat com>
Date:   Wed May 12 13:54:29 2010 +0200

    Bring back support for file caching when gvfs-fuse-daemon is not available
    
    The commit 73fa5d77a60861cd35c6f3425d27c88061af081c introduced trouble
    when user was trying to work with archives on remote GIO mounts and
    not having the fuse daemon running. This led to segfaults and error messages.
    
    Please see bug #617769 for details.

 src/fr-archive.c |  595 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 src/fr-archive.h |    2 +
 src/fr-window.c  |    8 +-
 src/gio-utils.c  |  155 +++++++++------
 src/gio-utils.h  |    2 +-
 5 files changed, 642 insertions(+), 120 deletions(-)
---
diff --git a/src/fr-archive.c b/src/fr-archive.c
index e4a1259..adf9976 100644
--- a/src/fr-archive.c
+++ b/src/fr-archive.c
@@ -114,16 +114,51 @@ struct _FrArchivePrivData {
 	FakeLoadFunc         add_is_stoppable_func;         /* Returns whether the add operation is
 							     * stoppable. */
 	gpointer             add_is_stoppable_data;
-  	GCancellable        *cancellable;
+	GCancellable        *cancellable;
 	char                *temp_dir;
 	gboolean             continue_adding_dropped_items;
 	DroppedItemsData    *dropped_items_data;
 
+	char                *temp_extraction_dir;
 	char                *extraction_destination;
+	gboolean             remote_extraction;
 	gboolean             extract_here;
 };
 
 
+typedef struct {
+	FrArchive      *archive;
+	char           *uri;
+	FrAction        action;
+	GList          *file_list;
+	char           *base_uri;
+	char           *dest_dir;
+	gboolean        update;
+	char           *tmp_dir;
+	guint           source_id;
+	char           *password;
+	gboolean        encrypt_header;
+	FrCompression   compression;
+	guint           volume_size;
+} XferData;
+
+
+static void
+xfer_data_free (XferData *data)
+{
+	if (data == NULL)
+		return;
+
+	g_free (data->uri);
+	g_free (data->password);
+	path_list_free (data->file_list);
+	g_free (data->base_uri);
+	g_free (data->dest_dir);
+	g_free (data->tmp_dir);
+	g_free (data);
+}
+
+
 #define MAX_CHUNK_LEN (NCARGS * 2 / 3) /* Max command line length */
 #define UNKNOWN_TYPE "application/octet-stream"
 #define SAME_FS (FALSE)
@@ -322,6 +357,8 @@ static void
 fr_archive_init (FrArchive *archive)
 {
 	archive->file = NULL;
+	archive->local_copy = NULL;
+	archive->is_remote = FALSE;
 	archive->command = NULL;
 	archive->is_compressed_file = FALSE;
 	archive->can_create_compressed_file = FALSE;
@@ -333,6 +370,7 @@ fr_archive_init (FrArchive *archive)
 	archive->priv->add_is_stoppable_data = NULL;
 
 	archive->priv->extraction_destination = NULL;
+	archive->priv->temp_extraction_dir = NULL;
 	archive->priv->cancellable = g_cancellable_new ();
 
 	archive->process = fr_process_new ();
@@ -350,18 +388,73 @@ fr_archive_new (void)
 }
 
 
+static GFile *
+get_local_copy_for_file (GFile *remote_file)
+{
+	char  *temp_dir;
+	GFile *local_copy = NULL;
+
+	temp_dir = get_temp_work_dir (NULL);
+	if (temp_dir != NULL) {
+		char  *archive_name;
+		char  *local_path;
+
+		archive_name = g_file_get_basename (remote_file);
+		local_path = g_build_filename (temp_dir, archive_name, NULL);
+		local_copy = g_file_new_for_path (local_path);
+
+		g_free (local_path);
+		g_free (archive_name);
+	}
+	g_free (temp_dir);
+
+	return local_copy;
+}
+
+
 static void
 fr_archive_set_uri (FrArchive  *archive,
 		    const char *uri)
 {
+	if ((archive->local_copy != NULL) && archive->is_remote) {
+		GFile  *temp_folder;
+		GError *err = NULL;
+
+		g_file_delete (archive->local_copy, NULL, &err);
+		if (err != NULL) {
+			g_warning ("Failed to delete the local copy: %s", err->message);
+			g_clear_error (&err);
+		}
+
+		temp_folder = g_file_get_parent (archive->local_copy);
+		g_file_delete (temp_folder, NULL, &err);
+		if (err != NULL) {
+			g_warning ("Failed to delete temp folder: %s", err->message);
+			g_clear_error (&err);
+		}
+
+		g_object_unref (temp_folder);
+	}
+
 	if (archive->file != NULL) {
 		g_object_unref (archive->file);
 		archive->file = NULL;
 	}
+	if (archive->local_copy != NULL) {
+		g_object_unref (archive->local_copy);
+		archive->local_copy = NULL;
+	}
 	archive->content_type = NULL;
 
-	if (uri != NULL)
-		archive->file = g_file_new_for_uri (uri);
+	if (uri == NULL)
+		return;
+
+	archive->file = g_file_new_for_uri (uri);
+	archive->is_remote = ! g_file_has_uri_scheme (archive->file, "file");
+	if (archive->is_remote)
+		archive->local_copy = get_local_copy_for_file (archive->file);
+	else
+		archive->local_copy = g_file_dup (archive->file);
 }
 
 
@@ -395,6 +488,7 @@ fr_archive_finalize (GObject *object)
 		dropped_items_data_free (archive->priv->dropped_items_data);
 		archive->priv->dropped_items_data = NULL;
 	}
+	g_free (archive->priv->temp_extraction_dir);
 	g_free (archive->priv->extraction_destination);
 	g_free (archive->priv);
 
@@ -501,7 +595,7 @@ create_command_from_type (FrArchive     *archive,
 	if (command_type == 0)
 		return FALSE;
 
-	filename = g_file_get_path (archive->file);
+	filename = g_file_get_path (archive->local_copy);
 	archive->command = FR_COMMAND (g_object_new (command_type,
 					             "process", archive->process,
 					             "filename", filename,
@@ -588,6 +682,79 @@ action_started (FrCommand *command,
 }
 
 
+/* -- copy_to_remote_location -- */
+
+
+static void
+fr_archive_copy_done (FrArchive *archive,
+		      FrAction   action,
+		      GError    *error)
+{
+	FrProcErrorType  error_type = FR_PROC_ERROR_NONE;
+	const char      *error_details = NULL;
+
+	if (error != NULL) {
+		error_type = (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC);
+		error_details = error->message;
+	}
+	fr_archive_action_completed (archive, action, error_type, error_details);
+}
+
+
+static void
+copy_to_remote_location_done (GError   *error,
+			      gpointer  user_data)
+{
+	XferData *xfer_data = user_data;
+
+	fr_archive_copy_done (xfer_data->archive, xfer_data->action, error);
+	xfer_data_free (xfer_data);
+}
+
+
+static void
+copy_to_remote_location_progress (goffset   current_file,
+                                  goffset   total_files,
+                                  GFile    *source,
+                                  GFile    *destination,
+                                  goffset   current_num_bytes,
+                                  goffset   total_num_bytes,
+                                  gpointer  user_data)
+{
+	XferData *xfer_data = user_data;
+
+	g_signal_emit (G_OBJECT (xfer_data->archive),
+		       fr_archive_signals[PROGRESS],
+		       0,
+		       (double) current_num_bytes / total_num_bytes);
+}
+
+
+static void
+copy_to_remote_location (FrArchive  *archive,
+			 FrAction    action)
+{
+	XferData *xfer_data;
+
+	xfer_data = g_new0 (XferData, 1);
+	xfer_data->archive = archive;
+	xfer_data->action = action;
+
+	g_copy_file_async (archive->local_copy,
+			   archive->file,
+			   G_FILE_COPY_OVERWRITE,
+			   G_PRIORITY_DEFAULT,
+			   archive->priv->cancellable,
+			   copy_to_remote_location_progress,
+			   xfer_data,
+			   copy_to_remote_location_done,
+			   xfer_data);
+}
+
+
+/* -- copy_extracted_files_to_destination -- */
+
+
 static void
 move_here (FrArchive *archive)
 {
@@ -656,6 +823,61 @@ move_here (FrArchive *archive)
 }
 
 
+static void
+copy_extracted_files_done (GError   *error,
+			   gpointer  user_data)
+{
+	FrArchive *archive = user_data;
+
+	remove_local_directory (archive->priv->temp_extraction_dir);
+	g_free (archive->priv->temp_extraction_dir);
+	archive->priv->temp_extraction_dir = NULL;
+
+	fr_archive_action_completed (archive,
+				     FR_ACTION_COPYING_FILES_TO_REMOTE,
+				     FR_PROC_ERROR_NONE,
+				     NULL);
+
+	if ((error == NULL) && (archive->priv->extract_here))
+		move_here (archive);
+
+	fr_archive_copy_done (archive, FR_ACTION_EXTRACTING_FILES, error);
+}
+
+
+static void
+copy_extracted_files_progress (goffset   current_file,
+                               goffset   total_files,
+                               GFile    *source,
+                               GFile    *destination,
+                               goffset   current_num_bytes,
+                               goffset   total_num_bytes,
+                               gpointer  user_data)
+{
+	FrArchive *archive = user_data;
+
+	g_signal_emit (G_OBJECT (archive),
+		       fr_archive_signals[PROGRESS],
+		       0,
+		       (double) current_file / (total_files + 1));
+}
+
+
+static void
+copy_extracted_files_to_destination (FrArchive *archive)
+{
+	g_directory_copy_async (archive->priv->temp_extraction_dir,
+				archive->priv->extraction_destination,
+				G_FILE_COPY_OVERWRITE,
+				G_PRIORITY_DEFAULT,
+				archive->priv->cancellable,
+				copy_extracted_files_progress,
+				archive,
+				copy_extracted_files_done,
+				archive);
+}
+
+
 static void add_dropped_items (DroppedItemsData *data);
 
 
@@ -672,6 +894,11 @@ fr_archive_change_name (FrArchive  *archive,
 	g_object_unref (archive->file);
 	archive->file = g_file_get_child (parent, name);
 	g_object_unref (parent);
+
+	parent = g_file_get_parent (archive->local_copy);
+	g_object_unref (archive->local_copy);
+	archive->local_copy = g_file_get_child (parent, name);
+	g_object_unref (parent);
 }
 
 
@@ -686,6 +913,15 @@ action_performed (FrCommand   *command,
 #endif
 
 	switch (action) {
+	case FR_ACTION_DELETING_FILES:
+		if (error->type == FR_PROC_ERROR_NONE) {
+			if (! g_file_has_uri_scheme (archive->file, "file")) {
+				copy_to_remote_location (archive, action);
+				return;
+			}
+		}
+		break;
+
 	case FR_ACTION_ADDING_FILES:
 		if (error->type == FR_PROC_ERROR_NONE) {
 			fr_archive_remove_temp_work_dir (archive);
@@ -701,15 +937,33 @@ action_performed (FrCommand   *command,
 			 * original name */
 			if (archive->command->multi_volume)
 				fr_archive_change_name (archive, archive->command->filename);
+			if (! g_file_has_uri_scheme (archive->file, "file")) {
+				copy_to_remote_location (archive, action);
+				return;
+			}
 		}
 		break;
 
 	case FR_ACTION_EXTRACTING_FILES:
 		if (error->type == FR_PROC_ERROR_NONE) {
-			if (archive->priv->extract_here)
+			if (archive->priv->remote_extraction) {
+				copy_extracted_files_to_destination (archive);
+				return;
+			}
+			else if (archive->priv->extract_here)
 				move_here (archive);
 		}
 		else {
+			/* if an error occurred during extraction remove the
+			 * temp extraction dir, if used. */
+			g_print ("action_performed: ERROR!\n");
+
+			if ((archive->priv->remote_extraction) && (archive->priv->temp_extraction_dir != NULL)) {
+				remove_local_directory (archive->priv->temp_extraction_dir);
+				g_free (archive->priv->temp_extraction_dir);
+				archive->priv->temp_extraction_dir = NULL;
+			}
+
 			if (archive->priv->extract_here)
 				remove_directory (archive->priv->extraction_destination);
 		}
@@ -817,7 +1071,7 @@ fr_archive_create (FrArchive  *archive,
 
 	tmp_command = archive->command;
 
-	mime_type = get_mime_type_from_filename (archive->file);
+	mime_type = get_mime_type_from_filename (archive->local_copy);
 	if (! create_command_to_create_archive (archive, mime_type)) {
 		archive->command = tmp_command;
 		return FALSE;
@@ -878,11 +1132,11 @@ load_local_archive (FrArchive  *archive,
 
 	tmp_command = archive->command;
 
-	mime_type = get_mime_type_from_filename (archive->file);
+	mime_type = get_mime_type_from_filename (archive->local_copy);
 	if (! create_command_to_load_archive (archive, mime_type)) {
-		mime_type = get_mime_type_from_content (archive->file);
+		mime_type = get_mime_type_from_content (archive->local_copy);
 		if (! create_command_to_load_archive (archive, mime_type)) {
-			mime_type = get_mime_type_from_magic_numbers (archive->file);
+			mime_type = get_mime_type_from_magic_numbers (archive->local_copy);
 			if (! create_command_to_load_archive (archive, mime_type)) {
 				archive->command = tmp_command;
 				archive->content_type = mime_type;
@@ -921,6 +1175,86 @@ load_local_archive (FrArchive  *archive,
 }
 
 
+static void
+copy_remote_file_done (GError   *error,
+		       gpointer  user_data)
+{
+	XferData *xfer_data = user_data;
+
+	if (error != NULL)
+		fr_archive_copy_done (xfer_data->archive, FR_ACTION_LOADING_ARCHIVE, error);
+	else
+		load_local_archive (xfer_data->archive, xfer_data->password);
+	xfer_data_free (xfer_data);
+}
+
+
+static void
+copy_remote_file_progress (goffset   current_file,
+                           goffset   total_files,
+                           GFile    *source,
+                           GFile    *destination,
+                           goffset   current_num_bytes,
+                           goffset   total_num_bytes,
+                           gpointer  user_data)
+{
+	XferData *xfer_data = user_data;
+
+	g_signal_emit (G_OBJECT (xfer_data->archive),
+		       fr_archive_signals[PROGRESS],
+		       0,
+		       (double) current_num_bytes / total_num_bytes);
+}
+
+
+static gboolean
+copy_remote_file_done_cb (gpointer user_data)
+{
+	XferData *xfer_data = user_data;
+
+	g_source_remove (xfer_data->source_id);
+	copy_remote_file_done (NULL, xfer_data);
+	return FALSE;
+}
+
+
+static void
+copy_remote_file (FrArchive  *archive,
+		  const char *password)
+{
+	XferData *xfer_data;
+
+	if (! g_file_query_exists (archive->file, NULL)) {
+		GError *error;
+		error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("The file doesn't exist"));
+		fr_archive_copy_done (archive, FR_ACTION_LOADING_ARCHIVE, error);
+		g_error_free (error);
+		return;
+	}
+
+	xfer_data = g_new0 (XferData, 1);
+	xfer_data->archive = archive;
+	xfer_data->uri = g_file_get_uri (archive->file);
+	if (password != NULL)
+		xfer_data->password = g_strdup (password);
+
+	if (! archive->is_remote) {
+		xfer_data->source_id = g_idle_add (copy_remote_file_done_cb, xfer_data);
+		return;
+	}
+
+	g_copy_file_async (archive->file,
+			   archive->local_copy,
+			   G_FILE_COPY_OVERWRITE,
+			   G_PRIORITY_DEFAULT,
+			   archive->priv->cancellable,
+			   copy_remote_file_progress,
+			   xfer_data,
+			   copy_remote_file_done,
+			   xfer_data);
+}
+
+
 gboolean
 fr_archive_load (FrArchive  *archive,
 		 const char *uri,
@@ -934,7 +1268,7 @@ fr_archive_load (FrArchive  *archive,
 		       FR_ACTION_LOADING_ARCHIVE);
 
 	fr_archive_set_uri (archive, uri);
-	load_local_archive (archive, password);
+	copy_remote_file (archive, password);
 
 	return TRUE;
 }
@@ -953,7 +1287,7 @@ fr_archive_load_local (FrArchive  *archive,
 		       FR_ACTION_LOADING_ARCHIVE);
 
 	fr_archive_set_uri (archive, uri);
-	load_local_archive (archive, password);
+	copy_remote_file (archive, password);
 
 	return TRUE;
 }
@@ -1120,18 +1454,12 @@ convert_to_local_file_list (GList *file_list)
 	GList *scan;
 
 	for (scan = file_list; scan; scan = scan->next) {
-		GFile *file;
-		char  *uri = scan->data;
-		char  *local_filename;
-
-		file = g_file_new_for_uri (uri);
-		local_filename = g_file_get_path (file);
-		if (local_filename == NULL)
-			local_filename = g_strdup (uri);
+		char *uri = scan->data;
+		char *local_filename;
+
+		local_filename = g_uri_unescape_string (uri, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH);
 		if (local_filename != NULL)
 			local_file_list = g_list_prepend (local_file_list, local_filename);
-
-		g_object_unref (file);
 	}
 
 	return local_file_list;
@@ -1446,6 +1774,145 @@ fr_archive_add_local_files (FrArchive     *archive,
 }
 
 
+static void
+copy_remote_files_done (GError   *error,
+			gpointer  user_data)
+{
+	XferData *xfer_data = user_data;
+
+	fr_archive_copy_done (xfer_data->archive, FR_ACTION_COPYING_FILES_FROM_REMOTE, error);
+
+	if (error == NULL)
+		fr_archive_add_local_files (xfer_data->archive,
+					    xfer_data->file_list,
+					    xfer_data->tmp_dir,
+					    xfer_data->dest_dir,
+					    FALSE,
+					    xfer_data->password,
+					    xfer_data->encrypt_header,
+					    xfer_data->compression,
+					    xfer_data->volume_size);
+	xfer_data_free (xfer_data);
+}
+
+
+static void
+copy_remote_files_progress (goffset   current_file,
+                            goffset   total_files,
+                            GFile    *source,
+                            GFile    *destination,
+                            goffset   current_num_bytes,
+                            goffset   total_num_bytes,
+                            gpointer  user_data)
+{
+	XferData *xfer_data = user_data;
+
+	g_signal_emit (G_OBJECT (xfer_data->archive),
+		       fr_archive_signals[PROGRESS],
+		       0,
+		       (double) current_file / (total_files + 1));
+}
+
+
+static void
+copy_remote_files (FrArchive     *archive,
+		   GList         *file_list,
+		   const char    *base_uri,
+		   const char    *dest_dir,
+		   gboolean       update,
+		   const char    *password,
+		   gboolean       encrypt_header,
+		   FrCompression  compression,
+		   guint          volume_size,
+		   const char    *tmp_dir)
+{
+	GList      *sources = NULL, *destinations = NULL;
+	GHashTable *created_folders;
+	GList      *scan;
+	XferData   *xfer_data;
+
+	created_folders = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+	for (scan = file_list; scan; scan = scan->next) {
+		char  *partial_filename = scan->data;
+		char  *local_uri;
+		char  *local_folder_uri;
+		char  *remote_uri;
+
+		local_uri = g_strconcat ("file://", tmp_dir, "/", partial_filename, NULL);
+		local_folder_uri = remove_level_from_path (local_uri);
+		if (g_hash_table_lookup (created_folders, local_folder_uri) == NULL) {
+			GError *error = NULL;
+			if (! ensure_dir_exists (local_folder_uri, 0755, &error)) {
+				g_free (local_folder_uri);
+				g_free (local_uri);
+				gio_file_list_free (sources);
+				gio_file_list_free (destinations);
+				g_hash_table_destroy (created_folders);
+
+				fr_archive_action_completed (archive,
+							     FR_ACTION_COPYING_FILES_FROM_REMOTE,
+							     FR_PROC_ERROR_GENERIC,
+							     error->message);
+				g_clear_error (&error);
+				return;
+			}
+
+			g_hash_table_insert (created_folders, local_folder_uri, GINT_TO_POINTER (1));
+		}
+		else
+			g_free (local_folder_uri);
+
+		remote_uri = g_strconcat (base_uri, "/", partial_filename, NULL);
+		sources = g_list_append (sources, g_file_new_for_uri (remote_uri));
+		g_free (remote_uri);
+
+		destinations = g_list_append (destinations, g_file_new_for_uri (local_uri));
+		g_free (local_uri);
+	}
+	g_hash_table_destroy (created_folders);
+
+	xfer_data = g_new0 (XferData, 1);
+	xfer_data->archive = archive;
+	xfer_data->file_list = path_list_dup (file_list);
+	xfer_data->base_uri = g_strdup (base_uri);
+	xfer_data->dest_dir = g_strdup (dest_dir);
+	xfer_data->update = update;
+	xfer_data->dest_dir = g_strdup (dest_dir);
+	xfer_data->password = g_strdup (password);
+	xfer_data->encrypt_header = encrypt_header;
+	xfer_data->compression = compression;
+	xfer_data->volume_size = volume_size;
+	xfer_data->tmp_dir = g_strdup (tmp_dir);
+
+	g_signal_emit (G_OBJECT (archive),
+		       fr_archive_signals[START],
+		       0,
+		       FR_ACTION_COPYING_FILES_FROM_REMOTE);
+
+	g_copy_files_async (sources,
+			    destinations,
+			    G_FILE_COPY_OVERWRITE,
+			    G_PRIORITY_DEFAULT,
+			    archive->priv->cancellable,
+			    copy_remote_files_progress,
+			    xfer_data,
+			    copy_remote_files_done,
+			    xfer_data);
+
+	gio_file_list_free (sources);
+	gio_file_list_free (destinations);
+}
+
+
+static char *
+fr_archive_get_temp_work_dir (FrArchive *archive)
+{
+	fr_archive_remove_temp_work_dir (archive);
+	archive->priv->temp_dir = get_temp_work_dir (NULL);
+	return archive->priv->temp_dir;
+}
+
+
 void
 fr_archive_add_files (FrArchive     *archive,
 		      GList         *file_list,
@@ -1457,23 +1924,30 @@ fr_archive_add_files (FrArchive     *archive,
 		      FrCompression  compression,
 		      guint          volume_size)
 {
-	GFile *file;
-	char  *local_dir;
-
-	file = g_file_new_for_uri (base_dir);
-	local_dir = g_file_get_path (file);
-	fr_archive_add_local_files (archive,
-				    file_list,
-				    local_dir,
-				    dest_dir,
-				    update,
-				    password,
-				    encrypt_header,
-				    compression,
-				    volume_size);
-
-	g_free (local_dir);
-	g_object_unref (file);
+	if (uri_is_local (base_dir)) {
+		char *local_dir = g_filename_from_uri (base_dir, NULL, NULL);
+		fr_archive_add_local_files (archive,
+					    file_list,
+					    local_dir,
+					    dest_dir,
+					    update,
+					    password,
+					    encrypt_header,
+					    compression,
+					    volume_size);
+		g_free (local_dir);
+	}
+	else
+		copy_remote_files (archive,
+				   file_list,
+				   base_dir,
+				   dest_dir,
+				   update,
+				   password,
+				   encrypt_header,
+				   compression,
+				   volume_size,
+				   fr_archive_get_temp_work_dir (archive));
 }
 
 
@@ -2675,25 +3149,38 @@ fr_archive_extract (FrArchive  *archive,
 		    gboolean    junk_paths,
 		    const char *password)
 {
-	GFile *file;
-	char  *local_destination;
-
 	g_free (archive->priv->extraction_destination);
 	archive->priv->extraction_destination = g_strdup (destination);
 
-	file = g_file_new_for_uri (destination);
-	local_destination = g_file_get_path (file);
-	fr_archive_extract_to_local (archive,
-				     file_list,
-				     local_destination,
-				     base_dir,
-				     skip_older,
-				     overwrite,
-				     junk_paths,
-				     password);
-
-	g_free (local_destination);
-	g_object_unref (file);
+	g_free (archive->priv->temp_extraction_dir);
+	archive->priv->temp_extraction_dir = NULL;
+
+	archive->priv->remote_extraction = ! uri_is_local (destination);
+	if (archive->priv->remote_extraction) {
+		archive->priv->temp_extraction_dir = get_temp_work_dir (NULL);
+		fr_archive_extract_to_local (archive,
+					     file_list,
+					     archive->priv->temp_extraction_dir,
+					     base_dir,
+					     skip_older,
+					     overwrite,
+					     junk_paths,
+					     password);
+	}
+	else {
+		char *local_destination;
+
+		local_destination = g_filename_from_uri (destination, NULL, NULL);
+		fr_archive_extract_to_local (archive,
+					     file_list,
+					     local_destination,
+					     base_dir,
+					     skip_older,
+					     overwrite,
+					     junk_paths,
+					     password);
+		g_free (local_destination);
+	}
 }
 
 
diff --git a/src/fr-archive.h b/src/fr-archive.h
index 2c485a2..530e49e 100644
--- a/src/fr-archive.h
+++ b/src/fr-archive.h
@@ -44,6 +44,8 @@ struct _FrArchive {
 	GObject  __parent;
 
 	GFile       *file;
+	GFile       *local_copy;
+	gboolean     is_remote;
 	const char  *content_type;
 	FrCommand   *command;
 	FrProcess   *process;
diff --git a/src/fr-window.c b/src/fr-window.c
index 8eeb833..d8fb08e 100644
--- a/src/fr-window.c
+++ b/src/fr-window.c
@@ -4226,14 +4226,14 @@ get_selection_data_from_clipboard_data (FrWindow        *window,
 		      			FrClipboardData *data)
 {
 	GString *list;
-	char    *archive_uri;
+	char    *local_filename;
 	GList   *scan;
 
 	list = g_string_new (NULL);
 
-	archive_uri = g_file_get_uri (window->archive->file);
-	g_string_append (list, archive_uri);
-	g_free (archive_uri);
+	local_filename = g_file_get_uri (window->archive->local_copy);
+	g_string_append (list, local_filename);
+	g_free (local_filename);
 
 	g_string_append (list, "\r\n");
 	if (window->priv->password != NULL)
diff --git a/src/gio-utils.c b/src/gio-utils.c
index f94e94f..7886b2e 100644
--- a/src/gio-utils.c
+++ b/src/gio-utils.c
@@ -127,7 +127,7 @@ filter_empty (Filter *filter)
 
 
 typedef struct {
-	char                 *base_directory;
+	GFile                *base_directory;
 	gboolean              recursive;
 	gboolean              follow_links;
 	StartDirCallback      start_dir_func;
@@ -153,7 +153,8 @@ for_each_child_data_free (ForEachChildData *fec)
 	if (fec == NULL)
 		return;
 
-	g_free (fec->base_directory);
+	if (fec->base_directory != NULL)
+		g_object_unref (fec->base_directory);
 	if (fec->current != NULL)
 		g_object_unref (fec->current);
 	if (fec->already_visited)
@@ -172,7 +173,7 @@ for_each_child_done_cb (gpointer user_data)
 	g_source_remove (fec->source_id);
 	if (fec->current != NULL) {
 		g_object_unref (fec->current);
-		 fec->current = NULL;
+		fec->current = NULL;
 	}
 	if (fec->done_func)
 		fec->done_func (fec->error, fec->user_data);
@@ -212,8 +213,8 @@ for_each_child_start (ForEachChildData *fec)
 
 
 static void
-for_each_child_set_current (ForEachChildData *fec,
-			    const char       *directory)
+for_each_child_set_current_uri (ForEachChildData *fec,
+				const char       *directory)
 {
 	if (fec->current != NULL)
 		g_object_unref (fec->current);
@@ -222,6 +223,15 @@ for_each_child_set_current (ForEachChildData *fec,
 
 
 static void
+for_each_child_set_current (ForEachChildData *fec,
+			    GFile            *directory)
+{
+	if (fec->current != NULL)
+		g_object_unref (fec->current);
+	fec->current = g_file_dup (directory);
+}
+
+static void
 for_each_child_start_next_sub_directory (ForEachChildData *fec)
 {
 	char *sub_directory = NULL;
@@ -236,7 +246,7 @@ for_each_child_start_next_sub_directory (ForEachChildData *fec)
 	}
 
 	if (sub_directory != NULL) {
-		for_each_child_set_current (fec, sub_directory);
+		for_each_child_set_current_uri (fec, sub_directory);
 		for_each_child_start (fec);
 	}
 	else
@@ -276,7 +286,6 @@ for_each_child_next_files_ready (GObject      *source_object,
 {
 	ForEachChildData *fec = user_data;
 	GList            *children, *scan;
-	char             *current_directory;
 
 	children = g_file_enumerator_next_files_finish (fec->enumerator,
                                                         result,
@@ -291,16 +300,16 @@ for_each_child_next_files_ready (GObject      *source_object,
 		return;
 	}
 
-	current_directory = g_file_get_uri (fec->current);
 	for (scan = children; scan; scan = scan->next) {
 		GFileInfo *child_info = scan->data;
-		char      *name, *uri;
+		GFile     *f;
+		char      *uri;
 
-		name = g_uri_escape_string (g_file_info_get_name (child_info), G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT, FALSE);
-		uri = g_strconcat (current_directory, "/", name, NULL);
+		f = g_file_get_child (fec->current, g_file_info_get_name (child_info));
+		uri = g_file_get_uri (f);
 
 		if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY) {
-			/* avoid to visit a directory more than ones */
+			/* avoid to visit a directory more than once */
 
 			if (g_hash_table_lookup (fec->already_visited, uri) == NULL) {
 				char *sub_directory;
@@ -314,9 +323,8 @@ for_each_child_next_files_ready (GObject      *source_object,
 		fec->for_each_file_func (uri, child_info, fec->user_data);
 
 		g_free (uri);
-		g_free (name);
+		g_object_unref (f);
 	}
-	g_free (current_directory);
 
 	g_file_enumerator_next_files_async (fec->enumerator,
                                             N_FILES_PER_REQUEST,
@@ -404,7 +412,7 @@ for_each_child_start_current (ForEachChildData *fec)
  * Each callback uses the same @user_data additional parameter.
  */
 void
-g_directory_foreach_child (const char           *directory,
+g_directory_foreach_child (GFile                *directory,
 			   gboolean              recursive,
 			   gboolean              follow_links,
 			   GCancellable         *cancellable,
@@ -419,7 +427,7 @@ g_directory_foreach_child (const char           *directory,
 
 	fec = g_new0 (ForEachChildData, 1);
 
-	fec->base_directory = g_strdup (directory);
+	fec->base_directory = g_object_ref (directory);
 	fec->recursive = recursive;
 	fec->follow_links = follow_links;
 	fec->cancellable = cancellable;
@@ -443,8 +451,8 @@ g_directory_foreach_child (const char           *directory,
 typedef struct {
 	GList             *files;
 	GList             *dirs;
-	char              *directory;
-	char              *base_dir;
+	GFile             *directory;
+	GFile             *base_dir;
 	GCancellable      *cancellable;
 	ListReadyCallback  done_func;
 	gpointer           done_data;
@@ -469,8 +477,10 @@ get_file_list_data_free (GetFileListData *gfl)
 	path_list_free (gfl->files);
 	path_list_free (gfl->dirs);
 	path_list_free (gfl->to_visit);
-	g_free (gfl->directory);
-	g_free (gfl->base_dir);
+	if (gfl->directory != NULL)
+		g_object_unref (gfl->directory);
+	if (gfl->base_dir != NULL)
+		g_object_unref (gfl->base_dir);
 	g_free (gfl);
 }
 
@@ -479,27 +489,25 @@ get_file_list_data_free (GetFileListData *gfl)
 
 
 static GList*
-get_relative_file_list (GList      *rel_list,
-			GList      *file_list,
-			const char *base_dir)
+get_relative_file_list (GList *rel_list,
+			GList *file_list,
+			GFile *base_dir)
 {
 	GList *scan;
-	int    base_len;
 
 	if (base_dir == NULL)
 		return NULL;
 
-	base_len = 0;
-	if (strcmp (base_dir, "/") != 0)
-		base_len = strlen (base_dir);
-
 	for (scan = file_list; scan; scan = scan->next) {
-		char *full_path = scan->data;
-
-		if (path_in_path (base_dir, full_path)) {
-			char *rel_path = g_uri_unescape_string (full_path + base_len + 1, NULL);
-			rel_list = g_list_prepend (rel_list, rel_path);
-		}
+		char  *full_path = scan->data;
+		GFile *f;
+		char  *relative_path;
+
+		f = g_file_new_for_commandline_arg (full_path);
+		relative_path = g_file_get_relative_path (base_dir, f);
+		if (relative_path != NULL)
+			rel_list = g_list_prepend (rel_list, relative_path);
+		g_object_unref (f);
 	}
 
 	return rel_list;
@@ -565,6 +573,7 @@ get_file_list_done (GError   *error,
 	GetFileListData *gfl = user_data;
 	GHashTable      *h_dirs;
 	GList           *scan;
+	char            *uri;
 
 	gfl->files = g_list_reverse (gfl->files);
 	gfl->dirs = g_list_reverse (gfl->dirs);
@@ -582,7 +591,7 @@ get_file_list_done (GError   *error,
 	if (gfl->base_dir != NULL) {
 		char *dir;
 
-		dir = g_strdup (gfl->base_dir);
+		dir = g_file_get_uri (gfl->base_dir);
 		gfl->dirs = g_list_prepend (gfl->dirs, dir);
 		g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1));
 	}
@@ -594,11 +603,13 @@ get_file_list_done (GError   *error,
 	for (scan = gfl->dirs; scan; scan = scan->next)
 		g_hash_table_insert (h_dirs, (char*)scan->data, GINT_TO_POINTER (1));
 
-	gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, gfl->base_dir, gfl->files, FALSE));
+	uri = g_file_get_uri (gfl->base_dir);
+	gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->files, FALSE));
 
 	if (filter_empty (gfl->include_filter))
-		gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, gfl->base_dir, gfl->dirs, TRUE));
+		gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->dirs, TRUE));
 
+	g_free (uri);
 	/**/
 
 	if (error == NULL) {
@@ -680,8 +691,8 @@ g_directory_list_async (const char        *directory,
 	FilterOptions    filter_options;
 
 	gfl = g_new0 (GetFileListData, 1);
-	gfl->directory = g_strdup (directory);
-	gfl->base_dir = g_strdup (base_dir);
+	gfl->directory = g_file_new_for_commandline_arg (directory);
+	gfl->base_dir = g_file_new_for_commandline_arg (base_dir);
 	gfl->done_func = done_func;
 	gfl->done_data = done_data;
 
@@ -696,7 +707,7 @@ g_directory_list_async (const char        *directory,
 	gfl->exclude_filter = filter_new (exclude_files, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT);
 	gfl->exclude_folders_filter = filter_new (exclude_folders, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT);
 
-	g_directory_foreach_child (directory,
+	g_directory_foreach_child (gfl->directory,
 				   recursive,
 				   follow_links,
 				   cancellable,
@@ -756,7 +767,9 @@ static void
 get_items_for_current_dir (GetFileListData *gfl)
 {
 	const char *directory_name;
+	GFile      *directory_file;
 	char       *directory_uri;
+	char       *base_dir_uri;
 
 	if (gfl->current_dir == NULL) {
 		if (gfl->done_func) {
@@ -770,19 +783,20 @@ get_items_for_current_dir (GetFileListData *gfl)
 	}
 
 	directory_name = file_name_from_path ((char*) gfl->current_dir->data);
-	if (strcmp (gfl->base_dir, "/") == 0)
-		directory_uri = g_strconcat (gfl->base_dir, directory_name, NULL);
-	else
-		directory_uri = g_strconcat (gfl->base_dir, "/", directory_name, NULL);
+	directory_file = g_file_get_child (gfl->base_dir, directory_name);
+	directory_uri = g_file_get_uri (directory_file);
+	base_dir_uri = g_file_get_uri (gfl->base_dir);
 
 	g_directory_list_all_async (directory_uri,
-			   	    gfl->base_dir,
+				    base_dir_uri,
 				    TRUE,
 				    gfl->cancellable,
-			   	    get_items_for_current_dir_done,
-			   	    gfl);
+				    get_items_for_current_dir_done,
+				    gfl);
 
 	g_free (directory_uri);
+	g_free (base_dir_uri);
+	g_object_unref (directory_file);
 }
 
 
@@ -800,7 +814,7 @@ g_list_items_async (GList             *items,
 	g_return_if_fail (base_dir != NULL);
 
 	gfl = g_new0 (GetFileListData, 1);
-	gfl->base_dir = g_strdup (base_dir);
+	gfl->base_dir = g_file_new_for_commandline_arg (base_dir);
 	gfl->cancellable = cancellable;
 	gfl->done_func = done_func;
 	gfl->done_data = done_data;
@@ -916,6 +930,19 @@ g_copy_files_ready_cb (GObject      *source_object,
 	GError        *error = NULL;
 
 	if (! g_file_copy_finish (source, result, &error)) {
+		/* source and target are directories, ignore the error */
+		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_MERGE))
+			g_clear_error (&error);
+		/* source is directory, create target directory */
+		if (g_error_matches (error, G_IO_ERROR,  G_IO_ERROR_WOULD_RECURSE)) {
+			g_clear_error (&error);
+			g_file_make_directory ((GFile*) cfd->destination->data,
+					       cfd->cancellable,
+					       &error);
+		}
+	}
+
+	if (error) {
 		if (cfd->callback)
 			cfd->callback (error, cfd->user_data);
 		g_clear_error (&error);
@@ -1123,8 +1150,8 @@ child_data_free (ChildData *child)
 
 
 typedef struct {
-	char                  *source;
-	char                  *destination;
+	GFile                 *source;
+	GFile                 *destination;
 	GFileCopyFlags         flags;
 	int                    io_priority;
 	GCancellable          *cancellable;
@@ -1149,8 +1176,10 @@ directory_copy_data_free (DirectoryCopyData *dcd)
 	if (dcd == NULL)
 		return;
 
-	g_free (dcd->source);
-	g_free (dcd->destination);
+	if (dcd->source != NULL)
+		g_object_unref (dcd->source);
+	if (dcd->destination != NULL)
+		g_object_unref (dcd->destination);
 	if (dcd->current_source != NULL) {
 		g_object_unref (dcd->current_source);
 		dcd->current_source = NULL;
@@ -1161,7 +1190,6 @@ directory_copy_data_free (DirectoryCopyData *dcd)
 	}
 	g_list_foreach (dcd->to_copy, (GFunc) child_data_free, NULL);
 	g_list_free (dcd->to_copy);
-	g_object_unref (dcd->cancellable);
 	g_free (dcd);
 }
 
@@ -1187,15 +1215,19 @@ static GFile *
 get_destination_for_uri (DirectoryCopyData *dcd,
 		         const char        *uri)
 {
-	char  *destination_uri;
+	GFile *f_uri;
 	GFile *destination_file;
+	char  *relative_path;
 
-	if (strlen (uri) <=  strlen (dcd->source))
-		return NULL;
+	f_uri = g_file_new_for_uri (uri);
+	relative_path = g_file_get_relative_path (dcd->source, f_uri);
+	if (relative_path != NULL)
+		destination_file = g_file_resolve_relative_path (dcd->destination, relative_path);
+	else
+		destination_file = g_file_dup (dcd->destination);
 
-	destination_uri = g_strconcat (dcd->destination, "/", uri + strlen (dcd->source) + 1, NULL);
-	destination_file = g_file_new_for_uri (destination_uri);
-	g_free (destination_uri);
+	g_free (relative_path);
+	g_object_unref (f_uri);
 
 	return destination_file;
 }
@@ -1417,9 +1449,10 @@ g_directory_copy_async (const char            *source,
 {
 	DirectoryCopyData *dcd;
 
+	/* Creating GFile objects here will save us lot of effort in path construction */
 	dcd = g_new0 (DirectoryCopyData, 1);
-	dcd->source = g_strdup (source);
-	dcd->destination = g_strdup (destination);
+	dcd->source = g_file_new_for_commandline_arg (source);
+	dcd->destination = g_file_new_for_commandline_arg (destination);
 	dcd->flags = flags;
 	dcd->io_priority = io_priority;
 	dcd->cancellable = cancellable;
diff --git a/src/gio-utils.h b/src/gio-utils.h
index 4cd0a49..7dfe306 100644
--- a/src/gio-utils.h
+++ b/src/gio-utils.h
@@ -58,7 +58,7 @@ typedef void (*CopyDoneCallback)     (GError      *error,
 
 /* asynchronous recursive list functions */
 
-void   g_directory_foreach_child     (const char            *directory,
+void   g_directory_foreach_child     (GFile                 *directory,
 				      gboolean               recursive,
 				      gboolean               follow_links,
 				      GCancellable          *cancellable,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]