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