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