qu1ck 52157acbfe PCM: fix kicad version restriction logic
The json schema for packages implies that kicad min/max version
restriction takes major.minor.patch string but actual logic
only checks major and minor.

This fixes the logic to work with full major.minor.patch tuple.
Additionally the version max logic will substitute missing portions
with 999 for a reasonable default so that say 7.1 is still considered
as >= 7.1.5 when checking for max version.

Reported here
https://forum.kicad.info/t/updating-already-installed-plugins-using-content-manager/35532/5?u=qu1ck

(cherry picked from commit 06028d50155f0f5b240a9748f6f06fb66269cd72)
2022-07-18 10:26:27 -07:00

320 lines
11 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PCM_H_
#define PCM_H_
#include "core/wx_stl_compat.h"
#include "pcm_data.h"
#include "widgets/wx_progress_reporters.h"
#include <iostream>
#include <map>
#include <nlohmann/json-schema.hpp>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <wx/wx.h>
///< Contains list of all valid directories that get extracted from a package archive
const std::unordered_set<wxString> PCM_PACKAGE_DIRECTORIES( {
wxT( "plugins" ),
wxT( "footprints" ),
wxT( "3dmodels" ),
wxT( "symbols" ),
wxT( "resources" ),
wxT( "colors" ),
} );
///< Package states
///< Package is available if it is not installed and there is a compatible version
///< Package is unavailable if it is not installed and there are no compatible versions
///< Pending states are intermediary when (un)installation is scheduled but not yet performed
enum PCM_PACKAGE_STATE
{
PPS_AVAILABLE = 0,
PPS_UNAVAILABLE = 1,
PPS_INSTALLED = 2,
PPS_PENDING_INSTALL = 3,
PPS_PENDING_UNINSTALL = 4
};
///< Package actions
enum PCM_PACKAGE_ACTION
{
PPA_INSTALL = 0,
PPA_UNINSTALL = 1
};
typedef std::vector<std::pair<wxString, wxString>> STRING_PAIR_LIST;
typedef std::vector<std::tuple<wxString, wxString, wxString>> STRING_TUPLE_LIST;
/**
* @brief Main class of Plugin and Content Manager subsystem
*
* This class handles logistics of repository management, caching, json validation,
* tracking installed packages and provides some utility methods.
*
* Repository caching is done in $KICADX_3RD_PARTY/cache directory with each
* repository storing it's metadata, packages and optionally resource files under
* it's id subdirectory.
*
* Repository id is a prefix of sha256 of it's main url.
*
* JSON schema file is expected to be in $KICAD_DATA/schemas directory
*
* Installed packages are stored in <user_settings>/installed_packages.json
* If that file is missing PCM will try to reconstruct it from existing
* directory structure inside $KICADX_3RD_PARTY but package descriptions
* and other metadata will be lost.
*/
class PLUGIN_CONTENT_MANAGER
{
public:
PLUGIN_CONTENT_MANAGER( wxWindow* aParent );
~PLUGIN_CONTENT_MANAGER();
/**
* @brief Fetches repository metadata from given url
*
* @param aUrl URL of the repository
* @param aRepository fetched repository metadata
* @param aReporter progress reporter dialog to use for download
* @return true if successful
* @return false if URL could not be downloaded or result could not be parsed
*/
bool FetchRepository( const wxString& aUrl, PCM_REPOSITORY& aRepository,
WX_PROGRESS_REPORTER* aReporter );
/**
* @brief Validates json against a specific definition in the PCM schema
*
* @param aJson JSON object to validate
* @param aUri JSON URI of a definition to validate against, default is root
* @throws std::invalid_argument on validation failure
*/
void ValidateJson( const nlohmann::json& aJson,
const nlohmann::json_uri& aUri = nlohmann::json_uri( "#" ) ) const;
/**
* @brief Verifies SHA256 hash of a binary stream
*
* @param aStream input stream
* @param aHash sha256 lowercase hex string
* @return true if hash matches
*/
bool VerifyHash( std::istream& aStream, const wxString& aHash ) const;
/**
* @brief Set list of repositories
*
* Discards cache of repositories that were previously cached but are not
* on the new list of repositories.
*
* @param aRepositories list of <URL, name> pairs of repositories
*/
void SetRepositoryList( const STRING_PAIR_LIST& aRepositories );
/**
* @brief Discard in-memory and on-disk cache of a repository
*
* @param aRepositoryId id of the repository
*/
void DiscardRepositoryCache( const wxString& aRepositoryId );
/**
* @brief Mark package as installed
*
* @param aPackage package metadata
* @param aVersion installed package version
* @param aRepositoryId id of the source repository or empty estring if
* installed from a local file
*/
void MarkInstalled( const PCM_PACKAGE& aPackage, const wxString& aVersion,
const wxString& aRepositoryId );
/**
* @brief Mark package as uninstalled
*
* @param aPackage package metadata
*/
void MarkUninstalled( const PCM_PACKAGE& aPackage );
/**
* @brief Get list of repositories
*
* @return const STRING_TUPLE_LIST& list of repositories in <id, URL, name> tuple format
*/
const STRING_TUPLE_LIST& GetRepositoryList() const { return m_repository_list; }
/**
* @brief Cache specified repository packages and other metadata
*
* This method fetches latest repository metadata, checks if there is cache on disk,
* compares it's last update timestamp to repository metadata and redownloads packages
* list if necessary.
* Then it checks sha256 if provided and parses the package list.
* Parsed packages metadata is stored in memory.
*
* Process is repeated with resources file except it is just stored on disk and
* any errors at this stage are ignored.
*
* @param aRepositoryId id of the repository to cache
* @return true if packages metadata was cached successfully
*/
const bool CacheRepository( const wxString& aRepositoryId );
/**
* @brief Get the packages metadata from a previously cached repository
*
* This should only be called after a successful CacheRepository call
*
* @param aRepositoryId id of the repository
* @return list of package metadata objects
*/
const std::vector<PCM_PACKAGE>& GetRepositoryPackages( const wxString& aRepositoryId ) const;
/**
* @brief Get list of installed packages
*
* @return vector of PCM_INSTALLATION_ENTRY objects
*/
const std::vector<PCM_INSTALLATION_ENTRY> GetInstalledPackages() const;
/**
* @brief Get the current version of an installed package
*
* @param aPackageId id of the package
* @return current version
* @throws std::out_of_range if package with given id is not installed
*/
const wxString& GetInstalledPackageVersion( const wxString& aPackageId ) const;
///< Returns current 3rd party directory path
const wxString& Get3rdPartyPath() const { return m_3rdparty_path; };
/**
* @brief Get current state of the package
*
* @param aRepositoryId repository id
* @param aPackageId package id
* @return PCM_PACKAGE_STATE
*/
PCM_PACKAGE_STATE GetPackageState( const wxString& aRepositoryId, const wxString& aPackageId );
/**
* @brief Downloads url to an output stream
*
* @param aUrl URL to download
* @param aOutput output stream
* @param aReporter progress dialog to use
* @param aSizeLimit maximum download size, 0 for unlimited
* @return true if download was successful
* @return false if download failed or was too large
*/
bool DownloadToStream( const wxString& aUrl, std::ostream* aOutput,
WX_PROGRESS_REPORTER* aReporter,
const size_t aSizeLimit = DEFAULT_DOWNLOAD_MEM_LIMIT );
/**
* @brief Get the approximate measure of how much given package matches the search term
*
* @param aPackage package metadata object
* @param aSearchTerm search term
* @return int search rank, higher number means better match, 0 means no match
*/
int GetPackageSearchRank( const PCM_PACKAGE& aPackage, const wxString& aSearchTerm );
/**
* @brief Get the icon bitmaps for repository packages
*
* Repository package icons are taken from repository's resources zip file
*
* @param aRepositoryId id of the repository
* @return map of package id -> bitmap
*/
std::unordered_map<wxString, wxBitmap>
GetRepositoryPackageBitmaps( const wxString& aRepositoryId );
/**
* @brief Get the icon bitmaps for installed packages
*
* Icons for installed packages are taken from package extracted files in
* $KICADX_3RD_PARTY/resources/<packageid> directories
*
* @return map of package id -> bitmap
*/
std::unordered_map<wxString, wxBitmap> GetInstalledPackageBitmaps();
private:
///< Default download limit of 10 Mb to not use too much memory
static constexpr size_t DEFAULT_DOWNLOAD_MEM_LIMIT = 10 * 1024 * 1024;
/**
* @brief Downloads packages metadata to in memory stream, verifies hash and attempts to parse it
*
* @param aUrl URL of the packages metadata
* @param aHash optional sha256 hash
* @param aPackages resulting packages metadata list
* @param aReporter progress dialog to use for download
* @return true if packages were successfully downloaded, verified and parsed
*/
bool fetchPackages( const wxString& aUrl, const boost::optional<wxString>& aHash,
std::vector<PCM_PACKAGE>& aPackages, WX_PROGRESS_REPORTER* aReporter );
/**
* @brief Get the cached repository metadata
*
* @param aRepositoryId id of the repository
* @return const PCM_REPOSITORY&
*/
const PCM_REPOSITORY& getCachedRepository( const wxString& aRepositoryId ) const;
/**
* @brief Parses version strings and calculates compatibility
*
* This should be called after loading package metadata from repository or from
* installation entries
*
* @param aPackage package metadata object
*/
static void preparePackage( PCM_PACKAGE& aPackage );
///< Returns current UTC timestamp
time_t getCurrentTimestamp() const;
wxWindow* m_dialog;
nlohmann::json_schema::json_validator m_schema_validator;
wxString m_3rdparty_path;
wxString m_cache_path;
std::unordered_map<wxString, PCM_REPOSITORY> m_repository_cache;
STRING_TUPLE_LIST m_repository_list; // (id, name, url) tuples
// Using sorted map to keep order of entries in installed list stable
std::map<wxString, PCM_INSTALLATION_ENTRY> m_installed;
const static std::tuple<int, int, int> m_kicad_version;
};
#endif // PCM_H_