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