1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2008 Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <string.h>
23 #include <glib.h>
24 #include <glib/gi18n.h>
25 #include <gio/gio.h>
26 #include "gth-file-data.h"
27 #include "gth-file-source.h"
28 #include "gth-file-source-vfs.h"
29 #include "gth-hook.h"
30 #include "gth-main.h"
31 #include "gth-metadata-provider.h"
32 #include "gth-overwrite-dialog.h"
33 #include "glib-utils.h"
34 #include "gio-utils.h"
35 
36 
37 #define N_FILES_PER_REQUEST 128
38 
39 
40 /* -- _g_directory_foreach_child -- */
41 
42 
43 typedef struct {
44 	GFile     *file;
45 	GFileInfo *info;
46 } ChildData;
47 
48 
49 static ChildData *
child_data_new(GFile * file,GFileInfo * info)50 child_data_new (GFile     *file,
51 		GFileInfo *info)
52 {
53 	ChildData *data;
54 
55 	data = g_new0 (ChildData, 1);
56 	data->file = g_file_dup (file);
57 	data->info = g_file_info_dup (info);
58 
59 	return data;
60 }
61 
62 
63 static void
child_data_free(ChildData * data)64 child_data_free (ChildData *data)
65 {
66 	if (data == NULL)
67 		return;
68 	_g_object_unref (data->file);
69 	_g_object_unref (data->info);
70 	g_free (data);
71 }
72 
73 
74 static void
clear_child_data(ChildData ** data)75 clear_child_data (ChildData **data)
76 {
77 	if (*data != NULL)
78 		child_data_free (*data);
79 	*data = NULL;
80 }
81 
82 
83 typedef struct {
84 	GFile                *base_directory;
85 	gboolean              recursive;
86 	gboolean              follow_links;
87 	StartDirCallback      start_dir_func;
88 	ForEachChildCallback  for_each_file_func;
89 	ReadyFunc             done_func;
90 	gpointer              user_data;
91 
92 	/* private */
93 
94 	ChildData            *current;
95 	GHashTable           *already_visited;
96 	GList                *to_visit;
97 	char                 *attributes;
98 	GCancellable         *cancellable;
99 	GFileEnumerator      *enumerator;
100 	GError               *error;
101 	guint                 source_id;
102 	gboolean              metadata_attributes;
103 	GList                *children;
104 	GList                *current_child;
105 } ForEachChildData;
106 
107 
108 static void
for_each_child_data_free(ForEachChildData * fec)109 for_each_child_data_free (ForEachChildData *fec)
110 {
111 	if (fec == NULL)
112 		return;
113 
114 	g_object_unref (fec->base_directory);
115 	if (fec->already_visited != NULL)
116 		g_hash_table_destroy (fec->already_visited);
117 	clear_child_data (&(fec->current));
118 	g_free (fec->attributes);
119 	if (fec->to_visit != NULL) {
120 		g_list_foreach (fec->to_visit, (GFunc) child_data_free, NULL);
121 		g_list_free (fec->to_visit);
122 	}
123 	_g_object_unref (fec->cancellable);
124 	g_free (fec);
125 }
126 
127 
128 static gboolean
for_each_child_done_cb(gpointer user_data)129 for_each_child_done_cb (gpointer user_data)
130 {
131 	ForEachChildData *fec = user_data;
132 
133 	g_source_remove (fec->source_id);
134 	clear_child_data (&(fec->current));
135 	if (fec->done_func)
136 		fec->done_func (fec->error, fec->user_data);
137 	for_each_child_data_free (fec);
138 
139 	return FALSE;
140 }
141 
142 
143 static void
for_each_child_done(ForEachChildData * fec)144 for_each_child_done (ForEachChildData *fec)
145 {
146 	fec->source_id = g_idle_add (for_each_child_done_cb, fec);
147 }
148 
149 
150 static void for_each_child_start_current (ForEachChildData *fec);
151 
152 
153 static void
for_each_child_start(ForEachChildData * fec)154 for_each_child_start (ForEachChildData *fec)
155 {
156 	for_each_child_start_current (fec);
157 }
158 
159 
160 static void
for_each_child_set_current(ForEachChildData * fec,ChildData * data)161 for_each_child_set_current (ForEachChildData *fec,
162 			    ChildData        *data)
163 {
164 	clear_child_data (&(fec->current));
165 	fec->current = data;
166 }
167 
168 
169 static void
for_each_child_start_next_sub_directory(ForEachChildData * fec)170 for_each_child_start_next_sub_directory (ForEachChildData *fec)
171 {
172 	ChildData *child = NULL;
173 
174 	if (fec->to_visit != NULL) {
175 		GList *tmp;
176 
177 		child = (ChildData *) fec->to_visit->data;
178 		tmp = fec->to_visit;
179 		fec->to_visit = g_list_remove_link (fec->to_visit, tmp);
180 		g_list_free (tmp);
181 	}
182 
183 	if (child != NULL) {
184 		for_each_child_set_current (fec, child);
185 		for_each_child_start (fec);
186 	}
187 	else
188 		for_each_child_done (fec);
189 }
190 
191 
192 static void
for_each_child_close_enumerator(GObject * source_object,GAsyncResult * result,gpointer user_data)193 for_each_child_close_enumerator (GObject      *source_object,
194 				 GAsyncResult *result,
195 		      		 gpointer      user_data)
196 {
197 	ForEachChildData *fec = user_data;
198 	GError           *error = NULL;
199 
200 	if (! g_file_enumerator_close_finish (fec->enumerator,
201 					      result,
202 					      &error))
203 	{
204 		if (fec->error == NULL)
205 			fec->error = g_error_copy (error);
206 		else
207 			g_clear_error (&error);
208 	}
209 
210 	if ((fec->error == NULL) && fec->recursive)
211 		for_each_child_start_next_sub_directory (fec);
212 	else
213 		for_each_child_done (fec);
214 }
215 
216 
217 static void for_each_child_next_files_ready (GObject      *source_object,
218 					     GAsyncResult *result,
219 					     gpointer      user_data);
220 
221 
222 static void
for_each_child_read_next_files(ForEachChildData * fec)223 for_each_child_read_next_files (ForEachChildData *fec)
224 {
225 	_g_object_list_unref (fec->children);
226 	fec->children = NULL;
227 	g_file_enumerator_next_files_async (fec->enumerator,
228 					    N_FILES_PER_REQUEST,
229 					    G_PRIORITY_DEFAULT,
230 					    fec->cancellable,
231 					    for_each_child_next_files_ready,
232 					    fec);
233 }
234 
235 
236 static void for_each_child_read_current_child_metadata (ForEachChildData *fec);
237 
238 
239 static void
for_each_child_read_next_child_metadata(ForEachChildData * fec)240 for_each_child_read_next_child_metadata (ForEachChildData *fec)
241 {
242 	fec->current_child = fec->current_child->next;
243 	if (fec->current_child != NULL)
244 		for_each_child_read_current_child_metadata (fec);
245 	else
246 		for_each_child_read_next_files (fec);
247 }
248 
249 
250 static void
for_each_child_compute_child(ForEachChildData * fec,GFile * file,GFileInfo * info)251 for_each_child_compute_child (ForEachChildData *fec,
252 			      GFile            *file,
253 			      GFileInfo        *info)
254 {
255 	if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
256 		char *id;
257 
258 		/* avoid to visit a directory more than ones */
259 
260 		id = g_strdup (g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE));
261 		if (id == NULL)
262 			id = g_file_get_uri (file);
263 
264 		if (g_hash_table_lookup (fec->already_visited, id) == NULL) {
265 			g_hash_table_insert (fec->already_visited, g_strdup (id), GINT_TO_POINTER (1));
266 			fec->to_visit = g_list_append (fec->to_visit, child_data_new (file, info));
267 		}
268 
269 		g_free (id);
270 	}
271 
272 	fec->for_each_file_func (file, info, fec->user_data);
273 }
274 
275 
276 static void
for_each_child_metadata_ready_func(GObject * source_object,GAsyncResult * result,gpointer user_data)277 for_each_child_metadata_ready_func (GObject      *source_object,
278                 		    GAsyncResult *result,
279                 		    gpointer      user_data)
280 {
281 	ForEachChildData *fec = user_data;
282 	GList            *files;
283 	GError           *error = NULL;
284 
285 	files = _g_query_metadata_finish (result, &error);
286 	if (files != NULL) {
287 		GthFileData *child_data = files->data;
288 		for_each_child_compute_child (fec, child_data->file, child_data->info);
289 	}
290 
291 	for_each_child_read_next_child_metadata (fec);
292 }
293 
294 
295 static void
for_each_child_read_current_child_metadata(ForEachChildData * fec)296 for_each_child_read_current_child_metadata (ForEachChildData *fec)
297 {
298 	GFileInfo   *child_info = fec->current_child->data;
299 	GFile       *child_file;
300 	GList       *file_list;
301 	GthFileData *child_data;
302 
303 	child_file = g_file_get_child (fec->current->file, g_file_info_get_name (child_info));
304 	child_data = gth_file_data_new (child_file, child_info);
305 	file_list = g_list_append (NULL, child_data);
306 	_g_query_metadata_async  (file_list,
307 				  fec->attributes,
308 				  NULL, /* FIXME: cannot use fec->cancellable here */
309 				  for_each_child_metadata_ready_func,
310 				  fec);
311 
312 	_g_object_list_unref (file_list);
313 	g_object_unref (child_file);
314 }
315 
316 
317 static void
for_each_child_next_files_ready(GObject * source_object,GAsyncResult * result,gpointer user_data)318 for_each_child_next_files_ready (GObject      *source_object,
319 				 GAsyncResult *result,
320 				 gpointer      user_data)
321 {
322 	ForEachChildData *fec = user_data;
323 
324 	fec->children = g_file_enumerator_next_files_finish (fec->enumerator,
325 							     result,
326 							     &(fec->error));
327 
328 	if (fec->children == NULL) {
329 		g_file_enumerator_close_async (fec->enumerator,
330 					       G_PRIORITY_DEFAULT,
331 					       fec->cancellable,
332 					       for_each_child_close_enumerator,
333 					       fec);
334 		return;
335 	}
336 
337 	if (fec->metadata_attributes) {
338 		fec->current_child = fec->children;
339 		for_each_child_read_current_child_metadata (fec);
340 	}
341 	else {
342 		GList *scan;
343 
344 		for (scan = fec->children; scan; scan = scan->next) {
345 			GFileInfo *child_info = scan->data;
346 			GFile     *child_file;
347 
348 			child_file = g_file_get_child (fec->current->file, g_file_info_get_name (child_info));
349 			for_each_child_compute_child (fec, child_file, child_info);
350 
351 			g_object_unref (child_file);
352 		}
353 
354 		for_each_child_read_next_files (fec);
355 	}
356 }
357 
358 
359 static void
for_each_child_ready(GObject * source_object,GAsyncResult * result,gpointer user_data)360 for_each_child_ready (GObject      *source_object,
361 		      GAsyncResult *result,
362 		      gpointer      user_data)
363 {
364 	ForEachChildData *fec = user_data;
365 
366 	fec->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), result, &(fec->error));
367 	if (fec->enumerator == NULL) {
368 		for_each_child_done (fec);
369 		return;
370 	}
371 
372 	g_file_enumerator_next_files_async (fec->enumerator,
373 					    N_FILES_PER_REQUEST,
374 					    G_PRIORITY_DEFAULT,
375 					    fec->cancellable,
376 					    for_each_child_next_files_ready,
377 					    fec);
378 }
379 
380 
381 static void
for_each_child_start_current(ForEachChildData * fec)382 for_each_child_start_current (ForEachChildData *fec)
383 {
384 	if (fec->start_dir_func != NULL) {
385 		DirOp  op;
386 
387 		op = fec->start_dir_func (fec->current->file, fec->current->info, &(fec->error), fec->user_data);
388 		switch (op) {
389 		case DIR_OP_SKIP:
390 			for_each_child_start_next_sub_directory (fec);
391 			return;
392 		case DIR_OP_STOP:
393 			for_each_child_done (fec);
394 			return;
395 		case DIR_OP_CONTINUE:
396 			break;
397 		}
398 	}
399 
400 	g_file_enumerate_children_async (fec->current->file,
401 					 fec->attributes,
402 					 fec->follow_links ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
403 					 G_PRIORITY_DEFAULT,
404 					 fec->cancellable,
405 					 for_each_child_ready,
406 					 fec);
407 }
408 
409 
410 static void
directory_info_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)411 directory_info_ready_cb (GObject      *source_object,
412 			 GAsyncResult *result,
413 			 gpointer      user_data)
414 {
415 	ForEachChildData *fec = user_data;
416 	GFileInfo        *info;
417 	ChildData        *child;
418 
419 	info = g_file_query_info_finish (G_FILE (source_object), result, &(fec->error));
420 	if (info == NULL) {
421 		for_each_child_done (fec);
422 		return;
423 	}
424 
425 	child = child_data_new (fec->base_directory, info);
426 	g_object_unref (info);
427 
428 	for_each_child_set_current (fec, child);
429 	for_each_child_start_current (fec);
430 }
431 
432 
433 /**
434  * _g_directory_foreach_child:
435  * @directory: The directory to visit.
436  * @recursive: Whether to traverse the @directory recursively.
437  * @follow_links: Whether to dereference the symbolic links.
438  * @attributes: The GFileInfo attributes to read.
439  * @cancellable: An optional @GCancellable object, used to cancel the process.
440  * @start_dir_func: the function called for each sub-directory, or %NULL if
441  *   not needed.
442  * @for_each_file_func: the function called for each file.  Can't be %NULL.
443  * @done_func: the function called at the end of the traversing process.
444  *   Can't be %NULL.
445  * @user_data: data to pass to @done_func
446  *
447  * Traverse the @directory's filesystem structure calling the
448  * @for_each_file_func function for each file in the directory; the
449  * @start_dir_func function on each directory before it's going to be
450  * traversed, this includes @directory too; the @done_func function is
451  * called at the end of the process.
452  * Some traversing options are available: if @recursive is TRUE the
453  * directory is traversed recursively; if @follow_links is TRUE, symbolic
454  * links are dereferenced, otherwise they are returned as links.
455  * Each callback uses the same @user_data additional parameter.
456  */
457 void
_g_directory_foreach_child(GFile * directory,gboolean recursive,gboolean follow_links,const char * attributes,GCancellable * cancellable,StartDirCallback start_dir_func,ForEachChildCallback for_each_file_func,ReadyFunc done_func,gpointer user_data)458 _g_directory_foreach_child (GFile                *directory,
459 			   gboolean              recursive,
460 			   gboolean              follow_links,
461 			   const char           *attributes,
462 			   GCancellable         *cancellable,
463 			   StartDirCallback      start_dir_func,
464 			   ForEachChildCallback  for_each_file_func,
465 			   ReadyFunc             done_func,
466 			   gpointer              user_data)
467 {
468 	ForEachChildData *fec;
469 
470 	g_return_if_fail (for_each_file_func != NULL);
471 
472 	fec = g_new0 (ForEachChildData, 1);
473 
474 	fec->base_directory = g_file_dup (directory);
475 	fec->recursive = recursive;
476 	fec->follow_links = follow_links;
477 	fec->attributes = g_strconcat (attributes, ",standard::name,standard::type,id::file", NULL);
478 	fec->cancellable = _g_object_ref (cancellable);
479 	fec->start_dir_func = start_dir_func;
480 	fec->for_each_file_func = for_each_file_func;
481 	fec->done_func = done_func;
482 	fec->user_data = user_data;
483 	fec->already_visited = g_hash_table_new_full (g_str_hash,
484 						      g_str_equal,
485 						      g_free,
486 						      NULL);
487 	fec->metadata_attributes = ! _g_file_attributes_matches_all (fec->attributes, GIO_ATTRIBUTES);
488 
489 	g_file_query_info_async (fec->base_directory,
490 				 fec->attributes,
491 				 G_FILE_QUERY_INFO_NONE,
492 				 G_PRIORITY_DEFAULT,
493 				 fec->cancellable,
494 				 directory_info_ready_cb,
495 				 fec);
496 }
497 
498 /* -- _g_file_list_query_info_async -- */
499 
500 
501 typedef struct {
502 	GList             *file_list;
503 	GthListFlags       flags;
504 	char              *attributes;
505 	GCancellable      *cancellable;
506 	InfoReadyCallback  callback;
507 	gpointer           user_data;
508 	GList             *current;
509 	GList             *files;
510 	GList             *sidecars;
511 	GList             *current_sidecar;
512 } QueryInfoData;
513 
514 
515 static void
query_data_free(QueryInfoData * query_data)516 query_data_free (QueryInfoData *query_data)
517 {
518 	_g_object_list_unref (query_data->sidecars);
519 	_g_object_list_unref (query_data->file_list);
520 	_g_object_list_unref (query_data->files);
521 	_g_object_unref (query_data->cancellable);
522 	g_free (query_data->attributes);
523 	g_free (query_data);
524 }
525 
526 
527 static void query_info__query_current (QueryInfoData *query_data);
528 
529 
530 static void
query_info__query_next(QueryInfoData * query_data)531 query_info__query_next (QueryInfoData *query_data)
532 {
533 	query_data->current = query_data->current->next;
534 	query_info__query_current (query_data);
535 }
536 
537 
538 static void
539 query_info__query_current_sidecar (QueryInfoData *query_data);
540 
541 
542 static void
query_info__query_next_sidecar(QueryInfoData * query_data)543 query_info__query_next_sidecar (QueryInfoData *query_data)
544 {
545 	query_data->current_sidecar = query_data->current_sidecar->next;
546 	query_info__query_current_sidecar (query_data);
547 }
548 
549 
550 static void
query_data_sidecar_info_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)551 query_data_sidecar_info_ready_cb (GObject      *source_object,
552 				  GAsyncResult *result,
553 				  gpointer      user_data)
554 {
555 	QueryInfoData *query_data = user_data;
556 	GFileInfo     *info;
557 	GFile         *file;
558 
559 	info = g_file_query_info_finish ((GFile *) source_object, result, NULL);
560 	if (info == NULL) {
561 		query_info__query_next_sidecar (query_data);
562 		return;
563 	}
564 
565 	file = G_FILE (query_data->current_sidecar->data);
566 	query_data->files = g_list_prepend (query_data->files, gth_file_data_new (file, info));
567 
568 	query_info__query_next_sidecar (query_data);
569 }
570 
571 
572 static void
query_info__query_current_sidecar(QueryInfoData * query_data)573 query_info__query_current_sidecar (QueryInfoData *query_data)
574 {
575 	GFileQueryInfoFlags flags;
576 
577 	if (query_data->current_sidecar == NULL) {
578 		query_info__query_next (query_data);
579 		return;
580 	}
581 
582 	flags = G_FILE_QUERY_INFO_NONE;
583 	if (query_data->flags & GTH_LIST_NO_FOLLOW_LINKS)
584 		flags |= G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS;
585 
586 	g_file_query_info_async (G_FILE (query_data->current_sidecar->data),
587 				 query_data->attributes,
588 				 flags,
589 				 G_PRIORITY_DEFAULT,
590 				 query_data->cancellable,
591 				 query_data_sidecar_info_ready_cb,
592 				 query_data);
593 }
594 
595 
596 static void
query_info__query_sidecars(QueryInfoData * query_data,GFile * file)597 query_info__query_sidecars (QueryInfoData *query_data,
598 			    GFile         *file)
599 {
600 	GList *sidecars = NULL;
601 
602 	gth_hook_invoke ("add-sidecars", file, &sidecars);
603 	if (sidecars == NULL) {
604 		query_info__query_next (query_data);
605 		return;
606 	}
607 
608 	_g_object_list_unref (query_data->sidecars);
609 	query_data->sidecars = sidecars;
610 	query_data->current_sidecar = query_data->sidecars;
611 	query_info__query_current_sidecar (query_data);
612 }
613 
614 
615 static void
query_data__done_cb(GError * error,gpointer user_data)616 query_data__done_cb (GError   *error,
617 		     gpointer  user_data)
618 {
619 	QueryInfoData *query_data = user_data;
620 
621 	if (error != NULL) {
622 		query_data->callback (NULL, error, query_data->user_data);
623 		query_data_free (query_data);
624 		return;
625 	}
626 
627 	query_info__query_next (query_data);
628 }
629 
630 
631 static void
query_data__for_each_file_cb(GFile * file,GFileInfo * info,gpointer user_data)632 query_data__for_each_file_cb (GFile     *file,
633 			      GFileInfo *info,
634 			      gpointer   user_data)
635 {
636 	QueryInfoData *query_data = user_data;
637 
638 	if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
639 		return;
640 	if ((query_data->flags & GTH_LIST_NO_BACKUP_FILES) && g_file_info_get_is_backup (info))
641 		return;
642 	if ((query_data->flags & GTH_LIST_NO_HIDDEN_FILES) && g_file_info_get_is_hidden (info))
643 		return;
644 
645 	query_data->files = g_list_prepend (query_data->files, gth_file_data_new (file, info));
646 }
647 
648 
649 static DirOp
query_data__start_dir_cb(GFile * directory,GFileInfo * info,GError ** error,gpointer user_data)650 query_data__start_dir_cb (GFile       *directory,
651 		          GFileInfo   *info,
652 		          GError     **error,
653 		          gpointer     user_data)
654 {
655 	QueryInfoData *query_data = user_data;
656 
657 	if ((query_data->flags & GTH_LIST_NO_BACKUP_FILES) && g_file_info_get_is_backup (info))
658 		return DIR_OP_SKIP;
659 	if ((query_data->flags & GTH_LIST_NO_HIDDEN_FILES) && g_file_info_get_is_hidden (info))
660 		return DIR_OP_SKIP;
661 
662 	query_data->files = g_list_prepend (query_data->files, gth_file_data_new (directory, info));
663 
664 	return DIR_OP_CONTINUE;
665 }
666 
667 
668 static void
query_data_info_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)669 query_data_info_ready_cb (GObject      *source_object,
670                           GAsyncResult *result,
671                           gpointer      user_data)
672 {
673 	QueryInfoData *query_data = user_data;
674 	GError        *error = NULL;
675 	GFileInfo     *info;
676 
677 	info = g_file_query_info_finish ((GFile *) source_object, result, &error);
678 	if (info == NULL) {
679 		query_data->callback (NULL, error, query_data->user_data);
680 		query_data_free (query_data);
681 		return;
682 	}
683 
684 	if ((query_data->flags & GTH_LIST_RECURSIVE)
685 		&& (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY))
686 	{
687 		_g_directory_foreach_child ((GFile *) query_data->current->data,
688 					   TRUE,
689 					   (query_data->flags & GTH_LIST_NO_FOLLOW_LINKS) == 0,
690 					   query_data->attributes,
691 					   query_data->cancellable,
692 					   query_data__start_dir_cb,
693 					   query_data__for_each_file_cb,
694 					   query_data__done_cb,
695 					   query_data);
696 	}
697 	else {
698 		GFile *file = G_FILE (query_data->current->data);
699 
700 		query_data->files = g_list_prepend (query_data->files, gth_file_data_new (file, info));
701 
702 		if ((query_data->flags & GTH_LIST_INCLUDE_SIDECARS)
703 			&& (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR))
704 		{
705 			query_info__query_sidecars (query_data, file);
706 		}
707 		else
708 			query_info__query_next (query_data);
709 	}
710 
711 	g_object_unref (info);
712 }
713 
714 
715 static void
query_info__query_current(QueryInfoData * query_data)716 query_info__query_current (QueryInfoData *query_data)
717 {
718 	GFileQueryInfoFlags flags;
719 
720 	if (query_data->current == NULL) {
721 		query_data->files = g_list_reverse (query_data->files);
722 		query_data->callback (query_data->files, NULL, query_data->user_data);
723 		query_data_free (query_data);
724 		return;
725 	}
726 
727 	flags = G_FILE_QUERY_INFO_NONE;
728 	if (query_data->flags & GTH_LIST_NO_FOLLOW_LINKS)
729 		flags |= G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS;
730 
731 	g_file_query_info_async ((GFile *) query_data->current->data,
732 				 query_data->attributes,
733 				 flags,
734 				 G_PRIORITY_DEFAULT,
735 				 query_data->cancellable,
736 				 query_data_info_ready_cb,
737 				 query_data);
738 }
739 
740 
741 void
_g_file_list_query_info_async(GList * file_list,GthListFlags flags,const char * attributes,GCancellable * cancellable,InfoReadyCallback ready_callback,gpointer user_data)742 _g_file_list_query_info_async (GList             *file_list,
743 			       GthListFlags       flags,
744 			       const char        *attributes,
745 			       GCancellable      *cancellable,
746 			       InfoReadyCallback  ready_callback,
747 			       gpointer           user_data)
748 {
749 	QueryInfoData *query_data;
750 
751 	query_data = g_new0 (QueryInfoData, 1);
752 	query_data->file_list = _g_object_list_ref (file_list);
753 	query_data->flags = flags;
754 	query_data->attributes = g_strconcat ("standard::name,standard::type,standard::is-hidden,standard::is-backup,id::file",
755 					      (((attributes != NULL) && (strcmp (attributes, "") != 0)) ? "," : NULL),
756 					      attributes,
757 					      NULL);
758 	query_data->cancellable = _g_object_ref (cancellable);
759 	query_data->callback = ready_callback;
760 	query_data->user_data = user_data;
761 	query_data->sidecars = NULL;
762 
763 	query_data->current = query_data->file_list;
764 	query_info__query_current (query_data);
765 }
766 
767 
768 /* -- _g_copy_file_async -- */
769 
770 
771 typedef struct {
772 	GthFileData          *source;
773 	GFile                *destination;
774 	gboolean              move;
775 	GthFileCopyFlags      flags;
776 	int                   io_priority;
777 	goffset               tot_size;
778 	goffset               copied_size;
779 	gsize                 tot_files;
780 	GCancellable         *cancellable;
781 	ProgressCallback      progress_callback;
782 	gpointer              progress_callback_data;
783 	DialogCallback        dialog_callback;
784 	gpointer              dialog_callback_data;
785 	CopyReadyCallback     ready_callback;
786 	gpointer              user_data;
787 	gboolean              move_succeed;
788 
789 	GFile                *current_destination;
790 	char                 *message;
791 	GthOverwriteResponse  default_response;
792 	gboolean              duplicating_file;
793 
794 	GList                *source_sidecars;  /* GFile list */
795 	GList                *destination_sidecars;  /* GFile list */
796 	GList                *current_source_sidecar;
797 	GList                *current_destination_sidecar;
798 } CopyFileData;
799 
800 
801 static void
copy_file_data_free(CopyFileData * copy_file_data)802 copy_file_data_free (CopyFileData *copy_file_data)
803 {
804 	g_object_unref (copy_file_data->source);
805 	g_object_unref (copy_file_data->destination);
806 	_g_object_unref (copy_file_data->cancellable);
807 	_g_object_unref (copy_file_data->current_destination);
808 	g_free (copy_file_data->message);
809 	_g_object_list_unref (copy_file_data->destination_sidecars);
810 	_g_object_list_unref (copy_file_data->source_sidecars);
811 	g_free (copy_file_data);
812 }
813 
814 
815 static void
copy_file__delete_source(CopyFileData * copy_file_data)816 copy_file__delete_source (CopyFileData *copy_file_data)
817 {
818 	GError *error = NULL;
819 
820 	/* delete the source if we are moving the file but the move operation
821 	 * is not supported. */
822 	if (copy_file_data->move && ! copy_file_data->move_succeed)
823 		g_file_delete (copy_file_data->source->file, copy_file_data->cancellable, &error);
824 
825 	copy_file_data->ready_callback (copy_file_data->default_response, copy_file_data->source_sidecars, error, copy_file_data->user_data);
826 	copy_file_data_free (copy_file_data);
827 }
828 
829 
830 static void copy_file__copy_current_sidecar (CopyFileData *copy_file_data);
831 
832 
833 static void
copy_file__copy_next_sidecar(CopyFileData * copy_file_data)834 copy_file__copy_next_sidecar (CopyFileData *copy_file_data)
835 {
836 	copy_file_data->current_source_sidecar = copy_file_data->current_source_sidecar->next;
837 	copy_file_data->current_destination_sidecar = copy_file_data->current_destination_sidecar->next;
838 	copy_file__copy_current_sidecar (copy_file_data);
839 }
840 
841 
842 static void
copy_file__copy_current_sidecar_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)843 copy_file__copy_current_sidecar_ready_cb (GObject      *source_object,
844 				          GAsyncResult *result,
845 				          gpointer      user_data)
846 {
847 	CopyFileData *copy_file_data = user_data;
848 
849 	if (g_file_copy_finish ((GFile *) source_object, result, NULL)) {
850 		if (copy_file_data->move)
851 			g_file_delete ((GFile *) copy_file_data->current_source_sidecar->data, copy_file_data->cancellable, NULL);
852 	}
853 
854 	copy_file__copy_next_sidecar (copy_file_data);
855 }
856 
857 
858 static void
copy_file__copy_current_sidecar(CopyFileData * copy_file_data)859 copy_file__copy_current_sidecar (CopyFileData *copy_file_data)
860 {
861 	GFile *source;
862 	GFile *destination;
863 	GFile *destination_parent;
864 
865 	if (copy_file_data->current_source_sidecar == NULL) {
866 		copy_file__delete_source (copy_file_data);
867 		return;
868 	}
869 
870 	source = copy_file_data->current_source_sidecar->data;
871 	if (! g_file_query_exists (source, copy_file_data->cancellable)) {
872 		copy_file__copy_next_sidecar (copy_file_data);
873 		return;
874 	}
875 
876 	destination = copy_file_data->current_destination_sidecar->data;
877 	destination_parent = g_file_get_parent (destination);
878 	g_file_make_directory (destination_parent, copy_file_data->cancellable, NULL);
879 
880 	g_file_copy_async (source,
881 			   destination,
882 			   G_FILE_COPY_OVERWRITE,
883 			   copy_file_data->io_priority,
884 			   copy_file_data->cancellable,
885 			   NULL,
886 			   NULL,
887 			   copy_file__copy_current_sidecar_ready_cb,
888 			   copy_file_data);
889 
890 	g_object_unref (destination_parent);
891 }
892 
893 
894 static void
copy_file__copy_sidecars(CopyFileData * copy_file_data)895 copy_file__copy_sidecars (CopyFileData *copy_file_data)
896 {
897 	/* copy the metadata sidecars if requested */
898 
899 	_g_object_list_unref (copy_file_data->source_sidecars);
900 	_g_object_list_unref (copy_file_data->destination_sidecars);
901 	copy_file_data->source_sidecars = NULL;
902 	copy_file_data->destination_sidecars = NULL;
903 	if (copy_file_data->flags & GTH_FILE_COPY_ALL_METADATA) {
904 		gth_hook_invoke ("add-sidecars", copy_file_data->source->file, &copy_file_data->source_sidecars);
905 		gth_hook_invoke ("add-sidecars", copy_file_data->destination, &copy_file_data->destination_sidecars);
906 		copy_file_data->source_sidecars = g_list_reverse (copy_file_data->source_sidecars);
907 		copy_file_data->destination_sidecars = g_list_reverse (copy_file_data->destination_sidecars);
908 	}
909 
910 	copy_file_data->current_source_sidecar = copy_file_data->source_sidecars;
911 	copy_file_data->current_destination_sidecar = copy_file_data->destination_sidecars;
912 	copy_file__copy_current_sidecar (copy_file_data);
913 }
914 
915 
916 static void _g_copy_file_to_destination (CopyFileData   *copy_file_data,
917 					 GFile          *destination,
918 					 GFileCopyFlags  flags);
919 
920 
921 static void
copy_file__overwrite_dialog_response_cb(GtkDialog * dialog,int response_id,gpointer user_data)922 copy_file__overwrite_dialog_response_cb (GtkDialog *dialog,
923 				         int        response_id,
924 				         gpointer   user_data)
925 {
926 	CopyFileData *copy_file_data = user_data;
927 
928 	if (response_id != GTK_RESPONSE_OK)
929 		copy_file_data->default_response = GTH_OVERWRITE_RESPONSE_CANCEL;
930 	else
931 		copy_file_data->default_response = gth_overwrite_dialog_get_response (GTH_OVERWRITE_DIALOG (dialog));
932 
933 	gtk_widget_hide (GTK_WIDGET (dialog));
934 
935 	if (copy_file_data->dialog_callback != NULL)
936 		copy_file_data->dialog_callback (FALSE, NULL, copy_file_data->dialog_callback_data);
937 
938 	switch (copy_file_data->default_response) {
939 	case GTH_OVERWRITE_RESPONSE_NO:
940 	case GTH_OVERWRITE_RESPONSE_ALWAYS_NO:
941 	case GTH_OVERWRITE_RESPONSE_UNSPECIFIED:
942 	case GTH_OVERWRITE_RESPONSE_CANCEL:
943 		{
944 			GError *error = NULL;
945 
946 			if (copy_file_data->default_response == GTH_OVERWRITE_RESPONSE_CANCEL)
947 				error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "");
948 			else
949 				error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_EXISTS, "");
950 			copy_file_data->ready_callback (copy_file_data->default_response, NULL, error, copy_file_data->user_data);
951 			copy_file_data_free (copy_file_data);
952 			return;
953 		}
954 
955 	case GTH_OVERWRITE_RESPONSE_YES:
956 	case GTH_OVERWRITE_RESPONSE_ALWAYS_YES:
957 		_g_copy_file_to_destination (copy_file_data, copy_file_data->current_destination, G_FILE_COPY_OVERWRITE);
958 		break;
959 
960 	case GTH_OVERWRITE_RESPONSE_RENAME:
961 		{
962 			GFile *parent;
963 			GFile *new_destination;
964 
965 			parent = g_file_get_parent (copy_file_data->current_destination);
966 			new_destination = g_file_get_child_for_display_name (parent, gth_overwrite_dialog_get_filename (GTH_OVERWRITE_DIALOG (dialog)), NULL);
967 			_g_copy_file_to_destination (copy_file_data, new_destination, G_FILE_COPY_NONE);
968 
969 			g_object_unref (new_destination);
970 			g_object_unref (parent);
971 		}
972 		break;
973 	}
974 
975 	gtk_widget_destroy (GTK_WIDGET (dialog));
976 }
977 
978 
979 static void
copy_file__handle_error(CopyFileData * copy_file_data,GError * error)980 copy_file__handle_error (CopyFileData *copy_file_data,
981 			 GError       *error)
982 {
983 	g_return_if_fail (GTH_IS_FILE_DATA (copy_file_data->source));
984 	g_return_if_fail (G_IS_FILE (copy_file_data->source->file));
985 
986 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
987 		if (copy_file_data->duplicating_file) {
988 
989 			/* the duplicated file already exists, try another one */
990 
991 			GFile *new_destination = _g_file_get_duplicated (copy_file_data->current_destination);
992 			_g_copy_file_to_destination (copy_file_data, new_destination, G_FILE_COPY_NONE);
993 
994 			g_object_unref (new_destination);
995 		}
996 		else if (copy_file_data->default_response != GTH_OVERWRITE_RESPONSE_ALWAYS_NO) {
997 			GtkWidget *dialog;
998 
999 			dialog = gth_overwrite_dialog_new (copy_file_data->source->file,
1000 							   NULL,
1001 							   copy_file_data->current_destination,
1002 							   copy_file_data->default_response,
1003 							   copy_file_data->tot_files == 1);
1004 
1005 			if (copy_file_data->dialog_callback != NULL)
1006 				copy_file_data->dialog_callback (TRUE, dialog, copy_file_data->dialog_callback_data);
1007 
1008 			g_signal_connect (dialog,
1009 					  "response",
1010 					  G_CALLBACK (copy_file__overwrite_dialog_response_cb),
1011 					  copy_file_data);
1012 			gtk_widget_show (dialog);
1013 		}
1014 		else {
1015 			copy_file_data->ready_callback (copy_file_data->default_response, NULL, error, copy_file_data->user_data);
1016 			copy_file_data_free (copy_file_data);
1017 		}
1018 	}
1019 	else {
1020 		copy_file_data->ready_callback (copy_file_data->default_response, NULL, error, copy_file_data->user_data);
1021 		copy_file_data_free (copy_file_data);
1022 	}
1023 }
1024 
1025 
1026 static void
copy_file_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1027 copy_file_ready_cb (GObject      *source_object,
1028 		    GAsyncResult *result,
1029 		    gpointer      user_data)
1030 {
1031 	CopyFileData *copy_file_data = user_data;
1032 	GError       *error = NULL;
1033 
1034 	if (! g_file_copy_finish (G_FILE (source_object), result, &error)) {
1035 		copy_file__handle_error (copy_file_data, error);
1036 		return;
1037 	}
1038 
1039 	copy_file__copy_sidecars (copy_file_data);
1040 }
1041 
1042 
1043 static void
copy_file_progress_cb(goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)1044 copy_file_progress_cb (goffset  current_num_bytes,
1045                        goffset  total_num_bytes,
1046                        gpointer user_data)
1047 {
1048 	CopyFileData *copy_file_data = user_data;
1049 	char         *s1;
1050 	char         *s2;
1051 	char         *details;
1052 
1053 	if (copy_file_data->progress_callback == NULL)
1054 		return;
1055 
1056 	s1 = g_format_size (copy_file_data->copied_size + current_num_bytes);
1057 	s2 = g_format_size (copy_file_data->tot_size);
1058 	/* For translators: This is a progress size indicator, for example: 230.4 MB of 512.8 MB */
1059 	details = g_strdup_printf (_("%s of %s"), s1, s2);
1060 
1061 	copy_file_data->progress_callback (NULL,
1062 					   copy_file_data->message,
1063 					   details,
1064 					   FALSE,
1065 					   (double) (copy_file_data->copied_size + current_num_bytes) / copy_file_data->tot_size,
1066 					   copy_file_data->progress_callback_data);
1067 
1068 	g_free (details);
1069 	g_free (s2);
1070 	g_free (s1);
1071 }
1072 
1073 
1074 static void
_g_copy_file_to_destination_call_ready_callback(gpointer user_data)1075 _g_copy_file_to_destination_call_ready_callback (gpointer user_data)
1076 {
1077 	CopyFileData *copy_file_data = user_data;
1078 
1079 	copy_file_data->ready_callback (copy_file_data->default_response, NULL, NULL, copy_file_data->user_data);
1080 	copy_file_data_free (copy_file_data);
1081 }
1082 
1083 
1084 static void
make_directory_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1085 make_directory_ready_cb (GObject      *source_object,
1086 			 GAsyncResult *result,
1087 			 gpointer      user_data)
1088 {
1089 	CopyFileData *copy_file_data = user_data;
1090 	GError       *error = NULL;
1091 
1092 	if (! g_file_make_directory_finish (G_FILE (source_object), result, &error))
1093 		/* ignore this kind of errors */
1094 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
1095 			g_clear_error (&error);
1096 
1097 	copy_file_data->ready_callback (copy_file_data->default_response, NULL, error, copy_file_data->user_data);
1098 	copy_file_data_free (copy_file_data);
1099 }
1100 
1101 
1102 static void
_g_copy_file_to_destination(CopyFileData * copy_file_data,GFile * destination,GFileCopyFlags flags)1103 _g_copy_file_to_destination (CopyFileData   *copy_file_data,
1104 			     GFile          *destination,
1105 			     GFileCopyFlags  flags)
1106 {
1107 	if (copy_file_data->default_response == GTH_OVERWRITE_RESPONSE_ALWAYS_YES)
1108 		flags |= G_FILE_COPY_OVERWRITE;
1109 	if (copy_file_data->flags & GTH_FILE_COPY_ALL_METADATA)
1110 		flags |= G_FILE_COPY_ALL_METADATA;
1111 
1112 	_g_object_unref (copy_file_data->current_destination);
1113 	copy_file_data->current_destination = g_file_dup (destination);
1114 
1115 	if (g_file_equal (copy_file_data->source->file, copy_file_data->current_destination)) {
1116 		GFile *old_destination;
1117 
1118 		if ((copy_file_data->move
1119 		    || (copy_file_data->default_response == GTH_OVERWRITE_RESPONSE_ALWAYS_YES)
1120 		    || ! (copy_file_data->flags & GTH_FILE_COPY_RENAME_SAME_FILE)))
1121 		{
1122 			call_when_idle (_g_copy_file_to_destination_call_ready_callback, copy_file_data);
1123 			return;
1124 		}
1125 
1126 		copy_file_data->duplicating_file = TRUE;
1127 
1128 		old_destination = copy_file_data->current_destination;
1129 		copy_file_data->current_destination = _g_file_get_duplicated (old_destination);
1130 		g_object_unref (old_destination);
1131 
1132 		/* duplicated files shouldn't be overwritten, if a duplicated
1133 		 * file already exists get another duplicated file and try
1134 		 * again. */
1135 		if (flags & G_FILE_COPY_OVERWRITE)
1136 			flags ^= G_FILE_COPY_OVERWRITE;
1137 	}
1138 
1139 	if (copy_file_data->progress_callback != NULL) {
1140 		GFile *destination_parent;
1141 		char  *destination_name;
1142 
1143 		g_free (copy_file_data->message);
1144 
1145 		destination_parent = g_file_get_parent (copy_file_data->current_destination);
1146 		destination_name = g_file_get_parse_name (destination_parent);
1147 		if (copy_file_data->move)
1148 			copy_file_data->message = g_strdup_printf (_("Moving “%s” to “%s”"), g_file_info_get_display_name (copy_file_data->source->info), destination_name);
1149 		else
1150 			copy_file_data->message = g_strdup_printf (_("Copying “%s” to “%s”"), g_file_info_get_display_name (copy_file_data->source->info), destination_name);
1151 
1152 		copy_file_progress_cb (0, 0, copy_file_data);
1153 
1154 		g_free (destination_name);
1155 		g_object_unref (destination_parent);
1156 	}
1157 
1158 	switch (g_file_info_get_file_type (copy_file_data->source->info)) {
1159 	case G_FILE_TYPE_DIRECTORY:
1160 		/* FIXME: handle the GTH_FILE_COPY_RENAME_SAME_FILE flag for directories */
1161 		g_file_make_directory_async (copy_file_data->current_destination,
1162 					     copy_file_data->io_priority,
1163 					     copy_file_data->cancellable,
1164 					     make_directory_ready_cb,
1165 					     copy_file_data);
1166 		break;
1167 
1168 	default:
1169 		if (copy_file_data->move) {
1170 			GError *error = NULL;
1171 
1172 			flags |= G_FILE_COPY_NO_FALLBACK_FOR_MOVE;
1173 			if (g_file_move (copy_file_data->source->file,
1174 					 copy_file_data->current_destination,
1175 					 flags,
1176 					 copy_file_data->cancellable,
1177 					 copy_file_progress_cb,
1178 					 copy_file_data,
1179 					 &error))
1180 			{
1181 				copy_file_data->move_succeed = TRUE;
1182 				copy_file__copy_sidecars (copy_file_data);
1183 				return;
1184 			}
1185 			else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
1186 				/* fallback to copy and delete */
1187 			}
1188 			else {
1189 				copy_file__handle_error (copy_file_data, error);
1190 				return;
1191 			}
1192 		}
1193 
1194 		g_file_copy_async (copy_file_data->source->file,
1195 				   copy_file_data->current_destination,
1196 				   flags,
1197 				   copy_file_data->io_priority,
1198 				   copy_file_data->cancellable,
1199 				   copy_file_progress_cb,
1200 				   copy_file_data,
1201 				   copy_file_ready_cb,
1202 				   copy_file_data);
1203 		break;
1204 	}
1205 }
1206 
1207 
1208 static void
_g_copy_file_async_private(GthFileData * source,GFile * destination,gboolean move,GthFileCopyFlags flags,GthOverwriteResponse default_response,int io_priority,goffset tot_size,goffset copied_size,gsize tot_files,GCancellable * cancellable,ProgressCallback progress_callback,gpointer progress_callback_data,DialogCallback dialog_callback,gpointer dialog_callback_data,CopyReadyCallback ready_callback,gpointer user_data)1209 _g_copy_file_async_private (GthFileData           *source,
1210 			    GFile                 *destination,
1211 			    gboolean               move,
1212 			    GthFileCopyFlags       flags,
1213 			    GthOverwriteResponse   default_response,
1214 			    int                    io_priority,
1215 			    goffset                tot_size,
1216 			    goffset                copied_size,
1217 			    gsize                  tot_files,
1218 			    GCancellable          *cancellable,
1219 			    ProgressCallback       progress_callback,
1220 			    gpointer               progress_callback_data,
1221 			    DialogCallback         dialog_callback,
1222 			    gpointer               dialog_callback_data,
1223 			    CopyReadyCallback      ready_callback,
1224 			    gpointer               user_data)
1225 {
1226 	CopyFileData *copy_file_data;
1227 
1228 	g_return_if_fail (GTH_IS_FILE_DATA (source));
1229 	g_return_if_fail (G_IS_FILE (destination));
1230 
1231 	copy_file_data = g_new0 (CopyFileData, 1);
1232 	copy_file_data->source = g_object_ref (source);
1233 	copy_file_data->destination = g_object_ref (destination);
1234 	copy_file_data->move = move;
1235 	copy_file_data->flags = flags;
1236 	copy_file_data->io_priority = io_priority;
1237 	copy_file_data->tot_size = tot_size;
1238 	copy_file_data->copied_size = copied_size;
1239 	copy_file_data->tot_files = tot_files;
1240 	copy_file_data->cancellable = _g_object_ref (cancellable);
1241 	copy_file_data->progress_callback = progress_callback;
1242 	copy_file_data->progress_callback_data = progress_callback_data;
1243 	copy_file_data->dialog_callback = dialog_callback;
1244 	copy_file_data->dialog_callback_data = dialog_callback_data;
1245 	copy_file_data->ready_callback = ready_callback;
1246 	copy_file_data->user_data = user_data;
1247 	copy_file_data->default_response = default_response;
1248 	copy_file_data->move_succeed = FALSE;
1249 
1250 	_g_copy_file_to_destination (copy_file_data, copy_file_data->destination, G_FILE_COPY_NONE);
1251 }
1252 
1253 
1254 void
_gth_file_data_copy_async(GthFileData * source,GFile * destination,gboolean move,GthFileCopyFlags flags,GthOverwriteResponse default_response,int io_priority,GCancellable * cancellable,ProgressCallback progress_callback,gpointer progress_callback_data,DialogCallback dialog_callback,gpointer dialog_callback_data,CopyReadyCallback ready_callback,gpointer user_data)1255 _gth_file_data_copy_async (GthFileData           *source,
1256 			   GFile                 *destination,
1257 			   gboolean               move,
1258 			   GthFileCopyFlags       flags,
1259 			   GthOverwriteResponse   default_response,
1260 			   int                    io_priority,
1261 			   GCancellable          *cancellable,
1262 			   ProgressCallback       progress_callback,
1263 			   gpointer               progress_callback_data,
1264 			   DialogCallback         dialog_callback,
1265 			   gpointer               dialog_callback_data,
1266 			   CopyReadyCallback      ready_callback,
1267 			   gpointer               user_data)
1268 {
1269 	_g_copy_file_async_private (source,
1270 				    destination,
1271 				    move,
1272 				    flags,
1273 				    default_response,
1274 				    io_priority,
1275 				    g_file_info_get_size (source->info),
1276 				    0,
1277 				    1,
1278 				    cancellable,
1279 				    progress_callback,
1280 				    progress_callback_data,
1281 				    dialog_callback,
1282 				    dialog_callback_data,
1283 				    ready_callback,
1284 				    user_data);
1285 }
1286 
1287 
1288 /* -- _g_file_list_copy_async -- */
1289 
1290 
1291 typedef struct {
1292 	GHashTable           *source_hash;
1293 	GFile                *destination;
1294 
1295 	GList                *files;  /* GthFileData list */
1296 	GList                *current;
1297 	GList                *copied_directories; /* GFile list */
1298 	GHashTable           *copied_files;
1299 	GFile                *source_base;
1300 	GFile                *current_destination;
1301 
1302 	GList                *source_sidecars;  /* GFile list */
1303 	GList                *destination_sidecars;  /* GFile list */
1304 	GList                *current_source_sidecar;
1305 	GList                *current_destination_sidecar;
1306 
1307 	goffset               tot_size;
1308 	goffset               copied_size;
1309 	gsize                 tot_files;
1310 
1311 	char                 *message;
1312 	GthOverwriteResponse  default_response;
1313 
1314 	gboolean              move;
1315 	GthFileCopyFlags      flags;
1316 	int                   io_priority;
1317 	GCancellable         *cancellable;
1318 	ProgressCallback      progress_callback;
1319 	gpointer              progress_callback_data;
1320 	DialogCallback        dialog_callback;
1321 	gpointer              dialog_callback_data;
1322 	ReadyFunc             done_callback;
1323 	gpointer              user_data;
1324 } CopyData;
1325 
1326 
1327 static void
copy_data_free(CopyData * copy_data)1328 copy_data_free (CopyData *copy_data)
1329 {
1330 	g_free (copy_data->message);
1331 	_g_object_list_unref (copy_data->destination_sidecars);
1332 	_g_object_list_unref (copy_data->source_sidecars);
1333 	_g_object_unref (copy_data->current_destination);
1334 	_g_object_list_unref (copy_data->copied_directories);
1335 	g_hash_table_destroy (copy_data->copied_files);
1336 	_g_object_list_unref (copy_data->files);
1337 	_g_object_unref (copy_data->source_base);
1338 	g_hash_table_destroy (copy_data->source_hash);
1339 	g_object_unref (copy_data->destination);
1340 	_g_object_unref (copy_data->cancellable);
1341 	g_free (copy_data);
1342 }
1343 
1344 
1345 static void
copy_data__done(CopyData * copy_data,GError * error)1346 copy_data__done (CopyData *copy_data,
1347 		 GError   *error)
1348 {
1349 	copy_data->done_callback (error, copy_data->user_data);
1350 	copy_data_free (copy_data);
1351 }
1352 
1353 
1354 static void copy_data__copy_current_file (CopyData *copy_data);
1355 
1356 
1357 static void
copy_data__copy_next_file(CopyData * copy_data)1358 copy_data__copy_next_file (CopyData *copy_data)
1359 {
1360 	GthFileData *source = (GthFileData *) copy_data->current->data;
1361 
1362 	copy_data->copied_size += g_file_info_get_size (source->info);
1363 	copy_data->current = copy_data->current->next;
1364 	copy_data__copy_current_file (copy_data);
1365 }
1366 
1367 
1368 static void
copy_data__copy_current_file_ready_cb(GthOverwriteResponse response,GList * other_files,GError * error,gpointer user_data)1369 copy_data__copy_current_file_ready_cb (GthOverwriteResponse  response,
1370 				       GList                *other_files,
1371 				       GError               *error,
1372 			 	       gpointer              user_data)
1373 {
1374 	CopyData    *copy_data = user_data;
1375 	GthFileData *source = (GthFileData *) copy_data->current->data;
1376 	GList       *scan;
1377 
1378 	if (error == NULL) {
1379 		/* save the correctly copied directories in order to delete
1380 		 * them after moving their content. */
1381 		if (copy_data->move) {
1382 			if (g_file_info_get_file_type (source->info) == G_FILE_TYPE_DIRECTORY)
1383 				copy_data->copied_directories = g_list_prepend (copy_data->copied_directories, g_file_dup (source->file));
1384 		}
1385 	}
1386 	else if ((response == GTH_OVERWRITE_RESPONSE_ALWAYS_NO) || ! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
1387 		copy_data__done (copy_data, error);
1388 		return;
1389 	}
1390 
1391 	g_hash_table_insert (copy_data->copied_files, g_file_dup (source->file), GINT_TO_POINTER (1));
1392 	for (scan = other_files; scan != NULL; scan = scan->next) {
1393 		GFile *other_file = G_FILE (scan->data);
1394 		g_hash_table_insert (copy_data->copied_files, g_file_dup (other_file), GINT_TO_POINTER (1));
1395 	}
1396 
1397 	copy_data->default_response = response;
1398 	copy_data__copy_next_file (copy_data);
1399 }
1400 
1401 
1402 static void
copy_data__delete_source_directories(CopyData * copy_data)1403 copy_data__delete_source_directories (CopyData *copy_data)
1404 {
1405 	GError *error = NULL;
1406 
1407 	if (copy_data->move) {
1408 		GList *scan;
1409 
1410 		for (scan = copy_data->copied_directories; scan; scan = scan->next) {
1411 			GFile *file = scan->data;
1412 
1413 			if (! g_file_delete (file, copy_data->cancellable, &error)) {
1414 				if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY)) {
1415 					/* ignore this kind of errors, because
1416 					 * it is caused by the user choice of
1417 					 * not move a file. */
1418 					g_clear_error (&error);
1419 				}
1420 				else
1421 					break;
1422 			}
1423 		}
1424 	}
1425 
1426 	copy_data__done (copy_data, error);
1427 }
1428 
1429 
1430 static void
copy_data__copy_current_file(CopyData * copy_data)1431 copy_data__copy_current_file (CopyData *copy_data)
1432 {
1433 	GthFileData *source;
1434 	gboolean     explicitly_requested;
1435 	GFile       *destination;
1436 
1437 	if (copy_data->current == NULL) {
1438 		copy_data__delete_source_directories (copy_data);
1439 		return;
1440 	}
1441 
1442 	source = GTH_FILE_DATA (copy_data->current->data);
1443 
1444 	/* Ignore already copied files.  These are sidecar files already copied
1445 	 * with _g_copy_file_async_private */
1446 
1447 	if (g_hash_table_lookup (copy_data->copied_files, source->file) != NULL) {
1448 		call_when_idle ((DataFunc) copy_data__copy_next_file, copy_data);
1449 		return;
1450 	}
1451 
1452 	/* Ignore non-existent files that weren't explicitly requested,
1453 	 * they are children of some requested directory and if they don't
1454 	 * exist anymore they have been already moved to the destination
1455 	 * because they are metadata of other files. */
1456 	explicitly_requested = (g_hash_table_lookup (copy_data->source_hash, source->file) != NULL);
1457 	if (! explicitly_requested) {
1458 		if (! g_file_query_exists (source->file, copy_data->cancellable)) {
1459 			call_when_idle ((DataFunc) copy_data__copy_next_file, copy_data);
1460 			return;
1461 		}
1462 	}
1463 
1464 	/* compute the destination */
1465 	if (explicitly_requested) {
1466 		_g_object_unref (copy_data->source_base);
1467 		copy_data->source_base = g_file_get_parent (source->file);
1468 	}
1469 	destination = _g_file_get_destination (source->file, copy_data->source_base, copy_data->destination);
1470 
1471 	_g_copy_file_async_private (source,
1472 				    destination,
1473 				    copy_data->move,
1474 				    copy_data->flags,
1475 				    copy_data->default_response,
1476 				    copy_data->io_priority,
1477 				    copy_data->tot_size,
1478 				    copy_data->copied_size,
1479 				    copy_data->tot_files,
1480 				    copy_data->cancellable,
1481 				    copy_data->progress_callback,
1482 				    copy_data->progress_callback_data,
1483 				    copy_data->dialog_callback,
1484 				    copy_data->dialog_callback_data,
1485 				    copy_data__copy_current_file_ready_cb,
1486 				    copy_data);
1487 
1488 	g_object_unref (destination);
1489 }
1490 
1491 
1492 static void
copy_files__sources_info_ready_cb(GList * files,GError * error,gpointer user_data)1493 copy_files__sources_info_ready_cb (GList    *files,
1494 			           GError   *error,
1495 			           gpointer  user_data)
1496 {
1497 	CopyData *copy_data = user_data;
1498 	GList    *scan;
1499 
1500 	if (error != NULL) {
1501 		copy_data__done (copy_data, error);
1502 		return;
1503 	}
1504 
1505 	copy_data->files = _g_object_list_ref (files);
1506 	copy_data->tot_size = 0;
1507 	copy_data->tot_files = 0;
1508 	for (scan = copy_data->files; scan; scan = scan->next) {
1509 		GthFileData *file_data = GTH_FILE_DATA (scan->data);
1510 
1511 		copy_data->tot_size += g_file_info_get_size (file_data->info);
1512 		copy_data->tot_files += 1;
1513 	}
1514 
1515 	copy_data->copied_size = 0;
1516 	copy_data->current = copy_data->files;
1517 	copy_data__copy_current_file (copy_data);
1518 }
1519 
1520 
1521 void
_g_file_list_copy_async(GList * sources,GFile * destination,gboolean move,GthFileCopyFlags flags,GthOverwriteResponse default_response,int io_priority,GCancellable * cancellable,ProgressCallback progress_callback,gpointer progress_callback_data,DialogCallback dialog_callback,gpointer dialog_callback_data,ReadyFunc done_callback,gpointer user_data)1522 _g_file_list_copy_async (GList                *sources, /* GFile list */
1523 			 GFile                *destination,
1524 			 gboolean              move,
1525 			 GthFileCopyFlags      flags,
1526 			 GthOverwriteResponse  default_response,
1527 			 int                   io_priority,
1528 			 GCancellable         *cancellable,
1529 			 ProgressCallback      progress_callback,
1530 			 gpointer              progress_callback_data,
1531 			 DialogCallback        dialog_callback,
1532 			 gpointer              dialog_callback_data,
1533 			 ReadyFunc             done_callback,
1534 			 gpointer              user_data)
1535 {
1536 	CopyData *copy_data;
1537 	GList    *scan;
1538 
1539 	copy_data = g_new0 (CopyData, 1);
1540 	copy_data->destination = g_object_ref (destination);
1541 	copy_data->move = move;
1542 	copy_data->flags = flags;
1543 	copy_data->io_priority = io_priority;
1544 	copy_data->cancellable = _g_object_ref (cancellable);
1545 	copy_data->progress_callback = progress_callback;
1546 	copy_data->progress_callback_data = progress_callback_data;
1547 	copy_data->dialog_callback = dialog_callback;
1548 	copy_data->dialog_callback_data = dialog_callback_data;
1549 	copy_data->done_callback = done_callback;
1550 	copy_data->user_data = user_data;
1551 	copy_data->default_response = default_response;
1552 	copy_data->copied_files = g_hash_table_new_full ((GHashFunc) g_file_hash, (GEqualFunc) g_file_equal, (GDestroyNotify) g_object_unref, NULL);
1553 
1554 	/* save the explicitly requested files */
1555 	copy_data->source_hash = g_hash_table_new_full ((GHashFunc) g_file_hash, (GEqualFunc) g_file_equal, (GDestroyNotify) g_object_unref, NULL);
1556 	for (scan = sources; scan; scan = scan->next)
1557 		g_hash_table_insert (copy_data->source_hash, g_object_ref (scan->data), GINT_TO_POINTER (1));
1558 
1559 	if (copy_data->progress_callback != NULL)
1560 		copy_data->progress_callback (NULL,
1561 					      copy_data->move ? _("Moving files") : _("Copying files"),
1562 					      _("Getting file information"),
1563 					      TRUE,
1564 					      0.0,
1565 					      copy_data->progress_callback_data);
1566 
1567 	/* for each directory in 'sources' this query will add all of its content
1568 	 * to the file list. */
1569 	_g_file_list_query_info_async (sources,
1570 				       GTH_LIST_RECURSIVE,
1571 				       "standard::name,standard::display-name,standard::type,standard::size",
1572 				       copy_data->cancellable,
1573 				       copy_files__sources_info_ready_cb,
1574 				       copy_data);
1575 }
1576 
1577 
1578 gboolean
_g_file_move(GFile * source,GFile * destination,GFileCopyFlags flags,GCancellable * cancellable,GFileProgressCallback progress_callback,gpointer progress_callback_data,GError ** error)1579 _g_file_move (GFile                 *source,
1580               GFile                 *destination,
1581               GFileCopyFlags         flags,
1582               GCancellable          *cancellable,
1583               GFileProgressCallback  progress_callback,
1584               gpointer               progress_callback_data,
1585               GError               **error)
1586 {
1587 	GList *source_sidecars = NULL;
1588 	GList *destination_sidecars = NULL;
1589 	GList *scan1;
1590 	GList *scan2;
1591 
1592 	if (! g_file_move (source,
1593 			   destination,
1594 			   flags,
1595 			   cancellable,
1596 			   progress_callback,
1597 			   progress_callback_data,
1598 			   error))
1599 	{
1600 		return FALSE;
1601 	}
1602 
1603 	if ((flags & G_FILE_COPY_ALL_METADATA) == 0)
1604 		return TRUE;
1605 
1606 	/* move the metadata sidecars if requested */
1607 
1608 	gth_hook_invoke ("add-sidecars", source, &source_sidecars);
1609 	source_sidecars = g_list_reverse (source_sidecars);
1610 
1611 	gth_hook_invoke ("add-sidecars", destination, &destination_sidecars);
1612 	destination_sidecars = g_list_reverse (destination_sidecars);
1613 
1614 	for (scan1 = source_sidecars, scan2 = destination_sidecars;
1615 	     scan1 && scan2;
1616 	     scan1 = scan1->next, scan2 = scan2->next)
1617 	{
1618 		source = scan1->data;
1619 		destination = scan2->data;
1620 
1621 		g_file_move (source, destination, 0, cancellable, NULL, NULL, NULL);
1622 	}
1623 
1624 	_g_object_list_unref (destination_sidecars);
1625 	_g_object_list_unref (source_sidecars);
1626 
1627 	return TRUE;
1628 }
1629 
1630 
1631 gboolean
_g_file_list_delete(GList * file_list,gboolean include_metadata,GError ** error)1632 _g_file_list_delete (GList     *file_list,
1633 		     gboolean   include_metadata,
1634 		     GError   **error)
1635 {
1636 	GList *scan;
1637 
1638 	for (scan = file_list; scan; scan = scan->next) {
1639 		GFile *file = scan->data;
1640 
1641 		if (! g_file_delete (file, NULL, error))
1642 			return FALSE;
1643 	}
1644 
1645 	if (include_metadata) {
1646 		GList *sidecars;
1647 		GList *scan;
1648 
1649 		sidecars = NULL;
1650 		for (scan = file_list; scan; scan = scan->next)
1651 			gth_hook_invoke ("add-sidecars", scan->data, &sidecars);
1652 		sidecars = g_list_reverse (sidecars);
1653 
1654 		for (scan = sidecars; scan; scan = scan->next) {
1655 			GFile *file = scan->data;
1656 			g_file_delete (file, NULL, NULL);
1657 		}
1658 
1659 		_g_object_list_unref (sidecars);
1660 	}
1661 
1662 	return TRUE;
1663 }
1664 
1665 
1666 /* -- _g_delete_files_async -- */
1667 
1668 
1669 typedef struct {
1670 	GList             *file_list;
1671 	GList             *current;
1672 	gboolean           include_metadata;
1673 	GCancellable      *cancellable;
1674 	ProgressCallback   progress_callback;
1675 	ReadyFunc          callback;
1676 	gpointer           user_data;
1677 	glong              n_files;
1678 	glong              n_deleted;
1679 } DeleteData;
1680 
1681 
1682 static void
delete_data_free(DeleteData * delete_data)1683 delete_data_free (DeleteData *delete_data)
1684 {
1685 	_g_object_list_unref (delete_data->file_list);
1686 	_g_object_unref (delete_data->cancellable);
1687 	g_free (delete_data);
1688 }
1689 
1690 
1691 static void delete_files__delete_current (DeleteData *delete_data);
1692 
1693 
1694 static void
delete_files__delete_current_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1695 delete_files__delete_current_cb (GObject      *source_object,
1696 				 GAsyncResult *result,
1697 				 gpointer      user_data)
1698 {
1699 	DeleteData *delete_data = user_data;
1700 	GError     *error = NULL;
1701 
1702 	if (! g_file_delete_finish (G_FILE (source_object), result, &error)) {
1703 		delete_data->callback (error, delete_data->user_data);
1704 		delete_data_free (delete_data);
1705 		g_error_free (error);
1706 		return;
1707 	}
1708 
1709 	delete_data->n_deleted++;
1710 	delete_data->current = delete_data->current->next;
1711 	delete_files__delete_current (delete_data);
1712 }
1713 
1714 
1715 static void
delete_files__delete_current(DeleteData * delete_data)1716 delete_files__delete_current (DeleteData *delete_data)
1717 {
1718 	GthFileData *file_data;
1719 
1720 	if (delete_data->current == NULL) {
1721 		delete_data->callback (NULL, delete_data->user_data);
1722 		delete_data_free (delete_data);
1723 		return;
1724 	}
1725 
1726 	if (delete_data->progress_callback != NULL)
1727 		delete_data->progress_callback (NULL,
1728 				_("Deleting files"),
1729 				NULL,
1730 				FALSE,
1731 				(double) (delete_data->n_deleted + 1) / (delete_data->n_files + 1),
1732 				delete_data->user_data);
1733 
1734 	file_data = delete_data->current->data;
1735 	g_file_delete_async (file_data->file,
1736 			     G_PRIORITY_DEFAULT,
1737 			     delete_data->cancellable,
1738 			     delete_files__delete_current_cb,
1739 			     delete_data);
1740 }
1741 
1742 
1743 static void
delete_files__info_ready_cb(GList * files,GError * error,gpointer user_data)1744 delete_files__info_ready_cb (GList    *files,
1745 			     GError   *error,
1746 			     gpointer  user_data)
1747 {
1748 	DeleteData *delete_data = user_data;
1749 
1750 	if (error != NULL) {
1751 		delete_data->callback (error, delete_data->user_data);
1752 		delete_data_free (delete_data);
1753 		return;
1754 	}
1755 
1756 	delete_data->file_list = _g_object_list_ref (files);
1757 	delete_data->file_list = g_list_reverse (delete_data->file_list);
1758 	delete_data->current = delete_data->file_list;
1759 	delete_data->n_files = g_list_length (delete_data->file_list);
1760 	delete_data->n_deleted = 0;
1761 	delete_files__delete_current (delete_data);
1762 }
1763 
1764 
1765 void
_g_file_list_delete_async(GList * file_list,gboolean recursive,gboolean include_metadata,GCancellable * cancellable,ProgressCallback progress_callback,ReadyFunc callback,gpointer user_data)1766 _g_file_list_delete_async (GList		 *file_list,
1767 			   gboolean		  recursive,
1768 			   gboolean		  include_metadata,
1769 			   GCancellable		 *cancellable,
1770 			   ProgressCallback	  progress_callback,
1771 			   ReadyFunc		  callback,
1772 			   gpointer		  user_data)
1773 {
1774 	DeleteData   *delete_data;
1775 	GthListFlags  flags;
1776 
1777 	delete_data = g_new0 (DeleteData, 1);
1778 	delete_data->file_list = NULL;
1779 	delete_data->include_metadata = include_metadata;
1780 	delete_data->cancellable = _g_object_ref (cancellable);
1781 	delete_data->progress_callback = progress_callback;
1782 	delete_data->callback = callback;
1783 	delete_data->user_data = user_data;
1784 
1785 	flags = GTH_LIST_NO_FOLLOW_LINKS;
1786 	if (recursive)
1787 		flags |= GTH_LIST_RECURSIVE;
1788 	if (include_metadata)
1789 		flags |= GTH_LIST_INCLUDE_SIDECARS;
1790 
1791 	if (delete_data->progress_callback != NULL)
1792 		delete_data->progress_callback (NULL,
1793 				_("Getting file information"),
1794 				NULL,
1795 				TRUE,
1796 				0.0,
1797 				delete_data->user_data);
1798 
1799 	_g_file_list_query_info_async (file_list,
1800 				       flags,
1801 				       GFILE_NAME_TYPE_ATTRIBUTES,
1802 				       delete_data->cancellable,
1803 				       delete_files__info_ready_cb,
1804 				       delete_data);
1805 }
1806 
1807 
1808 /* -- _g_trash_files_async -- */
1809 
1810 
1811 typedef struct {
1812 	GList            *file_list;
1813 	GList            *current;
1814 	GCancellable     *cancellable;
1815 	ProgressCallback  progress_callback;
1816 	ReadyFunc         callback;
1817 	gpointer          user_data;
1818 	glong             n_files;
1819 	glong             n_deleted;
1820 } TrashData;
1821 
1822 
1823 static void
trash_data_free(TrashData * tdata)1824 trash_data_free (TrashData *tdata)
1825 {
1826 	_g_object_list_unref (tdata->file_list);
1827 	_g_object_unref (tdata->cancellable);
1828 	g_free (tdata);
1829 }
1830 
1831 
1832 static void trash_files__delete_current (TrashData *tdata);
1833 
1834 
1835 static void
trash_files__delete_current_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1836 trash_files__delete_current_cb (GObject      *source_object,
1837 				GAsyncResult *result,
1838 				gpointer      user_data)
1839 {
1840 	TrashData *tdata = user_data;
1841 	GError    *error = NULL;
1842 
1843 	if (! g_file_trash_finish (G_FILE (source_object), result, &error)) {
1844 		tdata->callback (error, tdata->user_data);
1845 		trash_data_free (tdata);
1846 		g_error_free (error);
1847 		return;
1848 	}
1849 
1850 	tdata->n_deleted++;
1851 	tdata->current = tdata->current->next;
1852 	trash_files__delete_current (tdata);
1853 }
1854 
1855 
1856 static void
trash_files__delete_current(TrashData * tdata)1857 trash_files__delete_current (TrashData *tdata)
1858 {
1859 	GthFileData *file_data;
1860 
1861 	if (tdata->current == NULL) {
1862 		tdata->callback (NULL, tdata->user_data);
1863 		trash_data_free (tdata);
1864 		return;
1865 	}
1866 
1867 	if (tdata->progress_callback != NULL)
1868 		tdata->progress_callback (NULL,
1869 				_("Moving files to trash"),
1870 				NULL,
1871 				FALSE,
1872 				(double) (tdata->n_deleted + 1) / (tdata->n_files + 1),
1873 				tdata->user_data);
1874 
1875 	file_data = GTH_FILE_DATA (tdata->current->data);
1876 	g_file_trash_async (file_data->file,
1877 			    G_PRIORITY_DEFAULT,
1878 			    tdata->cancellable,
1879 			    trash_files__delete_current_cb,
1880 			    tdata);
1881 }
1882 
1883 
1884 static void
trash_files__info_ready_cb(GList * files,GError * error,gpointer user_data)1885 trash_files__info_ready_cb (GList    *files,
1886 			    GError   *error,
1887 			    gpointer  user_data)
1888 {
1889 	TrashData *tdata = user_data;
1890 
1891 	if (error != NULL) {
1892 		tdata->callback (error, tdata->user_data);
1893 		trash_data_free (tdata);
1894 		return;
1895 	}
1896 
1897 	tdata->file_list = _g_object_list_ref (files);
1898 	tdata->n_files = g_list_length (tdata->file_list);
1899 	tdata->n_deleted = 0;
1900 	tdata->current = tdata->file_list;
1901 	trash_files__delete_current (tdata);
1902 }
1903 
1904 
1905 void
_g_file_list_trash_async(GList * file_list,GCancellable * cancellable,ProgressCallback progress_callback,ReadyFunc callback,gpointer user_data)1906 _g_file_list_trash_async (GList			 *file_list, /* GFile list */
1907 			  GCancellable		 *cancellable,
1908 			  ProgressCallback	  progress_callback,
1909 			  ReadyFunc		  callback,
1910 			  gpointer		  user_data)
1911 {
1912 	TrashData *tdata;
1913 
1914 	tdata = g_new0 (TrashData, 1);
1915 	tdata->file_list = NULL;
1916 	tdata->cancellable = _g_object_ref (cancellable);
1917 	tdata->progress_callback = progress_callback;
1918 	tdata->callback = callback;
1919 	tdata->user_data = user_data;
1920 
1921 	if (tdata->progress_callback != NULL)
1922 		tdata->progress_callback (NULL,
1923 				_("Getting file information"),
1924 				NULL,
1925 				TRUE,
1926 				0.0,
1927 				tdata->user_data);
1928 
1929 	_g_file_list_query_info_async (file_list,
1930 				       GTH_LIST_INCLUDE_SIDECARS,
1931 				       GFILE_NAME_TYPE_ATTRIBUTES,
1932 				       tdata->cancellable,
1933 				       trash_files__info_ready_cb,
1934 				       tdata);
1935 }
1936 
1937 
1938 #define BUFFER_SIZE (64 * 1024)
1939 
1940 
1941 gboolean
_g_input_stream_read_all(GInputStream * istream,void ** buffer,gsize * size,GCancellable * cancellable,GError ** error)1942 _g_input_stream_read_all (GInputStream  *istream,
1943 			  void         **buffer,
1944 			  gsize         *size,
1945 			  GCancellable  *cancellable,
1946 			  GError       **error)
1947 {
1948 	gboolean  retval = FALSE;
1949 	guchar   *local_buffer = NULL;
1950 	char     *tmp_buffer;
1951 	gsize     count;
1952 	gssize    n;
1953 
1954 	tmp_buffer = g_new (char, BUFFER_SIZE);
1955 	count = 0;
1956 	for (;;) {
1957 		n = g_input_stream_read (istream, tmp_buffer, BUFFER_SIZE, cancellable, error);
1958 		if (n < 0) {
1959 			g_free (local_buffer);
1960 			retval = FALSE;
1961 			break;
1962 		}
1963 		else if (n == 0) {
1964 			*buffer = local_buffer;
1965 			*size = count;
1966 			retval = TRUE;
1967 			break;
1968 		}
1969 
1970 		/* the '+ 1' here is used to allow to add a NULL character at the
1971 		 * end of the buffer. */
1972 		local_buffer = g_realloc (local_buffer, count + n + 1);
1973 		memcpy (local_buffer + count, tmp_buffer, n);
1974 		count += n;
1975 	}
1976 
1977 	g_free (tmp_buffer);
1978 
1979 	return retval;
1980 }
1981 
1982 
1983 gboolean
_g_file_load_in_buffer(GFile * file,void ** buffer,gsize * size,GCancellable * cancellable,GError ** error)1984 _g_file_load_in_buffer (GFile         *file,
1985 		        void         **buffer,
1986 		        gsize         *size,
1987 		        GCancellable  *cancellable,
1988 		        GError       **error)
1989 {
1990 	GInputStream *istream;
1991 	gboolean      retval = FALSE;
1992 
1993 	istream = (GInputStream *) g_file_read (file, cancellable, error);
1994 	if (istream != NULL) {
1995 		retval = _g_input_stream_read_all (istream, buffer, size, cancellable, error);
1996 		g_object_unref (istream);
1997 	}
1998 
1999 	return retval;
2000 }
2001 
2002 
2003 typedef struct {
2004 	int                  io_priority;
2005 	GCancellable        *cancellable;
2006 	BufferReadyCallback  callback;
2007 	gpointer             user_data;
2008 	GInputStream        *stream;
2009 	guchar               tmp_buffer[BUFFER_SIZE];
2010 	guchar              *buffer;
2011 	gsize                count;
2012 } LoadData;
2013 
2014 
2015 static void
load_data_free(LoadData * load_data)2016 load_data_free (LoadData *load_data)
2017 {
2018 	if (load_data->stream != NULL)
2019 		g_object_unref (load_data->stream);
2020 	g_free (load_data->buffer);
2021 	g_free (load_data);
2022 }
2023 
2024 
2025 static void
load_file__stream_read_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2026 load_file__stream_read_cb (GObject      *source_object,
2027 			   GAsyncResult *result,
2028 			   gpointer      user_data)
2029 {
2030 	LoadData *load_data = user_data;
2031 	GError   *error = NULL;
2032 	gssize    count;
2033 
2034 	count = g_input_stream_read_finish (load_data->stream, result, &error);
2035 	if (count < 0) {
2036 		load_data->callback ((gpointer *) &load_data->buffer, -1, error, load_data->user_data);
2037 		load_data_free (load_data);
2038 		return;
2039 	}
2040 	else if (count == 0) {
2041 		if (load_data->buffer != NULL)
2042 			((char *)load_data->buffer)[load_data->count] = 0;
2043 		load_data->callback ((gpointer *) &load_data->buffer, load_data->count, NULL, load_data->user_data);
2044 		load_data_free (load_data);
2045 		return;
2046 	}
2047 
2048 	load_data->buffer = g_realloc (load_data->buffer, load_data->count + count + 1);
2049 	memcpy (load_data->buffer + load_data->count, load_data->tmp_buffer, count);
2050 	load_data->count += count;
2051 
2052 	g_input_stream_read_async (load_data->stream,
2053 				   load_data->tmp_buffer,
2054 				   BUFFER_SIZE,
2055 				   load_data->io_priority,
2056 				   load_data->cancellable,
2057 				   load_file__stream_read_cb,
2058 				   load_data);
2059 }
2060 
2061 
2062 static void
load_file__file_read_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2063 load_file__file_read_cb (GObject      *source_object,
2064 			 GAsyncResult *result,
2065 			 gpointer      user_data)
2066 {
2067 	LoadData *load_data = user_data;
2068 	GError   *error = NULL;
2069 
2070 	load_data->stream = (GInputStream *) g_file_read_finish (G_FILE (source_object), result, &error);
2071 	if (load_data->stream == NULL) {
2072 		load_data->callback ((gpointer *) &load_data->buffer, -1, error, load_data->user_data);
2073 		load_data_free (load_data);
2074 		return;
2075 	}
2076 
2077 	load_data->count = 0;
2078 	g_input_stream_read_async (load_data->stream,
2079 				   load_data->tmp_buffer,
2080 				   BUFFER_SIZE,
2081 				   load_data->io_priority,
2082 				   load_data->cancellable,
2083 				   load_file__stream_read_cb,
2084 				   load_data);
2085 }
2086 
2087 
2088 void
_g_file_load_async(GFile * file,int io_priority,GCancellable * cancellable,BufferReadyCallback callback,gpointer user_data)2089 _g_file_load_async (GFile               *file,
2090 		    int                  io_priority,
2091 		    GCancellable        *cancellable,
2092 		    BufferReadyCallback  callback,
2093 		    gpointer             user_data)
2094 {
2095 	LoadData *load_data;
2096 
2097 	load_data = g_new0 (LoadData, 1);
2098 	load_data->io_priority = io_priority;
2099 	load_data->cancellable = cancellable;
2100 	load_data->callback = callback;
2101 	load_data->user_data = user_data;
2102 
2103 	g_file_read_async (file, io_priority, cancellable, load_file__file_read_cb, load_data);
2104 }
2105 
2106 
2107 /* -- _g_file_write_async -- */
2108 
2109 
2110 typedef struct {
2111 	int                  io_priority;
2112 	GCancellable        *cancellable;
2113 	BufferReadyCallback  callback;
2114 	gpointer             user_data;
2115 	guchar              *buffer;
2116 	gsize                count;
2117 	gsize                written;
2118 	GError              *error;
2119 } WriteData;
2120 
2121 
2122 static void
write_data_free(WriteData * write_data)2123 write_data_free (WriteData *write_data)
2124 {
2125 	g_free (write_data->buffer);
2126 	g_free (write_data);
2127 }
2128 
2129 
2130 static void
write_file__notify(gpointer user_data)2131 write_file__notify (gpointer user_data)
2132 {
2133 	WriteData *write_data = user_data;
2134 
2135 	write_data->callback ((gpointer *) &write_data->buffer, write_data->count, write_data->error, write_data->user_data);
2136 	write_data_free (write_data);
2137 }
2138 
2139 
2140 static void
write_file__stream_flush_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2141 write_file__stream_flush_cb (GObject      *source_object,
2142 			     GAsyncResult *result,
2143 			     gpointer      user_data)
2144 {
2145 	GOutputStream *stream = (GOutputStream *) source_object;
2146 	WriteData     *write_data = user_data;
2147 	GError        *error = NULL;
2148 
2149 	g_output_stream_flush_finish (stream, result, &error);
2150 	write_data->error = error;
2151 	g_object_unref (stream);
2152 
2153 	call_when_idle (write_file__notify, write_data);
2154 }
2155 
2156 
2157 static void
write_file__stream_write_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2158 write_file__stream_write_ready_cb (GObject      *source_object,
2159 				   GAsyncResult *result,
2160 				   gpointer      user_data)
2161 {
2162 	GOutputStream *stream = (GOutputStream *) source_object;
2163 	WriteData     *write_data = user_data;
2164 	GError        *error = NULL;
2165 	gssize         count;
2166 
2167 	count = g_output_stream_write_finish (stream, result, &error);
2168 	write_data->written += count;
2169 
2170 	if ((count == 0) || (write_data->written == write_data->count)) {
2171 		g_output_stream_flush_async (stream,
2172 					     write_data->io_priority,
2173 					     write_data->cancellable,
2174 					     write_file__stream_flush_cb,
2175 					     user_data);
2176 		return;
2177 	}
2178 
2179 	g_output_stream_write_async (stream,
2180 				     write_data->buffer + write_data->written,
2181 				     write_data->count - write_data->written,
2182 				     write_data->io_priority,
2183 				     write_data->cancellable,
2184 				     write_file__stream_write_ready_cb,
2185 				     write_data);
2186 }
2187 
2188 
2189 static void
write_file__replace_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2190 write_file__replace_ready_cb (GObject      *source_object,
2191 			      GAsyncResult *result,
2192 			      gpointer      user_data)
2193 {
2194 	WriteData     *write_data = user_data;
2195 	GOutputStream *stream;
2196 	GError        *error = NULL;
2197 
2198 	stream = (GOutputStream*) g_file_replace_finish ((GFile*) source_object, result, &error);
2199 	if (stream == NULL) {
2200 		write_data->callback ((gpointer *) &write_data->buffer, write_data->count, error, write_data->user_data);
2201 		write_data_free (write_data);
2202 		return;
2203 	}
2204 
2205 	write_data->written = 0;
2206 	g_output_stream_write_async (stream,
2207 				     write_data->buffer,
2208 				     write_data->count,
2209 				     write_data->io_priority,
2210 				     write_data->cancellable,
2211 				     write_file__stream_write_ready_cb,
2212 				     write_data);
2213 }
2214 
2215 
2216 static void
write_file__create_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2217 write_file__create_ready_cb (GObject      *source_object,
2218 			     GAsyncResult *result,
2219 			     gpointer      user_data)
2220 {
2221 	WriteData     *write_data = user_data;
2222 	GOutputStream *stream;
2223 	GError        *error = NULL;
2224 
2225 	stream = (GOutputStream*) g_file_create_finish ((GFile*) source_object, result, &error);
2226 	if (stream == NULL) {
2227 		write_data->callback ((gpointer *) &write_data->buffer, write_data->count, error, write_data->user_data);
2228 		write_data_free (write_data);
2229 		return;
2230 	}
2231 
2232 	write_data->written = 0;
2233 	g_output_stream_write_async (stream,
2234 				     write_data->buffer,
2235 				     write_data->count,
2236 				     write_data->io_priority,
2237 				     write_data->cancellable,
2238 				     write_file__stream_write_ready_cb,
2239 				     write_data);
2240 }
2241 
2242 
2243 void
_g_file_write_async(GFile * file,void * buffer,gsize count,gboolean replace,int io_priority,GCancellable * cancellable,BufferReadyCallback callback,gpointer user_data)2244 _g_file_write_async (GFile               *file,
2245 		     void                *buffer,
2246 		     gsize                count,
2247 		     gboolean             replace,
2248 		     int                  io_priority,
2249 		     GCancellable        *cancellable,
2250 		     BufferReadyCallback  callback,
2251 		     gpointer             user_data)
2252 {
2253 	WriteData *write_data;
2254 
2255 	write_data = g_new0 (WriteData, 1);
2256 	write_data->buffer = buffer;
2257 	write_data->count = count;
2258 	write_data->io_priority = io_priority;
2259 	write_data->cancellable = cancellable;
2260 	write_data->callback = callback;
2261 	write_data->user_data = user_data;
2262 
2263 	if (replace)
2264 		g_file_replace_async (file,
2265 				      NULL,
2266 				      FALSE,
2267 				      0,
2268 				      io_priority,
2269 				      cancellable,
2270 				      write_file__replace_ready_cb,
2271 				      write_data);
2272 	else
2273 		g_file_create_async (file,
2274 				     0,
2275 				     io_priority,
2276 				     cancellable,
2277 				     write_file__create_ready_cb,
2278 				     write_data);
2279 }
2280 
2281 
2282 GFile *
_g_file_create_unique(GFile * parent,const char * display_name,const char * suffix,GError ** error)2283 _g_file_create_unique (GFile       *parent,
2284 		       const char  *display_name,
2285 		       const char  *suffix,
2286 		       GError     **error)
2287 {
2288 	GFile             *file = NULL;
2289 	GError            *local_error = NULL;
2290 	int                n;
2291 	GFileOutputStream *stream = NULL;
2292 
2293 	n = 0;
2294 	do {
2295 		char *new_display_name;
2296 
2297 		if (file != NULL)
2298 			g_object_unref (file);
2299 
2300 		n++;
2301 		if (n == 1)
2302 			new_display_name = g_strdup_printf ("%s%s", display_name, suffix);
2303 		else
2304 			new_display_name = g_strdup_printf ("%s %d%s", display_name, n, suffix);
2305 
2306 		file = g_file_get_child_for_display_name (parent, new_display_name, &local_error);
2307 		if (local_error == NULL)
2308 			stream = g_file_create (file, 0, NULL, &local_error);
2309 
2310 		if ((stream == NULL) && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
2311 			g_clear_error (&local_error);
2312 
2313 		g_free (new_display_name);
2314 	}
2315 	while ((stream == NULL) && (local_error == NULL));
2316 
2317 	if (stream == NULL) {
2318 		if (error != NULL)
2319 			*error = local_error;
2320 		g_object_unref (file);
2321 		file = NULL;
2322 	}
2323 	else
2324 		g_object_unref (stream);
2325 
2326 	return file;
2327 }
2328 
2329 
2330 GFile *
_g_directory_create_tmp(void)2331 _g_directory_create_tmp (void)
2332 {
2333 	const int  max_attemps = 10;
2334 	const int  name_len = 12;
2335 	GFile     *tmp_dir;
2336 	GFile     *dir = NULL;
2337 	int        n;
2338 
2339 	tmp_dir = g_file_new_for_path (g_get_tmp_dir ());
2340 	if (tmp_dir == NULL)
2341 		return NULL;
2342 
2343 	for (n = 0; n < max_attemps; n++) {
2344 		char  *name;
2345 
2346 		name = _g_str_random (name_len);
2347 		dir = g_file_get_child (tmp_dir, name);
2348 		g_free (name);
2349 
2350 		if (g_file_make_directory (dir, NULL, NULL))
2351 			break;
2352 
2353 		g_object_unref (dir);
2354 	}
2355 
2356 	g_object_unref (tmp_dir);
2357 
2358 	return dir;
2359 }
2360 
2361 
2362 /* -- _g_file_write -- */
2363 
2364 
2365 gboolean
_g_file_write(GFile * file,gboolean make_backup,GFileCreateFlags flags,void * buffer,gsize count,GCancellable * cancellable,GError ** error)2366 _g_file_write (GFile             *file,
2367 	       gboolean           make_backup,
2368 	       GFileCreateFlags   flags,
2369 	       void              *buffer,
2370 	       gsize              count,
2371 	       GCancellable      *cancellable,
2372 	       GError           **error)
2373 {
2374 	gboolean       success;
2375 	GOutputStream *stream;
2376 
2377 	stream = (GOutputStream *) g_file_replace (file, NULL, make_backup, flags, cancellable, error);
2378 	if (stream != NULL)
2379 		success = g_output_stream_write_all (stream, buffer, count, NULL, cancellable, error);
2380 	else
2381 		success = FALSE;
2382 
2383 	_g_object_unref (stream);
2384 
2385 	return success;
2386 }
2387 
2388 
2389 gboolean
_g_file_set_modification_time(GFile * file,GTimeVal * timeval,GCancellable * cancellable,GError ** error)2390 _g_file_set_modification_time (GFile         *file,
2391 			       GTimeVal      *timeval,
2392 			       GCancellable  *cancellable,
2393 			       GError       **error)
2394 {
2395 	GFileInfo *info;
2396 	gboolean   result;
2397 
2398 	info = g_file_info_new ();
2399 	g_file_info_set_modification_time (info, timeval);
2400 	result = g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, cancellable, error);
2401 
2402 	g_object_unref (info);
2403 
2404 	return result;
2405 }
2406 
2407 
2408 GFileInfo *
_g_file_get_info_for_display(GFile * file)2409 _g_file_get_info_for_display (GFile *file)
2410 {
2411 	GFileInfo     *file_info;
2412 	GthFileSource *file_source;
2413 
2414 	file_info = NULL;
2415 	file_source = gth_main_get_file_source (file);
2416 	if ((file_source != NULL) && ! GTH_IS_FILE_SOURCE_VFS (file_source))
2417 		file_info = gth_file_source_get_file_info (file_source, file, GFILE_DISPLAY_ATTRIBUTES);
2418 
2419 	if (file_info == NULL) {
2420 		char  *name;
2421 		char  *uri;
2422 		GIcon *icon;
2423 
2424 		file_info = g_file_info_new ();
2425 
2426 		name = _g_file_get_display_name (file);
2427 		g_file_info_set_display_name (file_info, name);
2428 
2429 		uri = g_file_get_uri (file);
2430 		icon = g_themed_icon_new (g_str_has_prefix (uri, "file://") ? "folder-symbolic" : "folder-remote-symbolic");
2431 		g_file_info_set_symbolic_icon (file_info, icon);
2432 
2433 		g_object_unref (icon);
2434 		g_free (uri);
2435 		g_free (name);
2436 	}
2437 
2438 	return file_info;
2439 }
2440 
2441 
2442 GMenuItem *
_g_menu_item_new_for_file(GFile * file,const char * custom_label)2443 _g_menu_item_new_for_file (GFile      *file,
2444 			   const char *custom_label)
2445 {
2446 	GMenuItem *item;
2447 	GFileInfo *info;
2448 
2449 	item = g_menu_item_new (NULL, NULL);
2450 	info = _g_file_get_info_for_display (file);
2451 	if (info != NULL) {
2452 		g_menu_item_set_label (item, (custom_label != NULL) ? custom_label : g_file_info_get_display_name (info));
2453 		g_menu_item_set_icon (item, g_file_info_get_symbolic_icon (info));
2454 
2455 		g_object_unref (info);
2456 	}
2457 
2458 	return item;
2459 }
2460 
2461 
2462 GMenuItem *
_g_menu_item_new_for_file_data(GthFileData * file_data)2463 _g_menu_item_new_for_file_data (GthFileData *file_data)
2464 {
2465 	GMenuItem *item;
2466 
2467 	item = g_menu_item_new (NULL, NULL);
2468 	g_menu_item_set_label (item, g_file_info_get_display_name (file_data->info));
2469 	g_menu_item_set_icon (item, g_file_info_get_symbolic_icon (file_data->info));
2470 
2471 	return item;
2472 }
2473