From 9636019e43c68610e1ce41828c534cd6b2512b2b Mon Sep 17 00:00:00 2001 From: howie-f Date: Tue, 4 Aug 2020 17:22:12 +0200 Subject: [PATCH] [addons] only allow update from official repos or origin --- xbmc/addons/AddonManager.cpp | 27 +++++++++---- xbmc/addons/AddonManager.h | 15 +++++++- xbmc/addons/AddonRepos.cpp | 75 ++++++++++++++++++++++++++++++------ xbmc/addons/AddonRepos.h | 21 ++++++++++ 4 files changed, 118 insertions(+), 20 deletions(-) diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp index 3c68d17ff6229..52fce827bde59 100644 --- a/xbmc/addons/AddonManager.cpp +++ b/xbmc/addons/AddonManager.cpp @@ -310,16 +310,29 @@ bool CAddonMgr::GetInstallableAddons(VECADDONS& addons, const TYPE &type) bool CAddonMgr::FindInstallableById(const std::string& addonId, AddonPtr& result) { - VECADDONS versions; + CSingleLock lock(m_critSection); + + CAddonRepos addonRepos(*this); + addonRepos.LoadAddonsFromDatabase(m_database, addonId); + + AddonPtr addonToUpdate; + + // check for an update if addon is installed already + + if (GetAddon(addonId, addonToUpdate, ADDON_UNKNOWN, false)) { - CSingleLock lock(m_critSection); - if (!m_database.FindByAddonId(addonId, versions) || versions.empty()) - return false; + return addonRepos.DoAddonUpdateCheck(addonToUpdate, result); } - result = *std::max_element(versions.begin(), versions.end(), - [](const AddonPtr& a, const AddonPtr& b) { return a->Version() < b->Version(); }); - return true; + // get the latest version from all repos if the + // addon is not installed yet (e.g. for addon select dialog) + + CLog::Log( + LOGDEBUG, + "CAddonMgr::{}: addon {} is not installed. falling back to get latest version from ALL repos", + __FUNCTION__, addonId); + + return addonRepos.GetLatestAddonVersionFromAllRepos(addonId, result); } bool CAddonMgr::GetAddonsInternal(const TYPE& type, diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h index ec2684776cef2..eb324a2e27012 100644 --- a/xbmc/addons/AddonManager.h +++ b/xbmc/addons/AddonManager.h @@ -64,7 +64,7 @@ namespace ADDON /*! \brief Retrieve a specific addon (of a specific type) \param id the id of the addon to retrieve. - \param addon [out] the retrieved addon pointer - only use if the function returns true. + \param addon[out] the retrieved addon pointer - only use if the function returns true. \param type type of addon to retrieve - defaults to any type. \param enabledOnly whether we only want enabled addons - set to false to allow both enabled and disabled addons - defaults to true. \return true if an addon matching the id of the given type is available and is enabled (if enabledOnly is true). @@ -104,7 +104,18 @@ namespace ADDON bool GetInstallableAddons(VECADDONS& addons, const TYPE &type); - /*! Get the installable addon with the highest version. */ + /*! \brief Get the installable addon depending on install rules + * or fall back to highest version. + * \note This function gets called in different contexts. If it's + * called for checking possible updates for already installed addons + * our update restriction rules apply. + * If it's called to (for example) populate an addon-select-dialog + * the addon is not installed yet, and we have to fall back to the + * highest version. + * \param addonId addon to check for update or installation + * \param addon[out] the retrieved addon pointer - only use if the function returns true. + * \return true if an addon matching the id is available. + */ bool FindInstallableById(const std::string& addonId, AddonPtr& addon); void AddToUpdateableAddons(AddonPtr &pAddon); diff --git a/xbmc/addons/AddonRepos.cpp b/xbmc/addons/AddonRepos.cpp index 637202da0e7ae..843707bf2cadd 100644 --- a/xbmc/addons/AddonRepos.cpp +++ b/xbmc/addons/AddonRepos.cpp @@ -190,19 +190,27 @@ bool CAddonRepos::DoAddonUpdateCheck(const std::shared_ptr& addon, update.reset(); - if (ORIGIN_SYSTEM == addon->Origin()) - { - if (!FindAddonAndCheckForUpdate(addon, m_latestOfficialVersions, update)) - return false; - } - else + const AddonRepoUpdateMode updateMode = + CAddonSystemSettings::GetInstance().GetAddonRepoUpdateMode(); + + bool hasOfficialUpdate = FindAddonAndCheckForUpdate(addon, m_latestOfficialVersions, update); + + if (ORIGIN_SYSTEM != addon->Origin() && !hasOfficialUpdate) // not a system addon { - // if the addon is not found in an official repo... - if (!FindAddonAndCheckForUpdate(addon, m_latestOfficialVersions, update)) + // If we didn't find an official update + if (IsFromOfficialRepo(addon, true)) // is an official addon + { + if (updateMode == AddonRepoUpdateMode::ANY_REPOSITORY) + if (!FindAddonAndCheckForUpdate(addon, m_latestPrivateVersions, update)) + return false; + } + else { - // ...we move on and check the private/3rd party repo(s) - if (!FindAddonAndCheckForUpdate(addon, m_latestPrivateVersions, update)) - return false; + // ...we check for updates in the origin repo only + const auto& repoEntry = m_latestVersionsByRepo.find(addon->Origin()); + if (repoEntry != m_latestVersionsByRepo.end()) + if (!FindAddonAndCheckForUpdate(addon, repoEntry->second, update)) + return false; } } @@ -240,3 +248,48 @@ bool CAddonRepos::FindAddonAndCheckForUpdate( return false; } + +bool CAddonRepos::GetLatestVersionByMap(const std::string& addonId, + const std::map>& map, + std::shared_ptr& result) const +{ + const auto& remote = map.find(addonId); + if (remote != map.end()) // is addon in the desired map? + { + result = remote->second; + return true; + } + + return false; +} + +bool CAddonRepos::GetLatestAddonVersionFromAllRepos(const std::string& addonId, + std::shared_ptr& result) const +{ + const AddonRepoUpdateMode updateMode = + CAddonSystemSettings::GetInstance().GetAddonRepoUpdateMode(); + + bool hasOfficialVersion = GetLatestVersionByMap(addonId, m_latestOfficialVersions, result); + + if (hasOfficialVersion) + { + if (updateMode == AddonRepoUpdateMode::ANY_REPOSITORY) + { + std::shared_ptr thirdPartyAddon; + + // only use this version if it's higher than the official one + if (GetLatestVersionByMap(addonId, m_latestPrivateVersions, thirdPartyAddon)) + { + if (thirdPartyAddon->Version() > result->Version()) + result = thirdPartyAddon; + } + } + } + else + { + if (!GetLatestVersionByMap(addonId, m_latestPrivateVersions, result)) + return false; + } + + return true; +} diff --git a/xbmc/addons/AddonRepos.h b/xbmc/addons/AddonRepos.h index f9aac1af21d9b..c0fd6626e564c 100644 --- a/xbmc/addons/AddonRepos.h +++ b/xbmc/addons/AddonRepos.h @@ -87,6 +87,16 @@ class CAddonRepos bool DoAddonUpdateCheck(const std::shared_ptr& addon, std::shared_ptr& update) const; + /*! + * \brief Retrieves the latest version of an addon from all installed repositories + * follows addon origin restriction rules + * \param addonId addon id we're looking the latest version for + * \param[out] result pointer to the found addon + * \return true if a version was found, false otherwise + */ + bool GetLatestAddonVersionFromAllRepos(const std::string& addonId, + std::shared_ptr& result) const; + private: /*! * \brief Looks up an addon in a given repository map and @@ -128,6 +138,17 @@ class CAddonRepos const std::shared_ptr& addonToAdd, std::map>>& map) const; + /*! + * \brief Looks up an addon entry in a specific map + * \param addonId addon we want to retrieve + * \param map the map we're looking into for the wanted addon + * \param[out] result pointer to the found addon, only use when function returns true + * \return true if the addon was found in the map, false otherwise + */ + bool GetLatestVersionByMap(const std::string& addonId, + const std::map>& map, + std::shared_ptr& result) const; + const CAddonMgr& m_addonMgr; std::vector> m_allAddons;