1 /* nautilus-file-operations.c - Nautilus file operations.
2 *
3 * Copyright (C) 1999, 2000 Free Software Foundation
4 * Copyright (C) 2000, 2001 Eazel, Inc.
5 * Copyright (C) 2007 Red Hat, Inc.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors: Alexander Larsson <alexl@redhat.com>
21 * Ettore Perazzoli <ettore@gnu.org>
22 * Pavel Cisler <pavel@eazel.com>
23 */
24
25 #include <config.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <locale.h>
30 #include <math.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <stdlib.h>
34
35 #include "nautilus-file-operations.h"
36
37 #include "nautilus-file-changes-queue.h"
38 #include "nautilus-lib-self-check-functions.h"
39
40 #include "nautilus-progress-info.h"
41
42 #include <eel/eel-glib-extensions.h>
43 #include <eel/eel-vfs-extensions.h>
44 #include <eel/eel-string.h>
45
46 #include <glib/gi18n.h>
47 #include <glib/gstdio.h>
48 #include <gdk/gdk.h>
49 #include <gtk/gtk.h>
50 #include <gio/gio.h>
51 #include <glib.h>
52
53 #include "nautilus-error-reporting.h"
54 #include "nautilus-operations-ui-manager.h"
55 #include "nautilus-file-changes-queue.h"
56 #include "nautilus-file-private.h"
57 #include "nautilus-trash-monitor.h"
58 #include "nautilus-file-utilities.h"
59 #include "nautilus-file-undo-operations.h"
60 #include "nautilus-file-undo-manager.h"
61 #include "nautilus-ui-utilities.h"
62
63 #ifdef GDK_WINDOWING_X11
64 #include <gdk/gdkx.h>
65 #endif
66
67 #ifdef GDK_WINDOWING_WAYLAND
68 #include <gdk/gdkwayland.h>
69 #endif
70
71 typedef struct
72 {
73 GTimer *time;
74 GtkWindow *parent_window;
75 NautilusFileOperationsDBusData *dbus_data;
76 guint 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 {
91 CommonJob common;
92 gboolean is_move;
93 GList *files;
94 GFile *destination;
95 GFile *fake_display_source;
96 GHashTable *debuting_files;
97 gchar *target_name;
98 NautilusCopyCallback done_callback;
99 gpointer done_callback_data;
100 } CopyMoveJob;
101
102 typedef struct
103 {
104 CommonJob common;
105 GList *files;
106 gboolean try_trash;
107 gboolean user_cancel;
108 NautilusDeleteCallback done_callback;
109 gpointer done_callback_data;
110 } DeleteJob;
111
112 typedef struct
113 {
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 GFile *created_file;
122 NautilusCreateCallback done_callback;
123 gpointer done_callback_data;
124 } CreateJob;
125
126
127 typedef struct
128 {
129 CommonJob common;
130 GList *trash_dirs;
131 gboolean should_confirm;
132 NautilusOpCallback done_callback;
133 gpointer done_callback_data;
134 } EmptyTrashJob;
135
136 typedef struct
137 {
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 {
147 CommonJob common;
148 GFile *file;
149 NautilusOpCallback done_callback;
150 gpointer done_callback_data;
151 guint32 file_permissions;
152 guint32 file_mask;
153 guint32 dir_permissions;
154 guint32 dir_mask;
155 } SetPermissionsJob;
156
157 typedef enum
158 {
159 OP_KIND_COPY,
160 OP_KIND_MOVE,
161 OP_KIND_DELETE,
162 OP_KIND_TRASH,
163 OP_KIND_COMPRESS
164 } OpKind;
165
166 typedef struct
167 {
168 int num_files_children;
169 goffset num_bytes_children;
170 } SourceDirInfo;
171
172 typedef struct
173 {
174 int num_files;
175 goffset num_bytes;
176 int num_files_since_progress;
177 OpKind op;
178 GHashTable *scanned_dirs_info;
179 } SourceInfo;
180
181 typedef struct
182 {
183 int num_files;
184 goffset num_bytes;
185 OpKind op;
186 guint64 last_report_time;
187 int last_reported_files_left;
188
189 /*
190 * This is used when reporting progress for copy/move operations to not show
191 * the remaining time. This is needed because some GVfs backends doesn't
192 * report progress from those operations. Consequently it looks like that it
193 * is hanged when the remaining time is not updated regularly. See:
194 * https://gitlab.gnome.org/GNOME/nautilus/-/merge_requests/605
195 */
196 gboolean partial_progress;
197 } TransferInfo;
198
199 typedef struct
200 {
201 CommonJob common;
202 GList *source_files;
203 GFile *destination_directory;
204 GList *output_files;
205 gboolean destination_decided;
206 gboolean extraction_failed;
207
208 gdouble base_progress;
209
210 guint64 archive_compressed_size;
211 guint64 total_compressed_size;
212 gint total_files;
213
214 NautilusExtractCallback done_callback;
215 gpointer done_callback_data;
216 } ExtractJob;
217
218 typedef struct
219 {
220 CommonJob common;
221 GList *source_files;
222 GFile *output_file;
223
224 AutoarFormat format;
225 AutoarFilter filter;
226 gchar *passphrase;
227
228 guint64 total_size;
229 guint total_files;
230
231 gboolean success;
232
233 NautilusCreateCallback done_callback;
234 gpointer done_callback_data;
235 } CompressJob;
236
237 static void
source_info_clear(SourceInfo * source_info)238 source_info_clear (SourceInfo *source_info)
239 {
240 if (source_info->scanned_dirs_info != NULL)
241 {
242 g_hash_table_unref (source_info->scanned_dirs_info);
243 }
244 }
245
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(SourceInfo,source_info_clear)246 G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (SourceInfo, source_info_clear)
247
248 #define SOURCE_INFO_INIT { 0 }
249 #define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 8
250 #define NSEC_PER_MICROSEC 1000
251 #define PROGRESS_NOTIFY_INTERVAL 100 * NSEC_PER_MICROSEC
252
253 #define MAXIMUM_DISPLAYED_FILE_NAME_LENGTH 50
254
255 #define IS_IO_ERROR(__error, KIND) (((__error)->domain == G_IO_ERROR && (__error)->code == G_IO_ERROR_ ## KIND))
256
257 #define CANCEL _("_Cancel")
258 #define SKIP _("_Skip")
259 #define SKIP_ALL _("S_kip All")
260 #define RETRY _("_Retry")
261 #define DELETE _("_Delete")
262 #define DELETE_ALL _("Delete _All")
263 #define REPLACE _("_Replace")
264 #define REPLACE_ALL _("Replace _All")
265 #define MERGE _("_Merge")
266 #define MERGE_ALL _("Merge _All")
267 #define COPY_FORCE _("Copy _Anyway")
268 #define EMPTY_TRASH _("Empty _Trash")
269
270 static gboolean
271 is_all_button_text (const char *button_text)
272 {
273 g_assert (button_text != NULL);
274
275 return !strcmp (button_text, SKIP_ALL) ||
276 !strcmp (button_text, REPLACE_ALL) ||
277 !strcmp (button_text, DELETE_ALL) ||
278 !strcmp (button_text, MERGE_ALL);
279 }
280
281 static void scan_sources (GList *files,
282 SourceInfo *source_info,
283 CommonJob *job,
284 OpKind kind);
285
286
287 static void empty_trash_thread_func (GTask *task,
288 gpointer source_object,
289 gpointer task_data,
290 GCancellable *cancellable);
291
292 static void empty_trash_task_done (GObject *source_object,
293 GAsyncResult *res,
294 gpointer user_data);
295
296 static char *query_fs_type (GFile *file,
297 GCancellable *cancellable);
298
299 static void nautilus_file_operations_copy (GTask *task,
300 gpointer source_object,
301 gpointer task_data,
302 GCancellable *cancellable);
303
304 static void nautilus_file_operations_move (GTask *task,
305 gpointer source_object,
306 gpointer task_data,
307 GCancellable *cancellable);
308
309 /* keep in time with get_formatted_time ()
310 *
311 * This counts and outputs the number of “time units”
312 * formatted and displayed by get_formatted_time ().
313 * For instance, if get_formatted_time outputs “3 hours, 4 minutes”
314 * it yields 7.
315 */
316 static int
seconds_count_format_time_units(int seconds)317 seconds_count_format_time_units (int seconds)
318 {
319 int minutes;
320 int hours;
321
322 if (seconds < 0)
323 {
324 /* Just to make sure... */
325 seconds = 0;
326 }
327
328 if (seconds < 60)
329 {
330 /* seconds */
331 return seconds;
332 }
333
334 if (seconds < 60 * 60)
335 {
336 /* minutes */
337 minutes = seconds / 60;
338 return minutes;
339 }
340
341 hours = seconds / (60 * 60);
342
343 if (seconds < 60 * 60 * 4)
344 {
345 /* minutes + hours */
346 minutes = (seconds - hours * 60 * 60) / 60;
347 return minutes + hours;
348 }
349
350 return hours;
351 }
352
353 static gchar *
get_formatted_time(int seconds)354 get_formatted_time (int seconds)
355 {
356 int minutes;
357 int hours;
358 gchar *res;
359
360 if (seconds < 0)
361 {
362 /* Just to make sure... */
363 seconds = 0;
364 }
365
366 if (seconds < 60)
367 {
368 return g_strdup_printf (ngettext ("%'d second", "%'d seconds", (int) seconds), (int) seconds);
369 }
370
371 if (seconds < 60 * 60)
372 {
373 minutes = seconds / 60;
374 return g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes), minutes);
375 }
376
377 hours = seconds / (60 * 60);
378
379 if (seconds < 60 * 60 * 4)
380 {
381 gchar *h, *m;
382
383 minutes = (seconds - hours * 60 * 60) / 60;
384
385 h = g_strdup_printf (ngettext ("%'d hour", "%'d hours", hours), hours);
386 m = g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes), minutes);
387 res = g_strconcat (h, ", ", m, NULL);
388 g_free (h);
389 g_free (m);
390 return res;
391 }
392
393 return g_strdup_printf (ngettext ("%'d hour",
394 "%'d hours",
395 hours), hours);
396 }
397
398 static char *
shorten_utf8_string(const char * base,int reduce_by_num_bytes)399 shorten_utf8_string (const char *base,
400 int reduce_by_num_bytes)
401 {
402 int len;
403 char *ret;
404 const char *p;
405
406 len = strlen (base);
407 len -= reduce_by_num_bytes;
408
409 if (len <= 0)
410 {
411 return NULL;
412 }
413
414 ret = g_new (char, len + 1);
415
416 p = base;
417 while (len)
418 {
419 char *next;
420 next = g_utf8_next_char (p);
421 if (next - p > len || *next == '\0')
422 {
423 break;
424 }
425
426 len -= next - p;
427 p = next;
428 }
429
430 if (p - base == 0)
431 {
432 g_free (ret);
433 return NULL;
434 }
435 else
436 {
437 memcpy (ret, base, p - base);
438 ret[p - base] = '\0';
439 return ret;
440 }
441 }
442
443 /* Note that we have these two separate functions with separate format
444 * strings for ease of localization.
445 */
446
447 static char *
get_link_name(const char * name,int count,int max_length)448 get_link_name (const char *name,
449 int count,
450 int max_length)
451 {
452 const char *format;
453 char *result;
454 int unshortened_length;
455 gboolean use_count;
456
457 g_assert (name != NULL);
458
459 if (count < 0)
460 {
461 g_warning ("bad count in get_link_name");
462 count = 0;
463 }
464
465 if (count <= 2)
466 {
467 /* Handle special cases for low numbers.
468 * Perhaps for some locales we will need to add more.
469 */
470 switch (count)
471 {
472 default:
473 {
474 g_assert_not_reached ();
475 /* fall through */
476 }
477
478 case 0:
479 {
480 /* duplicate original file name */
481 format = "%s";
482 }
483 break;
484
485 case 1:
486 {
487 /* appended to new link file */
488 format = _("Link to %s");
489 }
490 break;
491
492 case 2:
493 {
494 /* appended to new link file */
495 format = _("Another link to %s");
496 }
497 break;
498 }
499
500 use_count = FALSE;
501 }
502 else
503 {
504 /* Handle special cases for the first few numbers of each ten.
505 * For locales where getting this exactly right is difficult,
506 * these can just be made all the same as the general case below.
507 */
508 switch (count % 10)
509 {
510 case 1:
511 {
512 /* Localizers: Feel free to leave out the "st" suffix
513 * if there's no way to do that nicely for a
514 * particular language.
515 */
516 format = _("%'dst link to %s");
517 }
518 break;
519
520 case 2:
521 {
522 /* appended to new link file */
523 format = _("%'dnd link to %s");
524 }
525 break;
526
527 case 3:
528 {
529 /* appended to new link file */
530 format = _("%'drd link to %s");
531 }
532 break;
533
534 default:
535 {
536 /* appended to new link file */
537 format = _("%'dth link to %s");
538 }
539 break;
540 }
541
542 use_count = TRUE;
543 }
544
545 #pragma GCC diagnostic push
546 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
547 if (use_count)
548 {
549 result = g_strdup_printf (format, count, name);
550 }
551 else
552 {
553 result = g_strdup_printf (format, name);
554 }
555
556 if (max_length > 0 && (unshortened_length = strlen (result)) > max_length)
557 {
558 char *new_name;
559
560 new_name = shorten_utf8_string (name, unshortened_length - max_length);
561 if (new_name)
562 {
563 g_free (result);
564
565 if (use_count)
566 {
567 result = g_strdup_printf (format, count, new_name);
568 }
569 else
570 {
571 result = g_strdup_printf (format, new_name);
572 }
573
574 g_assert (strlen (result) <= max_length);
575 g_free (new_name);
576 }
577 }
578 #pragma GCC diagnostic pop
579 return result;
580 }
581
582
583 /* Localizers:
584 * Feel free to leave out the st, nd, rd and th suffix or
585 * make some or all of them match.
586 */
587
588 /* localizers: tag used to detect the first copy of a file */
589 static const char untranslated_copy_duplicate_tag[] = N_(" (copy)");
590 /* localizers: tag used to detect the second copy of a file */
591 static const char untranslated_another_copy_duplicate_tag[] = N_(" (another copy)");
592
593 /* localizers: tag used to detect the x11th copy of a file */
594 static const char untranslated_x11th_copy_duplicate_tag[] = N_("th copy)");
595 /* localizers: tag used to detect the x12th copy of a file */
596 static const char untranslated_x12th_copy_duplicate_tag[] = N_("th copy)");
597 /* localizers: tag used to detect the x13th copy of a file */
598 static const char untranslated_x13th_copy_duplicate_tag[] = N_("th copy)");
599
600 /* localizers: tag used to detect the x1st copy of a file */
601 static const char untranslated_st_copy_duplicate_tag[] = N_("st copy)");
602 /* localizers: tag used to detect the x2nd copy of a file */
603 static const char untranslated_nd_copy_duplicate_tag[] = N_("nd copy)");
604 /* localizers: tag used to detect the x3rd copy of a file */
605 static const char untranslated_rd_copy_duplicate_tag[] = N_("rd copy)");
606
607 /* localizers: tag used to detect the xxth copy of a file */
608 static const char untranslated_th_copy_duplicate_tag[] = N_("th copy)");
609
610 #define COPY_DUPLICATE_TAG _(untranslated_copy_duplicate_tag)
611 #define ANOTHER_COPY_DUPLICATE_TAG _(untranslated_another_copy_duplicate_tag)
612 #define X11TH_COPY_DUPLICATE_TAG _(untranslated_x11th_copy_duplicate_tag)
613 #define X12TH_COPY_DUPLICATE_TAG _(untranslated_x12th_copy_duplicate_tag)
614 #define X13TH_COPY_DUPLICATE_TAG _(untranslated_x13th_copy_duplicate_tag)
615
616 #define ST_COPY_DUPLICATE_TAG _(untranslated_st_copy_duplicate_tag)
617 #define ND_COPY_DUPLICATE_TAG _(untranslated_nd_copy_duplicate_tag)
618 #define RD_COPY_DUPLICATE_TAG _(untranslated_rd_copy_duplicate_tag)
619 #define TH_COPY_DUPLICATE_TAG _(untranslated_th_copy_duplicate_tag)
620
621 /* localizers: appended to first file copy */
622 static const char untranslated_first_copy_duplicate_format[] = N_("%s (copy)%s");
623 /* localizers: appended to second file copy */
624 static const char untranslated_second_copy_duplicate_format[] = N_("%s (another copy)%s");
625
626 /* localizers: appended to x11th file copy */
627 static const char untranslated_x11th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
628 /* localizers: appended to x12th file copy */
629 static const char untranslated_x12th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
630 /* localizers: appended to x13th file copy */
631 static const char untranslated_x13th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
632
633 /* localizers: if in your language there's no difference between 1st, 2nd, 3rd and nth
634 * plurals, you can leave the st, nd, rd suffixes out and just make all the translated
635 * strings look like "%s (copy %'d)%s".
636 */
637
638 /* localizers: appended to x1st file copy */
639 static const char untranslated_st_copy_duplicate_format[] = N_("%s (%'dst copy)%s");
640 /* localizers: appended to x2nd file copy */
641 static const char untranslated_nd_copy_duplicate_format[] = N_("%s (%'dnd copy)%s");
642 /* localizers: appended to x3rd file copy */
643 static const char untranslated_rd_copy_duplicate_format[] = N_("%s (%'drd copy)%s");
644 /* localizers: appended to xxth file copy */
645 static const char untranslated_th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
646
647 #define FIRST_COPY_DUPLICATE_FORMAT _(untranslated_first_copy_duplicate_format)
648 #define SECOND_COPY_DUPLICATE_FORMAT _(untranslated_second_copy_duplicate_format)
649 #define X11TH_COPY_DUPLICATE_FORMAT _(untranslated_x11th_copy_duplicate_format)
650 #define X12TH_COPY_DUPLICATE_FORMAT _(untranslated_x12th_copy_duplicate_format)
651 #define X13TH_COPY_DUPLICATE_FORMAT _(untranslated_x13th_copy_duplicate_format)
652
653 #define ST_COPY_DUPLICATE_FORMAT _(untranslated_st_copy_duplicate_format)
654 #define ND_COPY_DUPLICATE_FORMAT _(untranslated_nd_copy_duplicate_format)
655 #define RD_COPY_DUPLICATE_FORMAT _(untranslated_rd_copy_duplicate_format)
656 #define TH_COPY_DUPLICATE_FORMAT _(untranslated_th_copy_duplicate_format)
657
658 static char *
extract_string_until(const char * original,const char * until_substring)659 extract_string_until (const char *original,
660 const char *until_substring)
661 {
662 char *result;
663
664 g_assert ((int) strlen (original) >= until_substring - original);
665 g_assert (until_substring - original >= 0);
666
667 result = g_malloc (until_substring - original + 1);
668 strncpy (result, original, until_substring - original);
669 result[until_substring - original] = '\0';
670
671 return result;
672 }
673
674 /* Dismantle a file name, separating the base name, the file suffix and removing any
675 * (xxxcopy), etc. string. Figure out the count that corresponds to the given
676 * (xxxcopy) substring.
677 */
678 static void
parse_previous_duplicate_name(const char * name,char ** name_base,const char ** suffix,int * count,gboolean ignore_extension)679 parse_previous_duplicate_name (const char *name,
680 char **name_base,
681 const char **suffix,
682 int *count,
683 gboolean ignore_extension)
684 {
685 const char *tag;
686
687 g_assert (name[0] != '\0');
688
689 *suffix = eel_filename_get_extension_offset (name);
690
691 if (*suffix == NULL || (*suffix)[1] == '\0')
692 {
693 /* no suffix */
694 *suffix = "";
695 }
696
697 tag = strstr (name, COPY_DUPLICATE_TAG);
698 if (tag != NULL)
699 {
700 if (tag > *suffix)
701 {
702 /* handle case "foo. (copy)" */
703 *suffix = "";
704 }
705 *name_base = extract_string_until (name, tag);
706 *count = 1;
707 return;
708 }
709
710
711 tag = strstr (name, ANOTHER_COPY_DUPLICATE_TAG);
712 if (tag != NULL)
713 {
714 if (tag > *suffix)
715 {
716 /* handle case "foo. (another copy)" */
717 *suffix = "";
718 }
719 *name_base = extract_string_until (name, tag);
720 *count = 2;
721 return;
722 }
723
724
725 /* Check to see if we got one of st, nd, rd, th. */
726 tag = strstr (name, X11TH_COPY_DUPLICATE_TAG);
727
728 if (tag == NULL)
729 {
730 tag = strstr (name, X12TH_COPY_DUPLICATE_TAG);
731 }
732 if (tag == NULL)
733 {
734 tag = strstr (name, X13TH_COPY_DUPLICATE_TAG);
735 }
736
737 if (tag == NULL)
738 {
739 tag = strstr (name, ST_COPY_DUPLICATE_TAG);
740 }
741 if (tag == NULL)
742 {
743 tag = strstr (name, ND_COPY_DUPLICATE_TAG);
744 }
745 if (tag == NULL)
746 {
747 tag = strstr (name, RD_COPY_DUPLICATE_TAG);
748 }
749 if (tag == NULL)
750 {
751 tag = strstr (name, TH_COPY_DUPLICATE_TAG);
752 }
753
754 /* If we got one of st, nd, rd, th, fish out the duplicate number. */
755 if (tag != NULL)
756 {
757 /* localizers: opening parentheses to match the "th copy)" string */
758 tag = strstr (name, _(" ("));
759 if (tag != NULL)
760 {
761 if (tag > *suffix)
762 {
763 /* handle case "foo. (22nd copy)" */
764 *suffix = "";
765 }
766 *name_base = extract_string_until (name, tag);
767 /* localizers: opening parentheses of the "th copy)" string */
768 if (sscanf (tag, _(" (%'d"), count) == 1)
769 {
770 if (*count < 1 || *count > 1000000)
771 {
772 /* keep the count within a reasonable range */
773 *count = 0;
774 }
775 return;
776 }
777 *count = 0;
778 return;
779 }
780 }
781
782
783 *count = 0;
784 /* ignore_extension was not used before to let above code handle case "dir (copy).dir" for directories */
785 if (**suffix != '\0' && !ignore_extension)
786 {
787 *name_base = extract_string_until (name, *suffix);
788 }
789 else
790 {
791 /* making sure extension is ignored in directories */
792 *suffix = "";
793 *name_base = g_strdup (name);
794 }
795 }
796
797 static char *
make_next_duplicate_name(const char * base,const char * suffix,int count,int max_length)798 make_next_duplicate_name (const char *base,
799 const char *suffix,
800 int count,
801 int max_length)
802 {
803 const char *format;
804 char *result;
805 int unshortened_length;
806 gboolean use_count;
807
808 if (count < 1)
809 {
810 g_warning ("bad count %d in get_duplicate_name", count);
811 count = 1;
812 }
813
814 if (count <= 2)
815 {
816 /* Handle special cases for low numbers.
817 * Perhaps for some locales we will need to add more.
818 */
819 switch (count)
820 {
821 default:
822 {
823 g_assert_not_reached ();
824 /* fall through */
825 }
826
827 case 1:
828 {
829 format = FIRST_COPY_DUPLICATE_FORMAT;
830 }
831 break;
832
833 case 2:
834 {
835 format = SECOND_COPY_DUPLICATE_FORMAT;
836 }
837 break;
838 }
839
840 use_count = FALSE;
841 }
842 else
843 {
844 /* Handle special cases for the first few numbers of each ten.
845 * For locales where getting this exactly right is difficult,
846 * these can just be made all the same as the general case below.
847 */
848
849 /* Handle special cases for x11th - x20th.
850 */
851 switch (count % 100)
852 {
853 case 11:
854 {
855 format = X11TH_COPY_DUPLICATE_FORMAT;
856 }
857 break;
858
859 case 12:
860 {
861 format = X12TH_COPY_DUPLICATE_FORMAT;
862 }
863 break;
864
865 case 13:
866 {
867 format = X13TH_COPY_DUPLICATE_FORMAT;
868 }
869 break;
870
871 default:
872 {
873 format = NULL;
874 }
875 break;
876 }
877
878 if (format == NULL)
879 {
880 switch (count % 10)
881 {
882 case 1:
883 {
884 format = ST_COPY_DUPLICATE_FORMAT;
885 }
886 break;
887
888 case 2:
889 {
890 format = ND_COPY_DUPLICATE_FORMAT;
891 }
892 break;
893
894 case 3:
895 {
896 format = RD_COPY_DUPLICATE_FORMAT;
897 }
898 break;
899
900 default:
901 {
902 /* The general case. */
903 format = TH_COPY_DUPLICATE_FORMAT;
904 }
905 break;
906 }
907 }
908
909 use_count = TRUE;
910 }
911
912 #pragma GCC diagnostic push
913 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
914 if (use_count)
915 {
916 result = g_strdup_printf (format, base, count, suffix);
917 }
918 else
919 {
920 result = g_strdup_printf (format, base, suffix);
921 }
922
923 if (max_length > 0 && (unshortened_length = strlen (result)) > max_length)
924 {
925 char *new_base;
926
927 new_base = shorten_utf8_string (base, unshortened_length - max_length);
928 if (new_base)
929 {
930 g_free (result);
931
932 if (use_count)
933 {
934 result = g_strdup_printf (format, new_base, count, suffix);
935 }
936 else
937 {
938 result = g_strdup_printf (format, new_base, suffix);
939 }
940
941 g_assert (strlen (result) <= max_length);
942 g_free (new_base);
943 }
944 }
945 #pragma GCC diagnostic pop
946
947 return result;
948 }
949
950 static char *
get_duplicate_name(const char * name,int count_increment,int max_length,gboolean ignore_extension)951 get_duplicate_name (const char *name,
952 int count_increment,
953 int max_length,
954 gboolean ignore_extension)
955 {
956 char *result;
957 char *name_base;
958 const char *suffix;
959 int count;
960
961 parse_previous_duplicate_name (name, &name_base, &suffix, &count, ignore_extension);
962 result = make_next_duplicate_name (name_base, suffix, count + count_increment, max_length);
963
964 g_free (name_base);
965
966 return result;
967 }
968
969 static gboolean
has_invalid_xml_char(char * str)970 has_invalid_xml_char (char *str)
971 {
972 gunichar c;
973
974 while (*str != 0)
975 {
976 c = g_utf8_get_char (str);
977 /* characters XML permits */
978 if (!(c == 0x9 ||
979 c == 0xA ||
980 c == 0xD ||
981 (c >= 0x20 && c <= 0xD7FF) ||
982 (c >= 0xE000 && c <= 0xFFFD) ||
983 (c >= 0x10000 && c <= 0x10FFFF)))
984 {
985 return TRUE;
986 }
987 str = g_utf8_next_char (str);
988 }
989 return FALSE;
990 }
991
992 static gchar *
get_basename(GFile * file)993 get_basename (GFile *file)
994 {
995 GFileInfo *info;
996 gchar *name, *basename, *tmp;
997 GMount *mount;
998
999 if ((mount = nautilus_get_mounted_mount_for_root (file)) != NULL)
1000 {
1001 name = g_mount_get_name (mount);
1002 g_object_unref (mount);
1003 }
1004 else
1005 {
1006 info = g_file_query_info (file,
1007 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
1008 0,
1009 g_cancellable_get_current (),
1010 NULL);
1011 name = NULL;
1012 if (info)
1013 {
1014 name = g_strdup (g_file_info_get_display_name (info));
1015 g_object_unref (info);
1016 }
1017 }
1018
1019 if (name == NULL)
1020 {
1021 basename = g_file_get_basename (file);
1022 if (g_utf8_validate (basename, -1, NULL))
1023 {
1024 name = basename;
1025 }
1026 else
1027 {
1028 name = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
1029 g_free (basename);
1030 }
1031 }
1032
1033 /* Some chars can't be put in the markup we use for the dialogs... */
1034 if (has_invalid_xml_char (name))
1035 {
1036 tmp = name;
1037 name = g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
1038 g_free (tmp);
1039 }
1040
1041 /* Finally, if the string is too long, truncate it. */
1042 if (name != NULL)
1043 {
1044 tmp = name;
1045 name = eel_str_middle_truncate (tmp, MAXIMUM_DISPLAYED_FILE_NAME_LENGTH);
1046 g_free (tmp);
1047 }
1048
1049 return name;
1050 }
1051
1052 static gchar *
get_truncated_parse_name(GFile * file)1053 get_truncated_parse_name (GFile *file)
1054 {
1055 g_autofree gchar *parse_name = NULL;
1056
1057 g_assert (G_IS_FILE (file));
1058
1059 parse_name = g_file_get_parse_name (file);
1060
1061 return eel_str_middle_truncate (parse_name, MAXIMUM_DISPLAYED_FILE_NAME_LENGTH);
1062 }
1063
1064 #define op_job_new(__type, parent_window, dbus_data) ((__type *) (init_common (sizeof (__type), parent_window, dbus_data)))
1065
1066 static gpointer
init_common(gsize job_size,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data)1067 init_common (gsize job_size,
1068 GtkWindow *parent_window,
1069 NautilusFileOperationsDBusData *dbus_data)
1070 {
1071 CommonJob *common;
1072
1073 common = g_malloc0 (job_size);
1074
1075 if (parent_window)
1076 {
1077 common->parent_window = parent_window;
1078 g_object_add_weak_pointer (G_OBJECT (common->parent_window),
1079 (gpointer *) &common->parent_window);
1080 }
1081
1082 if (dbus_data)
1083 {
1084 common->dbus_data = nautilus_file_operations_dbus_data_ref (dbus_data);
1085 }
1086
1087 common->progress = nautilus_progress_info_new ();
1088 common->cancellable = nautilus_progress_info_get_cancellable (common->progress);
1089 common->time = g_timer_new ();
1090 common->inhibit_cookie = 0;
1091
1092 return common;
1093 }
1094
1095 static void
finalize_common(CommonJob * common)1096 finalize_common (CommonJob *common)
1097 {
1098 nautilus_progress_info_finish (common->progress);
1099
1100 if (common->inhibit_cookie != 0)
1101 {
1102 gtk_application_uninhibit (GTK_APPLICATION (g_application_get_default ()),
1103 common->inhibit_cookie);
1104 }
1105
1106 common->inhibit_cookie = 0;
1107 g_timer_destroy (common->time);
1108
1109 if (common->parent_window)
1110 {
1111 g_object_remove_weak_pointer (G_OBJECT (common->parent_window),
1112 (gpointer *) &common->parent_window);
1113 }
1114
1115 if (common->dbus_data)
1116 {
1117 nautilus_file_operations_dbus_data_unref (common->dbus_data);
1118 }
1119
1120 if (common->skip_files)
1121 {
1122 g_hash_table_destroy (common->skip_files);
1123 }
1124 if (common->skip_readdir_error)
1125 {
1126 g_hash_table_destroy (common->skip_readdir_error);
1127 }
1128
1129 if (common->undo_info != NULL)
1130 {
1131 nautilus_file_undo_manager_set_action (common->undo_info);
1132 g_object_unref (common->undo_info);
1133 }
1134
1135 g_object_unref (common->progress);
1136 g_object_unref (common->cancellable);
1137 g_free (common);
1138 }
1139
1140 static void
skip_file(CommonJob * common,GFile * file)1141 skip_file (CommonJob *common,
1142 GFile *file)
1143 {
1144 if (common->skip_files == NULL)
1145 {
1146 common->skip_files =
1147 g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
1148 }
1149
1150 g_hash_table_insert (common->skip_files, g_object_ref (file), file);
1151 }
1152
1153 static void
skip_readdir_error(CommonJob * common,GFile * dir)1154 skip_readdir_error (CommonJob *common,
1155 GFile *dir)
1156 {
1157 if (common->skip_readdir_error == NULL)
1158 {
1159 common->skip_readdir_error =
1160 g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
1161 }
1162
1163 g_hash_table_insert (common->skip_readdir_error, g_object_ref (dir), dir);
1164 }
1165
1166 static gboolean
should_skip_file(CommonJob * common,GFile * file)1167 should_skip_file (CommonJob *common,
1168 GFile *file)
1169 {
1170 if (common->skip_files != NULL)
1171 {
1172 return g_hash_table_lookup (common->skip_files, file) != NULL;
1173 }
1174 return FALSE;
1175 }
1176
1177 static gboolean
should_skip_readdir_error(CommonJob * common,GFile * dir)1178 should_skip_readdir_error (CommonJob *common,
1179 GFile *dir)
1180 {
1181 if (common->skip_readdir_error != NULL)
1182 {
1183 return g_hash_table_lookup (common->skip_readdir_error, dir) != NULL;
1184 }
1185 return FALSE;
1186 }
1187
1188 static gboolean
can_delete_without_confirm(GFile * file)1189 can_delete_without_confirm (GFile *file)
1190 {
1191 /* In the case of testing, we want to be able to delete
1192 * without asking for confirmation from the user.
1193 */
1194 if (g_file_has_uri_scheme (file, "burn") ||
1195 g_file_has_uri_scheme (file, "recent") ||
1196 !g_strcmp0 (g_getenv ("RUNNING_TESTS"), "TRUE"))
1197 {
1198 return TRUE;
1199 }
1200
1201 return FALSE;
1202 }
1203
1204 static gboolean
can_delete_files_without_confirm(GList * files)1205 can_delete_files_without_confirm (GList *files)
1206 {
1207 g_assert (files != NULL);
1208
1209 while (files != NULL)
1210 {
1211 if (!can_delete_without_confirm (files->data))
1212 {
1213 return FALSE;
1214 }
1215
1216 files = files->next;
1217 }
1218
1219 return TRUE;
1220 }
1221
1222 typedef struct
1223 {
1224 GtkWindow **parent_window;
1225 NautilusFileOperationsDBusData *dbus_data;
1226 gboolean ignore_close_box;
1227 GtkMessageType message_type;
1228 const char *primary_text;
1229 const char *secondary_text;
1230 const char *details_text;
1231 const char **button_titles;
1232 gboolean show_all;
1233 int result;
1234 /* Dialogs are ran from operation threads, which need to be blocked until
1235 * the user gives a valid response
1236 */
1237 gboolean completed;
1238 GMutex mutex;
1239 GCond cond;
1240 } RunSimpleDialogData;
1241
1242 static void
set_transient_for(GdkWindow * child_window,const char * parent_handle)1243 set_transient_for (GdkWindow *child_window,
1244 const char *parent_handle)
1245 {
1246 GdkDisplay *display;
1247 const char *prefix;
1248
1249 display = gdk_window_get_display (child_window);
1250
1251 #ifdef GDK_WINDOWING_X11
1252 if (GDK_IS_X11_DISPLAY (display))
1253 {
1254 prefix = "x11:";
1255
1256 if (g_str_has_prefix (parent_handle, prefix))
1257 {
1258 const char *handle;
1259 GdkWindow *window;
1260
1261 handle = parent_handle + strlen (prefix);
1262 window = gdk_x11_window_foreign_new_for_display (display, strtol (handle, NULL, 16));
1263
1264 if (window != NULL)
1265 {
1266 gdk_window_set_transient_for (child_window, window);
1267 g_object_unref (window);
1268 }
1269 }
1270 }
1271 #endif
1272
1273 #ifdef GDK_WINDOWING_WAYLAND
1274 if (GDK_IS_WAYLAND_DISPLAY (display))
1275 {
1276 prefix = "wayland:";
1277
1278 if (g_str_has_prefix (parent_handle, prefix))
1279 {
1280 const char *handle;
1281
1282 handle = parent_handle + strlen (prefix);
1283
1284 gdk_wayland_window_set_transient_for_exported (child_window, (char *) handle);
1285 }
1286 }
1287 #endif
1288 }
1289
1290 static void
dialog_realize_cb(GtkWidget * widget,gpointer user_data)1291 dialog_realize_cb (GtkWidget *widget,
1292 gpointer user_data)
1293 {
1294 NautilusFileOperationsDBusData *dbus_data = user_data;
1295 const char *parent_handle;
1296
1297 parent_handle = nautilus_file_operations_dbus_data_get_parent_handle (dbus_data);
1298 set_transient_for (gtk_widget_get_window (widget), parent_handle);
1299 }
1300
1301 static gboolean
do_run_simple_dialog(gpointer _data)1302 do_run_simple_dialog (gpointer _data)
1303 {
1304 RunSimpleDialogData *data = _data;
1305 const char *button_title;
1306 GtkWidget *dialog;
1307 GtkWidget *button;
1308 int result;
1309 int response_id;
1310
1311 g_mutex_lock (&data->mutex);
1312
1313 /* Create the dialog. */
1314 dialog = gtk_message_dialog_new (*data->parent_window,
1315 0,
1316 data->message_type,
1317 GTK_BUTTONS_NONE,
1318 NULL);
1319
1320 g_object_set (dialog,
1321 "text", data->primary_text,
1322 "secondary-text", data->secondary_text,
1323 NULL);
1324
1325 for (response_id = 0;
1326 data->button_titles[response_id] != NULL;
1327 response_id++)
1328 {
1329 button_title = data->button_titles[response_id];
1330 if (!data->show_all && is_all_button_text (button_title))
1331 {
1332 continue;
1333 }
1334
1335 button = gtk_dialog_add_button (GTK_DIALOG (dialog), button_title, response_id);
1336 gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id);
1337
1338 if (g_strcmp0 (button_title, DELETE) == 0 ||
1339 g_strcmp0 (button_title, EMPTY_TRASH) == 0 ||
1340 g_strcmp0 (button_title, DELETE_ALL) == 0)
1341 {
1342 gtk_style_context_add_class (gtk_widget_get_style_context (button),
1343 "destructive-action");
1344 }
1345 }
1346
1347 if (data->details_text)
1348 {
1349 GtkWidget *content_area, *label;
1350 content_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG (dialog));
1351
1352 label = gtk_label_new (data->details_text);
1353 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1354 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
1355 gtk_label_set_xalign (GTK_LABEL (label), 0);
1356 /* Ideally, we shouldn’t do this.
1357 *
1358 * Refer to https://gitlab.gnome.org/GNOME/nautilus/merge_requests/94
1359 * and https://gitlab.gnome.org/GNOME/nautilus/issues/270.
1360 */
1361 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
1362 gtk_label_set_max_width_chars (GTK_LABEL (label),
1363 MAXIMUM_DISPLAYED_ERROR_MESSAGE_LENGTH);
1364
1365 gtk_container_add (GTK_CONTAINER (content_area), label);
1366
1367 gtk_widget_show (label);
1368 }
1369
1370 if (data->dbus_data != NULL)
1371 {
1372 guint32 timestamp;
1373
1374 timestamp = nautilus_file_operations_dbus_data_get_timestamp (data->dbus_data);
1375
1376 /* Assuming this is used for desktop implementations, we want the
1377 * dialog to be centered on the screen rather than the parent window,
1378 * which could extend to all monitors. This is the case for
1379 * gnome-flashback.
1380 */
1381 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
1382
1383 if (nautilus_file_operations_dbus_data_get_parent_handle (data->dbus_data) != NULL)
1384 {
1385 g_signal_connect (dialog, "realize", G_CALLBACK (dialog_realize_cb), data->dbus_data);
1386 }
1387
1388 if (timestamp != 0)
1389 {
1390 gtk_window_present_with_time (GTK_WINDOW (dialog), timestamp);
1391 }
1392 }
1393
1394 /* Run it. */
1395 result = gtk_dialog_run (GTK_DIALOG (dialog));
1396
1397 while ((result == GTK_RESPONSE_NONE || result == GTK_RESPONSE_DELETE_EVENT) && data->ignore_close_box)
1398 {
1399 result = gtk_dialog_run (GTK_DIALOG (dialog));
1400 }
1401
1402 gtk_widget_destroy (dialog);
1403
1404 data->result = result;
1405 data->completed = TRUE;
1406
1407 g_cond_signal (&data->cond);
1408 g_mutex_unlock (&data->mutex);
1409
1410 return FALSE;
1411 }
1412
1413 /* NOTE: This frees the primary / secondary strings, in order to
1414 * avoid doing that everywhere. So, make sure they are strduped */
1415
1416 static int
run_simple_dialog_va(CommonJob * job,gboolean ignore_close_box,GtkMessageType message_type,char * primary_text,char * secondary_text,const char * details_text,gboolean show_all,va_list varargs)1417 run_simple_dialog_va (CommonJob *job,
1418 gboolean ignore_close_box,
1419 GtkMessageType message_type,
1420 char *primary_text,
1421 char *secondary_text,
1422 const char *details_text,
1423 gboolean show_all,
1424 va_list varargs)
1425 {
1426 RunSimpleDialogData *data;
1427 int res;
1428 const char *button_title;
1429 GPtrArray *ptr_array;
1430
1431 g_timer_stop (job->time);
1432
1433 data = g_new0 (RunSimpleDialogData, 1);
1434 data->parent_window = &job->parent_window;
1435 data->dbus_data = job->dbus_data;
1436 data->ignore_close_box = ignore_close_box;
1437 data->message_type = message_type;
1438 data->primary_text = primary_text;
1439 data->secondary_text = secondary_text;
1440 data->details_text = details_text;
1441 data->show_all = show_all;
1442 data->completed = FALSE;
1443 g_mutex_init (&data->mutex);
1444 g_cond_init (&data->cond);
1445
1446 ptr_array = g_ptr_array_new ();
1447 while ((button_title = va_arg (varargs, const char *)) != NULL)
1448 {
1449 g_ptr_array_add (ptr_array, (char *) button_title);
1450 }
1451 g_ptr_array_add (ptr_array, NULL);
1452 data->button_titles = (const char **) g_ptr_array_free (ptr_array, FALSE);
1453
1454 nautilus_progress_info_pause (job->progress);
1455
1456 g_mutex_lock (&data->mutex);
1457
1458 g_main_context_invoke (NULL,
1459 do_run_simple_dialog,
1460 data);
1461
1462 while (!data->completed)
1463 {
1464 g_cond_wait (&data->cond, &data->mutex);
1465 }
1466
1467 nautilus_progress_info_resume (job->progress);
1468 res = data->result;
1469
1470 g_mutex_unlock (&data->mutex);
1471 g_mutex_clear (&data->mutex);
1472 g_cond_clear (&data->cond);
1473
1474 g_free (data->button_titles);
1475 g_free (data);
1476
1477 g_timer_continue (job->time);
1478
1479 g_free (primary_text);
1480 g_free (secondary_text);
1481
1482 return res;
1483 }
1484
1485 #if 0 /* Not used at the moment */
1486 static int
1487 run_simple_dialog (CommonJob *job,
1488 gboolean ignore_close_box,
1489 GtkMessageType message_type,
1490 char *primary_text,
1491 char *secondary_text,
1492 const char *details_text,
1493 ...)
1494 {
1495 va_list varargs;
1496 int res;
1497
1498 va_start (varargs, details_text);
1499 res = run_simple_dialog_va (job,
1500 ignore_close_box,
1501 message_type,
1502 primary_text,
1503 secondary_text,
1504 details_text,
1505 varargs);
1506 va_end (varargs);
1507 return res;
1508 }
1509 #endif
1510
1511 static int
run_error(CommonJob * job,char * primary_text,char * secondary_text,const char * details_text,gboolean show_all,...)1512 run_error (CommonJob *job,
1513 char *primary_text,
1514 char *secondary_text,
1515 const char *details_text,
1516 gboolean show_all,
1517 ...)
1518 {
1519 va_list varargs;
1520 int res;
1521
1522 va_start (varargs, show_all);
1523 res = run_simple_dialog_va (job,
1524 FALSE,
1525 GTK_MESSAGE_ERROR,
1526 primary_text,
1527 secondary_text,
1528 details_text,
1529 show_all,
1530 varargs);
1531 va_end (varargs);
1532 return res;
1533 }
1534
1535 static int
run_warning(CommonJob * job,char * primary_text,char * secondary_text,const char * details_text,gboolean show_all,...)1536 run_warning (CommonJob *job,
1537 char *primary_text,
1538 char *secondary_text,
1539 const char *details_text,
1540 gboolean show_all,
1541 ...)
1542 {
1543 va_list varargs;
1544 int res;
1545
1546 va_start (varargs, show_all);
1547 res = run_simple_dialog_va (job,
1548 FALSE,
1549 GTK_MESSAGE_WARNING,
1550 primary_text,
1551 secondary_text,
1552 details_text,
1553 show_all,
1554 varargs);
1555 va_end (varargs);
1556 return res;
1557 }
1558
1559 static int
run_question(CommonJob * job,char * primary_text,char * secondary_text,const char * details_text,gboolean show_all,...)1560 run_question (CommonJob *job,
1561 char *primary_text,
1562 char *secondary_text,
1563 const char *details_text,
1564 gboolean show_all,
1565 ...)
1566 {
1567 va_list varargs;
1568 int res;
1569
1570 va_start (varargs, show_all);
1571 res = run_simple_dialog_va (job,
1572 FALSE,
1573 GTK_MESSAGE_QUESTION,
1574 primary_text,
1575 secondary_text,
1576 details_text,
1577 show_all,
1578 varargs);
1579 va_end (varargs);
1580 return res;
1581 }
1582
1583 static int
run_cancel_or_skip_warning(CommonJob * job,char * primary_text,char * secondary_text,const char * details_text,int total_operations,int operations_remaining)1584 run_cancel_or_skip_warning (CommonJob *job,
1585 char *primary_text,
1586 char *secondary_text,
1587 const char *details_text,
1588 int total_operations,
1589 int operations_remaining)
1590 {
1591 int response;
1592
1593 if (total_operations == 1)
1594 {
1595 response = run_warning (job,
1596 primary_text,
1597 secondary_text,
1598 details_text,
1599 FALSE,
1600 CANCEL,
1601 NULL);
1602 }
1603 else
1604 {
1605 response = run_warning (job,
1606 primary_text,
1607 secondary_text,
1608 details_text,
1609 operations_remaining > 1,
1610 CANCEL, SKIP_ALL, SKIP,
1611 NULL);
1612 }
1613
1614 return response;
1615 }
1616
1617 static void
inhibit_power_manager(CommonJob * job,const char * message)1618 inhibit_power_manager (CommonJob *job,
1619 const char *message)
1620 {
1621 job->inhibit_cookie = gtk_application_inhibit (GTK_APPLICATION (g_application_get_default ()),
1622 GTK_WINDOW (job->parent_window),
1623 GTK_APPLICATION_INHIBIT_LOGOUT |
1624 GTK_APPLICATION_INHIBIT_SUSPEND,
1625 message);
1626 }
1627
1628 static void
abort_job(CommonJob * job)1629 abort_job (CommonJob *job)
1630 {
1631 /* destroy the undo action data too */
1632 g_clear_object (&job->undo_info);
1633
1634 g_cancellable_cancel (job->cancellable);
1635 }
1636
1637 static gboolean
job_aborted(CommonJob * job)1638 job_aborted (CommonJob *job)
1639 {
1640 return g_cancellable_is_cancelled (job->cancellable);
1641 }
1642
1643 static gboolean
confirm_delete_from_trash(CommonJob * job,GList * files)1644 confirm_delete_from_trash (CommonJob *job,
1645 GList *files)
1646 {
1647 char *prompt;
1648 int file_count;
1649 int response;
1650
1651 file_count = g_list_length (files);
1652 g_assert (file_count > 0);
1653
1654 if (file_count == 1)
1655 {
1656 g_autofree gchar *basename = NULL;
1657
1658 basename = get_basename (files->data);
1659 prompt = g_strdup_printf (_("Are you sure you want to permanently delete “%s” "
1660 "from the trash?"), basename);
1661 }
1662 else
1663 {
1664 prompt = g_strdup_printf (ngettext ("Are you sure you want to permanently delete "
1665 "the %'d selected item from the trash?",
1666 "Are you sure you want to permanently delete "
1667 "the %'d selected items from the trash?",
1668 file_count),
1669 file_count);
1670 }
1671
1672 response = run_warning (job,
1673 prompt,
1674 g_strdup (_("If you delete an item, it will be permanently lost.")),
1675 NULL,
1676 FALSE,
1677 CANCEL, DELETE,
1678 NULL);
1679
1680 return (response == 1);
1681 }
1682
1683 static gboolean
confirm_empty_trash(CommonJob * job)1684 confirm_empty_trash (CommonJob *job)
1685 {
1686 char *prompt;
1687 int response;
1688
1689 prompt = g_strdup (_("Empty all items from Trash?"));
1690
1691 response = run_warning (job,
1692 prompt,
1693 g_strdup (_("All items in the Trash will be permanently deleted.")),
1694 NULL,
1695 FALSE,
1696 CANCEL, EMPTY_TRASH,
1697 NULL);
1698
1699 return (response == 1);
1700 }
1701
1702 static gboolean
confirm_delete_directly(CommonJob * job,GList * files)1703 confirm_delete_directly (CommonJob *job,
1704 GList *files)
1705 {
1706 char *prompt;
1707 int file_count;
1708 int response;
1709
1710 file_count = g_list_length (files);
1711 g_assert (file_count > 0);
1712
1713 if (can_delete_files_without_confirm (files))
1714 {
1715 return TRUE;
1716 }
1717
1718 if (file_count == 1)
1719 {
1720 g_autofree gchar *basename = NULL;
1721
1722 basename = get_basename (files->data);
1723 prompt = g_strdup_printf (_("Are you sure you want to permanently delete “%s”?"),
1724 basename);
1725 }
1726 else
1727 {
1728 prompt = g_strdup_printf (ngettext ("Are you sure you want to permanently delete "
1729 "the %'d selected item?",
1730 "Are you sure you want to permanently delete "
1731 "the %'d selected items?", file_count),
1732 file_count);
1733 }
1734
1735 response = run_warning (job,
1736 prompt,
1737 g_strdup (_("If you delete an item, it will be permanently lost.")),
1738 NULL,
1739 FALSE,
1740 CANCEL, DELETE,
1741 NULL);
1742
1743 return response == 1;
1744 }
1745
1746 #pragma GCC diagnostic push
1747 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
1748 static void
report_delete_progress(CommonJob * job,SourceInfo * source_info,TransferInfo * transfer_info)1749 report_delete_progress (CommonJob *job,
1750 SourceInfo *source_info,
1751 TransferInfo *transfer_info)
1752 {
1753 int files_left;
1754 double elapsed, transfer_rate;
1755 int remaining_time;
1756 gint64 now;
1757 char *details;
1758 char *status;
1759 DeleteJob *delete_job;
1760
1761 delete_job = (DeleteJob *) job;
1762 now = g_get_monotonic_time ();
1763 files_left = source_info->num_files - transfer_info->num_files;
1764
1765 /* Races and whatnot could cause this to be negative... */
1766 if (files_left < 0)
1767 {
1768 files_left = 0;
1769 }
1770
1771 /* If the number of files left is 0, we want to update the status without
1772 * considering this time, since we want to change the status to completed
1773 * and probably we won't get more calls to this function */
1774 if (transfer_info->last_report_time != 0 &&
1775 ABS ((gint64) (transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC &&
1776 files_left > 0)
1777 {
1778 return;
1779 }
1780
1781 transfer_info->last_report_time = now;
1782
1783 if (source_info->num_files == 1)
1784 {
1785 g_autofree gchar *basename = NULL;
1786
1787 if (files_left == 0)
1788 {
1789 status = _("Deleted “%s”");
1790 }
1791 else
1792 {
1793 status = _("Deleting “%s”");
1794 }
1795
1796 basename = get_basename (G_FILE (delete_job->files->data));
1797 nautilus_progress_info_take_status (job->progress,
1798 g_strdup_printf (status, basename));
1799 }
1800 else
1801 {
1802 if (files_left == 0)
1803 {
1804 status = ngettext ("Deleted %'d file",
1805 "Deleted %'d files",
1806 source_info->num_files);
1807 }
1808 else
1809 {
1810 status = ngettext ("Deleting %'d file",
1811 "Deleting %'d files",
1812 source_info->num_files);
1813 }
1814 nautilus_progress_info_take_status (job->progress,
1815 g_strdup_printf (status,
1816 source_info->num_files));
1817 }
1818
1819 elapsed = g_timer_elapsed (job->time, NULL);
1820 transfer_rate = 0;
1821 remaining_time = INT_MAX;
1822 if (elapsed > 0)
1823 {
1824 transfer_rate = transfer_info->num_files / elapsed;
1825 if (transfer_rate > 0)
1826 {
1827 remaining_time = (source_info->num_files - transfer_info->num_files) / transfer_rate;
1828 }
1829 }
1830
1831 if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE ||
1832 transfer_rate == 0)
1833 {
1834 if (files_left > 0)
1835 {
1836 /* To translators: %'d is the number of files completed for the operation,
1837 * so it will be something like 2/14. */
1838 details = g_strdup_printf (_("%'d / %'d"),
1839 transfer_info->num_files + 1,
1840 source_info->num_files);
1841 }
1842 else
1843 {
1844 /* To translators: %'d is the number of files completed for the operation,
1845 * so it will be something like 2/14. */
1846 details = g_strdup_printf (_("%'d / %'d"),
1847 transfer_info->num_files,
1848 source_info->num_files);
1849 }
1850 }
1851 else
1852 {
1853 if (files_left > 0)
1854 {
1855 gchar *time_left_message;
1856 gchar *files_per_second_message;
1857 gchar *concat_detail;
1858 g_autofree gchar *formatted_time = NULL;
1859
1860 /* To translators: %s will expand to a time duration like "2 minutes".
1861 * So the whole thing will be something like "1 / 5 -- 2 hours left (4 files/sec)"
1862 *
1863 * The singular/plural form will be used depending on the remaining time (i.e. the %s argument).
1864 */
1865 time_left_message = ngettext ("%'d / %'d \xE2\x80\x94 %s left",
1866 "%'d / %'d \xE2\x80\x94 %s left",
1867 seconds_count_format_time_units (remaining_time));
1868 transfer_rate += 0.5;
1869 files_per_second_message = ngettext ("(%d file/sec)",
1870 "(%d files/sec)",
1871 (int) transfer_rate);
1872 concat_detail = g_strconcat (time_left_message, " ", files_per_second_message, NULL);
1873
1874 formatted_time = get_formatted_time (remaining_time);
1875 details = g_strdup_printf (concat_detail,
1876 transfer_info->num_files + 1, source_info->num_files,
1877 formatted_time,
1878 (int) transfer_rate);
1879
1880 g_free (concat_detail);
1881 }
1882 else
1883 {
1884 /* To translators: %'d is the number of files completed for the operation,
1885 * so it will be something like 2/14. */
1886 details = g_strdup_printf (_("%'d / %'d"),
1887 transfer_info->num_files,
1888 source_info->num_files);
1889 }
1890 }
1891 nautilus_progress_info_take_details (job->progress, details);
1892
1893 if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE)
1894 {
1895 nautilus_progress_info_set_remaining_time (job->progress,
1896 remaining_time);
1897 nautilus_progress_info_set_elapsed_time (job->progress,
1898 elapsed);
1899 }
1900
1901 if (source_info->num_files != 0)
1902 {
1903 nautilus_progress_info_set_progress (job->progress, transfer_info->num_files, source_info->num_files);
1904 }
1905 }
1906 #pragma GCC diagnostic pop
1907
1908 typedef void (*DeleteCallback) (GFile *file,
1909 GError *error,
1910 gpointer callback_data);
1911
1912 static gboolean
delete_file_recursively(GFile * file,GCancellable * cancellable,DeleteCallback callback,gpointer callback_data)1913 delete_file_recursively (GFile *file,
1914 GCancellable *cancellable,
1915 DeleteCallback callback,
1916 gpointer callback_data)
1917 {
1918 gboolean success;
1919 g_autoptr (GError) error = NULL;
1920
1921 do
1922 {
1923 g_autoptr (GFileEnumerator) enumerator = NULL;
1924
1925 success = g_file_delete (file, cancellable, &error);
1926 if (success ||
1927 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY))
1928 {
1929 break;
1930 }
1931
1932 g_clear_error (&error);
1933
1934 enumerator = g_file_enumerate_children (file,
1935 G_FILE_ATTRIBUTE_STANDARD_NAME,
1936 G_FILE_QUERY_INFO_NONE,
1937 cancellable, &error);
1938
1939 if (enumerator)
1940 {
1941 GFileInfo *info;
1942
1943 success = TRUE;
1944
1945 info = g_file_enumerator_next_file (enumerator,
1946 cancellable,
1947 &error);
1948
1949 while (info != NULL)
1950 {
1951 g_autoptr (GFile) child = NULL;
1952
1953 child = g_file_enumerator_get_child (enumerator, info);
1954
1955 success = success && delete_file_recursively (child,
1956 cancellable,
1957 callback,
1958 callback_data);
1959
1960 g_object_unref (info);
1961
1962 info = g_file_enumerator_next_file (enumerator,
1963 cancellable,
1964 &error);
1965 }
1966 }
1967
1968 if (error != NULL)
1969 {
1970 success = FALSE;
1971 }
1972 }
1973 while (success);
1974
1975 if (callback)
1976 {
1977 callback (file, error, callback_data);
1978 }
1979
1980 return success;
1981 }
1982
1983 typedef struct
1984 {
1985 CommonJob *job;
1986 SourceInfo *source_info;
1987 TransferInfo *transfer_info;
1988 } DeleteData;
1989
1990 static void
file_deleted_callback(GFile * file,GError * error,gpointer callback_data)1991 file_deleted_callback (GFile *file,
1992 GError *error,
1993 gpointer callback_data)
1994 {
1995 DeleteData *data = callback_data;
1996 CommonJob *job;
1997 SourceInfo *source_info;
1998 TransferInfo *transfer_info;
1999 GFileType file_type;
2000 char *primary;
2001 char *secondary;
2002 char *details = NULL;
2003 int response;
2004 g_autofree gchar *basename = NULL;
2005
2006 job = data->job;
2007 source_info = data->source_info;
2008 transfer_info = data->transfer_info;
2009
2010 data->transfer_info->num_files++;
2011
2012 if (error == NULL)
2013 {
2014 nautilus_file_changes_queue_file_removed (file);
2015 report_delete_progress (data->job, data->source_info, data->transfer_info);
2016
2017 return;
2018 }
2019
2020 if (job_aborted (job) ||
2021 job->skip_all_error ||
2022 should_skip_file (job, file) ||
2023 should_skip_readdir_error (job, file))
2024 {
2025 return;
2026 }
2027
2028 primary = g_strdup (_("Error while deleting."));
2029
2030 file_type = g_file_query_file_type (file,
2031 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2032 job->cancellable);
2033
2034 basename = get_basename (file);
2035
2036 if (file_type == G_FILE_TYPE_DIRECTORY)
2037 {
2038 secondary = IS_IO_ERROR (error, PERMISSION_DENIED) ?
2039 g_strdup_printf (_("There was an error deleting the "
2040 "folder “%s”."),
2041 basename) :
2042 g_strdup_printf (_("You do not have sufficient permissions "
2043 "to delete the folder “%s”."),
2044 basename);
2045 }
2046 else
2047 {
2048 secondary = IS_IO_ERROR (error, PERMISSION_DENIED) ?
2049 g_strdup_printf (_("There was an error deleting the "
2050 "file “%s”."),
2051 basename) :
2052 g_strdup_printf (_("You do not have sufficient permissions "
2053 "to delete the file “%s”."),
2054 basename);
2055 }
2056
2057 details = error->message;
2058
2059 response = run_cancel_or_skip_warning (job,
2060 primary,
2061 secondary,
2062 details,
2063 source_info->num_files,
2064 source_info->num_files - transfer_info->num_files);
2065
2066 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
2067 {
2068 abort_job (job);
2069 }
2070 else if (response == 1)
2071 {
2072 /* skip all */
2073 job->skip_all_error = TRUE;
2074 }
2075 }
2076
2077 static void
delete_files(CommonJob * job,GList * files,int * files_skipped)2078 delete_files (CommonJob *job,
2079 GList *files,
2080 int *files_skipped)
2081 {
2082 GList *l;
2083 GFile *file;
2084 g_auto (SourceInfo) source_info = SOURCE_INFO_INIT;
2085 TransferInfo transfer_info;
2086 DeleteData data;
2087
2088 if (job_aborted (job))
2089 {
2090 return;
2091 }
2092
2093 scan_sources (files,
2094 &source_info,
2095 job,
2096 OP_KIND_DELETE);
2097 if (job_aborted (job))
2098 {
2099 return;
2100 }
2101
2102 g_timer_start (job->time);
2103
2104 memset (&transfer_info, 0, sizeof (transfer_info));
2105 report_delete_progress (job, &source_info, &transfer_info);
2106
2107 data.job = job;
2108 data.source_info = &source_info;
2109 data.transfer_info = &transfer_info;
2110
2111 for (l = files;
2112 l != NULL && !job_aborted (job);
2113 l = l->next)
2114 {
2115 gboolean success;
2116
2117 file = l->data;
2118
2119 if (should_skip_file (job, file))
2120 {
2121 (*files_skipped)++;
2122 continue;
2123 }
2124
2125 success = delete_file_recursively (file, job->cancellable,
2126 file_deleted_callback,
2127 &data);
2128
2129 if (!success)
2130 {
2131 (*files_skipped)++;
2132 }
2133 }
2134 }
2135
2136 #pragma GCC diagnostic push
2137 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
2138 static void
report_trash_progress(CommonJob * job,SourceInfo * source_info,TransferInfo * transfer_info)2139 report_trash_progress (CommonJob *job,
2140 SourceInfo *source_info,
2141 TransferInfo *transfer_info)
2142 {
2143 int files_left;
2144 double elapsed, transfer_rate;
2145 int remaining_time;
2146 gint64 now;
2147 char *details;
2148 char *status;
2149 DeleteJob *delete_job;
2150
2151 delete_job = (DeleteJob *) job;
2152 now = g_get_monotonic_time ();
2153 files_left = source_info->num_files - transfer_info->num_files;
2154
2155 /* Races and whatnot could cause this to be negative... */
2156 if (files_left < 0)
2157 {
2158 files_left = 0;
2159 }
2160
2161 /* If the number of files left is 0, we want to update the status without
2162 * considering this time, since we want to change the status to completed
2163 * and probably we won't get more calls to this function */
2164 if (transfer_info->last_report_time != 0 &&
2165 ABS ((gint64) (transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC &&
2166 files_left > 0)
2167 {
2168 return;
2169 }
2170
2171 transfer_info->last_report_time = now;
2172
2173 if (source_info->num_files == 1)
2174 {
2175 g_autofree gchar *basename = NULL;
2176
2177 if (files_left > 0)
2178 {
2179 status = _("Trashing “%s”");
2180 }
2181 else
2182 {
2183 status = _("Trashed “%s”");
2184 }
2185
2186 basename = get_basename (G_FILE (delete_job->files->data));
2187 nautilus_progress_info_take_status (job->progress,
2188 g_strdup_printf (status, basename));
2189 }
2190 else
2191 {
2192 if (files_left > 0)
2193 {
2194 status = ngettext ("Trashing %'d file",
2195 "Trashing %'d files",
2196 source_info->num_files);
2197 }
2198 else
2199 {
2200 status = ngettext ("Trashed %'d file",
2201 "Trashed %'d files",
2202 source_info->num_files);
2203 }
2204 nautilus_progress_info_take_status (job->progress,
2205 g_strdup_printf (status,
2206 source_info->num_files));
2207 }
2208
2209
2210 elapsed = g_timer_elapsed (job->time, NULL);
2211 transfer_rate = 0;
2212 remaining_time = INT_MAX;
2213 if (elapsed > 0)
2214 {
2215 transfer_rate = transfer_info->num_files / elapsed;
2216 if (transfer_rate > 0)
2217 {
2218 remaining_time = (source_info->num_files - transfer_info->num_files) / transfer_rate;
2219 }
2220 }
2221
2222 if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE ||
2223 transfer_rate == 0)
2224 {
2225 if (files_left > 0)
2226 {
2227 /* To translators: %'d is the number of files completed for the operation,
2228 * so it will be something like 2/14. */
2229 details = g_strdup_printf (_("%'d / %'d"),
2230 transfer_info->num_files + 1,
2231 source_info->num_files);
2232 }
2233 else
2234 {
2235 /* To translators: %'d is the number of files completed for the operation,
2236 * so it will be something like 2/14. */
2237 details = g_strdup_printf (_("%'d / %'d"),
2238 transfer_info->num_files,
2239 source_info->num_files);
2240 }
2241 }
2242 else
2243 {
2244 if (files_left > 0)
2245 {
2246 gchar *time_left_message;
2247 gchar *files_per_second_message;
2248 gchar *concat_detail;
2249 g_autofree gchar *formatted_time = NULL;
2250
2251 /* To translators: %s will expand to a time duration like "2 minutes".
2252 * So the whole thing will be something like "1 / 5 -- 2 hours left (4 files/sec)"
2253 *
2254 * The singular/plural form will be used depending on the remaining time (i.e. the %s argument).
2255 */
2256 time_left_message = ngettext ("%'d / %'d \xE2\x80\x94 %s left",
2257 "%'d / %'d \xE2\x80\x94 %s left",
2258 seconds_count_format_time_units (remaining_time));
2259 files_per_second_message = ngettext ("(%d file/sec)",
2260 "(%d files/sec)",
2261 (int) (transfer_rate + 0.5));
2262 concat_detail = g_strconcat (time_left_message, " ", files_per_second_message, NULL);
2263
2264 formatted_time = get_formatted_time (remaining_time);
2265 details = g_strdup_printf (concat_detail,
2266 transfer_info->num_files + 1,
2267 source_info->num_files,
2268 formatted_time,
2269 (int) transfer_rate + 0.5);
2270
2271 g_free (concat_detail);
2272 }
2273 else
2274 {
2275 /* To translators: %'d is the number of files completed for the operation,
2276 * so it will be something like 2/14. */
2277 details = g_strdup_printf (_("%'d / %'d"),
2278 transfer_info->num_files,
2279 source_info->num_files);
2280 }
2281 }
2282 nautilus_progress_info_set_details (job->progress, details);
2283
2284 if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE)
2285 {
2286 nautilus_progress_info_set_remaining_time (job->progress,
2287 remaining_time);
2288 nautilus_progress_info_set_elapsed_time (job->progress,
2289 elapsed);
2290 }
2291
2292 if (source_info->num_files != 0)
2293 {
2294 nautilus_progress_info_set_progress (job->progress, transfer_info->num_files, source_info->num_files);
2295 }
2296 }
2297 #pragma GCC diagnostic pop
2298
2299 static void
trash_file(CommonJob * job,GFile * file,gboolean * skipped_file,SourceInfo * source_info,TransferInfo * transfer_info,gboolean toplevel,GList ** to_delete)2300 trash_file (CommonJob *job,
2301 GFile *file,
2302 gboolean *skipped_file,
2303 SourceInfo *source_info,
2304 TransferInfo *transfer_info,
2305 gboolean toplevel,
2306 GList **to_delete)
2307 {
2308 GError *error;
2309 char *primary, *secondary, *details;
2310 int response;
2311 g_autofree gchar *basename = NULL;
2312
2313 if (should_skip_file (job, file))
2314 {
2315 *skipped_file = TRUE;
2316 return;
2317 }
2318
2319 error = NULL;
2320
2321 if (g_file_trash (file, job->cancellable, &error))
2322 {
2323 transfer_info->num_files++;
2324 nautilus_file_changes_queue_file_removed (file);
2325
2326 if (job->undo_info != NULL)
2327 {
2328 nautilus_file_undo_info_trash_add_file (NAUTILUS_FILE_UNDO_INFO_TRASH (job->undo_info), file);
2329 }
2330
2331 report_trash_progress (job, source_info, transfer_info);
2332 return;
2333 }
2334
2335 if (job->skip_all_error)
2336 {
2337 *skipped_file = TRUE;
2338 goto skip;
2339 }
2340
2341 if (job->delete_all)
2342 {
2343 *to_delete = g_list_prepend (*to_delete, file);
2344 goto skip;
2345 }
2346
2347 basename = get_basename (file);
2348 /* Translators: %s is a file name */
2349 primary = g_strdup_printf (_("“%s” can’t be put in the trash. Do you want "
2350 "to delete it immediately?"),
2351 basename);
2352
2353 details = NULL;
2354 secondary = NULL;
2355 if (!IS_IO_ERROR (error, NOT_SUPPORTED))
2356 {
2357 details = error->message;
2358 }
2359 else if (!g_file_is_native (file))
2360 {
2361 secondary = g_strdup (_("This remote location does not support sending items to the trash."));
2362 }
2363
2364 response = run_question (job,
2365 primary,
2366 secondary,
2367 details,
2368 (source_info->num_files - transfer_info->num_files) > 1,
2369 CANCEL, SKIP_ALL, SKIP, DELETE_ALL, DELETE,
2370 NULL);
2371
2372 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
2373 {
2374 ((DeleteJob *) job)->user_cancel = TRUE;
2375 abort_job (job);
2376 }
2377 else if (response == 1) /* skip all */
2378 {
2379 *skipped_file = TRUE;
2380 job->skip_all_error = TRUE;
2381 }
2382 else if (response == 2) /* skip */
2383 {
2384 *skipped_file = TRUE;
2385 job->skip_all_error = TRUE;
2386 }
2387 else if (response == 3) /* delete all */
2388 {
2389 *to_delete = g_list_prepend (*to_delete, file);
2390 job->delete_all = TRUE;
2391 }
2392 else if (response == 4) /* delete */
2393 {
2394 *to_delete = g_list_prepend (*to_delete, file);
2395 }
2396
2397 skip:
2398 g_error_free (error);
2399 }
2400
2401 static void
source_info_remove_descendent_files_from_count(GFile * dir,SourceDirInfo * dir_info,SourceInfo * source_info)2402 source_info_remove_descendent_files_from_count (GFile *dir,
2403 SourceDirInfo *dir_info,
2404 SourceInfo *source_info)
2405 {
2406 GFile *other_dir;
2407 SourceDirInfo *other_dir_info;
2408 GHashTableIter dir_info_iter;
2409
2410 source_info->num_files -= dir_info->num_files_children;
2411 source_info->num_bytes -= dir_info->num_bytes_children;
2412
2413 g_hash_table_iter_init (&dir_info_iter, source_info->scanned_dirs_info);
2414 while (g_hash_table_iter_next (&dir_info_iter, (gpointer *) &other_dir, (gpointer *) &other_dir_info))
2415 {
2416 g_assert (other_dir != NULL);
2417 g_assert (other_dir_info != NULL);
2418
2419 if (other_dir_info != dir_info &&
2420 g_file_has_parent (other_dir, dir))
2421 {
2422 source_info_remove_descendent_files_from_count (other_dir,
2423 other_dir_info,
2424 source_info);
2425 }
2426 }
2427 }
2428
2429 static void
source_info_remove_file_from_count(GFile * file,CommonJob * job,SourceInfo * source_info)2430 source_info_remove_file_from_count (GFile *file,
2431 CommonJob *job,
2432 SourceInfo *source_info)
2433 {
2434 g_autoptr (GFileInfo) file_info = NULL;
2435 SourceDirInfo *dir_info;
2436
2437 if (g_cancellable_is_cancelled (job->cancellable))
2438 {
2439 return;
2440 }
2441
2442 file_info = g_file_query_info (file,
2443 G_FILE_ATTRIBUTE_STANDARD_SIZE,
2444 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2445 job->cancellable,
2446 NULL);
2447
2448 source_info->num_files--;
2449 if (file_info != NULL)
2450 {
2451 source_info->num_bytes -= g_file_info_get_size (file_info);
2452 }
2453
2454 dir_info = g_hash_table_lookup (source_info->scanned_dirs_info, file);
2455
2456 if (dir_info != NULL)
2457 {
2458 source_info_remove_descendent_files_from_count (file,
2459 dir_info,
2460 source_info);
2461 }
2462 }
2463
2464 static void
trash_files(CommonJob * job,GList * files,int * files_skipped)2465 trash_files (CommonJob *job,
2466 GList *files,
2467 int *files_skipped)
2468 {
2469 GList *l;
2470 GFile *file;
2471 GList *to_delete;
2472 g_auto (SourceInfo) source_info = SOURCE_INFO_INIT;
2473 TransferInfo transfer_info;
2474 gboolean skipped_file;
2475
2476 if (job_aborted (job))
2477 {
2478 return;
2479 }
2480
2481 scan_sources (files,
2482 &source_info,
2483 job,
2484 OP_KIND_TRASH);
2485 if (job_aborted (job))
2486 {
2487 return;
2488 }
2489
2490 g_timer_start (job->time);
2491
2492 memset (&transfer_info, 0, sizeof (transfer_info));
2493 report_trash_progress (job, &source_info, &transfer_info);
2494
2495 to_delete = NULL;
2496 for (l = files;
2497 l != NULL && !job_aborted (job);
2498 l = l->next)
2499 {
2500 file = l->data;
2501
2502 skipped_file = FALSE;
2503 trash_file (job, file,
2504 &skipped_file,
2505 &source_info, &transfer_info,
2506 TRUE, &to_delete);
2507 if (skipped_file)
2508 {
2509 (*files_skipped)++;
2510 source_info_remove_file_from_count (file, job, &source_info);
2511 report_trash_progress (job, &source_info, &transfer_info);
2512 }
2513 }
2514
2515 if (to_delete)
2516 {
2517 to_delete = g_list_reverse (to_delete);
2518 delete_files (job, to_delete, files_skipped);
2519 g_list_free (to_delete);
2520 }
2521 }
2522
2523 static void
delete_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)2524 delete_task_done (GObject *source_object,
2525 GAsyncResult *res,
2526 gpointer user_data)
2527 {
2528 DeleteJob *job;
2529 GHashTable *debuting_uris;
2530
2531 job = user_data;
2532
2533 g_list_free_full (job->files, g_object_unref);
2534
2535 if (job->done_callback)
2536 {
2537 debuting_uris = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
2538 job->done_callback (debuting_uris, job->user_cancel, job->done_callback_data);
2539 g_hash_table_unref (debuting_uris);
2540 }
2541
2542 finalize_common ((CommonJob *) job);
2543
2544 nautilus_file_changes_consume_changes (TRUE);
2545 }
2546
2547 static void
trash_or_delete_internal(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)2548 trash_or_delete_internal (GTask *task,
2549 gpointer source_object,
2550 gpointer task_data,
2551 GCancellable *cancellable)
2552 {
2553 DeleteJob *job = task_data;
2554 g_autoptr (GList) to_trash_files = NULL;
2555 g_autoptr (GList) to_delete_files = NULL;
2556 GList *l;
2557 GFile *file;
2558 gboolean confirmed;
2559 CommonJob *common;
2560 gboolean must_confirm_delete_in_trash;
2561 gboolean must_confirm_delete;
2562 int files_skipped;
2563
2564 common = (CommonJob *) job;
2565
2566 nautilus_progress_info_start (job->common.progress);
2567
2568 must_confirm_delete_in_trash = FALSE;
2569 must_confirm_delete = FALSE;
2570 files_skipped = 0;
2571
2572 for (l = job->files; l != NULL; l = l->next)
2573 {
2574 file = l->data;
2575
2576 if (job->try_trash &&
2577 g_file_has_uri_scheme (file, "trash"))
2578 {
2579 must_confirm_delete_in_trash = TRUE;
2580 to_delete_files = g_list_prepend (to_delete_files, file);
2581 }
2582 else if (can_delete_without_confirm (file))
2583 {
2584 to_delete_files = g_list_prepend (to_delete_files, file);
2585 }
2586 else
2587 {
2588 if (job->try_trash)
2589 {
2590 to_trash_files = g_list_prepend (to_trash_files, file);
2591 }
2592 else
2593 {
2594 must_confirm_delete = TRUE;
2595 to_delete_files = g_list_prepend (to_delete_files, file);
2596 }
2597 }
2598 }
2599
2600 if (to_delete_files != NULL)
2601 {
2602 to_delete_files = g_list_reverse (to_delete_files);
2603 confirmed = TRUE;
2604 if (must_confirm_delete_in_trash)
2605 {
2606 confirmed = confirm_delete_from_trash (common, to_delete_files);
2607 }
2608 else if (must_confirm_delete)
2609 {
2610 confirmed = confirm_delete_directly (common, to_delete_files);
2611 }
2612 if (confirmed)
2613 {
2614 delete_files (common, to_delete_files, &files_skipped);
2615 }
2616 else
2617 {
2618 job->user_cancel = TRUE;
2619 }
2620 }
2621
2622 if (to_trash_files != NULL)
2623 {
2624 to_trash_files = g_list_reverse (to_trash_files);
2625
2626 trash_files (common, to_trash_files, &files_skipped);
2627 }
2628
2629 if (files_skipped == g_list_length (job->files))
2630 {
2631 /* User has skipped all files, report user cancel */
2632 job->user_cancel = TRUE;
2633 }
2634 }
2635
2636 static DeleteJob *
setup_delete_job(GList * files,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,gboolean try_trash,NautilusDeleteCallback done_callback,gpointer done_callback_data)2637 setup_delete_job (GList *files,
2638 GtkWindow *parent_window,
2639 NautilusFileOperationsDBusData *dbus_data,
2640 gboolean try_trash,
2641 NautilusDeleteCallback done_callback,
2642 gpointer done_callback_data)
2643 {
2644 DeleteJob *job;
2645
2646 /* TODO: special case desktop icon link files ... */
2647 job = op_job_new (DeleteJob, parent_window, dbus_data);
2648 job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
2649 job->try_trash = try_trash;
2650 job->user_cancel = FALSE;
2651 job->done_callback = done_callback;
2652 job->done_callback_data = done_callback_data;
2653
2654 if (g_strcmp0 (g_getenv ("RUNNING_TESTS"), "TRUE"))
2655 {
2656 if (try_trash)
2657 {
2658 inhibit_power_manager ((CommonJob *) job, _("Trashing Files"));
2659 }
2660 else
2661 {
2662 inhibit_power_manager ((CommonJob *) job, _("Deleting Files"));
2663 }
2664 }
2665
2666 if (!nautilus_file_undo_manager_is_operating () && try_trash)
2667 {
2668 job->common.undo_info = nautilus_file_undo_info_trash_new (g_list_length (files));
2669 }
2670
2671 return job;
2672 }
2673
2674 static void
trash_or_delete_internal_sync(GList * files,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,gboolean try_trash)2675 trash_or_delete_internal_sync (GList *files,
2676 GtkWindow *parent_window,
2677 NautilusFileOperationsDBusData *dbus_data,
2678 gboolean try_trash)
2679 {
2680 GTask *task;
2681 DeleteJob *job;
2682
2683 job = setup_delete_job (files,
2684 parent_window,
2685 dbus_data,
2686 try_trash,
2687 NULL,
2688 NULL);
2689
2690 task = g_task_new (NULL, NULL, NULL, job);
2691 g_task_set_task_data (task, job, NULL);
2692 g_task_run_in_thread_sync (task, trash_or_delete_internal);
2693 g_object_unref (task);
2694 /* Since g_task_run_in_thread_sync doesn't work with callbacks (in this case not reaching
2695 * delete_task_done) we need to set up the undo information ourselves.
2696 */
2697 delete_task_done (NULL, NULL, job);
2698 }
2699
2700 static void
trash_or_delete_internal_async(GList * files,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,gboolean try_trash,NautilusDeleteCallback done_callback,gpointer done_callback_data)2701 trash_or_delete_internal_async (GList *files,
2702 GtkWindow *parent_window,
2703 NautilusFileOperationsDBusData *dbus_data,
2704 gboolean try_trash,
2705 NautilusDeleteCallback done_callback,
2706 gpointer done_callback_data)
2707 {
2708 GTask *task;
2709 DeleteJob *job;
2710
2711 job = setup_delete_job (files,
2712 parent_window,
2713 dbus_data,
2714 try_trash,
2715 done_callback,
2716 done_callback_data);
2717
2718 task = g_task_new (NULL, NULL, delete_task_done, job);
2719 g_task_set_task_data (task, job, NULL);
2720 g_task_run_in_thread (task, trash_or_delete_internal);
2721 g_object_unref (task);
2722 }
2723
2724 void
nautilus_file_operations_trash_or_delete_sync(GList * files)2725 nautilus_file_operations_trash_or_delete_sync (GList *files)
2726 {
2727 trash_or_delete_internal_sync (files, NULL, NULL, TRUE);
2728 }
2729
2730 void
nautilus_file_operations_delete_sync(GList * files)2731 nautilus_file_operations_delete_sync (GList *files)
2732 {
2733 trash_or_delete_internal_sync (files, NULL, NULL, FALSE);
2734 }
2735
2736 void
nautilus_file_operations_trash_or_delete_async(GList * files,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusDeleteCallback done_callback,gpointer done_callback_data)2737 nautilus_file_operations_trash_or_delete_async (GList *files,
2738 GtkWindow *parent_window,
2739 NautilusFileOperationsDBusData *dbus_data,
2740 NautilusDeleteCallback done_callback,
2741 gpointer done_callback_data)
2742 {
2743 trash_or_delete_internal_async (files, parent_window,
2744 dbus_data,
2745 TRUE,
2746 done_callback, done_callback_data);
2747 }
2748
2749 void
nautilus_file_operations_delete_async(GList * files,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusDeleteCallback done_callback,gpointer done_callback_data)2750 nautilus_file_operations_delete_async (GList *files,
2751 GtkWindow *parent_window,
2752 NautilusFileOperationsDBusData *dbus_data,
2753 NautilusDeleteCallback done_callback,
2754 gpointer done_callback_data)
2755 {
2756 trash_or_delete_internal_async (files, parent_window,
2757 dbus_data,
2758 FALSE,
2759 done_callback, done_callback_data);
2760 }
2761
2762
2763
2764 typedef struct
2765 {
2766 gboolean eject;
2767 GMount *mount;
2768 GMountOperation *mount_operation;
2769 GtkWindow *parent_window;
2770 NautilusUnmountCallback callback;
2771 gpointer callback_data;
2772 } UnmountData;
2773
2774 static void
unmount_data_free(UnmountData * data)2775 unmount_data_free (UnmountData *data)
2776 {
2777 if (data->parent_window)
2778 {
2779 g_object_remove_weak_pointer (G_OBJECT (data->parent_window),
2780 (gpointer *) &data->parent_window);
2781 }
2782
2783 g_clear_object (&data->mount_operation);
2784 g_object_unref (data->mount);
2785 g_free (data);
2786 }
2787
2788 static void
unmount_mount_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)2789 unmount_mount_callback (GObject *source_object,
2790 GAsyncResult *res,
2791 gpointer user_data)
2792 {
2793 UnmountData *data = user_data;
2794 GError *error;
2795 char *primary;
2796 gboolean unmounted;
2797
2798 error = NULL;
2799 if (data->eject)
2800 {
2801 unmounted = g_mount_eject_with_operation_finish (G_MOUNT (source_object),
2802 res, &error);
2803 }
2804 else
2805 {
2806 unmounted = g_mount_unmount_with_operation_finish (G_MOUNT (source_object),
2807 res, &error);
2808 }
2809
2810 if (!unmounted)
2811 {
2812 if (error->code != G_IO_ERROR_FAILED_HANDLED)
2813 {
2814 g_autofree gchar *mount_name = NULL;
2815
2816 mount_name = g_mount_get_name (G_MOUNT (source_object));
2817 if (data->eject)
2818 {
2819 primary = g_strdup_printf (_("Unable to eject %s"),
2820 mount_name);
2821 }
2822 else
2823 {
2824 primary = g_strdup_printf (_("Unable to unmount %s"),
2825 mount_name);
2826 }
2827 show_dialog (primary,
2828 error->message,
2829 data->parent_window,
2830 GTK_MESSAGE_ERROR);
2831 g_free (primary);
2832 }
2833 }
2834
2835 if (data->callback)
2836 {
2837 data->callback (data->callback_data);
2838 }
2839
2840 if (error != NULL)
2841 {
2842 g_error_free (error);
2843 }
2844
2845 unmount_data_free (data);
2846 }
2847
2848 static void
do_unmount(UnmountData * data)2849 do_unmount (UnmountData *data)
2850 {
2851 GMountOperation *mount_op;
2852
2853 if (data->mount_operation)
2854 {
2855 mount_op = g_object_ref (data->mount_operation);
2856 }
2857 else
2858 {
2859 mount_op = gtk_mount_operation_new (data->parent_window);
2860 }
2861
2862 g_signal_connect (mount_op, "show-unmount-progress",
2863 G_CALLBACK (show_unmount_progress_cb), NULL);
2864 g_signal_connect (mount_op, "aborted",
2865 G_CALLBACK (show_unmount_progress_aborted_cb), NULL);
2866
2867 if (data->eject)
2868 {
2869 g_mount_eject_with_operation (data->mount,
2870 0,
2871 mount_op,
2872 NULL,
2873 unmount_mount_callback,
2874 data);
2875 }
2876 else
2877 {
2878 g_mount_unmount_with_operation (data->mount,
2879 0,
2880 mount_op,
2881 NULL,
2882 unmount_mount_callback,
2883 data);
2884 }
2885 g_object_unref (mount_op);
2886 }
2887
2888 static gboolean
dir_has_files(GFile * dir)2889 dir_has_files (GFile *dir)
2890 {
2891 GFileEnumerator *enumerator;
2892 gboolean res;
2893 GFileInfo *file_info;
2894
2895 res = FALSE;
2896
2897 enumerator = g_file_enumerate_children (dir,
2898 G_FILE_ATTRIBUTE_STANDARD_NAME,
2899 0,
2900 NULL, NULL);
2901 if (enumerator)
2902 {
2903 file_info = g_file_enumerator_next_file (enumerator, NULL, NULL);
2904 if (file_info != NULL)
2905 {
2906 res = TRUE;
2907 g_object_unref (file_info);
2908 }
2909
2910 g_file_enumerator_close (enumerator, NULL, NULL);
2911 g_object_unref (enumerator);
2912 }
2913
2914
2915 return res;
2916 }
2917
2918 static GList *
get_trash_dirs_for_mount(GMount * mount)2919 get_trash_dirs_for_mount (GMount *mount)
2920 {
2921 GFile *root;
2922 GFile *trash;
2923 char *relpath;
2924 GList *list;
2925
2926 root = g_mount_get_root (mount);
2927 if (root == NULL)
2928 {
2929 return NULL;
2930 }
2931
2932 list = NULL;
2933
2934 if (g_file_is_native (root))
2935 {
2936 relpath = g_strdup_printf (".Trash/%d", getuid ());
2937 trash = g_file_resolve_relative_path (root, relpath);
2938 g_free (relpath);
2939
2940 list = g_list_prepend (list, g_file_get_child (trash, "files"));
2941 list = g_list_prepend (list, g_file_get_child (trash, "info"));
2942
2943 g_object_unref (trash);
2944
2945 relpath = g_strdup_printf (".Trash-%d", getuid ());
2946 trash = g_file_get_child (root, relpath);
2947 g_free (relpath);
2948
2949 list = g_list_prepend (list, g_file_get_child (trash, "files"));
2950 list = g_list_prepend (list, g_file_get_child (trash, "info"));
2951
2952 g_object_unref (trash);
2953 }
2954
2955 g_object_unref (root);
2956
2957 return list;
2958 }
2959
2960 static gboolean
has_trash_files(GMount * mount)2961 has_trash_files (GMount *mount)
2962 {
2963 GList *dirs, *l;
2964 GFile *dir;
2965 gboolean res;
2966
2967 dirs = get_trash_dirs_for_mount (mount);
2968
2969 res = FALSE;
2970
2971 for (l = dirs; l != NULL; l = l->next)
2972 {
2973 dir = l->data;
2974
2975 if (dir_has_files (dir))
2976 {
2977 res = TRUE;
2978 break;
2979 }
2980 }
2981
2982 g_list_free_full (dirs, g_object_unref);
2983
2984 return res;
2985 }
2986
2987
2988 static gint
prompt_empty_trash(GtkWindow * parent_window)2989 prompt_empty_trash (GtkWindow *parent_window)
2990 {
2991 gint result;
2992 GtkWidget *dialog;
2993 GdkScreen *screen;
2994
2995 screen = NULL;
2996 if (parent_window != NULL)
2997 {
2998 screen = gtk_widget_get_screen (GTK_WIDGET (parent_window));
2999 }
3000
3001 /* Do we need to be modal ? */
3002 dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
3003 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
3004 _("Do you want to empty the trash before you unmount?"));
3005 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
3006 _("In order to regain the "
3007 "free space on this volume "
3008 "the trash must be emptied. "
3009 "All trashed items on the volume "
3010 "will be permanently lost."));
3011 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
3012 _("Do _not Empty Trash"), GTK_RESPONSE_REJECT,
3013 CANCEL, GTK_RESPONSE_CANCEL,
3014 _("Empty _Trash"), GTK_RESPONSE_ACCEPT, NULL);
3015 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
3016 gtk_window_set_title (GTK_WINDOW (dialog), ""); /* as per HIG */
3017 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
3018 if (screen)
3019 {
3020 gtk_window_set_screen (GTK_WINDOW (dialog), screen);
3021 }
3022 atk_object_set_role (gtk_widget_get_accessible (dialog), ATK_ROLE_ALERT);
3023
3024 /* Make transient for the window group */
3025 gtk_widget_realize (dialog);
3026 if (screen != NULL)
3027 {
3028 gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (dialog)),
3029 gdk_screen_get_root_window (screen));
3030 }
3031
3032 result = gtk_dialog_run (GTK_DIALOG (dialog));
3033 gtk_widget_destroy (dialog);
3034 return result;
3035 }
3036
3037 static void
empty_trash_for_unmount_done(gboolean success,gpointer user_data)3038 empty_trash_for_unmount_done (gboolean success,
3039 gpointer user_data)
3040 {
3041 UnmountData *data = user_data;
3042 do_unmount (data);
3043 }
3044
3045 void
nautilus_file_operations_unmount_mount_full(GtkWindow * parent_window,GMount * mount,GMountOperation * mount_operation,gboolean eject,gboolean check_trash,NautilusUnmountCallback callback,gpointer callback_data)3046 nautilus_file_operations_unmount_mount_full (GtkWindow *parent_window,
3047 GMount *mount,
3048 GMountOperation *mount_operation,
3049 gboolean eject,
3050 gboolean check_trash,
3051 NautilusUnmountCallback callback,
3052 gpointer callback_data)
3053 {
3054 UnmountData *data;
3055 int response;
3056
3057 data = g_new0 (UnmountData, 1);
3058 data->callback = callback;
3059 data->callback_data = callback_data;
3060 if (parent_window)
3061 {
3062 data->parent_window = parent_window;
3063 g_object_add_weak_pointer (G_OBJECT (data->parent_window),
3064 (gpointer *) &data->parent_window);
3065 }
3066 if (mount_operation)
3067 {
3068 data->mount_operation = g_object_ref (mount_operation);
3069 }
3070 data->eject = eject;
3071 data->mount = g_object_ref (mount);
3072
3073 if (check_trash && has_trash_files (mount))
3074 {
3075 response = prompt_empty_trash (parent_window);
3076
3077 if (response == GTK_RESPONSE_ACCEPT)
3078 {
3079 GTask *task;
3080 EmptyTrashJob *job;
3081
3082 job = op_job_new (EmptyTrashJob, parent_window, NULL);
3083 job->should_confirm = FALSE;
3084 job->trash_dirs = get_trash_dirs_for_mount (mount);
3085 job->done_callback = empty_trash_for_unmount_done;
3086 job->done_callback_data = data;
3087
3088 task = g_task_new (NULL, NULL, empty_trash_task_done, job);
3089 g_task_set_task_data (task, job, NULL);
3090 g_task_run_in_thread (task, empty_trash_thread_func);
3091 g_object_unref (task);
3092 return;
3093 }
3094 else if (response == GTK_RESPONSE_CANCEL)
3095 {
3096 if (callback)
3097 {
3098 callback (callback_data);
3099 }
3100
3101 unmount_data_free (data);
3102 return;
3103 }
3104 }
3105
3106 do_unmount (data);
3107 }
3108
3109 void
nautilus_file_operations_unmount_mount(GtkWindow * parent_window,GMount * mount,gboolean eject,gboolean check_trash)3110 nautilus_file_operations_unmount_mount (GtkWindow *parent_window,
3111 GMount *mount,
3112 gboolean eject,
3113 gboolean check_trash)
3114 {
3115 nautilus_file_operations_unmount_mount_full (parent_window, mount, NULL, eject,
3116 check_trash, NULL, NULL);
3117 }
3118
3119 static void
mount_callback_data_notify(gpointer data,GObject * object)3120 mount_callback_data_notify (gpointer data,
3121 GObject *object)
3122 {
3123 GMountOperation *mount_op;
3124
3125 mount_op = G_MOUNT_OPERATION (data);
3126 g_object_set_data (G_OBJECT (mount_op), "mount-callback", NULL);
3127 g_object_set_data (G_OBJECT (mount_op), "mount-callback-data", NULL);
3128 }
3129
3130 static void
volume_mount_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)3131 volume_mount_cb (GObject *source_object,
3132 GAsyncResult *res,
3133 gpointer user_data)
3134 {
3135 NautilusMountCallback mount_callback;
3136 GObject *mount_callback_data_object;
3137 GMountOperation *mount_op = user_data;
3138 GError *error;
3139 char *primary;
3140 char *name;
3141 gboolean success;
3142
3143 success = TRUE;
3144 error = NULL;
3145 if (!g_volume_mount_finish (G_VOLUME (source_object), res, &error))
3146 {
3147 if (error->code != G_IO_ERROR_FAILED_HANDLED &&
3148 error->code != G_IO_ERROR_ALREADY_MOUNTED)
3149 {
3150 GtkWindow *parent;
3151
3152 parent = gtk_mount_operation_get_parent (GTK_MOUNT_OPERATION (mount_op));
3153 name = g_volume_get_name (G_VOLUME (source_object));
3154 primary = g_strdup_printf (_("Unable to access “%s”"), name);
3155 g_free (name);
3156 success = FALSE;
3157 show_dialog (primary,
3158 error->message,
3159 parent,
3160 GTK_MESSAGE_ERROR);
3161 g_free (primary);
3162 }
3163 g_error_free (error);
3164 }
3165
3166 mount_callback = (NautilusMountCallback)
3167 g_object_get_data (G_OBJECT (mount_op), "mount-callback");
3168 mount_callback_data_object =
3169 g_object_get_data (G_OBJECT (mount_op), "mount-callback-data");
3170
3171 if (mount_callback != NULL)
3172 {
3173 (*mount_callback)(G_VOLUME (source_object),
3174 success,
3175 mount_callback_data_object);
3176
3177 if (mount_callback_data_object != NULL)
3178 {
3179 g_object_weak_unref (mount_callback_data_object,
3180 mount_callback_data_notify,
3181 mount_op);
3182 }
3183 }
3184
3185 g_object_unref (mount_op);
3186 }
3187
3188
3189 void
nautilus_file_operations_mount_volume(GtkWindow * parent_window,GVolume * volume)3190 nautilus_file_operations_mount_volume (GtkWindow *parent_window,
3191 GVolume *volume)
3192 {
3193 nautilus_file_operations_mount_volume_full (parent_window, volume,
3194 NULL, NULL);
3195 }
3196
3197 void
nautilus_file_operations_mount_volume_full(GtkWindow * parent_window,GVolume * volume,NautilusMountCallback mount_callback,GObject * mount_callback_data_object)3198 nautilus_file_operations_mount_volume_full (GtkWindow *parent_window,
3199 GVolume *volume,
3200 NautilusMountCallback mount_callback,
3201 GObject *mount_callback_data_object)
3202 {
3203 GMountOperation *mount_op;
3204
3205 mount_op = gtk_mount_operation_new (parent_window);
3206 g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
3207 g_object_set_data (G_OBJECT (mount_op),
3208 "mount-callback",
3209 mount_callback);
3210
3211 if (mount_callback != NULL &&
3212 mount_callback_data_object != NULL)
3213 {
3214 g_object_weak_ref (mount_callback_data_object,
3215 mount_callback_data_notify,
3216 mount_op);
3217 }
3218 g_object_set_data (G_OBJECT (mount_op),
3219 "mount-callback-data",
3220 mount_callback_data_object);
3221
3222 g_volume_mount (volume, 0, mount_op, NULL, volume_mount_cb, mount_op);
3223 }
3224
3225 static void
report_preparing_count_progress(CommonJob * job,SourceInfo * source_info)3226 report_preparing_count_progress (CommonJob *job,
3227 SourceInfo *source_info)
3228 {
3229 char *s;
3230
3231 switch (source_info->op)
3232 {
3233 default:
3234 case OP_KIND_COPY:
3235 {
3236 g_autofree gchar *formatted_size = NULL;
3237
3238 formatted_size = g_format_size (source_info->num_bytes);
3239 s = g_strdup_printf (ngettext ("Preparing to copy %'d file (%s)",
3240 "Preparing to copy %'d files (%s)",
3241 source_info->num_files),
3242 source_info->num_files,
3243 formatted_size);
3244 }
3245 break;
3246
3247 case OP_KIND_MOVE:
3248 {
3249 g_autofree gchar *formatted_size = NULL;
3250
3251 formatted_size = g_format_size (source_info->num_bytes);
3252 s = g_strdup_printf (ngettext ("Preparing to move %'d file (%s)",
3253 "Preparing to move %'d files (%s)",
3254 source_info->num_files),
3255 source_info->num_files,
3256 formatted_size);
3257 }
3258 break;
3259
3260 case OP_KIND_DELETE:
3261 {
3262 g_autofree gchar *formatted_size = NULL;
3263
3264 formatted_size = g_format_size (source_info->num_bytes);
3265 s = g_strdup_printf (ngettext ("Preparing to delete %'d file (%s)",
3266 "Preparing to delete %'d files (%s)",
3267 source_info->num_files),
3268 source_info->num_files,
3269 formatted_size);
3270 }
3271 break;
3272
3273 case OP_KIND_TRASH:
3274 {
3275 s = g_strdup_printf (ngettext ("Preparing to trash %'d file",
3276 "Preparing to trash %'d files",
3277 source_info->num_files),
3278 source_info->num_files);
3279 }
3280 break;
3281
3282 case OP_KIND_COMPRESS:
3283 {
3284 s = g_strdup_printf (ngettext ("Preparing to compress %'d file",
3285 "Preparing to compress %'d files",
3286 source_info->num_files),
3287 source_info->num_files);
3288 }
3289 }
3290
3291 nautilus_progress_info_take_details (job->progress, s);
3292 nautilus_progress_info_pulse_progress (job->progress);
3293 }
3294
3295 static void
count_file(GFileInfo * info,CommonJob * job,SourceInfo * source_info,SourceDirInfo * dir_info)3296 count_file (GFileInfo *info,
3297 CommonJob *job,
3298 SourceInfo *source_info,
3299 SourceDirInfo *dir_info)
3300 {
3301 goffset num_bytes = g_file_info_get_size (info);
3302
3303 source_info->num_files += 1;
3304 source_info->num_bytes += num_bytes;
3305
3306 if (dir_info != NULL)
3307 {
3308 dir_info->num_files_children += 1;
3309 dir_info->num_bytes_children += num_bytes;
3310 }
3311
3312 if (source_info->num_files_since_progress++ > 100)
3313 {
3314 report_preparing_count_progress (job, source_info);
3315 source_info->num_files_since_progress = 0;
3316 }
3317 }
3318
3319 static char *
get_scan_primary(OpKind kind)3320 get_scan_primary (OpKind kind)
3321 {
3322 switch (kind)
3323 {
3324 default:
3325 case OP_KIND_COPY:
3326 {
3327 return g_strdup (_("Error while copying."));
3328 }
3329
3330 case OP_KIND_MOVE:
3331 {
3332 return g_strdup (_("Error while moving."));
3333 }
3334
3335 case OP_KIND_DELETE:
3336 {
3337 return g_strdup (_("Error while deleting."));
3338 }
3339
3340 case OP_KIND_TRASH:
3341 {
3342 return g_strdup (_("Error while moving files to trash."));
3343 }
3344
3345 case OP_KIND_COMPRESS:
3346 {
3347 return g_strdup (_("Error while compressing files."));
3348 }
3349 }
3350 }
3351
3352 static void
scan_dir(GFile * dir,SourceInfo * source_info,CommonJob * job,GQueue * dirs)3353 scan_dir (GFile *dir,
3354 SourceInfo *source_info,
3355 CommonJob *job,
3356 GQueue *dirs)
3357 {
3358 GFileInfo *info;
3359 GError *error;
3360 GFile *subdir;
3361 GFileEnumerator *enumerator;
3362 char *primary, *secondary, *details;
3363 int response;
3364 SourceInfo saved_info;
3365 g_autolist (GFile) subdirs = NULL;
3366 SourceDirInfo *dir_info = NULL;
3367 gboolean skip_subdirs = FALSE;
3368
3369 /* It is possible for this function to be called multiple times for
3370 * the same directory.
3371 * We pass a NULL SourceDirInfo into count_file() if this directory has
3372 * already been scanned once so that its children are not counted more
3373 * than once in the SourceDirInfo corresponding to this directory.
3374 */
3375
3376 if (!g_hash_table_contains (source_info->scanned_dirs_info, dir))
3377 {
3378 dir_info = g_new0 (SourceDirInfo, 1);
3379
3380 g_hash_table_insert (source_info->scanned_dirs_info,
3381 g_object_ref (dir),
3382 dir_info);
3383 }
3384
3385 /* Stash a copy of the struct to restore state before goto retry. Note that
3386 * this assumes the code below does not access any pointer member */
3387 saved_info = *source_info;
3388
3389 retry:
3390
3391 if (dir_info != NULL)
3392 {
3393 dir_info->num_files_children = 0;
3394 dir_info->num_bytes_children = 0;
3395 }
3396
3397 error = NULL;
3398 enumerator = g_file_enumerate_children (dir,
3399 G_FILE_ATTRIBUTE_STANDARD_NAME ","
3400 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3401 G_FILE_ATTRIBUTE_STANDARD_SIZE,
3402 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3403 job->cancellable,
3404 &error);
3405 if (enumerator)
3406 {
3407 error = NULL;
3408 while ((info = g_file_enumerator_next_file (enumerator, job->cancellable, &error)) != NULL)
3409 {
3410 count_file (info, job, source_info, dir_info);
3411
3412 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
3413 {
3414 subdir = g_file_get_child (dir,
3415 g_file_info_get_name (info));
3416
3417 subdirs = g_list_prepend (subdirs, subdir);
3418 }
3419
3420 g_object_unref (info);
3421 }
3422 g_file_enumerator_close (enumerator, job->cancellable, NULL);
3423 g_object_unref (enumerator);
3424
3425 if (error && IS_IO_ERROR (error, CANCELLED))
3426 {
3427 g_error_free (error);
3428 }
3429 else if (error)
3430 {
3431 g_autofree gchar *basename = NULL;
3432
3433 primary = get_scan_primary (source_info->op);
3434 details = NULL;
3435 basename = get_basename (dir);
3436
3437 if (IS_IO_ERROR (error, PERMISSION_DENIED))
3438 {
3439 secondary = g_strdup_printf (_("Files in the folder “%s” cannot be handled "
3440 "because you do not have permissions to see them."),
3441 basename);
3442 }
3443 else
3444 {
3445 secondary = g_strdup_printf (_("There was an error getting information about the "
3446 "files in the folder “%s”."), basename);
3447 details = error->message;
3448 }
3449
3450 response = run_warning (job,
3451 primary,
3452 secondary,
3453 details,
3454 FALSE,
3455 CANCEL, RETRY, SKIP,
3456 NULL);
3457
3458 g_error_free (error);
3459
3460 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
3461 {
3462 abort_job (job);
3463 skip_subdirs = TRUE;
3464 }
3465 else if (response == 1)
3466 {
3467 g_clear_list (&subdirs, g_object_unref);
3468 *source_info = saved_info;
3469 goto retry;
3470 }
3471 else if (response == 2)
3472 {
3473 skip_readdir_error (job, dir);
3474 }
3475 else
3476 {
3477 g_assert_not_reached ();
3478 }
3479 }
3480 }
3481 else if (job->skip_all_error)
3482 {
3483 g_error_free (error);
3484 skip_file (job, dir);
3485 skip_subdirs = TRUE;
3486 }
3487 else if (IS_IO_ERROR (error, CANCELLED))
3488 {
3489 g_error_free (error);
3490 }
3491 else
3492 {
3493 g_autofree gchar *basename = NULL;
3494
3495 primary = get_scan_primary (source_info->op);
3496 details = NULL;
3497 basename = get_basename (dir);
3498 if (IS_IO_ERROR (error, PERMISSION_DENIED))
3499 {
3500 secondary = g_strdup_printf (_("The folder “%s” cannot be handled because you "
3501 "do not have permissions to read it."),
3502 basename);
3503 }
3504 else
3505 {
3506 secondary = g_strdup_printf (_("There was an error reading the folder “%s”."),
3507 basename);
3508 details = error->message;
3509 }
3510 /* set show_all to TRUE here, as we don't know how many
3511 * files we'll end up processing yet.
3512 */
3513 response = run_warning (job,
3514 primary,
3515 secondary,
3516 details,
3517 TRUE,
3518 CANCEL, SKIP_ALL, SKIP, RETRY,
3519 NULL);
3520
3521 g_error_free (error);
3522
3523 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
3524 {
3525 abort_job (job);
3526 skip_subdirs = TRUE;
3527 }
3528 else if (response == 1 || response == 2)
3529 {
3530 if (response == 1)
3531 {
3532 job->skip_all_error = TRUE;
3533 }
3534 skip_file (job, dir);
3535 skip_subdirs = TRUE;
3536 }
3537 else if (response == 3)
3538 {
3539 goto retry;
3540 }
3541 else
3542 {
3543 g_assert_not_reached ();
3544 }
3545 }
3546
3547 if (!skip_subdirs)
3548 {
3549 while (subdirs != NULL)
3550 {
3551 GList *l = subdirs;
3552 subdirs = g_list_remove_link (subdirs, l);
3553
3554 /* Push to head, since we want depth-first */
3555 g_queue_push_head_link (dirs, l);
3556 }
3557 }
3558 }
3559
3560 static void
scan_file(GFile * file,SourceInfo * source_info,CommonJob * job)3561 scan_file (GFile *file,
3562 SourceInfo *source_info,
3563 CommonJob *job)
3564 {
3565 GFileInfo *info;
3566 GError *error;
3567 GQueue *dirs;
3568 GFile *dir;
3569 char *primary;
3570 char *secondary;
3571 char *details;
3572 int response;
3573
3574 dirs = g_queue_new ();
3575
3576 retry:
3577 error = NULL;
3578 info = g_file_query_info (file,
3579 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3580 G_FILE_ATTRIBUTE_STANDARD_SIZE,
3581 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3582 job->cancellable,
3583 &error);
3584
3585 if (info)
3586 {
3587 count_file (info, job, source_info, NULL);
3588
3589 /* trashing operation doesn't recurse */
3590 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY &&
3591 source_info->op != OP_KIND_TRASH)
3592 {
3593 g_queue_push_head (dirs, g_object_ref (file));
3594 }
3595 g_object_unref (info);
3596 }
3597 else if (job->skip_all_error)
3598 {
3599 g_error_free (error);
3600 skip_file (job, file);
3601 }
3602 else if (IS_IO_ERROR (error, CANCELLED))
3603 {
3604 g_error_free (error);
3605 }
3606 else
3607 {
3608 g_autofree gchar *basename = NULL;
3609
3610 primary = get_scan_primary (source_info->op);
3611 details = NULL;
3612 basename = get_basename (file);
3613
3614 if (IS_IO_ERROR (error, PERMISSION_DENIED))
3615 {
3616 secondary = g_strdup_printf (_("The file “%s” cannot be handled because you do not have "
3617 "permissions to read it."), basename);
3618 }
3619 else
3620 {
3621 secondary = g_strdup_printf (_("There was an error getting information about “%s”."),
3622 basename);
3623 details = error->message;
3624 }
3625 /* set show_all to TRUE here, as we don't know how many
3626 * files we'll end up processing yet.
3627 */
3628 response = run_warning (job,
3629 primary,
3630 secondary,
3631 details,
3632 TRUE,
3633 CANCEL, SKIP_ALL, SKIP, RETRY,
3634 NULL);
3635
3636 g_error_free (error);
3637
3638 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
3639 {
3640 abort_job (job);
3641 }
3642 else if (response == 1 || response == 2)
3643 {
3644 if (response == 1)
3645 {
3646 job->skip_all_error = TRUE;
3647 }
3648 skip_file (job, file);
3649 }
3650 else if (response == 3)
3651 {
3652 goto retry;
3653 }
3654 else
3655 {
3656 g_assert_not_reached ();
3657 }
3658 }
3659
3660 while (!job_aborted (job) &&
3661 (dir = g_queue_pop_head (dirs)) != NULL)
3662 {
3663 scan_dir (dir, source_info, job, dirs);
3664 g_object_unref (dir);
3665 }
3666
3667 /* Free all from queue if we exited early */
3668 g_queue_foreach (dirs, (GFunc) g_object_unref, NULL);
3669 g_queue_free (dirs);
3670 }
3671
3672 static void
scan_sources(GList * files,SourceInfo * source_info,CommonJob * job,OpKind kind)3673 scan_sources (GList *files,
3674 SourceInfo *source_info,
3675 CommonJob *job,
3676 OpKind kind)
3677 {
3678 GList *l;
3679 GFile *file;
3680
3681 source_info->op = kind;
3682 source_info->scanned_dirs_info = g_hash_table_new_full (g_file_hash,
3683 (GEqualFunc) g_file_equal,
3684 (GDestroyNotify) g_object_unref,
3685 (GDestroyNotify) g_free);
3686
3687 report_preparing_count_progress (job, source_info);
3688
3689 for (l = files; l != NULL && !job_aborted (job); l = l->next)
3690 {
3691 file = l->data;
3692
3693 scan_file (file,
3694 source_info,
3695 job);
3696 }
3697
3698 /* Make sure we report the final count */
3699 report_preparing_count_progress (job, source_info);
3700 }
3701
3702 static void
verify_destination(CommonJob * job,GFile * dest,char ** dest_fs_id,goffset required_size)3703 verify_destination (CommonJob *job,
3704 GFile *dest,
3705 char **dest_fs_id,
3706 goffset required_size)
3707 {
3708 GFileInfo *info, *fsinfo;
3709 GError *error;
3710 guint64 free_size;
3711 guint64 size_difference;
3712 char *primary, *secondary, *details;
3713 int response;
3714 GFileType file_type;
3715 gboolean dest_is_symlink = FALSE;
3716
3717 if (dest_fs_id)
3718 {
3719 *dest_fs_id = NULL;
3720 }
3721
3722 retry:
3723
3724 error = NULL;
3725 info = g_file_query_info (dest,
3726 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3727 G_FILE_ATTRIBUTE_ID_FILESYSTEM,
3728 dest_is_symlink ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3729 job->cancellable,
3730 &error);
3731
3732 if (info == NULL)
3733 {
3734 g_autofree gchar *basename = NULL;
3735
3736 if (IS_IO_ERROR (error, CANCELLED))
3737 {
3738 g_error_free (error);
3739 return;
3740 }
3741
3742 basename = get_basename (dest);
3743 primary = g_strdup_printf (_("Error while copying to “%s”."), basename);
3744 details = NULL;
3745
3746 if (IS_IO_ERROR (error, PERMISSION_DENIED))
3747 {
3748 secondary = g_strdup (_("You do not have permissions to access the destination folder."));
3749 }
3750 else
3751 {
3752 secondary = g_strdup (_("There was an error getting information about the destination."));
3753 details = error->message;
3754 }
3755
3756 response = run_error (job,
3757 primary,
3758 secondary,
3759 details,
3760 FALSE,
3761 CANCEL, RETRY,
3762 NULL);
3763
3764 g_error_free (error);
3765
3766 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
3767 {
3768 abort_job (job);
3769 }
3770 else if (response == 1)
3771 {
3772 goto retry;
3773 }
3774 else
3775 {
3776 g_assert_not_reached ();
3777 }
3778
3779 return;
3780 }
3781
3782 file_type = g_file_info_get_file_type (info);
3783 if (!dest_is_symlink && file_type == G_FILE_TYPE_SYMBOLIC_LINK)
3784 {
3785 /* Record that destination is a symlink and do real stat() once again */
3786 dest_is_symlink = TRUE;
3787 g_object_unref (info);
3788 goto retry;
3789 }
3790
3791 if (dest_fs_id)
3792 {
3793 *dest_fs_id =
3794 g_strdup (g_file_info_get_attribute_string (info,
3795 G_FILE_ATTRIBUTE_ID_FILESYSTEM));
3796 }
3797
3798 g_object_unref (info);
3799
3800 if (file_type != G_FILE_TYPE_DIRECTORY)
3801 {
3802 g_autofree gchar *basename = NULL;
3803
3804 basename = get_basename (dest);
3805 primary = g_strdup_printf (_("Error while copying to “%s”."), basename);
3806 secondary = g_strdup (_("The destination is not a folder."));
3807
3808 run_error (job,
3809 primary,
3810 secondary,
3811 NULL,
3812 FALSE,
3813 CANCEL,
3814 NULL);
3815
3816 abort_job (job);
3817 return;
3818 }
3819
3820 if (dest_is_symlink)
3821 {
3822 /* We can't reliably statfs() destination if it's a symlink, thus not doing any further checks. */
3823 return;
3824 }
3825
3826 fsinfo = g_file_query_filesystem_info (dest,
3827 G_FILE_ATTRIBUTE_FILESYSTEM_FREE ","
3828 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,
3829 job->cancellable,
3830 NULL);
3831 if (fsinfo == NULL)
3832 {
3833 /* All sorts of things can go wrong getting the fs info (like not supported)
3834 * only check these things if the fs returns them
3835 */
3836 return;
3837 }
3838
3839 if (required_size > 0 &&
3840 g_file_info_has_attribute (fsinfo, G_FILE_ATTRIBUTE_FILESYSTEM_FREE))
3841 {
3842 free_size = g_file_info_get_attribute_uint64 (fsinfo,
3843 G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
3844
3845 if (free_size < required_size)
3846 {
3847 g_autofree gchar *basename = NULL;
3848 g_autofree gchar *formatted_size = NULL;
3849
3850 basename = get_basename (dest);
3851 size_difference = required_size - free_size;
3852 primary = g_strdup_printf (_("Error while copying to “%s”."), basename);
3853 secondary = g_strdup (_("There is not enough space on the destination."
3854 " Try to remove files to make space."));
3855
3856 formatted_size = g_format_size (size_difference);
3857 details = g_strdup_printf (_("%s more space is required to copy to the destination."),
3858 formatted_size);
3859
3860 response = run_warning (job,
3861 primary,
3862 secondary,
3863 details,
3864 FALSE,
3865 CANCEL,
3866 COPY_FORCE,
3867 RETRY,
3868 NULL);
3869
3870 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
3871 {
3872 abort_job (job);
3873 }
3874 else if (response == 2)
3875 {
3876 goto retry;
3877 }
3878 else if (response == 1)
3879 {
3880 /* We are forced to copy - just fall through ... */
3881 }
3882 else
3883 {
3884 g_assert_not_reached ();
3885 }
3886 }
3887 }
3888
3889 if (!job_aborted (job) &&
3890 g_file_info_get_attribute_boolean (fsinfo,
3891 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
3892 {
3893 g_autofree gchar *basename = NULL;
3894
3895 basename = get_basename (dest);
3896 primary = g_strdup_printf (_("Error while copying to “%s”."), basename);
3897 secondary = g_strdup (_("The destination is read-only."));
3898
3899 run_error (job,
3900 primary,
3901 secondary,
3902 NULL,
3903 FALSE,
3904 CANCEL,
3905 NULL);
3906
3907 g_error_free (error);
3908
3909 abort_job (job);
3910 }
3911
3912 g_object_unref (fsinfo);
3913 }
3914
3915 #pragma GCC diagnostic push
3916 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
3917 static void
report_copy_progress(CopyMoveJob * copy_job,SourceInfo * source_info,TransferInfo * transfer_info)3918 report_copy_progress (CopyMoveJob *copy_job,
3919 SourceInfo *source_info,
3920 TransferInfo *transfer_info)
3921 {
3922 int files_left;
3923 goffset total_size;
3924 double elapsed, transfer_rate;
3925 int remaining_time;
3926 guint64 now;
3927 CommonJob *job;
3928 gboolean is_move;
3929 gchar *status;
3930 char *details;
3931 gchar *tmp;
3932
3933 job = (CommonJob *) copy_job;
3934
3935 is_move = copy_job->is_move;
3936
3937 now = g_get_monotonic_time ();
3938
3939 files_left = source_info->num_files - transfer_info->num_files;
3940
3941 /* Races and whatnot could cause this to be negative... */
3942 if (files_left < 0)
3943 {
3944 files_left = 0;
3945 }
3946
3947 /* If the number of files left is 0, we want to update the status without
3948 * considering this time, since we want to change the status to completed
3949 * and probably we won't get more calls to this function */
3950 if (transfer_info->last_report_time != 0 &&
3951 ABS ((gint64) (transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC &&
3952 files_left > 0)
3953 {
3954 return;
3955 }
3956 transfer_info->last_report_time = now;
3957
3958 if (files_left != transfer_info->last_reported_files_left ||
3959 transfer_info->last_reported_files_left == 0)
3960 {
3961 /* Avoid changing this unless files_left changed since last time */
3962 transfer_info->last_reported_files_left = files_left;
3963
3964 if (source_info->num_files == 1)
3965 {
3966 g_autofree gchar *basename_dest = NULL;
3967
3968 if (copy_job->destination != NULL)
3969 {
3970 if (is_move)
3971 {
3972 if (files_left > 0)
3973 {
3974 status = _("Moving “%s” to “%s”");
3975 }
3976 else
3977 {
3978 status = _("Moved “%s” to “%s”");
3979 }
3980 }
3981 else
3982 {
3983 if (files_left > 0)
3984 {
3985 status = _("Copying “%s” to “%s”");
3986 }
3987 else
3988 {
3989 status = _("Copied “%s” to “%s”");
3990 }
3991 }
3992
3993 basename_dest = get_basename (G_FILE (copy_job->destination));
3994
3995 if (copy_job->fake_display_source != NULL)
3996 {
3997 g_autofree gchar *basename_fake_display_source = NULL;
3998
3999 basename_fake_display_source = get_basename (copy_job->fake_display_source);
4000 tmp = g_strdup_printf (status,
4001 basename_fake_display_source,
4002 basename_dest);
4003 }
4004 else
4005 {
4006 g_autofree gchar *basename_data = NULL;
4007
4008 basename_data = get_basename (G_FILE (copy_job->files->data));
4009 tmp = g_strdup_printf (status,
4010 basename_data,
4011 basename_dest);
4012 }
4013
4014 nautilus_progress_info_take_status (job->progress,
4015 tmp);
4016 }
4017 else
4018 {
4019 g_autofree gchar *basename = NULL;
4020
4021 if (files_left > 0)
4022 {
4023 status = _("Duplicating “%s”");
4024 }
4025 else
4026 {
4027 status = _("Duplicated “%s”");
4028 }
4029
4030 basename = get_basename (G_FILE (copy_job->files->data));
4031 nautilus_progress_info_take_status (job->progress,
4032 g_strdup_printf (status,
4033 basename));
4034 }
4035 }
4036 else if (copy_job->files != NULL)
4037 {
4038 if (copy_job->destination != NULL)
4039 {
4040 if (files_left > 0)
4041 {
4042 g_autofree gchar *basename = NULL;
4043
4044 if (is_move)
4045 {
4046 status = ngettext ("Moving %'d file to “%s”",
4047 "Moving %'d files to “%s”",
4048 source_info->num_files);
4049 }
4050 else
4051 {
4052 status = ngettext ("Copying %'d file to “%s”",
4053 "Copying %'d files to “%s”",
4054 source_info->num_files);
4055 }
4056
4057 basename = get_basename (G_FILE (copy_job->destination));
4058 tmp = g_strdup_printf (status,
4059 source_info->num_files,
4060 basename);
4061
4062 nautilus_progress_info_take_status (job->progress,
4063 tmp);
4064 }
4065 else
4066 {
4067 g_autofree gchar *basename = NULL;
4068
4069 if (is_move)
4070 {
4071 status = ngettext ("Moved %'d file to “%s”",
4072 "Moved %'d files to “%s”",
4073 source_info->num_files);
4074 }
4075 else
4076 {
4077 status = ngettext ("Copied %'d file to “%s”",
4078 "Copied %'d files to “%s”",
4079 source_info->num_files);
4080 }
4081
4082 basename = get_basename (G_FILE (copy_job->destination));
4083 tmp = g_strdup_printf (status,
4084 source_info->num_files,
4085 basename);
4086
4087 nautilus_progress_info_take_status (job->progress,
4088 tmp);
4089 }
4090 }
4091 else
4092 {
4093 GFile *parent;
4094 g_autofree gchar *basename = NULL;
4095
4096 parent = g_file_get_parent (copy_job->files->data);
4097 basename = get_basename (parent);
4098 if (files_left > 0)
4099 {
4100 status = ngettext ("Duplicating %'d file in “%s”",
4101 "Duplicating %'d files in “%s”",
4102 source_info->num_files);
4103 nautilus_progress_info_take_status (job->progress,
4104 g_strdup_printf (status,
4105 source_info->num_files,
4106 basename));
4107 }
4108 else
4109 {
4110 status = ngettext ("Duplicated %'d file in “%s”",
4111 "Duplicated %'d files in “%s”",
4112 source_info->num_files);
4113 nautilus_progress_info_take_status (job->progress,
4114 g_strdup_printf (status,
4115 source_info->num_files,
4116 basename));
4117 }
4118 g_object_unref (parent);
4119 }
4120 }
4121 }
4122
4123 total_size = MAX (source_info->num_bytes, transfer_info->num_bytes);
4124
4125 elapsed = g_timer_elapsed (job->time, NULL);
4126 transfer_rate = 0;
4127 remaining_time = INT_MAX;
4128 if (elapsed > 0)
4129 {
4130 transfer_rate = transfer_info->num_bytes / elapsed;
4131 if (transfer_rate > 0)
4132 {
4133 remaining_time = (total_size - transfer_info->num_bytes) / transfer_rate;
4134 }
4135 }
4136
4137 if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE ||
4138 transfer_rate == 0 ||
4139 !transfer_info->partial_progress)
4140 {
4141 if (source_info->num_files == 1)
4142 {
4143 g_autofree gchar *formatted_size_num_bytes = NULL;
4144 g_autofree gchar *formatted_size_total_size = NULL;
4145
4146 formatted_size_num_bytes = g_format_size (transfer_info->num_bytes);
4147 formatted_size_total_size = g_format_size (total_size);
4148 /* To translators: %s will expand to a size like "2 bytes" or "3 MB", so something like "4 kb / 4 MB" */
4149 details = g_strdup_printf (_("%s / %s"),
4150 formatted_size_num_bytes,
4151 formatted_size_total_size);
4152 }
4153 else
4154 {
4155 if (files_left > 0)
4156 {
4157 /* To translators: %'d is the number of files completed for the operation,
4158 * so it will be something like 2/14. */
4159 details = g_strdup_printf (_("%'d / %'d"),
4160 transfer_info->num_files + 1,
4161 source_info->num_files);
4162 }
4163 else
4164 {
4165 /* To translators: %'d is the number of files completed for the operation,
4166 * so it will be something like 2/14. */
4167 details = g_strdup_printf (_("%'d / %'d"),
4168 transfer_info->num_files,
4169 source_info->num_files);
4170 }
4171 }
4172 }
4173 else
4174 {
4175 if (source_info->num_files == 1)
4176 {
4177 if (files_left > 0)
4178 {
4179 g_autofree gchar *formatted_time = NULL;
4180 g_autofree gchar *formatted_size_num_bytes = NULL;
4181 g_autofree gchar *formatted_size_total_size = NULL;
4182 g_autofree gchar *formatted_size_transfer_rate = NULL;
4183
4184 formatted_time = get_formatted_time (remaining_time);
4185 formatted_size_num_bytes = g_format_size (transfer_info->num_bytes);
4186 formatted_size_total_size = g_format_size (total_size);
4187 formatted_size_transfer_rate = g_format_size ((goffset) transfer_rate);
4188 /* To translators: %s will expand to a size like "2 bytes" or "3 MB", %s to a time duration like
4189 * "2 minutes". So the whole thing will be something like "2 kb / 4 MB -- 2 hours left (4kb/sec)"
4190 *
4191 * The singular/plural form will be used depending on the remaining time (i.e. the %s argument).
4192 */
4193 details = g_strdup_printf (ngettext ("%s / %s \xE2\x80\x94 %s left (%s/sec)",
4194 "%s / %s \xE2\x80\x94 %s left (%s/sec)",
4195 seconds_count_format_time_units (remaining_time)),
4196 formatted_size_num_bytes,
4197 formatted_size_total_size,
4198 formatted_time,
4199 formatted_size_transfer_rate);
4200 }
4201 else
4202 {
4203 g_autofree gchar *formatted_size_num_bytes = NULL;
4204 g_autofree gchar *formatted_size_total_size = NULL;
4205
4206 formatted_size_num_bytes = g_format_size (transfer_info->num_bytes);
4207 formatted_size_total_size = g_format_size (total_size);
4208 /* To translators: %s will expand to a size like "2 bytes" or "3 MB". */
4209 details = g_strdup_printf (_("%s / %s"),
4210 formatted_size_num_bytes,
4211 formatted_size_total_size);
4212 }
4213 }
4214 else
4215 {
4216 if (files_left > 0)
4217 {
4218 g_autofree gchar *formatted_time = NULL;
4219 g_autofree gchar *formatted_size = NULL;
4220 formatted_time = get_formatted_time (remaining_time);
4221 formatted_size = g_format_size ((goffset) transfer_rate);
4222 /* To translators: %s will expand to a time duration like "2 minutes".
4223 * So the whole thing will be something like "1 / 5 -- 2 hours left (4kb/sec)"
4224 *
4225 * The singular/plural form will be used depending on the remaining time (i.e. the %s argument).
4226 */
4227 details = g_strdup_printf (ngettext ("%'d / %'d \xE2\x80\x94 %s left (%s/sec)",
4228 "%'d / %'d \xE2\x80\x94 %s left (%s/sec)",
4229 seconds_count_format_time_units (remaining_time)),
4230 transfer_info->num_files + 1, source_info->num_files,
4231 formatted_time,
4232 formatted_size);
4233 }
4234 else
4235 {
4236 /* To translators: %'d is the number of files completed for the operation,
4237 * so it will be something like 2/14. */
4238 details = g_strdup_printf (_("%'d / %'d"),
4239 transfer_info->num_files,
4240 source_info->num_files);
4241 }
4242 }
4243 }
4244 nautilus_progress_info_take_details (job->progress, details);
4245
4246 if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE)
4247 {
4248 nautilus_progress_info_set_remaining_time (job->progress,
4249 remaining_time);
4250 nautilus_progress_info_set_elapsed_time (job->progress,
4251 elapsed);
4252 }
4253
4254 nautilus_progress_info_set_progress (job->progress, transfer_info->num_bytes, total_size);
4255 }
4256 #pragma GCC diagnostic pop
4257
4258 #define FAT_FORBIDDEN_CHARACTERS "/:*?\"<>\\|"
4259
4260 static gboolean
fat_str_replace(char * str,char replacement)4261 fat_str_replace (char *str,
4262 char replacement)
4263 {
4264 gboolean success;
4265 int i;
4266
4267 success = FALSE;
4268 for (i = 0; str[i] != '\0'; i++)
4269 {
4270 if (strchr (FAT_FORBIDDEN_CHARACTERS, str[i]) ||
4271 str[i] < 32)
4272 {
4273 success = TRUE;
4274 str[i] = replacement;
4275 }
4276 }
4277
4278 return success;
4279 }
4280
4281 static gboolean
make_file_name_valid_for_dest_fs(char * filename,const char * dest_fs_type)4282 make_file_name_valid_for_dest_fs (char *filename,
4283 const char *dest_fs_type)
4284 {
4285 if (dest_fs_type != NULL && filename != NULL)
4286 {
4287 if (!strcmp (dest_fs_type, "fat") ||
4288 !strcmp (dest_fs_type, "vfat") ||
4289 /* The fuseblk filesystem type could be of any type
4290 * in theory, but in practice is usually NTFS or exFAT.
4291 * This assumption is a pragmatic way to solve
4292 * https://gitlab.gnome.org/GNOME/nautilus/-/issues/1343 */
4293 !strcmp (dest_fs_type, "fuse") ||
4294 !strcmp (dest_fs_type, "ntfs") ||
4295 !strcmp (dest_fs_type, "msdos") ||
4296 !strcmp (dest_fs_type, "msdosfs"))
4297 {
4298 gboolean ret;
4299 int i, old_len;
4300
4301 ret = fat_str_replace (filename, '_');
4302
4303 old_len = strlen (filename);
4304 for (i = 0; i < old_len; i++)
4305 {
4306 if (filename[i] != ' ')
4307 {
4308 g_strchomp (filename);
4309 ret |= (old_len != strlen (filename));
4310 break;
4311 }
4312 }
4313
4314 return ret;
4315 }
4316 }
4317
4318 return FALSE;
4319 }
4320
4321 static GFile *
get_unique_target_file(GFile * src,GFile * dest_dir,gboolean same_fs,const char * dest_fs_type,int count)4322 get_unique_target_file (GFile *src,
4323 GFile *dest_dir,
4324 gboolean same_fs,
4325 const char *dest_fs_type,
4326 int count)
4327 {
4328 const char *editname, *end;
4329 char *basename, *new_name;
4330 GFileInfo *info;
4331 GFile *dest;
4332 int max_length;
4333 NautilusFile *file;
4334 gboolean ignore_extension;
4335
4336 max_length = nautilus_get_max_child_name_length_for_location (dest_dir);
4337
4338 file = nautilus_file_get (src);
4339 ignore_extension = nautilus_file_is_directory (file);
4340 nautilus_file_unref (file);
4341
4342 dest = NULL;
4343 info = g_file_query_info (src,
4344 G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME,
4345 0, NULL, NULL);
4346 if (info != NULL)
4347 {
4348 editname = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME);
4349
4350 if (editname != NULL)
4351 {
4352 new_name = get_duplicate_name (editname, count, max_length, ignore_extension);
4353 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
4354 dest = g_file_get_child_for_display_name (dest_dir, new_name, NULL);
4355 g_free (new_name);
4356 }
4357
4358 g_object_unref (info);
4359 }
4360
4361 if (dest == NULL)
4362 {
4363 basename = g_file_get_basename (src);
4364
4365 if (g_utf8_validate (basename, -1, NULL))
4366 {
4367 new_name = get_duplicate_name (basename, count, max_length, ignore_extension);
4368 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
4369 dest = g_file_get_child_for_display_name (dest_dir, new_name, NULL);
4370 g_free (new_name);
4371 }
4372
4373 if (dest == NULL)
4374 {
4375 end = strrchr (basename, '.');
4376 if (end != NULL)
4377 {
4378 count += atoi (end + 1);
4379 }
4380 new_name = g_strdup_printf ("%s.%d", basename, count);
4381 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
4382 dest = g_file_get_child (dest_dir, new_name);
4383 g_free (new_name);
4384 }
4385
4386 g_free (basename);
4387 }
4388
4389 return dest;
4390 }
4391
4392 static GFile *
get_target_file_for_link(GFile * src,GFile * dest_dir,const char * dest_fs_type,int count)4393 get_target_file_for_link (GFile *src,
4394 GFile *dest_dir,
4395 const char *dest_fs_type,
4396 int count)
4397 {
4398 const char *editname;
4399 char *basename, *new_name;
4400 GFileInfo *info;
4401 GFile *dest;
4402 int max_length;
4403
4404 max_length = nautilus_get_max_child_name_length_for_location (dest_dir);
4405
4406 dest = NULL;
4407 info = g_file_query_info (src,
4408 G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME,
4409 0, NULL, NULL);
4410 if (info != NULL)
4411 {
4412 editname = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME);
4413
4414 if (editname != NULL)
4415 {
4416 new_name = get_link_name (editname, count, max_length);
4417 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
4418 dest = g_file_get_child_for_display_name (dest_dir, new_name, NULL);
4419 g_free (new_name);
4420 }
4421
4422 g_object_unref (info);
4423 }
4424
4425 if (dest == NULL)
4426 {
4427 basename = g_file_get_basename (src);
4428 make_file_name_valid_for_dest_fs (basename, dest_fs_type);
4429
4430 if (g_utf8_validate (basename, -1, NULL))
4431 {
4432 new_name = get_link_name (basename, count, max_length);
4433 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
4434 dest = g_file_get_child_for_display_name (dest_dir, new_name, NULL);
4435 g_free (new_name);
4436 }
4437
4438 if (dest == NULL)
4439 {
4440 if (count == 1)
4441 {
4442 new_name = g_strdup_printf ("%s.lnk", basename);
4443 }
4444 else
4445 {
4446 new_name = g_strdup_printf ("%s.lnk%d", basename, count);
4447 }
4448 make_file_name_valid_for_dest_fs (new_name, dest_fs_type);
4449 dest = g_file_get_child (dest_dir, new_name);
4450 g_free (new_name);
4451 }
4452
4453 g_free (basename);
4454 }
4455
4456 return dest;
4457 }
4458
4459 static GFile *
get_target_file_with_custom_name(GFile * src,GFile * dest_dir,const char * dest_fs_type,gboolean same_fs,const gchar * custom_name)4460 get_target_file_with_custom_name (GFile *src,
4461 GFile *dest_dir,
4462 const char *dest_fs_type,
4463 gboolean same_fs,
4464 const gchar *custom_name)
4465 {
4466 char *basename;
4467 GFile *dest;
4468 GFileInfo *info;
4469 char *copyname;
4470
4471 dest = NULL;
4472
4473 if (custom_name != NULL)
4474 {
4475 copyname = g_strdup (custom_name);
4476 make_file_name_valid_for_dest_fs (copyname, dest_fs_type);
4477 dest = g_file_get_child_for_display_name (dest_dir, copyname, NULL);
4478
4479 g_free (copyname);
4480 }
4481
4482 if (dest == NULL && !same_fs)
4483 {
4484 info = g_file_query_info (src,
4485 G_FILE_ATTRIBUTE_STANDARD_COPY_NAME ","
4486 G_FILE_ATTRIBUTE_TRASH_ORIG_PATH,
4487 0, NULL, NULL);
4488
4489 if (info)
4490 {
4491 copyname = NULL;
4492
4493 /* if file is being restored from trash make sure it uses its original name */
4494 if (g_file_has_uri_scheme (src, "trash"))
4495 {
4496 copyname = g_path_get_basename (g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH));
4497 }
4498
4499 if (copyname == NULL)
4500 {
4501 copyname = g_strdup (g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME));
4502 }
4503
4504 if (copyname)
4505 {
4506 make_file_name_valid_for_dest_fs (copyname, dest_fs_type);
4507 dest = g_file_get_child_for_display_name (dest_dir, copyname, NULL);
4508 g_free (copyname);
4509 }
4510
4511 g_object_unref (info);
4512 }
4513 }
4514
4515 if (dest == NULL)
4516 {
4517 basename = g_file_get_basename (src);
4518 make_file_name_valid_for_dest_fs (basename, dest_fs_type);
4519 dest = g_file_get_child (dest_dir, basename);
4520 g_free (basename);
4521 }
4522
4523 return dest;
4524 }
4525
4526 static GFile *
get_target_file(GFile * src,GFile * dest_dir,const char * dest_fs_type,gboolean same_fs)4527 get_target_file (GFile *src,
4528 GFile *dest_dir,
4529 const char *dest_fs_type,
4530 gboolean same_fs)
4531 {
4532 return get_target_file_with_custom_name (src, dest_dir, dest_fs_type, same_fs, NULL);
4533 }
4534
4535 static gboolean
has_fs_id(GFile * file,const char * fs_id)4536 has_fs_id (GFile *file,
4537 const char *fs_id)
4538 {
4539 const char *id;
4540 GFileInfo *info;
4541 gboolean res;
4542
4543 res = FALSE;
4544 info = g_file_query_info (file,
4545 G_FILE_ATTRIBUTE_ID_FILESYSTEM,
4546 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
4547 NULL, NULL);
4548
4549 if (info)
4550 {
4551 id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
4552
4553 if (id && strcmp (id, fs_id) == 0)
4554 {
4555 res = TRUE;
4556 }
4557
4558 g_object_unref (info);
4559 }
4560
4561 return res;
4562 }
4563
4564 static gboolean
is_dir(GFile * file)4565 is_dir (GFile *file)
4566 {
4567 GFileType file_type;
4568
4569 file_type = g_file_query_file_type (file,
4570 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
4571 NULL);
4572
4573 return file_type == G_FILE_TYPE_DIRECTORY;
4574 }
4575
4576 static GFile *
map_possibly_volatile_file_to_real(GFile * volatile_file,GCancellable * cancellable,GError ** error)4577 map_possibly_volatile_file_to_real (GFile *volatile_file,
4578 GCancellable *cancellable,
4579 GError **error)
4580 {
4581 GFile *real_file = NULL;
4582 GFileInfo *info = NULL;
4583
4584 info = g_file_query_info (volatile_file,
4585 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
4586 G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE ","
4587 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
4588 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
4589 cancellable,
4590 error);
4591 if (info == NULL)
4592 {
4593 return NULL;
4594 }
4595 else
4596 {
4597 gboolean is_volatile;
4598
4599 is_volatile = g_file_info_get_attribute_boolean (info,
4600 G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE);
4601 if (is_volatile)
4602 {
4603 const gchar *target;
4604
4605 target = g_file_info_get_symlink_target (info);
4606 real_file = g_file_resolve_relative_path (volatile_file, target);
4607 }
4608 }
4609
4610 g_object_unref (info);
4611
4612 if (real_file == NULL)
4613 {
4614 real_file = g_object_ref (volatile_file);
4615 }
4616
4617 return real_file;
4618 }
4619
4620 static GFile *
map_possibly_volatile_file_to_real_on_write(GFile * volatile_file,GFileOutputStream * stream,GCancellable * cancellable,GError ** error)4621 map_possibly_volatile_file_to_real_on_write (GFile *volatile_file,
4622 GFileOutputStream *stream,
4623 GCancellable *cancellable,
4624 GError **error)
4625 {
4626 GFile *real_file = NULL;
4627 GFileInfo *info = NULL;
4628
4629 info = g_file_output_stream_query_info (stream,
4630 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK ","
4631 G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE ","
4632 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
4633 cancellable,
4634 error);
4635 if (info == NULL)
4636 {
4637 return NULL;
4638 }
4639 else
4640 {
4641 gboolean is_volatile;
4642
4643 is_volatile = g_file_info_get_attribute_boolean (info,
4644 G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE);
4645 if (is_volatile)
4646 {
4647 const gchar *target;
4648
4649 target = g_file_info_get_symlink_target (info);
4650 real_file = g_file_resolve_relative_path (volatile_file, target);
4651 }
4652 }
4653
4654 g_object_unref (info);
4655
4656 if (real_file == NULL)
4657 {
4658 real_file = g_object_ref (volatile_file);
4659 }
4660
4661 return real_file;
4662 }
4663
4664 static void copy_move_file (CopyMoveJob *job,
4665 GFile *src,
4666 GFile *dest_dir,
4667 gboolean same_fs,
4668 gboolean unique_names,
4669 char **dest_fs_type,
4670 SourceInfo *source_info,
4671 TransferInfo *transfer_info,
4672 GHashTable *debuting_files,
4673 gboolean overwrite,
4674 gboolean *skipped_file,
4675 gboolean readonly_source_fs);
4676
4677 typedef enum
4678 {
4679 CREATE_DEST_DIR_RETRY,
4680 CREATE_DEST_DIR_FAILED,
4681 CREATE_DEST_DIR_SUCCESS
4682 } CreateDestDirResult;
4683
4684 static CreateDestDirResult
create_dest_dir(CommonJob * job,GFile * src,GFile ** dest,gboolean same_fs,char ** dest_fs_type)4685 create_dest_dir (CommonJob *job,
4686 GFile *src,
4687 GFile **dest,
4688 gboolean same_fs,
4689 char **dest_fs_type)
4690 {
4691 GError *error;
4692 GFile *new_dest, *dest_dir;
4693 char *primary, *secondary, *details;
4694 int response;
4695 gboolean handled_invalid_filename;
4696 gboolean res;
4697
4698 handled_invalid_filename = *dest_fs_type != NULL;
4699
4700 retry:
4701 /* First create the directory, then copy stuff to it before
4702 * copying the attributes, because we need to be sure we can write to it */
4703
4704 error = NULL;
4705 res = g_file_make_directory (*dest, job->cancellable, &error);
4706
4707 if (res)
4708 {
4709 GFile *real;
4710
4711 real = map_possibly_volatile_file_to_real (*dest, job->cancellable, &error);
4712 if (real == NULL)
4713 {
4714 res = FALSE;
4715 }
4716 else
4717 {
4718 g_object_unref (*dest);
4719 *dest = real;
4720 }
4721 }
4722
4723 if (!res)
4724 {
4725 g_autofree gchar *basename = NULL;
4726
4727 if (IS_IO_ERROR (error, CANCELLED))
4728 {
4729 g_error_free (error);
4730 return CREATE_DEST_DIR_FAILED;
4731 }
4732 else if (IS_IO_ERROR (error, INVALID_FILENAME) &&
4733 !handled_invalid_filename)
4734 {
4735 handled_invalid_filename = TRUE;
4736
4737 g_assert (*dest_fs_type == NULL);
4738
4739 dest_dir = g_file_get_parent (*dest);
4740
4741 if (dest_dir != NULL)
4742 {
4743 *dest_fs_type = query_fs_type (dest_dir, job->cancellable);
4744
4745 new_dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
4746 g_object_unref (dest_dir);
4747
4748 if (!g_file_equal (*dest, new_dest))
4749 {
4750 g_object_unref (*dest);
4751 *dest = new_dest;
4752 g_error_free (error);
4753 return CREATE_DEST_DIR_RETRY;
4754 }
4755 else
4756 {
4757 g_object_unref (new_dest);
4758 }
4759 }
4760 }
4761
4762 primary = g_strdup (_("Error while copying."));
4763 details = NULL;
4764 basename = get_basename (src);
4765
4766 if (IS_IO_ERROR (error, PERMISSION_DENIED))
4767 {
4768 secondary = g_strdup_printf (_("The folder “%s” cannot be copied because you do not "
4769 "have permissions to create it in the destination."),
4770 basename);
4771 }
4772 else
4773 {
4774 secondary = g_strdup_printf (_("There was an error creating the folder “%s”."),
4775 basename);
4776 details = error->message;
4777 }
4778
4779 response = run_warning (job,
4780 primary,
4781 secondary,
4782 details,
4783 FALSE,
4784 CANCEL, SKIP, RETRY,
4785 NULL);
4786
4787 g_error_free (error);
4788
4789 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
4790 {
4791 abort_job (job);
4792 }
4793 else if (response == 1)
4794 {
4795 /* Skip: Do Nothing */
4796 }
4797 else if (response == 2)
4798 {
4799 goto retry;
4800 }
4801 else
4802 {
4803 g_assert_not_reached ();
4804 }
4805 return CREATE_DEST_DIR_FAILED;
4806 }
4807 nautilus_file_changes_queue_file_added (*dest);
4808
4809 if (job->undo_info != NULL)
4810 {
4811 nautilus_file_undo_info_ext_add_origin_target_pair (NAUTILUS_FILE_UNDO_INFO_EXT (job->undo_info),
4812 src, *dest);
4813 }
4814
4815 return CREATE_DEST_DIR_SUCCESS;
4816 }
4817
4818 /* a return value of FALSE means retry, i.e.
4819 * the destination has changed and the source
4820 * is expected to re-try the preceding
4821 * g_file_move() or g_file_copy() call with
4822 * the new destination.
4823 */
4824 static gboolean
copy_move_directory(CopyMoveJob * copy_job,GFile * src,GFile ** dest,gboolean same_fs,gboolean create_dest,char ** parent_dest_fs_type,SourceInfo * source_info,TransferInfo * transfer_info,GHashTable * debuting_files,gboolean * skipped_file,gboolean readonly_source_fs)4825 copy_move_directory (CopyMoveJob *copy_job,
4826 GFile *src,
4827 GFile **dest,
4828 gboolean same_fs,
4829 gboolean create_dest,
4830 char **parent_dest_fs_type,
4831 SourceInfo *source_info,
4832 TransferInfo *transfer_info,
4833 GHashTable *debuting_files,
4834 gboolean *skipped_file,
4835 gboolean readonly_source_fs)
4836 {
4837 g_autoptr (GFileInfo) src_info = NULL;
4838 GFileInfo *info;
4839 GError *error;
4840 GFile *src_file;
4841 GFileEnumerator *enumerator;
4842 char *primary, *secondary, *details;
4843 char *dest_fs_type;
4844 int response;
4845 gboolean skip_error;
4846 gboolean local_skipped_file;
4847 CommonJob *job;
4848 GFileCopyFlags flags;
4849
4850 job = (CommonJob *) copy_job;
4851 *skipped_file = FALSE;
4852
4853 if (create_dest)
4854 {
4855 g_autofree char *attrs_to_read = NULL;
4856
4857 switch (create_dest_dir (job, src, dest, same_fs, parent_dest_fs_type))
4858 {
4859 case CREATE_DEST_DIR_RETRY:
4860 {
4861 /* next time copy_move_directory() is called,
4862 * create_dest will be FALSE if a directory already
4863 * exists under the new name (i.e. WOULD_RECURSE)
4864 */
4865 return FALSE;
4866 }
4867
4868 case CREATE_DEST_DIR_FAILED:
4869 {
4870 *skipped_file = TRUE;
4871 return TRUE;
4872 }
4873
4874 case CREATE_DEST_DIR_SUCCESS:
4875 default:
4876 {
4877 }
4878 break;
4879 }
4880
4881 if (debuting_files)
4882 {
4883 g_hash_table_replace (debuting_files, g_object_ref (*dest), GINT_TO_POINTER (TRUE));
4884 }
4885
4886 flags = G_FILE_COPY_NOFOLLOW_SYMLINKS;
4887 if (readonly_source_fs)
4888 {
4889 flags |= G_FILE_COPY_TARGET_DEFAULT_PERMS;
4890 }
4891 if (copy_job->is_move)
4892 {
4893 flags |= G_FILE_COPY_ALL_METADATA;
4894 }
4895
4896 /* Ignore errors here. Failure to copy metadata is not a hard error */
4897 attrs_to_read = g_file_build_attribute_list_for_copy (*dest, flags, job->cancellable, NULL);
4898 if (attrs_to_read != NULL)
4899 {
4900 src_info = g_file_query_info (src, attrs_to_read, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, job->cancellable, NULL);
4901 }
4902 }
4903
4904 local_skipped_file = FALSE;
4905 dest_fs_type = NULL;
4906
4907 skip_error = should_skip_readdir_error (job, src);
4908 retry:
4909 error = NULL;
4910 enumerator = g_file_enumerate_children (src,
4911 G_FILE_ATTRIBUTE_STANDARD_NAME,
4912 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
4913 job->cancellable,
4914 &error);
4915 if (enumerator)
4916 {
4917 error = NULL;
4918
4919 while (!job_aborted (job) &&
4920 (info = g_file_enumerator_next_file (enumerator, job->cancellable, skip_error ? NULL : &error)) != NULL)
4921 {
4922 src_file = g_file_get_child (src,
4923 g_file_info_get_name (info));
4924 copy_move_file (copy_job, src_file, *dest, same_fs, FALSE, &dest_fs_type,
4925 source_info, transfer_info, NULL, FALSE, &local_skipped_file,
4926 readonly_source_fs);
4927
4928 if (local_skipped_file)
4929 {
4930 source_info_remove_file_from_count (src_file, job, source_info);
4931 report_copy_progress (copy_job, source_info, transfer_info);
4932 }
4933
4934 g_object_unref (src_file);
4935 g_object_unref (info);
4936 }
4937 g_file_enumerator_close (enumerator, job->cancellable, NULL);
4938 g_object_unref (enumerator);
4939
4940 if (error && IS_IO_ERROR (error, CANCELLED))
4941 {
4942 g_error_free (error);
4943 }
4944 else if (error)
4945 {
4946 g_autofree gchar *basename = NULL;
4947
4948 if (copy_job->is_move)
4949 {
4950 primary = g_strdup (_("Error while moving."));
4951 }
4952 else
4953 {
4954 primary = g_strdup (_("Error while copying."));
4955 }
4956 details = NULL;
4957 basename = get_basename (src);
4958
4959 if (IS_IO_ERROR (error, PERMISSION_DENIED))
4960 {
4961 secondary = g_strdup_printf (_("Files in the folder “%s” cannot be copied because you do "
4962 "not have permissions to see them."), basename);
4963 }
4964 else
4965 {
4966 secondary = g_strdup_printf (_("There was an error getting information about "
4967 "the files in the folder “%s”."),
4968 basename);
4969 details = error->message;
4970 }
4971
4972 response = run_warning (job,
4973 primary,
4974 secondary,
4975 details,
4976 FALSE,
4977 CANCEL, _("_Skip files"),
4978 NULL);
4979
4980 g_error_free (error);
4981
4982 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
4983 {
4984 abort_job (job);
4985 }
4986 else if (response == 1)
4987 {
4988 /* Skip: Do Nothing */
4989 local_skipped_file = TRUE;
4990 }
4991 else
4992 {
4993 g_assert_not_reached ();
4994 }
4995 }
4996
4997 /* Count the copied directory as a file */
4998 transfer_info->num_files++;
4999
5000 info = g_file_query_info (src,
5001 G_FILE_ATTRIBUTE_STANDARD_SIZE,
5002 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
5003 job->cancellable,
5004 NULL);
5005
5006 g_warn_if_fail (info != NULL);
5007
5008 if (info != NULL)
5009 {
5010 transfer_info->num_bytes += g_file_info_get_size (info);
5011
5012 g_object_unref (info);
5013 }
5014
5015 report_copy_progress (copy_job, source_info, transfer_info);
5016
5017 if (debuting_files)
5018 {
5019 g_hash_table_replace (debuting_files, g_object_ref (*dest), GINT_TO_POINTER (create_dest));
5020 }
5021 }
5022 else if (IS_IO_ERROR (error, CANCELLED))
5023 {
5024 g_error_free (error);
5025 }
5026 else
5027 {
5028 g_autofree gchar *basename = NULL;
5029
5030 if (copy_job->is_move)
5031 {
5032 primary = g_strdup (_("Error while moving."));
5033 }
5034 else
5035 {
5036 primary = g_strdup (_("Error while copying."));
5037 }
5038 details = NULL;
5039 basename = get_basename (src);
5040
5041 if (IS_IO_ERROR (error, PERMISSION_DENIED))
5042 {
5043 secondary = g_strdup_printf (_("The folder “%s” cannot be copied because you do not have "
5044 "permissions to read it."), basename);
5045 }
5046 else
5047 {
5048 secondary = g_strdup_printf (_("There was an error reading the folder “%s”."),
5049 basename);
5050
5051 details = error->message;
5052 }
5053
5054 response = run_warning (job,
5055 primary,
5056 secondary,
5057 details,
5058 FALSE,
5059 CANCEL, SKIP, RETRY,
5060 NULL);
5061
5062 g_error_free (error);
5063
5064 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
5065 {
5066 abort_job (job);
5067 }
5068 else if (response == 1)
5069 {
5070 /* Skip: Do Nothing */
5071 *skipped_file = TRUE;
5072 }
5073 else if (response == 2)
5074 {
5075 goto retry;
5076 }
5077 else
5078 {
5079 g_assert_not_reached ();
5080 }
5081 }
5082
5083 if (src_info != NULL)
5084 {
5085 /* Ignore errors here. Failure to copy metadata is not a hard error */
5086 g_file_set_attributes_from_info (*dest,
5087 src_info,
5088 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
5089 job->cancellable,
5090 NULL);
5091 }
5092
5093 if (!job_aborted (job) && copy_job->is_move &&
5094 /* Don't delete source if there was a skipped file */
5095 !*skipped_file &&
5096 !local_skipped_file)
5097 {
5098 if (!g_file_delete (src, job->cancellable, &error))
5099 {
5100 g_autofree gchar *basename = NULL;
5101
5102 if (job->skip_all_error)
5103 {
5104 *skipped_file = TRUE;
5105 goto skip;
5106 }
5107 basename = get_basename (src);
5108 primary = g_strdup_printf (_("Error while moving “%s”."), basename);
5109 secondary = g_strdup (_("Could not remove the source folder."));
5110 details = error->message;
5111
5112 response = run_cancel_or_skip_warning (job,
5113 primary,
5114 secondary,
5115 details,
5116 source_info->num_files,
5117 source_info->num_files - transfer_info->num_files);
5118
5119 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
5120 {
5121 abort_job (job);
5122 }
5123 else if (response == 1) /* skip all */
5124 {
5125 job->skip_all_error = TRUE;
5126 *skipped_file = TRUE;
5127 }
5128 else if (response == 2) /* skip */
5129 {
5130 *skipped_file = TRUE;
5131 }
5132 else
5133 {
5134 g_assert_not_reached ();
5135 }
5136
5137 skip:
5138 g_error_free (error);
5139 }
5140 }
5141
5142 g_free (dest_fs_type);
5143 return TRUE;
5144 }
5145
5146
5147 typedef struct
5148 {
5149 CommonJob *job;
5150 GFile *source;
5151 } DeleteExistingFileData;
5152
5153 typedef struct
5154 {
5155 CopyMoveJob *job;
5156 goffset last_size;
5157 SourceInfo *source_info;
5158 TransferInfo *transfer_info;
5159 } ProgressData;
5160
5161 static void
copy_file_progress_callback(goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)5162 copy_file_progress_callback (goffset current_num_bytes,
5163 goffset total_num_bytes,
5164 gpointer user_data)
5165 {
5166 ProgressData *pdata;
5167 goffset new_size;
5168
5169 pdata = user_data;
5170
5171 if (current_num_bytes != 0 &&
5172 current_num_bytes != total_num_bytes)
5173 {
5174 pdata->transfer_info->partial_progress = TRUE;
5175 }
5176
5177 new_size = current_num_bytes - pdata->last_size;
5178
5179 if (new_size > 0)
5180 {
5181 pdata->transfer_info->num_bytes += new_size;
5182 pdata->last_size = current_num_bytes;
5183 report_copy_progress (pdata->job,
5184 pdata->source_info,
5185 pdata->transfer_info);
5186 }
5187 }
5188
5189 static gboolean
test_dir_is_parent(GFile * child,GFile * root)5190 test_dir_is_parent (GFile *child,
5191 GFile *root)
5192 {
5193 GFile *f, *tmp;
5194
5195 f = g_file_dup (child);
5196 while (f)
5197 {
5198 if (g_file_equal (f, root))
5199 {
5200 g_object_unref (f);
5201 return TRUE;
5202 }
5203 tmp = f;
5204 f = g_file_get_parent (f);
5205 g_object_unref (tmp);
5206 }
5207 if (f)
5208 {
5209 g_object_unref (f);
5210 }
5211 return FALSE;
5212 }
5213
5214 static char *
query_fs_type(GFile * file,GCancellable * cancellable)5215 query_fs_type (GFile *file,
5216 GCancellable *cancellable)
5217 {
5218 GFileInfo *fsinfo;
5219 char *ret;
5220
5221 ret = NULL;
5222
5223 fsinfo = g_file_query_filesystem_info (file,
5224 G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
5225 cancellable,
5226 NULL);
5227 if (fsinfo != NULL)
5228 {
5229 ret = g_strdup (g_file_info_get_attribute_string (fsinfo, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE));
5230 g_object_unref (fsinfo);
5231 }
5232
5233 if (ret == NULL)
5234 {
5235 /* ensure that we don't attempt to query
5236 * the FS type for each file in a given
5237 * directory, if it can't be queried. */
5238 ret = g_strdup ("");
5239 }
5240
5241 return ret;
5242 }
5243
5244 static FileConflictResponse *
handle_copy_move_conflict(CommonJob * job,GFile * src,GFile * dest,GFile * dest_dir)5245 handle_copy_move_conflict (CommonJob *job,
5246 GFile *src,
5247 GFile *dest,
5248 GFile *dest_dir)
5249 {
5250 FileConflictResponse *response;
5251 g_autofree gchar *basename = NULL;
5252 g_autoptr (GFile) suggested_file = NULL;
5253 g_autofree gchar *suggestion = NULL;
5254
5255 g_timer_stop (job->time);
5256 nautilus_progress_info_pause (job->progress);
5257
5258 basename = g_file_get_basename (dest);
5259 suggested_file = nautilus_generate_unique_file_in_directory (dest_dir, basename);
5260 suggestion = g_file_get_basename (suggested_file);
5261
5262 response = copy_move_conflict_ask_user_action (job->parent_window,
5263 src,
5264 dest,
5265 dest_dir,
5266 suggestion);
5267
5268 nautilus_progress_info_resume (job->progress);
5269 g_timer_continue (job->time);
5270
5271 return response;
5272 }
5273
5274 static GFile *
get_target_file_for_display_name(GFile * dir,const gchar * name)5275 get_target_file_for_display_name (GFile *dir,
5276 const gchar *name)
5277 {
5278 GFile *dest;
5279
5280 dest = NULL;
5281 dest = g_file_get_child_for_display_name (dir, name, NULL);
5282
5283 if (dest == NULL)
5284 {
5285 dest = g_file_get_child (dir, name);
5286 }
5287
5288 return dest;
5289 }
5290
5291 /* This is a workaround to resolve broken conflict dialog for google-drive
5292 * locations. This is needed to provide POSIX-like behavior for google-drive
5293 * filesystem, where each file has an unique identificator that is not tied to
5294 * its display_name. See the following MR for more details:
5295 * https://gitlab.gnome.org/GNOME/nautilus/merge_requests/514.
5296 */
5297 static GFile *
get_target_file_from_source_display_name(CopyMoveJob * copy_job,GFile * src,GFile * dir)5298 get_target_file_from_source_display_name (CopyMoveJob *copy_job,
5299 GFile *src,
5300 GFile *dir)
5301 {
5302 CommonJob *job;
5303 g_autoptr (GError) error = NULL;
5304 g_autoptr (GFileInfo) info = NULL;
5305 gchar *primary, *secondary;
5306 GFile *dest = NULL;
5307
5308 job = (CommonJob *) copy_job;
5309
5310 info = g_file_query_info (src, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, 0, NULL, &error);
5311 if (info == NULL)
5312 {
5313 if (copy_job->is_move)
5314 {
5315 primary = g_strdup (_("Error while moving."));
5316 }
5317 else
5318 {
5319 primary = g_strdup (_("Error while copying."));
5320 }
5321 secondary = g_strdup (_("There was an error getting information about the source."));
5322
5323 run_error (job,
5324 primary,
5325 secondary,
5326 error->message,
5327 FALSE,
5328 CANCEL,
5329 NULL);
5330
5331 abort_job (job);
5332 }
5333 else
5334 {
5335 dest = get_target_file_for_display_name (dir, g_file_info_get_display_name (info));
5336 }
5337
5338 return dest;
5339 }
5340
5341
5342 /* Debuting files is non-NULL only for toplevel items */
5343 static void
copy_move_file(CopyMoveJob * copy_job,GFile * src,GFile * dest_dir,gboolean same_fs,gboolean unique_names,char ** dest_fs_type,SourceInfo * source_info,TransferInfo * transfer_info,GHashTable * debuting_files,gboolean overwrite,gboolean * skipped_file,gboolean readonly_source_fs)5344 copy_move_file (CopyMoveJob *copy_job,
5345 GFile *src,
5346 GFile *dest_dir,
5347 gboolean same_fs,
5348 gboolean unique_names,
5349 char **dest_fs_type,
5350 SourceInfo *source_info,
5351 TransferInfo *transfer_info,
5352 GHashTable *debuting_files,
5353 gboolean overwrite,
5354 gboolean *skipped_file,
5355 gboolean readonly_source_fs)
5356 {
5357 GFile *dest, *new_dest;
5358 g_autofree gchar *dest_uri = NULL;
5359 GError *error;
5360 GFileCopyFlags flags;
5361 char *primary, *secondary, *details;
5362 ProgressData pdata;
5363 gboolean would_recurse;
5364 CommonJob *job;
5365 gboolean res;
5366 int unique_name_nr;
5367 gboolean handled_invalid_filename;
5368
5369 job = (CommonJob *) copy_job;
5370
5371 *skipped_file = FALSE;
5372
5373 if (should_skip_file (job, src))
5374 {
5375 *skipped_file = TRUE;
5376 return;
5377 }
5378
5379 unique_name_nr = 1;
5380
5381 /* another file in the same directory might have handled the invalid
5382 * filename condition for us
5383 */
5384 handled_invalid_filename = *dest_fs_type != NULL;
5385
5386 if (unique_names)
5387 {
5388 dest = get_unique_target_file (src, dest_dir, same_fs, *dest_fs_type, unique_name_nr++);
5389 }
5390 else if (copy_job->target_name != NULL)
5391 {
5392 dest = get_target_file_with_custom_name (src, dest_dir, *dest_fs_type, same_fs,
5393 copy_job->target_name);
5394 }
5395 else if (g_file_has_uri_scheme (src, "google-drive") &&
5396 g_file_has_uri_scheme (dest_dir, "google-drive"))
5397 {
5398 dest = get_target_file_from_source_display_name (copy_job, src, dest_dir);
5399 if (dest == NULL)
5400 {
5401 *skipped_file = TRUE;
5402 return;
5403 }
5404 }
5405 else
5406 {
5407 dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
5408 }
5409
5410 /* Don't allow recursive move/copy into itself.
5411 * (We would get a file system error if we proceeded but it is nicer to
5412 * detect and report it at this level) */
5413 if (test_dir_is_parent (dest_dir, src))
5414 {
5415 int response;
5416
5417 if (job->skip_all_error)
5418 {
5419 goto out;
5420 }
5421
5422 /* the run_warning() frees all strings passed in automatically */
5423 primary = copy_job->is_move ? g_strdup (_("You cannot move a folder into itself."))
5424 : g_strdup (_("You cannot copy a folder into itself."));
5425 secondary = g_strdup (_("The destination folder is inside the source folder."));
5426
5427 response = run_cancel_or_skip_warning (job,
5428 primary,
5429 secondary,
5430 NULL,
5431 source_info->num_files,
5432 source_info->num_files - transfer_info->num_files);
5433
5434 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
5435 {
5436 abort_job (job);
5437 }
5438 else if (response == 1) /* skip all */
5439 {
5440 job->skip_all_error = TRUE;
5441 }
5442 else if (response == 2) /* skip */
5443 { /* do nothing */
5444 }
5445 else
5446 {
5447 g_assert_not_reached ();
5448 }
5449
5450 goto out;
5451 }
5452
5453 /* Don't allow copying over the source or one of the parents of the source.
5454 */
5455 if (test_dir_is_parent (src, dest))
5456 {
5457 int response;
5458
5459 if (job->skip_all_error)
5460 {
5461 goto out;
5462 }
5463
5464 /* the run_warning() frees all strings passed in automatically */
5465 primary = copy_job->is_move ? g_strdup (_("You cannot move a file over itself."))
5466 : g_strdup (_("You cannot copy a file over itself."));
5467 secondary = g_strdup (_("The source file would be overwritten by the destination."));
5468
5469 response = run_cancel_or_skip_warning (job,
5470 primary,
5471 secondary,
5472 NULL,
5473 source_info->num_files,
5474 source_info->num_files - transfer_info->num_files);
5475
5476 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
5477 {
5478 abort_job (job);
5479 }
5480 else if (response == 1) /* skip all */
5481 {
5482 job->skip_all_error = TRUE;
5483 }
5484 else if (response == 2) /* skip */
5485 { /* do nothing */
5486 }
5487 else
5488 {
5489 g_assert_not_reached ();
5490 }
5491
5492 goto out;
5493 }
5494
5495
5496 retry:
5497
5498 error = NULL;
5499 flags = G_FILE_COPY_NOFOLLOW_SYMLINKS;
5500 if (overwrite)
5501 {
5502 flags |= G_FILE_COPY_OVERWRITE;
5503 }
5504 if (readonly_source_fs)
5505 {
5506 flags |= G_FILE_COPY_TARGET_DEFAULT_PERMS;
5507 }
5508
5509 pdata.job = copy_job;
5510 pdata.last_size = 0;
5511 pdata.source_info = source_info;
5512 pdata.transfer_info = transfer_info;
5513
5514 if (copy_job->is_move)
5515 {
5516 res = g_file_move (src, dest,
5517 flags,
5518 job->cancellable,
5519 copy_file_progress_callback,
5520 &pdata,
5521 &error);
5522 }
5523 else
5524 {
5525 res = g_file_copy (src, dest,
5526 flags,
5527 job->cancellable,
5528 copy_file_progress_callback,
5529 &pdata,
5530 &error);
5531 }
5532
5533 if (res)
5534 {
5535 GFile *real;
5536
5537 real = map_possibly_volatile_file_to_real (dest, job->cancellable, &error);
5538 if (real == NULL)
5539 {
5540 res = FALSE;
5541 }
5542 else
5543 {
5544 g_object_unref (dest);
5545 dest = real;
5546 }
5547 }
5548
5549 if (res)
5550 {
5551 transfer_info->num_files++;
5552 report_copy_progress (copy_job, source_info, transfer_info);
5553
5554 if (debuting_files)
5555 {
5556 dest_uri = g_file_get_uri (dest);
5557
5558 g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
5559 }
5560 if (copy_job->is_move)
5561 {
5562 nautilus_file_changes_queue_file_moved (src, dest);
5563 }
5564 else
5565 {
5566 nautilus_file_changes_queue_file_added (dest);
5567 }
5568
5569 if (job->undo_info != NULL)
5570 {
5571 nautilus_file_undo_info_ext_add_origin_target_pair (NAUTILUS_FILE_UNDO_INFO_EXT (job->undo_info),
5572 src, dest);
5573 }
5574
5575 g_object_unref (dest);
5576 return;
5577 }
5578
5579 if (!handled_invalid_filename &&
5580 IS_IO_ERROR (error, INVALID_FILENAME))
5581 {
5582 handled_invalid_filename = TRUE;
5583
5584 g_assert (*dest_fs_type == NULL);
5585 *dest_fs_type = query_fs_type (dest_dir, job->cancellable);
5586
5587 if (unique_names)
5588 {
5589 new_dest = get_unique_target_file (src, dest_dir, same_fs, *dest_fs_type, unique_name_nr);
5590 }
5591 else
5592 {
5593 new_dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
5594 }
5595
5596 if (!g_file_equal (dest, new_dest))
5597 {
5598 g_object_unref (dest);
5599 dest = new_dest;
5600
5601 g_error_free (error);
5602 goto retry;
5603 }
5604 else
5605 {
5606 g_object_unref (new_dest);
5607 }
5608 }
5609
5610 /* Conflict */
5611 if (!overwrite &&
5612 IS_IO_ERROR (error, EXISTS))
5613 {
5614 gboolean source_is_directory;
5615 gboolean destination_is_directory;
5616 gboolean is_merge;
5617 FileConflictResponse *response;
5618
5619 source_is_directory = is_dir (src);
5620 destination_is_directory = is_dir (dest);
5621
5622 g_error_free (error);
5623
5624 if (unique_names)
5625 {
5626 g_object_unref (dest);
5627 dest = get_unique_target_file (src, dest_dir, same_fs, *dest_fs_type, unique_name_nr++);
5628 goto retry;
5629 }
5630
5631 is_merge = FALSE;
5632
5633 if (source_is_directory && destination_is_directory)
5634 {
5635 is_merge = TRUE;
5636 }
5637 else if (!source_is_directory && destination_is_directory)
5638 {
5639 /* Any sane backend will fail with G_IO_ERROR_IS_DIRECTORY. */
5640 overwrite = TRUE;
5641 goto retry;
5642 }
5643
5644 if ((is_merge && job->merge_all) ||
5645 (!is_merge && job->replace_all))
5646 {
5647 overwrite = TRUE;
5648 goto retry;
5649 }
5650
5651 if (job->skip_all_conflict)
5652 {
5653 goto out;
5654 }
5655
5656 response = handle_copy_move_conflict (job, src, dest, dest_dir);
5657
5658 if (response->id == GTK_RESPONSE_CANCEL ||
5659 response->id == GTK_RESPONSE_DELETE_EVENT)
5660 {
5661 file_conflict_response_free (response);
5662 abort_job (job);
5663 }
5664 else if (response->id == CONFLICT_RESPONSE_SKIP)
5665 {
5666 if (response->apply_to_all)
5667 {
5668 job->skip_all_conflict = TRUE;
5669 }
5670 file_conflict_response_free (response);
5671 }
5672 else if (response->id == CONFLICT_RESPONSE_REPLACE) /* merge/replace */
5673 {
5674 if (response->apply_to_all)
5675 {
5676 if (is_merge)
5677 {
5678 job->merge_all = TRUE;
5679 }
5680 else
5681 {
5682 job->replace_all = TRUE;
5683 }
5684 }
5685 overwrite = TRUE;
5686 file_conflict_response_free (response);
5687 goto retry;
5688 }
5689 else if (response->id == CONFLICT_RESPONSE_RENAME)
5690 {
5691 g_object_unref (dest);
5692 dest = get_target_file_for_display_name (dest_dir,
5693 response->new_name);
5694 file_conflict_response_free (response);
5695 goto retry;
5696 }
5697 else
5698 {
5699 g_assert_not_reached ();
5700 }
5701 }
5702 /* Needs to recurse */
5703 else if (IS_IO_ERROR (error, WOULD_RECURSE) ||
5704 IS_IO_ERROR (error, WOULD_MERGE))
5705 {
5706 gboolean is_merge;
5707
5708 is_merge = error->code == G_IO_ERROR_WOULD_MERGE;
5709 would_recurse = error->code == G_IO_ERROR_WOULD_RECURSE;
5710 g_error_free (error);
5711
5712 if (overwrite && would_recurse)
5713 {
5714 error = NULL;
5715
5716 /* Copying a dir onto file, first remove the file */
5717 if (!g_file_delete (dest, job->cancellable, &error) &&
5718 !IS_IO_ERROR (error, NOT_FOUND))
5719 {
5720 g_autofree gchar *basename = NULL;
5721 g_autofree gchar *filename = NULL;
5722 int response;
5723
5724 if (job->skip_all_error)
5725 {
5726 g_error_free (error);
5727 goto out;
5728 }
5729
5730 basename = get_basename (src);
5731 if (copy_job->is_move)
5732 {
5733 primary = g_strdup_printf (_("Error while moving “%s”."), basename);
5734 }
5735 else
5736 {
5737 primary = g_strdup_printf (_("Error while copying “%s”."), basename);
5738 }
5739 filename = get_truncated_parse_name (dest_dir);
5740 secondary = g_strdup_printf (_("Could not remove the already existing file "
5741 "with the same name in %s."),
5742 filename);
5743 details = error->message;
5744
5745 /* setting TRUE on show_all here, as we could have
5746 * another error on the same file later.
5747 */
5748 response = run_warning (job,
5749 primary,
5750 secondary,
5751 details,
5752 TRUE,
5753 CANCEL, SKIP_ALL, SKIP,
5754 NULL);
5755
5756 g_error_free (error);
5757
5758 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
5759 {
5760 abort_job (job);
5761 }
5762 else if (response == 1) /* skip all */
5763 {
5764 job->skip_all_error = TRUE;
5765 }
5766 else if (response == 2) /* skip */
5767 { /* do nothing */
5768 }
5769 else
5770 {
5771 g_assert_not_reached ();
5772 }
5773 goto out;
5774 }
5775 if (error)
5776 {
5777 g_error_free (error);
5778 error = NULL;
5779 }
5780 nautilus_file_changes_queue_file_removed (dest);
5781 }
5782
5783 if (is_merge)
5784 {
5785 /* On merge we now write in the target directory, which may not
5786 * be in the same directory as the source, even if the parent is
5787 * (if the merged directory is a mountpoint). This could cause
5788 * problems as we then don't transcode filenames.
5789 * We just set same_fs to FALSE which is safe but a bit slower. */
5790 same_fs = FALSE;
5791 }
5792
5793 if (!copy_move_directory (copy_job, src, &dest, same_fs,
5794 would_recurse, dest_fs_type,
5795 source_info, transfer_info,
5796 debuting_files, skipped_file,
5797 readonly_source_fs))
5798 {
5799 /* destination changed, since it was an invalid file name */
5800 g_assert (*dest_fs_type != NULL);
5801 handled_invalid_filename = TRUE;
5802 goto retry;
5803 }
5804
5805 g_object_unref (dest);
5806 return;
5807 }
5808 else if (IS_IO_ERROR (error, CANCELLED))
5809 {
5810 g_error_free (error);
5811 }
5812 /* Other error */
5813 else
5814 {
5815 g_autofree gchar *basename = NULL;
5816 g_autofree gchar *filename = NULL;
5817 int response;
5818
5819 if (job->skip_all_error)
5820 {
5821 g_error_free (error);
5822 goto out;
5823 }
5824 basename = get_basename (src);
5825 primary = g_strdup_printf (_("Error while copying “%s”."), basename);
5826 filename = get_truncated_parse_name (dest_dir);
5827 secondary = g_strdup_printf (_("There was an error copying the file into %s."),
5828 filename);
5829 details = error->message;
5830
5831 response = run_cancel_or_skip_warning (job,
5832 primary,
5833 secondary,
5834 details,
5835 source_info->num_files,
5836 source_info->num_files - transfer_info->num_files);
5837
5838 g_error_free (error);
5839
5840 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
5841 {
5842 abort_job (job);
5843 }
5844 else if (response == 1) /* skip all */
5845 {
5846 job->skip_all_error = TRUE;
5847 }
5848 else if (response == 2) /* skip */
5849 { /* do nothing */
5850 }
5851 else
5852 {
5853 g_assert_not_reached ();
5854 }
5855 }
5856 out:
5857 *skipped_file = TRUE; /* Or aborted, but same-same */
5858 g_object_unref (dest);
5859 }
5860
5861 static void
copy_files(CopyMoveJob * job,const char * dest_fs_id,SourceInfo * source_info,TransferInfo * transfer_info)5862 copy_files (CopyMoveJob *job,
5863 const char *dest_fs_id,
5864 SourceInfo *source_info,
5865 TransferInfo *transfer_info)
5866 {
5867 CommonJob *common;
5868 GList *l;
5869 GFile *src;
5870 gboolean same_fs;
5871 int i;
5872 gboolean skipped_file;
5873 gboolean unique_names;
5874 GFile *dest;
5875 GFile *source_dir;
5876 char *dest_fs_type;
5877 GFileInfo *inf;
5878 gboolean readonly_source_fs;
5879
5880 dest_fs_type = NULL;
5881 readonly_source_fs = FALSE;
5882
5883 common = &job->common;
5884
5885 report_copy_progress (job, source_info, transfer_info);
5886
5887 /* Query the source dir, not the file because if it's a symlink we'll follow it */
5888 source_dir = g_file_get_parent ((GFile *) job->files->data);
5889 if (source_dir)
5890 {
5891 inf = g_file_query_filesystem_info (source_dir, "filesystem::readonly", NULL, NULL);
5892 if (inf != NULL)
5893 {
5894 readonly_source_fs = g_file_info_get_attribute_boolean (inf, "filesystem::readonly");
5895 g_object_unref (inf);
5896 }
5897 g_object_unref (source_dir);
5898 }
5899
5900 unique_names = (job->destination == NULL);
5901 i = 0;
5902 for (l = job->files;
5903 l != NULL && !job_aborted (common);
5904 l = l->next)
5905 {
5906 src = l->data;
5907
5908 same_fs = FALSE;
5909 if (dest_fs_id)
5910 {
5911 same_fs = has_fs_id (src, dest_fs_id);
5912 }
5913
5914 if (job->destination)
5915 {
5916 dest = g_object_ref (job->destination);
5917 }
5918 else
5919 {
5920 dest = g_file_get_parent (src);
5921 }
5922 if (dest)
5923 {
5924 skipped_file = FALSE;
5925 copy_move_file (job, src, dest,
5926 same_fs, unique_names,
5927 &dest_fs_type,
5928 source_info, transfer_info,
5929 job->debuting_files,
5930 FALSE, &skipped_file,
5931 readonly_source_fs);
5932 g_object_unref (dest);
5933
5934 if (skipped_file)
5935 {
5936 source_info_remove_file_from_count (src, common, source_info);
5937 report_copy_progress (job, source_info, transfer_info);
5938 }
5939 }
5940 i++;
5941 }
5942
5943 g_free (dest_fs_type);
5944 }
5945
5946 static void
copy_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)5947 copy_task_done (GObject *source_object,
5948 GAsyncResult *res,
5949 gpointer user_data)
5950 {
5951 CopyMoveJob *job;
5952
5953 job = user_data;
5954 if (job->done_callback)
5955 {
5956 job->done_callback (job->debuting_files,
5957 !job_aborted ((CommonJob *) job),
5958 job->done_callback_data);
5959 }
5960
5961 g_list_free_full (job->files, g_object_unref);
5962 if (job->destination)
5963 {
5964 g_object_unref (job->destination);
5965 }
5966 g_hash_table_unref (job->debuting_files);
5967 g_free (job->target_name);
5968
5969 g_clear_object (&job->fake_display_source);
5970
5971 finalize_common ((CommonJob *) job);
5972
5973 nautilus_file_changes_consume_changes (TRUE);
5974 }
5975
5976 static CopyMoveJob *
copy_job_setup(GList * files,GFile * target_dir,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusCopyCallback done_callback,gpointer done_callback_data)5977 copy_job_setup (GList *files,
5978 GFile *target_dir,
5979 GtkWindow *parent_window,
5980 NautilusFileOperationsDBusData *dbus_data,
5981 NautilusCopyCallback done_callback,
5982 gpointer done_callback_data)
5983 {
5984 CopyMoveJob *job;
5985
5986 job = op_job_new (CopyMoveJob, parent_window, dbus_data);
5987 job->done_callback = done_callback;
5988 job->done_callback_data = done_callback_data;
5989 job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
5990 job->destination = g_object_ref (target_dir);
5991 /* Need to indicate the destination for the operation notification open
5992 * button. */
5993 nautilus_progress_info_set_destination (((CommonJob *) job)->progress, target_dir);
5994 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
5995
5996 return job;
5997 }
5998
5999 static void
nautilus_file_operations_copy(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)6000 nautilus_file_operations_copy (GTask *task,
6001 gpointer source_object,
6002 gpointer task_data,
6003 GCancellable *cancellable)
6004 {
6005 CopyMoveJob *job;
6006 CommonJob *common;
6007 g_auto (SourceInfo) source_info = SOURCE_INFO_INIT;
6008 TransferInfo transfer_info;
6009 g_autofree char *dest_fs_id = NULL;
6010 GFile *dest;
6011
6012 job = task_data;
6013 common = &job->common;
6014
6015 if (g_strcmp0 (g_getenv ("RUNNING_TESTS"), "TRUE"))
6016 {
6017 inhibit_power_manager ((CommonJob *) job, _("Copying Files"));
6018 }
6019
6020 if (!nautilus_file_undo_manager_is_operating ())
6021 {
6022 g_autoptr (GFile) src_dir = NULL;
6023
6024 src_dir = g_file_get_parent (job->files->data);
6025 /* In the case of duplicate, the undo_info is already set, so we don't want to
6026 * overwrite it wrongfully.
6027 */
6028 if (job->common.undo_info == NULL)
6029 {
6030 job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_COPY,
6031 g_list_length (job->files),
6032 src_dir, job->destination);
6033 }
6034 }
6035
6036 nautilus_progress_info_start (job->common.progress);
6037
6038 scan_sources (job->files,
6039 &source_info,
6040 common,
6041 OP_KIND_COPY);
6042 if (job_aborted (common))
6043 {
6044 return;
6045 }
6046
6047 if (job->destination)
6048 {
6049 dest = g_object_ref (job->destination);
6050 }
6051 else
6052 {
6053 /* Duplication, no dest,
6054 * use source for free size, etc
6055 */
6056 dest = g_file_get_parent (job->files->data);
6057 }
6058
6059 verify_destination (&job->common,
6060 dest,
6061 &dest_fs_id,
6062 source_info.num_bytes);
6063 g_object_unref (dest);
6064 if (job_aborted (common))
6065 {
6066 return;
6067 }
6068
6069 g_timer_start (job->common.time);
6070
6071 memset (&transfer_info, 0, sizeof (transfer_info));
6072 copy_files (job,
6073 dest_fs_id,
6074 &source_info, &transfer_info);
6075 }
6076
6077 void
nautilus_file_operations_copy_sync(GList * files,GFile * target_dir)6078 nautilus_file_operations_copy_sync (GList *files,
6079 GFile *target_dir)
6080 {
6081 GTask *task;
6082 CopyMoveJob *job;
6083
6084 job = copy_job_setup (files,
6085 target_dir,
6086 NULL,
6087 NULL,
6088 NULL,
6089 NULL);
6090
6091 task = g_task_new (NULL, job->common.cancellable, NULL, job);
6092 g_task_set_task_data (task, job, NULL);
6093 g_task_run_in_thread_sync (task, nautilus_file_operations_copy);
6094 g_object_unref (task);
6095 /* Since g_task_run_in_thread_sync doesn't work with callbacks (in this case not reaching
6096 * copy_task_done) we need to set up the undo information ourselves.
6097 */
6098 copy_task_done (NULL, NULL, job);
6099 }
6100
6101 void
nautilus_file_operations_copy_async(GList * files,GFile * target_dir,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusCopyCallback done_callback,gpointer done_callback_data)6102 nautilus_file_operations_copy_async (GList *files,
6103 GFile *target_dir,
6104 GtkWindow *parent_window,
6105 NautilusFileOperationsDBusData *dbus_data,
6106 NautilusCopyCallback done_callback,
6107 gpointer done_callback_data)
6108 {
6109 GTask *task;
6110 CopyMoveJob *job;
6111
6112 job = copy_job_setup (files,
6113 target_dir,
6114 parent_window,
6115 dbus_data,
6116 done_callback,
6117 done_callback_data);
6118
6119 task = g_task_new (NULL, job->common.cancellable, copy_task_done, job);
6120 g_task_set_task_data (task, job, NULL);
6121 g_task_run_in_thread (task, nautilus_file_operations_copy);
6122 g_object_unref (task);
6123 }
6124
6125 static void
report_preparing_move_progress(CopyMoveJob * move_job,int total,int left)6126 report_preparing_move_progress (CopyMoveJob *move_job,
6127 int total,
6128 int left)
6129 {
6130 CommonJob *job;
6131 g_autofree gchar *basename = NULL;
6132
6133 job = (CommonJob *) move_job;
6134 basename = get_basename (move_job->destination);
6135
6136 nautilus_progress_info_take_status (job->progress,
6137 g_strdup_printf (_("Preparing to move to “%s”"),
6138 basename));
6139
6140 nautilus_progress_info_take_details (job->progress,
6141 g_strdup_printf (ngettext ("Preparing to move %'d file",
6142 "Preparing to move %'d files",
6143 left),
6144 left));
6145
6146 nautilus_progress_info_pulse_progress (job->progress);
6147 }
6148
6149 typedef struct
6150 {
6151 GFile *file;
6152 gboolean overwrite;
6153 } MoveFileCopyFallback;
6154
6155 static MoveFileCopyFallback *
move_copy_file_callback_new(GFile * file,gboolean overwrite)6156 move_copy_file_callback_new (GFile *file,
6157 gboolean overwrite)
6158 {
6159 MoveFileCopyFallback *fallback;
6160
6161 fallback = g_new (MoveFileCopyFallback, 1);
6162 fallback->file = file;
6163 fallback->overwrite = overwrite;
6164
6165 return fallback;
6166 }
6167
6168 static GList *
get_files_from_fallbacks(GList * fallbacks)6169 get_files_from_fallbacks (GList *fallbacks)
6170 {
6171 MoveFileCopyFallback *fallback;
6172 GList *res, *l;
6173
6174 res = NULL;
6175 for (l = fallbacks; l != NULL; l = l->next)
6176 {
6177 fallback = l->data;
6178 res = g_list_prepend (res, fallback->file);
6179 }
6180 return g_list_reverse (res);
6181 }
6182
6183 static void
move_file_prepare(CopyMoveJob * move_job,GFile * src,GFile * dest_dir,gboolean same_fs,char ** dest_fs_type,GHashTable * debuting_files,GList ** fallback_files,int files_left)6184 move_file_prepare (CopyMoveJob *move_job,
6185 GFile *src,
6186 GFile *dest_dir,
6187 gboolean same_fs,
6188 char **dest_fs_type,
6189 GHashTable *debuting_files,
6190 GList **fallback_files,
6191 int files_left)
6192 {
6193 GFile *dest, *new_dest;
6194 g_autofree gchar *dest_uri = NULL;
6195 GError *error;
6196 CommonJob *job;
6197 gboolean overwrite;
6198 char *primary, *secondary, *details;
6199 GFileCopyFlags flags;
6200 MoveFileCopyFallback *fallback;
6201 gboolean handled_invalid_filename;
6202
6203 overwrite = FALSE;
6204 handled_invalid_filename = *dest_fs_type != NULL;
6205
6206 job = (CommonJob *) move_job;
6207
6208 if (g_file_has_uri_scheme (src, "google-drive") &&
6209 g_file_has_uri_scheme (dest_dir, "google-drive"))
6210 {
6211 dest = get_target_file_from_source_display_name (move_job, src, dest_dir);
6212 if (dest == NULL)
6213 {
6214 return;
6215 }
6216 }
6217 else
6218 {
6219 dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
6220 }
6221
6222
6223 /* Don't allow recursive move/copy into itself.
6224 * (We would get a file system error if we proceeded but it is nicer to
6225 * detect and report it at this level) */
6226 if (test_dir_is_parent (dest_dir, src))
6227 {
6228 int response;
6229
6230 if (job->skip_all_error)
6231 {
6232 goto out;
6233 }
6234
6235 /* the run_warning() frees all strings passed in automatically */
6236 primary = move_job->is_move ? g_strdup (_("You cannot move a folder into itself."))
6237 : g_strdup (_("You cannot copy a folder into itself."));
6238 secondary = g_strdup (_("The destination folder is inside the source folder."));
6239
6240 response = run_warning (job,
6241 primary,
6242 secondary,
6243 NULL,
6244 files_left > 1,
6245 CANCEL, SKIP_ALL, SKIP,
6246 NULL);
6247
6248 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
6249 {
6250 abort_job (job);
6251 }
6252 else if (response == 1) /* skip all */
6253 {
6254 job->skip_all_error = TRUE;
6255 }
6256 else if (response == 2) /* skip */
6257 { /* do nothing */
6258 }
6259 else
6260 {
6261 g_assert_not_reached ();
6262 }
6263
6264 goto out;
6265 }
6266
6267 retry:
6268
6269 flags = G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_NO_FALLBACK_FOR_MOVE;
6270 if (overwrite)
6271 {
6272 flags |= G_FILE_COPY_OVERWRITE;
6273 }
6274
6275 error = NULL;
6276 if (g_file_move (src, dest,
6277 flags,
6278 job->cancellable,
6279 NULL,
6280 NULL,
6281 &error))
6282 {
6283 if (debuting_files)
6284 {
6285 g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
6286 }
6287
6288 nautilus_file_changes_queue_file_moved (src, dest);
6289
6290 dest_uri = g_file_get_uri (dest);
6291
6292 if (job->undo_info != NULL)
6293 {
6294 nautilus_file_undo_info_ext_add_origin_target_pair (NAUTILUS_FILE_UNDO_INFO_EXT (job->undo_info),
6295 src, dest);
6296 }
6297
6298 return;
6299 }
6300
6301 if (IS_IO_ERROR (error, INVALID_FILENAME) &&
6302 !handled_invalid_filename)
6303 {
6304 g_error_free (error);
6305
6306 handled_invalid_filename = TRUE;
6307
6308 g_assert (*dest_fs_type == NULL);
6309 *dest_fs_type = query_fs_type (dest_dir, job->cancellable);
6310
6311 new_dest = get_target_file (src, dest_dir, *dest_fs_type, same_fs);
6312 if (!g_file_equal (dest, new_dest))
6313 {
6314 g_object_unref (dest);
6315 dest = new_dest;
6316 goto retry;
6317 }
6318 else
6319 {
6320 g_object_unref (new_dest);
6321 }
6322 }
6323 /* Conflict */
6324 else if (!overwrite &&
6325 IS_IO_ERROR (error, EXISTS))
6326 {
6327 gboolean source_is_directory;
6328 gboolean destination_is_directory;
6329 gboolean is_merge;
6330 FileConflictResponse *response;
6331
6332 source_is_directory = is_dir (src);
6333 destination_is_directory = is_dir (dest);
6334
6335 g_error_free (error);
6336
6337 is_merge = FALSE;
6338 if (source_is_directory && destination_is_directory)
6339 {
6340 is_merge = TRUE;
6341 }
6342 else if (!source_is_directory && destination_is_directory)
6343 {
6344 /* Any sane backend will fail with G_IO_ERROR_IS_DIRECTORY. */
6345 overwrite = TRUE;
6346 goto retry;
6347 }
6348
6349 if ((is_merge && job->merge_all) ||
6350 (!is_merge && job->replace_all))
6351 {
6352 overwrite = TRUE;
6353 goto retry;
6354 }
6355
6356 if (job->skip_all_conflict)
6357 {
6358 goto out;
6359 }
6360
6361 response = handle_copy_move_conflict (job, src, dest, dest_dir);
6362
6363 if (response->id == GTK_RESPONSE_CANCEL ||
6364 response->id == GTK_RESPONSE_DELETE_EVENT)
6365 {
6366 file_conflict_response_free (response);
6367 abort_job (job);
6368 }
6369 else if (response->id == CONFLICT_RESPONSE_SKIP)
6370 {
6371 if (response->apply_to_all)
6372 {
6373 job->skip_all_conflict = TRUE;
6374 }
6375 file_conflict_response_free (response);
6376 }
6377 else if (response->id == CONFLICT_RESPONSE_REPLACE) /* merge/replace */
6378 {
6379 if (response->apply_to_all)
6380 {
6381 if (is_merge)
6382 {
6383 job->merge_all = TRUE;
6384 }
6385 else
6386 {
6387 job->replace_all = TRUE;
6388 }
6389 }
6390 overwrite = TRUE;
6391 file_conflict_response_free (response);
6392 goto retry;
6393 }
6394 else if (response->id == CONFLICT_RESPONSE_RENAME)
6395 {
6396 g_object_unref (dest);
6397 dest = get_target_file_for_display_name (dest_dir,
6398 response->new_name);
6399 file_conflict_response_free (response);
6400 goto retry;
6401 }
6402 else
6403 {
6404 g_assert_not_reached ();
6405 }
6406 }
6407 else if (IS_IO_ERROR (error, WOULD_RECURSE) ||
6408 IS_IO_ERROR (error, WOULD_MERGE) ||
6409 IS_IO_ERROR (error, NOT_SUPPORTED))
6410 {
6411 g_error_free (error);
6412
6413 fallback = move_copy_file_callback_new (src,
6414 overwrite);
6415 *fallback_files = g_list_prepend (*fallback_files, fallback);
6416 }
6417 else if (IS_IO_ERROR (error, CANCELLED))
6418 {
6419 g_error_free (error);
6420 }
6421 /* Other error */
6422 else
6423 {
6424 g_autofree gchar *basename = NULL;
6425 g_autofree gchar *filename = NULL;
6426 int response;
6427
6428 if (job->skip_all_error)
6429 {
6430 g_error_free (error);
6431 goto out;
6432 }
6433 basename = get_basename (src);
6434 primary = g_strdup_printf (_("Error while moving “%s”."), basename);
6435 filename = get_truncated_parse_name (dest_dir);
6436 secondary = g_strdup_printf (_("There was an error moving the file into %s."),
6437 filename);
6438
6439 details = error->message;
6440
6441 response = run_warning (job,
6442 primary,
6443 secondary,
6444 details,
6445 files_left > 1,
6446 CANCEL, SKIP_ALL, SKIP,
6447 NULL);
6448
6449 g_error_free (error);
6450
6451 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
6452 {
6453 abort_job (job);
6454 }
6455 else if (response == 1) /* skip all */
6456 {
6457 job->skip_all_error = TRUE;
6458 }
6459 else if (response == 2) /* skip */
6460 { /* do nothing */
6461 }
6462 else
6463 {
6464 g_assert_not_reached ();
6465 }
6466 }
6467
6468 out:
6469 g_object_unref (dest);
6470 }
6471
6472 static void
move_files_prepare(CopyMoveJob * job,const char * dest_fs_id,char ** dest_fs_type,GList ** fallbacks)6473 move_files_prepare (CopyMoveJob *job,
6474 const char *dest_fs_id,
6475 char **dest_fs_type,
6476 GList **fallbacks)
6477 {
6478 CommonJob *common;
6479 GList *l;
6480 GFile *src;
6481 gboolean same_fs;
6482 int i;
6483 int total, left;
6484
6485 common = &job->common;
6486
6487 total = left = g_list_length (job->files);
6488
6489 report_preparing_move_progress (job, total, left);
6490
6491 i = 0;
6492 for (l = job->files;
6493 l != NULL && !job_aborted (common);
6494 l = l->next)
6495 {
6496 src = l->data;
6497
6498 same_fs = FALSE;
6499 if (dest_fs_id)
6500 {
6501 same_fs = has_fs_id (src, dest_fs_id);
6502 }
6503
6504 move_file_prepare (job, src, job->destination,
6505 same_fs, dest_fs_type,
6506 job->debuting_files,
6507 fallbacks,
6508 left);
6509 report_preparing_move_progress (job, total, --left);
6510 i++;
6511 }
6512
6513 *fallbacks = g_list_reverse (*fallbacks);
6514 }
6515
6516 static void
move_files(CopyMoveJob * job,GList * fallbacks,const char * dest_fs_id,char ** dest_fs_type,SourceInfo * source_info,TransferInfo * transfer_info)6517 move_files (CopyMoveJob *job,
6518 GList *fallbacks,
6519 const char *dest_fs_id,
6520 char **dest_fs_type,
6521 SourceInfo *source_info,
6522 TransferInfo *transfer_info)
6523 {
6524 CommonJob *common;
6525 GList *l;
6526 GFile *src;
6527 gboolean same_fs;
6528 int i;
6529 gboolean skipped_file;
6530 MoveFileCopyFallback *fallback;
6531 common = &job->common;
6532
6533 report_copy_progress (job, source_info, transfer_info);
6534
6535 i = 0;
6536 for (l = fallbacks;
6537 l != NULL && !job_aborted (common);
6538 l = l->next)
6539 {
6540 fallback = l->data;
6541 src = fallback->file;
6542
6543 same_fs = FALSE;
6544 if (dest_fs_id)
6545 {
6546 same_fs = has_fs_id (src, dest_fs_id);
6547 }
6548
6549 /* Set overwrite to true, as the user has
6550 * selected overwrite on all toplevel items */
6551 skipped_file = FALSE;
6552 copy_move_file (job, src, job->destination,
6553 same_fs, FALSE, dest_fs_type,
6554 source_info, transfer_info,
6555 job->debuting_files,
6556 fallback->overwrite, &skipped_file, FALSE);
6557 i++;
6558
6559 if (skipped_file)
6560 {
6561 source_info_remove_file_from_count (src, common, source_info);
6562 report_copy_progress (job, source_info, transfer_info);
6563 }
6564 }
6565 }
6566
6567
6568 static void
move_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)6569 move_task_done (GObject *source_object,
6570 GAsyncResult *res,
6571 gpointer user_data)
6572 {
6573 CopyMoveJob *job;
6574
6575 job = user_data;
6576 if (job->done_callback)
6577 {
6578 job->done_callback (job->debuting_files,
6579 !job_aborted ((CommonJob *) job),
6580 job->done_callback_data);
6581 }
6582
6583 g_list_free_full (job->files, g_object_unref);
6584 g_object_unref (job->destination);
6585 g_hash_table_unref (job->debuting_files);
6586
6587 finalize_common ((CommonJob *) job);
6588
6589 nautilus_file_changes_consume_changes (TRUE);
6590 }
6591
6592 static CopyMoveJob *
move_job_setup(GList * files,GFile * target_dir,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusCopyCallback done_callback,gpointer done_callback_data)6593 move_job_setup (GList *files,
6594 GFile *target_dir,
6595 GtkWindow *parent_window,
6596 NautilusFileOperationsDBusData *dbus_data,
6597 NautilusCopyCallback done_callback,
6598 gpointer done_callback_data)
6599 {
6600 CopyMoveJob *job;
6601
6602 job = op_job_new (CopyMoveJob, parent_window, dbus_data);
6603 job->is_move = TRUE;
6604 job->done_callback = done_callback;
6605 job->done_callback_data = done_callback_data;
6606 job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
6607 job->destination = g_object_ref (target_dir);
6608
6609 /* Need to indicate the destination for the operation notification open
6610 * button. */
6611 nautilus_progress_info_set_destination (((CommonJob *) job)->progress, job->destination);
6612 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
6613
6614 return job;
6615 }
6616
6617 void
nautilus_file_operations_move_sync(GList * files,GFile * target_dir)6618 nautilus_file_operations_move_sync (GList *files,
6619 GFile *target_dir)
6620 {
6621 GTask *task;
6622 CopyMoveJob *job;
6623
6624 job = move_job_setup (files, target_dir, NULL, NULL, NULL, NULL);
6625 task = g_task_new (NULL, job->common.cancellable, NULL, job);
6626 g_task_set_task_data (task, job, NULL);
6627 g_task_run_in_thread_sync (task, nautilus_file_operations_move);
6628 g_object_unref (task);
6629 /* Since g_task_run_in_thread_sync doesn't work with callbacks (in this case not reaching
6630 * move_task_done) we need to set up the undo information ourselves.
6631 */
6632 move_task_done (NULL, NULL, job);
6633 }
6634
6635 void
nautilus_file_operations_move_async(GList * files,GFile * target_dir,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusCopyCallback done_callback,gpointer done_callback_data)6636 nautilus_file_operations_move_async (GList *files,
6637 GFile *target_dir,
6638 GtkWindow *parent_window,
6639 NautilusFileOperationsDBusData *dbus_data,
6640 NautilusCopyCallback done_callback,
6641 gpointer done_callback_data)
6642 {
6643 GTask *task;
6644 CopyMoveJob *job;
6645
6646 job = move_job_setup (files,
6647 target_dir,
6648 parent_window,
6649 dbus_data,
6650 done_callback,
6651 done_callback_data);
6652
6653 task = g_task_new (NULL, job->common.cancellable, move_task_done, job);
6654 g_task_set_task_data (task, job, NULL);
6655 g_task_run_in_thread (task, nautilus_file_operations_move);
6656 g_object_unref (task);
6657 }
6658
6659 static void
nautilus_file_operations_move(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)6660 nautilus_file_operations_move (GTask *task,
6661 gpointer source_object,
6662 gpointer task_data,
6663 GCancellable *cancellable)
6664 {
6665 CopyMoveJob *job;
6666 CommonJob *common;
6667 GList *fallbacks;
6668 g_auto (SourceInfo) source_info = SOURCE_INFO_INIT;
6669 TransferInfo transfer_info;
6670 g_autofree char *dest_fs_id = NULL;
6671 g_autofree char *dest_fs_type = NULL;
6672 GList *fallback_files;
6673
6674 job = task_data;
6675
6676 /* Since we never initiate the app (in the case of
6677 * testing), we can't inhibit its power manager.
6678 * This would terminate the testing. So we avoid
6679 * doing this by checking if the RUNNING_TESTS
6680 * environment variable is set to "TRUE".
6681 */
6682 if (g_strcmp0 (g_getenv ("RUNNING_TESTS"), "TRUE"))
6683 {
6684 inhibit_power_manager ((CommonJob *) job, _("Moving Files"));
6685 }
6686
6687 if (!nautilus_file_undo_manager_is_operating ())
6688 {
6689 g_autoptr (GFile) src_dir = NULL;
6690
6691 src_dir = g_file_get_parent ((job->files)->data);
6692
6693 if (g_file_has_uri_scheme (g_list_first (job->files)->data, "trash"))
6694 {
6695 job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_RESTORE_FROM_TRASH,
6696 g_list_length (job->files),
6697 src_dir, job->destination);
6698 }
6699 else
6700 {
6701 job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_MOVE,
6702 g_list_length (job->files),
6703 src_dir, job->destination);
6704 }
6705 }
6706
6707 common = &job->common;
6708
6709 nautilus_progress_info_start (job->common.progress);
6710
6711 fallbacks = NULL;
6712
6713 verify_destination (&job->common,
6714 job->destination,
6715 &dest_fs_id,
6716 -1);
6717 if (job_aborted (common))
6718 {
6719 goto aborted;
6720 }
6721
6722 /* This moves all files that we can do without copy + delete */
6723 move_files_prepare (job, dest_fs_id, &dest_fs_type, &fallbacks);
6724 if (job_aborted (common))
6725 {
6726 goto aborted;
6727 }
6728
6729 if (fallbacks == NULL)
6730 {
6731 gint total;
6732
6733 total = g_list_length (job->files);
6734
6735 memset (&source_info, 0, sizeof (source_info));
6736 source_info.num_files = total;
6737 memset (&transfer_info, 0, sizeof (transfer_info));
6738 transfer_info.num_files = total;
6739 report_copy_progress (job, &source_info, &transfer_info);
6740
6741 return;
6742 }
6743
6744 /* The rest we need to do deep copy + delete behind on,
6745 * so scan for size */
6746
6747 fallback_files = get_files_from_fallbacks (fallbacks);
6748 scan_sources (fallback_files,
6749 &source_info,
6750 common,
6751 OP_KIND_MOVE);
6752
6753 g_list_free (fallback_files);
6754
6755 if (job_aborted (common))
6756 {
6757 goto aborted;
6758 }
6759
6760 verify_destination (&job->common,
6761 job->destination,
6762 NULL,
6763 source_info.num_bytes);
6764 if (job_aborted (common))
6765 {
6766 goto aborted;
6767 }
6768
6769 memset (&transfer_info, 0, sizeof (transfer_info));
6770 move_files (job,
6771 fallbacks,
6772 dest_fs_id, &dest_fs_type,
6773 &source_info, &transfer_info);
6774
6775 aborted:
6776 g_list_free_full (fallbacks, g_free);
6777 }
6778
6779 static void
report_preparing_link_progress(CopyMoveJob * link_job,int total,int left)6780 report_preparing_link_progress (CopyMoveJob *link_job,
6781 int total,
6782 int left)
6783 {
6784 CommonJob *job;
6785 g_autofree gchar *basename = NULL;
6786
6787 job = (CommonJob *) link_job;
6788 basename = get_basename (link_job->destination);
6789 nautilus_progress_info_take_status (job->progress,
6790 g_strdup_printf (_("Creating links in “%s”"),
6791 basename));
6792
6793 nautilus_progress_info_take_details (job->progress,
6794 g_strdup_printf (ngettext ("Making link to %'d file",
6795 "Making links to %'d files",
6796 left),
6797 left));
6798
6799 nautilus_progress_info_set_progress (job->progress, left, total);
6800 }
6801
6802 static char *
get_abs_path_for_symlink(GFile * file,GFile * destination)6803 get_abs_path_for_symlink (GFile *file,
6804 GFile *destination)
6805 {
6806 GFile *root, *parent;
6807 char *relative, *abs;
6808
6809 if (g_file_is_native (file) || g_file_is_native (destination))
6810 {
6811 return g_file_get_path (file);
6812 }
6813
6814 root = g_object_ref (file);
6815 while ((parent = g_file_get_parent (root)) != NULL)
6816 {
6817 g_object_unref (root);
6818 root = parent;
6819 }
6820
6821 relative = g_file_get_relative_path (root, file);
6822 g_object_unref (root);
6823 abs = g_strconcat ("/", relative, NULL);
6824 g_free (relative);
6825 return abs;
6826 }
6827
6828
6829 static void
link_file(CopyMoveJob * job,GFile * src,GFile * dest_dir,char ** dest_fs_type,GHashTable * debuting_files,int files_left)6830 link_file (CopyMoveJob *job,
6831 GFile *src,
6832 GFile *dest_dir,
6833 char **dest_fs_type,
6834 GHashTable *debuting_files,
6835 int files_left)
6836 {
6837 GFile *src_dir;
6838 GFile *new_dest;
6839 g_autoptr (GFile) dest = NULL;
6840 g_autofree gchar *dest_uri = NULL;
6841 int count;
6842 char *path;
6843 gboolean not_local;
6844 GError *error;
6845 CommonJob *common;
6846 char *primary, *secondary, *details;
6847 int response;
6848 gboolean handled_invalid_filename;
6849
6850 common = (CommonJob *) job;
6851
6852 count = 0;
6853
6854 src_dir = g_file_get_parent (src);
6855 if (g_file_equal (src_dir, dest_dir))
6856 {
6857 count = 1;
6858 }
6859 g_object_unref (src_dir);
6860
6861 handled_invalid_filename = *dest_fs_type != NULL;
6862
6863 dest = get_target_file_for_link (src, dest_dir, *dest_fs_type, count);
6864
6865 retry:
6866 error = NULL;
6867 not_local = FALSE;
6868
6869 path = get_abs_path_for_symlink (src, dest);
6870 if (path == NULL)
6871 {
6872 not_local = TRUE;
6873 }
6874 else if (g_file_make_symbolic_link (dest,
6875 path,
6876 common->cancellable,
6877 &error))
6878 {
6879 if (common->undo_info != NULL)
6880 {
6881 nautilus_file_undo_info_ext_add_origin_target_pair (NAUTILUS_FILE_UNDO_INFO_EXT (common->undo_info),
6882 src, dest);
6883 }
6884
6885 g_free (path);
6886 if (debuting_files)
6887 {
6888 g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
6889 }
6890
6891 nautilus_file_changes_queue_file_added (dest);
6892 dest_uri = g_file_get_uri (dest);
6893
6894 return;
6895 }
6896 g_free (path);
6897
6898 if (error != NULL &&
6899 IS_IO_ERROR (error, INVALID_FILENAME) &&
6900 !handled_invalid_filename)
6901 {
6902 handled_invalid_filename = TRUE;
6903
6904 g_assert (*dest_fs_type == NULL);
6905 *dest_fs_type = query_fs_type (dest_dir, common->cancellable);
6906
6907 new_dest = get_target_file_for_link (src, dest_dir, *dest_fs_type, count);
6908
6909 if (!g_file_equal (dest, new_dest))
6910 {
6911 g_object_unref (dest);
6912 dest = new_dest;
6913 g_error_free (error);
6914
6915 goto retry;
6916 }
6917 else
6918 {
6919 g_object_unref (new_dest);
6920 }
6921 }
6922 /* Conflict */
6923 if (error != NULL && IS_IO_ERROR (error, EXISTS))
6924 {
6925 g_object_unref (dest);
6926 dest = get_target_file_for_link (src, dest_dir, *dest_fs_type, count++);
6927 g_error_free (error);
6928 goto retry;
6929 }
6930 else if (error != NULL && IS_IO_ERROR (error, CANCELLED))
6931 {
6932 g_error_free (error);
6933 }
6934 /* Other error */
6935 else if (error != NULL)
6936 {
6937 g_autofree gchar *basename = NULL;
6938
6939 if (common->skip_all_error)
6940 {
6941 return;
6942 }
6943 basename = get_basename (src);
6944 primary = g_strdup_printf (_("Error while creating link to %s."),
6945 basename);
6946 if (not_local)
6947 {
6948 secondary = g_strdup (_("Symbolic links only supported for local files"));
6949 details = NULL;
6950 }
6951 else if (IS_IO_ERROR (error, NOT_SUPPORTED))
6952 {
6953 secondary = g_strdup (_("The target doesn’t support symbolic links."));
6954 details = NULL;
6955 }
6956 else
6957 {
6958 g_autofree gchar *filename = NULL;
6959
6960 filename = get_truncated_parse_name (dest_dir);
6961 secondary = g_strdup_printf (_("There was an error creating the symlink in %s."),
6962 filename);
6963 details = error->message;
6964 }
6965
6966 response = run_warning (common,
6967 primary,
6968 secondary,
6969 details,
6970 files_left > 1,
6971 CANCEL, SKIP_ALL, SKIP,
6972 NULL);
6973
6974 if (error)
6975 {
6976 g_error_free (error);
6977 }
6978
6979 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
6980 {
6981 abort_job (common);
6982 }
6983 else if (response == 1) /* skip all */
6984 {
6985 common->skip_all_error = TRUE;
6986 }
6987 else if (response == 2) /* skip */
6988 { /* do nothing */
6989 }
6990 else
6991 {
6992 g_assert_not_reached ();
6993 }
6994 }
6995 }
6996
6997 static void
link_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)6998 link_task_done (GObject *source_object,
6999 GAsyncResult *res,
7000 gpointer user_data)
7001 {
7002 CopyMoveJob *job;
7003
7004 job = user_data;
7005 if (job->done_callback)
7006 {
7007 job->done_callback (job->debuting_files,
7008 !job_aborted ((CommonJob *) job),
7009 job->done_callback_data);
7010 }
7011
7012 g_list_free_full (job->files, g_object_unref);
7013 g_object_unref (job->destination);
7014 g_hash_table_unref (job->debuting_files);
7015
7016 finalize_common ((CommonJob *) job);
7017
7018 nautilus_file_changes_consume_changes (TRUE);
7019 }
7020
7021 static void
link_task_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)7022 link_task_thread_func (GTask *task,
7023 gpointer source_object,
7024 gpointer task_data,
7025 GCancellable *cancellable)
7026 {
7027 CopyMoveJob *job;
7028 CommonJob *common;
7029 GFile *src;
7030 g_autofree char *dest_fs_type = NULL;
7031 int total, left;
7032 int i;
7033 GList *l;
7034
7035 job = task_data;
7036 common = &job->common;
7037
7038 nautilus_progress_info_start (job->common.progress);
7039
7040 verify_destination (&job->common,
7041 job->destination,
7042 NULL,
7043 -1);
7044 if (job_aborted (common))
7045 {
7046 return;
7047 }
7048
7049 total = left = g_list_length (job->files);
7050
7051 report_preparing_link_progress (job, total, left);
7052
7053 i = 0;
7054 for (l = job->files;
7055 l != NULL && !job_aborted (common);
7056 l = l->next)
7057 {
7058 src = l->data;
7059
7060 link_file (job, src, job->destination,
7061 &dest_fs_type, job->debuting_files,
7062 left);
7063 report_preparing_link_progress (job, total, --left);
7064 i++;
7065 }
7066 }
7067
7068 void
nautilus_file_operations_link(GList * files,GFile * target_dir,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusCopyCallback done_callback,gpointer done_callback_data)7069 nautilus_file_operations_link (GList *files,
7070 GFile *target_dir,
7071 GtkWindow *parent_window,
7072 NautilusFileOperationsDBusData *dbus_data,
7073 NautilusCopyCallback done_callback,
7074 gpointer done_callback_data)
7075 {
7076 g_autoptr (GTask) task = NULL;
7077 CopyMoveJob *job;
7078
7079 job = op_job_new (CopyMoveJob, parent_window, dbus_data);
7080 job->done_callback = done_callback;
7081 job->done_callback_data = done_callback_data;
7082 job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
7083 job->destination = g_object_ref (target_dir);
7084 /* Need to indicate the destination for the operation notification open
7085 * button. */
7086 nautilus_progress_info_set_destination (((CommonJob *) job)->progress, target_dir);
7087 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
7088
7089 if (!nautilus_file_undo_manager_is_operating ())
7090 {
7091 g_autoptr (GFile) src_dir = NULL;
7092
7093 src_dir = g_file_get_parent (files->data);
7094 job->common.undo_info = nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_CREATE_LINK,
7095 g_list_length (files),
7096 src_dir, target_dir);
7097 }
7098
7099 task = g_task_new (NULL, job->common.cancellable, link_task_done, job);
7100 g_task_set_task_data (task, job, NULL);
7101 g_task_run_in_thread (task, link_task_thread_func);
7102 }
7103
7104
7105 void
nautilus_file_operations_duplicate(GList * files,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusCopyCallback done_callback,gpointer done_callback_data)7106 nautilus_file_operations_duplicate (GList *files,
7107 GtkWindow *parent_window,
7108 NautilusFileOperationsDBusData *dbus_data,
7109 NautilusCopyCallback done_callback,
7110 gpointer done_callback_data)
7111 {
7112 g_autoptr (GTask) task = NULL;
7113 CopyMoveJob *job;
7114 g_autoptr (GFile) parent = NULL;
7115
7116 job = op_job_new (CopyMoveJob, parent_window, dbus_data);
7117 job->done_callback = done_callback;
7118 job->done_callback_data = done_callback_data;
7119 job->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL);
7120 job->destination = NULL;
7121 /* Duplicate files doesn't have a destination, since is the same as source.
7122 * For that set as destination the source parent folder */
7123 parent = g_file_get_parent (files->data);
7124 /* Need to indicate the destination for the operation notification open
7125 * button. */
7126 nautilus_progress_info_set_destination (((CommonJob *) job)->progress, parent);
7127 job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
7128
7129 if (!nautilus_file_undo_manager_is_operating ())
7130 {
7131 g_autoptr (GFile) src_dir = NULL;
7132
7133 src_dir = g_file_get_parent (files->data);
7134 job->common.undo_info =
7135 nautilus_file_undo_info_ext_new (NAUTILUS_FILE_UNDO_OP_DUPLICATE,
7136 g_list_length (files),
7137 src_dir, src_dir);
7138 }
7139
7140 task = g_task_new (NULL, job->common.cancellable, copy_task_done, job);
7141 g_task_set_task_data (task, job, NULL);
7142 g_task_run_in_thread (task, nautilus_file_operations_copy);
7143 }
7144
7145 static void
set_permissions_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)7146 set_permissions_task_done (GObject *source_object,
7147 GAsyncResult *res,
7148 gpointer user_data)
7149 {
7150 SetPermissionsJob *job;
7151
7152 job = user_data;
7153
7154 g_object_unref (job->file);
7155
7156 if (job->done_callback)
7157 {
7158 job->done_callback (!job_aborted ((CommonJob *) job),
7159 job->done_callback_data);
7160 }
7161
7162 finalize_common ((CommonJob *) job);
7163 }
7164
7165 static void
7166 set_permissions_file (SetPermissionsJob *job,
7167 GFile *file,
7168 GFileInfo *info);
7169
7170 static void
set_permissions_contained_files(SetPermissionsJob * job,GFile * file)7171 set_permissions_contained_files (SetPermissionsJob *job,
7172 GFile *file)
7173 {
7174 CommonJob *common;
7175 GFileEnumerator *enumerator;
7176
7177 common = (CommonJob *) job;
7178
7179 enumerator = g_file_enumerate_children (file,
7180 G_FILE_ATTRIBUTE_STANDARD_NAME ","
7181 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
7182 G_FILE_ATTRIBUTE_UNIX_MODE,
7183 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
7184 common->cancellable,
7185 NULL);
7186 if (enumerator)
7187 {
7188 GFileInfo *child_info;
7189
7190 while (!job_aborted (common) &&
7191 (child_info = g_file_enumerator_next_file (enumerator, common->cancellable, NULL)) != NULL)
7192 {
7193 GFile *child;
7194
7195 child = g_file_get_child (file,
7196 g_file_info_get_name (child_info));
7197 set_permissions_file (job, child, child_info);
7198 g_object_unref (child);
7199 g_object_unref (child_info);
7200 }
7201 g_file_enumerator_close (enumerator, common->cancellable, NULL);
7202 g_object_unref (enumerator);
7203 }
7204 }
7205
7206 static void
set_permissions_file(SetPermissionsJob * job,GFile * file,GFileInfo * info)7207 set_permissions_file (SetPermissionsJob *job,
7208 GFile *file,
7209 GFileInfo *info)
7210 {
7211 CommonJob *common;
7212 gboolean free_info;
7213 guint32 current;
7214 guint32 value;
7215 guint32 mask;
7216
7217 common = (CommonJob *) job;
7218
7219 nautilus_progress_info_pulse_progress (common->progress);
7220
7221 free_info = FALSE;
7222 if (info == NULL)
7223 {
7224 free_info = TRUE;
7225 info = g_file_query_info (file,
7226 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
7227 G_FILE_ATTRIBUTE_UNIX_MODE,
7228 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
7229 common->cancellable,
7230 NULL);
7231 /* Ignore errors */
7232 if (info == NULL)
7233 {
7234 return;
7235 }
7236 }
7237
7238 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
7239 {
7240 value = job->dir_permissions;
7241 mask = job->dir_mask;
7242 }
7243 else
7244 {
7245 value = job->file_permissions;
7246 mask = job->file_mask;
7247 }
7248
7249
7250 if (!job_aborted (common) &&
7251 g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE))
7252 {
7253 current = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);
7254
7255 if (common->undo_info != NULL)
7256 {
7257 nautilus_file_undo_info_rec_permissions_add_file (NAUTILUS_FILE_UNDO_INFO_REC_PERMISSIONS (common->undo_info),
7258 file, current);
7259 }
7260
7261 current = (current & ~mask) | value;
7262
7263 g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_MODE,
7264 current, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
7265 common->cancellable, NULL);
7266 }
7267
7268 if (!job_aborted (common) &&
7269 g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
7270 {
7271 set_permissions_contained_files (job, file);
7272 }
7273 if (free_info)
7274 {
7275 g_object_unref (info);
7276 }
7277 }
7278
7279 static void
set_permissions_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)7280 set_permissions_thread_func (GTask *task,
7281 gpointer source_object,
7282 gpointer task_data,
7283 GCancellable *cancellable)
7284 {
7285 SetPermissionsJob *job = task_data;
7286 CommonJob *common;
7287
7288 common = (CommonJob *) job;
7289
7290 nautilus_progress_info_set_status (common->progress,
7291 _("Setting permissions"));
7292
7293 nautilus_progress_info_start (job->common.progress);
7294 set_permissions_contained_files (job, job->file);
7295 }
7296
7297 void
nautilus_file_set_permissions_recursive(const char * directory,guint32 file_permissions,guint32 file_mask,guint32 dir_permissions,guint32 dir_mask,NautilusOpCallback callback,gpointer callback_data)7298 nautilus_file_set_permissions_recursive (const char *directory,
7299 guint32 file_permissions,
7300 guint32 file_mask,
7301 guint32 dir_permissions,
7302 guint32 dir_mask,
7303 NautilusOpCallback callback,
7304 gpointer callback_data)
7305 {
7306 g_autoptr (GTask) task = NULL;
7307 SetPermissionsJob *job;
7308
7309 job = op_job_new (SetPermissionsJob, NULL, NULL);
7310 job->file = g_file_new_for_uri (directory);
7311 job->file_permissions = file_permissions;
7312 job->file_mask = file_mask;
7313 job->dir_permissions = dir_permissions;
7314 job->dir_mask = dir_mask;
7315 job->done_callback = callback;
7316 job->done_callback_data = callback_data;
7317
7318 if (!nautilus_file_undo_manager_is_operating ())
7319 {
7320 job->common.undo_info =
7321 nautilus_file_undo_info_rec_permissions_new (job->file,
7322 file_permissions, file_mask,
7323 dir_permissions, dir_mask);
7324 }
7325
7326 task = g_task_new (NULL, NULL, set_permissions_task_done, job);
7327 g_task_set_task_data (task, job, NULL);
7328 g_task_run_in_thread (task, set_permissions_thread_func);
7329 }
7330
7331 static GList *
location_list_from_uri_list(const GList * uris)7332 location_list_from_uri_list (const GList *uris)
7333 {
7334 const GList *l;
7335 GList *files;
7336 GFile *f;
7337
7338 files = NULL;
7339 for (l = uris; l != NULL; l = l->next)
7340 {
7341 f = g_file_new_for_uri (l->data);
7342 files = g_list_prepend (files, f);
7343 }
7344
7345 return g_list_reverse (files);
7346 }
7347
7348 typedef struct
7349 {
7350 NautilusCopyCallback real_callback;
7351 gpointer real_data;
7352 } MoveTrashCBData;
7353
7354 static void
callback_for_move_to_trash(GHashTable * debuting_uris,gboolean user_cancelled,MoveTrashCBData * data)7355 callback_for_move_to_trash (GHashTable *debuting_uris,
7356 gboolean user_cancelled,
7357 MoveTrashCBData *data)
7358 {
7359 if (data->real_callback)
7360 {
7361 data->real_callback (debuting_uris, !user_cancelled, data->real_data);
7362 }
7363 g_slice_free (MoveTrashCBData, data);
7364 }
7365
7366 void
nautilus_file_operations_copy_move(const GList * item_uris,const char * target_dir,GdkDragAction copy_action,GtkWidget * parent_view,NautilusFileOperationsDBusData * dbus_data,NautilusCopyCallback done_callback,gpointer done_callback_data)7367 nautilus_file_operations_copy_move (const GList *item_uris,
7368 const char *target_dir,
7369 GdkDragAction copy_action,
7370 GtkWidget *parent_view,
7371 NautilusFileOperationsDBusData *dbus_data,
7372 NautilusCopyCallback done_callback,
7373 gpointer done_callback_data)
7374 {
7375 GList *locations;
7376 GList *p;
7377 GFile *dest, *src_dir;
7378 GtkWindow *parent_window;
7379 gboolean target_is_mapping;
7380 gboolean have_nonmapping_source;
7381
7382 dest = NULL;
7383 target_is_mapping = FALSE;
7384 have_nonmapping_source = FALSE;
7385
7386 if (target_dir)
7387 {
7388 dest = g_file_new_for_uri (target_dir);
7389 if (g_file_has_uri_scheme (dest, "burn"))
7390 {
7391 target_is_mapping = TRUE;
7392 }
7393 }
7394
7395 locations = location_list_from_uri_list (item_uris);
7396
7397 for (p = locations; p != NULL; p = p->next)
7398 {
7399 if (!g_file_has_uri_scheme ((GFile * ) p->data, "burn"))
7400 {
7401 have_nonmapping_source = TRUE;
7402 }
7403 }
7404
7405 if (target_is_mapping && have_nonmapping_source && copy_action == GDK_ACTION_MOVE)
7406 {
7407 /* never move to "burn:///", but fall back to copy.
7408 * This is a workaround, because otherwise the source files would be removed.
7409 */
7410 copy_action = GDK_ACTION_COPY;
7411 }
7412
7413 parent_window = NULL;
7414 if (parent_view)
7415 {
7416 parent_window = (GtkWindow *) gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
7417 }
7418
7419 if (copy_action == GDK_ACTION_COPY)
7420 {
7421 src_dir = g_file_get_parent (locations->data);
7422 if (target_dir == NULL ||
7423 (src_dir != NULL &&
7424 g_file_equal (src_dir, dest)))
7425 {
7426 nautilus_file_operations_duplicate (locations,
7427 parent_window,
7428 dbus_data,
7429 done_callback, done_callback_data);
7430 }
7431 else
7432 {
7433 nautilus_file_operations_copy_async (locations,
7434 dest,
7435 parent_window,
7436 dbus_data,
7437 done_callback, done_callback_data);
7438 }
7439 if (src_dir)
7440 {
7441 g_object_unref (src_dir);
7442 }
7443 }
7444 else if (copy_action == GDK_ACTION_MOVE)
7445 {
7446 if (g_file_has_uri_scheme (dest, "trash"))
7447 {
7448 MoveTrashCBData *cb_data;
7449
7450 cb_data = g_slice_new0 (MoveTrashCBData);
7451 cb_data->real_callback = done_callback;
7452 cb_data->real_data = done_callback_data;
7453
7454 nautilus_file_operations_trash_or_delete_async (locations,
7455 parent_window,
7456 dbus_data,
7457 (NautilusDeleteCallback) callback_for_move_to_trash,
7458 cb_data);
7459 }
7460 else
7461 {
7462 nautilus_file_operations_move_async (locations,
7463 dest,
7464 parent_window,
7465 dbus_data,
7466 done_callback, done_callback_data);
7467 }
7468 }
7469 else
7470 {
7471 nautilus_file_operations_link (locations,
7472 dest,
7473 parent_window,
7474 dbus_data,
7475 done_callback, done_callback_data);
7476 }
7477
7478 g_list_free_full (locations, g_object_unref);
7479 if (dest)
7480 {
7481 g_object_unref (dest);
7482 }
7483 }
7484
7485 static void
create_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)7486 create_task_done (GObject *source_object,
7487 GAsyncResult *res,
7488 gpointer user_data)
7489 {
7490 CreateJob *job;
7491
7492 job = user_data;
7493 if (job->done_callback)
7494 {
7495 job->done_callback (job->created_file,
7496 !job_aborted ((CommonJob *) job),
7497 job->done_callback_data);
7498 }
7499
7500 g_object_unref (job->dest_dir);
7501 if (job->src)
7502 {
7503 g_object_unref (job->src);
7504 }
7505 g_free (job->src_data);
7506 g_free (job->filename);
7507 if (job->created_file)
7508 {
7509 g_object_unref (job->created_file);
7510 }
7511
7512 finalize_common ((CommonJob *) job);
7513
7514 nautilus_file_changes_consume_changes (TRUE);
7515 }
7516
7517 static void
create_task_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)7518 create_task_thread_func (GTask *task,
7519 gpointer source_object,
7520 gpointer task_data,
7521 GCancellable *cancellable)
7522 {
7523 CreateJob *job;
7524 CommonJob *common;
7525 int count;
7526 g_autoptr (GFile) dest = NULL;
7527 g_autofree gchar *dest_uri = NULL;
7528 g_autofree char *filename = NULL;
7529 char *filename_base;
7530 g_autofree char *dest_fs_type = NULL;
7531 GError *error;
7532 gboolean res;
7533 gboolean filename_is_utf8;
7534 char *primary, *secondary, *details;
7535 int response;
7536 char *data;
7537 int length;
7538 GFileOutputStream *out;
7539 gboolean handled_invalid_filename;
7540 int max_length, offset;
7541
7542 job = task_data;
7543 common = &job->common;
7544
7545 nautilus_progress_info_start (job->common.progress);
7546
7547 handled_invalid_filename = FALSE;
7548
7549 max_length = nautilus_get_max_child_name_length_for_location (job->dest_dir);
7550
7551 verify_destination (common,
7552 job->dest_dir,
7553 NULL, -1);
7554 if (job_aborted (common))
7555 {
7556 return;
7557 }
7558
7559 filename = g_strdup (job->filename);
7560 filename_is_utf8 = FALSE;
7561 if (filename)
7562 {
7563 filename_is_utf8 = g_utf8_validate (filename, -1, NULL);
7564 }
7565 if (filename == NULL)
7566 {
7567 if (job->make_dir)
7568 {
7569 /* localizers: the initial name of a new folder */
7570 filename = g_strdup (_("Untitled Folder"));
7571 filename_is_utf8 = TRUE; /* Pass in utf8 */
7572 }
7573 else
7574 {
7575 if (job->src != NULL)
7576 {
7577 g_autofree char *basename = NULL;
7578
7579 basename = g_file_get_basename (job->src);
7580 filename = g_strdup_printf ("%s", basename);
7581 }
7582 if (filename == NULL)
7583 {
7584 /* localizers: the initial name of a new empty document */
7585 filename = g_strdup (_("Untitled Document"));
7586 filename_is_utf8 = TRUE; /* Pass in utf8 */
7587 }
7588 }
7589 }
7590
7591 make_file_name_valid_for_dest_fs (filename, dest_fs_type);
7592 if (filename_is_utf8)
7593 {
7594 dest = g_file_get_child_for_display_name (job->dest_dir, filename, NULL);
7595 }
7596 if (dest == NULL)
7597 {
7598 dest = g_file_get_child (job->dest_dir, filename);
7599 }
7600 count = 1;
7601
7602 retry:
7603
7604 error = NULL;
7605 if (job->make_dir)
7606 {
7607 res = g_file_make_directory (dest,
7608 common->cancellable,
7609 &error);
7610
7611 if (res)
7612 {
7613 GFile *real;
7614
7615 real = map_possibly_volatile_file_to_real (dest, common->cancellable, &error);
7616 if (real == NULL)
7617 {
7618 res = FALSE;
7619 }
7620 else
7621 {
7622 g_object_unref (dest);
7623 dest = real;
7624 }
7625 }
7626
7627 if (res && common->undo_info != NULL)
7628 {
7629 nautilus_file_undo_info_create_set_data (NAUTILUS_FILE_UNDO_INFO_CREATE (common->undo_info),
7630 dest, NULL, 0);
7631 }
7632 }
7633 else
7634 {
7635 if (job->src)
7636 {
7637 res = g_file_copy (job->src,
7638 dest,
7639 G_FILE_COPY_TARGET_DEFAULT_PERMS,
7640 common->cancellable,
7641 NULL, NULL,
7642 &error);
7643
7644 if (res)
7645 {
7646 GFile *real;
7647
7648 real = map_possibly_volatile_file_to_real (dest, common->cancellable, &error);
7649 if (real == NULL)
7650 {
7651 res = FALSE;
7652 }
7653 else
7654 {
7655 g_object_unref (dest);
7656 dest = real;
7657 }
7658 }
7659
7660 if (res && common->undo_info != NULL)
7661 {
7662 g_autofree gchar *uri = NULL;
7663
7664 uri = g_file_get_uri (job->src);
7665 nautilus_file_undo_info_create_set_data (NAUTILUS_FILE_UNDO_INFO_CREATE (common->undo_info),
7666 dest, uri, 0);
7667 }
7668 }
7669 else
7670 {
7671 data = "";
7672 length = 0;
7673 if (job->src_data)
7674 {
7675 data = job->src_data;
7676 length = job->length;
7677 }
7678
7679 out = g_file_create (dest,
7680 G_FILE_CREATE_NONE,
7681 common->cancellable,
7682 &error);
7683 if (out)
7684 {
7685 GFile *real;
7686
7687 real = map_possibly_volatile_file_to_real_on_write (dest,
7688 out,
7689 common->cancellable,
7690 &error);
7691 if (real == NULL)
7692 {
7693 res = FALSE;
7694 g_object_unref (out);
7695 }
7696 else
7697 {
7698 g_object_unref (dest);
7699 dest = real;
7700
7701 res = g_output_stream_write_all (G_OUTPUT_STREAM (out),
7702 data, length,
7703 NULL,
7704 common->cancellable,
7705 &error);
7706 if (res)
7707 {
7708 res = g_output_stream_close (G_OUTPUT_STREAM (out),
7709 common->cancellable,
7710 &error);
7711
7712 if (res && common->undo_info != NULL)
7713 {
7714 nautilus_file_undo_info_create_set_data (NAUTILUS_FILE_UNDO_INFO_CREATE (common->undo_info),
7715 dest, data, length);
7716 }
7717 }
7718
7719 /* This will close if the write failed and we didn't close */
7720 g_object_unref (out);
7721 }
7722 }
7723 else
7724 {
7725 res = FALSE;
7726 }
7727 }
7728 }
7729
7730 if (res)
7731 {
7732 job->created_file = g_object_ref (dest);
7733 nautilus_file_changes_queue_file_added (dest);
7734 dest_uri = g_file_get_uri (dest);
7735 gtk_recent_manager_add_item (gtk_recent_manager_get_default (), dest_uri);
7736 }
7737 else
7738 {
7739 g_assert (error != NULL);
7740
7741 if (IS_IO_ERROR (error, INVALID_FILENAME) &&
7742 !handled_invalid_filename)
7743 {
7744 g_autofree gchar *new_filename = NULL;
7745
7746 handled_invalid_filename = TRUE;
7747
7748 g_assert (dest_fs_type == NULL);
7749 dest_fs_type = query_fs_type (job->dest_dir, common->cancellable);
7750
7751 if (count == 1)
7752 {
7753 new_filename = g_strdup (filename);
7754 }
7755 else
7756 {
7757 g_autofree char *filename2 = NULL;
7758 g_autofree char *suffix = NULL;
7759
7760 filename_base = filename;
7761 if (job->src != NULL)
7762 {
7763 g_autoptr (NautilusFile) file = NULL;
7764 file = nautilus_file_get (job->src);
7765 if (!nautilus_file_is_directory (file))
7766 {
7767 filename_base = eel_filename_strip_extension (filename);
7768 }
7769 }
7770
7771 offset = strlen (filename_base);
7772 suffix = g_strdup (filename + offset);
7773
7774 filename2 = g_strdup_printf ("%s %d%s", filename_base, count, suffix);
7775
7776 new_filename = NULL;
7777 if (max_length > 0 && strlen (filename2) > max_length)
7778 {
7779 new_filename = shorten_utf8_string (filename2, strlen (filename2) - max_length);
7780 }
7781
7782 if (new_filename == NULL)
7783 {
7784 new_filename = g_strdup (filename2);
7785 }
7786 }
7787
7788 if (make_file_name_valid_for_dest_fs (new_filename, dest_fs_type))
7789 {
7790 g_object_unref (dest);
7791
7792 if (filename_is_utf8)
7793 {
7794 dest = g_file_get_child_for_display_name (job->dest_dir, new_filename, NULL);
7795 }
7796 if (dest == NULL)
7797 {
7798 dest = g_file_get_child (job->dest_dir, new_filename);
7799 }
7800
7801 g_error_free (error);
7802 goto retry;
7803 }
7804 }
7805
7806 if (IS_IO_ERROR (error, EXISTS))
7807 {
7808 g_autofree char *suffix = NULL;
7809 g_autofree gchar *filename2 = NULL;
7810
7811 g_clear_object (&dest);
7812
7813 filename_base = filename;
7814 if (job->src != NULL)
7815 {
7816 g_autoptr (NautilusFile) file = NULL;
7817
7818 file = nautilus_file_get (job->src);
7819 if (!nautilus_file_is_directory (file))
7820 {
7821 filename_base = eel_filename_strip_extension (filename);
7822 }
7823 }
7824
7825
7826 offset = strlen (filename_base);
7827 suffix = g_strdup (filename + offset);
7828
7829 filename2 = g_strdup_printf ("%s %d%s", filename_base, ++count, suffix);
7830
7831 if (max_length > 0 && strlen (filename2) > max_length)
7832 {
7833 g_autofree char *new_filename = NULL;
7834
7835 new_filename = shorten_utf8_string (filename2, strlen (filename2) - max_length);
7836 if (new_filename != NULL)
7837 {
7838 g_free (filename2);
7839 filename2 = new_filename;
7840 }
7841 }
7842
7843 make_file_name_valid_for_dest_fs (filename2, dest_fs_type);
7844 if (filename_is_utf8)
7845 {
7846 dest = g_file_get_child_for_display_name (job->dest_dir, filename2, NULL);
7847 }
7848 if (dest == NULL)
7849 {
7850 dest = g_file_get_child (job->dest_dir, filename2);
7851 }
7852 g_error_free (error);
7853 goto retry;
7854 }
7855 else if (IS_IO_ERROR (error, CANCELLED))
7856 {
7857 g_error_free (error);
7858 }
7859 /* Other error */
7860 else
7861 {
7862 g_autofree gchar *basename = NULL;
7863 g_autofree gchar *parse_name = NULL;
7864
7865 basename = get_basename (dest);
7866 if (job->make_dir)
7867 {
7868 primary = g_strdup_printf (_("Error while creating directory %s."),
7869 basename);
7870 }
7871 else
7872 {
7873 primary = g_strdup_printf (_("Error while creating file %s."),
7874 basename);
7875 }
7876 parse_name = get_truncated_parse_name (job->dest_dir);
7877 secondary = g_strdup_printf (_("There was an error creating the directory in %s."),
7878 parse_name);
7879
7880 details = error->message;
7881
7882 response = run_warning (common,
7883 primary,
7884 secondary,
7885 details,
7886 FALSE,
7887 CANCEL, SKIP,
7888 NULL);
7889
7890 g_error_free (error);
7891
7892 if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT)
7893 {
7894 abort_job (common);
7895 }
7896 else if (response == 1) /* skip */
7897 { /* do nothing */
7898 }
7899 else
7900 {
7901 g_assert_not_reached ();
7902 }
7903 }
7904 }
7905 }
7906
7907 void
nautilus_file_operations_new_folder(GtkWidget * parent_view,NautilusFileOperationsDBusData * dbus_data,const char * parent_dir,const char * folder_name,NautilusCreateCallback done_callback,gpointer done_callback_data)7908 nautilus_file_operations_new_folder (GtkWidget *parent_view,
7909 NautilusFileOperationsDBusData *dbus_data,
7910 const char *parent_dir,
7911 const char *folder_name,
7912 NautilusCreateCallback done_callback,
7913 gpointer done_callback_data)
7914 {
7915 g_autoptr (GTask) task = NULL;
7916 CreateJob *job;
7917 GtkWindow *parent_window;
7918
7919 parent_window = NULL;
7920 if (parent_view)
7921 {
7922 parent_window = (GtkWindow *) gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
7923 }
7924
7925 job = op_job_new (CreateJob, parent_window, dbus_data);
7926 job->done_callback = done_callback;
7927 job->done_callback_data = done_callback_data;
7928 job->dest_dir = g_file_new_for_uri (parent_dir);
7929 job->filename = g_strdup (folder_name);
7930 job->make_dir = TRUE;
7931
7932 if (!nautilus_file_undo_manager_is_operating ())
7933 {
7934 job->common.undo_info = nautilus_file_undo_info_create_new (NAUTILUS_FILE_UNDO_OP_CREATE_FOLDER);
7935 }
7936
7937 task = g_task_new (NULL, job->common.cancellable, create_task_done, job);
7938 g_task_set_task_data (task, job, NULL);
7939 g_task_run_in_thread (task, create_task_thread_func);
7940 }
7941
7942 void
nautilus_file_operations_new_file_from_template(GtkWidget * parent_view,const char * parent_dir,const char * target_filename,const char * template_uri,NautilusCreateCallback done_callback,gpointer done_callback_data)7943 nautilus_file_operations_new_file_from_template (GtkWidget *parent_view,
7944 const char *parent_dir,
7945 const char *target_filename,
7946 const char *template_uri,
7947 NautilusCreateCallback done_callback,
7948 gpointer done_callback_data)
7949 {
7950 g_autoptr (GTask) task = NULL;
7951 CreateJob *job;
7952 GtkWindow *parent_window;
7953
7954 parent_window = NULL;
7955 if (parent_view)
7956 {
7957 parent_window = (GtkWindow *) gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
7958 }
7959
7960 job = op_job_new (CreateJob, parent_window, NULL);
7961 job->done_callback = done_callback;
7962 job->done_callback_data = done_callback_data;
7963 job->dest_dir = g_file_new_for_uri (parent_dir);
7964 job->filename = g_strdup (target_filename);
7965
7966 if (template_uri)
7967 {
7968 job->src = g_file_new_for_uri (template_uri);
7969 }
7970
7971 if (!nautilus_file_undo_manager_is_operating ())
7972 {
7973 job->common.undo_info = nautilus_file_undo_info_create_new (NAUTILUS_FILE_UNDO_OP_CREATE_FILE_FROM_TEMPLATE);
7974 }
7975
7976 task = g_task_new (NULL, job->common.cancellable, create_task_done, job);
7977 g_task_set_task_data (task, job, NULL);
7978 g_task_run_in_thread (task, create_task_thread_func);
7979 }
7980
7981 void
nautilus_file_operations_new_file(GtkWidget * parent_view,const char * parent_dir,const char * target_filename,const char * initial_contents,int length,NautilusCreateCallback done_callback,gpointer done_callback_data)7982 nautilus_file_operations_new_file (GtkWidget *parent_view,
7983 const char *parent_dir,
7984 const char *target_filename,
7985 const char *initial_contents,
7986 int length,
7987 NautilusCreateCallback done_callback,
7988 gpointer done_callback_data)
7989 {
7990 g_autoptr (GTask) task = NULL;
7991 CreateJob *job;
7992 GtkWindow *parent_window;
7993
7994 parent_window = NULL;
7995 if (parent_view)
7996 {
7997 parent_window = (GtkWindow *) gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
7998 }
7999
8000 job = op_job_new (CreateJob, parent_window, NULL);
8001 job->done_callback = done_callback;
8002 job->done_callback_data = done_callback_data;
8003 job->dest_dir = g_file_new_for_uri (parent_dir);
8004 job->src_data = g_memdup (initial_contents, length);
8005 job->length = length;
8006 job->filename = g_strdup (target_filename);
8007
8008 if (!nautilus_file_undo_manager_is_operating ())
8009 {
8010 job->common.undo_info = nautilus_file_undo_info_create_new (NAUTILUS_FILE_UNDO_OP_CREATE_EMPTY_FILE);
8011 }
8012
8013 task = g_task_new (NULL, job->common.cancellable, create_task_done, job);
8014 g_task_set_task_data (task, job, NULL);
8015 g_task_run_in_thread (task, create_task_thread_func);
8016 }
8017
8018
8019
8020 static void
delete_trash_file(CommonJob * job,GFile * file,gboolean del_file,gboolean del_children)8021 delete_trash_file (CommonJob *job,
8022 GFile *file,
8023 gboolean del_file,
8024 gboolean del_children)
8025 {
8026 GFileInfo *info;
8027 GFile *child;
8028 GFileEnumerator *enumerator;
8029
8030 if (job_aborted (job))
8031 {
8032 return;
8033 }
8034
8035 if (del_children)
8036 {
8037 gboolean should_recurse;
8038
8039 /* The g_file_delete operation works differently for locations provided
8040 * by the trash backend as it prevents modifications of trashed items
8041 * For that reason, it is enough to call g_file_delete on top-level
8042 * items only.
8043 */
8044 should_recurse = !g_file_has_uri_scheme (file, "trash");
8045
8046 enumerator = g_file_enumerate_children (file,
8047 G_FILE_ATTRIBUTE_STANDARD_NAME ","
8048 G_FILE_ATTRIBUTE_STANDARD_TYPE,
8049 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
8050 job->cancellable,
8051 NULL);
8052 if (enumerator)
8053 {
8054 while (!job_aborted (job) &&
8055 (info = g_file_enumerator_next_file (enumerator, job->cancellable, NULL)) != NULL)
8056 {
8057 gboolean is_dir;
8058
8059 child = g_file_get_child (file,
8060 g_file_info_get_name (info));
8061 is_dir = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
8062
8063 delete_trash_file (job, child, TRUE, should_recurse && is_dir);
8064 g_object_unref (child);
8065 g_object_unref (info);
8066 }
8067 g_file_enumerator_close (enumerator, job->cancellable, NULL);
8068 g_object_unref (enumerator);
8069 }
8070 }
8071
8072 if (!job_aborted (job) && del_file)
8073 {
8074 g_file_delete (file, job->cancellable, NULL);
8075 }
8076 }
8077
8078 static void
empty_trash_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)8079 empty_trash_task_done (GObject *source_object,
8080 GAsyncResult *res,
8081 gpointer user_data)
8082 {
8083 EmptyTrashJob *job;
8084
8085 job = user_data;
8086
8087 g_list_free_full (job->trash_dirs, g_object_unref);
8088
8089 if (job->done_callback)
8090 {
8091 job->done_callback (!job_aborted ((CommonJob *) job),
8092 job->done_callback_data);
8093 }
8094
8095 finalize_common ((CommonJob *) job);
8096 }
8097
8098 static void
empty_trash_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)8099 empty_trash_thread_func (GTask *task,
8100 gpointer source_object,
8101 gpointer task_data,
8102 GCancellable *cancellable)
8103 {
8104 EmptyTrashJob *job = task_data;
8105 CommonJob *common;
8106 GList *l;
8107 gboolean confirmed;
8108
8109 common = (CommonJob *) job;
8110
8111 nautilus_progress_info_start (job->common.progress);
8112
8113 if (job->should_confirm)
8114 {
8115 confirmed = confirm_empty_trash (common);
8116 }
8117 else
8118 {
8119 confirmed = TRUE;
8120 }
8121 if (confirmed)
8122 {
8123 for (l = job->trash_dirs;
8124 l != NULL && !job_aborted (common);
8125 l = l->next)
8126 {
8127 delete_trash_file (common, l->data, FALSE, TRUE);
8128 }
8129 }
8130 }
8131
8132 void
nautilus_file_operations_empty_trash(GtkWidget * parent_view,gboolean ask_confirmation,NautilusFileOperationsDBusData * dbus_data)8133 nautilus_file_operations_empty_trash (GtkWidget *parent_view,
8134 gboolean ask_confirmation,
8135 NautilusFileOperationsDBusData *dbus_data)
8136 {
8137 g_autoptr (GTask) task = NULL;
8138 EmptyTrashJob *job;
8139 GtkWindow *parent_window;
8140
8141 parent_window = NULL;
8142 if (parent_view)
8143 {
8144 parent_window = (GtkWindow *) gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
8145 }
8146
8147 job = op_job_new (EmptyTrashJob, parent_window, dbus_data);
8148 job->trash_dirs = g_list_prepend (job->trash_dirs,
8149 g_file_new_for_uri ("trash:"));
8150 job->should_confirm = ask_confirmation;
8151
8152 inhibit_power_manager ((CommonJob *) job, _("Emptying Trash"));
8153
8154 task = g_task_new (NULL, NULL, empty_trash_task_done, job);
8155 g_task_set_task_data (task, job, NULL);
8156 g_task_run_in_thread (task, empty_trash_thread_func);
8157 }
8158
8159 static void
extract_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)8160 extract_task_done (GObject *source_object,
8161 GAsyncResult *res,
8162 gpointer user_data)
8163 {
8164 ExtractJob *extract_job;
8165
8166 extract_job = user_data;
8167
8168 if (extract_job->done_callback)
8169 {
8170 extract_job->done_callback (extract_job->output_files,
8171 extract_job->done_callback_data);
8172 }
8173
8174 g_list_free_full (extract_job->source_files, g_object_unref);
8175 g_list_free_full (extract_job->output_files, g_object_unref);
8176 g_object_unref (extract_job->destination_directory);
8177
8178 finalize_common ((CommonJob *) extract_job);
8179
8180 nautilus_file_changes_consume_changes (TRUE);
8181 }
8182
8183 static GFile *
extract_job_on_decide_destination(AutoarExtractor * extractor,GFile * destination,GList * files,gpointer user_data)8184 extract_job_on_decide_destination (AutoarExtractor *extractor,
8185 GFile *destination,
8186 GList *files,
8187 gpointer user_data)
8188 {
8189 ExtractJob *extract_job = user_data;
8190 GFile *decided_destination;
8191 g_autofree char *basename = NULL;
8192
8193 nautilus_progress_info_set_details (extract_job->common.progress,
8194 _("Verifying destination"));
8195
8196 basename = g_file_get_basename (destination);
8197 decided_destination = nautilus_generate_unique_file_in_directory (extract_job->destination_directory,
8198 basename);
8199
8200 if (job_aborted ((CommonJob *) extract_job))
8201 {
8202 g_object_unref (decided_destination);
8203 return NULL;
8204 }
8205
8206 /* The extract_job->destination_decided variable signalizes whether the
8207 * extract_job->output_files list already contains the final location as
8208 * its first link. There is no way to get this over the AutoarExtractor
8209 * API currently.
8210 */
8211 extract_job->output_files = g_list_prepend (extract_job->output_files,
8212 decided_destination);
8213 extract_job->destination_decided = TRUE;
8214
8215 return g_object_ref (decided_destination);
8216 }
8217
8218 static void
extract_job_on_progress(AutoarExtractor * extractor,guint64 archive_current_decompressed_size,guint archive_current_decompressed_files,gpointer user_data)8219 extract_job_on_progress (AutoarExtractor *extractor,
8220 guint64 archive_current_decompressed_size,
8221 guint archive_current_decompressed_files,
8222 gpointer user_data)
8223 {
8224 ExtractJob *extract_job = user_data;
8225 CommonJob *common = user_data;
8226 GFile *source_file;
8227 char *details;
8228 double elapsed;
8229 double transfer_rate;
8230 int remaining_time;
8231 guint64 archive_total_decompressed_size;
8232 gdouble archive_weight;
8233 gdouble archive_decompress_progress;
8234 guint64 job_completed_size;
8235 gdouble job_progress;
8236 g_autofree gchar *basename = NULL;
8237 g_autofree gchar *formatted_size_job_completed_size = NULL;
8238 g_autofree gchar *formatted_size_total_compressed_size = NULL;
8239
8240 source_file = autoar_extractor_get_source_file (extractor);
8241
8242 basename = get_basename (source_file);
8243 nautilus_progress_info_take_status (common->progress,
8244 g_strdup_printf (_("Extracting “%s”"),
8245 basename));
8246
8247 archive_total_decompressed_size = autoar_extractor_get_total_size (extractor);
8248
8249 archive_decompress_progress = (gdouble) archive_current_decompressed_size /
8250 (gdouble) archive_total_decompressed_size;
8251
8252 archive_weight = 0;
8253 if (extract_job->total_compressed_size)
8254 {
8255 archive_weight = (gdouble) extract_job->archive_compressed_size /
8256 (gdouble) extract_job->total_compressed_size;
8257 }
8258
8259 job_progress = archive_decompress_progress * archive_weight + extract_job->base_progress;
8260
8261 elapsed = g_timer_elapsed (common->time, NULL);
8262
8263 transfer_rate = 0;
8264 remaining_time = -1;
8265
8266 job_completed_size = job_progress * extract_job->total_compressed_size;
8267
8268 if (elapsed > 0)
8269 {
8270 transfer_rate = job_completed_size / elapsed;
8271 }
8272 if (transfer_rate > 0)
8273 {
8274 remaining_time = (extract_job->total_compressed_size - job_completed_size) /
8275 transfer_rate;
8276 }
8277
8278 formatted_size_job_completed_size = g_format_size (job_completed_size);
8279 formatted_size_total_compressed_size = g_format_size (extract_job->total_compressed_size);
8280 if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE ||
8281 transfer_rate == 0)
8282 {
8283 /* To translators: %s will expand to a size like "2 bytes" or
8284 * "3 MB", so something like "4 kb / 4 MB"
8285 */
8286 details = g_strdup_printf (_("%s / %s"), formatted_size_job_completed_size,
8287 formatted_size_total_compressed_size);
8288 }
8289 else
8290 {
8291 g_autofree gchar *formatted_time = NULL;
8292 g_autofree gchar *formatted_size_transfer_rate = NULL;
8293
8294 formatted_time = get_formatted_time (remaining_time);
8295 formatted_size_transfer_rate = g_format_size ((goffset) transfer_rate);
8296 /* To translators: %s will expand to a size like "2 bytes" or
8297 * "3 MB", %s to a time duration like "2 minutes". So the whole
8298 * thing will be something like
8299 * "2 kb / 4 MB -- 2 hours left (4kb/sec)"
8300 *
8301 * The singular/plural form will be used depending on the
8302 * remaining time (i.e. the %s argument).
8303 */
8304 details = g_strdup_printf (ngettext ("%s / %s \xE2\x80\x94 %s left (%s/sec)",
8305 "%s / %s \xE2\x80\x94 %s left (%s/sec)",
8306 seconds_count_format_time_units (remaining_time)),
8307 formatted_size_job_completed_size,
8308 formatted_size_total_compressed_size,
8309 formatted_time,
8310 formatted_size_transfer_rate);
8311 }
8312
8313 nautilus_progress_info_take_details (common->progress, details);
8314
8315 if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE)
8316 {
8317 nautilus_progress_info_set_remaining_time (common->progress,
8318 remaining_time);
8319 nautilus_progress_info_set_elapsed_time (common->progress,
8320 elapsed);
8321 }
8322
8323 nautilus_progress_info_set_progress (common->progress, job_progress, 1);
8324 }
8325
8326 static void
extract_job_on_error(AutoarExtractor * extractor,GError * error,gpointer user_data)8327 extract_job_on_error (AutoarExtractor *extractor,
8328 GError *error,
8329 gpointer user_data)
8330 {
8331 ExtractJob *extract_job = user_data;
8332 GFile *source_file;
8333 GFile *destination;
8334 gint response_id;
8335 gint remaining_files;
8336 g_autofree gchar *basename = NULL;
8337
8338 source_file = autoar_extractor_get_source_file (extractor);
8339
8340 if (IS_IO_ERROR (error, NOT_SUPPORTED))
8341 {
8342 handle_unsupported_compressed_file (extract_job->common.parent_window,
8343 source_file);
8344
8345 return;
8346 }
8347
8348 extract_job->extraction_failed = TRUE;
8349
8350 /* It is safe to use extract_job->output_files->data only when the
8351 * extract_job->destination_decided variable was set, see comment in the
8352 * extract_job_on_decide_destination function.
8353 */
8354 if (extract_job->destination_decided)
8355 {
8356 destination = extract_job->output_files->data;
8357 delete_file_recursively (destination, NULL, NULL, NULL);
8358 extract_job->output_files = g_list_delete_link (extract_job->output_files,
8359 extract_job->output_files);
8360 g_object_unref (destination);
8361 }
8362
8363 if (extract_job->common.skip_all_error)
8364 {
8365 return;
8366 }
8367
8368 basename = get_basename (source_file);
8369 nautilus_progress_info_take_status (extract_job->common.progress,
8370 g_strdup_printf (_("Error extracting “%s”"),
8371 basename));
8372
8373 remaining_files = g_list_length (g_list_find_custom (extract_job->source_files,
8374 source_file,
8375 (GCompareFunc) g_file_equal)) - 1;
8376 response_id = run_cancel_or_skip_warning ((CommonJob *) extract_job,
8377 g_strdup_printf (_("There was an error while extracting “%s”."),
8378 basename),
8379 g_strdup (error->message),
8380 NULL,
8381 extract_job->total_files,
8382 remaining_files);
8383
8384 if (response_id == 0 || response_id == GTK_RESPONSE_DELETE_EVENT)
8385 {
8386 abort_job ((CommonJob *) extract_job);
8387 }
8388 else if (response_id == 1)
8389 {
8390 extract_job->common.skip_all_error = TRUE;
8391 }
8392 }
8393
8394 static void
extract_job_on_completed(AutoarExtractor * extractor,gpointer user_data)8395 extract_job_on_completed (AutoarExtractor *extractor,
8396 gpointer user_data)
8397 {
8398 ExtractJob *extract_job = user_data;
8399 GFile *output_file;
8400
8401 output_file = G_FILE (extract_job->output_files->data);
8402
8403 nautilus_file_changes_queue_file_added (output_file);
8404 }
8405
8406 typedef struct
8407 {
8408 ExtractJob *extract_job;
8409 AutoarExtractor *extractor;
8410 gchar *passphrase;
8411 GtkWidget *passphrase_entry;
8412 GMutex mutex;
8413 GCond cond;
8414 gboolean completed;
8415 } PassphraseRequestData;
8416
8417 static void
on_request_passphrase_cb(GtkDialog * dialog,gint response_id,gpointer user_data)8418 on_request_passphrase_cb (GtkDialog *dialog,
8419 gint response_id,
8420 gpointer user_data)
8421 {
8422 PassphraseRequestData *data = user_data;
8423
8424 if (response_id == GTK_RESPONSE_CANCEL ||
8425 response_id == GTK_RESPONSE_DELETE_EVENT)
8426 {
8427 abort_job ((CommonJob *) data->extract_job);
8428 }
8429 else
8430 {
8431 data->passphrase = g_strdup (gtk_entry_get_text (GTK_ENTRY (data->passphrase_entry)));
8432 }
8433
8434 data->completed = TRUE;
8435
8436 gtk_widget_destroy (GTK_WIDGET (dialog));
8437
8438 g_cond_signal (&data->cond);
8439 g_mutex_unlock (&data->mutex);
8440 }
8441
8442 static gboolean
run_passphrase_dialog(gpointer user_data)8443 run_passphrase_dialog (gpointer user_data)
8444 {
8445 PassphraseRequestData *data = user_data;
8446 g_autofree gchar *label_str = NULL;
8447 g_autofree gchar *basename = NULL;
8448 GtkWidget *dialog;
8449 GtkWidget *entry;
8450 GtkWidget *label;
8451 GtkWidget *box;
8452 GFile *source_file;
8453
8454 g_mutex_lock (&data->mutex);
8455
8456 dialog = gtk_dialog_new_with_buttons (_("Password Required"),
8457 data->extract_job->common.parent_window,
8458 GTK_DIALOG_USE_HEADER_BAR | GTK_DIALOG_MODAL,
8459 _("Cancel"), GTK_RESPONSE_CANCEL,
8460 _("Extract"), GTK_RESPONSE_OK,
8461 NULL);
8462 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
8463 source_file = autoar_extractor_get_source_file (data->extractor);
8464 basename = get_basename (source_file);
8465
8466 box = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
8467 gtk_widget_set_margin_start (box, 20);
8468 gtk_widget_set_margin_end (box, 20);
8469 gtk_widget_set_margin_top (box, 20);
8470 gtk_widget_set_margin_bottom (box, 20);
8471
8472 label_str = g_strdup_printf (_("“%s” is password-protected."), basename);
8473 label = gtk_label_new (label_str);
8474 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
8475 gtk_label_set_max_width_chars (GTK_LABEL (label), 60);
8476 gtk_container_add (GTK_CONTAINER (box), label);
8477
8478 entry = gtk_entry_new ();
8479 gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
8480 gtk_widget_set_valign (entry, GTK_ALIGN_END);
8481 gtk_widget_set_vexpand (entry, TRUE);
8482 gtk_entry_set_placeholder_text (GTK_ENTRY (entry), _("Enter password…"));
8483 gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
8484 gtk_entry_set_input_purpose (GTK_ENTRY (entry), GTK_INPUT_PURPOSE_PASSWORD);
8485 gtk_container_add (GTK_CONTAINER (box), entry);
8486
8487 data->passphrase_entry = entry;
8488 g_signal_connect (dialog, "response", G_CALLBACK (on_request_passphrase_cb), data);
8489 gtk_widget_show_all (dialog);
8490
8491 return G_SOURCE_REMOVE;
8492 }
8493
8494 static gchar *
extract_job_on_request_passphrase(AutoarExtractor * extractor,gpointer user_data)8495 extract_job_on_request_passphrase (AutoarExtractor *extractor,
8496 gpointer user_data)
8497 {
8498 PassphraseRequestData *data;
8499 ExtractJob *extract_job = user_data;
8500 gchar *passphrase;
8501
8502 data = g_new0 (PassphraseRequestData, 1);
8503 g_mutex_init (&data->mutex);
8504 g_cond_init (&data->cond);
8505 data->extract_job = extract_job;
8506 data->extractor = extractor;
8507
8508 g_mutex_lock (&data->mutex);
8509
8510 g_main_context_invoke (NULL,
8511 run_passphrase_dialog,
8512 data);
8513
8514 while (!data->completed)
8515 {
8516 g_cond_wait (&data->cond, &data->mutex);
8517 }
8518
8519 g_mutex_unlock (&data->mutex);
8520 g_mutex_clear (&data->mutex);
8521 g_cond_clear (&data->cond);
8522
8523 passphrase = g_steal_pointer (&data->passphrase);
8524 g_free (data);
8525
8526 return passphrase;
8527 }
8528
8529 static void
extract_job_on_scanned(AutoarExtractor * extractor,guint total_files,gpointer user_data)8530 extract_job_on_scanned (AutoarExtractor *extractor,
8531 guint total_files,
8532 gpointer user_data)
8533 {
8534 guint64 total_size;
8535 ExtractJob *extract_job;
8536 GFile *source_file;
8537 g_autofree gchar *basename = NULL;
8538 GFileInfo *fsinfo;
8539 guint64 free_size;
8540
8541 extract_job = user_data;
8542 total_size = autoar_extractor_get_total_size (extractor);
8543 source_file = autoar_extractor_get_source_file (extractor);
8544 basename = get_basename (source_file);
8545
8546 fsinfo = g_file_query_filesystem_info (source_file,
8547 G_FILE_ATTRIBUTE_FILESYSTEM_FREE ","
8548 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,
8549 extract_job->common.cancellable,
8550 NULL);
8551 free_size = g_file_info_get_attribute_uint64 (fsinfo,
8552 G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
8553
8554 /* FIXME: G_MAXUINT64 is the value used by autoar when the file size cannot
8555 * be determined. Ideally an API should be used instead.
8556 */
8557 if (total_size != G_MAXUINT64 && total_size > free_size)
8558 {
8559 nautilus_progress_info_take_status (extract_job->common.progress,
8560 g_strdup_printf (_("Error extracting “%s”"),
8561 basename));
8562 run_error (&extract_job->common,
8563 g_strdup_printf (_("Not enough free space to extract %s"), basename),
8564 NULL,
8565 NULL,
8566 FALSE,
8567 CANCEL,
8568 NULL);
8569
8570 abort_job ((CommonJob *) extract_job);
8571 }
8572 }
8573
8574 static void
report_extract_final_progress(ExtractJob * extract_job)8575 report_extract_final_progress (ExtractJob *extract_job)
8576 {
8577 char *status;
8578 g_autofree gchar *basename_dest = NULL;
8579 g_autofree gchar *formatted_size = NULL;
8580
8581 nautilus_progress_info_set_destination (extract_job->common.progress,
8582 extract_job->destination_directory);
8583 basename_dest = get_basename (extract_job->destination_directory);
8584
8585 /* The g_list_length function is used intentionally here instead of the
8586 * extract_job->total_files variable to avoid printing wrong basename in
8587 * the case of skipped files.
8588 */
8589 if (g_list_length (extract_job->source_files) == 1)
8590 {
8591 GFile *source_file;
8592 g_autofree gchar *basename = NULL;
8593
8594 source_file = G_FILE (extract_job->source_files->data);
8595 basename = get_basename (source_file);
8596 status = g_strdup_printf (_("Extracted “%s” to “%s”"),
8597 basename,
8598 basename_dest);
8599 }
8600 else
8601 {
8602 status = g_strdup_printf (ngettext ("Extracted %'d file to “%s”",
8603 "Extracted %'d files to “%s”",
8604 extract_job->total_files),
8605 extract_job->total_files,
8606 basename_dest);
8607 }
8608
8609 nautilus_progress_info_take_status (extract_job->common.progress,
8610 status);
8611 formatted_size = g_format_size (extract_job->total_compressed_size);
8612 nautilus_progress_info_take_details (extract_job->common.progress,
8613 g_strdup_printf (_("%s / %s"),
8614 formatted_size,
8615 formatted_size));
8616
8617 nautilus_progress_info_set_progress (extract_job->common.progress, 1, 1);
8618 }
8619
8620 static void
extract_task_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)8621 extract_task_thread_func (GTask *task,
8622 gpointer source_object,
8623 gpointer task_data,
8624 GCancellable *cancellable)
8625 {
8626 ExtractJob *extract_job = task_data;
8627 GList *l;
8628 g_autofree guint64 *archive_compressed_sizes = NULL;
8629 gint i;
8630
8631 g_timer_start (extract_job->common.time);
8632
8633 nautilus_progress_info_start (extract_job->common.progress);
8634
8635 nautilus_progress_info_set_details (extract_job->common.progress,
8636 _("Preparing to extract"));
8637
8638 extract_job->total_files = g_list_length (extract_job->source_files);
8639
8640 archive_compressed_sizes = g_malloc0_n (extract_job->total_files,
8641 sizeof (guint64));
8642 extract_job->total_compressed_size = 0;
8643
8644 for (l = extract_job->source_files, i = 0;
8645 l != NULL && !job_aborted ((CommonJob *) extract_job);
8646 l = l->next, i++)
8647 {
8648 GFile *source_file;
8649 g_autoptr (GFileInfo) info = NULL;
8650
8651 source_file = G_FILE (l->data);
8652 info = g_file_query_info (source_file,
8653 G_FILE_ATTRIBUTE_STANDARD_SIZE,
8654 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
8655 extract_job->common.cancellable,
8656 NULL);
8657
8658 if (info)
8659 {
8660 archive_compressed_sizes[i] = g_file_info_get_size (info);
8661 extract_job->total_compressed_size += archive_compressed_sizes[i];
8662 }
8663 }
8664
8665 extract_job->base_progress = 0;
8666
8667 for (l = extract_job->source_files, i = 0;
8668 l != NULL && !job_aborted ((CommonJob *) extract_job);
8669 l = l->next, i++)
8670 {
8671 g_autoptr (AutoarExtractor) extractor = NULL;
8672
8673 extractor = autoar_extractor_new (G_FILE (l->data),
8674 extract_job->destination_directory);
8675
8676 autoar_extractor_set_notify_interval (extractor,
8677 PROGRESS_NOTIFY_INTERVAL);
8678 g_signal_connect (extractor, "scanned",
8679 G_CALLBACK (extract_job_on_scanned),
8680 extract_job);
8681 g_signal_connect (extractor, "error",
8682 G_CALLBACK (extract_job_on_error),
8683 extract_job);
8684 g_signal_connect (extractor, "decide-destination",
8685 G_CALLBACK (extract_job_on_decide_destination),
8686 extract_job);
8687 g_signal_connect (extractor, "progress",
8688 G_CALLBACK (extract_job_on_progress),
8689 extract_job);
8690 g_signal_connect (extractor, "completed",
8691 G_CALLBACK (extract_job_on_completed),
8692 extract_job);
8693 g_signal_connect (extractor, "request-passphrase",
8694 G_CALLBACK (extract_job_on_request_passphrase),
8695 extract_job);
8696
8697 extract_job->archive_compressed_size = archive_compressed_sizes[i];
8698 extract_job->destination_decided = FALSE;
8699 extract_job->extraction_failed = FALSE;
8700
8701 autoar_extractor_start (extractor,
8702 extract_job->common.cancellable);
8703
8704 g_signal_handlers_disconnect_by_data (extractor,
8705 extract_job);
8706
8707 if (!extract_job->extraction_failed)
8708 {
8709 extract_job->base_progress += (gdouble) extract_job->archive_compressed_size /
8710 (gdouble) extract_job->total_compressed_size;
8711 }
8712 else
8713 {
8714 extract_job->total_files--;
8715 extract_job->base_progress *= extract_job->total_compressed_size;
8716 extract_job->total_compressed_size -= extract_job->archive_compressed_size;
8717 extract_job->base_progress /= extract_job->total_compressed_size;
8718 }
8719 }
8720
8721 if (!job_aborted ((CommonJob *) extract_job))
8722 {
8723 report_extract_final_progress (extract_job);
8724 }
8725
8726 if (extract_job->common.undo_info)
8727 {
8728 if (extract_job->output_files)
8729 {
8730 NautilusFileUndoInfoExtract *undo_info;
8731
8732 undo_info = NAUTILUS_FILE_UNDO_INFO_EXTRACT (extract_job->common.undo_info);
8733
8734 nautilus_file_undo_info_extract_set_outputs (undo_info,
8735 extract_job->output_files);
8736 }
8737 else
8738 {
8739 /* There is nothing to undo if there is no output */
8740 g_clear_object (&extract_job->common.undo_info);
8741 }
8742 }
8743 }
8744
8745 void
nautilus_file_operations_extract_files(GList * files,GFile * destination_directory,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusExtractCallback done_callback,gpointer done_callback_data)8746 nautilus_file_operations_extract_files (GList *files,
8747 GFile *destination_directory,
8748 GtkWindow *parent_window,
8749 NautilusFileOperationsDBusData *dbus_data,
8750 NautilusExtractCallback done_callback,
8751 gpointer done_callback_data)
8752 {
8753 ExtractJob *extract_job;
8754 g_autoptr (GTask) task = NULL;
8755
8756 extract_job = op_job_new (ExtractJob, parent_window, dbus_data);
8757 extract_job->source_files = g_list_copy_deep (files,
8758 (GCopyFunc) g_object_ref,
8759 NULL);
8760 extract_job->destination_directory = g_object_ref (destination_directory);
8761 extract_job->done_callback = done_callback;
8762 extract_job->done_callback_data = done_callback_data;
8763
8764 inhibit_power_manager ((CommonJob *) extract_job, _("Extracting Files"));
8765
8766 if (!nautilus_file_undo_manager_is_operating ())
8767 {
8768 extract_job->common.undo_info = nautilus_file_undo_info_extract_new (files,
8769 destination_directory);
8770 }
8771
8772 task = g_task_new (NULL, extract_job->common.cancellable,
8773 extract_task_done, extract_job);
8774 g_task_set_task_data (task, extract_job, NULL);
8775 g_task_run_in_thread (task, extract_task_thread_func);
8776 }
8777
8778 static void
compress_task_done(GObject * source_object,GAsyncResult * res,gpointer user_data)8779 compress_task_done (GObject *source_object,
8780 GAsyncResult *res,
8781 gpointer user_data)
8782 {
8783 CompressJob *compress_job = user_data;
8784
8785 if (compress_job->done_callback)
8786 {
8787 compress_job->done_callback (compress_job->output_file,
8788 compress_job->success,
8789 compress_job->done_callback_data);
8790 }
8791
8792 g_object_unref (compress_job->output_file);
8793 g_list_free_full (compress_job->source_files, g_object_unref);
8794 g_free (compress_job->passphrase);
8795
8796 finalize_common ((CommonJob *) compress_job);
8797
8798 nautilus_file_changes_consume_changes (TRUE);
8799 }
8800
8801 static void
compress_job_on_progress(AutoarCompressor * compressor,guint64 completed_size,guint completed_files,gpointer user_data)8802 compress_job_on_progress (AutoarCompressor *compressor,
8803 guint64 completed_size,
8804 guint completed_files,
8805 gpointer user_data)
8806 {
8807 CompressJob *compress_job = user_data;
8808 CommonJob *common = user_data;
8809 char *status;
8810 char *details;
8811 int files_left;
8812 double elapsed;
8813 double transfer_rate;
8814 int remaining_time;
8815 g_autofree gchar *basename_output_file = NULL;
8816
8817 files_left = compress_job->total_files - completed_files;
8818 basename_output_file = get_basename (compress_job->output_file);
8819 if (compress_job->total_files == 1)
8820 {
8821 g_autofree gchar *basename_data = NULL;
8822
8823 basename_data = get_basename (G_FILE (compress_job->source_files->data));
8824 status = g_strdup_printf (_("Compressing “%s” into “%s”"),
8825 basename_data,
8826 basename_output_file);
8827 }
8828 else
8829 {
8830 status = g_strdup_printf (ngettext ("Compressing %'d file into “%s”",
8831 "Compressing %'d files into “%s”",
8832 compress_job->total_files),
8833 compress_job->total_files,
8834 basename_output_file);
8835 }
8836 nautilus_progress_info_take_status (common->progress, status);
8837
8838 elapsed = g_timer_elapsed (common->time, NULL);
8839
8840 transfer_rate = 0;
8841 remaining_time = -1;
8842
8843 if (elapsed > 0)
8844 {
8845 if (completed_size > 0)
8846 {
8847 transfer_rate = completed_size / elapsed;
8848 remaining_time = (compress_job->total_size - completed_size) / transfer_rate;
8849 }
8850 else if (completed_files > 0)
8851 {
8852 transfer_rate = completed_files / elapsed;
8853 remaining_time = (compress_job->total_files - completed_files) / transfer_rate;
8854 }
8855 }
8856
8857 if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE ||
8858 transfer_rate == 0)
8859 {
8860 if (compress_job->total_files == 1)
8861 {
8862 g_autofree gchar *formatted_size_completed_size = NULL;
8863 g_autofree gchar *formatted_size_total_size = NULL;
8864
8865 formatted_size_completed_size = g_format_size (completed_size);
8866 formatted_size_total_size = g_format_size (compress_job->total_size);
8867 /* To translators: %s will expand to a size like "2 bytes" or "3 MB", so something like "4 kb / 4 MB" */
8868 details = g_strdup_printf (_("%s / %s"), formatted_size_completed_size,
8869 formatted_size_total_size);
8870 }
8871 else
8872 {
8873 details = g_strdup_printf (_("%'d / %'d"),
8874 files_left > 0 ? completed_files + 1 : completed_files,
8875 compress_job->total_files);
8876 }
8877 }
8878 else
8879 {
8880 if (compress_job->total_files == 1)
8881 {
8882 g_autofree gchar *formatted_size_completed_size = NULL;
8883 g_autofree gchar *formatted_size_total_size = NULL;
8884
8885 formatted_size_completed_size = g_format_size (completed_size);
8886 formatted_size_total_size = g_format_size (compress_job->total_size);
8887
8888 if (files_left > 0)
8889 {
8890 g_autofree gchar *formatted_time = NULL;
8891 g_autofree gchar *formatted_size_transfer_rate = NULL;
8892
8893 formatted_time = get_formatted_time (remaining_time);
8894 formatted_size_transfer_rate = g_format_size ((goffset) transfer_rate);
8895 /* To translators: %s will expand to a size like "2 bytes" or "3 MB", %s to a time duration like
8896 * "2 minutes". So the whole thing will be something like "2 kb / 4 MB -- 2 hours left (4kb/sec)"
8897 *
8898 * The singular/plural form will be used depending on the remaining time (i.e. the %s argument).
8899 */
8900 details = g_strdup_printf (ngettext ("%s / %s \xE2\x80\x94 %s left (%s/sec)",
8901 "%s / %s \xE2\x80\x94 %s left (%s/sec)",
8902 seconds_count_format_time_units (remaining_time)),
8903 formatted_size_completed_size,
8904 formatted_size_total_size,
8905 formatted_time,
8906 formatted_size_transfer_rate);
8907 }
8908 else
8909 {
8910 /* To translators: %s will expand to a size like "2 bytes" or "3 MB". */
8911 details = g_strdup_printf (_("%s / %s"),
8912 formatted_size_completed_size,
8913 formatted_size_total_size);
8914 }
8915 }
8916 else
8917 {
8918 if (files_left > 0)
8919 {
8920 g_autofree gchar *formatted_time = NULL;
8921 g_autofree gchar *formatted_size = NULL;
8922
8923 formatted_time = get_formatted_time (remaining_time);
8924 formatted_size = g_format_size ((goffset) transfer_rate);
8925 /* To translators: %s will expand to a time duration like "2 minutes".
8926 * So the whole thing will be something like "1 / 5 -- 2 hours left (4kb/sec)"
8927 *
8928 * The singular/plural form will be used depending on the remaining time (i.e. the %s argument).
8929 */
8930 details = g_strdup_printf (ngettext ("%'d / %'d \xE2\x80\x94 %s left (%s/sec)",
8931 "%'d / %'d \xE2\x80\x94 %s left (%s/sec)",
8932 seconds_count_format_time_units (remaining_time)),
8933 completed_files + 1, compress_job->total_files,
8934 formatted_time,
8935 formatted_size);
8936 }
8937 else
8938 {
8939 /* To translators: %'d is the number of files completed for the operation,
8940 * so it will be something like 2/14. */
8941 details = g_strdup_printf (_("%'d / %'d"),
8942 completed_files,
8943 compress_job->total_files);
8944 }
8945 }
8946 }
8947
8948 nautilus_progress_info_take_details (common->progress, details);
8949
8950 if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE)
8951 {
8952 nautilus_progress_info_set_remaining_time (common->progress,
8953 remaining_time);
8954 nautilus_progress_info_set_elapsed_time (common->progress,
8955 elapsed);
8956 }
8957
8958 nautilus_progress_info_set_progress (common->progress,
8959 completed_size,
8960 compress_job->total_size);
8961 }
8962
8963 static void
compress_job_on_error(AutoarCompressor * compressor,GError * error,gpointer user_data)8964 compress_job_on_error (AutoarCompressor *compressor,
8965 GError *error,
8966 gpointer user_data)
8967 {
8968 CompressJob *compress_job = user_data;
8969 char *status;
8970 g_autofree gchar *basename_output_file = NULL;
8971
8972 basename_output_file = get_basename (compress_job->output_file);
8973 if (compress_job->total_files == 1)
8974 {
8975 g_autofree gchar *basename_data = NULL;
8976
8977 basename_data = get_basename (G_FILE (compress_job->source_files->data));
8978 status = g_strdup_printf (_("Error compressing “%s” into “%s”"),
8979 basename_data,
8980 basename_output_file);
8981 }
8982 else
8983 {
8984 status = g_strdup_printf (ngettext ("Error compressing %'d file into “%s”",
8985 "Error compressing %'d files into “%s”",
8986 compress_job->total_files),
8987 compress_job->total_files,
8988 basename_output_file);
8989 }
8990 nautilus_progress_info_take_status (compress_job->common.progress,
8991 status);
8992
8993 run_error ((CommonJob *) compress_job,
8994 g_strdup (_("There was an error while compressing files.")),
8995 g_strdup (error->message),
8996 NULL,
8997 FALSE,
8998 CANCEL,
8999 NULL);
9000
9001 abort_job ((CommonJob *) compress_job);
9002 }
9003
9004 static void
compress_job_on_completed(AutoarCompressor * compressor,gpointer user_data)9005 compress_job_on_completed (AutoarCompressor *compressor,
9006 gpointer user_data)
9007 {
9008 CompressJob *compress_job = user_data;
9009 g_autoptr (GFile) destination_directory = NULL;
9010 char *status;
9011 g_autofree gchar *basename_output_file = NULL;
9012
9013 basename_output_file = get_basename (compress_job->output_file);
9014 if (compress_job->total_files == 1)
9015 {
9016 g_autofree gchar *basename_data = NULL;
9017
9018 basename_data = get_basename (G_FILE (compress_job->source_files->data));
9019 status = g_strdup_printf (_("Compressed “%s” into “%s”"),
9020 basename_data,
9021 basename_output_file);
9022 }
9023 else
9024 {
9025 status = g_strdup_printf (ngettext ("Compressed %'d file into “%s”",
9026 "Compressed %'d files into “%s”",
9027 compress_job->total_files),
9028 compress_job->total_files,
9029 basename_output_file);
9030 }
9031
9032 nautilus_progress_info_take_status (compress_job->common.progress,
9033 status);
9034
9035 nautilus_file_changes_queue_file_added (compress_job->output_file);
9036
9037 destination_directory = g_file_get_parent (compress_job->output_file);
9038 nautilus_progress_info_set_destination (compress_job->common.progress,
9039 destination_directory);
9040 }
9041
9042 static void
compress_task_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)9043 compress_task_thread_func (GTask *task,
9044 gpointer source_object,
9045 gpointer task_data,
9046 GCancellable *cancellable)
9047 {
9048 CompressJob *compress_job = task_data;
9049 g_auto (SourceInfo) source_info = SOURCE_INFO_INIT;
9050 g_autoptr (AutoarCompressor) compressor = NULL;
9051
9052 g_timer_start (compress_job->common.time);
9053
9054 nautilus_progress_info_start (compress_job->common.progress);
9055
9056 scan_sources (compress_job->source_files,
9057 &source_info,
9058 (CommonJob *) compress_job,
9059 OP_KIND_COMPRESS);
9060
9061 compress_job->total_files = source_info.num_files;
9062 compress_job->total_size = source_info.num_bytes;
9063
9064 compressor = autoar_compressor_new (compress_job->source_files,
9065 compress_job->output_file,
9066 compress_job->format,
9067 compress_job->filter,
9068 FALSE);
9069 autoar_compressor_set_passphrase (compressor, compress_job->passphrase);
9070
9071 autoar_compressor_set_output_is_dest (compressor, TRUE);
9072
9073 autoar_compressor_set_notify_interval (compressor,
9074 PROGRESS_NOTIFY_INTERVAL);
9075
9076 g_signal_connect (compressor, "progress",
9077 G_CALLBACK (compress_job_on_progress), compress_job);
9078 g_signal_connect (compressor, "error",
9079 G_CALLBACK (compress_job_on_error), compress_job);
9080 g_signal_connect (compressor, "completed",
9081 G_CALLBACK (compress_job_on_completed), compress_job);
9082 autoar_compressor_start (compressor,
9083 compress_job->common.cancellable);
9084
9085 compress_job->success = g_file_query_exists (compress_job->output_file,
9086 NULL);
9087
9088 /* There is nothing to undo if the output was not created */
9089 if (compress_job->common.undo_info != NULL && !compress_job->success)
9090 {
9091 g_clear_object (&compress_job->common.undo_info);
9092 }
9093 }
9094
9095 void
nautilus_file_operations_compress(GList * files,GFile * output,AutoarFormat format,AutoarFilter filter,const gchar * passphrase,GtkWindow * parent_window,NautilusFileOperationsDBusData * dbus_data,NautilusCreateCallback done_callback,gpointer done_callback_data)9096 nautilus_file_operations_compress (GList *files,
9097 GFile *output,
9098 AutoarFormat format,
9099 AutoarFilter filter,
9100 const gchar *passphrase,
9101 GtkWindow *parent_window,
9102 NautilusFileOperationsDBusData *dbus_data,
9103 NautilusCreateCallback done_callback,
9104 gpointer done_callback_data)
9105 {
9106 g_autoptr (GTask) task = NULL;
9107 CompressJob *compress_job;
9108
9109 compress_job = op_job_new (CompressJob, parent_window, dbus_data);
9110 compress_job->source_files = g_list_copy_deep (files,
9111 (GCopyFunc) g_object_ref,
9112 NULL);
9113 compress_job->output_file = g_object_ref (output);
9114 compress_job->format = format;
9115 compress_job->filter = filter;
9116 compress_job->passphrase = g_strdup (passphrase);
9117 compress_job->done_callback = done_callback;
9118 compress_job->done_callback_data = done_callback_data;
9119
9120 inhibit_power_manager ((CommonJob *) compress_job, _("Compressing Files"));
9121
9122 if (!nautilus_file_undo_manager_is_operating ())
9123 {
9124 compress_job->common.undo_info = nautilus_file_undo_info_compress_new (files,
9125 output,
9126 format,
9127 filter,
9128 passphrase);
9129 }
9130
9131 task = g_task_new (NULL, compress_job->common.cancellable,
9132 compress_task_done, compress_job);
9133 g_task_set_task_data (task, compress_job, NULL);
9134 g_task_run_in_thread (task, compress_task_thread_func);
9135 }
9136
9137 #if !defined (NAUTILUS_OMIT_SELF_CHECK)
9138
9139 void
nautilus_self_check_file_operations(void)9140 nautilus_self_check_file_operations (void)
9141 {
9142 setlocale (LC_MESSAGES, "C");
9143
9144
9145 /* test the next duplicate name generator */
9146 EEL_CHECK_STRING_RESULT (get_duplicate_name (" (copy)", 1, -1, FALSE), " (another copy)");
9147 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo", 1, -1, FALSE), "foo (copy)");
9148 EEL_CHECK_STRING_RESULT (get_duplicate_name (".bashrc", 1, -1, FALSE), ".bashrc (copy)");
9149 EEL_CHECK_STRING_RESULT (get_duplicate_name (".foo.txt", 1, -1, FALSE), ".foo (copy).txt");
9150 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo", 1, -1, FALSE), "foo foo (copy)");
9151 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo.txt", 1, -1, FALSE), "foo (copy).txt");
9152 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo.txt", 1, -1, FALSE), "foo foo (copy).txt");
9153 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo.txt txt", 1, -1, FALSE), "foo foo (copy).txt txt");
9154 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo...txt", 1, -1, FALSE), "foo.. (copy).txt");
9155 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo...", 1, -1, FALSE), "foo... (copy)");
9156 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo. (copy)", 1, -1, FALSE), "foo. (another copy)");
9157 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (copy)", 1, -1, FALSE), "foo (another copy)");
9158 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (copy).txt", 1, -1, FALSE), "foo (another copy).txt");
9159 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (another copy)", 1, -1, FALSE), "foo (3rd copy)");
9160 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (another copy).txt", 1, -1, FALSE), "foo (3rd copy).txt");
9161 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (another copy).txt", 1, -1, FALSE), "foo foo (3rd copy).txt");
9162 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (13th copy)", 1, -1, FALSE), "foo (14th copy)");
9163 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (13th copy).txt", 1, -1, FALSE), "foo (14th copy).txt");
9164 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (21st copy)", 1, -1, FALSE), "foo (22nd copy)");
9165 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (21st copy).txt", 1, -1, FALSE), "foo (22nd copy).txt");
9166 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (22nd copy)", 1, -1, FALSE), "foo (23rd copy)");
9167 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (22nd copy).txt", 1, -1, FALSE), "foo (23rd copy).txt");
9168 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (23rd copy)", 1, -1, FALSE), "foo (24th copy)");
9169 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (23rd copy).txt", 1, -1, FALSE), "foo (24th copy).txt");
9170 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (24th copy)", 1, -1, FALSE), "foo (25th copy)");
9171 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (24th copy).txt", 1, -1, FALSE), "foo (25th copy).txt");
9172 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (24th copy)", 1, -1, FALSE), "foo foo (25th copy)");
9173 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (24th copy).txt", 1, -1, FALSE), "foo foo (25th copy).txt");
9174 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (100000000000000th copy).txt", 1, -1, FALSE), "foo foo (copy).txt");
9175 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (10th copy)", 1, -1, FALSE), "foo (11th copy)");
9176 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (10th copy).txt", 1, -1, FALSE), "foo (11th copy).txt");
9177 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (11th copy)", 1, -1, FALSE), "foo (12th copy)");
9178 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (11th copy).txt", 1, -1, FALSE), "foo (12th copy).txt");
9179 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (12th copy)", 1, -1, FALSE), "foo (13th copy)");
9180 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (12th copy).txt", 1, -1, FALSE), "foo (13th copy).txt");
9181 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (110th copy)", 1, -1, FALSE), "foo (111th copy)");
9182 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (110th copy).txt", 1, -1, FALSE), "foo (111th copy).txt");
9183 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (122nd copy)", 1, -1, FALSE), "foo (123rd copy)");
9184 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (122nd copy).txt", 1, -1, FALSE), "foo (123rd copy).txt");
9185 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (123rd copy)", 1, -1, FALSE), "foo (124th copy)");
9186 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (123rd copy).txt", 1, -1, FALSE), "foo (124th copy).txt");
9187 EEL_CHECK_STRING_RESULT (get_duplicate_name ("dir.with.dots", 1, -1, TRUE), "dir.with.dots (copy)");
9188 EEL_CHECK_STRING_RESULT (get_duplicate_name ("dir (copy).dir", 1, -1, TRUE), "dir (another copy).dir");
9189
9190 setlocale (LC_MESSAGES, "");
9191 }
9192
9193 #endif
9194