1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  Engrampa
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, write to the Free Software
20  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02110-1301, USA.
21  */
22 
23 #include <config.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 
28 #include <glib.h>
29 
30 #include "file-data.h"
31 #include "file-utils.h"
32 #include "glib-utils.h"
33 #include "fr-command.h"
34 #include "fr-command-zip.h"
35 
36 #define EMPTY_ARCHIVE_WARNING        "Empty zipfile."
37 #define ZIP_SPECIAL_CHARACTERS       "[]*?!^-\\"
38 
39 static void fr_command_zip_class_init  (FrCommandZipClass *class);
40 static void fr_command_zip_init        (FrCommand         *afile);
41 static void fr_command_zip_finalize    (GObject           *object);
42 
43 /* Parent Class */
44 
45 static FrCommandClass *parent_class = NULL;
46 
47 
48 /* -- list -- */
49 
50 static time_t
mktime_from_string(char * datetime_s)51 mktime_from_string (char *datetime_s)
52 {
53 	struct tm  tm = {0, };
54 	char      *date;
55 	char      *time;
56 	char      *year;
57 	char      *month;
58 	char      *day;
59 	char      *hour;
60 	char      *min;
61 	char      *sec;
62 
63 	tm.tm_isdst = -1;
64 
65 	/* date */
66 
67 	date = datetime_s;
68 	year = g_strndup (date, 4);
69 	month = g_strndup (date + 4, 2);
70 	day = g_strndup (date + 6, 2);
71 	tm.tm_year = atoi (year) - 1900;
72 	tm.tm_mon = atoi (month) - 1;
73 	tm.tm_mday = atoi (day);
74 	g_free (year);
75 	g_free (month);
76 	g_free (day);
77 
78 	/* time */
79 
80 	time = datetime_s + 9;
81 	hour = g_strndup (time, 2);
82 	min = g_strndup (time + 2, 2);
83 	sec = g_strndup (time + 4, 2);
84 	tm.tm_hour = atoi (hour);
85 	tm.tm_min = atoi (min);
86 	tm.tm_sec = atoi (sec);
87 	g_free(hour);
88 	g_free(min);
89 	g_free(sec);
90 
91 	return mktime (&tm);
92 }
93 
94 
95 static void
list__process_line(char * line,gpointer data)96 list__process_line (char     *line,
97 		    gpointer  data)
98 {
99 	FileData    *fdata;
100 	FrCommand   *comm = FR_COMMAND (data);
101 	char       **fields;
102 	const char  *name_field;
103 	gint         line_l;
104 
105 	g_return_if_fail (line != NULL);
106 
107 	/* check whether unzip gave the empty archive warning. */
108 
109 	if (FR_COMMAND_ZIP (comm)->is_empty)
110 		return;
111 
112 	line_l = strlen (line);
113 
114 	if (line_l == 0)
115 		return;
116 
117 	if (strcmp (line, EMPTY_ARCHIVE_WARNING) == 0) {
118 		FR_COMMAND_ZIP (comm)->is_empty = TRUE;
119 		return;
120 	}
121 
122 	/* ignore lines that do not describe a file or a
123 	 * directory. */
124 	if ((line[0] != '?') && (line[0] != 'd') && (line[0] != '-'))
125 		return;
126 
127 	/**/
128 
129 	fdata = file_data_new ();
130 
131 	fields = split_line (line, 7);
132 	fdata->size = g_ascii_strtoull (fields[3], NULL, 10);
133 	fdata->modified = mktime_from_string (fields[6]);
134 	fdata->encrypted = (*fields[4] == 'B') || (*fields[4] == 'T');
135 	g_strfreev (fields);
136 
137 	/* Full path */
138 
139 	name_field = get_last_field (line, 8);
140 
141 	if (*name_field == '/') {
142 		fdata->full_path = g_strdup (name_field);
143 		fdata->original_path = fdata->full_path;
144 	} else {
145 		fdata->full_path = g_strconcat ("/", name_field, NULL);
146 		fdata->original_path = fdata->full_path + 1;
147 	}
148 
149 	fdata->link = NULL;
150 
151 	fdata->dir = line[0] == 'd';
152 	if (fdata->dir)
153 		fdata->name = dir_name_from_path (fdata->full_path);
154 	else
155 		fdata->name = g_strdup (file_name_from_path (fdata->full_path));
156 	fdata->path = remove_level_from_path (fdata->full_path);
157 
158 	if (*fdata->name == 0)
159 		file_data_free (fdata);
160 	else
161 		fr_command_add_file (comm, fdata);
162 }
163 
164 
165 static void
add_password_arg(FrCommand * comm,const char * password)166 add_password_arg (FrCommand  *comm,
167 		  const char *password)
168 {
169 	if ((password != NULL) && (password[0] != '\0')) {
170 		fr_process_add_arg (comm->process, "-P");
171 		fr_process_add_arg (comm->process, password);
172 	}
173 }
174 
175 
176 static void
list__begin(gpointer data)177 list__begin (gpointer data)
178 {
179 	FrCommandZip *comm = data;
180 
181 	comm->is_empty = FALSE;
182 }
183 
184 
185 static void
fr_command_zip_list(FrCommand * comm)186 fr_command_zip_list (FrCommand  *comm)
187 {
188 	fr_process_set_out_line_func (comm->process, list__process_line, comm);
189 
190 	fr_process_begin_command (comm->process, "unzip");
191 	fr_process_set_begin_func (comm->process, list__begin, comm);
192 	fr_process_add_arg (comm->process, "-ZTs");
193 	fr_process_add_arg (comm->process, "--");
194 	fr_process_add_arg (comm->process, comm->filename);
195 	fr_process_end_command (comm->process);
196 	fr_process_start (comm->process);
197 }
198 
199 
200 static void
process_line__common(char * line,gpointer data)201 process_line__common (char     *line,
202 		      gpointer  data)
203 {
204 	FrCommand  *comm = FR_COMMAND (data);
205 
206 	if (line == NULL)
207 		return;
208 
209 	if (comm->n_files != 0) {
210 		double fraction = (double) ++comm->n_file / (comm->n_files + 1);
211 		fr_command_progress (comm, fraction);
212 	}
213 	else
214 		fr_command_message (comm, line);
215 }
216 
217 
218 static void
fr_command_zip_add(FrCommand * comm,const char * from_file,GList * file_list,const char * base_dir,gboolean update,gboolean recursive)219 fr_command_zip_add (FrCommand     *comm,
220 		    const char    *from_file,
221 		    GList         *file_list,
222 		    const char    *base_dir,
223 		    gboolean       update,
224 		    gboolean       recursive)
225 {
226 	GList *scan;
227 
228 	fr_process_set_out_line_func (FR_COMMAND (comm)->process,
229 				      process_line__common,
230 				      comm);
231 
232 	fr_process_begin_command (comm->process, "zip");
233 
234 	if (base_dir != NULL)
235 		fr_process_set_working_dir (comm->process, base_dir);
236 
237 	/* preserve links. */
238 	fr_process_add_arg (comm->process, "-y");
239 
240 	if (update)
241 		fr_process_add_arg (comm->process, "-u");
242 
243 	add_password_arg (comm, comm->password);
244 
245 	switch (comm->compression) {
246 	case FR_COMPRESSION_VERY_FAST:
247 		fr_process_add_arg (comm->process, "-1"); break;
248 	case FR_COMPRESSION_FAST:
249 		fr_process_add_arg (comm->process, "-3"); break;
250 	case FR_COMPRESSION_NORMAL:
251 		fr_process_add_arg (comm->process, "-6"); break;
252 	case FR_COMPRESSION_MAXIMUM:
253 		fr_process_add_arg (comm->process, "-9"); break;
254 	}
255 
256 	fr_process_add_arg (comm->process, comm->filename);
257 	fr_process_add_arg (comm->process, "--");
258 
259 	for (scan = file_list; scan; scan = scan->next)
260 		fr_process_add_arg (comm->process, scan->data);
261 
262 	fr_process_end_command (comm->process);
263 }
264 
265 
266 static void
fr_command_zip_delete(FrCommand * comm,const char * from_file,GList * file_list)267 fr_command_zip_delete (FrCommand  *comm,
268 		       const char *from_file,
269 		       GList      *file_list)
270 {
271 	GList *scan;
272 
273 	fr_process_set_out_line_func (FR_COMMAND (comm)->process,
274 				      process_line__common,
275 				      comm);
276 
277 	fr_process_begin_command (comm->process, "zip");
278 	fr_process_add_arg (comm->process, "-d");
279 
280 	fr_process_add_arg (comm->process, comm->filename);
281 	fr_process_add_arg (comm->process, "--");
282 
283 	for (scan = file_list; scan; scan = scan->next) {
284 		char *escaped;
285 
286  		escaped = escape_str (scan->data, ZIP_SPECIAL_CHARACTERS);
287  		fr_process_add_arg (comm->process, escaped);
288  		g_free (escaped);
289 	}
290 
291 	fr_process_end_command (comm->process);
292 }
293 
294 
295 static void
fr_command_zip_extract(FrCommand * comm,const char * from_file,GList * file_list,const char * dest_dir,gboolean overwrite,gboolean skip_older,gboolean junk_paths)296 fr_command_zip_extract (FrCommand  *comm,
297 			const char *from_file,
298 			GList      *file_list,
299 			const char *dest_dir,
300 			gboolean    overwrite,
301 			gboolean    skip_older,
302 			gboolean    junk_paths)
303 {
304 	GList *scan;
305 
306 	fr_process_set_out_line_func (FR_COMMAND (comm)->process,
307 				      process_line__common,
308 				      comm);
309 
310 	fr_process_begin_command (comm->process, "unzip");
311 
312 	if (dest_dir != NULL) {
313 		fr_process_add_arg (comm->process, "-d");
314 		fr_process_add_arg (comm->process, dest_dir);
315 	}
316 	if (overwrite)
317 		fr_process_add_arg (comm->process, "-o");
318 	else
319 		fr_process_add_arg (comm->process, "-n");
320 	if (skip_older)
321 		fr_process_add_arg (comm->process, "-u");
322 	if (junk_paths)
323 		fr_process_add_arg (comm->process, "-j");
324 	add_password_arg (comm, comm->password);
325 
326 	fr_process_add_arg (comm->process, "--");
327 	fr_process_add_arg (comm->process, comm->filename);
328 
329 	for (scan = file_list; scan; scan = scan->next) {
330 		char *escaped;
331 
332  		escaped = escape_str (scan->data, ZIP_SPECIAL_CHARACTERS);
333  		fr_process_add_arg (comm->process, escaped);
334  		g_free (escaped);
335 	}
336 
337 	fr_process_end_command (comm->process);
338 }
339 
340 
341 static void
fr_command_zip_test(FrCommand * comm)342 fr_command_zip_test (FrCommand   *comm)
343 {
344 	fr_process_begin_command (comm->process, "unzip");
345 	fr_process_add_arg (comm->process, "-t");
346 	add_password_arg (comm, comm->password);
347 	fr_process_add_arg (comm->process, "--");
348 	fr_process_add_arg (comm->process, comm->filename);
349 	fr_process_end_command (comm->process);
350 }
351 
352 
353 static void
fr_command_zip_handle_error(FrCommand * comm,FrProcError * error)354 fr_command_zip_handle_error (FrCommand   *comm,
355 			     FrProcError *error)
356 {
357 	if (error->type != FR_PROC_ERROR_NONE) {
358 		if (error->status <= 1)
359 			error->type = FR_PROC_ERROR_NONE;
360 		else if ((error->status == 82) || (error->status == 5))
361 			error->type = FR_PROC_ERROR_ASK_PASSWORD;
362 		else {
363 			GList *output;
364 			GList *scan;
365 
366 			if (comm->action == FR_ACTION_TESTING_ARCHIVE)
367 				output = comm->process->out.raw;
368 			else
369 				output = comm->process->err.raw;
370 
371 			for (scan = g_list_last (output); scan; scan = scan->prev) {
372 				char *line = scan->data;
373 
374 				if (strstr (line, "incorrect password") != NULL) {
375 					error->type = FR_PROC_ERROR_ASK_PASSWORD;
376 					break;
377 				}
378 			}
379 		}
380 	}
381 }
382 
383 
384 const char *zip_mime_type[] = { "application/x-cbz",
385 				"application/x-ear",
386 				"application/x-ms-dos-executable",
387 				"application/x-war",
388 				"application/zip",
389 				NULL };
390 
391 
392 static const char **
fr_command_zip_get_mime_types(FrCommand * comm)393 fr_command_zip_get_mime_types (FrCommand *comm)
394 {
395 	return zip_mime_type;
396 }
397 
398 
399 static FrCommandCap
fr_command_zip_get_capabilities(FrCommand * comm,const char * mime_type,gboolean check_command)400 fr_command_zip_get_capabilities (FrCommand  *comm,
401 			         const char *mime_type,
402 				 gboolean    check_command)
403 {
404 	FrCommandCap capabilities;
405 
406 	capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES | FR_COMMAND_CAN_ENCRYPT;
407 	if (is_program_available ("zip", check_command)) {
408 		if (strcmp (mime_type, "application/x-ms-dos-executable") == 0)
409 			capabilities |= FR_COMMAND_CAN_READ;
410 		else
411 			capabilities |= FR_COMMAND_CAN_WRITE;
412 	}
413 	if (is_program_available ("unzip", check_command))
414 		capabilities |= FR_COMMAND_CAN_READ;
415 
416 	return capabilities;
417 }
418 
419 
420 static const char *
fr_command_zip_get_packages(FrCommand * comm,const char * mime_type)421 fr_command_zip_get_packages (FrCommand  *comm,
422 			     const char *mime_type)
423 {
424 	return PACKAGES ("zip,unzip");
425 }
426 
427 
428 static void
fr_command_zip_class_init(FrCommandZipClass * class)429 fr_command_zip_class_init (FrCommandZipClass *class)
430 {
431 	GObjectClass   *gobject_class = G_OBJECT_CLASS (class);
432 	FrCommandClass *afc;
433 
434 	parent_class = g_type_class_peek_parent (class);
435 	afc = (FrCommandClass*) class;
436 
437 	gobject_class->finalize = fr_command_zip_finalize;
438 
439 	afc->list             = fr_command_zip_list;
440 	afc->add              = fr_command_zip_add;
441 	afc->delete_           = fr_command_zip_delete;
442 	afc->extract          = fr_command_zip_extract;
443 	afc->test             = fr_command_zip_test;
444 	afc->handle_error     = fr_command_zip_handle_error;
445 	afc->get_mime_types   = fr_command_zip_get_mime_types;
446 	afc->get_capabilities = fr_command_zip_get_capabilities;
447 	afc->get_packages     = fr_command_zip_get_packages;
448 }
449 
450 
451 static void
fr_command_zip_init(FrCommand * comm)452 fr_command_zip_init (FrCommand *comm)
453 {
454 	comm->propAddCanUpdate             = TRUE;
455 	comm->propAddCanReplace            = TRUE;
456 	comm->propAddCanStoreFolders       = TRUE;
457 	comm->propExtractCanAvoidOverwrite = TRUE;
458 	comm->propExtractCanSkipOlder      = TRUE;
459 	comm->propExtractCanJunkPaths      = TRUE;
460 	comm->propPassword                 = TRUE;
461 	comm->propTest                     = TRUE;
462 
463 	FR_COMMAND_ZIP (comm)->is_empty = FALSE;
464 }
465 
466 
467 static void
fr_command_zip_finalize(GObject * object)468 fr_command_zip_finalize (GObject *object)
469 {
470 	g_return_if_fail (object != NULL);
471 	g_return_if_fail (FR_IS_COMMAND_ZIP (object));
472 
473 	/* Chain up */
474 	if (G_OBJECT_CLASS (parent_class)->finalize)
475 		G_OBJECT_CLASS (parent_class)->finalize (object);
476 }
477 
478 
479 GType
fr_command_zip_get_type()480 fr_command_zip_get_type ()
481 {
482 	static GType type = 0;
483 
484 	if (! type) {
485 		GTypeInfo type_info = {
486 			sizeof (FrCommandZipClass),
487 			NULL,
488 			NULL,
489 			(GClassInitFunc) fr_command_zip_class_init,
490 			NULL,
491 			NULL,
492 			sizeof (FrCommandZip),
493 			0,
494 			(GInstanceInitFunc) fr_command_zip_init
495 		};
496 
497 		type = g_type_register_static (FR_TYPE_COMMAND,
498 					       "FRCommandZip",
499 					       &type_info,
500 					       0);
501 	}
502 
503 	return type;
504 }
505