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