1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  File-Roller
5  *
6  *  Copyright (C) 2001 The 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, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include "file-data.h"
30 #include "file-utils.h"
31 #include "gio-utils.h"
32 #include "glib-utils.h"
33 #include "fr-command.h"
34 #include "fr-command-rar.h"
35 #include "fr-error.h"
36 #include "rar-utils.h"
37 
38 
39 struct _FrCommandRar
40 {
41 	FrCommand  parent_instance;
42 
43 	gboolean   list_started;
44 	gboolean   rar4_odd_line;
45 	gboolean   rar5;
46 	gboolean   rar5_30;
47 	FileData  *fdata;
48 };
49 
50 
G_DEFINE_TYPE(FrCommandRar,fr_command_rar,fr_command_get_type ())51 G_DEFINE_TYPE (FrCommandRar, fr_command_rar, fr_command_get_type ())
52 
53 
54 static gboolean
55 have_rar (void)
56 {
57 	return _g_program_is_in_path ("rar");
58 }
59 
60 
61 /* -- list -- */
62 
63 
64 static time_t
mktime_from_string(const char * date_s,const char * time_s)65 mktime_from_string (const char *date_s,
66 		    const char *time_s)
67 {
68 	struct tm   tm = {0, };
69 	char      **fields;
70 
71 	tm.tm_isdst = -1;
72 
73 	/* date */
74 
75 	fields = g_strsplit (date_s, "-", 3);
76 	if (fields[0] != NULL) {
77 		tm.tm_mday = atoi (fields[0]);
78 		if (fields[1] != NULL) {
79 			tm.tm_mon = atoi (fields[1]) - 1;
80 			if (fields[2] != NULL)
81 				tm.tm_year = 100 + atoi (fields[2]);
82 		}
83 	}
84 	g_strfreev (fields);
85 
86 	/* time */
87 
88 	fields = g_strsplit (time_s, ":", 2);
89 	if (fields[0] != NULL) {
90 		tm.tm_hour = atoi (fields[0]);
91 		if (fields[1] != NULL)
92 			tm.tm_min = atoi (fields[1]);
93 	}
94 	g_strfreev (fields);
95 
96 	return mktime (&tm);
97 }
98 
99 
100 static time_t
mktime_from_string_rar_5_30(const char * date_s,const char * time_s)101 mktime_from_string_rar_5_30 (const char *date_s,
102 			     const char *time_s)
103 {
104 	struct tm   tm = {0, };
105 	char      **fields;
106 
107 	tm.tm_isdst = -1;
108 
109 	/* date */
110 
111 	fields = g_strsplit (date_s, "-", 3);
112 	if (fields[0] != NULL) {
113 		tm.tm_year = atoi (fields[0]) - 1900;
114 		if (fields[1] != NULL) {
115 			tm.tm_mon = atoi (fields[1]) - 1;
116 			if (fields[2] != NULL)
117 				tm.tm_mday = atoi (fields[2]);
118 		}
119 	}
120 	g_strfreev (fields);
121 
122 	/* time */
123 
124 	fields = g_strsplit (time_s, ":", 2);
125 	if (fields[0] != NULL) {
126 		tm.tm_hour = atoi (fields[0]);
127 		if (fields[1] != NULL)
128 			tm.tm_min = atoi (fields[1]);
129 	}
130 	g_strfreev (fields);
131 
132 	return mktime (&tm);
133 }
134 
135 
136 /*
137  * Sample rar 5.30 or higher output:
138  *
139 
140 RAR 5.30   Copyright (c) 1993-2017 Alexander Roshal   11 Aug 2017
141 Trial version             Type 'rar -?' for help
142 
143 Archive: test.rar
144 Details: RAR 5
145 
146  Attributes      Size    Packed Ratio    Date    Time   Checksum  Name
147 ----------- ---------  -------- ----- ---------- -----  --------  ----
148  -rw-r--r--        51        47  92%  2017-11-19 16:20  80179DAB  loremipsum.txt
149 ----------- ---------  -------- ----- ---------- -----  --------  ----
150                    51        47  92%                              1
151 
152  */
153 
154 /* Sample rar-5 listing output:
155 
156 RAR 5.00 beta 8   Copyright (c) 1993-2013 Alexander Roshal   22 Aug 2013
157 Trial version             Type RAR -? for help
158 
159 Archive: test.rar
160 Details: RAR 4
161 
162  Attributes      Size    Packed Ratio   Date   Time   Checksum  Name
163 ----------- ---------  -------- ----- -------- -----  --------  ----
164  -rw-r--r--       453       304  67%  05-09-13 09:55  56DA5EF3  loremipsum.txt
165 ----------- ---------  -------- ----- -------- -----  --------  ----
166                   453       304  67%                            1
167 
168  *
169  * Sample rar-4 listing output:
170  *
171 
172 RAR 4.20   Copyright (c) 1993-2012 Alexander Roshal   9 Jun 2012
173 Trial version             Type RAR -? for help
174 
175 Archive test.rar
176 
177 Pathname/Comment
178                   Size   Packed Ratio  Date   Time     Attr      CRC   Meth Ver
179 -------------------------------------------------------------------------------
180  loremipsum.txt
181                    453      304  67% 05-09-13 09:55 -rw-r--r-- 56DA5EF3 m3b 2.9
182 -------------------------------------------------------------------------------
183     1              453      304  67%
184 
185  */
186 
187 static gboolean
attribute_field_with_space(char * line)188 attribute_field_with_space (char *line)
189 {
190 	/* sometimes when the archive is encrypted the attributes field is
191 	 * like this: "*   ..A...."
192 	 * */
193 	return ((line[0] != ' ') && (line[1] == ' '));
194 }
195 
196 
197 static void
parse_name_field(char * line,FrCommandRar * rar_comm)198 parse_name_field (char         *line,
199 		  FrCommandRar *rar_comm)
200 {
201 	char     *name_field;
202 	FileData *fdata;
203 
204 	rar_comm->fdata = fdata = file_data_new ();
205 
206 	/* read file name. */
207 
208 	fdata->encrypted = (line[0] == '*') ? TRUE : FALSE;
209 
210 	if (rar_comm->rar5)
211 		/* rar-5 output adds trailing spaces to short file names :( */
212 		name_field = g_strchomp (g_strdup (_g_str_get_last_field (line, attribute_field_with_space (line) ? 9 : 8)));
213 	else
214 		name_field = g_strdup (line + 1);
215 
216 	if (name_field == NULL)
217 		return;
218 
219 	if (*name_field == '/') {
220 		fdata->full_path = g_strdup (name_field);
221 		fdata->original_path = fdata->full_path;
222 	}
223 	else {
224 		fdata->full_path = g_strconcat ("/", name_field, NULL);
225 		fdata->original_path = fdata->full_path + 1;
226 	}
227 
228 	fdata->link = NULL;
229 	fdata->path = _g_path_remove_level (fdata->full_path);
230 
231 	g_free (name_field);
232 }
233 
234 static gboolean
attr_field_is_dir(const char * attr_field,FrCommandRar * rar_comm)235 attr_field_is_dir (const char   *attr_field,
236                    FrCommandRar *rar_comm)
237 {
238         if ((attr_field[0] == 'd') ||
239             (rar_comm->rar5 && attr_field[3] == 'D') ||
240             (!rar_comm->rar5 && attr_field[1] == 'D'))
241                 return TRUE;
242 
243         return FALSE;
244 }
245 
246 static void
process_line(char * line,gpointer data)247 process_line (char     *line,
248 	      gpointer  data)
249 {
250 	FrCommand     *comm = FR_COMMAND (data);
251 	FrCommandRar  *rar_comm = FR_COMMAND_RAR (comm);
252 	char         **fields;
253 
254 	g_return_if_fail (line != NULL);
255 
256 	if (! rar_comm->list_started) {
257 		if ((strncmp (line, "RAR ", 4) == 0) || (strncmp (line, "UNRAR ", 6) == 0)) {
258 			int major_version;
259 			int minor_version;
260 
261 			if (strncmp (line, "RAR ", 4) == 0)
262 				sscanf (line, "RAR %d.%d", &major_version, &minor_version);
263 			else
264 				sscanf (line, "UNRAR %d.%d", &major_version, &minor_version);
265 
266 			rar_comm->rar5 = (major_version >= 5);
267 			rar_comm->rar5_30 = ((major_version == 5) && (minor_version >= 30)) || (major_version >= 6);
268 		}
269 		else if (strncmp (line, "--------", 8) == 0) {
270 			rar_comm->list_started = TRUE;
271 			if (! rar_comm->rar5)
272 			    rar_comm->rar4_odd_line = TRUE;
273 		}
274 		else if (strncmp (line, "Volume ", 7) == 0)
275 			FR_ARCHIVE (comm)->multi_volume = TRUE;
276 		return;
277 	}
278 
279 	if (strncmp (line, "--------", 8) == 0) {
280 		rar_comm->list_started = FALSE;
281 		return;
282 	}
283 
284 	if (rar_comm->rar4_odd_line || rar_comm->rar5)
285 		parse_name_field (line, rar_comm);
286 
287 	if (! rar_comm->rar4_odd_line) {
288 		FileData   *fdata;
289 		const char *size_field, *ratio_field, *date_field, *time_field, *attr_field;
290 
291 		fdata = rar_comm->fdata;
292 
293 		/* read file info. */
294 
295 		fields = _g_str_split_line (line, attribute_field_with_space (line) ? 7 : 6);
296 		if (rar_comm->rar5) {
297 			int offset = attribute_field_with_space (line) ? 1 : 0;
298 
299 			size_field = fields[1+offset];
300 			ratio_field = fields[3+offset];
301 			date_field = fields[4+offset];
302 			time_field = fields[5+offset];
303 			attr_field = fields[0+offset];
304 		}
305 		else {
306 			size_field = fields[0];
307 			ratio_field = fields[2];
308 			date_field = fields[3];
309 			time_field = fields[4];
310 			attr_field = fields[5];
311 		}
312 		if (g_strv_length (fields) < 6) {
313 			/* wrong line format, treat this line as a filename line */
314 			g_strfreev (fields);
315 			file_data_free (rar_comm->fdata);
316 			rar_comm->fdata = NULL;
317 			rar_comm->rar4_odd_line = TRUE;
318 			parse_name_field (line, rar_comm);
319 		}
320 		else {
321 			if ((strcmp (ratio_field, "<->") == 0)
322 			    || (strcmp (ratio_field, "<--") == 0))
323 			{
324 				/* ignore files that span more volumes */
325 
326 				file_data_free (rar_comm->fdata);
327 				rar_comm->fdata = NULL;
328 			}
329 			else {
330 				fdata->size = g_ascii_strtoull (size_field, NULL, 10);
331 
332 				fdata->modified = rar_comm->rar5_30 ? mktime_from_string_rar_5_30 (date_field, time_field) : mktime_from_string (date_field, time_field);
333 
334 				if (attr_field_is_dir (attr_field, rar_comm)) {
335 					char *tmp;
336 
337 					tmp = fdata->full_path;
338 					fdata->full_path = g_strconcat (fdata->full_path, "/", NULL);
339 
340 					fdata->original_path = g_strdup (fdata->original_path);
341 					fdata->free_original_path = TRUE;
342 
343 					g_free (tmp);
344 
345 					fdata->name = _g_path_get_dir_name (fdata->full_path);
346 					fdata->dir = TRUE;
347 				}
348 				else {
349 					fdata->name = g_strdup (_g_path_get_basename (fdata->full_path));
350 					if (attr_field[0] == 'l')
351 						fdata->link = g_strdup (_g_path_get_basename (fdata->full_path));
352 				}
353 
354 				fr_archive_add_file (FR_ARCHIVE (comm), fdata);
355 				rar_comm->fdata = NULL;
356 			}
357 
358 			g_strfreev (fields);
359 		}
360 	}
361 
362 	if (! rar_comm->rar5)
363 		rar_comm->rar4_odd_line = ! rar_comm->rar4_odd_line;
364 }
365 
366 
367 static void
add_password_arg(FrCommand * comm,const char * password,gboolean disable_query)368 add_password_arg (FrCommand  *comm,
369 		  const char *password,
370 		  gboolean    disable_query)
371 {
372 	if ((password != NULL) && (password[0] != '\0')) {
373 		if (FR_ARCHIVE (comm)->encrypt_header)
374 			fr_process_add_arg_concat (comm->process, "-hp", password, NULL);
375 		else
376 			fr_process_add_arg_concat (comm->process, "-p", password, NULL);
377 	}
378 	else if (disable_query)
379 		fr_process_add_arg (comm->process, "-p-");
380 }
381 
382 
383 static void
list__begin(gpointer data)384 list__begin (gpointer data)
385 {
386 	FrCommandRar *comm = data;
387 
388 	comm->list_started = FALSE;
389 }
390 
391 
392 static gboolean
fr_command_rar_list(FrCommand * comm)393 fr_command_rar_list (FrCommand  *comm)
394 {
395 	rar_check_multi_volume (comm);
396 
397 	fr_process_set_out_line_func (comm->process, process_line, comm);
398 
399 	if (have_rar ())
400 		fr_process_begin_command (comm->process, "rar");
401 	else
402 		fr_process_begin_command (comm->process, "unrar");
403 	fr_process_set_begin_func (comm->process, list__begin, comm);
404 	fr_process_add_arg (comm->process, "v");
405 	fr_process_add_arg (comm->process, "-c-");
406 	fr_process_add_arg (comm->process, "-v");
407 
408 	add_password_arg (comm, FR_ARCHIVE (comm)->password, TRUE);
409 
410 	/* stop switches scanning */
411 	fr_process_add_arg (comm->process, "--");
412 
413 	fr_process_add_arg (comm->process, comm->filename);
414 	fr_process_end_command (comm->process);
415 
416 	return TRUE;
417 }
418 
419 
420 static void
parse_progress_line(FrCommand * comm,const char * prefix,const char * message_format,const char * line)421 parse_progress_line (FrCommand  *comm,
422 		     const char *prefix,
423 		     const char *message_format,
424 		     const char *line)
425 {
426 	FrArchive *archive = FR_ARCHIVE (comm);
427 	int        prefix_len;
428 
429 	prefix_len = strlen (prefix);
430 	if (strncmp (line, prefix, prefix_len) == 0) {
431 		if (fr_archive_progress_get_total_files (archive) > 0) {
432 			fr_archive_progress (archive, fr_archive_progress_inc_completed_files (archive, 1));
433 		}
434 		else {
435 			char  filename[4096];
436 			char *b_idx;
437 			int   len;
438 			char *msg;
439 
440 			strcpy (filename, line + prefix_len);
441 
442 			/* when a new volume is created a sequence of backspaces is
443 			 * issued, remove the backspaces from the filename */
444 			b_idx = strchr (filename, '\x08');
445 			if (b_idx != NULL)
446 				*b_idx = 0;
447 
448 			/* remove the OK at the end of the filename */
449 			len = strlen (filename);
450 			if ((len > 5) && (strncmp (filename + len - 5, "  OK ", 5) == 0))
451 				filename[len - 5] = 0;
452 
453 			msg = g_strdup_printf (message_format, _g_path_get_basename (filename), NULL);
454 			fr_archive_message (archive, msg);
455 
456 			g_free (msg);
457 		}
458 	}
459 }
460 
461 
462 static void
process_line__add(char * line,gpointer data)463 process_line__add (char     *line,
464 		   gpointer  data)
465 {
466 	FrCommand *comm = FR_COMMAND (data);
467 	FrArchive *archive = FR_ARCHIVE (comm);
468 
469 	if (strncmp (line, "Creating archive ", 17) == 0) {
470 		const char *archive_filename = line + 17;
471 		char *uri;
472 
473 		uri = g_filename_to_uri (archive_filename, NULL, NULL);
474 		if ((archive->volume_size > 0)
475 		    && g_regex_match_simple ("^.*\\.part(0)*2\\.rar$", uri, G_REGEX_CASELESS, 0))
476 		{
477 			char  *volume_filename;
478 			GFile *volume_file;
479 
480 			volume_filename = g_strdup (archive_filename);
481 			volume_filename[strlen (volume_filename) - 5] = '1';
482 			volume_file = g_file_new_for_path (volume_filename);
483 			fr_archive_set_multi_volume (archive, volume_file);
484 
485 			g_object_unref (volume_file);
486 			g_free (volume_filename);
487 		}
488 		fr_archive_working_archive (archive, uri);
489 
490 		g_free (uri);
491 		return;
492 	}
493 
494 	if (fr_archive_progress_get_total_files (archive) > 0)
495 		parse_progress_line (comm, "Adding    ", _("Adding “%s”"), line);
496 }
497 
498 
499 static void
fr_command_rar_add(FrCommand * comm,const char * from_file,GList * file_list,const char * base_dir,gboolean update,gboolean follow_links)500 fr_command_rar_add (FrCommand  *comm,
501 		    const char *from_file,
502 		    GList      *file_list,
503 		    const char *base_dir,
504 		    gboolean    update,
505 		    gboolean    follow_links)
506 {
507 	GList *scan;
508 
509 	fr_process_use_standard_locale (comm->process, TRUE);
510 	fr_process_set_out_line_func (comm->process,
511 				      process_line__add,
512 				      comm);
513 
514 	fr_process_begin_command (comm->process, "rar");
515 
516 	if (base_dir != NULL)
517 		fr_process_set_working_dir (comm->process, base_dir);
518 
519 	if (update)
520 		fr_process_add_arg (comm->process, "u");
521 	else
522 		fr_process_add_arg (comm->process, "a");
523 
524 	if (! follow_links)
525 		fr_process_add_arg (comm->process, "-ol");
526 
527 	switch (FR_ARCHIVE (comm)->compression) {
528 	case FR_COMPRESSION_VERY_FAST:
529 		fr_process_add_arg (comm->process, "-m1"); break;
530 	case FR_COMPRESSION_FAST:
531 		fr_process_add_arg (comm->process, "-m2"); break;
532 	case FR_COMPRESSION_NORMAL:
533 		fr_process_add_arg (comm->process, "-m3"); break;
534 	case FR_COMPRESSION_MAXIMUM:
535 		fr_process_add_arg (comm->process, "-m5"); break;
536 	}
537 
538 	add_password_arg (comm, FR_ARCHIVE (comm)->password, FALSE);
539 
540 	if (FR_ARCHIVE (comm)->volume_size > 0)
541 		fr_process_add_arg_printf (comm->process, "-v%ub", FR_ARCHIVE (comm)->volume_size);
542 
543 	/* disable percentage indicator */
544 	fr_process_add_arg (comm->process, "-Idp");
545 
546 	fr_process_add_arg (comm->process, "--");
547 	fr_process_add_arg (comm->process, comm->filename);
548 
549 	if (from_file == NULL)
550 		for (scan = file_list; scan; scan = scan->next)
551 			fr_process_add_arg (comm->process, scan->data);
552 	else
553 		fr_process_add_arg_concat (comm->process, "@", from_file, NULL);
554 
555 	fr_process_end_command (comm->process);
556 }
557 
558 
559 static void
process_line__delete(char * line,gpointer data)560 process_line__delete (char     *line,
561 		      gpointer  data)
562 {
563 	FrCommand *comm = FR_COMMAND (data);
564 
565 	if (strncmp (line, "Deleting from ", 14) == 0) {
566 		char *uri;
567 
568 		uri = g_filename_to_uri (line + 14, NULL, NULL);
569 		fr_archive_working_archive (FR_ARCHIVE (comm), uri);
570 		g_free (uri);
571 
572 		return;
573 	}
574 
575 	if (fr_archive_progress_get_total_files (FR_ARCHIVE (comm)) > 0)
576 		parse_progress_line (comm, "Deleting ", _("Removing “%s”"), line);
577 }
578 
579 
580 static void
fr_command_rar_delete(FrCommand * comm,const char * from_file,GList * file_list)581 fr_command_rar_delete (FrCommand  *comm,
582 		       const char *from_file,
583 		       GList      *file_list)
584 {
585 	GList *scan;
586 
587 	fr_process_use_standard_locale (comm->process, TRUE);
588 	fr_process_set_out_line_func (comm->process,
589 				      process_line__delete,
590 				      comm);
591 
592 	fr_process_begin_command (comm->process, "rar");
593 	fr_process_add_arg (comm->process, "d");
594 
595 	add_password_arg (comm, FR_ARCHIVE (comm)->password, FALSE);
596 
597 	fr_process_add_arg (comm->process, "--");
598 	fr_process_add_arg (comm->process, comm->filename);
599 
600 	if (from_file == NULL)
601 		for (scan = file_list; scan; scan = scan->next)
602 			fr_process_add_arg (comm->process, scan->data);
603 	else
604 		fr_process_add_arg_concat (comm->process, "@", from_file, NULL);
605 
606 	fr_process_end_command (comm->process);
607 }
608 
609 
610 static void
process_line__extract(char * line,gpointer data)611 process_line__extract (char     *line,
612 		       gpointer  data)
613 {
614 	FrCommand *comm = FR_COMMAND (data);
615 
616 	if (strncmp (line, "Extracting from ", 16) == 0) {
617 		char *uri;
618 
619 		uri = g_filename_to_uri (line + 16, NULL, NULL);
620 		fr_archive_working_archive (FR_ARCHIVE (comm), uri);
621 		g_free (uri);
622 
623 		return;
624 	}
625 
626 	if (fr_archive_progress_get_total_files (FR_ARCHIVE (comm)) > 0)
627 		parse_progress_line (comm, "Extracting  ", _("Extracting “%s”"), line);
628 }
629 
630 
631 static void
fr_command_rar_extract(FrCommand * comm,const char * from_file,GList * file_list,const char * dest_dir,gboolean overwrite,gboolean skip_older,gboolean junk_paths)632 fr_command_rar_extract (FrCommand  *comm,
633 			const char *from_file,
634 			GList      *file_list,
635 			const char *dest_dir,
636 			gboolean    overwrite,
637 			gboolean    skip_older,
638 			gboolean    junk_paths)
639 {
640 	GList *scan;
641 
642 	fr_process_use_standard_locale (comm->process, TRUE);
643 	fr_process_set_out_line_func (comm->process,
644 				      process_line__extract,
645 				      comm);
646 
647 	if (have_rar ())
648 		fr_process_begin_command (comm->process, "rar");
649 	else
650 		fr_process_begin_command (comm->process, "unrar");
651 
652 	fr_process_add_arg (comm->process, "x");
653 
654 	/* keep broken extracted files */
655 	fr_process_add_arg (comm->process, "-kb");
656 
657 	if (overwrite)
658 		fr_process_add_arg (comm->process, "-o+");
659 	else
660 		fr_process_add_arg (comm->process, "-o-");
661 
662 	if (skip_older)
663 		fr_process_add_arg (comm->process, "-u");
664 
665 	if (junk_paths)
666 		fr_process_add_arg (comm->process, "-ep");
667 
668 	add_password_arg (comm, FR_ARCHIVE (comm)->password, TRUE);
669 
670 	/* disable percentage indicator */
671 	fr_process_add_arg (comm->process, "-Idp");
672 
673 	fr_process_add_arg (comm->process, "--");
674 	fr_process_add_arg (comm->process, comm->filename);
675 
676 	if (from_file == NULL)
677 		for (scan = file_list; scan; scan = scan->next)
678 			fr_process_add_arg (comm->process, scan->data);
679 	else
680 		fr_process_add_arg_concat (comm->process, "@", from_file, NULL);
681 
682 	if (dest_dir != NULL)
683 		fr_process_add_arg (comm->process, dest_dir);
684 
685 	fr_process_end_command (comm->process);
686 }
687 
688 
689 static void
fr_command_rar_test(FrCommand * comm)690 fr_command_rar_test (FrCommand   *comm)
691 {
692 	if (have_rar ())
693 		fr_process_begin_command (comm->process, "rar");
694 	else
695 		fr_process_begin_command (comm->process, "unrar");
696 
697 	fr_process_add_arg (comm->process, "t");
698 
699 	add_password_arg (comm, FR_ARCHIVE (comm)->password, TRUE);
700 
701 	/* disable percentage indicator */
702 	fr_process_add_arg (comm->process, "-Idp");
703 
704 	/* stop switches scanning */
705 	fr_process_add_arg (comm->process, "--");
706 
707 	fr_process_add_arg (comm->process, comm->filename);
708 	fr_process_end_command (comm->process);
709 }
710 
711 
712 static void
fr_command_rar_handle_error(FrCommand * comm,FrError * error)713 fr_command_rar_handle_error (FrCommand *comm,
714 			     FrError   *error)
715 {
716 	GList *scan;
717 
718 #if 0
719 	{
720 		GList *scan;
721 
722 		for (scan = g_list_last (comm->process->err.raw); scan; scan = scan->prev)
723 			g_print ("%s\n", (char*)scan->data);
724 	}
725 #endif
726 
727 	if (error->type == FR_ERROR_NONE)
728 		return;
729 
730 	/* ignore warnings */
731 	if (error->status <= 1)
732 		fr_error_clear_gerror (error);
733 
734 	for (scan = g_list_last (comm->process->err.raw); scan; scan = scan->prev) {
735 		char *line = scan->data;
736 
737 		if (strstr (line, "password incorrect") != NULL) {
738 			fr_error_take_gerror (error, g_error_new_literal (FR_ERROR, FR_ERROR_ASK_PASSWORD, ""));
739 			break;
740 		}
741 
742 		if (strstr (line, "password is incorrect") != NULL) {
743 			fr_error_take_gerror (error, g_error_new_literal (FR_ERROR, FR_ERROR_ASK_PASSWORD, ""));
744 			break;
745 		}
746 
747 		if (strstr (line, "wrong password") != NULL) {
748 			fr_error_take_gerror (error, g_error_new_literal (FR_ERROR, FR_ERROR_ASK_PASSWORD, ""));
749 			break;
750 		}
751 
752 		if (strncmp (line, "Unexpected end of archive", 25) == 0) {
753 			/* FIXME: handle this type of errors at a higher level when the freeze is over. */
754 		}
755 
756 		if (strncmp (line, "Cannot find volume", 18) == 0) {
757 			char *volume_filename = g_path_get_basename (line + strlen ("Cannot find volume "));
758 			fr_error_take_gerror (error, g_error_new (FR_ERROR, FR_ERROR_MISSING_VOLUME, _("Could not find the volume: %s"), volume_filename));
759 
760 			g_free (volume_filename);
761 			break;
762 		}
763 	}
764 }
765 
766 
767 const char *rar_mime_type[] = { "application/x-cbr",
768 				"application/x-rar",
769 				NULL };
770 
771 
772 static const char **
fr_command_rar_get_mime_types(FrArchive * archive)773 fr_command_rar_get_mime_types (FrArchive *archive)
774 {
775 	return rar_mime_type;
776 }
777 
778 
779 static FrArchiveCap
fr_command_rar_get_capabilities(FrArchive * archive,const char * mime_type,gboolean check_command)780 fr_command_rar_get_capabilities (FrArchive  *archive,
781 			         const char *mime_type,
782 				 gboolean    check_command)
783 {
784 	FrArchiveCap capabilities;
785 
786 	capabilities = FR_ARCHIVE_CAN_STORE_MANY_FILES | FR_ARCHIVE_CAN_ENCRYPT | FR_ARCHIVE_CAN_ENCRYPT_HEADER;
787 	if (_g_program_is_available ("rar", check_command))
788 		capabilities |= FR_ARCHIVE_CAN_READ_WRITE | FR_ARCHIVE_CAN_CREATE_VOLUMES;
789 	else if (_g_program_is_available ("unrar", check_command))
790 		capabilities |= FR_ARCHIVE_CAN_READ;
791 
792 	/* multi-volumes are read-only */
793 	if ((archive->files->len > 0) && archive->multi_volume && (capabilities & FR_ARCHIVE_CAN_WRITE))
794 		capabilities ^= FR_ARCHIVE_CAN_WRITE;
795 
796 	return capabilities;
797 }
798 
799 
800 static const char *
fr_command_rar_get_packages(FrArchive * archive,const char * mime_type)801 fr_command_rar_get_packages (FrArchive  *archive,
802 			     const char *mime_type)
803 {
804 	return PACKAGES ("rar,unrar");
805 }
806 
807 
808 static void
fr_command_rar_finalize(GObject * object)809 fr_command_rar_finalize (GObject *object)
810 {
811 	g_return_if_fail (object != NULL);
812 	g_return_if_fail (FR_IS_COMMAND_RAR (object));
813 
814 	if (G_OBJECT_CLASS (fr_command_rar_parent_class)->finalize)
815 		G_OBJECT_CLASS (fr_command_rar_parent_class)->finalize (object);
816 }
817 
818 
819 static void
fr_command_rar_class_init(FrCommandRarClass * klass)820 fr_command_rar_class_init (FrCommandRarClass *klass)
821 {
822 	GObjectClass   *gobject_class;
823 	FrArchiveClass *archive_class;
824 	FrCommandClass *command_class;
825 
826 	fr_command_rar_parent_class = g_type_class_peek_parent (klass);
827 
828 	gobject_class = G_OBJECT_CLASS (klass);
829 	gobject_class->finalize = fr_command_rar_finalize;
830 
831 	archive_class = FR_ARCHIVE_CLASS (klass);
832 	archive_class->get_mime_types   = fr_command_rar_get_mime_types;
833 	archive_class->get_capabilities = fr_command_rar_get_capabilities;
834 	archive_class->get_packages     = fr_command_rar_get_packages;
835 
836 	command_class = FR_COMMAND_CLASS (klass);
837 	command_class->list             = fr_command_rar_list;
838 	command_class->add              = fr_command_rar_add;
839 	command_class->delete           = fr_command_rar_delete;
840 	command_class->extract          = fr_command_rar_extract;
841 	command_class->test             = fr_command_rar_test;
842 	command_class->handle_error     = fr_command_rar_handle_error;
843 }
844 
845 
846 static void
fr_command_rar_init(FrCommandRar * self)847 fr_command_rar_init (FrCommandRar *self)
848 {
849 	FrArchive *base = FR_ARCHIVE (self);
850 
851 	base->propAddCanUpdate             = TRUE;
852 	base->propAddCanReplace            = TRUE;
853 	base->propAddCanStoreFolders       = TRUE;
854 	base->propAddCanStoreLinks         = TRUE;
855 	base->propExtractCanAvoidOverwrite = TRUE;
856 	base->propExtractCanSkipOlder      = TRUE;
857 	base->propExtractCanJunkPaths      = TRUE;
858 	base->propCanDeleteAllFiles        = FALSE;
859 	base->propPassword                 = TRUE;
860 	base->propTest                     = TRUE;
861 	base->propListFromFile             = TRUE;
862 }
863