1134 lines
31 KiB
C++
1134 lines
31 KiB
C++
/*
|
|
* Copyright (c) 2007-2010, Haiku, Inc.
|
|
* Distributed under the terms of the MIT license.
|
|
*
|
|
* Author:
|
|
* Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
|
|
*/
|
|
|
|
|
|
#include "PackageInfo.h"
|
|
|
|
#include <Alert.h>
|
|
#include <ByteOrder.h>
|
|
#include <Catalog.h>
|
|
#include <FindDirectory.h>
|
|
#include <Locale.h>
|
|
#include <Path.h>
|
|
#include <kernel/OS.h>
|
|
|
|
|
|
#undef B_TRANSLATION_CONTEXT
|
|
#define B_TRANSLATION_CONTEXT "PackageInfo"
|
|
|
|
#define RETURN_AND_SET_STATUS(err) fStatus = err; \
|
|
fprintf(stderr, "err at %s():%d: %x\n", __FUNCTION__, __LINE__, err); \
|
|
return fStatus;
|
|
|
|
const uint32 kSkipOffset = 33;
|
|
|
|
// Section constants
|
|
enum {
|
|
P_GROUPS_SECTION = 0,
|
|
P_PATH_SECTION,
|
|
P_USER_PATH_SECTION,
|
|
P_LICENSE_SECTION
|
|
};
|
|
|
|
|
|
// Element constants
|
|
enum {
|
|
P_NONE = 0,
|
|
P_FILE,
|
|
P_DIRECTORY,
|
|
P_LINK,
|
|
P_SCRIPT
|
|
};
|
|
|
|
typedef enum {
|
|
B_BEBOX_PLATFORM = 0,
|
|
B_MAC_PLATFORM,
|
|
B_AT_CLONE_PLATFORM,
|
|
B_ENIAC_PLATFORM,
|
|
B_APPLE_II_PLATFORM,
|
|
B_CRAY_PLATFORM,
|
|
B_LISA_PLATFORM,
|
|
B_TI_994A_PLATFORM,
|
|
B_TIMEX_SINCLAIR_PLATFORM,
|
|
B_ORAC_1_PLATFORM,
|
|
B_HAL_PLATFORM,
|
|
B_INVALID_PLATFORM
|
|
} platform_type;
|
|
|
|
|
|
PackageInfo::PackageInfo()
|
|
:
|
|
fStatus(B_NO_INIT),
|
|
fPackageFile(0),
|
|
fDescription(B_TRANSLATE("No package available.")),
|
|
fProfiles(2),
|
|
fHasImage(false)
|
|
{
|
|
}
|
|
|
|
|
|
PackageInfo::PackageInfo(const entry_ref *ref)
|
|
:
|
|
fStatus(B_NO_INIT),
|
|
fPackageFile(new BFile(ref, B_READ_ONLY)),
|
|
fDescription(B_TRANSLATE("No package selected.")),
|
|
fProfiles(2),
|
|
fHasImage(false)
|
|
{
|
|
fStatus = Parse();
|
|
}
|
|
|
|
|
|
PackageInfo::~PackageInfo()
|
|
{
|
|
pkg_profile *iter = 0;
|
|
while (1) {
|
|
iter = static_cast<pkg_profile *>(fProfiles.RemoveItem((int32)0));
|
|
if (iter == NULL)
|
|
break;
|
|
|
|
delete iter;
|
|
}
|
|
|
|
PackageItem *file = 0;
|
|
while (true) {
|
|
file = static_cast<PackageItem *>(fFiles.RemoveItem((int32)0));
|
|
if (file == NULL)
|
|
break;
|
|
|
|
delete file;
|
|
}
|
|
|
|
while (true) {
|
|
file = static_cast<PackageScript *>(fScripts.RemoveItem((int32)0));
|
|
if (file == NULL)
|
|
break;
|
|
|
|
delete file;
|
|
}
|
|
|
|
delete fPackageFile;
|
|
}
|
|
|
|
|
|
status_t
|
|
PackageInfo::Parse()
|
|
{
|
|
// TODO: Clean up
|
|
if (!fPackageFile || fPackageFile->InitCheck() != B_OK) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
// Check for the presence of the first AlB tag - as the 'magic number'.
|
|
// This also ensures that the file header section is present - which
|
|
// is a crucial pkg section
|
|
char buffer[16];
|
|
fPackageFile->Read(buffer, 8);
|
|
if (buffer[0] != 'A' || buffer[1] != 'l' || buffer[2] != 'B'
|
|
|| buffer[3] != 0x1a) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
fHasImage = false;
|
|
|
|
// Parse all known parts of the given .pkg file
|
|
|
|
uint32 i;
|
|
int8 bytesRead;
|
|
off_t actualSize = 0;
|
|
fPackageFile->GetSize(&actualSize);
|
|
uint64 fileSize = 0;
|
|
|
|
const char padding[7] = { 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
platform_type thisPlatform = B_INVALID_PLATFORM;
|
|
cpu_topology_node_info topologyRoot;
|
|
uint32 topologyNodeCount = 1;
|
|
if (get_cpu_topology_info(&topologyRoot, &topologyNodeCount) == B_OK) {
|
|
switch (topologyRoot.data.root.platform) {
|
|
case B_CPU_x86:
|
|
thisPlatform = B_AT_CLONE_PLATFORM;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint64 infoOffset = 0, groupsOffset = 0;
|
|
uint64 length = 0;
|
|
|
|
// Parse the file header
|
|
while (true) {
|
|
bytesRead = fPackageFile->Read(buffer, 7);
|
|
if (bytesRead != 7) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
if (!memcmp(buffer, "PhIn", 5)) {
|
|
} else if (!memcmp(buffer, "FVer", 5)) {
|
|
// Not used right now
|
|
fPackageFile->Seek(4, SEEK_CUR);
|
|
parser_debug("FVer\n");
|
|
} else if (!memcmp(buffer, "AFla", 5)) {
|
|
// Not used right now TODO: Check what this tag is for
|
|
fPackageFile->Seek(8, SEEK_CUR);
|
|
parser_debug("AFla\n");
|
|
} else if (!memcmp(buffer, "FSiz", 5)) {
|
|
fPackageFile->Read(&fileSize, 8);
|
|
swap_data(B_UINT64_TYPE, &fileSize, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
parser_debug("FSiz %llu\n", fileSize);
|
|
} else if (!memcmp(buffer, "COff", 5)) {
|
|
fPackageFile->Read(&infoOffset, 8);
|
|
swap_data(B_UINT64_TYPE, &infoOffset, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
parser_debug("COff %llu\n", infoOffset);
|
|
} else if (!memcmp(buffer, "AOff", 5)) {
|
|
fPackageFile->Read(&groupsOffset, 8);
|
|
swap_data(B_UINT64_TYPE, &groupsOffset, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
parser_debug("AOff %llu\n", groupsOffset);
|
|
} else if (!memcmp(buffer, padding, 7)) {
|
|
// This means the end of this section - we should move to the
|
|
// groups section.
|
|
if (groupsOffset) {
|
|
fPackageFile->Seek(groupsOffset, SEEK_SET);
|
|
}
|
|
parser_debug("End!\n");
|
|
break;
|
|
} else {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
}
|
|
|
|
fPackageFile->Read(buffer, 7);
|
|
if (memcmp(buffer, "PkgA", 5) || !groupsOffset || !infoOffset) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
// Section header identifying constant byte sequences:
|
|
const char groupsMarker[7] = { 0, 0, 0, 1, 0, 0, 4 };
|
|
const char idMarker[7] = { 0, 0, 0, 2, 0, 0, 4 };
|
|
const char pathMarker[7] = { 0, 0, 0, 3, 0, 0, 4 };
|
|
const char upathMarker[7] = { 0, 0, 0, 4, 0, 0, 4 };
|
|
const char licenseMarker[7] = { 0, 0, 0, 18, 0, 0, 4 };
|
|
const char descMarker[7] = { 0, 0, 0, 5, 0, 0, 2 };
|
|
const char helpMarker[7] = { 0, 0, 0, 10, 0, 0, 3 };
|
|
|
|
const char splashScreenMarker[7] = { 0, 0, 0, 8, 0, 0, 3 };
|
|
const char disclaimerMarker[7] = { 0, 0, 0, 7, 0, 0, 3 };
|
|
|
|
const char nameMarker[7] = { 0, 0, 0, 13, 0, 0, 2 };
|
|
const char versionMarker[7] = { 0, 0, 0, 14, 0, 0, 2 };
|
|
const char devMarker[7] = { 0, 0, 0, 15, 0, 0, 2 };
|
|
const char shortDescMarker[7] = { 0, 0, 0, 17, 0, 0, 2 };
|
|
|
|
int8 section = P_GROUPS_SECTION, installDirectoryFlag = 0;
|
|
|
|
pkg_profile group;
|
|
BList groups(3), userPaths(3), systemPaths(10);
|
|
bool groupStarted = false;
|
|
parser_debug("Package Info reached!\n");
|
|
// TODO: Maybe checking whether the needed number of bytes are read
|
|
// everytime would be a good idea
|
|
|
|
// Parse the package info section
|
|
while (true) {
|
|
bytesRead = fPackageFile->Read(buffer, 7);
|
|
if (bytesRead != 7) {
|
|
parser_debug("EOF!\n");
|
|
break;
|
|
}
|
|
|
|
if (!memcmp(buffer, groupsMarker, 7)) {
|
|
section = P_GROUPS_SECTION;
|
|
parser_debug("Got to Groups section\n");
|
|
continue;
|
|
} else if (!memcmp(buffer, pathMarker, 7)) {
|
|
section = P_PATH_SECTION;
|
|
parser_debug("Got to System Paths\n");
|
|
continue;
|
|
} else if (!memcmp(buffer, upathMarker, 7)) {
|
|
section = P_USER_PATH_SECTION;
|
|
parser_debug("Got to User Paths\n");
|
|
continue;
|
|
} else if (!memcmp(buffer, licenseMarker, 7)) {
|
|
section = P_LICENSE_SECTION;
|
|
parser_debug("Got to License\n");
|
|
continue;
|
|
// After this, non sectioned tags follow
|
|
} else if (!memcmp(buffer, disclaimerMarker, 7)) {
|
|
uint64 length;
|
|
fPackageFile->Read(&length, 8);
|
|
swap_data(B_UINT64_TYPE, &length, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
uint64 original;
|
|
if (fPackageFile->Read(&original, 8) != 8) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
swap_data(B_UINT64_TYPE, &original, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
fPackageFile->Seek(4, SEEK_CUR);
|
|
|
|
uint8 *compressed = new uint8[length];
|
|
if (fPackageFile->Read(compressed, length)
|
|
!= static_cast<int64>(length)) {
|
|
delete[] compressed;
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
uint8 *disclaimer = new uint8[original + 1];
|
|
status_t ret = inflate_data(compressed, length, disclaimer,
|
|
original);
|
|
disclaimer[original] = 0;
|
|
delete[] compressed;
|
|
if (ret != B_OK) {
|
|
delete[] disclaimer;
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
fDisclaimer = (char *)disclaimer;
|
|
delete[] disclaimer;
|
|
|
|
continue;
|
|
} else if (!memcmp(buffer, splashScreenMarker, 7)) {
|
|
uint64 length;
|
|
fPackageFile->Read(&length, 8);
|
|
swap_data(B_UINT64_TYPE, &length, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
uint64 original;
|
|
if (fPackageFile->Read(&original, 8) != 8) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
swap_data(B_UINT64_TYPE, &original, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
fPackageFile->Seek(4, SEEK_CUR);
|
|
|
|
uint8 *compressed = new uint8[length];
|
|
if (fPackageFile->Read(compressed, length)
|
|
!= static_cast<int64>(length)) {
|
|
delete[] compressed;
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
fImage.SetSize(original);
|
|
status_t ret = inflate_data(compressed, length,
|
|
static_cast<uint8 *>(const_cast<void *>(fImage.Buffer())),
|
|
original);
|
|
delete[] compressed;
|
|
if (ret != B_OK) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
fHasImage = true;
|
|
continue;
|
|
}
|
|
|
|
switch (section) {
|
|
case P_PATH_SECTION:
|
|
{
|
|
if (!memcmp(buffer, "DPat", 5)) {
|
|
parser_debug("DPat\n");
|
|
continue;
|
|
} else if (!memcmp(buffer, "FDst", 5)) {
|
|
parser_debug("FDst - ");
|
|
directory_which dir;
|
|
if (fPackageFile->Read(&dir, 4) != 4) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
swap_data(B_UINT32_TYPE, &dir, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
BPath *path = new BPath();
|
|
status_t ret = find_directory(dir, path);
|
|
if (ret != B_OK) {
|
|
delete path;
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("%s\n", path->Path());
|
|
|
|
systemPaths.AddItem(path);
|
|
} else if (!memcmp(buffer, "PaNa", 5)) {
|
|
parser_debug("PaNa\n");
|
|
if (fPackageFile->Read(&length, 4) != 4) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
// Since its a default, system path, we can ignore the path
|
|
// name - all information needed is beside the FDst tag.
|
|
fPackageFile->Seek(length, SEEK_CUR);
|
|
} else if (!memcmp(buffer, padding, 7)) {
|
|
parser_debug("Padding!\n");
|
|
continue;
|
|
} else {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case P_GROUPS_SECTION:
|
|
{
|
|
if (!memcmp(buffer, "IGrp", 5)) {
|
|
// Creata a new group
|
|
groupStarted = true;
|
|
group = pkg_profile();
|
|
parser_debug("IGrp\n");
|
|
} else if (!memcmp(buffer, "GrpN", 5)) {
|
|
if (!groupStarted) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("GrpN\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *name = new char[length + 1];
|
|
fPackageFile->Read(name, length);
|
|
name[length] = 0;
|
|
group.name = name;
|
|
delete[] name;
|
|
} else if (!memcmp(buffer, "GrpD", 5)) {
|
|
if (!groupStarted) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("GrpD\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *desc = new char[length + 1];
|
|
fPackageFile->Read(desc, length);
|
|
desc[length] = 0;
|
|
group.description = desc;
|
|
delete[] desc;
|
|
} else if (!memcmp(buffer, "GrHt", 5)) {
|
|
if (!groupStarted) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("GrHt\n");
|
|
// For now, we don't need group help
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
fPackageFile->Seek(length, SEEK_CUR);
|
|
} else if (!memcmp(buffer, padding, 5)) {
|
|
if (!groupStarted) {
|
|
parser_debug("No group - padding!\n");
|
|
continue;
|
|
}
|
|
|
|
fProfiles.AddItem(new pkg_profile(group));
|
|
parser_debug("Group added: %s %s\n", group.name.String(),
|
|
group.description.String());
|
|
|
|
groupStarted = false;
|
|
} else if (!memcmp(buffer, "GrId", 5)) {
|
|
uint32 id;
|
|
fPackageFile->Read(&id, 4);
|
|
swap_data(B_UINT32_TYPE, &id, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
parser_debug("GrId\n");
|
|
|
|
if (id == 0xffffffff)
|
|
groups.AddItem(NULL);
|
|
else
|
|
groups.AddItem(fProfiles.ItemAt(id));
|
|
} else if (!memcmp(buffer, idMarker, 7)
|
|
|| !memcmp(buffer, groupsMarker, 7)) {
|
|
parser_debug("Marker, jumping!\n");
|
|
continue;
|
|
} else {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case P_LICENSE_SECTION:
|
|
{
|
|
if (!memcmp(buffer, "Lic?", 5)) {
|
|
parser_debug("Lic?\n");
|
|
// This tag informs whether a license is present in the
|
|
// package or not. Since we don't care about licenses right
|
|
// now, just skip this section
|
|
fPackageFile->Seek(4, SEEK_CUR);
|
|
} else if (!memcmp(buffer, "LicP", 5)) {
|
|
parser_debug("LicP\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
fPackageFile->Seek(length, SEEK_CUR);
|
|
} else if (!memcmp(buffer, padding, 7)) {
|
|
continue;
|
|
} else if (!memcmp(buffer, descMarker, 7)) {
|
|
parser_debug("Description text reached\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *description = new char[length + 1];
|
|
fPackageFile->Read(description, length);
|
|
description[length] = 0;
|
|
fDescription = description;
|
|
|
|
// Truncate all leading newlines
|
|
for (i = 0; i < length; i++) {
|
|
if (fDescription[i] != '\n')
|
|
break;
|
|
}
|
|
fDescription.Remove(0, i);
|
|
|
|
delete[] description;
|
|
parser_debug("Description text reached\n");
|
|
|
|
// After this, there's a known size sequence of bytes, which
|
|
// meaning is yet to be determined.
|
|
|
|
// One is already known. The byte (or just its least
|
|
// significant bit) at offset 21 from the description text
|
|
// is responsible for the install folder existence
|
|
// information. If it is 0, there is no install folder, if
|
|
// it is 1 (or the least significant bit is set) it means
|
|
// we should install all 0xffffffff files/directories to
|
|
// the first directory existing in the package
|
|
fPackageFile->Seek(21, SEEK_CUR);
|
|
if (fPackageFile->Read(&installDirectoryFlag, 1) != 1) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
fPackageFile->Seek(11, SEEK_CUR);
|
|
} else if (!memcmp(buffer, nameMarker, 7)) {
|
|
parser_debug("Package name reached\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *name = new char[length + 1];
|
|
fPackageFile->Read(name, length);
|
|
name[length] = 0;
|
|
fName = name;
|
|
delete[] name;
|
|
} else if (!memcmp(buffer, versionMarker, 7)) {
|
|
parser_debug("Package version reached\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *version = new char[length + 1];
|
|
fPackageFile->Read(version, length);
|
|
version[length] = 0;
|
|
fVersion = version;
|
|
delete[] version;
|
|
} else if (!memcmp(buffer, devMarker, 7)) {
|
|
parser_debug("Package developer reached\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *dev = new char[length + 1];
|
|
fPackageFile->Read(dev, length);
|
|
dev[length] = 0;
|
|
fDeveloper = dev;
|
|
delete[] dev;
|
|
} else if (!memcmp(buffer, shortDescMarker, 7)) {
|
|
parser_debug("Package short description reached\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *desc = new char[length + 1];
|
|
fPackageFile->Read(desc, length);
|
|
desc[length] = 0;
|
|
fShortDesc = desc;
|
|
delete[] desc;
|
|
} else if (!memcmp(buffer, helpMarker, 7)) {
|
|
// The help text is a stored in deflated state, preceded by a 64 bit
|
|
// compressed size, 64 bit inflated size and a 32 bit integer
|
|
// Since there was no discussion whether we need this help text,
|
|
// it will be skipped
|
|
parser_debug("Help text reached\n");
|
|
//uint64 length64;
|
|
fPackageFile->Read(&length, 8);
|
|
swap_data(B_UINT64_TYPE, &length, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
fPackageFile->Seek(12 + length, SEEK_CUR);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case P_USER_PATH_SECTION:
|
|
{
|
|
if (!memcmp(buffer, "DPat", 5)) {
|
|
parser_debug("DPat\n");
|
|
continue;
|
|
} else if (!memcmp(buffer, "DQue", 5)) {
|
|
parser_debug("DQue\n");
|
|
continue;
|
|
} else if (!memcmp(buffer, "DQTi", 5)) {
|
|
parser_debug("DQTi\n");
|
|
uint32 length;
|
|
if (fPackageFile->Read(&length, 4) != 4) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
char *ti = new char[length + 1];
|
|
fPackageFile->Read(ti, length);
|
|
ti[length] = 0;
|
|
parser_debug("DQTi - %s\n", ti);
|
|
delete[] ti;
|
|
} else if (!memcmp(buffer, "DQSz", 5)) {
|
|
parser_debug("DQSz\n");
|
|
uint64 size;
|
|
if (fPackageFile->Read(&size, 8) != 8) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
swap_data(B_UINT64_TYPE, &size, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
parser_debug("DQSz - %Ld\n", size);
|
|
} else if (!memcmp(buffer, "DQMi", 5)) {
|
|
// TODO actually check if the query finds a file with
|
|
// size found previously
|
|
parser_debug("DQMi\n");
|
|
uint32 length;
|
|
if (fPackageFile->Read(&length, 4) != 4) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
char *signature = new char[length + 1];
|
|
fPackageFile->Read(signature, length);
|
|
signature[length] = 0;
|
|
parser_debug("DQMi - %s\n", signature);
|
|
delete[] signature;
|
|
} else if (!memcmp(buffer, "PaNa", 5)) {
|
|
parser_debug("PaNa\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *pathname = new char[length + 1];
|
|
fPackageFile->Read(pathname, length);
|
|
pathname[length] = 0;
|
|
BString *path = new BString(pathname);
|
|
if (length > 0 && pathname[length - 1] == '/')
|
|
path->Remove(length - 1, 1);
|
|
userPaths.AddItem(path);
|
|
delete[] pathname;
|
|
} else if (!memcmp(buffer, padding, 7)) {
|
|
parser_debug("Padding!\n");
|
|
continue;
|
|
} else {
|
|
parser_debug("Unknown user path section %s\n", buffer);
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BString nameString, mimeString, signatureString, linkString;
|
|
BString itemPath = "", installDirectory = "";
|
|
uint32 directoryCount = 0;
|
|
|
|
uint8 element = P_NONE;
|
|
uint32 itemGroups = 0, path = 0, cust = 0, ctime = 0, mtime = 0;
|
|
uint32 platform = 0xffffffff;
|
|
uint64 offset = 0, size = 0, originalSize = 0, mode = 0;
|
|
uint8 pathType = P_INSTALL_PATH;
|
|
status_t ret;
|
|
|
|
fPackageFile->Seek(infoOffset, SEEK_SET);
|
|
|
|
// Parse package file data
|
|
while (true) {
|
|
bytesRead = fPackageFile->Read(buffer, 7);
|
|
if (bytesRead != 7) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
#define INIT_VARS(tag, type) \
|
|
parser_debug(tag "\n"); \
|
|
element = type; \
|
|
mimeString = ""; \
|
|
nameString = ""; \
|
|
linkString = ""; \
|
|
signatureString = ""; \
|
|
itemGroups = 0; \
|
|
ctime = 0; \
|
|
mtime = 0; \
|
|
offset = 0; \
|
|
cust = 0; \
|
|
mode = 0; \
|
|
platform = 0xffffffff; \
|
|
size = 0; \
|
|
originalSize = 0
|
|
|
|
if (!memcmp(buffer, "FilI", 5)) {
|
|
INIT_VARS("FilI", P_FILE);
|
|
} else if (!memcmp(buffer, "FldI", 5)) {
|
|
INIT_VARS("FldI", P_DIRECTORY);
|
|
} else if (!memcmp(buffer, "LnkI", 5)) {
|
|
INIT_VARS("LnkI", P_LINK);
|
|
} else if (!memcmp(buffer, "ScrI", 5)) {
|
|
INIT_VARS("ScrI", P_SCRIPT);
|
|
} else if (!memcmp(buffer, "Name", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("Name\n");
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *name = new char[length + 1];
|
|
fPackageFile->Read(name, length);
|
|
name[length] = 0;
|
|
|
|
nameString = name;
|
|
delete[] name;
|
|
} else if (!memcmp(buffer, "Grps", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("Grps\n");
|
|
fPackageFile->Read(&itemGroups, 4);
|
|
swap_data(B_UINT32_TYPE, &itemGroups, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "Dest", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("Dest\n");
|
|
fPackageFile->Read(&path, 4);
|
|
swap_data(B_UINT32_TYPE, &path, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "Cust", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("Cust\n");
|
|
fPackageFile->Read(&cust, 4);
|
|
swap_data(B_UINT32_TYPE, &cust, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "Repl", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("Repl\n");
|
|
fPackageFile->Seek(4, SEEK_CUR);
|
|
// TODO: Should the replace philosophy depend on this flag? For now
|
|
// I always leave the decision to the user
|
|
} else if (!memcmp(buffer, "Plat", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("Plat\n");
|
|
fPackageFile->Read(&platform, 4);
|
|
swap_data(B_UINT32_TYPE, &platform, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "CTim", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("CTim\n");
|
|
fPackageFile->Read(&ctime, 4);
|
|
swap_data(B_UINT32_TYPE, &ctime, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "MTim", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("MTim\n");
|
|
fPackageFile->Read(&mtime, 4);
|
|
swap_data(B_UINT32_TYPE, &mtime, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "OffT", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("OffT\n");
|
|
fPackageFile->Read(&offset, 8);
|
|
swap_data(B_UINT64_TYPE, &offset, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "Mime", 5)) {
|
|
if (element != P_FILE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *mime = new char[length + 1];
|
|
fPackageFile->Read(mime, length);
|
|
mime[length] = 0;
|
|
parser_debug("Mime: %s\n", mime);
|
|
|
|
mimeString = mime;
|
|
delete[] mime;
|
|
} else if (!memcmp(buffer, "CmpS", 5)) {
|
|
if (element == P_NONE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("CmpS\n");
|
|
fPackageFile->Read(&size, 8);
|
|
swap_data(B_UINT64_TYPE, &size, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "OrgS", 5)) {
|
|
if (element != P_FILE && element != P_LINK && element != P_SCRIPT) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("OrgS\n");
|
|
fPackageFile->Read(&originalSize, 8);
|
|
swap_data(B_UINT64_TYPE, &originalSize, sizeof(uint64),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "VrsI", 5)) {
|
|
if (element != P_FILE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("VrsI\n");
|
|
fPackageFile->Seek(24, SEEK_CUR);
|
|
// TODO
|
|
// Also, check what those empty 20 bytes mean
|
|
} else if (!memcmp(buffer, "Mode", 5)) {
|
|
if (element != P_FILE && element != P_LINK) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("Mode\n");
|
|
fPackageFile->Read(&mode, 4);
|
|
swap_data(B_UINT32_TYPE, &mode, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
} else if (!memcmp(buffer, "FDat", 5)) {
|
|
if (element != P_DIRECTORY) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
parser_debug("FDat\n");
|
|
} else if (!memcmp(buffer, "ASig", 5)) {
|
|
if (element != P_FILE) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *signature = new char[length + 1];
|
|
fPackageFile->Read(signature, length);
|
|
signature[length] = 0;
|
|
parser_debug("Signature: %s\n", signature);
|
|
|
|
signatureString = signature;
|
|
delete[] signature;
|
|
} else if (!memcmp(buffer, "Link", 5)) {
|
|
if (element != P_LINK) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
fPackageFile->Read(&length, 4);
|
|
swap_data(B_UINT32_TYPE, &length, sizeof(uint32),
|
|
B_SWAP_BENDIAN_TO_HOST);
|
|
|
|
char *link = new char[length + 1];
|
|
fPackageFile->Read(link, length);
|
|
link[length] = 0;
|
|
parser_debug("Link: %s\n", link);
|
|
|
|
linkString = link;
|
|
delete[] link;
|
|
} else if (!memcmp(buffer, padding, 7)) {
|
|
PackageItem *item = NULL;
|
|
|
|
parser_debug("Padding!\n");
|
|
if (platform != 0xffffffff
|
|
&& static_cast<platform_type>(platform) != thisPlatform) {
|
|
// If the file/directory/item's platform is different than the
|
|
// target platform (or different than the 'any' constant),
|
|
// ignore this file
|
|
} else if (element == P_FILE) {
|
|
if (itemGroups && offset && size) {
|
|
BString dest = "";
|
|
uint8 localType = pathType;
|
|
|
|
if (path == 0xfffffffe)
|
|
dest << itemPath << "/" << nameString.String();
|
|
else if (path == 0xffffffff) {
|
|
localType = P_INSTALL_PATH;
|
|
dest = installDirectory;
|
|
dest << nameString;
|
|
} else {
|
|
if (cust) {
|
|
BString *def = static_cast<BString *>(
|
|
userPaths.ItemAt(path));
|
|
if (!def) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
if ((*def)[0] == '/')
|
|
localType = P_SYSTEM_PATH;
|
|
else
|
|
localType = P_USER_PATH;
|
|
|
|
dest << *def << "/" << nameString;
|
|
} else {
|
|
BPath *def = static_cast<BPath *>(
|
|
systemPaths.ItemAt(path));
|
|
if (!def) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
localType = P_SYSTEM_PATH;
|
|
|
|
dest << def->Path() << "/" << nameString;
|
|
}
|
|
}
|
|
|
|
parser_debug("Adding file: %s!\n", dest.String());
|
|
|
|
item = new PackageFile(fPackageFile, dest, localType, ctime,
|
|
mtime, offset, size, originalSize, 0, mimeString,
|
|
signatureString, mode);
|
|
}
|
|
} else if (element == P_DIRECTORY) {
|
|
if (itemGroups) {
|
|
if (installDirectoryFlag != 0) {
|
|
if (installDirectoryFlag < 0) {
|
|
// Normal directory
|
|
if (path == 0xfffffffe) {
|
|
// Install to current directory
|
|
itemPath << "/" << nameString.String();
|
|
directoryCount++;
|
|
} else if (path == 0xffffffff) {
|
|
// Install to install directory
|
|
pathType = P_INSTALL_PATH;
|
|
itemPath = installDirectory;
|
|
itemPath << nameString;
|
|
directoryCount = 1;
|
|
} else {
|
|
// Install to defined directory
|
|
if (cust) {
|
|
BString *def = static_cast<BString *>(
|
|
userPaths.ItemAt(path));
|
|
if (!def) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
if ((*def)[0] == '/')
|
|
pathType = P_SYSTEM_PATH;
|
|
else
|
|
pathType = P_USER_PATH;
|
|
|
|
itemPath = *def;
|
|
} else {
|
|
BPath *def = static_cast<BPath *>(
|
|
systemPaths.ItemAt(path));
|
|
if (!def) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
pathType = P_SYSTEM_PATH;
|
|
|
|
itemPath = def->Path();
|
|
}
|
|
|
|
itemPath << "/" << nameString;
|
|
directoryCount = 1;
|
|
}
|
|
} else {
|
|
// Install directory
|
|
if (path != 0xffffffff) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
|
|
installDirectory = nameString;
|
|
installDirectory << "/";
|
|
pathType = P_INSTALL_PATH;
|
|
itemPath = nameString;
|
|
|
|
installDirectoryFlag = -1;
|
|
}
|
|
|
|
parser_debug("Adding the directory %s!\n",
|
|
itemPath.String());
|
|
|
|
item = new PackageDirectory(fPackageFile, itemPath,
|
|
pathType, ctime, mtime, offset, size);
|
|
} else
|
|
installDirectoryFlag = -1;
|
|
}
|
|
} else if (element == P_LINK) {
|
|
if (itemGroups && linkString.Length()) {
|
|
BString dest = "";
|
|
uint8 localType = pathType;
|
|
|
|
if (path == 0xfffffffe)
|
|
dest << itemPath << "/" << nameString.String();
|
|
else if (path == 0xffffffff) {
|
|
localType = P_INSTALL_PATH;
|
|
dest = installDirectory;
|
|
dest << nameString;
|
|
} else {
|
|
if (cust) {
|
|
BString *def = static_cast<BString *>(
|
|
userPaths.ItemAt(path));
|
|
if (!def) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
if ((*def)[0] == '/')
|
|
localType = P_SYSTEM_PATH;
|
|
else
|
|
localType = P_USER_PATH;
|
|
|
|
dest << *def << "/" << nameString;
|
|
} else {
|
|
BPath *def = static_cast<BPath *>(systemPaths.ItemAt(path));
|
|
if (!def) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
localType = P_SYSTEM_PATH;
|
|
|
|
dest << def->Path() << "/" << nameString;
|
|
}
|
|
}
|
|
|
|
parser_debug("Adding link: %s! (type %s)\n", dest.String(),
|
|
pathType == P_SYSTEM_PATH
|
|
? "System" : localType == P_INSTALL_PATH
|
|
? "Install" : "User");
|
|
|
|
item = new PackageLink(fPackageFile, dest, linkString,
|
|
localType, ctime, mtime, mode, offset, size);
|
|
}
|
|
} else if (element == P_SCRIPT) {
|
|
parser_debug("Adding the script %s!\n",
|
|
nameString.String());
|
|
|
|
BString workingDirectory;
|
|
uint8 localType = P_SYSTEM_PATH;
|
|
if (path == 1)
|
|
workingDirectory << itemPath;
|
|
else if (path == 0xffffffff) {
|
|
workingDirectory << installDirectory;
|
|
localType = P_INSTALL_PATH;
|
|
}
|
|
|
|
fScripts.AddItem(new PackageScript(fPackageFile,
|
|
workingDirectory, localType, offset, size, originalSize));
|
|
} else {
|
|
// If the directory tree count is equal to zero, this means all
|
|
// directory trees have been closed and a padding sequence means the
|
|
// end of the section
|
|
if (directoryCount == 0)
|
|
break;
|
|
ret = itemPath.FindLast('/');
|
|
if (ret == B_ERROR) {
|
|
itemPath = "";
|
|
}
|
|
else {
|
|
itemPath.Truncate(ret);
|
|
}
|
|
directoryCount--;
|
|
}
|
|
|
|
if (item) {
|
|
_AddItem(item, originalSize, itemGroups, path, cust);
|
|
}
|
|
|
|
element = P_NONE;
|
|
} else if (!memcmp(buffer, "PkgA", 5)) {
|
|
parser_debug("PkgA\n");
|
|
break;
|
|
} else if (!memcmp(buffer, "PtcI", 5)) {
|
|
parser_debug("PtcI\n");
|
|
break;
|
|
} else {
|
|
fprintf(stderr, "Unknown file tag %s\n", buffer);
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
}
|
|
|
|
if (static_cast<uint64>(actualSize) != fileSize) {
|
|
// Inform the user of a possible error
|
|
int32 selection;
|
|
BAlert *warning = new BAlert("filesize_wrong",
|
|
B_TRANSLATE("There seems to be a file size mismatch in the "
|
|
"package file. The package might be corrupted or have been "
|
|
"modified after its creation. Do you still wish to continue?"),
|
|
B_TRANSLATE("Continue"),
|
|
B_TRANSLATE("Abort"), NULL,
|
|
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
|
warning->SetShortcut(1, B_ESCAPE);
|
|
selection = warning->Go();
|
|
|
|
if (selection == 1) {
|
|
RETURN_AND_SET_STATUS(B_ERROR);
|
|
}
|
|
}
|
|
|
|
if (!groups.IsEmpty())
|
|
fProfiles = groups;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
PackageInfo::_AddItem(PackageItem *item, uint64 size, uint32 groups,
|
|
uint32 path, uint32 cust)
|
|
{
|
|
// Add the item to all groups it resides in
|
|
uint32 i, n = fProfiles.CountItems(), mask = 1;
|
|
pkg_profile *profile;
|
|
|
|
fFiles.AddItem(item);
|
|
|
|
for (i = 0;i < n;i++) {
|
|
if (groups & mask) {
|
|
profile = static_cast<pkg_profile *>(fProfiles.ItemAt(i));
|
|
profile->items.AddItem(item);
|
|
profile->space_needed += size;
|
|
// If there is at least one non-predefined destination element
|
|
// in the package, we give the user the ability to select the
|
|
// installation directory.
|
|
// If there are only predefined path files in the package, but
|
|
// such defined by the user, the user will be able to select
|
|
// the destination volume
|
|
if (path == 0xffffffff)
|
|
profile->path_type = P_INSTALL_PATH;
|
|
else if (path < 0xfffffffe &&
|
|
profile->path_type != P_INSTALL_PATH) {
|
|
if (cust) {
|
|
profile->path_type = P_USER_PATH;
|
|
}
|
|
}
|
|
}
|
|
mask = mask << 1;
|
|
}
|
|
}
|
|
|