2020-04-21 15:30:12 +08:00
# include "pch.h"
2020-10-23 00:02:59 +08:00
# include "Generated Files/resource.h"
2021-06-14 17:55:59 +08:00
# include "ActionRunnerUtils.h"
2021-05-21 18:32:34 +08:00
# include "general_settings.h"
2021-06-14 17:55:59 +08:00
# include "UpdateUtils.h"
2020-04-21 15:30:12 +08:00
2023-02-24 22:35:33 +08:00
# include <common/utils/gpo.h>
2021-05-21 18:32:34 +08:00
# include <common/logger/logger.h>
2021-06-14 17:55:59 +08:00
# include <common/notifications/notifications.h>
2020-11-20 16:34:34 +08:00
# include <common/updating/installer.h>
2020-04-21 15:30:12 +08:00
# include <common/updating/updating.h>
2021-05-21 18:32:34 +08:00
# include <common/updating/updateState.h>
2021-06-14 17:55:59 +08:00
# include <common/utils/HttpClient.h>
# include <common/utils/process_path.h>
2020-12-15 20:16:09 +08:00
# include <common/utils/resources.h>
# include <common/utils/timeutil.h>
2021-06-14 17:55:59 +08:00
# include <common/version/version.h>
2020-10-23 00:02:59 +08:00
2021-01-12 23:34:02 +08:00
namespace
{
constexpr int64_t UPDATE_CHECK_INTERVAL_MINUTES = 60 * 24 ;
constexpr int64_t UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES = 60 * 2 ;
2023-02-24 22:35:33 +08:00
// How many minor versions to suspend the toast notification (example: installed=0.60.0, suspend=2, next notification=0.63.*)
// Attention: When changing this value please update the ADML file to.
const int UPDATE_NOTIFICATION_TOAST_SUSPEND_MINOR_VERSION_COUNT = 2 ;
2021-01-12 23:34:02 +08:00
}
2021-06-14 17:55:59 +08:00
using namespace notifications ;
using namespace updating ;
2021-01-12 23:34:02 +08:00
2021-06-14 17:55:59 +08:00
std : : wstring CurrentVersionToNextVersion ( const new_version_download_info & info )
2020-04-21 15:30:12 +08:00
{
2021-06-14 17:55:59 +08:00
auto result = VersionHelper { VERSION_MAJOR , VERSION_MINOR , VERSION_REVISION } . toWstring ( ) ;
result + = L " -> " ;
result + = info . version . toWstring ( ) ;
return result ;
}
2020-04-21 15:30:12 +08:00
2021-06-14 17:55:59 +08:00
void ShowNewVersionAvailable ( const new_version_download_info & info )
{
remove_toasts_by_tag ( UPDATING_PROCESS_TOAST_TAG ) ;
toast_params toast_params { UPDATING_PROCESS_TOAST_TAG , false } ;
std : : wstring contents = GET_RESOURCE_STRING ( IDS_GITHUB_NEW_VERSION_AVAILABLE ) ;
contents + = L ' \n ' ;
contents + = CurrentVersionToNextVersion ( info ) ;
show_toast_with_activations ( std : : move ( contents ) ,
GET_RESOURCE_STRING ( IDS_TOAST_TITLE ) ,
{ } ,
{ link_button { GET_RESOURCE_STRING ( IDS_GITHUB_NEW_VERSION_UPDATE_NOW ) ,
L " powertoys://update_now/ " } ,
link_button { GET_RESOURCE_STRING ( IDS_GITHUB_NEW_VERSION_MORE_INFO ) ,
L " powertoys://open_settings/ " } } ,
std : : move ( toast_params ) ) ;
}
2020-04-21 15:30:12 +08:00
2021-06-14 17:55:59 +08:00
void ShowOpenSettingsForUpdate ( )
{
remove_toasts_by_tag ( UPDATING_PROCESS_TOAST_TAG ) ;
toast_params toast_params { UPDATING_PROCESS_TOAST_TAG , false } ;
std : : vector < action_t > actions = {
link_button { GET_RESOURCE_STRING ( IDS_GITHUB_NEW_VERSION_MORE_INFO ) ,
L " powertoys://open_settings/ " } ,
} ;
show_toast_with_activations ( GET_RESOURCE_STRING ( IDS_GITHUB_NEW_VERSION_AVAILABLE ) ,
GET_RESOURCE_STRING ( IDS_TOAST_TITLE ) ,
{ } ,
std : : move ( actions ) ,
std : : move ( toast_params ) ) ;
}
2020-04-21 15:30:12 +08:00
2021-06-14 17:55:59 +08:00
SHELLEXECUTEINFOW LaunchPowerToysUpdate ( const wchar_t * cmdline )
{
std : : wstring powertoysUpdaterPath ;
powertoysUpdaterPath = get_module_folderpath ( ) ;
powertoysUpdaterPath + = L " \\ PowerToys.Update.exe " ;
SHELLEXECUTEINFOW sei { sizeof ( sei ) } ;
sei . fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS } ;
sei . lpFile = powertoysUpdaterPath . c_str ( ) ;
sei . nShow = SW_SHOWNORMAL ;
sei . lpParameters = cmdline ;
ShellExecuteExW ( & sei ) ;
return sei ;
2020-04-21 15:30:12 +08:00
}
2021-06-14 17:55:59 +08:00
bool IsMeteredConnection ( )
2021-05-21 18:32:34 +08:00
{
using namespace winrt : : Windows : : Networking : : Connectivity ;
ConnectionProfile internetConnectionProfile = NetworkInformation : : GetInternetConnectionProfile ( ) ;
2023-02-14 00:29:26 +08:00
if ( ! internetConnectionProfile )
{
return false ;
}
if ( internetConnectionProfile . IsWwanConnectionProfile ( ) )
{
return true ;
}
ConnectionCost connectionCost = internetConnectionProfile . GetConnectionCost ( ) ;
if ( connectionCost . Roaming ( )
| | connectionCost . OverDataLimit ( )
| | connectionCost . NetworkCostType ( ) = = NetworkCostType : : Fixed
| | connectionCost . NetworkCostType ( ) = = NetworkCostType : : Variable )
{
return true ;
}
return false ;
2021-05-21 18:32:34 +08:00
}
2021-06-14 17:55:59 +08:00
void ProcessNewVersionInfo ( const github_version_info & version_info ,
UpdateState & state ,
const bool download_update ,
2023-02-24 22:35:33 +08:00
bool show_notifications )
2021-05-21 18:32:34 +08:00
{
state . githubUpdateLastCheckedDate . emplace ( timeutil : : now ( ) ) ;
if ( std : : holds_alternative < version_up_to_date > ( version_info ) )
{
state . state = UpdateState : : upToDate ;
state . releasePageUrl = { } ;
state . downloadedInstallerFilename = { } ;
Logger : : trace ( L " Version is up to date " ) ;
return ;
}
const auto new_version_info = std : : get < new_version_download_info > ( version_info ) ;
state . releasePageUrl = new_version_info . release_page_uri . ToString ( ) . c_str ( ) ;
Logger : : trace ( L " Discovered new version {} " , new_version_info . version . toWstring ( ) ) ;
const bool already_downloaded = state . state = = UpdateState : : readyToInstall & & state . downloadedInstallerFilename = = new_version_info . installer_filename ;
if ( already_downloaded )
{
Logger : : trace ( L " New version is already downloaded " ) ;
return ;
}
2023-02-24 22:35:33 +08:00
// Check notification GPO.
// We check only if notifications are allowed. This is the case if we are triggered by the periodic check.
if ( show_notifications & & powertoys_gpo : : getSuspendNewUpdateToastValue ( ) = = powertoys_gpo : : gpo_rule_configured_enabled )
{
Logger : : info ( L " GPO to suspend new update toast notification is enabled. " ) ;
if ( new_version_info . version . major < = VERSION_MAJOR & & new_version_info . version . minor - VERSION_MINOR < = UPDATE_NOTIFICATION_TOAST_SUSPEND_MINOR_VERSION_COUNT )
{
Logger : : info ( L " The difference between the installed version and the newer version is within the allowed period. The toast notification is not shown. " ) ;
show_notifications = false ;
}
else
{
Logger : : info ( L " The installed version is older than allowed for suspending the toast notification. The toast notification is shown. " ) ;
}
}
2021-05-21 18:32:34 +08:00
if ( download_update )
{
Logger : : trace ( L " Downloading installer for a new version " ) ;
if ( download_new_version ( new_version_info ) . get ( ) )
{
state . state = UpdateState : : readyToInstall ;
state . downloadedInstallerFilename = new_version_info . installer_filename ;
if ( show_notifications )
{
2021-06-14 17:55:59 +08:00
ShowNewVersionAvailable ( new_version_info ) ;
2021-05-21 18:32:34 +08:00
}
}
else
{
state . state = UpdateState : : errorDownloading ;
state . downloadedInstallerFilename = { } ;
Logger : : error ( " Couldn't download new installer " ) ;
}
}
else
{
Logger : : trace ( L " New version is ready to download, showing notification " ) ;
state . state = UpdateState : : readyToDownload ;
state . downloadedInstallerFilename = { } ;
if ( show_notifications )
{
2021-06-14 17:55:59 +08:00
ShowOpenSettingsForUpdate ( ) ;
2021-05-21 18:32:34 +08:00
}
}
}
2021-06-14 17:55:59 +08:00
void PeriodicUpdateWorker ( )
2020-04-21 15:30:12 +08:00
{
2023-02-24 22:35:33 +08:00
// Check if periodic update check is disabled by GPO.
// This policy code is implemented but not active. It is for later usage in PT version after 1.0 release.
//if (powertoys_gpo::getDisablePeriodicUpdateCheckValue() == powertoys_gpo::gpo_rule_configured_enabled)
//{
// Logger::info(L"Initialization of periodic update checks stopped. Periodic update checks are disabled by GPO.");
// return;
//}
2020-04-21 15:30:12 +08:00
for ( ; ; )
{
auto state = UpdateState : : read ( ) ;
2022-04-02 00:44:49 +08:00
int64_t sleep_minutes_till_next_update = UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES ;
2021-05-21 18:32:34 +08:00
if ( state . githubUpdateLastCheckedDate . has_value ( ) )
2020-04-21 15:30:12 +08:00
{
2021-05-21 18:32:34 +08:00
int64_t last_checked_minutes_ago = timeutil : : diff : : in_minutes ( timeutil : : now ( ) , * state . githubUpdateLastCheckedDate ) ;
2020-04-21 15:30:12 +08:00
if ( last_checked_minutes_ago < 0 )
{
2021-01-12 23:34:02 +08:00
last_checked_minutes_ago = UPDATE_CHECK_INTERVAL_MINUTES ;
2020-04-21 15:30:12 +08:00
}
2021-01-12 23:34:02 +08:00
sleep_minutes_till_next_update = max ( 0 , UPDATE_CHECK_INTERVAL_MINUTES - last_checked_minutes_ago ) ;
2020-04-21 15:30:12 +08:00
}
2021-01-12 23:34:02 +08:00
std : : this_thread : : sleep_for ( std : : chrono : : minutes { sleep_minutes_till_next_update } ) ;
2021-05-21 18:32:34 +08:00
2023-02-24 22:35:33 +08:00
// Auto download setting.
bool download_update = ! IsMeteredConnection ( ) & & get_general_settings ( ) . downloadUpdatesAutomatically ;
if ( powertoys_gpo : : getDisableAutomaticUpdateDownloadValue ( ) = = powertoys_gpo : : gpo_rule_configured_enabled )
{
Logger : : info ( L " Automatic download of updates is disabled by GPO. " ) ;
download_update = false ;
}
2021-05-21 18:32:34 +08:00
bool version_info_obtained = false ;
2020-04-21 15:30:12 +08:00
try
{
2021-06-14 17:55:59 +08:00
const auto new_version_info = get_github_version_info_async ( ) . get ( ) ;
2021-05-21 18:32:34 +08:00
if ( new_version_info . has_value ( ) )
{
version_info_obtained = true ;
2021-06-14 17:55:59 +08:00
ProcessNewVersionInfo ( * new_version_info , state , download_update , true ) ;
2021-05-21 18:32:34 +08:00
}
else
{
Logger : : error ( L " Couldn't obtain version info from github: {} " , new_version_info . error ( ) ) ;
}
2020-04-21 15:30:12 +08:00
}
catch ( . . . )
{
2021-05-21 18:32:34 +08:00
Logger : : error ( " periodic_update_worker: error while processing version info " ) ;
2021-01-12 23:34:02 +08:00
}
2021-05-21 18:32:34 +08:00
if ( version_info_obtained )
2021-01-12 23:34:02 +08:00
{
2021-05-21 18:32:34 +08:00
UpdateState : : store ( [ & ] ( UpdateState & v ) {
v = std : : move ( state ) ;
2021-01-12 23:34:02 +08:00
} ) ;
}
else
{
std : : this_thread : : sleep_for ( std : : chrono : : minutes { UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES } ) ;
2020-04-21 15:30:12 +08:00
}
}
}
2021-06-14 17:55:59 +08:00
void CheckForUpdatesCallback ( )
2020-04-21 15:30:12 +08:00
{
2021-05-21 18:32:34 +08:00
Logger : : trace ( L " Check for updates callback invoked " ) ;
auto state = UpdateState : : read ( ) ;
2020-04-21 15:30:12 +08:00
try
{
2021-06-14 17:55:59 +08:00
auto new_version_info = get_github_version_info_async ( ) . get ( ) ;
2021-05-21 18:32:34 +08:00
if ( ! new_version_info )
2020-04-21 15:30:12 +08:00
{
2021-05-21 18:32:34 +08:00
// If we couldn't get a new version from github for some reason, assume we're up to date, but also log error
new_version_info = version_up_to_date { } ;
Logger : : error ( L " Couldn't obtain version info from github: {} " , new_version_info . error ( ) ) ;
2020-04-21 15:30:12 +08:00
}
2023-02-24 22:35:33 +08:00
// Auto download setting
bool download_update = ! IsMeteredConnection ( ) & & get_general_settings ( ) . downloadUpdatesAutomatically ;
if ( powertoys_gpo : : getDisableAutomaticUpdateDownloadValue ( ) = = powertoys_gpo : : gpo_rule_configured_enabled )
{
Logger : : info ( L " Automatic download of updates is disabled by GPO. " ) ;
download_update = false ;
}
2021-06-14 17:55:59 +08:00
ProcessNewVersionInfo ( * new_version_info , state , download_update , false ) ;
2021-05-21 18:32:34 +08:00
UpdateState : : store ( [ & ] ( UpdateState & v ) {
v = std : : move ( state ) ;
} ) ;
2020-04-21 15:30:12 +08:00
}
catch ( . . . )
{
2021-06-14 17:55:59 +08:00
Logger : : error ( " CheckForUpdatesCallback: error while processing version info " ) ;
2020-04-21 15:30:12 +08:00
}
}