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