root/src/apps/haikudepot/server/LocalRepositoryUpdateProcess.cpp
/*
 * Copyright 2018-2026, Andrew Lindesay <apl@lindesay.co.nz>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */


#include "LocalRepositoryUpdateProcess.h"

#include <Catalog.h>
#include <Roster.h>
#include <String.h>
#include <StringList.h>

#include <package/Context.h>
#include <package/manager/Exceptions.h>
#include <package/PackageRoster.h>
#include <package/RefreshRepositoryRequest.h>

#include "App.h"
#include "AppUtils.h"
#include "DecisionProvider.h"
#include "JobStateListener.h"
#include "Logger.h"
#include "HaikuDepotConstants.h"


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "LocalRepositoryUpdateProcess"


using namespace BPackageKit;
using namespace BPackageKit::BManager::BPrivate;


LocalRepositoryUpdateProcess::LocalRepositoryUpdateProcess(
        Model *model, bool force)
        :
        AbstractProcess(),
        fModel(model),
        fForce(force)
{
}


LocalRepositoryUpdateProcess::~LocalRepositoryUpdateProcess()
{
}


const char*
LocalRepositoryUpdateProcess::Name() const
{
        return "LocalRepositoryUpdateProcess";
}


const char*
LocalRepositoryUpdateProcess::Description() const
{
        return B_TRANSLATE("Fetching remote repository data");
}


status_t
LocalRepositoryUpdateProcess::RunInternal()
{
        BPackageRoster roster;
        BStringList repoNames;
        HDINFO("[%s] will update local repositories' caches", Name());

        status_t result = roster.GetRepositoryNames(repoNames);

        if (result == B_OK) {
                DecisionProvider decisionProvider;
                JobStateListener listener;
                BContext context(decisionProvider, listener);
                BRepositoryCache cache;

                for (
                        int32 i = 0;
                        result == B_OK && i < repoNames.CountStrings() && !WasStopped();
                        ++i) {
                        result = _RunForRepositoryName(repoNames.StringAt(i), context,
                                roster, &cache);
                }
        } else {
                _NotifyError(strerror(result));
                result = B_ERROR;
        }

        if (result == B_OK) {
                HDINFO("[%s] did update %" B_PRIi32 " local repositories' caches",
                        Name(), repoNames.CountStrings());
        }

        return result;
}


bool
LocalRepositoryUpdateProcess::_ShouldRunForRepositoryName(
        const BString& repoName, BPackageKit::BPackageRoster& roster,
        BPackageKit::BRepositoryCache* cache)
{
        if (fForce) {
                HDINFO("[%s] am refreshing cache for repo [%s] as it was forced",
                        Name(), repoName.String());
                return true;
        }

        if (roster.GetRepositoryCache(repoName, cache) != B_OK) {
                HDINFO("[%s] am updating cache for repo [%s] as there was no cache",
                        Name(), repoName.String());
                return true;
        }

        if (static_cast<App*>(be_app)->IsFirstRun()) {
                HDINFO("[%s] am updating cache for repo [%s] as this is the first"
                        " time that the application has run", Name(), repoName.String());
                return true;
        }

        HDDEBUG("[%s] skipped update local repo [%s] cache", Name(),
                repoName.String());

        return false;
}


status_t
LocalRepositoryUpdateProcess::_RunForRepositoryName(const BString& repoName,
        BPackageKit::BContext& context, BPackageKit::BPackageRoster& roster,
        BPackageKit::BRepositoryCache* cache)
{
        HDINFO("[%s] will update local repo [%s] cache", Name(), repoName.String());

        status_t result = B_ERROR;
        BRepositoryConfig repoConfig;
        result = roster.GetRepositoryConfig(repoName, &repoConfig);
        if (result == B_OK) {
                if (_ShouldRunForRepositoryName(repoName, roster, cache)) {
                        try {
                                BRefreshRepositoryRequest refreshRequest(context, repoConfig);
                                result = refreshRequest.Process();
                                HDINFO("[%s] did update local repo [%s] cache", Name(), repoName.String());
                                result = B_OK;
                        } catch (BFatalErrorException& ex) {
                                _NotifyError(ex.Message(), ex.Details());
                        } catch (BException& ex) {
                                _NotifyError(ex.Message());
                        }
                }
        } else {
                _NotifyError(strerror(result));
        }

        return result;
}


void
LocalRepositoryUpdateProcess::_NotifyError(const BString& error) const
{
        _NotifyError(error, "");
}


void
LocalRepositoryUpdateProcess::_NotifyError(const BString& error,
        const BString& details) const
{
        HDINFO("an error has arisen updating the local repositories : %s",
                error.String());

        BString alertText(B_TRANSLATE("An error occurred while refreshing the "
                "repository: %error%"));
        alertText.ReplaceFirst("%error%", error);

        if (!details.IsEmpty()) {
                alertText.Append(" (");
                alertText.Append(details);
                alertText.Append(")");
        }

        AppUtils::NotifySimpleError(SimpleAlert(B_TRANSLATE("Repository update error"), alertText));
}