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