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