1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699 |
- /*
- * UpdateCheck.cpp
- * ---------------
- * Purpose: Class for easy software update check.
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "UpdateCheck.h"
- #include "mpt/binary/hex.hpp"
- #include "BuildVariants.h"
- #include "../common/version.h"
- #include "../common/misc_util.h"
- #include "../common/mptStringBuffer.h"
- #include "Mptrack.h"
- #include "TrackerSettings.h"
- // Setup dialog stuff
- #include "Mainfrm.h"
- #include "mpt/system_error/system_error.hpp"
- #include "mpt/crypto/hash.hpp"
- #include "mpt/crypto/jwk.hpp"
- #include "HTTP.h"
- #include "mpt/json/json.hpp"
- #include "dlg_misc.h"
- #include "openmpt/sounddevice/SoundDeviceManager.hpp"
- #include "ProgressDialog.h"
- #include "Moddoc.h"
- #include "mpt/io/io.hpp"
- #include "mpt/io/io_stdstream.hpp"
- OPENMPT_NAMESPACE_BEGIN
- #if defined(MPT_ENABLE_UPDATE)
- namespace Update {
- struct windowsversion {
- uint64 version_major = 0;
- uint64 version_minor = 0;
- uint64 servicepack_major = 0;
- uint64 servicepack_minor = 0;
- uint64 build = 0;
- uint64 wine_major = 0;
- uint64 wine_minor = 0;
- uint64 wine_update = 0;
- };
- NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(windowsversion
- ,version_major
- ,version_minor
- ,servicepack_major
- ,servicepack_minor
- ,build
- ,wine_major
- ,wine_minor
- ,wine_update
- )
- struct autoupdate_installer {
- std::vector<mpt::ustring> arguments = {};
- };
- NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(autoupdate_installer
- ,arguments
- )
- struct autoupdate_archive {
- mpt::ustring subfolder = U_("");
- mpt::ustring restartbinary = U_("");
- };
- NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(autoupdate_archive
- ,subfolder
- ,restartbinary
- )
- struct downloadinfo {
- mpt::ustring url = U_("");
- std::map<mpt::ustring, mpt::ustring> checksums = {};
- mpt::ustring filename = U_("");
- std::optional<autoupdate_installer> autoupdate_installer;
- std::optional<autoupdate_archive> autoupdate_archive;
- };
- NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(downloadinfo
- ,url
- ,checksums
- ,filename
- ,autoupdate_installer
- ,autoupdate_archive
- )
- struct download {
- mpt::ustring url = U_("");
- mpt::ustring download_url = U_("");
- mpt::ustring type = U_("");
- bool can_autoupdate = false;
- mpt::ustring autoupdate_minversion = U_("");
- mpt::ustring os = U_("");
- std::optional<windowsversion> required_windows_version;
- std::map<mpt::ustring, bool> required_architectures = {};
- std::map<mpt::ustring, bool> supported_architectures = {};
- std::map<mpt::ustring, std::map<mpt::ustring, bool>> required_processor_features = {};
- };
- NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(download
- ,url
- ,download_url
- ,type
- ,can_autoupdate
- ,autoupdate_minversion
- ,os
- ,required_windows_version
- ,required_architectures
- ,supported_architectures
- ,required_processor_features
- )
- struct versioninfo {
- mpt::ustring version = U_("");
- mpt::ustring date = U_("");
- mpt::ustring announcement_url = U_("");
- mpt::ustring changelog_url = U_("");
- std::map<mpt::ustring, download> downloads = {};
- };
- NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(versioninfo
- ,version
- ,date
- ,announcement_url
- ,changelog_url
- ,downloads
- )
- using versions = std::map<mpt::ustring, versioninfo>;
- } // namespace Update
- struct UpdateInfo {
- mpt::ustring version;
- mpt::ustring download;
- bool IsAvailable() const
- {
- return !version.empty();
- }
- };
- static bool IsCurrentArchitecture(const mpt::ustring &architecture)
- {
- return mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()) == architecture;
- }
- static bool IsArchitectureSupported(const mpt::ustring &architecture)
- {
- const auto & architectures = mpt::OS::Windows::GetSupportedProcessArchitectures(mpt::OS::Windows::GetHostArchitecture());
- for(const auto & arch : architectures)
- {
- if(mpt::OS::Windows::Name(arch) == architecture)
- {
- return true;
- }
- }
- return false;
- }
- static bool IsArchitectureFeatureSupported(const mpt::ustring &architecture, const mpt::ustring &feature)
- {
- MPT_UNUSED_VARIABLE(architecture);
- #ifdef MPT_ENABLE_ARCH_INTRINSICS
- const CPU::Info CPUInfo = CPU::Info::Get();
- if(feature == U_("")) return true;
- else if(feature == U_("lm")) return (CPUInfo.AvailableFeatures & CPU::feature::lm) != 0;
- else if(feature == U_("mmx")) return (CPUInfo.AvailableFeatures & CPU::feature::mmx) != 0;
- else if(feature == U_("sse")) return (CPUInfo.AvailableFeatures & CPU::feature::sse) != 0;
- else if(feature == U_("sse2")) return (CPUInfo.AvailableFeatures & CPU::feature::sse2) != 0;
- else if(feature == U_("sse3")) return (CPUInfo.AvailableFeatures & CPU::feature::sse3) != 0;
- else if(feature == U_("ssse3")) return (CPUInfo.AvailableFeatures & CPU::feature::ssse3) != 0;
- else if(feature == U_("sse4.1")) return (CPUInfo.AvailableFeatures & CPU::feature::sse4_1) != 0;
- else if(feature == U_("sse4.2")) return (CPUInfo.AvailableFeatures & CPU::feature::sse4_2) != 0;
- else if(feature == U_("avx")) return (CPUInfo.AvailableFeatures & CPU::feature::avx) != 0;
- else if(feature == U_("avx2")) return (CPUInfo.AvailableFeatures & CPU::feature::avx2) != 0;
- else return false;
- #else // !MPT_ENABLE_ARCH_INTRINSICS
- return true;
- #endif // MPT_ENABLE_ARCH_INTRINSICS
- }
- static mpt::ustring GetChannelName(UpdateChannel channel)
- {
- mpt::ustring channelName = U_("release");
- switch(channel)
- {
- case UpdateChannelDevelopment:
- channelName = U_("development");
- break;
- case UpdateChannelNext:
- channelName = U_("next");
- break;
- case UpdateChannelRelease:
- channelName = U_("release");
- break;
- default:
- channelName = U_("release");
- break;
- }
- return channelName;
- }
- static UpdateInfo GetBestDownload(const Update::versions &versions)
- {
-
- UpdateInfo result;
- VersionWithRevision bestVersion = VersionWithRevision::Current();
- for(const auto & [versionname, versioninfo] : versions)
- {
- if(!VersionWithRevision::Parse(versioninfo.version).IsNewerThan(bestVersion))
- {
- continue;
- }
- mpt::ustring bestDownloadName;
- // check if version supports the current system
- bool is_supported = false;
- for(auto & [downloadname, download] : versioninfo.downloads)
- {
- // is it for windows?
- if(download.os != U_("windows") || !download.required_windows_version)
- {
- continue;
- }
- // can the installer run on the current system?
- bool download_supported = true;
- for(const auto & [architecture, required] : download.required_architectures)
- {
- if(!(required && IsArchitectureSupported(architecture)))
- {
- download_supported = false;
- }
- }
- // does the download run on current architecture?
- bool architecture_supported = false;
- for(const auto & [architecture, supported] : download.supported_architectures)
- {
- if(supported && IsCurrentArchitecture(architecture))
- {
- architecture_supported = true;
- }
- }
- if(!architecture_supported)
- {
- download_supported = false;
- }
- // does the current system have all required features?
- for(const auto & [architecture, features] : download.required_processor_features)
- {
- if(IsCurrentArchitecture(architecture))
- {
- for(const auto & [feature, required] : features)
- {
- if(!(required && IsArchitectureFeatureSupported(architecture, feature)))
- {
- download_supported = false;
- }
- }
- }
- }
- if(mpt::OS::Windows::Version::Current().IsBefore(
- mpt::osinfo::windows::Version::System(mpt::saturate_cast<uint32>(download.required_windows_version->version_major), mpt::saturate_cast<uint32>(download.required_windows_version->version_minor)),
- mpt::osinfo::windows::Version::ServicePack(mpt::saturate_cast<uint16>(download.required_windows_version->servicepack_major), mpt::saturate_cast<uint16>(download.required_windows_version->servicepack_minor)),
- mpt::osinfo::windows::Version::Build(mpt::saturate_cast<uint32>(download.required_windows_version->build))
- ))
- {
- download_supported = false;
- }
- if(mpt::OS::Windows::IsWine() && theApp.GetWineVersion()->Version().IsValid())
- {
- if(theApp.GetWineVersion()->Version().IsBefore(mpt::OS::Wine::Version(mpt::saturate_cast<uint8>(download.required_windows_version->wine_major), mpt::saturate_cast<uint8>(download.required_windows_version->wine_minor), mpt::saturate_cast<uint8>(download.required_windows_version->wine_update))))
- {
- download_supported = false;
- }
- }
- if(download_supported)
- {
- is_supported = true;
- if(theApp.IsInstallerMode() && download.type == U_("installer"))
- {
- bestDownloadName = downloadname;
- } else if(theApp.IsPortableMode() && download.type == U_("archive"))
- {
- bestDownloadName = downloadname;
- }
- }
- }
- if(is_supported)
- {
- bestVersion = VersionWithRevision::Parse(versioninfo.version);
- result.version = versionname;
- result.download = bestDownloadName;
- }
- }
- return result;
- }
- // Update notification dialog
- class UpdateDialog : public CDialog
- {
- protected:
- const CString m_releaseVersion;
- const CString m_releaseDate;
- const CString m_releaseURL;
- const CString m_buttonText;
- CFont m_boldFont;
- public:
- UpdateDialog(const CString &releaseVersion, const CString &releaseDate, const CString &releaseURL, const CString &buttonText = _T("&Update"))
- : CDialog(IDD_UPDATE)
- , m_releaseVersion(releaseVersion)
- , m_releaseDate(releaseDate)
- , m_releaseURL(releaseURL)
- , m_buttonText(buttonText)
- { }
- BOOL OnInitDialog() override
- {
- CDialog::OnInitDialog();
- SetDlgItemText(IDOK, m_buttonText);
- CFont *font = GetDlgItem(IDC_VERSION2)->GetFont();
- LOGFONT lf;
- font->GetLogFont(&lf);
- lf.lfWeight = FW_BOLD;
- m_boldFont.CreateFontIndirect(&lf);
- GetDlgItem(IDC_VERSION2)->SetFont(&m_boldFont);
- SetDlgItemText(IDC_VERSION1, mpt::cfmt::val(VersionWithRevision::Current()));
- SetDlgItemText(IDC_VERSION2, m_releaseVersion);
- SetDlgItemText(IDC_DATE, m_releaseDate);
- SetDlgItemText(IDC_SYSLINK1, _T("More information about this build:\n<a href=\"") + m_releaseURL + _T("\">") + m_releaseURL + _T("</a>"));
- CheckDlgButton(IDC_CHECK1, (TrackerSettings::Instance().UpdateIgnoreVersion == m_releaseVersion) ? BST_CHECKED : BST_UNCHECKED);
- return FALSE;
- }
- void OnDestroy()
- {
- TrackerSettings::Instance().UpdateIgnoreVersion = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED ? m_releaseVersion : CString();
- m_boldFont.DeleteObject();
- CDialog::OnDestroy();
- }
- void OnClickURL(NMHDR * /*pNMHDR*/, LRESULT * /*pResult*/)
- {
- CTrackApp::OpenURL(m_releaseURL);
- }
- DECLARE_MESSAGE_MAP()
- };
- BEGIN_MESSAGE_MAP(UpdateDialog, CDialog)
- ON_NOTIFY(NM_CLICK, IDC_SYSLINK1, &UpdateDialog::OnClickURL)
- ON_WM_DESTROY()
- END_MESSAGE_MAP()
- mpt::ustring CUpdateCheck::GetStatisticsUserInformation(bool shortText)
- {
- if(shortText)
- {
- return U_("A randomized user ID is sent together with basic system information."
- " This ID cannot be linked to you personally in any way."
- "\nOpenMPT will use this information to gather usage statistics and to plan system support for future OpenMPT versions.");
- } else
- {
- return U_(
- "When checking for updates, OpenMPT can additionally collect basic statistical information."
- " A randomized user ID is sent alongside the update check. This ID and the transmitted statistics cannot be linked to you personally in any way."
- " OpenMPT will use this information to gather usage statistics and to plan system support for future OpenMPT versions."
- "\nOpenMPT would collect the following statistical data points: OpenMPT version, Windows version, type of CPU, amount of RAM, sound device settings, configured update check frequency of OpenMPT.");
- }
- }
- std::vector<mpt::ustring> CUpdateCheck::GetDefaultUpdateSigningKeysRootAnchors()
- {
- // IMPORTANT:
- // Signing keys are *NOT* stored on the same server as openmpt.org or the updates themselves,
- // because otherwise, a single compromised server could allow for rogue updates.
- return {
- U_("https://update.openmpt.de/update/"),
- U_("https://demo-scene.de/openmpt/update/")
- };
- }
- mpt::ustring CUpdateCheck::GetDefaultAPIURL()
- {
- return U_("https://update.openmpt.org/api/v3/");
- }
- std::atomic<int32> CUpdateCheck::s_InstanceCount(0);
- int32 CUpdateCheck::GetNumCurrentRunningInstances()
- {
- return s_InstanceCount.load();
- }
- bool CUpdateCheck::IsSuitableUpdateMoment()
- {
- const auto documents = theApp.GetOpenDocuments();
- return std::all_of(documents.begin(), documents.end(), [](auto doc) { return !doc->IsModified(); });
- }
- // Start update check
- void CUpdateCheck::StartUpdateCheckAsync(bool isAutoUpdate)
- {
- bool loadPersisted = false;
- if(isAutoUpdate)
- {
- if(!TrackerSettings::Instance().UpdateEnabled)
- {
- return;
- }
- if(!IsSuitableUpdateMoment())
- {
- return;
- }
- int updateCheckPeriod = TrackerSettings::Instance().UpdateIntervalDays;
- if(updateCheckPeriod < 0)
- {
- return;
- }
- // Do we actually need to run the update check right now?
- const time_t now = time(nullptr);
- const time_t lastCheck = TrackerSettings::Instance().UpdateLastUpdateCheck.Get();
- // Check update interval. Note that we always check for updates when the system time had gone backwards (i.e. when the last update check supposedly happened in the future).
- const double secsSinceLastCheck = difftime(now, lastCheck);
- if(secsSinceLastCheck > 0.0 && secsSinceLastCheck < updateCheckPeriod * 86400.0)
- {
- loadPersisted = true;
- }
- // Never ran update checks before, so we notify the user of automatic update checks.
- if(TrackerSettings::Instance().UpdateShowUpdateHint)
- {
- TrackerSettings::Instance().UpdateShowUpdateHint = false;
- const auto checkIntervalDays = TrackerSettings::Instance().UpdateIntervalDays.Get();
- CString msg = MPT_CFORMAT("OpenMPT would like to check for updates now, proceed?\n\nNote: In the future, OpenMPT will check for updates {}. If you do not want this, you can disable update checks in the setup.")
- (
- checkIntervalDays == 0 ? CString(_T("on every program start")) :
- checkIntervalDays == 1 ? CString(_T("every day")) :
- MPT_CFORMAT("every {} days")(checkIntervalDays)
- );
- if(Reporting::Confirm(msg, _T("OpenMPT Update")) == cnfNo)
- {
- TrackerSettings::Instance().UpdateLastUpdateCheck = mpt::Date::Unix(now);
- return;
- }
- }
- } else
- {
- if(!IsSuitableUpdateMoment())
- {
- Reporting::Notification(_T("Please save all modified modules before updating OpenMPT."), _T("OpenMPT Update"));
- return;
- }
- if(!TrackerSettings::Instance().UpdateEnabled)
- {
- if(Reporting::Confirm(_T("Update Check is disabled. Do you want to check anyway?"), _T("OpenMPT Update")) != cnfYes)
- {
- return;
- }
- }
- }
- TrackerSettings::Instance().UpdateShowUpdateHint = false;
- // ask if user wants to contribute system statistics
- if(!TrackerSettings::Instance().UpdateStatisticsConsentAsked)
- {
- const auto enableStatistics = Reporting::Confirm(
- U_("Do you want to contribute to OpenMPT by providing system statistics?\r\n\r\n") +
- mpt::String::Replace(CUpdateCheck::GetStatisticsUserInformation(false), U_("\n"), U_("\r\n")) + U_("\r\n\r\n") +
- MPT_UFORMAT("This option was previously {} on your system.\r\n")(TrackerSettings::Instance().UpdateStatistics ? U_("enabled") : U_("disabled")),
- false, !TrackerSettings::Instance().UpdateStatistics.Get());
- TrackerSettings::Instance().UpdateStatistics = (enableStatistics == ConfirmAnswer::cnfYes);
- TrackerSettings::Instance().UpdateStatisticsConsentAsked = true;
- }
- int32 expected = 0;
- if(!s_InstanceCount.compare_exchange_strong(expected, 1))
- {
- return;
- }
- CUpdateCheck::Context context;
- context.window = CMainFrame::GetMainFrame();
- context.msgStart = MPT_WM_APP_UPDATECHECK_START;
- context.msgProgress = MPT_WM_APP_UPDATECHECK_PROGRESS;
- context.msgCanceled = MPT_WM_APP_UPDATECHECK_CANCELED;
- context.msgFailure = MPT_WM_APP_UPDATECHECK_FAILURE;
- context.msgSuccess = MPT_WM_APP_UPDATECHECK_SUCCESS;
- context.autoUpdate = isAutoUpdate;
- context.loadPersisted = loadPersisted;
- context.statistics = GetStatisticsDataV3(CUpdateCheck::Settings());
- std::thread(CUpdateCheck::ThreadFunc(CUpdateCheck::Settings(), context)).detach();
- }
- CUpdateCheck::Settings::Settings()
- : periodDays(TrackerSettings::Instance().UpdateIntervalDays)
- , channel(static_cast<UpdateChannel>(TrackerSettings::Instance().UpdateChannel.Get()))
- , persistencePath(theApp.GetConfigPath())
- , apiURL(TrackerSettings::Instance().UpdateAPIURL)
- , sendStatistics(TrackerSettings::Instance().UpdateStatistics)
- , statisticsUUID(TrackerSettings::Instance().VersionInstallGUID)
- {
- }
- CUpdateCheck::ThreadFunc::ThreadFunc(const CUpdateCheck::Settings &settings, const CUpdateCheck::Context &context)
- : settings(settings)
- , context(context)
- {
- return;
- }
- void CUpdateCheck::ThreadFunc::operator () ()
- {
- SetThreadPriority(GetCurrentThread(), context.autoUpdate ? THREAD_PRIORITY_BELOW_NORMAL : THREAD_PRIORITY_NORMAL);
- CheckForUpdate(settings, context);
- }
- std::string CUpdateCheck::GetStatisticsDataV3(const Settings &settings)
- {
- nlohmann::json j;
- j["OpenMPT"]["Version"] = mpt::ufmt::val(Version::Current());
- j["OpenMPT"]["BuildVariant"] = BuildVariants().GetBuildVariantName(BuildVariants().GetBuildVariant());
- j["OpenMPT"]["Architecture"] = mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture());
- j["Update"]["PeriodDays"] = settings.periodDays;
- j["Update"]["Channel"] = ((settings.channel == UpdateChannelRelease) ? U_("Release") : (settings.channel == UpdateChannelNext) ? U_("Next") : (settings.channel == UpdateChannelDevelopment) ? U_("Development") : U_(""));
- j["System"]["Windows"]["Version"]["Name"] = mpt::OS::Windows::Version::GetName(mpt::OS::Windows::Version::Current());
- j["System"]["Windows"]["Version"]["Major"] = mpt::OS::Windows::Version::Current().GetSystem().Major;
- j["System"]["Windows"]["Version"]["Minor"] = mpt::OS::Windows::Version::Current().GetSystem().Minor;
- j["System"]["Windows"]["ServicePack"]["Major"] = mpt::OS::Windows::Version::Current().GetServicePack().Major;
- j["System"]["Windows"]["ServicePack"]["Minor"] = mpt::OS::Windows::Version::Current().GetServicePack().Minor;
- j["System"]["Windows"]["Build"] = mpt::OS::Windows::Version::Current().GetBuild();
- j["System"]["Windows"]["Architecture"] = mpt::OS::Windows::Name(mpt::OS::Windows::GetHostArchitecture());
- j["System"]["Windows"]["IsWine"] = mpt::OS::Windows::IsWine();
- j["System"]["Windows"]["TypeRaw"] = MPT_AFORMAT("0x{}")(mpt::afmt::HEX0<8>(mpt::OS::Windows::Version::Current().GetTypeId()));
- std::vector<mpt::OS::Windows::Architecture> architectures = mpt::OS::Windows::GetSupportedProcessArchitectures(mpt::OS::Windows::GetHostArchitecture());
- for(const auto & arch : architectures)
- {
- j["System"]["Windows"]["ProcessArchitectures"][mpt::ToCharset(mpt::Charset::UTF8, mpt::OS::Windows::Name(arch))] = true;
- }
- j["System"]["Memory"] = mpt::OS::Windows::GetSystemMemorySize() / 1024 / 1024; // MB
- j["System"]["Threads"] = std::thread::hardware_concurrency();
- if(mpt::OS::Windows::IsWine())
- {
- mpt::OS::Wine::VersionContext v;
- j["System"]["Windows"]["Wine"]["Version"]["Raw"] = v.RawVersion();
- if(v.Version().IsValid())
- {
- j["System"]["Windows"]["Wine"]["Version"]["Major"] = v.Version().GetMajor();
- j["System"]["Windows"]["Wine"]["Version"]["Minor"] = v.Version().GetMinor();
- j["System"]["Windows"]["Wine"]["Version"]["Update"] = v.Version().GetUpdate();
- }
- j["System"]["Windows"]["Wine"]["HostSysName"] = v.RawHostSysName();
- }
- const SoundDevice::Identifier deviceIdentifier = TrackerSettings::Instance().GetSoundDeviceIdentifier();
- const SoundDevice::Info deviceInfo = theApp.GetSoundDevicesManager()->FindDeviceInfo(deviceIdentifier);
- const SoundDevice::Settings deviceSettings = TrackerSettings::Instance().GetSoundDeviceSettings(deviceIdentifier);
- j["OpenMPT"]["SoundDevice"]["Type"] = deviceInfo.type;
- j["OpenMPT"]["SoundDevice"]["Name"] = deviceInfo.name;
- j["OpenMPT"]["SoundDevice"]["Settings"]["Samplerate"] = deviceSettings.Samplerate;
- j["OpenMPT"]["SoundDevice"]["Settings"]["Latency"] = deviceSettings.Latency;
- j["OpenMPT"]["SoundDevice"]["Settings"]["UpdateInterval"] = deviceSettings.UpdateInterval;
- j["OpenMPT"]["SoundDevice"]["Settings"]["Channels"] = deviceSettings.Channels.GetNumHostChannels();
- j["OpenMPT"]["SoundDevice"]["Settings"]["BoostThreadPriority"] = deviceSettings.BoostThreadPriority;
- j["OpenMPT"]["SoundDevice"]["Settings"]["ExclusiveMode"] = deviceSettings.ExclusiveMode;
- j["OpenMPT"]["SoundDevice"]["Settings"]["UseHardwareTiming"] = deviceSettings.UseHardwareTiming;
- j["OpenMPT"]["SoundDevice"]["Settings"]["KeepDeviceRunning"] = deviceSettings.KeepDeviceRunning;
- #ifdef MPT_ENABLE_ARCH_INTRINSICS
- const CPU::Info CPUInfo = CPU::Info::Get();
- j["System"]["Processor"]["Vendor"] = std::string(mpt::String::ReadAutoBuf(CPUInfo.VendorID));
- j["System"]["Processor"]["Brand"] = std::string(mpt::String::ReadAutoBuf(CPUInfo.BrandID));
- j["System"]["Processor"]["CpuidRaw"] = mpt::afmt::hex0<8>(CPUInfo.CPUID);
- j["System"]["Processor"]["Id"]["Family"] = CPUInfo.Family;
- j["System"]["Processor"]["Id"]["Model"] = CPUInfo.Model;
- j["System"]["Processor"]["Id"]["Stepping"] = CPUInfo.Stepping;
- j["System"]["Processor"]["Features"]["lm"] = ((CPUInfo.AvailableFeatures & CPU::feature::lm) != 0);
- j["System"]["Processor"]["Features"]["mmx"] = ((CPUInfo.AvailableFeatures & CPU::feature::mmx) != 0);
- j["System"]["Processor"]["Features"]["sse"] = ((CPUInfo.AvailableFeatures & CPU::feature::sse) != 0);
- j["System"]["Processor"]["Features"]["sse2"] = ((CPUInfo.AvailableFeatures & CPU::feature::sse2) != 0);
- j["System"]["Processor"]["Features"]["sse3"] = ((CPUInfo.AvailableFeatures & CPU::feature::sse3) != 0);
- j["System"]["Processor"]["Features"]["ssse3"] = ((CPUInfo.AvailableFeatures & CPU::feature::ssse3) != 0);
- j["System"]["Processor"]["Features"]["sse4.1"] = ((CPUInfo.AvailableFeatures & CPU::feature::sse4_1) != 0);
- j["System"]["Processor"]["Features"]["sse4.2"] = ((CPUInfo.AvailableFeatures & CPU::feature::sse4_2) != 0);
- j["System"]["Processor"]["Features"]["avx"] = ((CPUInfo.AvailableFeatures & CPU::feature::avx) != 0);
- j["System"]["Processor"]["Features"]["avx2"] = ((CPUInfo.AvailableFeatures & CPU::feature::avx2) != 0);
- #endif // MPT_ENABLE_ARCH_INTRINSICS
- return j.dump(1, '\t');
- }
- // Run update check (independent thread)
- UpdateCheckResult CUpdateCheck::SearchUpdate(const CUpdateCheck::Context &context, const CUpdateCheck::Settings &settings, const std::string &statistics)
- {
- UpdateCheckResult result;
- {
- if(!context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, 0))
- {
- throw CUpdateCheck::Cancel();
- }
- if(!context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, 10))
- {
- throw CUpdateCheck::Cancel();
- }
- bool loaded = false;
- // try to load cached results before establishing any connection
- if(context.loadPersisted)
- {
- try
- {
- InputFile f(settings.persistencePath + P_("update-") + mpt::PathString::FromUnicode(GetChannelName(settings.channel)) + P_(".json"));
- if(f.IsValid())
- {
- std::vector<std::byte> data = GetFileReader(f).ReadRawDataAsByteVector();
- nlohmann::json::parse(mpt::buffer_cast<std::string>(data)).get<Update::versions>();
- result.CheckTime = time_t{};
- result.json = data;
- loaded = true;
- }
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- } catch(const std::exception &)
- {
- // ignore
- }
- }
- if(!context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, 20))
- {
- throw CUpdateCheck::Cancel();
- }
- if(!loaded)
- {
- HTTP::InternetSession internet(Version::Current().GetOpenMPTVersionString());
- if(!context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, 30))
- {
- throw CUpdateCheck::Cancel();
- }
- result = SearchUpdateModern(internet, settings);
- try
- {
- mpt::SafeOutputFile f(settings.persistencePath + P_("update-") + mpt::PathString::FromUnicode(GetChannelName(settings.channel)) + P_(".json"), std::ios::binary);
- f.stream().imbue(std::locale::classic());
- mpt::IO::WriteRaw(f.stream(), mpt::as_span(result.json));
- f.stream().flush();
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- } catch(const std::exception &)
- {
- // ignore
- }
- if(!context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, 50))
- {
- throw CUpdateCheck::Cancel();
- }
- SendStatistics(internet, settings, statistics);
- if(!context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, 70))
- {
- throw CUpdateCheck::Cancel();
- }
- }
- if(!context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, 90))
- {
- throw CUpdateCheck::Cancel();
- }
- CleanOldUpdates(settings, context);
- if(!context.window->SendMessage(context.msgProgress, context.autoUpdate ? 1 : 0, 100))
- {
- throw CUpdateCheck::Cancel();
- }
- }
- return result;
- }
- void CUpdateCheck::CleanOldUpdates(const CUpdateCheck::Settings & /* settings */ , const CUpdateCheck::Context & /* context */ )
- {
- mpt::PathString dirTemp = mpt::GetTempDirectory();
- if(dirTemp.empty())
- {
- return;
- }
- if(PathIsRelative(dirTemp.AsNative().c_str()))
- {
- return;
- }
- if(!dirTemp.IsDirectory())
- {
- return;
- }
- mpt::PathString dirTempOpenMPT = dirTemp + P_("OpenMPT") + mpt::PathString::FromNative(mpt::RawPathString(1, mpt::PathString::GetDefaultPathSeparator()));
- mpt::PathString dirTempOpenMPTUpdates = dirTempOpenMPT + P_("Updates") + mpt::PathString::FromNative(mpt::RawPathString(1, mpt::PathString::GetDefaultPathSeparator()));
- mpt::DeleteWholeDirectoryTree(dirTempOpenMPTUpdates);
- }
- void CUpdateCheck::SendStatistics(HTTP::InternetSession &internet, const CUpdateCheck::Settings &settings, const std::string &statistics)
- {
- if(settings.sendStatistics)
- {
- HTTP::Request requestStatistics;
- if(settings.statisticsUUID.IsValid())
- {
- requestStatistics.SetURI(ParseURI(settings.apiURL + MPT_UFORMAT("statistics/{}")(settings.statisticsUUID)));
- requestStatistics.method = HTTP::Method::Put;
- } else
- {
- requestStatistics.SetURI(ParseURI(settings.apiURL + U_("statistics/")));
- requestStatistics.method = HTTP::Method::Post;
- }
- requestStatistics.dataMimeType = HTTP::MimeType::JSON();
- requestStatistics.acceptMimeTypes = HTTP::MimeTypes::JSON();
- std::string jsondata = statistics;
- MPT_LOG_GLOBAL(LogInformation, "Update", mpt::ToUnicode(mpt::Charset::UTF8, jsondata));
- requestStatistics.data = mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(jsondata));
- #if defined(MPT_BUILD_RETRO)
- requestSatistics.InsecureTLSDowngradeWindowsXP();
- #endif // MPT_BUILD_RETRO
- internet(requestStatistics);
- }
- }
- UpdateCheckResult CUpdateCheck::SearchUpdateModern(HTTP::InternetSession &internet, const CUpdateCheck::Settings &settings)
- {
- HTTP::Request request;
- request.SetURI(ParseURI(settings.apiURL + MPT_UFORMAT("update/{}")(GetChannelName(static_cast<UpdateChannel>(settings.channel)))));
- request.method = HTTP::Method::Get;
- request.acceptMimeTypes = HTTP::MimeTypes::JSON();
- request.flags = HTTP::NoCache;
- #if defined(MPT_BUILD_RETRO)
- request.InsecureTLSDowngradeWindowsXP();
- #endif // MPT_BUILD_RETRO
- HTTP::Result resultHTTP = internet(request);
- // Retrieve HTTP status code.
- if(resultHTTP.Status >= 400)
- {
- throw CUpdateCheck::Error(MPT_CFORMAT("Version information could not be found on the server (HTTP status code {}). Maybe your version of OpenMPT is too old!")(resultHTTP.Status));
- }
- // Now, evaluate the downloaded data.
- UpdateCheckResult result;
- result.CheckTime = time(nullptr);
- try
- {
- nlohmann::json::parse(mpt::buffer_cast<std::string>(resultHTTP.Data)).get<Update::versions>();
- result.json = resultHTTP.Data;
- } catch(mpt::out_of_memory e)
- {
- mpt::rethrow_out_of_memory(e);
- } catch(const nlohmann::json::exception &e)
- {
- throw CUpdateCheck::Error(MPT_CFORMAT("Could not understand server response ({}). Maybe your version of OpenMPT is too old!")(mpt::get_exception_text<mpt::ustring>(e)));
- }
- return result;
- }
- void CUpdateCheck::CheckForUpdate(const CUpdateCheck::Settings &settings, const CUpdateCheck::Context &context)
- {
- // incremented before starting the thread
- MPT_ASSERT(s_InstanceCount.load() >= 1);
- UpdateCheckResult result;
- try
- {
- context.window->SendMessage(context.msgStart, context.autoUpdate ? 1 : 0, 0);
- try
- {
- result = SearchUpdate(context, settings, context.statistics);
- } catch(const bad_uri &e)
- {
- throw CUpdateCheck::Error(MPT_CFORMAT("Error parsing update URL: {}")(mpt::get_exception_text<mpt::ustring>(e)));
- } catch(const HTTP::exception &e)
- {
- throw CUpdateCheck::Error(CString(_T("HTTP error: ")) + mpt::ToCString(e.GetMessage()));
- }
- } catch(const CUpdateCheck::Cancel &)
- {
- context.window->SendMessage(context.msgCanceled, context.autoUpdate ? 1 : 0, 0);
- s_InstanceCount.fetch_sub(1);
- MPT_ASSERT(s_InstanceCount.load() >= 0);
- return;
- } catch(const CUpdateCheck::Error &e)
- {
- context.window->SendMessage(context.msgFailure, context.autoUpdate ? 1 : 0, reinterpret_cast<LPARAM>(&e));
- s_InstanceCount.fetch_sub(1);
- MPT_ASSERT(s_InstanceCount.load() >= 0);
- return;
- }
- context.window->SendMessage(context.msgSuccess, context.autoUpdate ? 1 : 0, reinterpret_cast<LPARAM>(&result));
- s_InstanceCount.fetch_sub(1);
- MPT_ASSERT(s_InstanceCount.load() >= 0);
- }
- bool CUpdateCheck::IsAutoUpdateFromMessage(WPARAM wparam, LPARAM /* lparam */ )
- {
- return wparam ? true : false;
- }
- const UpdateCheckResult &CUpdateCheck::MessageAsResult(WPARAM /* wparam */ , LPARAM lparam)
- {
- return *reinterpret_cast<UpdateCheckResult *>(lparam);
- }
- const CUpdateCheck::Error &CUpdateCheck::MessageAsError(WPARAM /* wparam */ , LPARAM lparam)
- {
- return *reinterpret_cast<CUpdateCheck::Error*>(lparam);
- }
- static const char * const updateScript = R"vbs(
- Wscript.Echo
- Wscript.Echo "OpenMPT portable Update"
- Wscript.Echo "======================="
- Wscript.Echo "[ 0%] Waiting for OpenMPT to close..."
- WScript.Sleep 2000
- Wscript.Echo "[ 10%] Loading update settings..."
- zip = WScript.Arguments.Item(0)
- subfolder = WScript.Arguments.Item(1)
- dst = WScript.Arguments.Item(2)
- restartbinary = WScript.Arguments.Item(3)
- Wscript.Echo "[ 20%] Preparing update..."
- Set fso = CreateObject("Scripting.FileSystemObject")
- Set shell = CreateObject("Wscript.Shell")
- Set application = CreateObject("Shell.Application")
- Sub CreateFolder(pathname)
- If Not fso.FolderExists(pathname) Then
- fso.CreateFolder pathname
- End If
- End Sub
- Sub DeleteFolder(pathname)
- If fso.FolderExists(pathname) Then
- fso.DeleteFolder pathname
- End If
- End Sub
- Sub UnZIP(zipfilename, destinationfolder)
- If Not fso.FolderExists(destinationfolder) Then
- fso.CreateFolder(destinationfolder)
- End If
- application.NameSpace(destinationfolder).Copyhere application.NameSpace(zipfilename).Items, 16+256
- End Sub
- Wscript.Echo "[ 30%] Changing to temporary directory..."
- shell.CurrentDirectory = fso.GetParentFolderName(WScript.ScriptFullName)
- Wscript.Echo "[ 40%] Decompressing update..."
- UnZIP zip, fso.BuildPath(fso.GetAbsolutePathName("."), "tmp")
- Wscript.Echo "[ 60%] Installing update..."
- If subfolder = "" Or subfolder = "." Then
- fso.CopyFolder fso.BuildPath(fso.GetAbsolutePathName("."), "tmp"), dst, True
- Else
- fso.CopyFolder fso.BuildPath(fso.BuildPath(fso.GetAbsolutePathName("."), "tmp"), subfolder), dst, True
- End If
- Wscript.Echo "[ 80%] Deleting temporary directory..."
- DeleteFolder fso.BuildPath(fso.GetAbsolutePathName("."), "tmp")
- Wscript.Echo "[ 90%] Restarting OpenMPT..."
- application.ShellExecute fso.BuildPath(dst, restartbinary), , dst, , 10
- Wscript.Echo "[100%] Update successful!"
- Wscript.Echo
- WScript.Sleep 1000
- Wscript.Echo "Closing update window in 5 seconds..."
- WScript.Sleep 1000
- Wscript.Echo "Closing update window in 4 seconds..."
- WScript.Sleep 1000
- Wscript.Echo "Closing update window in 3 seconds..."
- WScript.Sleep 1000
- Wscript.Echo "Closing update window in 2 seconds..."
- WScript.Sleep 1000
- Wscript.Echo "Closing update window in 1 seconds..."
- WScript.Sleep 1000
- Wscript.Echo "Closing update window..."
- WScript.Quit
- )vbs";
- class CDoUpdate: public CProgressDialog
- {
- private:
- Update::download download;
- class Aborted : public std::exception {};
- class Warning : public std::exception
- {
- private:
- mpt::ustring msg;
- public:
- Warning(const mpt::ustring &msg_)
- : msg(msg_)
- {
- return;
- }
- mpt::ustring get_msg() const
- {
- return msg;
- }
- };
- class Error : public std::exception
- {
- private:
- mpt::ustring msg;
- public:
- Error(const mpt::ustring &msg_)
- : msg(msg_)
- {
- return;
- }
- mpt::ustring get_msg() const
- {
- return msg;
- }
- };
- public:
- CDoUpdate(Update::download download, CWnd *parent = nullptr)
- : CProgressDialog(parent)
- , download(download)
- {
- return;
- }
- void UpdateProgress(const CString &text, double percent)
- {
- SetText(text);
- SetProgress(static_cast<uint64>(percent * 100.0));
- ProcessMessages();
- if(m_abort)
- {
- throw Aborted();
- }
- }
- void Run() override
- {
- try
- {
- SetTitle(_T("OpenMPT Update"));
- SetAbortText(_T("Cancel"));
- SetText(_T("OpenMPT Update"));
- SetRange(0, 10000);
- ProcessMessages();
- Update::downloadinfo downloadinfo;
- mpt::PathString dirTempOpenMPTUpdates;
- mpt::PathString updateFilename;
- {
- UpdateProgress(_T("Connecting..."), 0.0);
- HTTP::InternetSession internet(Version::Current().GetOpenMPTVersionString());
- UpdateProgress(_T("Downloading update information..."), 1.0);
- std::vector<std::byte> rawDownloadInfo;
- {
- HTTP::Request request;
- request.SetURI(ParseURI(download.url));
- request.method = HTTP::Method::Get;
- request.acceptMimeTypes = HTTP::MimeTypes::JSON();
- #if defined(MPT_BUILD_RETRO)
- request.InsecureTLSDowngradeWindowsXP();
- #endif // MPT_BUILD_RETRO
- HTTP::Result resultHTTP = internet(request);
- if(resultHTTP.Status != 200)
- {
- throw Error(MPT_UFORMAT("Error downloading update information: HTTP status {}.")(resultHTTP.Status));
- }
- rawDownloadInfo = std::move(resultHTTP.Data);
- }
- if(!TrackerSettings::Instance().UpdateSkipSignatureVerificationUNSECURE)
- {
- std::vector<std::byte> rawSignature;
- UpdateProgress(_T("Retrieving update signature..."), 2.0);
- {
- HTTP::Request request;
- request.SetURI(ParseURI(download.url + U_(".jws.json")));
- request.method = HTTP::Method::Get;
- request.acceptMimeTypes = HTTP::MimeTypes::JSON();
- #if defined(MPT_BUILD_RETRO)
- request.InsecureTLSDowngradeWindowsXP();
- #endif // MPT_BUILD_RETRO
- HTTP::Result resultHTTP = internet(request);
- if(resultHTTP.Status != 200)
- {
- throw Error(MPT_UFORMAT("Error downloading update signature: HTTP status {}.")(resultHTTP.Status));
- }
- rawSignature = std::move(resultHTTP.Data);
- }
- UpdateProgress(_T("Retrieving update signing public keys..."), 3.0);
- std::vector<mpt::crypto::asymmetric::rsassa_pss<>::public_key> keys;
- {
- std::vector<mpt::ustring> keyAnchors = TrackerSettings::Instance().UpdateSigningKeysRootAnchors;
- if(keyAnchors.empty())
- {
- Reporting::Warning(U_("Warning: No update signing public key root anchors configured. Update cannot be verified."), U_("OpenMPT Update"));
- }
- for(const auto & keyAnchor : keyAnchors)
- {
- HTTP::Request request;
- request.SetURI(ParseURI(keyAnchor + U_("signingkeys.jwkset.json")));
- request.method = HTTP::Method::Get;
- request.flags = HTTP::NoCache;
- request.acceptMimeTypes = HTTP::MimeTypes::JSON();
- try
- {
- #if defined(MPT_BUILD_RETRO)
- request.InsecureTLSDowngradeWindowsXP();
- #endif // MPT_BUILD_RETRO
- HTTP::Result resultHTTP = internet(request);
- resultHTTP.CheckStatus(200);
- mpt::append(keys, mpt::crypto::asymmetric::rsassa_pss<>::parse_jwk_set(mpt::ToUnicode(mpt::Charset::UTF8, mpt::buffer_cast<std::string>(resultHTTP.Data))));
- } catch(mpt::out_of_memory e)
- {
- mpt::rethrow_out_of_memory(e);
- } catch(const std::exception &e)
- {
- Reporting::Warning(MPT_UFORMAT("Warning: Retrieving update signing public keys from {} failed: {}")(keyAnchor, mpt::get_exception_text<mpt::ustring>(e)), U_("OpenMPT Update"));
- } catch(...)
- {
- Reporting::Warning(MPT_UFORMAT("Warning: Retrieving update signing public keys from {} failed.")(keyAnchor), U_("OpenMPT Update"));
- }
- }
- }
- if(keys.empty())
- {
- throw Error(U_("Error retrieving update signing public keys."));
- }
- UpdateProgress(_T("Verifying signature..."), 4.0);
- std::vector<std::byte> expectedPayload = mpt::buffer_cast<std::vector<std::byte>>(rawDownloadInfo);
- mpt::ustring signature = mpt::ToUnicode(mpt::Charset::UTF8, mpt::buffer_cast<std::string>(rawSignature));
- mpt::crypto::asymmetric::rsassa_pss<>::jws_verify_at_least_one(keys, expectedPayload, signature);
-
- }
- UpdateProgress(_T("Parsing update information..."), 5.0);
- try
- {
- downloadinfo = nlohmann::json::parse(mpt::buffer_cast<std::string>(rawDownloadInfo)).get<Update::downloadinfo>();
- } catch(const nlohmann::json::exception &e)
- {
- throw Error(MPT_UFORMAT("Error parsing update information: {}.")(mpt::get_exception_text<mpt::ustring>(e)));
- }
- UpdateProgress(_T("Preparing download..."), 6.0);
- mpt::PathString dirTemp = mpt::GetTempDirectory();
- mpt::PathString dirTempOpenMPT = dirTemp + P_("OpenMPT") + mpt::PathString::FromNative(mpt::RawPathString(1, mpt::PathString::GetDefaultPathSeparator()));
- dirTempOpenMPTUpdates = dirTempOpenMPT + P_("Updates") + mpt::PathString::FromNative(mpt::RawPathString(1, mpt::PathString::GetDefaultPathSeparator()));
- updateFilename = dirTempOpenMPTUpdates + mpt::PathString::FromUnicode(downloadinfo.filename);
- ::CreateDirectory(dirTempOpenMPT.AsNativePrefixed().c_str(), NULL);
- ::CreateDirectory(dirTempOpenMPTUpdates.AsNativePrefixed().c_str(), NULL);
-
- {
-
- UpdateProgress(_T("Creating file..."), 7.0);
- mpt::SafeOutputFile file(updateFilename, std::ios::binary);
- file.stream().imbue(std::locale::classic());
- file.stream().exceptions(std::ios::failbit | std::ios::badbit);
-
- UpdateProgress(_T("Downloading update..."), 8.0);
- HTTP::Request request;
- request.SetURI(ParseURI(downloadinfo.url));
- request.method = HTTP::Method::Get;
- request.acceptMimeTypes = HTTP::MimeTypes::Binary();
- request.outputStream = &file.stream();
- request.progressCallback = [&](HTTP::Progress progress, uint64 transferred, std::optional<uint64> expectedSize) {
- switch(progress)
- {
- case HTTP::Progress::Start:
- SetProgress(900);
- break;
- case HTTP::Progress::ConnectionEstablished:
- SetProgress(1000);
- break;
- case HTTP::Progress::RequestOpened:
- SetProgress(1100);
- break;
- case HTTP::Progress::RequestSent:
- SetProgress(1200);
- break;
- case HTTP::Progress::ResponseReceived:
- SetProgress(1300);
- break;
- case HTTP::Progress::TransferBegin:
- SetProgress(1400);
- break;
- case HTTP::Progress::TransferRunning:
- if(expectedSize && ((*expectedSize) != 0))
- {
- SetProgress(static_cast<int64>((static_cast<double>(transferred) / static_cast<double>(*expectedSize)) * (10000.0-1500.0-400.0) + 1500.0));
- } else
- {
- SetProgress((1500 + 9600) / 2);
- }
- break;
- case HTTP::Progress::TransferDone:
- SetProgress(9600);
- break;
- }
- ProcessMessages();
- if(m_abort)
- {
- throw HTTP::Abort();
- }
- };
- #if defined(MPT_BUILD_RETRO)
- request.InsecureTLSDowngradeWindowsXP();
- #endif // MPT_BUILD_RETRO
- HTTP::Result resultHTTP = internet(request);
- if(resultHTTP.Status != 200)
- {
- throw Error(MPT_UFORMAT("Error downloading update: HTTP status {}.")(resultHTTP.Status));
- }
- }
- UpdateProgress(_T("Disconnecting..."), 97.0);
- }
- UpdateProgress(_T("Verifying download..."), 98.0);
- bool verified = false;
- for(const auto & [algorithm, value] : downloadinfo.checksums)
- {
- if(algorithm == U_("SHA-512"))
- {
- std::vector<std::byte> binhash = mpt::decode_hex(value);
- if(binhash.size() != 512/8)
- {
- throw Error(U_("Download verification failed."));
- }
- std::array<std::byte, 512/8> expected;
- std::copy(binhash.begin(), binhash.end(), expected.begin());
- mpt::crypto::hash::SHA512 hash;
- mpt::ifstream f(updateFilename, std::ios::binary);
- f.imbue(std::locale::classic());
- f.exceptions(std::ios::badbit);
- while(!mpt::IO::IsEof(f))
- {
- std::array<std::byte, mpt::IO::BUFFERSIZE_TINY> buf;
- hash.process(mpt::IO::ReadRaw(f, mpt::as_span(buf)));
- }
- std::array<std::byte, 512/8> gotten = hash.result();
- if(gotten != expected)
- {
- throw Error(U_("Download verification failed."));
- }
- verified = true;
- }
- }
- if(!verified)
- {
- throw Error(U_("Error verifying update: No suitable checksum found."));
- }
- UpdateProgress(_T("Installing update..."), 99.0);
- bool wantClose = false;
- if(download.can_autoupdate && (Version::Current() >= Version::Parse(download.autoupdate_minversion)))
- {
- if(download.type == U_("installer") && downloadinfo.autoupdate_installer)
- {
- if(theApp.IsSourceTreeMode())
- {
- throw Warning(MPT_UFORMAT("Refusing to launch update '{} {}' when running from source tree.")(updateFilename, mpt::String::Combine(downloadinfo.autoupdate_installer->arguments, U_(" "))));
- }
- if(reinterpret_cast<INT_PTR>(ShellExecute(NULL, NULL,
- updateFilename.AsNative().c_str(),
- mpt::ToWin(mpt::String::Combine(downloadinfo.autoupdate_installer->arguments, U_(" "))).c_str(),
- dirTempOpenMPTUpdates.AsNative().c_str(),
- SW_SHOWDEFAULT)) < 32)
- {
- throw Error(U_("Error launching update."));
- }
- } else if(download.type == U_("archive") && downloadinfo.autoupdate_archive)
- {
- try
- {
- mpt::SafeOutputFile file(dirTempOpenMPTUpdates + P_("update.vbs"), std::ios::binary);
- file.stream().imbue(std::locale::classic());
- file.stream().exceptions(std::ios::failbit | std::ios::badbit);
- mpt::IO::WriteRaw(file.stream(), mpt::as_span(std::string(updateScript)));
- } catch(...)
- {
- throw Error(U_("Error creating update script."));
- }
- std::vector<mpt::ustring> arguments;
- arguments.push_back(U_("\"") + (dirTempOpenMPTUpdates + P_("update.vbs")).ToUnicode() + U_("\""));
- arguments.push_back(U_("\"") + updateFilename.ToUnicode() + U_("\""));
- arguments.push_back(U_("\"") + (downloadinfo.autoupdate_archive->subfolder.empty() ? U_(".") : downloadinfo.autoupdate_archive->subfolder) + U_("\""));
- arguments.push_back(U_("\"") + theApp.GetInstallPath().WithoutTrailingSlash().ToUnicode() + U_("\""));
- arguments.push_back(U_("\"") + downloadinfo.autoupdate_archive->restartbinary + U_("\""));
- if(theApp.IsSourceTreeMode())
- {
- throw Warning(MPT_UFORMAT("Refusing to launch update '{} {}' when running from source tree.")(P_("cscript.exe"), mpt::String::Combine(arguments, U_(" "))));
- }
- if(reinterpret_cast<INT_PTR>(ShellExecute(NULL, NULL,
- P_("cscript.exe").AsNative().c_str(),
- mpt::ToWin(mpt::String::Combine(arguments, U_(" "))).c_str(),
- dirTempOpenMPTUpdates.AsNative().c_str(),
- SW_SHOWDEFAULT)) < 32)
- {
- throw Error(U_("Error launching update."));
- }
- wantClose = true;
- } else
- {
- CTrackApp::OpenDirectory(dirTempOpenMPTUpdates);
- wantClose = true;
- }
- } else
- {
- CTrackApp::OpenDirectory(dirTempOpenMPTUpdates);
- wantClose = true;
- }
- UpdateProgress(_T("Waiting for installer..."), 100.0);
- if(wantClose)
- {
- CMainFrame::GetMainFrame()->PostMessage(WM_QUIT, 0, 0);
- }
- EndDialog(IDOK);
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- Reporting::Error(U_("Not enough memory to install update."), U_("OpenMPT Update Error"));
- EndDialog(IDCANCEL);
- return;
- } catch(const HTTP::Abort &)
- {
- EndDialog(IDCANCEL);
- return;
- } catch(const Aborted &)
- {
- EndDialog(IDCANCEL);
- return;
- } catch(const Warning &e)
- {
- Reporting::Warning(e.get_msg(), U_("OpenMPT Update"));
- EndDialog(IDCANCEL);
- return;
- } catch(const Error &e)
- {
- Reporting::Error(e.get_msg(), U_("OpenMPT Update Error"));
- EndDialog(IDCANCEL);
- return;
- } catch(const std::exception &e)
- {
- Reporting::Error(MPT_UFORMAT("Error installing update: {}")(mpt::get_exception_text<mpt::ustring>(e)), U_("OpenMPT Update Error"));
- EndDialog(IDCANCEL);
- return;
- } catch(...)
- {
- Reporting::Error(U_("Error installing update."), U_("OpenMPT Update Error"));
- EndDialog(IDCANCEL);
- return;
- }
- }
- };
- void CUpdateCheck::AcknowledgeSuccess(const UpdateCheckResult &result)
- {
- if(!result.IsFromCache())
- {
- TrackerSettings::Instance().UpdateLastUpdateCheck = mpt::Date::Unix(result.CheckTime);
- }
- }
- void CUpdateCheck::ShowSuccessGUI(const bool autoUpdate, const UpdateCheckResult &result)
- {
- bool modal = !autoUpdate;
- Update::versions updateData = nlohmann::json::parse(mpt::buffer_cast<std::string>(result.json)).get<Update::versions>();
- UpdateInfo updateInfo = GetBestDownload(updateData);
- if(!updateInfo.IsAvailable())
- {
- if(modal)
- {
- Reporting::Information(U_("You already have the latest version of OpenMPT installed."), U_("OpenMPT Update"));
- }
- return;
- }
- auto &versionInfo = updateData[updateInfo.version];
- if(autoUpdate && (mpt::ToCString(versionInfo.version) == TrackerSettings::Instance().UpdateIgnoreVersion))
- {
- return;
- }
- if(autoUpdate && TrackerSettings::Instance().UpdateInstallAutomatically && !updateInfo.download.empty() && versionInfo.downloads[updateInfo.download].can_autoupdate && (Version::Current() >= Version::Parse(versionInfo.downloads[updateInfo.download].autoupdate_minversion)))
- {
- CDoUpdate updateDlg(versionInfo.downloads[updateInfo.download], theApp.GetMainWnd());
- if(updateDlg.DoModal() != IDOK)
- {
- return;
- }
- } else
- {
- const TCHAR *action = _T("&View Announcement");
- const bool canInstall = mpt::OS::Windows::IsOriginal() && !updateInfo.download.empty() && versionInfo.downloads[updateInfo.download].can_autoupdate && (Version::Current() >= Version::Parse(versionInfo.downloads[updateInfo.download].autoupdate_minversion));
- const bool canDownload = !canInstall && !updateInfo.download.empty() && !versionInfo.downloads[updateInfo.download].download_url.empty();
- if(canInstall)
- {
- action = _T("&Install Now");
- } else if(canDownload)
- {
- action = _T("&Download Now");
- }
- // always show indicator, do not highlight it with a tooltip if we show a modal window later or when it is a cached result
- if(!CMainFrame::GetMainFrame()->ShowUpdateIndicator(result, mpt::ToCString(versionInfo.version), mpt::ToCString(versionInfo.announcement_url), !modal && !result.IsFromCache()))
- {
- // on failure to show indicator, continue and show modal dialog
- modal = true;
- }
- if(!modal)
- {
- return;
- }
- UpdateDialog dlg(
- mpt::ToCString(versionInfo.version),
- mpt::ToCString(versionInfo.date),
- mpt::ToCString(versionInfo.announcement_url),
- action);
- if(dlg.DoModal() != IDOK)
- {
- return;
- }
- if(canInstall)
- {
- CDoUpdate updateDlg(versionInfo.downloads[updateInfo.download], theApp.GetMainWnd());
- if(updateDlg.DoModal() != IDOK)
- {
- return;
- }
- } else if(canDownload)
- {
- CTrackApp::OpenURL(versionInfo.downloads[updateInfo.download].download_url);
- } else
- {
- CTrackApp::OpenURL(versionInfo.announcement_url);
- }
- }
- }
- void CUpdateCheck::ShowFailureGUI(const bool autoUpdate, const CUpdateCheck::Error &error)
- {
- if(!autoUpdate)
- {
- Reporting::Error(error.GetMessage(), _T("OpenMPT Update Error"));
- }
- }
- CUpdateCheck::Error::Error(CString errorMessage)
- : std::runtime_error(mpt::ToCharset(mpt::CharsetException, errorMessage))
- , m_Message(errorMessage)
- {
- return;
- }
- CUpdateCheck::Error::Error(CString errorMessage, DWORD errorCode)
- : std::runtime_error(mpt::ToCharset(mpt::CharsetException, FormatErrorCode(errorMessage, errorCode)))
- , m_Message(errorMessage)
- {
- return;
- }
- CString CUpdateCheck::Error::GetMessage() const
- {
- return m_Message;
- }
- CString CUpdateCheck::Error::FormatErrorCode(CString errorMessage, DWORD errorCode)
- {
- errorMessage += mpt::ToCString(mpt::windows::GetErrorMessage(errorCode, GetModuleHandle(TEXT("wininet.dll"))));
- return errorMessage;
- }
- CUpdateCheck::Cancel::Cancel()
- {
- return;
- }
- /////////////////////////////////////////////////////////////
- // CUpdateSetupDlg
- BEGIN_MESSAGE_MAP(CUpdateSetupDlg, CPropertyPage)
- ON_COMMAND(IDC_CHECK_UPDATEENABLED, &CUpdateSetupDlg::OnSettingsChanged)
- ON_COMMAND(IDC_RADIO1, &CUpdateSetupDlg::OnSettingsChanged)
- ON_COMMAND(IDC_RADIO2, &CUpdateSetupDlg::OnSettingsChanged)
- ON_COMMAND(IDC_RADIO3, &CUpdateSetupDlg::OnSettingsChanged)
- ON_COMMAND(IDC_BUTTON1, &CUpdateSetupDlg::OnCheckNow)
- ON_CBN_SELCHANGE(IDC_COMBO_UPDATEFREQUENCY, &CUpdateSetupDlg::OnSettingsChanged)
- ON_COMMAND(IDC_CHECK_UPDATEINSTALLAUTOMATICALLY, &CUpdateSetupDlg::OnSettingsChanged)
- ON_COMMAND(IDC_CHECK1, &CUpdateSetupDlg::OnSettingsChanged)
- ON_NOTIFY(NM_CLICK, IDC_SYSLINK1, &CUpdateSetupDlg::OnShowStatisticsData)
- END_MESSAGE_MAP()
- CUpdateSetupDlg::CUpdateSetupDlg()
- : CPropertyPage(IDD_OPTIONS_UPDATE)
- , m_SettingChangedNotifyGuard(theApp.GetSettings(), TrackerSettings::Instance().UpdateLastUpdateCheck.GetPath())
- {
- return;
- }
- void CUpdateSetupDlg::DoDataExchange(CDataExchange *pDX)
- {
- CDialog::DoDataExchange(pDX);
- DDX_Control(pDX, IDC_COMBO_UPDATEFREQUENCY, m_CbnUpdateFrequency);
- }
- BOOL CUpdateSetupDlg::OnInitDialog()
- {
- CPropertyPage::OnInitDialog();
- CheckDlgButton(IDC_CHECK_UPDATEENABLED, TrackerSettings::Instance().UpdateEnabled ? BST_CHECKED : BST_UNCHECKED);
- int radioID = 0;
- uint32 updateChannel = TrackerSettings::Instance().UpdateChannel;
- if(updateChannel == UpdateChannelRelease)
- {
- radioID = IDC_RADIO1;
- } else if(updateChannel == UpdateChannelNext)
- {
- radioID = IDC_RADIO2;
- } else if(updateChannel == UpdateChannelDevelopment)
- {
- radioID = IDC_RADIO3;
- } else
- {
- radioID = IDC_RADIO1;
- }
- CheckRadioButton(IDC_RADIO1, IDC_RADIO3, radioID);
- int32 periodDays = TrackerSettings::Instance().UpdateIntervalDays;
- int ndx;
- ndx = m_CbnUpdateFrequency.AddString(_T("always"));
- m_CbnUpdateFrequency.SetItemData(ndx, 0);
- if(periodDays >= 0)
- {
- m_CbnUpdateFrequency.SetCurSel(ndx);
- }
- ndx = m_CbnUpdateFrequency.AddString(_T("daily"));
- m_CbnUpdateFrequency.SetItemData(ndx, 1);
- if(periodDays >= 1)
- {
- m_CbnUpdateFrequency.SetCurSel(ndx);
- }
- ndx = m_CbnUpdateFrequency.AddString(_T("weekly"));
- m_CbnUpdateFrequency.SetItemData(ndx, 7);
- if(periodDays >= 7)
- {
- m_CbnUpdateFrequency.SetCurSel(ndx);
- }
- ndx = m_CbnUpdateFrequency.AddString(_T("monthly"));
- m_CbnUpdateFrequency.SetItemData(ndx, 30);
- if(periodDays >= 30)
- {
- m_CbnUpdateFrequency.SetCurSel(ndx);
- }
- ndx = m_CbnUpdateFrequency.AddString(_T("never"));
- m_CbnUpdateFrequency.SetItemData(ndx, ~(DWORD_PTR)0);
- if(periodDays < 0)
- {
- m_CbnUpdateFrequency.SetCurSel(ndx);
- }
- CheckDlgButton(IDC_CHECK_UPDATEINSTALLAUTOMATICALLY, TrackerSettings::Instance().UpdateInstallAutomatically ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(IDC_CHECK1, TrackerSettings::Instance().UpdateStatistics ? BST_CHECKED : BST_UNCHECKED);
- GetDlgItem(IDC_STATIC_UPDATEPRIVACYTEXT)->SetWindowText(mpt::ToCString(CUpdateCheck::GetStatisticsUserInformation(true)));
- EnableDisableDialog();
- m_SettingChangedNotifyGuard.Register(this);
- SettingChanged(TrackerSettings::Instance().UpdateLastUpdateCheck.GetPath());
- return TRUE;
- }
- void CUpdateSetupDlg::OnShowStatisticsData(NMHDR * /*pNMHDR*/, LRESULT * /*pResult*/)
- {
- CUpdateCheck::Settings settings;
- uint32 updateChannel = TrackerSettings::Instance().UpdateChannel;
- const int channelRadio = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO3);
- if(channelRadio == IDC_RADIO1) updateChannel = UpdateChannelRelease;
- if(channelRadio == IDC_RADIO2) updateChannel = UpdateChannelNext;
- if(channelRadio == IDC_RADIO3) updateChannel = UpdateChannelDevelopment;
- int updateCheckPeriod = (m_CbnUpdateFrequency.GetItemData(m_CbnUpdateFrequency.GetCurSel()) == ~(DWORD_PTR)0) ? -1 : static_cast<int>(m_CbnUpdateFrequency.GetItemData(m_CbnUpdateFrequency.GetCurSel()));
- settings.periodDays = updateCheckPeriod;
- settings.channel = static_cast<UpdateChannel>(updateChannel);
- settings.sendStatistics = (IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED);
- mpt::ustring statistics;
- statistics += U_("Update:") + UL_("\n");
- statistics += UL_("\n");
- {
- statistics += U_("GET ") + settings.apiURL + MPT_UFORMAT("update/{}")(GetChannelName(static_cast<UpdateChannel>(settings.channel))) + UL_("\n");
- statistics += UL_("\n");
- std::vector<mpt::ustring> keyAnchors = TrackerSettings::Instance().UpdateSigningKeysRootAnchors;
- for(const auto & keyAnchor : keyAnchors)
- {
- statistics += U_("GET ") + keyAnchor + U_("signingkeys.jwkset.json") + UL_("\n");
- statistics += UL_("\n");
- }
- }
- if(settings.sendStatistics)
- {
- statistics += U_("Statistics:") + UL_("\n");
- statistics += UL_("\n");
- if(settings.statisticsUUID.IsValid())
- {
- statistics += U_("PUT ") + settings.apiURL + MPT_UFORMAT("statistics/{}")(settings.statisticsUUID) + UL_("\n");
- } else
- {
- statistics += U_("POST ") + settings.apiURL + U_("statistics/") + UL_("\n");
- }
- statistics += mpt::String::Replace(mpt::ToUnicode(mpt::Charset::UTF8, CUpdateCheck::GetStatisticsDataV3(settings)), U_("\t"), U_(" "));
- }
- InfoDialog dlg(this);
- dlg.SetCaption(_T("Update Statistics Data"));
- dlg.SetContent(mpt::ToWin(mpt::String::Replace(statistics, U_("\n"), U_("\r\n"))));
- dlg.DoModal();
- }
- void CUpdateSetupDlg::SettingChanged(const SettingPath &changedPath)
- {
- if(changedPath == TrackerSettings::Instance().UpdateLastUpdateCheck.GetPath())
- {
- CString updateText;
- const time_t t = TrackerSettings::Instance().UpdateLastUpdateCheck.Get();
- if(t > 0)
- {
- const tm* const lastUpdate = localtime(&t);
- if(lastUpdate != nullptr)
- {
- updateText.Format(_T("The last successful update check was run on %04d-%02d-%02d, %02d:%02d."), lastUpdate->tm_year + 1900, lastUpdate->tm_mon + 1, lastUpdate->tm_mday, lastUpdate->tm_hour, lastUpdate->tm_min);
- }
- }
- updateText += _T("\r\n");
- SetDlgItemText(IDC_LASTUPDATE, updateText);
- }
- }
- void CUpdateSetupDlg::EnableDisableDialog()
- {
- BOOL status = ((IsDlgButtonChecked(IDC_CHECK_UPDATEENABLED) != BST_UNCHECKED) ? TRUE : FALSE);
- GetDlgItem(IDC_STATIC_UDATECHANNEL)->EnableWindow(status);
- GetDlgItem(IDC_RADIO1)->EnableWindow(status);
- GetDlgItem(IDC_RADIO2)->EnableWindow(status);
- GetDlgItem(IDC_RADIO3)->EnableWindow(status);
- GetDlgItem(IDC_STATIC_UPDATECHECK)->EnableWindow(status);
- GetDlgItem(IDC_STATIC_UPDATEFREQUENCY)->EnableWindow(status);
- GetDlgItem(IDC_COMBO_UPDATEFREQUENCY)->EnableWindow(status);
- GetDlgItem(IDC_BUTTON1)->EnableWindow(status);
- GetDlgItem(IDC_LASTUPDATE)->EnableWindow(status);
- GetDlgItem(IDC_CHECK_UPDATEINSTALLAUTOMATICALLY)->EnableWindow(status);
- GetDlgItem(IDC_STATIC_UPDATEPRIVACY)->EnableWindow(status);
- GetDlgItem(IDC_CHECK1)->EnableWindow(status);
- GetDlgItem(IDC_STATIC_UPDATEPRIVACYTEXT)->EnableWindow(status);
- GetDlgItem(IDC_SYSLINK1)->EnableWindow(status);
- }
- void CUpdateSetupDlg::OnSettingsChanged()
- {
- EnableDisableDialog();
- SetModified(TRUE);
- }
- void CUpdateSetupDlg::OnOK()
- {
- uint32 updateChannel = TrackerSettings::Instance().UpdateChannel;
- const int channelRadio = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO3);
- if(channelRadio == IDC_RADIO1) updateChannel = UpdateChannelRelease;
- if(channelRadio == IDC_RADIO2) updateChannel = UpdateChannelNext;
- if(channelRadio == IDC_RADIO3) updateChannel = UpdateChannelDevelopment;
- int updateCheckPeriod = (m_CbnUpdateFrequency.GetItemData(m_CbnUpdateFrequency.GetCurSel()) == ~(DWORD_PTR)0) ? -1 : static_cast<int>(m_CbnUpdateFrequency.GetItemData(m_CbnUpdateFrequency.GetCurSel()));
-
- TrackerSettings::Instance().UpdateEnabled = (IsDlgButtonChecked(IDC_CHECK_UPDATEENABLED) != BST_UNCHECKED);
- TrackerSettings::Instance().UpdateIntervalDays = updateCheckPeriod;
- TrackerSettings::Instance().UpdateInstallAutomatically = (IsDlgButtonChecked(IDC_CHECK_UPDATEINSTALLAUTOMATICALLY) != BST_UNCHECKED);
- TrackerSettings::Instance().UpdateChannel = updateChannel;
- TrackerSettings::Instance().UpdateStatistics = (IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED);
-
- CPropertyPage::OnOK();
- }
- BOOL CUpdateSetupDlg::OnSetActive()
- {
- CMainFrame::m_nLastOptionsPage = OPTIONS_PAGE_UPDATE;
- return CPropertyPage::OnSetActive();
- }
- void CUpdateSetupDlg::OnCheckNow()
- {
- CUpdateCheck::DoManualUpdateCheck();
- }
- #endif // MPT_ENABLE_UPDATE
- OPENMPT_NAMESPACE_END
|