1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  File-Roller
5  *
6  *  Copyright (C) 2012 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 #define _XOPEN_SOURCE       /* See feature_test_macros(7) */
23 #include <time.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <glib.h>
28 #include "tr-wrapper.h"
29 #include <json-glib/json-glib.h>
30 #include "file-data.h"
31 #include "file-utils.h"
32 #include "gio-utils.h"
33 #include "glib-utils.h"
34 #include "fr-command.h"
35 #include "fr-command-unarchiver.h"
36 #include "fr-error.h"
37 
38 #define LSAR_SUPPORTED_FORMAT 2
39 #define LSAR_DATE_FORMAT "%Y-%m-%d %H:%M:%S %z"
40 
41 static void fr_command_unarchiver_class_init  (FrCommandUnarchiverClass *class);
42 static void fr_command_unarchiver_init        (FrCommand         *afile);
43 static void fr_command_unarchiver_finalize    (GObject           *object);
44 
45 /* Parent Class */
46 
47 static FrCommandClass *parent_class = NULL;
48 
49 
50 /* -- list -- */
51 
52 
53 static void
process_line(char * line,gpointer data)54 process_line (char     *line,
55 	      gpointer  data)
56 {
57 	FrCommandUnarchiver *unar_comm = FR_COMMAND_UNARCHIVER (data);
58 	g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (unar_comm->stream), line, -1, NULL);
59 }
60 
61 
62 static time_t
mktime_from_string(const char * time_s)63 mktime_from_string (const char *time_s)
64 {
65 	struct tm tm = {0, };
66 	tm.tm_isdst = -1;
67 	strptime (time_s, LSAR_DATE_FORMAT, &tm);
68 	return mktime (&tm);
69 }
70 
71 static void
list_command_completed(gpointer data)72 list_command_completed (gpointer data)
73 {
74 	FrCommandUnarchiver *unar_comm = FR_COMMAND_UNARCHIVER (data);
75 	JsonParser          *parser;
76 	GError              *error = NULL;
77 
78 	parser = json_parser_new ();
79 	if (json_parser_load_from_stream (parser, unar_comm->stream, NULL, &error)) {
80 		JsonObject *root;
81 
82 		root = json_node_get_object (json_parser_get_root (parser));
83 
84 		if (json_object_get_int_member (root, "lsarFormatVersion") == LSAR_SUPPORTED_FORMAT) {
85 			JsonArray *content;
86 			int        i;
87 
88 			content = json_object_get_array_member (root, "lsarContents");
89 			for (i = 0; i < json_array_get_length (content); i++) {
90 				JsonObject *entry;
91 				FileData   *fdata;
92 				const char *filename;
93 
94 				entry = json_array_get_object_element (content, i);
95 				fdata = file_data_new ();
96 				fdata->size = json_object_get_int_member (entry, "XADFileSize");
97 				fdata->modified = mktime_from_string (json_object_get_string_member (entry, "XADLastModificationDate"));
98 				if (json_object_has_member (entry, "XADIsEncrypted"))
99 					fdata->encrypted = json_object_get_int_member (entry, "XADIsEncrypted") == 1;
100 
101 				filename = json_object_get_string_member (entry, "XADFileName");
102 				if (*filename == '/') {
103 					fdata->full_path = g_strdup (filename);
104 					fdata->original_path = fdata->full_path;
105 				}
106 				else {
107 					fdata->full_path = g_strconcat ("/", filename, NULL);
108 					fdata->original_path = fdata->full_path + 1;
109 				}
110 
111 				fdata->link = NULL;
112 				if (json_object_has_member (entry, "XADIsDirectory"))
113 					fdata->dir = json_object_get_int_member (entry, "XADIsDirectory") == 1;
114 				if (fdata->dir)
115 					fdata->name = dir_name_from_path (fdata->full_path);
116 				else
117 					fdata->name = g_strdup (file_name_from_path (fdata->full_path));
118 				fdata->path = remove_level_from_path (fdata->full_path);
119 
120 				fr_command_add_file (FR_COMMAND (unar_comm), fdata);
121 			}
122 		}
123 	}
124 
125 	g_object_unref (parser);
126 }
127 
128 
129 static void
fr_command_unarchiver_list(FrCommand * comm)130 fr_command_unarchiver_list (FrCommand  *comm)
131 {
132 	FrCommandUnarchiver *unar_comm = FR_COMMAND_UNARCHIVER (comm);
133 
134 	_g_object_unref (unar_comm->stream);
135 	unar_comm->stream = g_memory_input_stream_new ();
136 
137 	fr_process_set_out_line_func (comm->process, process_line, comm);
138 
139 	fr_process_begin_command (comm->process, "lsar");
140 	fr_process_set_end_func (comm->process, list_command_completed, comm);
141 	fr_process_add_arg (comm->process, "-j");
142 	if ((comm->password != NULL) && (comm->password[0] != '\0'))
143 		fr_process_add_arg_concat (comm->process, "-password=", comm->password, NULL);
144 	fr_process_add_arg (comm->process, comm->filename);
145 	fr_process_end_command (comm->process);
146 
147 	fr_process_start (comm->process);
148 }
149 
150 
151 static void
process_line__extract(char * line,gpointer data)152 process_line__extract (char     *line,
153 		       gpointer  data)
154 {
155 	FrCommand           *comm = FR_COMMAND (data);
156 	FrCommandUnarchiver *unar_comm = FR_COMMAND_UNARCHIVER (comm);
157 
158 	if (line == NULL)
159 		return;
160 
161 	unar_comm->n_line++;
162 
163 	/* the first line is the name of the archive */
164 	if (unar_comm->n_line == 1)
165 		return;
166 
167 	if (comm->n_files > 1) {
168 		double fraction = (double) ++comm->n_file / (comm->n_files + 1);
169 		fr_command_progress (comm, CLAMP (fraction, 0.0, 1.0));
170 	}
171 	else
172 		fr_command_message (comm, line);
173 }
174 
175 
176 static void
fr_command_unarchiver_extract(FrCommand * comm,const char * from_file,GList * file_list,const char * dest_dir,gboolean overwrite,gboolean skip_older,gboolean junk_paths)177 fr_command_unarchiver_extract (FrCommand  *comm,
178 			       const char *from_file,
179 			       GList      *file_list,
180 			       const char *dest_dir,
181 			       gboolean    overwrite,
182 			       gboolean    skip_older,
183 			       gboolean    junk_paths)
184 {
185 	FrCommandUnarchiver *unar_comm = FR_COMMAND_UNARCHIVER (comm);
186 	GList               *scan;
187 
188 	unar_comm->n_line = 0;
189 
190 	fr_process_use_standard_locale (comm->process, TRUE);
191 	fr_process_set_out_line_func (comm->process,
192 				      process_line__extract,
193 				      comm);
194 
195 	fr_process_begin_command (comm->process, "unar");
196 
197 	if (overwrite)
198 		fr_process_add_arg (comm->process, "-f");
199 	else
200 		fr_process_add_arg (comm->process, "-s");
201 
202 	fr_process_add_arg (comm->process, "-D");
203 
204 	if ((comm->password != NULL) && (comm->password[0] != '\0'))
205 		fr_process_add_arg_concat (comm->process, "-password=", comm->password, NULL);
206 
207 	if (dest_dir != NULL)
208 		fr_process_add_arg_concat (comm->process, "-output-directory=", dest_dir, NULL);
209 
210 	fr_process_add_arg (comm->process, comm->filename);
211 
212 	for (scan = file_list; scan; scan = scan->next) {
213 		char *escaped;
214 
215 		escaped = escape_str (scan->data, "[?");
216 		fr_process_add_arg (comm->process, escaped);
217 		g_free (escaped);
218 	}
219 
220 	fr_process_end_command (comm->process);
221 }
222 
223 
224 static void
fr_command_unarchiver_handle_error(FrCommand * comm,FrProcError * error)225 fr_command_unarchiver_handle_error (FrCommand   *comm,
226 				    FrProcError *error)
227 {
228 	GList *scan;
229 
230 #if 0
231 	{
232 		for (scan = g_list_last (comm->process->err.raw); scan; scan = scan->prev)
233 			g_print ("%s\n", (char*)scan->data);
234 	}
235 #endif
236 
237 	if (error->type == FR_PROC_ERROR_NONE)
238 		return;
239 
240 	for (scan = g_list_last (comm->process->err.raw); scan; scan = scan->prev) {
241 		char *line = scan->data;
242 
243 		if (strstr (line, "password") != NULL) {
244 			error->type = FR_PROC_ERROR_ASK_PASSWORD;
245 			break;
246 		}
247 	}
248 }
249 
250 
251 const char *unarchiver_mime_type[] = { "application/x-cbr",
252 				       "application/x-rar",
253 				       NULL };
254 
255 
256 static const char **
fr_command_unarchiver_get_mime_types(FrCommand * comm)257 fr_command_unarchiver_get_mime_types (FrCommand *comm)
258 {
259 	return unarchiver_mime_type;
260 }
261 
262 
263 static FrCommandCap
fr_command_unarchiver_get_capabilities(FrCommand * comm,const char * mime_type,gboolean check_command)264 fr_command_unarchiver_get_capabilities (FrCommand  *comm,
265 					const char *mime_type,
266 					gboolean    check_command)
267 {
268 	FrCommandCap capabilities;
269 
270 	capabilities = FR_COMMAND_CAN_DO_NOTHING;
271 	if (is_program_available ("lsar", check_command) && is_program_available ("unar", check_command))
272 		capabilities |= FR_COMMAND_CAN_READ;
273 
274 	return capabilities;
275 }
276 
277 
278 static const char *
fr_command_unarchiver_get_packages(FrCommand * comm,const char * mime_type)279 fr_command_unarchiver_get_packages (FrCommand  *comm,
280 				    const char *mime_type)
281 {
282 	return PACKAGES ("unarchiver");
283 }
284 
285 
286 static void
fr_command_unarchiver_class_init(FrCommandUnarchiverClass * class)287 fr_command_unarchiver_class_init (FrCommandUnarchiverClass *class)
288 {
289 	GObjectClass *gobject_class = G_OBJECT_CLASS (class);
290 	FrCommandClass *afc;
291 
292 	parent_class = g_type_class_peek_parent (class);
293 	afc = (FrCommandClass*) class;
294 
295 	gobject_class->finalize = fr_command_unarchiver_finalize;
296 
297 	afc->list             = fr_command_unarchiver_list;
298 	afc->extract          = fr_command_unarchiver_extract;
299 	afc->handle_error     = fr_command_unarchiver_handle_error;
300 	afc->get_mime_types   = fr_command_unarchiver_get_mime_types;
301 	afc->get_capabilities = fr_command_unarchiver_get_capabilities;
302 	afc->get_packages     = fr_command_unarchiver_get_packages;
303 }
304 
305 
306 static void
fr_command_unarchiver_init(FrCommand * comm)307 fr_command_unarchiver_init (FrCommand *comm)
308 {
309 	FrCommandUnarchiver *unar_comm;
310 
311 	comm->propExtractCanAvoidOverwrite = TRUE;
312 	comm->propExtractCanSkipOlder      = FALSE;
313 	comm->propExtractCanJunkPaths      = FALSE;
314 	comm->propPassword                 = TRUE;
315 	comm->propTest                     = FALSE;
316 	comm->propListFromFile             = FALSE;
317 
318 	unar_comm = FR_COMMAND_UNARCHIVER (comm);
319 	unar_comm->stream = NULL;
320 }
321 
322 
323 static void
fr_command_unarchiver_finalize(GObject * object)324 fr_command_unarchiver_finalize (GObject *object)
325 {
326 	FrCommandUnarchiver *unar_comm;
327 
328 	g_return_if_fail (object != NULL);
329 	g_return_if_fail (FR_IS_COMMAND_UNARCHIVER (object));
330 
331 	unar_comm = FR_COMMAND_UNARCHIVER (object);
332 	_g_object_unref (unar_comm->stream);
333 
334 	/* Chain up */
335 	if (G_OBJECT_CLASS (parent_class)->finalize)
336 		G_OBJECT_CLASS (parent_class)->finalize (object);
337 }
338 
339 
340 GType
fr_command_unarchiver_get_type()341 fr_command_unarchiver_get_type ()
342 {
343 	static GType type = 0;
344 
345 	if (! type) {
346 		GTypeInfo type_info = {
347 			sizeof (FrCommandUnarchiverClass),
348 			NULL,
349 			NULL,
350 			(GClassInitFunc) fr_command_unarchiver_class_init,
351 			NULL,
352 			NULL,
353 			sizeof (FrCommandUnarchiver),
354 			0,
355 			(GInstanceInitFunc) fr_command_unarchiver_init
356 		};
357 
358 		type = g_type_register_static (FR_TYPE_COMMAND,
359 					       "FrCommandUnarchiver",
360 					       &type_info,
361 					       0);
362 	}
363 
364 	return type;
365 }
366