source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-xfer.c @ 15497

Revision 15497, 61.7 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15496, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* gnome-vfs-xfer.c - File transfers in the GNOME Virtual File System.
3
4   Copyright (C) 1999 Free Software Foundation
5
6   The Gnome Library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10
11   The Gnome Library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15
16   You should have received a copy of the GNU Library General Public
17   License along with the Gnome Library; see the file COPYING.LIB.  If not,
18   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19   Boston, MA 02111-1307, USA.
20
21   Authors:
22   Ettore Perazzoli <ettore@comm2000.it>
23   Pavel Cisler <pavel@eazel.com>
24   */
25
26/* FIXME bugzilla.eazel.com 1197:
27   Check that all the flags passed by address are set at least once by
28   functions that are expected to set them.  */
29/* FIXME bugzilla.eazel.com 1198:
30   There should be a concept of a `context' in the file methods that would
31   allow us to set a prefix URI for all the operations.  This way we can
32   greatly optimize access to "strange" file systems.  */
33
34#ifdef HAVE_CONFIG_H
35#include <config.h>
36#endif
37
38#include <sys/time.h>
39#include <string.h>
40
41#include "gnome-vfs.h"
42#include "gnome-vfs-private.h"
43#include "gnome-vfs-private-types.h"
44
45/* Implementation of file transfers (`gnome_vfs_xfer*()' functions).  */
46
47static GnomeVFSResult   remove_directory        (GnomeVFSURI *uri,
48                                                 gboolean recursive,
49                                                 GnomeVFSProgressCallbackState *progress,
50                                                 GnomeVFSXferOptions xfer_options,
51                                                 GnomeVFSXferErrorMode *error_mode,
52                                                 gboolean *skip);
53
54
55enum {
56        /* size used for accounting for the expense of copying directories,
57         * doing a move operation, etc. in a progress callback
58         * (during a move the file size is used for a regular file).
59         */
60        DEFAULT_SIZE_OVERHEAD = 1024
61};
62
63/* in asynch mode the progress callback does a context switch every time
64 * it gets called. We'll only call it every now and then to not loose a
65 * lot of performance
66 */
67#define UPDATE_PERIOD ((gint64) (100 * 1000))
68
69static gint64
70system_time (void)
71{
72        struct timeval time_of_day;
73
74        gettimeofday (&time_of_day, NULL);
75        return (gint64) time_of_day.tv_usec + ((gint64) time_of_day.tv_sec) * 1000000;
76}
77
78static void
79init_progress (GnomeVFSProgressCallbackState *progress_state,
80               GnomeVFSXferProgressInfo *progress_info)
81{
82        progress_info->source_name = NULL;
83        progress_info->target_name = NULL;
84        progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
85        progress_info->vfs_status = GNOME_VFS_OK;
86        progress_info->phase = GNOME_VFS_XFER_PHASE_INITIAL;
87        progress_info->file_index = 1;
88        progress_info->files_total = 0;
89        progress_info->bytes_total = 0;
90        progress_info->file_size = 0;
91        progress_info->bytes_copied = 0;
92        progress_info->total_bytes_copied = 0;
93        progress_info->duplicate_name = NULL;
94        progress_info->top_level_item = FALSE;
95
96        progress_state->progress_info = progress_info;
97        progress_state->sync_callback = NULL;
98        progress_state->update_callback = NULL;
99        progress_state->async_job_data = NULL;
100        progress_state->next_update_callback_time = 0;
101        progress_state->next_text_update_callback_time = 0;
102        progress_state->update_callback_period = UPDATE_PERIOD;
103}
104
105static void
106free_progress (GnomeVFSXferProgressInfo *progress_info)
107{
108        g_free (progress_info->source_name);
109        progress_info->source_name = NULL;
110        g_free (progress_info->target_name);
111        progress_info->target_name = NULL;
112}
113
114static void
115progress_set_source_target_uris (GnomeVFSProgressCallbackState *progress,
116              const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri)
117{
118        g_free (progress->progress_info->source_name);
119        progress->progress_info->source_name = source_uri ? gnome_vfs_uri_to_string (source_uri,
120                                                       GNOME_VFS_URI_HIDE_NONE) : NULL;
121        g_free (progress->progress_info->target_name);
122        progress->progress_info->target_name = dest_uri ? gnome_vfs_uri_to_string (dest_uri,
123                                                       GNOME_VFS_URI_HIDE_NONE) : NULL;
124
125}
126
127static int
128call_progress (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase)
129{
130        int result;
131
132        /* FIXME bugzilla.eazel.com 1199: should use proper progress result values from an enum here */
133
134        result = 0;
135        progress_set_source_target_uris (progress, NULL, NULL);
136
137        progress->next_update_callback_time = system_time () + progress->update_callback_period;
138       
139        progress->progress_info->phase = phase;
140
141        if (progress->sync_callback != NULL)
142                result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
143
144        if (progress->update_callback != NULL)
145                result = (* progress->update_callback) (progress->progress_info,
146                progress->async_job_data);
147
148        return result; 
149}
150
151static GnomeVFSXferErrorAction
152call_progress_with_current_names (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase)
153{
154        int result;
155
156        result = GNOME_VFS_XFER_ERROR_ACTION_ABORT;
157
158        progress->next_update_callback_time = system_time () + progress->update_callback_period;
159        progress->next_update_callback_time = progress->next_text_update_callback_time;
160       
161        progress->progress_info->phase = phase;
162
163        if (progress->sync_callback != NULL)
164                result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
165
166        if (progress->update_callback != NULL)
167                result = (* progress->update_callback) (progress->progress_info,
168                progress->async_job_data);
169
170        return result; 
171}
172
173static int
174call_progress_uri (GnomeVFSProgressCallbackState *progress,
175                   const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri,
176                   GnomeVFSXferPhase phase)
177{
178        int result;
179
180        result = 0;
181        progress_set_source_target_uris (progress, source_uri, dest_uri);
182
183        progress->next_text_update_callback_time = system_time () + progress->update_callback_period;
184        progress->next_update_callback_time = progress->next_text_update_callback_time;
185       
186        progress->progress_info->phase = phase;
187
188        if (progress->sync_callback != NULL)
189                result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
190
191        if (progress->update_callback != NULL)
192                result = (* progress->update_callback) (progress->progress_info, progress->async_job_data);
193
194        return result; 
195}
196
197static int
198call_progress_often (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase)
199{
200        int result;
201        gint64 now;
202
203        result = 1;
204        now = system_time ();
205
206        progress->progress_info->phase = phase;
207
208        if (progress->sync_callback != NULL) {
209                result = (* progress->sync_callback) (progress->progress_info, progress->user_data);
210        }
211
212        if (now < progress->next_update_callback_time) {
213                return result;
214        }
215
216        progress->next_update_callback_time = now + progress->update_callback_period;
217        if (progress->update_callback != NULL) {
218                result = (* progress->update_callback) (progress->progress_info, progress->async_job_data);
219        }
220
221        return result;
222}
223
224static int
225call_progress_with_uris_often (GnomeVFSProgressCallbackState *progress,
226                   const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri,
227                   GnomeVFSXferPhase phase)
228{
229        int result;
230        gint64 now;
231
232        progress_set_source_target_uris (progress, source_uri, dest_uri);
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        if (now < progress->next_text_update_callback_time)
242                return result;
243
244        progress->next_text_update_callback_time = now + progress->update_callback_period;
245       
246        if (progress->update_callback != NULL)
247                result = (* progress->update_callback) (progress->progress_info, progress->async_job_data);
248
249        return result;
250}
251
252/* Handle an error condition according to `error_mode'.  Returns `TRUE' if the
253 * operation should be retried, `FALSE' otherwise.  `*result' will be set to
254 * the result value we want to be returned to the caller of the xfer
255 * function.
256 */
257static gboolean
258handle_error (GnomeVFSResult *result,
259              GnomeVFSProgressCallbackState *progress,
260              GnomeVFSXferErrorMode *error_mode,
261              gboolean *skip)
262{
263        GnomeVFSXferErrorAction action;
264
265        *skip = FALSE;
266
267        switch (*error_mode) {
268        case GNOME_VFS_XFER_ERROR_MODE_ABORT:
269                return FALSE;
270
271        case GNOME_VFS_XFER_ERROR_MODE_QUERY:
272                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR;
273                progress->progress_info->vfs_status = *result;
274                action = call_progress_with_current_names (progress, progress->progress_info->phase);
275                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
276
277                switch (action) {
278                case GNOME_VFS_XFER_ERROR_ACTION_RETRY:
279                        return TRUE;
280                case GNOME_VFS_XFER_ERROR_ACTION_ABORT:
281                        *result = GNOME_VFS_ERROR_INTERRUPTED;
282                        return FALSE;
283                case GNOME_VFS_XFER_ERROR_ACTION_SKIP:
284                        *result = GNOME_VFS_OK;
285                        *skip = TRUE;
286                        return FALSE;
287                }
288                break;
289        }
290
291        *skip = FALSE;
292        return FALSE;
293}
294
295/* This is conceptually similiar to the previous `handle_error()' function, but
296 * handles the overwrite case.
297 */
298static gboolean
299handle_overwrite (GnomeVFSResult *result,
300                  GnomeVFSProgressCallbackState *progress,
301                  GnomeVFSXferErrorMode *error_mode,
302                  GnomeVFSXferOverwriteMode *overwrite_mode,
303                  gboolean *replace,
304                  gboolean *skip)
305{
306        GnomeVFSXferOverwriteAction action;
307
308        switch (*overwrite_mode) {
309        case GNOME_VFS_XFER_OVERWRITE_MODE_ABORT:
310                *replace = FALSE;
311                *result = GNOME_VFS_ERROR_FILE_EXISTS;
312                *skip = FALSE;
313                return FALSE;
314        case GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE:
315                *replace = TRUE;
316                *skip = FALSE;
317                return TRUE;
318        case GNOME_VFS_XFER_OVERWRITE_MODE_SKIP:
319                *replace = FALSE;
320                *skip = TRUE;
321                return FALSE;
322        case GNOME_VFS_XFER_OVERWRITE_MODE_QUERY:
323                progress->progress_info->vfs_status = *result;
324                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE;
325                action = call_progress_with_current_names (progress, progress->progress_info->phase);
326                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
327
328                switch (action) {
329                case GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT:
330                        *replace = FALSE;
331                        *result = GNOME_VFS_ERROR_FILE_EXISTS;
332                        *skip = FALSE;
333                        return FALSE;
334                case GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE:
335                        *replace = TRUE;
336                        *skip = FALSE;
337                        return TRUE;
338                case GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE_ALL:
339                        *replace = TRUE;
340                        *overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
341                        *skip = FALSE;
342                        return TRUE;
343                case GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP:
344                        *replace = FALSE;
345                        *skip = TRUE;
346                        return FALSE;
347                case GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP_ALL:
348                        *replace = FALSE;
349                        *skip = TRUE;
350                        *overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_SKIP;
351                        return FALSE;
352                }
353        }
354
355        *replace = FALSE;
356        *skip = FALSE;
357        return FALSE;
358}
359
360static GnomeVFSResult
361remove_file (GnomeVFSURI *uri,
362             GnomeVFSProgressCallbackState *progress,
363             GnomeVFSXferOptions xfer_options,
364             GnomeVFSXferErrorMode *error_mode,
365             gboolean *skip)
366{
367        GnomeVFSResult result;
368        gboolean retry;
369
370        do {
371                retry = FALSE;
372
373                result = gnome_vfs_unlink_from_uri (uri);
374                if (result != GNOME_VFS_OK)
375                        retry = handle_error (&result, progress,
376                                              error_mode, skip);
377                else {
378                        progress->progress_info->file_index++;
379                        /* add some small size for each deleted item because delete overhead
380                         * does not depend on file/directory size
381                         */
382                        progress->progress_info->bytes_copied += DEFAULT_SIZE_OVERHEAD;
383                        if (call_progress_with_uris_often (progress, uri, NULL,
384                                                           GNOME_VFS_XFER_PHASE_DELETESOURCE)
385                                == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
386                                result = GNOME_VFS_ERROR_INTERRUPTED;
387                                break;
388                        }
389                }
390
391
392        } while (retry);
393
394        return result;
395}
396
397static GnomeVFSResult
398empty_directory (GnomeVFSURI *uri,
399                 GnomeVFSProgressCallbackState *progress,
400                 GnomeVFSXferOptions xfer_options,
401                 GnomeVFSXferErrorMode *error_mode,
402                 gboolean *skip)
403{
404        GnomeVFSResult result;
405        GnomeVFSDirectoryHandle *directory_handle;
406        gboolean retry;
407
408
409        *skip = FALSE;
410        do {
411                result = gnome_vfs_directory_open_from_uri (&directory_handle, uri,
412                                                            GNOME_VFS_FILE_INFO_DEFAULT,
413                                                            NULL);
414                retry = FALSE;
415                if (result != GNOME_VFS_OK) {
416                        retry = handle_error (&result, progress,
417                                              error_mode, skip);
418                }
419        } while (retry);
420
421        if (result != GNOME_VFS_OK || *skip) {
422                return result;
423        }
424       
425        for (;;) {
426                GnomeVFSFileInfo info;
427                GnomeVFSURI *item_uri;
428               
429                gnome_vfs_file_info_init (&info);
430                result = gnome_vfs_directory_read_next (directory_handle, &info);
431                if (result != GNOME_VFS_OK)
432                        break;
433
434                /* Skip "." and "..".  */
435                if (strcmp (info.name, ".") == 0
436                    || strcmp (info.name, "..") == 0) {
437                        continue;
438                }
439
440                item_uri = gnome_vfs_uri_append_file_name (uri, info.name);
441               
442                if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
443                        result = remove_directory (item_uri, TRUE,
444                                            progress, xfer_options, error_mode,
445                                            skip);
446                } else {
447                        result = remove_file (item_uri, progress,
448                                            xfer_options, error_mode,
449                                            skip);
450                }
451
452                gnome_vfs_uri_unref (item_uri);
453
454                if (result != GNOME_VFS_OK)
455                        break;
456        }
457        gnome_vfs_directory_close (directory_handle);
458
459        if (result == GNOME_VFS_ERROR_EOF)
460                result = GNOME_VFS_OK;
461
462        return result;
463}
464
465typedef struct {
466        const GnomeVFSURI *base_uri;
467        GList *uri_list;
468} PrependOneURIParams;
469
470static gboolean
471PrependOneURIToList (const gchar *rel_path, GnomeVFSFileInfo *info,
472        gboolean recursing_will_loop, gpointer cast_to_params, gboolean *recurse)
473{
474        PrependOneURIParams *params;
475
476        params = (PrependOneURIParams *)cast_to_params;
477        params->uri_list = g_list_prepend (params->uri_list, gnome_vfs_uri_append_string (
478                params->base_uri, rel_path));
479
480        if (recursing_will_loop) {
481                return FALSE;
482        }
483        *recurse = TRUE;
484        return TRUE;
485}
486
487static GnomeVFSResult
488non_recursive_empty_directory (GnomeVFSURI *directory_uri,
489                               GnomeVFSProgressCallbackState *progress,
490                               GnomeVFSXferOptions xfer_options,
491                               GnomeVFSXferErrorMode *error_mode,
492                               gboolean *skip)
493{
494        /* Used as a fallback when we run out of file descriptors during
495         * a deep recursive deletion.
496         * We instead compile a flat list of URIs, doing a recursion that does not
497         * keep directories open.
498         */
499
500        GList *uri_list;
501        GList *p;
502        GnomeVFSURI *uri;
503        GnomeVFSResult result;
504        GnomeVFSFileInfo info;
505        PrependOneURIParams visit_params;
506
507        /* Build up the list so that deep items appear before their parents
508         * so that we can delete directories, children first.
509         */
510        visit_params.base_uri = directory_uri;
511        visit_params.uri_list = NULL;
512        result = gnome_vfs_directory_visit_uri (directory_uri, GNOME_VFS_FILE_INFO_DEFAULT,
513                NULL, GNOME_VFS_DIRECTORY_VISIT_SAMEFS | GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK,
514                PrependOneURIToList, &visit_params);
515
516        uri_list = visit_params.uri_list;
517
518        if (result == GNOME_VFS_OK) {
519                for (p = uri_list; p != NULL; p = p->next) {
520
521                        uri = (GnomeVFSURI *)p->data;
522                        gnome_vfs_file_info_init (&info);
523                        result = gnome_vfs_get_file_info_uri (uri, &info, GNOME_VFS_FILE_INFO_DEFAULT);
524                        if (result == GNOME_VFS_OK) {
525                                if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
526                                        result = remove_directory (uri, FALSE,
527                                                progress, xfer_options, error_mode, skip);
528                                } else {
529                                        result = remove_file (uri, progress,
530                                                xfer_options, error_mode, skip);
531                                }
532                        }
533                        gnome_vfs_file_info_clear (&info);
534                }
535        }
536
537        gnome_vfs_uri_list_free (uri_list);
538
539        return result;
540}
541
542static GnomeVFSResult
543remove_directory (GnomeVFSURI *uri,
544                  gboolean recursive,
545                  GnomeVFSProgressCallbackState *progress,
546                  GnomeVFSXferOptions xfer_options,
547                  GnomeVFSXferErrorMode *error_mode,
548                  gboolean *skip)
549{
550        GnomeVFSResult result;
551        gboolean retry;
552
553        result = GNOME_VFS_OK;
554
555        if (recursive) {
556                result = empty_directory (uri, progress, xfer_options, error_mode, skip);
557                if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) {
558                        result = non_recursive_empty_directory (uri, progress, xfer_options,
559                                error_mode, skip);
560                }
561        }
562
563        if (result == GNOME_VFS_ERROR_EOF)
564                result = GNOME_VFS_OK;
565
566        if (result == GNOME_VFS_OK) {
567                do {
568                        retry = FALSE;
569
570                        result = gnome_vfs_remove_directory_from_uri (uri);
571                        if (result != GNOME_VFS_OK) {
572                                retry = handle_error (&result, progress,
573                                                      error_mode, skip);
574                        } else {
575                                progress->progress_info->file_index++;
576                                /* add some small size for each deleted item */
577                                progress->progress_info->bytes_total += DEFAULT_SIZE_OVERHEAD;
578                                progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
579
580                                if (call_progress_with_uris_often (progress, uri, NULL,
581                                        GNOME_VFS_XFER_PHASE_DELETESOURCE)
582                                        == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
583                                        result = GNOME_VFS_ERROR_INTERRUPTED;
584                                        break;
585                                }
586                        }
587
588                } while (retry);
589        }
590
591        return result;
592}
593
594/* iterates the list of names in a given directory, applies @callback on each,
595 * optionaly recurses into directories
596 */
597static GnomeVFSResult
598gnome_vfs_visit_list (const GList *name_uri_list,
599                      GnomeVFSFileInfoOptions info_options,
600                      GnomeVFSDirectoryVisitOptions visit_options,
601                      gboolean recursive,
602                      GnomeVFSDirectoryVisitFunc callback,
603                      gpointer data)
604{
605        GnomeVFSResult result;
606        const GList *p;
607        GnomeVFSURI *uri;
608        GnomeVFSFileInfo info;
609       
610        result = GNOME_VFS_OK;
611
612        /* go through our list of items */
613        for (p = name_uri_list; p != NULL; p = p->next) {
614               
615                /* get the URI and VFSFileInfo for each */
616                uri = p->data;
617                gnome_vfs_file_info_init (&info);
618                result = gnome_vfs_get_file_info_uri (uri, &info, info_options);
619               
620                if (result == GNOME_VFS_OK) {
621                        gboolean tmp_recurse;
622                       
623                        tmp_recurse = TRUE;
624                       
625                        /* call our callback on each item*/
626                        if (!callback (p->data, &info, FALSE, data, &tmp_recurse))
627                                result = GNOME_VFS_ERROR_INTERRUPTED;
628                       
629                        if (result == GNOME_VFS_OK
630                            && recursive
631                            && info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
632                                /* let gnome_vfs_directory_visit_uri call our callback
633                                 * recursively
634                                 */
635                                result = gnome_vfs_directory_visit_uri (uri, info_options,
636                                        NULL, visit_options, callback, data);
637                        }
638                }               
639                if (result != GNOME_VFS_OK)
640                        break;
641        }
642        return result;
643}
644
645typedef struct {
646        GnomeVFSProgressCallbackState *progress;
647        GnomeVFSResult result;
648} CountEachFileSizeParams;
649
650/* iteratee for count_items_and_size */
651static gboolean
652count_each_file_size_one (const gchar *rel_path,
653                          GnomeVFSFileInfo *info,
654                          gboolean recursing_will_loop,
655                          gpointer data,
656                          gboolean *recurse)
657{
658        CountEachFileSizeParams *params;
659
660        params = (CountEachFileSizeParams *)data;
661
662        if (call_progress_often (params->progress, params->progress->progress_info->phase) == 0) {
663                /* progress callback requested to stop */
664                params->result = GNOME_VFS_ERROR_INTERRUPTED;
665                *recurse = FALSE;
666                return FALSE;
667        }
668
669        /* count each file, folder, symlink */
670        params->progress->progress_info->files_total++;
671        if (info->type == GNOME_VFS_FILE_TYPE_REGULAR) {
672                /* add each file size */
673                params->progress->progress_info->bytes_total += info->size;
674        } else if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
675                /* add some small size for each directory */
676                params->progress->progress_info->bytes_total += DEFAULT_SIZE_OVERHEAD;
677                params->progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
678        }
679
680        /* watch out for infinite recursion*/
681        if (recursing_will_loop) {
682                params->result = GNOME_VFS_ERROR_LOOP;
683                return FALSE;
684        }
685
686        *recurse = TRUE;
687
688        return TRUE;
689}
690
691/* calculate the number of items and their total size; used as a preflight
692 * before the transfer operation starts
693 */
694static GnomeVFSResult
695count_items_and_size (const GList *name_uri_list,
696                      GnomeVFSXferOptions xfer_options,
697                      GnomeVFSProgressCallbackState *progress,
698                      gboolean move,
699                      gboolean link)
700{
701        /*
702         * FIXME bugzilla.eazel.com 1200:
703         * Deal with errors here, respond to skip by pulling items from the name list
704         */
705        GnomeVFSFileInfoOptions info_options;
706        GnomeVFSDirectoryVisitOptions visit_options;
707        CountEachFileSizeParams each_params;
708
709        /* initialize the results */
710        progress->progress_info->files_total = 0;
711        progress->progress_info->bytes_total = 0;
712
713        /* set up the params for recursion */
714        visit_options = GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK;
715        if (xfer_options & GNOME_VFS_XFER_SAMEFS)
716                visit_options |= GNOME_VFS_DIRECTORY_VISIT_SAMEFS;
717        each_params.progress = progress;
718        each_params.result = GNOME_VFS_OK;
719
720        if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS) {
721                info_options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS;
722        } else {
723                info_options = GNOME_VFS_FILE_INFO_DEFAULT;
724        }
725
726        return gnome_vfs_visit_list (name_uri_list, info_options,
727                visit_options, !link && !move && (xfer_options & GNOME_VFS_XFER_RECURSIVE) != 0,
728                count_each_file_size_one, &each_params);
729}
730
731/* calculate the number of items and their total size; used as a preflight
732 * before the transfer operation starts
733 */
734static GnomeVFSResult
735directory_add_items_and_size (GnomeVFSURI *dir_uri,
736                              GnomeVFSXferOptions xfer_options,
737                              GnomeVFSProgressCallbackState *progress)
738{
739        GnomeVFSFileInfoOptions info_options;
740        GnomeVFSDirectoryVisitOptions visit_options;
741        CountEachFileSizeParams each_params;
742
743        /* set up the params for recursion */
744        visit_options = GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK;
745        if (xfer_options & GNOME_VFS_XFER_SAMEFS)
746                visit_options |= GNOME_VFS_DIRECTORY_VISIT_SAMEFS;
747        each_params.progress = progress;
748        each_params.result = GNOME_VFS_OK;
749
750        if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS) {
751                info_options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS;
752        } else {
753                info_options = GNOME_VFS_FILE_INFO_DEFAULT;
754        }
755
756        return gnome_vfs_directory_visit_uri (dir_uri, info_options, NULL,
757                visit_options, count_each_file_size_one, &each_params);
758
759}
760
761/* Compares the list of files about to be created by a transfer with
762 * any possible existing files with conflicting names in the target directory.
763 * Handles conflicts, optionaly removing the conflicting file/directory
764 */
765static GnomeVFSResult
766handle_name_conflicts (GList **source_uri_list,
767                       GList **target_uri_list,
768                       GnomeVFSXferOptions xfer_options,
769                       GnomeVFSXferErrorMode *error_mode,
770                       GnomeVFSXferOverwriteMode *overwrite_mode,
771                       GnomeVFSProgressCallbackState *progress)
772{
773        GnomeVFSResult result;
774        GList *source_item;
775        GList *target_item;
776
777        int conflict_count; /* values are 0, 1, many */
778       
779        result = GNOME_VFS_OK;
780        conflict_count = 0;
781
782        /* Go through the list of names, find out if there is 0, 1 or more conflicts. */
783        for (target_item = *target_uri_list; target_item != NULL;
784             target_item = target_item->next) {
785                if (gnome_vfs_uri_exists ((GnomeVFSURI *)target_item->data)) {
786                        conflict_count++;
787                        if (conflict_count > 1)
788                                break;
789                }
790        }
791
792        if (conflict_count == 0) {
793                /* No conflicts, we are done. */
794                return GNOME_VFS_OK;
795        }
796
797        /* Pass in the conflict count so that we can decide to present the Replace All
798         * for multiple conflicts.
799         */
800        progress->progress_info->duplicate_count = conflict_count;
801
802       
803        /* Go through the list of names again, present overwrite alerts for each. */
804        for (target_item = *target_uri_list, source_item = *source_uri_list;
805             target_item != NULL;) {
806                gboolean replace;
807                gboolean skip;
808                GnomeVFSURI *uri;
809                GnomeVFSFileInfo info;
810               
811                skip = FALSE;
812                uri = (GnomeVFSURI *)target_item->data;
813                if (gnome_vfs_uri_exists (uri)) {
814                        progress_set_source_target_uris (progress, NULL, uri);
815                         
816                        /* no error getting info -- file exists, ask what to do */
817                        replace = handle_overwrite (&result, progress, error_mode,
818                                overwrite_mode, &replace, &skip);
819
820                        /* FIXME bugzilla.eazel.com 1207:
821                         * move items to Trash here
822                         */
823
824                        /* get rid of the conflicting file */
825                        if (replace) {
826                                gnome_vfs_file_info_init (&info);
827                                gnome_vfs_get_file_info_uri (uri, &info, GNOME_VFS_FILE_INFO_DEFAULT);
828                                if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
829                                        remove_directory (uri, TRUE, progress,
830                                                xfer_options, error_mode, &skip);
831                                } else {
832                                        remove_file (uri, progress,
833                                                xfer_options, error_mode, &skip);
834                                }
835                                gnome_vfs_file_info_clear (&info);
836                        }
837                }
838
839               
840                if (result != GNOME_VFS_OK) {
841                        break;
842                }
843
844                if (skip) {
845                        /* skipping a file, remove it's name from the source and target name
846                         * lists.
847                         */
848                        GList *source_item_to_remove;
849                        GList *target_item_to_remove;
850
851                        source_item_to_remove = source_item;
852                        target_item_to_remove = target_item;
853                       
854                        source_item = source_item->next;
855                        target_item = target_item->next;
856
857                        gnome_vfs_uri_unref ((GnomeVFSURI *)source_item_to_remove->data);
858                        gnome_vfs_uri_unref ((GnomeVFSURI *)target_item_to_remove->data);
859                        *source_uri_list = g_list_remove_link (*source_uri_list, source_item_to_remove);
860                        *target_uri_list = g_list_remove_link (*target_uri_list, target_item_to_remove);
861                       
862                        continue;
863                }
864
865
866                target_item = target_item->next;
867                source_item = source_item->next;
868        }
869
870        return result;
871}
872
873/* Create new directory. If GNOME_VFS_XFER_USE_UNIQUE_NAMES is set,
874 * return with an error if a name conflict occurs, else
875 * handle the overwrite.
876 * On success, opens the new directory
877 */
878static GnomeVFSResult
879create_directory (GnomeVFSURI *dir_uri,
880                  GnomeVFSDirectoryHandle **return_handle,
881                  GnomeVFSXferOptions xfer_options,
882                  GnomeVFSXferErrorMode *error_mode,
883                  GnomeVFSXferOverwriteMode *overwrite_mode,
884                  GnomeVFSProgressCallbackState *progress,
885                  gboolean *skip)
886{
887        GnomeVFSResult result;
888        gboolean retry;
889       
890        *skip = FALSE;
891        do {
892                retry = FALSE;
893
894                result = gnome_vfs_make_directory_for_uri (dir_uri, 0700);
895
896                if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
897                        gboolean force_replace;
898
899                        if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) != 0) {
900                                /* just let the caller pass a unique name*/
901                                return result;
902                        }
903
904                        retry = handle_overwrite (&result,
905                                                  progress,
906                                                  error_mode,
907                                                  overwrite_mode,
908                                                  &force_replace,
909                                                  skip);
910
911                        if (*skip) {
912                                return GNOME_VFS_OK;
913                        }
914                        if (force_replace) {
915                                result = remove_directory (dir_uri, TRUE, progress,
916                                                           xfer_options, error_mode,
917                                                           skip);
918                        } else {
919                                result = GNOME_VFS_OK;
920                        }
921                }
922
923                if (result == GNOME_VFS_OK) {
924                        return gnome_vfs_directory_open_from_uri (return_handle,
925                                                                  dir_uri,
926                                                                  GNOME_VFS_FILE_INFO_DEFAULT,
927                                                                  NULL);
928                }
929                /* handle the error case */
930                retry = handle_error (&result, progress,
931                                      error_mode, skip);
932
933                if (*skip) {
934                        return GNOME_VFS_OK;
935                }
936
937        } while (retry);
938
939        return result;
940}
941
942/* Copy the data of a single file. */
943static GnomeVFSResult
944copy_file_data (GnomeVFSHandle *target_handle,
945                GnomeVFSHandle *source_handle,
946                GnomeVFSProgressCallbackState *progress,
947                GnomeVFSXferOptions xfer_options,
948                GnomeVFSXferErrorMode *error_mode,
949                guint block_size,
950                gboolean *skip)
951{
952        GnomeVFSResult result;
953        gpointer buffer;
954
955        *skip = FALSE;
956
957        if (call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING) == 0) {
958                return GNOME_VFS_ERROR_INTERRUPTED;
959        }
960
961        buffer = g_malloc (block_size);
962
963        do {
964                GnomeVFSFileSize bytes_read;
965                GnomeVFSFileSize bytes_to_write;
966                GnomeVFSFileSize bytes_written;
967                gboolean retry;
968
969                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
970                progress->progress_info->vfs_status = GNOME_VFS_OK;
971
972                progress->progress_info->phase = GNOME_VFS_XFER_PHASE_READSOURCE;
973
974                do {
975                        retry = FALSE;
976
977                        result = gnome_vfs_read (source_handle, buffer,
978                                                 block_size, &bytes_read);
979                        if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF)
980                                retry = handle_error (&result, progress,
981                                                      error_mode, skip);
982                } while (retry && bytes_read > 0 && result != GNOME_VFS_ERROR_EOF);
983
984                if (result != GNOME_VFS_OK || bytes_read == 0 || *skip) {
985                        break;
986                }
987
988                bytes_to_write = bytes_read;
989
990                progress->progress_info->phase = GNOME_VFS_XFER_PHASE_WRITETARGET;
991
992                do {
993                        retry = FALSE;
994
995                        result = gnome_vfs_write (target_handle, buffer,
996                                                  bytes_to_write,
997                                                  &bytes_written);
998
999                        if (result != GNOME_VFS_OK) {
1000                                retry = handle_error (&result, progress, error_mode, skip);
1001                        }
1002
1003                        bytes_to_write -= bytes_written;
1004                } while (retry || bytes_to_write > 0);
1005
1006                progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COPYING;
1007                progress->progress_info->bytes_copied += bytes_read;
1008                progress->progress_info->total_bytes_copied += bytes_read;
1009
1010                if (call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING) == 0) {
1011                        g_free (buffer);
1012                        return GNOME_VFS_ERROR_INTERRUPTED;
1013                }
1014
1015                if (*skip) {
1016                        break;
1017                }
1018
1019        } while (result == GNOME_VFS_OK);
1020
1021        g_free (buffer);
1022
1023        if (result == GNOME_VFS_ERROR_EOF) {
1024                return GNOME_VFS_OK;
1025        }
1026
1027        return result;
1028}
1029
1030static GnomeVFSResult
1031xfer_open_source (GnomeVFSHandle **source_handle,
1032                  GnomeVFSURI *source_uri,
1033                  GnomeVFSProgressCallbackState *progress,
1034                  GnomeVFSXferOptions xfer_options,
1035                  GnomeVFSXferErrorMode *error_mode,
1036                  gboolean *skip)
1037{
1038        GnomeVFSResult result;
1039        gboolean retry;
1040
1041        *skip = FALSE;
1042        do {
1043                retry = FALSE;
1044
1045                result = gnome_vfs_open_uri (source_handle, source_uri,
1046                                             GNOME_VFS_OPEN_READ);
1047
1048                if (result != GNOME_VFS_OK) {
1049                        retry = handle_error (&result, progress, error_mode, skip);
1050                }
1051        } while (retry);
1052
1053        return result;
1054}
1055
1056static GnomeVFSResult
1057xfer_create_target (GnomeVFSHandle **target_handle,
1058                    GnomeVFSURI *target_uri,
1059                    GnomeVFSProgressCallbackState *progress,
1060                    GnomeVFSXferOptions xfer_options,
1061                    GnomeVFSXferErrorMode *error_mode,
1062                    GnomeVFSXferOverwriteMode *overwrite_mode,
1063                    gboolean *skip)
1064{
1065        GnomeVFSResult result;
1066        gboolean retry;
1067        gboolean exclusive;
1068
1069        exclusive = (*overwrite_mode != GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE);
1070
1071        *skip = FALSE;
1072        do {
1073                retry = FALSE;
1074
1075                result = gnome_vfs_create_uri (target_handle, target_uri,
1076                                               GNOME_VFS_OPEN_WRITE,
1077                                               exclusive, 0600);
1078
1079                if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
1080                        gboolean replace;
1081
1082                        retry = handle_overwrite (&result,
1083                                                  progress,
1084                                                  error_mode,
1085                                                  overwrite_mode,
1086                                                  &replace,
1087                                                  skip);
1088
1089                        if (replace) {
1090                                exclusive = FALSE;
1091                        }
1092
1093                } else if (result != GNOME_VFS_OK) {
1094                        retry = handle_error (&result,  progress, error_mode, skip);
1095                }
1096        } while (retry);
1097
1098        return result;
1099}
1100
1101static GnomeVFSResult
1102copy_file (GnomeVFSFileInfo *info, 
1103           GnomeVFSURI *source_uri,
1104           GnomeVFSURI *target_uri,
1105           GnomeVFSXferOptions xfer_options,
1106           GnomeVFSXferErrorMode *error_mode,
1107           GnomeVFSXferOverwriteMode *overwrite_mode,
1108           GnomeVFSProgressCallbackState *progress,
1109           gboolean *skip)
1110{
1111        GnomeVFSResult close_result, result;
1112        GnomeVFSHandle *source_handle, *target_handle;
1113
1114        progress->progress_info->phase = GNOME_VFS_XFER_PHASE_OPENSOURCE;
1115        progress->progress_info->bytes_copied = 0;
1116        result = xfer_open_source (&source_handle, source_uri,
1117                                   progress, xfer_options,
1118                                   error_mode, skip);
1119        if (*skip) {
1120                return GNOME_VFS_OK;
1121        }
1122       
1123        if (result != GNOME_VFS_OK) {
1124                return result;
1125        }
1126
1127        progress->progress_info->phase = GNOME_VFS_XFER_PHASE_OPENTARGET;
1128        result = xfer_create_target (&target_handle, target_uri,
1129                                     progress, xfer_options,
1130                                     error_mode, overwrite_mode,
1131                                     skip);
1132
1133
1134        if (*skip) {
1135                gnome_vfs_close (source_handle);
1136                return GNOME_VFS_OK;
1137        }
1138        if (result != GNOME_VFS_OK) {
1139                gnome_vfs_close (source_handle);
1140                return result;
1141        }
1142
1143        if (call_progress_with_uris_often (progress, source_uri, target_uri,
1144                GNOME_VFS_XFER_PHASE_OPENTARGET) != GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1145
1146
1147                result = copy_file_data (target_handle, source_handle,
1148                                        progress, xfer_options, error_mode,
1149                                        /* use an arbitrary default block size of 4096
1150                                         * if one isn't available for this file system
1151                                         */
1152                                        (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE)
1153                                                ? info->io_block_size : 4096,
1154                                        skip);
1155        }
1156
1157        progress->progress_info->file_index++;
1158        if (result == GNOME_VFS_OK
1159                && call_progress_often (progress, GNOME_VFS_XFER_PHASE_CLOSETARGET) == 0) {
1160                result = GNOME_VFS_ERROR_INTERRUPTED;
1161        }
1162
1163        close_result = gnome_vfs_close (source_handle);
1164        if (result == GNOME_VFS_OK &&
1165            close_result != GNOME_VFS_OK) {
1166                handle_error (&close_result, progress, error_mode, skip);
1167                return close_result;
1168        }
1169        close_result = gnome_vfs_close (target_handle);
1170        if (result == GNOME_VFS_OK &&
1171            close_result != GNOME_VFS_OK) {
1172                handle_error (&close_result, progress, error_mode, skip);
1173                return close_result;
1174        }
1175
1176        if (*skip) {
1177                return GNOME_VFS_OK;
1178        }
1179
1180        return result;
1181}
1182
1183static GnomeVFSResult
1184copy_directory (GnomeVFSURI *source_dir_uri,
1185                GnomeVFSURI *target_dir_uri,
1186                GnomeVFSXferOptions xfer_options,
1187                GnomeVFSXferErrorMode *error_mode,
1188                GnomeVFSXferOverwriteMode *overwrite_mode,
1189                GnomeVFSProgressCallbackState *progress,
1190                gboolean *skip)
1191{
1192        GnomeVFSResult result;
1193        GnomeVFSDirectoryHandle *source_directory_handle;
1194        GnomeVFSDirectoryHandle *dest_directory_handle;
1195
1196        source_directory_handle = NULL;
1197        dest_directory_handle = NULL;
1198       
1199        result = gnome_vfs_directory_open_from_uri (&source_directory_handle, source_dir_uri,
1200                                                    GNOME_VFS_FILE_INFO_DEFAULT, NULL);
1201
1202        if (result != GNOME_VFS_OK) {
1203                return result;
1204        }
1205
1206        progress->progress_info->bytes_copied = 0;
1207        if (call_progress_with_uris_often (progress, source_dir_uri, target_dir_uri,
1208                               GNOME_VFS_XFER_PHASE_COPYING)
1209                == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1210                return GNOME_VFS_ERROR_INTERRUPTED;
1211        }
1212
1213        result = create_directory (target_dir_uri,
1214                                   &dest_directory_handle,
1215                                   xfer_options,
1216                                   error_mode,
1217                                   overwrite_mode,
1218                                   progress,
1219                                   skip);
1220
1221        if (*skip) {
1222                gnome_vfs_directory_close (source_directory_handle);
1223                return GNOME_VFS_OK;
1224        }
1225        if (result != GNOME_VFS_OK) {
1226                gnome_vfs_directory_close (source_directory_handle);
1227                return result;
1228        }
1229
1230        if (call_progress_with_uris_often (progress, source_dir_uri, target_dir_uri,
1231                               GNOME_VFS_XFER_PHASE_OPENTARGET) != 0) {
1232
1233                progress->progress_info->file_index++;
1234                progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD;
1235                progress->progress_info->top_level_item = FALSE;
1236
1237                /* We do not deal with symlink loops here.
1238                 * That's OK because we don't follow symlinks.
1239                 */
1240                do {
1241                        GnomeVFSURI *source_uri;
1242                        GnomeVFSURI *dest_uri;
1243                        GnomeVFSFileInfo info;
1244
1245                        gnome_vfs_file_info_init (&info);
1246
1247                        result = gnome_vfs_directory_read_next (source_directory_handle, &info);
1248                        if (result != GNOME_VFS_OK) {
1249                                break;
1250                        }
1251                       
1252                        /* Skip "." and "..".  */
1253                        if (strcmp (info.name, ".") == 0 || strcmp (info.name, "..") == 0) {
1254                                continue;
1255                        }
1256
1257                        source_uri = gnome_vfs_uri_append_file_name (source_dir_uri, info.name);
1258                        dest_uri = gnome_vfs_uri_append_file_name (target_dir_uri, info.name);
1259                       
1260                        if (info.type == GNOME_VFS_FILE_TYPE_REGULAR) {
1261                                result = copy_file (&info, source_uri, dest_uri,
1262                                                    xfer_options, error_mode, overwrite_mode,
1263                                                    progress, skip);
1264                        } else if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
1265                                result = copy_directory (source_uri, dest_uri,
1266                                                    xfer_options, error_mode, overwrite_mode,
1267                                                    progress, skip);
1268                        } else if (info.type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) {
1269                                result = gnome_vfs_create_symbolic_link (dest_uri, info.symlink_name);
1270                        } else {
1271                                g_assert_not_reached ();
1272                        }
1273
1274                        gnome_vfs_uri_unref (dest_uri);
1275                        gnome_vfs_uri_unref (source_uri);
1276
1277                } while (result == GNOME_VFS_OK);
1278        }
1279
1280        if (result == GNOME_VFS_ERROR_EOF) {
1281                /* all is well, we just finished iterating the directory */
1282                result = GNOME_VFS_OK;
1283        }
1284
1285        gnome_vfs_directory_close (dest_directory_handle);
1286        gnome_vfs_directory_close (source_directory_handle);
1287
1288        return result;
1289}
1290
1291static GnomeVFSResult
1292copy_items (const GList *source_uri_list,
1293            const GList *target_uri_list,
1294            GnomeVFSXferOptions xfer_options,
1295            GnomeVFSXferErrorMode *error_mode,
1296            GnomeVFSXferOverwriteMode overwrite_mode,
1297            GnomeVFSProgressCallbackState *progress)
1298{
1299        GnomeVFSResult result;
1300        const GList *source_item, *target_item;
1301       
1302        result = GNOME_VFS_OK;
1303
1304        /* go through the list of names */
1305        for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) {
1306
1307                GnomeVFSURI *source_uri;
1308                GnomeVFSURI *target_uri;
1309                GnomeVFSURI *target_dir_uri;
1310
1311                GnomeVFSFileInfo info;
1312                gboolean skip;
1313                int count;
1314                int progress_result;
1315
1316                skip = FALSE;
1317                target_uri = NULL;
1318
1319                source_uri = (GnomeVFSURI *)source_item->data;
1320                target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data);
1321               
1322                /* get source URI and file info */
1323                gnome_vfs_file_info_init (&info);
1324                result = gnome_vfs_get_file_info_uri (source_uri, &info,
1325                                                      GNOME_VFS_FILE_INFO_DEFAULT);
1326
1327                progress->progress_info->duplicate_name =
1328                        gnome_vfs_uri_extract_short_path_name
1329                        ((GnomeVFSURI *)target_item->data);
1330
1331                if (result == GNOME_VFS_OK) {
1332                        /* optionally keep trying until we hit a unique target name */
1333                        for (count = 1; ; count++) {
1334                                GnomeVFSXferOverwriteMode overwrite_mode_abort;
1335
1336                                target_uri = gnome_vfs_uri_append_string
1337                                        (target_dir_uri,
1338                                         progress->progress_info->duplicate_name);
1339
1340                                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1341                                progress->progress_info->file_size = info.size;
1342                                progress->progress_info->bytes_copied = 0;
1343                                progress->progress_info->top_level_item = TRUE;
1344
1345                                if (call_progress_with_uris_often (progress, source_uri, target_uri,
1346                                                       GNOME_VFS_XFER_PHASE_COPYING) == 0) {
1347                                        result = GNOME_VFS_ERROR_INTERRUPTED;
1348                                }
1349
1350                                overwrite_mode_abort = GNOME_VFS_XFER_OVERWRITE_MODE_ABORT;
1351                               
1352                               
1353                                if (info.type == GNOME_VFS_FILE_TYPE_REGULAR) {
1354                                        result = copy_file (&info, source_uri, target_uri,
1355                                                            xfer_options, error_mode,
1356                                                            &overwrite_mode_abort,
1357                                                            progress, &skip);
1358                                } else if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
1359                                        result = copy_directory (source_uri, target_uri,
1360                                                            xfer_options, error_mode,
1361                                                            &overwrite_mode_abort,
1362                                                            progress, &skip);
1363                                } else if (info.type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) {
1364                                        result = gnome_vfs_create_symbolic_link (target_uri, info.symlink_name);
1365                                } else {
1366                                        g_message ("type: %d", info.type);
1367                                        g_assert_not_reached ();
1368                                }
1369
1370                                if (result != GNOME_VFS_ERROR_FILE_EXISTS) {
1371                                        /* whatever happened, it wasn't a name conflict */
1372                                        break;
1373                                }
1374
1375                                if (overwrite_mode != GNOME_VFS_XFER_OVERWRITE_MODE_QUERY
1376                                    || (xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0)
1377                                        break;
1378
1379                                /* pass in the current target name, progress will update it to
1380                                 * a new unique name such as 'foo (copy)' or 'bar (copy 2)'
1381                                 */
1382                                g_free (progress->progress_info->duplicate_name);
1383                                progress->progress_info->duplicate_name =
1384                                        gnome_vfs_uri_extract_short_path_name
1385                                        ((GnomeVFSURI *)target_item->data);
1386                                progress->progress_info->duplicate_count = count;
1387                                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE;
1388                                progress->progress_info->vfs_status = result;
1389                                progress_result = call_progress_uri (progress, source_uri, target_uri,
1390                                                       GNOME_VFS_XFER_PHASE_COPYING);
1391                                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1392
1393                                if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1394                                        break;
1395                                }
1396
1397                                if (skip) {
1398                                        break;
1399                                }
1400                               
1401                                /* try again with new uri */
1402                                gnome_vfs_uri_unref (target_uri);
1403
1404                        }
1405                }
1406
1407                g_free (progress->progress_info->duplicate_name);
1408
1409                if (result != GNOME_VFS_OK) {
1410                        break;
1411                }
1412
1413                gnome_vfs_uri_unref (target_dir_uri);
1414
1415                source_item = source_item->next;
1416                target_item = target_item->next;
1417
1418                g_assert ((source_item != NULL) == (target_item != NULL));
1419        }
1420
1421        return result;
1422}
1423
1424static GnomeVFSResult
1425move_items (const GList *source_uri_list,
1426            const GList *target_uri_list,
1427            GnomeVFSXferOptions xfer_options,
1428            GnomeVFSXferErrorMode *error_mode,
1429            GnomeVFSXferOverwriteMode *overwrite_mode,
1430            GnomeVFSProgressCallbackState *progress)
1431{
1432        GnomeVFSResult result;
1433        const GList *source_item, *target_item;
1434       
1435        result = GNOME_VFS_OK;
1436
1437        /* go through the list of names */
1438        for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) {
1439
1440                GnomeVFSURI *source_uri;
1441                GnomeVFSURI *target_uri;
1442                GnomeVFSURI *target_dir_uri;
1443                gboolean retry;
1444                gboolean skip;
1445                int conflict_count;
1446                int progress_result;
1447
1448                source_uri = (GnomeVFSURI *)source_item->data;
1449                target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data);
1450
1451                progress->progress_info->duplicate_name = 
1452                        gnome_vfs_uri_extract_short_path_name
1453                        ((GnomeVFSURI *)target_item->data);
1454
1455                skip = FALSE;
1456                conflict_count = 1;
1457
1458                do {
1459                        retry = FALSE;
1460                        target_uri = gnome_vfs_uri_append_string (target_dir_uri,
1461                                 progress->progress_info->duplicate_name);
1462
1463                        progress->progress_info->file_size = DEFAULT_SIZE_OVERHEAD;
1464                        progress->progress_info->bytes_copied = 0;
1465                        progress_set_source_target_uris (progress, source_uri, target_uri);
1466                        progress->progress_info->top_level_item = TRUE;
1467
1468                        /* no matter what the replace mode, just overwrite the destination
1469                         * handle_name_conflicts took care of conflicting files
1470                         */
1471                        result = gnome_vfs_move_uri (source_uri, target_uri,
1472                                                     (xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) != 0
1473                                                     ? GNOME_VFS_XFER_OVERWRITE_MODE_ABORT
1474                                                     : GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE);
1475
1476
1477                        if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
1478                                /* deal with a name conflict -- ask the progress_callback for a better name */
1479                                g_free (progress->progress_info->duplicate_name);
1480                                progress->progress_info->duplicate_name =
1481                                        gnome_vfs_uri_extract_short_path_name
1482                                        ((GnomeVFSURI *)target_item->data);
1483                                progress->progress_info->duplicate_count = conflict_count;
1484                                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE;
1485                                progress->progress_info->vfs_status = result;
1486                                progress_result = call_progress_uri (progress, source_uri, target_uri,
1487                                                       GNOME_VFS_XFER_PHASE_COPYING);
1488                                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1489
1490                                if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1491                                        gnome_vfs_uri_unref (target_uri);
1492                                        break;
1493                                }
1494                                conflict_count++;
1495                                result = GNOME_VFS_OK;
1496                                retry = TRUE;
1497                                continue;
1498                        }
1499
1500                        if (result != GNOME_VFS_OK) {
1501                                retry = handle_error (&result, progress, error_mode, &skip);
1502                        }
1503
1504                        if (result == GNOME_VFS_OK
1505                                && call_progress_with_uris_often (progress, source_uri,
1506                                target_uri, GNOME_VFS_XFER_PHASE_MOVING) == 0) {
1507                                result = GNOME_VFS_ERROR_INTERRUPTED;
1508                                gnome_vfs_uri_unref (target_uri);
1509                                break;
1510                        }
1511                        gnome_vfs_uri_unref (target_uri);
1512                } while (retry);
1513               
1514                gnome_vfs_uri_unref (target_dir_uri);
1515
1516                if (result != GNOME_VFS_OK && !skip)
1517                        break;
1518
1519                source_item = source_item->next;
1520                target_item = target_item->next;
1521                g_assert ((source_item != NULL) == (target_item != NULL));
1522        }
1523
1524        return result;
1525}
1526
1527static GnomeVFSResult
1528link_items (const GList *source_uri_list,
1529            const GList *target_uri_list,
1530            GnomeVFSXferOptions xfer_options,
1531            GnomeVFSXferErrorMode *error_mode,
1532            GnomeVFSXferOverwriteMode *overwrite_mode,
1533            GnomeVFSProgressCallbackState *progress)
1534{
1535        GnomeVFSResult result;
1536        const GList *source_item, *target_item;
1537        GnomeVFSURI *source_uri;
1538        GnomeVFSURI *target_dir_uri;
1539        GnomeVFSURI *target_uri;
1540        gboolean retry;
1541        gboolean skip;
1542        int conflict_count;
1543        int progress_result;
1544        char *source_reference;
1545
1546        result = GNOME_VFS_OK;
1547
1548        /* go through the list of names, create a link to each item */
1549        for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) {
1550
1551                source_uri = (GnomeVFSURI *)source_item->data;
1552                source_reference = gnome_vfs_uri_to_string (source_uri, GNOME_VFS_URI_HIDE_NONE);
1553
1554                target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data);
1555                progress->progress_info->duplicate_name =
1556                        gnome_vfs_uri_extract_short_path_name
1557                        ((GnomeVFSURI *)target_item->data);
1558
1559                skip = FALSE;
1560                conflict_count = 1;
1561
1562                do {
1563                        retry = FALSE;
1564                        target_uri = gnome_vfs_uri_append_string
1565                                (target_dir_uri,
1566                                 progress->progress_info->duplicate_name);
1567
1568                        progress->progress_info->file_size = DEFAULT_SIZE_OVERHEAD;
1569                        progress->progress_info->bytes_copied = 0;
1570                        progress->progress_info->top_level_item = TRUE;
1571                        progress_set_source_target_uris (progress, source_uri, target_uri);
1572
1573                        /* no matter what the replace mode, just overwrite the destination
1574                         * handle_name_conflicts took care of conflicting files
1575                         */
1576                        result = gnome_vfs_create_symbolic_link (target_uri, source_reference);
1577                        if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
1578                                /* deal with a name conflict -- ask the progress_callback for a better name */
1579                                g_free (progress->progress_info->duplicate_name);
1580                                progress->progress_info->duplicate_name =
1581                                        gnome_vfs_uri_extract_short_path_name
1582                                        ((GnomeVFSURI *)target_item->data);
1583                                progress->progress_info->duplicate_count = conflict_count;
1584                                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE;
1585                                progress->progress_info->vfs_status = result;
1586                                progress_result = call_progress_uri (progress, source_uri, target_uri,
1587                                                       GNOME_VFS_XFER_PHASE_COPYING);
1588                                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1589
1590                                if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1591                                        gnome_vfs_uri_unref (target_uri);
1592                                        break;
1593                                }
1594                                conflict_count++;
1595                                result = GNOME_VFS_OK;
1596                                retry = TRUE;
1597                                continue;
1598                        }
1599                       
1600                        if (result != GNOME_VFS_OK) {
1601                                retry = handle_error (&result, progress, error_mode, &skip);
1602                        }
1603
1604                        if (result == GNOME_VFS_OK
1605                                && call_progress_with_uris_often (progress, source_uri,
1606                                                target_uri, GNOME_VFS_XFER_PHASE_OPENTARGET) == 0) {
1607                                result = GNOME_VFS_ERROR_INTERRUPTED;
1608                                gnome_vfs_uri_unref (target_uri);
1609                                break;
1610                        }
1611                        gnome_vfs_uri_unref (target_uri);
1612                } while (retry);
1613               
1614                gnome_vfs_uri_unref (target_dir_uri);
1615                g_free (source_reference);
1616
1617                if (result != GNOME_VFS_OK && !skip)
1618                        break;
1619
1620                source_item = source_item->next;
1621                target_item = target_item->next;
1622                g_assert ((source_item != NULL) == (target_item != NULL));
1623        }
1624
1625        return result;
1626}
1627
1628static GnomeVFSResult
1629gnome_vfs_xfer_empty_trash (const GList *trash_dir_uris,
1630                            GnomeVFSXferErrorMode error_mode,
1631                            GnomeVFSProgressCallbackState *progress)
1632{
1633        GnomeVFSResult result;
1634        const GList *p;
1635        gboolean skip;
1636
1637        result = GNOME_VFS_OK;
1638       
1639        call_progress (progress, GNOME_VFS_XFER_PHASE_INITIAL);
1640
1641                /* initialize the results */
1642        progress->progress_info->files_total = 0;
1643        progress->progress_info->bytes_total = 0;
1644        progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COLLECTING;
1645
1646
1647        for (p = trash_dir_uris;  p != NULL; p = p->next) {
1648                result = directory_add_items_and_size (p->data,
1649                        GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE,
1650                        progress);
1651                if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) {
1652                        /* out of file descriptors -- we will deal with that */
1653                        result = GNOME_VFS_OK;
1654                        break;
1655                }
1656                if (result != GNOME_VFS_OK)
1657                        break;
1658                /* set up a fake total size to represent the bulk of the operation
1659                 * -- we'll subtract a proportional value for every deletion
1660                 */
1661                progress->progress_info->bytes_total
1662                        = progress->progress_info->files_total * DEFAULT_SIZE_OVERHEAD;
1663        }
1664        if (result == GNOME_VFS_OK) {
1665                call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO);
1666                for (p = trash_dir_uris;  p != NULL; p = p->next) {
1667                        result = empty_directory ((GnomeVFSURI *)p->data, progress,
1668                                GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE,
1669                                &error_mode, &skip);
1670                        if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) {
1671                                result = non_recursive_empty_directory ((GnomeVFSURI *)p->data,
1672                                        progress, GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE,
1673                                        &error_mode, &skip);
1674                        }
1675                        if (result != GNOME_VFS_OK) {
1676                                break;
1677                        }
1678                }
1679        }
1680
1681        call_progress (progress, GNOME_VFS_XFER_PHASE_COMPLETED);
1682
1683        return result;
1684}
1685
1686static GnomeVFSResult
1687gnome_vfs_xfer_delete_items_common (const GList *source_uri_list,
1688                                    GnomeVFSXferErrorMode error_mode,
1689                                    GnomeVFSXferOptions xfer_options,
1690                                    GnomeVFSProgressCallbackState *progress)
1691{
1692        GnomeVFSFileInfo info;
1693        GnomeVFSResult result;
1694        GnomeVFSURI *uri;
1695        const GList *p;
1696        gboolean skip;
1697
1698        result = GNOME_VFS_OK;
1699       
1700        for (p = source_uri_list;  p != NULL; p = p->next) {
1701       
1702                skip = FALSE;
1703                /* get the URI and VFSFileInfo for each */
1704                uri = p->data;
1705
1706                gnome_vfs_file_info_init (&info);
1707                result = gnome_vfs_get_file_info_uri (uri, &info,
1708                                                      GNOME_VFS_FILE_INFO_DEFAULT);
1709
1710                if (result != GNOME_VFS_OK) {
1711                        break;
1712                }
1713
1714                if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
1715                        remove_directory (uri, TRUE, progress, xfer_options,
1716                                          &error_mode, &skip);
1717                } else {
1718                        remove_file (uri, progress, xfer_options, &error_mode,
1719                                     &skip);
1720                }
1721        }
1722
1723        return result;
1724}
1725
1726static GnomeVFSResult
1727gnome_vfs_xfer_delete_items (const GList *source_uri_list,
1728                             GnomeVFSXferErrorMode error_mode,
1729                             GnomeVFSXferOptions xfer_options,
1730                             GnomeVFSProgressCallbackState *progress)
1731{
1732
1733        GnomeVFSResult result;
1734               
1735        call_progress (progress, GNOME_VFS_XFER_PHASE_INITIAL);
1736
1737                /* initialize the results */
1738        progress->progress_info->files_total = 0;
1739        progress->progress_info->bytes_total = 0;
1740        progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COLLECTING;
1741
1742
1743        result = count_items_and_size (source_uri_list,
1744                GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE, progress, TRUE, FALSE);
1745
1746        if (result == GNOME_VFS_OK) {
1747                call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO);
1748                result = gnome_vfs_xfer_delete_items_common (source_uri_list,
1749                        error_mode, xfer_options, progress);
1750        }
1751
1752        call_progress (progress, GNOME_VFS_XFER_PHASE_COMPLETED);
1753
1754        return result;
1755}
1756
1757static GnomeVFSResult
1758gnome_vfs_new_directory_with_unique_name (const GnomeVFSURI *target_dir_uri,
1759                                          const char *name,
1760                                          GnomeVFSXferErrorMode error_mode,
1761                                          GnomeVFSXferOverwriteMode overwrite_mode,
1762                                          GnomeVFSProgressCallbackState *progress)
1763{
1764        GnomeVFSResult result;
1765        GnomeVFSURI *target_uri;
1766        GnomeVFSDirectoryHandle *dest_directory_handle;
1767        gboolean dummy;
1768        int progress_result;
1769        int conflict_count;
1770       
1771        dest_directory_handle = NULL;
1772        progress->progress_info->duplicate_name = g_strdup (name);
1773
1774        for (conflict_count = 1; ; conflict_count++) {
1775
1776                target_uri = gnome_vfs_uri_append_string
1777                        (target_dir_uri,
1778                         progress->progress_info->duplicate_name);
1779                result = create_directory (target_uri,
1780                                           &dest_directory_handle,
1781                                           GNOME_VFS_XFER_USE_UNIQUE_NAMES,
1782                                           &error_mode,
1783                                           &overwrite_mode,
1784                                           progress,
1785                                           &dummy);
1786
1787                if (result != GNOME_VFS_ERROR_FILE_EXISTS
1788                        && result != GNOME_VFS_ERROR_NAME_TOO_LONG) {
1789                        break;
1790                }
1791
1792                /* deal with a name conflict -- ask the progress_callback for a better name */
1793                g_free (progress->progress_info->duplicate_name);
1794                progress->progress_info->duplicate_name = g_strdup (name);
1795                progress->progress_info->duplicate_count = conflict_count;
1796                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE;
1797                progress->progress_info->vfs_status = result;
1798                progress_result = call_progress_uri (progress, NULL, target_uri,
1799                                       GNOME_VFS_XFER_PHASE_COPYING);
1800                progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK;
1801
1802                if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) {
1803                        break;
1804                }
1805                gnome_vfs_uri_unref (target_uri);
1806                result = GNOME_VFS_OK;
1807        }
1808
1809        call_progress_uri (progress, NULL, target_uri,
1810                GNOME_VFS_XFER_PHASE_OPENTARGET);
1811
1812        if (dest_directory_handle != NULL) {
1813                gnome_vfs_directory_close (dest_directory_handle);
1814        }
1815
1816        gnome_vfs_uri_unref (target_uri);
1817        g_free (progress->progress_info->duplicate_name);
1818
1819        return result;
1820}
1821
1822static GnomeVFSResult
1823gnome_vfs_destination_is_writable (const GnomeVFSURI *uri)
1824{
1825        GnomeVFSURI *test_uri;
1826        GnomeVFSResult result;
1827        GnomeVFSHandle *handle;
1828
1829        if (!gnome_vfs_uri_is_local (uri)) {
1830                /* if destination is not local, do not test it for writability, just
1831                 * assume it's writable
1832                 */
1833                return GNOME_VFS_OK;
1834        }
1835
1836        /* test writability by creating and erasing a temporary file */
1837        test_uri = gnome_vfs_uri_append_file_name (uri, ".vfs-write.tmp");
1838        result = gnome_vfs_create_uri (&handle, test_uri, GNOME_VFS_OPEN_WRITE, TRUE, 0600);
1839
1840        if (result == GNOME_VFS_OK) {
1841                gnome_vfs_close (handle);
1842        }
1843       
1844        if (result == GNOME_VFS_OK || result == GNOME_VFS_ERROR_FILE_EXISTS) {
1845                gnome_vfs_unlink_from_uri (test_uri);
1846                result = GNOME_VFS_OK;
1847        }
1848       
1849        gnome_vfs_uri_unref (test_uri);
1850        return result;
1851}
1852
1853static GnomeVFSResult
1854gnome_vfs_xfer_uri_internal (const GList *source_uris,
1855                             const GList *target_uris,
1856                             GnomeVFSXferOptions xfer_options,
1857                             GnomeVFSXferErrorMode error_mode,
1858                             GnomeVFSXferOverwriteMode overwrite_mode,
1859                             GnomeVFSProgressCallbackState *progress)
1860{
1861        GnomeVFSResult result;
1862        GList *source_uri_list, *target_uri_list;
1863        GList *source_uri, *target_uri;
1864        GnomeVFSURI *target_dir_uri;
1865        gboolean move, link;
1866        GnomeVFSFileSize free_bytes;
1867        gboolean skip;
1868       
1869        result = GNOME_VFS_OK;
1870        move = FALSE;
1871        link = FALSE;
1872        target_dir_uri = NULL;
1873
1874        /* Check and see if target is writable. Return error if it is not. */
1875        target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)((GList *)target_uris)->data);
1876        result = gnome_vfs_destination_is_writable (target_dir_uri);
1877        progress_set_source_target_uris (progress, NULL, target_dir_uri);
1878        if (result != GNOME_VFS_OK) {
1879                handle_error (&result, progress, &error_mode, &skip);
1880                gnome_vfs_uri_unref (target_dir_uri);
1881                return result;
1882        }
1883
1884        /* Create an owning list of source and destination uris.
1885         * We want to be able to remove items that we decide to skip during
1886         * name conflict check.
1887         */
1888        source_uri_list = gnome_vfs_uri_list_copy ((GList *)source_uris);
1889        target_uri_list = gnome_vfs_uri_list_copy ((GList *)target_uris);
1890
1891        move = (xfer_options & GNOME_VFS_XFER_REMOVESOURCE) != 0;
1892        link = (xfer_options & GNOME_VFS_XFER_LINK_ITEMS) != 0;
1893
1894        if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0) {
1895                /* see if moved files are on the same file system so that we decide to do
1896                 * a simple move or have to do a copy/remove
1897                 */
1898                for (source_uri = source_uri_list, target_uri = target_uri_list;
1899                        source_uri != NULL;
1900                        source_uri = source_uri->next, target_uri = target_uri->next) {
1901                        gboolean same_fs;
1902
1903                        g_assert (target_dir_uri != NULL);
1904
1905                        result = gnome_vfs_check_same_fs_uris ((GnomeVFSURI *)source_uri->data,
1906                                target_dir_uri, &same_fs);
1907
1908                        if (result != GNOME_VFS_OK) {
1909                                break;
1910                        }
1911
1912                        move &= same_fs;
1913                }
1914        }
1915
1916        if (target_dir_uri != NULL) {
1917                gnome_vfs_uri_unref (target_dir_uri);
1918                target_dir_uri = NULL;
1919        }
1920       
1921        if (result == GNOME_VFS_OK) {
1922
1923                call_progress (progress, GNOME_VFS_XFER_PHASE_INITIAL);
1924
1925                progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COLLECTING;
1926                result = count_items_and_size (source_uri_list, xfer_options, progress, move, link);
1927                if (result == GNOME_VFS_OK) {
1928
1929                        /* Calculate free space on destination. If an error is returned, we have a non-local
1930                         * file system, so we just forge ahead and hope for the best
1931                         */                     
1932                        target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_uri_list->data);
1933                        result = gnome_vfs_get_volume_free_space (target_dir_uri, &free_bytes);
1934                        if (target_dir_uri != NULL) {
1935                                gnome_vfs_uri_unref (target_dir_uri);
1936                                target_dir_uri = NULL;
1937                        }
1938
1939                        if (result == GNOME_VFS_OK) {
1940                                if (!move && progress->progress_info->bytes_total > free_bytes) {
1941                                        result = GNOME_VFS_ERROR_NO_SPACE;
1942                                        handle_error (&result, progress, &error_mode, &skip);
1943                                        return result;
1944                                }
1945                        }
1946                       
1947                        /* Errors from gnome_vfs_get_volume_free_space should be ignored */
1948                        result = GNOME_VFS_OK;
1949                       
1950                        if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0) {
1951                                result = handle_name_conflicts (&source_uri_list, &target_uri_list,
1952                                                                xfer_options, &error_mode, &overwrite_mode,
1953                                                                progress);
1954                        }
1955                        /* reset the preflight numbers */
1956                        progress->progress_info->file_index = 0;
1957                        progress->progress_info->total_bytes_copied = 0;
1958
1959                        if (result != GNOME_VFS_OK) {
1960                                /* don't care about any results from handle_error */
1961                                handle_error (&result, progress, &error_mode, &skip);
1962
1963                                /* whatever error it was, we handled it */
1964                                result = GNOME_VFS_OK;
1965                        } else {
1966                                call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO);
1967
1968                                if (move) {
1969                                        g_assert (!link);
1970                                        result = move_items (source_uri_list, target_uri_list,
1971                                                             xfer_options, &error_mode, &overwrite_mode,
1972                                                             progress);
1973                                } else if (link) {
1974                                        result = link_items (source_uri_list, target_uri_list,
1975                                                             xfer_options, &error_mode, &overwrite_mode,
1976                                                             progress);
1977                                } else {
1978                                        result = copy_items (source_uri_list, target_uri_list,
1979                                                             xfer_options, &error_mode, overwrite_mode,
1980                                                             progress);
1981                                }
1982                               
1983                                if (result == GNOME_VFS_OK) {
1984                                        if (xfer_options & GNOME_VFS_XFER_REMOVESOURCE) {
1985                                                call_progress (progress, GNOME_VFS_XFER_PHASE_CLEANUP);
1986                                                result = gnome_vfs_xfer_delete_items_common (source_uri_list,
1987                                                        error_mode, xfer_options, progress);
1988                                        }
1989                                }
1990                        }
1991                }
1992        }
1993
1994        /* Done, at last.  At this point, there is no chance to interrupt the
1995           operation anymore so we don't check the return value.  */
1996        call_progress (progress, GNOME_VFS_XFER_PHASE_COMPLETED);
1997
1998        free_progress (progress->progress_info);
1999        gnome_vfs_uri_list_free (source_uri_list);
2000        gnome_vfs_uri_list_free (target_uri_list);
2001
2002        return result;
2003}
2004
2005GnomeVFSResult
2006gnome_vfs_xfer_private (const GList *source_uri_list,
2007                        const GList *target_uri_list,
2008                        GnomeVFSXferOptions xfer_options,
2009                        GnomeVFSXferErrorMode error_mode,
2010                        GnomeVFSXferOverwriteMode overwrite_mode,
2011                        GnomeVFSXferProgressCallback progress_callback,
2012                        gpointer data,
2013                        GnomeVFSXferProgressCallback sync_progress_callback,
2014                        gpointer sync_progress_data)
2015{
2016        GnomeVFSProgressCallbackState progress_state;
2017        GnomeVFSXferProgressInfo progress_info;
2018        GnomeVFSURI *target_dir_uri;
2019        GnomeVFSResult result;
2020       
2021        init_progress (&progress_state, &progress_info);
2022        progress_state.sync_callback = sync_progress_callback;
2023        progress_state.user_data = sync_progress_data;
2024        progress_state.update_callback = progress_callback;
2025        progress_state.async_job_data = data;
2026
2027        if ((xfer_options & GNOME_VFS_XFER_EMPTY_DIRECTORIES) != 0) {
2028                /* Directory empty operation (Empty Trash, etc.). */
2029                g_assert (source_uri_list != NULL);
2030                g_assert (target_uri_list == NULL);
2031               
2032                result = gnome_vfs_xfer_empty_trash (source_uri_list, error_mode, &progress_state);
2033        } else if ((xfer_options & GNOME_VFS_XFER_DELETE_ITEMS) != 0) {
2034                /* Delete items operation */
2035                g_assert (source_uri_list != NULL);
2036                g_assert (target_uri_list == NULL);
2037                               
2038                result = gnome_vfs_xfer_delete_items (source_uri_list,
2039                      error_mode, xfer_options, &progress_state);
2040        } else if ((xfer_options & GNOME_VFS_XFER_NEW_UNIQUE_DIRECTORY) != 0) {
2041                /* New directory create operation */
2042                g_assert (source_uri_list == NULL);
2043                g_assert (g_list_length ((GList *)target_uri_list) == 1);
2044
2045
2046                target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_uri_list->data);
2047                if (target_dir_uri == NULL
2048                        || gnome_vfs_uri_get_basename ((GnomeVFSURI *)target_uri_list->data) == NULL)
2049                        return GNOME_VFS_ERROR_INVALID_URI;
2050                       
2051                result = gnome_vfs_new_directory_with_unique_name (target_dir_uri,
2052                        gnome_vfs_uri_get_basename ((GnomeVFSURI *)target_uri_list->data),
2053                        error_mode, overwrite_mode, &progress_state);
2054                gnome_vfs_uri_unref (target_dir_uri);
2055        } else {
2056                /* Copy items operation */
2057                g_assert (source_uri_list != NULL);
2058                g_assert (target_uri_list != NULL);
2059                g_assert (g_list_length ((GList *)source_uri_list) == g_list_length ((GList *)target_uri_list));
2060
2061                result = gnome_vfs_xfer_uri_internal (source_uri_list, target_uri_list,
2062                        xfer_options, error_mode, overwrite_mode, &progress_state);
2063        }
2064
2065        /* FIXME bugzilla.eazel.com 1218:
2066         *
2067         * The async job setup will try to call the callback function with the callback data
2068         * even though they are usually dead at this point because the callback detected
2069         * that we are giving up and cleaned up after itself.
2070         *
2071         * Should fix this in the async job call setup.
2072         *
2073         * For now just pretend everything worked well.
2074         *
2075         */
2076        result = GNOME_VFS_OK;
2077
2078        return result;
2079}
2080
2081GnomeVFSResult
2082gnome_vfs_xfer_uri_list (const GList *source_uri_list,
2083                         const GList *target_uri_list,
2084                         GnomeVFSXferOptions xfer_options,
2085                         GnomeVFSXferErrorMode error_mode,
2086                         GnomeVFSXferOverwriteMode overwrite_mode,
2087                         GnomeVFSXferProgressCallback progress_callback,
2088                         gpointer data)
2089{
2090        GnomeVFSProgressCallbackState progress_state;
2091        GnomeVFSXferProgressInfo progress_info;
2092
2093        g_return_val_if_fail (source_uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2094        g_return_val_if_fail (target_uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2095        g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY,
2096                              GNOME_VFS_ERROR_BAD_PARAMETERS); 
2097               
2098        init_progress (&progress_state, &progress_info);
2099        progress_state.sync_callback = progress_callback;
2100        progress_state.user_data = data;
2101
2102        return gnome_vfs_xfer_uri_internal (source_uri_list, target_uri_list,
2103                xfer_options, error_mode, overwrite_mode, &progress_state);
2104}
2105
2106GnomeVFSResult 
2107gnome_vfs_xfer_uri (const GnomeVFSURI *source_uri,
2108                    const GnomeVFSURI *target_uri,
2109                    GnomeVFSXferOptions xfer_options,
2110                    GnomeVFSXferErrorMode error_mode,
2111                    GnomeVFSXferOverwriteMode overwrite_mode,
2112                    GnomeVFSXferProgressCallback progress_callback,
2113                    gpointer data)
2114{
2115        GList *source_uri_list, *target_uri_list;
2116        GnomeVFSResult result;
2117
2118        g_return_val_if_fail (source_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2119        g_return_val_if_fail (target_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);     
2120        g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY,
2121                              GNOME_VFS_ERROR_BAD_PARAMETERS); 
2122
2123        source_uri_list = g_list_append (NULL, (void *)source_uri);
2124        target_uri_list = g_list_append (NULL, (void *)target_uri);
2125
2126        result = gnome_vfs_xfer_uri_list (source_uri_list, target_uri_list,
2127                xfer_options, error_mode, overwrite_mode, progress_callback, data);
2128
2129        g_list_free (source_uri_list);
2130        g_list_free (target_uri_list);
2131
2132        return result;
2133}
2134
2135GnomeVFSResult
2136gnome_vfs_xfer_delete_list (const GList *uri_list,
2137                            GnomeVFSXferErrorMode error_mode,
2138                            GnomeVFSXferOptions xfer_options,
2139                            GnomeVFSXferProgressCallback
2140                                   progress_callback,
2141                            gpointer data)
2142{
2143        GnomeVFSProgressCallbackState progress_state;
2144        GnomeVFSXferProgressInfo progress_info;
2145
2146        g_return_val_if_fail (uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
2147        g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY,
2148                              GNOME_VFS_ERROR_BAD_PARAMETERS);
2149
2150        init_progress (&progress_state, &progress_info);
2151        progress_state.sync_callback = progress_callback;
2152        progress_state.user_data = data;
2153
2154        return gnome_vfs_xfer_delete_items (uri_list, error_mode, xfer_options,
2155                &progress_state);
2156}
2157
Note: See TracBrowser for help on using the repository browser.