root/src/kits/package/RepositoryConfig.cpp
/*
 * Copyright 2011-2020, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Oliver Tappe <zooey@hirschkaefer.de>
 *              Andrew Lindesay <apl@lindesay.co.nz>
 */


#include <package/RepositoryConfig.h>

#include <stdlib.h>

#include <new>

#include <Directory.h>
#include <driver_settings.h>
#include <File.h>
#include <FindDirectory.h>
#include <Path.h>

#include <DriverSettings.h>


#define STORAGE_VERSION 2

#define KEY_BASE_URL_V1 "url"
#define KEY_IDENTIFIER_V1 "url"

#define KEY_BASE_URL_V2 "baseurl"
#define KEY_IDENTIFIER_V2 "identifier"
#define KEY_IDENTIFIER_V2_ALT "url"
        // should not be used any more in favour of 'identifier'

#define KEY_PRIORITY "priority"
#define KEY_CONFIG_VERSION "cfgversion"

namespace BPackageKit {


// these are mappings of known legacy identifier URLs that are possibly
// still present in some installations.  These are in pairs; the first
// being the legacy URL and the next being the replacement.  This can
// be phased out over time.

static const char* kLegacyUrlMappings[] = {
        "https://eu.hpkg.haiku-os.org/haikuports/master/x86_gcc2/current",
        "https://hpkg.haiku-os.org/haikuports/master/x86_gcc2/current",
        "https://eu.hpkg.haiku-os.org/haikuports/master/x86_64/current",
        "https://hpkg.haiku-os.org/haikuports/master/x86_64/current",
        NULL,
        NULL
};


BRepositoryConfig::BRepositoryConfig()
        :
        fInitStatus(B_NO_INIT),
        fPriority(kUnsetPriority),
        fIsUserSpecific(false)
{
}


BRepositoryConfig::BRepositoryConfig(const BEntry& entry)
{
        SetTo(entry);
}


BRepositoryConfig::BRepositoryConfig(const BString& name,
        const BString& baseURL, uint8 priority)
        :
        fInitStatus(B_OK),
        fName(name),
        fBaseURL(baseURL),
        fPriority(priority),
        fIsUserSpecific(false)
{
}


BRepositoryConfig::~BRepositoryConfig()
{
}


status_t
BRepositoryConfig::Store(const BEntry& entry) const
{
        BFile file(&entry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
        status_t result = file.InitCheck();
        if (result != B_OK)
                return result;

        BString configString;
        configString << KEY_CONFIG_VERSION << "=" << STORAGE_VERSION << "\n";
        configString << "\n";
        configString << "# the url where the repository data can be "
                "accessed.\n";
        configString << KEY_BASE_URL_V2 << "=" << fBaseURL << "\n";
        configString << "\n";

        configString << "# an identifier for the repository that is "
                "consistent across mirrors\n";
        if (fIdentifier.IsEmpty())
                configString << "# " << KEY_IDENTIFIER_V2 << "=???\n";
        else
                configString << KEY_IDENTIFIER_V2 << "=" << fIdentifier << "\n";
        configString << "\n";

        configString << "# a deprecated copy of the ["
                << KEY_IDENTIFIER_V2 << "] key above for older os versions\n";
        if (fIdentifier.IsEmpty())
                configString << "# " << KEY_IDENTIFIER_V2_ALT << "=???\n";
        else
                configString << KEY_IDENTIFIER_V2_ALT << "=" << fIdentifier << "\n";
        configString << "\n";

        configString << KEY_PRIORITY << "=" << fPriority << "\n";

        int32 size = configString.Length();
        if ((result = file.Write(configString.String(), size)) < size)
                return (result >= 0) ? B_ERROR : result;

        return B_OK;
}


status_t
BRepositoryConfig::InitCheck() const
{
        return fInitStatus;
}


static const char*
repository_config_swap_legacy_identifier_v1(const char* identifier)
{
        for (int32 i = 0; kLegacyUrlMappings[i] != NULL; i += 2) {
                if (strcmp(identifier, kLegacyUrlMappings[i]) == 0)
                        return kLegacyUrlMappings[i + 1];
        }
        return identifier;
}


status_t
BRepositoryConfig::SetTo(const BEntry& entry)
{
        fEntry = entry;
        fInitStatus = B_NO_INIT;

        entry_ref ref;
        status_t result = entry.GetRef(&ref);
        if (result != B_OK)
                return result;

        BDriverSettings driverSettings;
        result = driverSettings.Load(ref);
        if (result != B_OK)
                return result;

        const char* version = driverSettings.GetParameterValue(KEY_CONFIG_VERSION);
        int versionNumber = version == NULL ? 1 : atoi(version);
        const char *baseUrlKey;
        const char *identifierKeys[3] = { NULL, NULL, NULL };

        switch (versionNumber) {
                case 1:
                        baseUrlKey = KEY_BASE_URL_V1;
                        identifierKeys[0] = KEY_IDENTIFIER_V1;
                        break;
                case 2:
                        baseUrlKey = KEY_BASE_URL_V2;
                        identifierKeys[0] = KEY_IDENTIFIER_V2;
                        identifierKeys[1] = KEY_IDENTIFIER_V2_ALT;
                        break;
                default:
                        return B_BAD_DATA;
        }

        const char* baseUrl = driverSettings.GetParameterValue(baseUrlKey);
        const char* priorityString = driverSettings.GetParameterValue(KEY_PRIORITY);
        const char* identifier = NULL;

        for (int32 i = 0; identifier == NULL && identifierKeys[i] != NULL; i++)
                identifier = driverSettings.GetParameterValue(identifierKeys[i]);

        if (baseUrl == NULL || *baseUrl == '\0')
                return B_BAD_DATA;
        if (identifier == NULL || *identifier == '\0')
                return B_BAD_DATA;

        if (versionNumber == 1)
                identifier = repository_config_swap_legacy_identifier_v1(identifier);

        fName = entry.Name();
        fBaseURL = baseUrl;
        fPriority = priorityString == NULL
                ? kUnsetPriority : atoi(priorityString);
        fIdentifier = identifier;

        BPath userSettingsPath;
        if (find_directory(B_USER_SETTINGS_DIRECTORY, &userSettingsPath) == B_OK) {
                BDirectory userSettingsDir(userSettingsPath.Path());
                fIsUserSpecific = userSettingsDir.Contains(&entry);
        } else
                fIsUserSpecific = false;

        fInitStatus = B_OK;

        return B_OK;
}


const BString&
BRepositoryConfig::Name() const
{
        return fName;
}


const BString&
BRepositoryConfig::BaseURL() const
{
        return fBaseURL;
}


const BString&
BRepositoryConfig::Identifier() const
{
        return fIdentifier;
}


uint8
BRepositoryConfig::Priority() const
{
        return fPriority;
}


bool
BRepositoryConfig::IsUserSpecific() const
{
        return fIsUserSpecific;
}


const BEntry&
BRepositoryConfig::Entry() const
{
        return fEntry;
}


BString
BRepositoryConfig::PackagesURL() const
{
        if (fBaseURL.IsEmpty())
                return BString();
        return BString().SetToFormat("%s/packages", fBaseURL.String());
}


void
BRepositoryConfig::SetName(const BString& name)
{
        fName = name;
}


void
BRepositoryConfig::SetBaseURL(const BString& baseURL)
{
        fBaseURL = baseURL;
}


void
BRepositoryConfig::SetIdentifier(const BString& identifier)
{
        fIdentifier = identifier;
}


void
BRepositoryConfig::SetPriority(uint8 priority)
{
        fPriority = priority;
}


void
BRepositoryConfig::SetIsUserSpecific(bool isUserSpecific)
{
        fIsUserSpecific = isUserSpecific;
}


}       // namespace BPackageKit