xref: /haiku/src/apps/packageinstaller/PackageInfo.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
1 /*
2  * Copyright (c) 2007-2010, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Author:
6  *		Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7  */
8 
9 
10 #include "PackageInfo.h"
11 
12 #include <Alert.h>
13 #include <ByteOrder.h>
14 #include <Catalog.h>
15 #include <FindDirectory.h>
16 #include <Locale.h>
17 #include <Path.h>
18 #include <kernel/OS.h>
19 
20 
21 #undef B_TRANSLATION_CONTEXT
22 #define B_TRANSLATION_CONTEXT "PackageInfo"
23 
24 #define RETURN_AND_SET_STATUS(err) fStatus = err; \
25 	fprintf(stderr, "err at %s():%d: %x\n", __FUNCTION__, __LINE__, err); \
26 	return fStatus;
27 
28 const uint32 kSkipOffset = 33;
29 
30 // Section constants
31 enum {
32 	P_GROUPS_SECTION = 0,
33 	P_PATH_SECTION,
34 	P_USER_PATH_SECTION,
35 	P_LICENSE_SECTION
36 };
37 
38 
39 // Element constants
40 enum {
41 	P_NONE = 0,
42 	P_FILE,
43 	P_DIRECTORY,
44 	P_LINK,
45 	P_SCRIPT
46 };
47 
48 typedef enum {
49     B_BEBOX_PLATFORM = 0,
50     B_MAC_PLATFORM,
51     B_AT_CLONE_PLATFORM,
52     B_ENIAC_PLATFORM,
53     B_APPLE_II_PLATFORM,
54     B_CRAY_PLATFORM,
55     B_LISA_PLATFORM,
56     B_TI_994A_PLATFORM,
57     B_TIMEX_SINCLAIR_PLATFORM,
58     B_ORAC_1_PLATFORM,
59     B_HAL_PLATFORM,
60 	B_INVALID_PLATFORM
61 } platform_type;
62 
63 
64 PackageInfo::PackageInfo()
65 	:
66 	fStatus(B_NO_INIT),
67 	fPackageFile(0),
68 	fDescription(B_TRANSLATE("No package available.")),
69 	fProfiles(2),
70 	fHasImage(false)
71 {
72 }
73 
74 
75 PackageInfo::PackageInfo(const entry_ref *ref)
76 	:
77 	fStatus(B_NO_INIT),
78 	fPackageFile(new BFile(ref, B_READ_ONLY)),
79 	fDescription(B_TRANSLATE("No package selected.")),
80 	fProfiles(2),
81 	fHasImage(false)
82 {
83 	fStatus = Parse();
84 }
85 
86 
87 PackageInfo::~PackageInfo()
88 {
89 	pkg_profile *iter = 0;
90 	while (1) {
91 		iter = static_cast<pkg_profile *>(fProfiles.RemoveItem((int32)0));
92 		if (iter == NULL)
93 			break;
94 
95 		delete iter;
96 	}
97 
98 	PackageItem *file = 0;
99 	while (true) {
100 		file = static_cast<PackageItem *>(fFiles.RemoveItem((int32)0));
101 		if (file == NULL)
102 			break;
103 
104 		delete file;
105 	}
106 
107 	while (true) {
108 		file = static_cast<PackageScript *>(fScripts.RemoveItem((int32)0));
109 		if (file == NULL)
110 			break;
111 
112 		delete file;
113 	}
114 
115 	delete fPackageFile;
116 }
117 
118 
119 status_t
120 PackageInfo::Parse()
121 {
122 	// TODO: Clean up
123 	if (!fPackageFile || fPackageFile->InitCheck() != B_OK) {
124 		RETURN_AND_SET_STATUS(B_ERROR);
125 	}
126 
127 	// Check for the presence of the first AlB tag - as the 'magic number'.
128 	// This also ensures that the file header section is present - which
129 	// is a crucial pkg section
130 	char buffer[16];
131 	fPackageFile->Read(buffer, 8);
132 	if (buffer[0] != 'A' || buffer[1] != 'l' || buffer[2] != 'B'
133 		|| buffer[3] != 0x1a) {
134 		RETURN_AND_SET_STATUS(B_ERROR);
135 	}
136 
137 	fHasImage = false;
138 
139 	// Parse all known parts of the given .pkg file
140 
141 	uint32 i;
142 	int8 bytesRead;
143 	off_t actualSize = 0;
144 	fPackageFile->GetSize(&actualSize);
145 	uint64 fileSize = 0;
146 
147 	const char padding[7] = { 0, 0, 0, 0, 0, 0, 0 };
148 
149 	platform_type thisPlatform = B_INVALID_PLATFORM;
150 	cpu_topology_node_info topologyRoot;
151 	uint32 topologyNodeCount = 1;
152 	if (get_cpu_topology_info(&topologyRoot, &topologyNodeCount) == B_OK) {
153 		switch (topologyRoot.data.root.platform) {
154 			case B_CPU_x86:
155 				thisPlatform = B_AT_CLONE_PLATFORM;
156 				break;
157 
158 			default:
159 				break;
160 		}
161 	}
162 
163 	uint64 infoOffset = 0, groupsOffset = 0;
164 	uint64 length = 0;
165 
166 	// Parse the file header
167 	while (true) {
168 		bytesRead = fPackageFile->Read(buffer, 7);
169 		if (bytesRead != 7) {
170 			RETURN_AND_SET_STATUS(B_ERROR);
171 		}
172 
173 		if (!memcmp(buffer, "PhIn", 5)) {
174 		} else if (!memcmp(buffer, "FVer", 5)) {
175 			// Not used right now
176 			fPackageFile->Seek(4, SEEK_CUR);
177 			parser_debug("FVer\n");
178 		} else if (!memcmp(buffer, "AFla", 5)) {
179 			// Not used right now TODO: Check what this tag is for
180 			fPackageFile->Seek(8, SEEK_CUR);
181 			parser_debug("AFla\n");
182 		} else if (!memcmp(buffer, "FSiz", 5)) {
183 			fPackageFile->Read(&fileSize, 8);
184 			swap_data(B_UINT64_TYPE, &fileSize, sizeof(uint64),
185 					B_SWAP_BENDIAN_TO_HOST);
186 			parser_debug("FSiz %llu\n", fileSize);
187 		} else if (!memcmp(buffer, "COff", 5)) {
188 			fPackageFile->Read(&infoOffset, 8);
189 			swap_data(B_UINT64_TYPE, &infoOffset, sizeof(uint64),
190 					B_SWAP_BENDIAN_TO_HOST);
191 			parser_debug("COff %llu\n", infoOffset);
192 		} else if (!memcmp(buffer, "AOff", 5)) {
193 			fPackageFile->Read(&groupsOffset, 8);
194 			swap_data(B_UINT64_TYPE, &groupsOffset, sizeof(uint64),
195 					B_SWAP_BENDIAN_TO_HOST);
196 			parser_debug("AOff %llu\n", groupsOffset);
197 		} else if (!memcmp(buffer, padding, 7)) {
198 			// This means the end of this section - we should move to the
199 			// groups section.
200 			if (groupsOffset) {
201 				fPackageFile->Seek(groupsOffset, SEEK_SET);
202 			}
203 			parser_debug("End!\n");
204 			break;
205 		} else {
206 			RETURN_AND_SET_STATUS(B_ERROR);
207 		}
208 	}
209 
210 	fPackageFile->Read(buffer, 7);
211 	if (memcmp(buffer, "PkgA", 5) || !groupsOffset || !infoOffset) {
212 		RETURN_AND_SET_STATUS(B_ERROR);
213 	}
214 
215 	// Section header identifying constant byte sequences:
216 	const char groupsMarker[7] = { 0, 0, 0, 1, 0, 0, 4 };
217 	const char idMarker[7] = { 0, 0, 0, 2, 0, 0, 4 };
218 	const char pathMarker[7] = { 0, 0, 0, 3, 0, 0, 4 };
219 	const char upathMarker[7] = { 0, 0, 0, 4, 0, 0, 4 };
220 	const char licenseMarker[7] = { 0, 0, 0, 18, 0, 0, 4 };
221 	const char descMarker[7] = { 0, 0, 0, 5, 0, 0, 2 };
222 	const char helpMarker[7] = { 0, 0, 0, 10, 0, 0, 3 };
223 
224 	const char splashScreenMarker[7] = { 0, 0, 0, 8, 0, 0, 3 };
225 	const char disclaimerMarker[7] = { 0, 0, 0, 7, 0, 0, 3 };
226 
227 	const char nameMarker[7] = { 0, 0, 0, 13, 0, 0, 2 };
228 	const char versionMarker[7] = { 0, 0, 0, 14, 0, 0, 2 };
229 	const char devMarker[7] = { 0, 0, 0, 15, 0, 0, 2 };
230 	const char shortDescMarker[7] = { 0, 0, 0, 17, 0, 0, 2 };
231 
232 	int8 section = P_GROUPS_SECTION, installDirectoryFlag = 0;
233 
234 	pkg_profile group;
235 	BList groups(3), userPaths(3), systemPaths(10);
236 	bool groupStarted = false;
237 	parser_debug("Package Info reached!\n");
238 	// TODO: Maybe checking whether the needed number of bytes are read
239 	//	everytime would be a good idea
240 
241 	// Parse the package info section
242 	while (true) {
243 		bytesRead = fPackageFile->Read(buffer, 7);
244 		if (bytesRead != 7) {
245 			parser_debug("EOF!\n");
246 			break;
247 		}
248 
249 		if (!memcmp(buffer, groupsMarker, 7)) {
250 			section = P_GROUPS_SECTION;
251 			parser_debug("Got to Groups section\n");
252 			continue;
253 		} else if (!memcmp(buffer, pathMarker, 7)) {
254 			section = P_PATH_SECTION;
255 			parser_debug("Got to System Paths\n");
256 			continue;
257 		} else if (!memcmp(buffer, upathMarker, 7)) {
258 			section = P_USER_PATH_SECTION;
259 			parser_debug("Got to User Paths\n");
260 			continue;
261 		} else if (!memcmp(buffer, licenseMarker, 7)) {
262 			section = P_LICENSE_SECTION;
263 			parser_debug("Got to License\n");
264 			continue;
265 			// After this, non sectioned tags follow
266 		} else if (!memcmp(buffer, disclaimerMarker, 7)) {
267 			uint64 length;
268 			fPackageFile->Read(&length, 8);
269 			swap_data(B_UINT64_TYPE, &length, sizeof(uint64),
270 				B_SWAP_BENDIAN_TO_HOST);
271 
272 			uint64 original;
273 			if (fPackageFile->Read(&original, 8) != 8) {
274 				RETURN_AND_SET_STATUS(B_ERROR);
275 			}
276 			swap_data(B_UINT64_TYPE, &original, sizeof(uint64),
277 				B_SWAP_BENDIAN_TO_HOST);
278 
279 			fPackageFile->Seek(4, SEEK_CUR);
280 
281 			uint8 *compressed = new uint8[length];
282 			if (fPackageFile->Read(compressed, length)
283 					!= static_cast<int64>(length)) {
284 				delete[] compressed;
285 				RETURN_AND_SET_STATUS(B_ERROR);
286 			}
287 
288 			uint8 *disclaimer = new uint8[original + 1];
289 			status_t ret = inflate_data(compressed, length, disclaimer,
290 				original);
291 			disclaimer[original] = 0;
292 			delete[] compressed;
293 			if (ret != B_OK) {
294 				delete[] disclaimer;
295 				RETURN_AND_SET_STATUS(B_ERROR);
296 			}
297 
298 			fDisclaimer = (char *)disclaimer;
299 			delete[] disclaimer;
300 
301 			continue;
302 		} else if (!memcmp(buffer, splashScreenMarker, 7)) {
303 			uint64 length;
304 			fPackageFile->Read(&length, 8);
305 			swap_data(B_UINT64_TYPE, &length, sizeof(uint64),
306 				B_SWAP_BENDIAN_TO_HOST);
307 
308 			uint64 original;
309 			if (fPackageFile->Read(&original, 8) != 8) {
310 				RETURN_AND_SET_STATUS(B_ERROR);
311 			}
312 			swap_data(B_UINT64_TYPE, &original, sizeof(uint64),
313 				B_SWAP_BENDIAN_TO_HOST);
314 
315 			fPackageFile->Seek(4, SEEK_CUR);
316 
317 			uint8 *compressed = new uint8[length];
318 			if (fPackageFile->Read(compressed, length)
319 					!= static_cast<int64>(length)) {
320 				delete[] compressed;
321 				RETURN_AND_SET_STATUS(B_ERROR);
322 			}
323 
324 			fImage.SetSize(original);
325 			status_t ret = inflate_data(compressed, length,
326 				static_cast<uint8 *>(const_cast<void *>(fImage.Buffer())),
327 				original);
328 			delete[] compressed;
329 			if (ret != B_OK) {
330 				RETURN_AND_SET_STATUS(B_ERROR);
331 			}
332 			fHasImage = true;
333 			continue;
334 		}
335 
336 		switch (section) {
337 			case P_PATH_SECTION:
338 			{
339 				if (!memcmp(buffer, "DPat", 5)) {
340 					parser_debug("DPat\n");
341 					continue;
342 				} else if (!memcmp(buffer, "FDst", 5)) {
343 					parser_debug("FDst - ");
344 					directory_which dir;
345 					if (fPackageFile->Read(&dir, 4) != 4) {
346 						RETURN_AND_SET_STATUS(B_ERROR);
347 					}
348 					swap_data(B_UINT32_TYPE, &dir, sizeof(uint32),
349 						B_SWAP_BENDIAN_TO_HOST);
350 					BPath *path = new BPath();
351 					status_t ret = find_directory(dir, path);
352 					if (ret != B_OK) {
353 						RETURN_AND_SET_STATUS(B_ERROR);
354 					}
355 
356 					parser_debug("%s\n", path->Path());
357 
358 					systemPaths.AddItem(path);
359 				} else if (!memcmp(buffer, "PaNa", 5)) {
360 					parser_debug("PaNa\n");
361 					if (fPackageFile->Read(&length, 4) != 4) {
362 						RETURN_AND_SET_STATUS(B_ERROR);
363 					}
364 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
365 						B_SWAP_BENDIAN_TO_HOST);
366 					// Since its a default, system path, we can ignore the path
367 					// name - all information needed is beside the FDst tag.
368 					fPackageFile->Seek(length, SEEK_CUR);
369 				} else if (!memcmp(buffer, padding, 7)) {
370 					parser_debug("Padding!\n");
371 					continue;
372 				} else {
373 					RETURN_AND_SET_STATUS(B_ERROR);
374 				}
375 				break;
376 			}
377 
378 			case P_GROUPS_SECTION:
379 			{
380 				if (!memcmp(buffer, "IGrp", 5)) {
381 					// Creata a new group
382 					groupStarted = true;
383 					group = pkg_profile();
384 					parser_debug("IGrp\n");
385 				} else if (!memcmp(buffer, "GrpN", 5)) {
386 					if (!groupStarted) {
387 						RETURN_AND_SET_STATUS(B_ERROR);
388 					}
389 
390 					parser_debug("GrpN\n");
391 					fPackageFile->Read(&length, 4);
392 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
393 						B_SWAP_BENDIAN_TO_HOST);
394 
395 					char *name = new char[length + 1];
396 					fPackageFile->Read(name, length);
397 					name[length] = 0;
398 					group.name = name;
399 					delete[] name;
400 				} else if (!memcmp(buffer, "GrpD", 5)) {
401 					if (!groupStarted) {
402 						RETURN_AND_SET_STATUS(B_ERROR);
403 					}
404 
405 					parser_debug("GrpD\n");
406 					fPackageFile->Read(&length, 4);
407 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
408 						B_SWAP_BENDIAN_TO_HOST);
409 
410 					char *desc = new char[length + 1];
411 					fPackageFile->Read(desc, length);
412 					desc[length] = 0;
413 					group.description = desc;
414 					delete[] desc;
415 				} else if (!memcmp(buffer, "GrHt", 5)) {
416 					if (!groupStarted) {
417 						RETURN_AND_SET_STATUS(B_ERROR);
418 					}
419 
420 					parser_debug("GrHt\n");
421 					// For now, we don't need group help
422 					fPackageFile->Read(&length, 4);
423 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
424 						B_SWAP_BENDIAN_TO_HOST);
425 					fPackageFile->Seek(length, SEEK_CUR);
426 				} else if (!memcmp(buffer, padding, 5)) {
427 					if (!groupStarted) {
428 						parser_debug("No group - padding!\n");
429 						continue;
430 					}
431 
432 					fProfiles.AddItem(new pkg_profile(group));
433 					parser_debug("Group added: %s %s\n", group.name.String(),
434 						group.description.String());
435 
436 					groupStarted = false;
437 				} else if (!memcmp(buffer, "GrId", 5)) {
438 					uint32 id;
439 					fPackageFile->Read(&id, 4);
440 					swap_data(B_UINT32_TYPE, &id, sizeof(uint32),
441 						B_SWAP_BENDIAN_TO_HOST);
442 
443 					parser_debug("GrId\n");
444 
445 					if (id == 0xffffffff)
446 						groups.AddItem(NULL);
447 					else
448 						groups.AddItem(fProfiles.ItemAt(id));
449 				} else if (!memcmp(buffer, idMarker, 7)
450 					|| !memcmp(buffer, groupsMarker, 7)) {
451 					parser_debug("Marker, jumping!\n");
452 					continue;
453 				} else {
454 					RETURN_AND_SET_STATUS(B_ERROR);
455 				}
456 				break;
457 			}
458 
459 			case P_LICENSE_SECTION:
460 			{
461 				if (!memcmp(buffer, "Lic?", 5)) {
462 					parser_debug("Lic?\n");
463 					// This tag informs whether a license is present in the
464 					// package or not. Since we don't care about licenses right
465 					// now, just skip this section
466 					fPackageFile->Seek(4, SEEK_CUR);
467 				} else if (!memcmp(buffer, "LicP", 5)) {
468 					parser_debug("LicP\n");
469 					fPackageFile->Read(&length, 4);
470 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
471 						B_SWAP_BENDIAN_TO_HOST);
472 
473 					fPackageFile->Seek(length, SEEK_CUR);
474 				} else if (!memcmp(buffer, padding, 7)) {
475 					continue;
476 				} else if (!memcmp(buffer, descMarker, 7)) {
477 					parser_debug("Description text reached\n");
478 					fPackageFile->Read(&length, 4);
479 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
480 						B_SWAP_BENDIAN_TO_HOST);
481 
482 					char *description = new char[length + 1];
483 					fPackageFile->Read(description, length);
484 					description[length] = 0;
485 					fDescription = description;
486 
487 					// Truncate all leading newlines
488 					for (i = 0; i < length; i++) {
489 						if (fDescription[i] != '\n')
490 							break;
491 					}
492 					fDescription.Remove(0, i);
493 
494 					delete[] description;
495 					parser_debug("Description text reached\n");
496 
497 					// After this, there's a known size sequence of bytes, which
498 					// meaning is yet to be determined.
499 
500 					// One is already known. The byte (or just its least
501 					// significant bit) at offset 21 from the description text
502 					// is responsible for the install folder existence
503 					// information. If it is 0, there is no install folder, if
504 					// it is 1 (or the least significant bit is set) it means
505 					// we should install all 0xffffffff files/directories to
506 					// the first directory existing in the package
507 					fPackageFile->Seek(21, SEEK_CUR);
508 					if (fPackageFile->Read(&installDirectoryFlag, 1) != 1) {
509 						RETURN_AND_SET_STATUS(B_ERROR);
510 					}
511 
512 					fPackageFile->Seek(11, SEEK_CUR);
513 				} else if (!memcmp(buffer, nameMarker, 7)) {
514 					parser_debug("Package name reached\n");
515 					fPackageFile->Read(&length, 4);
516 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
517 						B_SWAP_BENDIAN_TO_HOST);
518 
519 					char *name = new char[length + 1];
520 					fPackageFile->Read(name, length);
521 					name[length] = 0;
522 					fName = name;
523 					delete[] name;
524 				} else if (!memcmp(buffer, versionMarker, 7)) {
525 					parser_debug("Package version reached\n");
526 					fPackageFile->Read(&length, 4);
527 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
528 						B_SWAP_BENDIAN_TO_HOST);
529 
530 					char *version = new char[length + 1];
531 					fPackageFile->Read(version, length);
532 					version[length] = 0;
533 					fVersion = version;
534 					delete[] version;
535 				} else if (!memcmp(buffer, devMarker, 7)) {
536 					parser_debug("Package developer reached\n");
537 					fPackageFile->Read(&length, 4);
538 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
539 						B_SWAP_BENDIAN_TO_HOST);
540 
541 					char *dev = new char[length + 1];
542 					fPackageFile->Read(dev, length);
543 					dev[length] = 0;
544 					fDeveloper = dev;
545 					delete[] dev;
546 				} else if (!memcmp(buffer, shortDescMarker, 7)) {
547 					parser_debug("Package short description reached\n");
548 					fPackageFile->Read(&length, 4);
549 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
550 						B_SWAP_BENDIAN_TO_HOST);
551 
552 					char *desc = new char[length + 1];
553 					fPackageFile->Read(desc, length);
554 					desc[length] = 0;
555 					fShortDesc = desc;
556 					delete[] desc;
557 				} else if (!memcmp(buffer, helpMarker, 7)) {
558 					// The help text is a stored in deflated state, preceded by a 64 bit
559 					// compressed size, 64 bit inflated size and a 32 bit integer
560 					// Since there was no discussion whether we need this help text,
561 					// it will be skipped
562 					parser_debug("Help text reached\n");
563 					//uint64 length64;
564 					fPackageFile->Read(&length, 8);
565 					swap_data(B_UINT64_TYPE, &length, sizeof(uint64),
566 						B_SWAP_BENDIAN_TO_HOST);
567 
568 					fPackageFile->Seek(12 + length, SEEK_CUR);
569 				}
570 				break;
571 			}
572 
573 			case P_USER_PATH_SECTION:
574 			{
575 				if (!memcmp(buffer, "DPat", 5)) {
576 					parser_debug("DPat\n");
577 					continue;
578 				} else if (!memcmp(buffer, "DQue", 5)) {
579 					parser_debug("DQue\n");
580 					continue;
581 				} else if (!memcmp(buffer, "DQTi", 5)) {
582 					parser_debug("DQTi\n");
583 					uint32 length;
584 					if (fPackageFile->Read(&length, 4) != 4) {
585 						RETURN_AND_SET_STATUS(B_ERROR);
586 					}
587 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
588 						B_SWAP_BENDIAN_TO_HOST);
589 					char *ti = new char[length + 1];
590 					fPackageFile->Read(ti, length);
591 					ti[length] = 0;
592 					parser_debug("DQTi - %s\n", ti);
593 					delete[] ti;
594 				} else if (!memcmp(buffer, "DQSz", 5)) {
595 					parser_debug("DQSz\n");
596 					uint64 size;
597 					if (fPackageFile->Read(&size, 8) != 8) {
598 						RETURN_AND_SET_STATUS(B_ERROR);
599 					}
600 					swap_data(B_UINT64_TYPE, &size, sizeof(uint64),
601 						B_SWAP_BENDIAN_TO_HOST);
602 					parser_debug("DQSz - %Ld\n", size);
603 				} else if (!memcmp(buffer, "DQMi", 5)) {
604 					// TODO actually check if the query finds a file with
605 					// size found previously
606 					parser_debug("DQMi\n");
607 					uint32 length;
608 					if (fPackageFile->Read(&length, 4) != 4) {
609 						RETURN_AND_SET_STATUS(B_ERROR);
610 					}
611 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
612 						B_SWAP_BENDIAN_TO_HOST);
613 					char *signature = new char[length + 1];
614 					fPackageFile->Read(signature, length);
615 					signature[length] = 0;
616 					parser_debug("DQMi - %s\n", signature);
617 					delete[] signature;
618 				} else if (!memcmp(buffer, "PaNa", 5)) {
619 					parser_debug("PaNa\n");
620 					fPackageFile->Read(&length, 4);
621 					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
622 						B_SWAP_BENDIAN_TO_HOST);
623 
624 					char *pathname = new char[length + 1];
625 					fPackageFile->Read(pathname, length);
626 					pathname[length] = 0;
627 					BString *path = new BString(pathname);
628 					if (length > 0 && pathname[length - 1] == '/')
629 						path->Remove(length - 1, 1);
630 					userPaths.AddItem(path);
631 					delete[] pathname;
632 				} else if (!memcmp(buffer, padding, 7)) {
633 					parser_debug("Padding!\n");
634 					continue;
635 				} else {
636 					parser_debug("Unknown user path section %s\n", buffer);
637 					RETURN_AND_SET_STATUS(B_ERROR);
638 				}
639 				break;
640 			}
641 		}
642 	}
643 
644 	BString nameString, mimeString, signatureString, linkString;
645 	BString itemPath = "", installDirectory = "";
646 	uint32 directoryCount = 0;
647 
648 	uint8 element = P_NONE;
649 	uint32 itemGroups = 0, path = 0, cust = 0, ctime = 0, mtime = 0;
650 	uint32 platform = 0xffffffff;
651 	uint64 offset = 0, size = 0, originalSize = 0, mode = 0;
652 	uint8 pathType = P_INSTALL_PATH;
653 	status_t ret;
654 
655 	fPackageFile->Seek(infoOffset, SEEK_SET);
656 
657 	// Parse package file data
658 	while (true) {
659 		bytesRead = fPackageFile->Read(buffer, 7);
660 		if (bytesRead != 7) {
661 			RETURN_AND_SET_STATUS(B_ERROR);
662 		}
663 
664 #define INIT_VARS(tag, type) \
665 		parser_debug(tag "\n"); \
666 		element = type; \
667 		mimeString = ""; \
668 		nameString = ""; \
669 		linkString = ""; \
670 		signatureString = ""; \
671 		itemGroups = 0; \
672 		ctime = 0; \
673 		mtime = 0; \
674 		offset = 0; \
675 		cust = 0; \
676 		mode = 0; \
677 		platform = 0xffffffff; \
678 		size = 0; \
679 		originalSize = 0
680 
681 		if (!memcmp(buffer, "FilI", 5)) {
682 			INIT_VARS("FilI", P_FILE);
683 		} else if (!memcmp(buffer, "FldI", 5)) {
684 			INIT_VARS("FldI", P_DIRECTORY);
685 		} else if (!memcmp(buffer, "LnkI", 5)) {
686 			INIT_VARS("LnkI", P_LINK);
687 		} else if (!memcmp(buffer, "ScrI", 5)) {
688 			INIT_VARS("ScrI", P_SCRIPT);
689 		} else if (!memcmp(buffer, "Name", 5)) {
690 			if (element == P_NONE) {
691 				RETURN_AND_SET_STATUS(B_ERROR);
692 			}
693 
694 			parser_debug("Name\n");
695 			fPackageFile->Read(&length, 4);
696 			swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
697 				B_SWAP_BENDIAN_TO_HOST);
698 
699 			char *name = new char[length + 1];
700 			fPackageFile->Read(name, length);
701 			name[length] = 0;
702 
703 			nameString = name;
704 			delete[] name;
705 		} else if (!memcmp(buffer, "Grps", 5)) {
706 			if (element == P_NONE) {
707 				RETURN_AND_SET_STATUS(B_ERROR);
708 			}
709 
710 			parser_debug("Grps\n");
711 			fPackageFile->Read(&itemGroups, 4);
712 			swap_data(B_UINT32_TYPE, &itemGroups, sizeof(uint32),
713 				B_SWAP_BENDIAN_TO_HOST);
714 		} else if (!memcmp(buffer, "Dest", 5)) {
715 			if (element == P_NONE) {
716 				RETURN_AND_SET_STATUS(B_ERROR);
717 			}
718 
719 			parser_debug("Dest\n");
720 			fPackageFile->Read(&path, 4);
721 			swap_data(B_UINT32_TYPE, &path, sizeof(uint32),
722 				B_SWAP_BENDIAN_TO_HOST);
723 		} else if (!memcmp(buffer, "Cust", 5)) {
724 			if (element == P_NONE) {
725 				RETURN_AND_SET_STATUS(B_ERROR);
726 			}
727 
728 			parser_debug("Cust\n");
729 			fPackageFile->Read(&cust, 4);
730 			swap_data(B_UINT32_TYPE, &cust, sizeof(uint32),
731 				B_SWAP_BENDIAN_TO_HOST);
732 		} else if (!memcmp(buffer, "Repl", 5)) {
733 			if (element == P_NONE) {
734 				RETURN_AND_SET_STATUS(B_ERROR);
735 			}
736 
737 			parser_debug("Repl\n");
738 			fPackageFile->Seek(4, SEEK_CUR);
739 			// TODO: Should the replace philosophy depend on this flag? For now
740 			//	I always leave the decision to the user
741 		} else if (!memcmp(buffer, "Plat", 5)) {
742 			if (element == P_NONE) {
743 				RETURN_AND_SET_STATUS(B_ERROR);
744 			}
745 
746 			parser_debug("Plat\n");
747 			fPackageFile->Read(&platform, 4);
748 			swap_data(B_UINT32_TYPE, &platform, sizeof(uint32),
749 				B_SWAP_BENDIAN_TO_HOST);
750 		} else if (!memcmp(buffer, "CTim", 5)) {
751 			if (element == P_NONE) {
752 				RETURN_AND_SET_STATUS(B_ERROR);
753 			}
754 
755 			parser_debug("CTim\n");
756 			fPackageFile->Read(&ctime, 4);
757 			swap_data(B_UINT32_TYPE, &ctime, sizeof(uint32),
758 				B_SWAP_BENDIAN_TO_HOST);
759 		} else if (!memcmp(buffer, "MTim", 5)) {
760 			if (element == P_NONE) {
761 				RETURN_AND_SET_STATUS(B_ERROR);
762 			}
763 
764 			parser_debug("MTim\n");
765 			fPackageFile->Read(&mtime, 4);
766 			swap_data(B_UINT32_TYPE, &mtime, sizeof(uint32),
767 				B_SWAP_BENDIAN_TO_HOST);
768 		} else if (!memcmp(buffer, "OffT", 5)) {
769 			if (element == P_NONE) {
770 				RETURN_AND_SET_STATUS(B_ERROR);
771 			}
772 
773 			parser_debug("OffT\n");
774 			fPackageFile->Read(&offset, 8);
775 			swap_data(B_UINT64_TYPE, &offset, sizeof(uint64),
776 				B_SWAP_BENDIAN_TO_HOST);
777 		} else if (!memcmp(buffer, "Mime", 5)) {
778 			if (element != P_FILE) {
779 				RETURN_AND_SET_STATUS(B_ERROR);
780 			}
781 
782 			fPackageFile->Read(&length, 4);
783 			swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
784 				B_SWAP_BENDIAN_TO_HOST);
785 
786 			char *mime = new char[length + 1];
787 			fPackageFile->Read(mime, length);
788 			mime[length] = 0;
789 			parser_debug("Mime: %s\n", mime);
790 
791 			mimeString = mime;
792 			delete[] mime;
793 		} else if (!memcmp(buffer, "CmpS", 5)) {
794 			if (element == P_NONE) {
795 				RETURN_AND_SET_STATUS(B_ERROR);
796 			}
797 
798 			parser_debug("CmpS\n");
799 			fPackageFile->Read(&size, 8);
800 			swap_data(B_UINT64_TYPE, &size, sizeof(uint64),
801 				B_SWAP_BENDIAN_TO_HOST);
802 		} else if (!memcmp(buffer, "OrgS", 5)) {
803 			if (element != P_FILE && element != P_LINK && element != P_SCRIPT) {
804 				RETURN_AND_SET_STATUS(B_ERROR);
805 			}
806 
807 			parser_debug("OrgS\n");
808 			fPackageFile->Read(&originalSize, 8);
809 			swap_data(B_UINT64_TYPE, &originalSize, sizeof(uint64),
810 				B_SWAP_BENDIAN_TO_HOST);
811 		} else if (!memcmp(buffer, "VrsI", 5)) {
812 			if (element != P_FILE) {
813 				RETURN_AND_SET_STATUS(B_ERROR);
814 			}
815 
816 			parser_debug("VrsI\n");
817 			fPackageFile->Seek(24, SEEK_CUR);
818 			// TODO
819 			// Also, check what those empty 20 bytes mean
820 		} else if (!memcmp(buffer, "Mode", 5)) {
821 			if (element != P_FILE && element != P_LINK) {
822 				RETURN_AND_SET_STATUS(B_ERROR);
823 			}
824 
825 			parser_debug("Mode\n");
826 			fPackageFile->Read(&mode, 4);
827 			swap_data(B_UINT32_TYPE, &mode, sizeof(uint32),
828 				B_SWAP_BENDIAN_TO_HOST);
829 		} else if (!memcmp(buffer, "FDat", 5)) {
830 			if (element != P_DIRECTORY) {
831 				RETURN_AND_SET_STATUS(B_ERROR);
832 			}
833 
834 			parser_debug("FDat\n");
835 		} else if (!memcmp(buffer, "ASig", 5)) {
836 			if (element != P_FILE) {
837 				RETURN_AND_SET_STATUS(B_ERROR);
838 			}
839 
840 			fPackageFile->Read(&length, 4);
841 			swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
842 				B_SWAP_BENDIAN_TO_HOST);
843 
844 			char *signature = new char[length + 1];
845 			fPackageFile->Read(signature, length);
846 			signature[length] = 0;
847 			parser_debug("Signature: %s\n", signature);
848 
849 			signatureString = signature;
850 			delete[] signature;
851 		} else if (!memcmp(buffer, "Link", 5)) {
852 			if (element != P_LINK) {
853 				RETURN_AND_SET_STATUS(B_ERROR);
854 			}
855 
856 			fPackageFile->Read(&length, 4);
857 			swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
858 				B_SWAP_BENDIAN_TO_HOST);
859 
860 			char *link = new char[length + 1];
861 			fPackageFile->Read(link, length);
862 			link[length] = 0;
863 			parser_debug("Link: %s\n", link);
864 
865 			linkString = link;
866 			delete[] link;
867 		} else if (!memcmp(buffer, padding, 7)) {
868 			PackageItem *item = NULL;
869 
870 			parser_debug("Padding!\n");
871 			if (platform != 0xffffffff
872 				&& static_cast<platform_type>(platform) != thisPlatform) {
873 				// If the file/directory/item's platform is different than the
874 				// target platform (or different than the 'any' constant),
875 				// ignore this file
876 			} else if (element == P_FILE) {
877 				if (itemGroups && offset && size) {
878 					BString dest = "";
879 					uint8 localType = pathType;
880 
881 					if (path == 0xfffffffe)
882 						dest << itemPath << "/" << nameString.String();
883 					else if (path == 0xffffffff) {
884 						localType = P_INSTALL_PATH;
885 						dest = installDirectory;
886 						dest << nameString;
887 					} else {
888 						if (cust) {
889 							BString *def = static_cast<BString *>(
890 								userPaths.ItemAt(path));
891 							if (!def) {
892 								RETURN_AND_SET_STATUS(B_ERROR);
893 							}
894 							if ((*def)[0] == '/')
895 								localType = P_SYSTEM_PATH;
896 							else
897 								localType = P_USER_PATH;
898 
899 							dest << *def << "/" << nameString;
900 						} else {
901 							BPath *def = static_cast<BPath *>(
902 								systemPaths.ItemAt(path));
903 							if (!def) {
904 								RETURN_AND_SET_STATUS(B_ERROR);
905 							}
906 							localType = P_SYSTEM_PATH;
907 
908 							dest << def->Path() << "/" << nameString;
909 						}
910 					}
911 
912 					parser_debug("Adding file: %s!\n", dest.String());
913 
914 					item = new PackageFile(fPackageFile, dest, localType, ctime,
915 						mtime, offset, size, originalSize, 0, mimeString,
916 						signatureString, mode);
917 				}
918 			} else if (element == P_DIRECTORY) {
919 				if (itemGroups) {
920 					if (installDirectoryFlag != 0) {
921 						if (installDirectoryFlag < 0) {
922 							// Normal directory
923 							if (path == 0xfffffffe) {
924 								// Install to current directory
925 								itemPath << "/" << nameString.String();
926 								directoryCount++;
927 							} else if (path == 0xffffffff) {
928 								// Install to install directory
929 								pathType = P_INSTALL_PATH;
930 								itemPath = installDirectory;
931 								itemPath << nameString;
932 								directoryCount = 1;
933 							} else {
934 								// Install to defined directory
935 								if (cust) {
936 									BString *def = static_cast<BString *>(
937 										userPaths.ItemAt(path));
938 									if (!def) {
939 										RETURN_AND_SET_STATUS(B_ERROR);
940 									}
941 									if ((*def)[0] == '/')
942 										pathType = P_SYSTEM_PATH;
943 									else
944 										pathType = P_USER_PATH;
945 
946 									itemPath = *def;
947 								} else {
948 									BPath *def = static_cast<BPath *>(
949 										systemPaths.ItemAt(path));
950 									if (!def) {
951 										RETURN_AND_SET_STATUS(B_ERROR);
952 									}
953 									pathType = P_SYSTEM_PATH;
954 
955 									itemPath = def->Path();
956 								}
957 
958 								itemPath << "/" << nameString;
959 								directoryCount = 1;
960 							}
961 						} else {
962 							// Install directory
963 							if (path != 0xffffffff) {
964 								RETURN_AND_SET_STATUS(B_ERROR);
965 							}
966 
967 							installDirectory = nameString;
968 							installDirectory << "/";
969 							pathType = P_INSTALL_PATH;
970 							itemPath = nameString;
971 
972 							installDirectoryFlag = -1;
973 						}
974 
975 						parser_debug("Adding the directory %s!\n",
976 							itemPath.String());
977 
978 						item = new PackageDirectory(fPackageFile, itemPath,
979 							pathType, ctime, mtime, offset, size);
980 					} else
981 						installDirectoryFlag = -1;
982 				}
983 			} else if (element == P_LINK) {
984 				if (itemGroups && linkString.Length()) {
985 					BString dest = "";
986 					uint8 localType = pathType;
987 
988 					if (path == 0xfffffffe)
989 						dest << itemPath << "/" << nameString.String();
990 					else if (path == 0xffffffff) {
991 						localType = P_INSTALL_PATH;
992 						dest = installDirectory;
993 						dest << nameString;
994 					} else {
995 						if (cust) {
996 							BString *def = static_cast<BString *>(
997 								userPaths.ItemAt(path));
998 							if (!def) {
999 								RETURN_AND_SET_STATUS(B_ERROR);
1000 							}
1001 							if ((*def)[0] == '/')
1002 								localType = P_SYSTEM_PATH;
1003 							else
1004 								localType = P_USER_PATH;
1005 
1006 							dest << *def << "/" << nameString;
1007 						} else {
1008 							BPath *def = static_cast<BPath *>(systemPaths.ItemAt(path));
1009 							if (!def) {
1010 								RETURN_AND_SET_STATUS(B_ERROR);
1011 							}
1012 							localType = P_SYSTEM_PATH;
1013 
1014 							dest << def->Path() << "/" << nameString;
1015 						}
1016 					}
1017 
1018 					parser_debug("Adding link: %s! (type %s)\n", dest.String(),
1019 						pathType == P_SYSTEM_PATH
1020 							? "System" : localType == P_INSTALL_PATH
1021 							? "Install" : "User");
1022 
1023 					item = new PackageLink(fPackageFile, dest, linkString,
1024 						localType, ctime, mtime, mode, offset, size);
1025 				}
1026 			} else if (element == P_SCRIPT) {
1027 				parser_debug("Adding the script %s!\n",
1028 					nameString.String());
1029 
1030 				BString workingDirectory;
1031 				uint8 localType = P_SYSTEM_PATH;
1032 				if (path == 1)
1033 					workingDirectory << itemPath;
1034 				else if (path == 0xffffffff) {
1035 					workingDirectory << installDirectory;
1036 					localType = P_INSTALL_PATH;
1037 				}
1038 
1039 				fScripts.AddItem(new PackageScript(fPackageFile,
1040 					workingDirectory, localType, offset, size, originalSize));
1041 			} else {
1042 				// If the directory tree count is equal to zero, this means all
1043 				// directory trees have been closed and a padding sequence means the
1044 				// end of the section
1045 				if (directoryCount == 0)
1046 					break;
1047 				ret = itemPath.FindLast('/');
1048 				if (ret == B_ERROR) {
1049 					itemPath = "";
1050 				}
1051 				else {
1052 					itemPath.Truncate(ret);
1053 				}
1054 				directoryCount--;
1055 			}
1056 
1057 			if (item) {
1058 				_AddItem(item, originalSize, itemGroups, path, cust);
1059 			}
1060 
1061 			element = P_NONE;
1062 		} else if (!memcmp(buffer, "PkgA", 5)) {
1063 			parser_debug("PkgA\n");
1064 			break;
1065 		} else if (!memcmp(buffer, "PtcI", 5)) {
1066 			parser_debug("PtcI\n");
1067 			break;
1068 		} else {
1069 			fprintf(stderr, "Unknown file tag %s\n", buffer);
1070 			RETURN_AND_SET_STATUS(B_ERROR);
1071 		}
1072 	}
1073 
1074 	if (static_cast<uint64>(actualSize) != fileSize) {
1075 		// Inform the user of a possible error
1076 		int32 selection;
1077 		BAlert *warning = new BAlert("filesize_wrong",
1078 			B_TRANSLATE("There seems to be a file size mismatch in the "
1079 				"package file. The package might be corrupted or have been "
1080 				"modified after its creation. Do you still wish to continue?"),
1081 				B_TRANSLATE("Continue"),
1082 				B_TRANSLATE("Abort"), NULL,
1083 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1084 		warning->SetShortcut(1, B_ESCAPE);
1085 		selection = warning->Go();
1086 
1087 		if (selection == 1) {
1088 			RETURN_AND_SET_STATUS(B_ERROR);
1089 		}
1090 	}
1091 
1092 	if (!groups.IsEmpty())
1093 		fProfiles = groups;
1094 
1095 	return B_OK;
1096 }
1097 
1098 
1099 void
1100 PackageInfo::_AddItem(PackageItem *item, uint64 size, uint32 groups,
1101 	uint32 path, uint32 cust)
1102 {
1103 	// Add the item to all groups it resides in
1104 	uint32 i, n = fProfiles.CountItems(), mask = 1;
1105 	pkg_profile *profile;
1106 
1107 	fFiles.AddItem(item);
1108 
1109 	for (i = 0;i < n;i++) {
1110 		if (groups & mask) {
1111 			profile = static_cast<pkg_profile *>(fProfiles.ItemAt(i));
1112 			profile->items.AddItem(item);
1113 			profile->space_needed += size;
1114 			// If there is at least one non-predefined destination element
1115 			// in the package, we give the user the ability to select the
1116 			// installation directory.
1117 			// If there are only predefined path files in the package, but
1118 			// such defined by the user, the user will be able to select
1119 			// the destination volume
1120 			if (path == 0xffffffff)
1121 				profile->path_type = P_INSTALL_PATH;
1122 			else if (path < 0xfffffffe &&
1123 					profile->path_type != P_INSTALL_PATH) {
1124 				if (cust) {
1125 					profile->path_type = P_USER_PATH;
1126 				}
1127 			}
1128 		}
1129 		mask = mask << 1;
1130 	}
1131 }
1132 
1133