1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /*
3 * Copyright (C) 2017, Bastien Nocera <hadess@hadess.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "ev-archive.h"
22
23 #include <archive.h>
24 #include <archive_entry.h>
25 #include <unarr/unarr.h>
26 #include <gio/gio.h>
27
28 #define BUFFER_SIZE (64 * 1024)
29
30 struct _EvArchive {
31 GObject parent_instance;
32 EvArchiveType type;
33
34 /* libarchive */
35 struct archive *libar;
36 struct archive_entry *libar_entry;
37
38 /* unarr */
39 ar_stream *unarr_stream;
40 ar_archive *unarr;
41 };
42
43 G_DEFINE_TYPE(EvArchive, ev_archive, G_TYPE_OBJECT);
44
45 static void
ev_archive_finalize(GObject * object)46 ev_archive_finalize (GObject *object)
47 {
48 EvArchive *archive = EV_ARCHIVE (object);
49
50 switch (archive->type) {
51 case EV_ARCHIVE_TYPE_RAR:
52 g_clear_pointer (&archive->unarr, ar_close_archive);
53 g_clear_pointer (&archive->unarr_stream, ar_close);
54 break;
55 case EV_ARCHIVE_TYPE_ZIP:
56 case EV_ARCHIVE_TYPE_7Z:
57 case EV_ARCHIVE_TYPE_TAR:
58 case EV_ARCHIVE_TYPE_RAR5:
59 g_clear_pointer (&archive->libar, archive_free);
60 break;
61 default:
62 break;
63 }
64
65 G_OBJECT_CLASS (ev_archive_parent_class)->finalize (object);
66 }
67
68 static void
ev_archive_class_init(EvArchiveClass * klass)69 ev_archive_class_init (EvArchiveClass *klass)
70 {
71 GObjectClass *object_class = (GObjectClass *) klass;
72
73 object_class->finalize = ev_archive_finalize;
74 }
75
76 EvArchive *
ev_archive_new(void)77 ev_archive_new (void)
78 {
79 return g_object_new (EV_TYPE_ARCHIVE, NULL);
80 }
81
82 static void
libarchive_set_archive_type(EvArchive * archive,EvArchiveType archive_type)83 libarchive_set_archive_type (EvArchive *archive,
84 EvArchiveType archive_type)
85 {
86 archive->type = archive_type;
87 archive->libar = archive_read_new ();
88
89 if (archive_type == EV_ARCHIVE_TYPE_ZIP)
90 archive_read_support_format_zip (archive->libar);
91 else if (archive_type == EV_ARCHIVE_TYPE_7Z)
92 archive_read_support_format_7zip (archive->libar);
93 else if (archive_type == EV_ARCHIVE_TYPE_TAR)
94 archive_read_support_format_tar (archive->libar);
95 else if (archive_type == EV_ARCHIVE_TYPE_RAR5)
96 archive_read_support_format_rar5 (archive->libar);
97 else
98 g_assert_not_reached ();
99 }
100
101 EvArchiveType
ev_archive_get_archive_type(EvArchive * archive)102 ev_archive_get_archive_type (EvArchive *archive)
103 {
104 g_return_val_if_fail (EV_IS_ARCHIVE (archive), EV_ARCHIVE_TYPE_NONE);
105
106 return archive->type;
107 }
108
109 gboolean
ev_archive_set_archive_type(EvArchive * archive,EvArchiveType archive_type)110 ev_archive_set_archive_type (EvArchive *archive,
111 EvArchiveType archive_type)
112 {
113 g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
114 g_return_val_if_fail (archive->type == EV_ARCHIVE_TYPE_NONE, FALSE);
115
116 switch (archive_type) {
117 case EV_ARCHIVE_TYPE_RAR:
118 archive->type = archive_type;
119 break;
120 case EV_ARCHIVE_TYPE_ZIP:
121 case EV_ARCHIVE_TYPE_7Z:
122 case EV_ARCHIVE_TYPE_TAR:
123 case EV_ARCHIVE_TYPE_RAR5:
124 libarchive_set_archive_type (archive, archive_type);
125 break;
126 default:
127 g_assert_not_reached ();
128 }
129
130 return TRUE;
131 }
132
133 gboolean
ev_archive_open_filename(EvArchive * archive,const char * path,GError ** error)134 ev_archive_open_filename (EvArchive *archive,
135 const char *path,
136 GError **error)
137 {
138 int r;
139 ArArchiveError code;
140
141 g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
142 g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
143 g_return_val_if_fail (path != NULL, FALSE);
144
145 switch (archive->type) {
146 case EV_ARCHIVE_TYPE_NONE:
147 g_assert_not_reached ();
148 case EV_ARCHIVE_TYPE_RAR:
149 archive->unarr_stream = ar_open_file (path);
150 if (archive->unarr_stream == NULL) {
151 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
152 "Error opening archive");
153 return FALSE;
154 }
155 archive->unarr = ar_open_rar_archive_with_error (archive->unarr_stream, &code);
156 if (archive->unarr == NULL) {
157 g_clear_pointer (&archive->unarr_stream, ar_close);
158 if (code == AR_ARCHIVE_ERROR_RAR5) {
159 libarchive_set_archive_type (archive, EV_ARCHIVE_TYPE_RAR5);
160 return ev_archive_open_filename (archive, path, error);
161 }
162 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
163 "Error opening RAR archive");
164 return FALSE;
165 }
166 return TRUE;
167 case EV_ARCHIVE_TYPE_ZIP:
168 case EV_ARCHIVE_TYPE_7Z:
169 case EV_ARCHIVE_TYPE_TAR:
170 case EV_ARCHIVE_TYPE_RAR5:
171 r = archive_read_open_filename (archive->libar, path, BUFFER_SIZE);
172 if (r != ARCHIVE_OK) {
173 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
174 "Error opening archive: %s", archive_error_string (archive->libar));
175 return FALSE;
176 }
177 return TRUE;
178 }
179
180 return FALSE;
181 }
182
183 static gboolean
libarchive_read_next_header(EvArchive * archive,GError ** error)184 libarchive_read_next_header (EvArchive *archive,
185 GError **error)
186 {
187 while (1) {
188 int r;
189
190 r = archive_read_next_header (archive->libar, &archive->libar_entry);
191 if (r != ARCHIVE_OK) {
192 if (r != ARCHIVE_EOF)
193 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
194 "Error reading archive: %s", archive_error_string (archive->libar));
195 return FALSE;
196 }
197
198 if (archive_entry_filetype (archive->libar_entry) != AE_IFREG) {
199 g_debug ("Skipping '%s' as it's not a regular file",
200 archive_entry_pathname (archive->libar_entry));
201 continue;
202 }
203
204 g_debug ("At header for file '%s'", archive_entry_pathname (archive->libar_entry));
205
206 break;
207 }
208
209 return TRUE;
210 }
211
212 gboolean
ev_archive_read_next_header(EvArchive * archive,GError ** error)213 ev_archive_read_next_header (EvArchive *archive,
214 GError **error)
215 {
216 g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
217 g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
218
219 switch (archive->type) {
220 case EV_ARCHIVE_TYPE_NONE:
221 g_assert_not_reached ();
222 case EV_ARCHIVE_TYPE_RAR:
223 return ar_parse_entry (archive->unarr);
224 case EV_ARCHIVE_TYPE_ZIP:
225 case EV_ARCHIVE_TYPE_7Z:
226 case EV_ARCHIVE_TYPE_TAR:
227 case EV_ARCHIVE_TYPE_RAR5:
228 return libarchive_read_next_header (archive, error);
229 }
230
231 return FALSE;
232 }
233
234 const char *
ev_archive_get_entry_pathname(EvArchive * archive)235 ev_archive_get_entry_pathname (EvArchive *archive)
236 {
237 g_return_val_if_fail (EV_IS_ARCHIVE (archive), NULL);
238 g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, NULL);
239
240 switch (archive->type) {
241 case EV_ARCHIVE_TYPE_NONE:
242 g_assert_not_reached ();
243 case EV_ARCHIVE_TYPE_RAR:
244 g_return_val_if_fail (archive->unarr != NULL, NULL);
245 return ar_entry_get_name (archive->unarr);
246 case EV_ARCHIVE_TYPE_ZIP:
247 case EV_ARCHIVE_TYPE_7Z:
248 case EV_ARCHIVE_TYPE_TAR:
249 case EV_ARCHIVE_TYPE_RAR5:
250 g_return_val_if_fail (archive->libar_entry != NULL, NULL);
251 return archive_entry_pathname (archive->libar_entry);
252 }
253
254 return NULL;
255 }
256
257 gint64
ev_archive_get_entry_size(EvArchive * archive)258 ev_archive_get_entry_size (EvArchive *archive)
259 {
260 g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1);
261 g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1);
262
263 switch (archive->type) {
264 case EV_ARCHIVE_TYPE_RAR:
265 g_return_val_if_fail (archive->unarr != NULL, -1);
266 return ar_entry_get_size (archive->unarr);
267 case EV_ARCHIVE_TYPE_NONE:
268 g_assert_not_reached ();
269 case EV_ARCHIVE_TYPE_ZIP:
270 case EV_ARCHIVE_TYPE_7Z:
271 case EV_ARCHIVE_TYPE_TAR:
272 case EV_ARCHIVE_TYPE_RAR5:
273 g_return_val_if_fail (archive->libar_entry != NULL, -1);
274 return archive_entry_size (archive->libar_entry);
275 }
276
277 return -1;
278 }
279
280 gboolean
ev_archive_get_entry_is_encrypted(EvArchive * archive)281 ev_archive_get_entry_is_encrypted (EvArchive *archive)
282 {
283 g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
284 g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
285
286 switch (archive->type) {
287 case EV_ARCHIVE_TYPE_RAR:
288 g_return_val_if_fail (archive->unarr != NULL, FALSE);
289 /* password-protected RAR is not even detected right now */
290 return FALSE;
291 case EV_ARCHIVE_TYPE_NONE:
292 g_assert_not_reached ();
293 case EV_ARCHIVE_TYPE_ZIP:
294 case EV_ARCHIVE_TYPE_7Z:
295 case EV_ARCHIVE_TYPE_TAR:
296 case EV_ARCHIVE_TYPE_RAR5:
297 g_return_val_if_fail (archive->libar_entry != NULL, -1);
298 return archive_entry_is_encrypted (archive->libar_entry);
299 }
300
301 return FALSE;
302 }
303
304 gssize
ev_archive_read_data(EvArchive * archive,void * buf,gsize count,GError ** error)305 ev_archive_read_data (EvArchive *archive,
306 void *buf,
307 gsize count,
308 GError **error)
309 {
310 gssize r = -1;
311
312 g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1);
313 g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1);
314
315 switch (archive->type) {
316 case EV_ARCHIVE_TYPE_RAR:
317 g_return_val_if_fail (archive->unarr != NULL, -1);
318 if (!ar_entry_uncompress (archive->unarr, buf, count)) {
319 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
320 "Failed to decompress RAR data");
321 return -1;
322 }
323 r = count;
324 break;
325 case EV_ARCHIVE_TYPE_NONE:
326 g_assert_not_reached ();
327 case EV_ARCHIVE_TYPE_ZIP:
328 case EV_ARCHIVE_TYPE_7Z:
329 case EV_ARCHIVE_TYPE_TAR:
330 case EV_ARCHIVE_TYPE_RAR5:
331 g_return_val_if_fail (archive->libar_entry != NULL, -1);
332 r = archive_read_data (archive->libar, buf, count);
333 if (r < 0) {
334 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
335 "Failed to decompress data: %s", archive_error_string (archive->libar));
336 }
337 break;
338 }
339
340 return r;
341 }
342
343 void
ev_archive_reset(EvArchive * archive)344 ev_archive_reset (EvArchive *archive)
345 {
346 g_return_if_fail (EV_IS_ARCHIVE (archive));
347 g_return_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE);
348
349 switch (archive->type) {
350 case EV_ARCHIVE_TYPE_RAR:
351 g_clear_pointer (&archive->unarr, ar_close_archive);
352 g_clear_pointer (&archive->unarr_stream, ar_close);
353 break;
354 case EV_ARCHIVE_TYPE_ZIP:
355 case EV_ARCHIVE_TYPE_7Z:
356 case EV_ARCHIVE_TYPE_TAR:
357 case EV_ARCHIVE_TYPE_RAR5:
358 g_clear_pointer (&archive->libar, archive_free);
359 libarchive_set_archive_type (archive, archive->type);
360 break;
361 default:
362 g_assert_not_reached ();
363 }
364 }
365
366 static void
ev_archive_init(EvArchive * archive)367 ev_archive_init (EvArchive *archive)
368 {
369 }
370