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 "rar.h"
22 #include "date_utils.h"
23 #include "interface.h"
24 #include "main.h"
25 #include "string_utils.h"
26 #include "support.h"
27 #include "window.h"
28 
29 int rar_version;
30 
31 static gboolean header_line, data_line, fname_line, last_line;
32 
xa_rar_check_version(gchar * path)33 void xa_rar_check_version (gchar *path)
34 {
35 	gchar *output, *id;
36 	gchar version;
37 
38 	g_spawn_command_line_sync(path, &output, NULL, NULL, NULL);
39 
40 	id = strstr(output, "\nRAR ");
41 
42 	if (!id)
43 		id = strstr(output, "\nUNRAR ");
44 
45 	if (id)
46 	{
47 		version = *(strchr(id, ' ') + 1);
48 
49 		if (version > '1' && version <= '9')
50 			rar_version = version - '0';
51 	}
52 
53 	g_free(output);
54 }
55 
xa_rar_ask(XArchive * archive)56 void xa_rar_ask (XArchive *archive)
57 {
58 	archive->can_test = TRUE;
59 	archive->can_extract = TRUE;
60 	archive->can_add = archiver[archive->type].is_compressor;
61 	archive->can_delete = archiver[archive->type].is_compressor;
62 	archive->can_sfx = archiver[archive->type].is_compressor;
63 	archive->can_password = archiver[archive->type].is_compressor;
64 	archive->can_full_path[0] = TRUE;
65 	archive->can_full_path[1] = archiver[archive->type].is_compressor;
66 	archive->can_touch = TRUE;
67 	archive->can_overwrite = TRUE;
68 	archive->can_update[0] = TRUE;
69 	archive->can_update[1] = archiver[archive->type].is_compressor;
70 	archive->can_freshen[0] = TRUE;
71 	archive->can_freshen[1] = archiver[archive->type].is_compressor;
72 	archive->can_move = archiver[archive->type].is_compressor;
73 	archive->can_solid = archiver[archive->type].is_compressor;
74 }
75 
xa_rar_password_str(XArchive * archive)76 static gchar *xa_rar_password_str (XArchive *archive)
77 {
78 	if (archive->password)
79 		return g_strconcat(" -p", archive->password, NULL);
80 	else
81 		return g_strdup("");
82 }
83 
xa_rar_parse_output(gchar * line,XArchive * archive)84 static void xa_rar_parse_output (gchar *line, XArchive *archive)
85 {
86 	XEntry *entry;
87 	gpointer item[9];
88 	unsigned short int i = 0;
89 	unsigned int linesize,n,a;
90 	gboolean dir = FALSE;
91 	static gboolean encrypted;
92 	static gchar *filename;
93 
94 	if (last_line)
95 		return;
96 
97 	if (!data_line)
98 	{
99 		if (!header_line)
100 		{
101 			if ((strncmp(line, "Solid ", 6) == 0 || strncmp(line, "SFX ", 4) == 0 ||
102 			     strncmp(line, "Volume ", 7) == 0 || strncmp(line, "Archive ", 8) == 0)
103 			     && strstr(line, archive->path[0]))
104 			{
105 				header_line = TRUE;
106 
107 				if (archive->comment)
108 				{
109 					if (archive->comment->len > 2)
110 					{
111 						archive->has_comment = TRUE;
112 						archive->comment = g_string_truncate(archive->comment, archive->comment->len - 2);
113 						archive->comment = g_string_erase(archive->comment, 0, 1);
114 					}
115 					else
116 					{
117 						g_string_free(archive->comment, TRUE);
118 						archive->comment = NULL;
119 					}
120 				}
121 			}
122 			else
123 			{
124 				if (!archive->comment)
125 					archive->comment = g_string_new("");
126 
127 				archive->comment = g_string_append(archive->comment, line);
128 			}
129 			return;
130 		}
131 		if (line[0] == '-')
132 		{
133 			data_line = TRUE;
134 			return;
135 		}
136 		return;
137 	}
138 
139 	if (!fname_line)
140 	{
141 		encrypted = FALSE;
142 		linesize = strlen(line);
143 		if(line[0] == '*')
144 		{
145 			archive->has_password = TRUE;
146 			encrypted = TRUE;
147 		}
148 		else if (line[0] == '-')
149 		{
150 			last_line = TRUE;
151 			return;
152 		}
153 		else if (line[0] != ' ')
154 			return;
155 		line[linesize - 1] = '\0';
156 		filename = g_strdup(line+1);
157 		fname_line = TRUE;
158 	}
159 	else
160 	{
161 		linesize = strlen(line);
162 		/* Size */
163 		for(n=0; n < linesize && line[n] == ' '; n++);
164 		a = n;
165 		for(; n < linesize && line[n] != ' '; n++);
166 		line[n]='\0';
167 		item[i] = line + a;
168 		i++;
169 		n++;
170 
171 		/* Compressed */
172 		for(; n < linesize && line[n] == ' '; n++);
173 		a = n;
174 		for(; n < linesize && line[n] != ' '; n++);
175 		line[n]='\0';
176 		item[i] = line + a;
177 		i++;
178 		n++;
179 
180 		/* Ratio */
181 		for(; n < linesize && line[n] == ' '; n++);
182 		a = n;
183 		for(; n < linesize && line[n] != ' '; n++);
184 		line[n] = '\0';
185 		item[i] = line + a;
186 		i++;
187 		n++;
188 
189 		/* Date */
190 		for(; n < linesize && line[n] == ' '; n++);
191 		a = n;
192 		for(; n < linesize && line[n] != ' '; n++);
193 		line[n] = '\0';
194 		item[i] = date_DD_MM_YY(line + a);
195 		i++;
196 		n++;
197 
198 		/* Time */
199 		for(; n < linesize && line[n] == ' '; n++);
200 		a = n;
201 		for(; n < linesize && line[n] != ' '; n++);
202 		line[n] = '\0';
203 		item[i] = line + a;
204 		i++;
205 		n++;
206 
207 		/* Permissions */
208 		for(; n < linesize && line[n] == ' '; n++);
209 		a = n;
210 		for(; n < linesize && line[n] != ' '; n++);
211 		line[n] = '\0';
212 		/* archive may originate from Unix or Windows type OS */
213 		if (*(line + a) == 'd' || *(line + a + 1) == 'D')
214 			dir = TRUE;
215 		item[i] = line + a;
216 		i++;
217 		n++;
218 
219 		/* CRC */
220 		for(; n < linesize && line[n] == ' '; n++);
221 		a = n;
222 		for(; n < linesize && line[n] != ' '; n++);
223 		line[n] = '\0';
224 		item[i] = line + a;
225 		i++;
226 		n++;
227 
228 		/* Method */
229 		for(; n < linesize && line[n] == ' '; n++);
230 		a = n;
231 		for(; n < linesize && line[n] != ' '; n++);
232 		line[n] = '\0';
233 		item[i] = line + a;
234 		i++;
235 		n++;
236 
237 		/* version */
238 		for(; n < linesize && line[n] == ' '; n++);
239 		a = n;
240 		for(; n < linesize && line[n] != ' ' && line[n] != '\n'; n++);
241 		line[n] = '\0';
242 		item[i] = line + a;
243 
244 		entry = xa_set_archive_entries_for_each_row(archive, filename, item);
245 
246 		if (entry)
247 		{
248 			if (dir)
249 				entry->is_dir = TRUE;
250 
251 			entry->is_encrypted = encrypted;
252 
253 			if (!entry->is_dir)
254 				archive->files++;
255 
256 			archive->files_size += g_ascii_strtoull(item[0], NULL, 0);
257 		}
258 
259 		g_free(filename);
260 		fname_line = FALSE;
261 	}
262 }
263 
xa_rar5_parse_output(gchar * line,XArchive * archive)264 static void xa_rar5_parse_output (gchar *line, XArchive *archive)
265 {
266 	XEntry *entry;
267 	gpointer item[7];
268 	unsigned short int i = 0;
269 	unsigned int linesize, n, a;
270 	gboolean encrypted = FALSE, dir = FALSE;
271 	static gchar *filename, *end;
272 
273 	if (last_line)
274 		return;
275 
276 	if (!data_line)
277 	{
278 		if (!header_line)
279 		{
280 			if ((strncmp(line, "Archive: ", 9) == 0) && strstr(line, archive->path[0]))
281 			{
282 				header_line = TRUE;
283 
284 				if (archive->comment)
285 				{
286 					if (archive->comment->len > 2)
287 					{
288 						archive->has_comment = TRUE;
289 						archive->comment = g_string_truncate(archive->comment, archive->comment->len - 2);
290 						archive->comment = g_string_erase(archive->comment, 0, 1);
291 					}
292 					else
293 					{
294 						g_string_free(archive->comment, TRUE);
295 						archive->comment = NULL;
296 					}
297 				}
298 			}
299 			else
300 			{
301 				if (!archive->comment)
302 					archive->comment = g_string_new("");
303 
304 				archive->comment = g_string_append(archive->comment, line);
305 			}
306 			return;
307 		}
308 		if (line[0] == '-')
309 		{
310 			data_line = TRUE;
311 			return;
312 		}
313 		return;
314 	}
315 
316 	linesize = strlen(line);
317 	line[linesize - 1] = '\0';
318 
319 	if(line[0] == '*')
320 	{
321 		archive->has_password = TRUE;
322 		encrypted = TRUE;
323 	}
324 	else if (line[0] == '-')
325 	{
326 		last_line = TRUE;
327 		return;
328 	}
329 
330 	/* Permissions */
331 	for (n = encrypted ? 1 : 0; n < linesize && line[n] == ' '; n++);
332 	a = n;
333 	for(; n < linesize && line[n] != ' '; n++);
334 	line[n] = '\0';
335 	/* archive may originate from Unix or Windows type OS */
336 	if (*(line + a) == 'd' || *(line + a + 3) == 'D')
337 		dir = TRUE;
338 	item[5] = line + a;
339 	n++;
340 
341 	/* Size */
342 	for(; n < linesize && line[n] == ' '; n++);
343 	a = n;
344 	for(; n < linesize && line[n] != ' '; n++);
345 	line[n]='\0';
346 	item[i] = line + a;
347 	i++;
348 	n++;
349 
350 	/* Compressed */
351 	for(; n < linesize && line[n] == ' '; n++);
352 	a = n;
353 	for(; n < linesize && line[n] != ' '; n++);
354 	line[n]='\0';
355 	item[i] = line + a;
356 	i++;
357 	n++;
358 
359 	/* Ratio */
360 	for(; n < linesize && line[n] == ' '; n++);
361 	a = n;
362 	for(; n < linesize && line[n] != ' '; n++);
363 	line[n] = '\0';
364 	item[i] = line + a;
365 	i++;
366 	n++;
367 
368 	/* Date */
369 	for(; n < linesize && line[n] == ' '; n++);
370 	a = n;
371 	for(; n < linesize && line[n] != ' '; n++);
372 	line[n] = '\0';
373 	item[i] = line + a;          // date is YYYY-MM-DD since v5.30
374 	if (strlen(item[i]) != 10)   // and was DD-MM-YY before
375 		item[i] = date_DD_MM_YY(item[i]);
376 	i++;
377 	n++;
378 
379 	/* Time */
380 	for(; n < linesize && line[n] == ' '; n++);
381 	a = n;
382 	for(; n < linesize && line[n] != ' '; n++);
383 	line[n] = '\0';
384 	item[i] = line + a;
385 	i+=2;
386 	n++;
387 
388 	/* CRC */
389 	for(; n < linesize && line[n] == ' '; n++);
390 	a = n;
391 	for(; n < linesize && line[n] != ' '; n++);
392 	line[n] = '\0';
393 	item[i] = line + a;
394 
395 	/* FileName */
396 	line[linesize - 1] = '\0';
397 	filename = g_strdup(line + n + 2);
398 
399 	/* Strip trailing whitespace */
400 	end = filename + strlen(filename) - 1;
401 	while(end >= filename && *end == ' ') end--;
402 	*(end + 1) = '\0';
403 
404 	entry = xa_set_archive_entries_for_each_row (archive,filename,item);
405 
406 	if (entry)
407 	{
408 		if (dir)
409 			entry->is_dir = TRUE;
410 
411 		entry->is_encrypted = encrypted;
412 
413 		if (!entry->is_dir)
414 			archive->files++;
415 
416 		archive->files_size += g_ascii_strtoull(item[0], NULL, 0);
417 	}
418 
419 	g_free(filename);
420 }
421 
xa_rar_list(XArchive * archive)422 void xa_rar_list (XArchive *archive)
423 {
424 	GIOChannel *file;
425 	gchar *password_str, *command;
426 	guint i;
427 
428 	file = g_io_channel_new_file(archive->path[0], "r", NULL);
429 
430 	if (file)
431 	{
432 		gchar byte[2];
433 
434 		g_io_channel_set_encoding(file, NULL, NULL);
435 
436 		/* skip RAR 4 and 5 common signature part */
437 		g_io_channel_seek_position(file, 6, G_SEEK_SET, NULL);
438 
439 		g_io_channel_read_chars(file, byte, sizeof(*byte), NULL, NULL);
440 
441 		/* RAR 4 archive */
442 		if (*byte == 0)
443 		{
444 			/* skip header CRC16 */
445 			g_io_channel_seek_position(file, 2, G_SEEK_CUR, NULL);
446 
447 			/* block type */
448 			g_io_channel_read_chars(file, &byte[0], sizeof(*byte), NULL, NULL);
449 
450 			/* block flag */
451 			g_io_channel_read_chars(file, &byte[1], sizeof(*byte), NULL, NULL);
452 
453 			archive->has_password = (byte[0] == 0x73 && byte[1] & 0x80);
454 		}
455 		/* RAR 5 archive */
456 		else
457 		{
458 			/* skip last signature byte and header CRC32 */
459 			g_io_channel_seek_position(file, 5, G_SEEK_CUR, NULL);
460 
461 			/* skip vint header size */
462 			do
463 				g_io_channel_read_chars(file, byte, sizeof(*byte), NULL, NULL);
464 			while (*byte & 0x80);
465 
466 			/* header type */
467 			g_io_channel_read_chars(file, byte, sizeof(*byte), NULL, NULL);
468 
469 			archive->has_password = (*byte == 4);
470 		}
471 
472 		g_io_channel_shutdown(file, FALSE, NULL);
473 
474 		if (archive->has_password)
475 			if (!xa_check_password(archive))
476 				return;
477 	}
478 
479 	header_line = FALSE;
480 	data_line = FALSE;
481 	fname_line = FALSE;
482 	last_line = FALSE;
483 
484 	password_str = xa_rar_password_str(archive);
485 	command = g_strconcat(archiver[archive->type].program[0], " v", password_str, " -idc ", archive->path[1], NULL);
486 	g_free(password_str);
487 
488 	archive->files_size = 0;
489 	archive->files = 0;
490 
491 
492 	if (rar_version >= 5)
493 	{
494 		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_STRING, G_TYPE_STRING, G_TYPE_POINTER};
495 		const gchar *titles[] = {_("Original Size"), _("Compressed"), _("Occupancy"), _("Date"), _("Time"), _("Attributes"), _("Checksum")};
496 
497 		archive->parse_output = xa_rar5_parse_output;
498 		xa_spawn_async_process (archive,command);
499 		g_free ( command );
500 
501 		archive->columns = 10;
502 		archive->size_column = 2;
503 		archive->column_types = g_malloc0(sizeof(types));
504 
505 		for (i = 0; i < archive->columns; i++)
506 			archive->column_types[i] = types[i];
507 
508 		xa_create_liststore(archive, titles);
509 	}
510 	else
511 	{
512 		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_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER};
513 		const gchar *titles[] = {_("Original Size"), _("Compressed"), _("Occupancy"), _("Date"), _("Time"), _("Attributes"), _("Checksum"), _("Method"), _("Version")};
514 
515 		archive->parse_output = xa_rar_parse_output;
516 		xa_spawn_async_process (archive,command);
517 		g_free ( command );
518 
519 		archive->columns = 12;
520 		archive->size_column = 2;
521 		archive->column_types = g_malloc0(sizeof(types));
522 
523 		for (i = 0; i < archive->columns; i++)
524 			archive->column_types[i] = types[i];
525 
526 		xa_create_liststore(archive, titles);
527 	}
528 }
529 
xa_rar_test(XArchive * archive)530 void xa_rar_test (XArchive *archive)
531 {
532 	gchar *password_str, *command;
533 
534 	password_str = xa_rar_password_str(archive);
535 	command = g_strconcat(archiver[archive->type].program[0], " t", password_str, " -idp -y ", archive->path[1], NULL);
536 	g_free(password_str);
537 
538 	xa_run_command(archive, command);
539 	g_free(command);
540 }
541 
542 /*
543  * Note: rar does not seem to be able to handle wildcards in file names.
544  */
545 
xa_rar_extract(XArchive * archive,GSList * file_list)546 gboolean xa_rar_extract (XArchive *archive, GSList *file_list)
547 {
548 	GString *files;
549 	gchar *password_str, *command;
550 	gboolean result;
551 
552 	files = xa_quote_filenames(file_list, NULL, FALSE);
553 	password_str = xa_rar_password_str(archive);
554 	command = g_strconcat(archiver[archive->type].program[0],
555 	                      archive->do_full_path ? " x" : " e",
556 	                      archive->do_touch ? " -tsm-" : "",
557 	                      archive->do_overwrite ? " -o+" : (archive->do_update ? " -u" : (archive->do_freshen ? " -f" : " -o-")),
558 	                      password_str, " -idp -y ",
559 	                      archive->path[1], files->str,
560 	                      " ", archive->extraction_dir, NULL);
561 	g_free(password_str);
562 	g_string_free(files,TRUE);
563 
564 	result = xa_run_command(archive, command);
565 	g_free(command);
566 
567 	return result;
568 }
569 
xa_rar_add(XArchive * archive,GSList * file_list,gchar * compression)570 void xa_rar_add (XArchive *archive, GSList *file_list, gchar *compression)
571 {
572 	GString *files;
573 	gchar *password_str, *command, *version_switch;
574 
575 
576 	if (archive->location_path != NULL)
577 		archive->child_dir = g_strdup(archive->working_dir);
578 
579 	if (rar_version >= 5)
580 	{
581 		if (archive->tag == 5)
582 			version_switch = " -ma5";
583 		else
584 			version_switch = " -ma4";
585 	}
586 	else
587 		version_switch = "";
588 
589 	if (!compression)
590 		compression = "3";
591 
592 	files = xa_quote_filenames(file_list, NULL, FALSE);
593 	password_str = xa_rar_password_str(archive);
594 	command = g_strconcat(archiver[archive->type].program[0],
595 	                      archive->do_update ? " u" : " a", version_switch,
596 	                      archive->do_freshen ? " -f" : "",
597 	                      archive->do_move ? " -df" : "",
598 	                      archive->do_solid ? " -s" : "",
599 	                      " -m", compression,
600 	                      password_str, " -idp -y ",
601 	                      archive->path[1], files->str, NULL);
602 	g_free(password_str);
603 	g_string_free(files,TRUE);
604 
605 	xa_run_command(archive, command);
606 	g_free(command);
607 }
608 
xa_rar_delete(XArchive * archive,GSList * file_list)609 void xa_rar_delete (XArchive *archive, GSList *file_list)
610 {
611 	GString *files;
612 	gchar *command;
613 
614 	files = xa_quote_filenames(file_list, NULL, FALSE);
615 	command = g_strconcat(archiver[archive->type].program[0], " d -idp -y ", archive->path[1], files->str, NULL);
616 	g_string_free(files,TRUE);
617 
618 	xa_run_command(archive, command);
619 	g_free(command);
620 }
621