KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcm_task_manager.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2021 Andrew Lutsenko, anlutsenko at gmail dot com
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20// kicad_curl_easy.h **must be** included before any wxWidgets header to avoid conflicts
21// at least on Windows/msys2
24
25#include <paths.h>
26#include "pcm_task_manager.h"
27#include <reporter.h>
28#include <wx_filename.h>
29#include <wxstream_helper.h>
30
31#include <fstream>
32#include <thread>
33#include <unordered_set>
34#include <wx/dir.h>
35#include <wx/filename.h>
36#include <wx/msgdlg.h>
37#include <wx/sstream.h>
38#include <wx/wfstream.h>
39#include <wx/zipstrm.h>
40
41
43 std::forward_list<wxRegEx>& aKeepOnUpdate )
44{
45 auto compile_regex = [&]( const wxString& regex )
46 {
47 aKeepOnUpdate.emplace_front( regex, wxRE_DEFAULT );
48
49 if( !aKeepOnUpdate.front().IsValid() )
50 aKeepOnUpdate.pop_front();
51 };
52
53 std::for_each( pkg.keep_on_update.begin(), pkg.keep_on_update.end(), compile_regex );
54 std::for_each( ver.keep_on_update.begin(), ver.keep_on_update.end(), compile_regex );
55}
56
57
59 const wxString& aRepositoryId, const bool isUpdate )
60{
61 PCM_TASK download_task = [aPackage, aVersion, aRepositoryId, isUpdate, this]() -> PCM_TASK_MANAGER::STATUS
62 {
63 wxFileName file_path( PATHS::GetUserCachePath(), "" );
64 file_path.AppendDir( "pcm" );
65 file_path.SetFullName( wxString::Format( "%s_v%s.zip", aPackage.identifier, aVersion ) );
66
67 auto find_pkgver = std::find_if( aPackage.versions.begin(), aPackage.versions.end(),
68 [&aVersion]( const PACKAGE_VERSION& pv )
69 {
70 return pv.version == aVersion;
71 } );
72
73 if( find_pkgver == aPackage.versions.end() )
74 {
75 m_reporter->PCMReport( wxString::Format( _( "Version %s of package %s not found!" ),
76 aVersion, aPackage.identifier ),
79 }
80
81 if( !wxDirExists( file_path.GetPath() )
82 && !wxFileName::Mkdir( file_path.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
83 {
84 m_reporter->PCMReport( _( "Unable to create download directory!" ),
87 }
88
89 int code = downloadFile( file_path.GetFullPath(), *find_pkgver->download_url );
90
91 if( code != CURLE_OK )
92 {
93 // Cleanup after ourselves and exit
94 wxRemoveFile( file_path.GetFullPath() );
96 }
97
98 PCM_TASK install_task = [aPackage, aVersion, aRepositoryId, file_path, isUpdate, this]()
99 {
100 return installDownloadedPackage( aPackage, aVersion, aRepositoryId, file_path, isUpdate );
101 };
102
103 m_install_queue.push( install_task );
104
106 };
107
108 m_download_queue.push( download_task );
110}
111
112
113int PCM_TASK_MANAGER::downloadFile( const wxString& aFilePath, const wxString& url )
114{
115 TRANSFER_CALLBACK callback = [&]( size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow )
116 {
117 if( dltotal > 1024 )
118 m_reporter->SetDownloadProgress( dlnow, dltotal );
119 else
120 m_reporter->SetDownloadProgress( 0.0, 0.0 );
121
122 return m_reporter->IsCancelled();
123 };
124
125 std::ofstream out( aFilePath.ToUTF8(), std::ofstream::binary );
126
127 KICAD_CURL_EASY curl;
128 curl.SetOutputStream( &out );
129 curl.SetURL( url.ToUTF8().data() );
130 curl.SetFollowRedirects( true );
131 curl.SetTransferCallback( callback, 250000L );
132
133 m_reporter->PCMReport( wxString::Format( _( "Downloading package url: '%s'" ), url ),
135
136 int code = curl.Perform();
137
138 out.close();
139
140 uint64_t download_total;
141
142 if( CURLE_OK == curl.GetTransferTotal( download_total ) )
143 m_reporter->SetDownloadProgress( download_total, download_total );
144
145 if( code != CURLE_OK && code != CURLE_ABORTED_BY_CALLBACK )
146 {
147 m_reporter->PCMReport( wxString::Format( _( "Failed to download url %s\n%s" ), url,
148 curl.GetErrorText( code ) ),
150 }
151
152 return code;
153}
154
155
157 const wxString& aVersion,
158 const wxString& aRepositoryId,
159 const wxFileName& aFilePath, const bool isUpdate )
160{
161 auto pkgver = std::find_if( aPackage.versions.begin(), aPackage.versions.end(),
162 [&aVersion]( const PACKAGE_VERSION& pv )
163 {
164 return pv.version == aVersion;
165 } );
166
167 if( pkgver == aPackage.versions.end() )
168 {
169 m_reporter->PCMReport( wxString::Format( _( "Version %s of package %s not found!" ),
170 aVersion, aPackage.identifier ),
173 }
174
175 // wxRegEx is not CopyConstructible hence the weird choice of forward_list
176 std::forward_list<wxRegEx> keep_on_update;
177
178 if( isUpdate )
179 compile_keep_on_update_regex( aPackage, *pkgver, keep_on_update );
180
181 const std::optional<wxString>& hash = pkgver->download_sha256;
182 bool hash_match = true;
183
184 if( hash )
185 {
186 std::ifstream stream( aFilePath.GetFullPath().fn_str(), std::ios::binary );
187 hash_match = m_pcm->VerifyHash( stream, *hash );
188 }
189
190 if( !hash_match )
191 {
192 m_reporter->PCMReport( wxString::Format( _( "Downloaded archive hash for package "
193 "%s does not match repository entry. "
194 "This may indicate a problem with the "
195 "package, if the issue persists "
196 "report this to repository maintainers." ),
197 aPackage.name ),
199 wxRemoveFile( aFilePath.GetFullPath() );
201 }
202 else
203 {
204 if( isUpdate )
205 {
206 m_reporter->PCMReport(
207 wxString::Format( _( "Removing previous version of package '%s'." ),
208 aPackage.name ),
210
211 deletePackageDirectories( aPackage.identifier, keep_on_update );
212 }
213
214 m_reporter->PCMReport(
215 wxString::Format( _( "Installing package '%s'." ), aPackage.name ),
217
218 if( extract( aFilePath.GetFullPath(), aPackage.identifier, true ) )
219 {
220 m_pcm->MarkInstalled( aPackage, pkgver->version, aRepositoryId );
221 }
222 else
223 {
224 // Cleanup possibly partially extracted package
226 }
227
228 std::unique_lock lock( m_changed_package_types_guard );
229 m_changed_package_types.insert( aPackage.type );
230 }
231
232 wxRemoveFile( aFilePath.GetFullPath() );
234}
235
236
237bool PCM_TASK_MANAGER::extract( const wxString& aFilePath, const wxString& aPackageId,
238 bool isMultiThreaded )
239{
240 wxFFileInputStream stream( aFilePath );
241 wxZipInputStream zip( stream );
242
243 wxLogNull no_wx_logging;
244
245 int entries = zip.GetTotalEntries();
246 int extracted = 0;
247
248 wxArchiveEntry* entry = zip.GetNextEntry();
249
250 if( !zip.IsOk() )
251 {
252 m_reporter->PCMReport( _( "Error extracting file!" ), RPT_SEVERITY_ERROR );
253 return false;
254 }
255
256 // Namespace delimiter changed on disk to allow flat loading of Python modules
257 wxString clean_package_id = aPackageId;
258 clean_package_id.Replace( '.', '_' );
259
260 for( ; entry; entry = zip.GetNextEntry() )
261 {
262 wxArrayString path_parts;
263
264 if( !WX_FILENAME::SplitArchiveEntryName( entry->GetName(), path_parts ) )
265 {
266 m_reporter->PCMReport( wxString::Format( _( "Package archive entry '%s' would be extracted outside "
267 "of the package directory." ),
268 entry->GetName() ),
270 return false;
271 }
272
273 if( entry->IsDir() || path_parts.size() < 2
274 || PCM_PACKAGE_DIRECTORIES.find( path_parts[0] ) == PCM_PACKAGE_DIRECTORIES.end() )
275 {
276 // Ignore directory entries, files in the root of the archive and files outside of
277 // package dirs.
278 continue;
279 }
280
281 // Transform paths from
282 // <PackageRoot>/$folder/$contents
283 // To
284 // $KICAD7_3RD_PARTY/$folder/$package_id/$contents
285 path_parts.Insert( clean_package_id, 1 );
286
287 wxFileName target;
288
289 if( !WX_FILENAME::ResolveArchiveEntryPath( m_pcm->Get3rdPartyPath(), wxJoin( path_parts, '/', (wxChar) 0 ),
290 target ) )
291 {
292 m_reporter->PCMReport( wxString::Format( _( "Package archive entry '%s' would be extracted outside "
293 "of the package directory." ),
294 entry->GetName() ),
296 return false;
297 }
298
299 wxString fullname = target.GetFullPath();
300
301 // Ensure the target directory exists and create it if not.
302 wxString t_path = wxPathOnly( fullname );
303
304 if( !wxDirExists( t_path ) )
305 {
306 wxFileName::Mkdir( t_path, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
307 }
308
309 wxTempFileOutputStream out( fullname );
310
311 if( !( CopyStreamData( zip, out, entry->GetSize() ) && out.Commit() ) )
312 {
313 m_reporter->PCMReport( _( "Error extracting file!" ), RPT_SEVERITY_ERROR );
314 return false;
315 }
316
317#ifndef __WXMSW__
318 if( const wxZipEntry* zipEntry = dynamic_cast<const wxZipEntry*>( entry ) )
319 {
320 int exec = zipEntry->GetMode() & ( wxPOSIX_USER_EXECUTE | wxPOSIX_GROUP_EXECUTE | wxPOSIX_OTHERS_EXECUTE );
321
322 wxStructStat stat;
323
324 if( exec && wxStat( fullname, &stat ) == 0 )
325 wxChmod( fullname, stat.st_mode | exec );
326 }
327#endif
328
329 extracted++;
330 m_reporter->SetPackageProgress( extracted, entries );
331
332 if( !isMultiThreaded )
333 m_reporter->KeepRefreshing( false );
334
335 if( m_reporter->IsCancelled() )
336 break;
337 }
338
339 zip.CloseEntry();
340
341 if( m_reporter->IsCancelled() )
342 {
343 m_reporter->PCMReport( _( "Aborting package installation." ), RPT_SEVERITY_INFO );
344 return false;
345 }
346
347 m_reporter->SetPackageProgress( entries, entries );
348
349 return true;
350}
351
352
354 const wxString& aFilePath )
355{
356 wxFFileInputStream stream( aFilePath );
357
358 if( !stream.IsOk() )
359 {
360 wxLogError( _( "Could not open archive file." ) );
362 }
363
364 wxZipInputStream zip( stream );
365
366 if( !zip.IsOk() )
367 {
368 wxLogError( _( "Invalid archive file format." ) );
370 }
371
372 nlohmann::json metadata;
373
374 for( wxArchiveEntry* entry = zip.GetNextEntry(); entry != nullptr; entry = zip.GetNextEntry() )
375 {
376 // Find and load metadata.json
377 if( entry->GetName() != "metadata.json" )
378 continue;
379
380 wxStringOutputStream strStream;
381
382 if( CopyStreamData( zip, strStream, entry->GetSize() ) )
383 {
384 try
385 {
386 metadata = nlohmann::json::parse( strStream.GetString().ToUTF8().data() );
387 m_pcm->ValidateJson( metadata );
388 }
389 catch( const std::exception& e )
390 {
391 wxLogError( wxString::Format( _( "Unable to parse package metadata:\n\n%s" ),
392 e.what() ) );
393 break;
394 }
395 }
396 }
397
398 if( metadata.empty() )
399 {
400 wxLogError( _( "Archive does not contain a valid metadata.json file" ) );
402 }
403
404 PCM_PACKAGE package = metadata.get<PCM_PACKAGE>();
406
407 if( package.versions.size() != 1 )
408 {
409 wxLogError( _( "Archive metadata must have a single version defined" ) );
411 }
412
413 if( !package.versions[0].compatible
414 && wxMessageBox( _( "This package version is incompatible with your KiCad version or "
415 "platform. Are you sure you want to install it anyway?" ),
416 _( "Install package" ), wxICON_EXCLAMATION | wxYES_NO, aParent )
417 == wxNO )
418 {
420 }
421
422 bool isUpdate = false;
423 // wxRegEx is not CopyConstructible hence the weird choice of forward_list
424 std::forward_list<wxRegEx> keep_on_update;
425 const std::vector<PCM_INSTALLATION_ENTRY> installed_packages = m_pcm->GetInstalledPackages();
426
427 if( std::find_if( installed_packages.begin(), installed_packages.end(),
428 [&]( const PCM_INSTALLATION_ENTRY& entry )
429 {
430 return entry.package.identifier == package.identifier;
431 } )
432 != installed_packages.end() )
433 {
434 if( wxMessageBox(
435 wxString::Format(
436 _( "Package with identifier %s is already installed. "
437 "Would you like to update it to the version from selected file?" ),
438 package.identifier ),
439 _( "Update package" ), wxICON_EXCLAMATION | wxYES_NO, aParent )
440 == wxNO )
442
443 isUpdate = true;
444
445 compile_keep_on_update_regex( package, package.versions[0], keep_on_update );
446 }
447
448 m_reporter = std::make_unique<DIALOG_PCM_PROGRESS>( aParent, false );
449#ifdef __WXMAC__
450 m_reporter->ShowWindowModal();
451#else
452 m_reporter->Show();
453#endif
454
455 if( isUpdate )
456 {
457 m_reporter->PCMReport( wxString::Format( _( "Removing previous version of package '%s'." ),
458 package.name ),
460
461 deletePackageDirectories( package.identifier, keep_on_update );
462 }
463
464 const bool extracted = extract( aFilePath, package.identifier, false );
465
466 if( extracted )
467 m_pcm->MarkInstalled( package, package.versions[0].version, "" );
468 else
469 deletePackageDirectories( package.identifier ); // Cleanup partial extraction
470
471 m_reporter->SetFinished();
472 m_pcm->ShowApiEnablePromptIfNeeded();
473
474 // Keep the reporting dialog open if we failed extraction
475 m_reporter->KeepRefreshing( !extracted && !m_reporter->IsCancelled() );
476
477 m_reporter->Destroy();
478 m_reporter.reset();
479
480 aParent->Raise();
481
482 std::unique_lock lock( m_changed_package_types_guard );
483 m_changed_package_types.insert( package.type );
485}
486
487
488class PATH_COLLECTOR : public wxDirTraverser
489{
490private:
491 std::vector<wxString>& m_files;
492 std::vector<wxString>& m_dirs;
493
494public:
495 explicit PATH_COLLECTOR( std::vector<wxString>& aFiles, std::vector<wxString>& aDirs ) :
496 m_files( aFiles ), m_dirs( aDirs )
497 {
498 }
499
500 wxDirTraverseResult OnFile( const wxString& aFilePath ) override
501 {
502 m_files.push_back( aFilePath );
503 return wxDIR_CONTINUE;
504 }
505
506 wxDirTraverseResult OnDir( const wxString& dirPath ) override
507 {
508 m_dirs.push_back( dirPath );
509 return wxDIR_CONTINUE;
510 }
511};
512
513
514void PCM_TASK_MANAGER::deletePackageDirectories( const wxString& aPackageId,
515 const std::forward_list<wxRegEx>& aKeep )
516{
517 // Namespace delimiter changed on disk to allow flat loading of Python modules
518 wxString clean_package_id = aPackageId;
519 clean_package_id.Replace( '.', '_' );
520
521 int path_prefix_len = m_pcm->Get3rdPartyPath().Length();
522
523 auto sort_func = []( const wxString& a, const wxString& b )
524 {
525 if( a.length() > b.length() )
526 return true;
527 if( a.length() < b.length() )
528 return false;
529
530 if( a != b )
531 return a < b;
532
533 return false;
534 };
535
536 for( const wxString& dir : PCM_PACKAGE_DIRECTORIES )
537 {
538 wxFileName d( m_pcm->Get3rdPartyPath(), "" );
539 d.AppendDir( dir );
540 d.AppendDir( clean_package_id );
541
542 if( !d.DirExists() )
543 continue;
544
545 m_reporter->PCMReport( wxString::Format( _( "Removing directory %s" ), d.GetPath() ),
547
548 if( aKeep.empty() )
549 {
550 if( !d.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
551 {
552 m_reporter->PCMReport(
553 wxString::Format( _( "Failed to remove directory %s" ), d.GetPath() ),
555 }
556 }
557 else
558 {
559 std::vector<wxString> files;
560 std::vector<wxString> dirs;
561 PATH_COLLECTOR collector( files, dirs );
562
563 wxDir( d.GetFullPath() )
564 .Traverse( collector, wxEmptyString, wxDIR_DEFAULT | wxDIR_NO_FOLLOW );
565
566 // Do a poor mans post order traversal by sorting paths in reverse length order
567 std::sort( files.begin(), files.end(), sort_func );
568 std::sort( dirs.begin(), dirs.end(), sort_func );
569
570 // Delete files that don't match any of the aKeep regexs
571 for( const wxString& file : files )
572 {
573 bool del = true;
574
575 for( const wxRegEx& re : aKeep )
576 {
577 wxString tmp = file.Mid( path_prefix_len );
578 tmp.Replace( "\\", "/" );
579
580 if( re.Matches( tmp ) )
581 {
582 del = false;
583 break;
584 }
585 }
586
587 if( del )
588 wxRemoveFile( file );
589 }
590
591 // Delete any empty dirs
592 for( const wxString& empty_dir : dirs )
593 {
594 wxFileName dname( empty_dir, "" );
595 dname.Rmdir(); // not passing any flags here will only remove empty directories
596 }
597 }
598 }
599}
600
601
603{
604 PCM_TASK task = [aPackage, this]
605 {
607
608 m_pcm->MarkUninstalled( aPackage );
609
610 std::unique_lock lock( m_changed_package_types_guard );
611 m_changed_package_types.insert( aPackage.type );
612
613 m_reporter->PCMReport(
614 wxString::Format( _( "Package %s uninstalled" ), aPackage.name ),
617 };
618
619 m_install_queue.push( task );
621}
622
623
624void PCM_TASK_MANAGER::RunQueue( wxWindow* aParent )
625{
626 m_reporter = std::make_unique<DIALOG_PCM_PROGRESS>( aParent );
627
628 m_reporter->SetNumPhases( m_download_queue.size() + m_install_queue.size() );
629#ifdef __WXMAC__
630 m_reporter->ShowWindowModal();
631#else
632 m_reporter->Show();
633#endif
634
635 wxSafeYield();
636
637 std::mutex mutex;
638 std::condition_variable condvar;
639 bool download_complete = false;
640 int count_tasks = 0;
641 int count_failed_tasks = 0;
642 int count_success_tasks = 0;
643
644 std::thread download_thread(
645 [&]()
646 {
647 while( !m_download_queue.empty() && !m_reporter->IsCancelled() )
648 {
649 PCM_TASK task;
650 m_download_queue.pop( task );
651 PCM_TASK_MANAGER::STATUS task_status = task();
652
653 count_tasks++;
654
655 if( task_status == PCM_TASK_MANAGER::STATUS::SUCCESS )
656 count_success_tasks++;
657 else if( task_status != PCM_TASK_MANAGER::STATUS::INITIALIZED )
658 count_failed_tasks++;
659
660 m_reporter->AdvancePhase();
661
662 condvar.notify_all();
663 }
664
665 std::unique_lock<std::mutex> lock( mutex );
666 download_complete = true;
667 condvar.notify_all();
668 } );
669
670 std::thread install_thread(
671 [&]()
672 {
673 std::unique_lock<std::mutex> lock( mutex );
674
675 do
676 {
677 condvar.wait( lock,
678 [&]()
679 {
680 return download_complete || !m_install_queue.empty()
681 || m_reporter->IsCancelled();
682 } );
683
684 lock.unlock();
685
686 while( !m_install_queue.empty() && !m_reporter->IsCancelled() )
687 {
688 PCM_TASK task;
689 m_install_queue.pop( task );
690 PCM_TASK_MANAGER::STATUS task_status = task();
691
692 count_tasks++;
693
694 if( task_status == PCM_TASK_MANAGER::STATUS::SUCCESS )
695 count_success_tasks++;
696 else if( task_status != PCM_TASK_MANAGER::STATUS::INITIALIZED )
697 count_failed_tasks++;
698
699 m_reporter->AdvancePhase();
700 }
701
702 lock.lock();
703
704 } while( ( !m_install_queue.empty() || !download_complete )
705 && !m_reporter->IsCancelled() );
706
707 if( count_failed_tasks != 0 )
708 {
709 m_reporter->PCMReport(
710 wxString::Format( _( "%d out of %d operations failed." ), count_failed_tasks, count_tasks ),
712 }
713 else
714 {
715 if( count_success_tasks == count_tasks )
716 {
717 m_reporter->PCMReport( _( "All operations completed successfully." ), RPT_SEVERITY_INFO );
718 }
719 else
720 {
721 m_reporter->PCMReport(
722 wxString::Format( _( "%d out of %d operations were initialized but not successful." ),
723 count_tasks - count_success_tasks, count_tasks ),
725 }
726 }
727
728 m_reporter->SetFinished();
729 } );
730
731 m_reporter->KeepRefreshing( true );
732
733 download_thread.join();
734 install_thread.join();
735
736 // Show deferred API enable prompt when threads are done
737 m_pcm->ShowApiEnablePromptIfNeeded();
738
739 // Destroy the reporter only after the threads joined
740 // Incase the reporter terminated due to cancellation
741 m_reporter->Destroy();
742 m_reporter.reset();
743
744 aParent->Raise();
745}
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.
Definition paths.cpp:460
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.
SYNC_QUEUE< PCM_TASK > m_install_queue
std::shared_ptr< PLUGIN_CONTENT_MANAGER > m_pcm
SYNC_QUEUE< PCM_TASK > m_download_queue
std::function< STATUS()> PCM_TASK
int downloadFile(const wxString &aFilePath, const wxString &aUrl)
Download URL to a file.
PCM_TASK_MANAGER::STATUS DownloadAndInstall(const PCM_PACKAGE &aPackage, const wxString &aVersion, const wxString &aRepositoryId, const bool isUpdate)
Enqueue package download and installation.
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
PCM_TASK_MANAGER::STATUS installDownloadedPackage(const PCM_PACKAGE &aPackage, const wxString &aVersion, const wxString &aRepositoryId, const wxFileName &aFilePath, const bool isUpdate)
Installs downloaded package archive.
std::unique_ptr< DIALOG_PCM_PROGRESS > m_reporter
PCM_TASK_MANAGER::STATUS Uninstall(const PCM_PACKAGE &aPackage)
Enqueue package uninstallation.
PCM_TASK_MANAGER::STATUS InstallFromFile(wxWindow *aParent, const wxString &aFilePath)
Installs package from an archive file on disk.
std::unordered_set< PCM_PACKAGE_TYPE > m_changed_package_types
static void PreparePackage(PCM_PACKAGE &aPackage)
Parses version strings and calculates compatibility.
Definition pcm.cpp:663
static bool ResolveArchiveEntryPath(const wxString &aDestDir, const wxString &aEntryName, wxFileName &aResult)
Resolve an untrusted archive entry name against the directory it is extracted into.
static bool SplitArchiveEntryName(const wxString &aEntryName, wxArrayString &aParts)
Split an untrusted archive entry name into its path components.
#define _(s)
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", "templates", "scripts" })
< 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)
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
< Package version metadataPackage metadata
Definition pcm_data.h:92
std::vector< std::string > keep_on_update
Definition pcm_data.h:103
Definition pcm_data.h:159
Repository reference to a resource.
Definition pcm_data.h:114
wxString identifier
Definition pcm_data.h:118
wxString name
Definition pcm_data.h:115
std::vector< PACKAGE_VERSION > versions
Definition pcm_data.h:127
PCM_PACKAGE_TYPE type
Definition pcm_data.h:119
std::vector< std::string > keep_on_update
Definition pcm_data.h:126
static bool CopyStreamData(wxInputStream &inputStream, wxOutputStream &outputStream, wxFileOffset size)