* Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerIconExportUpdateProcess.h"
#include <sys/stat.h>
#include <time.h>
#include <AutoDeleter.h>
#include <BufferIO.h>
#include <Catalog.h>
#include <FileIO.h>
#include "DataIOUtils.h"
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "PackageIconTarRepository.h"
#include "ServerHelper.h"
#include "StandardMetaDataJsonEventListener.h"
#include "StorageUtils.h"
#include "TarArchiveService.h"
#define ENTRY_PATH_METADATA "hicn/info.json"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerIconExportUpdateProcess"
find the meta-data file. This is a JSON piece of data that describes the
timestamp of the last modified data in the tar file. This is then used to
establish if the server has any newer data and hence if it is worth
downloading fresh data. The process uses the standard HTTP
If-Modified-Since header.
*/
class InfoJsonExtractEntryListener : public TarEntryListener
{
public:
InfoJsonExtractEntryListener();
virtual ~InfoJsonExtractEntryListener();
virtual status_t Handle(
const TarArchiveHeader& header,
size_t offset,
BDataIO *data);
BPositionIO& GetInfoJsonData();
private:
BMallocIO fInfoJsonData;
};
InfoJsonExtractEntryListener::InfoJsonExtractEntryListener()
{
}
InfoJsonExtractEntryListener::~InfoJsonExtractEntryListener()
{
}
BPositionIO&
InfoJsonExtractEntryListener::GetInfoJsonData()
{
return fInfoJsonData;
}
status_t
InfoJsonExtractEntryListener::Handle(const TarArchiveHeader& header, size_t offset, BDataIO* data)
{
if (header.Length() > 0 && header.FileName() == ENTRY_PATH_METADATA) {
status_t copyResult = DataIOUtils::CopyAll(&fInfoJsonData, data);
if (copyResult == B_OK) {
HDINFO("[InfoJsonExtractEntryListener] did extract [%s]", ENTRY_PATH_METADATA);
fInfoJsonData.Seek(0, SEEK_SET);
return B_CANCELED;
}
return copyResult;
}
return B_OK;
}
*/
ServerIconExportUpdateProcess::ServerIconExportUpdateProcess(Model* model,
uint32 serverProcessOptions)
:
AbstractSingleFileServerProcess(serverProcessOptions),
fModel(model)
{
}
ServerIconExportUpdateProcess::~ServerIconExportUpdateProcess()
{
}
const char*
ServerIconExportUpdateProcess::Name() const
{
return "ServerIconExportUpdateProcess";
}
const char*
ServerIconExportUpdateProcess::Description() const
{
return B_TRANSLATE("Synchronizing icons");
}
status_t
ServerIconExportUpdateProcess::ProcessLocalData()
{
BPath tarPath;
status_t result = GetLocalPath(tarPath);
if (result == B_OK) {
PackageIconTarRepository* tarRepository = new PackageIconTarRepository(tarPath);
result = tarRepository->Init();
if (result == B_OK)
fModel->SetIconRepository(PackageIconRepositoryRef(tarRepository, true));
else
delete tarRepository;
}
return result;
}
status_t
ServerIconExportUpdateProcess::GetLocalPath(BPath& path) const
{
return StorageUtils::IconTarPath(path);
}
apparently scan the entire tarball for the file that it is looking for, but
actually it will cancel the scan once it has found it's target object (the
JSON data containing the meta-data) and by convention, the server side will
place the meta-data as one of the first objects in the tar-ball so it will
find it quickly.
*/
status_t
ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue)
{
headerValue.SetTo("");
BPath tarPath;
status_t result = GetLocalPath(tarPath);
if (result == B_OK) {
off_t size;
bool hasFile;
result = StorageUtils::ExistsObject(tarPath, &hasFile, NULL, &size);
if (result == B_OK && (!hasFile || size == 0))
return result;
}
if (result == B_OK) {
BFile* tarIo = new BFile(tarPath.Path(), O_RDONLY);
BBufferIO* bufferTarIo = new BBufferIO(tarIo, 10 * 1024, true);
ObjectDeleter<BBufferIO> tarIoDeleter(bufferTarIo);
InfoJsonExtractEntryListener* extractDataListener = new InfoJsonExtractEntryListener();
ObjectDeleter<InfoJsonExtractEntryListener> extractDataListenerDeleter(extractDataListener);
result = TarArchiveService::ForEachEntry(*bufferTarIo, extractDataListener);
if (result == B_CANCELED) {
result = B_OK;
StandardMetaData metaData;
BString metaDataJsonPath;
GetStandardMetaDataJsonPath(metaDataJsonPath);
StandardMetaDataJsonEventListener parseInfoJsonListener(metaDataJsonPath, metaData);
BPrivate::BJson::Parse(&(extractDataListener->GetInfoJsonData()),
&parseInfoJsonListener);
result = parseInfoJsonListener.ErrorStatus();
if (result == B_OK)
SetIfModifiedSinceHeaderValueFromMetaData(headerValue, metaData);
else
HDERROR("[%s] unable to parse the meta data from the tar file", Name());
} else {
HDERROR("[%s] did not find the metadata [%s] in the tar", Name(), ENTRY_PATH_METADATA);
result = B_BAD_DATA;
}
}
return result;
}
status_t
ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const
{
return B_ERROR;
}
void
ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath(BString& jsonPath) const
{
jsonPath.SetTo("$");
}
BString
ServerIconExportUpdateProcess::UrlPathComponent()
{
return "/__pkgicon/all.tar.gz";
}