Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
nautilus-file-operations.c:5223:32 | clang-analyzer | Access to field 'data' results in a dereference of a null pointer (loaded from variable 'files')* | ||
nautilus-file-operations.c:5547:32 | clang-analyzer | Access to field 'data' results in a dereference of a null pointer (loaded from variable 'files') | ||
nautilus-file-operations.c:5848:32 | clang-analyzer | Access to field 'data' results in a dereference of a null pointer (loaded from variable 'locations')* |
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* nautilus-file-operations.c - Nautilus file operations.
4
5 Copyright (C) 1999, 2000 Free Software Foundation
6 Copyright (C) 2000, 2001 Eazel, Inc.
7 Copyright (C) 2007 Red Hat, Inc.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU General Public
20 License along with this program; if not, write to the
21 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.
23
24 Authors: Alexander Larsson <alexl@redhat.com>
25 Ettore Perazzoli <ettore@gnu.org>
26 Pavel Cisler <pavel@eazel.com>
27 */
28
29 #include <config.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <locale.h>
34 #include <math.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <stdlib.h>
38
39 #include "nautilus-file-operations.h"
40
41 #include "nautilus-file-changes-queue.h"
42 #include "nautilus-lib-self-check-functions.h"
43
44 #include "nautilus-progress-info.h"
45
46 #include <eel/eel-glib-extensions.h>
47 #include <eel/eel-gtk-extensions.h>
48 #include <eel/eel-stock-dialogs.h>
49 #include <eel/eel-vfs-extensions.h>
50
51 #include <glib/gi18n.h>
52 #include <glib/gstdio.h>
53 #include <gdk/gdk.h>
54 #include <gtk/gtk.h>
55 #include <gio/gio.h>
56 #include <glib.h>
57 #include "nautilus-file-changes-queue.h"
58 #include "nautilus-file-private.h"
59 #include "nautilus-desktop-icon-file.h"
60 #include "nautilus-desktop-link-monitor.h"
61 #include "nautilus-global-preferences.h"
62 #include "nautilus-link.h"
63 #include "nautilus-trash-monitor.h"
64 #include "nautilus-file-utilities.h"
65 #include "nautilus-file-conflict-dialog.h"
66 #include "nautilus-file-undo-operations.h"
67 #include "nautilus-file-undo-manager.h"
68
69 /* TODO: TESTING!!! */
70
71 typedef struct {
72 GIOSchedulerJob *io_job;
73 GTimer *time;
74 GtkWindow *parent_window;
75 int screen_num;
76 int inhibit_cookie;
77 NautilusProgressInfo *progress;
78 GCancellable *cancellable;
79 GHashTable *skip_files;
80 GHashTable *skip_readdir_error;
81 NautilusFileUndoInfo *undo_info;
82 gboolean skip_all_error;
83 gboolean skip_all_conflict;
84 gboolean merge_all;
85 gboolean replace_all;
86 gboolean delete_all;
87 } CommonJob;
88
89 typedef struct {
90 CommonJob common;
91 gboolean is_move;
92 GList *files;
93 GFile *destination;
94 GFile *desktop_location;
95 GFile *fake_display_source;
96 GdkPoint *icon_positions;
97 int n_icon_positions;
98 GHashTable *debuting_files;
99 gchar *target_name;
100 NautilusCopyCallback done_callback;
101 gpointer done_callback_data;
102 } CopyMoveJob;
103
104 typedef struct {
105 CommonJob common;
106 GList *files;
107 gboolean try_trash;
108 gboolean user_cancel;
109 NautilusDeleteCallback done_callback;
110 gpointer done_callback_data;
111 } DeleteJob;
112
113 typedef struct {
114 CommonJob common;
115 GFile *dest_dir;
116 char *filename;
117 gboolean make_dir;
118 GFile *src;
119 char *src_data;
120 int length;
121 GdkPoint position;
122 gboolean has_position;
123 GFile *created_file;
124 NautilusCreateCallback done_callback;
125 gpointer done_callback_data;
126 } CreateJob;
127
128
129 typedef struct {
130 CommonJob common;
131 GList *trash_dirs;
132 gboolean should_confirm;
133 NautilusOpCallback done_callback;
134 gpointer done_callback_data;
135 } EmptyTrashJob;
136
137 typedef struct {
138 CommonJob common;
139 GFile *file;
140 gboolean interactive;
141 NautilusOpCallback done_callback;
142 gpointer done_callback_data;
143 } MarkTrustedJob;
144
145 typedef struct {
146 CommonJob common;
147 GFile *file;
148 NautilusOpCallback done_callback;
149 gpointer done_callback_data;
150 guint32 file_permissions;
151 guint32 file_mask;
152 guint32 dir_permissions;
153 guint32 dir_mask;
154 } SetPermissionsJob;
155
156 typedef enum {
157 OP_KIND_COPY,
158 OP_KIND_MOVE,
159 OP_KIND_DELETE,
160 OP_KIND_TRASH
161 } OpKind;
162
163 typedef struct {
164 int num_files;
165 goffset num_bytes;
166 int num_files_since_progress;
167 OpKind op;
168 } SourceInfo;
169
170 typedef struct {
171 int num_files;
172 goffset num_bytes;
173 OpKind op;
174 guint64 last_report_time;
175 int last_reported_files_left;
176 } TransferInfo;
177
178 #define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 15
179 #define NSEC_PER_MICROSEC 1000
180
181 #define MAXIMUM_DISPLAYED_FILE_NAME_LENGTH 50
182
183 #define IS_IO_ERROR(__error, KIND) (((__error)->domain == G_IO_ERROR && (__error)->code == G_IO_ERROR_ ## KIND))
184
185 #define SKIP _("_Skip")
186 #define SKIP_ALL _("S_kip All")
187 #define RETRY _("_Retry")
188 #define DELETE_ALL _("Delete _All")
189 #define REPLACE _("_Replace")
190 #define REPLACE_ALL _("Replace _All")
191 #define MERGE _("_Merge")
192 #define MERGE_ALL _("Merge _All")
193 #define COPY_FORCE _("Copy _Anyway")
194
195 static void
196 mark_desktop_file_trusted (CommonJob *common,
197 GCancellable *cancellable,
198 GFile *file,
199 gboolean interactive);
200
201 static gboolean
202 is_all_button_text (const char *button_text)
203 {
204 g_assert (button_text != NULL);
205
206 return !strcmp (button_text, SKIP_ALL) ||
207 !strcmp (button_text, REPLACE_ALL) ||
208 !strcmp (button_text, DELETE_ALL) ||
209 !strcmp (button_text, MERGE_ALL);
210 }
211
212 static void scan_sources (GList *files,
213 SourceInfo *source_info,
214 CommonJob *job,
215 OpKind kind);
216
217
218 static gboolean empty_trash_job (GIOSchedulerJob *io_job,
219 GCancellable *cancellable,
220 gpointer user_data);
221
222 static char * query_fs_type (GFile *file,
223 GCancellable *cancellable);
224
225 /* keep in time with format_time()
226 *
227 * This counts and outputs the number of “time units”
228 * formatted and displayed by format_time().
229 * For instance, if format_time outputs “3 hours, 4 minutes”
230 * it yields 7.
231 */
232 static int
233 seconds_count_format_time_units (int seconds)
234 {
235 int minutes;
236 int hours;
237
238 if (seconds < 0) {
239 /* Just to make sure... */
240 seconds = 0;
241 }
242
243 if (seconds < 60) {
244 /* seconds */
245 return seconds;
246 }
247
248 if (seconds < 60*60) {
249 /* minutes */
250 minutes = seconds / 60;
251 return minutes;
252 }
253
254 hours = seconds / (60*60);
255
256 if (seconds < 60*60*4) {
257 /* minutes + hours */
258 minutes = (seconds - hours * 60 * 60) / 60;
259 return minutes + hours;
260 }
261
262 return hours;
263 }
264
265 static char *
266 format_time (int seconds)
267 {
268 int minutes;
269 int hours;
270 char *res;
271
272 if (seconds < 0) {
273 /* Just to make sure... */
274 seconds = 0;
275 }
276
277 if (seconds < 60) {
278 return g_strdup_printf (ngettext ("%'d second","%'d seconds", (int) seconds), (int) seconds);
279 }
280
281 if (seconds < 60*60) {
282 minutes = seconds / 60;
283 return g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes), minutes);
284 }
285
286 hours = seconds / (60*60);
287
288 if (seconds < 60*60*4) {
289 char *h, *m;
290
291 minutes = (seconds - hours * 60 * 60) / 60;
292
293 h = g_strdup_printf (ngettext ("%'d hour", "%'d hours", hours), hours);
294 m = g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes), minutes);
295 res = g_strconcat (h, ", ", m, NULL);
296 g_free (h);
297 g_free (m);
298 return res;
299 }
300
301 return g_strdup_printf (ngettext ("approximately %'d hour",
302 "approximately %'d hours",
303 hours), hours);
304 }
305
306 static char *
307 shorten_utf8_string (const char *base, int reduce_by_num_bytes)
308 {
309 int len;
310 char *ret;
311 const char *p;
312
313 len = strlen (base);
314 len -= reduce_by_num_bytes;
315
316 if (len <= 0) {
317 return NULL;
318 }
319
320 ret = g_new (char, len + 1);
321
322 p = base;
323 while (len) {
324 char *next;
325 next = g_utf8_next_char (p);
326 if (next - p > len || *next == '\0') {
327 break;
328 }
329
330 len -= next - p;
331 p = next;
332 }
333
334 if (p - base == 0) {
335 g_free (ret);
336 return NULL;
337 } else {
338 memcpy (ret, base, p - base);
339 ret[p - base] = '\0';
340 return ret;
341 }
342 }
343
344 /* Note that we have these two separate functions with separate format
345 * strings for ease of localization.
346 */
347
348 static char *
349 get_link_name (const char *name, int count, int max_length)
350 {
351 const char *format;
352 char *result;
353 int unshortened_length;
354 gboolean use_count;
355
356 g_assert (name != NULL);
357
358 if (count < 0) {
359 g_warning ("bad count in get_link_name");
360 count = 0;
361 }
362
363 if (count <= 2) {
364 /* Handle special cases for low numbers.
365 * Perhaps for some locales we will need to add more.
366 */
367 switch (count) {
368 default:
369 g_assert_not_reached ();
370 /* fall through */
371 case 0:
372 /* duplicate original file name */
373 format = "%s";
374 break;
375 case 1:
376 /* appended to new link file */
377 format = _("Link to %s");
378 break;
379 case 2:
380 /* appended to new link file */
381 format = _("Another link to %s");
382 break;
383 }
384
385 use_count = FALSE;
386 } else {
387 /* Handle special cases for the first few numbers of each ten.
388 * For locales where getting this exactly right is difficult,
389 * these can just be made all the same as the general case below.
390 */
391 switch (count % 10) {
392 case 1:
393 /* Localizers: Feel free to leave out the "st" suffix
394 * if there's no way to do that nicely for a
395 * particular language.
396 */
397 format = _("%'dst link to %s");
398 break;
399 case 2:
400 /* appended to new link file */
401 format = _("%'dnd link to %s");
402 break;
403 case 3:
404 /* appended to new link file */
405 format = _("%'drd link to %s");
406 break;
407 default:
408 /* appended to new link file */
409 format = _("%'dth link to %s");
410 break;
411 }
412
413 use_count = TRUE;
414 }
415
416 if (use_count)
417 result = g_strdup_printf (format, count, name);
418 else
419 result = g_strdup_printf (format, name);
420
421 if (max_length > 0 && (unshortened_length = strlen (result)) > max_length) {
422 char *new_name;
423
424 new_name = shorten_utf8_string (name, unshortened_length - max_length);
425 if (new_name) {
426 g_free (result);
427
428 if (use_count)
429 result = g_strdup_printf (format, count, new_name);
430 else
431 result = g_strdup_printf (format, new_name);
432
433 g_assert (strlen (result) <= max_length);
434 g_free (new_name);
435 }
436 }
437
438 return result;
439 }
440
441
442 /* Localizers:
443 * Feel free to leave out the st, nd, rd and th suffix or
444 * make some or all of them match.
445 */
446
447 /* localizers: tag used to detect the first copy of a file */
448 static const char untranslated_copy_duplicate_tag[] = N_(" (copy)");
449 /* localizers: tag used to detect the second copy of a file */
450 static const char untranslated_another_copy_duplicate_tag[] = N_(" (another copy)");
451
452 /* localizers: tag used to detect the x11th copy of a file */
453 static const char untranslated_x11th_copy_duplicate_tag[] = N_("th copy)");
454 /* localizers: tag used to detect the x12th copy of a file */
455 static const char untranslated_x12th_copy_duplicate_tag[] = N_("th copy)");
456 /* localizers: tag used to detect the x13th copy of a file */
457 static const char untranslated_x13th_copy_duplicate_tag[] = N_("th copy)");
458
459 /* localizers: tag used to detect the x1st copy of a file */
460 static const char untranslated_st_copy_duplicate_tag[] = N_("st copy)");
461 /* localizers: tag used to detect the x2nd copy of a file */
462 static const char untranslated_nd_copy_duplicate_tag[] = N_("nd copy)");
463 /* localizers: tag used to detect the x3rd copy of a file */
464 static const char untranslated_rd_copy_duplicate_tag[] = N_("rd copy)");
465
466 /* localizers: tag used to detect the xxth copy of a file */
467 static const char untranslated_th_copy_duplicate_tag[] = N_("th copy)");
468
469 #define COPY_DUPLICATE_TAG _(untranslated_copy_duplicate_tag)
470 #define ANOTHER_COPY_DUPLICATE_TAG _(untranslated_another_copy_duplicate_tag)
471 #define X11TH_COPY_DUPLICATE_TAG _(untranslated_x11th_copy_duplicate_tag)
472 #define X12TH_COPY_DUPLICATE_TAG _(untranslated_x12th_copy_duplicate_tag)
473 #define X13TH_COPY_DUPLICATE_TAG _(untranslated_x13th_copy_duplicate_tag)
474
475 #define ST_COPY_DUPLICATE_TAG _(untranslated_st_copy_duplicate_tag)
476 #define ND_COPY_DUPLICATE_TAG _(untranslated_nd_copy_duplicate_tag)
477 #define RD_COPY_DUPLICATE_TAG _(untranslated_rd_copy_duplicate_tag)
478 #define TH_COPY_DUPLICATE_TAG _(untranslated_th_copy_duplicate_tag)
479
480 /* localizers: appended to first file copy */
481 static const char untranslated_first_copy_duplicate_format[] = N_("%s (copy)%s");
482 /* localizers: appended to second file copy */
483 static const char untranslated_second_copy_duplicate_format[] = N_("%s (another copy)%s");
484
485 /* localizers: appended to x11th file copy */
486 static const char untranslated_x11th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
487 /* localizers: appended to x12th file copy */
488 static const char untranslated_x12th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
489 /* localizers: appended to x13th file copy */
490 static const char untranslated_x13th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
491
492 /* localizers: if in your language there's no difference between 1st, 2nd, 3rd and nth
493 * plurals, you can leave the st, nd, rd suffixes out and just make all the translated
494 * strings look like "%s (copy %'d)%s".
495 */
496
497 /* localizers: appended to x1st file copy */
498 static const char untranslated_st_copy_duplicate_format[] = N_("%s (%'dst copy)%s");
499 /* localizers: appended to x2nd file copy */
500 static const char untranslated_nd_copy_duplicate_format[] = N_("%s (%'dnd copy)%s");
501 /* localizers: appended to x3rd file copy */
502 static const char untranslated_rd_copy_duplicate_format[] = N_("%s (%'drd copy)%s");
503 /* localizers: appended to xxth file copy */
504 static const char untranslated_th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
505
506 #define FIRST_COPY_DUPLICATE_FORMAT _(untranslated_first_copy_duplicate_format)
507 #define SECOND_COPY_DUPLICATE_FORMAT _(untranslated_second_copy_duplicate_format)
508 #define X11TH_COPY_DUPLICATE_FORMAT _(untranslated_x11th_copy_duplicate_format)
509 #define X12TH_COPY_DUPLICATE_FORMAT _(untranslated_x12th_copy_duplicate_format)
510 #define X13TH_COPY_DUPLICATE_FORMAT _(untranslated_x13th_copy_duplicate_format)
511
512 #define ST_COPY_DUPLICATE_FORMAT _(untranslated_st_copy_duplicate_format)
513 #define ND_COPY_DUPLICATE_FORMAT _(untranslated_nd_copy_duplicate_format)
514 #define RD_COPY_DUPLICATE_FORMAT _(untranslated_rd_copy_duplicate_format)
515 #define TH_COPY_DUPLICATE_FORMAT _(untranslated_th_copy_duplicate_format)
516
517 static char *
518 extract_string_until (const char *original, const char *until_substring)
519 {
520 char *result;
521
522 g_assert ((int) strlen (original) >= until_substring - original);
523 g_assert (until_substring - original >= 0);
524
525 result = g_malloc (until_substring - original + 1);
526 strncpy (result, original, until_substring - original);
527 result[until_substring - original] = '\0';
528
529 return result;
530 }
531
532 /* Dismantle a file name, separating the base name, the file suffix and removing any
533 * (xxxcopy), etc. string. Figure out the count that corresponds to the given
534 * (xxxcopy) substring.
535 */
536 static void
537 parse_previous_duplicate_name (const char *name,
538 char **name_base,
539 const char **suffix,
540 int *count)
541 {
542 const char *tag;
543
544 g_assert (name[0] != '\0');
545
546 *suffix = eel_filename_get_extension_offset (name);
547
548 if (*suffix == NULL || (*suffix)[1] == '\0') {
549 /* no suffix */
550 *suffix = "";
551 }
552
553 tag = strstr (name, COPY_DUPLICATE_TAG);
554 if (tag != NULL) {
555 if (tag > *suffix) {
556 /* handle case "foo. (copy)" */
557 *suffix = "";
558 }
559 *name_base = extract_string_until (name, tag);
560 *count = 1;
561 return;
562 }
563
564
565 tag = strstr (name, ANOTHER_COPY_DUPLICATE_TAG);
566 if (tag != NULL) {
567 if (tag > *suffix) {
568 /* handle case "foo. (another copy)" */
569 *suffix = "";
570 }
571 *name_base = extract_string_until (name, tag);
572 *count = 2;
573 return;
574 }
575
576
577 /* Check to see if we got one of st, nd, rd, th. */
578 tag = strstr (name, X11TH_COPY_DUPLICATE_TAG);
579
580 if (tag == NULL) {
581 tag = strstr (name, X12TH_COPY_DUPLICATE_TAG);
582 }
583 if (tag == NULL) {
584 tag = strstr (name, X13TH_COPY_DUPLICATE_TAG);
585 }
586
587 if (tag == NULL) {
588 tag = strstr (name, ST_COPY_DUPLICATE_TAG);
589 }
590 if (tag == NULL) {
591 tag = strstr (name, ND_COPY_DUPLICATE_TAG);
592 }
593 if (tag == NULL) {
594 tag = strstr (name, RD_COPY_DUPLICATE_TAG);
595 }
596 if (tag == NULL) {
597 tag = strstr (name, TH_COPY_DUPLICATE_TAG);
598 }
599
600 /* If we got one of st, nd, rd, th, fish out the duplicate number. */
601 if (tag != NULL) {
602 /* localizers: opening parentheses to match the "th copy)" string */
603 tag = strstr (name, _(" ("));
604 if (tag != NULL) {
605 if (tag > *suffix) {
606 /* handle case "foo. (22nd copy)" */
607 *suffix = "";
608 }
609 *name_base = extract_string_until (name, tag);
610 /* localizers: opening parentheses of the "th copy)" string */
611 if (sscanf (tag, _(" (%'d"), count) == 1) {
612 if (*count < 1 || *count > 1000000) {
613 /* keep the count within a reasonable range */
614 *count = 0;
615 }
616 return;
617 }
618 *count = 0;
619 return;
620 }
621 }
622
623
624 *count = 0;
625 if (**suffix != '\0') {
626 *name_base = extract_string_until (name, *suffix);
627 } else {
628 *name_base = g_strdup (name);
629 }
630 }
631
632 static char *
633 make_next_duplicate_name (const char *base, const char *suffix, int count, int max_length)
634 {
635 const char *format;
636 char *result;
637 int unshortened_length;
638 gboolean use_count;
639
640 if (count < 1) {
641 g_warning ("bad count %d in get_duplicate_name", count);
642 count = 1;
643 }
644
645 if (count <= 2) {
646
647 /* Handle special cases for low numbers.
648 * Perhaps for some locales we will need to add more.
649 */
650 switch (count) {
651 default:
652 g_assert_not_reached ();
653 /* fall through */
654 case 1:
655 format = FIRST_COPY_DUPLICATE_FORMAT;
656 break;
657 case 2:
658 format = SECOND_COPY_DUPLICATE_FORMAT;
659 break;
660
661 }
662
663 use_count = FALSE;
664 } else {
665
666 /* Handle special cases for the first few numbers of each ten.
667 * For locales where getting this exactly right is difficult,
668 * these can just be made all the same as the general case below.
669 */
670
671 /* Handle special cases for x11th - x20th.
672 */
673 switch (count % 100) {
674 case 11:
675 format = X11TH_COPY_DUPLICATE_FORMAT;
676 break;
677 case 12:
678 format = X12TH_COPY_DUPLICATE_FORMAT;
679 break;
680 case 13:
681 format = X13TH_COPY_DUPLICATE_FORMAT;
682 break;
683 default:
684 format = NULL;
685 break;
686 }
687
688 if (format == NULL) {
689 switch (count % 10) {
690 case 1:
691 format = ST_COPY_DUPLICATE_FORMAT;
692 break;
693 case 2:
694 format = ND_COPY_DUPLICATE_FORMAT;
695 break;
696 case 3:
697 format = RD_COPY_DUPLICATE_FORMAT;
698 break;
699 default:
700 /* The general case. */
701 format = TH_COPY_DUPLICATE_FORMAT;
702 break;
703 }
704 }
705
706 use_count = TRUE;
707
708 }
709
710 if (use_count)
711 result = g_strdup_printf (format, base, count, suffix);
712 else
713 result = g_strdup_printf (format, base, suffix);
714
715 if (max_length > 0 && (unshortened_length = strlen (result)) > max_length) {
716 char *new_base;
717
718 new_base = shorten_utf8_string (base, unshortened_length - max_length);
719 if (new_base) {
720 g_free (result);
721
722 if (use_count)
723 result = g_strdup_printf (format, new_base, count, suffix);
724 else
725 result = g_strdup_printf (format, new_base, suffix);
726
727 g_assert (strlen (result) <= max_length);
728 g_free (new_base);
729 }
730 }
731
732 return result;
733 }
734
735 static char *
736 get_duplicate_name (const char *name, int count_increment, int max_length)
737 {
738 char *result;
739 char *name_base;
740 const char *suffix;
741 int count;
742
743 parse_previous_duplicate_name (name, &name_base, &suffix, &count);
744 result = make_next_duplicate_name (name_base, suffix, count + count_increment, max_length);
745
746 g_free (name_base);
747
748 return result;
749 }
750
751 static gboolean
752 has_invalid_xml_char (char *str)
753 {
754 gunichar c;
755
756 while (*str != 0) {
757 c = g_utf8_get_char (str);
758 /* characters XML permits */
759 if (!(c == 0x9 ||
760 c == 0xA ||
761 c == 0xD ||
762 (c >= 0x20 && c <= 0xD7FF) ||
763 (c >= 0xE000 && c <= 0xFFFD) ||
764 (c >= 0x10000 && c <= 0x10FFFF))) {
765 return TRUE;
766 }
767 str = g_utf8_next_char (str);
768 }
769 return FALSE;
770 }
771
772
773 static char *
774 custom_full_name_to_string (char *format, va_list va)
775 {
776 GFile *file;
777
778 file = va_arg (va, GFile *);
779
780 return g_file_get_parse_name (file);
781 }
782
783 static void
784 custom_full_name_skip (va_list *va)
785 {
786 (void) va_arg (*va, GFile *);
787 }
788
789 static char *
790 custom_basename_to_string (char *format, va_list va)
791 {
792 GFile *file;
793 GFileInfo *info;
794 char *name, *basename, *tmp;
795
796 file = va_arg (va, GFile *);
797
798 info = g_file_query_info (file,
799 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
800 0,
801 g_cancellable_get_current (),
802 NULL);
803
804 name = NULL;
805 if (info) {
806 name = g_strdup (g_file_info_get_display_name (info));
807 g_object_unref (info);
808 }
809
810 if (name == NULL) {
811 basename = g_file_get_basename (file);
812 if (g_utf8_validate (basename, -1, NULL)) {
813 name = basename;
814 } else {
815 name = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
816 g_free (basename);
817 }
818 }
819
820 /* Some chars can't be put in the markup we use for the dialogs... */
821 if (has_invalid_xml_char (name)) {
822 tmp = name;
823 name = g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
824 g_free (tmp);
825 }
826
827 /* Finally, if the string is too long, truncate it. */
828 if (name != NULL) {
829 tmp = name;
830 name = eel_str_middle_truncate (tmp, MAXIMUM_DISPLAYED_FILE_NAME_LENGTH);
831 g_free (tmp);
832 }
833
834
835 return name;
836 }
837
838 static void
839 custom_basename_skip (va_list *va)
840 {
841 (void) va_arg (*va, GFile *);
842 }
843
844
845 static char *
846 custom_size_to_string (char *format, va_list va)
847 {
848 goffset size;
849
850 size = va_arg (va, goffset);
851 return g_format_size (size);
852 }
853
854 static void
855 custom_size_skip (va_list *va)
856 {
857 (void) va_arg (*va, goffset);
858 }
859
860 static char *
861 custom_time_to_string (char *format, va_list va)
862 {
863 int secs;
864
865 secs = va_arg (va, int);
866 return format_time (secs);
867 }
868
869 static void
870 custom_time_skip (va_list *va)
871 {
872 (void) va_arg (*va, int);
873 }
874
875 static char *
876 custom_mount_to_string (char *format, va_list va)
877 {
878 GMount *mount;
879
880 mount = va_arg (va, GMount *);
881 return g_mount_get_name (mount);
882 }
883
884 static void
885 custom_mount_skip (va_list *va)
886 {
887 (void) va_arg (*va, GMount *);
888 }
889
890
891 static EelPrintfHandler handlers[] = {
892 { 'F', custom_full_name_to_string, custom_full_name_skip },
893 { 'B', custom_basename_to_string, custom_basename_skip },
894 { 'S', custom_size_to_string, custom_size_skip },
895 { 'T', custom_time_to_string, custom_time_skip },
896 { 'V', custom_mount_to_string, custom_mount_skip },
897 { 0 }
898 };
899
900
901 static char *
902 f (const char *format, ...) {
903 va_list va;
904 char *res;
905
906 va_start (va, format);
907 res = eel_strdup_vprintf_with_custom (handlers, format, va);
908 va_end (va);
909
910 return res;
911 }
912
913 #define op_job_new(__type, parent_window) ((__type *)(init_common (sizeof(__type), parent_window)))
914
915 static gpointer
916 init_common (gsize job_size,
917 GtkWindow *parent_window)
918 {
919 CommonJob *common;
920 GdkScreen *screen;
921
922 common = g_malloc0 (job_size);
923
924 if (parent_window) {
925 common->parent_window = parent_window;
926 g_object_add_weak_pointer (G_OBJECT (common->parent_window),
927 (gpointer *) &common->parent_window);
928
929 }
930 common->progress = nautilus_progress_info_new ();
931 common->cancellable = nautilus_progress_info_get_cancellable (common->progress);
932 common->time = g_timer_new ();
933 common->inhibit_cookie = -1;
934 common->screen_num = 0;
935 if (parent_window) {
936 screen = gtk_widget_get_screen (GTK_WIDGET (parent_window));
937 common->screen_num = gdk_screen_get_number (screen);
938 }
939
940 return common;
941 }
942
943 static void
944 finalize_common (CommonJob *common)
945 {
946 nautilus_progress_info_finish (common->progress);
947
948 if (common->inhibit_cookie != -1) {
949 gtk_application_uninhibit (GTK_APPLICATION (g_application_get_default ()),
950 common->inhibit_cookie);
951 }
952
953 common->inhibit_cookie = -1;
954 g_timer_destroy (common->time);
955
956 if (common->parent_window) {
957 g_object_remove_weak_pointer (G_OBJECT (common->parent_window),
958 (gpointer *) &common->parent_window);
959 }
960
961 if (common->skip_files) {
962 g_hash_table_destroy (common->skip_files);
963 }
964 if (common->skip_readdir_error) {
965 g_hash_table_destroy (common->skip_readdir_error);
966 }
967
968 if (common->undo_info != NULL) {
969 nautilus_file_undo_manager_set_action (common->undo_info);
970 g_object_unref (common->undo_info);
971 }
972
973 g_object_unref (common->progress);
974 g_object_unref (common->cancellable);
975 g_free (common);
976 }
977
978 static void
979 skip_file (CommonJob *common,
980 GFile *file)
981 {
982 if (common->skip_files == NULL) {
983 common->skip_files =
984 g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
985 }
986
987 g_hash_table_insert (common->skip_files, g_object_ref (file), file);
988 }
989
990 static void
991 skip_readdir_error (CommonJob *common,
992 GFile *dir)
993 {
994 if (common->skip_readdir_error == NULL) {
995 common->skip_readdir_error =
996 g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
997 }
998
999 g_hash_table_insert (common->skip_readdir_error, g_object_ref (dir), dir);
1000 }
1001
1002 static gboolean
1003 should_skip_file (CommonJob *common,
1004 GFile *file)
1005 {
1006 if (common->skip_files != NULL) {
1007 return g_hash_table_lookup (common->skip_files, file) != NULL;
1008 }
1009 return FALSE;
1010 }
1011
1012 static gboolean
1013 should_skip_readdir_error (CommonJob *common,
1014 GFile *dir)
1015 {
1016 if (common->skip_readdir_error != NULL) {
1017 return g_hash_table_lookup (common->skip_readdir_error, dir) != NULL;
1018 }
1019 return FALSE;
1020 }
1021
1022 static gboolean
1023 can_delete_without_confirm (GFile *file)
1024 {
1025 if (g_file_has_uri_scheme (file, "burn") ||
1026 g_file_has_uri_scheme (file, "recent") ||
1027 g_file_has_uri_scheme (file, "x-nautilus-desktop")) {
1028 return TRUE;
1029 }
1030
1031 return FALSE;
1032 }
1033
1034 static gboolean
1035 can_delete_files_without_confirm (GList *files)
1036 {
1037 g_assert (files != NULL);
1038
1039 while (files != NULL) {
1040 if (!can_delete_without_confirm (files->data)) {
1041 return FALSE;
1042 }
1043
1044 files = files->next;
1045 }
1046
1047 return TRUE;
1048 }
1049
1050 typedef struct {
1051 GtkWindow **parent_window;
1052 gboolean ignore_close_box;
1053 GtkMessageType message_type;
1054 const char *primary_text;
1055 const char *secondary_text;
1056 const char *details_text;
1057 const char **button_titles;
1058 gboolean show_all;
1059
1060 int result;
1061 } RunSimpleDialogData;
1062
1063 static gboolean
1064 do_run_simple_dialog (gpointer _data)
1065 {
1066 RunSimpleDialogData *data = _data;
1067 const char *button_title;
1068 GtkWidget *dialog;
1069 int result;
1070 int response_id;
1071
1072 /* Create the dialog. */
1073 dialog = gtk_message_dialog_new (*data->parent_window,
1074 0,
1075 data->message_type,
1076 GTK_BUTTONS_NONE,
1077 NULL);
1078
1079 g_object_set (dialog,
1080 "text", data->primary_text,
1081 "secondary-text", data->secondary_text,
1082 NULL);
1083
1084 for (response_id = 0;
1085 data->button_titles[response_id] != NULL;
1086 response_id++) {
1087 button_title = data->button_titles[response_id];
1088 if (!data->show_all && is_all_button_text (button_title)) {
1089 continue;
1090 }
1091
1092 gtk_dialog_add_button (GTK_DIALOG (dialog), button_title, response_id);
1093 gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id);
1094 }
1095
1096 if (data->details_text) {
1097 eel_gtk_message_dialog_set_details_label (GTK_MESSAGE_DIALOG (dialog),
1098 data->details_text);
1099 }
1100
1101 /* Run it. */
1102 result = gtk_dialog_run (GTK_DIALOG (dialog));
1103
1104 while ((result == GTK_RESPONSE_NONE || result == GTK_RESPONSE_DELETE_EVENT) && data->ignore_close_box) {
1105 result = gtk_dialog_run (GTK_DIALOG (dialog));
1106 }
1107
1108 gtk_widget_destroy (dialog);
1109
1110 data->result = result;
1111
1112 return FALSE;
1113 }
1114
1115 /* NOTE: This frees the primary / secondary strings, in order to
1116 avoid doing that everywhere. So, make sure they are strduped */
1117
1118 static int
1119 run_simple_dialog_va (CommonJob *job,
1120 gboolean ignore_close_box,
1121 GtkMessageType message_type,
1122 char *primary_text,
1123 char *secondary_text,
1124 const char *details_text,
1125 gboolean show_all,
1126 va_list varargs)
1127 {
1128 RunSimpleDialogData *data;
1129 int res;
1130 const char *button_title;
1131 GPtrArray *ptr_array;
1132
1133 g_timer_stop (job->time);
1134
1135 data = g_new0 (RunSimpleDialogData, 1);
1136 data->parent_window = &job->parent_window;
1137 data->ignore_close_box = ignore_close_box;
1138 data->message_type = message_type;
1139 data->primary_text = primary_text;
1140 data->secondary_text = secondary_text;
1141 data->details_text = details_text;
1142 data->show_all = show_all;
1143
1144 ptr_array = g_ptr_array_new ();
1145 while ((button_title = va_arg (varargs, const char *)) != NULL) {
1146 g_ptr_array_add (ptr_array, (char *)button_title);
1147 }
1148 g_ptr_array_add (ptr_array, NULL);
1149 data->button_titles = (const char **)g_ptr_array_free (ptr_array, FALSE);
1150
1151 nautilus_progress_info_pause (job->progress);
1152 g_io_scheduler_job_send_to_mainloop (job->io_job,
1153 do_run_simple_dialog,
1154 data,
1155 NULL);
1156 nautilus_progress_info_resume (job->progress);
1157 res = data->result;
1158
1159 g_free (data->button_titles);
1160 g_free (data);
1161
1162 g_timer_continue (job->time);
1163
1164 g_free (primary_text);
1165 g_free (secondary_text);
1166
1167 return res;
1168 }
1169
1170 #if 0 /* Not used at the moment */
1171 static int
1172 run_simple_dialog (CommonJob *job,
1173 gboolean ignore_close_box,
1174 GtkMessageType message_type,
1175 char *primary_text,
1176 char *secondary_text,
1177 const char *details_text,
1178 ...)
1179 {
1180 va_list varargs;
1181 int res;
1182
1183 va_start (varargs, details_text);
1184 res = run_simple_dialog_va (job,
1185 ignore_close_box,
1186 message_type,
1187 primary_text,
1188 secondary_text,
1189 details_text,
1190 varargs);
1191 va_end (varargs);
1192 return res;
1193 }
1194 #endif
1195
1196 static int
1197 run_error (CommonJob *job,
1198 char *primary_text,
1199 char *secondary_text,
1200 const char *details_text,
1201 gboolean show_all,
1202 ...)
1203 {
1204 va_list varargs;
1205 int res;
1206
1207 va_start (varargs, show_all);
1208 res = run_simple_dialog_va (job,
1209 FALSE,
1210 GTK_MESSAGE_ERROR,
1211 primary_text,
1212 secondary_text,
1213 details_text,
1214 show_all,
1215 varargs);
1216 va_end (varargs);
1217 return res;
1218 }
1219
1220 static int
1221 run_warning (CommonJob *job,
1222 char *primary_text,
1223 char *secondary_text,
1224 const char *details_text,
1225 gboolean show_all,
1226 ...)
1227 {
1228 va_list varargs;
1229 int res;
1230
1231 va_start (varargs, show_all);
1232 res = run_simple_dialog_va (job,
1233 FALSE,
1234 GTK_MESSAGE_WARNING,
1235 primary_text,
1236 secondary_text,
1237 details_text,
1238 show_all,
1239 varargs);
1240 va_end (varargs);
1241 return res;
1242 }
1243
1244 static int
1245 run_question (CommonJob *job,
1246 char *primary_text,
1247 char *secondary_text,
1248 const char *details_text,
1249 gboolean show_all,
1250 ...)
1251 {
1252 va_list varargs;
1253 int res;
1254
1255 va_start (varargs, show_all);
1256 res = run_simple_dialog_va (job,
1257 FALSE,
1258 GTK_MESSAGE_QUESTION,
1259 primary_text,
1260 secondary_text,
1261 details_text,
1262 show_all,
1263 varargs);
1264 va_end (varargs);
1265 return res;
1266 }
1267
1268 static void
1269 inhibit_power_manager (CommonJob *job, const char *message)
1270 {
1271 job->inhibit_cookie = gtk_application_inhibit (GTK_APPLICATION (g_application_get_default ()),
1272 GTK_WINDOW (job->parent_window),
1273 GTK_APPLICATION_INHIBIT_LOGOUT |
1274 GTK_APPLICATION_INHIBIT_SUSPEND,
1275 message);
1276 }
1277
1278 static void
1279 abort_job (CommonJob *job)
1280 {
1281 /* destroy the undo action data too */
1282 g_clear_object (&job->undo_info);
1283
1284 g_cancellable_cancel (job->cancellable);
1285 }
1286
1287 static gboolean
1288 job_aborted (CommonJob *job)
1289 {
1290 return g_cancellable_is_cancelled (job->cancellable);
1291 }
1292
1293 /* Since this happens on a thread we can't use the global prefs object */
1294 static gboolean
1295 should_confirm_trash (void)
1296 {
1297 GSettings *prefs;
1298 gboolean confirm_trash;
1299
1300 prefs = g_settings_new ("org.gnome.nautilus.preferences");
1301 confirm_trash = g_settings_get_boolean (prefs, NAUTILUS_PREFERENCES_CONFIRM_TRASH);
1302 g_object_unref (prefs);
1303 return confirm_trash;
1304 }
1305
1306 static gboolean
1307 confirm_delete_from_trash (CommonJob *job,
1308 GList *files)
1309 {
1310 char *prompt;
1311 int file_count;
1312 int response;
1313
1314 /* Just Say Yes if the preference says not to confirm. */
1315 if (!should_confirm_trash ()) {
1316 return TRUE;
1317 }
1318
1319 file_count = g_list_length (files);
1320 g_assert (file_count > 0);
1321
1322 if (file_count == 1) {
1323 prompt = f (_("Are you sure you want to permanently delete “%B” "
1324 "from the trash?"), files->data);
1325 } else {
1326 prompt = f (ngettext("Are you sure you want to permanently delete "
1327 "the %'d selected item from the trash?",
1328 "Are you sure you want to permanently delete "
1329 "the %'d selected items from the trash?",
1330 file_count),
1331 file_count);
1332 }
1333
1334 response = run_warning (job,
1335 prompt,
1336 f (_("If you delete an item, it will be permanently lost.")),
1337 NULL,
1338 FALSE,
1339 GTK_STOCK_CANCEL, GTK_STOCK_DELETE,
1340 NULL);
1341
1342 return (response == 1);
1343 }
1344
1345 static gboolean
1346 confirm_empty_trash (CommonJob *job)
1347 {
1348 char *prompt;
1349 int response;
1350
1351 /* Just Say Yes if the preference says not to confirm. */
1352 if (!should_confirm_trash ()) {
1353 return TRUE;
1354 }
1355
1356 prompt = f (_("Empty all items from Trash?"));
1357
1358 response = run_warning (job,
1359 prompt,
1360 f(_("All items in the Trash will be permanently deleted.")),
1361 NULL,
1362 FALSE,
1363 GTK_STOCK_CANCEL, _("Empty _Trash"),
1364 NULL);
1365
1366 return (response == 1);
1367 }
1368
1369 static gboolean
1370 confirm_delete_directly (CommonJob *job,
1371 GList *files)
1372 {
1373 char *prompt;
1374 int file_count;
1375 int response;
1376
1377 /* Just Say Yes if the preference says not to confirm. */
1378 if (!should_confirm_trash ()) {
1379 return TRUE;
1380 }
1381
1382 file_count = g_list_length (files);
1383 g_assert (file_count > 0);
1384
1385 if (can_delete_files_without_confirm (files)) {
1386 return TRUE;
1387 }
1388
1389 if (file_count == 1) {
1390 prompt = f (_("Are you sure you want to permanently delete “%B”?"),
1391 files->data);
1392 } else {
1393 prompt = f (ngettext("Are you sure you want to permanently delete "
1394 "the %'d selected item?",
1395 "Are you sure you want to permanently delete "
1396 "the %'d selected items?", file_count),
1397 file_count);
1398 }
1399
1400 response = run_warning (job,
1401 prompt,
1402 f (_("If you delete an item, it will be permanently lost.")),
1403 NULL,
1404 FALSE,
1405 GTK_STOCK_CANCEL, GTK_STOCK_DELETE,
1406 NULL);
1407
1408 return response == 1;
1409 }
1410
1411 static void
1412 report_delete_progress (CommonJob *job,
1413 SourceInfo *source_info,
1414 TransferInfo *transfer_info)
1415 {
1416 int files_left;
1417 double elapsed, transfer_rate;
1418 int remaining_time;
1419 gint64 now;
1420 char *files_left_s;
1421
1422 now = g_get_monotonic_time ();
1423 if (transfer_info->last_report_time != 0 &&
1424 ABS ((gint64)(transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC) {
1425 return;
1426 }
1427 transfer_info->last_report_time = now;
1428
1429 files_left = source_info->num_files - transfer_info->num_files;
1430
1431 /* Races and whatnot could cause this to be negative... */
1432 if (files_left < 0) {
1433 files_left = 1;
1434 }
1435
1436 files_left_s = f (ngettext ("%'d file left to delete",
1437 "%'d files left to delete",
1438 files_left),
1439 files_left);
1440
1441 nautilus_progress_info_take_status (job->progress,
1442 f (_("Deleting files")));
1443
1444 elapsed = g_timer_elapsed (job->time, NULL);
1445 if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE) {
1446
1447 nautilus_progress_info_set_details (job->progress, files_left_s);
1448 } else {
1449 char *details, *time_left_s;
1450 transfer_rate = transfer_info->num_files / elapsed;
1451 remaining_time = files_left / transfer_rate;
1452
1453 /* To translators: %T will expand to a time like "2 minutes".
1454 * The singular/plural form will be used depending on the remaining time (i.e. the %T argument).
1455 */
1456 time_left_s = f (ngettext ("%T left",
1457 "%T left",
1458 seconds_count_format_time_units (remaining_time)),
1459 remaining_time);
1460
1461 details = g_strconcat (files_left_s, "\xE2\x80\x94", time_left_s, NULL);
1462 nautilus_progress_info_take_details (job->progress, details);
1463
1464 g_free (time_left_s);
1465 }
1466
1467 g_free (files_left_s);
1468
1469 if (source_info->num_files != 0) {
1470 nautilus_progress_info_set_progress (job->progress, transfer_info->num_files, source_info->num_files);
1471 }
1472 }
1473
1474 static void delete_file (CommonJob *job, GFile *file,
1475 gboolean *skipped_file,
1476 SourceInfo *source_info,
1477 TransferInfo *transfer_info,
1478 gboolean toplevel);
1479
1480 static void
1481 delete_dir (CommonJob *job, GFile *dir,
1482 gboolean *skipped_file,
1483 SourceInfo *source_info,
1484 TransferInfo *transfer_info,
1485 gboolean toplevel)
1486 {
1487 GFileInfo *info;
1488 GError *error;
1489 GFile *file;
1490 GFileEnumerator *enumerator;
1491 char *primary, *secondary, *details;
1492 int response;
1493 gboolean skip_error;
1494 gboolean local_skipped_file;
1495
1496 local_skipped_file = FALSE;
1497
1498 skip_error = should_skip_readdir_error (job, dir);
1499 retry:
1500 error = NULL;
1501 enumerator = g_file_enumerate_children (dir,
1502 G_FILE_ATTRIBUTE_STANDARD_NAME,
1503 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
1504 job->cancellable,
1505 &error);
1506 if (enumerator) {
1507 error = NULL;
1508
1509 while (!job_aborted (job) &&
1510 (info = g_file_enumerator_next_file (enumerator, job->cancellable, skip_error?NULL:&error)) != NULL) {
1511 file = g_file_get_child (dir,
1512 g_file_info_get_name (info));
1513 delete_file (job, file, &local_skipped_file, source_info, transfer_info, FALSE);
1514 g_object_unref (file);
1515 g_object_unref (info);
1516 }
1517 g_file_enumerator_close (enumerator, job->cancellable, NULL);
1518 g_object_unref (enumerator);
1519
1520 if (error && IS_IO_ERROR (error, CANCELLED)) {
1521 g_error_free (error);
1522 } else if (error) {
1523 primary = f (_("Error while deleting."));
1524 details = NULL;
1525
1526 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
1527 secondary = f (_("Files in the folder “%B” cannot be deleted because you do "
1528 "not have permissions to see them."), dir);
1529 } else {
1530 secondary = f (_("There was an error getting information about the files in the folder “%B”."), dir);
1531 details = error->message;
1532 }
1533
1534 response = run_warning (job,
1535 primary,
1536 secondary,
1537 details,
1538 FALSE,
1539 GTK_STOCK_CANCEL, _("_Skip files"),
1540 NULL);
1541
1542 g_error_free (error);
1543
1544 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
1545 abort_job (job);
1546 } else if (response == 1) {
1547 /* Skip: Do Nothing */
1548 local_skipped_file = TRUE;
1549 } else {
1550 g_assert_not_reached ();
1551 }
1552 }
1553
1554 } else if (IS_IO_ERROR (error, CANCELLED)) {
1555 g_error_free (error);
1556 } else {
1557 primary = f (_("Error while deleting."));
1558 details = NULL;
1559 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
1560 secondary = f (_("The folder “%B” cannot be deleted because you do not have "
1561 "permissions to read it."), dir);
1562 } else {
1563 secondary = f (_("There was an error reading the folder “%B”."), dir);
1564 details = error->message;
1565 }
1566
1567 response = run_warning (job,
1568 primary,
1569 secondary,
1570 details,
1571 FALSE,
1572 GTK_STOCK_CANCEL, SKIP, RETRY,
1573 NULL);
1574
1575 g_error_free (error);
1576
1577 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
1578 abort_job (job);
1579 } else if (response == 1) {
1580 /* Skip: Do Nothing */
1581 local_skipped_file = TRUE;
1582 } else if (response == 2) {
1583 goto retry;
1584 } else {
1585 g_assert_not_reached ();
1586 }
1587 }
1588
1589 if (!job_aborted (job) &&
1590 /* Don't delete dir if there was a skipped file */
1591 !local_skipped_file) {
1592 if (!g_file_delete (dir, job->cancellable, &error)) {
1593 if (job->skip_all_error) {
1594 goto skip;
1595 }
1596 primary = f (_("Error while deleting."));
1597 secondary = f (_("Could not remove the folder %B."), dir);
1598 details = error->message;
1599
1600 response = run_warning (job,
1601 primary,
1602 secondary,
1603 details,
1604 (source_info->num_files - transfer_info->num_files) > 1,
1605 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
1606 NULL);
1607
1608 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
1609 abort_job (job);
1610 } else if (response == 1) { /* skip all */
1611 job->skip_all_error = TRUE;
1612 local_skipped_file = TRUE;
1613 } else if (response == 2) { /* skip */
1614 local_skipped_file = TRUE;
1615 } else {
1616 g_assert_not_reached ();
1617 }
1618
1619 skip:
1620 g_error_free (error);
1621 } else {
1622 nautilus_file_changes_queue_file_removed (dir);
1623 transfer_info->num_files ++;
1624 report_delete_progress (job, source_info, transfer_info);
1625 return;
1626 }
1627 }
1628
1629 if (local_skipped_file) {
1630 *skipped_file = TRUE;
1631 }
1632 }
1633
1634 static void
1635 delete_file (CommonJob *job, GFile *file,
1636 gboolean *skipped_file,
1637 SourceInfo *source_info,
1638 TransferInfo *transfer_info,
1639 gboolean toplevel)
1640 {
1641 GError *error;
1642 char *primary, *secondary, *details;
1643 int response;
1644
1645 if (should_skip_file (job, file)) {
1646 *skipped_file = TRUE;
1647 return;
1648 }
1649
1650 error = NULL;
1651 if (g_file_delete (file, job->cancellable, &error)) {
1652 nautilus_file_changes_queue_file_removed (file);
1653 transfer_info->num_files ++;
1654 report_delete_progress (job, source_info, transfer_info);
1655 return;
1656 }
1657
1658 if (IS_IO_ERROR (error, NOT_EMPTY)) {
1659 g_error_free (error);
1660 delete_dir (job, file,
1661 skipped_file,
1662 source_info, transfer_info,
1663 toplevel);
1664 return;
1665
1666 } else if (IS_IO_ERROR (error, CANCELLED)) {
1667 g_error_free (error);
1668
1669 } else {
1670 if (job->skip_all_error) {
1671 goto skip;
1672 }
1673 primary = f (_("Error while deleting."));
1674 secondary = f (_("There was an error deleting %B."), file);
1675 details = error->message;
1676
1677 response = run_warning (job,
1678 primary,
1679 secondary,
1680 details,
1681 (source_info->num_files - transfer_info->num_files) > 1,
1682 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
1683 NULL);
1684
1685 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
1686 abort_job (job);
1687 } else if (response == 1) { /* skip all */
1688 job->skip_all_error = TRUE;
1689 } else if (response == 2) { /* skip */
1690 /* do nothing */
1691 } else {
1692 g_assert_not_reached ();
1693 }
1694 skip:
1695 g_error_free (error);
1696 }
1697
1698 *skipped_file = TRUE;
1699 }
1700
1701 static void
1702 delete_files (CommonJob *job, GList *files, int *files_skipped)
1703 {
1704 GList *l;
1705 GFile *file;
1706 SourceInfo source_info;
1707 TransferInfo transfer_info;
1708 gboolean skipped_file;
1709
1710 if (job_aborted (job)) {
1711 return;
1712 }
1713
1714 scan_sources (files,
1715 &source_info,
1716 job,
1717 OP_KIND_DELETE);
1718 if (job_aborted (job)) {
1719 return;
1720 }
1721
1722 g_timer_start (job->time);
1723
1724 memset (&transfer_info, 0, sizeof (transfer_info));
1725 report_delete_progress (job, &source_info, &transfer_info);
1726
1727 for (l = files;
1728 l != NULL && !job_aborted (job);
1729 l = l->next) {
1730 file = l->data;
1731
1732 skipped_file = FALSE;
1733 delete_file (job, file,
1734 &skipped_file,
1735 &source_info, &transfer_info,
1736 TRUE);
1737 if (skipped_file) {
1738 (*files_skipped)++;
1739 }
1740 }
1741 }
1742
1743 static void
1744 report_trash_progress (CommonJob *job,
1745 int files_trashed,
1746 int total_files)
1747 {
1748 int files_left;
1749 char *s;
1750
1751 files_left = total_files - files_trashed;
1752
1753 nautilus_progress_info_take_status (job->progress,
1754 f (_("Moving files to trash")));
1755
1756 s = f (ngettext ("%'d file left to trash",
1757 "%'d files left to trash",
1758 files_left),
1759 files_left);
1760 nautilus_progress_info_take_details (job->progress, s);
1761
1762 if (total_files != 0) {
1763 nautilus_progress_info_set_progress (job->progress, files_trashed, total_files);
1764 }
1765 }
1766
1767
1768 static void
1769 trash_files (CommonJob *job, GList *files, int *files_skipped)
1770 {
1771 GList *l;
1772 GFile *file;
1773 GList *to_delete;
1774 GError *error;
1775 int total_files, files_trashed;
1776 char *primary, *secondary, *details;
1777 int response;
1778
1779 if (job_aborted (job)) {
1780 return;
1781 }
1782
1783 total_files = g_list_length (files);
1784 files_trashed = 0;
1785
1786 report_trash_progress (job, files_trashed, total_files);
1787
1788 to_delete = NULL;
1789 for (l = files;
1790 l != NULL && !job_aborted (job);
1791 l = l->next) {
1792 file = l->data;
1793
1794 error = NULL;
1795
1796 if (!g_file_trash (file, job->cancellable, &error)) {
1797 if (job->skip_all_error) {
1798 (*files_skipped)++;
1799 goto skip;
1800 }
1801
1802 if (job->delete_all) {
1803 to_delete = g_list_prepend (to_delete, file);
1804 goto skip;
1805 }
1806
1807 /* Translators: %B is a file name */
1808 primary = f (_("“%B” can't be put in the trash. Do you want to delete it immediately?"), file);
1809 details = NULL;
1810 secondary = NULL;
1811 if (!IS_IO_ERROR (error, NOT_SUPPORTED)) {
1812 details = error->message;
1813 } else if (!g_file_is_native (file)) {
1814 secondary = f (_("This remote location does not support sending items to the trash."));
1815 }
1816
1817 response = run_question (job,
1818 primary,
1819 secondary,
1820 details,
1821 (total_files - files_trashed) > 1,
1822 GTK_STOCK_CANCEL, SKIP_ALL, SKIP, DELETE_ALL, GTK_STOCK_DELETE,
1823 NULL);
1824
1825 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
1826 ((DeleteJob *) job)->user_cancel = TRUE;
1827 abort_job (job);
1828 } else if (response == 1) { /* skip all */
1829 (*files_skipped)++;
1830 job->skip_all_error = TRUE;
1831 } else if (response == 2) { /* skip */
1832 (*files_skipped)++;
1833 } else if (response == 3) { /* delete all */
1834 to_delete = g_list_prepend (to_delete, file);
1835 job->delete_all = TRUE;
1836 } else if (response == 4) { /* delete */
1837 to_delete = g_list_prepend (to_delete, file);
1838 }
1839
1840 skip:
1841 g_error_free (error);
1842 total_files--;
1843 } else {
1844 nautilus_file_changes_queue_file_removed (file);
1845
1846 if (job->undo_info != NULL) {
1847 nautilus_file_undo_info_trash_add_file (NAUTILUS_FILE_UNDO_INFO_TRASH (job->undo_info), file);
1848 }
1849
1850 files_trashed++;
1851 report_trash_progress (job, files_trashed, total_files);
1852 }
1853 }
1854
1855 if (to_delete) {
1856 to_delete = g_list_reverse (to_delete);
1857 delete_files (job, to_delete, files_skipped);
1858 g_list_free (to_delete);
1859 }
1860 }
1861
1862 static gboolean
1863 delete_job_done (gpointer user_data)
1864 {
1865 DeleteJob *job;
1866 GHashTable *debuting_uris;
1867
1868 job = user_data;
1869
1870 g_list_free_full (job->files, g_object_unref);
1871
1872 if (job->done_callback) {
1873 debuting_uris = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
1874 job->done_callback (debuting_uris, job->user_cancel, job->done_callback_data);
1875 g_hash_table_unref (debuting_uris);
1876 }
1877
1878 finalize_common ((CommonJob *)job);
1879
1880 nautilus_file_changes_consume_changes (TRUE);
1881
1882 return FALSE;
1883 }
1884
1885 static gboolean
1886 delete_job (GIOSchedulerJob *io_job,
1887 GCancellable *cancellable,
1888 gpointer user_data)
1889 {
1890 DeleteJob *job = user_data;
1891 GList *to_trash_files;
1892 GList *to_delete_files;
1893 GList *l;
1894 GFile *file;
1895 gboolean confirmed;
1896 CommonJob *common;
1897 gboolean must_confirm_delete_in_trash;
1898 gboolean must_confirm_delete;
1899 int files_skipped;
1900
1901 common = (CommonJob *)job;
1902 common->io_job = io_job;
1903
1904 nautilus_progress_info_start (job->common.progress);
1905
1906 to_trash_files = NULL;
1907 to_delete_files = NULL;
1908
1909 must_confirm_delete_in_trash = FALSE;
1910 must_confirm_delete = FALSE;
1911 files_skipped = 0;
1912
1913 for (l = job->files; l != NULL; l = l->next) {
1914 file = l->data;
1915
1916 if (job->try_trash &&
1917 g_file_has_uri_scheme (file, "trash")) {
1918 must_confirm_delete_in_trash = TRUE;
1919 to_delete_files = g_list_prepend (to_delete_files, file);
1920 } else if (can_delete_without_confirm (file)) {
1921 to_delete_files = g_list_prepend (to_delete_files, file);
1922 } else {
1923 if (job->try_trash) {
1924 to_trash_files = g_list_prepend (to_trash_files, file);
1925 } else {
1926 must_confirm_delete = TRUE;
1927 to_delete_files = g_list_prepend (to_delete_files, file);
1928 }
1929 }
1930 }
1931
1932 if (to_delete_files != NULL) {
1933 to_delete_files = g_list_reverse (to_delete_files);
1934 confirmed = TRUE;
1935 if (must_confirm_delete_in_trash) {
1936 confirmed = confirm_delete_from_trash (common, to_delete_files);
1937 } else if (must_confirm_delete) {
1938 confirmed = confirm_delete_directly (common, to_delete_files);
1939 }
1940 if (confirmed) {
1941 delete_files (common, to_delete_files, &files_skipped);
1942 } else {
1943 job->user_cancel = TRUE;
1944 }
1945 }
1946
1947 if (to_trash_files != NULL) {
1948 to_trash_files = g_list_reverse (to_trash_files);
1949
1950 trash_files (common, to_trash_files, &files_skipped);
1951 }
1952
1953 g_list_free (to_trash_files);
1954 g_list_free (to_delete_files);
1955
1956 if (files_skipped == g_list_length (job->files)) {
1957 /* User has skipped all files, report user cancel */
1958 job->user_cancel = TRUE;
1959 }
1960
1961 g_io_scheduler_job_send_to_mainloop_async (io_job,
1962 delete_job_done,
1963 job,
1964 NULL);
1965
1966 return FALSE;
1967 }
1968
1969 static void
1970 trash_or_delete_internal (GList *files,
1971 GtkWindow *parent_window,
1972 gboolean try_trash,
1973 NautilusDeleteCallback done_callback,
1974 gpointer done_callback_data)
1975 {
1976 DeleteJob *job;
1977
1978 /* TODO: special case desktop icon link files ... */
1979
1980 job = op_job_new (DeleteJob, parent_window);
1981 job->files = eel_g_object_list_copy (files);
1982 job->try_trash = try_trash;
1983 job->user_cancel = FALSE;
1984 job->done_callback = done_callback;
1985 job->done_callback_data = done_callback_data;
1986
1987 if (try_trash) {
1988 inhibit_power_manager ((CommonJob *)job, _("Trashing Files"));
1989 } else {
1990 inhibit_power_manager ((CommonJob *)job, _("Deleting Files"));
1991 }
1992
1993 if (try_trash && !nautilus_file_undo_manager_pop_flag ()) {
1994 job->common.undo_info = nautilus_file_undo_info_trash_new (g_list_length (files));
1995 }
1996
1997 g_io_scheduler_push_job (delete_job,
1998 job,
1999 NULL,
2000 0,
2001 NULL);
2002 }
2003
2004 void
2005 nautilus_file_operations_trash_or_delete (GList *files,
2006 GtkWindow *parent_window,
2007 NautilusDeleteCallback done_callback,
2008 gpointer done_callback_data)
2009 {
2010 trash_or_delete_internal (files, parent_window,
2011 TRUE,
2012 done_callback, done_callback_data);
2013 }
2014
2015 void
2016 nautilus_file_operations_delete (GList *files,
2017 GtkWindow *parent_window,
2018 NautilusDeleteCallback done_callback,
2019 gpointer done_callback_data)
2020 {
2021 trash_or_delete_internal (files, parent_window,
2022 FALSE,
2023 done_callback, done_callback_data);
2024 }
2025
2026
2027
2028 typedef struct {
2029 gboolean eject;
2030 GMount *mount;
2031 GMountOperation *mount_operation;
2032 GtkWindow *parent_window;
2033 NautilusUnmountCallback callback;
2034 gpointer callback_data;
2035 } UnmountData;
2036
2037 static void
2038 unmount_data_free (UnmountData *data)
2039 {
2040 if (data->parent_window) {
2041 g_object_remove_weak_pointer (G_OBJECT (data->parent_window),
2042 (gpointer *) &data->parent_window);
2043 }
2044
2045 g_clear_object (&data->mount_operation);
2046 g_object_unref (data->mount);
2047 g_free (data);
2048 }
2049
2050 static void
2051 unmount_mount_callback (GObject *source_object,
2052 GAsyncResult *res,
2053 gpointer user_data)
2054 {
2055 UnmountData *data = user_data;
2056 GError *error;
2057 char *primary;
2058 gboolean unmounted;
2059
2060 error = NULL;
2061 if (data->eject) {
2062 unmounted = g_mount_eject_with_operation_finish (G_MOUNT (source_object),
2063 res, &error);
2064 } else {
2065 unmounted = g_mount_unmount_with_operation_finish (G_MOUNT (source_object),
2066 res, &error);
2067 }
2068
2069 if (! unmounted) {
2070 if (error->code != G_IO_ERROR_FAILED_HANDLED) {
2071 if (data->eject) {
2072 primary = f (_("Unable to eject %V"), source_object);
2073 } else {
2074 primary = f (_("Unable to unmount %V"), source_object);
2075 }
2076 eel_show_error_dialog (primary,
2077 error->message,
2078 data->parent_window);
2079 g_free (primary);
2080 }
2081 }
2082
2083 if (data->callback) {
2084 data->callback (data->callback_data);
2085 }
2086
2087 if (error != NULL) {
2088 g_error_free (error);
2089 }
2090
2091 unmount_data_free (data);
2092 }
2093
2094 static void
2095 do_unmount (UnmountData *data)
2096 {
2097 GMountOperation *mount_op;
2098
2099 if (data->mount_operation) {
2100 mount_op = g_object_ref (data->mount_operation);
2101 } else {
2102 mount_op = gtk_mount_operation_new (data->parent_window);
2103 }
2104 if (data->eject) {
2105 g_mount_eject_with_operation (data->mount,
2106 0,
2107 mount_op,
2108 NULL,
2109 unmount_mount_callback,
2110 data);
2111 } else {
2112 g_mount_unmount_with_operation (data->mount,
2113 0,
2114 mount_op,
2115 NULL,
2116 unmount_mount_callback,
2117 data);
2118 }
2119 g_object_unref (mount_op);
2120 }
2121
2122 static gboolean
2123 dir_has_files (GFile *dir)
2124 {
2125 GFileEnumerator *enumerator;
2126 gboolean res;
2127 GFileInfo *file_info;
2128
2129 res = FALSE;
2130
2131 enumerator = g_file_enumerate_children (dir,
2132 G_FILE_ATTRIBUTE_STANDARD_NAME,
2133 0,
2134 NULL, NULL);
2135 if (enumerator) {
2136 file_info = g_file_enumerator_next_file (enumerator, NULL, NULL);
2137 if (file_info != NULL) {
2138 res = TRUE;
2139 g_object_unref (file_info);
2140 }
2141
2142 g_file_enumerator_close (enumerator, NULL, NULL);
2143 g_object_unref (enumerator);
2144 }
2145
2146
2147 return res;
2148 }
2149
2150 static GList *
2151 get_trash_dirs_for_mount (GMount *mount)
2152 {
2153 GFile *root;
2154 GFile *trash;
2155 char *relpath;
2156 GList *list;
2157
2158 root = g_mount_get_root (mount);
2159 if (root == NULL) {
2160 return NULL;
2161 }
2162
2163 list = NULL;
2164
2165 if (g_file_is_native (root)) {
2166 relpath = g_strdup_printf (".Trash/%d", getuid ());
2167 trash = g_file_resolve_relative_path (root, relpath);
2168 g_free (relpath);
2169
2170 list = g_list_prepend (list, g_file_get_child (trash, "files"));
2171 list = g_list_prepend (list, g_file_get_child (trash, "info"));
2172
2173 g_object_unref (trash);
2174
2175 relpath = g_strdup_printf (".Trash-%d", getuid ());
2176 trash = g_file_get_child (root, relpath);
2177 g_free (relpath);
2178
2179 list = g_list_prepend (list, g_file_get_child (trash, "files"));
2180 list = g_list_prepend (list, g_file_get_child (trash, "info"));
2181
2182 g_object_unref (trash);
2183 }
2184
2185 g_object_unref (root);
2186
2187 return list;
2188 }
2189
2190 static gboolean
2191 has_trash_files (GMount *mount)
2192 {
2193 GList *dirs, *l;
2194 GFile *dir;
2195 gboolean res;
2196
2197 dirs = get_trash_dirs_for_mount (mount);
2198
2199 res = FALSE;
2200
2201 for (l = dirs; l != NULL; l = l->next) {
2202 dir = l->data;
2203
2204 if (dir_has_files (dir)) {
2205 res = TRUE;
2206 break;
2207 }
2208 }
2209
2210 g_list_free_full (dirs, g_object_unref);
2211
2212 return res;
2213 }
2214
2215
2216 static gint
2217 prompt_empty_trash (GtkWindow *parent_window)
2218 {
2219 gint result;
2220 GtkWidget *dialog;
2221 GdkScreen *screen;
2222
2223 screen = NULL;
2224 if (parent_window != NULL) {
2225 screen = gtk_widget_get_screen (GTK_WIDGET (parent_window));
2226 }
2227
2228 /* Do we need to be modal ? */
2229 dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
2230 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
2231 _("Do you want to empty the trash before you unmount?"));
2232 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
2233 _("In order to regain the "
2234 "free space on this volume "
2235 "the trash must be emptied. "
2236 "All trashed items on the volume "
2237 "will be permanently lost."));
2238 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
2239 _("Do _not Empty Trash"), GTK_RESPONSE_REJECT,
2240 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2241 _("Empty _Trash"), GTK_RESPONSE_ACCEPT, NULL);
2242 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
2243 gtk_window_set_title (GTK_WINDOW (dialog), ""); /* as per HIG */
2244 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
2245 if (screen) {
2246 gtk_window_set_screen (GTK_WINDOW (dialog), screen);
2247 }
2248 atk_object_set_role (gtk_widget_get_accessible (dialog), ATK_ROLE_ALERT);
2249 gtk_window_set_wmclass (GTK_WINDOW (dialog), "empty_trash",
2250 "Nautilus");
2251
2252 /* Make transient for the window group */
2253 gtk_widget_realize (dialog);
2254 if (screen != NULL) {
2255 gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (dialog)),
2256 gdk_screen_get_root_window (screen));
2257 }
2258
2259 result = gtk_dialog_run (GTK_DIALOG (dialog));
2260 gtk_widget_destroy (dialog);
2261 return result;
2262 }
2263
2264 static void
2265 empty_trash_for_unmount_done (gboolean success,
2266 gpointer user_data)
2267 {
2268 UnmountData *data = user_data;
2269 do_unmount (data);
2270 }
2271
2272 void
2273 nautilus_file_operations_unmount_mount_full (GtkWindow *parent_window,
2274 GMount *mount,
2275 GMountOperation *mount_operation,
2276 gboolean eject,
2277 gboolean check_trash,
2278 NautilusUnmountCallback callback,
2279 gpointer callback_data)
2280 {
2281 UnmountData *data;
2282 int response;
2283
2284 data = g_new0 (UnmountData, 1);
2285 data->callback = callback;
2286 data->callback_data = callback_data;
2287 if (parent_window) {
2288 data->parent_window = parent_window;
2289 g_object_add_weak_pointer (G_OBJECT (data->parent_window),
2290 (gpointer *) &data->parent_window);
2291
2292 }
2293 if (mount_operation) {
2294 data->mount_operation = g_object_ref (mount_operation);
2295 }
2296 data->eject = eject;
2297 data->mount = g_object_ref (mount);
2298
2299 if (check_trash && has_trash_files (mount)) {
2300 response = prompt_empty_trash (parent_window);
2301
2302 if (response == GTK_RESPONSE_ACCEPT) {
2303 EmptyTrashJob *job;
2304
2305 job = op_job_new (EmptyTrashJob, parent_window);
2306 job->should_confirm = FALSE;
2307 job->trash_dirs = get_trash_dirs_for_mount (mount);
2308 job->done_callback = empty_trash_for_unmount_done;
2309 job->done_callback_data = data;
2310 g_io_scheduler_push_job (empty_trash_job,
2311 job,
2312 NULL,
2313 0,
2314 NULL);
2315 return;
2316 } else if (response == GTK_RESPONSE_CANCEL) {
2317 if (callback) {
2318 callback (callback_data);
2319 }
2320
2321 unmount_data_free (data);
2322 return;
2323 }
2324 }
2325
2326 do_unmount (data);
2327 }
2328
2329 void
2330 nautilus_file_operations_unmount_mount (GtkWindow *parent_window,
2331 GMount *mount,
2332 gboolean eject,
2333 gboolean check_trash)
2334 {
2335 nautilus_file_operations_unmount_mount_full (parent_window, mount, NULL, eject,
2336 check_trash, NULL, NULL);
2337 }
2338
2339 static void
2340 mount_callback_data_notify (gpointer data,
2341 GObject *object)
2342 {
2343 GMountOperation *mount_op;
2344
2345 mount_op = G_MOUNT_OPERATION (data);
2346 g_object_set_data (G_OBJECT (mount_op), "mount-callback", NULL);
2347 g_object_set_data (G_OBJECT (mount_op), "mount-callback-data", NULL);
2348 }
2349
2350 static void
2351 volume_mount_cb (GObject *source_object,
2352 GAsyncResult *res,
2353 gpointer user_data)
2354 {
2355 NautilusMountCallback mount_callback;
2356 GObject *mount_callback_data_object;
2357 GMountOperation *mount_op = user_data;
2358 GError *error;
2359 char *primary;
2360 char *name;
2361 gboolean success;
2362
2363 success = TRUE;
2364 error = NULL;
2365 if (!g_volume_mount_finish (G_VOLUME (source_object), res, &error)) {
2366 if (error->code != G_IO_ERROR_FAILED_HANDLED &&
2367 error->code != G_IO_ERROR_ALREADY_MOUNTED) {
2368 GtkWindow *parent;
2369
2370 parent = gtk_mount_operation_get_parent (GTK_MOUNT_OPERATION (mount_op));
2371 name = g_volume_get_name (G_VOLUME (source_object));
2372 primary = g_strdup_printf (_("Unable to access “%s”"), name);
2373 g_free (name);
2374 success = FALSE;
2375 eel_show_error_dialog (primary,
2376 error->message,
2377 parent);
2378 g_free (primary);
2379 }
2380 g_error_free (error);
2381 }
2382
2383 mount_callback = (NautilusMountCallback)
2384 g_object_get_data (G_OBJECT (mount_op), "mount-callback");
2385 mount_callback_data_object =
2386 g_object_get_data (G_OBJECT (mount_op), "mount-callback-data");
2387
2388 if (mount_callback != NULL) {
2389 (* mount_callback) (G_VOLUME (source_object),
2390 success,
2391 mount_callback_data_object);
2392
2393 if (mount_callback_data_object != NULL) {
2394 g_object_weak_unref (mount_callback_data_object,
2395 mount_callback_data_notify,
2396 mount_op);
2397 }
2398 }
2399
2400 g_object_unref (mount_op);
2401 }
2402
2403
2404 void
2405 nautilus_file_operations_mount_volume (GtkWindow *parent_window,
2406 GVolume *volume)
2407 {
2408 nautilus_file_operations_mount_volume_full (parent_window, volume,
2409 NULL, NULL);
2410 }
2411
2412 void
2413 nautilus_file_operations_mount_volume_full (GtkWindow *parent_window,
2414 GVolume *volume,
2415 NautilusMountCallback mount_callback,
2416 GObject *mount_callback_data_object)
2417 {
2418 GMountOperation *mount_op;
2419
2420 mount_op = gtk_mount_operation_new (parent_window);
2421 g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
2422 g_object_set_data (G_OBJECT (mount_op),
2423 "mount-callback",
2424 mount_callback);
2425
2426 if (mount_callback != NULL &&
2427 mount_callback_data_object != NULL) {
2428 g_object_weak_ref (mount_callback_data_object,
2429 mount_callback_data_notify,
2430 mount_op);
2431 }
2432 g_object_set_data (G_OBJECT (mount_op),
2433 "mount-callback-data",
2434 mount_callback_data_object);
2435
2436 g_volume_mount (volume, 0, mount_op, NULL, volume_mount_cb, mount_op);
2437 }
2438
2439 static void
2440 report_count_progress (CommonJob *job,
2441 SourceInfo *source_info)
2442 {
2443 char *s;
2444
2445 switch (source_info->op) {
2446 default:
2447 case OP_KIND_COPY:
2448 s = f (ngettext("Preparing to copy %'d file (%S)",
2449 "Preparing to copy %'d files (%S)",
2450 source_info->num_files),
2451 source_info->num_files, source_info->num_bytes);
2452 break;
2453 case OP_KIND_MOVE:
2454 s = f (ngettext("Preparing to move %'d file (%S)",
2455 "Preparing to move %'d files (%S)",
2456 source_info->num_files),
2457 source_info->num_files, source_info->num_bytes);
2458 break;
2459 case OP_KIND_DELETE:
2460 s = f (ngettext("Preparing to delete %'d file (%S)",
2461 "Preparing to delete %'d files (%S)",
2462 source_info->num_files),
2463 source_info->num_files, source_info->num_bytes);
2464 break;
2465 case OP_KIND_TRASH:
2466 s = f (ngettext("Preparing to trash %'d file",
2467 "Preparing to trash %'d files",
2468 source_info->num_files),
2469 source_info->num_files);
2470 break;
2471 }
2472
2473 nautilus_progress_info_take_details (job->progress, s);
2474 nautilus_progress_info_pulse_progress (job->progress);
2475 }
2476
2477 static void
2478 count_file (GFileInfo *info,
2479 CommonJob *job,
2480 SourceInfo *source_info)
2481 {
2482 source_info->num_files += 1;
2483 source_info->num_bytes += g_file_info_get_size (info);
2484
2485 if (source_info->num_files_since_progress++ > 100) {
2486 report_count_progress (job, source_info);
2487 source_info->num_files_since_progress = 0;
2488 }
2489 }
2490
2491 static char *
2492 get_scan_primary (OpKind kind)
2493 {
2494 switch (kind) {
2495 default:
2496 case OP_KIND_COPY:
2497 return f (_("Error while copying."));
2498 case OP_KIND_MOVE:
2499 return f (_("Error while moving."));
2500 case OP_KIND_DELETE:
2501 return f (_("Error while deleting."));
2502 case OP_KIND_TRASH:
2503 return f (_("Error while moving files to trash."));
2504 }
2505 }
2506
2507 static void
2508 scan_dir (GFile *dir,
2509 SourceInfo *source_info,
2510 CommonJob *job,
2511 GQueue *dirs)
2512 {
2513 GFileInfo *info;
2514 GError *error;
2515 GFile *subdir;
2516 GFileEnumerator *enumerator;
2517 char *primary, *secondary, *details;
2518 int response;
2519 SourceInfo saved_info;
2520
2521 saved_info = *source_info;
2522
2523 retry:
2524 error = NULL;
2525 enumerator = g_file_enumerate_children (dir,
2526 G_FILE_ATTRIBUTE_STANDARD_NAME","
2527 G_FILE_ATTRIBUTE_STANDARD_TYPE","
2528 G_FILE_ATTRIBUTE_STANDARD_SIZE,
2529 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2530 job->cancellable,
2531 &error);
2532 if (enumerator) {
2533 error = NULL;
2534 while ((info = g_file_enumerator_next_file (enumerator, job->cancellable, &error)) != NULL) {
2535 count_file (info, job, source_info);
2536
2537 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
2538 subdir = g_file_get_child (dir,
2539 g_file_info_get_name (info));
2540
2541 /* Push to head, since we want depth-first */
2542 g_queue_push_head (dirs, subdir);
2543 }
2544
2545 g_object_unref (info);
2546 }
2547 g_file_enumerator_close (enumerator, job->cancellable, NULL);
2548 g_object_unref (enumerator);
2549
2550 if (error && IS_IO_ERROR (error, CANCELLED)) {
2551 g_error_free (error);
2552 } else if (error) {
2553 primary = get_scan_primary (source_info->op);
2554 details = NULL;
2555
2556 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
2557 secondary = f (_("Files in the folder “%B” cannot be handled because you do "
2558 "not have permissions to see them."), dir);
2559 } else {
2560 secondary = f (_("There was an error getting information about the files in the folder “%B”."), dir);
2561 details = error->message;
2562 }
2563
2564 response = run_warning (job,
2565 primary,
2566 secondary,
2567 details,
2568 FALSE,
2569 GTK_STOCK_CANCEL, RETRY, SKIP,
2570 NULL);
2571
2572 g_error_free (error);
2573
2574 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
2575 abort_job (job);
2576 } else if (response == 1) {
2577 *source_info = saved_info;
2578 goto retry;
2579 } else if (response == 2) {
2580 skip_readdir_error (job, dir);
2581 } else {
2582 g_assert_not_reached ();
2583 }
2584 }
2585
2586 } else if (job->skip_all_error) {
2587 g_error_free (error);
2588 skip_file (job, dir);
2589 } else if (IS_IO_ERROR (error, CANCELLED)) {
2590 g_error_free (error);
2591 } else {
2592 primary = get_scan_primary (source_info->op);
2593 details = NULL;
2594
2595 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
2596 secondary = f (_("The folder “%B” cannot be handled because you do not have "
2597 "permissions to read it."), dir);
2598 } else {
2599 secondary = f (_("There was an error reading the folder “%B”."), dir);
2600 details = error->message;
2601 }
2602 /* set show_all to TRUE here, as we don't know how many
2603 * files we'll end up processing yet.
2604 */
2605 response = run_warning (job,
2606 primary,
2607 secondary,
2608 details,
2609 TRUE,
2610 GTK_STOCK_CANCEL, SKIP_ALL, SKIP, RETRY,
2611 NULL);
2612
2613 g_error_free (error);
2614
2615 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
2616 abort_job (job);
2617 } else if (response == 1 || response == 2) {
2618 if (response == 1) {
2619 job->skip_all_error = TRUE;
2620 }
2621 skip_file (job, dir);
2622 } else if (response == 3) {
2623 goto retry;
2624 } else {
2625 g_assert_not_reached ();
2626 }
2627 }
2628 }
2629
2630 static void
2631 scan_file (GFile *file,
2632 SourceInfo *source_info,
2633 CommonJob *job)
2634 {
2635 GFileInfo *info;
2636 GError *error;
2637 GQueue *dirs;
2638 GFile *dir;
2639 char *primary;
2640 char *secondary;
2641 char *details;
2642 int response;
2643
2644 dirs = g_queue_new ();
2645
2646 retry:
2647 error = NULL;
2648 info = g_file_query_info (file,
2649 G_FILE_ATTRIBUTE_STANDARD_TYPE","
2650 G_FILE_ATTRIBUTE_STANDARD_SIZE,
2651 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2652 job->cancellable,
2653 &error);
2654
2655 if (info) {
2656 count_file (info, job, source_info);
2657
2658 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
2659 g_queue_push_head (dirs, g_object_ref (file));
2660 }
2661
2662 g_object_unref (info);
2663 } else if (job->skip_all_error) {
2664 g_error_free (error);
2665 skip_file (job, file);
2666 } else if (IS_IO_ERROR (error, CANCELLED)) {
2667 g_error_free (error);
2668 } else {
2669 primary = get_scan_primary (source_info->op);
2670 details = NULL;
2671
2672 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
2673 secondary = f (_("The file “%B” cannot be handled because you do not have "
2674 "permissions to read it."), file);
2675 } else {
2676 secondary = f (_("There was an error getting information about “%B”."), file);
2677 details = error->message;
2678 }
2679 /* set show_all to TRUE here, as we don't know how many
2680 * files we'll end up processing yet.
2681 */
2682 response = run_warning (job,
2683 primary,
2684 secondary,
2685 details,
2686 TRUE,
2687 GTK_STOCK_CANCEL, SKIP_ALL, SKIP, RETRY,
2688 NULL);
2689
2690 g_error_free (error);
2691
2692 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
2693 abort_job (job);
2694 } else if (response == 1 || response == 2) {
2695 if (response == 1) {
2696 job->skip_all_error = TRUE;
2697 }
2698 skip_file (job, file);
2699 } else if (response == 3) {
2700 goto retry;
2701 } else {
2702 g_assert_not_reached ();
2703 }
2704 }
2705
2706 while (!job_aborted (job) &&
2707 (dir = g_queue_pop_head (dirs)) != NULL) {
2708 scan_dir (dir, source_info, job, dirs);
2709 g_object_unref (dir);
2710 }
2711
2712 /* Free all from queue if we exited early */
2713 g_queue_foreach (dirs, (GFunc)g_object_unref, NULL);
2714 g_queue_free (dirs);
2715 }
2716
2717 static void
2718 scan_sources (GList *files,
2719 SourceInfo *source_info,
2720 CommonJob *job,
2721 OpKind kind)
2722 {
2723 GList *l;
2724 GFile *file;
2725
2726 memset (source_info, 0, sizeof (SourceInfo));
2727 source_info->op = kind;
2728
2729 report_count_progress (job, source_info);
2730
2731 for (l = files; l != NULL && !job_aborted (job); l = l->next) {
2732 file = l->data;
2733
2734 scan_file (file,
2735 source_info,
2736 job);
2737 }
2738
2739 /* Make sure we report the final count */
2740 report_count_progress (job, source_info);
2741 }
2742
2743 static void
2744 verify_destination (CommonJob *job,
2745 GFile *dest,
2746 char **dest_fs_id,
2747 goffset required_size)
2748 {
2749 GFileInfo *info, *fsinfo;
2750 GError *error;
2751 guint64 free_size;
2752 guint64 size_difference;
2753 char *primary, *secondary, *details;
2754 int response;
2755 GFileType file_type;
2756 gboolean dest_is_symlink = FALSE;
2757
2758 if (dest_fs_id) {
2759 *dest_fs_id = NULL;
2760 }
2761
2762 retry:
2763
2764 error = NULL;
2765 info = g_file_query_info (dest,
2766 G_FILE_ATTRIBUTE_STANDARD_TYPE","
2767 G_FILE_ATTRIBUTE_ID_FILESYSTEM,
2768 dest_is_symlink ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2769 job->cancellable,
2770 &error);
2771
2772 if (info == NULL) {
2773 if (IS_IO_ERROR (error, CANCELLED)) {
2774 g_error_free (error);
2775 return;
2776 }
2777
2778 primary = f (_("Error while copying to “%B”."), dest);
2779 details = NULL;
2780
2781 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
2782 secondary = f (_("You do not have permissions to access the destination folder."));
2783 } else {
2784 secondary = f (_("There was an error getting information about the destination."));
2785 details = error->message;
2786 }
2787
2788 response = run_error (job,
2789 primary,
2790 secondary,
2791 details,
2792 FALSE,
2793 GTK_STOCK_CANCEL, RETRY,
2794 NULL);
2795
2796 g_error_free (error);
2797
2798 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
2799 abort_job (job);
2800 } else if (response == 1) {
2801 goto retry;
2802 } else {
2803 g_assert_not_reached ();
2804 }
2805
2806 return;
2807 }
2808
2809 file_type = g_file_info_get_file_type (info);
2810 if (!dest_is_symlink && file_type == G_FILE_TYPE_SYMBOLIC_LINK) {
2811 /* Record that destination is a symlink and do real stat() once again */
2812 dest_is_symlink = TRUE;
2813 g_object_unref (info);
2814 goto retry;
2815 }
2816
2817 if (dest_fs_id) {
2818 *dest_fs_id =
2819 g_strdup (g_file_info_get_attribute_string (info,
2820 G_FILE_ATTRIBUTE_ID_FILESYSTEM));
2821 }
2822
2823 g_object_unref (info);
2824
2825 if (file_type != G_FILE_TYPE_DIRECTORY) {
2826 primary = f (_("Error while copying to “%B”."), dest);
2827 secondary = f (_("The destination is not a folder."));
2828
2829 run_error (job,
2830 primary,
2831 secondary,
2832 NULL,
2833 FALSE,
2834 GTK_STOCK_CANCEL,
2835 NULL);
2836
2837 abort_job (job);
2838 return;
2839 }
2840
2841 if (dest_is_symlink) {
2842 /* We can't reliably statfs() destination if it's a symlink, thus not doing any further checks. */
2843 return;
2844 }
2845
2846 fsinfo = g_file_query_filesystem_info (dest,
2847 G_FILE_ATTRIBUTE_FILESYSTEM_FREE","
2848 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,
2849 job->cancellable,
2850 NULL);
2851 if (fsinfo == NULL) {
2852 /* All sorts of things can go wrong getting the fs info (like not supported)
2853 * only check these things if the fs returns them
2854 */
2855 return;
2856 }
2857
2858 if (required_size > 0 &&
2859 g_file_info_has_attribute (fsinfo, G_FILE_ATTRIBUTE_FILESYSTEM_FREE)) {
2860 free_size = g_file_info_get_attribute_uint64 (fsinfo,
2861 G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
2862
2863 if (free_size < required_size) {
2864 size_difference = required_size - free_size;
2865 primary = f (_("Error while copying to “%B”."), dest);
2866 secondary = f (_("There is not enough space on the destination. Try to remove files to make space."));
2867
2868 details = f (_("%S more space is required to copy to the destination."), size_difference);
2869
2870 response = run_warning (job,
2871 primary,
2872 secondary,
2873 details,
2874 FALSE,
2875 GTK_STOCK_CANCEL,
2876 COPY_FORCE,
2877 RETRY,
2878 NULL);
2879
2880 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
2881 abort_job (job);
2882 } else if (response == 2) {
2883 goto retry;
2884 } else if (response == 1) {
2885 /* We are forced to copy - just fall through ... */
2886 } else {
2887 g_assert_not_reached ();
2888 }
2889 }
2890 }
2891
2892 if (!job_aborted (job) &&
2893 g_file_info_get_attribute_boolean (fsinfo,
2894 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY)) {
2895 primary = f (_("Error while copying to “%B”."), dest);
2896 secondary = f (_("The destination is read-only."));
2897
2898 run_error (job,
2899 primary,
2900 secondary,
2901 NULL,
2902 FALSE,
2903 GTK_STOCK_CANCEL,
2904 NULL);
2905
2906 g_error_free (error);
2907
2908 abort_job (job);
2909 }
2910
2911 g_object_unref (fsinfo);
2912 }
2913
2914 static void
2915 report_copy_progress (CopyMoveJob *copy_job,
2916 SourceInfo *source_info,
2917 TransferInfo *transfer_info)
2918 {
2919 int files_left;
2920 goffset total_size;
2921 double elapsed, transfer_rate;
2922 int remaining_time;
2923 guint64 now;
2924 CommonJob *job;
2925 gboolean is_move;
2926
2927 job = (CommonJob *)copy_job;
2928
2929 is_move = copy_job->is_move;
2930
2931 now = g_get_monotonic_time ();
2932
2933 if (transfer_info->last_report_time != 0 &&
2934 ABS ((gint64)(transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC) {
2935 return;
2936 }
2937 transfer_info->last_report_time = now;
2938
2939 files_left = source_info->num_files - transfer_info->num_files;
2940
2941 /* Races and whatnot could cause this to be negative... */
2942 if (files_left < 0) {
2943 files_left = 1;
2944 }
2945
2946 if (files_left != transfer_info->last_reported_files_left ||
2947 transfer_info->last_reported_files_left == 0) {
2948 /* Avoid changing this unless files_left changed since last time */
2949 transfer_info->last_reported_files_left = files_left;
2950
2951 if (source_info->num_files == 1) {
2952 if (copy_job->destination != NULL) {
2953 nautilus_progress_info_take_status (job->progress,
2954 f (is_move ?
2955 _("Moving “%B” to “%B”"):
2956 _("Copying “%B” to “%B”"),
2957 copy_job->fake_display_source != NULL ?
2958 copy_job->fake_display_source :
2959 (GFile *)copy_job->files->data,
2960 copy_job->destination));
2961 } else {
2962 nautilus_progress_info_take_status (job->progress,
2963 f (_("Duplicating “%B”"),
2964 (GFile *)copy_job->files->data));
2965 }
2966 } else if (copy_job->files != NULL &&
2967 copy_job->files->next == NULL) {
2968 if (copy_job->destination != NULL) {
2969 nautilus_progress_info_take_status (job->progress,
2970 f (is_move ?
2971 _("Moving file %'d of %'d (in “%B”) to “%B”")
2972 :
2973 _("Copying file %'d of %'d (in “%B”) to “%B”"),
2974 transfer_info->num_files + 1,
2975 source_info->num_files,
2976 (GFile *)copy_job->files->data,
2977 copy_job->destination));
2978 } else {
2979 nautilus_progress_info_take_status (job->progress,
2980 f (_("Duplicating file %'d of %'d (in “%B”)"),
2981 transfer_info->num_files + 1,
2982 source_info->num_files,
2983 (GFile *)copy_job->files->data));
2984 }
2985 } else {
2986 if (copy_job->destination != NULL) {
2987 nautilus_progress_info_take_status (job->progress,
2988 f (is_move ?
2989 _("Moving file %'d of %'d to “%B”")
2990 :
2991 _ ("Copying file %'d of %'d to “%B”"),
2992 transfer_info->num_files + 1,
2993 source_info->num_files,
2994 copy_job->destination));
2995 } else {
2996 nautilus_progress_info_take_status (job->progress,
2997 f (_("Duplicating file %'d of %'d"),
2998 transfer_info->num_files + 1,
2999 source_info->num_files));
3000 }
3001 }
3002 }
3003
3004 total_size = MAX (source_info->num_bytes, transfer_info->num_bytes);
3005
3006 elapsed = g_timer_elapsed (job->time, NULL);
3007 transfer_rate = 0;
3008 if (elapsed > 0) {
3009 transfer_rate = transfer_info->num_bytes / elapsed;
3010 }
3011
3012 if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE &&
3013 transfer_rate > 0) {
3014 char *s;
3015 /* To translators: %S will expand to a size like "2 bytes" or "3 MB", so something like "4 kb of 4 MB" */
3016 s = f (_("%S of %S"), transfer_info->num_bytes, total_size);
3017 nautilus_progress_info_take_details (job->progress, s);
3018 } else {
3019 char *s;
3020 remaining_time = (total_size - transfer_info->num_bytes) / transfer_rate;
3021
3022 /* To translators: %S will expand to a size like "2 bytes" or "3 MB", %T to a time duration like
3023 * "2 minutes". So the whole thing will be something like "2 kb of 4 MB -- 2 hours left (4kb/sec)"
3024 *
3025 * The singular/plural form will be used depending on the remaining time (i.e. the %T argument).
3026 */
3027 s = f (ngettext ("%S of %S \xE2\x80\x94 %T left (%S/sec)",
3028 "%S of %S \xE2\x80\x94 %T left (%S/sec)",
3029 seconds_count_format_time_units (remaining_time)),
3030 transfer_info->num_bytes, total_size,
3031 remaining_time,
3032 (goffset)transfer_rate);
3033 nautilus_progress_info_take_details (job->progress, s);
3034 }
3035
3036 nautilus_progress_info_set_progress (job->progress, transfer_info->num_bytes, total_size);
3037 }
3038
3039 static int
3040 get_max_name_length (GFile *file_dir)
3041 {
3042 int max_length;
3043 char *dir;
3044 long max_path;
3045 long max_name;
3046
3047 max_length = -1;
3048
3049 if (!g_file_has_uri_scheme (file_dir, "file"))
3050 return max_length;
3051
3052 dir = g_file_get_path (file_dir);
3053 if (!dir)
3054 return max_length;
3055
3056 max_path = pathconf (dir, _PC_PATH_MAX);
3057 max_name = pathconf (dir, _PC_NAME_MAX);
3058
3059 if (max_name == -1 && max_path == -1) {
3060 max_length = -1;
3061 } else if (max_name == -1 && max_path != -1) {
3062 max_length = max_path - (strlen (dir) + 1);
3063 } else if (max_name != -1 && max_path == -1) {
3064 max_length = max_name;
3065 } else {
3066 int leftover;
3067
3068 leftover = max_path - (strlen (dir) + 1);
3069
3070 max_length = MIN (leftover, max_name);
3071 }
3072
3073 g_free (dir);
3074
3075 return max_length;
3076 }
3077
3078 #define FAT_FORBIDDEN_CHARACTERS "/:;*?\"<>"
3079
3080 static gboolean
3081 str_replace (char *str,
3082 const char *chars_to_replace,
3083 char replacement)
3084 {
3085 gboolean success;
3086 int i;
3087
3088 success = FALSE;
3089 for (i = 0; str[i] != '\0'; i++) {
3090 if (strchr (chars_to_replace, str[i])) {
3091 success = TRUE;
3092 str[i] = replacement;
3093 }
3094 }
3095
3096 return success;
3097 }
3098
3099 static gboolean
3100 make_file_name_valid_for_dest_fs (char *filename,
3101 const char *dest_fs_type)
3102 {
3103 if (dest_fs_type != NULL && filename != NULL) {
3104 if (!strcmp (dest_fs_type, "fat") ||
3105 !strcmp (dest_fs_type, "vfat") ||
3106 !strcmp (dest_fs_type, "msdos") ||
3107 !strcmp (dest_fs_type, "msdosfs")) {
3108 gboolean ret;
3109 int i, old_len;
3110
3111 ret = str_replace (filename, FAT_FORBIDDEN_CHARACTERS, '_');
3112
3113 old_len = strlen (filename);
3114 for (i = 0; i < old_len; i++) {
3115 if (filename[i] != ' ') {
3116 g_strchomp (filename);
3117 ret |= (old_len != strlen (filename));
3118 break;
3119 }
3120 }
3121
3122 return ret;
3123 }
3124 }
3125
3126 return FALSE;
3127 }
3128
3129 static GFile *
3130 get_unique_target_file (GFile *src,
3131 GFile *dest_dir,
3132 gboolean same_fs,
3133 const char *dest_fs_type,
3134 int count)
3135 {
3136 const char *editname, *end;
3137 char *basename, *new_name;
3138 GFileInfo *info;
3139 GFile *dest;
3140 int max_length;
3141
3142 max_length = get_max_name_length (dest_dir);
3143
3144 dest = NULL;
3145 info = g_file_query_info (src,
3146 G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME,
3147 0, NULL, NULL);
3148 if (info != NULL) {
3149 editname = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME);
3150
3151 if (editname != NULL) {
3152 new_name = get_duplicate_name (editname, count, max_length);
3153 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
3154 dest = g_file_get_child_for_display_name (dest_dir, new_name, NULL);
3155 g_free (new_name);
3156 }
3157
3158 g_object_unref (info);
3159 }
3160
3161 if (dest == NULL) {
3162 basename = g_file_get_basename (src);
3163
3164 if (g_utf8_validate (basename, -1, NULL)) {
3165 new_name = get_duplicate_name (basename, count, max_length);
3166 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
3167 dest = g_file_get_child_for_display_name (dest_dir, new_name, NULL);
3168 g_free (new_name);
3169 }
3170
3171 if (dest == NULL) {
3172 end = strrchr (basename, '.');
3173 if (end != NULL) {
3174 count += atoi (end + 1);
3175 }
3176 new_name = g_strdup_printf ("%s.%d", basename, count);
3177 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
3178 dest = g_file_get_child (dest_dir, new_name);
3179 g_free (new_name);
3180 }
3181
3182 g_free (basename);
3183 }
3184
3185 return dest;
3186 }
3187
3188 static GFile *
3189 get_target_file_for_link (GFile *src,
3190 GFile *dest_dir,
3191 const char *dest_fs_type,
3192 int count)
3193 {
3194 const char *editname;
3195 char *basename, *new_name;
3196 GFileInfo *info;
3197 GFile *dest;
3198 int max_length;
3199
3200 max_length = get_max_name_length (dest_dir);
3201
3202 dest = NULL;
3203 info = g_file_query_info (src,
3204 G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME,
3205 0, NULL, NULL);
3206 if (info != NULL) {
3207 editname = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME);
3208
3209 if (editname != NULL) {
3210 new_name = get_link_name (editname, count, max_length);
3211 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
3212 dest = g_file_get_child_for_display_name (dest_dir, new_name, NULL);
3213 g_free (new_name);
3214 }
3215
3216 g_object_unref (info);
3217 }
3218
3219 if (dest == NULL) {
3220 basename = g_file_get_basename (src);
3221 make_file_name_valid_for_dest_fs (basename, dest_fs_type);
3222
3223 if (g_utf8_validate (basename, -1, NULL)) {
3224 new_name = get_link_name (basename, count, max_length);
3225 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
3226 dest = g_file_get_child_for_display_name (dest_dir, new_name, NULL);
3227 g_free (new_name);
3228 }
3229
3230 if (dest == NULL) {
3231 if (count == 1) {
3232 new_name = g_strdup_printf ("%s.lnk", basename);
3233 } else {
3234 new_name = g_strdup_printf ("%s.lnk%d", basename, count);
3235 }
3236 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
3237 dest = g_file_get_child (dest_dir, new_name);
3238 g_free (new_name);
3239 }
3240
3241 g_free (basename);
3242 }
3243
3244 return dest;
3245 }
3246
3247 static GFile *
3248 get_target_file_with_custom_name (GFile *src,
3249 GFile *dest_dir,
3250 const char *dest_fs_type,
3251 gboolean same_fs,
3252 const gchar *custom_name)
3253 {
3254 char *basename;
3255 GFile *dest;
3256 GFileInfo *info;
3257 char *copyname;
3258
3259 dest = NULL;
3260
3261 if (custom_name != NULL) {
3262 copyname = g_strdup (custom_name);
3263 make_file_name_valid_for_dest_fs (copyname, dest_fs_type);
3264 dest = g_file_get_child_for_display_name (dest_dir, copyname, NULL);
3265
3266 g_free (copyname);
3267 }
3268
3269 if (dest == NULL && !same_fs) {
3270 info = g_file_query_info (src,
3271 G_FILE_ATTRIBUTE_STANDARD_COPY_NAME,
3272 0, NULL, NULL);
3273
3274 if (info) {
3275 copyname = g_strdup (g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME));
3276
3277 if (copyname) {
3278 make_file_name_valid_for_dest_fs (copyname, dest_fs_type);
3279 dest = g_file_get_child_for_display_name (dest_dir, copyname, NULL);
3280 g_free (copyname);
3281 }
3282
3283 g_object_unref (info);
3284 }
3285 }
3286
3287 if (dest == NULL) {
3288 basename = g_file_get_basename (src);
3289 make_file_name_valid_for_dest_fs (basename, dest_fs_type);
3290 dest = g_file_get_child (dest_dir, basename);
3291 g_free (basename);
3292 }
3293
3294 return dest;
3295 }
3296
3297 static GFile *
3298 get_target_file (GFile *src,
3299 GFile *dest_dir,
3300 const char *dest_fs_type,
3301 gboolean same_fs)
3302 {
3303 return get_target_file_with_custom_name (src, dest_dir, dest_fs_type, same_fs, NULL);
3304 }
3305
3306 static gboolean
3307 has_fs_id (GFile *file, const char *fs_id)
3308 {
3309 const char *id;
3310 GFileInfo *info;
3311 gboolean res;
3312
3313 res = FALSE;
3314 info = g_file_query_info (file,
3315 G_FILE_ATTRIBUTE_ID_FILESYSTEM,
3316 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3317 NULL, NULL);
3318
3319 if (info) {
3320 id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
3321
3322 if (id && strcmp (id, fs_id) == 0) {
3323 res = TRUE;
3324 }
3325
3326 g_object_unref (info);
3327 }
3328
3329 return res;
3330 }
3331
3332 static gboolean
3333 is_dir (GFile *file)
3334 {
3335 GFileInfo *info;
3336 gboolean res;
3337
3338 res = FALSE;
3339 info = g_file_query_info (file,
3340 G_FILE_ATTRIBUTE_STANDARD_TYPE,
3341 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3342 NULL, NULL);
3343 if (info) {
3344 res = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
3345 g_object_unref (info);
3346 }
3347
3348 return res;
3349 }
3350
3351 static void copy_move_file (CopyMoveJob *job,
3352 GFile *src,
3353 GFile *dest_dir,
3354 gboolean same_fs,
3355 gboolean unique_names,
3356 char **dest_fs_type,
3357 SourceInfo *source_info,
3358 TransferInfo *transfer_info,
3359 GHashTable *debuting_files,
3360 GdkPoint *point,
3361 gboolean overwrite,
3362 gboolean *skipped_file,
3363 gboolean readonly_source_fs);
3364
3365 typedef enum {
3366 CREATE_DEST_DIR_RETRY,
3367 CREATE_DEST_DIR_FAILED,
3368 CREATE_DEST_DIR_SUCCESS
3369 } CreateDestDirResult;
3370
3371 static CreateDestDirResult
3372 create_dest_dir (CommonJob *job,
3373 GFile *src,
3374 GFile **dest,
3375 gboolean same_fs,
3376 char **dest_fs_type)
3377 {
3378 GError *error;
3379 GFile *new_dest, *dest_dir;
3380 char *primary, *secondary, *details;
3381 int response;
3382 gboolean handled_invalid_filename;
3383
3384 handled_invalid_filename = *dest_fs_type != NULL;
3385
3386 retry:
3387 /* First create the directory, then copy stuff to it before
3388 copying the attributes, because we need to be sure we can write to it */
3389
3390 error = NULL;
3391 if (!g_file_make_directory (*dest, job->cancellable, &error)) {
3392 if (IS_IO_ERROR (error, CANCELLED)) {
3393 g_error_free (error);
3394 return CREATE_DEST_DIR_FAILED;
3395 } else if (IS_IO_ERROR (error, INVALID_FILENAME) &&
3396 !handled_invalid_filename) {
3397 handled_invalid_filename = TRUE;
3398
3399 g_assert (*dest_fs_type == NULL);
3400
3401 dest_dir = g_file_get_parent (*dest);
3402
3403 if (dest_dir != NULL) {
3404 *dest_fs_type = query_fs_type (dest_dir, job->cancellable);
3405
3406 new_dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
3407 g_object_unref (dest_dir);
3408
3409 if (!g_file_equal (*dest, new_dest)) {
3410 g_object_unref (*dest);
3411 *dest = new_dest;
3412 g_error_free (error);
3413 return CREATE_DEST_DIR_RETRY;
3414 } else {
3415 g_object_unref (new_dest);
3416 }
3417 }
3418 }
3419
3420 primary = f (_("Error while copying."));
3421 details = NULL;
3422
3423 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
3424 secondary = f (_("The folder “%B” cannot be copied because you do not have "
3425 "permissions to create it in the destination."), src);
3426 } else {
3427 secondary = f (_("There was an error creating the folder “%B”."), src);
3428 details = error->message;
3429 }
3430
3431 response = run_warning (job,
3432 primary,
3433 secondary,
3434 details,
3435 FALSE,
3436 GTK_STOCK_CANCEL, SKIP, RETRY,
3437 NULL);
3438
3439 g_error_free (error);
3440
3441 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
3442 abort_job (job);
3443 } else if (response == 1) {
3444 /* Skip: Do Nothing */
3445 } else if (response == 2) {
3446 goto retry;
3447 } else {
3448 g_assert_not_reached ();
3449 }
3450 return CREATE_DEST_DIR_FAILED;
3451 }
3452 nautilus_file_changes_queue_file_added (*dest);
3453
3454 if (job->undo_info != NULL) {
3455 nautilus_file_undo_info_ext_add_origin_target_pair (NAUTILUS_FILE_UNDO_INFO_EXT (job->undo_info),
3456 src, *dest);
3457 }
3458
3459 return CREATE_DEST_DIR_SUCCESS;
3460 }
3461
3462 /* a return value of FALSE means retry, i.e.
3463 * the destination has changed and the source
3464 * is expected to re-try the preceeding
3465 * g_file_move() or g_file_copy() call with
3466 * the new destination.
3467 */
3468 static gboolean
3469 copy_move_directory (CopyMoveJob *copy_job,
3470 GFile *src,
3471 GFile **dest,
3472 gboolean same_fs,
3473 gboolean create_dest,
3474 char **parent_dest_fs_type,
3475 SourceInfo *source_info,
3476 TransferInfo *transfer_info,
3477 GHashTable *debuting_files,
3478 gboolean *skipped_file,
3479 gboolean readonly_source_fs)
3480 {
3481 GFileInfo *info;
3482 GError *error;
3483 GFile *src_file;
3484 GFileEnumerator *enumerator;
3485 char *primary, *secondary, *details;
3486 char *dest_fs_type;
3487 int response;
3488 gboolean skip_error;
3489 gboolean local_skipped_file;
3490 CommonJob *job;
3491 GFileCopyFlags flags;
3492
3493 job = (CommonJob *)copy_job;
3494
3495 if (create_dest) {
3496 switch (create_dest_dir (job, src, dest, same_fs, parent_dest_fs_type)) {
3497 case CREATE_DEST_DIR_RETRY:
3498 /* next time copy_move_directory() is called,
3499 * create_dest will be FALSE if a directory already
3500 * exists under the new name (i.e. WOULD_RECURSE)
3501 */
3502 return FALSE;
3503
3504 case CREATE_DEST_DIR_FAILED:
3505 *skipped_file = TRUE;
3506 return TRUE;
3507
3508 case CREATE_DEST_DIR_SUCCESS:
3509 default:
3510 break;
3511 }
3512
3513 if (debuting_files) {
3514 g_hash_table_replace (debuting_files, g_object_ref (*dest), GINT_TO_POINTER (TRUE));
3515 }
3516
3517 }
3518
3519 local_skipped_file = FALSE;
3520 dest_fs_type = NULL;
3521
3522 skip_error = should_skip_readdir_error (job, src);
3523 retry:
3524 error = NULL;
3525 enumerator = g_file_enumerate_children (src,
3526 G_FILE_ATTRIBUTE_STANDARD_NAME,
3527 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3528 job->cancellable,
3529 &error);
3530 if (enumerator) {
3531 error = NULL;
3532
3533 while (!job_aborted (job) &&
3534 (info = g_file_enumerator_next_file (enumerator, job->cancellable, skip_error?NULL:&error)) != NULL) {
3535 src_file = g_file_get_child (src,
3536 g_file_info_get_name (info));
3537 copy_move_file (copy_job, src_file, *dest, same_fs, FALSE, &dest_fs_type,
3538 source_info, transfer_info, NULL, NULL, FALSE, &local_skipped_file,
3539 readonly_source_fs);
3540 g_object_unref (src_file);
3541 g_object_unref (info);
3542 }
3543 g_file_enumerator_close (enumerator, job->cancellable, NULL);
3544 g_object_unref (enumerator);
3545
3546 if (error && IS_IO_ERROR (error, CANCELLED)) {
3547 g_error_free (error);
3548 } else if (error) {
3549 if (copy_job->is_move) {
3550 primary = f (_("Error while moving."));
3551 } else {
3552 primary = f (_("Error while copying."));
3553 }
3554 details = NULL;
3555
3556 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
3557 secondary = f (_("Files in the folder “%B” cannot be copied because you do "
3558 "not have permissions to see them."), src);
3559 } else {
3560 secondary = f (_("There was an error getting information about the files in the folder “%B”."), src);
3561 details = error->message;
3562 }
3563
3564 response = run_warning (job,
3565 primary,
3566 secondary,
3567 details,
3568 FALSE,
3569 GTK_STOCK_CANCEL, _("_Skip files"),
3570 NULL);
3571
3572 g_error_free (error);
3573
3574 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
3575 abort_job (job);
3576 } else if (response == 1) {
3577 /* Skip: Do Nothing */
3578 local_skipped_file = TRUE;
3579 } else {
3580 g_assert_not_reached ();
3581 }
3582 }
3583
3584 /* Count the copied directory as a file */
3585 transfer_info->num_files ++;
3586 report_copy_progress (copy_job, source_info, transfer_info);
3587
3588 if (debuting_files) {
3589 g_hash_table_replace (debuting_files, g_object_ref (*dest), GINT_TO_POINTER (create_dest));
3590 }
3591 } else if (IS_IO_ERROR (error, CANCELLED)) {
3592 g_error_free (error);
3593 } else {
3594 if (copy_job->is_move) {
3595 primary = f (_("Error while moving."));
3596 } else {
3597 primary = f (_("Error while copying."));
3598 }
3599 details = NULL;
3600
3601 if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
3602 secondary = f (_("The folder “%B” cannot be copied because you do not have "
3603 "permissions to read it."), src);
3604 } else {
3605 secondary = f (_("There was an error reading the folder “%B”."), src);
3606 details = error->message;
3607 }
3608
3609 response = run_warning (job,
3610 primary,
3611 secondary,
3612 details,
3613 FALSE,
3614 GTK_STOCK_CANCEL, SKIP, RETRY,
3615 NULL);
3616
3617 g_error_free (error);
3618
3619 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
3620 abort_job (job);
3621 } else if (response == 1) {
3622 /* Skip: Do Nothing */
3623 local_skipped_file = TRUE;
3624 } else if (response == 2) {
3625 goto retry;
3626 } else {
3627 g_assert_not_reached ();
3628 }
3629 }
3630
3631 if (create_dest) {
3632 flags = (readonly_source_fs) ? G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_TARGET_DEFAULT_PERMS
3633 : G_FILE_COPY_NOFOLLOW_SYMLINKS;
3634 /* Ignore errors here. Failure to copy metadata is not a hard error */
3635 g_file_copy_attributes (src, *dest,
3636 flags,
3637 job->cancellable, NULL);
3638 }
3639
3640 if (!job_aborted (job) && copy_job->is_move &&
3641 /* Don't delete source if there was a skipped file */
3642 !local_skipped_file) {
3643 if (!g_file_delete (src, job->cancellable, &error)) {
3644 if (job->skip_all_error) {
3645 goto skip;
3646 }
3647 primary = f (_("Error while moving “%B”."), src);
3648 secondary = f (_("Could not remove the source folder."));
3649 details = error->message;
3650
3651 response = run_warning (job,
3652 primary,
3653 secondary,
3654 details,
3655 (source_info->num_files - transfer_info->num_files) > 1,
3656 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
3657 NULL);
3658
3659 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
3660 abort_job (job);
3661 } else if (response == 1) { /* skip all */
3662 job->skip_all_error = TRUE;
3663 local_skipped_file = TRUE;
3664 } else if (response == 2) { /* skip */
3665 local_skipped_file = TRUE;
3666 } else {
3667 g_assert_not_reached ();
3668 }
3669
3670 skip:
3671 g_error_free (error);
3672 }
3673 }
3674
3675 if (local_skipped_file) {
3676 *skipped_file = TRUE;
3677 }
3678
3679 g_free (dest_fs_type);
3680 return TRUE;
3681 }
3682
3683 static gboolean
3684 remove_target_recursively (CommonJob *job,
3685 GFile *src,
3686 GFile *toplevel_dest,
3687 GFile *file)
3688 {
3689 GFileEnumerator *enumerator;
3690 GError *error;
3691 GFile *child;
3692 gboolean stop;
3693 char *primary, *secondary, *details;
3694 int response;
3695 GFileInfo *info;
3696
3697 stop = FALSE;
3698
3699 error = NULL;
3700 enumerator = g_file_enumerate_children (file,
3701 G_FILE_ATTRIBUTE_STANDARD_NAME,
3702 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3703 job->cancellable,
3704 &error);
3705 if (enumerator) {
3706 error = NULL;
3707
3708 while (!job_aborted (job) &&
3709 (info = g_file_enumerator_next_file (enumerator, job->cancellable, &error)) != NULL) {
3710 child = g_file_get_child (file,
3711 g_file_info_get_name (info));
3712 if (!remove_target_recursively (job, src, toplevel_dest, child)) {
3713 stop = TRUE;
3714 break;
3715 }
3716 g_object_unref (child);
3717 g_object_unref (info);
3718 }
3719 g_file_enumerator_close (enumerator, job->cancellable, NULL);
3720 g_object_unref (enumerator);
3721
3722 } else if (IS_IO_ERROR (error, NOT_DIRECTORY)) {
3723 /* Not a dir, continue */
3724 g_error_free (error);
3725
3726 } else if (IS_IO_ERROR (error, CANCELLED)) {
3727 g_error_free (error);
3728 } else {
3729 if (job->skip_all_error) {
3730 goto skip1;
3731 }
3732
3733 primary = f (_("Error while copying “%B”."), src);
3734 secondary = f (_("Could not remove files from the already existing folder %F."), file);
3735 details = error->message;
3736
3737 /* set show_all to TRUE here, as we don't know how many
3738 * files we'll end up processing yet.
3739 */
3740 response = run_warning (job,
3741 primary,
3742 secondary,
3743 details,
3744 TRUE,
3745 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
3746 NULL);
3747
3748 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
3749 abort_job (job);
3750 } else if (response == 1) { /* skip all */
3751 job->skip_all_error = TRUE;
3752 } else if (response == 2) { /* skip */
3753 /* do nothing */
3754 } else {
3755 g_assert_not_reached ();
3756 }
3757 skip1:
3758 g_error_free (error);
3759
3760 stop = TRUE;
3761 }
3762
3763 if (stop) {
3764 return FALSE;
3765 }
3766
3767 error = NULL;
3768
3769 if (!g_file_delete (file, job->cancellable, &error)) {
3770 if (job->skip_all_error ||
3771 IS_IO_ERROR (error, CANCELLED)) {
3772 goto skip2;
3773 }
3774 primary = f (_("Error while copying “%B”."), src);
3775 secondary = f (_("Could not remove the already existing file %F."), file);
3776 details = error->message;
3777
3778 /* set show_all to TRUE here, as we don't know how many
3779 * files we'll end up processing yet.
3780 */
3781 response = run_warning (job,
3782 primary,
3783 secondary,
3784 details,
3785 TRUE,
3786 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
3787 NULL);
3788
3789 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
3790 abort_job (job);
3791 } else if (response == 1) { /* skip all */
3792 job->skip_all_error = TRUE;
3793 } else if (response == 2) { /* skip */
3794 /* do nothing */
3795 } else {
3796 g_assert_not_reached ();
3797 }
3798
3799 skip2:
3800 g_error_free (error);
3801
3802 return FALSE;
3803 }
3804 nautilus_file_changes_queue_file_removed (file);
3805
3806 return TRUE;
3807
3808 }
3809
3810 typedef struct {
3811 CopyMoveJob *job;
3812 goffset last_size;
3813 SourceInfo *source_info;
3814 TransferInfo *transfer_info;
3815 } ProgressData;
3816
3817 static void
3818 copy_file_progress_callback (goffset current_num_bytes,
3819 goffset total_num_bytes,
3820 gpointer user_data)
3821 {
3822 ProgressData *pdata;
3823 goffset new_size;
3824
3825 pdata = user_data;
3826
3827 new_size = current_num_bytes - pdata->last_size;
3828
3829 if (new_size > 0) {
3830 pdata->transfer_info->num_bytes += new_size;
3831 pdata->last_size = current_num_bytes;
3832 report_copy_progress (pdata->job,
3833 pdata->source_info,
3834 pdata->transfer_info);
3835 }
3836 }
3837
3838 static gboolean
3839 test_dir_is_parent (GFile *child, GFile *root)
3840 {
3841 GFile *f, *tmp;
3842
3843 f = g_file_dup (child);
3844 while (f) {
3845 if (g_file_equal (f, root)) {
3846 g_object_unref (f);
3847 return TRUE;
3848 }
3849 tmp = f;
3850 f = g_file_get_parent (f);
3851 g_object_unref (tmp);
3852 }
3853 if (f) {
3854 g_object_unref (f);
3855 }
3856 return FALSE;
3857 }
3858
3859 static char *
3860 query_fs_type (GFile *file,
3861 GCancellable *cancellable)
3862 {
3863 GFileInfo *fsinfo;
3864 char *ret;
3865
3866 ret = NULL;
3867
3868 fsinfo = g_file_query_filesystem_info (file,
3869 G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
3870 cancellable,
3871 NULL);
3872 if (fsinfo != NULL) {
3873 ret = g_strdup (g_file_info_get_attribute_string (fsinfo, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE));
3874 g_object_unref (fsinfo);
3875 }
3876
3877 if (ret == NULL) {
3878 /* ensure that we don't attempt to query
3879 * the FS type for each file in a given
3880 * directory, if it can't be queried. */
3881 ret = g_strdup ("");
3882 }
3883
3884 return ret;
3885 }
3886
3887 static gboolean
3888 is_trusted_desktop_file (GFile *file,
3889 GCancellable *cancellable)
3890 {
3891 char *basename;
3892 gboolean res;
3893 GFileInfo *info;
3894
3895 /* Don't trust non-local files */
3896 if (!g_file_is_native (file)) {
3897 return FALSE;
3898 }
3899
3900 basename = g_file_get_basename (file);
3901 if (!g_str_has_suffix (basename, ".desktop")) {
3902 g_free (basename);
3903 return FALSE;
3904 }
3905 g_free (basename);
3906
3907 info = g_file_query_info (file,
3908 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3909 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
3910 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3911 cancellable,
3912 NULL);
3913
3914 if (info == NULL) {
3915 return FALSE;
3916 }
3917
3918 res = FALSE;
3919
3920 /* Weird file => not trusted,
3921 Already executable => no need to mark trusted */
3922 if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR &&
3923 !g_file_info_get_attribute_boolean (info,
3924 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE) &&
3925 nautilus_is_in_system_dir (file)) {
3926 res = TRUE;
3927 }
3928 g_object_unref (info);
3929
3930 return res;
3931 }
3932
3933 typedef struct {
3934 int id;
3935 char *new_name;
3936 gboolean apply_to_all;
3937 } ConflictResponseData;
3938
3939 typedef struct {
3940 GFile *src;
3941 GFile *dest;
3942 GFile *dest_dir;
3943 GtkWindow *parent;
3944 ConflictResponseData *resp_data;
3945 } ConflictDialogData;
3946
3947 static gboolean
3948 do_run_conflict_dialog (gpointer _data)
3949 {
3950 ConflictDialogData *data = _data;
3951 GtkWidget *dialog;
3952 int response;
3953
3954 dialog = nautilus_file_conflict_dialog_new (data->parent,
3955 data->src,
3956 data->dest,
3957 data->dest_dir);
3958 response = gtk_dialog_run (GTK_DIALOG (dialog));
3959
3960 if (response == CONFLICT_RESPONSE_RENAME) {
3961 data->resp_data->new_name =
3962 nautilus_file_conflict_dialog_get_new_name (NAUTILUS_FILE_CONFLICT_DIALOG (dialog));
3963 } else if (response != GTK_RESPONSE_CANCEL ||
3964 response != GTK_RESPONSE_NONE) {
3965 data->resp_data->apply_to_all =
3966 nautilus_file_conflict_dialog_get_apply_to_all
3967 (NAUTILUS_FILE_CONFLICT_DIALOG (dialog));
3968 }
3969
3970 data->resp_data->id = response;
3971
3972 gtk_widget_destroy (dialog);
3973
3974 return FALSE;
3975 }
3976
3977 static ConflictResponseData *
3978 run_conflict_dialog (CommonJob *job,
3979 GFile *src,
3980 GFile *dest,
3981 GFile *dest_dir)
3982 {
3983 ConflictDialogData *data;
3984 ConflictResponseData *resp_data;
3985
3986 g_timer_stop (job->time);
3987
3988 data = g_slice_new0 (ConflictDialogData);
3989 data->parent = job->parent_window;
3990 data->src = src;
3991 data->dest = dest;
3992 data->dest_dir = dest_dir;
3993
3994 resp_data = g_slice_new0 (ConflictResponseData);
3995 resp_data->new_name = NULL;
3996 data->resp_data = resp_data;
3997
3998 nautilus_progress_info_pause (job->progress);
3999 g_io_scheduler_job_send_to_mainloop (job->io_job,
4000 do_run_conflict_dialog,
4001 data,
4002 NULL);
4003 nautilus_progress_info_resume (job->progress);
4004
4005 g_slice_free (ConflictDialogData, data);
4006
4007 g_timer_continue (job->time);
4008
4009 return resp_data;
4010 }
4011
4012 static void
4013 conflict_response_data_free (ConflictResponseData *data)
4014 {
4015 g_free (data->new_name);
4016 g_slice_free (ConflictResponseData, data);
4017 }
4018
4019 static GFile *
4020 get_target_file_for_display_name (GFile *dir,
4021 const gchar *name)
4022 {
4023 GFile *dest;
4024
4025 dest = NULL;
4026 dest = g_file_get_child_for_display_name (dir, name, NULL);
4027
4028 if (dest == NULL) {
4029 dest = g_file_get_child (dir, name);
4030 }
4031
4032 return dest;
4033 }
4034
4035 /* Debuting files is non-NULL only for toplevel items */
4036 static void
4037 copy_move_file (CopyMoveJob *copy_job,
4038 GFile *src,
4039 GFile *dest_dir,
4040 gboolean same_fs,
4041 gboolean unique_names,
4042 char **dest_fs_type,
4043 SourceInfo *source_info,
4044 TransferInfo *transfer_info,
4045 GHashTable *debuting_files,
4046 GdkPoint *position,
4047 gboolean overwrite,
4048 gboolean *skipped_file,
4049 gboolean readonly_source_fs)
4050 {
4051 GFile *dest, *new_dest;
4052 GError *error;
4053 GFileCopyFlags flags;
4054 char *primary, *secondary, *details;
4055 int response;
4056 ProgressData pdata;
4057 gboolean would_recurse, is_merge;
4058 CommonJob *job;
4059 gboolean res;
4060 int unique_name_nr;
4061 gboolean handled_invalid_filename;
4062
4063 job = (CommonJob *)copy_job;
4064
4065 if (should_skip_file (job, src)) {
4066 *skipped_file = TRUE;
4067 return;
4068 }
4069
4070 unique_name_nr = 1;
4071
4072 /* another file in the same directory might have handled the invalid
4073 * filename condition for us
4074 */
4075 handled_invalid_filename = *dest_fs_type != NULL;
4076
4077 if (unique_names) {
4078 dest = get_unique_target_file (src, dest_dir, same_fs, *dest_fs_type, unique_name_nr++);
4079 } else if (copy_job->target_name != NULL) {
4080 dest = get_target_file_with_custom_name (src, dest_dir, *dest_fs_type, same_fs,
4081 copy_job->target_name);
4082 } else {
4083 dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
4084 }
4085
4086 /* Don't allow recursive move/copy into itself.
4087 * (We would get a file system error if we proceeded but it is nicer to
4088 * detect and report it at this level) */
4089 if (test_dir_is_parent (dest_dir, src)) {
4090 if (job->skip_all_error) {
4091 goto out;
4092 }
4093
4094 /* the run_warning() frees all strings passed in automatically */
4095 primary = copy_job->is_move ? g_strdup (_("You cannot move a folder into itself."))
4096 : g_strdup (_("You cannot copy a folder into itself."));
4097 secondary = g_strdup (_("The destination folder is inside the source folder."));
4098
4099 response = run_warning (job,
4100 primary,
4101 secondary,
4102 NULL,
4103 (source_info->num_files - transfer_info->num_files) > 1,
4104 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
4105 NULL);
4106
4107 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
4108 abort_job (job);
4109 } else if (response == 1) { /* skip all */
4110 job->skip_all_error = TRUE;
4111 } else if (response == 2) { /* skip */
4112 /* do nothing */
4113 } else {
4114 g_assert_not_reached ();
4115 }
4116
4117 goto out;
4118 }
4119
4120 /* Don't allow copying over the source or one of the parents of the source.
4121 */
4122 if (test_dir_is_parent (src, dest)) {
4123 if (job->skip_all_error) {
4124 goto out;
4125 }
4126
4127 /* the run_warning() frees all strings passed in automatically */
4128 primary = copy_job->is_move ? g_strdup (_("You cannot move a file over itself."))
4129 : g_strdup (_("You cannot copy a file over itself."));
4130 secondary = g_strdup (_("The source file would be overwritten by the destination."));
4131
4132 response = run_warning (job,
4133 primary,
4134 secondary,
4135 NULL,
4136 (source_info->num_files - transfer_info->num_files) > 1,
4137 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
4138 NULL);
4139
4140 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
4141 abort_job (job);
4142 } else if (response == 1) { /* skip all */
4143 job->skip_all_error = TRUE;
4144 } else if (response == 2) { /* skip */
4145 /* do nothing */
4146 } else {
4147 g_assert_not_reached ();
4148 }
4149
4150 goto out;
4151 }
4152
4153
4154 retry:
4155
4156 error = NULL;
4157 flags = G_FILE_COPY_NOFOLLOW_SYMLINKS;
4158 if (overwrite) {
4159 flags |= G_FILE_COPY_OVERWRITE;
4160 }
4161 if (readonly_source_fs) {
4162 flags |= G_FILE_COPY_TARGET_DEFAULT_PERMS;
4163 }
4164
4165 pdata.job = copy_job;
4166 pdata.last_size = 0;
4167 pdata.source_info = source_info;
4168 pdata.transfer_info = transfer_info;
4169
4170 if (copy_job->is_move) {
4171 res = g_file_move (src, dest,
4172 flags,
4173 job->cancellable,
4174 copy_file_progress_callback,
4175 &pdata,
4176 &error);
4177 } else {
4178 res = g_file_copy (src, dest,
4179 flags,
4180 job->cancellable,
4181 copy_file_progress_callback,
4182 &pdata,
4183 &error);
4184 }
4185
4186 if (res) {
4187 transfer_info->num_files ++;
4188 report_copy_progress (copy_job, source_info, transfer_info);
4189
4190 if (debuting_files) {
4191 if (position) {
4192 nautilus_file_changes_queue_schedule_position_set (dest, *position, job->screen_num);
4193 } else {
4194 nautilus_file_changes_queue_schedule_position_remove (dest);
4195 }
4196
4197 g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
4198 }
4199 if (copy_job->is_move) {
4200 nautilus_file_changes_queue_file_moved (src, dest);
4201 } else {
4202 nautilus_file_changes_queue_file_added (dest);
4203 }
4204
4205 /* If copying a trusted desktop file to the desktop,
4206 mark it as trusted. */
4207 if (copy_job->desktop_location != NULL &&
4208 g_file_equal (copy_job->desktop_location, dest_dir) &&
4209 is_trusted_desktop_file (src, job->cancellable)) {
4210 mark_desktop_file_trusted (job,
4211 job->cancellable,
4212 dest,
4213 FALSE);
4214 }
4215
4216 if (job->undo_info != NULL) {
4217 nautilus_file_undo_info_ext_add_origin_target_pair (NAUTILUS_FILE_UNDO_INFO_EXT (job->undo_info),
4218 src, dest);
4219 }
4220
4221 g_object_unref (dest);
4222 return;
4223 }
4224
4225 if (!handled_invalid_filename &&
4226 IS_IO_ERROR (error, INVALID_FILENAME)) {
4227 handled_invalid_filename = TRUE;
4228
4229 g_assert (*dest_fs_type == NULL);
4230 *dest_fs_type = query_fs_type (dest_dir, job->cancellable);
4231
4232 if (unique_names) {
4233 new_dest = get_unique_target_file (src, dest_dir, same_fs, *dest_fs_type, unique_name_nr);
4234 } else {
4235 new_dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
4236 }
4237
4238 if (!g_file_equal (dest, new_dest)) {
4239 g_object_unref (dest);
4240 dest = new_dest;
4241
4242 g_error_free (error);
4243 goto retry;
4244 } else {
4245 g_object_unref (new_dest);
4246 }
4247 }
4248
4249 /* Conflict */
4250 if (!overwrite &&
4251 IS_IO_ERROR (error, EXISTS)) {
4252 gboolean is_merge;
4253 ConflictResponseData *response;
4254
4255 g_error_free (error);
4256
4257 if (unique_names) {
4258 g_object_unref (dest);
4259 dest = get_unique_target_file (src, dest_dir, same_fs, *dest_fs_type, unique_name_nr++);
4260 goto retry;
4261 }
4262
4263 is_merge = FALSE;
4264
4265 if (is_dir (dest) && is_dir (src)) {
4266 is_merge = TRUE;
4267 }
4268
4269 if ((is_merge && job->merge_all) ||
4270 (!is_merge && job->replace_all)) {
4271 overwrite = TRUE;
4272 goto retry;
4273 }
4274
4275 if (job->skip_all_conflict) {
4276 goto out;
4277 }
4278
4279 response = run_conflict_dialog (job, src, dest, dest_dir);
4280
4281 if (response->id == GTK_RESPONSE_CANCEL ||
4282 response->id == GTK_RESPONSE_DELETE_EVENT) {
4283 conflict_response_data_free (response);
4284 abort_job (job);
4285 } else if (response->id == CONFLICT_RESPONSE_SKIP) {
4286 if (response->apply_to_all) {
4287 job->skip_all_conflict = TRUE;
4288 }
4289 conflict_response_data_free (response);
4290 } else if (response->id == CONFLICT_RESPONSE_REPLACE) { /* merge/replace */
4291 if (response->apply_to_all) {
4292 if (is_merge) {
4293 job->merge_all = TRUE;
4294 } else {
4295 job->replace_all = TRUE;
4296 }
4297 }
4298 overwrite = TRUE;
4299 conflict_response_data_free (response);
4300 goto retry;
4301 } else if (response->id == CONFLICT_RESPONSE_RENAME) {
4302 g_object_unref (dest);
4303 dest = get_target_file_for_display_name (dest_dir,
4304 response->new_name);
4305 conflict_response_data_free (response);
4306 goto retry;
4307 } else {
4308 g_assert_not_reached ();
4309 }
4310 }
4311
4312 else if (overwrite &&
4313 IS_IO_ERROR (error, IS_DIRECTORY)) {
4314
4315 g_error_free (error);
4316
4317 if (remove_target_recursively (job, src, dest, dest)) {
4318 goto retry;
4319 }
4320 }
4321
4322 /* Needs to recurse */
4323 else if (IS_IO_ERROR (error, WOULD_RECURSE) ||
4324 IS_IO_ERROR (error, WOULD_MERGE)) {
4325 is_merge = error->code == G_IO_ERROR_WOULD_MERGE;
4326 would_recurse = error->code == G_IO_ERROR_WOULD_RECURSE;
4327 g_error_free (error);
4328
4329 if (overwrite && would_recurse) {
4330 error = NULL;
4331
4332 /* Copying a dir onto file, first remove the file */
4333 if (!g_file_delete (dest, job->cancellable, &error) &&
4334 !IS_IO_ERROR (error, NOT_FOUND)) {
4335 if (job->skip_all_error) {
4336 g_error_free (error);
4337 goto out;
4338 }
4339 if (copy_job->is_move) {
4340 primary = f (_("Error while moving “%B”."), src);
4341 } else {
4342 primary = f (_("Error while copying “%B”."), src);
4343 }
4344 secondary = f (_("Could not remove the already existing file with the same name in %F."), dest_dir);
4345 details = error->message;
4346
4347 /* setting TRUE on show_all here, as we could have
4348 * another error on the same file later.
4349 */
4350 response = run_warning (job,
4351 primary,
4352 secondary,
4353 details,
4354 TRUE,
4355 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
4356 NULL);
4357
4358 g_error_free (error);
4359
4360 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
4361 abort_job (job);
4362 } else if (response == 1) { /* skip all */
4363 job->skip_all_error = TRUE;
4364 } else if (response == 2) { /* skip */
4365 /* do nothing */
4366 } else {
4367 g_assert_not_reached ();
4368 }
4369 goto out;
4370
4371 }
4372 if (error) {
4373 g_error_free (error);
4374 error = NULL;
4375 }
4376 nautilus_file_changes_queue_file_removed (dest);
4377 }
4378
4379 if (is_merge) {
4380 /* On merge we now write in the target directory, which may not
4381 be in the same directory as the source, even if the parent is
4382 (if the merged directory is a mountpoint). This could cause
4383 problems as we then don't transcode filenames.
4384 We just set same_fs to FALSE which is safe but a bit slower. */
4385 same_fs = FALSE;
4386 }
4387
4388 if (!copy_move_directory (copy_job, src, &dest, same_fs,
4389 would_recurse, dest_fs_type,
4390 source_info, transfer_info,
4391 debuting_files, skipped_file,
4392 readonly_source_fs)) {
4393 /* destination changed, since it was an invalid file name */
4394 g_assert (*dest_fs_type != NULL);
4395 handled_invalid_filename = TRUE;
4396 goto retry;
4397 }
4398
4399 g_object_unref (dest);
4400 return;
4401 }
4402
4403 else if (IS_IO_ERROR (error, CANCELLED)) {
4404 g_error_free (error);
4405 }
4406
4407 /* Other error */
4408 else {
4409 if (job->skip_all_error) {
4410 g_error_free (error);
4411 goto out;
4412 }
4413 primary = f (_("Error while copying “%B”."), src);
4414 secondary = f (_("There was an error copying the file into %F."), dest_dir);
4415 details = error->message;
4416
4417 response = run_warning (job,
4418 primary,
4419 secondary,
4420 details,
4421 (source_info->num_files - transfer_info->num_files) > 1,
4422 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
4423 NULL);
4424
4425 g_error_free (error);
4426
4427 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
4428 abort_job (job);
4429 } else if (response == 1) { /* skip all */
4430 job->skip_all_error = TRUE;
4431 } else if (response == 2) { /* skip */
4432 /* do nothing */
4433 } else {
4434 g_assert_not_reached ();
4435 }
4436 }
4437 out:
4438 *skipped_file = TRUE; /* Or aborted, but same-same */
4439 g_object_unref (dest);
4440 }
4441
4442 static void
4443 copy_files (CopyMoveJob *job,
4444 const char *dest_fs_id,
4445 SourceInfo *source_info,
4446 TransferInfo *transfer_info)
4447 {
4448 CommonJob *common;
4449 GList *l;
4450 GFile *src;
4451 gboolean same_fs;
4452 int i;
4453 GdkPoint *point;
4454 gboolean skipped_file;
4455 gboolean unique_names;
4456 GFile *dest;
4457 GFile *source_dir;
4458 char *dest_fs_type;
4459 GFileInfo *inf;
4460 gboolean readonly_source_fs;
4461
4462 dest_fs_type = NULL;
4463 readonly_source_fs = FALSE;
4464
4465 common = &job->common;
4466
4467 report_copy_progress (job, source_info, transfer_info);
4468
4469 /* Query the source dir, not the file because if its a symlink we'll follow it */
4470 source_dir = g_file_get_parent ((GFile *) job->files->data);
4471 if (source_dir) {
4472 inf = g_file_query_filesystem_info (source_dir, "filesystem::readonly", NULL, NULL);
4473 if (inf != NULL) {
4474 readonly_source_fs = g_file_info_get_attribute_boolean (inf, "filesystem::readonly");
4475 g_object_unref (inf);
4476 }
4477 g_object_unref (source_dir);
4478 }
4479
4480 unique_names = (job->destination == NULL);
4481 i = 0;
4482 for (l = job->files;
4483 l != NULL && !job_aborted (common);
4484 l = l->next) {
4485 src = l->data;
4486
4487 if (i < job->n_icon_positions) {
4488 point = &job->icon_positions[i];
4489 } else {
4490 point = NULL;
4491 }
4492
4493
4494 same_fs = FALSE;
4495 if (dest_fs_id) {
4496 same_fs = has_fs_id (src, dest_fs_id);
4497 }
4498
4499 if (job->destination) {
4500 dest = g_object_ref (job->destination);
4501 } else {
4502 dest = g_file_get_parent (src);
4503
4504 }
4505 if (dest) {
4506 skipped_file = FALSE;
4507 copy_move_file (job, src, dest,
4508 same_fs, unique_names,
4509 &dest_fs_type,
4510 source_info, transfer_info,
4511 job->debuting_files,
4512 point, FALSE, &skipped_file,
4513 readonly_source_fs);
4514 g_object_unref (dest);
4515 }
4516 i++;
4517 }
4518
4519 g_free (dest_fs_type);
4520 }
4521
4522 static gboolean
4523 copy_job_done (gpointer user_data)
4524 {
4525 CopyMoveJob *job;
4526
4527 job = user_data;
4528 if (job->done_callback) {
4529 job->done_callback (job->debuting_files,
4530 !job_aborted ((CommonJob *) job),
4531 job->done_callback_data);
4532 }
4533
4534 g_list_free_full (job->files, g_object_unref);
4535 if (job->destination) {
4536 g_object_unref (job->destination);
4537 }
4538 if (job->desktop_location) {
4539 g_object_unref (job->desktop_location);
4540 }
4541 g_hash_table_unref (job->debuting_files);
4542 g_free (job->icon_positions);
4543 g_free (job->target_name);
4544
4545 g_clear_object (&job->fake_display_source);
4546
4547 finalize_common ((CommonJob *)job);
4548
4549 nautilus_file_changes_consume_changes (TRUE);
4550 return FALSE;
4551 }
4552
4553 static gboolean
4554 copy_job (GIOSchedulerJob *io_job,
4555 GCancellable *cancellable,
4556 gpointer user_data)
4557 {
4558 CopyMoveJob *job;
4559 CommonJob *common;
4560 SourceInfo source_info;
4561 TransferInfo transfer_info;
4562 char *dest_fs_id;
4563 GFile *dest;
4564
4565 job = user_data;
4566 common = &job->common;
4567 common->io_job = io_job;
4568
4569 dest_fs_id = NULL;
4570
4571 nautilus_progress_info_start (job->common.progress);
4572
4573 scan_sources (job->files,
4574 &source_info,
4575 common,
4576 OP_KIND_COPY);
4577 if (job_aborted (common)) {
4578 goto aborted;
4579 }
4580
4581 if (job->destination) {
4582 dest = g_object_ref (job->destination);
4583 } else {
4584 /* Duplication, no dest,
4585 * use source for free size, etc
4586 */
4587 dest = g_file_get_parent (job->files->data);
4588 }
4589
4590 verify_destination (&job->common,
4591 dest,
4592 &dest_fs_id,
4593 source_info.num_bytes);
4594 g_object_unref (dest);
4595 if (job_aborted (common)) {
4596 goto aborted;
4597 }
4598
4599 g_timer_start (job->common.time);
4600
4601 memset (&transfer_info, 0, sizeof (transfer_info));
4602 copy_files (job,
4603 dest_fs_id,
4604 &source_info, &transfer_info);
4605
4606 aborted:
4607
4608 g_free (dest_fs_id);
4609
4610 g_io_scheduler_job_send_to_mainloop_async (io_job,
4611 copy_job_done,
4612 job,
4613 NULL);
4614
4615 return FALSE;
4616 }
4617
4618 void
4619 nautilus_file_operations_copy_file (GFile *source_file,
4620 GFile *target_dir,
4621 const gchar *source_display_name,
4622 const gchar *new_name,
4623 GtkWindow *parent_window,
4624 NautilusCopyCallback done_callback,
4625 gpointer done_callback_data)
4626 {
4627 CopyMoveJob *job;
4628
4629 job = op_job_new (CopyMoveJob, parent_window);
4630 job->done_callback = done_callback;
4631 job->done_callback_data = done_callback_data;
4632 job->files = g_list_append (NULL, g_object_ref (source_file));
4633 job->destination = g_object_ref (target_dir);
4634 job->target_name = g_strdup (new_name);
4635 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
4636
4637 if (source_display_name != NULL) {
4638 gchar *path;
4639
4640 path = g_build_filename ("/", source_display_name, NULL);
4641 job->fake_display_source = g_file_new_for_path (path);
4642
4643 g_free (path);
4644 }
4645
4646 inhibit_power_manager ((CommonJob *)job, _("Copying Files"));
4647
4648 g_io_scheduler_push_job (copy_job,
4649 job,
4650 NULL, /* destroy notify */
4651 0,
4652 job->common.cancellable);
4653 }
4654
4655 void
4656 nautilus_file_operations_copy (GList *files,
4657 GArray *relative_item_points,
4658 GFile *target_dir,
4659 GtkWindow *parent_window,
4660 NautilusCopyCallback done_callback,
4661 gpointer done_callback_data)
4662 {
4663 CopyMoveJob *job;
4664
4665 job = op_job_new (CopyMoveJob, parent_window);
4666 job->desktop_location = nautilus_get_desktop_location ();
4667 job->done_callback = done_callback;
4668 job->done_callback_data = done_callback_data;
4669 job->files = eel_g_object_list_copy (files);
4670 job->destination = g_object_ref (target_dir);
4671 if (relative_item_points != NULL &&
4672 relative_item_points->len > 0) {
4673 job->icon_positions =
4674 g_memdup (relative_item_points->data,
4675 sizeof (GdkPoint) * relative_item_points->len);
4676 job->n_icon_positions = relative_item_points->len;
4677 }
4678 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
4679
4680 inhibit_power_manager ((CommonJob *)job, _("Copying Files"));
4681
4682 if (!nautilus_file_undo_manager_pop_flag ()) {
4683 GFile* src_dir;
4684
4685 src_dir = g_file_get_parent (files->data);
4686 job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_COPY,
4687 g_list_length (files),
4688 src_dir, target_dir);
4689
4690 g_object_unref (src_dir);
4691 }
4692
4693 g_io_scheduler_push_job (copy_job,
4694 job,
4695 NULL, /* destroy notify */
4696 0,
4697 job->common.cancellable);
4698 }
4699
4700 static void
4701 report_move_progress (CopyMoveJob *move_job, int total, int left)
4702 {
4703 CommonJob *job;
4704
4705 job = (CommonJob *)move_job;
4706
4707 nautilus_progress_info_take_status (job->progress,
4708 f (_("Preparing to Move to “%B”"),
4709 move_job->destination));
4710
4711 nautilus_progress_info_take_details (job->progress,
4712 f (ngettext ("Preparing to move %'d file",
4713 "Preparing to move %'d files",
4714 left), left));
4715
4716 nautilus_progress_info_pulse_progress (job->progress);
4717 }
4718
4719 typedef struct {
4720 GFile *file;
4721 gboolean overwrite;
4722 gboolean has_position;
4723 GdkPoint position;
4724 } MoveFileCopyFallback;
4725
4726 static MoveFileCopyFallback *
4727 move_copy_file_callback_new (GFile *file,
4728 gboolean overwrite,
4729 GdkPoint *position)
4730 {
4731 MoveFileCopyFallback *fallback;
4732
4733 fallback = g_new (MoveFileCopyFallback, 1);
4734 fallback->file = file;
4735 fallback->overwrite = overwrite;
4736 if (position) {
4737 fallback->has_position = TRUE;
4738 fallback->position = *position;
4739 } else {
4740 fallback->has_position = FALSE;
4741 }
4742
4743 return fallback;
4744 }
4745
4746 static GList *
4747 get_files_from_fallbacks (GList *fallbacks)
4748 {
4749 MoveFileCopyFallback *fallback;
4750 GList *res, *l;
4751
4752 res = NULL;
4753 for (l = fallbacks; l != NULL; l = l->next) {
4754 fallback = l->data;
4755 res = g_list_prepend (res, fallback->file);
4756 }
4757 return g_list_reverse (res);
4758 }
4759
4760 static void
4761 move_file_prepare (CopyMoveJob *move_job,
4762 GFile *src,
4763 GFile *dest_dir,
4764 gboolean same_fs,
4765 char **dest_fs_type,
4766 GHashTable *debuting_files,
4767 GdkPoint *position,
4768 GList **fallback_files,
4769 int files_left)
4770 {
4771 GFile *dest, *new_dest;
4772 GError *error;
4773 CommonJob *job;
4774 gboolean overwrite;
4775 char *primary, *secondary, *details;
4776 int response;
4777 GFileCopyFlags flags;
4778 MoveFileCopyFallback *fallback;
4779 gboolean handled_invalid_filename;
4780
4781 overwrite = FALSE;
4782 handled_invalid_filename = *dest_fs_type != NULL;
4783
4784 job = (CommonJob *)move_job;
4785
4786 dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
4787
4788
4789 /* Don't allow recursive move/copy into itself.
4790 * (We would get a file system error if we proceeded but it is nicer to
4791 * detect and report it at this level) */
4792 if (test_dir_is_parent (dest_dir, src)) {
4793 if (job->skip_all_error) {
4794 goto out;
4795 }
4796
4797 /* the run_warning() frees all strings passed in automatically */
4798 primary = move_job->is_move ? g_strdup (_("You cannot move a folder into itself."))
4799 : g_strdup (_("You cannot copy a folder into itself."));
4800 secondary = g_strdup (_("The destination folder is inside the source folder."));
4801
4802 response = run_warning (job,
4803 primary,
4804 secondary,
4805 NULL,
4806 files_left > 1,
4807 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
4808 NULL);
4809
4810 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
4811 abort_job (job);
4812 } else if (response == 1) { /* skip all */
4813 job->skip_all_error = TRUE;
4814 } else if (response == 2) { /* skip */
4815 /* do nothing */
4816 } else {
4817 g_assert_not_reached ();
4818 }
4819
4820 goto out;
4821 }
4822
4823 retry:
4824
4825 flags = G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_NO_FALLBACK_FOR_MOVE;
4826 if (overwrite) {
4827 flags |= G_FILE_COPY_OVERWRITE;
4828 }
4829
4830 error = NULL;
4831 if (g_file_move (src, dest,
4832 flags,
4833 job->cancellable,
4834 NULL,
4835 NULL,
4836 &error)) {
4837
4838 if (debuting_files) {
4839 g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
4840 }
4841
4842 nautilus_file_changes_queue_file_moved (src, dest);
4843
4844 if (position) {
4845 nautilus_file_changes_queue_schedule_position_set (dest, *position, job->screen_num);
4846 } else {
4847 nautilus_file_changes_queue_schedule_position_remove (dest);
4848 }
4849
4850 if (job->undo_info != NULL) {
4851 nautilus_file_undo_info_ext_add_origin_target_pair (NAUTILUS_FILE_UNDO_INFO_EXT (job->undo_info),
4852 src, dest);
4853 }
4854
4855 return;
4856 }
4857
4858 if (IS_IO_ERROR (error, INVALID_FILENAME) &&
4859 !handled_invalid_filename) {
4860 g_error_free (error);
4861
4862 handled_invalid_filename = TRUE;
4863
4864 g_assert (*dest_fs_type == NULL);
4865 *dest_fs_type = query_fs_type (dest_dir, job->cancellable);
4866
4867 new_dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
4868 if (!g_file_equal (dest, new_dest)) {
4869 g_object_unref (dest);
4870 dest = new_dest;
4871 goto retry;
4872 } else {
4873 g_object_unref (new_dest);
4874 }
4875 }
4876
4877 /* Conflict */
4878 else if (!overwrite &&
4879 IS_IO_ERROR (error, EXISTS)) {
4880 gboolean is_merge;
4881 ConflictResponseData *response;
4882
4883 g_error_free (error);
4884
4885 is_merge = FALSE;
4886 if (is_dir (dest) && is_dir (src)) {
4887 is_merge = TRUE;
4888 }
4889
4890 if ((is_merge && job->merge_all) ||
4891 (!is_merge && job->replace_all)) {
4892 overwrite = TRUE;
4893 goto retry;
4894 }
4895
4896 if (job->skip_all_conflict) {
4897 goto out;
4898 }
4899
4900 response = run_conflict_dialog (job, src, dest, dest_dir);
4901
4902 if (response->id == GTK_RESPONSE_CANCEL ||
4903 response->id == GTK_RESPONSE_DELETE_EVENT) {
4904 conflict_response_data_free (response);
4905 abort_job (job);
4906 } else if (response->id == CONFLICT_RESPONSE_SKIP) {
4907 if (response->apply_to_all) {
4908 job->skip_all_conflict = TRUE;
4909 }
4910 conflict_response_data_free (response);
4911 } else if (response->id == CONFLICT_RESPONSE_REPLACE) { /* merge/replace */
4912 if (response->apply_to_all) {
4913 if (is_merge) {
4914 job->merge_all = TRUE;
4915 } else {
4916 job->replace_all = TRUE;
4917 }
4918 }
4919 overwrite = TRUE;
4920 conflict_response_data_free (response);
4921 goto retry;
4922 } else if (response->id == CONFLICT_RESPONSE_RENAME) {
4923 g_object_unref (dest);
4924 dest = get_target_file_for_display_name (dest_dir,
4925 response->new_name);
4926 conflict_response_data_free (response);
4927 goto retry;
4928 } else {
4929 g_assert_not_reached ();
4930 }
4931 }
4932
4933 else if (IS_IO_ERROR (error, WOULD_RECURSE) ||
4934 IS_IO_ERROR (error, WOULD_MERGE) ||
4935 IS_IO_ERROR (error, NOT_SUPPORTED) ||
4936 (overwrite && IS_IO_ERROR (error, IS_DIRECTORY))) {
4937 g_error_free (error);
4938
4939 fallback = move_copy_file_callback_new (src,
4940 overwrite,
4941 position);
4942 *fallback_files = g_list_prepend (*fallback_files, fallback);
4943 }
4944
4945 else if (IS_IO_ERROR (error, CANCELLED)) {
4946 g_error_free (error);
4947 }
4948
4949 /* Other error */
4950 else {
4951 if (job->skip_all_error) {
4952 g_error_free (error);
4953 goto out;
4954 }
4955 primary = f (_("Error while moving “%B”."), src);
4956 secondary = f (_("There was an error moving the file into %F."), dest_dir);
4957 details = error->message;
4958
4959 response = run_warning (job,
4960 primary,
4961 secondary,
4962 details,
4963 files_left > 1,
4964 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
4965 NULL);
4966
4967 g_error_free (error);
4968
4969 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
4970 abort_job (job);
4971 } else if (response == 1) { /* skip all */
4972 job->skip_all_error = TRUE;
4973 } else if (response == 2) { /* skip */
4974 /* do nothing */
4975 } else {
4976 g_assert_not_reached ();
4977 }
4978 }
4979
4980 out:
4981 g_object_unref (dest);
4982 }
4983
4984 static void
4985 move_files_prepare (CopyMoveJob *job,
4986 const char *dest_fs_id,
4987 char **dest_fs_type,
4988 GList **fallbacks)
4989 {
4990 CommonJob *common;
4991 GList *l;
4992 GFile *src;
4993 gboolean same_fs;
4994 int i;
4995 GdkPoint *point;
4996 int total, left;
4997
4998 common = &job->common;
4999
5000 total = left = g_list_length (job->files);
5001
5002 report_move_progress (job, total, left);
5003
5004 i = 0;
5005 for (l = job->files;
5006 l != NULL && !job_aborted (common);
5007 l = l->next) {
5008 src = l->data;
5009
5010 if (i < job->n_icon_positions) {
5011 point = &job->icon_positions[i];
5012 } else {
5013 point = NULL;
5014 }
5015
5016
5017 same_fs = FALSE;
5018 if (dest_fs_id) {
5019 same_fs = has_fs_id (src, dest_fs_id);
5020 }
5021
5022 move_file_prepare (job, src, job->destination,
5023 same_fs, dest_fs_type,
5024 job->debuting_files,
5025 point,
5026 fallbacks,
5027 left);
5028 report_move_progress (job, total, --left);
5029 i++;
5030 }
5031
5032 *fallbacks = g_list_reverse (*fallbacks);
5033
5034
5035 }
5036
5037 static void
5038 move_files (CopyMoveJob *job,
5039 GList *fallbacks,
5040 const char *dest_fs_id,
5041 char **dest_fs_type,
5042 SourceInfo *source_info,
5043 TransferInfo *transfer_info)
5044 {
5045 CommonJob *common;
5046 GList *l;
5047 GFile *src;
5048 gboolean same_fs;
5049 int i;
5050 GdkPoint *point;
5051 gboolean skipped_file;
5052 MoveFileCopyFallback *fallback;
5053 common = &job->common;
5054
5055 report_copy_progress (job, source_info, transfer_info);
5056
5057 i = 0;
5058 for (l = fallbacks;
5059 l != NULL && !job_aborted (common);
5060 l = l->next) {
5061 fallback = l->data;
5062 src = fallback->file;
5063
5064 if (fallback->has_position) {
5065 point = &fallback->position;
5066 } else {
5067 point = NULL;
5068 }
5069
5070 same_fs = FALSE;
5071 if (dest_fs_id) {
5072 same_fs = has_fs_id (src, dest_fs_id);
5073 }
5074
5075 /* Set overwrite to true, as the user has
5076 selected overwrite on all toplevel items */
5077 skipped_file = FALSE;
5078 copy_move_file (job, src, job->destination,
5079 same_fs, FALSE, dest_fs_type,
5080 source_info, transfer_info,
5081 job->debuting_files,
5082 point, fallback->overwrite, &skipped_file, FALSE);
5083 i++;
5084 }
5085 }
5086
5087
5088 static gboolean
5089 move_job_done (gpointer user_data)
5090 {
5091 CopyMoveJob *job;
5092
5093 job = user_data;
5094 if (job->done_callback) {
5095 job->done_callback (job->debuting_files,
5096 !job_aborted ((CommonJob *) job),
5097 job->done_callback_data);
5098 }
5099
5100 g_list_free_full (job->files, g_object_unref);
5101 g_object_unref (job->destination);
5102 g_hash_table_unref (job->debuting_files);
5103 g_free (job->icon_positions);
5104
5105 finalize_common ((CommonJob *)job);
5106
5107 nautilus_file_changes_consume_changes (TRUE);
5108 return FALSE;
5109 }
5110
5111 static gboolean
5112 move_job (GIOSchedulerJob *io_job,
5113 GCancellable *cancellable,
5114 gpointer user_data)
5115 {
5116 CopyMoveJob *job;
5117 CommonJob *common;
5118 GList *fallbacks;
5119 SourceInfo source_info;
5120 TransferInfo transfer_info;
5121 char *dest_fs_id;
5122 char *dest_fs_type;
5123 GList *fallback_files;
5124
5125 job = user_data;
5126 common = &job->common;
5127 common->io_job = io_job;
5128
5129 dest_fs_id = NULL;
5130 dest_fs_type = NULL;
5131
5132 fallbacks = NULL;
5133
5134 nautilus_progress_info_start (job->common.progress);
5135
5136 verify_destination (&job->common,
5137 job->destination,
5138 &dest_fs_id,
5139 -1);
5140 if (job_aborted (common)) {
5141 goto aborted;
5142 }
5143
5144 /* This moves all files that we can do without copy + delete */
5145 move_files_prepare (job, dest_fs_id, &dest_fs_type, &fallbacks);
5146 if (job_aborted (common)) {
5147 goto aborted;
5148 }
5149
5150 /* The rest we need to do deep copy + delete behind on,
5151 so scan for size */
5152
5153 fallback_files = get_files_from_fallbacks (fallbacks);
5154 scan_sources (fallback_files,
5155 &source_info,
5156 common,
5157 OP_KIND_MOVE);
5158
5159 g_list_free (fallback_files);
5160
5161 if (job_aborted (common)) {
5162 goto aborted;
5163 }
5164
5165 verify_destination (&job->common,
5166 job->destination,
5167 NULL,
5168 source_info.num_bytes);
5169 if (job_aborted (common)) {
5170 goto aborted;
5171 }
5172
5173 memset (&transfer_info, 0, sizeof (transfer_info));
5174 move_files (job,
5175 fallbacks,
5176 dest_fs_id, &dest_fs_type,
5177 &source_info, &transfer_info);
5178
5179 aborted:
5180 g_list_free_full (fallbacks, g_free);
5181
5182 g_free (dest_fs_id);
5183 g_free (dest_fs_type);
5184
5185 g_io_scheduler_job_send_to_mainloop (io_job,
5186 move_job_done,
5187 job,
5188 NULL);
5189
5190 return FALSE;
5191 }
5192
5193 void
5194 nautilus_file_operations_move (GList *files,
5195 GArray *relative_item_points,
5196 GFile *target_dir,
5197 GtkWindow *parent_window,
5198 NautilusCopyCallback done_callback,
5199 gpointer done_callback_data)
5200 {
5201 CopyMoveJob *job;
5202
5203 job = op_job_new (CopyMoveJob, parent_window);
5204 job->is_move = TRUE;
5205 job->done_callback = done_callback;
5206 job->done_callback_data = done_callback_data;
5207 job->files = eel_g_object_list_copy (files);
5208 job->destination = g_object_ref (target_dir);
5209 if (relative_item_points != NULL &&
5210 relative_item_points->len > 0) {
5211 job->icon_positions =
5212 g_memdup (relative_item_points->data,
5213 sizeof (GdkPoint) * relative_item_points->len);
5214 job->n_icon_positions = relative_item_points->len;
5215 }
5216 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
5217
5218 inhibit_power_manager ((CommonJob *)job, _("Moving Files"));
5219
5220 if (!nautilus_file_undo_manager_pop_flag ()) {
5221 GFile* src_dir;
5222
5223 src_dir = g_file_get_parent (files->data);
Possibly related backtrace: 2a2c08219faa5f80a13ebe5a4b1d55d11a493f82 MatchResult(frame_number=1, dist=0)
Possibly related backtrace: 248a2415ad56492f05b0e5efc5d90732be58e935 MatchResult(frame_number=1, dist=0)
Possibly related backtrace: 260c1ab3ca4f7554ba914fdc1daa20edbea74d6d MatchResult(frame_number=1, dist=0)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
5224
5225 if (g_file_has_uri_scheme (g_list_first (files)->data, "trash")) {
5226 job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_RESTORE_FROM_TRASH,
5227 g_list_length (files),
5228 src_dir, target_dir);
5229 } else {
5230 job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_MOVE,
5231 g_list_length (files),
5232 src_dir, target_dir);
5233 }
5234
5235 g_object_unref (src_dir);
5236 }
5237
5238 g_io_scheduler_push_job (move_job,
5239 job,
5240 NULL, /* destroy notify */
5241 0,
5242 job->common.cancellable);
5243 }
5244
5245 static void
5246 report_link_progress (CopyMoveJob *link_job, int total, int left)
5247 {
5248 CommonJob *job;
5249
5250 job = (CommonJob *)link_job;
5251
5252 nautilus_progress_info_take_status (job->progress,
5253 f (_("Creating links in “%B”"),
5254 link_job->destination));
5255
5256 nautilus_progress_info_take_details (job->progress,
5257 f (ngettext ("Making link to %'d file",
5258 "Making links to %'d files",
5259 left), left));
5260
5261 nautilus_progress_info_set_progress (job->progress, left, total);
5262 }
5263
5264 static char *
5265 get_abs_path_for_symlink (GFile *file, GFile *destination)
5266 {
5267 GFile *root, *parent;
5268 char *relative, *abs;
5269
5270 if (g_file_is_native (file) || g_file_is_native (destination)) {
5271 return g_file_get_path (file);
5272 }
5273
5274 root = g_object_ref (file);
5275 while ((parent = g_file_get_parent (root)) != NULL) {
5276 g_object_unref (root);
5277 root = parent;
5278 }
5279
5280 relative = g_file_get_relative_path (root, file);
5281 g_object_unref (root);
5282 abs = g_strconcat ("/", relative, NULL);
5283 g_free (relative);
5284 return abs;
5285 }
5286
5287
5288 static void
5289 link_file (CopyMoveJob *job,
5290 GFile *src, GFile *dest_dir,
5291 char **dest_fs_type,
5292 GHashTable *debuting_files,
5293 GdkPoint *position,
5294 int files_left)
5295 {
5296 GFile *src_dir, *dest, *new_dest;
5297 int count;
5298 char *path;
5299 gboolean not_local;
5300 GError *error;
5301 CommonJob *common;
5302 char *primary, *secondary, *details;
5303 int response;
5304 gboolean handled_invalid_filename;
5305
5306 common = (CommonJob *)job;
5307
5308 count = 0;
5309
5310 src_dir = g_file_get_parent (src);
5311 if (g_file_equal (src_dir, dest_dir)) {
5312 count = 1;
5313 }
5314 g_object_unref (src_dir);
5315
5316 handled_invalid_filename = *dest_fs_type != NULL;
5317
5318 dest = get_target_file_for_link (src, dest_dir, *dest_fs_type, count);
5319
5320 retry:
5321 error = NULL;
5322 not_local = FALSE;
5323
5324 path = get_abs_path_for_symlink (src, dest);
5325 if (path == NULL) {
5326 not_local = TRUE;
5327 } else if (g_file_make_symbolic_link (dest,
5328 path,
5329 common->cancellable,
5330 &error)) {
5331
5332 if (common->undo_info != NULL) {
5333 nautilus_file_undo_info_ext_add_origin_target_pair (NAUTILUS_FILE_UNDO_INFO_EXT (common->undo_info),
5334 src, dest);
5335 }
5336
5337 g_free (path);
5338 if (debuting_files) {
5339 g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
5340 }
5341
5342 nautilus_file_changes_queue_file_added (dest);
5343 if (position) {
5344 nautilus_file_changes_queue_schedule_position_set (dest, *position, common->screen_num);
5345 } else {
5346 nautilus_file_changes_queue_schedule_position_remove (dest);
5347 }
5348
5349 g_object_unref (dest);
5350
5351 return;
5352 }
5353 g_free (path);
5354
5355 if (error != NULL &&
5356 IS_IO_ERROR (error, INVALID_FILENAME) &&
5357 !handled_invalid_filename) {
5358 handled_invalid_filename = TRUE;
5359
5360 g_assert (*dest_fs_type == NULL);
5361 *dest_fs_type = query_fs_type (dest_dir, common->cancellable);
5362
5363 new_dest = get_target_file_for_link (src, dest_dir, *dest_fs_type, count);
5364
5365 if (!g_file_equal (dest, new_dest)) {
5366 g_object_unref (dest);
5367 dest = new_dest;
5368 g_error_free (error);
5369
5370 goto retry;
5371 } else {
5372 g_object_unref (new_dest);
5373 }
5374 }
5375 /* Conflict */
5376 if (error != NULL && IS_IO_ERROR (error, EXISTS)) {
5377 g_object_unref (dest);
5378 dest = get_target_file_for_link (src, dest_dir, *dest_fs_type, count++);
5379 g_error_free (error);
5380 goto retry;
5381 }
5382
5383 else if (error != NULL && IS_IO_ERROR (error, CANCELLED)) {
5384 g_error_free (error);
5385 }
5386
5387 /* Other error */
5388 else if (error != NULL) {
5389 if (common->skip_all_error) {
5390 goto out;
5391 }
5392 primary = f (_("Error while creating link to %B."), src);
5393 if (not_local) {
5394 secondary = f (_("Symbolic links only supported for local files"));
5395 details = NULL;
5396 } else if (IS_IO_ERROR (error, NOT_SUPPORTED)) {
5397 secondary = f (_("The target doesn't support symbolic links."));
5398 details = NULL;
5399 } else {
5400 secondary = f (_("There was an error creating the symlink in %F."), dest_dir);
5401 details = error->message;
5402 }
5403
5404 response = run_warning (common,
5405 primary,
5406 secondary,
5407 details,
5408 files_left > 1,
5409 GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
5410 NULL);
5411
5412 if (error) {
5413 g_error_free (error);
5414 }
5415
5416 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
5417 abort_job (common);
5418 } else if (response == 1) { /* skip all */
5419 common->skip_all_error = TRUE;
5420 } else if (response == 2) { /* skip */
5421 /* do nothing */
5422 } else {
5423 g_assert_not_reached ();
5424 }
5425 }
5426
5427 out:
5428 g_object_unref (dest);
5429 }
5430
5431 static gboolean
5432 link_job_done (gpointer user_data)
5433 {
5434 CopyMoveJob *job;
5435
5436 job = user_data;
5437 if (job->done_callback) {
5438 job->done_callback (job->debuting_files,
5439 !job_aborted ((CommonJob *) job),
5440 job->done_callback_data);
5441 }
5442
5443 g_list_free_full (job->files, g_object_unref);
5444 g_object_unref (job->destination);
5445 g_hash_table_unref (job->debuting_files);
5446 g_free (job->icon_positions);
5447
5448 finalize_common ((CommonJob *)job);
5449
5450 nautilus_file_changes_consume_changes (TRUE);
5451 return FALSE;
5452 }
5453
5454 static gboolean
5455 link_job (GIOSchedulerJob *io_job,
5456 GCancellable *cancellable,
5457 gpointer user_data)
5458 {
5459 CopyMoveJob *job;
5460 CommonJob *common;
5461 GFile *src;
5462 GdkPoint *point;
5463 char *dest_fs_type;
5464 int total, left;
5465 int i;
5466 GList *l;
5467
5468 job = user_data;
5469 common = &job->common;
5470 common->io_job = io_job;
5471
5472 dest_fs_type = NULL;
5473
5474 nautilus_progress_info_start (job->common.progress);
5475
5476 verify_destination (&job->common,
5477 job->destination,
5478 NULL,
5479 -1);
5480 if (job_aborted (common)) {
5481 goto aborted;
5482 }
5483
5484 total = left = g_list_length (job->files);
5485
5486 report_link_progress (job, total, left);
5487
5488 i = 0;
5489 for (l = job->files;
5490 l != NULL && !job_aborted (common);
5491 l = l->next) {
5492 src = l->data;
5493
5494 if (i < job->n_icon_positions) {
5495 point = &job->icon_positions[i];
5496 } else {
5497 point = NULL;
5498 }
5499
5500
5501 link_file (job, src, job->destination,
5502 &dest_fs_type, job->debuting_files,
5503 point, left);
5504 report_link_progress (job, total, --left);
5505 i++;
5506
5507 }
5508
5509 aborted:
5510 g_free (dest_fs_type);
5511
5512 g_io_scheduler_job_send_to_mainloop (io_job,
5513 link_job_done,
5514 job,
5515 NULL);
5516
5517 return FALSE;
5518 }
5519
5520 void
5521 nautilus_file_operations_link (GList *files,
5522 GArray *relative_item_points,
5523 GFile *target_dir,
5524 GtkWindow *parent_window,
5525 NautilusCopyCallback done_callback,
5526 gpointer done_callback_data)
5527 {
5528 CopyMoveJob *job;
5529
5530 job = op_job_new (CopyMoveJob, parent_window);
5531 job->done_callback = done_callback;
5532 job->done_callback_data = done_callback_data;
5533 job->files = eel_g_object_list_copy (files);
5534 job->destination = g_object_ref (target_dir);
5535 if (relative_item_points != NULL &&
5536 relative_item_points->len > 0) {
5537 job->icon_positions =
5538 g_memdup (relative_item_points->data,
5539 sizeof (GdkPoint) * relative_item_points->len);
5540 job->n_icon_positions = relative_item_points->len;
5541 }
5542 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
5543
5544 if (!nautilus_file_undo_manager_pop_flag ()) {
5545 GFile* src_dir;
5546
5547 src_dir = g_file_get_parent (files->data);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
5548 job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_CREATE_LINK,
5549 g_list_length (files),
5550 src_dir, target_dir);
5551 g_object_unref (src_dir);
5552 }
5553
5554 g_io_scheduler_push_job (link_job,
5555 job,
5556 NULL, /* destroy notify */
5557 0,
5558 job->common.cancellable);
5559 }
5560
5561
5562 void
5563 nautilus_file_operations_duplicate (GList *files,
5564 GArray *relative_item_points,
5565 GtkWindow *parent_window,
5566 NautilusCopyCallback done_callback,
5567 gpointer done_callback_data)
5568 {
5569 CopyMoveJob *job;
5570
5571 job = op_job_new (CopyMoveJob, parent_window);
5572 job->done_callback = done_callback;
5573 job->done_callback_data = done_callback_data;
5574 job->files = eel_g_object_list_copy (files);
5575 job->destination = NULL;
5576 if (relative_item_points != NULL &&
5577 relative_item_points->len > 0) {
5578 job->icon_positions =
5579 g_memdup (relative_item_points->data,
5580 sizeof (GdkPoint) * relative_item_points->len);
5581 job->n_icon_positions = relative_item_points->len;
5582 }
5583 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
5584
5585 if (!nautilus_file_undo_manager_pop_flag ()) {
5586 GFile* src_dir;
5587
5588 src_dir = g_file_get_parent (files->data);
5589 job->common.undo_info =
5590 nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_DUPLICATE,
5591 g_list_length (files),
5592 src_dir, src_dir);
5593 g_object_unref (src_dir);
5594 }
5595
5596 g_io_scheduler_push_job (copy_job,
5597 job,
5598 NULL, /* destroy notify */
5599 0,
5600 job->common.cancellable);
5601 }
5602
5603 static gboolean
5604 set_permissions_job_done (gpointer user_data)
5605 {
5606 SetPermissionsJob *job;
5607
5608 job = user_data;
5609
5610 g_object_unref (job->file);
5611
5612 if (job->done_callback) {
5613 job->done_callback (!job_aborted ((CommonJob *) job),
5614 job->done_callback_data);
5615 }
5616
5617 finalize_common ((CommonJob *)job);
5618 return FALSE;
5619 }
5620
5621 static void
5622 set_permissions_file (SetPermissionsJob *job,
5623 GFile *file,
5624 GFileInfo *info)
5625 {
5626 CommonJob *common;
5627 GFileInfo *child_info;
5628 gboolean free_info;
5629 guint32 current;
5630 guint32 value;
5631 guint32 mask;
5632 GFileEnumerator *enumerator;
5633 GFile *child;
5634
5635 common = (CommonJob *)job;
5636
5637 nautilus_progress_info_pulse_progress (common->progress);
5638
5639 free_info = FALSE;
5640 if (info == NULL) {
5641 free_info = TRUE;
5642 info = g_file_query_info (file,
5643 G_FILE_ATTRIBUTE_STANDARD_TYPE","
5644 G_FILE_ATTRIBUTE_UNIX_MODE,
5645 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
5646 common->cancellable,
5647 NULL);
5648 /* Ignore errors */
5649 if (info == NULL) {
5650 return;
5651 }
5652 }
5653
5654 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
5655 value = job->dir_permissions;
5656 mask = job->dir_mask;
5657 } else {
5658 value = job->file_permissions;
5659 mask = job->file_mask;
5660 }
5661
5662
5663 if (!job_aborted (common) &&
5664 g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE)) {
5665 current = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
5666
5667 if (common->undo_info != NULL) {
5668 nautilus_file_undo_info_rec_permissions_add_file (NAUTILUS_FILE_UNDO_INFO_REC_PERMISSIONS (common->undo_info),
5669 file, current);
5670 }
5671
5672 current = (current & ~mask) | value;
5673
5674 g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_MODE,
5675 current, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
5676 common->cancellable, NULL);
5677 }
5678
5679 if (!job_aborted (common) &&
5680 g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
5681 enumerator = g_file_enumerate_children (file,
5682 G_FILE_ATTRIBUTE_STANDARD_NAME","
5683 G_FILE_ATTRIBUTE_STANDARD_TYPE","
5684 G_FILE_ATTRIBUTE_UNIX_MODE,
5685 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
5686 common->cancellable,
5687 NULL);
5688 if (enumerator) {
5689 while (!job_aborted (common) &&
5690 (child_info = g_file_enumerator_next_file (enumerator, common->cancellable, NULL)) != NULL) {
5691 child = g_file_get_child (file,
5692 g_file_info_get_name (child_info));
5693 set_permissions_file (job, child, child_info);
5694 g_object_unref (child);
5695 g_object_unref (child_info);
5696 }
5697 g_file_enumerator_close (enumerator, common->cancellable, NULL);
5698 g_object_unref (enumerator);
5699 }
5700 }
5701 if (free_info) {
5702 g_object_unref (info);
5703 }
5704 }
5705
5706
5707 static gboolean
5708 set_permissions_job (GIOSchedulerJob *io_job,
5709 GCancellable *cancellable,
5710 gpointer user_data)
5711 {
5712 SetPermissionsJob *job = user_data;
5713 CommonJob *common;
5714
5715 common = (CommonJob *)job;
5716 common->io_job = io_job;
5717
5718 nautilus_progress_info_set_status (common->progress,
5719 _("Setting permissions"));
5720
5721 nautilus_progress_info_start (job->common.progress);
5722
5723 set_permissions_file (job, job->file, NULL);
5724
5725 g_io_scheduler_job_send_to_mainloop_async (io_job,
5726 set_permissions_job_done,
5727 job,
5728 NULL);
5729
5730 return FALSE;
5731 }
5732
5733
5734
5735 void
5736 nautilus_file_set_permissions_recursive (const char *directory,
5737 guint32 file_permissions,
5738 guint32 file_mask,
5739 guint32 dir_permissions,
5740 guint32 dir_mask,
5741 NautilusOpCallback callback,
5742 gpointer callback_data)
5743 {
5744 SetPermissionsJob *job;
5745
5746 job = op_job_new (SetPermissionsJob, NULL);
5747 job->file = g_file_new_for_uri (directory);
5748 job->file_permissions = file_permissions;
5749 job->file_mask = file_mask;
5750 job->dir_permissions = dir_permissions;
5751 job->dir_mask = dir_mask;
5752 job->done_callback = callback;
5753 job->done_callback_data = callback_data;
5754
5755 if (!nautilus_file_undo_manager_pop_flag ()) {
5756 job->common.undo_info =
5757 nautilus_file_undo_info_rec_permissions_new (job->file,
5758 file_permissions, file_mask,
5759 dir_permissions, dir_mask);
5760 }
5761
5762 g_io_scheduler_push_job (set_permissions_job,
5763 job,
5764 NULL,
5765 0,
5766 NULL);
5767 }
5768
5769 static GList *
5770 location_list_from_uri_list (const GList *uris)
5771 {
5772 const GList *l;
5773 GList *files;
5774 GFile *f;
5775
5776 files = NULL;
5777 for (l = uris; l != NULL; l = l->next) {
5778 f = g_file_new_for_uri (l->data);
5779 files = g_list_prepend (files, f);
5780 }
5781
5782 return g_list_reverse (files);
5783 }
5784
5785 typedef struct {
5786 NautilusCopyCallback real_callback;
5787 gpointer real_data;
5788 } MoveTrashCBData;
5789
5790 static void
5791 callback_for_move_to_trash (GHashTable *debuting_uris,
5792 gboolean user_cancelled,
5793 MoveTrashCBData *data)
5794 {
5795 if (data->real_callback)
5796 data->real_callback (debuting_uris, !user_cancelled, data->real_data);
5797 g_slice_free (MoveTrashCBData, data);
5798 }
5799
5800 void
5801 nautilus_file_operations_copy_move (const GList *item_uris,
5802 GArray *relative_item_points,
5803 const char *target_dir,
5804 GdkDragAction copy_action,
5805 GtkWidget *parent_view,
5806 NautilusCopyCallback done_callback,
5807 gpointer done_callback_data)
5808 {
5809 GList *locations;
5810 GList *p;
5811 GFile *dest, *src_dir;
5812 GtkWindow *parent_window;
5813 gboolean target_is_mapping;
5814 gboolean have_nonmapping_source;
5815
5816 dest = NULL;
5817 target_is_mapping = FALSE;
5818 have_nonmapping_source = FALSE;
5819
5820 if (target_dir) {
5821 dest = g_file_new_for_uri (target_dir);
5822 if (g_file_has_uri_scheme (dest, "burn")) {
5823 target_is_mapping = TRUE;
5824 }
5825 }
5826
5827 locations = location_list_from_uri_list (item_uris);
5828
5829 for (p = locations; p != NULL; p = p->next) {
5830 if (!g_file_has_uri_scheme ((GFile* )p->data, "burn")) {
5831 have_nonmapping_source = TRUE;
5832 }
5833 }
5834
5835 if (target_is_mapping && have_nonmapping_source && copy_action == GDK_ACTION_MOVE) {
5836 /* never move to "burn:///", but fall back to copy.
5837 * This is a workaround, because otherwise the source files would be removed.
5838 */
5839 copy_action = GDK_ACTION_COPY;
5840 }
5841
5842 parent_window = NULL;
5843 if (parent_view) {
5844 parent_window = (GtkWindow *)gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
5845 }
5846
5847 if (copy_action == GDK_ACTION_COPY) {
5848 src_dir = g_file_get_parent (locations->data);
Possibly related backtrace: eea39b8c6dde0dd1f59b69f3003b0370e488eced MatchResult(frame_number=1, dist=0)
Possibly related backtrace: 048839ce381ca4262c25bd0aef4f48b66024e1dc MatchResult(frame_number=1, dist=0)
Possibly related backtrace: ed7137125d7d9d67abb802a0420da284fe56dd02 MatchResult(frame_number=1, dist=0)
Possibly related backtrace: ff1539257de06347cc753b30a5f21f2dd9d512c9 MatchResult(frame_number=1, dist=0)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
5849 if (target_dir == NULL ||
5850 (src_dir != NULL &&
5851 g_file_equal (src_dir, dest))) {
5852
5853 nautilus_file_operations_duplicate (locations,
5854 relative_item_points,
5855 parent_window,
5856 done_callback, done_callback_data);
5857 } else {
5858 nautilus_file_operations_copy (locations,
5859 relative_item_points,
5860 dest,
5861 parent_window,
5862 done_callback, done_callback_data);
5863 }
5864 if (src_dir) {
5865 g_object_unref (src_dir);
5866 }
5867
5868 } else if (copy_action == GDK_ACTION_MOVE) {
5869 if (g_file_has_uri_scheme (dest, "trash")) {
5870 MoveTrashCBData *cb_data;
5871
5872 cb_data = g_slice_new0 (MoveTrashCBData);
5873 cb_data->real_callback = done_callback;
5874 cb_data->real_data = done_callback_data;
5875
5876 nautilus_file_operations_trash_or_delete (locations,
5877 parent_window,
5878 (NautilusDeleteCallback) callback_for_move_to_trash,
5879 cb_data);
5880 } else {
5881
5882 nautilus_file_operations_move (locations,
5883 relative_item_points,
5884 dest,
5885 parent_window,
5886 done_callback, done_callback_data);
5887 }
5888 } else {
5889
5890 nautilus_file_operations_link (locations,
5891 relative_item_points,
5892 dest,
5893 parent_window,
5894 done_callback, done_callback_data);
5895 }
5896
5897 g_list_free_full (locations, g_object_unref);
5898 if (dest) {
5899 g_object_unref (dest);
5900 }
5901 }
5902
5903 static gboolean
5904 create_job_done (gpointer user_data)
5905 {
5906 CreateJob *job;
5907
5908 job = user_data;
5909 if (job->done_callback) {
5910 job->done_callback (job->created_file,
5911 !job_aborted ((CommonJob *) job),
5912 job->done_callback_data);
5913 }
5914
5915 g_object_unref (job->dest_dir);
5916 if (job->src) {
5917 g_object_unref (job->src);
5918 }
5919 g_free (job->src_data);
5920 g_free (job->filename);
5921 if (job->created_file) {
5922 g_object_unref (job->created_file);
5923 }
5924
5925 finalize_common ((CommonJob *)job);
5926
5927 nautilus_file_changes_consume_changes (TRUE);
5928 return FALSE;
5929 }
5930
5931 static gboolean
5932 create_job (GIOSchedulerJob *io_job,
5933 GCancellable *cancellable,
5934 gpointer user_data)
5935 {
5936 CreateJob *job;
5937 CommonJob *common;
5938 int count;
5939 GFile *dest;
5940 char *basename;
5941 char *filename, *filename2, *new_filename;
5942 char *filename_base, *suffix;
5943 char *dest_fs_type;
5944 GError *error;
5945 gboolean res;
5946 gboolean filename_is_utf8;
5947 char *primary, *secondary, *details;
5948 int response;
5949 char *data;
5950 int length;
5951 GFileOutputStream *out;
5952 gboolean handled_invalid_filename;
5953 int max_length, offset;
5954
5955 job = user_data;
5956 common = &job->common;
5957 common->io_job = io_job;
5958
5959 nautilus_progress_info_start (job->common.progress);
5960
5961 handled_invalid_filename = FALSE;
5962
5963 dest_fs_type = NULL;
5964 filename = NULL;
5965 dest = NULL;
5966
5967 max_length = get_max_name_length (job->dest_dir);
5968
5969 verify_destination (common,
5970 job->dest_dir,
5971 NULL, -1);
5972 if (job_aborted (common)) {
5973 goto aborted;
5974 }
5975
5976 filename = g_strdup (job->filename);
5977 filename_is_utf8 = FALSE;
5978 if (filename) {
5979 filename_is_utf8 = g_utf8_validate (filename, -1, NULL);
5980 }
5981 if (filename == NULL) {
5982 if (job->make_dir) {
5983 /* localizers: the initial name of a new folder */
5984 filename = g_strdup (_("Untitled Folder"));
5985 filename_is_utf8 = TRUE; /* Pass in utf8 */
5986 } else {
5987 if (job->src != NULL) {
5988 basename = g_file_get_basename (job->src);
5989 /* localizers: the initial name of a new template document */
5990 filename = g_strdup_printf (_("Untitled %s"), basename);
5991
5992 g_free (basename);
5993 }
5994 if (filename == NULL) {
5995 /* localizers: the initial name of a new empty document */
5996 filename = g_strdup (_("Untitled Document"));
5997 filename_is_utf8 = TRUE; /* Pass in utf8 */
5998 }
5999 }
6000 }
6001
6002 make_file_name_valid_for_dest_fs (filename, dest_fs_type);
6003 if (filename_is_utf8) {
6004 dest = g_file_get_child_for_display_name (job->dest_dir, filename, NULL);
6005 }
6006 if (dest == NULL) {
6007 dest = g_file_get_child (job->dest_dir, filename);
6008 }
6009 count = 1;
6010
6011 retry:
6012
6013 error = NULL;
6014 if (job->make_dir) {
6015 res = g_file_make_directory (dest,
6016 common->cancellable,
6017 &error);
6018
6019 if (res && common->undo_info != NULL) {
6020 nautilus_file_undo_info_create_set_data (NAUTILUS_FILE_UNDO_INFO_CREATE (common->undo_info),
6021 dest, NULL, 0);
6022 }
6023
6024 } else {
6025 if (job->src) {
6026 res = g_file_copy (job->src,
6027 dest,
6028 G_FILE_COPY_NONE,
6029 common->cancellable,
6030 NULL, NULL,
6031 &error);
6032
6033 if (res && common->undo_info != NULL) {
6034 gchar *uri;
6035
6036 uri = g_file_get_uri (job->src);
6037 nautilus_file_undo_info_create_set_data (NAUTILUS_FILE_UNDO_INFO_CREATE (common->undo_info),
6038 dest, uri, 0);
6039
6040 g_free (uri);
6041 }
6042
6043 } else {
6044 data = "";
6045 length = 0;
6046 if (job->src_data) {
6047 data = job->src_data;
6048 length = job->length;
6049 }
6050
6051 out = g_file_create (dest,
6052 G_FILE_CREATE_NONE,
6053 common->cancellable,
6054 &error);
6055 if (out) {
6056 res = g_output_stream_write_all (G_OUTPUT_STREAM (out),
6057 data, length,
6058 NULL,
6059 common->cancellable,
6060 &error);
6061 if (res) {
6062 res = g_output_stream_close (G_OUTPUT_STREAM (out),
6063 common->cancellable,
6064 &error);
6065
6066 if (res && common->undo_info != NULL) {
6067 nautilus_file_undo_info_create_set_data (NAUTILUS_FILE_UNDO_INFO_CREATE (common->undo_info),
6068 dest, data, length);
6069 }
6070 }
6071
6072 /* This will close if the write failed and we didn't close */
6073 g_object_unref (out);
6074 } else {
6075 res = FALSE;
6076 }
6077 }
6078 }
6079
6080 if (res) {
6081 job->created_file = g_object_ref (dest);
6082 nautilus_file_changes_queue_file_added (dest);
6083 if (job->has_position) {
6084 nautilus_file_changes_queue_schedule_position_set (dest, job->position, common->screen_num);
6085 } else {
6086 nautilus_file_changes_queue_schedule_position_remove (dest);
6087 }
6088 } else {
6089 g_assert (error != NULL);
6090
6091 if (IS_IO_ERROR (error, INVALID_FILENAME) &&
6092 !handled_invalid_filename) {
6093 handled_invalid_filename = TRUE;
6094
6095 g_assert (dest_fs_type == NULL);
6096 dest_fs_type = query_fs_type (job->dest_dir, common->cancellable);
6097
6098 g_object_unref (dest);
6099
6100 if (count == 1) {
6101 new_filename = g_strdup (filename);
6102 } else {
6103 filename_base = eel_filename_strip_extension (filename);
6104 offset = strlen (filename_base);
6105 suffix = g_strdup (filename + offset);
6106
6107 filename2 = g_strdup_printf ("%s %d%s", filename_base, count, suffix);
6108
6109 new_filename = NULL;
6110 if (max_length > 0 && strlen (filename2) > max_length) {
6111 new_filename = shorten_utf8_string (filename2, strlen (filename2) - max_length);
6112 }
6113
6114 if (new_filename == NULL) {
6115 new_filename = g_strdup (filename2);
6116 }
6117
6118 g_free (filename2);
6119 g_free (suffix);
6120 }
6121
6122 if (make_file_name_valid_for_dest_fs (new_filename, dest_fs_type)) {
6123 g_object_unref (dest);
6124
6125 if (filename_is_utf8) {
6126 dest = g_file_get_child_for_display_name (job->dest_dir, new_filename, NULL);
6127 }
6128 if (dest == NULL) {
6129 dest = g_file_get_child (job->dest_dir, new_filename);
6130 }
6131
6132 g_free (new_filename);
6133 g_error_free (error);
6134 goto retry;
6135 }
6136 g_free (new_filename);
6137 } else if (IS_IO_ERROR (error, EXISTS)) {
6138 g_object_unref (dest);
6139 dest = NULL;
6140 filename_base = eel_filename_strip_extension (filename);
6141 offset = strlen (filename_base);
6142 suffix = g_strdup (filename + offset);
6143
6144 filename2 = g_strdup_printf ("%s %d%s", filename_base, ++count, suffix);
6145
6146 if (max_length > 0 && strlen (filename2) > max_length) {
6147 new_filename = shorten_utf8_string (filename2, strlen (filename2) - max_length);
6148 if (new_filename != NULL) {
6149 g_free (filename2);
6150 filename2 = new_filename;
6151 }
6152 }
6153
6154 make_file_name_valid_for_dest_fs (filename2, dest_fs_type);
6155 if (filename_is_utf8) {
6156 dest = g_file_get_child_for_display_name (job->dest_dir, filename2, NULL);
6157 }
6158 if (dest == NULL) {
6159 dest = g_file_get_child (job->dest_dir, filename2);
6160 }
6161 g_free (filename2);
6162 g_free (suffix);
6163 g_error_free (error);
6164 goto retry;
6165 }
6166
6167 else if (IS_IO_ERROR (error, CANCELLED)) {
6168 g_error_free (error);
6169 }
6170
6171 /* Other error */
6172 else {
6173 if (job->make_dir) {
6174 primary = f (_("Error while creating directory %B."), dest);
6175 } else {
6176 primary = f (_("Error while creating file %B."), dest);
6177 }
6178 secondary = f (_("There was an error creating the directory in %F."), job->dest_dir);
6179 details = error->message;
6180
6181 response = run_warning (common,
6182 primary,
6183 secondary,
6184 details,
6185 FALSE,
6186 GTK_STOCK_CANCEL, SKIP,
6187 NULL);
6188
6189 g_error_free (error);
6190
6191 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
6192 abort_job (common);
6193 } else if (response == 1) { /* skip */
6194 /* do nothing */
6195 } else {
6196 g_assert_not_reached ();
6197 }
6198 }
6199 }
6200
6201 aborted:
6202 if (dest) {
6203 g_object_unref (dest);
6204 }
6205 g_free (filename);
6206 g_free (dest_fs_type);
6207 g_io_scheduler_job_send_to_mainloop_async (io_job,
6208 create_job_done,
6209 job,
6210 NULL);
6211
6212 return FALSE;
6213 }
6214
6215 void
6216 nautilus_file_operations_new_folder (GtkWidget *parent_view,
6217 GdkPoint *target_point,
6218 const char *parent_dir,
6219 NautilusCreateCallback done_callback,
6220 gpointer done_callback_data)
6221 {
6222 CreateJob *job;
6223 GtkWindow *parent_window;
6224
6225 parent_window = NULL;
6226 if (parent_view) {
6227 parent_window = (GtkWindow *)gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
6228 }
6229
6230 job = op_job_new (CreateJob, parent_window);
6231 job->done_callback = done_callback;
6232 job->done_callback_data = done_callback_data;
6233 job->dest_dir = g_file_new_for_uri (parent_dir);
6234 job->make_dir = TRUE;
6235 if (target_point != NULL) {
6236 job->position = *target_point;
6237 job->has_position = TRUE;
6238 }
6239
6240 if (!nautilus_file_undo_manager_pop_flag ()) {
6241 job->common.undo_info = nautilus_file_undo_info_create_new (NAUTILUS_FILE_UNDO_OP_CREATE_FOLDER);
6242 }
6243
6244 g_io_scheduler_push_job (create_job,
6245 job,
6246 NULL, /* destroy notify */
6247 0,
6248 job->common.cancellable);
6249 }
6250
6251 void
6252 nautilus_file_operations_new_file_from_template (GtkWidget *parent_view,
6253 GdkPoint *target_point,
6254 const char *parent_dir,
6255 const char *target_filename,
6256 const char *template_uri,
6257 NautilusCreateCallback done_callback,
6258 gpointer done_callback_data)
6259 {
6260 CreateJob *job;
6261 GtkWindow *parent_window;
6262
6263 parent_window = NULL;
6264 if (parent_view) {
6265 parent_window = (GtkWindow *)gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
6266 }
6267
6268 job = op_job_new (CreateJob, parent_window);
6269 job->done_callback = done_callback;
6270 job->done_callback_data = done_callback_data;
6271 job->dest_dir = g_file_new_for_uri (parent_dir);
6272 if (target_point != NULL) {
6273 job->position = *target_point;
6274 job->has_position = TRUE;
6275 }
6276 job->filename = g_strdup (target_filename);
6277
6278 if (template_uri) {
6279 job->src = g_file_new_for_uri (template_uri);
6280 }
6281
6282 if (!nautilus_file_undo_manager_pop_flag ()) {
6283 job->common.undo_info = nautilus_file_undo_info_create_new (NAUTILUS_FILE_UNDO_OP_CREATE_FILE_FROM_TEMPLATE);
6284 }
6285
6286 g_io_scheduler_push_job (create_job,
6287 job,
6288 NULL, /* destroy notify */
6289 0,
6290 job->common.cancellable);
6291 }
6292
6293 void
6294 nautilus_file_operations_new_file (GtkWidget *parent_view,
6295 GdkPoint *target_point,
6296 const char *parent_dir,
6297 const char *target_filename,
6298 const char *initial_contents,
6299 int length,
6300 NautilusCreateCallback done_callback,
6301 gpointer done_callback_data)
6302 {
6303 CreateJob *job;
6304 GtkWindow *parent_window;
6305
6306 parent_window = NULL;
6307 if (parent_view) {
6308 parent_window = (GtkWindow *)gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
6309 }
6310
6311 job = op_job_new (CreateJob, parent_window);
6312 job->done_callback = done_callback;
6313 job->done_callback_data = done_callback_data;
6314 job->dest_dir = g_file_new_for_uri (parent_dir);
6315 if (target_point != NULL) {
6316 job->position = *target_point;
6317 job->has_position = TRUE;
6318 }
6319 job->src_data = g_memdup (initial_contents, length);
6320 job->length = length;
6321 job->filename = g_strdup (target_filename);
6322
6323 if (!nautilus_file_undo_manager_pop_flag ()) {
6324 job->common.undo_info = nautilus_file_undo_info_create_new (NAUTILUS_FILE_UNDO_OP_CREATE_EMPTY_FILE);
6325 }
6326
6327 g_io_scheduler_push_job (create_job,
6328 job,
6329 NULL, /* destroy notify */
6330 0,
6331 job->common.cancellable);
6332 }
6333
6334
6335
6336 static void
6337 delete_trash_file (CommonJob *job,
6338 GFile *file,
6339 gboolean del_file,
6340 gboolean del_children)
6341 {
6342 GFileInfo *info;
6343 GFile *child;
6344 GFileEnumerator *enumerator;
6345
6346 if (job_aborted (job)) {
6347 return;
6348 }
6349
6350 if (del_children) {
6351 enumerator = g_file_enumerate_children (file,
6352 G_FILE_ATTRIBUTE_STANDARD_NAME ","
6353 G_FILE_ATTRIBUTE_STANDARD_TYPE,
6354 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
6355 job->cancellable,
6356 NULL);
6357 if (enumerator) {
6358 while (!job_aborted (job) &&
6359 (info = g_file_enumerator_next_file (enumerator, job->cancellable, NULL)) != NULL) {
6360 child = g_file_get_child (file,
6361 g_file_info_get_name (info));
6362 delete_trash_file (job, child, TRUE,
6363 g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
6364 g_object_unref (child);
6365 g_object_unref (info);
6366 }
6367 g_file_enumerator_close (enumerator, job->cancellable, NULL);
6368 g_object_unref (enumerator);
6369 }
6370 }
6371
6372 if (!job_aborted (job) && del_file) {
6373 g_file_delete (file, job->cancellable, NULL);
6374 }
6375 }
6376
6377 static gboolean
6378 empty_trash_job_done (gpointer user_data)
6379 {
6380 EmptyTrashJob *job;
6381
6382 job = user_data;
6383
6384 g_list_free_full (job->trash_dirs, g_object_unref);
6385
6386 if (job->done_callback) {
6387 job->done_callback (!job_aborted ((CommonJob *) job),
6388 job->done_callback_data);
6389 }
6390
6391 finalize_common ((CommonJob *)job);
6392 return FALSE;
6393 }
6394
6395 static gboolean
6396 empty_trash_job (GIOSchedulerJob *io_job,
6397 GCancellable *cancellable,
6398 gpointer user_data)
6399 {
6400 EmptyTrashJob *job = user_data;
6401 CommonJob *common;
6402 GList *l;
6403 gboolean confirmed;
6404
6405 common = (CommonJob *)job;
6406 common->io_job = io_job;
6407
6408 nautilus_progress_info_start (job->common.progress);
6409
6410 if (job->should_confirm) {
6411 confirmed = confirm_empty_trash (common);
6412 } else {
6413 confirmed = TRUE;
6414 }
6415 if (confirmed) {
6416 for (l = job->trash_dirs;
6417 l != NULL && !job_aborted (common);
6418 l = l->next) {
6419 delete_trash_file (common, l->data, FALSE, TRUE);
6420 }
6421 }
6422
6423 g_io_scheduler_job_send_to_mainloop_async (io_job,
6424 empty_trash_job_done,
6425 job,
6426 NULL);
6427
6428 return FALSE;
6429 }
6430
6431 void
6432 nautilus_file_operations_empty_trash (GtkWidget *parent_view)
6433 {
6434 EmptyTrashJob *job;
6435 GtkWindow *parent_window;
6436
6437 parent_window = NULL;
6438 if (parent_view) {
6439 parent_window = (GtkWindow *)gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
6440 }
6441
6442 job = op_job_new (EmptyTrashJob, parent_window);
6443 job->trash_dirs = g_list_prepend (job->trash_dirs,
6444 g_file_new_for_uri ("trash:"));
6445 job->should_confirm = TRUE;
6446
6447 inhibit_power_manager ((CommonJob *)job, _("Emptying Trash"));
6448
6449 g_io_scheduler_push_job (empty_trash_job,
6450 job,
6451 NULL,
6452 0,
6453 NULL);
6454 }
6455
6456 static gboolean
6457 mark_trusted_job_done (gpointer user_data)
6458 {
6459 MarkTrustedJob *job = user_data;
6460
6461 g_object_unref (job->file);
6462
6463 if (job->done_callback) {
6464 job->done_callback (!job_aborted ((CommonJob *) job),
6465 job->done_callback_data);
6466 }
6467
6468 finalize_common ((CommonJob *)job);
6469 return FALSE;
6470 }
6471
6472 #define TRUSTED_SHEBANG "#!/usr/bin/env xdg-open\n"
6473
6474 static void
6475 mark_desktop_file_trusted (CommonJob *common,
6476 GCancellable *cancellable,
6477 GFile *file,
6478 gboolean interactive)
6479 {
6480 char *contents, *new_contents;
6481 gsize length, new_length;
6482 GError *error;
6483 guint32 current_perms, new_perms;
6484 int response;
6485 GFileInfo *info;
6486
6487 retry:
6488 error = NULL;
6489 if (!g_file_load_contents (file,
6490 cancellable,
6491 &contents, &length,
6492 NULL, &error)) {
6493 if (interactive) {
6494 response = run_error (common,
6495 g_strdup (_("Unable to mark launcher trusted (executable)")),
6496 error->message,
6497 NULL,
6498 FALSE,
6499 GTK_STOCK_CANCEL, RETRY,
6500 NULL);
6501 } else {
6502 response = 0;
6503 }
6504
6505
6506 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
6507 abort_job (common);
6508 } else if (response == 1) {
6509 goto retry;
6510 } else {
6511 g_assert_not_reached ();
6512 }
6513
6514 goto out;
6515 }
6516
6517 if (!g_str_has_prefix (contents, "#!")) {
6518 new_length = length + strlen (TRUSTED_SHEBANG);
6519 new_contents = g_malloc (new_length);
6520
6521 strcpy (new_contents, TRUSTED_SHEBANG);
6522 memcpy (new_contents + strlen (TRUSTED_SHEBANG),
6523 contents, length);
6524
6525 if (!g_file_replace_contents (file,
6526 new_contents,
6527 new_length,
6528 NULL,
6529 FALSE, 0,
6530 NULL, cancellable, &error)) {
6531 g_free (contents);
6532 g_free (new_contents);
6533
6534 if (interactive) {
6535 response = run_error (common,
6536 g_strdup (_("Unable to mark launcher trusted (executable)")),
6537 error->message,
6538 NULL,
6539 FALSE,
6540 GTK_STOCK_CANCEL, RETRY,
6541 NULL);
6542 } else {
6543 response = 0;
6544 }
6545
6546 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
6547 abort_job (common);
6548 } else if (response == 1) {
6549 goto retry;
6550 } else {
6551 g_assert_not_reached ();
6552 }
6553
6554 goto out;
6555 }
6556 g_free (new_contents);
6557
6558 }
6559 g_free (contents);
6560
6561 info = g_file_query_info (file,
6562 G_FILE_ATTRIBUTE_STANDARD_TYPE","
6563 G_FILE_ATTRIBUTE_UNIX_MODE,
6564 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
6565 common->cancellable,
6566 &error);
6567
6568 if (info == NULL) {
6569 if (interactive) {
6570 response = run_error (common,
6571 g_strdup (_("Unable to mark launcher trusted (executable)")),
6572 error->message,
6573 NULL,
6574 FALSE,
6575 GTK_STOCK_CANCEL, RETRY,
6576 NULL);
6577 } else {
6578 response = 0;
6579 }
6580
6581 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
6582 abort_job (common);
6583 } else if (response == 1) {
6584 goto retry;
6585 } else {
6586 g_assert_not_reached ();
6587 }
6588
6589 goto out;
6590 }
6591
6592
6593 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE)) {
6594 current_perms = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
6595 new_perms = current_perms | S_IXGRP | S_IXUSR | S_IXOTH;
6596
6597 if ((current_perms != new_perms) &&
6598 !g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_MODE,
6599 new_perms, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
6600 common->cancellable, &error))
6601 {
6602 g_object_unref (info);
6603
6604 if (interactive) {
6605 response = run_error (common,
6606 g_strdup (_("Unable to mark launcher trusted (executable)")),
6607 error->message,
6608 NULL,
6609 FALSE,
6610 GTK_STOCK_CANCEL, RETRY,
6611 NULL);
6612 } else {
6613 response = 0;
6614 }
6615
6616 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
6617 abort_job (common);
6618 } else if (response == 1) {
6619 goto retry;
6620 } else {
6621 g_assert_not_reached ();
6622 }
6623
6624 goto out;
6625 }
6626 }
6627 g_object_unref (info);
6628 out:
6629 ;
6630 }
6631
6632 static gboolean
6633 mark_trusted_job (GIOSchedulerJob *io_job,
6634 GCancellable *cancellable,
6635 gpointer user_data)
6636 {
6637 MarkTrustedJob *job = user_data;
6638 CommonJob *common;
6639
6640 common = (CommonJob *)job;
6641 common->io_job = io_job;
6642
6643 nautilus_progress_info_start (job->common.progress);
6644
6645 mark_desktop_file_trusted (common,
6646 cancellable,
6647 job->file,
6648 job->interactive);
6649
6650 g_io_scheduler_job_send_to_mainloop_async (io_job,
6651 mark_trusted_job_done,
6652 job,
6653 NULL);
6654
6655 return FALSE;
6656 }
6657
6658 void
6659 nautilus_file_mark_desktop_file_trusted (GFile *file,
6660 GtkWindow *parent_window,
6661 gboolean interactive,
6662 NautilusOpCallback done_callback,
6663 gpointer done_callback_data)
6664 {
6665 MarkTrustedJob *job;
6666
6667 job = op_job_new (MarkTrustedJob, parent_window);
6668 job->file = g_object_ref (file);
6669 job->interactive = interactive;
6670 job->done_callback = done_callback;
6671 job->done_callback_data = done_callback_data;
6672
6673 g_io_scheduler_push_job (mark_trusted_job,
6674 job,
6675 NULL,
6676 0,
6677 NULL);
6678 }
6679
6680 #if !defined (NAUTILUS_OMIT_SELF_CHECK)
6681
6682 void
6683 nautilus_self_check_file_operations (void)
6684 {
6685 setlocale (LC_MESSAGES, "C");
6686
6687
6688 /* test the next duplicate name generator */
6689 EEL_CHECK_STRING_RESULT (get_duplicate_name (" (copy)", 1, -1), " (another copy)");
6690 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo", 1, -1), "foo (copy)");
6691 EEL_CHECK_STRING_RESULT (get_duplicate_name (".bashrc", 1, -1), ".bashrc (copy)");
6692 EEL_CHECK_STRING_RESULT (get_duplicate_name (".foo.txt", 1, -1), ".foo (copy).txt");
6693 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo", 1, -1), "foo foo (copy)");
6694 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo.txt", 1, -1), "foo (copy).txt");
6695 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo.txt", 1, -1), "foo foo (copy).txt");
6696 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo.txt txt", 1, -1), "foo foo (copy).txt txt");
6697 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo...txt", 1, -1), "foo.. (copy).txt");
6698 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo...", 1, -1), "foo... (copy)");
6699 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo. (copy)", 1, -1), "foo. (another copy)");
6700 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (copy)", 1, -1), "foo (another copy)");
6701 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (copy).txt", 1, -1), "foo (another copy).txt");
6702 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (another copy)", 1, -1), "foo (3rd copy)");
6703 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (another copy).txt", 1, -1), "foo (3rd copy).txt");
6704 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (another copy).txt", 1, -1), "foo foo (3rd copy).txt");
6705 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (13th copy)", 1, -1), "foo (14th copy)");
6706 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (13th copy).txt", 1, -1), "foo (14th copy).txt");
6707 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (21st copy)", 1, -1), "foo (22nd copy)");
6708 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (21st copy).txt", 1, -1), "foo (22nd copy).txt");
6709 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (22nd copy)", 1, -1), "foo (23rd copy)");
6710 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (22nd copy).txt", 1, -1), "foo (23rd copy).txt");
6711 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (23rd copy)", 1, -1), "foo (24th copy)");
6712 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (23rd copy).txt", 1, -1), "foo (24th copy).txt");
6713 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (24th copy)", 1, -1), "foo (25th copy)");
6714 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (24th copy).txt", 1, -1), "foo (25th copy).txt");
6715 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (24th copy)", 1, -1), "foo foo (25th copy)");
6716 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (24th copy).txt", 1, -1), "foo foo (25th copy).txt");
6717 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (100000000000000th copy).txt", 1, -1), "foo foo (copy).txt");
6718 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (10th copy)", 1, -1), "foo (11th copy)");
6719 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (10th copy).txt", 1, -1), "foo (11th copy).txt");
6720 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (11th copy)", 1, -1), "foo (12th copy)");
6721 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (11th copy).txt", 1, -1), "foo (12th copy).txt");
6722 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (12th copy)", 1, -1), "foo (13th copy)");
6723 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (12th copy).txt", 1, -1), "foo (13th copy).txt");
6724 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (110th copy)", 1, -1), "foo (111th copy)");
6725 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (110th copy).txt", 1, -1), "foo (111th copy).txt");
6726 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (122nd copy)", 1, -1), "foo (123rd copy)");
6727 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (122nd copy).txt", 1, -1), "foo (123rd copy).txt");
6728 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (123rd copy)", 1, -1), "foo (124th copy)");
6729 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (123rd copy).txt", 1, -1), "foo (124th copy).txt");
6730
6731 setlocale (LC_MESSAGES, "");
6732 }
6733
6734 #endif