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