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