Skip to content

Commit

Permalink
Add application update checker (#553)
Browse files Browse the repository at this point in the history
  • Loading branch information
vktr committed Mar 18, 2018
1 parent ce84389 commit 3ab3094
Show file tree
Hide file tree
Showing 12 changed files with 417 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,13 @@ add_executable(
src/picotorrent/ipc/applicationoptionsconnection
src/picotorrent/ipc/server

src/picotorrent/http/httpclient

src/picotorrent/addtorrentdlg
src/picotorrent/addtorrentproc
src/picotorrent/application
src/picotorrent/applicationoptions
src/picotorrent/applicationupdater
src/picotorrent/buildinfo
src/picotorrent/config
src/picotorrent/config_section
Expand Down Expand Up @@ -157,6 +160,7 @@ target_link_libraries(
crypt32
iphlpapi
wininet
winhttp

# Breakpad
breakpad
Expand Down
2 changes: 1 addition & 1 deletion lang/1033.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"no_update_available": "No update available",
"show_on_github": "Show on GitHub",
"new_version_available": "A newer version of PicoTorrent is available.",
"ignore_update": "Ignore this update.",
"ignore_update": "Ignore this update",
"confirm_remove_description": "This will remove any downloaded files for this torrent.",
"confirm_remove": "Confirm remove",
"do_not_ask_again": "Do not ask again",
Expand Down
134 changes: 134 additions & 0 deletions src/picotorrent/applicationupdater.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#include "applicationupdater.hpp"

#include <wx/richmsgdlg.h>

#include "buildinfo.hpp"
#include "config.hpp"
#include "translator.hpp"
#include "http/httpclient.hpp"
#include "picojson.hpp"
#include "semver.hpp"
#include "utils.hpp"

using pt::ApplicationUpdater;

ApplicationUpdater::ApplicationUpdater(wxFrame* parent, std::shared_ptr<pt::Configuration> cfg, std::shared_ptr<pt::Translator> translator)
: m_httpClient(std::make_unique<http::HttpClient>()),
m_config(cfg),
m_translator(translator),
m_parent(parent)
{
}

ApplicationUpdater::~ApplicationUpdater()
{
}

void ApplicationUpdater::Check(bool force)
{
std::string updateUrl = m_config->UpdateUrl();

m_httpClient->GetAsync(
Utils::ToWideString(updateUrl.c_str(), static_cast<int>(updateUrl.size())),
std::bind(&ApplicationUpdater::OnHttpResponse, this, std::placeholders::_1, force));
}

void ApplicationUpdater::OnHttpResponse(pt::http::HttpResponse const& response, bool force)
{
if (response.statusCode != 200)
{
return;
}

picojson::value v;
std::string err = picojson::parse(v, response.body);

if (!err.empty())
{
return;
}

picojson::object obj = v.get<picojson::object>();
std::string version = obj["version"].get<std::string>();

// If we haven't forced an update (via the menu item)
// and the version is the same as the stored ignored version,
// just return silently.

if (version == m_config->IgnoredVersion() && !force)
{
return;
}

semver::version parsedVersion(version);
semver::version currentVersion(BuildInfo::Version());

if (parsedVersion > currentVersion)
{
std::string url = obj["url"].get<std::string>();

m_parent->GetEventHandler()->CallAfter(std::bind(&ApplicationUpdater::ShowUpdateDialog, this, version, url));
}
else if (force)
{
m_parent->GetEventHandler()->CallAfter(std::bind(&ApplicationUpdater::ShowNoUpdateDialog, this));
}
}

void ApplicationUpdater::ShowUpdateDialog(std::string const& version, std::string& url)
{
// Load translations
wxString content = i18n(m_translator, "new_version_available");
wxString main = i18n(m_translator, "picotorrent_v_available");
wxString mainFormatted = wxString::Format(main, version);
wxString verification = i18n(m_translator, "ignore_update");
wxString show = i18n(m_translator, "show_on_github");

const TASKDIALOG_BUTTON pButtons[] =
{
{ 1000, show.c_str() },
};

TASKDIALOGCONFIG tdf = { sizeof(TASKDIALOGCONFIG) };
tdf.cButtons = ARRAYSIZE(pButtons);
tdf.dwCommonButtons = TDCBF_CLOSE_BUTTON;
tdf.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW | TDF_USE_COMMAND_LINKS;
tdf.hwndParent = m_parent->GetHWND();
tdf.pButtons = pButtons;
tdf.pszMainIcon = TD_INFORMATION_ICON;
tdf.pszMainInstruction = mainFormatted;
tdf.pszVerificationText = verification.c_str();
tdf.pszWindowTitle = TEXT("PicoTorrent");

int pnButton = -1;
int pnRadioButton = -1;
BOOL pfVerificationFlagChecked = FALSE;

TaskDialogIndirect(&tdf, &pnButton, &pnRadioButton, &pfVerificationFlagChecked);

if (pnButton == 1000)
{
wxLaunchDefaultBrowser(url, wxBROWSER_NEW_WINDOW);
}

if (pfVerificationFlagChecked)
{
m_config->IgnoredVersion(version);
}
}

void ApplicationUpdater::ShowNoUpdateDialog()
{
// Load translations
wxString main = i18n(m_translator, "no_update_available");

TASKDIALOGCONFIG tdf = { sizeof(TASKDIALOGCONFIG) };
tdf.dwCommonButtons = TDCBF_OK_BUTTON;
tdf.dwFlags = TDF_POSITION_RELATIVE_TO_WINDOW;
tdf.hwndParent = m_parent->GetHWND();
tdf.pszMainIcon = TD_INFORMATION_ICON;
tdf.pszMainInstruction = main.c_str();
tdf.pszWindowTitle = TEXT("PicoTorrent");

TaskDialogIndirect(&tdf, nullptr, nullptr, nullptr);
}
40 changes: 40 additions & 0 deletions src/picotorrent/applicationupdater.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include <memory>

namespace pt
{
namespace http
{
class HttpClient;
struct HttpResponse;
}

class Configuration;
class Translator;

class ApplicationUpdater
{
public:
ApplicationUpdater(wxFrame* parent, std::shared_ptr<Configuration> cfg, std::shared_ptr<Translator> translator);
~ApplicationUpdater();

void Check(bool force);

private:
void OnHttpResponse(http::HttpResponse const& response, bool force);
void ShowUpdateDialog(std::string const& version, std::string& url);
void ShowNoUpdateDialog();

wxFrame* m_parent;

std::shared_ptr<Configuration> m_config;
std::shared_ptr<Translator> m_translator;
std::unique_ptr<http::HttpClient> m_httpClient;
};
}
15 changes: 15 additions & 0 deletions src/picotorrent/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ void Configuration::CurrentLanguageId(int languageId)
Set<int>("language_id", languageId);
}

std::string Configuration::IgnoredVersion()
{
return Get<std::string>("ignored_version", "");
}

void Configuration::IgnoredVersion(std::string const& version)
{
Set("ignored_version", version);
}

fs::path Configuration::LanguagesPath()
{
return Get<std::string>("languages_path", (m_env->GetApplicationDataPath() / "Languages").string());
Expand Down Expand Up @@ -225,6 +235,11 @@ void Configuration::StartPosition(Configuration::WindowState state)
Set<int>("start_position", static_cast<int>(state));
}

std::string Configuration::UpdateUrl()
{
return Get<std::string>("update_url", "https://api.picotorrent.org/releases/latest");
}

Configuration::Configuration(std::shared_ptr<pt::Environment> env, std::shared_ptr<picojson::object> obj)
: m_env(env),
m_obj(obj)
Expand Down
5 changes: 5 additions & 0 deletions src/picotorrent/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ namespace pt
fs::path DefaultSavePath();
void DefaultSavePath(fs::path path);

std::string IgnoredVersion();
void IgnoredVersion(std::string const& version);

std::vector<std::pair<std::string, int>> ListenInterfaces();
void ListenInterfaces(const std::vector<std::pair<std::string, int>>& interfaces);

Expand Down Expand Up @@ -159,6 +162,8 @@ namespace pt
bool ProxyTrackers();
void ProxyTrackers(bool value);

std::string UpdateUrl();

WindowState StartPosition();
void StartPosition(WindowState state);

Expand Down

0 comments on commit 3ab3094

Please sign in to comment.