winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

PinningData.cpp (8313B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Public/winget/PinningData.h"
      5 #include "Microsoft/PinningIndex.h"
      6 #include "Public/winget/RepositorySource.h"
      7 
      8 using namespace AppInstaller::SQLite;
      9 using namespace AppInstaller::Repository;
     10 using namespace AppInstaller::Repository::Microsoft;
     11 
     12 namespace AppInstaller::Pinning
     13 {
     14     namespace
     15     {
     16         // Evaluates the pinning state of a version for a single pin.
     17         PinType EvaluatePinnedStateForVersion(
     18             const Utility::Version& version,
     19             const std::optional<Pin>& pin,
     20             PinBehavior behavior)
     21         {
     22             if (pin)
     23             {
     24                 if (pin->GetType() == PinType::Blocking
     25                     || (pin->GetType() == PinType::Pinning && behavior != PinBehavior::IncludePinned)
     26                     || (pin->GetType() == PinType::Gating && !pin->GetGatedVersion().IsValidVersion(version)))
     27                 {
     28                     return pin->GetType();
     29                 }
     30             }
     31 
     32             return PinType::Unknown;
     33         }
     34 
     35         // Gets the pinned state for an available version that may have a pin,
     36         // and optionally an additional pin that could come from the installed version.
     37         // If both pins are present, we return the one that is the most strict.
     38         Pinning::PinType GetPinnedStateForVersion(
     39             const Utility::Version& version,
     40             const std::optional<Pinning::Pin>& availablePin,
     41             const std::optional<Pinning::Pin>& installedPin,
     42             PinBehavior behavior)
     43         {
     44             if (behavior == PinBehavior::IgnorePins)
     45             {
     46                 return Pinning::PinType::Unknown;
     47             }
     48 
     49             return Stricter(
     50                 EvaluatePinnedStateForVersion(version, availablePin, behavior),
     51                 EvaluatePinnedStateForVersion(version, installedPin, behavior));
     52         }
     53     }
     54 
     55     PinningData::PinningData() = default;
     56     PinningData::PinningData(const PinningData&) = default;
     57     PinningData& PinningData::operator=(const PinningData&) = default;
     58     PinningData::PinningData(PinningData&&) noexcept = default;
     59     PinningData& PinningData::operator=(PinningData&&) noexcept = default;
     60     PinningData::~PinningData() = default;
     61 
     62     PinningData::PinningData(Disposition disposition)
     63     {
     64         if (disposition == Disposition::ReadOnly)
     65         {
     66             m_database = PinningIndex::OpenIfExists(SQLiteStorageBase::OpenDisposition::Read);
     67         }
     68         else
     69         {
     70             m_database = PinningIndex::OpenOrCreateDefault(SQLiteStorageBase::OpenDisposition::ReadWrite);
     71         }
     72     }
     73 
     74     PinningData::operator bool() const
     75     {
     76         return IsDatabaseConnected();
     77     }
     78 
     79     bool PinningData::IsDatabaseConnected() const
     80     {
     81         return static_cast<bool>(m_database);
     82     }
     83 
     84     void PinningData::AddOrUpdatePin(const Pin& pin)
     85     {
     86         THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected());
     87         m_database->AddOrUpdatePin(pin);
     88     }
     89 
     90     void PinningData::RemovePin(const PinKey& pinKey)
     91     {
     92         THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected());
     93         m_database->RemovePin(pinKey);
     94     }
     95 
     96     std::optional<Pin> PinningData::GetPin(const PinKey& pinKey)
     97     {
     98         return IsDatabaseConnected() ? m_database->GetPin(pinKey) : std::nullopt;
     99     }
    100 
    101     std::vector<Pin> PinningData::GetAllPins()
    102     {
    103         return IsDatabaseConnected() ? m_database->GetAllPins() : std::vector<Pin>{};
    104     }
    105 
    106     bool PinningData::ResetAllPins(std::string_view sourceId)
    107     {
    108         THROW_HR_IF(E_NOT_VALID_STATE, !IsDatabaseConnected());
    109         return m_database->ResetAllPins(sourceId);
    110     }
    111 
    112     PinningData::PinStateEvaluator::PinStateEvaluator(
    113         PinBehavior behavior,
    114         std::shared_ptr<PinningIndex> database,
    115         const std::shared_ptr<IPackageVersion>& installedVersion) :
    116         m_behavior(behavior), m_database(std::move(database))
    117     {
    118         if (m_behavior == PinBehavior::IgnorePins || !installedVersion)
    119         {
    120             // Because the database isn't guaranteed to be present, align ignoring pins with there being no pins to ignore.
    121             // Also do not consider pins when there is no installed version. This is to remain consistent with the previous
    122             // implementation. If this is to be changed, more install paths will need to be do pinning checks to ensure
    123             // that one could, for instance, block the install of a package.
    124             m_database.reset();
    125         }
    126         else if (m_database)
    127         {
    128             PinKey key = PinKey::GetPinKeyForInstalled(installedVersion->GetProperty(PackageVersionProperty::Id));
    129             m_installedPin = m_database->GetPin(key);
    130         }
    131 
    132         if (installedVersion)
    133         {
    134             m_installedVersion = Utility::VersionAndChannel{
    135                 Utility::Version{ installedVersion->GetProperty(PackageVersionProperty::Version) },
    136                 Utility::Channel{ installedVersion->GetProperty(PackageVersionProperty::Channel) }
    137             };
    138         }
    139     }
    140 
    141     PinningData::PinStateEvaluator::PinStateEvaluator(const PinStateEvaluator&) = default;
    142     PinningData::PinStateEvaluator& PinningData::PinStateEvaluator::operator=(const PinStateEvaluator&) = default;
    143     PinningData::PinStateEvaluator::PinStateEvaluator(PinStateEvaluator&&) noexcept = default;
    144     PinningData::PinStateEvaluator& PinningData::PinStateEvaluator::operator=(PinStateEvaluator&&) noexcept = default;
    145 
    146     PinningData::PinStateEvaluator::~PinStateEvaluator() = default;
    147 
    148     std::shared_ptr<IPackageVersion> PinningData::PinStateEvaluator::GetLatestAvailableVersionForPins(const std::shared_ptr<IPackageVersionCollection>& package)
    149     {
    150         if (!m_database)
    151         {
    152             return package->GetLatestVersion();
    153         }
    154 
    155         auto availableVersionKeys = package->GetVersionKeys();
    156 
    157         // Skip until we find a version that isn't pinned
    158         for (const auto& availableVersion : availableVersionKeys)
    159         {
    160             std::shared_ptr<IPackageVersion> packageVersion = package->GetVersion(availableVersion);
    161             if (EvaluatePinType(packageVersion) == Pinning::PinType::Unknown)
    162             {
    163                 return packageVersion;
    164             }
    165         }
    166 
    167         return {};
    168     }
    169 
    170     bool PinningData::PinStateEvaluator::IsUpdate(const std::shared_ptr<IPackageVersion>& availableVersion)
    171     {
    172         if (m_installedVersion && availableVersion)
    173         {
    174             Utility::VersionAndChannel availableVersionAndChannel{
    175                 Utility::Version{ availableVersion->GetProperty(PackageVersionProperty::Version) },
    176                 Utility::Channel{ availableVersion->GetProperty(PackageVersionProperty::Channel) }
    177             };
    178 
    179             return m_installedVersion->IsUpdatedBy(availableVersionAndChannel);
    180         }
    181 
    182         return false;
    183     }
    184 
    185     PinType PinningData::PinStateEvaluator::EvaluatePinType(const std::shared_ptr<AppInstaller::Repository::IPackageVersion>& packageVersion)
    186     {
    187         if (!m_database || !packageVersion)
    188         {
    189             return PinType::Unknown;
    190         }
    191 
    192         std::optional<Pin> incomingPin;
    193 
    194         PinKey pinKey{ packageVersion->GetProperty(PackageVersionProperty::Id).get(), packageVersion->GetSource().GetIdentifier()};
    195         auto itr = m_availablePins.find(pinKey);
    196         if (itr == m_availablePins.end())
    197         {
    198             incomingPin = m_database->GetPin(pinKey);
    199             m_availablePins[pinKey] = incomingPin;
    200         }
    201         else
    202         {
    203             incomingPin = itr->second;
    204         }
    205 
    206         return GetPinnedStateForVersion(packageVersion->GetProperty(PackageVersionProperty::Version).get(), incomingPin, m_installedPin, m_behavior);
    207     }
    208 
    209     // Creates an object for use in evaluating pinning data for a given package
    210     PinningData::PinStateEvaluator PinningData::CreatePinStateEvaluator(
    211         PinBehavior behavior,
    212         const std::shared_ptr<IPackageVersion>& installedVersion)
    213     {
    214         return { behavior, m_database, installedVersion };
    215     }
    216 }