1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* gnome-vfs-xfer.c - File transfers in the GNOME Virtual File System.
5 Copyright (C) 1999 Free Software Foundation
6 Copyright (C) 2000, 2001 Eazel, Inc.
8 The Gnome Library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
13 The Gnome Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public
19 License along with the Gnome Library; see the file COPYING.LIB. If not,
20 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
24 Ettore Perazzoli <ettore@comm2000.it>
25 Pavel Cisler <pavel@eazel.com>
28 /* FIXME bugzilla.eazel.com 1197:
29 Check that all the flags passed by address are set at least once by
30 functions that are expected to set them. */
31 /* FIXME bugzilla.eazel.com 1198:
32 There should be a concept of a `context' in the file methods that would
33 allow us to set a prefix URI for all the operations. This way we can
34 greatly optimize access to "strange" file systems. */
37 #include "gnome-vfs-xfer.h"
39 #include "gnome-vfs-cancellable-ops.h"
40 #include "gnome-vfs-directory.h"
41 #include "gnome-vfs-ops.h"
42 #include "gnome-vfs-utils.h"
43 #include "gnome-vfs-private-utils.h"
44 #include <glib/gstrfuncs.h>
45 #include <glib/gmessages.h>
49 /* Implementation of file transfers (`gnome_vfs_xfer*()' functions). */
51 static GnomeVFSResult remove_directory (GnomeVFSURI *uri,
53 GnomeVFSProgressCallbackState *progress,
54 GnomeVFSXferOptions xfer_options,
55 GnomeVFSXferErrorMode *error_mode,
60 /* size used for accounting for the expense of copying directories,
61 * doing a move operation, etc. in a progress callback
62 * (during a move the file size is used for a regular file).
64 DEFAULT_SIZE_OVERHEAD = 1024
67 /* in asynch mode the progress callback does a context switch every time
68 * it gets called. We'll only call it every now and then to not loose a
71 #define UPDATE_PERIOD ((gint64) (100 * 1000))
76 struct timeval time_of_day;
78 gettimeofday (&time_of_day, NULL);
79 return (gint64) time_of_day.tv_usec + ((gint64) time_of_day.tv_sec) * 1000000;
83 init_progress (GnomeVFSProgressCallbackState *progress_state,
84 GnomeVFSXferProgressInfo *progress_info)
86 progress_info->source_name = NULL;
87 progress_info->target_name = NULL;
88 progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
89 progress_info->vfs_status = GNOME_VFS_OK;
90 progress_info->phase = GNOME_VFS_XFER_PHASE_INITIAL;
91 progress_info->file_index = 0;
92 progress_info->files_total = 0;
93 progress_info->bytes_total = 0;
94 progress_info->file_size = 0;
95 progress_info->bytes_copied = 0;
96 progress_info->total_bytes_copied = 0;
97 progress_info->duplicate_name = NULL;
98 progress_info->top_level_item = FALSE;
100 progress_state->progress_info = progress_info;
101 progress_state->sync_callback = NULL;
102 progress_state->update_callback = NULL;
103 progress_state->async_job_data = NULL;
104 progress_state->next_update_callback_time = 0;
105 progress_state->next_text_update_callback_time = 0;
106 progress_state->update_callback_period = UPDATE_PERIOD;
110 free_progress (GnomeVFSXferProgressInfo *progress_info)
112 g_free (progress_info->source_name);
113 progress_info->source_name = NULL;
114 g_free (progress_info->target_name);
115 progress_info->target_name = NULL;
119 progress_set_source_target_uris (GnomeVFSProgressCallbackState *progress,
120 const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri)
122 g_free (progress->progress_info->source_name);
123 progress->progress_info->source_name = source_uri ? gnome_vfs_uri_to_string (source_uri,
124 GNOME_VFS_URI_HIDE_NONE) : NULL;
125 g_free (progress->progress_info->target_name);
126 progress->progress_info->target_name = dest_uri ? gnome_vfs_uri_to_string (dest_uri,
127 GNOME_VFS_URI_HIDE_NONE) : NULL;
132 progress_set_source_target (GnomeVFSProgressCallbackState *progress,
133 const char *source, const char *dest)
135 g_free (progress->progress_info->source_name);
136 progress->progress_info->source_name = source ? g_strdup (source) : NULL;
137 g_free (progress->progress_info->target_name);
138 progress->progress_info->target_name = dest ? g_strdup (dest) : NULL;
143 call_progress (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase)
147 /* FIXME bugzilla.eazel.com 1199: should use proper progress result values from an enum here */
150 progress_set_source_target_uris (progress, NULL, NULL);
152 progress->next_update_callback_time = system_time () + progress->update_callback_period;
154 progress->progress_info->phase = phase;
156 if (progress->sync_callback != NULL)
157 result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
159 if (progress->update_callback != NULL) {
160 result = (* progress->update_callback) (progress->progress_info,
161 progress->async_job_data);
167 static GnomeVFSXferErrorAction
168 call_progress_with_current_names (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase)
172 result = GNOME_VFS_XFER_ERROR_ACTION_ABORT;
174 progress->next_update_callback_time = system_time () + progress->update_callback_period;
175 progress->next_update_callback_time = progress->next_text_update_callback_time;
177 progress->progress_info->phase = phase;
179 if (progress->sync_callback != NULL) {
180 result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
183 if (progress->update_callback != NULL) {
184 result = (* progress->update_callback) (progress->progress_info,
185 progress->async_job_data);
192 call_progress_uri (GnomeVFSProgressCallbackState *progress,
193 const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri,
194 GnomeVFSXferPhase phase)
199 progress_set_source_target_uris (progress, source_uri, dest_uri);
201 progress->next_text_update_callback_time = system_time () + progress->update_callback_period;
202 progress->next_update_callback_time = progress->next_text_update_callback_time;
204 progress->progress_info->phase = phase;
206 if (progress->sync_callback != NULL) {
207 result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
210 if (progress->update_callback != NULL) {
211 result = (* progress->update_callback) (progress->progress_info,
212 progress->async_job_data);
219 at_end (GnomeVFSProgressCallbackState *progress)
221 return progress->progress_info->total_bytes_copied
222 >= progress->progress_info->bytes_total;
226 call_progress_often_internal (GnomeVFSProgressCallbackState *progress,
227 GnomeVFSXferPhase phase,
234 now = system_time ();
236 progress->progress_info->phase = phase;
238 if (progress->sync_callback != NULL) {
239 result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
242 if (now < *next_time && !at_end (progress)) {
246 *next_time = now + progress->update_callback_period;
247 if (progress->update_callback != NULL) {
248 result = (* progress->update_callback) (progress->progress_info, progress->async_job_data);
255 call_progress_often (GnomeVFSProgressCallbackState *progress,
256 GnomeVFSXferPhase phase)
258 return call_progress_often_internal
259 (progress, phase, &progress->next_update_callback_time);
263 call_progress_with_uris_often (GnomeVFSProgressCallbackState *progress,
264 const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri,
265 GnomeVFSXferPhase phase)
267 progress_set_source_target_uris (progress, source_uri, dest_uri);
268 return call_progress_often_internal
269 (progress, phase, &progress->next_text_update_callback_time);
272 /* Handle an error condition according to `error_mode'. Returns `TRUE' if the
273 * operation should be retried, `FALSE' otherwise. `*result' will be set to
274 * the result value we want to be returned to the caller of the xfer
278 handle_error (GnomeVFSResult *result,
279 GnomeVFSProgressCallbackState *progress,
280 GnomeVFSXferErrorMode *error_mode,
283 GnomeVFSXferErrorAction action;
287 switch (*error_mode) {
288 case GNOME_VFS_XFER_ERROR_MODE_ABORT:
291 case GNOME_VFS_XFER_ERROR_MODE_QUERY:
292 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR;
293 progress->progress_info->vfs_status = *result;
294 action = call_progress_with_current_names (progress, progress->progress_info->phase);
295 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
298 case GNOME_VFS_XFER_ERROR_ACTION_RETRY:
300 case GNOME_VFS_XFER_ERROR_ACTION_ABORT:
301 *result = GNOME_VFS_ERROR_INTERRUPTED;
303 case GNOME_VFS_XFER_ERROR_ACTION_SKIP:
304 *result = GNOME_VFS_OK;
315 /* This is conceptually similiar to the previous `handle_error()' function, but
316 * handles the overwrite case.
319 handle_overwrite (GnomeVFSResult *result,
320 GnomeVFSProgressCallbackState *progress,
321 GnomeVFSXferErrorMode *error_mode,
322 GnomeVFSXferOverwriteMode *overwrite_mode,
326 GnomeVFSXferOverwriteAction action;
328 switch (*overwrite_mode) {
329 case GNOME_VFS_XFER_OVERWRITE_MODE_ABORT:
331 *result = GNOME_VFS_ERROR_FILE_EXISTS;
334 case GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE:
338 case GNOME_VFS_XFER_OVERWRITE_MODE_SKIP:
342 case GNOME_VFS_XFER_OVERWRITE_MODE_QUERY:
343 progress->progress_info->vfs_status = *result;
344 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE;
345 action = call_progress_with_current_names (progress, progress->progress_info->phase);
346 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
349 case GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT:
351 *result = GNOME_VFS_ERROR_FILE_EXISTS;
354 case GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE:
358 case GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE_ALL:
360 *overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
363 case GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP:
367 case GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP_ALL:
370 *overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_SKIP;
380 static GnomeVFSResult
381 remove_file (GnomeVFSURI *uri,
382 GnomeVFSProgressCallbackState *progress,
383 GnomeVFSXferOptions xfer_options,
384 GnomeVFSXferErrorMode *error_mode,
387 GnomeVFSResult result;
390 progress->progress_info->file_index++;
393 result = gnome_vfs_unlink_from_uri (uri);
394 if (result != GNOME_VFS_OK) {
395 retry = handle_error (&result, progress,
398 /* add some small size for each deleted item because delete overhead
399 * does not depend on file/directory size
401 progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
403 if (call_progress_with_uris_often (progress, uri, NULL,
404 GNOME_VFS_XFER_PHASE_DELETESOURCE)
405 == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
406 result = GNOME_VFS_ERROR_INTERRUPTED;
417 static GnomeVFSResult
418 empty_directory (GnomeVFSURI *uri,
419 GnomeVFSProgressCallbackState *progress,
420 GnomeVFSXferOptions xfer_options,
421 GnomeVFSXferErrorMode *error_mode,
424 GnomeVFSResult result;
425 GnomeVFSDirectoryHandle *directory_handle;
431 result = gnome_vfs_directory_open_from_uri (&directory_handle, uri,
432 GNOME_VFS_FILE_INFO_DEFAULT);
434 if (result != GNOME_VFS_OK) {
435 retry = handle_error (&result, progress,
440 if (result != GNOME_VFS_OK || *skip) {
445 GnomeVFSFileInfo *info;
446 GnomeVFSURI *item_uri;
449 info = gnome_vfs_file_info_new ();
450 result = gnome_vfs_directory_read_next (directory_handle, info);
451 if (result != GNOME_VFS_OK) {
452 gnome_vfs_file_info_unref (info);
456 /* Skip "." and "..". */
457 if (strcmp (info->name, ".") != 0 && strcmp (info->name, "..") != 0) {
459 item_uri = gnome_vfs_uri_append_file_name (uri, info->name);
461 progress_set_source_target_uris (progress, item_uri, NULL);
463 if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
464 result = remove_directory (item_uri, TRUE,
465 progress, xfer_options, error_mode,
468 result = remove_file (item_uri, progress,
476 gnome_vfs_file_info_unref (info);
478 if (item_uri != NULL) {
479 gnome_vfs_uri_unref (item_uri);
482 if (result != GNOME_VFS_OK) {
486 gnome_vfs_directory_close (directory_handle);
488 if (result == GNOME_VFS_ERROR_EOF) {
489 result = GNOME_VFS_OK;
496 const GnomeVFSURI *base_uri;
498 } PrependOneURIParams;
501 PrependOneURIToList (const gchar *rel_path, GnomeVFSFileInfo *info,
502 gboolean recursing_will_loop, gpointer cast_to_params, gboolean *recurse)
504 PrependOneURIParams *params;
506 params = (PrependOneURIParams *)cast_to_params;
507 params->uri_list = g_list_prepend (params->uri_list, gnome_vfs_uri_append_string (
508 params->base_uri, rel_path));
510 if (recursing_will_loop) {
517 static GnomeVFSResult
518 non_recursive_empty_directory (GnomeVFSURI *directory_uri,
519 GnomeVFSProgressCallbackState *progress,
520 GnomeVFSXferOptions xfer_options,
521 GnomeVFSXferErrorMode *error_mode,
524 /* Used as a fallback when we run out of file descriptors during
525 * a deep recursive deletion.
526 * We instead compile a flat list of URIs, doing a recursion that does not
527 * keep directories open.
533 GnomeVFSResult result;
534 GnomeVFSFileInfo *info;
535 PrependOneURIParams visit_params;
537 /* Build up the list so that deep items appear before their parents
538 * so that we can delete directories, children first.
540 visit_params.base_uri = directory_uri;
541 visit_params.uri_list = NULL;
542 result = gnome_vfs_directory_visit_uri (directory_uri, GNOME_VFS_FILE_INFO_DEFAULT,
543 GNOME_VFS_DIRECTORY_VISIT_SAMEFS | GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK,
544 PrependOneURIToList, &visit_params);
546 uri_list = visit_params.uri_list;
548 if (result == GNOME_VFS_OK) {
549 for (p = uri_list; p != NULL; p = p->next) {
551 uri = (GnomeVFSURI *)p->data;
552 info = gnome_vfs_file_info_new ();
553 result = gnome_vfs_get_file_info_uri (uri, info, GNOME_VFS_FILE_INFO_DEFAULT);
554 progress_set_source_target_uris (progress, uri, NULL);
555 if (result == GNOME_VFS_OK) {
556 if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
557 result = remove_directory (uri, FALSE,
558 progress, xfer_options, error_mode, skip);
560 result = remove_file (uri, progress,
561 xfer_options, error_mode, skip);
564 gnome_vfs_file_info_unref (info);
568 gnome_vfs_uri_list_free (uri_list);
573 static GnomeVFSResult
574 remove_directory (GnomeVFSURI *uri,
576 GnomeVFSProgressCallbackState *progress,
577 GnomeVFSXferOptions xfer_options,
578 GnomeVFSXferErrorMode *error_mode,
581 GnomeVFSResult result;
584 result = GNOME_VFS_OK;
587 result = empty_directory (uri, progress, xfer_options, error_mode, skip);
588 if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) {
589 result = non_recursive_empty_directory (uri, progress, xfer_options,
594 if (result == GNOME_VFS_ERROR_EOF) {
595 result = GNOME_VFS_OK;
598 if (result == GNOME_VFS_OK) {
599 progress->progress_info->file_index++;
604 result = gnome_vfs_remove_directory_from_uri (uri);
605 if (result != GNOME_VFS_OK) {
606 retry = handle_error (&result, progress,
609 /* add some small size for each deleted item */
610 progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
612 if (call_progress_with_uris_often (progress, uri, NULL,
613 GNOME_VFS_XFER_PHASE_DELETESOURCE)
614 == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
615 result = GNOME_VFS_ERROR_INTERRUPTED;
626 /* iterates the list of names in a given directory, applies @callback on each,
627 * optionally recurses into directories
629 static GnomeVFSResult
630 gnome_vfs_visit_list (const GList *name_uri_list,
631 GnomeVFSFileInfoOptions info_options,
632 GnomeVFSDirectoryVisitOptions visit_options,
634 GnomeVFSDirectoryVisitFunc callback,
637 GnomeVFSResult result;
640 GnomeVFSFileInfo *info;
641 gboolean tmp_recurse;
643 result = GNOME_VFS_OK;
645 /* go through our list of items */
646 for (p = name_uri_list; p != NULL; p = p->next) {
648 /* get the URI and VFSFileInfo for each */
649 uri = (GnomeVFSURI *)p->data;
650 info = gnome_vfs_file_info_new ();
651 result = gnome_vfs_get_file_info_uri (uri, info, info_options);
653 if (result == GNOME_VFS_OK) {
656 /* call our callback on each item*/
657 if (!callback (gnome_vfs_uri_get_path (uri), info, FALSE, data, &tmp_recurse)) {
658 result = GNOME_VFS_ERROR_INTERRUPTED;
661 if (result == GNOME_VFS_OK
663 && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
664 /* let gnome_vfs_directory_visit_uri call our callback
667 result = gnome_vfs_directory_visit_uri (uri, info_options,
668 visit_options, callback, data);
671 gnome_vfs_file_info_unref (info);
673 if (result != GNOME_VFS_OK) {
681 GnomeVFSProgressCallbackState *progress;
682 GnomeVFSResult result;
683 } CountEachFileSizeParams;
685 /* iteratee for count_items_and_size */
687 count_each_file_size_one (const gchar *rel_path,
688 GnomeVFSFileInfo *info,
689 gboolean recursing_will_loop,
693 CountEachFileSizeParams *params;
695 params = (CountEachFileSizeParams *)data;
697 if (call_progress_often (params->progress, params->progress->progress_info->phase) == 0) {
698 /* progress callback requested to stop */
699 params->result = GNOME_VFS_ERROR_INTERRUPTED;
704 /* keep track of the item we are counting so we can correctly report errors */
705 progress_set_source_target (params->progress, rel_path, NULL);
707 /* count each file, folder, symlink */
708 params->progress->progress_info->files_total++;
709 if (info->type == GNOME_VFS_FILE_TYPE_REGULAR) {
710 /* add each file size */
711 params->progress->progress_info->bytes_total += info->size + DEFAULT_SIZE_OVERHEAD;
712 } else if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
713 /* add some small size for each directory */
714 params->progress->progress_info->bytes_total += DEFAULT_SIZE_OVERHEAD;
717 /* watch out for infinite recursion*/
718 if (recursing_will_loop) {
719 params->result = GNOME_VFS_ERROR_LOOP;
728 /* calculate the number of items and their total size; used as a preflight
729 * before the transfer operation starts
731 static GnomeVFSResult
732 count_items_and_size (const GList *name_uri_list,
733 GnomeVFSXferOptions xfer_options,
734 GnomeVFSProgressCallbackState *progress,
739 * FIXME bugzilla.eazel.com 1200:
740 * Deal with errors here, respond to skip by pulling items from the name list
742 GnomeVFSFileInfoOptions info_options;
743 GnomeVFSDirectoryVisitOptions visit_options;
744 CountEachFileSizeParams each_params;
746 /* initialize the results */
747 progress->progress_info->files_total = 0;
748 progress->progress_info->bytes_total = 0;
750 /* set up the params for recursion */
751 visit_options = GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK;
752 if (xfer_options & GNOME_VFS_XFER_SAMEFS)
753 visit_options |= GNOME_VFS_DIRECTORY_VISIT_SAMEFS;
754 each_params.progress = progress;
755 each_params.result = GNOME_VFS_OK;
757 if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS) {
758 info_options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS;
760 info_options = GNOME_VFS_FILE_INFO_DEFAULT;
763 return gnome_vfs_visit_list (name_uri_list, info_options,
764 visit_options, !link && !move && (xfer_options & GNOME_VFS_XFER_RECURSIVE) != 0,
765 count_each_file_size_one, &each_params);
768 /* calculate the number of items and their total size; used as a preflight
769 * before the transfer operation starts
771 static GnomeVFSResult
772 directory_add_items_and_size (GnomeVFSURI *dir_uri,
773 GnomeVFSXferOptions xfer_options,
774 GnomeVFSProgressCallbackState *progress)
776 GnomeVFSFileInfoOptions info_options;
777 GnomeVFSDirectoryVisitOptions visit_options;
778 CountEachFileSizeParams each_params;
780 /* set up the params for recursion */
781 visit_options = GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK;
782 if (xfer_options & GNOME_VFS_XFER_SAMEFS)
783 visit_options |= GNOME_VFS_DIRECTORY_VISIT_SAMEFS;
784 each_params.progress = progress;
785 each_params.result = GNOME_VFS_OK;
787 if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS) {
788 info_options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS;
790 info_options = GNOME_VFS_FILE_INFO_DEFAULT;
793 return gnome_vfs_directory_visit_uri (dir_uri, info_options,
794 visit_options, count_each_file_size_one, &each_params);
798 /* Checks to see if any part of the source pathname of a move
799 * is inside the target. If it is we can't remove the target
800 * and then move the source to it since we would then remove
801 * the source before we moved it.
804 move_source_is_in_target (GnomeVFSURI *source, GnomeVFSURI *target)
806 GnomeVFSURI *parent, *tmp;
809 parent = gnome_vfs_uri_ref (source);
812 while (parent != NULL) {
813 if (_gnome_vfs_uri_is_in_subdir (parent, target)) {
817 tmp = gnome_vfs_uri_get_parent (parent);
818 gnome_vfs_uri_unref (parent);
821 gnome_vfs_uri_unref (parent);
826 /* Compares the list of files about to be created by a transfer with
827 * any possible existing files with conflicting names in the target directory.
828 * Handles conflicts, optionaly removing the conflicting file/directory
830 static GnomeVFSResult
831 handle_name_conflicts (GList **source_uri_list,
832 GList **target_uri_list,
833 GnomeVFSXferOptions xfer_options,
834 GnomeVFSXferErrorMode *error_mode,
835 GnomeVFSXferOverwriteMode *overwrite_mode,
836 GnomeVFSProgressCallbackState *progress)
838 GnomeVFSResult result;
842 int conflict_count; /* values are 0, 1, many */
844 result = GNOME_VFS_OK;
847 /* Go through the list of names, find out if there is 0, 1 or more conflicts. */
848 for (target_item = *target_uri_list; target_item != NULL;
849 target_item = target_item->next) {
850 if (gnome_vfs_uri_exists ((GnomeVFSURI *)target_item->data)) {
852 if (conflict_count > 1) {
858 if (conflict_count == 0) {
859 /* No conflicts, we are done. */
863 /* Pass in the conflict count so that we can decide to present the Replace All
864 * for multiple conflicts.
866 progress->progress_info->duplicate_count = conflict_count;
869 /* Go through the list of names again, present overwrite alerts for each. */
870 for (target_item = *target_uri_list, source_item = *source_uri_list;
871 target_item != NULL;) {
874 gboolean is_move_to_self;
875 GnomeVFSURI *uri, *source_uri;
876 GnomeVFSFileInfo *info;
879 source_uri = (GnomeVFSURI *)source_item->data;
880 uri = (GnomeVFSURI *)target_item->data;
881 is_move_to_self = (xfer_options & GNOME_VFS_XFER_REMOVESOURCE) != 0
882 && gnome_vfs_uri_equal (source_uri, uri);
883 if (!is_move_to_self && gnome_vfs_uri_exists (uri)) {
884 progress_set_source_target_uris (progress, source_uri, uri);
886 /* no error getting info -- file exists, ask what to do */
887 replace = handle_overwrite (&result, progress, error_mode,
888 overwrite_mode, &replace, &skip);
890 /* FIXME bugzilla.eazel.com 1207:
891 * move items to Trash here
894 /* get rid of the conflicting file */
896 info = gnome_vfs_file_info_new ();
897 gnome_vfs_get_file_info_uri (uri, info, GNOME_VFS_FILE_INFO_DEFAULT);
898 progress_set_source_target_uris (progress, uri, NULL);
899 if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
900 if (move_source_is_in_target (source_uri, uri)) {
901 /* Would like a better error here */
902 result = GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY;
904 remove_directory (uri, TRUE, progress,
905 xfer_options, error_mode, &skip);
908 remove_file (uri, progress,
909 xfer_options, error_mode, &skip);
911 gnome_vfs_file_info_unref (info);
916 if (result != GNOME_VFS_OK) {
921 /* skipping a file, remove it's name from the source and target name
924 GList *source_item_to_remove;
925 GList *target_item_to_remove;
927 source_item_to_remove = source_item;
928 target_item_to_remove = target_item;
930 source_item = source_item->next;
931 target_item = target_item->next;
933 gnome_vfs_uri_unref ((GnomeVFSURI *)source_item_to_remove->data);
934 gnome_vfs_uri_unref ((GnomeVFSURI *)target_item_to_remove->data);
935 *source_uri_list = g_list_remove_link (*source_uri_list, source_item_to_remove);
936 *target_uri_list = g_list_remove_link (*target_uri_list, target_item_to_remove);
942 target_item = target_item->next;
943 source_item = source_item->next;
949 /* Create new directory. If GNOME_VFS_XFER_USE_UNIQUE_NAMES is set,
950 * return with an error if a name conflict occurs, else
951 * handle the overwrite.
952 * On success, opens the new directory
954 static GnomeVFSResult
955 create_directory (GnomeVFSURI *dir_uri,
956 GnomeVFSDirectoryHandle **return_handle,
957 GnomeVFSXferOptions xfer_options,
958 GnomeVFSXferErrorMode *error_mode,
959 GnomeVFSXferOverwriteMode *overwrite_mode,
960 GnomeVFSProgressCallbackState *progress,
963 GnomeVFSResult result;
970 result = gnome_vfs_make_directory_for_uri (dir_uri, 0777);
972 if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
973 gboolean force_replace;
975 if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) != 0) {
976 /* just let the caller pass a unique name*/
980 retry = handle_overwrite (&result,
991 result = remove_directory (dir_uri, TRUE, progress,
992 xfer_options, error_mode,
995 result = GNOME_VFS_OK;
999 if (result == GNOME_VFS_OK) {
1000 return gnome_vfs_directory_open_from_uri (return_handle,
1002 GNOME_VFS_FILE_INFO_DEFAULT);
1004 /* handle the error case */
1005 retry = handle_error (&result, progress,
1009 return GNOME_VFS_OK;
1017 /* Copy the data of a single file. */
1018 static GnomeVFSResult
1019 copy_file_data (GnomeVFSHandle *target_handle,
1020 GnomeVFSHandle *source_handle,
1021 GnomeVFSProgressCallbackState *progress,
1022 GnomeVFSXferOptions xfer_options,
1023 GnomeVFSXferErrorMode *error_mode,
1027 GnomeVFSResult result;
1029 const char *write_buffer;
1030 GnomeVFSFileSize total_bytes_read;
1034 if (call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING) == 0) {
1035 return GNOME_VFS_ERROR_INTERRUPTED;
1038 buffer = g_malloc (block_size);
1039 total_bytes_read = 0;
1042 GnomeVFSFileSize bytes_read;
1043 GnomeVFSFileSize bytes_to_write;
1044 GnomeVFSFileSize bytes_written;
1047 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1048 progress->progress_info->vfs_status = GNOME_VFS_OK;
1050 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_READSOURCE;
1055 result = gnome_vfs_read (source_handle, buffer,
1056 block_size, &bytes_read);
1057 if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF)
1058 retry = handle_error (&result, progress,
1062 if (result != GNOME_VFS_OK || bytes_read == 0 || *skip) {
1066 total_bytes_read += total_bytes_read;
1067 bytes_to_write = bytes_read;
1069 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_WRITETARGET;
1071 write_buffer = buffer;
1075 result = gnome_vfs_write (target_handle, write_buffer,
1079 if (result != GNOME_VFS_OK) {
1080 retry = handle_error (&result, progress, error_mode, skip);
1083 bytes_to_write -= bytes_written;
1084 write_buffer += bytes_written;
1085 } while ((result == GNOME_VFS_OK && bytes_to_write > 0) || retry);
1087 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COPYING;
1089 progress->progress_info->bytes_copied += bytes_read;
1090 progress->progress_info->total_bytes_copied += bytes_read;
1092 if (call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING) == 0) {
1094 return GNOME_VFS_ERROR_INTERRUPTED;
1101 } while (result == GNOME_VFS_OK);
1103 if (result == GNOME_VFS_ERROR_EOF) {
1104 result = GNOME_VFS_OK;
1107 if (result == GNOME_VFS_OK) {
1108 /* tiny (0 - sized) files still present non-zero overhead during a copy, make sure
1109 * we count at least a default amount for each file
1111 progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
1113 call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING);
1121 static GnomeVFSResult
1122 xfer_open_source (GnomeVFSHandle **source_handle,
1123 GnomeVFSURI *source_uri,
1124 GnomeVFSProgressCallbackState *progress,
1125 GnomeVFSXferOptions xfer_options,
1126 GnomeVFSXferErrorMode *error_mode,
1129 GnomeVFSResult result;
1136 result = gnome_vfs_open_uri (source_handle, source_uri,
1137 GNOME_VFS_OPEN_READ);
1139 if (result != GNOME_VFS_OK) {
1140 retry = handle_error (&result, progress, error_mode, skip);
1147 static GnomeVFSResult
1148 xfer_create_target (GnomeVFSHandle **target_handle,
1149 GnomeVFSURI *target_uri,
1150 GnomeVFSProgressCallbackState *progress,
1151 GnomeVFSXferOptions xfer_options,
1152 GnomeVFSXferErrorMode *error_mode,
1153 GnomeVFSXferOverwriteMode *overwrite_mode,
1156 GnomeVFSResult result;
1160 exclusive = (*overwrite_mode != GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE);
1166 result = gnome_vfs_create_uri (target_handle, target_uri,
1167 GNOME_VFS_OPEN_WRITE,
1170 if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
1173 retry = handle_overwrite (&result,
1184 } else if (result != GNOME_VFS_OK) {
1185 retry = handle_error (&result, progress, error_mode, skip);
1192 static GnomeVFSResult
1193 copy_symlink (GnomeVFSURI *source_uri,
1194 GnomeVFSURI *target_uri,
1195 const char *link_name,
1196 GnomeVFSXferErrorMode *error_mode,
1197 GnomeVFSProgressCallbackState *progress,
1200 GnomeVFSResult result;
1207 result = gnome_vfs_create_symbolic_link (target_uri, link_name);
1209 if (result != GNOME_VFS_OK) {
1210 retry = handle_error (&result, progress, error_mode, skip);
1211 } else if (result == GNOME_VFS_OK &&
1212 call_progress_with_uris_often (progress, source_uri,
1213 target_uri, GNOME_VFS_XFER_PHASE_OPENTARGET) == 0) {
1214 result = GNOME_VFS_ERROR_INTERRUPTED;
1219 return GNOME_VFS_OK;
1225 static GnomeVFSResult
1226 copy_file (GnomeVFSFileInfo *info,
1227 GnomeVFSURI *source_uri,
1228 GnomeVFSURI *target_uri,
1229 GnomeVFSXferOptions xfer_options,
1230 GnomeVFSXferErrorMode *error_mode,
1231 GnomeVFSXferOverwriteMode *overwrite_mode,
1232 GnomeVFSProgressCallbackState *progress,
1235 GnomeVFSResult close_result, result;
1236 GnomeVFSHandle *source_handle, *target_handle;
1237 GnomeVFSSetFileInfoMask set_mask;
1239 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_OPENSOURCE;
1240 progress->progress_info->bytes_copied = 0;
1241 result = xfer_open_source (&source_handle, source_uri,
1242 progress, xfer_options,
1245 return GNOME_VFS_OK;
1248 if (result != GNOME_VFS_OK) {
1252 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_OPENTARGET;
1253 result = xfer_create_target (&target_handle, target_uri,
1254 progress, xfer_options,
1255 error_mode, overwrite_mode,
1260 gnome_vfs_close (source_handle);
1261 return GNOME_VFS_OK;
1263 if (result != GNOME_VFS_OK) {
1264 gnome_vfs_close (source_handle);
1268 if (call_progress_with_uris_often (progress, source_uri, target_uri,
1269 GNOME_VFS_XFER_PHASE_OPENTARGET) != GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1271 result = copy_file_data (target_handle, source_handle,
1272 progress, xfer_options, error_mode,
1273 /* use an arbitrary default block size of 4096
1274 * if one isn't available for this file system
1276 (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE && info->io_block_size > 0)
1277 ? info->io_block_size : 4096,
1281 if (result == GNOME_VFS_OK
1282 && call_progress_often (progress, GNOME_VFS_XFER_PHASE_CLOSETARGET) == 0) {
1283 result = GNOME_VFS_ERROR_INTERRUPTED;
1286 close_result = gnome_vfs_close (source_handle);
1287 if (result == GNOME_VFS_OK && close_result != GNOME_VFS_OK) {
1288 handle_error (&close_result, progress, error_mode, skip);
1289 return close_result;
1292 close_result = gnome_vfs_close (target_handle);
1293 if (result == GNOME_VFS_OK && close_result != GNOME_VFS_OK) {
1294 handle_error (&close_result, progress, error_mode, skip);
1295 return close_result;
1298 if (result == GNOME_VFS_OK) {
1299 /* FIXME the modules should ignore setting of permissions if
1300 * "valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS" is clear
1301 * for now, make sure permissions aren't set to 000
1304 if ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS) == 0) {
1305 set_mask = GNOME_VFS_SET_FILE_INFO_TIME;
1307 set_mask = GNOME_VFS_SET_FILE_INFO_PERMISSIONS
1308 | GNOME_VFS_SET_FILE_INFO_OWNER
1309 | GNOME_VFS_SET_FILE_INFO_TIME;
1312 /* ignore errors while setting file info attributes at this point */
1313 gnome_vfs_set_file_info_uri (target_uri, info, set_mask);
1317 return GNOME_VFS_OK;
1323 static GnomeVFSResult
1324 copy_directory (GnomeVFSFileInfo *source_file_info,
1325 GnomeVFSURI *source_dir_uri,
1326 GnomeVFSURI *target_dir_uri,
1327 GnomeVFSXferOptions xfer_options,
1328 GnomeVFSXferErrorMode *error_mode,
1329 GnomeVFSXferOverwriteMode *overwrite_mode,
1330 GnomeVFSProgressCallbackState *progress,
1333 GnomeVFSResult result;
1334 GnomeVFSDirectoryHandle *source_directory_handle;
1335 GnomeVFSDirectoryHandle *dest_directory_handle;
1336 GnomeVFSSetFileInfoMask set_mask;
1338 source_directory_handle = NULL;
1339 dest_directory_handle = NULL;
1341 result = gnome_vfs_directory_open_from_uri (&source_directory_handle, source_dir_uri,
1342 GNOME_VFS_FILE_INFO_DEFAULT);
1344 if (result != GNOME_VFS_OK) {
1348 progress->progress_info->bytes_copied = 0;
1349 if (call_progress_with_uris_often (progress, source_dir_uri, target_dir_uri,
1350 GNOME_VFS_XFER_PHASE_COPYING)
1351 == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1352 return GNOME_VFS_ERROR_INTERRUPTED;
1355 result = create_directory (target_dir_uri,
1356 &dest_directory_handle,
1364 gnome_vfs_directory_close (source_directory_handle);
1365 return GNOME_VFS_OK;
1368 if (result != GNOME_VFS_OK) {
1369 gnome_vfs_directory_close (source_directory_handle);
1373 if (call_progress_with_uris_often (progress, source_dir_uri, target_dir_uri,
1374 GNOME_VFS_XFER_PHASE_OPENTARGET) != 0) {
1376 progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
1377 progress->progress_info->top_level_item = FALSE;
1379 /* We do not deal with symlink loops here. That's OK
1380 * because we don't follow symlinks, unless the user
1381 * explicitly requests this with
1382 * GNOME_VFS_XFER_FOLLOW_LINKS_RECURSIVE.
1385 GnomeVFSURI *source_uri;
1386 GnomeVFSURI *dest_uri;
1387 GnomeVFSFileInfo *info;
1391 info = gnome_vfs_file_info_new ();
1393 result = gnome_vfs_directory_read_next (source_directory_handle, info);
1394 if (result != GNOME_VFS_OK) {
1395 gnome_vfs_file_info_unref (info);
1399 /* Skip "." and "..". */
1400 if (strcmp (info->name, ".") != 0 && strcmp (info->name, "..") != 0) {
1402 progress->progress_info->file_index++;
1404 source_uri = gnome_vfs_uri_append_file_name (source_dir_uri, info->name);
1405 dest_uri = gnome_vfs_uri_append_file_name (target_dir_uri, info->name);
1407 if (info->type == GNOME_VFS_FILE_TYPE_REGULAR) {
1408 result = copy_file (info, source_uri, dest_uri,
1409 xfer_options, error_mode, overwrite_mode,
1411 } else if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
1412 result = copy_directory (info, source_uri, dest_uri,
1413 xfer_options, error_mode, overwrite_mode,
1415 } else if (info->type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) {
1416 if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS_RECURSIVE) {
1417 GnomeVFSFileInfo *symlink_target_info = gnome_vfs_file_info_new ();
1418 result = gnome_vfs_get_file_info_uri (source_uri, symlink_target_info,
1419 GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
1420 if (result == GNOME_VFS_OK)
1421 result = copy_file (symlink_target_info, source_uri, dest_uri,
1422 xfer_options, error_mode, overwrite_mode,
1424 gnome_vfs_file_info_unref (symlink_target_info);
1426 result = copy_symlink (source_uri, dest_uri, info->symlink_name,
1427 error_mode, progress, skip);
1430 /* just ignore all the other special file system objects here */
1433 gnome_vfs_file_info_unref (info);
1435 if (dest_uri != NULL) {
1436 gnome_vfs_uri_unref (dest_uri);
1438 if (source_uri != NULL) {
1439 gnome_vfs_uri_unref (source_uri);
1442 } while (result == GNOME_VFS_OK);
1445 if (result == GNOME_VFS_ERROR_EOF) {
1446 /* all is well, we just finished iterating the directory */
1447 result = GNOME_VFS_OK;
1450 gnome_vfs_directory_close (dest_directory_handle);
1451 gnome_vfs_directory_close (source_directory_handle);
1453 if (result == GNOME_VFS_OK) {
1454 /* FIXME the modules should ignore setting of permissions if
1455 * "valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS" is clear
1456 * for now, make sure permissions aren't set to 000
1459 if ((source_file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS) == 0) {
1460 set_mask = GNOME_VFS_SET_FILE_INFO_TIME;
1462 set_mask = GNOME_VFS_SET_FILE_INFO_PERMISSIONS
1463 | GNOME_VFS_SET_FILE_INFO_OWNER
1464 | GNOME_VFS_SET_FILE_INFO_TIME;
1467 /* ignore errors while setting file info attributes at this point */
1468 gnome_vfs_set_file_info_uri (target_dir_uri, source_file_info, set_mask);
1474 static GnomeVFSResult
1475 copy_items (const GList *source_uri_list,
1476 const GList *target_uri_list,
1477 GnomeVFSXferOptions xfer_options,
1478 GnomeVFSXferErrorMode *error_mode,
1479 GnomeVFSXferOverwriteMode overwrite_mode,
1480 GnomeVFSProgressCallbackState *progress,
1481 GList **p_source_uris_copied_list)
1483 GnomeVFSResult result;
1484 const GList *source_item, *target_item;
1486 result = GNOME_VFS_OK;
1488 /* go through the list of names */
1489 for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) {
1491 GnomeVFSURI *source_uri;
1492 GnomeVFSURI *target_uri;
1493 GnomeVFSURI *target_dir_uri;
1495 GnomeVFSFileInfo *info;
1498 int progress_result;
1500 progress->progress_info->file_index++;
1505 source_uri = (GnomeVFSURI *)source_item->data;
1506 target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data);
1508 /* get source URI and file info */
1509 info = gnome_vfs_file_info_new ();
1510 result = gnome_vfs_get_file_info_uri (source_uri, info,
1511 GNOME_VFS_FILE_INFO_DEFAULT);
1513 progress->progress_info->duplicate_name =
1514 gnome_vfs_uri_extract_short_path_name
1515 ((GnomeVFSURI *)target_item->data);
1517 if (result == GNOME_VFS_OK) {
1518 /* optionally keep trying until we hit a unique target name */
1519 for (count = 1; ; count++) {
1520 GnomeVFSXferOverwriteMode overwrite_mode_abort;
1522 target_uri = gnome_vfs_uri_append_string
1524 progress->progress_info->duplicate_name);
1526 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1527 progress->progress_info->file_size = info->size;
1528 progress->progress_info->bytes_copied = 0;
1529 progress->progress_info->top_level_item = TRUE;
1531 if (call_progress_with_uris_often (progress, source_uri, target_uri,
1532 GNOME_VFS_XFER_PHASE_COPYING) == 0) {
1533 result = GNOME_VFS_ERROR_INTERRUPTED;
1536 overwrite_mode_abort = GNOME_VFS_XFER_OVERWRITE_MODE_ABORT;
1539 if (info->type == GNOME_VFS_FILE_TYPE_REGULAR) {
1540 result = copy_file (info, source_uri, target_uri,
1541 xfer_options, error_mode,
1542 &overwrite_mode_abort,
1544 } else if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
1545 result = copy_directory (info, source_uri, target_uri,
1546 xfer_options, error_mode,
1547 &overwrite_mode_abort,
1549 } else if (info->type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) {
1550 result = copy_symlink (source_uri, target_uri, info->symlink_name,
1551 error_mode, progress, &skip);
1553 /* just ignore all the other special file system objects here */
1555 if (result == GNOME_VFS_OK && !skip) {
1556 /* Add to list of successfully copied files... */
1557 *p_source_uris_copied_list = g_list_prepend (*p_source_uris_copied_list, source_uri);
1558 gnome_vfs_uri_ref (source_uri);
1561 if (result != GNOME_VFS_ERROR_FILE_EXISTS) {
1562 /* whatever happened, it wasn't a name conflict */
1566 if (overwrite_mode != GNOME_VFS_XFER_OVERWRITE_MODE_QUERY
1567 || (xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0)
1570 /* pass in the current target name, progress will update it to
1571 * a new unique name such as 'foo (copy)' or 'bar (copy 2)'
1573 g_free (progress->progress_info->duplicate_name);
1574 progress->progress_info->duplicate_name =
1575 gnome_vfs_uri_extract_short_path_name
1576 ((GnomeVFSURI *)target_item->data);
1577 progress->progress_info->duplicate_count = count;
1578 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE;
1579 progress->progress_info->vfs_status = result;
1580 progress_result = call_progress_uri (progress, source_uri, target_uri,
1581 GNOME_VFS_XFER_PHASE_COPYING);
1582 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1584 if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1592 /* try again with new uri */
1593 gnome_vfs_uri_unref (target_uri);
1598 gnome_vfs_file_info_unref (info);
1599 g_free (progress->progress_info->duplicate_name);
1601 if (result != GNOME_VFS_OK) {
1605 gnome_vfs_uri_unref (target_dir_uri);
1607 source_item = source_item->next;
1608 target_item = target_item->next;
1610 g_assert ((source_item != NULL) == (target_item != NULL));
1616 static GnomeVFSResult
1617 move_items (const GList *source_uri_list,
1618 const GList *target_uri_list,
1619 GnomeVFSXferOptions xfer_options,
1620 GnomeVFSXferErrorMode *error_mode,
1621 GnomeVFSXferOverwriteMode *overwrite_mode,
1622 GnomeVFSProgressCallbackState *progress)
1624 GnomeVFSResult result;
1625 const GList *source_item, *target_item;
1627 result = GNOME_VFS_OK;
1629 /* go through the list of names */
1630 for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) {
1632 GnomeVFSURI *source_uri;
1633 GnomeVFSURI *target_uri;
1634 GnomeVFSURI *target_dir_uri;
1638 int progress_result;
1640 progress->progress_info->file_index++;
1642 source_uri = (GnomeVFSURI *)source_item->data;
1643 target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data);
1645 progress->progress_info->duplicate_name =
1646 gnome_vfs_uri_extract_short_path_name
1647 ((GnomeVFSURI *)target_item->data);
1654 target_uri = gnome_vfs_uri_append_string (target_dir_uri,
1655 progress->progress_info->duplicate_name);
1657 progress->progress_info->file_size = 0;
1658 progress->progress_info->bytes_copied = 0;
1659 progress_set_source_target_uris (progress, source_uri, target_uri);
1660 progress->progress_info->top_level_item = TRUE;
1662 /* no matter what the replace mode, just overwrite the destination
1663 * handle_name_conflicts took care of conflicting files
1665 result = gnome_vfs_move_uri (source_uri, target_uri,
1666 (xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) != 0
1667 ? GNOME_VFS_XFER_OVERWRITE_MODE_ABORT
1668 : GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE);
1671 if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
1672 /* deal with a name conflict -- ask the progress_callback for a better name */
1673 g_free (progress->progress_info->duplicate_name);
1674 progress->progress_info->duplicate_name =
1675 gnome_vfs_uri_extract_short_path_name
1676 ((GnomeVFSURI *)target_item->data);
1677 progress->progress_info->duplicate_count = conflict_count;
1678 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE;
1679 progress->progress_info->vfs_status = result;
1680 progress_result = call_progress_uri (progress, source_uri, target_uri,
1681 GNOME_VFS_XFER_PHASE_COPYING);
1682 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1684 if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1685 gnome_vfs_uri_unref (target_uri);
1689 result = GNOME_VFS_OK;
1694 if (result != GNOME_VFS_OK) {
1695 retry = handle_error (&result, progress, error_mode, &skip);
1698 if (result == GNOME_VFS_OK
1700 && call_progress_with_uris_often (progress, source_uri,
1701 target_uri, GNOME_VFS_XFER_PHASE_MOVING) == 0) {
1702 result = GNOME_VFS_ERROR_INTERRUPTED;
1703 gnome_vfs_uri_unref (target_uri);
1706 gnome_vfs_uri_unref (target_uri);
1709 gnome_vfs_uri_unref (target_dir_uri);
1711 if (result != GNOME_VFS_OK && !skip)
1714 source_item = source_item->next;
1715 target_item = target_item->next;
1716 g_assert ((source_item != NULL) == (target_item != NULL));
1722 static GnomeVFSResult
1723 link_items (const GList *source_uri_list,
1724 const GList *target_uri_list,
1725 GnomeVFSXferOptions xfer_options,
1726 GnomeVFSXferErrorMode *error_mode,
1727 GnomeVFSXferOverwriteMode *overwrite_mode,
1728 GnomeVFSProgressCallbackState *progress)
1730 GnomeVFSResult result;
1731 const GList *source_item, *target_item;
1732 GnomeVFSURI *source_uri;
1733 GnomeVFSURI *target_dir_uri;
1734 GnomeVFSURI *target_uri;
1738 int progress_result;
1739 char *source_reference;
1741 result = GNOME_VFS_OK;
1743 /* go through the list of names, create a link to each item */
1744 for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) {
1746 progress->progress_info->file_index++;
1748 source_uri = (GnomeVFSURI *)source_item->data;
1749 source_reference = gnome_vfs_uri_to_string (source_uri, GNOME_VFS_URI_HIDE_NONE);
1751 target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data);
1752 progress->progress_info->duplicate_name =
1753 gnome_vfs_uri_extract_short_path_name
1754 ((GnomeVFSURI *)target_item->data);
1761 target_uri = gnome_vfs_uri_append_string
1763 progress->progress_info->duplicate_name);
1765 progress->progress_info->file_size = 0;
1766 progress->progress_info->bytes_copied = 0;
1767 progress->progress_info->top_level_item = TRUE;
1768 progress_set_source_target_uris (progress, source_uri, target_uri);
1770 /* no matter what the replace mode, just overwrite the destination
1771 * handle_name_conflicts took care of conflicting files
1773 result = gnome_vfs_create_symbolic_link (target_uri, source_reference);
1774 if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
1775 /* deal with a name conflict -- ask the progress_callback for a better name */
1776 g_free (progress->progress_info->duplicate_name);
1777 progress->progress_info->duplicate_name =
1778 gnome_vfs_uri_extract_short_path_name
1779 ((GnomeVFSURI *)target_item->data);
1780 progress->progress_info->duplicate_count = conflict_count;
1781 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE;
1782 progress->progress_info->vfs_status = result;
1783 progress_result = call_progress_uri (progress, source_uri, target_uri,
1784 GNOME_VFS_XFER_PHASE_COPYING);
1785 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1787 if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1788 gnome_vfs_uri_unref (target_uri);
1792 result = GNOME_VFS_OK;
1797 if (result != GNOME_VFS_OK) {
1798 retry = handle_error (&result, progress, error_mode, &skip);
1801 if (result == GNOME_VFS_OK
1802 && call_progress_with_uris_often (progress, source_uri,
1803 target_uri, GNOME_VFS_XFER_PHASE_OPENTARGET) == 0) {
1804 result = GNOME_VFS_ERROR_INTERRUPTED;
1805 gnome_vfs_uri_unref (target_uri);
1808 gnome_vfs_uri_unref (target_uri);
1811 gnome_vfs_uri_unref (target_dir_uri);
1812 g_free (source_reference);
1814 if (result != GNOME_VFS_OK && !skip)
1817 source_item = source_item->next;
1818 target_item = target_item->next;
1819 g_assert ((source_item != NULL) == (target_item != NULL));
1826 static GnomeVFSResult
1827 gnome_vfs_xfer_empty_directories (const GList *trash_dir_uris,
1828 GnomeVFSXferErrorMode error_mode,
1829 GnomeVFSProgressCallbackState *progress)
1831 GnomeVFSResult result;
1835 result = GNOME_VFS_OK;
1837 /* initialize the results */
1838 progress->progress_info->files_total = 0;
1839 progress->progress_info->bytes_total = 0;
1840 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COLLECTING;
1843 for (p = trash_dir_uris; p != NULL; p = p->next) {
1844 result = directory_add_items_and_size (p->data,
1845 GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE,
1847 if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) {
1848 /* out of file descriptors -- we will deal with that */
1849 result = GNOME_VFS_OK;
1852 /* set up a fake total size to represent the bulk of the operation
1853 * -- we'll subtract a proportional value for every deletion
1855 progress->progress_info->bytes_total
1856 = progress->progress_info->files_total * DEFAULT_SIZE_OVERHEAD;
1858 call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO);
1859 for (p = trash_dir_uris; p != NULL; p = p->next) {
1860 result = empty_directory ((GnomeVFSURI *)p->data, progress,
1861 GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE,
1862 &error_mode, &skip);
1863 if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) {
1864 result = non_recursive_empty_directory ((GnomeVFSURI *)p->data,
1865 progress, GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE,
1866 &error_mode, &skip);
1874 static GnomeVFSResult
1875 gnome_vfs_xfer_delete_items_common (const GList *source_uri_list,
1876 GnomeVFSXferErrorMode error_mode,
1877 GnomeVFSXferOptions xfer_options,
1878 GnomeVFSProgressCallbackState *progress)
1880 GnomeVFSFileInfo *info;
1881 GnomeVFSResult result;
1886 result = GNOME_VFS_OK;
1888 for (p = source_uri_list; p != NULL; p = p->next) {
1891 /* get the URI and VFSFileInfo for each */
1894 info = gnome_vfs_file_info_new ();
1895 result = gnome_vfs_get_file_info_uri (uri, info,
1896 GNOME_VFS_FILE_INFO_DEFAULT);
1898 if (result != GNOME_VFS_OK) {
1902 progress_set_source_target_uris (progress, uri, NULL);
1903 if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
1904 remove_directory (uri, TRUE, progress, xfer_options,
1905 &error_mode, &skip);
1907 remove_file (uri, progress, xfer_options, &error_mode,
1916 static GnomeVFSResult
1917 gnome_vfs_xfer_delete_items (const GList *source_uri_list,
1918 GnomeVFSXferErrorMode error_mode,
1919 GnomeVFSXferOptions xfer_options,
1920 GnomeVFSProgressCallbackState *progress)
1923 GnomeVFSResult result;
1925 /* initialize the results */
1926 progress->progress_info->files_total = 0;
1927 progress->progress_info->bytes_total = 0;
1928 call_progress (progress, GNOME_VFS_XFER_PHASE_COLLECTING);
1930 result = count_items_and_size (source_uri_list,
1931 GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE, progress, FALSE, FALSE);
1933 /* When deleting, ignore the real file sizes, just count the same DEFAULT_SIZE_OVERHEAD
1936 progress->progress_info->bytes_total
1937 = progress->progress_info->files_total * DEFAULT_SIZE_OVERHEAD;
1938 if (result != GNOME_VFS_ERROR_INTERRUPTED) {
1939 call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO);
1940 result = gnome_vfs_xfer_delete_items_common (source_uri_list,
1941 error_mode, xfer_options, progress);
1948 static GnomeVFSResult
1949 gnome_vfs_new_directory_with_unique_name (const GnomeVFSURI *target_dir_uri,
1951 GnomeVFSXferErrorMode error_mode,
1952 GnomeVFSXferOverwriteMode overwrite_mode,
1953 GnomeVFSProgressCallbackState *progress)
1955 GnomeVFSResult result;
1956 GnomeVFSURI *target_uri;
1957 GnomeVFSDirectoryHandle *dest_directory_handle;
1959 int progress_result;
1962 dest_directory_handle = NULL;
1963 progress->progress_info->top_level_item = TRUE;
1964 progress->progress_info->duplicate_name = g_strdup (name);
1966 for (conflict_count = 1; ; conflict_count++) {
1968 target_uri = gnome_vfs_uri_append_string
1970 progress->progress_info->duplicate_name);
1971 result = create_directory (target_uri,
1972 &dest_directory_handle,
1973 GNOME_VFS_XFER_USE_UNIQUE_NAMES,
1979 if (result != GNOME_VFS_ERROR_FILE_EXISTS
1980 && result != GNOME_VFS_ERROR_NAME_TOO_LONG) {
1984 /* deal with a name conflict -- ask the progress_callback for a better name */
1985 g_free (progress->progress_info->duplicate_name);
1986 progress->progress_info->duplicate_name = g_strdup (name);
1987 progress->progress_info->duplicate_count = conflict_count;
1988 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE;
1989 progress->progress_info->vfs_status = result;
1990 progress_result = call_progress_uri (progress, NULL, target_uri,
1991 GNOME_VFS_XFER_PHASE_COPYING);
1992 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1994 if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1998 gnome_vfs_uri_unref (target_uri);
2001 call_progress_uri (progress, NULL, target_uri,
2002 GNOME_VFS_XFER_PHASE_OPENTARGET);
2004 if (dest_directory_handle != NULL) {
2005 gnome_vfs_directory_close (dest_directory_handle);
2008 gnome_vfs_uri_unref (target_uri);
2009 g_free (progress->progress_info->duplicate_name);
2014 static GnomeVFSResult
2015 gnome_vfs_destination_is_writable (const GnomeVFSURI *uri)
2017 GnomeVFSURI *test_uri;
2018 GnomeVFSResult result;
2019 GnomeVFSHandle *handle;
2021 if (!gnome_vfs_uri_is_local (uri)) {
2022 /* if destination is not local, do not test it for writability, just
2023 * assume it's writable
2025 return GNOME_VFS_OK;
2028 /* test writability by creating and erasing a temporary file */
2029 test_uri = gnome_vfs_uri_append_file_name (uri, ".vfs-write.tmp");
2030 result = gnome_vfs_create_uri (&handle, test_uri, GNOME_VFS_OPEN_WRITE, TRUE, 0600);
2032 if (result == GNOME_VFS_OK) {
2033 gnome_vfs_close (handle);
2036 if (result == GNOME_VFS_OK || result == GNOME_VFS_ERROR_FILE_EXISTS) {
2037 gnome_vfs_unlink_from_uri (test_uri);
2038 result = GNOME_VFS_OK;
2041 /* some methods only allow certain filenames (e.g. .desktop files) */
2042 if (result == GNOME_VFS_ERROR_INVALID_URI) {
2043 result = GNOME_VFS_OK;
2046 gnome_vfs_uri_unref (test_uri);
2050 static GnomeVFSResult
2051 gnome_vfs_xfer_uri_internal (const GList *source_uris,
2052 const GList *target_uris,
2053 GnomeVFSXferOptions xfer_options,
2054 GnomeVFSXferErrorMode error_mode,
2055 GnomeVFSXferOverwriteMode overwrite_mode,
2056 GnomeVFSProgressCallbackState *progress)
2058 GnomeVFSResult result;
2059 GList *source_uri_list, *target_uri_list;
2060 GList *source_uri, *target_uri;
2061 GList *source_uri_list_copied;
2062 GnomeVFSURI *target_dir_uri;
2063 gboolean move, link;
2064 GnomeVFSFileSize free_bytes;
2065 GnomeVFSFileSize bytes_total;
2069 result = GNOME_VFS_OK;
2072 target_dir_uri = NULL;
2073 source_uri_list_copied = NULL;
2075 /* Check and see if target is writable. Return error if it is not. */
2076 call_progress (progress, GNOME_VFS_XFER_CHECKING_DESTINATION);
2077 target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)((GList *)target_uris)->data);
2078 result = gnome_vfs_destination_is_writable (target_dir_uri);
2079 progress_set_source_target_uris (progress, NULL, target_dir_uri);
2080 if (result != GNOME_VFS_OK) {
2081 handle_error (&result, progress, &error_mode, &skip);
2082 gnome_vfs_uri_unref (target_dir_uri);
2086 move = (xfer_options & GNOME_VFS_XFER_REMOVESOURCE) != 0;
2087 link = (xfer_options & GNOME_VFS_XFER_LINK_ITEMS) != 0;
2090 return GNOME_VFS_ERROR_BAD_PARAMETERS;
2093 /* Create an owning list of source and destination uris.
2094 * We want to be able to remove items that we decide to skip during
2095 * name conflict check.
2097 source_uri_list = gnome_vfs_uri_list_copy ((GList *)source_uris);
2098 target_uri_list = gnome_vfs_uri_list_copy ((GList *)target_uris);
2100 if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0) {
2101 /* see if moved files are on the same file system so that we decide to do
2102 * a simple move or have to do a copy/remove
2104 for (source_uri = source_uri_list, target_uri = target_uri_list;
2106 source_uri = source_uri->next, target_uri = target_uri->next) {
2109 g_assert (target_dir_uri != NULL);
2111 result = gnome_vfs_check_same_fs_uris ((GnomeVFSURI *)source_uri->data,
2112 target_dir_uri, &same_fs);
2114 if (result != GNOME_VFS_OK) {
2122 if (target_dir_uri != NULL) {
2123 gnome_vfs_uri_unref (target_dir_uri);
2124 target_dir_uri = NULL;
2127 if (result == GNOME_VFS_OK) {
2128 call_progress (progress, GNOME_VFS_XFER_PHASE_COLLECTING);
2129 result = count_items_and_size (source_uri_list, xfer_options, progress, move, link);
2130 if (result != GNOME_VFS_ERROR_INTERRUPTED) {
2131 /* Ignore anything but interruptions here -- we will deal with the errors
2132 * during the actual copy
2134 result = GNOME_VFS_OK;
2138 if (result == GNOME_VFS_OK) {
2139 /* Calculate free space on destination. If an error is returned, we have a non-local
2140 * file system, so we just forge ahead and hope for the best
2142 target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_uri_list->data);
2143 result = gnome_vfs_get_volume_free_space (target_dir_uri, &free_bytes);
2146 if (result == GNOME_VFS_OK) {
2147 if (!move && progress->progress_info->bytes_total > free_bytes) {
2148 result = GNOME_VFS_ERROR_NO_SPACE;
2149 progress_set_source_target_uris (progress, NULL, target_dir_uri);
2150 handle_error (&result, progress, &error_mode, &skip);
2153 /* Errors from gnome_vfs_get_volume_free_space should be ignored */
2154 result = GNOME_VFS_OK;
2157 if (target_dir_uri != NULL) {
2158 gnome_vfs_uri_unref (target_dir_uri);
2159 target_dir_uri = NULL;
2162 if (result != GNOME_VFS_OK) {
2166 if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0) {
2168 /* Save the preflight numbers, handle_name_conflicts would overwrite them */
2169 bytes_total = progress->progress_info->bytes_total;
2170 files_total = progress->progress_info->files_total;
2172 /* Set the preflight numbers to 0, we don't want to run progress on the
2173 * removal of conflicting items.
2175 progress->progress_info->bytes_total = 0;
2176 progress->progress_info->files_total = 0;
2178 result = handle_name_conflicts (&source_uri_list, &target_uri_list,
2179 xfer_options, &error_mode, &overwrite_mode,
2182 progress->progress_info->bytes_total = bytes_total;
2183 progress->progress_info->files_total = files_total;
2187 /* reset the preflight numbers */
2188 progress->progress_info->file_index = 0;
2189 progress->progress_info->total_bytes_copied = 0;
2191 if (result != GNOME_VFS_OK) {
2192 /* don't care about any results from handle_error */
2193 handle_error (&result, progress, &error_mode, &skip);
2195 /* whatever error it was, we handled it */
2196 result = GNOME_VFS_OK;
2198 call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO);
2202 result = move_items (source_uri_list, target_uri_list,
2203 xfer_options, &error_mode, &overwrite_mode,
2206 result = link_items (source_uri_list, target_uri_list,
2207 xfer_options, &error_mode, &overwrite_mode,
2210 result = copy_items (source_uri_list, target_uri_list,
2211 xfer_options, &error_mode, overwrite_mode,
2212 progress, &source_uri_list_copied);
2215 if (result == GNOME_VFS_OK) {
2216 if (xfer_options & GNOME_VFS_XFER_REMOVESOURCE
2217 && ! (move || link)) {
2218 call_progress (progress, GNOME_VFS_XFER_PHASE_CLEANUP);
2219 result = gnome_vfs_xfer_delete_items_common (
2220 source_uri_list_copied,
2221 error_mode, xfer_options, progress);
2227 gnome_vfs_uri_list_free (source_uri_list);
2228 gnome_vfs_uri_list_free (target_uri_list);
2229 gnome_vfs_uri_list_free (source_uri_list_copied);
2235 _gnome_vfs_xfer_private (const GList *source_uri_list,
2236 const GList *target_uri_list,
2237 GnomeVFSXferOptions xfer_options,
2238 GnomeVFSXferErrorMode error_mode,
2239 GnomeVFSXferOverwriteMode overwrite_mode,
2240 GnomeVFSXferProgressCallback progress_callback,
2242 GnomeVFSXferProgressCallback sync_progress_callback,
2243 gpointer sync_progress_data)
2245 GnomeVFSProgressCallbackState progress_state;
2246 GnomeVFSXferProgressInfo progress_info;
2247 GnomeVFSURI *target_dir_uri;
2248 GnomeVFSResult result;
2251 init_progress (&progress_state, &progress_info);
2252 progress_state.sync_callback = sync_progress_callback;
2253 progress_state.user_data = sync_progress_data;
2254 progress_state.update_callback = progress_callback;
2255 progress_state.async_job_data = data;
2258 if ((xfer_options & GNOME_VFS_XFER_EMPTY_DIRECTORIES) != 0) {
2259 /* Directory empty operation (Empty Trash, etc.). */
2260 g_assert (source_uri_list != NULL);
2261 g_assert (target_uri_list == NULL);
2263 call_progress (&progress_state, GNOME_VFS_XFER_PHASE_INITIAL);
2264 result = gnome_vfs_xfer_empty_directories (source_uri_list, error_mode, &progress_state);
2265 } else if ((xfer_options & GNOME_VFS_XFER_DELETE_ITEMS) != 0) {
2266 /* Delete items operation */
2267 g_assert (source_uri_list != NULL);
2268 g_assert (target_uri_list == NULL);
2270 call_progress (&progress_state, GNOME_VFS_XFER_PHASE_INITIAL);
2271 result = gnome_vfs_xfer_delete_items (source_uri_list,
2272 error_mode, xfer_options, &progress_state);
2273 } else if ((xfer_options & GNOME_VFS_XFER_NEW_UNIQUE_DIRECTORY) != 0) {
2274 /* New directory create operation */
2275 g_assert (source_uri_list == NULL);
2276 g_assert (g_list_length ((GList *) target_uri_list) == 1);
2278 target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *) target_uri_list->data);
2279 result = GNOME_VFS_ERROR_INVALID_URI;
2280 if (target_dir_uri != NULL) {
2281 short_name = gnome_vfs_uri_extract_short_path_name ((GnomeVFSURI *) target_uri_list->data);
2282 result = gnome_vfs_new_directory_with_unique_name (target_dir_uri, short_name,
2283 error_mode, overwrite_mode, &progress_state);
2284 g_free (short_name);
2285 gnome_vfs_uri_unref (target_dir_uri);
2288 /* Copy items operation */
2289 g_assert (source_uri_list != NULL);
2290 g_assert (target_uri_list != NULL);
2291 g_assert (g_list_length ((GList *)source_uri_list) == g_list_length ((GList *)target_uri_list));
2293 call_progress (&progress_state, GNOME_VFS_XFER_PHASE_INITIAL);
2294 result = gnome_vfs_xfer_uri_internal (source_uri_list, target_uri_list,
2295 xfer_options, error_mode, overwrite_mode, &progress_state);
2298 call_progress (&progress_state, GNOME_VFS_XFER_PHASE_COMPLETED);
2299 free_progress (&progress_info);
2301 /* FIXME bugzilla.eazel.com 1218:
2303 * The async job setup will try to call the callback function with the callback data
2304 * even though they are usually dead at this point because the callback detected
2305 * that we are giving up and cleaned up after itself.
2307 * Should fix this in the async job call setup.
2309 * For now just pretend everything worked well.
2312 result = GNOME_VFS_OK;
2318 * gnome_vfs_xfer_uri_list:
2319 * @source_uri_list: A Glist of uris (ie file;//)
2320 * @target_uri_list: A GList of uris
2321 * @xfer_options: These are options you wish to set for the transfer. For
2322 * instance by setting the xfer behavior you can either make a copy or a
2324 * @error_mode: Decide how to behave if the xfer is interrupted. For instance
2325 * you could set your application to return an error code in case of an
2327 * @overwrite_mode: How to react if a file your copying is being overwritten.
2328 * @progress_callback: This is used to monitor the progress of a transfer.
2329 * Common use would be to check to see if the transfer is asking for permission
2330 * to overwrite a file.
2331 * @data: Data to be want passed back in callbacks from the xfer engine
2333 * This function will transfer multiple files to a multiple targets. Given a
2334 * a source uri(s) and a destination uri(s). There are a list of options that
2335 * your application can use to control how the transfer is done.
2337 * Returns: If all goes well it returns GNOME_VFS_OK. Check GnomeVFSResult for
2341 gnome_vfs_xfer_uri_list (const GList *source_uri_list,
2342 const GList *target_uri_list,
2343 GnomeVFSXferOptions xfer_options,
2344 GnomeVFSXferErrorMode error_mode,
2345 GnomeVFSXferOverwriteMode overwrite_mode,
2346 GnomeVFSXferProgressCallback progress_callback,
2349 GnomeVFSProgressCallbackState progress_state;
2350 GnomeVFSXferProgressInfo progress_info;
2351 GnomeVFSResult result;
2353 g_return_val_if_fail (source_uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2354 g_return_val_if_fail (target_uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2355 g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY,
2356 GNOME_VFS_ERROR_BAD_PARAMETERS);
2358 init_progress (&progress_state, &progress_info);
2359 progress_state.sync_callback = progress_callback;
2360 progress_state.user_data = data;
2362 call_progress (&progress_state, GNOME_VFS_XFER_PHASE_INITIAL);
2364 result = gnome_vfs_xfer_uri_internal (source_uri_list, target_uri_list,
2365 xfer_options, error_mode, overwrite_mode, &progress_state);
2367 call_progress (&progress_state, GNOME_VFS_XFER_PHASE_COMPLETED);
2368 free_progress (&progress_info);
2374 * gnome_vfs_xfer_uri:
2375 * @source_uri: This is the location of where your data resides.
2376 * @target_uri: This is the location of where you want your data to go.
2377 * @xfer_options: Set the kind of transfers you want. These are:
2378 * GNOME_VFS_XFER_DEFAULT: Default behavior. Which is to do a straight one to
2380 * GNOME_VFS_XFER_FOLLOW_LINKS: This means follow the value of the symbolic
2381 * link when copying. (ie treat a symbolic link as a directory)
2382 * GNOME_VFS_RECURSIVE: Do a recursive copy of the source to the destination.
2383 * Equivalent to the cp -r option in GNU cp.
2384 * GNOME_VFS_XFER_SAME_FS: This only allows copying onto the same filesystem.
2385 * GNOME_VFS_DELETE_ITEM: This is equivalent to a mv. Where you will copy the
2386 * contents of the source to the destination and then remove data from the
2388 * GNOME_VFS_XFER_EMPTY_DIRECTORIES: <TBA>
2389 * GNOME_VFS_XFER_NEW_UNIQUE_DIRECTORY: This will create a directory if it
2390 * doesn't exist in the destination area. Useful with the
2391 * GNOME_VFS_XFER_RECURSIVE xfer option.
2392 * GNOME_VFS_XFER_REMOVESOURCE: This option will remove the source data after
2393 * whatever xfer option has been taken.
2394 * GNOME_VFS_USE_UNIQUE_NAMES: This is a check ot make sure that what you copy
2395 * onto the destination is not overwritten. It will only copy the unique items
2396 * from the source to the destjnation.
2397 * GNOME_VFS_XFER_LINK_ITEMS: <TBA>
2398 * @error_mode: When this function returns you need to check the error code
2399 * for the results of the copy. The results are generally:
2400 * GNOME_VFS_XFER_ERROR_MODE_ABORT: This means that the operation was aborted
2401 * by some sort of signal that interrupted the transfer.
2402 * GNOME_VFS_ERROR_MODE_QUERY: This means that no error has occured and that
2403 * you should query with the GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR. See
2404 * @overwrite_mode: This sets the options to deal with data that are duplicate
2405 * between the source and the destination. Your choices are:
2406 * GNOME_VFS_XFER_OVERWRITE_MODE_ABORT: This means abort the transfer if you
2407 * see duplicate data.
2408 * GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE: Replace the files silently. Don't
2410 * GNOME_VFS_XFER_OVERWRITE_MODE_SKIP: Skip duplicate files silenty.
2412 * @progress_callback: This is an important call back because this is how you
2413 * communicate with your copy process.
2414 * @data: Data to be want passed back in callbacks from the xfer engine
2416 * This function will allow a person to copy data from one location to another.
2417 * The location is specified using a URIs as the means to describe the location
2418 * of the data. Like any copy there are several options that can be set.
2419 * These can be set using the xfer_options. In addition there are callback
2420 * mechanisms and error codes to provide feedback in the copy
2423 * Return value: An integer representing the result of the operation.
2427 gnome_vfs_xfer_uri (const GnomeVFSURI *source_uri,
2428 const GnomeVFSURI *target_uri,
2429 GnomeVFSXferOptions xfer_options,
2430 GnomeVFSXferErrorMode error_mode,
2431 GnomeVFSXferOverwriteMode overwrite_mode,
2432 GnomeVFSXferProgressCallback progress_callback,
2435 GList *source_uri_list, *target_uri_list;
2436 GnomeVFSResult result;
2438 g_return_val_if_fail (source_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2439 g_return_val_if_fail (target_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2440 g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY,
2441 GNOME_VFS_ERROR_BAD_PARAMETERS);
2443 source_uri_list = g_list_append (NULL, (void *)source_uri);
2444 target_uri_list = g_list_append (NULL, (void *)target_uri);
2446 result = gnome_vfs_xfer_uri_list (source_uri_list, target_uri_list,
2447 xfer_options, error_mode, overwrite_mode, progress_callback, data);
2449 g_list_free (source_uri_list);
2450 g_list_free (target_uri_list);
2456 * gnome_vfs_xfer_delete_list:
2457 * @source_uri_list: This is a list containing uris
2458 * @error_mode: Decide how you want to deal with interruptions
2459 * @xfer_options: Set whatever transfer options you need.
2460 * @progress_callback: Callback to check on progress of transfer.
2461 * @data: Data to be want passed back in callbacks from the xfer engine
2463 * Unlink items in the list @source_uri_list from their filesystems.
2465 * Return value: %GNOME_VFS_OK if successful, or the appropriate error code otherwise
2468 gnome_vfs_xfer_delete_list (const GList *source_uri_list,
2469 GnomeVFSXferErrorMode error_mode,
2470 GnomeVFSXferOptions xfer_options,
2471 GnomeVFSXferProgressCallback
2475 GnomeVFSProgressCallbackState progress_state;
2476 GnomeVFSXferProgressInfo progress_info;
2477 GnomeVFSResult result;
2479 g_return_val_if_fail (source_uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2480 g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY,
2481 GNOME_VFS_ERROR_BAD_PARAMETERS);
2483 init_progress (&progress_state, &progress_info);
2484 progress_state.sync_callback = progress_callback;
2485 progress_state.user_data = data;
2486 call_progress (&progress_state, GNOME_VFS_XFER_PHASE_INITIAL);
2488 result = gnome_vfs_xfer_delete_items (source_uri_list, error_mode, xfer_options,
2491 call_progress (&progress_state, GNOME_VFS_XFER_PHASE_COMPLETED);
2492 free_progress (&progress_info);