xref: /haiku/src/apps/packageinstaller/PackageInfo.cpp (revision 6c4a44e36ba846c54467103f884d65dfa13e7fcb)
1 /*
2  * Copyright (c) 2007-2010, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Author:
6  *		Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7  */
8 
9 
10 #include "PackageInfo.h"
11 
12 #include <Alert.h>
13 #include <ByteOrder.h>
14 #include <Catalog.h>
15 #include <FindDirectory.h>
16 #include <Locale.h>
17 #include <Path.h>
18 #include <kernel/OS.h>
19 
20 
21 #undef B_TRANSLATION_CONTEXT
22 #define B_TRANSLATION_CONTEXT "PackageInfo"
23 
24 #define RETURN_AND_SET_STATUS(err) fStatus = err; \
25 	fprintf(stderr, "err at %s():%d: %x\n", __FUNCTION__, __LINE__, err); \
26 	return fStatus;
27 
28 const uint32 kSkipOffset = 33;
29 
30 // Section constants
31 enum {
32 	P_GROUPS_SECTION = 0,
33 	P_PATH_SECTION,
34 	P_USER_PATH_SECTION,
35 	P_LICENSE_SECTION
36 };
37 
38 
39 // Element constants
40 enum {
41 	P_NONE = 0,
42 	P_FILE,
43 	P_DIRECTORY,
44 	P_LINK,
45 	P_SCRIPT
46 };
47 
48 
49 PackageInfo::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 
60 PackageInfo::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 
72 PackageInfo::~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 
104 status_t
105 PackageInfo::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 
1062 void
1063 PackageInfo::_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