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