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 "tar.h"
22 #include "gzip_et_al.h"
23 #include "main.h"
24 #include "string_utils.h"
25 #include "support.h"
26 #include "window.h"
27 
isTar(FILE * file)28 gboolean isTar (FILE *file)
29 {
30 	unsigned char magic[8];
31 	gboolean result;
32 
33 	fseek(file, 0, SEEK_SET);
34 
35 	if (fseek(file, 257, SEEK_CUR) != 0 ||
36 	    fread(magic, sizeof(magic), 1, file) != 1)
37 	{
38 		fseek(file, 0, SEEK_SET);
39 		return FALSE;
40 	}
41 
42 	result = (memcmp(magic, "ustar" "\x00" "00", sizeof(magic)) == 0 ||
43 	          memcmp(magic, "ustar" "  " "\x00", sizeof(magic)) == 0);
44 
45 	if (!result &&
46 	    memcmp(magic, "\x00\x00\x00\x00\x00\x00\x00\x00", sizeof(magic)) == 0)
47 	{
48 		/* next block */
49 		if (fseek(file, 512, SEEK_SET) == 0 &&
50 		    fread(magic, sizeof(*magic), 1, file) == 1)
51 			result = (*magic != 0);
52 	}
53 
54 	fseek(file, 0, SEEK_SET);
55 
56 	return result;
57 }
58 
xa_tar_get_compressor_type(XArchive * archive)59 static XArchiveType xa_tar_get_compressor_type (XArchive *archive)
60 {
61 	switch (archive->type)
62 	{
63 		case XARCHIVETYPE_TAR_BZIP2:
64 			return XARCHIVETYPE_BZIP2;
65 
66 		case XARCHIVETYPE_TAR_COMPRESS:
67 			return XARCHIVETYPE_COMPRESS;
68 
69 		case XARCHIVETYPE_TAR_GZIP:
70 			return XARCHIVETYPE_GZIP;
71 
72 		case XARCHIVETYPE_TAR_LRZIP:
73 			return XARCHIVETYPE_LRZIP;
74 
75 		case XARCHIVETYPE_TAR_LZ4:
76 			return XARCHIVETYPE_LZ4;
77 
78 		case XARCHIVETYPE_TAR_LZIP:
79 			return XARCHIVETYPE_LZIP;
80 
81 		case XARCHIVETYPE_TAR_LZMA:
82 			return XARCHIVETYPE_LZMA;
83 
84 		case XARCHIVETYPE_TAR_LZOP:
85 			return XARCHIVETYPE_LZOP;
86 
87 		case XARCHIVETYPE_TAR_XZ:
88 			return XARCHIVETYPE_XZ;
89 
90 		case XARCHIVETYPE_TAR_ZSTD:
91 			return XARCHIVETYPE_ZSTD;
92 
93 		default:
94 			return XARCHIVETYPE_TAR;
95 	}
96 }
97 
xa_tar_ask(XArchive * archive)98 void xa_tar_ask (XArchive *archive)
99 {
100 	archive->can_extract = TRUE;
101 	archive->can_add = archiver[xa_tar_get_compressor_type(archive)].is_compressor;
102 	archive->can_delete = archiver[xa_tar_get_compressor_type(archive)].is_compressor;
103 	archive->can_full_path[0] = TRUE;
104 	archive->can_full_path[1] = archiver[xa_tar_get_compressor_type(archive)].is_compressor;
105 	archive->can_touch = TRUE;
106 	archive->can_overwrite = TRUE;
107 	archive->can_update[0] = TRUE;
108 	archive->can_update[1] = archiver[xa_tar_get_compressor_type(archive)].is_compressor;
109 	archive->can_move = archiver[xa_tar_get_compressor_type(archive)].is_compressor;
110 
111 	/* this is solely for display with the archive properties */
112 	if (!archiver[archive->type].program[0])
113 	{
114 		archiver[archive->type].program[0] = g_strdup(archiver[xa_tar_get_compressor_type(archive)].program[0]);
115 		archiver[archive->type].program[1] = g_strdup(archiver[XARCHIVETYPE_TAR].program[0]);
116 	}
117 }
118 
xa_tar_parse_output(gchar * line,XArchive * archive)119 static void xa_tar_parse_output (gchar *line, XArchive *archive)
120 {
121 	XEntry *entry;
122 	gchar *filename;
123 	gpointer item[6];
124 	gint n = 0, a = 0 ,linesize = 0;
125 
126 	linesize = strlen(line);
127 
128 	/* Permissions */
129 	line[10] = '\0';
130 	item[4] = line;
131 
132 	/* Owner */
133 	for(n=13; n < linesize; ++n)
134 		if(line[n] == ' ')
135 			break;
136 	line[n] = '\0';
137 	item[5] = line+11;
138 
139 	/* Size */
140 	for(++n; n < linesize; ++n)
141 		if(line[n] >= '0' && line[n] <= '9')
142 			break;
143 	a = n;
144 
145 	for(; n < linesize; ++n)
146 		if(line[n] == ' ')
147 			break;
148 
149 	line[n] = '\0';
150 	item[1] = line + a;
151 	a = ++n;
152 
153 	/* Date */
154 	for(; n < linesize; n++)
155 		if(line[n] == ' ')
156 			break;
157 
158 	line[n] = '\0';
159 	item[2] = line + a;
160 
161 	/* Time */
162 	a = ++n;
163 	for (; n < linesize; n++)
164 		if (line[n] == ' ')
165 			break;
166 
167 	line[n] = '\0';
168 	item[3] = line + a;
169 	n++;
170 	line[linesize-1] = '\0';
171 
172 	filename = line + n;
173 
174 	/* Symbolic link */
175 	gchar *temp = g_strrstr (filename,"->");
176 	if (temp)
177 	{
178 		gint len = strlen(filename) - strlen(temp);
179 		item[0] = (filename +=3) + len;
180 		filename[strlen(filename) - strlen(temp)-1] = '\0';
181 	}
182 	else
183 		item[0] = NULL;
184 
185 	if(line[0] == 'd')
186 	{
187 		/* Work around for gtar, which does not output / with directories */
188 		if(line[linesize-2] != '/')
189 			filename = g_strconcat(line + n, "/", NULL);
190 		else
191 			filename = g_strdup(line + n);
192 	}
193 	else
194 		filename = g_strdup(line + n);
195 
196 	entry = xa_set_archive_entries_for_each_row(archive, filename, item);
197 
198 	if (entry)
199 	{
200 		entry->is_encrypted = (archive->password != NULL);
201 
202 		if (!entry->is_dir)
203 			archive->files++;
204 
205 		archive->files_size += g_ascii_strtoull(item[1], NULL, 0);
206 	}
207 
208 	g_free(filename);
209 }
210 
xa_tar_list(XArchive * archive)211 void xa_tar_list (XArchive *archive)
212 {
213 	const GType types[] = {GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER};
214 	const gchar *titles[] = {_("Points to"), _("Original Size"), _("Date"), _("Time"), _("Permissions"), _("Owner/Group")};
215 	gchar *command;
216 	guint i;
217 
218 	if (!archive->path[2])
219 		archive->path[2] = g_shell_quote(archive->path[0]);
220 
221 	archive->files = 0;
222 	archive->files_size = 0;
223 
224 	command = g_strconcat(archiver[XARCHIVETYPE_TAR].program[0], " -tvf ", archive->path[2], NULL);
225 	archive->parse_output = xa_tar_parse_output;
226 	xa_spawn_async_process(archive, command);
227 	g_free(command);
228 
229 	archive->columns = 9;
230 	archive->size_column = 3;
231 	archive->column_types = g_malloc0(sizeof(types));
232 
233 	for (i = 0; i < archive->columns; i++)
234 		archive->column_types[i] = types[i];
235 
236 	xa_create_liststore(archive, titles);
237 }
238 
239 /*
240  * Note: tar lists '\' as '\\' while it extracts '\', i.e.
241  * file names containing this character can't be handled entirely.
242  */
243 
xa_tar_extract(XArchive * archive,GSList * file_list)244 gboolean xa_tar_extract (XArchive *archive, GSList *file_list)
245 {
246 	GString *files;
247 	gchar *extract_to, *command;
248 	gboolean result;
249 
250 	if (archive->do_full_path)
251 		extract_to = g_strdup(archive->extraction_dir);
252 	else
253 	{
254 		if (!xa_create_working_directory(archive))
255 			return FALSE;
256 
257 		extract_to = g_strconcat(archive->working_dir, "/xa-tmp.XXXXXX", NULL);
258 
259 		if (!g_mkdtemp(extract_to))
260 		{
261 			g_free(extract_to);
262 			return FALSE;
263 		}
264 	}
265 
266 	files = xa_quote_filenames(file_list, NULL, TRUE);
267 	command = g_strconcat(archiver[XARCHIVETYPE_TAR].program[0],
268 	                      " -x --no-recursion --no-wildcards",
269 	                      " -f ", archive->path[2],
270 	                      archive->do_touch ? " -m" : "",
271 	                      archive->do_overwrite ? "" : (archive->do_update ? " --keep-newer-files" : " -k"),
272 	                      " -C ", extract_to, files->str, NULL);
273 
274 	result = xa_run_command(archive, command);
275 	g_free(command);
276 
277 	/* collect all files that have been extracted to move them without full path */
278 	if (result && !archive->do_full_path)
279 	{
280 		GString *all_files = xa_collect_files_in_dir(extract_to);
281 
282 		archive->child_dir = g_strdup(extract_to);
283 		command = g_strconcat("mv",
284 		                      archive->do_overwrite ? " -f" : " -n",
285 		                      archive->do_update ? " -fu" : "",
286 		                      all_files->str, " ", archive->extraction_dir, NULL);
287 		g_string_free(all_files, TRUE);
288 
289 		result = xa_run_command(archive, command);
290 		g_free(command);
291 
292 		g_free(archive->child_dir);
293 		archive->child_dir = NULL;
294 	}
295 
296 	g_free(extract_to);
297 	g_string_free(files, TRUE);
298 
299 	return result;
300 }
301 
xa_tar_add(XArchive * archive,GSList * file_list,gchar * compression)302 void xa_tar_add (XArchive *archive, GSList *file_list, gchar *compression)
303 {
304 	GString *files;
305 	gchar *command;
306 
307 	if (archive->location_path != NULL)
308 		archive->child_dir = g_strdup(archive->working_dir);
309 
310 	files = xa_quote_filenames(file_list, NULL, TRUE);
311 
312 	if (!g_file_test(archive->path[0], G_FILE_TEST_EXISTS))
313 	{
314 		if (archive->type == XARCHIVETYPE_TAR)
315 			archive->path[2] = g_shell_quote(archive->path[0]);
316 		else
317 		{
318 			gchar *workfile;
319 
320 			if (!xa_create_working_directory(archive))
321 			{
322 				g_string_free(files, TRUE);
323 				return;
324 			}
325 
326 			workfile = g_strconcat(archive->working_dir, "/xa-tmp.tar", NULL);
327 			archive->path[2] = g_shell_quote(workfile);
328 			g_free(workfile);
329 		}
330 
331 		command = g_strconcat(archiver[XARCHIVETYPE_TAR].program[0],
332 		                      " -c --no-recursion --no-wildcards",
333 		                      archive->do_move ? " --remove-files" : "",
334 		                      " -f ", archive->path[2], files->str, NULL);
335 	}
336 	else
337 		command = g_strconcat(archiver[XARCHIVETYPE_TAR].program[0],
338 		                      archive->do_update ? " -u" : " -r",
339 		                      " --no-recursion --no-wildcards",
340 		                      archive->do_move ? " --remove-files" : "",
341 		                      " -f ", archive->path[2], files->str, NULL);
342 
343 	if (archive->type != XARCHIVETYPE_TAR)
344 	{
345 		xa_run_command(archive, command);
346 		g_free(command);
347 
348 		command = xa_gzip_et_al_get_command(archiver[xa_tar_get_compressor_type(archive)].program[0], archive->path[2], archive->path[0], archive->password, xa_tar_get_compressor_type(archive));
349 	}
350 
351 	g_string_free(files, TRUE);
352 
353 	xa_run_command(archive, command);
354 	g_free(command);
355 }
356 
xa_tar_delete(XArchive * archive,GSList * file_list)357 void xa_tar_delete (XArchive *archive, GSList *file_list)
358 {
359 	GString *files;
360 	gchar *command;
361 
362 	files = xa_quote_filenames(file_list, NULL, TRUE);
363 	command = g_strconcat(archiver[XARCHIVETYPE_TAR].program[0], " --delete --no-recursion --no-wildcards -f ", archive->path[2], files->str, NULL);
364 
365 	if (archive->type != XARCHIVETYPE_TAR)
366 	{
367 		xa_run_command(archive, command);
368 		g_free(command);
369 
370 		command = xa_gzip_et_al_get_command(archiver[xa_tar_get_compressor_type(archive)].program[0], archive->path[2], archive->path[0], archive->password, xa_tar_get_compressor_type(archive));
371 	}
372 
373 	g_string_free(files, TRUE);
374 
375 	xa_run_command(archive, command);
376 	g_free(command);
377 }
378