ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-xfer.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* gnome-vfs-xfer.c - File transfers in the GNOME Virtual File System.
4
5    Copyright (C) 1999 Free Software Foundation
6    Copyright (C) 2000, 2001 Eazel, Inc.
7
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.
12
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.
17
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.
22
23    Authors: 
24    Ettore Perazzoli <ettore@comm2000.it> 
25    Pavel Cisler <pavel@eazel.com> 
26 */
27
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.  */
35
36 #include <config.h>
37 #include "gnome-vfs-xfer.h"
38
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>
46 #include <string.h>
47 #include <sys/time.h>
48
49 /* Implementation of file transfers (`gnome_vfs_xfer*()' functions).  */
50
51 static GnomeVFSResult   remove_directory        (GnomeVFSURI *uri,
52                                                  gboolean recursive,
53                                                  GnomeVFSProgressCallbackState *progress,
54                                                  GnomeVFSXferOptions xfer_options,
55                                                  GnomeVFSXferErrorMode *error_mode,
56                                                  gboolean *skip);
57
58
59 enum {
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).
63          */
64         DEFAULT_SIZE_OVERHEAD = 1024
65 };
66
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
69  * lot of performance
70  */
71 #define UPDATE_PERIOD ((gint64) (100 * 1000))
72
73 static gint64
74 system_time (void)
75 {
76         struct timeval time_of_day;
77
78         gettimeofday (&time_of_day, NULL);
79         return (gint64) time_of_day.tv_usec + ((gint64) time_of_day.tv_sec) * 1000000;
80 }
81
82 static void
83 init_progress (GnomeVFSProgressCallbackState *progress_state,
84                GnomeVFSXferProgressInfo *progress_info)
85 {
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;
99
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;
107 }
108
109 static void
110 free_progress (GnomeVFSXferProgressInfo *progress_info)
111 {
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;
116 }
117
118 static void
119 progress_set_source_target_uris (GnomeVFSProgressCallbackState *progress, 
120               const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri)
121 {
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;
128
129 }
130
131 static void
132 progress_set_source_target (GnomeVFSProgressCallbackState *progress, 
133               const char *source, const char *dest)
134 {
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;
139
140 }
141
142 static int
143 call_progress (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase)
144 {
145         int result;
146
147         /* FIXME bugzilla.eazel.com 1199: should use proper progress result values from an enum here */
148
149         result = 0;
150         progress_set_source_target_uris (progress, NULL, NULL);
151
152         progress->next_update_callback_time = system_time () + progress->update_callback_period;
153         
154         progress->progress_info->phase = phase;
155
156         if (progress->sync_callback != NULL)
157                 result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
158
159         if (progress->update_callback != NULL) {
160                 result = (* progress->update_callback) (progress->progress_info, 
161                         progress->async_job_data);
162         }
163
164         return result;  
165 }
166
167 static GnomeVFSXferErrorAction
168 call_progress_with_current_names (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase)
169 {
170         int result;
171
172         result = GNOME_VFS_XFER_ERROR_ACTION_ABORT;
173
174         progress->next_update_callback_time = system_time () + progress->update_callback_period;
175         progress->next_update_callback_time = progress->next_text_update_callback_time;
176         
177         progress->progress_info->phase = phase;
178
179         if (progress->sync_callback != NULL) {
180                 result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
181         }
182         
183         if (progress->update_callback != NULL) {
184                 result = (* progress->update_callback) (progress->progress_info, 
185                         progress->async_job_data);
186         }
187
188         return result;  
189 }
190
191 static int
192 call_progress_uri (GnomeVFSProgressCallbackState *progress, 
193                    const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri, 
194                    GnomeVFSXferPhase phase)
195 {
196         int result;
197
198         result = 0;
199         progress_set_source_target_uris (progress, source_uri, dest_uri);
200
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;
203         
204         progress->progress_info->phase = phase;
205
206         if (progress->sync_callback != NULL) {
207                 result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
208         }
209         
210         if (progress->update_callback != NULL) {
211                 result = (* progress->update_callback) (progress->progress_info,
212                         progress->async_job_data);
213         }
214         
215         return result;  
216 }
217
218 static gboolean
219 at_end (GnomeVFSProgressCallbackState *progress)
220 {
221         return progress->progress_info->total_bytes_copied
222                 >= progress->progress_info->bytes_total;
223 }
224
225 static int
226 call_progress_often_internal (GnomeVFSProgressCallbackState *progress,
227                               GnomeVFSXferPhase phase,
228                               gint64 *next_time)
229 {
230         int result;
231         gint64 now;
232
233         result = 1;
234         now = system_time ();
235
236         progress->progress_info->phase = phase;
237
238         if (progress->sync_callback != NULL) {
239                 result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
240         }
241
242         if (now < *next_time && !at_end (progress)) {
243                 return result;
244         }
245
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);
249         }
250
251         return result;
252 }
253
254 static int
255 call_progress_often (GnomeVFSProgressCallbackState *progress,
256                      GnomeVFSXferPhase phase)
257 {
258         return call_progress_often_internal
259                 (progress, phase, &progress->next_update_callback_time);
260 }
261
262 static int
263 call_progress_with_uris_often (GnomeVFSProgressCallbackState *progress, 
264                                const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri, 
265                                GnomeVFSXferPhase phase)
266 {
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);
270 }
271
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
275  * function.
276  */
277 static gboolean
278 handle_error (GnomeVFSResult *result,
279               GnomeVFSProgressCallbackState *progress,
280               GnomeVFSXferErrorMode *error_mode,
281               gboolean *skip)
282 {
283         GnomeVFSXferErrorAction action;
284
285         *skip = FALSE;
286
287         switch (*error_mode) {
288         case GNOME_VFS_XFER_ERROR_MODE_ABORT:
289                 return FALSE;
290
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;
296
297                 switch (action) {
298                 case GNOME_VFS_XFER_ERROR_ACTION_RETRY:
299                         return TRUE;
300                 case GNOME_VFS_XFER_ERROR_ACTION_ABORT:
301                         *result = GNOME_VFS_ERROR_INTERRUPTED;
302                         return FALSE;
303                 case GNOME_VFS_XFER_ERROR_ACTION_SKIP:
304                         *result = GNOME_VFS_OK;
305                         *skip = TRUE;
306                         return FALSE;
307                 }
308                 break;
309         }
310
311         *skip = FALSE;
312         return FALSE;
313 }
314
315 /* This is conceptually similiar to the previous `handle_error()' function, but
316  * handles the overwrite case. 
317  */
318 static gboolean
319 handle_overwrite (GnomeVFSResult *result,
320                   GnomeVFSProgressCallbackState *progress,
321                   GnomeVFSXferErrorMode *error_mode,
322                   GnomeVFSXferOverwriteMode *overwrite_mode,
323                   gboolean *replace,
324                   gboolean *skip)
325 {
326         GnomeVFSXferOverwriteAction action;
327
328         switch (*overwrite_mode) {
329         case GNOME_VFS_XFER_OVERWRITE_MODE_ABORT:
330                 *replace = FALSE;
331                 *result = GNOME_VFS_ERROR_FILE_EXISTS;
332                 *skip = FALSE;
333                 return FALSE;
334         case GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE:
335                 *replace = TRUE;
336                 *skip = FALSE;
337                 return TRUE;
338         case GNOME_VFS_XFER_OVERWRITE_MODE_SKIP:
339                 *replace = FALSE;
340                 *skip = TRUE;
341                 return FALSE;
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;
347
348                 switch (action) {
349                 case GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT:
350                         *replace = FALSE;
351                         *result = GNOME_VFS_ERROR_FILE_EXISTS;
352                         *skip = FALSE;
353                         return FALSE;
354                 case GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE:
355                         *replace = TRUE;
356                         *skip = FALSE;
357                         return TRUE;
358                 case GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE_ALL:
359                         *replace = TRUE;
360                         *overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
361                         *skip = FALSE;
362                         return TRUE;
363                 case GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP:
364                         *replace = FALSE;
365                         *skip = TRUE;
366                         return FALSE;
367                 case GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP_ALL:
368                         *replace = FALSE;
369                         *skip = TRUE;
370                         *overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_SKIP;
371                         return FALSE;
372                 }
373         }
374
375         *replace = FALSE;
376         *skip = FALSE;
377         return FALSE;
378 }
379
380 static GnomeVFSResult
381 remove_file (GnomeVFSURI *uri,
382              GnomeVFSProgressCallbackState *progress,
383              GnomeVFSXferOptions xfer_options,
384              GnomeVFSXferErrorMode *error_mode,
385              gboolean *skip)
386 {
387         GnomeVFSResult result;
388         gboolean retry;
389
390         progress->progress_info->file_index++;
391         do {
392                 retry = FALSE;
393                 result = gnome_vfs_unlink_from_uri (uri);               
394                 if (result != GNOME_VFS_OK) {
395                         retry = handle_error (&result, progress,
396                                               error_mode, skip);
397                 } else {
398                         /* add some small size for each deleted item because delete overhead
399                          * does not depend on file/directory size
400                          */
401                         progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
402
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;
407                                 break;
408                         }
409                 }
410
411
412         } while (retry);
413
414         return result;
415 }
416
417 static GnomeVFSResult
418 empty_directory (GnomeVFSURI *uri,
419                  GnomeVFSProgressCallbackState *progress,
420                  GnomeVFSXferOptions xfer_options,
421                  GnomeVFSXferErrorMode *error_mode,
422                  gboolean *skip)
423 {
424         GnomeVFSResult result;
425         GnomeVFSDirectoryHandle *directory_handle;
426         gboolean retry;
427
428
429         *skip = FALSE;
430         do {
431                 result = gnome_vfs_directory_open_from_uri (&directory_handle, uri, 
432                                                             GNOME_VFS_FILE_INFO_DEFAULT);
433                 retry = FALSE;
434                 if (result != GNOME_VFS_OK) {
435                         retry = handle_error (&result, progress,
436                                               error_mode, skip);
437                 }
438         } while (retry);
439
440         if (result != GNOME_VFS_OK || *skip) {
441                 return result;
442         }
443         
444         for (;;) {
445                 GnomeVFSFileInfo *info;
446                 GnomeVFSURI *item_uri;
447                 
448                 item_uri = NULL;
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);
453                         break;
454                 }
455
456                 /* Skip "." and "..".  */
457                 if (strcmp (info->name, ".") != 0 && strcmp (info->name, "..") != 0) {
458
459                         item_uri = gnome_vfs_uri_append_file_name (uri, info->name);
460                         
461                         progress_set_source_target_uris (progress, item_uri, NULL);
462                         
463                         if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
464                                 result = remove_directory (item_uri, TRUE, 
465                                                            progress, xfer_options, error_mode, 
466                                                            skip);
467                         } else {
468                                 result = remove_file (item_uri, progress, 
469                                                       xfer_options,
470                                                       error_mode,
471                                                       skip);
472                         }
473
474                 }
475
476                 gnome_vfs_file_info_unref (info);
477                 
478                 if (item_uri != NULL) {
479                         gnome_vfs_uri_unref (item_uri);
480                 }
481                 
482                 if (result != GNOME_VFS_OK) {
483                         break;
484                 }
485         }
486         gnome_vfs_directory_close (directory_handle);
487
488         if (result == GNOME_VFS_ERROR_EOF) {
489                 result = GNOME_VFS_OK;
490         }
491
492         return result;
493 }
494
495 typedef struct {
496         const GnomeVFSURI *base_uri;
497         GList *uri_list;
498 } PrependOneURIParams;
499
500 static gboolean
501 PrependOneURIToList (const gchar *rel_path, GnomeVFSFileInfo *info,
502         gboolean recursing_will_loop, gpointer cast_to_params, gboolean *recurse)
503 {
504         PrependOneURIParams *params;
505
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));
509
510         if (recursing_will_loop) {
511                 return FALSE;
512         }
513         *recurse = TRUE;
514         return TRUE;
515 }
516
517 static GnomeVFSResult
518 non_recursive_empty_directory (GnomeVFSURI *directory_uri,
519                                GnomeVFSProgressCallbackState *progress,
520                                GnomeVFSXferOptions xfer_options,
521                                GnomeVFSXferErrorMode *error_mode,
522                                gboolean *skip)
523 {
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.
528          */
529
530         GList *uri_list;
531         GList *p;
532         GnomeVFSURI *uri;
533         GnomeVFSResult result;
534         GnomeVFSFileInfo *info;
535         PrependOneURIParams visit_params;
536
537         /* Build up the list so that deep items appear before their parents
538          * so that we can delete directories, children first.
539          */
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);
545
546         uri_list = visit_params.uri_list;
547
548         if (result == GNOME_VFS_OK) {
549                 for (p = uri_list; p != NULL; p = p->next) {
550
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);
559                                 } else {
560                                         result = remove_file (uri, progress, 
561                                                 xfer_options, error_mode, skip);
562                                 }
563                         }
564                         gnome_vfs_file_info_unref (info);
565                 }
566         }
567
568         gnome_vfs_uri_list_free (uri_list);
569
570         return result;
571 }
572
573 static GnomeVFSResult
574 remove_directory (GnomeVFSURI *uri,
575                   gboolean recursive,
576                   GnomeVFSProgressCallbackState *progress,
577                   GnomeVFSXferOptions xfer_options,
578                   GnomeVFSXferErrorMode *error_mode,
579                   gboolean *skip)
580 {
581         GnomeVFSResult result;
582         gboolean retry;
583
584         result = GNOME_VFS_OK;
585
586         if (recursive) {
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,
590                                 error_mode, skip);
591                 }
592         }
593
594         if (result == GNOME_VFS_ERROR_EOF) {
595                 result = GNOME_VFS_OK;
596         }
597
598         if (result == GNOME_VFS_OK) {
599                 progress->progress_info->file_index++;
600
601                 do {
602                         retry = FALSE;
603
604                         result = gnome_vfs_remove_directory_from_uri (uri);
605                         if (result != GNOME_VFS_OK) {
606                                 retry = handle_error (&result, progress,
607                                                       error_mode, skip);
608                         } else {
609                                 /* add some small size for each deleted item */
610                                 progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
611
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;
616                                         break;
617                                 }
618                         }
619
620                 } while (retry);
621         }
622
623         return result;
624 }
625
626 /* iterates the list of names in a given directory, applies @callback on each,
627  * optionally recurses into directories
628  */
629 static GnomeVFSResult
630 gnome_vfs_visit_list (const GList *name_uri_list,
631                       GnomeVFSFileInfoOptions info_options,
632                       GnomeVFSDirectoryVisitOptions visit_options,
633                       gboolean recursive,
634                       GnomeVFSDirectoryVisitFunc callback,
635                       gpointer data)
636 {
637         GnomeVFSResult result;
638         const GList *p;
639         GnomeVFSURI *uri;
640         GnomeVFSFileInfo *info;
641         gboolean tmp_recurse;
642         
643         result = GNOME_VFS_OK;
644
645         /* go through our list of items */
646         for (p = name_uri_list; p != NULL; p = p->next) {
647                 
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);
652                 
653                 if (result == GNOME_VFS_OK) {
654                         tmp_recurse = TRUE;
655                         
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;
659                         }
660                         
661                         if (result == GNOME_VFS_OK
662                             && recursive
663                             && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
664                                 /* let gnome_vfs_directory_visit_uri call our callback 
665                                  * recursively 
666                                  */
667                                 result = gnome_vfs_directory_visit_uri (uri, info_options, 
668                                         visit_options, callback, data);
669                         }
670                 }
671                 gnome_vfs_file_info_unref (info);
672                 
673                 if (result != GNOME_VFS_OK) {
674                         break;
675                 }
676         }
677         return result;
678 }
679
680 typedef struct {
681         GnomeVFSProgressCallbackState *progress;
682         GnomeVFSResult result;
683 } CountEachFileSizeParams;
684
685 /* iteratee for count_items_and_size */
686 static gboolean
687 count_each_file_size_one (const gchar *rel_path,
688                           GnomeVFSFileInfo *info,
689                           gboolean recursing_will_loop,
690                           gpointer data,
691                           gboolean *recurse)
692 {
693         CountEachFileSizeParams *params;
694
695         params = (CountEachFileSizeParams *)data;
696
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;
700                 *recurse = FALSE;
701                 return FALSE;
702         }
703
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);
706
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;
715         }
716
717         /* watch out for infinite recursion*/
718         if (recursing_will_loop) {
719                 params->result = GNOME_VFS_ERROR_LOOP;
720                 return FALSE;
721         }
722
723         *recurse = TRUE;
724
725         return TRUE;
726 }
727
728 /* calculate the number of items and their total size; used as a preflight
729  * before the transfer operation starts
730  */
731 static GnomeVFSResult
732 count_items_and_size (const GList *name_uri_list,
733                       GnomeVFSXferOptions xfer_options,
734                       GnomeVFSProgressCallbackState *progress,
735                       gboolean move,
736                       gboolean link)
737 {
738         /*
739          * FIXME bugzilla.eazel.com 1200:
740          * Deal with errors here, respond to skip by pulling items from the name list
741          */
742         GnomeVFSFileInfoOptions info_options;
743         GnomeVFSDirectoryVisitOptions visit_options;
744         CountEachFileSizeParams each_params;
745
746         /* initialize the results */
747         progress->progress_info->files_total = 0;
748         progress->progress_info->bytes_total = 0;
749
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;
756
757         if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS) {
758                 info_options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS;
759         } else {
760                 info_options = GNOME_VFS_FILE_INFO_DEFAULT;
761         }
762
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);
766 }
767
768 /* calculate the number of items and their total size; used as a preflight
769  * before the transfer operation starts
770  */
771 static GnomeVFSResult
772 directory_add_items_and_size (GnomeVFSURI *dir_uri,
773                               GnomeVFSXferOptions xfer_options,
774                               GnomeVFSProgressCallbackState *progress)
775 {
776         GnomeVFSFileInfoOptions info_options;
777         GnomeVFSDirectoryVisitOptions visit_options;
778         CountEachFileSizeParams each_params;
779
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;
786
787         if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS) {
788                 info_options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS;
789         } else {
790                 info_options = GNOME_VFS_FILE_INFO_DEFAULT;
791         }
792
793         return gnome_vfs_directory_visit_uri (dir_uri, info_options,
794                 visit_options, count_each_file_size_one, &each_params);
795
796 }
797
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.
802  */
803 static gboolean
804 move_source_is_in_target (GnomeVFSURI *source, GnomeVFSURI *target)
805 {
806         GnomeVFSURI *parent, *tmp;
807         gboolean res;
808
809         parent = gnome_vfs_uri_ref (source);
810
811         res = FALSE;
812         while (parent != NULL) {
813                 if (_gnome_vfs_uri_is_in_subdir (parent, target)) {
814                         res = TRUE;
815                         break;
816                 }
817                 tmp = gnome_vfs_uri_get_parent (parent);
818                 gnome_vfs_uri_unref (parent);
819                 parent = tmp;
820         }
821         gnome_vfs_uri_unref (parent);
822         
823         return res;
824 }
825
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
829  */
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)
837 {
838         GnomeVFSResult result;
839         GList *source_item;
840         GList *target_item;
841
842         int conflict_count; /* values are 0, 1, many */
843         
844         result = GNOME_VFS_OK;
845         conflict_count = 0;
846
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)) {
851                         conflict_count++;
852                         if (conflict_count > 1) {
853                                 break;
854                         }
855                 }
856         }
857
858         if (conflict_count == 0) {
859                 /* No conflicts, we are done. */
860                 return GNOME_VFS_OK;
861         }
862
863         /* Pass in the conflict count so that we can decide to present the Replace All
864          * for multiple conflicts.
865          */
866         progress->progress_info->duplicate_count = conflict_count;
867
868         
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;) {
872                 gboolean replace;
873                 gboolean skip;
874                 gboolean is_move_to_self;
875                 GnomeVFSURI *uri, *source_uri;
876                 GnomeVFSFileInfo *info;
877                 
878                 skip = FALSE;
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);
885                          
886                         /* no error getting info -- file exists, ask what to do */
887                         replace = handle_overwrite (&result, progress, error_mode,
888                                 overwrite_mode, &replace, &skip);
889
890                         /* FIXME bugzilla.eazel.com 1207:
891                          * move items to Trash here
892                          */
893
894                         /* get rid of the conflicting file */
895                         if (replace) {
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;
903                                         } else {                                        
904                                                 remove_directory (uri, TRUE, progress, 
905                                                                   xfer_options, error_mode, &skip);
906                                         }
907                                 } else {
908                                         remove_file (uri, progress,
909                                                 xfer_options, error_mode, &skip);
910                                 }
911                                 gnome_vfs_file_info_unref (info);
912                         }
913                 }
914
915                 
916                 if (result != GNOME_VFS_OK) {
917                         break;
918                 }
919
920                 if (skip) {
921                         /* skipping a file, remove it's name from the source and target name
922                          * lists.
923                          */
924                         GList *source_item_to_remove;
925                         GList *target_item_to_remove;
926
927                         source_item_to_remove = source_item;
928                         target_item_to_remove = target_item;
929                         
930                         source_item = source_item->next;
931                         target_item = target_item->next;
932
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);
937                         
938                         continue;
939                 }
940
941
942                 target_item = target_item->next; 
943                 source_item = source_item->next;
944         }
945
946         return result;
947 }
948
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
953  */
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,
961                   gboolean *skip)
962 {
963         GnomeVFSResult result;
964         gboolean retry;
965         
966         *skip = FALSE;
967         do {
968                 retry = FALSE;
969
970                 result = gnome_vfs_make_directory_for_uri (dir_uri, 0777);
971
972                 if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
973                         gboolean force_replace;
974
975                         if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) != 0) {
976                                 /* just let the caller pass a unique name*/
977                                 return result;
978                         }
979
980                         retry = handle_overwrite (&result,
981                                                   progress,
982                                                   error_mode,
983                                                   overwrite_mode,
984                                                   &force_replace,
985                                                   skip);
986
987                         if (*skip) {
988                                 return GNOME_VFS_OK;
989                         }
990                         if (force_replace) {
991                                 result = remove_directory (dir_uri, TRUE, progress, 
992                                                            xfer_options, error_mode, 
993                                                            skip);
994                         } else {
995                                 result = GNOME_VFS_OK;
996                         }
997                 }
998
999                 if (result == GNOME_VFS_OK) {
1000                         return gnome_vfs_directory_open_from_uri (return_handle, 
1001                                                                   dir_uri, 
1002                                                                   GNOME_VFS_FILE_INFO_DEFAULT);
1003                 }
1004                 /* handle the error case */
1005                 retry = handle_error (&result, progress,
1006                                       error_mode, skip);
1007
1008                 if (*skip) {
1009                         return GNOME_VFS_OK;
1010                 }
1011
1012         } while (retry);
1013
1014         return result;
1015 }
1016
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,
1024                 guint block_size,
1025                 gboolean *skip)
1026 {
1027         GnomeVFSResult result;
1028         gpointer buffer;
1029         const char *write_buffer;
1030         GnomeVFSFileSize total_bytes_read;
1031
1032         *skip = FALSE;
1033
1034         if (call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING) == 0) {
1035                 return GNOME_VFS_ERROR_INTERRUPTED;
1036         }
1037
1038         buffer = g_malloc (block_size);
1039         total_bytes_read = 0;
1040         
1041         do {
1042                 GnomeVFSFileSize bytes_read;
1043                 GnomeVFSFileSize bytes_to_write;
1044                 GnomeVFSFileSize bytes_written;
1045                 gboolean retry;
1046
1047                 progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1048                 progress->progress_info->vfs_status = GNOME_VFS_OK;
1049
1050                 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_READSOURCE;
1051
1052                 do {
1053                         retry = FALSE;
1054
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,
1059                                                       error_mode, skip);
1060                 } while (retry);
1061
1062                 if (result != GNOME_VFS_OK || bytes_read == 0 || *skip) {
1063                         break;
1064                 }
1065
1066                 total_bytes_read += total_bytes_read;
1067                 bytes_to_write = bytes_read;
1068
1069                 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_WRITETARGET;
1070
1071                 write_buffer = buffer;
1072                 do {
1073                         retry = FALSE;
1074
1075                         result = gnome_vfs_write (target_handle, write_buffer,
1076                                                   bytes_to_write,
1077                                                   &bytes_written);
1078
1079                         if (result != GNOME_VFS_OK) {
1080                                 retry = handle_error (&result, progress, error_mode, skip);
1081                         }
1082
1083                         bytes_to_write -= bytes_written;
1084                         write_buffer += bytes_written;
1085                 } while ((result == GNOME_VFS_OK && bytes_to_write > 0) || retry);
1086
1087                 progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COPYING;
1088
1089                 progress->progress_info->bytes_copied += bytes_read;
1090                 progress->progress_info->total_bytes_copied += bytes_read;
1091
1092                 if (call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING) == 0) {
1093                         g_free (buffer);
1094                         return GNOME_VFS_ERROR_INTERRUPTED;
1095                 }
1096
1097                 if (*skip) {
1098                         break;
1099                 }
1100
1101         } while (result == GNOME_VFS_OK);
1102
1103         if (result == GNOME_VFS_ERROR_EOF) {
1104                 result = GNOME_VFS_OK;
1105         }
1106
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
1110                  */
1111                 progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
1112
1113                 call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING);
1114         }
1115
1116         g_free (buffer);
1117
1118         return result;
1119 }
1120
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,
1127                   gboolean *skip)
1128 {
1129         GnomeVFSResult result;
1130         gboolean retry;
1131
1132         *skip = FALSE;
1133         do {
1134                 retry = FALSE;
1135
1136                 result = gnome_vfs_open_uri (source_handle, source_uri,
1137                                              GNOME_VFS_OPEN_READ);
1138
1139                 if (result != GNOME_VFS_OK) {
1140                         retry = handle_error (&result, progress, error_mode, skip);
1141                 }
1142         } while (retry);
1143
1144         return result;
1145 }
1146
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,
1154                     gboolean *skip)
1155 {
1156         GnomeVFSResult result;
1157         gboolean retry;
1158         gboolean exclusive;
1159
1160         exclusive = (*overwrite_mode != GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE);
1161
1162         *skip = FALSE;
1163         do {
1164                 retry = FALSE;
1165
1166                 result = gnome_vfs_create_uri (target_handle, target_uri,
1167                                                GNOME_VFS_OPEN_WRITE,
1168                                                exclusive, 0666);
1169
1170                 if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
1171                         gboolean replace;
1172
1173                         retry = handle_overwrite (&result,
1174                                                   progress,
1175                                                   error_mode,
1176                                                   overwrite_mode,
1177                                                   &replace,
1178                                                   skip);
1179
1180                         if (replace) {
1181                                 exclusive = FALSE;
1182                         }
1183
1184                 } else if (result != GNOME_VFS_OK) {
1185                         retry = handle_error (&result,  progress, error_mode, skip);
1186                 }
1187         } while (retry);
1188
1189         return result;
1190 }
1191
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,
1198               gboolean *skip)
1199 {
1200         GnomeVFSResult result;
1201         gboolean retry;
1202
1203         *skip = FALSE;
1204         do {
1205                 retry = FALSE;
1206                 
1207                 result = gnome_vfs_create_symbolic_link (target_uri, link_name);
1208
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;
1215                 }
1216         } while (retry);
1217
1218         if (*skip) {
1219                 return GNOME_VFS_OK;
1220         }
1221         
1222         return result;
1223 }
1224
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,
1233            gboolean *skip)
1234 {
1235         GnomeVFSResult close_result, result;
1236         GnomeVFSHandle *source_handle, *target_handle;
1237         GnomeVFSSetFileInfoMask set_mask;
1238
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,
1243                                    error_mode, skip);
1244         if (*skip) {
1245                 return GNOME_VFS_OK;
1246         }
1247         
1248         if (result != GNOME_VFS_OK) {
1249                 return result;
1250         }
1251
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,
1256                                      skip);
1257
1258
1259         if (*skip) {
1260                 gnome_vfs_close (source_handle);
1261                 return GNOME_VFS_OK;
1262         }
1263         if (result != GNOME_VFS_OK) {
1264                 gnome_vfs_close (source_handle);
1265                 return result;
1266         }
1267
1268         if (call_progress_with_uris_often (progress, source_uri, target_uri, 
1269                 GNOME_VFS_XFER_PHASE_OPENTARGET) != GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1270
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 
1275                                          */
1276                                         (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE && info->io_block_size > 0)
1277                                                 ? info->io_block_size : 4096,
1278                                         skip);
1279         }
1280
1281         if (result == GNOME_VFS_OK 
1282                 && call_progress_often (progress, GNOME_VFS_XFER_PHASE_CLOSETARGET) == 0) {
1283                 result = GNOME_VFS_ERROR_INTERRUPTED;
1284         }
1285
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;
1290         }
1291
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;
1296         }
1297
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
1302                  */
1303
1304                 if ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS) == 0) {
1305                         set_mask = GNOME_VFS_SET_FILE_INFO_TIME;
1306                 } else {
1307                         set_mask = GNOME_VFS_SET_FILE_INFO_PERMISSIONS
1308                                         | GNOME_VFS_SET_FILE_INFO_OWNER
1309                                         | GNOME_VFS_SET_FILE_INFO_TIME;
1310                 }
1311
1312                 /* ignore errors while setting file info attributes at this point */
1313                 gnome_vfs_set_file_info_uri (target_uri, info, set_mask);
1314         }
1315
1316         if (*skip) {
1317                 return GNOME_VFS_OK;
1318         }
1319
1320         return result;
1321 }
1322
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,
1331                 gboolean *skip)
1332 {
1333         GnomeVFSResult result;
1334         GnomeVFSDirectoryHandle *source_directory_handle;
1335         GnomeVFSDirectoryHandle *dest_directory_handle;
1336         GnomeVFSSetFileInfoMask set_mask;
1337
1338         source_directory_handle = NULL;
1339         dest_directory_handle = NULL;
1340         
1341         result = gnome_vfs_directory_open_from_uri (&source_directory_handle, source_dir_uri, 
1342                                                     GNOME_VFS_FILE_INFO_DEFAULT);
1343
1344         if (result != GNOME_VFS_OK) {
1345                 return result;
1346         }
1347
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;
1353         }
1354
1355         result = create_directory (target_dir_uri, 
1356                                    &dest_directory_handle,
1357                                    xfer_options,
1358                                    error_mode,
1359                                    overwrite_mode,
1360                                    progress,
1361                                    skip);
1362
1363         if (*skip) {
1364                 gnome_vfs_directory_close (source_directory_handle);
1365                 return GNOME_VFS_OK;
1366         }
1367         
1368         if (result != GNOME_VFS_OK) {
1369                 gnome_vfs_directory_close (source_directory_handle);
1370                 return result;
1371         }
1372
1373         if (call_progress_with_uris_often (progress, source_dir_uri, target_dir_uri, 
1374                                            GNOME_VFS_XFER_PHASE_OPENTARGET) != 0) {
1375
1376                 progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
1377                 progress->progress_info->top_level_item = FALSE;
1378
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.
1383                  */
1384                 do {
1385                         GnomeVFSURI *source_uri;
1386                         GnomeVFSURI *dest_uri;
1387                         GnomeVFSFileInfo *info;
1388
1389                         source_uri = NULL;
1390                         dest_uri = NULL;
1391                         info = gnome_vfs_file_info_new ();
1392
1393                         result = gnome_vfs_directory_read_next (source_directory_handle, info);
1394                         if (result != GNOME_VFS_OK) {
1395                                 gnome_vfs_file_info_unref (info);       
1396                                 break;
1397                         }
1398                         
1399                         /* Skip "." and "..".  */
1400                         if (strcmp (info->name, ".") != 0 && strcmp (info->name, "..") != 0) {
1401
1402                                 progress->progress_info->file_index++;
1403
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);
1406                                 
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, 
1410                                                             progress, skip);
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, 
1414                                                                  progress, skip);
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, 
1423                                                                             progress, skip);
1424                                                 gnome_vfs_file_info_unref (symlink_target_info);
1425                                         } else {
1426                                                 result = copy_symlink (source_uri, dest_uri, info->symlink_name,
1427                                                                        error_mode, progress, skip);
1428                                         }
1429                                 }
1430                                 /* just ignore all the other special file system objects here */
1431                         }
1432                         
1433                         gnome_vfs_file_info_unref (info);
1434                         
1435                         if (dest_uri != NULL) {
1436                                 gnome_vfs_uri_unref (dest_uri);
1437                         }
1438                         if (source_uri != NULL) {
1439                                 gnome_vfs_uri_unref (source_uri);
1440                         }
1441
1442                 } while (result == GNOME_VFS_OK);
1443         }
1444
1445         if (result == GNOME_VFS_ERROR_EOF) {
1446                 /* all is well, we just finished iterating the directory */
1447                 result = GNOME_VFS_OK;
1448         }
1449
1450         gnome_vfs_directory_close (dest_directory_handle);
1451         gnome_vfs_directory_close (source_directory_handle);
1452
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
1457                  */
1458
1459                 if ((source_file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS) == 0) {
1460                         set_mask = GNOME_VFS_SET_FILE_INFO_TIME;
1461                 } else {
1462                         set_mask = GNOME_VFS_SET_FILE_INFO_PERMISSIONS
1463                                         | GNOME_VFS_SET_FILE_INFO_OWNER
1464                                         | GNOME_VFS_SET_FILE_INFO_TIME;
1465                 }
1466
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);
1469         }
1470
1471         return result;
1472 }
1473
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)
1482 {
1483         GnomeVFSResult result;
1484         const GList *source_item, *target_item;
1485         
1486         result = GNOME_VFS_OK;
1487
1488         /* go through the list of names */
1489         for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) {
1490
1491                 GnomeVFSURI *source_uri;
1492                 GnomeVFSURI *target_uri;
1493                 GnomeVFSURI *target_dir_uri;
1494
1495                 GnomeVFSFileInfo *info;
1496                 gboolean skip;
1497                 int count;
1498                 int progress_result;
1499
1500                 progress->progress_info->file_index++;
1501
1502                 skip = FALSE;
1503                 target_uri = NULL;
1504
1505                 source_uri = (GnomeVFSURI *)source_item->data;
1506                 target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data);
1507                 
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);
1512
1513                 progress->progress_info->duplicate_name =
1514                         gnome_vfs_uri_extract_short_path_name
1515                         ((GnomeVFSURI *)target_item->data);
1516
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;
1521
1522                                 target_uri = gnome_vfs_uri_append_string
1523                                         (target_dir_uri, 
1524                                          progress->progress_info->duplicate_name);
1525
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;
1530
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;
1534                                 }
1535
1536                                 overwrite_mode_abort = GNOME_VFS_XFER_OVERWRITE_MODE_ABORT;
1537                                 
1538                                 
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, 
1543                                                             progress, &skip);
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,
1548                                                                  progress, &skip);
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);
1552                                 }
1553                                 /* just ignore all the other special file system objects here */
1554
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);
1559                                 }
1560
1561                                 if (result != GNOME_VFS_ERROR_FILE_EXISTS) {
1562                                         /* whatever happened, it wasn't a name conflict */
1563                                         break;
1564                                 }
1565
1566                                 if (overwrite_mode != GNOME_VFS_XFER_OVERWRITE_MODE_QUERY
1567                                     || (xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0)
1568                                         break;
1569
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)'
1572                                  */
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;
1583
1584                                 if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1585                                         break;
1586                                 }
1587
1588                                 if (skip) {
1589                                         break;
1590                                 }
1591                                 
1592                                 /* try again with new uri */
1593                                 gnome_vfs_uri_unref (target_uri);
1594
1595                         }
1596                 }
1597
1598                 gnome_vfs_file_info_unref (info);
1599                 g_free (progress->progress_info->duplicate_name);
1600
1601                 if (result != GNOME_VFS_OK) {
1602                         break;
1603                 }
1604
1605                 gnome_vfs_uri_unref (target_dir_uri);
1606
1607                 source_item = source_item->next;
1608                 target_item = target_item->next;
1609
1610                 g_assert ((source_item != NULL) == (target_item != NULL));
1611         }
1612
1613         return result;
1614 }
1615
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)
1623 {
1624         GnomeVFSResult result;
1625         const GList *source_item, *target_item;
1626         
1627         result = GNOME_VFS_OK;
1628
1629         /* go through the list of names */
1630         for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) {
1631
1632                 GnomeVFSURI *source_uri;
1633                 GnomeVFSURI *target_uri;
1634                 GnomeVFSURI *target_dir_uri;
1635                 gboolean retry;
1636                 gboolean skip;
1637                 int conflict_count;
1638                 int progress_result;
1639
1640                 progress->progress_info->file_index++;
1641
1642                 source_uri = (GnomeVFSURI *)source_item->data;
1643                 target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data);
1644
1645                 progress->progress_info->duplicate_name =  
1646                         gnome_vfs_uri_extract_short_path_name
1647                         ((GnomeVFSURI *)target_item->data);
1648
1649                 skip = FALSE;
1650                 conflict_count = 1;
1651
1652                 do {
1653                         retry = FALSE;
1654                         target_uri = gnome_vfs_uri_append_string (target_dir_uri, 
1655                                  progress->progress_info->duplicate_name);
1656
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;
1661
1662                         /* no matter what the replace mode, just overwrite the destination
1663                          * handle_name_conflicts took care of conflicting files
1664                          */
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);
1669
1670
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;
1683
1684                                 if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1685                                         gnome_vfs_uri_unref (target_uri);
1686                                         break;
1687                                 }
1688                                 conflict_count++;
1689                                 result = GNOME_VFS_OK;
1690                                 retry = TRUE;
1691                                 continue;
1692                         }
1693
1694                         if (result != GNOME_VFS_OK) {
1695                                 retry = handle_error (&result, progress, error_mode, &skip);
1696                         }
1697
1698                         if (result == GNOME_VFS_OK 
1699                             && !skip
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);
1704                                 break;
1705                         }
1706                         gnome_vfs_uri_unref (target_uri);
1707                 } while (retry);
1708                 
1709                 gnome_vfs_uri_unref (target_dir_uri);
1710
1711                 if (result != GNOME_VFS_OK && !skip)
1712                         break;
1713
1714                 source_item = source_item->next;
1715                 target_item = target_item->next;
1716                 g_assert ((source_item != NULL) == (target_item != NULL));
1717         }
1718
1719         return result;
1720 }
1721
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)
1729 {
1730         GnomeVFSResult result;
1731         const GList *source_item, *target_item;
1732         GnomeVFSURI *source_uri;
1733         GnomeVFSURI *target_dir_uri;
1734         GnomeVFSURI *target_uri;
1735         gboolean retry;
1736         gboolean skip;
1737         int conflict_count;
1738         int progress_result;
1739         char *source_reference;
1740
1741         result = GNOME_VFS_OK;
1742
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;) {
1745
1746                 progress->progress_info->file_index++;
1747
1748                 source_uri = (GnomeVFSURI *)source_item->data;
1749                 source_reference = gnome_vfs_uri_to_string (source_uri, GNOME_VFS_URI_HIDE_NONE);
1750
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);
1755
1756                 skip = FALSE;
1757                 conflict_count = 1;
1758
1759                 do {
1760                         retry = FALSE;
1761                         target_uri = gnome_vfs_uri_append_string
1762                                 (target_dir_uri,
1763                                  progress->progress_info->duplicate_name);
1764
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);
1769
1770                         /* no matter what the replace mode, just overwrite the destination
1771                          * handle_name_conflicts took care of conflicting files
1772                          */
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;
1786
1787                                 if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1788                                         gnome_vfs_uri_unref (target_uri);
1789                                         break;
1790                                 }
1791                                 conflict_count++;
1792                                 result = GNOME_VFS_OK;
1793                                 retry = TRUE;
1794                                 continue;
1795                         }
1796                         
1797                         if (result != GNOME_VFS_OK) {
1798                                 retry = handle_error (&result, progress, error_mode, &skip);
1799                         }
1800
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);
1806                                 break;
1807                         }
1808                         gnome_vfs_uri_unref (target_uri);
1809                 } while (retry);
1810                 
1811                 gnome_vfs_uri_unref (target_dir_uri);
1812                 g_free (source_reference);
1813
1814                 if (result != GNOME_VFS_OK && !skip)
1815                         break;
1816
1817                 source_item = source_item->next;
1818                 target_item = target_item->next;
1819                 g_assert ((source_item != NULL) == (target_item != NULL));
1820         }
1821
1822         return result;
1823 }
1824
1825
1826 static GnomeVFSResult
1827 gnome_vfs_xfer_empty_directories (const GList *trash_dir_uris,
1828                                   GnomeVFSXferErrorMode error_mode,
1829                                   GnomeVFSProgressCallbackState *progress)
1830 {
1831         GnomeVFSResult result;
1832         const GList *p;
1833         gboolean skip;
1834
1835         result = GNOME_VFS_OK;
1836
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;
1841
1842
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, 
1846                         progress);
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;
1850                         break;
1851                 }
1852                 /* set up a fake total size to represent the bulk of the operation
1853                  * -- we'll subtract a proportional value for every deletion
1854                  */
1855                 progress->progress_info->bytes_total 
1856                         = progress->progress_info->files_total * DEFAULT_SIZE_OVERHEAD;
1857         }
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);
1867                 }
1868         }
1869
1870         return result;
1871 }
1872
1873
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)
1879 {
1880         GnomeVFSFileInfo *info;
1881         GnomeVFSResult result;
1882         GnomeVFSURI *uri;
1883         const GList *p;
1884         gboolean skip;
1885
1886         result = GNOME_VFS_OK;
1887         
1888         for (p = source_uri_list;  p != NULL; p = p->next) {
1889         
1890                 skip = FALSE;
1891                 /* get the URI and VFSFileInfo for each */
1892                 uri = p->data;
1893
1894                 info = gnome_vfs_file_info_new ();
1895                 result = gnome_vfs_get_file_info_uri (uri, info, 
1896                                                       GNOME_VFS_FILE_INFO_DEFAULT);
1897
1898                 if (result != GNOME_VFS_OK) {
1899                         break;
1900                 }
1901
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);
1906                 } else {
1907                         remove_file (uri, progress, xfer_options, &error_mode,
1908                                      &skip);
1909                 }
1910         }
1911
1912         return result;
1913 }
1914
1915
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)
1921 {
1922
1923         GnomeVFSResult result;
1924                 
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);
1929
1930         result = count_items_and_size (source_uri_list,
1931                 GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE, progress, FALSE, FALSE);
1932
1933         /* When deleting, ignore the real file sizes, just count the same DEFAULT_SIZE_OVERHEAD
1934          * for each file.
1935          */
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);
1942         }
1943
1944         return result;
1945 }
1946
1947
1948 static GnomeVFSResult
1949 gnome_vfs_new_directory_with_unique_name (const GnomeVFSURI *target_dir_uri,
1950                                           const char *name,
1951                                           GnomeVFSXferErrorMode error_mode,
1952                                           GnomeVFSXferOverwriteMode overwrite_mode,
1953                                           GnomeVFSProgressCallbackState *progress)
1954 {
1955         GnomeVFSResult result;
1956         GnomeVFSURI *target_uri;
1957         GnomeVFSDirectoryHandle *dest_directory_handle;
1958         gboolean dummy;
1959         int progress_result;
1960         int conflict_count;
1961         
1962         dest_directory_handle = NULL;
1963         progress->progress_info->top_level_item = TRUE;
1964         progress->progress_info->duplicate_name = g_strdup (name);
1965
1966         for (conflict_count = 1; ; conflict_count++) {
1967
1968                 target_uri = gnome_vfs_uri_append_string
1969                         (target_dir_uri, 
1970                          progress->progress_info->duplicate_name);
1971                 result = create_directory (target_uri, 
1972                                            &dest_directory_handle,
1973                                            GNOME_VFS_XFER_USE_UNIQUE_NAMES,
1974                                            &error_mode,
1975                                            &overwrite_mode,
1976                                            progress,
1977                                            &dummy);
1978
1979                 if (result != GNOME_VFS_ERROR_FILE_EXISTS
1980                         && result != GNOME_VFS_ERROR_NAME_TOO_LONG) {
1981                         break;
1982                 }
1983
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;
1993
1994                 if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1995                         break;
1996                 }
1997
1998                 gnome_vfs_uri_unref (target_uri);
1999         }
2000
2001         call_progress_uri (progress, NULL, target_uri,
2002                 GNOME_VFS_XFER_PHASE_OPENTARGET);
2003
2004         if (dest_directory_handle != NULL) {
2005                 gnome_vfs_directory_close (dest_directory_handle);
2006         }
2007
2008         gnome_vfs_uri_unref (target_uri);
2009         g_free (progress->progress_info->duplicate_name);
2010
2011         return result;
2012 }
2013
2014 static GnomeVFSResult
2015 gnome_vfs_destination_is_writable (const GnomeVFSURI *uri)
2016 {
2017         GnomeVFSURI *test_uri;
2018         GnomeVFSResult result;
2019         GnomeVFSHandle *handle;
2020
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
2024                  */
2025                 return GNOME_VFS_OK;
2026         }
2027
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);
2031
2032         if (result == GNOME_VFS_OK) {
2033                 gnome_vfs_close (handle);
2034         }
2035         
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;
2039         }
2040         
2041         /* some methods only allow certain filenames (e.g. .desktop files) */
2042         if (result == GNOME_VFS_ERROR_INVALID_URI) {
2043                 result = GNOME_VFS_OK;
2044         }
2045
2046         gnome_vfs_uri_unref (test_uri);
2047         return result;
2048 }
2049
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)
2057 {
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;
2066         gulong files_total;
2067         gboolean skip;
2068         
2069         result = GNOME_VFS_OK;
2070         move = FALSE;
2071         link = FALSE;
2072         target_dir_uri = NULL;
2073         source_uri_list_copied = NULL;
2074
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);
2083                 return result;
2084         }
2085
2086         move = (xfer_options & GNOME_VFS_XFER_REMOVESOURCE) != 0;
2087         link = (xfer_options & GNOME_VFS_XFER_LINK_ITEMS) != 0;
2088
2089         if (move && link) {
2090                 return GNOME_VFS_ERROR_BAD_PARAMETERS;
2091         }
2092
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.
2096          */
2097         source_uri_list = gnome_vfs_uri_list_copy ((GList *)source_uris);
2098         target_uri_list = gnome_vfs_uri_list_copy ((GList *)target_uris);
2099
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
2103                  */
2104                 for (source_uri = source_uri_list, target_uri = target_uri_list;
2105                         source_uri != NULL;
2106                         source_uri = source_uri->next, target_uri = target_uri->next) {
2107                         gboolean same_fs;
2108
2109                         g_assert (target_dir_uri != NULL);
2110
2111                         result = gnome_vfs_check_same_fs_uris ((GnomeVFSURI *)source_uri->data, 
2112                                 target_dir_uri, &same_fs);
2113
2114                         if (result != GNOME_VFS_OK) {
2115                                 break;
2116                         }
2117
2118                         move &= same_fs;
2119                 }
2120         }
2121
2122         if (target_dir_uri != NULL) {
2123                 gnome_vfs_uri_unref (target_dir_uri);
2124                 target_dir_uri = NULL;
2125         }
2126         
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
2133                          */
2134                         result = GNOME_VFS_OK;
2135                 }
2136         }
2137                         
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 
2141                  */                      
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);
2144
2145
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);
2151                         }
2152                 } else {
2153                         /* Errors from gnome_vfs_get_volume_free_space should be ignored */
2154                         result = GNOME_VFS_OK;
2155                 }
2156                 
2157                 if (target_dir_uri != NULL) {
2158                         gnome_vfs_uri_unref (target_dir_uri);
2159                         target_dir_uri = NULL;
2160                 }
2161
2162                 if (result != GNOME_VFS_OK) {
2163                         return result;
2164                 }
2165                         
2166                 if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0) {
2167                 
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;
2171
2172                         /* Set the preflight numbers to 0, we don't want to run progress on the
2173                          * removal of conflicting items.
2174                          */
2175                         progress->progress_info->bytes_total = 0;
2176                         progress->progress_info->files_total = 0;
2177                         
2178                         result = handle_name_conflicts (&source_uri_list, &target_uri_list,
2179                                                         xfer_options, &error_mode, &overwrite_mode,
2180                                                         progress);
2181                                                         
2182                         progress->progress_info->bytes_total = bytes_total;
2183                         progress->progress_info->files_total = files_total;
2184
2185                 }
2186
2187                 /* reset the preflight numbers */
2188                 progress->progress_info->file_index = 0;
2189                 progress->progress_info->total_bytes_copied = 0;
2190
2191                 if (result != GNOME_VFS_OK) {
2192                         /* don't care about any results from handle_error */
2193                         handle_error (&result, progress, &error_mode, &skip);
2194
2195                         /* whatever error it was, we handled it */
2196                         result = GNOME_VFS_OK;
2197                 } else {
2198                         call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO);
2199
2200                         if (move) {
2201                                 g_assert (!link);
2202                                 result = move_items (source_uri_list, target_uri_list,
2203                                                      xfer_options, &error_mode, &overwrite_mode, 
2204                                                      progress);
2205                         } else if (link) {
2206                                 result = link_items (source_uri_list, target_uri_list,
2207                                                      xfer_options, &error_mode, &overwrite_mode,
2208                                                      progress);
2209                         } else {
2210                                 result = copy_items (source_uri_list, target_uri_list,
2211                                                      xfer_options, &error_mode, overwrite_mode, 
2212                                                      progress, &source_uri_list_copied);
2213                         }
2214                         
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);
2222                                 }
2223                         }
2224                 }
2225         }
2226
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);
2230
2231         return result;
2232 }
2233
2234 GnomeVFSResult
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,
2241                         gpointer data,
2242                         GnomeVFSXferProgressCallback sync_progress_callback,
2243                         gpointer sync_progress_data)
2244 {
2245         GnomeVFSProgressCallbackState progress_state;
2246         GnomeVFSXferProgressInfo progress_info;
2247         GnomeVFSURI *target_dir_uri;
2248         GnomeVFSResult result;
2249         char *short_name;
2250         
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;
2256
2257
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);
2262                 
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);
2269                                 
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);
2277
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);
2286                 }
2287         } else {
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));
2292
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);
2296         }
2297
2298         call_progress (&progress_state, GNOME_VFS_XFER_PHASE_COMPLETED);
2299         free_progress (&progress_info);
2300
2301         /* FIXME bugzilla.eazel.com 1218:
2302          * 
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.
2306          * 
2307          * Should fix this in the async job call setup.
2308          * 
2309          * For now just pretend everything worked well.
2310          * 
2311          */
2312         result = GNOME_VFS_OK;
2313
2314         return result;
2315 }
2316
2317 /**
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 
2323  * move.  
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
2326  * interuption.
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
2332  *
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.
2336  *
2337  * Returns: If all goes well it returns GNOME_VFS_OK.  Check GnomeVFSResult for
2338  * other values.
2339  **/
2340 GnomeVFSResult
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,
2347                          gpointer data)
2348 {
2349         GnomeVFSProgressCallbackState progress_state;
2350         GnomeVFSXferProgressInfo progress_info;
2351         GnomeVFSResult result;
2352
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);  
2357                 
2358         init_progress (&progress_state, &progress_info);
2359         progress_state.sync_callback = progress_callback;
2360         progress_state.user_data = data;
2361
2362         call_progress (&progress_state, GNOME_VFS_XFER_PHASE_INITIAL);
2363
2364         result = gnome_vfs_xfer_uri_internal (source_uri_list, target_uri_list,
2365                 xfer_options, error_mode, overwrite_mode, &progress_state);
2366
2367         call_progress (&progress_state, GNOME_VFS_XFER_PHASE_COMPLETED);
2368         free_progress (&progress_info);
2369
2370         return result;
2371 }
2372
2373 /**
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
2379  * one copy.
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
2387  * source URI.
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
2409  * worry be happy.
2410  * GNOME_VFS_XFER_OVERWRITE_MODE_SKIP: Skip duplicate files silenty.
2411  * target.
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
2415  * 
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
2421  * process.
2422  *
2423  * Return value: An integer representing the result of the operation.
2424  *
2425  **/
2426 GnomeVFSResult  
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,
2433                     gpointer data)
2434 {
2435         GList *source_uri_list, *target_uri_list;
2436         GnomeVFSResult result;
2437
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);  
2442
2443         source_uri_list = g_list_append (NULL, (void *)source_uri);
2444         target_uri_list = g_list_append (NULL, (void *)target_uri);
2445
2446         result = gnome_vfs_xfer_uri_list (source_uri_list, target_uri_list,
2447                 xfer_options, error_mode, overwrite_mode, progress_callback, data);
2448
2449         g_list_free (source_uri_list);
2450         g_list_free (target_uri_list);
2451
2452         return result;
2453 }
2454
2455 /**
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
2462  *
2463  * Unlink items in the list @source_uri_list from their filesystems.
2464  *
2465  * Return value: %GNOME_VFS_OK if successful, or the appropriate error code otherwise
2466  **/
2467 GnomeVFSResult 
2468 gnome_vfs_xfer_delete_list (const GList *source_uri_list, 
2469                             GnomeVFSXferErrorMode error_mode,
2470                             GnomeVFSXferOptions xfer_options,
2471                             GnomeVFSXferProgressCallback
2472                                    progress_callback,
2473                             gpointer data)
2474 {
2475         GnomeVFSProgressCallbackState progress_state;
2476         GnomeVFSXferProgressInfo progress_info;
2477         GnomeVFSResult result;
2478
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);
2482
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);
2487
2488         result = gnome_vfs_xfer_delete_items (source_uri_list, error_mode, xfer_options,
2489                 &progress_state);
2490         
2491         call_progress (&progress_state, GNOME_VFS_XFER_PHASE_COMPLETED);
2492         free_progress (&progress_info);
2493
2494         return result;
2495 }
2496