323 lines
6.7 KiB
C++
323 lines
6.7 KiB
C++
/*
|
|
* Copyright (c) 2007, Haiku, Inc.
|
|
* Distributed under the terms of the MIT license.
|
|
*
|
|
* Author:
|
|
* Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
|
|
*/
|
|
|
|
|
|
#include "InstalledPackageInfo.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <Directory.h>
|
|
#include <Entry.h>
|
|
#include <FindDirectory.h>
|
|
|
|
|
|
const char * kPackagesDir = "packages";
|
|
|
|
|
|
static status_t
|
|
info_prepare(const char *filename, BFile *file, BMessage *info)
|
|
{
|
|
if (filename == NULL || file == NULL || info == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
BPath path;
|
|
status_t ret = find_directory(B_USER_CONFIG_DIRECTORY, &path);
|
|
if (ret == B_OK)
|
|
ret = path.Append(kPackagesDir);
|
|
if (ret == B_OK)
|
|
ret = path.Append(filename);
|
|
if (ret == B_OK)
|
|
ret = file->SetTo(path.Path(), B_READ_ONLY);
|
|
if (ret == B_OK)
|
|
ret = info->Unflatten(file);
|
|
if (ret == B_OK && info->what != P_PACKAGE_INFO)
|
|
ret = B_ERROR;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
status_t
|
|
info_get_package_name(const char *filename, BString &name)
|
|
{
|
|
BFile file;
|
|
BMessage info;
|
|
status_t ret = info_prepare(filename, &file, &info);
|
|
if (ret == B_OK)
|
|
ret = info.FindString("package_name", &name);
|
|
return ret;
|
|
}
|
|
|
|
|
|
status_t
|
|
info_get_package_version(const char *filename, BString &version)
|
|
{
|
|
BFile file;
|
|
BMessage info;
|
|
status_t ret = info_prepare(filename, &file, &info);
|
|
if (ret == B_OK)
|
|
ret = info.FindString("package_version", &version);
|
|
return ret;
|
|
}
|
|
|
|
|
|
InstalledPackageInfo::InstalledPackageInfo()
|
|
:
|
|
fStatus(B_NO_INIT),
|
|
fIsUpToDate(false),
|
|
fCreate(false),
|
|
fSpaceNeeded(0),
|
|
fInstalledItems(10)
|
|
{
|
|
}
|
|
|
|
|
|
InstalledPackageInfo::InstalledPackageInfo(const char *packageName,
|
|
const char *version, bool create)
|
|
:
|
|
fStatus(B_NO_INIT),
|
|
fIsUpToDate(false),
|
|
fSpaceNeeded(0),
|
|
fInstalledItems(10)
|
|
{
|
|
SetTo(packageName, version, create);
|
|
}
|
|
|
|
|
|
InstalledPackageInfo::~InstalledPackageInfo()
|
|
{
|
|
_ClearItemList();
|
|
}
|
|
|
|
|
|
status_t
|
|
InstalledPackageInfo::InitCheck()
|
|
{
|
|
return fStatus;
|
|
}
|
|
|
|
|
|
status_t
|
|
InstalledPackageInfo::SetTo(const char *packageName, const char *version,
|
|
bool create)
|
|
{
|
|
_ClearItemList();
|
|
|
|
fCreate = create;
|
|
fStatus = B_NO_INIT;
|
|
fVersion = version;
|
|
|
|
if (!packageName)
|
|
return fStatus;
|
|
|
|
BPath configPath;
|
|
if (find_directory(B_USER_CONFIG_DIRECTORY, &configPath) != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
|
|
if (fPathToInfo.SetTo(configPath.Path(), kPackagesDir) != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
|
|
// Check whether the directory exists
|
|
BDirectory packageDir(fPathToInfo.Path());
|
|
fStatus = packageDir.InitCheck();
|
|
if (fStatus == B_ENTRY_NOT_FOUND) {
|
|
// If not, create it
|
|
packageDir.SetTo(configPath.Path());
|
|
if (packageDir.CreateDirectory(kPackagesDir, &packageDir) != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
}
|
|
|
|
BString filename = packageName;
|
|
filename << version << ".pdb";
|
|
if (fPathToInfo.Append(filename.String()) != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
|
|
BFile package(fPathToInfo.Path(), B_READ_ONLY);
|
|
fStatus = package.InitCheck();
|
|
if (fStatus == B_OK) {
|
|
// The given package exists, so we can unflatten the data to a message
|
|
// and then pass it further
|
|
BMessage info;
|
|
if (info.Unflatten(&package) != B_OK || info.what != P_PACKAGE_INFO) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
|
|
int32 count;
|
|
fStatus = info.FindString("package_name", &fName);
|
|
fStatus |= info.FindString("package_desc", &fDescription);
|
|
fStatus |= info.FindString("package_version", &fVersion);
|
|
int64 spaceNeeded = 0;
|
|
fStatus |= info.FindInt64("package_size", &spaceNeeded);
|
|
fSpaceNeeded = static_cast<uint64>(spaceNeeded);
|
|
fStatus |= info.FindInt32("file_count", &count);
|
|
if (fStatus != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
|
|
int32 i;
|
|
BString itemPath;
|
|
for (i = 0; i < count; i++) {
|
|
if (info.FindString("items", i, &itemPath) != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
fInstalledItems.AddItem(new BString(itemPath)); // Or maybe BPath better?
|
|
}
|
|
fIsUpToDate = true;
|
|
} else if (fStatus == B_ENTRY_NOT_FOUND) {
|
|
if (create) {
|
|
fStatus = B_OK;
|
|
fIsUpToDate = false;
|
|
}
|
|
}
|
|
|
|
return fStatus;
|
|
}
|
|
|
|
|
|
status_t
|
|
InstalledPackageInfo::AddItem(const char *itemName)
|
|
{
|
|
if (!itemName)
|
|
return B_ERROR;
|
|
|
|
return fInstalledItems.AddItem(new BString(itemName));
|
|
}
|
|
|
|
|
|
status_t
|
|
InstalledPackageInfo::Uninstall()
|
|
{
|
|
if (fStatus != B_OK)
|
|
return fStatus;
|
|
|
|
BString *iter;
|
|
uint32 i, count = fInstalledItems.CountItems();
|
|
BEntry entry;
|
|
status_t ret;
|
|
|
|
// Try to remove all entries that are present in the list
|
|
for (i = 0; i < count; i++) {
|
|
iter = static_cast<BString *>(fInstalledItems.ItemAt(count - i - 1));
|
|
ret = entry.SetTo(iter->String());
|
|
if (ret == B_BUSY) {
|
|
// The entry's directory is locked - wait a few cycles for it to
|
|
// unlock itself
|
|
int32 tries = 0;
|
|
for (tries = 0; tries < P_BUSY_TRIES; tries++) {
|
|
ret = entry.SetTo(iter->String());
|
|
if (ret != B_BUSY)
|
|
break;
|
|
// Wait a moment
|
|
usleep(1000);
|
|
}
|
|
}
|
|
|
|
if (ret == B_ENTRY_NOT_FOUND)
|
|
continue;
|
|
else if (ret != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
|
|
if (entry.Exists() && entry.Remove() != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
fInstalledItems.RemoveItem(count - i - 1);
|
|
}
|
|
|
|
if (entry.SetTo(fPathToInfo.Path()) != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
if (entry.Exists() && entry.Remove() != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
|
|
return fStatus;
|
|
}
|
|
|
|
|
|
status_t
|
|
InstalledPackageInfo::Save()
|
|
{
|
|
// If the package info is not up to date and everything till now was
|
|
// done correctly, we will save all data as a flattened BMessage to the
|
|
// package info file
|
|
if (fIsUpToDate || fStatus != B_OK)
|
|
return fStatus;
|
|
|
|
BFile package;
|
|
if (fCreate) {
|
|
fStatus = package.SetTo(fPathToInfo.Path(), B_WRITE_ONLY | B_CREATE_FILE
|
|
| B_ERASE_FILE);
|
|
}
|
|
else {
|
|
fStatus = package.SetTo(fPathToInfo.Path(), B_WRITE_ONLY | B_ERASE_FILE);
|
|
}
|
|
|
|
if (fStatus != B_OK)
|
|
return fStatus;
|
|
|
|
status_t ret;
|
|
int32 i, count = fInstalledItems.CountItems();
|
|
BMessage info(P_PACKAGE_INFO);
|
|
ret = info.AddString("package_name", fName);
|
|
ret |= info.AddString("package_desc", fDescription);
|
|
ret |= info.AddString("package_version", fVersion);
|
|
ret |= info.AddInt64("package_size", fSpaceNeeded);
|
|
ret |= info.AddInt32("file_count", count);
|
|
if (ret != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
|
|
BString *iter;
|
|
for (i = 0; i < count; i++) {
|
|
iter = static_cast<BString *>(fInstalledItems.ItemAt(i));
|
|
if (info.AddString("items", *iter) != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
}
|
|
|
|
if (info.Flatten(&package) != B_OK) {
|
|
fStatus = B_ERROR;
|
|
return fStatus;
|
|
}
|
|
fIsUpToDate = true;
|
|
|
|
return fStatus;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
InstalledPackageInfo::_ClearItemList()
|
|
{
|
|
for (int32 i = fInstalledItems.CountItems() - 1; i >= 0; i--)
|
|
delete static_cast<BString*>(fInstalledItems.ItemAtFast(i));
|
|
fInstalledItems.MakeEmpty();
|
|
}
|
|
|