2018-03-28 11:36:36 +02:00
#include "PresetUpdater.hpp"
2018-04-12 20:04:48 +02:00
#include <algorithm>
2018-03-28 11:36:36 +02:00
#include <thread>
2018-04-24 18:06:42 +02:00
#include <unordered_map>
2018-04-23 11:16:47 +02:00
#include <ostream>
2019-06-03 11:31:32 +02:00
#include <utility>
2018-04-13 15:08:58 +02:00
#include <stdexcept>
#include <boost/format.hpp>
2018-03-28 11:36:36 +02:00
#include <boost/algorithm/string.hpp>
2018-04-11 13:12:08 +02:00
#include <boost/filesystem.hpp>
2018-03-28 11:36:36 +02:00
#include <boost/filesystem/fstream.hpp>
2018-12-20 17:44:24 +01:00
#include <boost/lexical_cast.hpp>
2018-04-17 11:54:59 +02:00
#include <boost/log/trivial.hpp>
2018-03-28 11:36:36 +02:00
#include <wx/app.h>
2018-04-12 20:04:48 +02:00
#include <wx/msgdlg.h>
2018-03-28 11:36:36 +02:00
2018-04-17 16:59:53 +02:00
#include "libslic3r/libslic3r.h"
2018-03-28 11:36:36 +02:00
#include "libslic3r/Utils.hpp"
#include "slic3r/GUI/GUI.hpp"
2018-11-26 14:41:58 +01:00
#include "slic3r/GUI/I18N.hpp"
2018-03-28 11:36:36 +02:00
#include "slic3r/GUI/PresetBundle.hpp"
2018-04-24 18:06:42 +02:00
#include "slic3r/GUI/UpdateDialogs.hpp"
#include "slic3r/GUI/ConfigWizard.hpp"
2018-11-26 14:41:58 +01:00
#include "slic3r/GUI/GUI_App.hpp"
2018-03-28 11:36:36 +02:00
#include "slic3r/Utils/Http.hpp"
2018-04-12 20:04:48 +02:00
#include "slic3r/Config/Version.hpp"
#include "slic3r/Config/Snapshot.hpp"
2018-03-28 11:36:36 +02:00
namespace fs = boost :: filesystem ;
2018-04-12 20:04:48 +02:00
using Slic3r :: GUI :: Config :: Index ;
using Slic3r :: GUI :: Config :: Version ;
using Slic3r :: GUI :: Config :: Snapshot ;
using Slic3r :: GUI :: Config :: SnapshotDB ;
2018-03-28 11:36:36 +02:00
2019-08-11 18:57:33 +02:00
// FIXME: Incompat bundle resolution doesn't deal with inherited user presets
2018-03-28 11:36:36 +02:00
namespace Slic3r {
enum {
SLIC3R_VERSION_BODY_MAX = 256 ,
};
2018-04-13 15:08:58 +02:00
static const char * INDEX_FILENAME = "index.idx" ;
static const char * TMP_EXTENSION = ".download" ;
2018-04-12 20:04:48 +02:00
2019-06-03 11:31:32 +02:00
void copy_file_fix ( const fs :: path & source , const fs :: path & target )
{
static const auto perms = fs :: owner_read | fs :: owner_write | fs :: group_read | fs :: others_read ; // aka 644
BOOST_LOG_TRIVIAL ( debug ) << boost :: format ( "PresetUpdater: Copying %1% -> %2%" ) % source % target ;
// Make sure the file has correct permission both before and after we copy over it
if ( fs :: exists ( target )) {
fs :: permissions ( target , perms );
}
fs :: copy_file ( source , target , fs :: copy_option :: overwrite_if_exists );
fs :: permissions ( target , perms );
}
2018-04-12 20:04:48 +02:00
struct Update
{
fs :: path source ;
fs :: path target ;
2019-06-03 11:31:32 +02:00
2018-04-12 20:04:48 +02:00
Version version ;
2019-05-13 18:38:48 +02:00
std :: string vendor ;
std :: string changelog_url ;
2018-04-12 20:04:48 +02:00
2019-05-13 18:38:48 +02:00
Update ( fs :: path && source , fs :: path && target , const Version & version , std :: string vendor , std :: string changelog_url )
: source ( std :: move ( source ))
, target ( std :: move ( target ))
, version ( version )
, vendor ( std :: move ( vendor ))
, changelog_url ( std :: move ( changelog_url ))
2018-04-12 20:04:48 +02:00
{}
2019-06-03 11:31:32 +02:00
void install () const
{
copy_file_fix ( source , target );
}
friend std :: ostream & operator << ( std :: ostream & os , const Update & self )
{
2018-04-24 18:06:42 +02:00
os << "Update(" << self . source . string () << " -> " << self . target . string () << ')' ;
2018-04-23 11:16:47 +02:00
return os ;
}
2018-04-12 20:04:48 +02:00
};
2018-04-24 18:06:42 +02:00
struct Incompat
{
fs :: path bundle ;
Version version ;
2019-05-13 18:38:48 +02:00
std :: string vendor ;
2018-04-24 18:06:42 +02:00
2019-05-13 18:38:48 +02:00
Incompat ( fs :: path && bundle , const Version & version , std :: string vendor )
: bundle ( std :: move ( bundle ))
, version ( version )
, vendor ( std :: move ( vendor ))
2018-04-24 18:06:42 +02:00
{}
2019-08-05 17:28:02 +02:00
void remove () {
// Remove the bundle file
fs :: remove ( bundle );
// Look for an installed index and remove it too if any
const fs :: path installed_idx = bundle . replace_extension ( "idx" );
if ( fs :: exists ( installed_idx )) {
fs :: remove ( installed_idx );
}
}
2018-04-24 18:06:42 +02:00
friend std :: ostream & operator << ( std :: ostream & os , const Incompat & self ) {
os << "Incompat(" << self . bundle . string () << ')' ;
return os ;
}
};
struct Updates
{
std :: vector < Incompat > incompats ;
std :: vector < Update > updates ;
};
2018-04-12 20:04:48 +02:00
2019-02-14 15:28:18 +01:00
2019-02-14 16:51:41 +01:00
wxDEFINE_EVENT ( EVT_SLIC3R_VERSION_ONLINE , wxCommandEvent );
2018-04-12 20:04:48 +02:00
2018-03-28 11:36:36 +02:00
struct PresetUpdater :: priv
{
2018-04-13 15:08:58 +02:00
std :: vector < Index > index_db ;
bool enabled_version_check ;
bool enabled_config_update ;
std :: string version_check_url ;
2018-03-28 11:36:36 +02:00
fs :: path cache_path ;
2018-04-12 20:04:48 +02:00
fs :: path rsrc_path ;
fs :: path vendor_path ;
2018-03-28 11:36:36 +02:00
bool cancel ;
std :: thread thread ;
2018-09-21 01:33:41 +02:00
priv ();
2018-03-28 11:36:36 +02:00
2018-04-13 15:08:58 +02:00
void set_download_prefs ( AppConfig * app_config );
bool get_file ( const std :: string & url , const fs :: path & target_path ) const ;
void prune_tmps () const ;
void sync_version () const ;
2018-12-20 17:44:24 +01:00
void sync_config ( const std :: set < VendorProfile > vendors );
2018-04-12 20:04:48 +02:00
void check_install_indices () const ;
2018-04-23 13:58:03 +02:00
Updates get_config_updates () const ;
2018-04-18 11:40:43 +02:00
void perform_updates ( Updates && updates , bool snapshot = true ) const ;
2018-03-28 11:36:36 +02:00
};
2019-02-14 15:28:18 +01:00
PresetUpdater :: priv :: priv ()
2019-08-09 17:01:37 +02:00
: cache_path ( fs :: path ( Slic3r :: data_dir ()) / "cache" )
2019-02-14 15:28:18 +01:00
, rsrc_path ( fs :: path ( resources_dir ()) / "profiles" )
, vendor_path ( fs :: path ( Slic3r :: data_dir ()) / "vendor" )
, cancel ( false )
2018-03-28 11:36:36 +02:00
{
2018-09-21 01:33:41 +02:00
set_download_prefs ( GUI :: wxGetApp (). app_config );
2018-04-13 15:08:58 +02:00
check_install_indices ();
2018-12-20 17:44:24 +01:00
index_db = Index :: load_db ();
2018-04-13 15:08:58 +02:00
}
2018-03-28 11:36:36 +02:00
2018-04-23 11:16:47 +02:00
// Pull relevant preferences from AppConfig
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: set_download_prefs ( AppConfig * app_config )
{
enabled_version_check = app_config -> get ( "version_check" ) == "1" ;
2018-04-25 17:43:01 +02:00
version_check_url = app_config -> version_check_url ();
2018-05-15 12:14:26 +02:00
enabled_config_update = app_config -> get ( "preset_update" ) == "1" && ! app_config -> legacy_datadir ();
2018-04-13 15:08:58 +02:00
}
2018-04-23 11:16:47 +02:00
// Downloads a file (http get operation). Cancels if the Updater is being destroyed.
2018-04-13 15:08:58 +02:00
bool PresetUpdater :: priv :: get_file ( const std :: string & url , const fs :: path & target_path ) const
{
bool res = false ;
fs :: path tmp_path = target_path ;
2018-04-20 11:05:00 +02:00
tmp_path += ( boost :: format ( ".%1%%2%" ) % get_current_pid () % TMP_EXTENSION ). str ();
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Get: `%1%` \n\t -> `%2%` \n\t via tmp path `%3%`" )
% url
% target_path . string ()
% tmp_path . string ();
2018-04-13 15:08:58 +02:00
Http :: get ( url )
. on_progress ([ this ]( Http :: Progress , bool & cancel ) {
if ( cancel ) { cancel = true ; }
})
2018-04-23 11:16:47 +02:00
. on_error ([ & ]( std :: string body , std :: string error , unsigned http_status ) {
( void ) body ;
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Error getting: `%1%`: HTTP %2%, %3%" )
% url
% http_status
2018-04-24 18:06:42 +02:00
% error ;
2018-04-23 11:16:47 +02:00
})
2018-06-06 10:52:19 +02:00
. on_complete ([ & ]( std :: string body , unsigned /* http_status */ ) {
2018-04-13 15:08:58 +02:00
fs :: fstream file ( tmp_path , std :: ios :: out | std :: ios :: binary | std :: ios :: trunc );
file . write ( body . c_str (), body . size ());
2018-04-16 16:52:11 +02:00
file . close ();
2018-04-13 15:08:58 +02:00
fs :: rename ( tmp_path , target_path );
res = true ;
})
. perform_sync ();
return res ;
}
2018-04-23 11:16:47 +02:00
// Remove leftover paritally downloaded files, if any.
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: prune_tmps () const
{
2019-02-03 15:30:37 +01:00
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( cache_path ))
if ( is_plain_file ( dir_entry ) && dir_entry . path (). extension () == TMP_EXTENSION ) {
BOOST_LOG_TRIVIAL ( debug ) << "Cache prune: " << dir_entry . path (). string ();
fs :: remove ( dir_entry . path ());
2018-04-13 15:08:58 +02:00
}
}
2018-04-23 11:16:47 +02:00
// Get Slic3rPE version available online, save in AppConfig.
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: sync_version () const
{
if ( ! enabled_version_check ) { return ; }
2019-04-15 16:14:19 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Downloading %1% online version from: `%2%`" ) % SLIC3R_APP_NAME % version_check_url ;
2018-04-23 11:16:47 +02:00
2018-04-13 15:08:58 +02:00
Http :: get ( version_check_url )
2018-03-28 11:36:36 +02:00
. size_limit ( SLIC3R_VERSION_BODY_MAX )
. on_progress ([ this ]( Http :: Progress , bool & cancel ) {
cancel = this -> cancel ;
})
2018-04-23 11:16:47 +02:00
. on_error ([ & ]( std :: string body , std :: string error , unsigned http_status ) {
( void ) body ;
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Error getting: `%1%`: HTTP %2%, %3%" )
% version_check_url
% http_status
2018-04-24 18:06:42 +02:00
% error ;
2018-04-23 11:16:47 +02:00
})
2018-06-06 10:52:19 +02:00
. on_complete ([ & ]( std :: string body , unsigned /* http_status */ ) {
2018-03-28 11:36:36 +02:00
boost :: trim ( body );
2019-04-15 16:14:19 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Got %1% online version: `%2%`. Sending to GUI thread..." ) % SLIC3R_APP_NAME % body ;
2019-02-14 15:28:18 +01:00
2019-02-14 16:51:41 +01:00
wxCommandEvent * evt = new wxCommandEvent ( EVT_SLIC3R_VERSION_ONLINE );
evt -> SetString ( GUI :: from_u8 ( body ));
GUI :: wxGetApp (). QueueEvent ( evt );
2018-03-28 11:36:36 +02:00
})
. perform_sync ();
2018-04-13 15:08:58 +02:00
}
2018-03-28 11:36:36 +02:00
2018-04-23 11:16:47 +02:00
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
// Both are saved in cache.
2018-12-20 17:44:24 +01:00
void PresetUpdater :: priv :: sync_config ( const std :: set < VendorProfile > vendors )
2018-04-13 15:08:58 +02:00
{
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Syncing configuration cache" ;
2018-04-13 15:08:58 +02:00
if ( ! enabled_config_update ) { return ; }
2018-03-28 11:36:36 +02:00
// Donwload vendor preset bundles
2018-12-20 17:44:24 +01:00
for ( auto & index : index_db ) {
2018-03-28 11:36:36 +02:00
if ( cancel ) { return ; }
2018-04-13 15:08:58 +02:00
const auto vendor_it = vendors . find ( VendorProfile ( index . vendor ()));
2018-04-23 11:16:47 +02:00
if ( vendor_it == vendors . end ()) {
BOOST_LOG_TRIVIAL ( warning ) << "No such vendor: " << index . vendor ();
continue ;
}
2018-03-28 11:36:36 +02:00
2018-04-13 15:08:58 +02:00
const VendorProfile & vendor = * vendor_it ;
2018-04-23 11:16:47 +02:00
if ( vendor . config_update_url . empty ()) {
BOOST_LOG_TRIVIAL ( info ) << "Vendor has no config_update_url: " << vendor . name ;
continue ;
}
2018-04-13 15:08:58 +02:00
// Download a fresh index
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Downloading index for vendor: " << vendor . name ;
2018-04-13 15:08:58 +02:00
const auto idx_url = vendor . config_update_url + "/" + INDEX_FILENAME ;
2018-12-20 17:44:24 +01:00
const std :: string idx_path = ( cache_path / ( vendor . id + ".idx" )). string ();
const std :: string idx_path_temp = idx_path + "-update" ;
if ( ! get_file ( idx_url , idx_path_temp )) { continue ; }
2018-04-13 15:08:58 +02:00
if ( cancel ) { return ; }
// Load the fresh index up
2018-12-20 17:44:24 +01:00
{
Index new_index ;
try {
new_index . load ( idx_path_temp );
2018-12-22 10:02:42 +01:00
} catch ( const std :: exception & /* err */ ) {
2019-06-03 11:31:32 +02:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Could not load downloaded index %1% for vendor %2%: invalid index?" ) % idx_path_temp % vendor . name ;
2018-12-20 17:44:24 +01:00
continue ;
}
if ( new_index . version () < index . version ()) {
2019-02-14 15:28:18 +01:00
BOOST_LOG_TRIVIAL ( warning ) << boost :: format ( "The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index." ) % idx_path_temp % vendor . name ;
2018-12-20 17:44:24 +01:00
continue ;
}
Slic3r :: rename_file ( idx_path_temp , idx_path );
index = std :: move ( new_index );
if ( cancel )
return ;
}
2018-04-13 15:08:58 +02:00
// See if a there's a new version to download
2018-12-20 17:44:24 +01:00
const auto recommended_it = index . recommended ();
if ( recommended_it == index . end ()) {
2018-05-01 10:35:07 +02:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "No recommended version for vendor: %1%, invalid index?" ) % vendor . name ;
2018-04-23 11:16:47 +02:00
continue ;
}
2019-02-14 15:28:18 +01:00
2018-04-13 15:08:58 +02:00
const auto recommended = recommended_it -> config_version ;
2018-06-19 18:26:38 +02:00
BOOST_LOG_TRIVIAL ( debug ) << boost :: format ( "Got index for vendor: %1%: current version: %2%, recommended version: %3%" )
2018-04-23 11:16:47 +02:00
% vendor . name
% vendor . config_version . to_string ()
% recommended . to_string ();
2018-04-13 15:08:58 +02:00
if ( vendor . config_version >= recommended ) { continue ; }
// Download a fresh bundle
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Downloading new bundle for vendor: " << vendor . name ;
2018-04-13 15:08:58 +02:00
const auto bundle_url = ( boost :: format ( "%1%/%2%.ini" ) % vendor . config_update_url % recommended . to_string ()). str ();
const auto bundle_path = cache_path / ( vendor . id + ".ini" );
if ( ! get_file ( bundle_url , bundle_path )) { continue ; }
if ( cancel ) { return ; }
2018-03-28 11:36:36 +02:00
}
}
2018-04-23 11:16:47 +02:00
// Install indicies from resources. Only installs those that are either missing or older than in resources.
2018-04-12 20:04:48 +02:00
void PresetUpdater :: priv :: check_install_indices () const
{
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Checking if indices need to be installed from resources..." ;
2019-02-03 15:30:37 +01:00
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( rsrc_path ))
if ( is_idx_file ( dir_entry )) {
const auto & path = dir_entry . path ();
2018-04-12 20:04:48 +02:00
const auto path_in_cache = cache_path / path . filename ();
if ( ! fs :: exists ( path_in_cache )) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Install index from resources: " << path . filename ();
2019-06-03 11:31:32 +02:00
copy_file_fix ( path , path_in_cache );
2018-04-17 10:28:32 +02:00
} else {
Index idx_rsrc , idx_cache ;
idx_rsrc . load ( path );
idx_cache . load ( path_in_cache );
if ( idx_cache . version () < idx_rsrc . version ()) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Update index from resources: " << path . filename ();
2019-06-03 11:31:32 +02:00
copy_file_fix ( path , path_in_cache );
2018-04-17 10:28:32 +02:00
}
2018-04-12 20:04:48 +02:00
}
}
}
2018-04-23 11:16:47 +02:00
// Generates a list of bundle updates that are to be performed
2018-04-23 13:58:03 +02:00
Updates PresetUpdater :: priv :: get_config_updates () const
2018-04-12 20:04:48 +02:00
{
Updates updates ;
2018-04-23 13:58:03 +02:00
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Checking for cached configuration updates..." ;
2018-04-12 20:04:48 +02:00
for ( const auto idx : index_db ) {
2018-04-24 18:06:42 +02:00
auto bundle_path = vendor_path / ( idx . vendor () + ".ini" );
2019-06-03 11:31:32 +02:00
auto bundle_path_idx = vendor_path / idx . path (). filename ();
2018-04-12 20:04:48 +02:00
if ( ! fs :: exists ( bundle_path )) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Bundle not present for index, skipping: " << idx . vendor ();
2018-04-12 20:04:48 +02:00
continue ;
}
// Perform a basic load and check the version
2019-05-13 18:38:48 +02:00
auto vp = VendorProfile :: from_ini ( bundle_path , false );
2018-04-12 20:04:48 +02:00
2018-07-21 09:32:45 +02:00
// Getting a recommended version from the latest index, wich may have been downloaded
// from the internet, or installed / updated from the installation resources.
2018-04-12 20:04:48 +02:00
const auto recommended = idx . recommended ();
if ( recommended == idx . end ()) {
2018-05-01 10:35:07 +02:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "No recommended version for vendor: %1%, invalid index?" ) % idx . vendor ();
2019-06-03 11:31:32 +02:00
// XXX: what should be done here?
continue ;
2018-04-12 20:04:48 +02:00
}
2019-02-22 11:36:58 +01:00
const auto ver_current = idx . find ( vp . config_version );
const bool ver_current_found = ver_current != idx . end ();
BOOST_LOG_TRIVIAL ( debug ) << boost :: format ( "Vendor: %1%, version installed: %2%%3%, version cached: %4%" )
2018-04-23 11:16:47 +02:00
% vp . name
2019-02-22 11:36:58 +01:00
% vp . config_version . to_string ()
% ( ver_current_found ? "" : " (not found in index!)" )
2018-04-24 18:06:42 +02:00
% recommended -> config_version . to_string ();
2018-04-23 11:16:47 +02:00
2019-06-28 11:35:54 +02:00
if ( ! ver_current_found ) {
auto message = ( boost :: format ( "Preset bundle `%1%` version not found in index: %2%" ) % idx . vendor () % vp . config_version . to_string ()). str ();
BOOST_LOG_TRIVIAL ( error ) << message ;
GUI :: show_error ( nullptr , GUI :: from_u8 ( message ));
continue ;
}
2019-02-22 11:36:58 +01:00
if ( ver_current_found && ! ver_current -> is_current_slic3r_supported ()) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( warning ) << "Current Slic3r incompatible with installed bundle: " << bundle_path . string ();
2019-05-13 18:38:48 +02:00
updates . incompats . emplace_back ( std :: move ( bundle_path ), * ver_current , vp . name );
2019-02-22 11:36:58 +01:00
} else if ( recommended -> config_version > vp . config_version ) {
2018-04-12 20:04:48 +02:00
// Config bundle update situation
2019-08-05 17:28:02 +02:00
// Load 'installed' idx, if any.
// 'Installed' indices are kept alongside the bundle in the `vendor` subdir
// for bookkeeping to remember a cancelled update and not offer it again.
if ( fs :: exists ( bundle_path_idx )) {
Index existing_idx ;
try {
existing_idx . load ( bundle_path_idx );
const auto existing_recommended = existing_idx . recommended ();
if ( existing_recommended != existing_idx . end () && recommended -> config_version == existing_recommended -> config_version ) {
// The user has already seen (and presumably rejected) this update
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Downloaded index for `%1%` is the same as installed one, not offering an update." ) % idx . vendor ();
continue ;
}
} catch ( const std :: exception & err ) {
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Could not load installed index at `%1%`: %2%" ) % bundle_path_idx % err . what ();
}
}
2018-04-23 13:58:03 +02:00
// Check if the update is already present in a snapshot
const auto recommended_snap = SnapshotDB :: singleton (). snapshot_with_vendor_preset ( vp . name , recommended -> config_version );
if ( recommended_snap != SnapshotDB :: singleton (). end ()) {
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Bundle update %1% %2% already found in snapshot %3%, skipping..." )
% vp . name
% recommended -> config_version . to_string ()
% recommended_snap -> id ;
continue ;
}
2018-06-19 18:26:38 +02:00
auto path_src = cache_path / ( idx . vendor () + ".ini" );
2018-07-21 09:32:45 +02:00
auto path_in_rsrc = rsrc_path / ( idx . vendor () + ".ini" );
2018-06-19 18:26:38 +02:00
if ( ! fs :: exists ( path_src )) {
if ( ! fs :: exists ( path_in_rsrc )) {
BOOST_LOG_TRIVIAL ( warning ) << boost :: format ( "Index for vendor %1% indicates update, but bundle found in neither cache nor resources" )
2018-07-21 09:32:45 +02:00
% idx . vendor ();
2018-06-19 18:26:38 +02:00
continue ;
} else {
path_src = std :: move ( path_in_rsrc );
2018-07-21 09:32:45 +02:00
path_in_rsrc . clear ();
2018-06-19 18:26:38 +02:00
}
2018-04-13 15:08:58 +02:00
}
2018-07-21 09:32:45 +02:00
auto new_vp = VendorProfile :: from_ini ( path_src , false );
bool found = false ;
2018-06-19 18:26:38 +02:00
if ( new_vp . config_version == recommended -> config_version ) {
2019-05-13 18:38:48 +02:00
updates . updates . emplace_back ( std :: move ( path_src ), std :: move ( bundle_path ), * recommended , vp . name , vp . changelog_url );
2018-07-21 09:32:45 +02:00
found = true ;
} else if ( ! path_in_rsrc . empty () && fs :: exists ( path_in_rsrc )) {
new_vp = VendorProfile :: from_ini ( path_in_rsrc , false );
if ( new_vp . config_version == recommended -> config_version ) {
2019-05-13 18:38:48 +02:00
updates . updates . emplace_back ( std :: move ( path_in_rsrc ), std :: move ( bundle_path ), * recommended , vp . name , vp . changelog_url );
2018-07-21 09:32:45 +02:00
found = true ;
}
}
2019-06-28 11:35:54 +02:00
if ( found ) {
// 'Install' the index in the vendor directory. This is used to memoize
// offered updates and to not offer the same update again if it was cancelled by the user.
copy_file_fix ( idx . path (), bundle_path_idx );
} else {
2018-06-19 18:26:38 +02:00
BOOST_LOG_TRIVIAL ( warning ) << boost :: format ( "Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources" )
2018-05-02 17:13:39 +02:00
% idx . vendor ()
2018-06-19 18:26:38 +02:00
% recommended -> config_version . to_string ();
2019-06-28 11:35:54 +02:00
}
2018-04-12 20:04:48 +02:00
}
}
return updates ;
}
2018-04-18 11:40:43 +02:00
void PresetUpdater :: priv :: perform_updates ( Updates && updates , bool snapshot ) const
2018-04-16 16:52:11 +02:00
{
2018-04-24 18:06:42 +02:00
if ( updates . incompats . size () > 0 ) {
if ( snapshot ) {
BOOST_LOG_TRIVIAL ( info ) << "Taking a snapshot..." ;
2018-09-21 01:33:41 +02:00
SnapshotDB :: singleton (). take_snapshot ( * GUI :: wxGetApp (). app_config , Snapshot :: SNAPSHOT_DOWNGRADE );
2018-04-24 18:06:42 +02:00
}
2018-04-23 11:16:47 +02:00
2018-04-24 18:06:42 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Deleting %1% incompatible bundles" ) % updates . incompats . size ();
2019-08-05 17:28:02 +02:00
for ( auto & incompat : updates . incompats ) {
2018-04-24 18:06:42 +02:00
BOOST_LOG_TRIVIAL ( info ) << '\t' << incompat ;
2019-08-05 17:28:02 +02:00
incompat . remove ();
2018-04-24 18:06:42 +02:00
}
2019-08-05 17:28:02 +02:00
} else if ( updates . updates . size () > 0 ) {
2018-04-24 18:06:42 +02:00
if ( snapshot ) {
BOOST_LOG_TRIVIAL ( info ) << "Taking a snapshot..." ;
2018-09-21 01:33:41 +02:00
SnapshotDB :: singleton (). take_snapshot ( * GUI :: wxGetApp (). app_config , Snapshot :: SNAPSHOT_UPGRADE );
2018-04-24 18:06:42 +02:00
}
2018-04-16 16:52:11 +02:00
2018-04-24 18:06:42 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Performing %1% updates" ) % updates . updates . size ();
2018-04-23 11:16:47 +02:00
2018-04-24 18:06:42 +02:00
for ( const auto & update : updates . updates ) {
BOOST_LOG_TRIVIAL ( info ) << '\t' << update ;
2018-04-16 16:52:11 +02:00
2019-06-03 11:31:32 +02:00
update . install ();
2018-04-16 16:52:11 +02:00
2018-04-24 18:06:42 +02:00
PresetBundle bundle ;
2019-06-03 11:31:32 +02:00
bundle . load_configbundle ( update . source . string (), PresetBundle :: LOAD_CFGBNDLE_SYSTEM );
2018-04-16 16:52:11 +02:00
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Deleting %1% conflicting presets" )
% ( bundle . prints . size () + bundle . filaments . size () + bundle . printers . size ());
2018-04-24 18:06:42 +02:00
auto preset_remover = []( const Preset & preset ) {
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << '\t' << preset . file ;
2018-04-24 18:06:42 +02:00
fs :: remove ( preset . file );
};
for ( const auto & preset : bundle . prints ) { preset_remover ( preset ); }
for ( const auto & preset : bundle . filaments ) { preset_remover ( preset ); }
for ( const auto & preset : bundle . printers ) { preset_remover ( preset ); }
2018-05-16 19:41:35 +02:00
// Also apply the `obsolete_presets` property, removing obsolete ini files
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Deleting %1% obsolete presets" )
% ( bundle . obsolete_presets . prints . size () + bundle . obsolete_presets . filaments . size () + bundle . obsolete_presets . printers . size ());
2018-05-16 19:41:35 +02:00
auto obsolete_remover = []( const char * subdir , const std :: string & preset ) {
auto path = fs :: path ( Slic3r :: data_dir ()) / subdir / preset ;
path += ".ini" ;
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << '\t' << path . string ();
2018-05-16 19:41:35 +02:00
fs :: remove ( path );
};
for ( const auto & name : bundle . obsolete_presets . prints ) { obsolete_remover ( "print" , name ); }
for ( const auto & name : bundle . obsolete_presets . filaments ) { obsolete_remover ( "filament" , name ); }
2018-11-19 11:10:22 +01:00
for ( const auto & name : bundle . obsolete_presets . sla_prints ) { obsolete_remover ( "sla_print" , name ); }
for ( const auto & name : bundle . obsolete_presets . sla_materials /*filaments*/ ) { obsolete_remover ( "sla_material" , name ); }
2018-05-16 19:41:35 +02:00
for ( const auto & name : bundle . obsolete_presets . printers ) { obsolete_remover ( "printer" , name ); }
2018-04-24 18:06:42 +02:00
}
2018-04-16 16:52:11 +02:00
}
}
2018-09-21 01:33:41 +02:00
PresetUpdater :: PresetUpdater () :
p ( new priv ())
2018-04-13 15:08:58 +02:00
{}
2018-03-28 11:36:36 +02:00
// Public
PresetUpdater ::~ PresetUpdater ()
{
if ( p && p -> thread . joinable ()) {
2018-04-23 11:16:47 +02:00
// This will stop transfers being done by the thread, if any.
// Cancelling takes some time, but should complete soon enough.
2018-03-28 11:36:36 +02:00
p -> cancel = true ;
p -> thread . join ();
}
}
2018-04-17 16:59:53 +02:00
void PresetUpdater :: sync ( PresetBundle * preset_bundle )
2018-03-28 11:36:36 +02:00
{
2018-09-21 01:33:41 +02:00
p -> set_download_prefs ( GUI :: wxGetApp (). app_config );
2018-04-13 15:08:58 +02:00
if ( ! p -> enabled_version_check && ! p -> enabled_config_update ) { return ; }
2018-03-28 11:36:36 +02:00
// Copy the whole vendors data for use in the background thread
// Unfortunatelly as of C++11, it needs to be copied again
// into the closure (but perhaps the compiler can elide this).
std :: set < VendorProfile > vendors = preset_bundle -> vendors ;
p -> thread = std :: move ( std :: thread ([ this , vendors ]() {
2018-04-13 15:08:58 +02:00
this -> p -> prune_tmps ();
this -> p -> sync_version ();
this -> p -> sync_config ( std :: move ( vendors ));
2018-03-28 11:36:36 +02:00
}));
}
2018-04-17 16:59:53 +02:00
void PresetUpdater :: slic3r_update_notify ()
{
2018-04-23 11:16:47 +02:00
if ( ! p -> enabled_version_check ) { return ; }
2018-09-21 01:33:41 +02:00
auto * app_config = GUI :: wxGetApp (). app_config ;
2018-04-20 14:53:11 +02:00
const auto ver_online_str = app_config -> get ( "version_online" );
const auto ver_online = Semver :: parse ( ver_online_str );
const auto ver_online_seen = Semver :: parse ( app_config -> get ( "version_online_seen" ));
2018-04-17 16:59:53 +02:00
2018-04-20 14:53:11 +02:00
if ( ver_online ) {
// Only display the notification if the version available online is newer AND if we haven't seen it before
2019-08-09 17:01:37 +02:00
if ( * ver_online > Slic3r :: SEMVER && ( ! ver_online_seen || * ver_online_seen < * ver_online )) {
GUI :: MsgUpdateSlic3r notification ( Slic3r :: SEMVER , * ver_online );
2018-04-20 14:53:11 +02:00
notification . ShowModal ();
if ( notification . disable_version_check ()) {
app_config -> set ( "version_check" , "0" );
p -> enabled_version_check = false ;
}
2018-04-17 16:59:53 +02:00
}
2019-05-13 18:38:48 +02:00
2018-04-20 14:53:11 +02:00
app_config -> set ( "version_online_seen" , ver_online_str );
2018-04-17 16:59:53 +02:00
}
}
2019-05-17 17:35:15 +02:00
PresetUpdater :: UpdateResult PresetUpdater :: config_update () const
2018-04-11 13:12:08 +02:00
{
2019-05-17 17:35:15 +02:00
if ( ! p -> enabled_config_update ) { return R_NOOP ; }
2018-04-13 15:08:58 +02:00
2018-04-23 13:58:03 +02:00
auto updates = p -> get_config_updates ();
2018-04-24 18:06:42 +02:00
if ( updates . incompats . size () > 0 ) {
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "%1% bundles incompatible. Asking for action..." ) % updates . incompats . size ();
2018-04-23 11:16:47 +02:00
2018-04-24 18:40:06 +02:00
std :: unordered_map < std :: string , wxString > incompats_map ;
2018-04-24 18:06:42 +02:00
for ( const auto & incompat : updates . incompats ) {
2018-07-23 12:34:07 +02:00
const auto min_slic3r = incompat . version . min_slic3r_version ;
const auto max_slic3r = incompat . version . max_slic3r_version ;
wxString restrictions ;
if ( min_slic3r != Semver :: zero () && max_slic3r != Semver :: inf ()) {
restrictions = wxString :: Format ( _ ( L ( "requires min. %s and max. %s" )),
min_slic3r . to_string (),
max_slic3r . to_string ()
);
} else if ( min_slic3r != Semver :: zero ()) {
restrictions = wxString :: Format ( _ ( L ( "requires min. %s" )), min_slic3r . to_string ());
} else {
restrictions = wxString :: Format ( _ ( L ( "requires max. %s" )), max_slic3r . to_string ());
}
2019-05-13 18:38:48 +02:00
incompats_map . emplace ( std :: make_pair ( incompat . vendor , std :: move ( restrictions )));
2018-04-12 20:04:48 +02:00
}
2018-04-24 18:06:42 +02:00
GUI :: MsgDataIncompatible dlg ( std :: move ( incompats_map ));
const auto res = dlg . ShowModal ();
if ( res == wxID_REPLACE ) {
BOOST_LOG_TRIVIAL ( info ) << "User wants to re-configure..." ;
2019-08-11 18:57:33 +02:00
// This effectively removes the incompatible bundles:
// (snapshot is taken beforehand)
2018-04-24 18:06:42 +02:00
p -> perform_updates ( std :: move ( updates ));
2019-08-11 18:57:33 +02:00
2018-04-24 18:06:42 +02:00
GUI :: ConfigWizard wizard ( nullptr , GUI :: ConfigWizard :: RR_DATA_INCOMPAT );
2019-08-11 18:57:33 +02:00
2018-10-01 15:09:31 +02:00
if ( ! wizard . run ( GUI :: wxGetApp (). preset_bundle , this )) {
2019-05-17 17:35:15 +02:00
return R_INCOMPAT_EXIT ;
2018-04-24 18:06:42 +02:00
}
2019-08-11 18:57:33 +02:00
2018-10-01 15:09:31 +02:00
GUI :: wxGetApp (). load_current_presets ();
2019-05-17 17:35:15 +02:00
return R_INCOMPAT_CONFIGURED ;
2018-04-24 18:06:42 +02:00
} else {
BOOST_LOG_TRIVIAL ( info ) << "User wants to exit Slic3r, bye..." ;
2019-05-17 17:35:15 +02:00
return R_INCOMPAT_EXIT ;
2018-04-24 18:06:42 +02:00
}
2019-05-17 17:35:15 +02:00
} else if ( updates . updates . size () > 0 ) {
2018-04-24 18:06:42 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Update of %1% bundles available. Asking for confirmation ..." ) % updates . updates . size ();
2019-05-13 18:38:48 +02:00
std :: vector < GUI :: MsgUpdateConfig :: Update > updates_msg ;
2018-04-24 18:06:42 +02:00
for ( const auto & update : updates . updates ) {
2019-05-13 18:38:48 +02:00
std :: string changelog_url = update . version . config_version . prerelease () == nullptr ? update . changelog_url : std :: string ();
updates_msg . emplace_back ( update . vendor , update . version . config_version , update . version . comment , std :: move ( changelog_url ));
2018-04-24 18:06:42 +02:00
}
2019-05-13 18:38:48 +02:00
GUI :: MsgUpdateConfig dlg ( updates_msg );
2018-04-24 18:06:42 +02:00
2018-04-12 20:04:48 +02:00
const auto res = dlg . ShowModal ();
2018-04-25 15:20:46 +02:00
if ( res == wxID_OK ) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( debug ) << "User agreed to perform the update" ;
2018-04-17 16:59:53 +02:00
p -> perform_updates ( std :: move ( updates ));
2018-06-06 10:52:19 +02:00
// Reload global configuration
2018-09-21 01:33:41 +02:00
auto * app_config = GUI :: wxGetApp (). app_config ;
2019-06-03 11:31:32 +02:00
GUI :: wxGetApp (). preset_bundle -> load_presets ( * app_config );
2018-10-01 15:09:31 +02:00
GUI :: wxGetApp (). load_current_presets ();
2019-05-17 17:35:15 +02:00
return R_UPDATE_INSTALLED ;
2018-04-23 11:16:47 +02:00
} else {
BOOST_LOG_TRIVIAL ( info ) << "User refused the update" ;
2019-05-17 17:35:15 +02:00
return R_UPDATE_REJECT ;
2018-04-11 13:12:08 +02:00
}
2018-04-23 11:16:47 +02:00
} else {
BOOST_LOG_TRIVIAL ( info ) << "No configuration updates available." ;
2018-04-11 13:12:08 +02:00
}
2018-04-24 18:06:42 +02:00
2019-05-17 17:35:15 +02:00
return R_NOOP ;
2018-04-11 13:12:08 +02:00
}
2018-04-24 18:06:42 +02:00
void PresetUpdater :: install_bundles_rsrc ( std :: vector < std :: string > bundles , bool snapshot ) const
2018-04-16 16:52:11 +02:00
{
Updates updates ;
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Installing %1% bundles from resources ..." ) % bundles . size ();
2018-04-16 16:52:11 +02:00
for ( const auto & bundle : bundles ) {
auto path_in_rsrc = p -> rsrc_path / bundle ;
auto path_in_vendors = p -> vendor_path / bundle ;
2019-05-13 18:38:48 +02:00
updates . updates . emplace_back ( std :: move ( path_in_rsrc ), std :: move ( path_in_vendors ), Version (), "" , "" );
2018-04-16 16:52:11 +02:00
}
2018-04-18 11:40:43 +02:00
p -> perform_updates ( std :: move ( updates ), snapshot );
2018-04-16 16:52:11 +02:00
}
2018-03-28 11:36:36 +02:00
}