1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  Engrampa
5  *
6  *  Copyright (C) 2001, 2003, 2007, 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, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include <config.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/param.h>
29 
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <gio/gio.h>
33 #include "glib-utils.h"
34 #include "file-utils.h"
35 #include "gio-utils.h"
36 #include "file-data.h"
37 #include "fr-archive.h"
38 #include "fr-command.h"
39 #include "fr-error.h"
40 #include "fr-marshal.h"
41 #include "fr-proc-error.h"
42 #include "fr-process.h"
43 #include "fr-init.h"
44 
45 #ifdef ENABLE_MAGIC
46 #include <magic.h>
47 #endif
48 
49 #ifndef NCARGS
50 #define NCARGS _POSIX_ARG_MAX
51 #endif
52 
53 
54 /* -- DroppedItemsData -- */
55 
56 
57 typedef struct {
58 	FrArchive     *archive;
59 	GList         *item_list;
60 	char          *base_dir;
61 	char          *dest_dir;
62 	gboolean       update;
63 	char          *password;
64 	gboolean       encrypt_header;
65 	FrCompression  compression;
66 	guint          volume_size;
67 } DroppedItemsData;
68 
69 
70 static DroppedItemsData *
dropped_items_data_new(FrArchive * archive,GList * item_list,const char * base_dir,const char * dest_dir,gboolean update,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size)71 dropped_items_data_new (FrArchive     *archive,
72 			GList         *item_list,
73 			const char    *base_dir,
74 			const char    *dest_dir,
75 			gboolean       update,
76 			const char    *password,
77 			gboolean       encrypt_header,
78 			FrCompression  compression,
79 			guint          volume_size)
80 {
81 	DroppedItemsData *data;
82 
83 	data = g_new0 (DroppedItemsData, 1);
84 	data->archive = archive;
85 	data->item_list = path_list_dup (item_list);
86 	if (base_dir != NULL)
87 		data->base_dir = g_strdup (base_dir);
88 	if (dest_dir != NULL)
89 		data->dest_dir = g_strdup (dest_dir);
90 	data->update = update;
91 	if (password != NULL)
92 		data->password = g_strdup (password);
93 	data->encrypt_header = encrypt_header;
94 	data->compression = compression;
95 	data->volume_size = volume_size;
96 
97 	return data;
98 }
99 
100 
101 static void
dropped_items_data_free(DroppedItemsData * data)102 dropped_items_data_free (DroppedItemsData *data)
103 {
104 	if (data == NULL)
105 		return;
106 	path_list_free (data->item_list);
107 	g_free (data->base_dir);
108 	g_free (data->dest_dir);
109 	g_free (data->password);
110 	g_free (data);
111 }
112 
113 
114 struct _FrArchivePrivData {
115 	FakeLoadFunc         fake_load_func;                /* If returns TRUE, archives are not read when
116 							     * fr_archive_load is invoked, used
117 							     * in batch mode. */
118 	gpointer             fake_load_data;
119 	GCancellable        *cancellable;
120 	char                *temp_dir;
121 	gboolean             continue_adding_dropped_items;
122 	DroppedItemsData    *dropped_items_data;
123 
124 	char                *temp_extraction_dir;
125 	char                *extraction_destination;
126 	gboolean             remote_extraction;
127 	gboolean             extract_here;
128 };
129 
130 
131 typedef struct {
132 	FrArchive      *archive;
133 	char           *uri;
134 	FrAction        action;
135 	GList          *file_list;
136 	char           *base_uri;
137 	char           *dest_dir;
138 	gboolean        update;
139 	char           *tmp_dir;
140 	guint           source_id;
141 	char           *password;
142 	gboolean        encrypt_header;
143 	FrCompression   compression;
144 	guint           volume_size;
145 } XferData;
146 
147 
148 static void
xfer_data_free(XferData * data)149 xfer_data_free (XferData *data)
150 {
151 	if (data == NULL)
152 		return;
153 
154 	g_free (data->uri);
155 	g_free (data->password);
156 	path_list_free (data->file_list);
157 	g_free (data->base_uri);
158 	g_free (data->dest_dir);
159 	g_free (data->tmp_dir);
160 	g_free (data);
161 }
162 
163 
164 #define MAX_CHUNK_LEN (NCARGS * 2 / 3) /* Max command line length */
165 #define NO_BACKUP_FILES (TRUE)
166 #define NO_DOT_FILES (FALSE)
167 #define IGNORE_CASE (FALSE)
168 #define LIST_LENGTH_TO_USE_FILE 10 /* FIXME: find a good value */
169 
170 
171 enum {
172 	START,
173 	DONE,
174 	PROGRESS,
175 	MESSAGE,
176 	STOPPABLE,
177 	WORKING_ARCHIVE,
178 	LAST_SIGNAL
179 };
180 
181 static GObjectClass *parent_class;
182 static guint fr_archive_signals[LAST_SIGNAL] = { 0 };
183 
184 static void fr_archive_class_init (FrArchiveClass *class);
185 static void fr_archive_init       (FrArchive *archive);
186 static void fr_archive_finalize   (GObject *object);
187 
188 
189 GType
fr_archive_get_type(void)190 fr_archive_get_type (void)
191 {
192 	static GType type = 0;
193 
194 	if (! type) {
195 		static const GTypeInfo type_info = {
196 			sizeof (FrArchiveClass),
197 			NULL,
198 			NULL,
199 			(GClassInitFunc) fr_archive_class_init,
200 			NULL,
201 			NULL,
202 			sizeof (FrArchive),
203 			0,
204 			(GInstanceInitFunc) fr_archive_init,
205 			NULL
206 		};
207 
208 		type = g_type_register_static (G_TYPE_OBJECT,
209 					       "FrArchive",
210 					       &type_info,
211 					       0);
212 	}
213 
214 	return type;
215 }
216 
217 
218 static void
fr_archive_class_init(FrArchiveClass * class)219 fr_archive_class_init (FrArchiveClass *class)
220 {
221 	GObjectClass *gobject_class = G_OBJECT_CLASS (class);
222 
223 	parent_class = g_type_class_peek_parent (class);
224 
225 	gobject_class->finalize = fr_archive_finalize;
226 
227 	class->start = NULL;
228 	class->done = NULL;
229 	class->progress = NULL;
230 	class->message = NULL;
231 	class->working_archive = NULL;
232 
233 	/* signals */
234 
235 	fr_archive_signals[START] =
236 		g_signal_new ("start",
237 			      G_TYPE_FROM_CLASS (class),
238 			      G_SIGNAL_RUN_LAST,
239 			      G_STRUCT_OFFSET (FrArchiveClass, start),
240 			      NULL, NULL,
241 			      fr_marshal_VOID__INT,
242 			      G_TYPE_NONE,
243 			      1, G_TYPE_INT);
244 	fr_archive_signals[DONE] =
245 		g_signal_new ("done",
246 			      G_TYPE_FROM_CLASS (class),
247 			      G_SIGNAL_RUN_LAST,
248 			      G_STRUCT_OFFSET (FrArchiveClass, done),
249 			      NULL, NULL,
250 			      fr_marshal_VOID__INT_BOXED,
251 			      G_TYPE_NONE, 2,
252 			      G_TYPE_INT,
253 			      FR_TYPE_PROC_ERROR);
254 	fr_archive_signals[PROGRESS] =
255 		g_signal_new ("progress",
256 			      G_TYPE_FROM_CLASS (class),
257 			      G_SIGNAL_RUN_LAST,
258 			      G_STRUCT_OFFSET (FrArchiveClass, progress),
259 			      NULL, NULL,
260 			      fr_marshal_VOID__DOUBLE,
261 			      G_TYPE_NONE, 1,
262 			      G_TYPE_DOUBLE);
263 	fr_archive_signals[MESSAGE] =
264 		g_signal_new ("message",
265 			      G_TYPE_FROM_CLASS (class),
266 			      G_SIGNAL_RUN_LAST,
267 			      G_STRUCT_OFFSET (FrArchiveClass, message),
268 			      NULL, NULL,
269 			      fr_marshal_VOID__STRING,
270 			      G_TYPE_NONE, 1,
271 			      G_TYPE_STRING);
272 	fr_archive_signals[STOPPABLE] =
273 		g_signal_new ("stoppable",
274 			      G_TYPE_FROM_CLASS (class),
275 			      G_SIGNAL_RUN_LAST,
276 			      G_STRUCT_OFFSET (FrArchiveClass, stoppable),
277 			      NULL, NULL,
278 			      fr_marshal_VOID__BOOLEAN,
279 			      G_TYPE_NONE,
280 			      1, G_TYPE_BOOLEAN);
281 	fr_archive_signals[WORKING_ARCHIVE] =
282 		g_signal_new ("working_archive",
283 			      G_TYPE_FROM_CLASS (class),
284 			      G_SIGNAL_RUN_LAST,
285 			      G_STRUCT_OFFSET (FrArchiveClass, working_archive),
286 			      NULL, NULL,
287 			      fr_marshal_VOID__STRING,
288 			      G_TYPE_NONE, 1,
289 			      G_TYPE_STRING);
290 }
291 
292 
293 void
fr_archive_stoppable(FrArchive * archive,gboolean stoppable)294 fr_archive_stoppable (FrArchive *archive,
295 		      gboolean   stoppable)
296 {
297 	g_signal_emit (G_OBJECT (archive),
298 		       fr_archive_signals[STOPPABLE],
299 		       0,
300 		       stoppable);
301 }
302 
303 
304 void
fr_archive_stop(FrArchive * archive)305 fr_archive_stop (FrArchive *archive)
306 {
307 	if (archive->process != NULL) {
308 		fr_process_stop (archive->process);
309 		return;
310 	}
311 
312 	if (! g_cancellable_is_cancelled (archive->priv->cancellable))
313 		g_cancellable_cancel (archive->priv->cancellable);
314 }
315 
316 
317 void
fr_archive_action_completed(FrArchive * archive,FrAction action,FrProcErrorType error_type,const char * error_details)318 fr_archive_action_completed (FrArchive       *archive,
319 			     FrAction         action,
320 			     FrProcErrorType  error_type,
321 			     const char      *error_details)
322 {
323 	archive->error.type = error_type;
324 	archive->error.status = 0;
325 	g_clear_error (&archive->error.gerror);
326 	if (error_details != NULL)
327 		archive->error.gerror = g_error_new_literal (fr_error_quark (),
328 							     0,
329 							     error_details);
330 	g_signal_emit (G_OBJECT (archive),
331 		       fr_archive_signals[DONE],
332 		       0,
333 		       action,
334 		       &archive->error);
335 }
336 
337 
338 static gboolean
archive_sticky_only_cb(FrProcess * process,FrArchive * archive)339 archive_sticky_only_cb (FrProcess *process,
340 			FrArchive *archive)
341 {
342 	fr_archive_stoppable (archive, FALSE);
343 	return TRUE;
344 }
345 
346 
347 static void
fr_archive_init(FrArchive * archive)348 fr_archive_init (FrArchive *archive)
349 {
350 	archive->file = NULL;
351 	archive->local_copy = NULL;
352 	archive->is_remote = FALSE;
353 	archive->command = NULL;
354 	archive->is_compressed_file = FALSE;
355 	archive->can_create_compressed_file = FALSE;
356 
357 	archive->priv = g_new0 (FrArchivePrivData, 1);
358 	archive->priv->fake_load_func = NULL;
359 	archive->priv->fake_load_data = NULL;
360 
361 	archive->priv->extraction_destination = NULL;
362 	archive->priv->temp_extraction_dir = NULL;
363 	archive->priv->cancellable = g_cancellable_new ();
364 
365 	archive->process = fr_process_new ();
366 	g_signal_connect (G_OBJECT (archive->process),
367 			  "sticky_only",
368 			  G_CALLBACK (archive_sticky_only_cb),
369 			  archive);
370 }
371 
372 
373 FrArchive *
fr_archive_new(void)374 fr_archive_new (void)
375 {
376 	return FR_ARCHIVE (g_object_new (FR_TYPE_ARCHIVE, NULL));
377 }
378 
379 
380 static GFile *
get_local_copy_for_file(GFile * remote_file)381 get_local_copy_for_file (GFile *remote_file)
382 {
383 	char  *temp_dir;
384 	GFile *local_copy = NULL;
385 
386 	temp_dir = get_temp_work_dir (NULL);
387 	if (temp_dir != NULL) {
388 		char  *archive_name;
389 		char  *local_path;
390 
391 		archive_name = g_file_get_basename (remote_file);
392 		local_path = g_build_filename (temp_dir, archive_name, NULL);
393 		local_copy = g_file_new_for_path (local_path);
394 
395 		g_free (local_path);
396 		g_free (archive_name);
397 	}
398 	g_free (temp_dir);
399 
400 	return local_copy;
401 }
402 
403 
404 static void
fr_archive_set_uri(FrArchive * archive,const char * uri)405 fr_archive_set_uri (FrArchive  *archive,
406 		    const char *uri)
407 {
408 	if ((archive->local_copy != NULL) && archive->is_remote) {
409 		GFile  *temp_folder;
410 		GError *err = NULL;
411 
412 		g_file_delete (archive->local_copy, NULL, &err);
413 		if (err != NULL) {
414 			g_warning ("Failed to delete the local copy: %s", err->message);
415 			g_clear_error (&err);
416 		}
417 
418 		temp_folder = g_file_get_parent (archive->local_copy);
419 		g_file_delete (temp_folder, NULL, &err);
420 		if (err != NULL) {
421 			g_warning ("Failed to delete temp folder: %s", err->message);
422 			g_clear_error (&err);
423 		}
424 
425 		g_object_unref (temp_folder);
426 	}
427 
428 	if (archive->file != NULL) {
429 		g_object_unref (archive->file);
430 		archive->file = NULL;
431 	}
432 	if (archive->local_copy != NULL) {
433 		g_object_unref (archive->local_copy);
434 		archive->local_copy = NULL;
435 	}
436 	archive->content_type = NULL;
437 
438 	if (uri == NULL)
439 		return;
440 
441 	archive->file = g_file_new_for_uri (uri);
442 	archive->is_remote = ! g_file_has_uri_scheme (archive->file, "file");
443 	if (archive->is_remote)
444 		archive->local_copy = get_local_copy_for_file (archive->file);
445 	else
446 		archive->local_copy = g_file_dup (archive->file);
447 }
448 
449 
450 static void
fr_archive_remove_temp_work_dir(FrArchive * archive)451 fr_archive_remove_temp_work_dir (FrArchive *archive)
452 {
453 	if (archive->priv->temp_dir == NULL)
454 		return;
455 	remove_local_directory (archive->priv->temp_dir);
456 	g_free (archive->priv->temp_dir);
457 	archive->priv->temp_dir = NULL;
458 }
459 
460 
461 static void
fr_archive_finalize(GObject * object)462 fr_archive_finalize (GObject *object)
463 {
464 	FrArchive *archive;
465 
466 	g_return_if_fail (object != NULL);
467 	g_return_if_fail (FR_IS_ARCHIVE (object));
468 
469 	archive = FR_ARCHIVE (object);
470 
471 	fr_archive_set_uri (archive, NULL);
472 	fr_archive_remove_temp_work_dir (archive);
473 	if (archive->command != NULL)
474 		g_object_unref (archive->command);
475 	g_object_unref (archive->process);
476 	if (archive->priv->dropped_items_data != NULL) {
477 		dropped_items_data_free (archive->priv->dropped_items_data);
478 		archive->priv->dropped_items_data = NULL;
479 	}
480 	g_free (archive->priv->temp_extraction_dir);
481 	g_free (archive->priv->extraction_destination);
482 	g_free (archive->priv);
483 
484 	/* Chain up */
485 
486 	if (G_OBJECT_CLASS (parent_class)->finalize)
487 		G_OBJECT_CLASS (parent_class)->finalize (object);
488 }
489 
490 
491 static const char *
get_mime_type_from_content(GFile * file)492 get_mime_type_from_content (GFile *file)
493 {
494 	GFileInfo  *info;
495 	GError     *err = NULL;
496  	const char *content_type = NULL;
497 
498 	info = g_file_query_info (file,
499 				  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
500 				  0, NULL, &err);
501 	if (info == NULL) {
502 		g_warning ("could not get content type: %s", err->message);
503 		g_clear_error (&err);
504 	}
505 	else {
506 		content_type = get_static_string (g_file_info_get_content_type (info));
507 		g_object_unref (info);
508 	}
509 
510 	return content_type;
511 }
512 
513 
514 static const char *
get_mime_type_from_magic_numbers(GFile * file)515 get_mime_type_from_magic_numbers (GFile *file)
516 {
517 #ifdef ENABLE_MAGIC
518 	static magic_t magic = NULL;
519 
520 	if (! magic) {
521 		magic = magic_open (MAGIC_MIME_TYPE);
522 		if (magic)
523 			magic_load (magic, NULL);
524 		else
525 			g_warning ("unable to open magic database");
526 	}
527 
528 	if (magic) {
529 		const char * mime_type = NULL;
530 		char * filepath = g_file_get_path (file);
531 
532 		if (filepath) {
533 			mime_type = magic_file (magic, filepath);
534 			g_free (filepath);
535 		}
536 
537 		if (mime_type)
538 			return mime_type;
539 
540 		g_warning ("unable to detect filetype from magic: %s",
541 			   magic_error (magic));
542 	}
543 #else
544 	static const struct magic {
545 		const unsigned int off;
546 		const unsigned int len;
547 		const char * const id;
548 		const char * const mime_type;
549 	} magic_ids [] = {
550 		/* magic ids taken from magic/Magdir/archive from the file-4.21 tarball */
551 		{ 0, 21, "!<arch>" "\x0a" "debian-binary",       "application/vnd.debian.binary-package" },
552 		{ 0,  8, "!<arch>" "\x0a",                       "application/x-archive"       },
553 		{ 0,  6, "7z\274\257\047\034",                   "application/x-7z-compressed" },
554 		{ 7,  7, "**ACE**",                              "application/x-ace"           },
555 		{ 0,  2, "\x60\xea",                             "application/x-arj"           },
556 		{ 0,  4, "\xce\xb2\xcf\x81",                     "application/x-brotli"        },
557 		{ 0,  3, "BZh",                                  "application/x-bzip2"         },
558 		{ 0,  2, "\037\213",                             "application/x-gzip"          },
559 		{ 2,  3, "-lh",                                  "application/x-lzh-compressed"},
560 		{ 0,  4, "LZIP",                                 "application/x-lzip"          },
561 		{ 0,  9, "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a", "application/x-lzop",         },
562 		{ 0,  4, "Rar!",                                 "application/x-rar"           },
563 		{ 0,  4, "\xed\xab\xee\xdb",                     "application/x-rpm"           },
564 		{ 0,  4, "RZIP",                                 "application/x-rzip"          },
565 		{ 0,  6, "\3757zXZ\000",                         "application/x-xz"            },
566 		{ 20, 4, "\xdc\xa7\xc4\xfd",                     "application/x-zoo",          },
567 		{ 0,  4, "PK\003\004",                           "application/zip"             },
568 		{ 0,  8, "PK00PK\003\004",                       "application/zip"             },
569 		{ 0,  4, "LRZI",                                 "application/x-lrzip"         },
570 		{ 0,  2, "\x1f\x9d",                             "application/x-compress"      }, /* using Lempel-Ziv-Welch   algorithm */
571 		{ 0,  2, "\x1f\xa0",                             "application/x-compress"      }, /* using Lempel-Ziv-Huffman algorithm */
572 	};
573 
574 	char   buffer[32];
575 	size_t i;
576 
577 	if (! g_load_file_in_buffer (file, buffer, sizeof (buffer), NULL))
578 		return NULL;
579 
580 	for (i = 0; i < G_N_ELEMENTS (magic_ids); i++) {
581 		const struct magic * const magic = &magic_ids[i];
582 
583 		if (sizeof (buffer) < (magic->off + magic->len))
584 			g_warning ("buffer underrun for mime type '%s' magic",
585 				   magic->mime_type);
586 		else if (! memcmp (buffer + magic->off, magic->id, magic->len))
587 			return magic->mime_type;
588 	}
589 #endif
590 
591 	return NULL;
592 }
593 
594 
595 static const char *
get_mime_type_from_filename(GFile * file)596 get_mime_type_from_filename (GFile *file)
597 {
598 	const char *mime_type = NULL;
599 	char       *filename;
600 
601 	if (file == NULL)
602 		return NULL;
603 
604 	filename = g_file_get_path (file);
605 	mime_type = get_mime_type_from_extension (get_file_extension (filename));
606 	g_free (filename);
607 
608 	return mime_type;
609 }
610 
611 
612 static gboolean
create_command_from_type(FrArchive * archive,const char * mime_type,GType command_type,FrCommandCaps requested_capabilities)613 create_command_from_type (FrArchive     *archive,
614 			  const char    *mime_type,
615 		          GType          command_type,
616 		          FrCommandCaps  requested_capabilities)
617 {
618 	if (command_type == 0)
619 		return FALSE;
620 
621 	archive->command = FR_COMMAND (g_object_new (command_type,
622 					             "process", archive->process,
623 					             "mime-type", mime_type,
624 					             NULL));
625 
626 	if (! fr_command_is_capable_of (archive->command, requested_capabilities)) {
627 		g_object_unref (archive->command);
628 		archive->command = NULL;
629 		archive->is_compressed_file = FALSE;
630 	}
631 	else
632 		archive->is_compressed_file = ! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_ARCHIVE_MANY_FILES);
633 
634 	return (archive->command != NULL);
635 }
636 
637 
638 static gboolean
create_command_to_load_archive(FrArchive * archive,const char * mime_type)639 create_command_to_load_archive (FrArchive  *archive,
640 			        const char *mime_type)
641 {
642 	FrCommandCaps requested_capabilities = FR_COMMAND_CAN_DO_NOTHING;
643 	GType         command_type;
644 
645 	if (mime_type == NULL)
646 		return FALSE;
647 
648 	/* try with the WRITE capability even when loading, this way we give
649 	 * priority to the commands that can read and write over commands
650 	 * that can only read a specific file format. */
651 
652 	requested_capabilities |= FR_COMMAND_CAN_READ_WRITE;
653 	command_type = get_command_type_from_mime_type (mime_type, requested_capabilities);
654 
655 	/* if no command was found, remove the write capability and try again */
656 
657 	if (command_type == 0) {
658 		requested_capabilities ^= FR_COMMAND_CAN_WRITE;
659 		command_type = get_command_type_from_mime_type (mime_type, requested_capabilities);
660 	}
661 
662 	return create_command_from_type (archive,
663 					 mime_type,
664 					 command_type,
665 					 requested_capabilities);
666 }
667 
668 
669 static gboolean
create_command_to_create_archive(FrArchive * archive,const char * mime_type)670 create_command_to_create_archive (FrArchive  *archive,
671 			          const char *mime_type)
672 {
673 	FrCommandCaps requested_capabilities = FR_COMMAND_CAN_DO_NOTHING;
674 	GType         command_type;
675 
676 	if (mime_type == NULL)
677 		return FALSE;
678 
679 	requested_capabilities |= FR_COMMAND_CAN_WRITE;
680 	command_type = get_command_type_from_mime_type (mime_type, requested_capabilities);
681 
682 	return create_command_from_type (archive,
683 					 mime_type,
684 					 command_type,
685 					 requested_capabilities);
686 }
687 
688 
689 static void
action_started(FrCommand * command,FrAction action,FrArchive * archive)690 action_started (FrCommand *command,
691 		FrAction   action,
692 		FrArchive *archive)
693 {
694 #ifdef MATE_ENABLE_DEBUG
695 	debug (DEBUG_INFO, "%s [START] (FR::Archive)\n", get_action_name (action));
696 #endif
697 
698 	g_signal_emit (G_OBJECT (archive),
699 		       fr_archive_signals[START],
700 		       0,
701 		       action);
702 }
703 
704 
705 /* -- copy_to_remote_location -- */
706 
707 
708 static void
fr_archive_copy_done(FrArchive * archive,FrAction action,GError * error)709 fr_archive_copy_done (FrArchive *archive,
710 		      FrAction   action,
711 		      GError    *error)
712 {
713 	FrProcErrorType  error_type = FR_PROC_ERROR_NONE;
714 	const char      *error_details = NULL;
715 
716 	if (error != NULL) {
717 		error_type = (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC);
718 		error_details = error->message;
719 	}
720 	fr_archive_action_completed (archive, action, error_type, error_details);
721 }
722 
723 
724 static void
copy_to_remote_location_done(GError * error,gpointer user_data)725 copy_to_remote_location_done (GError   *error,
726 			      gpointer  user_data)
727 {
728 	XferData *xfer_data = user_data;
729 
730 	fr_archive_copy_done (xfer_data->archive, xfer_data->action, error);
731 	xfer_data_free (xfer_data);
732 }
733 
734 
735 static void
copy_to_remote_location_progress(goffset current_file,goffset total_files,GFile * source,GFile * destination,goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)736 copy_to_remote_location_progress (goffset   current_file,
737                                   goffset   total_files,
738                                   GFile    *source,
739                                   GFile    *destination,
740                                   goffset   current_num_bytes,
741                                   goffset   total_num_bytes,
742                                   gpointer  user_data)
743 {
744 	XferData *xfer_data = user_data;
745 
746 	g_signal_emit (G_OBJECT (xfer_data->archive),
747 		       fr_archive_signals[PROGRESS],
748 		       0,
749 		       (double) current_num_bytes / total_num_bytes);
750 }
751 
752 
753 static void
copy_to_remote_location(FrArchive * archive,FrAction action)754 copy_to_remote_location (FrArchive  *archive,
755 			 FrAction    action)
756 {
757 	XferData *xfer_data;
758 
759 	xfer_data = g_new0 (XferData, 1);
760 	xfer_data->archive = archive;
761 	xfer_data->action = action;
762 
763 	g_copy_file_async (archive->local_copy,
764 			   archive->file,
765 			   G_FILE_COPY_OVERWRITE,
766 			   G_PRIORITY_DEFAULT,
767 			   archive->priv->cancellable,
768 			   copy_to_remote_location_progress,
769 			   xfer_data,
770 			   copy_to_remote_location_done,
771 			   xfer_data);
772 }
773 
774 
775 /* -- copy_extracted_files_to_destination -- */
776 
777 
778 static void
move_here(FrArchive * archive)779 move_here (FrArchive *archive)
780 {
781 	char   *content_uri;
782 	char   *parent;
783 	char   *parent_parent;
784 	char   *new_content_uri;
785 	GFile  *source, *destination, *parent_file;
786 	GError *error = NULL;
787 
788 	content_uri = get_dir_content_if_unique (archive->priv->extraction_destination);
789 	if (content_uri == NULL)
790 		return;
791 
792 	parent = remove_level_from_path (content_uri);
793 
794 	if (uricmp (parent, archive->priv->extraction_destination) == 0) {
795 		char *new_uri;
796 
797 		new_uri = get_alternative_uri_for_uri (archive->priv->extraction_destination);
798 
799 		source = g_file_new_for_uri (archive->priv->extraction_destination);
800 		destination = g_file_new_for_uri (new_uri);
801 		if (! g_file_move (source, destination, 0, NULL, NULL, NULL, &error)) {
802 			g_warning ("could not rename %s to %s: %s", archive->priv->extraction_destination, new_uri, error->message);
803 			g_clear_error (&error);
804 		}
805 		g_object_unref (source);
806 		g_object_unref (destination);
807 
808 		g_free (archive->priv->extraction_destination);
809 		archive->priv->extraction_destination = new_uri;
810 
811 		g_free (parent);
812 
813 		content_uri = get_dir_content_if_unique (archive->priv->extraction_destination);
814 		if (content_uri == NULL)
815 			return;
816 
817 		parent = remove_level_from_path (content_uri);
818 	}
819 
820 	parent_parent = remove_level_from_path (parent);
821 	new_content_uri = get_alternative_uri (parent_parent, file_name_from_path (content_uri));
822 
823 	source = g_file_new_for_uri (content_uri);
824 	destination = g_file_new_for_uri (new_content_uri);
825 	if (! g_file_move (source, destination, 0, NULL, NULL, NULL, &error)) {
826 		g_warning ("could not rename %s to %s: %s", content_uri, new_content_uri, error->message);
827 		g_clear_error (&error);
828 	}
829 
830 	parent_file = g_file_new_for_uri (parent);
831 	if (! g_file_delete (parent_file, NULL, &error)) {
832 		g_warning ("could not remove directory %s: %s", parent, error->message);
833 		g_clear_error (&error);
834 	}
835 	g_object_unref (parent_file);
836 
837 	g_free (archive->priv->extraction_destination);
838 	archive->priv->extraction_destination = new_content_uri;
839 
840 	g_free (parent_parent);
841 	g_free (parent);
842 	g_free (content_uri);
843 }
844 
845 
846 static void
copy_extracted_files_done(GError * error,gpointer user_data)847 copy_extracted_files_done (GError   *error,
848 			   gpointer  user_data)
849 {
850 	FrArchive *archive = user_data;
851 
852 	remove_local_directory (archive->priv->temp_extraction_dir);
853 	g_free (archive->priv->temp_extraction_dir);
854 	archive->priv->temp_extraction_dir = NULL;
855 
856 	fr_archive_action_completed (archive,
857 				     FR_ACTION_COPYING_FILES_TO_REMOTE,
858 				     FR_PROC_ERROR_NONE,
859 				     NULL);
860 
861 	if ((error == NULL) && (archive->priv->extract_here))
862 		move_here (archive);
863 
864 	fr_archive_copy_done (archive, FR_ACTION_EXTRACTING_FILES, error);
865 }
866 
867 
868 static void
copy_extracted_files_progress(goffset current_file,goffset total_files,GFile * source,GFile * destination,goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)869 copy_extracted_files_progress (goffset   current_file,
870                                goffset   total_files,
871                                GFile    *source,
872                                GFile    *destination,
873                                goffset   current_num_bytes,
874                                goffset   total_num_bytes,
875                                gpointer  user_data)
876 {
877 	FrArchive *archive = user_data;
878 
879 	g_signal_emit (G_OBJECT (archive),
880 		       fr_archive_signals[PROGRESS],
881 		       0,
882 		       (double) current_file / (total_files + 1));
883 }
884 
885 
886 static void
copy_extracted_files_to_destination(FrArchive * archive)887 copy_extracted_files_to_destination (FrArchive *archive)
888 {
889 	g_directory_copy_async (archive->priv->temp_extraction_dir,
890 				archive->priv->extraction_destination,
891 				G_FILE_COPY_OVERWRITE,
892 				G_PRIORITY_DEFAULT,
893 				archive->priv->cancellable,
894 				copy_extracted_files_progress,
895 				archive,
896 				copy_extracted_files_done,
897 				archive);
898 }
899 
900 
901 static void add_dropped_items (DroppedItemsData *data);
902 
903 
904 static void
fr_archive_change_name(FrArchive * archive,const char * filename)905 fr_archive_change_name (FrArchive  *archive,
906 		        const char *filename)
907 {
908 	const char *name;
909 	GFile      *parent;
910 
911 	name = file_name_from_path (filename);
912 
913 	parent = g_file_get_parent (archive->file);
914 	g_object_unref (archive->file);
915 	archive->file = g_file_get_child (parent, name);
916 	g_object_unref (parent);
917 
918 	parent = g_file_get_parent (archive->local_copy);
919 	g_object_unref (archive->local_copy);
920 	archive->local_copy = g_file_get_child (parent, name);
921 	g_object_unref (parent);
922 }
923 
924 
925 static void
action_performed(FrCommand * command,FrAction action,FrProcError * error,FrArchive * archive)926 action_performed (FrCommand   *command,
927 		  FrAction     action,
928 		  FrProcError *error,
929 		  FrArchive   *archive)
930 {
931 #ifdef MATE_ENABLE_DEBUG
932 	debug (DEBUG_INFO, "%s [DONE] (FR::Archive)\n", get_action_name (action));
933 #endif
934 
935 	switch (action) {
936 	case FR_ACTION_DELETING_FILES:
937 		if (error->type == FR_PROC_ERROR_NONE) {
938 			if (! g_file_has_uri_scheme (archive->file, "file")) {
939 				copy_to_remote_location (archive, action);
940 				return;
941 			}
942 		}
943 		break;
944 
945 	case FR_ACTION_ADDING_FILES:
946 		if (error->type == FR_PROC_ERROR_NONE) {
947 			fr_archive_remove_temp_work_dir (archive);
948 			if (archive->priv->continue_adding_dropped_items) {
949 				add_dropped_items (archive->priv->dropped_items_data);
950 				return;
951 			}
952 			if (archive->priv->dropped_items_data != NULL) {
953 				dropped_items_data_free (archive->priv->dropped_items_data);
954 				archive->priv->dropped_items_data = NULL;
955 			}
956 			/* the name of the volumes are different from the
957 			 * original name */
958 			if (archive->command->multi_volume)
959 				fr_archive_change_name (archive, archive->command->filename);
960 			if (! g_file_has_uri_scheme (archive->file, "file")) {
961 				copy_to_remote_location (archive, action);
962 				return;
963 			}
964 		}
965 		break;
966 
967 	case FR_ACTION_EXTRACTING_FILES:
968 		if (error->type == FR_PROC_ERROR_NONE) {
969 			if (archive->priv->remote_extraction) {
970 				copy_extracted_files_to_destination (archive);
971 				return;
972 			}
973 			else if (archive->priv->extract_here)
974 				move_here (archive);
975 		}
976 		else {
977 			/* if an error occurred during extraction remove the
978 			 * temp extraction dir, if used. */
979 			g_print ("action_performed: ERROR!\n");
980 
981 			if ((archive->priv->remote_extraction) && (archive->priv->temp_extraction_dir != NULL)) {
982 				remove_local_directory (archive->priv->temp_extraction_dir);
983 				g_free (archive->priv->temp_extraction_dir);
984 				archive->priv->temp_extraction_dir = NULL;
985 			}
986 
987 			if (archive->priv->extract_here)
988 				remove_directory (archive->priv->extraction_destination);
989 		}
990 		break;
991 
992 	case FR_ACTION_LISTING_CONTENT:
993 		/* the name of the volumes are different from the
994 		 * original name */
995 		if (archive->command->multi_volume)
996 			fr_archive_change_name (archive, archive->command->filename);
997 		fr_command_update_capabilities (archive->command);
998 		if (! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_WRITE))
999 			archive->read_only = TRUE;
1000 		break;
1001 
1002 	default:
1003 		/* nothing */
1004 		break;
1005 	}
1006 
1007 	g_signal_emit (G_OBJECT (archive),
1008 		       fr_archive_signals[DONE],
1009 		       0,
1010 		       action,
1011 		       error);
1012 }
1013 
1014 
1015 static gboolean
archive_progress_cb(FrCommand * command,double fraction,FrArchive * archive)1016 archive_progress_cb (FrCommand *command,
1017 		     double     fraction,
1018 		     FrArchive *archive)
1019 {
1020 	g_signal_emit (G_OBJECT (archive),
1021 		       fr_archive_signals[PROGRESS],
1022 		       0,
1023 		       fraction);
1024 	return TRUE;
1025 }
1026 
1027 
1028 static gboolean
archive_message_cb(FrCommand * command,const char * msg,FrArchive * archive)1029 archive_message_cb  (FrCommand  *command,
1030 		     const char *msg,
1031 		     FrArchive  *archive)
1032 {
1033 	g_signal_emit (G_OBJECT (archive),
1034 		       fr_archive_signals[MESSAGE],
1035 		       0,
1036 		       msg);
1037 	return TRUE;
1038 }
1039 
1040 
1041 static gboolean
archive_working_archive_cb(FrCommand * command,const char * archive_filename,FrArchive * archive)1042 archive_working_archive_cb  (FrCommand  *command,
1043 			     const char *archive_filename,
1044 			     FrArchive  *archive)
1045 {
1046 	g_signal_emit (G_OBJECT (archive),
1047 		       fr_archive_signals[WORKING_ARCHIVE],
1048 		       0,
1049 		       archive_filename);
1050 	return TRUE;
1051 }
1052 
1053 
1054 static void
fr_archive_connect_to_command(FrArchive * archive)1055 fr_archive_connect_to_command (FrArchive *archive)
1056 {
1057 	g_signal_connect (G_OBJECT (archive->command),
1058 			  "start",
1059 			  G_CALLBACK (action_started),
1060 			  archive);
1061 	g_signal_connect (G_OBJECT (archive->command),
1062 			  "done",
1063 			  G_CALLBACK (action_performed),
1064 			  archive);
1065 	g_signal_connect (G_OBJECT (archive->command),
1066 			  "progress",
1067 			  G_CALLBACK (archive_progress_cb),
1068 			  archive);
1069 	g_signal_connect (G_OBJECT (archive->command),
1070 			  "message",
1071 			  G_CALLBACK (archive_message_cb),
1072 			  archive);
1073 	g_signal_connect (G_OBJECT (archive->command),
1074 			  "working_archive",
1075 			  G_CALLBACK (archive_working_archive_cb),
1076 			  archive);
1077 }
1078 
1079 
1080 gboolean
fr_archive_create(FrArchive * archive,const char * uri)1081 fr_archive_create (FrArchive  *archive,
1082 		   const char *uri)
1083 {
1084 	FrCommand  *tmp_command;
1085 	const char *mime_type;
1086 
1087 	if (uri == NULL)
1088 		return FALSE;
1089 
1090 	fr_archive_set_uri (archive, uri);
1091 
1092 	tmp_command = archive->command;
1093 
1094 	mime_type = get_mime_type_from_filename (archive->local_copy);
1095 	if (! create_command_to_create_archive (archive, mime_type)) {
1096 		archive->command = tmp_command;
1097 		return FALSE;
1098 	}
1099 
1100 	if (tmp_command != NULL) {
1101 		g_signal_handlers_disconnect_by_data (tmp_command, archive);
1102 		g_object_unref (G_OBJECT (tmp_command));
1103 	}
1104 
1105 	fr_archive_connect_to_command (archive);
1106 	archive->read_only = FALSE;
1107 
1108 	return TRUE;
1109 }
1110 
1111 
1112 void
fr_archive_set_fake_load_func(FrArchive * archive,FakeLoadFunc func,gpointer data)1113 fr_archive_set_fake_load_func (FrArchive    *archive,
1114 			       FakeLoadFunc  func,
1115 			       gpointer      data)
1116 {
1117 	archive->priv->fake_load_func = func;
1118 	archive->priv->fake_load_data = data;
1119 }
1120 
1121 
1122 gboolean
fr_archive_fake_load(FrArchive * archive)1123 fr_archive_fake_load (FrArchive *archive)
1124 {
1125 	if (archive->priv->fake_load_func != NULL)
1126 		return (*archive->priv->fake_load_func) (archive, archive->priv->fake_load_data);
1127 	else
1128 		return FALSE;
1129 }
1130 
1131 
1132 /* -- fr_archive_load -- */
1133 
1134 
1135 static void
load_local_archive(FrArchive * archive,const char * password)1136 load_local_archive (FrArchive  *archive,
1137 		    const char *password)
1138 {
1139 	FrCommand  *old_command;
1140 	const char *mime_type;
1141 
1142 	if (! g_file_query_exists (archive->file, archive->priv->cancellable)) {
1143 		fr_archive_action_completed (archive,
1144 					     FR_ACTION_LOADING_ARCHIVE,
1145 					     FR_PROC_ERROR_GENERIC,
1146 					     _("File not found."));
1147 		return;
1148 	}
1149 
1150 	archive->have_permissions = check_file_permissions (archive->file, W_OK);
1151 	archive->read_only = ! archive->have_permissions;
1152 
1153 	old_command = archive->command;
1154 
1155 	mime_type = get_mime_type_from_filename (archive->local_copy);
1156 	if (! create_command_to_load_archive (archive, mime_type)) {
1157 		mime_type = get_mime_type_from_content (archive->local_copy);
1158 		if (! create_command_to_load_archive (archive, mime_type)) {
1159 			mime_type = get_mime_type_from_magic_numbers (archive->local_copy);
1160 			if (! create_command_to_load_archive (archive, mime_type)) {
1161 				archive->command = old_command;
1162 				archive->content_type = mime_type;
1163 				fr_archive_action_completed (archive,
1164 							     FR_ACTION_LOADING_ARCHIVE,
1165 							     FR_PROC_ERROR_UNSUPPORTED_FORMAT,
1166 							     _("Archive type not supported."));
1167 				return;
1168 			}
1169 		}
1170 	}
1171 
1172 	if (old_command != NULL) {
1173 		g_signal_handlers_disconnect_by_data (old_command, archive);
1174 		g_object_unref (old_command);
1175 	}
1176 
1177 	fr_archive_connect_to_command (archive);
1178 	archive->content_type = mime_type;
1179 	if (! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_WRITE))
1180 		archive->read_only = TRUE;
1181 	fr_archive_stoppable (archive, TRUE);
1182 	archive->command->fake_load = fr_archive_fake_load (archive);
1183 
1184 	fr_archive_action_completed (archive,
1185 				     FR_ACTION_LOADING_ARCHIVE,
1186 				     FR_PROC_ERROR_NONE,
1187 				     NULL);
1188 
1189 	/**/
1190 
1191 	fr_process_clear (archive->process);
1192 	g_object_set (archive->command,
1193 		      "file", archive->local_copy,
1194 		      "password", password,
1195 		      NULL);
1196 	fr_command_list (archive->command);
1197 }
1198 
1199 
1200 static void
copy_remote_file_done(GError * error,gpointer user_data)1201 copy_remote_file_done (GError   *error,
1202 		       gpointer  user_data)
1203 {
1204 	XferData *xfer_data = user_data;
1205 
1206 	if (error != NULL)
1207 		fr_archive_copy_done (xfer_data->archive, FR_ACTION_LOADING_ARCHIVE, error);
1208 	else
1209 		load_local_archive (xfer_data->archive, xfer_data->password);
1210 	xfer_data_free (xfer_data);
1211 }
1212 
1213 
1214 static void
copy_remote_file_progress(goffset current_file,goffset total_files,GFile * source,GFile * destination,goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)1215 copy_remote_file_progress (goffset   current_file,
1216                            goffset   total_files,
1217                            GFile    *source,
1218                            GFile    *destination,
1219                            goffset   current_num_bytes,
1220                            goffset   total_num_bytes,
1221                            gpointer  user_data)
1222 {
1223 	XferData *xfer_data = user_data;
1224 
1225 	g_signal_emit (G_OBJECT (xfer_data->archive),
1226 		       fr_archive_signals[PROGRESS],
1227 		       0,
1228 		       (double) current_num_bytes / total_num_bytes);
1229 }
1230 
1231 
1232 static gboolean
copy_remote_file_done_cb(gpointer user_data)1233 copy_remote_file_done_cb (gpointer user_data)
1234 {
1235 	XferData *xfer_data = user_data;
1236 
1237 	g_source_remove (xfer_data->source_id);
1238 	copy_remote_file_done (NULL, xfer_data);
1239 	return FALSE;
1240 }
1241 
1242 
1243 static void
copy_remote_file(FrArchive * archive,const char * password)1244 copy_remote_file (FrArchive  *archive,
1245 		  const char *password)
1246 {
1247 	XferData *xfer_data;
1248 
1249 	if (! g_file_query_exists (archive->file, archive->priv->cancellable)) {
1250 		GError *error;
1251 
1252 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Archive not found"));
1253 		fr_archive_copy_done (archive, FR_ACTION_LOADING_ARCHIVE, error);
1254 		g_error_free (error);
1255 
1256 		return;
1257 	}
1258 
1259 	xfer_data = g_new0 (XferData, 1);
1260 	xfer_data->archive = archive;
1261 	xfer_data->uri = g_file_get_uri (archive->file);
1262 	if (password != NULL)
1263 		xfer_data->password = g_strdup (password);
1264 
1265 	if (! archive->is_remote) {
1266 		xfer_data->source_id = g_idle_add (copy_remote_file_done_cb, xfer_data);
1267 		return;
1268 	}
1269 
1270 	g_copy_file_async (archive->file,
1271 			   archive->local_copy,
1272 			   G_FILE_COPY_OVERWRITE,
1273 			   G_PRIORITY_DEFAULT,
1274 			   archive->priv->cancellable,
1275 			   copy_remote_file_progress,
1276 			   xfer_data,
1277 			   copy_remote_file_done,
1278 			   xfer_data);
1279 }
1280 
1281 
1282 gboolean
fr_archive_load(FrArchive * archive,const char * uri,const char * password)1283 fr_archive_load (FrArchive  *archive,
1284 		 const char *uri,
1285 		 const char *password)
1286 {
1287 	g_return_val_if_fail (archive != NULL, FALSE);
1288 
1289 	g_signal_emit (G_OBJECT (archive),
1290 		       fr_archive_signals[START],
1291 		       0,
1292 		       FR_ACTION_LOADING_ARCHIVE);
1293 
1294 	fr_archive_set_uri (archive, uri);
1295 	copy_remote_file (archive, password);
1296 
1297 	return TRUE;
1298 }
1299 
1300 
1301 gboolean
fr_archive_load_local(FrArchive * archive,const char * uri,const char * password)1302 fr_archive_load_local (FrArchive  *archive,
1303 		       const char *uri,
1304 		       const char *password)
1305 {
1306 	g_return_val_if_fail (archive != NULL, FALSE);
1307 
1308 	g_signal_emit (G_OBJECT (archive),
1309 		       fr_archive_signals[START],
1310 		       0,
1311 		       FR_ACTION_LOADING_ARCHIVE);
1312 
1313 	fr_archive_set_uri (archive, uri);
1314 	copy_remote_file (archive, password);
1315 
1316 	return TRUE;
1317 }
1318 
1319 
1320 void
fr_archive_reload(FrArchive * archive,const char * password)1321 fr_archive_reload (FrArchive  *archive,
1322 		   const char *password)
1323 {
1324 	char *uri;
1325 
1326 	g_return_if_fail (archive != NULL);
1327 	g_return_if_fail (archive->file != NULL);
1328 
1329 	fr_archive_stoppable (archive, TRUE);
1330 	archive->command->fake_load = fr_archive_fake_load (archive);
1331 
1332 	uri = g_file_get_uri (archive->file);
1333 	fr_archive_load (archive, uri, password);
1334 	g_free (uri);
1335 }
1336 
1337 
1338 /* -- add -- */
1339 
1340 
1341 static char *
create_tmp_base_dir(const char * base_dir,const char * dest_path)1342 create_tmp_base_dir (const char *base_dir,
1343 		     const char *dest_path)
1344 {
1345 	char *dest_dir;
1346 	char *temp_dir;
1347 	char *tmp;
1348 	char *parent_dir;
1349 	char *dir;
1350 
1351 	if ((dest_path == NULL)
1352 	    || (*dest_path == '\0')
1353 	    || (strcmp (dest_path, "/") == 0))
1354 	{
1355 		return g_strdup (base_dir);
1356 	}
1357 
1358 	dest_dir = g_strdup (dest_path);
1359 	if (dest_dir[strlen (dest_dir) - 1] == G_DIR_SEPARATOR)
1360 		dest_dir[strlen (dest_dir) - 1] = 0;
1361 
1362 	debug (DEBUG_INFO, "base_dir: %s\n", base_dir);
1363 	debug (DEBUG_INFO, "dest_dir: %s\n", dest_dir);
1364 
1365 	temp_dir = get_temp_work_dir (NULL);
1366 	tmp = remove_level_from_path (dest_dir);
1367 	parent_dir =  g_build_filename (temp_dir, tmp, NULL);
1368 	g_free (tmp);
1369 
1370 	debug (DEBUG_INFO, "mkdir %s\n", parent_dir);
1371 	make_directory_tree_from_path (parent_dir, 0700, NULL);
1372 	g_free (parent_dir);
1373 
1374 	dir = g_build_filename (temp_dir, "/", dest_dir, NULL);
1375 	debug (DEBUG_INFO, "symlink %s --> %s\n", dir, base_dir);
1376 	if (! symlink (base_dir, dir))
1377 		g_warning("Could not create the symbolic link '%s', pointing to '%s'", dir, base_dir);
1378 
1379 	g_free (dir);
1380 	g_free (dest_dir);
1381 
1382 	return temp_dir;
1383 }
1384 
1385 
1386 static FileData *
find_file_in_archive(FrArchive * archive,char * path)1387 find_file_in_archive (FrArchive *archive,
1388 		      char      *path)
1389 {
1390 	int i;
1391 
1392 	g_return_val_if_fail (path != NULL, NULL);
1393 
1394 	i = find_path_in_file_data_array (archive->command->files, path);
1395 	if (i >= 0)
1396 		return (FileData *) g_ptr_array_index (archive->command->files, i);
1397 	else
1398 		return NULL;
1399 }
1400 
1401 
1402 static void delete_from_archive (FrArchive *archive, GList *file_list);
1403 
1404 
1405 static GList *
newer_files_only(FrArchive * archive,GList * file_list,const char * base_dir)1406 newer_files_only (FrArchive  *archive,
1407 		  GList      *file_list,
1408 		  const char *base_dir)
1409 {
1410 	GList *newer_files = NULL;
1411 	GList *scan;
1412 
1413 	for (scan = file_list; scan; scan = scan->next) {
1414 		char     *filename = scan->data;
1415 		char     *fullpath;
1416 		char     *uri;
1417 		FileData *fdata;
1418 
1419 		fdata = find_file_in_archive (archive, filename);
1420 
1421 		if (fdata == NULL) {
1422 			newer_files = g_list_prepend (newer_files, g_strdup (scan->data));
1423 			continue;
1424 		}
1425 
1426 		fullpath = g_strconcat (base_dir, "/", filename, NULL);
1427 		uri = g_filename_to_uri (fullpath, NULL, NULL);
1428 
1429 		if (fdata->modified >= get_file_mtime (uri)) {
1430 			g_free (fullpath);
1431 			g_free (uri);
1432 			continue;
1433 		}
1434 		g_free (fullpath);
1435 		g_free (uri);
1436 
1437 		newer_files = g_list_prepend (newer_files, g_strdup (scan->data));
1438 	}
1439 
1440 	return newer_files;
1441 }
1442 
1443 static gboolean
save_list_to_temp_file(GList * file_list,char ** list_dir,char ** list_filename,GError ** error)1444 save_list_to_temp_file (GList   *file_list,
1445 		        char   **list_dir,
1446 		        char   **list_filename,
1447 		        GError **error)
1448 {
1449 	gboolean           error_occurred = FALSE;
1450 	GFile             *list_file;
1451 	GFileOutputStream *ostream;
1452 
1453 	if (error != NULL)
1454 		*error = NULL;
1455 	*list_dir = get_temp_work_dir (NULL);
1456 	*list_filename = g_build_filename (*list_dir, "file-list", NULL);
1457 	list_file = g_file_new_for_path (*list_filename);
1458 	ostream = g_file_create (list_file, G_FILE_CREATE_PRIVATE, NULL, error);
1459 
1460 	if (ostream != NULL) {
1461 		GList *scan;
1462 
1463 		for (scan = file_list; scan != NULL; scan = scan->next) {
1464 			char *filename = scan->data;
1465 
1466 			filename = str_substitute (filename, "\n", "\\n");
1467 			if ((g_output_stream_write (G_OUTPUT_STREAM (ostream), filename, strlen (filename), NULL, error) < 0)
1468 			    || (g_output_stream_write (G_OUTPUT_STREAM (ostream), "\n", 1, NULL, error) < 0))
1469 			{
1470 				error_occurred = TRUE;
1471 			}
1472 
1473 			g_free (filename);
1474 
1475 			if (error_occurred)
1476 				break;
1477 		}
1478 		if (! error_occurred && ! g_output_stream_close (G_OUTPUT_STREAM (ostream), NULL, error))
1479 			error_occurred = TRUE;
1480 		g_object_unref (ostream);
1481 	}
1482 	else
1483 		error_occurred = TRUE;
1484 
1485 	if (error_occurred) {
1486 		remove_local_directory (*list_dir);
1487 		g_free (*list_dir);
1488 		g_free (*list_filename);
1489 		*list_dir = NULL;
1490 		*list_filename = NULL;
1491 	}
1492 
1493 	g_object_unref (list_file);
1494 
1495 	return ! error_occurred;
1496 }
1497 
1498 
1499 static GList *
split_in_chunks(GList * file_list)1500 split_in_chunks (GList *file_list)
1501 {
1502 	GList *chunks = NULL;
1503 	GList *new_file_list;
1504 	GList *scan;
1505 
1506 	new_file_list = g_list_copy (file_list);
1507 	for (scan = new_file_list; scan != NULL; /* void */) {
1508 		GList *prev = scan->prev;
1509 		GList *chunk;
1510 		int    l;
1511 
1512 		chunk = scan;
1513 		l = 0;
1514 		while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
1515 			if (l == 0)
1516 				l = strlen (scan->data);
1517 			prev = scan;
1518 			scan = scan->next;
1519 			if (scan != NULL)
1520 				l += strlen (scan->data);
1521 		}
1522 		if (prev != NULL) {
1523 			if (prev->next != NULL)
1524 				prev->next->prev = NULL;
1525 			prev->next = NULL;
1526 		}
1527 		chunks = g_list_append (chunks, chunk);
1528 	}
1529 
1530 	return chunks;
1531 }
1532 
1533 
1534 void
fr_archive_add(FrArchive * archive,GList * file_list,const char * base_dir,const char * dest_dir,gboolean update,gboolean recursive,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size)1535 fr_archive_add (FrArchive     *archive,
1536 		GList         *file_list,
1537 		const char    *base_dir,
1538 		const char    *dest_dir,
1539 		gboolean       update,
1540 		gboolean       recursive,
1541 		const char    *password,
1542 		gboolean       encrypt_header,
1543 		FrCompression  compression,
1544 		guint          volume_size)
1545 {
1546 	GList    *new_file_list = NULL;
1547 	gboolean  base_dir_created = FALSE;
1548 	GList    *scan;
1549 	char     *tmp_base_dir = NULL;
1550 	char     *tmp_archive_dir = NULL;
1551 	char     *archive_filename = NULL;
1552 	char     *tmp_archive_filename = NULL;
1553 	gboolean  error_occurred = FALSE;
1554 
1555 	if (file_list == NULL)
1556 		return;
1557 
1558 	if (archive->read_only)
1559 		return;
1560 
1561 	g_object_set (archive->command,
1562 		      "password", password,
1563 		      "encrypt_header", encrypt_header,
1564 		      "compression", compression,
1565 		      "volume_size", volume_size,
1566 		      NULL);
1567 
1568 	fr_archive_stoppable (archive, TRUE);
1569 
1570 	/* dest_dir is the destination folder inside the archive */
1571 
1572 	if ((dest_dir != NULL) && (*dest_dir != '\0') && (strcmp (dest_dir, "/") != 0)) {
1573 		const char *rel_dest_dir = dest_dir;
1574 
1575 		tmp_base_dir = create_tmp_base_dir (base_dir, dest_dir);
1576 		base_dir_created = TRUE;
1577 
1578 		if (dest_dir[0] == G_DIR_SEPARATOR)
1579 			rel_dest_dir = dest_dir + 1;
1580 
1581 		new_file_list = NULL;
1582 		for (scan = file_list; scan != NULL; scan = scan->next) {
1583 			char *filename = scan->data;
1584 			new_file_list = g_list_prepend (new_file_list, g_build_filename (rel_dest_dir, filename, NULL));
1585 		}
1586 	}
1587 	else {
1588 		tmp_base_dir = g_strdup (base_dir);
1589 		new_file_list = path_list_dup(file_list);
1590 	}
1591 
1592 	/* if the command cannot update,  get the list of files that are
1593 	 * newer than the ones in the archive. */
1594 
1595 	if (update && ! archive->command->propAddCanUpdate) {
1596 		GList *tmp_file_list;
1597 
1598 		tmp_file_list = new_file_list;
1599 		new_file_list = newer_files_only (archive, tmp_file_list, tmp_base_dir);
1600 		path_list_free (tmp_file_list);
1601 	}
1602 
1603 	if (new_file_list == NULL) {
1604 		debug (DEBUG_INFO, "nothing to update.\n");
1605 
1606 		if (base_dir_created)
1607 			remove_local_directory (tmp_base_dir);
1608 		g_free (tmp_base_dir);
1609 
1610 		archive->process->error.type = FR_PROC_ERROR_NONE;
1611 		g_signal_emit_by_name (G_OBJECT (archive->process),
1612 				       "done",
1613 				       &archive->process->error);
1614 		return;
1615 	}
1616 
1617 	archive->command->creating_archive = ! g_file_query_exists (archive->local_copy, archive->priv->cancellable);
1618 
1619 	/* create the new archive in a temporary sub-directory, this allows
1620 	 * to cancel the operation without losing the original archive and
1621 	 * removing possible temporary files created by the command. */
1622 
1623 	{
1624 		GFile *local_copy_parent;
1625 		char  *archive_dir;
1626 		GFile *tmp_file;
1627 
1628 		/* create the new archive in a sub-folder of the original
1629 		 * archive this way the 'mv' command is fast. */
1630 
1631 		local_copy_parent = g_file_get_parent (archive->local_copy);
1632 		archive_dir = g_file_get_path (local_copy_parent);
1633 		tmp_archive_dir = get_temp_work_dir (archive_dir);
1634 		archive_filename = g_file_get_path (archive->local_copy);
1635 		tmp_archive_filename = g_build_filename (tmp_archive_dir, file_name_from_path (archive_filename), NULL);
1636 		tmp_file = g_file_new_for_path (tmp_archive_filename);
1637 		g_object_set (archive->command, "file", tmp_file, NULL);
1638 
1639 		if (! archive->command->creating_archive) {
1640 			/* copy the original archive to the new position */
1641 
1642 			fr_process_begin_command (archive->process, "cp");
1643 			fr_process_add_arg (archive->process, "-f");
1644 			fr_process_add_arg (archive->process, archive_filename);
1645 			fr_process_add_arg (archive->process, tmp_archive_filename);
1646 			fr_process_end_command (archive->process);
1647 		}
1648 
1649 		g_object_unref (tmp_file);
1650 		g_free (archive_dir);
1651 		g_object_unref (local_copy_parent);
1652 	}
1653 
1654 	fr_command_uncompress (archive->command);
1655 
1656 	/* when files are already present in a tar archive and are added
1657 	 * again, they are not replaced, so we have to delete them first. */
1658 
1659 	/* if we are adding (== ! update) and 'add' cannot replace or
1660 	 * if we are updating and 'add' cannot update,
1661 	 * delete the files first. */
1662 
1663 	if ((! update && ! archive->command->propAddCanReplace)
1664 	    || (update && ! archive->command->propAddCanUpdate))
1665 	{
1666 		GList *del_list = NULL;
1667 
1668 		for (scan = new_file_list; scan != NULL; scan = scan->next) {
1669 			char *filename = scan->data;
1670 			if (find_file_in_archive (archive, filename))
1671 				del_list = g_list_prepend (del_list, filename);
1672 		}
1673 
1674 		/* delete */
1675 
1676 		if (del_list != NULL) {
1677 			delete_from_archive (archive, del_list);
1678 			fr_process_set_ignore_error (archive->process, TRUE);
1679 			g_list_free (del_list);
1680 		}
1681 	}
1682 
1683 	/* add now. */
1684 
1685 	fr_command_set_n_files (archive->command, g_list_length (new_file_list));
1686 
1687 	if (archive->command->propListFromFile
1688 	    && (archive->command->n_files > LIST_LENGTH_TO_USE_FILE))
1689 	{
1690 		char   *list_dir;
1691 		char   *list_filename;
1692 		GError *error = NULL;
1693 
1694 		if (! save_list_to_temp_file (new_file_list, &list_dir, &list_filename, &error)) {
1695 			archive->process->error.type = FR_PROC_ERROR_GENERIC;
1696 			archive->process->error.status = 0;
1697 			archive->process->error.gerror = g_error_copy (error);
1698 			g_signal_emit_by_name (G_OBJECT (archive->process),
1699 					       "done",
1700 					       &archive->process->error);
1701 			g_clear_error (&error);
1702 			error_occurred = TRUE;
1703 		}
1704 		else {
1705 			fr_command_add (archive->command,
1706 					list_filename,
1707 					new_file_list,
1708 					tmp_base_dir,
1709 					update,
1710 					recursive);
1711 
1712 			/* remove the temp dir */
1713 
1714 			fr_process_begin_command (archive->process, "rm");
1715 			fr_process_set_working_dir (archive->process, g_get_tmp_dir());
1716 			fr_process_set_sticky (archive->process, TRUE);
1717 			fr_process_add_arg (archive->process, "-rf");
1718 			fr_process_add_arg (archive->process, list_dir);
1719 			fr_process_end_command (archive->process);
1720 		}
1721 
1722 		g_free (list_filename);
1723 		g_free (list_dir);
1724 	}
1725 	else {
1726 		GList *chunks = NULL;
1727 
1728 		/* specify the file list on the command line, splitting
1729 		 * in more commands to avoid to overflow the command line
1730 		 * length limit. */
1731 
1732 		chunks = split_in_chunks (new_file_list);
1733 		for (scan = chunks; scan != NULL; scan = scan->next) {
1734 			GList *chunk = scan->data;
1735 
1736 			fr_command_add (archive->command,
1737 					NULL,
1738 					chunk,
1739 					tmp_base_dir,
1740 					update,
1741 					recursive);
1742 			g_list_free (chunk);
1743 		}
1744 
1745 		g_list_free (chunks);
1746 	}
1747 
1748 	path_list_free (new_file_list);
1749 
1750 	if (! error_occurred) {
1751 		fr_command_recompress (archive->command);
1752 
1753 		/* move the new archive to the original position */
1754 
1755 		fr_process_begin_command (archive->process, "mv");
1756 		fr_process_add_arg (archive->process, "-f");
1757 		fr_process_add_arg (archive->process, tmp_archive_filename);
1758 		fr_process_add_arg (archive->process, archive_filename);
1759 		fr_process_end_command (archive->process);
1760 
1761 		/* remove the temp sub-directory */
1762 
1763 		fr_process_begin_command (archive->process, "rm");
1764 		fr_process_set_working_dir (archive->process, g_get_tmp_dir());
1765 		fr_process_set_sticky (archive->process, TRUE);
1766 		fr_process_add_arg (archive->process, "-rf");
1767 		fr_process_add_arg (archive->process, tmp_archive_dir);
1768 		fr_process_end_command (archive->process);
1769 
1770 		/* remove the base dir */
1771 
1772 		if (base_dir_created) {
1773 			fr_process_begin_command (archive->process, "rm");
1774 			fr_process_set_working_dir (archive->process, g_get_tmp_dir());
1775 			fr_process_set_sticky (archive->process, TRUE);
1776 			fr_process_add_arg (archive->process, "-rf");
1777 			fr_process_add_arg (archive->process, tmp_base_dir);
1778 			fr_process_end_command (archive->process);
1779 		}
1780 	}
1781 
1782 	g_free (tmp_archive_filename);
1783 	g_free (archive_filename);
1784 	g_free (tmp_archive_dir);
1785 	g_free (tmp_base_dir);
1786 }
1787 
1788 
1789 static void
fr_archive_add_local_files(FrArchive * archive,GList * file_list,const char * base_dir,const char * dest_dir,gboolean update,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size)1790 fr_archive_add_local_files (FrArchive     *archive,
1791 			    GList         *file_list,
1792 			    const char    *base_dir,
1793 			    const char    *dest_dir,
1794 			    gboolean       update,
1795 			    const char    *password,
1796 			    gboolean       encrypt_header,
1797 			    FrCompression  compression,
1798 			    guint          volume_size)
1799 {
1800 	fr_process_clear (archive->process);
1801 	fr_archive_add (archive,
1802 			file_list,
1803 			base_dir,
1804 			dest_dir,
1805 			update,
1806 			FALSE,
1807 			password,
1808 			encrypt_header,
1809 			compression,
1810 			volume_size);
1811 	fr_process_start (archive->process);
1812 }
1813 
1814 
1815 static void
copy_remote_files_done(GError * error,gpointer user_data)1816 copy_remote_files_done (GError   *error,
1817 			gpointer  user_data)
1818 {
1819 	XferData *xfer_data = user_data;
1820 
1821 	fr_archive_copy_done (xfer_data->archive, FR_ACTION_COPYING_FILES_FROM_REMOTE, error);
1822 
1823 	if (error == NULL)
1824 		fr_archive_add_local_files (xfer_data->archive,
1825 					    xfer_data->file_list,
1826 					    xfer_data->tmp_dir,
1827 					    xfer_data->dest_dir,
1828 					    FALSE,
1829 					    xfer_data->password,
1830 					    xfer_data->encrypt_header,
1831 					    xfer_data->compression,
1832 					    xfer_data->volume_size);
1833 	xfer_data_free (xfer_data);
1834 }
1835 
1836 
1837 static void
copy_remote_files_progress(goffset current_file,goffset total_files,GFile * source,GFile * destination,goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)1838 copy_remote_files_progress (goffset   current_file,
1839                             goffset   total_files,
1840                             GFile    *source,
1841                             GFile    *destination,
1842                             goffset   current_num_bytes,
1843                             goffset   total_num_bytes,
1844                             gpointer  user_data)
1845 {
1846 	XferData *xfer_data = user_data;
1847 
1848 	g_signal_emit (G_OBJECT (xfer_data->archive),
1849 		       fr_archive_signals[PROGRESS],
1850 		       0,
1851 		       (double) current_file / (total_files + 1));
1852 }
1853 
1854 
1855 static void
copy_remote_files(FrArchive * archive,GList * file_list,const char * base_uri,const char * dest_dir,gboolean update,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size,const char * tmp_dir)1856 copy_remote_files (FrArchive     *archive,
1857 		   GList         *file_list,
1858 		   const char    *base_uri,
1859 		   const char    *dest_dir,
1860 		   gboolean       update,
1861 		   const char    *password,
1862 		   gboolean       encrypt_header,
1863 		   FrCompression  compression,
1864 		   guint          volume_size,
1865 		   const char    *tmp_dir)
1866 {
1867 	GList      *sources = NULL, *destinations = NULL;
1868 	GHashTable *created_folders;
1869 	GList      *scan;
1870 	XferData   *xfer_data;
1871 
1872 	created_folders = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
1873 	for (scan = file_list; scan; scan = scan->next) {
1874 		char  *partial_filename = scan->data;
1875 		char  *local_uri;
1876 		char  *local_folder_uri;
1877 		char  *remote_uri;
1878 
1879 		local_uri = g_strconcat ("file://", tmp_dir, "/", partial_filename, NULL);
1880 		local_folder_uri = remove_level_from_path (local_uri);
1881 		if (g_hash_table_lookup (created_folders, local_folder_uri) == NULL) {
1882 			GError *error = NULL;
1883 			if (! ensure_dir_exists (local_folder_uri, 0755, &error)) {
1884 				g_free (local_folder_uri);
1885 				g_free (local_uri);
1886 				gio_file_list_free (sources);
1887 				gio_file_list_free (destinations);
1888 				g_hash_table_destroy (created_folders);
1889 
1890 				fr_archive_action_completed (archive,
1891 							     FR_ACTION_COPYING_FILES_FROM_REMOTE,
1892 							     FR_PROC_ERROR_GENERIC,
1893 							     error->message);
1894 				g_clear_error (&error);
1895 				return;
1896 			}
1897 
1898 			g_hash_table_insert (created_folders, local_folder_uri, GINT_TO_POINTER (1));
1899 		}
1900 		else
1901 			g_free (local_folder_uri);
1902 
1903 		remote_uri = g_strconcat (base_uri, "/", partial_filename, NULL);
1904 		sources = g_list_append (sources, g_file_new_for_uri (remote_uri));
1905 		g_free (remote_uri);
1906 
1907 		destinations = g_list_append (destinations, g_file_new_for_uri (local_uri));
1908 		g_free (local_uri);
1909 	}
1910 	g_hash_table_destroy (created_folders);
1911 
1912 	xfer_data = g_new0 (XferData, 1);
1913 	xfer_data->archive = archive;
1914 	xfer_data->file_list = path_list_dup (file_list);
1915 	xfer_data->base_uri = g_strdup (base_uri);
1916 	xfer_data->dest_dir = g_strdup (dest_dir);
1917 	xfer_data->update = update;
1918 	xfer_data->password = g_strdup (password);
1919 	xfer_data->encrypt_header = encrypt_header;
1920 	xfer_data->compression = compression;
1921 	xfer_data->volume_size = volume_size;
1922 	xfer_data->tmp_dir = g_strdup (tmp_dir);
1923 
1924 	g_signal_emit (G_OBJECT (archive),
1925 		       fr_archive_signals[START],
1926 		       0,
1927 		       FR_ACTION_COPYING_FILES_FROM_REMOTE);
1928 
1929 	g_copy_files_async (sources,
1930 			    destinations,
1931 			    G_FILE_COPY_OVERWRITE,
1932 			    G_PRIORITY_DEFAULT,
1933 			    archive->priv->cancellable,
1934 			    copy_remote_files_progress,
1935 			    xfer_data,
1936 			    copy_remote_files_done,
1937 			    xfer_data);
1938 
1939 	gio_file_list_free (sources);
1940 	gio_file_list_free (destinations);
1941 }
1942 
1943 
1944 static char *
fr_archive_get_temp_work_dir(FrArchive * archive)1945 fr_archive_get_temp_work_dir (FrArchive *archive)
1946 {
1947 	fr_archive_remove_temp_work_dir (archive);
1948 	archive->priv->temp_dir = get_temp_work_dir (NULL);
1949 	return archive->priv->temp_dir;
1950 }
1951 
1952 
1953 void
fr_archive_add_files(FrArchive * archive,GList * file_list,const char * base_dir,const char * dest_dir,gboolean update,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size)1954 fr_archive_add_files (FrArchive     *archive,
1955 		      GList         *file_list,
1956 		      const char    *base_dir,
1957 		      const char    *dest_dir,
1958 		      gboolean       update,
1959 		      const char    *password,
1960 		      gboolean       encrypt_header,
1961 		      FrCompression  compression,
1962 		      guint          volume_size)
1963 {
1964 	if (uri_is_local (base_dir)) {
1965 		char *local_dir = g_filename_from_uri (base_dir, NULL, NULL);
1966 		fr_archive_add_local_files (archive,
1967 					    file_list,
1968 					    local_dir,
1969 					    dest_dir,
1970 					    update,
1971 					    password,
1972 					    encrypt_header,
1973 					    compression,
1974 					    volume_size);
1975 		g_free (local_dir);
1976 	}
1977 	else
1978 		copy_remote_files (archive,
1979 				   file_list,
1980 				   base_dir,
1981 				   dest_dir,
1982 				   update,
1983 				   password,
1984 				   encrypt_header,
1985 				   compression,
1986 				   volume_size,
1987 				   fr_archive_get_temp_work_dir (archive));
1988 }
1989 
1990 
1991 /* -- add with wildcard -- */
1992 
1993 
1994 typedef struct {
1995 	FrArchive     *archive;
1996 	char          *source_dir;
1997 	char          *dest_dir;
1998 	gboolean       update;
1999 	char          *password;
2000 	gboolean       encrypt_header;
2001 	FrCompression  compression;
2002 	guint          volume_size;
2003 } AddWithWildcardData;
2004 
2005 
2006 static void
add_with_wildcard_data_free(AddWithWildcardData * aww_data)2007 add_with_wildcard_data_free (AddWithWildcardData *aww_data)
2008 {
2009 	g_free (aww_data->source_dir);
2010 	g_free (aww_data->dest_dir);
2011 	g_free (aww_data->password);
2012 	g_free (aww_data);
2013 }
2014 
2015 
2016 static void
add_with_wildcard__step2(GList * file_list,GList * dirs_list,GError * error,gpointer data)2017 add_with_wildcard__step2 (GList    *file_list,
2018 			  GList    *dirs_list,
2019 			  GError   *error,
2020 			  gpointer  data)
2021 {
2022 	AddWithWildcardData *aww_data = data;
2023 	FrArchive           *archive = aww_data->archive;
2024 
2025 	if (error != NULL) {
2026 		fr_archive_action_completed (archive,
2027 					     FR_ACTION_GETTING_FILE_LIST,
2028 					     (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC),
2029 					     error->message);
2030 		return;
2031 	}
2032 
2033 	fr_archive_action_completed (archive,
2034 				     FR_ACTION_GETTING_FILE_LIST,
2035 				     FR_PROC_ERROR_NONE,
2036 				     NULL);
2037 
2038 	if (file_list != NULL)
2039 		fr_archive_add_files (aww_data->archive,
2040 				      file_list,
2041 				      aww_data->source_dir,
2042 				      aww_data->dest_dir,
2043 				      aww_data->update,
2044 				      aww_data->password,
2045 				      aww_data->encrypt_header,
2046 				      aww_data->compression,
2047 				      aww_data->volume_size);
2048 
2049 	path_list_free (file_list);
2050 	path_list_free (dirs_list);
2051 	add_with_wildcard_data_free (aww_data);
2052 }
2053 
2054 
2055 void
fr_archive_add_with_wildcard(FrArchive * archive,const char * include_files,const char * exclude_files,const char * exclude_folders,const char * source_dir,const char * dest_dir,gboolean update,gboolean follow_links,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size)2056 fr_archive_add_with_wildcard (FrArchive     *archive,
2057 			      const char    *include_files,
2058 			      const char    *exclude_files,
2059 			      const char    *exclude_folders,
2060 			      const char    *source_dir,
2061 			      const char    *dest_dir,
2062 			      gboolean       update,
2063 			      gboolean       follow_links,
2064 			      const char    *password,
2065 			      gboolean       encrypt_header,
2066 			      FrCompression  compression,
2067 			      guint          volume_size)
2068 {
2069 	AddWithWildcardData *aww_data;
2070 
2071 	g_return_if_fail (! archive->read_only);
2072 
2073 	aww_data = g_new0 (AddWithWildcardData, 1);
2074 	aww_data->archive = archive;
2075 	aww_data->source_dir = g_strdup (source_dir);
2076 	aww_data->dest_dir = g_strdup (dest_dir);
2077 	aww_data->update = update;
2078 	aww_data->password = g_strdup (password);
2079 	aww_data->encrypt_header = encrypt_header;
2080 	aww_data->compression = compression;
2081 	aww_data->volume_size = volume_size;
2082 
2083 	g_signal_emit (G_OBJECT (archive),
2084 		       fr_archive_signals[START],
2085 		       0,
2086 		       FR_ACTION_GETTING_FILE_LIST);
2087 
2088 	g_directory_list_async (source_dir,
2089 				source_dir,
2090 				TRUE,
2091 				follow_links,
2092 				NO_BACKUP_FILES,
2093 				NO_DOT_FILES,
2094 				include_files,
2095 				exclude_files,
2096 				exclude_folders,
2097 				IGNORE_CASE,
2098 				archive->priv->cancellable,
2099 				add_with_wildcard__step2,
2100 				aww_data);
2101 }
2102 
2103 
2104 /* -- fr_archive_add_directory -- */
2105 
2106 
2107 typedef struct {
2108 	FrArchive     *archive;
2109 	char          *base_dir;
2110 	char          *dest_dir;
2111 	gboolean       update;
2112 	char          *password;
2113 	gboolean       encrypt_header;
2114 	FrCompression  compression;
2115 	guint          volume_size;
2116 } AddDirectoryData;
2117 
2118 
2119 static void
add_directory_data_free(AddDirectoryData * ad_data)2120 add_directory_data_free (AddDirectoryData *ad_data)
2121 {
2122 	g_free (ad_data->base_dir);
2123 	g_free (ad_data->dest_dir);
2124 	g_free (ad_data->password);
2125 	g_free (ad_data);
2126 }
2127 
2128 
2129 static void
add_directory__step2(GList * file_list,GList * dir_list,GError * error,gpointer data)2130 add_directory__step2 (GList    *file_list,
2131 		      GList    *dir_list,
2132 		      GError   *error,
2133 		      gpointer  data)
2134 {
2135 	AddDirectoryData *ad_data = data;
2136 	FrArchive        *archive = ad_data->archive;
2137 
2138 	if (error != NULL) {
2139 		fr_archive_action_completed (archive,
2140 					     FR_ACTION_GETTING_FILE_LIST,
2141 					     (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC),
2142 					     error->message);
2143 		return;
2144 	}
2145 
2146 	fr_archive_action_completed (archive,
2147 				     FR_ACTION_GETTING_FILE_LIST,
2148 				     FR_PROC_ERROR_NONE,
2149 				     NULL);
2150 
2151 	if (archive->command->propAddCanStoreFolders)
2152 		file_list = g_list_concat (file_list, dir_list);
2153 	else
2154 		path_list_free (dir_list);
2155 
2156 	if (file_list != NULL) {
2157 		fr_archive_add_files (ad_data->archive,
2158 				      file_list,
2159 				      ad_data->base_dir,
2160 				      ad_data->dest_dir,
2161 				      ad_data->update,
2162 				      ad_data->password,
2163 				      ad_data->encrypt_header,
2164 				      ad_data->compression,
2165 				      ad_data->volume_size);
2166 		path_list_free (file_list);
2167 	}
2168 
2169 	add_directory_data_free (ad_data);
2170 }
2171 
2172 
2173 void
fr_archive_add_directory(FrArchive * archive,const char * directory,const char * base_dir,const char * dest_dir,gboolean update,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size)2174 fr_archive_add_directory (FrArchive     *archive,
2175 			  const char    *directory,
2176 			  const char    *base_dir,
2177 			  const char    *dest_dir,
2178 			  gboolean       update,
2179 			  const char    *password,
2180 			  gboolean       encrypt_header,
2181 			  FrCompression  compression,
2182 			  guint          volume_size)
2183 
2184 {
2185 	AddDirectoryData *ad_data;
2186 
2187 	g_return_if_fail (! archive->read_only);
2188 
2189 	ad_data = g_new0 (AddDirectoryData, 1);
2190 	ad_data->archive = archive;
2191 	ad_data->base_dir = g_strdup (base_dir);
2192 	ad_data->dest_dir = g_strdup (dest_dir);
2193 	ad_data->update = update;
2194 	ad_data->password = g_strdup (password);
2195 	ad_data->encrypt_header = encrypt_header;
2196 	ad_data->compression = compression;
2197 	ad_data->volume_size = volume_size;
2198 
2199 	g_signal_emit (G_OBJECT (archive),
2200 		       fr_archive_signals[START],
2201 		       0,
2202 		       FR_ACTION_GETTING_FILE_LIST);
2203 
2204 	g_directory_list_all_async (directory,
2205 				    base_dir,
2206 				    TRUE,
2207 				    archive->priv->cancellable,
2208 				    add_directory__step2,
2209 				    ad_data);
2210 }
2211 
2212 
2213 void
fr_archive_add_items(FrArchive * archive,GList * item_list,const char * base_dir,const char * dest_dir,gboolean update,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size)2214 fr_archive_add_items (FrArchive     *archive,
2215 		      GList         *item_list,
2216 		      const char    *base_dir,
2217 		      const char    *dest_dir,
2218 		      gboolean       update,
2219 		      const char    *password,
2220 		      gboolean       encrypt_header,
2221 		      FrCompression  compression,
2222 		      guint          volume_size)
2223 
2224 {
2225 	AddDirectoryData *ad_data;
2226 
2227 	g_return_if_fail (! archive->read_only);
2228 
2229 	ad_data = g_new0 (AddDirectoryData, 1);
2230 	ad_data->archive = archive;
2231 	ad_data->base_dir = g_strdup (base_dir);
2232 	ad_data->dest_dir = g_strdup (dest_dir);
2233 	ad_data->update = update;
2234 	ad_data->password = g_strdup (password);
2235 	ad_data->encrypt_header = encrypt_header;
2236 	ad_data->compression = compression;
2237 	ad_data->volume_size = volume_size;
2238 
2239 	g_signal_emit (G_OBJECT (archive),
2240 		       fr_archive_signals[START],
2241 		       0,
2242 		       FR_ACTION_GETTING_FILE_LIST);
2243 
2244 	g_list_items_async (item_list,
2245 			    base_dir,
2246 			    archive->priv->cancellable,
2247 			    add_directory__step2,
2248 			    ad_data);
2249 }
2250 
2251 
2252 /* -- fr_archive_add_dropped_items -- */
2253 
2254 
2255 static gboolean
all_files_in_same_dir(GList * list)2256 all_files_in_same_dir (GList *list)
2257 {
2258 	gboolean  same_dir = TRUE;
2259 	char     *first_basedir;
2260 	GList    *scan;
2261 
2262 	if (list == NULL)
2263 		return FALSE;
2264 
2265 	first_basedir = remove_level_from_path (list->data);
2266 	if (first_basedir == NULL)
2267 		return TRUE;
2268 
2269 	for (scan = list->next; scan; scan = scan->next) {
2270 		char *path = scan->data;
2271 		char *basedir;
2272 
2273 		basedir = remove_level_from_path (path);
2274 		if (basedir == NULL) {
2275 			same_dir = FALSE;
2276 			break;
2277 		}
2278 
2279 		if (strcmp (first_basedir, basedir) != 0) {
2280 			same_dir = FALSE;
2281 			g_free (basedir);
2282 			break;
2283 		}
2284 		g_free (basedir);
2285 	}
2286 	g_free (first_basedir);
2287 
2288 	return same_dir;
2289 }
2290 
2291 
2292 static void
add_dropped_items(DroppedItemsData * data)2293 add_dropped_items (DroppedItemsData *data)
2294 {
2295 	FrArchive *archive = data->archive;
2296 	GList     *list = data->item_list;
2297 	GList     *scan;
2298 
2299 	if (list == NULL) {
2300 		dropped_items_data_free (archive->priv->dropped_items_data);
2301 		archive->priv->dropped_items_data = NULL;
2302 		fr_archive_action_completed (archive,
2303 					     FR_ACTION_ADDING_FILES,
2304 					     FR_PROC_ERROR_NONE,
2305 					     NULL);
2306 		return;
2307 	}
2308 
2309 	/* if all files/dirs are in the same directory call fr_archive_add_items... */
2310 
2311 	if (all_files_in_same_dir (list)) {
2312 		char *first_base_dir;
2313 
2314 		first_base_dir = remove_level_from_path (list->data);
2315 		fr_archive_add_items (data->archive,
2316 				      list,
2317 				      first_base_dir,
2318 				      data->dest_dir,
2319 				      data->update,
2320 				      data->password,
2321 				      data->encrypt_header,
2322 				      data->compression,
2323 				      data->volume_size);
2324 		g_free (first_base_dir);
2325 
2326 		dropped_items_data_free (archive->priv->dropped_items_data);
2327 		archive->priv->dropped_items_data = NULL;
2328 
2329 		return;
2330 	}
2331 
2332 	/* ...else add a directory at a time. */
2333 
2334 	for (scan = list; scan; scan = scan->next) {
2335 		char *path = scan->data;
2336 		char *base_dir;
2337 
2338 		if (! uri_is_dir (path))
2339 			continue;
2340 
2341 		data->item_list = g_list_remove_link (list, scan);
2342 		if (data->item_list != NULL)
2343 			archive->priv->continue_adding_dropped_items = TRUE;
2344 		base_dir = remove_level_from_path (path);
2345 
2346 		fr_archive_add_directory (archive,
2347 					  file_name_from_path (path),
2348 					  base_dir,
2349 					  data->dest_dir,
2350 					  data->update,
2351 					  data->password,
2352 					  data->encrypt_header,
2353 					  data->compression,
2354 					  data->volume_size);
2355 
2356 		g_free (base_dir);
2357 		g_free (path);
2358 
2359 		return;
2360 	}
2361 
2362 	/* if all files are in the same directory call fr_archive_add_files. */
2363 
2364 	if (all_files_in_same_dir (list)) {
2365 		char  *first_basedir;
2366 		GList *only_names_list = NULL;
2367 
2368 		first_basedir = remove_level_from_path (list->data);
2369 
2370 		for (scan = list; scan; scan = scan->next) {
2371 			char *name;
2372 
2373 			name = g_uri_unescape_string (file_name_from_path (scan->data), NULL);
2374 			only_names_list = g_list_prepend (only_names_list, name);
2375 		}
2376 
2377 		fr_archive_add_files (archive,
2378 				      only_names_list,
2379 				      first_basedir,
2380 				      data->dest_dir,
2381 				      data->update,
2382 				      data->password,
2383 				      data->encrypt_header,
2384 				      data->compression,
2385 				      data->volume_size);
2386 
2387 		path_list_free (only_names_list);
2388 		g_free (first_basedir);
2389 
2390 		return;
2391 	}
2392 
2393 	/* ...else call fr_command_add for each file.  This is needed to add
2394 	 * files without path info. FIXME: doesn't work with remote files. */
2395 
2396 	fr_archive_stoppable (archive, FALSE);
2397 	archive->command->creating_archive = ! g_file_query_exists (archive->local_copy, archive->priv->cancellable);
2398 	g_object_set (archive->command,
2399 		      "file", archive->local_copy,
2400 		      "password", data->password,
2401 		      "encrypt_header", data->encrypt_header,
2402 		      "compression", data->compression,
2403 		      "volume_size", data->volume_size,
2404 		      NULL);
2405 	fr_process_clear (archive->process);
2406 	fr_command_uncompress (archive->command);
2407 	for (scan = list; scan; scan = scan->next) {
2408 		char  *fullpath = scan->data;
2409 		char  *basedir;
2410 		GList *singleton;
2411 
2412 		basedir = remove_level_from_path (fullpath);
2413 		singleton = g_list_prepend (NULL, (char*)file_name_from_path (fullpath));
2414 		fr_command_add (archive->command,
2415 				NULL,
2416 				singleton,
2417 				basedir,
2418 				data->update,
2419 				FALSE);
2420 		g_list_free (singleton);
2421 		g_free (basedir);
2422 	}
2423 	fr_command_recompress (archive->command);
2424 	fr_process_start (archive->process);
2425 
2426 	path_list_free (data->item_list);
2427 	data->item_list = NULL;
2428 }
2429 
2430 
2431 void
fr_archive_add_dropped_items(FrArchive * archive,GList * item_list,const char * base_dir,const char * dest_dir,gboolean update,const char * password,gboolean encrypt_header,FrCompression compression,guint volume_size)2432 fr_archive_add_dropped_items (FrArchive     *archive,
2433 			      GList         *item_list,
2434 			      const char    *base_dir,
2435 			      const char    *dest_dir,
2436 			      gboolean       update,
2437 			      const char    *password,
2438 			      gboolean       encrypt_header,
2439 			      FrCompression  compression,
2440 			      guint          volume_size)
2441 {
2442 	GList *scan;
2443 	char  *archive_uri;
2444 
2445 	if (archive->read_only) {
2446 		fr_archive_action_completed (archive,
2447 					     FR_ACTION_ADDING_FILES,
2448 					     FR_PROC_ERROR_GENERIC,
2449 					     ! archive->have_permissions ? _("You don't have the right permissions.") : _("This archive type cannot be modified"));
2450 		return;
2451 	}
2452 
2453 	/* FIXME: make this check for all the add actions */
2454 	archive_uri = g_file_get_uri (archive->file);
2455 	for (scan = item_list; scan; scan = scan->next) {
2456 		if (uricmp (scan->data, archive_uri) == 0) {
2457 			g_free (archive_uri);
2458 			fr_archive_action_completed (archive,
2459 						     FR_ACTION_ADDING_FILES,
2460 						     FR_PROC_ERROR_GENERIC,
2461 						     _("You can't add an archive to itself."));
2462 			return;
2463 		}
2464 	}
2465 	g_free (archive_uri);
2466 
2467 	if (archive->priv->dropped_items_data != NULL)
2468 		dropped_items_data_free (archive->priv->dropped_items_data);
2469 	archive->priv->dropped_items_data = dropped_items_data_new (
2470 						archive,
2471 				       		item_list,
2472 				       		base_dir,
2473 				       		dest_dir,
2474 				       		update,
2475 				       		password,
2476 				       		encrypt_header,
2477 				       		compression,
2478 				       		volume_size);
2479 	add_dropped_items (archive->priv->dropped_items_data);
2480 }
2481 
2482 
2483 /* -- remove -- */
2484 
2485 
2486 static gboolean
file_is_in_subfolder_of(const char * filename,GList * folder_list)2487 file_is_in_subfolder_of (const char *filename,
2488 		         GList      *folder_list)
2489 {
2490 	GList *scan;
2491 
2492 	if (filename == NULL)
2493 		return FALSE;
2494 
2495 	for (scan = folder_list; scan; scan = scan->next) {
2496 		char *folder_in_list = (char*) scan->data;
2497 
2498 		if (path_in_path (folder_in_list, filename))
2499 			return TRUE;
2500 	}
2501 
2502 	return FALSE;
2503 }
2504 
2505 static gboolean
archive_type_has_issues_deleting_non_empty_folders(FrArchive * archive)2506 archive_type_has_issues_deleting_non_empty_folders (FrArchive *archive)
2507 {
2508        return ! archive->command->propCanDeleteNonEmptyFolders;
2509 }
2510 
2511 
2512 static void
delete_from_archive(FrArchive * archive,GList * file_list)2513 delete_from_archive (FrArchive *archive,
2514 		     GList     *file_list)
2515 {
2516 	gboolean  file_list_created = FALSE;
2517 	GList    *tmp_file_list = NULL;
2518 	gboolean  tmp_file_list_created = FALSE;
2519 	GList    *scan;
2520 
2521 	/* file_list == NULL means delete all the files in the archive. */
2522 
2523 	if (file_list == NULL) {
2524 		guint i;
2525 
2526 		for (i = 0; i < archive->command->files->len; i++) {
2527 			FileData *fdata = g_ptr_array_index (archive->command->files, i);
2528 			file_list = g_list_prepend (file_list, fdata->original_path);
2529 		}
2530 
2531 		file_list_created = TRUE;
2532 	}
2533 
2534 	if (archive_type_has_issues_deleting_non_empty_folders (archive)) {
2535 		GList *folders_to_remove;
2536 
2537 		/* remove from the list the files contained in folders to be
2538 		 * removed. */
2539 
2540 		folders_to_remove = NULL;
2541 		for (scan = file_list; scan != NULL; scan = scan->next) {
2542 			char *path = scan->data;
2543 
2544 			if (path[strlen (path) - 1] == '/')
2545 				folders_to_remove = g_list_prepend (folders_to_remove, path);
2546 		}
2547 
2548 		if (folders_to_remove != NULL) {
2549 			tmp_file_list = NULL;
2550 			for (scan = file_list; scan != NULL; scan = scan->next) {
2551 				char *path = scan->data;
2552 
2553 				if (! file_is_in_subfolder_of (path, folders_to_remove))
2554 					tmp_file_list = g_list_prepend (tmp_file_list, path);
2555 			}
2556 			tmp_file_list_created = TRUE;
2557 			g_list_free (folders_to_remove);
2558 		}
2559 	}
2560 
2561 	if (! tmp_file_list_created)
2562 		tmp_file_list = g_list_copy (file_list);
2563 
2564 	if (file_list_created)
2565 		g_list_free (file_list);
2566 
2567 	fr_command_set_n_files (archive->command, g_list_length (tmp_file_list));
2568 
2569 	if (archive->command->propListFromFile
2570 	    && (archive->command->n_files > LIST_LENGTH_TO_USE_FILE))
2571 	{
2572 		char *list_dir;
2573 		char *list_filename;
2574 
2575 		if (save_list_to_temp_file (tmp_file_list, &list_dir, &list_filename, NULL)) {
2576 			fr_command_delete (archive->command,
2577 					   list_filename,
2578 					   tmp_file_list);
2579 
2580 			/* remove the temp dir */
2581 
2582 			fr_process_begin_command (archive->process, "rm");
2583 			fr_process_set_working_dir (archive->process, g_get_tmp_dir());
2584 			fr_process_set_sticky (archive->process, TRUE);
2585 			fr_process_add_arg (archive->process, "-rf");
2586 			fr_process_add_arg (archive->process, list_dir);
2587 			fr_process_end_command (archive->process);
2588 		}
2589 
2590 		g_free (list_filename);
2591 		g_free (list_dir);
2592 	}
2593 	else {
2594 		for (scan = tmp_file_list; scan != NULL; ) {
2595 			GList *prev = scan->prev;
2596 			GList *chunk_list;
2597 			int    l;
2598 
2599 			chunk_list = scan;
2600 			l = 0;
2601 			while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
2602 				if (l == 0)
2603 					l = strlen (scan->data);
2604 				prev = scan;
2605 				scan = scan->next;
2606 				if (scan != NULL)
2607 					l += strlen (scan->data);
2608 			}
2609 
2610 			prev->next = NULL;
2611 			fr_command_delete (archive->command, NULL, chunk_list);
2612 			prev->next = scan;
2613 		}
2614 	}
2615 
2616 	g_list_free (tmp_file_list);
2617 }
2618 
2619 
2620 void
fr_archive_remove(FrArchive * archive,GList * file_list,FrCompression compression)2621 fr_archive_remove (FrArchive     *archive,
2622 		   GList         *file_list,
2623 		   FrCompression  compression)
2624 {
2625 	char *tmp_archive_dir = NULL;
2626 	char *archive_filename = NULL;
2627 	char *tmp_archive_filename = NULL;
2628 
2629 	g_return_if_fail (archive != NULL);
2630 
2631 	if (archive->read_only)
2632 		return;
2633 
2634 	fr_archive_stoppable (archive, TRUE);
2635 	archive->command->creating_archive = FALSE;
2636 	g_object_set (archive->command, "compression", compression, NULL);
2637 
2638 	/* create the new archive in a temporary sub-directory, this allows
2639 	 * to cancel the operation without losing the original archive and
2640 	 * removing possible temporary files created by the command. */
2641 
2642 	{
2643 		GFile *local_copy_parent;
2644 		char  *archive_dir;
2645 		GFile *tmp_file;
2646 
2647 		/* create the new archive in a sub-folder of the original
2648 		 * archive this way the 'mv' command is fast. */
2649 
2650 		local_copy_parent = g_file_get_parent (archive->local_copy);
2651 		archive_dir = g_file_get_path (local_copy_parent);
2652 		tmp_archive_dir = get_temp_work_dir (archive_dir);
2653 		archive_filename = g_file_get_path (archive->local_copy);
2654 		tmp_archive_filename = g_build_filename (tmp_archive_dir, file_name_from_path (archive_filename), NULL);
2655 		tmp_file = g_file_new_for_path (tmp_archive_filename);
2656 		g_object_set (archive->command, "file", tmp_file, NULL);
2657 
2658 		if (! archive->command->creating_archive) {
2659 			/* copy the original archive to the new position */
2660 
2661 			fr_process_begin_command (archive->process, "cp");
2662 			fr_process_add_arg (archive->process, "-f");
2663 			fr_process_add_arg (archive->process, archive_filename);
2664 			fr_process_add_arg (archive->process, tmp_archive_filename);
2665 			fr_process_end_command (archive->process);
2666 		}
2667 
2668 		g_object_unref (tmp_file);
2669 		g_free (archive_dir);
2670 		g_object_unref (local_copy_parent);
2671 	}
2672 
2673 	/* uncompress, delete and recompress */
2674 
2675 	fr_command_uncompress (archive->command);
2676 	delete_from_archive (archive, file_list);
2677 	fr_command_recompress (archive->command);
2678 
2679 	/* move the new archive to the original position */
2680 
2681 	fr_process_begin_command (archive->process, "mv");
2682 	fr_process_add_arg (archive->process, "-f");
2683 	fr_process_add_arg (archive->process, tmp_archive_filename);
2684 	fr_process_add_arg (archive->process, archive_filename);
2685 	fr_process_end_command (archive->process);
2686 
2687 	/* remove the temp sub-directory */
2688 
2689 	fr_process_begin_command (archive->process, "rm");
2690 	fr_process_set_working_dir (archive->process, g_get_tmp_dir());
2691 	fr_process_set_sticky (archive->process, TRUE);
2692 	fr_process_add_arg (archive->process, "-rf");
2693 	fr_process_add_arg (archive->process, tmp_archive_dir);
2694 	fr_process_end_command (archive->process);
2695 
2696 	g_free (tmp_archive_filename);
2697 	g_free (archive_filename);
2698 	g_free (tmp_archive_dir);
2699 }
2700 
2701 
2702 /* -- extract -- */
2703 
2704 
2705 static void
move_files_to_dir(FrArchive * archive,GList * file_list,const char * source_dir,const char * dest_dir,gboolean overwrite)2706 move_files_to_dir (FrArchive  *archive,
2707 		   GList      *file_list,
2708 		   const char *source_dir,
2709 		   const char *dest_dir,
2710 		   gboolean    overwrite)
2711 {
2712 	GList *list;
2713 	GList *scan;
2714 
2715 	/* we prefer mv instead of cp for performance reasons,
2716 	 * but if the destination folder already exists mv
2717 	 * doesn't work correctly. (bug #590027) */
2718 
2719 	list = g_list_copy (file_list);
2720 	for (scan = list; scan; /* void */) {
2721 		GList *next = scan->next;
2722 		char  *filename = scan->data;
2723 		char  *basename;
2724 		char  *destname;
2725 
2726 		basename = g_path_get_basename (filename);
2727 		destname = g_build_filename (dest_dir, basename, NULL);
2728 		if (g_file_test (destname, G_FILE_TEST_IS_DIR)) {
2729 			fr_process_begin_command (archive->process, "cp");
2730 			fr_process_add_arg (archive->process, "-R");
2731 			if (overwrite)
2732 				fr_process_add_arg (archive->process, "-f");
2733 			else
2734 				fr_process_add_arg (archive->process, "-n");
2735 			if (filename[0] == '/')
2736 				fr_process_add_arg_concat (archive->process, source_dir, filename, NULL);
2737 			else
2738 				fr_process_add_arg_concat (archive->process, source_dir, "/", filename, NULL);
2739 			fr_process_add_arg (archive->process, dest_dir);
2740 			fr_process_end_command (archive->process);
2741 
2742 			list = g_list_remove_link (list, scan);
2743 			g_list_free (scan);
2744 		}
2745 
2746 		g_free (destname);
2747 		g_free (basename);
2748 
2749 		scan = next;
2750 	}
2751 
2752 	if (list == NULL)
2753 		return;
2754 
2755 	/* 'list' now contains the files that can be moved without problems */
2756 
2757 	fr_process_begin_command (archive->process, "mv");
2758 	if (overwrite)
2759 		fr_process_add_arg (archive->process, "-f");
2760 	else
2761 		fr_process_add_arg (archive->process, "-n");
2762 	for (scan = list; scan; scan = scan->next) {
2763 		char *filename = scan->data;
2764 
2765 		if (filename[0] == '/')
2766 			fr_process_add_arg_concat (archive->process, source_dir, filename, NULL);
2767 		else
2768 			fr_process_add_arg_concat (archive->process, source_dir, "/", filename, NULL);
2769 	}
2770 	fr_process_add_arg (archive->process, dest_dir);
2771 	fr_process_end_command (archive->process);
2772 
2773 	g_list_free (list);
2774 }
2775 
2776 
2777 static void
move_files_in_chunks(FrArchive * archive,GList * file_list,const char * temp_dir,const char * dest_dir,gboolean overwrite)2778 move_files_in_chunks (FrArchive  *archive,
2779 		      GList      *file_list,
2780 		      const char *temp_dir,
2781 		      const char *dest_dir,
2782 		      gboolean    overwrite)
2783 {
2784 	GList *scan;
2785 	int    temp_dir_l;
2786 
2787 	temp_dir_l = strlen (temp_dir);
2788 
2789 	for (scan = file_list; scan != NULL; ) {
2790 		GList *prev = scan->prev;
2791 		GList *chunk_list;
2792 		int    l;
2793 
2794 		chunk_list = scan;
2795 		l = 0;
2796 		while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
2797 			if (l == 0)
2798 				l = temp_dir_l + 1 + strlen (scan->data);
2799 			prev = scan;
2800 			scan = scan->next;
2801 			if (scan != NULL)
2802 				l += temp_dir_l + 1 + strlen (scan->data);
2803 		}
2804 
2805 		prev->next = NULL;
2806 		move_files_to_dir (archive, chunk_list, temp_dir, dest_dir, overwrite);
2807 		prev->next = scan;
2808 	}
2809 }
2810 
2811 
2812 static void
extract_from_archive(FrArchive * archive,GList * file_list,const char * dest_dir,gboolean overwrite,gboolean skip_older,gboolean junk_paths,const char * password)2813 extract_from_archive (FrArchive  *archive,
2814 		      GList      *file_list,
2815 		      const char *dest_dir,
2816 		      gboolean    overwrite,
2817 		      gboolean    skip_older,
2818 		      gboolean    junk_paths,
2819 		      const char *password)
2820 {
2821 	FrCommand *command = archive->command;
2822 	GList     *scan;
2823 
2824 	g_object_set (command, "password", password, NULL);
2825 
2826 	if (file_list == NULL) {
2827 		fr_command_extract (command,
2828 				    NULL,
2829 				    file_list,
2830 				    dest_dir,
2831 				    overwrite,
2832 				    skip_older,
2833 				    junk_paths);
2834 		return;
2835 	}
2836 
2837 	if (command->propListFromFile
2838 	    && (g_list_length (file_list) > LIST_LENGTH_TO_USE_FILE))
2839 	{
2840 		char *list_dir;
2841 		char *list_filename;
2842 
2843 		if (save_list_to_temp_file (file_list, &list_dir, &list_filename, NULL)) {
2844 			fr_command_extract (command,
2845 					    list_filename,
2846 					    file_list,
2847 					    dest_dir,
2848 					    overwrite,
2849 					    skip_older,
2850 					    junk_paths);
2851 
2852 			/* remove the temp dir */
2853 
2854 			fr_process_begin_command (archive->process, "rm");
2855 			fr_process_set_working_dir (archive->process, g_get_tmp_dir());
2856 			fr_process_set_sticky (archive->process, TRUE);
2857 			fr_process_add_arg (archive->process, "-rf");
2858 			fr_process_add_arg (archive->process, list_dir);
2859 			fr_process_end_command (archive->process);
2860 		}
2861 
2862 		g_free (list_filename);
2863 		g_free (list_dir);
2864 	}
2865 	else {
2866 		for (scan = file_list; scan != NULL; ) {
2867 			GList *prev = scan->prev;
2868 			GList *chunk_list;
2869 			int    l;
2870 
2871 			chunk_list = scan;
2872 			l = 0;
2873 			while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
2874 				if (l == 0)
2875 					l = strlen (scan->data);
2876 				prev = scan;
2877 				scan = scan->next;
2878 				if (scan != NULL)
2879 					l += strlen (scan->data);
2880 			}
2881 
2882 			prev->next = NULL;
2883 			fr_command_extract (command,
2884 					    NULL,
2885 					    chunk_list,
2886 					    dest_dir,
2887 					    overwrite,
2888 					    skip_older,
2889 					    junk_paths);
2890 			prev->next = scan;
2891 		}
2892 	}
2893 }
2894 
2895 
2896 static char*
compute_base_path(const char * base_dir,const char * path,gboolean junk_paths,gboolean can_junk_paths)2897 compute_base_path (const char *base_dir,
2898 		   const char *path,
2899 		   gboolean    junk_paths,
2900 		   gboolean    can_junk_paths)
2901 {
2902 	int         base_dir_len = strlen (base_dir);
2903 	int         path_len = strlen (path);
2904 	const char *base_path;
2905 	char       *name_end;
2906 	char       *new_path;
2907 
2908 	if (junk_paths) {
2909 		if (can_junk_paths)
2910 			new_path = g_strdup (file_name_from_path (path));
2911 		else
2912 			new_path = g_strdup (path);
2913 
2914 		/*debug (DEBUG_INFO, "%s, %s --> %s\n", base_dir, path, new_path);*/
2915 
2916 		return new_path;
2917 	}
2918 
2919 	if (path_len < base_dir_len)
2920 		return NULL;
2921 
2922 	base_path = path + base_dir_len;
2923 	if (path[0] != '/')
2924 		base_path -= 1;
2925 	name_end = strchr (base_path, '/');
2926 
2927 	if (name_end == NULL)
2928 		new_path = g_strdup (path);
2929 	else {
2930 		int name_len = name_end - path;
2931 		new_path = g_strndup (path, name_len);
2932 	}
2933 
2934 	/*debug (DEBUG_INFO, "%s, %s --> %s\n", base_dir, path, new_path);*/
2935 
2936 	return new_path;
2937 }
2938 
2939 
2940 static GList*
compute_list_base_path(const char * base_dir,GList * filtered,gboolean junk_paths,gboolean can_junk_paths)2941 compute_list_base_path (const char *base_dir,
2942 			GList      *filtered,
2943 			gboolean    junk_paths,
2944 			gboolean    can_junk_paths)
2945 {
2946 	GList *scan;
2947 	GList *list = NULL, *list_unique = NULL;
2948 	GList *last_inserted;
2949 
2950 	if (filtered == NULL)
2951 		return NULL;
2952 
2953 	for (scan = filtered; scan; scan = scan->next) {
2954 		const char *path = scan->data;
2955 		char       *new_path;
2956 
2957 		new_path = compute_base_path (base_dir, path, junk_paths, can_junk_paths);
2958 		if (new_path != NULL)
2959 			list = g_list_prepend (list, new_path);
2960 	}
2961 
2962 	/* The above operation can create duplicates, we remove them here. */
2963 	list = g_list_sort (list, (GCompareFunc)strcmp);
2964 
2965 	last_inserted = NULL;
2966 	for (scan = list; scan; scan = scan->next) {
2967 		const char *path = scan->data;
2968 
2969 		if (last_inserted != NULL) {
2970 			const char *last_path = (const char*)last_inserted->data;
2971 			if (strcmp (last_path, path) == 0) {
2972 				g_free (scan->data);
2973 				continue;
2974 			}
2975 		}
2976 
2977 		last_inserted = scan;
2978 		list_unique = g_list_prepend (list_unique, scan->data);
2979 	}
2980 
2981 	g_list_free (list);
2982 
2983 	return list_unique;
2984 }
2985 
2986 
2987 static gboolean
archive_type_has_issues_extracting_non_empty_folders(FrArchive * archive)2988 archive_type_has_issues_extracting_non_empty_folders (FrArchive *archive)
2989 {
2990 	/*if ((archive->command->files == NULL) || (archive->command->files->len == 0))
2991 		return FALSE;  FIXME: test with extract_here */
2992 	return ! archive->command->propCanExtractNonEmptyFolders;
2993 }
2994 
2995 
2996 static gboolean
file_list_contains_files_in_this_dir(GList * file_list,const char * dirname)2997 file_list_contains_files_in_this_dir (GList      *file_list,
2998 				      const char *dirname)
2999 {
3000 	GList *scan;
3001 
3002 	for (scan = file_list; scan; scan = scan->next) {
3003 		char *filename = scan->data;
3004 
3005 		if (path_in_path (dirname, filename))
3006 			return TRUE;
3007 	}
3008 
3009 	return FALSE;
3010 }
3011 
3012 
3013 static GList*
remove_files_contained_in_this_dir(GList * file_list,GList * dir_pointer)3014 remove_files_contained_in_this_dir (GList *file_list,
3015 				    GList *dir_pointer)
3016 {
3017 	char  *dirname = dir_pointer->data;
3018 	int    dirname_l = strlen (dirname);
3019 	GList *scan;
3020 
3021 	for (scan = dir_pointer->next; scan; /* empty */) {
3022 		char *filename = scan->data;
3023 
3024 		if (strncmp (dirname, filename, dirname_l) != 0)
3025 			break;
3026 
3027 		if (path_in_path (dirname, filename)) {
3028 			GList *next = scan->next;
3029 
3030 			file_list = g_list_remove_link (file_list, scan);
3031 			g_list_free (scan);
3032 
3033 			scan = next;
3034 		}
3035 		else
3036 			scan = scan->next;
3037 	}
3038 
3039 	return file_list;
3040 }
3041 
3042 
3043 void
fr_archive_extract_to_local(FrArchive * archive,GList * file_list,const char * destination,const char * base_dir,gboolean skip_older,gboolean overwrite,gboolean junk_paths,const char * password)3044 fr_archive_extract_to_local (FrArchive  *archive,
3045 			     GList      *file_list,
3046 			     const char *destination,
3047 			     const char *base_dir,
3048 			     gboolean    skip_older,
3049 			     gboolean    overwrite,
3050 			     gboolean    junk_paths,
3051 			     const char *password)
3052 {
3053 	GList    *filtered;
3054 	GList    *scan;
3055 	gboolean  extract_all;
3056 	gboolean  use_base_dir;
3057 	gboolean  all_options_supported;
3058 	gboolean  move_to_dest_dir;
3059 	gboolean  file_list_created = FALSE;
3060 
3061 	g_return_if_fail (archive != NULL);
3062 
3063 	fr_archive_stoppable (archive, TRUE);
3064 	g_object_set (archive->command, "file", archive->local_copy, NULL);
3065 
3066 	/* if a command supports all the requested options use
3067 	 * fr_command_extract directly. */
3068 
3069 	use_base_dir = ! ((base_dir == NULL)
3070 			  || (strcmp (base_dir, "") == 0)
3071 			  || (strcmp (base_dir, "/") == 0));
3072 
3073 	all_options_supported = (! use_base_dir
3074 				 && ! (! overwrite && ! archive->command->propExtractCanAvoidOverwrite)
3075 				 && ! (skip_older && ! archive->command->propExtractCanSkipOlder)
3076 				 && ! (junk_paths && ! archive->command->propExtractCanJunkPaths));
3077 
3078 	extract_all = (file_list == NULL);
3079 	if (extract_all && (! all_options_supported || ! archive->command->propCanExtractAll)) {
3080 		guint i;
3081 
3082 		file_list = NULL;
3083 		for (i = 0; i < archive->command->files->len; i++) {
3084 			FileData *fdata = g_ptr_array_index (archive->command->files, i);
3085 			file_list = g_list_prepend (file_list, g_strdup (fdata->original_path));
3086 		}
3087 		file_list_created = TRUE;
3088 	}
3089 
3090 	if (extract_all && (file_list == NULL))
3091 		fr_command_set_n_files (archive->command, archive->command->n_regular_files);
3092 	else
3093 		fr_command_set_n_files (archive->command, g_list_length (file_list));
3094 
3095 	if (all_options_supported) {
3096 		gboolean created_filtered_list = FALSE;
3097 
3098 		if (! extract_all && archive_type_has_issues_extracting_non_empty_folders (archive)) {
3099 			created_filtered_list = TRUE;
3100 			filtered = g_list_copy (file_list);
3101 			filtered = g_list_sort (filtered, (GCompareFunc) strcmp);
3102 			for (scan = filtered; scan; scan = scan->next)
3103 				filtered = remove_files_contained_in_this_dir (filtered, scan);
3104 		}
3105 		else
3106 			filtered = file_list;
3107 
3108 		if (! (created_filtered_list && (filtered == NULL)))
3109 			extract_from_archive (archive,
3110 					      filtered,
3111 					      destination,
3112 					      overwrite,
3113 					      skip_older,
3114 					      junk_paths,
3115 					      password);
3116 
3117 		if (created_filtered_list && (filtered != NULL))
3118 			g_list_free (filtered);
3119 
3120 		if (file_list_created)
3121 			path_list_free (file_list);
3122 
3123 		return;
3124 	}
3125 
3126 	/* .. else we have to implement the unsupported options. */
3127 
3128 	move_to_dest_dir = (use_base_dir
3129 			    || ((junk_paths
3130 				 && ! archive->command->propExtractCanJunkPaths)));
3131 
3132 	if (extract_all && ! file_list_created) {
3133 		guint i;
3134 
3135 		file_list = NULL;
3136 		for (i = 0; i < archive->command->files->len; i++) {
3137 			FileData *fdata = g_ptr_array_index (archive->command->files, i);
3138 			file_list = g_list_prepend (file_list, g_strdup (fdata->original_path));
3139 		}
3140 
3141 		file_list_created = TRUE;
3142 	}
3143 
3144 	filtered = NULL;
3145 	for (scan = file_list; scan; scan = scan->next) {
3146 		FileData   *fdata;
3147 		char       *archive_list_filename = scan->data;
3148 		char        dest_filename[4096];
3149 		const char *filename;
3150 
3151 		fdata = find_file_in_archive (archive, archive_list_filename);
3152 
3153 		if (fdata == NULL)
3154 			continue;
3155 
3156 		if (archive_type_has_issues_extracting_non_empty_folders (archive)
3157 		    && fdata->dir
3158 		    && file_list_contains_files_in_this_dir (file_list, archive_list_filename))
3159 			continue;
3160 
3161 		/* get the destination file path. */
3162 
3163 		if (! junk_paths)
3164 			filename = archive_list_filename;
3165 		else
3166 			filename = file_name_from_path (archive_list_filename);
3167 
3168 		if ((destination[strlen (destination) - 1] == '/')
3169 		    || (filename[0] == '/'))
3170 			sprintf (dest_filename, "%s%s", destination, filename);
3171 		else
3172 			sprintf (dest_filename, "%s/%s", destination, filename);
3173 
3174 		/*debug (DEBUG_INFO, "-> %s\n", dest_filename);*/
3175 
3176 		/**/
3177 
3178 		if (! archive->command->propExtractCanSkipOlder
3179 		    && skip_older
3180 		    && g_file_test (dest_filename, G_FILE_TEST_EXISTS)
3181 		    && (fdata->modified < get_file_mtime_for_path (dest_filename)))
3182 			continue;
3183 
3184 		if (! archive->command->propExtractCanAvoidOverwrite
3185 		    && ! overwrite
3186 		    && g_file_test (dest_filename, G_FILE_TEST_EXISTS))
3187 			continue;
3188 
3189 		filtered = g_list_prepend (filtered, fdata->original_path);
3190 	}
3191 
3192 	if (filtered == NULL) {
3193 		/* all files got filtered, do nothing. */
3194 		debug (DEBUG_INFO, "All files got filtered, nothing to do.\n");
3195 
3196 		if (extract_all)
3197 			path_list_free (file_list);
3198 		return;
3199 	}
3200 
3201 	if (move_to_dest_dir) {
3202 		char *temp_dir;
3203 
3204 		temp_dir = get_temp_work_dir (destination);
3205 		extract_from_archive (archive,
3206 				      filtered,
3207 				      temp_dir,
3208 				      overwrite,
3209 				      skip_older,
3210 				      junk_paths,
3211 				      password);
3212 
3213 		if (use_base_dir) {
3214 			GList *tmp_list = compute_list_base_path (base_dir, filtered, junk_paths, archive->command->propExtractCanJunkPaths);
3215 			g_list_free (filtered);
3216 			filtered = tmp_list;
3217 		}
3218 
3219 		move_files_in_chunks (archive,
3220 				      filtered,
3221 				      temp_dir,
3222 				      destination,
3223 				      overwrite);
3224 
3225 		/* remove the temp dir. */
3226 
3227 		fr_process_begin_command (archive->process, "rm");
3228 		fr_process_add_arg (archive->process, "-rf");
3229 		fr_process_add_arg (archive->process, temp_dir);
3230 		fr_process_end_command (archive->process);
3231 
3232 		g_free (temp_dir);
3233 	}
3234 	else
3235 		extract_from_archive (archive,
3236 				      filtered,
3237 				      destination,
3238 				      overwrite,
3239 				      skip_older,
3240 				      junk_paths,
3241 				      password);
3242 
3243 	if (filtered != NULL)
3244 		g_list_free (filtered);
3245 	if (file_list_created)
3246 		path_list_free (file_list);
3247 }
3248 
3249 
3250 void
fr_archive_extract(FrArchive * archive,GList * file_list,const char * destination,const char * base_dir,gboolean skip_older,gboolean overwrite,gboolean junk_paths,const char * password)3251 fr_archive_extract (FrArchive  *archive,
3252 		    GList      *file_list,
3253 		    const char *destination,
3254 		    const char *base_dir,
3255 		    gboolean    skip_older,
3256 		    gboolean    overwrite,
3257 		    gboolean    junk_paths,
3258 		    const char *password)
3259 {
3260 	g_free (archive->priv->extraction_destination);
3261 	archive->priv->extraction_destination = g_strdup (destination);
3262 
3263 	g_free (archive->priv->temp_extraction_dir);
3264 	archive->priv->temp_extraction_dir = NULL;
3265 
3266 	archive->priv->remote_extraction = ! uri_is_local (destination);
3267 	if (archive->priv->remote_extraction) {
3268 		archive->priv->temp_extraction_dir = get_temp_work_dir (NULL);
3269 		fr_archive_extract_to_local (archive,
3270 					     file_list,
3271 					     archive->priv->temp_extraction_dir,
3272 					     base_dir,
3273 					     skip_older,
3274 					     overwrite,
3275 					     junk_paths,
3276 					     password);
3277 	}
3278 	else {
3279 		char *local_destination;
3280 
3281 		local_destination = g_filename_from_uri (destination, NULL, NULL);
3282 		fr_archive_extract_to_local (archive,
3283 					     file_list,
3284 					     local_destination,
3285 					     base_dir,
3286 					     skip_older,
3287 					     overwrite,
3288 					     junk_paths,
3289 					     password);
3290 		g_free (local_destination);
3291 	}
3292 }
3293 
3294 
3295 static char *
get_desired_destination_for_archive(GFile * file)3296 get_desired_destination_for_archive (GFile *file)
3297 {
3298 	GFile      *directory;
3299 	char       *directory_uri;
3300 	char       *name;
3301 	const char *ext;
3302 	char       *new_name;
3303 	char       *new_name_escaped;
3304 	char       *desired_destination = NULL;
3305 
3306 	directory = g_file_get_parent (file);
3307 	directory_uri = g_file_get_uri (directory);
3308 
3309 	name = g_file_get_basename (file);
3310 	ext = get_archive_filename_extension (name);
3311 	if (ext == NULL)
3312 		/* if no extension is present add a suffix to the name... */
3313 		new_name = g_strconcat (name, "_FILES", NULL);
3314 	else
3315 		/* ...else use the name without the extension */
3316 		new_name = g_strndup (name, strlen (name) - strlen (ext));
3317 	new_name_escaped = g_uri_escape_string (new_name, "", FALSE);
3318 
3319 	desired_destination = g_strconcat (directory_uri, "/", new_name_escaped, NULL);
3320 
3321 	g_free (new_name_escaped);
3322 	g_free (new_name);
3323 	g_free (name);
3324 	g_free (directory_uri);
3325 	g_object_unref (directory);
3326 
3327 	return desired_destination;
3328 }
3329 
3330 
3331 static char *
get_extract_here_destination(GFile * file,GError ** error)3332 get_extract_here_destination (GFile   *file,
3333 			      GError **error)
3334 {
3335 	char  *desired_destination;
3336 	char  *destination = NULL;
3337 	int    n = 1;
3338 
3339 	desired_destination = get_desired_destination_for_archive (file);
3340 	do {
3341 		GFile *directory;
3342 
3343 		*error = NULL;
3344 
3345 		g_free (destination);
3346 		if (n == 1)
3347 			destination = g_strdup (desired_destination);
3348 		else
3349 			destination = g_strdup_printf ("%s%%20(%d)", desired_destination, n);
3350 
3351 		directory = g_file_new_for_uri (destination);
3352 		g_file_make_directory (directory, NULL, error);
3353 		g_object_unref (directory);
3354 
3355 		n++;
3356 	} while (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_EXISTS));
3357 
3358 	g_free (desired_destination);
3359 
3360 	if (*error != NULL) {
3361 		g_warning ("could not create destination folder: %s\n", (*error)->message);
3362 		g_free (destination);
3363 		destination = NULL;
3364 	}
3365 
3366 	return destination;
3367 }
3368 
3369 
3370 gboolean
fr_archive_extract_here(FrArchive * archive,gboolean skip_older,gboolean overwrite,gboolean junk_path,const char * password)3371 fr_archive_extract_here (FrArchive  *archive,
3372 			 gboolean    skip_older,
3373 			 gboolean    overwrite,
3374 			 gboolean    junk_path,
3375 			 const char *password)
3376 {
3377 	char   *destination;
3378 	GError *error = NULL;
3379 
3380 	destination = get_extract_here_destination (archive->file, &error);
3381 	if (error != NULL) {
3382 		fr_archive_action_completed (archive,
3383 					     FR_ACTION_EXTRACTING_FILES,
3384 					     FR_PROC_ERROR_GENERIC,
3385 					     error->message);
3386 		g_clear_error (&error);
3387 		return FALSE;
3388 	}
3389 
3390 	archive->priv->extract_here = TRUE;
3391 	fr_archive_extract (archive,
3392 			    NULL,
3393 			    destination,
3394 			    NULL,
3395 			    skip_older,
3396 			    overwrite,
3397 			    junk_path,
3398 			    password);
3399 
3400 	g_free (destination);
3401 
3402 	return TRUE;
3403 }
3404 
3405 
3406 const char *
fr_archive_get_last_extraction_destination(FrArchive * archive)3407 fr_archive_get_last_extraction_destination (FrArchive *archive)
3408 {
3409 	return archive->priv->extraction_destination;
3410 }
3411 
3412 
3413 void
fr_archive_test(FrArchive * archive,const char * password)3414 fr_archive_test (FrArchive  *archive,
3415 		 const char *password)
3416 {
3417 	fr_archive_stoppable (archive, TRUE);
3418 
3419 	g_object_set (archive->command,
3420 		      "file", archive->local_copy,
3421 		      "password", password,
3422 		      NULL);
3423 	fr_process_clear (archive->process);
3424 	fr_command_set_n_files (archive->command, 0);
3425 	fr_command_test (archive->command);
3426 	fr_process_start (archive->process);
3427 }
3428 
3429 
3430 gboolean
uri_is_archive(const char * uri)3431 uri_is_archive (const char *uri)
3432 {
3433 	GFile      *file;
3434 	const char *mime_type;
3435 	gboolean    is_archive = FALSE;
3436 
3437 	file = g_file_new_for_uri (uri);
3438 	mime_type = get_mime_type_from_magic_numbers (file);
3439 	if (mime_type == NULL)
3440 		mime_type = get_mime_type_from_content (file);
3441 	if (mime_type == NULL)
3442 		mime_type = get_mime_type_from_filename (file);
3443 
3444 	if (mime_type != NULL) {
3445 		int i;
3446 
3447 		for (i = 0; mime_type_desc[i].mime_type != NULL; i++) {
3448 			if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) {
3449 				is_archive = TRUE;
3450 				break;
3451 			}
3452 		}
3453 	}
3454 	g_object_unref (file);
3455 
3456 	return is_archive;
3457 }
3458