diff --git a/src/dll_api.cpp b/src/dll_api.cpp index b665f618..35af0ae0 100644 --- a/src/dll_api.cpp +++ b/src/dll_api.cpp @@ -46,70 +46,35 @@ extern "C" WIN_SPARKLE_API void __cdecl win_sparkle_init() { - try - { - // finish initialization - if (!Settings::GetLanguage().IsOk()) - { - LANGID lang = 0; - if (IsWindowsVistaOrGreater()) - { - auto f_GetThreadUILanguage = LOAD_DYNAMIC_FUNC(GetThreadUILanguage, kernel32); - if (f_GetThreadUILanguage) - lang = f_GetThreadUILanguage(); - } - if (PRIMARYLANGID(lang) == 0) - { - lang = LANGIDFROMLCID(GetThreadLocale()); - } - if (PRIMARYLANGID(lang) != 0) - Settings::SetLanguage(lang); - } - - // first things first - UpdateDownloader::CleanLeftovers(); - - // check for updates - bool checkUpdates; - if ( Settings::ReadConfigValue("CheckForUpdates", checkUpdates) ) - { - if ( checkUpdates ) - { - static const time_t ONE_DAY = 60*60*24; - - time_t lastCheck = 0; - Settings::ReadConfigValue("LastCheckTime", lastCheck); - const time_t currentTime = time(NULL); - - // Only check for updates in reasonable intervals: - const int interval = win_sparkle_get_update_check_interval(); - if ( currentTime - lastCheck >= interval ) - { - // Run the check in background. Only show UI if updates - // are available. - UpdateChecker *check = new UpdateChecker(); - check->Start(); - } - } - } - else // not yet configured - { - bool didRunOnce; - Settings::ReadConfigValue("DidRunOnce", didRunOnce, false); - if ( !didRunOnce ) - { - // Do nothing on the first execution of the app, for better - // first-time impression. - Settings::WriteConfigValue("DidRunOnce", true); - } - else - { - // Only when the app is launched for the second time, ask the - // user for their permission to check for updates. - UI::AskForPermission(); - } - } - } + try + { + // finish initialization + if (!Settings::GetLanguage().IsOk()) + { + LANGID lang = 0; + if (IsWindowsVistaOrGreater()) + { + auto f_GetThreadUILanguage = LOAD_DYNAMIC_FUNC(GetThreadUILanguage, kernel32); + if (f_GetThreadUILanguage) + lang = f_GetThreadUILanguage(); + } + if (PRIMARYLANGID(lang) == 0) + { + lang = LANGIDFROMLCID(GetThreadLocale()); + } + if (PRIMARYLANGID(lang) != 0) + Settings::SetLanguage(lang); + } + + // first things first + UpdateDownloader::CleanLeftovers(); + + // Run the check in background. Only show UI if updates + // are available. + UpdateChecker *check = new UpdateChecker(); + check->Start(); + } + CATCH_ALL_EXCEPTIONS } diff --git a/src/download.cpp b/src/download.cpp index 7c40402c..c4d28736 100644 --- a/src/download.cpp +++ b/src/download.cpp @@ -51,12 +51,7 @@ struct InetHandle ~InetHandle() { - if (m_handle) - { - if (m_callback) - InternetSetStatusCallback(m_handle, NULL); - InternetCloseHandle(m_handle); - } + Close(); } void SetStatusCallback(INTERNET_STATUS_CALLBACK callback) @@ -65,6 +60,17 @@ struct InetHandle InternetSetStatusCallback(m_handle, m_callback); } + void Close() + { + if (m_handle) + { + if (m_callback) + InternetSetStatusCallback(m_handle, NULL); + InternetCloseHandle(m_handle); + m_handle = NULL; + } + } + operator HINTERNET() const { return m_handle; } HINTERNET m_handle; @@ -142,6 +148,10 @@ void CALLBACK DownloadInternetStatusCallback(_In_ HINTERNET hInternet, case INTERNET_STATUS_REQUEST_COMPLETE: context->eventRequestComplete.Signal(); break; + + case INTERNET_STATUS_CONNECTION_CLOSED: + context->conn->Close(); + break; } } @@ -297,7 +307,14 @@ void DownloadFile(const std::string& url, IDownloadSink *sink, Thread *onThread, } if (ibuf.dwBufferLength == 0) - break; // all of the file was downloaded + { + // This check is required in case the INTERNET_STATUS_CONNECTION_CLOSED event was + // received (and the handle was closed) during the call to InternetReadFileEx() + if (!conn) + throw Win32Exception(); + else + break; // all of the file was downloaded + } sink->Add(ibuf.lpvBuffer, ibuf.dwBufferLength); } diff --git a/src/ui.cpp b/src/ui.cpp index b8cbdb11..f5e8932e 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -320,13 +320,11 @@ class WinSparkleDialog : public wxDialog WinSparkleDialog::WinSparkleDialog() : wxDialog(NULL, wxID_ANY, _("Software Update"), wxDefaultPosition, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxDIALOG_NO_PARENT) + wxDEFAULT_DIALOG_STYLE | wxDIALOG_NO_PARENT) { wxSize dpi = wxClientDC(this).GetPPI(); m_scaleFactor = dpi.y / 96.0; - SetIcon(LoadNamedIcon(UI::GetDllHINSTANCE(), L"UpdateAvailable", GetSystemMetrics(SM_CXSMICON))); - wxSizer *topSizer = new wxBoxSizer(wxHORIZONTAL); // Load the dialog box icon: the first 48x48 application icon will be loaded, if available, @@ -388,7 +386,6 @@ void WinSparkleDialog::SetHeadingFont(wxWindow *win) // 9pt is base font, 12pt is for "Main instructions". See // http://msdn.microsoft.com/en-us/library/aa511282%28v=MSDN.10%29.aspx f.SetPointSize(f.GetPointSize() + 3); - win->SetForegroundColour(wxColour(0x00, 0x33, 0x99)); } else // Windows XP/2000 { @@ -404,8 +401,8 @@ void WinSparkleDialog::SetHeadingFont(wxWindow *win) Window for communicating with the user *--------------------------------------------------------------------------*/ -const int ID_SKIP_VERSION = wxNewId(); -const int ID_REMIND_LATER = wxNewId(); +const int ID_REMIND_HOUR = wxNewId(); +const int ID_REMIND_TOMORROW = wxNewId(); const int ID_INSTALL = wxNewId(); const int ID_RUN_INSTALLER = wxNewId(); @@ -435,8 +432,8 @@ class UpdateDialog : public WinSparkleDialog void OnCloseButton(wxCommandEvent& event); void OnClose(wxCloseEvent& event); - void OnSkipVersion(wxCommandEvent&); - void OnRemindLater(wxCommandEvent&); + void OnRemindOneHour(wxCommandEvent&); + void OnRemindTomorrow(wxCommandEvent&); void OnInstall(wxCommandEvent&); void OnRunInstaller(wxCommandEvent&); @@ -532,20 +529,21 @@ UpdateDialog::UpdateDialog() m_updateButtonsSizer = new wxBoxSizer(wxHORIZONTAL); m_updateButtonsSizer->Add ( - new wxButton(this, ID_SKIP_VERSION, _("Skip this version")), + new wxButton(this, ID_REMIND_HOUR, _("Remind me in an hour")), wxSizerFlags().Border(wxRIGHT, PX(20)) ); - m_updateButtonsSizer->AddStretchSpacer(1); m_updateButtonsSizer->Add ( - new wxButton(this, ID_REMIND_LATER, _("Remind me later")), + new wxButton(this, ID_REMIND_TOMORROW, _("Remind me tomorrow")), wxSizerFlags().Border(wxRIGHT, PX(10)) ); - m_updateButtonsSizer->Add + m_updateButtonsSizer->AddStretchSpacer(1); + m_updateButtonsSizer->Add ( - m_installButton = new wxButton(this, ID_INSTALL, _("Install update")), + m_installButton = new wxButton(this, ID_INSTALL, _("Download")), wxSizerFlags() ); + m_installButton->SetFocus(); m_buttonSizer->Add(m_updateButtonsSizer, wxSizerFlags(1)); m_closeButtonSizer = new wxBoxSizer(wxHORIZONTAL); @@ -572,8 +570,8 @@ UpdateDialog::UpdateDialog() Bind(wxEVT_CLOSE_WINDOW, &UpdateDialog::OnClose, this); Bind(wxEVT_TIMER, &UpdateDialog::OnTimer, this); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnCloseButton, this, wxID_CANCEL); - Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnSkipVersion, this, ID_SKIP_VERSION); - Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindLater, this, ID_REMIND_LATER); + Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindOneHour, this, ID_REMIND_HOUR); + Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRemindTomorrow, this, ID_REMIND_TOMORROW); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnInstall, this, ID_INSTALL); Bind(wxEVT_COMMAND_BUTTON_CLICKED, &UpdateDialog::OnRunInstaller, this, ID_RUN_INSTALLER); } @@ -629,17 +627,18 @@ void UpdateDialog::OnClose(wxCloseEvent&) } -void UpdateDialog::OnSkipVersion(wxCommandEvent&) +void UpdateDialog::OnRemindOneHour(wxCommandEvent&) { - Settings::WriteConfigValue("SkipThisVersion", m_appcast.Version); + static const int ONE_HOUR_IN_SECONDS = 3600; + Settings::WriteConfigValue("UpdateInterval", ONE_HOUR_IN_SECONDS); Close(); } -void UpdateDialog::OnRemindLater(wxCommandEvent&) +void UpdateDialog::OnRemindTomorrow(wxCommandEvent&) { - // Just abort the update. Next time it's scheduled to run, - // the user will be prompted. + static const int ONE_DAY_IN_SECONDS = 86400; + Settings::WriteConfigValue("UpdateInterval", ONE_DAY_IN_SECONDS); Close(); } @@ -753,27 +752,12 @@ void UpdateDialog::StateNoUpdateFound(bool installAutomatically) LayoutChangesGuard guard(this); - m_heading->SetLabel(_("You're up to date!")); - - wxString msg; - try - { - msg = wxString::Format - ( - _("%s %s is currently the newest version available."), - Settings::GetAppName(), - Settings::GetAppVersion() - ); - } - catch ( std::exception& ) - { - // GetAppVersion() may fail - msg = "Error: Updates checking not properly configured."; - } + m_heading->SetLabel(_("You are up to date!")); + wxString msg = _("There are no new versions available at this time."); SetMessage(msg); - m_closeButton->SetLabel(_("Close")); + m_closeButton->SetLabel(_("OK")); m_closeButton->SetDefault(); EnablePulsing(false); @@ -854,8 +838,8 @@ void UpdateDialog::StateUpdateAvailable(const Appcast& info, bool installAutomat ( wxString::Format ( - _("%s %s is now available (you have %s). Would you like to download it now?"), - appname, ver_new, ver_my + _("You are currently using %s version %s.\nWould you like to download our new version (%s) now?"), + appname, ver_my, ver_new ), showRelnotes ? RELNOTES_WIDTH : MESSAGE_AREA_WIDTH ); @@ -1003,10 +987,14 @@ void UpdateDialog::ShowReleaseNotes(const Appcast& info) if( !info.ReleaseNotesURL.empty() ) { - m_webBrowser->Navigate + VARIANT varFlags; + varFlags.vt = VT_I4; + varFlags.llVal = (navNoHistory | navNoReadFromCache | navNoWriteToCache); + + m_webBrowser->Navigate ( wxBasicString(info.ReleaseNotesURL), - NULL, // Flags + &varFlags, // Flags NULL, // TargetFrameName NULL, // PostData NULL // Headers @@ -1071,7 +1059,7 @@ void UpdateDialog::ShowReleaseNotes(const Appcast& info) } } - SetWindowStyleFlag(wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + SetWindowStyleFlag(wxDEFAULT_DIALOG_STYLE); } diff --git a/src/updatechecker.cpp b/src/updatechecker.cpp index 437b990a..1c30e329 100644 --- a/src/updatechecker.cpp +++ b/src/updatechecker.cpp @@ -36,6 +36,7 @@ #include #include #include +#include using namespace std; @@ -219,52 +220,78 @@ UpdateChecker::UpdateChecker(): Thread("WinSparkle updates check") void UpdateChecker::Run() { - // no initialization to do, so signal readiness immediately - SignalReady(); - - try - { - const std::string url = Settings::GetAppcastURL(); - if ( url.empty() ) - throw std::runtime_error("Appcast URL not specified."); - CheckForInsecureURL(url, "appcast feed"); - - StringDownloadSink appcast_xml; - DownloadFile(url, &appcast_xml, this, GetAppcastDownloadFlags()); - - Appcast appcast = Appcast::Load(appcast_xml.data); - if (!appcast.ReleaseNotesURL.empty()) - CheckForInsecureURL(appcast.ReleaseNotesURL, "release notes"); - if (!appcast.DownloadURL.empty()) - CheckForInsecureURL(appcast.DownloadURL, "update file"); - - Settings::WriteConfigValue("LastCheckTime", time(NULL)); - - const std::string currentVersion = - WideToAnsi(Settings::GetAppBuildVersion()); - - // Check if our version is out of date. - if ( !appcast.IsValid() || CompareVersions(currentVersion, appcast.Version) >= 0 ) - { - // The same or newer version is already installed. - UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); - return; - } - - // Check if the user opted to ignore this particular version. - if ( ShouldSkipUpdate(appcast) ) - { - UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); - return; - } + while (1) + { + // no initialization to do, so signal readiness immediately + SignalReady(); + + bool checkUpdates; + if (Settings::ReadConfigValue("CheckForUpdates", checkUpdates)) + { + if (checkUpdates) + { + time_t lastCheck = 0; + Settings::ReadConfigValue("LastCheckTime", lastCheck); + const time_t currentTime = time(NULL); + + // Only check for updates in reasonable intervals: + const int interval = win_sparkle_get_update_check_interval(); + if (currentTime - lastCheck >= interval) + { + UpdateCheckWorker(); + } + } + } + // Check every 5 minutes + Sleep(win_sparkle_get_update_check_interval()); + } +} - UI::NotifyUpdateAvailable(appcast, ShouldAutomaticallyInstall()); - } - catch ( ... ) - { - UI::NotifyUpdateError(); - throw; - } +void UpdateChecker::UpdateCheckWorker() +{ + try + { + const std::string url = Settings::GetAppcastURL(); + if (url.empty()) + throw std::runtime_error("Appcast URL not specified."); + CheckForInsecureURL(url, "appcast feed"); + + StringDownloadSink appcast_xml; + DownloadFile(url, &appcast_xml, this, GetAppcastDownloadFlags()); + + Appcast appcast = Appcast::Load(appcast_xml.data); + if (!appcast.ReleaseNotesURL.empty()) + CheckForInsecureURL(appcast.ReleaseNotesURL, "release notes"); + if (!appcast.DownloadURL.empty()) + CheckForInsecureURL(appcast.DownloadURL, "update file"); + + Settings::WriteConfigValue("LastCheckTime", time(NULL)); + + const std::string currentVersion = + WideToAnsi(Settings::GetAppBuildVersion()); + + // Check if our version is out of date. + if (!appcast.IsValid() || CompareVersions(currentVersion, appcast.Version) >= 0) + { + // The same or newer version is already installed. + UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); + return; + } + + // Check if the user opted to ignore this particular version. + if (ShouldSkipUpdate(appcast)) + { + UI::NotifyNoUpdates(ShouldAutomaticallyInstall()); + return; + } + + UI::NotifyUpdateAvailable(appcast, ShouldAutomaticallyInstall()); + } + catch (...) + { + UI::NotifyUpdateError(); + throw; + } } bool UpdateChecker::ShouldSkipUpdate(const Appcast& appcast) const @@ -302,4 +329,11 @@ bool ManualUpdateChecker::ShouldSkipUpdate(const Appcast&) const return false; } +void ManualUpdateChecker::Run() +{ + // no initialization to do, so signal readiness immediately + SignalReady(); + + UpdateCheckWorker(); +} } // namespace winsparkle diff --git a/src/updatechecker.h b/src/updatechecker.h index 1009e395..74e1ec0b 100644 --- a/src/updatechecker.h +++ b/src/updatechecker.h @@ -70,7 +70,8 @@ class UpdateChecker : public Thread virtual bool ShouldAutomaticallyInstall() const { return false; } protected: - virtual void Run(); + void Run(); + virtual void UpdateCheckWorker(); virtual bool IsJoinable() const { return false; } }; @@ -87,6 +88,7 @@ class ManualUpdateChecker : public UpdateChecker protected: virtual int GetAppcastDownloadFlags() const; virtual bool ShouldSkipUpdate(const Appcast& appcast) const; + void Run(); };