1 /*
2 * Copyright (C) 2008 Giuseppe Torelli - <colossus73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA.
18 */
19
20 #include <string.h>
21 #include <glib/gstdio.h>
22 #include "7zip.h"
23 #include "gzip_et_al.h"
24 #include "interface.h"
25 #include "main.h"
26 #include "string_utils.h"
27 #include "support.h"
28 #include "window.h"
29
30 #define INDEX (archive->type == XARCHIVETYPE_RAR ? (archive->tag == 5 ? 1 : 0) : 0)
31
32 static gboolean data_line, encrypted, last_line;
33
xa_7zip_seek_position(const gchar * filename,GIOChannel ** file,gint64 offset,GSeekType type)34 static void xa_7zip_seek_position (const gchar *filename, GIOChannel **file, gint64 offset, GSeekType type)
35 {
36 gchar byte;
37
38 g_io_channel_seek_position(*file, offset, type, NULL);
39
40 /* check whether it's a volume. i.e. whether offset is beyond the end of the file */
41 if (g_io_channel_read_chars(*file, &byte, sizeof(byte), NULL, NULL) == G_IO_STATUS_NORMAL)
42 /* doesn't seem so - back to requested position */
43 g_io_channel_seek_position(*file, -(gint64) sizeof(byte), G_SEEK_CUR, NULL);
44 else /* find the volume the offset is pointing to */
45 {
46 guint64 position, volsizes = 0;
47 gchar *fvname;
48 size_t ext;
49 guint i;
50 GStatBuf st;
51 GIOChannel *fnew;
52
53 if (!g_str_has_suffix(filename, ".001"))
54 return;
55
56 position = 12 + 8 + (guint64) offset; // absolute position
57
58 fvname = g_strdup(filename);
59 ext = strlen(fvname) - 4;
60
61 /* check volumes ... */
62 for (i = 1; i < 1000; i++)
63 {
64 fvname[ext] = 0;
65 sprintf(fvname, "%s.%03u", fvname, i);
66
67 if (!g_file_test(fvname, G_FILE_TEST_EXISTS) || (g_stat(fvname, &st) != 0))
68 break;
69
70 volsizes += (guint64) st.st_size;
71
72 /* ... up to the one we're looking for */
73 if (volsizes > position)
74 {
75 fnew = g_io_channel_new_file(fvname, "r", NULL);
76
77 if (!fnew)
78 break;
79
80 /* switch to volume */
81
82 g_io_channel_shutdown(*file, FALSE, NULL);
83
84 *file = fnew;
85
86 g_io_channel_set_encoding(*file, NULL, NULL);
87 g_io_channel_seek_position(*file, position - (volsizes - (guint64) st.st_size), G_SEEK_SET, NULL);
88
89 break;
90 }
91 }
92
93 g_free(fvname);
94 }
95 }
96
xa_7zip_uint64_skip(GIOChannel * file)97 static void xa_7zip_uint64_skip (GIOChannel *file)
98 {
99 gchar first, byte;
100 guchar mask = 0x80;
101
102 g_io_channel_read_chars(file, &first, sizeof(first), NULL, NULL);
103
104 /* 7z uint64 is specially encoded */
105 while ((mask > 1) && (first & mask))
106 {
107 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
108 mask >>= 1;
109 }
110 }
111
is_encrypted(XArchive * archive)112 static gboolean is_encrypted (XArchive *archive)
113 {
114 gchar *command, *output;
115 gboolean result;
116
117 command = g_strconcat(archiver[archive->type].program[INDEX], " l -slt ", archive->path[0], NULL);
118 g_spawn_command_line_sync(command, &output, NULL, NULL, NULL);
119
120 result = (strstr(output, "\nEncrypted = +\n") != NULL);
121
122 g_free(output);
123 g_free(command);
124
125 return result;
126 }
127
is7zip_mhe(const gchar * filename)128 gboolean is7zip_mhe (const gchar *filename)
129 {
130 GIOChannel *file;
131 guint i;
132 gchar byte;
133 guint64 offset = 0;
134 gboolean result = FALSE;
135
136 file = g_io_channel_new_file(filename, "r", NULL);
137
138 if (file)
139 {
140 g_io_channel_set_encoding(file, NULL, NULL);
141 g_io_channel_set_buffered(file, FALSE);
142
143 /* skip signature, version and header CRC32 */
144 g_io_channel_seek_position(file, 12, G_SEEK_SET, NULL);
145
146 /* next header offset (uint64_t) */
147 for (i = 0; i < 8; i++)
148 {
149 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
150 offset |= (guint64) (guchar) byte << (8 * i);
151 }
152
153 /* skip next header size and CRC32 */
154 xa_7zip_seek_position(filename, &file, 12 + offset, G_SEEK_CUR);
155
156 /* header info */
157 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
158
159 /* encoded header */
160 if (byte == 0x17)
161 {
162 /* streams info */
163 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
164
165 /* pack info */
166 if (byte == 0x06)
167 {
168 /* skip pack position */
169 xa_7zip_uint64_skip(file);
170 /* skip number of pack streams */
171 xa_7zip_uint64_skip(file);
172
173 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
174
175 /* size info */
176 if (byte == 0x09)
177 {
178 /* skip unpack sizes */
179 xa_7zip_uint64_skip(file);
180
181 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
182
183 /* pack info end */
184 if (byte == 0x00)
185 {
186 /* coders info */
187 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
188
189 /* unpack info */
190 if (byte == 0x07)
191 {
192 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
193
194 /* folder */
195 if (byte == 0x0b)
196 {
197 /* skip number of folders */
198 xa_7zip_uint64_skip(file);
199
200 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
201
202 /* coders info end */
203 if (byte == 0x00)
204 {
205 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
206
207 /* header or archive properties */
208 if (byte == 0x01 || byte == 0x02)
209 {
210 /* codec id size */
211 g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);
212
213 if ((byte & 0x0f) == 4)
214 {
215 gchar id[4];
216
217 /* codec id */
218 g_io_channel_read_chars(file, id, sizeof(id), NULL, NULL);
219
220 /* check for id of 7zAES */
221 result = (memcmp(id, "\x06\xf1\x07\x01", 4) == 0);
222 }
223 }
224 }
225 }
226 }
227 }
228 }
229 }
230 }
231
232 g_io_channel_shutdown(file, FALSE, NULL);
233 }
234
235 return result;
236 }
237
238 /* it can handle other archive types as well */
xa_7zip_ask(XArchive * archive)239 void xa_7zip_ask (XArchive *archive)
240 {
241 archive->can_test = TRUE;
242 archive->can_extract = TRUE;
243 archive->can_add = archiver[archive->type].is_compressor;
244 archive->can_delete = (archiver[archive->type].is_compressor && !SINGLE_FILE_COMPRESSOR(archive));
245 archive->can_sfx = (archive->type == XARCHIVETYPE_7ZIP);
246 archive->can_password = (archive->type == XARCHIVETYPE_7ZIP);
247 archive->can_full_path[0] = TRUE;
248 archive->can_overwrite = TRUE;
249 archive->can_update[1] = archiver[archive->type].is_compressor;
250 archive->can_freshen[1] = (archiver[archive->type].is_compressor && !SINGLE_FILE_COMPRESSOR(archive));
251 archive->can_move = archiver[archive->type].is_compressor;
252 archive->can_solid = (archive->type == XARCHIVETYPE_7ZIP);
253 }
254
xa_7zip_password_str(XArchive * archive)255 static gchar *xa_7zip_password_str (XArchive *archive)
256 {
257 if (archive->password)
258 return g_strconcat(" -p", archive->password, NULL);
259 else
260 return g_strdup("");
261 }
262
xa_7zip_parse_output(gchar * line,XArchive * archive)263 static void xa_7zip_parse_output (gchar *line, XArchive *archive)
264 {
265 XEntry *entry;
266 gchar *filename;
267 gpointer item[5];
268 gint linesize = 0,a = 0;
269 gboolean dir;
270
271 if (last_line)
272 return;
273
274 if (!data_line)
275 {
276 if (strncmp(line, "Method = ", 9) == 0 && strstr(line, "7zAES"))
277 {
278 encrypted = TRUE;
279 archive->has_password = TRUE;
280 }
281
282 if (strncmp(line, "-----", 5) == 0)
283 {
284 data_line = TRUE;
285 return;
286 }
287 return;
288 }
289 if (line[0] == '-')
290 {
291 last_line = TRUE;
292 return;
293 }
294
295 linesize = strlen(line);
296
297 /* Date */
298 line[10] = '\0';
299 item[2] = line;
300
301 /* Time */
302 line[19] = '\0';
303 item[3] = line + 11;
304
305 /* Permissions */
306 line[25] = '\0';
307 item[4] = line + 20;
308
309 dir = (*(char *) item[4] == 'D');
310
311 /* Size */
312 for(a=26; a < linesize; ++a)
313 if(line[a] >= '0' && line[a] <= '9')
314 break;
315
316 line[38] = '\0';
317 item[0] = line + a;
318
319 /* Compressed */
320 /* Is this item solid? */
321 if (line[50] == ' ')
322 {
323 line[linesize-1] = '\0';
324 item[1] = "0";
325 }
326 else
327 {
328 for(a=39; a < linesize; ++a)
329 if(line[a] >= '0' && line[a] <= '9')
330 break;
331 line[51] = '\0';
332 item[1] = line + a;
333 line[linesize-1] = '\0';
334 }
335
336 filename = g_strdup(line + 53);
337 entry = xa_set_archive_entries_for_each_row(archive, filename, item);
338
339 if (entry)
340 {
341 if (dir)
342 entry->is_dir = TRUE;
343
344 entry->is_encrypted = encrypted;
345
346 if (!entry->is_dir)
347 archive->files++;
348
349 archive->files_size += g_ascii_strtoull(item[0], NULL, 0);
350 }
351
352 g_free(filename);
353 }
354
xa_7zip_list(XArchive * archive)355 void xa_7zip_list (XArchive *archive)
356 {
357 const GType types[] = {GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER};
358 const gchar *titles[] = {_("Original Size"), _("Compressed"), _("Date"), _("Time"), _("Attributes")};
359 gchar *password_str, *command;
360 guint i;
361
362 if (!archive->has_password)
363 {
364 if (archive->type == XARCHIVETYPE_7ZIP)
365 archive->has_password = is7zip_mhe(archive->path[0]);
366 else
367 archive->has_password = is_encrypted(archive);
368 }
369
370 if ((archive->type == XARCHIVETYPE_7ZIP) && archive->has_password)
371 if (!xa_check_password(archive))
372 return;
373
374 /* a single file compressor archive is no longer new and empty now */
375 archive->can_add = (archiver[archive->type].is_compressor && !SINGLE_FILE_COMPRESSOR(archive));
376
377 data_line = FALSE;
378 last_line = FALSE;
379 encrypted = archive->has_password;
380
381 password_str = xa_7zip_password_str(archive);
382
383 if (archive->type == XARCHIVETYPE_CPIO && archive->tag == 'E')
384 command = g_strconcat("sh -c \"echo ", _("Unsupported binary format!"), " >&2; exit 1\"", NULL);
385 else
386 command = g_strconcat(archiver[archive->type].program[INDEX], " l", password_str, " ", archive->path[1], NULL);
387
388 g_free(password_str);
389
390 archive->files_size = 0;
391 archive->files = 0;
392 archive->parse_output = xa_7zip_parse_output;
393 xa_spawn_async_process (archive,command);
394 g_free ( command );
395
396 archive->columns = 8;
397 archive->size_column = 2;
398 archive->column_types = g_malloc0(sizeof(types));
399
400 for (i = 0; i < archive->columns; i++)
401 archive->column_types[i] = types[i];
402
403 xa_create_liststore(archive, titles);
404 }
405
xa_7zip_test(XArchive * archive)406 void xa_7zip_test (XArchive *archive)
407 {
408 gchar *password_str, *command;
409
410 password_str = xa_7zip_password_str(archive);
411 command = g_strconcat(archiver[archive->type].program[INDEX], " t", password_str, " -bd -y ", archive->path[1], NULL);
412 g_free(password_str);
413
414 xa_run_command(archive, command);
415 g_free(command);
416 }
417
418 /*
419 * Note: 7zip's wildcard handling (even with switch -spd) seems buggy.
420 * Everything is okay as long as no file name in the working directory
421 * matches. If there is a wildcard match, it asks "would you like to replace
422 * the existing file" and fails, i.e. extraction of files named '?' or '*'
423 * always fails (even in an empty directory) and extraction of a file named
424 * 't*' fails if there is already a file name 'test', for example, in the
425 * extraction path (while extraction would succeed otherwise).
426 */
427
xa_7zip_extract(XArchive * archive,GSList * file_list)428 gboolean xa_7zip_extract (XArchive *archive, GSList *file_list)
429 {
430 GString *files;
431 gchar *password_str, *command;
432 gboolean result;
433
434 files = xa_quote_filenames(file_list, NULL, TRUE);
435 password_str = xa_7zip_password_str(archive);
436 command = g_strconcat(archiver[archive->type].program[INDEX],
437 archive->do_full_path ? " x" : " e",
438 archive->do_overwrite ? " -aoa" : " -aos",
439 password_str, " -bd -spd -y ",
440 archive->path[1], files->str,
441 " -o", archive->extraction_dir, NULL);
442 g_free(password_str);
443 g_string_free(files,TRUE);
444
445 result = xa_run_command(archive, command);
446 g_free(command);
447
448 return result;
449 }
450
xa_7zip_add(XArchive * archive,GSList * file_list,gchar * compression)451 void xa_7zip_add (XArchive *archive, GSList *file_list, gchar *compression)
452 {
453 GString *files;
454 gchar *password_str, *solid, *command;
455
456 if (archive->location_path != NULL)
457 archive->child_dir = g_strdup(archive->working_dir);
458
459 if (!compression)
460 compression = "5";
461
462 files = xa_quote_filenames(file_list, NULL, TRUE);
463 password_str = xa_7zip_password_str(archive);
464 solid = g_strconcat(" -ms=", archive->do_solid ? "on" : "off", NULL);
465 command = g_strconcat(archiver[archive->type].program[0],
466 archive->do_update ? " u" : " a",
467 archive->do_freshen ? " -ur0w0x1z1" : "",
468 archive->do_move ? " -sdel" : "",
469 archive->type == XARCHIVETYPE_7ZIP ? solid : "",
470 " -mx=", compression,
471 password_str, " -bd -spd -y ",
472 archive->path[1], files->str, NULL);
473 g_free(solid);
474 g_free(password_str);
475 g_string_free(files,TRUE);
476
477 xa_run_command(archive, command);
478 g_free(command);
479 }
480
xa_7zip_delete(XArchive * archive,GSList * file_list)481 void xa_7zip_delete (XArchive *archive, GSList *file_list)
482 {
483 GString *files;
484 gchar *password_str, *command;
485
486 files = xa_quote_filenames(file_list, NULL, TRUE);
487 password_str = xa_7zip_password_str(archive);
488 command = g_strconcat(archiver[archive->type].program[0], " d", password_str, " -bd -spd -y ", archive->path[1], files->str, NULL);
489 g_free(password_str);
490 g_string_free(files,TRUE);
491
492 xa_run_command(archive, command);
493 g_free(command);
494 }
495