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 }