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