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 <errno.h>
21 #include <string.h>
22 #include <glib/gprintf.h>
23 #include "rpm.h"
24 #include "date_utils.h"
25 #include "main.h"
26 #include "string_utils.h"
27 #include "support.h"
28 #include "window.h"
29 
30 #define LEAD_LEN 96
31 #define HDRSIG_MAGIC_LEN 3
32 #define HDRSIG_VERSION_LEN 1
33 #define HDRSIG_RESERVED_LEN 4
34 #define HDRSIG_LEAD_IN_LEN (HDRSIG_MAGIC_LEN + HDRSIG_VERSION_LEN + HDRSIG_RESERVED_LEN)
35 #define SIGNATURE_START (LEAD_LEN + HDRSIG_LEAD_IN_LEN)
36 #define HDRSIG_ENTRY_INFO_LEN 8
37 #define HDRSIG_ENTRY_INDEX_LEN 16
38 
xa_rpm_ask(XArchive * archive)39 void xa_rpm_ask (XArchive *archive)
40 {
41 	archive->can_extract = TRUE;
42 	archive->can_full_path[0] = TRUE;
43 	archive->can_touch = TRUE;
44 	archive->can_overwrite = TRUE;
45 }
46 
xa_rpm2cpio(XArchive * archive)47 static gchar *xa_rpm2cpio (XArchive *archive)
48 {
49 	unsigned char bytes[HDRSIG_ENTRY_INFO_LEN];
50 	int datalen, entries;
51 	long offset;
52 	gchar *cpio_z, *ibs, *command, *executable;
53 	FILE *stream;
54 	gboolean success;
55 	ArchiveType xa;
56 
57 	signal(SIGPIPE, SIG_IGN);
58 	stream = fopen(archive->path[0], "r");
59 
60 	if (stream == NULL)
61 	{
62 		gchar *msg, *err;
63 
64 		msg = g_strdup_printf(_("Can't open RPM file %s:"), archive->path[0]);
65 		err = g_strconcat(msg, " ", g_strerror(errno), NULL);
66 
67 		g_free(msg);
68 
69 		return err;
70 	}
71 
72 	/* Signature section */
73 	if (fseek(stream, SIGNATURE_START, SEEK_CUR) == -1)
74 	{
75 		fclose (stream);
76 		return g_strconcat(_("Can't fseek to position 104:"), " ", g_strerror(errno), NULL);
77 	}
78 	if (fread(bytes, sizeof(*bytes), HDRSIG_ENTRY_INFO_LEN, stream) != HDRSIG_ENTRY_INFO_LEN)
79 	{
80 		fclose ( stream );
81 		return g_strconcat(_("Can't read data from file:"), " ", g_strerror(errno), NULL);
82 	}
83 	entries = 256 * (256 * (256 * bytes[0] + bytes[1]) + bytes[2]) + bytes[3];
84 	datalen = 256 * (256 * (256 * bytes[4] + bytes[5]) + bytes[6]) + bytes[7];
85 	datalen += (16 - (datalen % 16)) % 16;  // header section is aligned
86 	offset = HDRSIG_ENTRY_INDEX_LEN * entries + datalen;
87 
88 	/* Header section */
89 	if (fseek(stream, offset, SEEK_CUR))
90 	{
91 		fclose (stream);
92 		return g_strconcat(_("Can't fseek in file:"), " ", g_strerror(errno), NULL);
93 	}
94 	if (fread(bytes, sizeof(*bytes), HDRSIG_ENTRY_INFO_LEN, stream) != HDRSIG_ENTRY_INFO_LEN)
95 	{
96 		fclose ( stream );
97 		return g_strconcat(_("Can't read data from file:"), " ", g_strerror(errno), NULL);
98 	}
99 	entries = 256 * (256 * (256 * bytes[0] + bytes[1]) + bytes[2]) + bytes[3];
100 	datalen = 256 * (256 * (256 * bytes[4] + bytes[5]) + bytes[6]) + bytes[7];
101 	offset = HDRSIG_ENTRY_INDEX_LEN * entries + datalen;
102 	offset += ftell(stream);  // offset from top
103 
104 	fclose(stream);
105 
106 	/* create a unique temp dir in /tmp */
107 	if (!xa_create_working_directory(archive))
108 		return g_strdup("");
109 
110 	cpio_z = g_strconcat(archive->working_dir, "/xa-tmp.cpio_z", NULL);
111 	ibs = g_strdup_printf("%lu", offset);
112 
113 	/* run dd to have the payload (compressed cpio archive) in /tmp */
114 	command = g_strconcat("dd if=", archive->path[1], " ibs=", ibs, " skip=1 of=", cpio_z, NULL);
115 	g_free(ibs);
116 
117 	success = xa_run_command(archive, command);
118 	g_free(command);
119 
120 	if (!success)
121 	{
122 		g_free(cpio_z);
123 		return g_strdup("");
124 	}
125 
126 	xa = xa_detect_archive_type(cpio_z);
127 
128 	switch (xa.type)
129 	{
130 		case XARCHIVETYPE_GZIP:
131 			executable = "gzip -dc ";
132 			break;
133 
134 		case XARCHIVETYPE_BZIP2:
135 			executable = "bzip2 -dc ";
136 			break;
137 
138 		case XARCHIVETYPE_LZMA:
139 		case XARCHIVETYPE_XZ:
140 			executable = "xz -dc ";
141 			break;
142 
143 		default:
144 			g_free(cpio_z);
145 			return g_strdup(_("Unknown compression type!"));
146 	}
147 
148 	command = g_strconcat("sh -c \"", executable, cpio_z, " > ", archive->working_dir, "/xa-tmp.cpio\"", NULL);
149 	g_free(cpio_z);
150 
151 	success = xa_run_command(archive, command);
152 	g_free(command);
153 
154 	return (success ? NULL : g_strdup(""));
155 }
156 
xa_cpio_parse_output(gchar * line,XArchive * archive)157 static void xa_cpio_parse_output (gchar *line, XArchive *archive)
158 {
159 	XEntry *entry;
160 	gchar *filename, time[6];
161 	gpointer item[7];
162 	gint n = 0, a = 0 ,linesize = 0;
163 
164 	linesize = strlen(line);
165 
166 	/* Permissions */
167 	line[10] = '\0';
168 	item[4] = line;
169 	a = 11;
170 
171 	/* Hard Link */
172 	for(n=a; n < linesize && line[n] == ' '; ++n);
173 	line[++n] = '\0';
174 	//item[5] = line + a;
175 	n++;
176 	a = n;
177 
178 	/* Owner */
179 	for(; n < linesize && line[n] != ' '; ++n);
180 	line[n] = '\0';
181 	item[5] = line + a;
182 	n++;
183 
184 	/* Group */
185 	for(; n < linesize && line[n] == ' '; ++n);
186 	a = n;
187 
188 	for(; n < linesize && line[n] != ' '; ++n);
189 	line[n] = '\0';
190 	item[6] = line + a;
191 	n++;
192 
193 	/* Size */
194 	for(; n < linesize && line[n] == ' '; ++n);
195 	a = n;
196 
197 	for(; n < linesize && line[n] != ' '; ++n);
198 	line[n] = '\0';
199 	item[1] = line + a;
200 	n++;
201 
202 	/* Date and Time */
203 	line[54] = '\0';
204 	item[2] = line + n;
205 	n = 55;
206 
207 	/* Time */
208 	if (((char *) item[2])[9] == ':')
209 	{
210 		memcpy(time, item[2] + 7, 5);
211 		time[5] = 0;
212 	}
213 	else
214 		strcpy(time, "-----");
215 
216 	item[2] = date_MMM_dD_HourYear(item[2]);
217 	item[3] = time;
218 
219 	line[linesize-1] = '\0';
220 	filename = line + n;
221 
222 	/* Symbolic link */
223 	gchar *temp = g_strrstr (filename,"->");
224 	if (temp)
225 	{
226 		a = 3;
227 		gint len = strlen(filename) - strlen(temp);
228 		item[0] = filename + a + len;
229 		filename[strlen(filename) - strlen(temp)] = '\0';
230 	}
231 	else
232 		item[0] = NULL;
233 
234 	if(line[0] == 'd')
235 	{
236 		/* Work around for cpio, which does
237 		 * not output / with directories */
238 
239 		if(line[linesize-2] != '/')
240 			filename = g_strconcat(line + n, "/", NULL);
241 		else
242 			filename = g_strdup(line + n);
243 	}
244 	else
245 		filename = g_strdup(line + n);
246 
247 	entry = xa_set_archive_entries_for_each_row(archive, filename, item);
248 
249 	if (entry)
250 	{
251 		if (!entry->is_dir)
252 			archive->files++;
253 
254 		archive->files_size += g_ascii_strtoull(item[1], NULL, 0);
255 	}
256 
257 	g_free (filename);
258 }
259 
xa_rpm_list(XArchive * archive)260 void xa_rpm_list (XArchive *archive)
261 {
262 	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_STRING, G_TYPE_POINTER};
263 	const gchar *titles[] = {_("Points to"), _("Original Size"), _("Date"), _("Time"), _("Permissions"), _("Owner"), _("Group")};
264 	gchar *result, *command;
265 	guint i;
266 
267 	result = xa_rpm2cpio(archive);
268 
269 	archive->files_size = 0;
270 	archive->files = 0;
271 
272 	archive->columns = 10;
273 	archive->size_column = 3;
274 	archive->column_types = g_malloc0(sizeof(types));
275 
276 	for (i = 0; i < archive->columns; i++)
277 		archive->column_types[i] = types[i];
278 
279 	xa_create_liststore(archive, titles);
280 
281 	if (result != NULL)
282 	{
283 		if (*result)
284 			command = g_strconcat("sh -c \"echo ", result, " >&2; exit 1\"", NULL);
285 		else
286 			command = g_strdup("sh -c \"\"");
287 
288 		g_free(result);
289 	}
290 	else
291 		command = g_strconcat(archiver[archive->type].program[0], " -tv -I ", archive->working_dir, "/xa-tmp.cpio", NULL);
292 
293 	archive->parse_output = xa_cpio_parse_output;
294 	xa_spawn_async_process (archive,command);
295 	g_free(command);
296 }
297 
298 /*
299  * Note: cpio lists ' ' as '\ ', '"' as '\"' and '\' as '\\' while it
300  * extracts ' ', '"' and '\' respectively, i.e. file names containing
301  * one of these three characters can't be handled entirely.
302  */
303 
xa_rpm_extract(XArchive * archive,GSList * file_list)304 gboolean xa_rpm_extract (XArchive *archive, GSList *file_list)
305 {
306 	GString *files;
307 	gchar *extract_to, *command;
308 	gboolean result;
309 
310 	if (archive->do_full_path)
311 		extract_to = g_strdup(archive->extraction_dir);
312 	else
313 	{
314 		if (!xa_create_working_directory(archive))
315 			return FALSE;
316 
317 		extract_to = g_strconcat(archive->working_dir, "/xa-tmp.XXXXXX", NULL);
318 
319 		if (!g_mkdtemp(extract_to))
320 		{
321 			g_free(extract_to);
322 			return FALSE;
323 		}
324 	}
325 
326 	files = xa_quote_filenames(file_list, "*?[]\"", FALSE);
327 	archive->child_dir = g_strdup(extract_to);
328 	command = g_strconcat(archiver[archive->type].program[0], " -id",
329 	                      archive->do_touch ? "" : " -m",
330 	                      archive->do_overwrite ? " -u" : "",
331 	                      " -I ", archive->working_dir, "/xa-tmp.cpio",
332 	                      files->str, NULL);
333 	result = xa_run_command(archive, command);
334 	g_free(command);
335 
336 	g_free(archive->child_dir);
337 	archive->child_dir = NULL;
338 
339 	/* collect all files that have been extracted to move them without full path */
340 	if (result && !archive->do_full_path)
341 	{
342 		GString *all_files = xa_collect_files_in_dir(extract_to);
343 
344 		archive->child_dir = g_strdup(extract_to);
345 		command = g_strconcat("mv",
346 		                      archive->do_overwrite ? " -f" : " -n",
347 		                      all_files->str, " ", archive->extraction_dir, NULL);
348 		g_string_free(all_files, TRUE);
349 
350 		result = xa_run_command(archive, command);
351 		g_free(command);
352 
353 		g_free(archive->child_dir);
354 		archive->child_dir = NULL;
355 	}
356 
357 	g_free(extract_to);
358 	g_string_free(files,TRUE);
359 
360 	return result;
361 }
362