/* * Copyright (c) 2007-2010, Haiku, Inc. * Distributed under the terms of the MIT license. * * Author: * Ɓukasz 'Sil2100' Zemczak */ #include "PackageInfo.h" #include #include #include #include #include #include #include #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(fProfiles.RemoveItem((int32)0)); if (iter == NULL) break; delete iter; } PackageItem *file = 0; while (true) { file = static_cast(fFiles.RemoveItem((int32)0)); if (file == NULL) break; delete file; } while (true) { file = static_cast(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(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(length)) { delete[] compressed; RETURN_AND_SET_STATUS(B_ERROR); } fImage.SetSize(original); status_t ret = inflate_data(compressed, length, static_cast(const_cast(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 - %lld\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) != 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( 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( 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( 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( 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( 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(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(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(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; } }