32#include <unordered_set>
34#include <wx/filename.h>
36#include <wx/sstream.h>
37#include <wx/wfstream.h>
38#include <wx/zipstrm.h>
42 std::forward_list<wxRegEx>& aKeepOnUpdate )
44 auto compile_regex = [&](
const wxString& regex )
46 aKeepOnUpdate.emplace_front( regex, wxRE_DEFAULT );
48 if( !aKeepOnUpdate.front().IsValid() )
49 aKeepOnUpdate.pop_front();
58 const wxString& aRepositoryId,
const bool isUpdate )
60 PCM_TASK download_task = [aPackage, aVersion, aRepositoryId, isUpdate,
this]()
63 file_path.AppendDir(
"pcm" );
66 auto find_pkgver = std::find_if( aPackage.
versions.begin(), aPackage.
versions.end(),
69 return pv.version == aVersion;
72 if( find_pkgver == aPackage.
versions.end() )
80 if( !wxDirExists( file_path.GetPath() )
81 && !wxFileName::Mkdir( file_path.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
83 m_reporter->PCMReport(
_(
"Unable to create download directory!" ),
88 int code =
downloadFile( file_path.GetFullPath(), *find_pkgver->download_url );
90 if( code != CURLE_OK )
93 wxRemoveFile( file_path.GetFullPath() );
97 PCM_TASK install_task = [aPackage, aVersion, aRepositoryId, file_path, isUpdate,
this]()
111 TRANSFER_CALLBACK callback = [&](
size_t dltotal,
size_t dlnow,
size_t ultotal,
size_t ulnow )
114 m_reporter->SetDownloadProgress( dlnow, dltotal );
121 std::ofstream out( aFilePath.ToUTF8(), std::ofstream::binary );
125 curl.
SetURL( url.ToUTF8().data() );
136 uint64_t download_total;
139 m_reporter->SetDownloadProgress( download_total, download_total );
141 if( code != CURLE_OK && code != CURLE_ABORTED_BY_CALLBACK )
153 const wxString& aVersion,
154 const wxString& aRepositoryId,
155 const wxFileName& aFilePath,
const bool isUpdate )
157 auto pkgver = std::find_if( aPackage.
versions.begin(), aPackage.
versions.end(),
160 return pv.version == aVersion;
163 if( pkgver == aPackage.
versions.end() )
172 std::forward_list<wxRegEx> keep_on_update;
177 const std::optional<wxString>& hash = pkgver->download_sha256;
178 bool hash_match =
true;
182 std::ifstream stream( aFilePath.GetFullPath().ToUTF8(), std::ios::binary );
183 hash_match =
m_pcm->VerifyHash( stream, *hash );
189 "%s does not match repository entry. "
190 "This may indicate a problem with the "
191 "package, if the issue persists "
192 "report this to repository maintainers." ),
214 m_pcm->MarkInstalled( aPackage, pkgver->version, aRepositoryId );
227 wxString::Format(
_(
"Removing downloaded archive '%s'." ), aFilePath.GetFullName() ),
230 wxRemoveFile( aFilePath.GetFullPath() );
235 bool isMultiThreaded )
237 wxFFileInputStream stream( aFilePath );
238 wxZipInputStream
zip( stream );
240 wxLogNull no_wx_logging;
242 int entries =
zip.GetTotalEntries();
245 wxArchiveEntry* entry =
zip.GetNextEntry();
254 wxString clean_package_id = aPackageId;
255 clean_package_id.Replace(
'.',
'_' );
257 for( ; entry; entry =
zip.GetNextEntry() )
259 wxArrayString path_parts =
260 wxSplit( entry->GetName(), wxFileName::GetPathSeparator(), (wxChar) 0 );
262 if( path_parts.size() < 2
264 || path_parts[path_parts.size() - 1].IsEmpty() )
277 path_parts.Insert( clean_package_id, 1 );
278 path_parts.Insert(
m_pcm->Get3rdPartyPath(), 0 );
280 wxString fullname = wxJoin( path_parts, wxFileName::GetPathSeparator(), (wxChar) 0 );
283 wxString t_path = wxPathOnly( fullname );
285 if( !wxDirExists( t_path ) )
287 wxFileName::Mkdir( t_path, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
290 wxTempFileOutputStream out( fullname );
299 m_reporter->SetPackageProgress( extracted, entries );
301 if( !isMultiThreaded )
317 m_reporter->SetPackageProgress( entries, entries );
325 wxFFileInputStream stream( aFilePath );
329 wxLogError(
_(
"Could not open archive file." ) );
333 wxZipInputStream
zip( stream );
337 wxLogError(
_(
"Invalid archive file format." ) );
343 for( wxArchiveEntry* entry =
zip.GetNextEntry(); entry !=
nullptr; entry =
zip.GetNextEntry() )
346 if( entry->GetName() !=
"metadata.json" )
349 wxStringOutputStream strStream;
356 m_pcm->ValidateJson( metadata );
358 catch(
const std::exception& e )
367 if( metadata.empty() )
369 wxLogError(
_(
"Archive does not contain a valid metadata.json file" ) );
375 if( package.versions.size() != 1 )
377 wxLogError(
_(
"Archive metadata must have a single version defined" ) );
381 bool isUpdate =
false;
383 std::forward_list<wxRegEx> keep_on_update;
384 const std::vector<PCM_INSTALLATION_ENTRY> installed_packages =
m_pcm->GetInstalledPackages();
386 if( std::find_if( installed_packages.begin(), installed_packages.end(),
389 return entry.package.identifier == package.identifier;
391 != installed_packages.end() )
395 _(
"Package with identifier %s is already installed. "
396 "Would you like to update it to the version from selected file?" ),
397 package.identifier ),
398 _(
"Update package" ), wxICON_EXCLAMATION | wxYES_NO, aParent )
407 m_reporter = std::make_unique<DIALOG_PCM_PROGRESS>( aParent,
false );
417 package.identifier ),
423 if(
extract( aFilePath, package.identifier,
false ) )
424 m_pcm->MarkInstalled( package, package.versions[0].version,
"" );
445 explicit PATH_COLLECTOR( std::vector<wxString>& aFiles, std::vector<wxString>& aDirs ) :
446 m_files( aFiles ), m_dirs( aDirs )
450 wxDirTraverseResult
OnFile(
const wxString& aFilePath )
override
452 m_files.push_back( aFilePath );
453 return wxDIR_CONTINUE;
456 wxDirTraverseResult
OnDir(
const wxString& dirPath )
override
458 m_dirs.push_back( dirPath );
459 return wxDIR_CONTINUE;
465 const std::forward_list<wxRegEx>& aKeep )
468 wxString clean_package_id = aPackageId;
469 clean_package_id.Replace(
'.',
'_' );
471 int path_prefix_len =
m_pcm->Get3rdPartyPath().Length();
473 auto sort_func = [](
const wxString& a,
const wxString& b )
475 if( a.length() > b.length() )
477 if( a.length() < b.length() )
488 wxFileName d(
m_pcm->Get3rdPartyPath(),
"" );
490 d.AppendDir( clean_package_id );
500 if( !d.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
509 std::vector<wxString> files;
510 std::vector<wxString> dirs;
513 wxDir( d.GetFullPath() )
514 .Traverse( collector, wxEmptyString, wxDIR_DEFAULT | wxDIR_NO_FOLLOW );
517 std::sort( files.begin(), files.end(), sort_func );
518 std::sort( dirs.begin(), dirs.end(), sort_func );
521 for(
const wxString& file : files )
525 for(
const wxRegEx& re : aKeep )
527 wxString tmp = file.Mid( path_prefix_len );
528 tmp.Replace(
"\\",
"/" );
530 if( re.Matches( tmp ) )
541 wxRemoveFile( file );
545 for(
const wxString& empty_dir : dirs )
547 wxFileName dname( empty_dir,
"" );
561 m_pcm->MarkUninstalled( aPackage );
577 m_reporter = std::make_unique<DIALOG_PCM_PROGRESS>( aParent );
589 std::condition_variable condvar;
590 bool download_complete =
false;
592 std::thread download_thread(
598 m_download_queue.pop( task );
600 condvar.notify_all();
603 std::unique_lock<std::mutex> lock( mutex );
604 download_complete =
true;
605 condvar.notify_all();
608 std::thread install_thread(
611 std::unique_lock<std::mutex> lock( mutex );
644 download_thread.join();
645 install_thread.join();
int Perform()
Equivalent to curl_easy_perform.
bool SetTransferCallback(const TRANSFER_CALLBACK &aCallback, size_t aInterval)
bool SetURL(const std::string &aURL)
Set the request URL.
bool SetFollowRedirects(bool aFollow)
Enable the following of HTTP(s) and other redirects, by default curl does not follow redirects.
int GetTransferTotal(uint64_t &aDownloadedBytes) const
bool SetOutputStream(const std::ostream *aOutput)
const std::string GetErrorText(int aCode)
Fetch CURL's "friendly" error string for a given error code.
static wxString GetUserCachePath()
Gets the stock (install) 3d viewer plugins path.
PATH_COLLECTOR(std::vector< wxString > &aFiles, std::vector< wxString > &aDirs)
wxDirTraverseResult OnDir(const wxString &dirPath) override
std::vector< wxString > & m_dirs
std::vector< wxString > & m_files
wxDirTraverseResult OnFile(const wxString &aFilePath) override
void deletePackageDirectories(const wxString &aPackageId, const std::forward_list< wxRegEx > &aKeep={})
Delete all package files.
void InstallFromFile(wxWindow *aParent, const wxString &aFilePath)
Installs package from an archive file on disk.
SYNC_QUEUE< PCM_TASK > m_install_queue
std::shared_ptr< PLUGIN_CONTENT_MANAGER > m_pcm
void Uninstall(const PCM_PACKAGE &aPackage)
Enqueue package uninstallation.
SYNC_QUEUE< PCM_TASK > m_download_queue
void DownloadAndInstall(const PCM_PACKAGE &aPackage, const wxString &aVersion, const wxString &aRepositoryId, const bool isUpdate)
Enqueue package download and installation.
int downloadFile(const wxString &aFilePath, const wxString &aUrl)
Download URL to a file.
void installDownloadedPackage(const PCM_PACKAGE &aPackage, const wxString &aVersion, const wxString &aRepositoryId, const wxFileName &aFilePath, const bool isUpdate)
Installs downloaded package archive.
bool extract(const wxString &aFilePath, const wxString &aPackageId, bool isMultiThreaded)
Extract package archive.
void RunQueue(wxWindow *aParent)
Run queue of pending actions.
std::mutex m_changed_package_types_guard
std::unique_ptr< DIALOG_PCM_PROGRESS > m_reporter
std::unordered_set< PCM_PACKAGE_TYPE > m_changed_package_types
bool pop(T &aReceiver)
Pop a value if the queue into the provided variable.
bool empty() const
Return true if the queue is empty.
size_t size() const
Return the size of the queue.
void push(T const &aValue)
Push a value onto the queue.
std::function< int(size_t, size_t, size_t, size_t)> TRANSFER_CALLBACK
Wrapper interface around the curl_easy API/.
const std::unordered_set< wxString > PCM_PACKAGE_DIRECTORIES({ "plugins", "footprints", "3dmodels", "symbols", "resources", "colors", })
< Contains list of all valid directories that get extracted from a package archive
void compile_keep_on_update_regex(const PCM_PACKAGE &pkg, const PACKAGE_VERSION &ver, std::forward_list< wxRegEx > &aKeepOnUpdate)
std::function< void()> PCM_TASK
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
< Package version metadata Package metadata
std::vector< std::string > keep_on_update
Repository reference to a resource.
std::vector< PACKAGE_VERSION > versions
std::vector< std::string > keep_on_update
static bool CopyStreamData(wxInputStream &inputStream, wxOutputStream &outputStream, wxFileOffset size)