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
28const uint32 kSkipOffset = 33;
29
30// Section constants
31enum {
32	P_GROUPS_SECTION = 0,
33	P_PATH_SECTION,
34	P_USER_PATH_SECTION,
35	P_LICENSE_SECTION
36};
37
38
39// Element constants
40enum {
41	P_NONE = 0,
42	P_FILE,
43	P_DIRECTORY,
44	P_LINK,
45	P_SCRIPT
46};
47
48typedef 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
64PackageInfo::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
75PackageInfo::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
87PackageInfo::~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
119status_t
120PackageInfo::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						delete path;
354						RETURN_AND_SET_STATUS(B_ERROR);
355					}
356
357					parser_debug("%s\n", path->Path());
358
359					systemPaths.AddItem(path);
360				} else if (!memcmp(buffer, "PaNa", 5)) {
361					parser_debug("PaNa\n");
362					if (fPackageFile->Read(&length, 4) != 4) {
363						RETURN_AND_SET_STATUS(B_ERROR);
364					}
365					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
366						B_SWAP_BENDIAN_TO_HOST);
367					// Since its a default, system path, we can ignore the path
368					// name - all information needed is beside the FDst tag.
369					fPackageFile->Seek(length, SEEK_CUR);
370				} else if (!memcmp(buffer, padding, 7)) {
371					parser_debug("Padding!\n");
372					continue;
373				} else {
374					RETURN_AND_SET_STATUS(B_ERROR);
375				}
376				break;
377			}
378
379			case P_GROUPS_SECTION:
380			{
381				if (!memcmp(buffer, "IGrp", 5)) {
382					// Creata a new group
383					groupStarted = true;
384					group = pkg_profile();
385					parser_debug("IGrp\n");
386				} else if (!memcmp(buffer, "GrpN", 5)) {
387					if (!groupStarted) {
388						RETURN_AND_SET_STATUS(B_ERROR);
389					}
390
391					parser_debug("GrpN\n");
392					fPackageFile->Read(&length, 4);
393					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
394						B_SWAP_BENDIAN_TO_HOST);
395
396					char *name = new char[length + 1];
397					fPackageFile->Read(name, length);
398					name[length] = 0;
399					group.name = name;
400					delete[] name;
401				} else if (!memcmp(buffer, "GrpD", 5)) {
402					if (!groupStarted) {
403						RETURN_AND_SET_STATUS(B_ERROR);
404					}
405
406					parser_debug("GrpD\n");
407					fPackageFile->Read(&length, 4);
408					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
409						B_SWAP_BENDIAN_TO_HOST);
410
411					char *desc = new char[length + 1];
412					fPackageFile->Read(desc, length);
413					desc[length] = 0;
414					group.description = desc;
415					delete[] desc;
416				} else if (!memcmp(buffer, "GrHt", 5)) {
417					if (!groupStarted) {
418						RETURN_AND_SET_STATUS(B_ERROR);
419					}
420
421					parser_debug("GrHt\n");
422					// For now, we don't need group help
423					fPackageFile->Read(&length, 4);
424					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
425						B_SWAP_BENDIAN_TO_HOST);
426					fPackageFile->Seek(length, SEEK_CUR);
427				} else if (!memcmp(buffer, padding, 5)) {
428					if (!groupStarted) {
429						parser_debug("No group - padding!\n");
430						continue;
431					}
432
433					fProfiles.AddItem(new pkg_profile(group));
434					parser_debug("Group added: %s %s\n", group.name.String(),
435						group.description.String());
436
437					groupStarted = false;
438				} else if (!memcmp(buffer, "GrId", 5)) {
439					uint32 id;
440					fPackageFile->Read(&id, 4);
441					swap_data(B_UINT32_TYPE, &id, sizeof(uint32),
442						B_SWAP_BENDIAN_TO_HOST);
443
444					parser_debug("GrId\n");
445
446					if (id == 0xffffffff)
447						groups.AddItem(NULL);
448					else
449						groups.AddItem(fProfiles.ItemAt(id));
450				} else if (!memcmp(buffer, idMarker, 7)
451					|| !memcmp(buffer, groupsMarker, 7)) {
452					parser_debug("Marker, jumping!\n");
453					continue;
454				} else {
455					RETURN_AND_SET_STATUS(B_ERROR);
456				}
457				break;
458			}
459
460			case P_LICENSE_SECTION:
461			{
462				if (!memcmp(buffer, "Lic?", 5)) {
463					parser_debug("Lic?\n");
464					// This tag informs whether a license is present in the
465					// package or not. Since we don't care about licenses right
466					// now, just skip this section
467					fPackageFile->Seek(4, SEEK_CUR);
468				} else if (!memcmp(buffer, "LicP", 5)) {
469					parser_debug("LicP\n");
470					fPackageFile->Read(&length, 4);
471					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
472						B_SWAP_BENDIAN_TO_HOST);
473
474					fPackageFile->Seek(length, SEEK_CUR);
475				} else if (!memcmp(buffer, padding, 7)) {
476					continue;
477				} else if (!memcmp(buffer, descMarker, 7)) {
478					parser_debug("Description text reached\n");
479					fPackageFile->Read(&length, 4);
480					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
481						B_SWAP_BENDIAN_TO_HOST);
482
483					char *description = new char[length + 1];
484					fPackageFile->Read(description, length);
485					description[length] = 0;
486					fDescription = description;
487
488					// Truncate all leading newlines
489					for (i = 0; i < length; i++) {
490						if (fDescription[i] != '\n')
491							break;
492					}
493					fDescription.Remove(0, i);
494
495					delete[] description;
496					parser_debug("Description text reached\n");
497
498					// After this, there's a known size sequence of bytes, which
499					// meaning is yet to be determined.
500
501					// One is already known. The byte (or just its least
502					// significant bit) at offset 21 from the description text
503					// is responsible for the install folder existence
504					// information. If it is 0, there is no install folder, if
505					// it is 1 (or the least significant bit is set) it means
506					// we should install all 0xffffffff files/directories to
507					// the first directory existing in the package
508					fPackageFile->Seek(21, SEEK_CUR);
509					if (fPackageFile->Read(&installDirectoryFlag, 1) != 1) {
510						RETURN_AND_SET_STATUS(B_ERROR);
511					}
512
513					fPackageFile->Seek(11, SEEK_CUR);
514				} else if (!memcmp(buffer, nameMarker, 7)) {
515					parser_debug("Package name reached\n");
516					fPackageFile->Read(&length, 4);
517					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
518						B_SWAP_BENDIAN_TO_HOST);
519
520					char *name = new char[length + 1];
521					fPackageFile->Read(name, length);
522					name[length] = 0;
523					fName = name;
524					delete[] name;
525				} else if (!memcmp(buffer, versionMarker, 7)) {
526					parser_debug("Package version reached\n");
527					fPackageFile->Read(&length, 4);
528					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
529						B_SWAP_BENDIAN_TO_HOST);
530
531					char *version = new char[length + 1];
532					fPackageFile->Read(version, length);
533					version[length] = 0;
534					fVersion = version;
535					delete[] version;
536				} else if (!memcmp(buffer, devMarker, 7)) {
537					parser_debug("Package developer reached\n");
538					fPackageFile->Read(&length, 4);
539					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
540						B_SWAP_BENDIAN_TO_HOST);
541
542					char *dev = new char[length + 1];
543					fPackageFile->Read(dev, length);
544					dev[length] = 0;
545					fDeveloper = dev;
546					delete[] dev;
547				} else if (!memcmp(buffer, shortDescMarker, 7)) {
548					parser_debug("Package short description reached\n");
549					fPackageFile->Read(&length, 4);
550					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
551						B_SWAP_BENDIAN_TO_HOST);
552
553					char *desc = new char[length + 1];
554					fPackageFile->Read(desc, length);
555					desc[length] = 0;
556					fShortDesc = desc;
557					delete[] desc;
558				} else if (!memcmp(buffer, helpMarker, 7)) {
559					// The help text is a stored in deflated state, preceded by a 64 bit
560					// compressed size, 64 bit inflated size and a 32 bit integer
561					// Since there was no discussion whether we need this help text,
562					// it will be skipped
563					parser_debug("Help text reached\n");
564					//uint64 length64;
565					fPackageFile->Read(&length, 8);
566					swap_data(B_UINT64_TYPE, &length, sizeof(uint64),
567						B_SWAP_BENDIAN_TO_HOST);
568
569					fPackageFile->Seek(12 + length, SEEK_CUR);
570				}
571				break;
572			}
573
574			case P_USER_PATH_SECTION:
575			{
576				if (!memcmp(buffer, "DPat", 5)) {
577					parser_debug("DPat\n");
578					continue;
579				} else if (!memcmp(buffer, "DQue", 5)) {
580					parser_debug("DQue\n");
581					continue;
582				} else if (!memcmp(buffer, "DQTi", 5)) {
583					parser_debug("DQTi\n");
584					uint32 length;
585					if (fPackageFile->Read(&length, 4) != 4) {
586						RETURN_AND_SET_STATUS(B_ERROR);
587					}
588					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
589						B_SWAP_BENDIAN_TO_HOST);
590					char *ti = new char[length + 1];
591					fPackageFile->Read(ti, length);
592					ti[length] = 0;
593					parser_debug("DQTi - %s\n", ti);
594					delete[] ti;
595				} else if (!memcmp(buffer, "DQSz", 5)) {
596					parser_debug("DQSz\n");
597					uint64 size;
598					if (fPackageFile->Read(&size, 8) != 8) {
599						RETURN_AND_SET_STATUS(B_ERROR);
600					}
601					swap_data(B_UINT64_TYPE, &size, sizeof(uint64),
602						B_SWAP_BENDIAN_TO_HOST);
603					parser_debug("DQSz - %lld\n", size);
604				} else if (!memcmp(buffer, "DQMi", 5)) {
605					// TODO actually check if the query finds a file with
606					// size found previously
607					parser_debug("DQMi\n");
608					uint32 length;
609					if (fPackageFile->Read(&length, 4) != 4) {
610						RETURN_AND_SET_STATUS(B_ERROR);
611					}
612					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
613						B_SWAP_BENDIAN_TO_HOST);
614					char *signature = new char[length + 1];
615					fPackageFile->Read(signature, length);
616					signature[length] = 0;
617					parser_debug("DQMi - %s\n", signature);
618					delete[] signature;
619				} else if (!memcmp(buffer, "PaNa", 5)) {
620					parser_debug("PaNa\n");
621					fPackageFile->Read(&length, 4);
622					swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
623						B_SWAP_BENDIAN_TO_HOST);
624
625					char *pathname = new char[length + 1];
626					fPackageFile->Read(pathname, length);
627					pathname[length] = 0;
628					BString *path = new BString(pathname);
629					if (length > 0 && pathname[length - 1] == '/')
630						path->Remove(length - 1, 1);
631					userPaths.AddItem(path);
632					delete[] pathname;
633				} else if (!memcmp(buffer, padding, 7)) {
634					parser_debug("Padding!\n");
635					continue;
636				} else {
637					parser_debug("Unknown user path section %s\n", buffer);
638					RETURN_AND_SET_STATUS(B_ERROR);
639				}
640				break;
641			}
642		}
643	}
644
645	BString nameString, mimeString, signatureString, linkString;
646	BString itemPath = "", installDirectory = "";
647	uint32 directoryCount = 0;
648
649	uint8 element = P_NONE;
650	uint32 itemGroups = 0, path = 0, cust = 0, ctime = 0, mtime = 0;
651	uint32 platform = 0xffffffff;
652	uint64 offset = 0, size = 0, originalSize = 0, mode = 0;
653	uint8 pathType = P_INSTALL_PATH;
654	status_t ret;
655
656	fPackageFile->Seek(infoOffset, SEEK_SET);
657
658	// Parse package file data
659	while (true) {
660		bytesRead = fPackageFile->Read(buffer, 7);
661		if (bytesRead != 7) {
662			RETURN_AND_SET_STATUS(B_ERROR);
663		}
664
665#define INIT_VARS(tag, type) \
666		parser_debug(tag "\n"); \
667		element = type; \
668		mimeString = ""; \
669		nameString = ""; \
670		linkString = ""; \
671		signatureString = ""; \
672		itemGroups = 0; \
673		ctime = 0; \
674		mtime = 0; \
675		offset = 0; \
676		cust = 0; \
677		mode = 0; \
678		platform = 0xffffffff; \
679		size = 0; \
680		originalSize = 0
681
682		if (!memcmp(buffer, "FilI", 5)) {
683			INIT_VARS("FilI", P_FILE);
684		} else if (!memcmp(buffer, "FldI", 5)) {
685			INIT_VARS("FldI", P_DIRECTORY);
686		} else if (!memcmp(buffer, "LnkI", 5)) {
687			INIT_VARS("LnkI", P_LINK);
688		} else if (!memcmp(buffer, "ScrI", 5)) {
689			INIT_VARS("ScrI", P_SCRIPT);
690		} else if (!memcmp(buffer, "Name", 5)) {
691			if (element == P_NONE) {
692				RETURN_AND_SET_STATUS(B_ERROR);
693			}
694
695			parser_debug("Name\n");
696			fPackageFile->Read(&length, 4);
697			swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
698				B_SWAP_BENDIAN_TO_HOST);
699
700			char *name = new char[length + 1];
701			fPackageFile->Read(name, length);
702			name[length] = 0;
703
704			nameString = name;
705			delete[] name;
706		} else if (!memcmp(buffer, "Grps", 5)) {
707			if (element == P_NONE) {
708				RETURN_AND_SET_STATUS(B_ERROR);
709			}
710
711			parser_debug("Grps\n");
712			fPackageFile->Read(&itemGroups, 4);
713			swap_data(B_UINT32_TYPE, &itemGroups, sizeof(uint32),
714				B_SWAP_BENDIAN_TO_HOST);
715		} else if (!memcmp(buffer, "Dest", 5)) {
716			if (element == P_NONE) {
717				RETURN_AND_SET_STATUS(B_ERROR);
718			}
719
720			parser_debug("Dest\n");
721			fPackageFile->Read(&path, 4);
722			swap_data(B_UINT32_TYPE, &path, sizeof(uint32),
723				B_SWAP_BENDIAN_TO_HOST);
724		} else if (!memcmp(buffer, "Cust", 5)) {
725			if (element == P_NONE) {
726				RETURN_AND_SET_STATUS(B_ERROR);
727			}
728
729			parser_debug("Cust\n");
730			fPackageFile->Read(&cust, 4);
731			swap_data(B_UINT32_TYPE, &cust, sizeof(uint32),
732				B_SWAP_BENDIAN_TO_HOST);
733		} else if (!memcmp(buffer, "Repl", 5)) {
734			if (element == P_NONE) {
735				RETURN_AND_SET_STATUS(B_ERROR);
736			}
737
738			parser_debug("Repl\n");
739			fPackageFile->Seek(4, SEEK_CUR);
740			// TODO: Should the replace philosophy depend on this flag? For now
741			//	I always leave the decision to the user
742		} else if (!memcmp(buffer, "Plat", 5)) {
743			if (element == P_NONE) {
744				RETURN_AND_SET_STATUS(B_ERROR);
745			}
746
747			parser_debug("Plat\n");
748			fPackageFile->Read(&platform, 4);
749			swap_data(B_UINT32_TYPE, &platform, sizeof(uint32),
750				B_SWAP_BENDIAN_TO_HOST);
751		} else if (!memcmp(buffer, "CTim", 5)) {
752			if (element == P_NONE) {
753				RETURN_AND_SET_STATUS(B_ERROR);
754			}
755
756			parser_debug("CTim\n");
757			fPackageFile->Read(&ctime, 4);
758			swap_data(B_UINT32_TYPE, &ctime, sizeof(uint32),
759				B_SWAP_BENDIAN_TO_HOST);
760		} else if (!memcmp(buffer, "MTim", 5)) {
761			if (element == P_NONE) {
762				RETURN_AND_SET_STATUS(B_ERROR);
763			}
764
765			parser_debug("MTim\n");
766			fPackageFile->Read(&mtime, 4);
767			swap_data(B_UINT32_TYPE, &mtime, sizeof(uint32),
768				B_SWAP_BENDIAN_TO_HOST);
769		} else if (!memcmp(buffer, "OffT", 5)) {
770			if (element == P_NONE) {
771				RETURN_AND_SET_STATUS(B_ERROR);
772			}
773
774			parser_debug("OffT\n");
775			fPackageFile->Read(&offset, 8);
776			swap_data(B_UINT64_TYPE, &offset, sizeof(uint64),
777				B_SWAP_BENDIAN_TO_HOST);
778		} else if (!memcmp(buffer, "Mime", 5)) {
779			if (element != P_FILE) {
780				RETURN_AND_SET_STATUS(B_ERROR);
781			}
782
783			fPackageFile->Read(&length, 4);
784			swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
785				B_SWAP_BENDIAN_TO_HOST);
786
787			char *mime = new char[length + 1];
788			fPackageFile->Read(mime, length);
789			mime[length] = 0;
790			parser_debug("Mime: %s\n", mime);
791
792			mimeString = mime;
793			delete[] mime;
794		} else if (!memcmp(buffer, "CmpS", 5)) {
795			if (element == P_NONE) {
796				RETURN_AND_SET_STATUS(B_ERROR);
797			}
798
799			parser_debug("CmpS\n");
800			fPackageFile->Read(&size, 8);
801			swap_data(B_UINT64_TYPE, &size, sizeof(uint64),
802				B_SWAP_BENDIAN_TO_HOST);
803		} else if (!memcmp(buffer, "OrgS", 5)) {
804			if (element != P_FILE && element != P_LINK && element != P_SCRIPT) {
805				RETURN_AND_SET_STATUS(B_ERROR);
806			}
807
808			parser_debug("OrgS\n");
809			fPackageFile->Read(&originalSize, 8);
810			swap_data(B_UINT64_TYPE, &originalSize, sizeof(uint64),
811				B_SWAP_BENDIAN_TO_HOST);
812		} else if (!memcmp(buffer, "VrsI", 5)) {
813			if (element != P_FILE) {
814				RETURN_AND_SET_STATUS(B_ERROR);
815			}
816
817			parser_debug("VrsI\n");
818			fPackageFile->Seek(24, SEEK_CUR);
819			// TODO
820			// Also, check what those empty 20 bytes mean
821		} else if (!memcmp(buffer, "Mode", 5)) {
822			if (element != P_FILE && element != P_LINK) {
823				RETURN_AND_SET_STATUS(B_ERROR);
824			}
825
826			parser_debug("Mode\n");
827			fPackageFile->Read(&mode, 4);
828			swap_data(B_UINT32_TYPE, &mode, sizeof(uint32),
829				B_SWAP_BENDIAN_TO_HOST);
830		} else if (!memcmp(buffer, "FDat", 5)) {
831			if (element != P_DIRECTORY) {
832				RETURN_AND_SET_STATUS(B_ERROR);
833			}
834
835			parser_debug("FDat\n");
836		} else if (!memcmp(buffer, "ASig", 5)) {
837			if (element != P_FILE) {
838				RETURN_AND_SET_STATUS(B_ERROR);
839			}
840
841			fPackageFile->Read(&length, 4);
842			swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
843				B_SWAP_BENDIAN_TO_HOST);
844
845			char *signature = new char[length + 1];
846			fPackageFile->Read(signature, length);
847			signature[length] = 0;
848			parser_debug("Signature: %s\n", signature);
849
850			signatureString = signature;
851			delete[] signature;
852		} else if (!memcmp(buffer, "Link", 5)) {
853			if (element != P_LINK) {
854				RETURN_AND_SET_STATUS(B_ERROR);
855			}
856
857			fPackageFile->Read(&length, 4);
858			swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
859				B_SWAP_BENDIAN_TO_HOST);
860
861			char *link = new char[length + 1];
862			fPackageFile->Read(link, length);
863			link[length] = 0;
864			parser_debug("Link: %s\n", link);
865
866			linkString = link;
867			delete[] link;
868		} else if (!memcmp(buffer, padding, 7)) {
869			PackageItem *item = NULL;
870
871			parser_debug("Padding!\n");
872			if (platform != 0xffffffff
873				&& static_cast<platform_type>(platform) != thisPlatform) {
874				// If the file/directory/item's platform is different than the
875				// target platform (or different than the 'any' constant),
876				// ignore this file
877			} else if (element == P_FILE) {
878				if (itemGroups && offset && size) {
879					BString dest = "";
880					uint8 localType = pathType;
881
882					if (path == 0xfffffffe)
883						dest << itemPath << "/" << nameString.String();
884					else if (path == 0xffffffff) {
885						localType = P_INSTALL_PATH;
886						dest = installDirectory;
887						dest << nameString;
888					} else {
889						if (cust) {
890							BString *def = static_cast<BString *>(
891								userPaths.ItemAt(path));
892							if (!def) {
893								RETURN_AND_SET_STATUS(B_ERROR);
894							}
895							if ((*def)[0] == '/')
896								localType = P_SYSTEM_PATH;
897							else
898								localType = P_USER_PATH;
899
900							dest << *def << "/" << nameString;
901						} else {
902							BPath *def = static_cast<BPath *>(
903								systemPaths.ItemAt(path));
904							if (!def) {
905								RETURN_AND_SET_STATUS(B_ERROR);
906							}
907							localType = P_SYSTEM_PATH;
908
909							dest << def->Path() << "/" << nameString;
910						}
911					}
912
913					parser_debug("Adding file: %s!\n", dest.String());
914
915					item = new PackageFile(fPackageFile, dest, localType, ctime,
916						mtime, offset, size, originalSize, 0, mimeString,
917						signatureString, mode);
918				}
919			} else if (element == P_DIRECTORY) {
920				if (itemGroups) {
921					if (installDirectoryFlag != 0) {
922						if (installDirectoryFlag < 0) {
923							// Normal directory
924							if (path == 0xfffffffe) {
925								// Install to current directory
926								itemPath << "/" << nameString.String();
927								directoryCount++;
928							} else if (path == 0xffffffff) {
929								// Install to install directory
930								pathType = P_INSTALL_PATH;
931								itemPath = installDirectory;
932								itemPath << nameString;
933								directoryCount = 1;
934							} else {
935								// Install to defined directory
936								if (cust) {
937									BString *def = static_cast<BString *>(
938										userPaths.ItemAt(path));
939									if (!def) {
940										RETURN_AND_SET_STATUS(B_ERROR);
941									}
942									if ((*def)[0] == '/')
943										pathType = P_SYSTEM_PATH;
944									else
945										pathType = P_USER_PATH;
946
947									itemPath = *def;
948								} else {
949									BPath *def = static_cast<BPath *>(
950										systemPaths.ItemAt(path));
951									if (!def) {
952										RETURN_AND_SET_STATUS(B_ERROR);
953									}
954									pathType = P_SYSTEM_PATH;
955
956									itemPath = def->Path();
957								}
958
959								itemPath << "/" << nameString;
960								directoryCount = 1;
961							}
962						} else {
963							// Install directory
964							if (path != 0xffffffff) {
965								RETURN_AND_SET_STATUS(B_ERROR);
966							}
967
968							installDirectory = nameString;
969							installDirectory << "/";
970							pathType = P_INSTALL_PATH;
971							itemPath = nameString;
972
973							installDirectoryFlag = -1;
974						}
975
976						parser_debug("Adding the directory %s!\n",
977							itemPath.String());
978
979						item = new PackageDirectory(fPackageFile, itemPath,
980							pathType, ctime, mtime, offset, size);
981					} else
982						installDirectoryFlag = -1;
983				}
984			} else if (element == P_LINK) {
985				if (itemGroups && linkString.Length()) {
986					BString dest = "";
987					uint8 localType = pathType;
988
989					if (path == 0xfffffffe)
990						dest << itemPath << "/" << nameString.String();
991					else if (path == 0xffffffff) {
992						localType = P_INSTALL_PATH;
993						dest = installDirectory;
994						dest << nameString;
995					} else {
996						if (cust) {
997							BString *def = static_cast<BString *>(
998								userPaths.ItemAt(path));
999							if (!def) {
1000								RETURN_AND_SET_STATUS(B_ERROR);
1001							}
1002							if ((*def)[0] == '/')
1003								localType = P_SYSTEM_PATH;
1004							else
1005								localType = P_USER_PATH;
1006
1007							dest << *def << "/" << nameString;
1008						} else {
1009							BPath *def = static_cast<BPath *>(systemPaths.ItemAt(path));
1010							if (!def) {
1011								RETURN_AND_SET_STATUS(B_ERROR);
1012							}
1013							localType = P_SYSTEM_PATH;
1014
1015							dest << def->Path() << "/" << nameString;
1016						}
1017					}
1018
1019					parser_debug("Adding link: %s! (type %s)\n", dest.String(),
1020						pathType == P_SYSTEM_PATH
1021							? "System" : localType == P_INSTALL_PATH
1022							? "Install" : "User");
1023
1024					item = new PackageLink(fPackageFile, dest, linkString,
1025						localType, ctime, mtime, mode, offset, size);
1026				}
1027			} else if (element == P_SCRIPT) {
1028				parser_debug("Adding the script %s!\n",
1029					nameString.String());
1030
1031				BString workingDirectory;
1032				uint8 localType = P_SYSTEM_PATH;
1033				if (path == 1)
1034					workingDirectory << itemPath;
1035				else if (path == 0xffffffff) {
1036					workingDirectory << installDirectory;
1037					localType = P_INSTALL_PATH;
1038				}
1039
1040				fScripts.AddItem(new PackageScript(fPackageFile,
1041					workingDirectory, localType, offset, size, originalSize));
1042			} else {
1043				// If the directory tree count is equal to zero, this means all
1044				// directory trees have been closed and a padding sequence means the
1045				// end of the section
1046				if (directoryCount == 0)
1047					break;
1048				ret = itemPath.FindLast('/');
1049				if (ret == B_ERROR) {
1050					itemPath = "";
1051				}
1052				else {
1053					itemPath.Truncate(ret);
1054				}
1055				directoryCount--;
1056			}
1057
1058			if (item) {
1059				_AddItem(item, originalSize, itemGroups, path, cust);
1060			}
1061
1062			element = P_NONE;
1063		} else if (!memcmp(buffer, "PkgA", 5)) {
1064			parser_debug("PkgA\n");
1065			break;
1066		} else if (!memcmp(buffer, "PtcI", 5)) {
1067			parser_debug("PtcI\n");
1068			break;
1069		} else {
1070			fprintf(stderr, "Unknown file tag %s\n", buffer);
1071			RETURN_AND_SET_STATUS(B_ERROR);
1072		}
1073	}
1074
1075	if (static_cast<uint64>(actualSize) != fileSize) {
1076		// Inform the user of a possible error
1077		int32 selection;
1078		BAlert *warning = new BAlert("filesize_wrong",
1079			B_TRANSLATE("There seems to be a file size mismatch in the "
1080				"package file. The package might be corrupted or have been "
1081				"modified after its creation. Do you still wish to continue?"),
1082				B_TRANSLATE("Continue"),
1083				B_TRANSLATE("Abort"), NULL,
1084			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1085		warning->SetShortcut(1, B_ESCAPE);
1086		selection = warning->Go();
1087
1088		if (selection == 1) {
1089			RETURN_AND_SET_STATUS(B_ERROR);
1090		}
1091	}
1092
1093	if (!groups.IsEmpty())
1094		fProfiles = groups;
1095
1096	return B_OK;
1097}
1098
1099
1100void
1101PackageInfo::_AddItem(PackageItem *item, uint64 size, uint32 groups,
1102	uint32 path, uint32 cust)
1103{
1104	// Add the item to all groups it resides in
1105	uint32 i, n = fProfiles.CountItems(), mask = 1;
1106	pkg_profile *profile;
1107
1108	fFiles.AddItem(item);
1109
1110	for (i = 0;i < n;i++) {
1111		if (groups & mask) {
1112			profile = static_cast<pkg_profile *>(fProfiles.ItemAt(i));
1113			profile->items.AddItem(item);
1114			profile->space_needed += size;
1115			// If there is at least one non-predefined destination element
1116			// in the package, we give the user the ability to select the
1117			// installation directory.
1118			// If there are only predefined path files in the package, but
1119			// such defined by the user, the user will be able to select
1120			// the destination volume
1121			if (path == 0xffffffff)
1122				profile->path_type = P_INSTALL_PATH;
1123			else if (path < 0xfffffffe &&
1124					profile->path_type != P_INSTALL_PATH) {
1125				if (cust) {
1126					profile->path_type = P_USER_PATH;
1127				}
1128			}
1129		}
1130		mask = mask << 1;
1131	}
1132}
1133
1134