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