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