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