Update check logic moved to standalone DLL to make it reusable

This commit is contained in:
Simon Rozman 2016-05-24 16:50:36 +02:00
parent 54e64515d4
commit 529bd00da5
30 changed files with 1681 additions and 583 deletions

View File

@ -82,6 +82,7 @@ s38 s$(MSIBUILD_LENGTH_ID)
FeatureComponents Feature_ Component_ FeatureComponents Feature_ Component_
featUpdCheck compUpdCheck.exe.$(PLAT) featUpdCheck compUpdCheck.exe.$(PLAT)
featUpdCheck compwxExtend.dll.$(PLAT) featUpdCheck compwxExtend.dll.$(PLAT)
featUpdCheck compUpdater.dll.$(PLAT)
<<NOKEEP <<NOKEEP

View File

@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup> <ItemDefinitionGroup>
<ClCompile> <ClCompile>
<AdditionalIncludeDirectories>..\..\lib\wxExtend\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\Updater\include;..\..\lib\wxExtend\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link /> <Link />
</ItemDefinitionGroup> </ItemDefinitionGroup>

Binary file not shown.

View File

@ -85,7 +85,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="stdafx.h" /> <ClInclude Include="stdafx.h" />
<ClInclude Include="UpdCheck.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="UpdCheck.rc" /> <ResourceCompile Include="UpdCheck.rc" />
@ -103,6 +102,9 @@
<ProjectReference Include="..\..\lib\wxExtend\build\wxExtend.vcxproj"> <ProjectReference Include="..\..\lib\wxExtend\build\wxExtend.vcxproj">
<Project>{a3a36689-ac35-4026-93da-a3ba0c0e767c}</Project> <Project>{a3a36689-ac35-4026-93da-a3ba0c0e767c}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\Updater\build\Updater.vcxproj">
<Project>{990d8cf9-4457-4dc0-aa18-4968ef434741}</Project>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -30,9 +30,6 @@
<ClInclude Include="stdafx.h"> <ClInclude Include="stdafx.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="UpdCheck.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="UpdCheck.rc"> <ResourceCompile Include="UpdCheck.rc">

View File

@ -2,7 +2,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: UpdCheck\n" "Project-Id-Version: UpdCheck\n"
"POT-Creation-Date: 2016-05-13 19:52+0200\n" "POT-Creation-Date: 2016-05-24 13:24+0200\n"
"PO-Revision-Date: 2016-03-30 11:37+0200\n" "PO-Revision-Date: 2016-03-30 11:37+0200\n"
"Last-Translator: Simon Rozman <simon.rozman@amebis.si>\n" "Last-Translator: Simon Rozman <simon.rozman@amebis.si>\n"
"Language-Team: Amebis, d. o. o., Kamnik <info@amebis.si>\n" "Language-Team: Amebis, d. o. o., Kamnik <info@amebis.si>\n"
@ -18,33 +18,45 @@ msgstr ""
"X-Poedit-KeywordsList: _\n" "X-Poedit-KeywordsList: _\n"
"X-Poedit-SearchPath-0: .\n" "X-Poedit-SearchPath-0: .\n"
#: MSIBuild/En.Win32.Debug.ScheduledTask-2.idtx:4
#: MSIBuild/En.Win32.Release.ScheduledTask-2.idtx:4 #: MSIBuild/En.Win32.Release.ScheduledTask-2.idtx:4
#: MSIBuild/En.x64.Debug.ScheduledTask-2.idtx:4
#: MSIBuild/En.x64.Release.ScheduledTask-2.idtx:4 #: MSIBuild/En.x64.Release.ScheduledTask-2.idtx:4
#, fuzzy #, fuzzy
msgid "[SimpleProductName] Update" msgid "[SimpleProductName] Update"
msgstr "Posodabljanje [SimpleProductName]" msgstr "Posodabljanje [SimpleProductName]"
#: MSIBuild/En.Win32.Debug.Feature-2.idtx:3
#: MSIBuild/En.Win32.Debug.ScheduledTask-2.idtx:3
#: MSIBuild/En.Win32.Release.Feature-2.idtx:3 #: MSIBuild/En.Win32.Release.Feature-2.idtx:3
#: MSIBuild/En.Win32.Release.ScheduledTask-2.idtx:3 #: MSIBuild/En.Win32.Release.ScheduledTask-2.idtx:3
#: MSIBuild/En.x64.Debug.Feature-2.idtx:3
#: MSIBuild/En.x64.Debug.ScheduledTask-2.idtx:3
#: MSIBuild/En.x64.Release.Feature-2.idtx:3 #: MSIBuild/En.x64.Release.Feature-2.idtx:3
#: MSIBuild/En.x64.Release.ScheduledTask-2.idtx:3 #: MSIBuild/En.x64.Release.ScheduledTask-2.idtx:3
#, fuzzy #, fuzzy
msgid "1252" msgid "1252"
msgstr "1250" msgstr "1250"
#: MSIBuild/En.Win32.Debug.Feature-2.idtx:4
#: MSIBuild/En.Win32.Release.Feature-2.idtx:4 #: MSIBuild/En.Win32.Release.Feature-2.idtx:4
#: MSIBuild/En.x64.Debug.Feature-2.idtx:4
#: MSIBuild/En.x64.Release.Feature-2.idtx:4 #: MSIBuild/En.x64.Release.Feature-2.idtx:4
#, fuzzy #, fuzzy
msgid "Automatic Updating" msgid "Automatic Updating"
msgstr "Samodejno posodabljanje" msgstr "Samodejno posodabljanje"
#: MSIBuild/En.Win32.Debug.ScheduledTask-2.idtx:4
#: MSIBuild/En.Win32.Release.ScheduledTask-2.idtx:4 #: MSIBuild/En.Win32.Release.ScheduledTask-2.idtx:4
#: MSIBuild/En.x64.Debug.ScheduledTask-2.idtx:4
#: MSIBuild/En.x64.Release.ScheduledTask-2.idtx:4 #: MSIBuild/En.x64.Release.ScheduledTask-2.idtx:4
#, fuzzy #, fuzzy
msgid "Regularly checks for program updates, downloads, and installs them" msgid "Regularly checks for program updates, downloads, and installs them"
msgstr "Redno preverja za posodobitve programa, prenese in jih namesti" msgstr "Redno preverja za posodobitve programa, prenese in jih namesti"
#: MSIBuild/En.Win32.Debug.Feature-2.idtx:4
#: MSIBuild/En.Win32.Release.Feature-2.idtx:4 #: MSIBuild/En.Win32.Release.Feature-2.idtx:4
#: MSIBuild/En.x64.Debug.Feature-2.idtx:4
#: MSIBuild/En.x64.Release.Feature-2.idtx:4 #: MSIBuild/En.x64.Release.Feature-2.idtx:4
#, fuzzy #, fuzzy
msgid "Task for background product updating" msgid "Task for background product updating"

View File

@ -20,558 +20,6 @@
#include "stdafx.h" #include "stdafx.h"
///
/// UpdCheck Application
///
class wxUpdCheckApp
{
public:
wxUpdCheckApp();
virtual ~wxUpdCheckApp();
///
/// Application's main method
///
/// \returns Return code
///
int Run();
protected:
///
/// Downloads repository database (with integrity check).
///
/// \returns Repository database. Use wxDELETE after use.
///
wxXmlDocument* GetCatalogue();
///
/// Parses repository database.
///
/// \returns
/// - true if parsing succeeded
/// - false otherwise
///
bool ParseCatalogue(const wxXmlDocument &doc);
///
/// Stores update package meta-information to cache.
///
/// \returns
/// - true if writing succeeded
/// - false otherwise
///
bool WriteUpdatePackageMeta();
///
/// Retrieves update package meta-information from cache.
///
/// \returns
/// - true if reading succeeded
/// - false otherwise
///
bool ReadUpdatePackageMeta();
///
/// Downloads update package.
///
/// \param[in] fileName File name to save downloaded package to
///
/// \returns
/// - true if downloading succeeded
/// - false otherwise
///
bool DownloadUpdatePackage(const wxString &fileName);
///
/// Launch update.
///
/// \param[in] fileName File name of the update package
///
/// \returns
/// - true if launch succeeded
/// - false otherwise
///
bool LaunchUpdate(const wxString &fileName);
protected:
bool m_ok; ///< Is class initialized correctly
wxConfig m_config; ///< Application configuration
wxString m_path; ///< Working directory (log, downloaded files, etc.)
wxFFile m_log; ///< Log file
wxLocale m_locale; ///< Current locale
wxCryptoSession *m_cs; ///< Cryptographics session
wxCryptoKey *m_ck; ///< Public key for update database integrity validation
wxUint32 m_version; ///< Latest product version available (numerical)
wxString m_versionStr; ///< Latest product version available (string)
wxArrayString m_urls; ///< List of update package file downloads
wxMemoryBuffer m_hash; ///< Update package SHA-1 hash
};
//////////////////////////////////////////////////////////////////////////
// wxUpdCheckApp
//////////////////////////////////////////////////////////////////////////
wxUpdCheckApp::wxUpdCheckApp() :
m_config(wxT(UPDATER_CFG_APPLICATION) wxT("\\Updater"), wxT(UPDATER_CFG_VENDOR)),
m_cs(NULL),
m_ck(NULL),
m_version(0)
{
m_path = wxFileName::GetTempDir();
if (!wxEndsWithPathSeparator(m_path))
m_path += wxFILE_SEP_PATH;
if (!wxDirExists(m_path))
wxMkdir(m_path);
m_log.Open(m_path + wxT("Updater-") wxT(UPDATER_CFG_APPLICATION) wxT(".log"), wxT("wt"));
if (m_log.IsOpened())
delete wxLog::SetActiveTarget(new wxLogStderr(m_log.fp()));
// Set desired locale.
wxLanguage language = (wxLanguage)m_config.Read(wxT("Language"), wxLANGUAGE_DEFAULT);
if (wxLocale::IsAvailable(language))
wxVERIFY(m_locale.Init(language));
wxASSERT_MSG(!m_cs, wxT("cryptographics session already initialized"));
m_cs = new wxCryptoSessionRSAAES();
wxCHECK2_MSG(m_cs, { m_ok = false; return; }, wxT("wxCryptoSessionRSAAES creation failed"));
// Import public key.
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PUBLIC), RT_RCDATA);
wxCHECK2_MSG(res, { m_ok = false; return; }, wxT("public key not found"));
HGLOBAL res_handle = ::LoadResource(NULL, res);
wxCHECK2_MSG(res_handle, { m_ok = false; return; }, wxT("loading resource failed"));
m_ck = new wxCryptoKey();
wxCHECK2_MSG(m_ck, { m_ok = false; return; }, wxT("wxCryptoKey creation failed"));
wxCHECK2(m_ck->ImportPublic(*m_cs, ::LockResource(res_handle), ::SizeofResource(NULL, res)), { m_ok = false; return; });
}
wxUpdCheckApp::~wxUpdCheckApp()
{
if (m_ck) delete m_ck;
if (m_cs) delete m_cs;
}
int wxUpdCheckApp::Run()
{
if (!m_ok)
return -1; // Initialization failed.
// Download update catalogue.
wxXmlDocument *doc = GetCatalogue();
if (doc) {
// Parse the update catalogue to check for an update package availability.
if (ParseCatalogue(*doc)) {
// Save update package meta-information for the next time.
WriteUpdatePackageMeta();
} else {
wxLogStatus(wxT("Update check complete. Your package is up to date."));
return 0;
}
} else {
// Update download catalogue failed, or repository database didn't change. Read a cached version of update package meta-information?
if (!ReadUpdatePackageMeta()) {
// Reset CatalogueLastModified to force update catalogue download next time.
m_config.DeleteEntry(wxT("CatalogueLastModified"));
return 1; // No update package information available.
}
if (m_version <= UPDATER_PRODUCT_VERSION) {
wxLogStatus(wxT("Update check complete. Your package is up to date."));
return 0;
}
}
wxString fileName(m_path);
fileName += wxT("Updater-") wxT(UPDATER_CFG_APPLICATION) wxT("-");
fileName += m_versionStr;
fileName += wxT(".msi");
if (!DownloadUpdatePackage(fileName))
return 2; // Update package not available.
if (!LaunchUpdate(fileName))
return 3; // Update launch failed.
return 0;
}
wxXmlDocument* wxUpdCheckApp::GetCatalogue()
{
wxLogStatus(wxT("Downloading repository catalogue..."));
wxString lastModified;
m_config.Read(wxT("CatalogueLastModified"), &lastModified, wxT(""));
for (const wxChar *server = wxT(UPDATER_HTTP_SERVER); server[0]; server += wcslen(server) + 1) {
wxLogStatus(wxT("Contacting repository at %s..."), server);
// Load repository database.
wxHTTP http;
http.SetHeader(wxS("User-Agent"), wxS("Updater/") wxS(UPDATER_VERSION_STR));
if (!lastModified.IsEmpty())
http.SetHeader(wxS("If-Modified-Since"), lastModified);
if (!http.Connect(server, UPDATER_HTTP_PORT)) {
wxLogWarning(wxT("Error resolving %s server name."), server);
continue;
}
wxInputStream *httpStream = http.GetInputStream(wxS(UPDATER_HTTP_PATH));
if (http.GetResponse() == 304) {
wxLogStatus(wxT("Repository did not change since the last time."));
wxDELETE(httpStream);
return NULL;
} else if (!httpStream) {
wxLogWarning(wxT("Error response received from server %s (port %u) requesting %s."), server, UPDATER_HTTP_PORT, UPDATER_HTTP_PATH);
continue;
}
wxXmlDocument *doc = new wxXmlDocument();
if (!doc->Load(*httpStream, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) {
wxLogWarning(wxT("Error loading repository catalogue."));
wxDELETE(httpStream);
http.Close();
delete doc;
continue;
}
wxDELETE(httpStream);
m_config.Write(wxT("CatalogueLastModified"), http.GetHeader(wxT("Last-Modified")));
http.Close();
wxLogStatus(wxT("Verifying repository catalogue signature..."));
// Find the (first) signature.
wxXmlNode *document = doc->GetDocumentNode();
wxMemoryBuffer sig;
for (wxXmlNode *prolog = document->GetChildren(); prolog; prolog = prolog->GetNext()) {
if (prolog->GetType() == wxXML_COMMENT_NODE) {
wxString content = prolog->GetContent();
size_t content_len = content.length();
if (content_len >= _countof(wxS(UPDATER_SIGNATURE_MARK)) - 1 &&
memcmp((const wxStringCharType*)content, wxS(UPDATER_SIGNATURE_MARK), sizeof(wxStringCharType)*(_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1)) == 0)
{
// Read the signature.
size_t signature_len = content_len - (_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1);
size_t len = wxBase64DecodedSize(signature_len);
size_t res = wxBase64Decode(sig.GetWriteBuf(len), len, content.Right(signature_len), wxBase64DecodeMode_SkipWS);
if (res != wxCONV_FAILED) {
sig.SetDataLen(res);
// Remove the signature as it is not a part of the validation check.
document->RemoveChild(prolog);
wxDELETE(prolog);
break;
} else
sig.Clear();
}
}
}
if (sig.IsEmpty()) {
wxLogWarning(wxT("Signature not found in the repository catalogue."));
delete doc;
continue;
}
// Hash the content.
wxCryptoHashSHA1 ch(*m_cs);
if (!wxXmlHashNode(ch, document)) {
delete doc;
continue;
}
// We have the hash, we have the signature, we have the public key. Now verify.
if (!wxCryptoVerifySignature(ch, sig, *m_ck)) {
wxLogWarning(wxT("Repository catalogue signature does not match its content, or signature verification failed."));
delete doc;
continue;
}
// Verify document type (root element).
const wxString &nameRoot = doc->GetRoot()->GetName();
if (nameRoot != wxT("Packages")) {
wxLogWarning(wxT("Invalid root element in repository catalogue (actual: %s, expected %s)."), nameRoot.c_str(), wxT("Packages"));
delete doc;
continue;
}
// The downloaded repository database passed all checks.
return doc;
}
// No repository database downloaded successfully.
return NULL;
}
bool wxUpdCheckApp::ParseCatalogue(const wxXmlDocument &doc)
{
bool found = false;
wxLogStatus(wxT("Parsing repository catalogue..."));
// Iterate over packages.
for (wxXmlNode *elPackage = doc.GetRoot()->GetChildren(); elPackage; elPackage = elPackage->GetNext()) {
if (elPackage->GetType() == wxXML_ELEMENT_NODE && elPackage->GetName() == wxT("Package")) {
// Get package version.
wxUint32 version = 0;
wxString versionStr;
for (wxXmlNode *elVersion = elPackage->GetChildren(); elVersion; elVersion = elVersion->GetNext()) {
if (elVersion->GetType() == wxXML_ELEMENT_NODE && elVersion->GetName() == wxT("Version")) {
for (wxXmlNode *elVersionNote = elVersion->GetChildren(); elVersionNote; elVersionNote = elVersionNote->GetNext()) {
if (elVersionNote->GetType() == wxXML_ELEMENT_NODE) {
const wxString &name = elVersionNote->GetName();
if (name == wxT("hex"))
version = _tcstoul(elVersionNote->GetNodeContent(), NULL, 16);
else if (name == wxT("desc"))
versionStr = elVersionNote->GetNodeContent();
}
}
}
}
if (version <= UPDATER_PRODUCT_VERSION || version < m_version) {
// This package is older than currently installed product or the superseeded package found already.
continue;
}
// Get package download URL for our platform and language.
wxString platformId;
#if defined(__WINDOWS__)
platformId += wxT("win");
#endif
#if defined(_WIN64)
platformId += wxT("-amd64");
#else
platformId += wxT("-x86");
#endif
wxString languageId(m_locale.GetCanonicalName());
wxArrayString urls;
wxMemoryBuffer hash;
for (wxXmlNode *elDownloads = elPackage->GetChildren(); elDownloads; elDownloads = elDownloads->GetNext()) {
if (elDownloads->GetType() == wxXML_ELEMENT_NODE && elDownloads->GetName() == wxT("Downloads")) {
for (wxXmlNode *elPlatform = elDownloads->GetChildren(); elPlatform; elPlatform = elPlatform->GetNext()) {
if (elPlatform->GetType() == wxXML_ELEMENT_NODE) {
if (elPlatform->GetName() == wxT("Platform") && elPlatform->GetAttribute(wxT("id")) == platformId) {
// Get language.
for (wxXmlNode *elLocale = elPlatform->GetChildren(); elLocale; elLocale = elLocale->GetNext()) {
if (elLocale->GetType() == wxXML_ELEMENT_NODE && elLocale->GetName() == wxT("Localization") && elLocale->GetAttribute(wxT("lang")) == languageId) {
for (wxXmlNode *elLocaleNote = elLocale->GetChildren(); elLocaleNote; elLocaleNote = elLocaleNote->GetNext()) {
if (elLocaleNote->GetType() == wxXML_ELEMENT_NODE) {
const wxString &name = elLocaleNote->GetName();
if (name == wxT("url"))
urls.Add(elLocaleNote->GetNodeContent());
else if (name == wxT("hash")) {
// Read the hash.
wxString content(elLocaleNote->GetNodeContent());
size_t len = wxHexDecodedSize(content.length());
size_t res = wxHexDecode(hash.GetWriteBuf(len), len, content, wxHexDecodeMode_SkipWS);
if (res != wxCONV_FAILED)
hash.SetDataLen(res);
else
hash.Clear();
}
}
}
}
}
}
}
}
}
}
if (urls.empty() || hash.IsEmpty()) {
// This package is for different architecture and/or language.
// ... or missing URL and/or hash.
continue;
}
// Save found package info.
m_version = version;
m_versionStr = versionStr;
m_urls = urls;
m_hash = hash;
found = true;
wxLogMessage(wxT("Update package found (version: %s)."), m_versionStr.c_str());
}
}
return found;
}
bool wxUpdCheckApp::WriteUpdatePackageMeta()
{
// Concatenate URLs.
wxString urls;
for (size_t i = 0, n = m_urls.GetCount(); i < n; i++) {
if (i) urls += wxT('\n');
urls += m_urls[i];
}
bool result = true;
if (!m_config.Write(wxT("PackageVersion" ), m_version )) result = false;
if (!m_config.Write(wxT("PackageVersionDesc"), m_versionStr )) result = false;
if (!m_config.Write(wxT("PackageURLs" ), urls )) result = false;
if (!m_config.Write(wxT("PackageHash" ), wxHexEncode(m_hash))) result = false;
return result;
}
bool wxUpdCheckApp::ReadUpdatePackageMeta()
{
long lng;
if (m_config.Read(wxT("PackageVersion"), &lng)) {
m_version = lng;
if (m_config.Read(wxT("PackageVersionDesc"), &m_versionStr)) {
wxString str;
if (m_config.Read(wxT("PackageURLs"), &str)) {
// Split URLs
m_urls.Clear();
const wxStringCharType *buf = str;
for (size_t i = 0, j = 0, n = str.Length(); i < n;) {
for (; j < n && buf[j] != wxT('\n'); j++);
m_urls.Add(str.SubString(i, j - 1));
i = ++j;
}
if (m_config.Read(wxT("PackageHash"), &str)) {
// Convert to binary.
size_t len = wxHexDecodedSize(str.length());
size_t res = wxHexDecode(m_hash.GetWriteBuf(len), len, str, wxHexDecodeMode_SkipWS);
if (res != wxCONV_FAILED) {
m_hash.SetDataLen(res);
return true;
} else
m_hash.Clear();
}
}
}
}
wxLogWarning(wxT("Reading update package meta-information from cache failed or incomplete."));
m_version = 0;
m_versionStr.Clear();
m_urls.Clear();
m_hash.Clear();
return false;
}
bool wxUpdCheckApp::DownloadUpdatePackage(const wxString &fileName)
{
// Calculate file hash (if exists).
wxCryptoHashSHA1 ch(*m_cs);
if (ch.HashFile(fileName)) {
wxMemoryBuffer buf;
ch.GetValue(buf);
if (buf.GetDataLen() == m_hash.GetDataLen() &&
memcmp(buf.GetData(), m_hash.GetData(), buf.GetDataLen()) == 0)
{
// Update package file exists and its hash is correct.
wxLogStatus(wxT("Update package file already downloaded."));
return true;
}
}
wxFFile file(fileName, wxT("wb"));
if (!file.IsOpened()) {
wxLogError(wxT("Can not open/create %s file for writing."), fileName.c_str());
return false;
}
// Download update package file.
for (size_t i = 0, n = m_urls.GetCount(); i < n; i++) {
wxURL url(m_urls[i]);
if (!url.IsOk())
continue;
if (url.HasScheme()) {
const wxString scheme = url.GetScheme();
if (scheme == wxT("http") || scheme == wxT("https")) {
wxHTTP &http = (wxHTTP&)url.GetProtocol();
http.SetHeader(wxS("User-Agent"), wxS("Updater/") wxS(UPDATER_VERSION_STR));
}
}
wxLogStatus(wxT("Downloading update package from %s..."), m_urls[i].c_str());
wxInputStream *stream = url.GetInputStream();
if (!stream) {
wxLogWarning(wxT("Error response received."));
continue;
}
// Save update package to file, and calculate hash.
wxCryptoHashSHA1 ch(*m_cs);
wxMemoryBuffer buf(4*1024);
char *data = (char*)buf.GetData();
size_t nBlock = buf.GetBufSize();
do {
stream->Read(data, nBlock);
size_t nRead = stream->LastRead();
file.Write(data, nRead);
if (file.Error()) {
wxLogError(wxT("Can not write to %s file."), fileName.c_str());
return false;
}
ch.Hash(data, nRead);
} while (stream->IsOk());
ch.GetValue(buf);
if (buf.GetDataLen() == m_hash.GetDataLen() &&
memcmp(buf.GetData(), m_hash.GetData(), buf.GetDataLen()) == 0)
{
// Update package file downloaded and its hash is correct.
return true;
} else
wxLogWarning(wxT("Update package file corrupt."));
}
return false;
}
bool wxUpdCheckApp::LaunchUpdate(const wxString &fileName)
{
wxLogStatus(wxT("Launching update..."));
// Headless Install
wxString param("/qn");
// Package
param += wxT(" /i \"");
param += fileName;
param += wxT("\"");
// Logging
wxString fileNameLog(m_path);
fileNameLog += wxT("Updater-") wxT(UPDATER_CFG_APPLICATION) wxT("-msiexec.log");
param += wxT(" /l* \"");
param += fileNameLog;
param += wxT("\"");
int result = (int)::ShellExecute(NULL, NULL, wxT("msiexec.exe"), param, NULL, SW_SHOWNORMAL);
if (result > 32) {
wxLogStatus(wxT("msiexec.exe launch succeeded. For further information, see %s file."), fileNameLog.c_str());
return true;
} else {
wxLogError(wxT("msiexec.exe launch failed. ShellExecute returned %i."), result);
return false;
}
}
/// ///
/// Main function /// Main function
/// ///
@ -584,7 +32,34 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In
if (!initializer.IsOk()) if (!initializer.IsOk())
return -1; return -1;
// Do everything. // Create output folder.
wxUpdCheckApp app; wxString path(wxFileName::GetTempDir());
return app.Run(); if (!wxEndsWithPathSeparator(path))
path += wxFILE_SEP_PATH;
if (!wxDirExists(path))
wxMkdir(path);
// Prepare log file.
wxFFile log_file(path + wxT("Updater-") wxT(UPDATER_CFG_APPLICATION) wxT(".log"), wxT("wt"));
if (log_file.IsOpened())
delete wxLog::SetActiveTarget(new wxLogStderr(log_file.fp()));
// Set desired locale.
wxConfig config(wxT(UPDATER_CFG_APPLICATION) wxT("\\Updater"), wxT(UPDATER_CFG_VENDOR));
wxLanguage language = (wxLanguage)config.Read(wxT("Language"), wxLANGUAGE_DEFAULT);
wxLocale locale;
if (wxLocale::IsAvailable(language)) {
wxVERIFY(locale.Init(language));
// Do not add translation catalog, to keep log messages in English.
// Log files are for help-desk and should remain globally intelligible.
//wxVERIFY(locale.AddCatalog(wxT("Updater") wxT(wxExtendVersion)));
}
wxUpdCheckThread worker(locale.GetCanonicalName());
wxUpdCheckThread::wxResult res = worker.CheckForUpdate();
switch (res) {
case wxUpdCheckThread::wxUpdUpdateAvailable: return worker.LaunchUpdate() ? 0 : 1;
case wxUpdCheckThread::wxUpdUpToDate : return 0;
default : return res;
}
} }

View File

@ -19,23 +19,13 @@
#pragma once #pragma once
#include "../../include/UpdaterCfg.h" #include <Updater/chkthread.h>
#include "UpdCheck.h"
#include "../include/Updater.h"
#include <wx/app.h> #include <wx/app.h>
#include <wx/arrstr.h>
#include <wx/base64.h>
#include <wx/buffer.h>
#include <wx/config.h> #include <wx/config.h>
#include <wx/dir.h> #include <wx/dir.h>
#include <wx/ffile.h> #include <wx/ffile.h>
#include <wx/filename.h> #include <wx/filename.h>
#include <wx/protocol/http.h> #include <wx/init.h>
#include <wx/url.h>
#include <wx/xml/xml.h>
#include <wxex/crypto.h> #include <wxex/common.h>
#include <wxex/hex.h>
#include <wxex/xml.h>

Binary file not shown.

View File

@ -19,10 +19,9 @@
#pragma once #pragma once
#include "../../include/UpdaterCfg.h" #include "../Updater/include/Updater/common.h"
#include "UpdPublish.h" #include "UpdPublish.h"
#include "../include/Updater.h"
#include <wx/app.h> #include <wx/app.h>
#include <wx/base64.h> #include <wx/base64.h>

Binary file not shown.

View File

@ -19,8 +19,9 @@
#pragma once #pragma once
#include "../Updater/include/Updater/common.h"
#include "UpdSignXML.h" #include "UpdSignXML.h"
#include "../include/Updater.h"
#include <wx/app.h> #include <wx/app.h>
#include <wx/base64.h> #include <wx/base64.h>

View File

@ -3,11 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010 # Visual Studio 2010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{A7D28E0C-BB96-444D-AAB0-F22A6483FE5F}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{A7D28E0C-BB96-444D-AAB0-F22A6483FE5F}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ECE3F336-FFD2-41EE-AD8F-17BD7472BDCB}"
ProjectSection(SolutionItems) = preProject
include\Updater.h = include\Updater.h
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdCheck", "UpdCheck\UpdCheck.vcxproj", "{0B2B7A10-3FA0-46CD-AD89-6D02DA2C5FC3}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdCheck", "UpdCheck\UpdCheck.vcxproj", "{0B2B7A10-3FA0-46CD-AD89-6D02DA2C5FC3}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxExtend", "..\lib\wxExtend\build\wxExtend.vcxproj", "{A3A36689-AC35-4026-93DA-A3BA0C0E767C}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxExtend", "..\lib\wxExtend\build\wxExtend.vcxproj", "{A3A36689-AC35-4026-93DA-A3BA0C0E767C}"
@ -16,6 +11,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdSignXML", "UpdSignXML\Up
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdPublish", "UpdPublish\UpdPublish.vcxproj", "{C5829278-D55E-41EB-B866-1E7D814694D1}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdPublish", "UpdPublish\UpdPublish.vcxproj", "{C5829278-D55E-41EB-B866-1E7D814694D1}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Updater", "Updater\build\Updater.vcxproj", "{990D8CF9-4457-4DC0-AA18-4968EF434741}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32 Debug|Win32 = Debug|Win32
@ -56,11 +53,20 @@ Global
{C5829278-D55E-41EB-B866-1E7D814694D1}.Release|Win32.Build.0 = Release|Win32 {C5829278-D55E-41EB-B866-1E7D814694D1}.Release|Win32.Build.0 = Release|Win32
{C5829278-D55E-41EB-B866-1E7D814694D1}.Release|x64.ActiveCfg = Release|x64 {C5829278-D55E-41EB-B866-1E7D814694D1}.Release|x64.ActiveCfg = Release|x64
{C5829278-D55E-41EB-B866-1E7D814694D1}.Release|x64.Build.0 = Release|x64 {C5829278-D55E-41EB-B866-1E7D814694D1}.Release|x64.Build.0 = Release|x64
{990D8CF9-4457-4DC0-AA18-4968EF434741}.Debug|Win32.ActiveCfg = Debug|Win32
{990D8CF9-4457-4DC0-AA18-4968EF434741}.Debug|Win32.Build.0 = Debug|Win32
{990D8CF9-4457-4DC0-AA18-4968EF434741}.Debug|x64.ActiveCfg = Debug|x64
{990D8CF9-4457-4DC0-AA18-4968EF434741}.Debug|x64.Build.0 = Debug|x64
{990D8CF9-4457-4DC0-AA18-4968EF434741}.Release|Win32.ActiveCfg = Release|Win32
{990D8CF9-4457-4DC0-AA18-4968EF434741}.Release|Win32.Build.0 = Release|Win32
{990D8CF9-4457-4DC0-AA18-4968EF434741}.Release|x64.ActiveCfg = Release|x64
{990D8CF9-4457-4DC0-AA18-4968EF434741}.Release|x64.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{A3A36689-AC35-4026-93DA-A3BA0C0E767C} = {A7D28E0C-BB96-444D-AAB0-F22A6483FE5F} {A3A36689-AC35-4026-93DA-A3BA0C0E767C} = {A7D28E0C-BB96-444D-AAB0-F22A6483FE5F}
{990D8CF9-4457-4DC0-AA18-4968EF434741} = {A7D28E0C-BB96-444D-AAB0-F22A6483FE5F}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

9
Updater/MSIBuild/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
/*-1.idt
/*-2.idt
/*-2.idtx
/*.Binary-1
/*.Binary-2
/*.Icon-1
/*.Icon-2
/*.lst
/*.msm

106
Updater/MSIBuild/Makefile Normal file
View File

@ -0,0 +1,106 @@
#
# Copyright 1991-2016 Amebis
#
# This file is part of ZRCola.
#
# ZRCola is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ZRCola is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ZRCola. If not, see <http://www.gnu.org/licenses/>.
#
!INCLUDE "..\..\..\include\MSIBuildCfg.mak"
!IFNDEF UPDATER_BIN_DIR
!ERROR Parameter UPDATER_BIN_DIR is undefined.
!ENDIF
!IFNDEF UPDATER_LOC_DIR
!ERROR Parameter UPDATER_LOC_DIR is undefined.
!ENDIF
######################################################################
# Component
All :: "$(LANG).$(PLAT).$(CFG).Component-1.idt"
"$(LANG).$(PLAT).$(CFG).Component-1.idt" : "Makefile" "..\..\..\include\MSIBuildCfg.mak"
-if exist $@ del /f /q $@
move /y << $@ > NUL
Component ComponentId Directory_ Attributes Condition KeyPath
s$(MSIBUILD_LENGTH_ID) S38 s$(MSIBUILD_LENGTH_ID) i2 S255 S$(MSIBUILD_LENGTH_ID)
Component Component
!IF "$(PLAT)" == "Win32"
compUpdater.dll.Win32 {F4962976-71C3-4200-B736-BC841FEE29C2} $(UPDATER_BIN_DIR) 0 fileUpdater.dll.Win32
!ENDIF
!IF "$(PLAT)" == "x64"
compUpdater.dll.x64 {A968C387-EC14-4CAC-8406-F21C9779D3EF} $(UPDATER_BIN_DIR) 256 fileUpdater.dll.x64
!ENDIF
!IF "$(LANG)" == "Sl"
compUpdater.mo.sl_SI {2A56FA39-F4E6-492F-A863-AFE9E53FA988} UPDATERLOCSLSIDIR $(MSIBUILD_COMPONENT_ATTRIB_FILE) fileUpdater.mo.sl_SI
!ENDIF
<<NOKEEP
######################################################################
# Directory
All :: "$(LANG).$(PLAT).$(CFG).Directory-1.idt"
"$(LANG).$(PLAT).$(CFG).Directory-1.idt" : "Makefile" "..\..\..\include\MSIBuildCfg.mak"
-if exist $@ del /f /q $@
move /y << $@ > NUL
Directory Directory_Parent DefaultDir
s$(MSIBUILD_LENGTH_ID) S$(MSIBUILD_LENGTH_ID) l255
Directory Directory
UPDATERLOCSLSIDIR $(UPDATER_LOC_DIR) sl_SI
<<NOKEEP
######################################################################
# File
All :: "$(LANG).$(PLAT).$(CFG).File-1.idt"
"$(LANG).$(PLAT).$(CFG).File-1.idt" : "Makefile" "..\..\..\include\MSIBuildCfg.mak"
-if exist $@ del /f /q $@
move /y << $@ > NUL
File Component_ FileName FileSize Version Language Attributes Sequence
s$(MSIBUILD_LENGTH_ID) s$(MSIBUILD_LENGTH_ID) l255 i4 S$(MSIBUILD_LENGTH_ID) S20 I2 i2
File File
!IF "$(PLAT)" == "Win32"
!IF "$(CFG)" == "Release"
fileUpdater.dll.Win32 compUpdater.dll.Win32 UPDATE~2.DLL|Updater10u_vc100.dll 0 0 1536 1
!ENDIF
!IF "$(CFG)" == "Debug"
fileUpdater.dll.Win32 compUpdater.dll.Win32 UPDATE~4.DLL|Updater10ud_vc100.dll 0 0 1536 1
!ENDIF
!ENDIF
!IF "$(PLAT)" == "x64"
!IF "$(CFG)" == "Release"
fileUpdater.dll.x64 compUpdater.dll.x64 UPDATE~6.DLL|Updater10u_vc100_x64.dll 0 0 1536 1
!ENDIF
!IF "$(CFG)" == "Debug"
fileUpdater.dll.x64 compUpdater.dll.x64 UPDATE~8.DLL|Updater10ud_vc100_x64.dll 0 0 1536 1
!ENDIF
!ENDIF
!IF "$(LANG)" == "Sl"
fileUpdater.mo.sl_SI compUpdater.mo.sl_SI UPDATE~1.MO|Updater10.mo 0 1060 0 1
!ENDIF
<<NOKEEP
######################################################################
# Build MSM module!
######################################################################
!INCLUDE "..\..\..\MSI\MSIBuild\MSM.mak"

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros">
<UpdaterVersion>10</UpdaterVersion>
</PropertyGroup>
<PropertyGroup>
<OutDir>..\..\..\output\$(Platform).$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>UPDATER;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\..\lib\wxExtend\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<POCompile>
<OutputFile>$(OutDir)..\locale\%(Filename)\$(ProjectName)$(UpdaterVersion).mo</OutputFile>
</POCompile>
</ItemDefinitionGroup>
<ItemGroup>
<BuildMacro Include="UpdaterVersion">
<Value>$(UpdaterVersion)</Value>
</BuildMacro>
</ItemGroup>
</Project>

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\main.cpp" />
<ClCompile Include="..\src\stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\src\chkthread.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\updater\chkthread.h" />
<ClInclude Include="..\include\Updater\common.h" />
<ClInclude Include="..\src\stdafx.h" />
</ItemGroup>
<ItemGroup>
<POCompile Include="..\locale\de_DE.po" />
<POCompile Include="..\locale\ru_RU.po" />
<POCompile Include="..\locale\sl_SI.po" />
</ItemGroup>
<ItemGroup>
<None Include="..\locale\Updater.pot" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\res\Updater.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\lib\wxExtend\build\wxExtend.vcxproj">
<Project>{a3a36689-ac35-4026-93da-a3ba0c0e767c}</Project>
</ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{990D8CF9-4457-4DC0-AA18-4968EF434741}</ProjectGuid>
<RootNamespace>Updater</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\include\Win32.props" />
<Import Project="..\..\..\include\Debug.props" />
<Import Project="Updater.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\include\x64.props" />
<Import Project="..\..\..\include\Debug.props" />
<Import Project="Updater.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\include\Win32.props" />
<Import Project="..\..\..\include\Release.props" />
<Import Project="Updater.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\include\x64.props" />
<Import Project="..\..\..\include\Release.props" />
<Import Project="Updater.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<TargetName>$(ProjectName)$(UpdaterVersion)ud_vc$(PlatformToolsetVersion)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetName>$(ProjectName)$(UpdaterVersion)ud_vc$(PlatformToolsetVersion)_$(Platform)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetName>$(ProjectName)$(UpdaterVersion)u_vc$(PlatformToolsetVersion)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetName>$(ProjectName)$(UpdaterVersion)u_vc$(PlatformToolsetVersion)_$(Platform)</TargetName>
</PropertyGroup>
<PropertyGroup />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\include\xgettext.targets" />
</ImportGroup>
</Project>

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Resource Files\Localization">
<UniqueIdentifier>{e43059ae-37ac-4b28-84fb-18d1b3972b30}</UniqueIdentifier>
<Extensions>po;pot</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\chkthread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\Updater\common.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\updater\chkthread.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\locale\Updater.pot">
<Filter>Resource Files\Localization</Filter>
</None>
</ItemGroup>
<ItemGroup>
<POCompile Include="..\locale\sl_SI.po">
<Filter>Resource Files\Localization</Filter>
</POCompile>
<POCompile Include="..\locale\de_DE.po">
<Filter>Resource Files\Localization</Filter>
</POCompile>
<POCompile Include="..\locale\ru_RU.po">
<Filter>Resource Files\Localization</Filter>
</POCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\res\Updater.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,153 @@
/*
Copyright 2016 Amebis
This file is part of Updater.
Updater is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Updater is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Updater. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common.h"
#include <wx/arrstr.h>
#include <wx/config.h>
#include <wx/event.h>
#include <wx/string.h>
#include <wx/thread.h>
#include <wx/xml/xml.h>
#include <wxex/crypto.h>
///
/// wxEVT_UPDATER_CHECK_COMPLETE event
///
wxDECLARE_EXPORTED_EVENT(UPDATER_API, wxEVT_UPDATER_CHECK_COMPLETE, wxThreadEvent);
///
/// Update check thread
///
class UPDATER_API wxUpdCheckThread : public wxThread
{
public:
///
/// Thread exit codes
///
enum wxResult {
wxUpdUninitialized = -1, ///< Initialization failed
wxUpdAborted = -2, ///< Thread aborted
wxUpdRepoUnavailable = -3, ///< No update package information available
wxUpdPkgUnavailable = -4, ///< Update package not available
wxUpdUpdateAvailable = 0, ///< Update downloaded, verified and prepared to install
wxUpdUpToDate, ///< Product is up-to-date
};
wxUpdCheckThread(const wxString &langId, wxEvtHandler *parent = NULL);
virtual ~wxUpdCheckThread();
///
/// Checks for updates and safely downloads update package when available.
///
inline wxResult CheckForUpdate()
{
return DoCheckForUpdate();
}
protected:
///
/// Thread's body
///
/// \returns Exit code
///
virtual ExitCode Entry();
///
/// Checks for updates and safely downloads update package when available.
///
wxResult DoCheckForUpdate();
///
/// Downloads repository database (with integrity check).
///
/// \returns Repository database. Use wxDELETE after use.
///
wxXmlDocument* GetCatalogue();
///
/// Parses repository database.
///
/// \returns
/// - true if parsing succeeded
/// - false otherwise
///
bool ParseCatalogue(const wxXmlDocument &doc);
///
/// Stores update package meta-information to cache.
///
/// \returns
/// - true if writing succeeded
/// - false otherwise
///
bool WriteUpdatePackageMeta();
///
/// Retrieves update package meta-information from cache.
///
/// \returns
/// - true if reading succeeded
/// - false otherwise
///
bool ReadUpdatePackageMeta();
///
/// Downloads update package.
///
/// \returns
/// - true if downloading succeeded
/// - false otherwise
///
bool DownloadUpdatePackage();
public:
///
/// Launches update.
///
/// \returns
/// - true if launch succeeded
/// - false otherwise
///
bool LaunchUpdate();
protected:
wxEvtHandler *m_parent; ///< Parent to notify
bool m_ok; ///< Is class initialized correctly?
wxString m_langId; ///< Language identifier
wxConfig m_config; ///< Application configuration
wxString m_path; ///< Working directory (downloaded files, etc.)
wxCryptoSession *m_cs; ///< Cryptographics session
wxCryptoKey *m_ck; ///< Public key for update database integrity validation
wxUint32 m_version; ///< Latest product version available (numerical)
wxString m_versionStr; ///< Latest product version available (string)
wxArrayString m_urls; ///< List of update package file downloads
wxMemoryBuffer m_hash; ///< Update package SHA-1 hash
wxString m_fileName; ///< Downloaded package file name
};

View File

@ -17,7 +17,11 @@
along with Updater. If not, see <http://www.gnu.org/licenses/>. along with Updater. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #if !defined(__UPDATER_common_h__)
#define __UPDATER_common_h__
#include "../../../../include/UpdaterCfg.h"
// //
// Product version as a single DWORD // Product version as a single DWORD
@ -42,9 +46,28 @@
#define UPDATER_VERSION_STR "1.0" #define UPDATER_VERSION_STR "1.0"
#define UPDATER_BUILD_YEAR_STR "2016" #define UPDATER_BUILD_YEAR_STR "2016"
//
// Updater API level
//
#define wxUpdaterVersion "10"
//
// Resource IDs
//
#define UPDATER_IDR_KEY_PUBLIC 1
#if !defined(RC_INVOKED) && !defined(MIDL_PASS) #if !defined(RC_INVOKED) && !defined(MIDL_PASS)
///
/// Public function calling convention
///
#ifdef UPDATER
#define UPDATER_API __declspec(dllexport)
#else
#define UPDATER_API __declspec(dllimport)
#endif
#define UPDATER_SIGNATURE_MARK "SHA1SIGN:" #define UPDATER_SIGNATURE_MARK "SHA1SIGN:"
#endif // !defined(RC_INVOKED) && !defined(MIDL_PASS) #endif // !defined(RC_INVOKED) && !defined(MIDL_PASS)
#endif // !defined(__UPDATER_common_h__)

1
Updater/locale/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/*.mo

121
Updater/locale/Updater.pot Normal file
View File

@ -0,0 +1,121 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Updater\n"
"POT-Creation-Date: 2016-05-24 15:34+0200\n"
"PO-Revision-Date: 2016-02-06 09:04+0100\n"
"Last-Translator: Simon Rozman <simon.rozman@amebis.si>\n"
"Language-Team: Amebis, d. o. o., Kamnik <info@amebis.si>\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.7\n"
"X-Poedit-Basepath: ..\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: _\n"
"X-Poedit-SearchPath-0: src\n"
"X-Poedit-SearchPath-1: include\n"
#: src/chkthread.cpp:79 src/chkthread.cpp:92
msgid "Update check complete. Your product is up to date."
msgstr ""
#: src/chkthread.cpp:112
msgid "Downloading repository catalogue..."
msgstr ""
#: src/chkthread.cpp:120
#, c-format
msgid "Contacting repository at %s..."
msgstr ""
#: src/chkthread.cpp:128
#, c-format
msgid "Error resolving %s server name."
msgstr ""
#: src/chkthread.cpp:133
msgid "Repository did not change since the last time."
msgstr ""
#: src/chkthread.cpp:137
#, c-format
msgid "Error response received from server %s (port %u) requesting %s."
msgstr ""
#: src/chkthread.cpp:145
msgid "Error loading repository catalogue."
msgstr ""
#: src/chkthread.cpp:155
msgid "Verifying repository catalogue signature..."
msgstr ""
#: src/chkthread.cpp:186
msgid "Signature not found in the repository catalogue."
msgstr ""
#: src/chkthread.cpp:208
msgid "Repository catalogue signature does not match its content, or signature verification failed."
msgstr ""
#: src/chkthread.cpp:216
#, c-format
msgid "Invalid root element in repository catalogue (actual: %s, expected: %s)."
msgstr ""
#: src/chkthread.cpp:234
msgid "Parsing repository catalogue..."
msgstr ""
#: src/chkthread.cpp:319
#, c-format
msgid "Update package found (version: %s)."
msgstr ""
#: src/chkthread.cpp:377
msgid "Reading update package meta-information from cache failed or incomplete."
msgstr ""
#: src/chkthread.cpp:399
msgid "Update package file already downloaded."
msgstr ""
#: src/chkthread.cpp:406
#, c-format
msgid "Can not open/create %s file for writing."
msgstr ""
#: src/chkthread.cpp:426
#, c-format
msgid "Downloading update package from %s..."
msgstr ""
#: src/chkthread.cpp:429
msgid "Error response received."
msgstr ""
#: src/chkthread.cpp:448
#, c-format
msgid "Can not write to %s file."
msgstr ""
#: src/chkthread.cpp:462
msgid "Update package file corrupt."
msgstr ""
#: src/chkthread.cpp:471
msgid "Launching update..."
msgstr ""
#: src/chkthread.cpp:491
#, c-format
msgid "msiexec.exe launch succeeded. For further information, see %s file."
msgstr ""
#: src/chkthread.cpp:494
#, c-format
msgid "msiexec.exe launch failed. ShellExecute returned %i."
msgstr ""

122
Updater/locale/de_DE.po Normal file
View File

@ -0,0 +1,122 @@
msgid ""
msgstr ""
"Project-Id-Version: Updater\n"
"POT-Creation-Date: 2016-05-24 15:35+0200\n"
"PO-Revision-Date: 2016-05-24 15:35+0200\n"
"Last-Translator: Simon Rozman <simon.rozman@amebis.si>\n"
"Language-Team: Amebis, d. o. o., Kamnik <info@amebis.si>\n"
"Language: de_DE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.7\n"
"X-Poedit-Basepath: .\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: _\n"
#: src/chkthread.cpp:79 src/chkthread.cpp:92
msgid "Update check complete. Your product is up to date."
msgstr ""
#: src/chkthread.cpp:112
msgid "Downloading repository catalogue..."
msgstr ""
#: src/chkthread.cpp:120
#, c-format
msgid "Contacting repository at %s..."
msgstr ""
#: src/chkthread.cpp:128
#, c-format
msgid "Error resolving %s server name."
msgstr ""
#: src/chkthread.cpp:133
msgid "Repository did not change since the last time."
msgstr ""
#: src/chkthread.cpp:137
#, c-format
msgid "Error response received from server %s (port %u) requesting %s."
msgstr ""
#: src/chkthread.cpp:145
msgid "Error loading repository catalogue."
msgstr ""
#: src/chkthread.cpp:155
msgid "Verifying repository catalogue signature..."
msgstr ""
#: src/chkthread.cpp:186
msgid "Signature not found in the repository catalogue."
msgstr ""
#: src/chkthread.cpp:208
msgid ""
"Repository catalogue signature does not match its content, or signature "
"verification failed."
msgstr ""
#: src/chkthread.cpp:216
#, c-format
msgid ""
"Invalid root element in repository catalogue (actual: %s, expected: %s)."
msgstr ""
#: src/chkthread.cpp:234
msgid "Parsing repository catalogue..."
msgstr ""
#: src/chkthread.cpp:319
#, c-format
msgid "Update package found (version: %s)."
msgstr ""
#: src/chkthread.cpp:377
msgid ""
"Reading update package meta-information from cache failed or incomplete."
msgstr ""
#: src/chkthread.cpp:399
msgid "Update package file already downloaded."
msgstr ""
#: src/chkthread.cpp:406
#, c-format
msgid "Can not open/create %s file for writing."
msgstr ""
#: src/chkthread.cpp:426
#, c-format
msgid "Downloading update package from %s..."
msgstr ""
#: src/chkthread.cpp:429
msgid "Error response received."
msgstr ""
#: src/chkthread.cpp:448
#, c-format
msgid "Can not write to %s file."
msgstr ""
#: src/chkthread.cpp:462
msgid "Update package file corrupt."
msgstr ""
#: src/chkthread.cpp:471
msgid "Launching update..."
msgstr ""
#: src/chkthread.cpp:491
#, c-format
msgid "msiexec.exe launch succeeded. For further information, see %s file."
msgstr ""
#: src/chkthread.cpp:494
#, c-format
msgid "msiexec.exe launch failed. ShellExecute returned %i."
msgstr ""

123
Updater/locale/ru_RU.po Normal file
View File

@ -0,0 +1,123 @@
msgid ""
msgstr ""
"Project-Id-Version: Updater\n"
"POT-Creation-Date: 2016-05-24 15:35+0200\n"
"PO-Revision-Date: 2016-05-24 15:35+0200\n"
"Last-Translator: Simon Rozman <simon.rozman@amebis.si>\n"
"Language-Team: Amebis, d. o. o., Kamnik <info@amebis.si>\n"
"Language: ru_RU\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.7\n"
"X-Poedit-Basepath: .\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: _\n"
#: src/chkthread.cpp:79 src/chkthread.cpp:92
msgid "Update check complete. Your product is up to date."
msgstr ""
#: src/chkthread.cpp:112
msgid "Downloading repository catalogue..."
msgstr ""
#: src/chkthread.cpp:120
#, c-format
msgid "Contacting repository at %s..."
msgstr ""
#: src/chkthread.cpp:128
#, c-format
msgid "Error resolving %s server name."
msgstr ""
#: src/chkthread.cpp:133
msgid "Repository did not change since the last time."
msgstr ""
#: src/chkthread.cpp:137
#, c-format
msgid "Error response received from server %s (port %u) requesting %s."
msgstr ""
#: src/chkthread.cpp:145
msgid "Error loading repository catalogue."
msgstr ""
#: src/chkthread.cpp:155
msgid "Verifying repository catalogue signature..."
msgstr ""
#: src/chkthread.cpp:186
msgid "Signature not found in the repository catalogue."
msgstr ""
#: src/chkthread.cpp:208
msgid ""
"Repository catalogue signature does not match its content, or signature "
"verification failed."
msgstr ""
#: src/chkthread.cpp:216
#, c-format
msgid ""
"Invalid root element in repository catalogue (actual: %s, expected: %s)."
msgstr ""
#: src/chkthread.cpp:234
msgid "Parsing repository catalogue..."
msgstr ""
#: src/chkthread.cpp:319
#, c-format
msgid "Update package found (version: %s)."
msgstr ""
#: src/chkthread.cpp:377
msgid ""
"Reading update package meta-information from cache failed or incomplete."
msgstr ""
#: src/chkthread.cpp:399
msgid "Update package file already downloaded."
msgstr ""
#: src/chkthread.cpp:406
#, c-format
msgid "Can not open/create %s file for writing."
msgstr ""
#: src/chkthread.cpp:426
#, c-format
msgid "Downloading update package from %s..."
msgstr ""
#: src/chkthread.cpp:429
msgid "Error response received."
msgstr ""
#: src/chkthread.cpp:448
#, c-format
msgid "Can not write to %s file."
msgstr ""
#: src/chkthread.cpp:462
msgid "Update package file corrupt."
msgstr ""
#: src/chkthread.cpp:471
msgid "Launching update..."
msgstr ""
#: src/chkthread.cpp:491
#, c-format
msgid "msiexec.exe launch succeeded. For further information, see %s file."
msgstr ""
#: src/chkthread.cpp:494
#, c-format
msgid "msiexec.exe launch failed. ShellExecute returned %i."
msgstr ""

130
Updater/locale/sl_SI.po Normal file
View File

@ -0,0 +1,130 @@
msgid ""
msgstr ""
"Project-Id-Version: Updater\n"
"POT-Creation-Date: 2016-05-24 15:34+0200\n"
"PO-Revision-Date: 2016-05-24 15:35+0200\n"
"Last-Translator: Simon Rozman <simon.rozman@amebis.si>\n"
"Language-Team: Amebis, d. o. o., Kamnik <info@amebis.si>\n"
"Language: sl_SI\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.7\n"
"X-Poedit-Basepath: .\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
"%100==4 ? 2 : 3);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: _\n"
#: src/chkthread.cpp:79 src/chkthread.cpp:92
msgid "Update check complete. Your product is up to date."
msgstr "Preverjanje posodobitev končano. Vaš izdelek je zadnje verzije."
#: src/chkthread.cpp:112
msgid "Downloading repository catalogue..."
msgstr "Prenos repozitorijevega kataloga ..."
#: src/chkthread.cpp:120
#, c-format
msgid "Contacting repository at %s..."
msgstr "Dostop do repozitorija na %s ..."
#: src/chkthread.cpp:128
#, c-format
msgid "Error resolving %s server name."
msgstr "Napaka pri ugotavljanju naslova strežnika %s."
#: src/chkthread.cpp:133
msgid "Repository did not change since the last time."
msgstr "Repozitorij se od zadnjič ni spremenil."
#: src/chkthread.cpp:137
#, c-format
msgid "Error response received from server %s (port %u) requesting %s."
msgstr "Prejet odgovor o napaki s strežnika %s (vrata %u) pri zahtevi za %s."
#: src/chkthread.cpp:145
msgid "Error loading repository catalogue."
msgstr "Napaka pri nalaganju repozitorijevega kataloga."
#: src/chkthread.cpp:155
msgid "Verifying repository catalogue signature..."
msgstr "Preverjanje podpisa repozitorijevega kataloga ..."
#: src/chkthread.cpp:186
msgid "Signature not found in the repository catalogue."
msgstr "V repozitorijevem katalogu ni podpisa."
#: src/chkthread.cpp:208
msgid ""
"Repository catalogue signature does not match its content, or signature "
"verification failed."
msgstr ""
"Podpis repozitorijevega kataloga ne ustreza njegovi vsebini, oziroma "
"preverjanje podpisa ni uspelo."
#: src/chkthread.cpp:216
#, c-format
msgid ""
"Invalid root element in repository catalogue (actual: %s, expected: %s)."
msgstr ""
"Napačen korenski element v repozitorijevem katalogu (dejanski: %s, "
"pričakovan: %s)."
#: src/chkthread.cpp:234
msgid "Parsing repository catalogue..."
msgstr "Razčlemba repozitorijevega kataloga ..."
#: src/chkthread.cpp:319
#, c-format
msgid "Update package found (version: %s)."
msgstr "Posodobitveni paket najden (verzija: %s)."
#: src/chkthread.cpp:377
msgid ""
"Reading update package meta-information from cache failed or incomplete."
msgstr ""
"Branje meta-informacij posodobitvenega paketa iz predpomnilnika ni uspelo "
"ali ni bilo celotno."
#: src/chkthread.cpp:399
msgid "Update package file already downloaded."
msgstr "Posodobitveni paket je že prenesen."
#: src/chkthread.cpp:406
#, c-format
msgid "Can not open/create %s file for writing."
msgstr "Ne morem odpreti/ustvariti datoteke %s za pisanje."
#: src/chkthread.cpp:426
#, c-format
msgid "Downloading update package from %s..."
msgstr "Prenos posodobitvenega paketa s/z %s ..."
#: src/chkthread.cpp:429
msgid "Error response received."
msgstr "Prejet odgovor o napaki."
#: src/chkthread.cpp:448
#, c-format
msgid "Can not write to %s file."
msgstr "Ne morem pisati v datoteko %s."
#: src/chkthread.cpp:462
msgid "Update package file corrupt."
msgstr "Posodobitveni paket je pokvarjen."
#: src/chkthread.cpp:471
msgid "Launching update..."
msgstr "Zagon posodobitve ..."
#: src/chkthread.cpp:491
#, c-format
msgid "msiexec.exe launch succeeded. For further information, see %s file."
msgstr ""
"Zagon msiexec.exe je uspel. Za nadaljnje informacije glejte datoteko %s."
#: src/chkthread.cpp:494
#, c-format
msgid "msiexec.exe launch failed. ShellExecute returned %i."
msgstr "Zagon msiexec.exe ni uspel. ShellExecute je vrnil %i."

BIN
Updater/res/Updater.rc Normal file

Binary file not shown.

518
Updater/src/chkthread.cpp Normal file
View File

@ -0,0 +1,518 @@
/*
Copyright 2016 Amebis
This file is part of Updater.
Updater is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Updater is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Updater. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
wxDEFINE_EVENT(wxEVT_UPDATER_CHECK_COMPLETE, wxThreadEvent);
//////////////////////////////////////////////////////////////////////////
// wxUpdCheckThread
//////////////////////////////////////////////////////////////////////////
wxUpdCheckThread::wxUpdCheckThread(const wxString &langId, wxEvtHandler *parent) :
m_parent(parent),
m_langId(langId),
m_config(wxT(UPDATER_CFG_APPLICATION) wxT("\\Updater"), wxT(UPDATER_CFG_VENDOR)),
m_cs(NULL),
m_ck(NULL),
m_version(0),
wxThread(wxTHREAD_JOINABLE)
{
m_path = wxFileName::GetTempDir();
if (!wxEndsWithPathSeparator(m_path))
m_path += wxFILE_SEP_PATH;
if (!wxDirExists(m_path))
wxMkdir(m_path);
m_cs = new wxCryptoSessionRSAAES();
wxCHECK2_MSG(m_cs, { m_ok = false; return; }, wxT("wxCryptoSessionRSAAES creation failed"));
// Import public key.
HRSRC res = ::FindResource(g_hInstance, MAKEINTRESOURCE(UPDATER_IDR_KEY_PUBLIC), RT_RCDATA);
wxCHECK2_MSG(res, { m_ok = false; return; }, wxT("public key not found"));
HGLOBAL res_handle = ::LoadResource(g_hInstance, res);
wxCHECK2_MSG(res_handle, { m_ok = false; return; }, wxT("loading resource failed"));
m_ck = new wxCryptoKey();
wxCHECK2_MSG(m_ck, { m_ok = false; return; }, wxT("wxCryptoKey creation failed"));
wxCHECK2(m_ck->ImportPublic(*m_cs, ::LockResource(res_handle), ::SizeofResource(g_hInstance, res)), { m_ok = false; return; });
}
wxUpdCheckThread::~wxUpdCheckThread()
{
if (m_ck) delete m_ck;
if (m_cs) delete m_cs;
}
wxThread::ExitCode wxUpdCheckThread::Entry()
{
wxResult result = DoCheckForUpdate();
if (m_parent) {
// Signal the event handler that this thread is going to be destroyed.
// NOTE: here we assume that using the m_parent pointer is safe,
// (in this case this is assured by the wxZRColaCharSelect destructor)
wxThreadEvent *e = new wxThreadEvent(wxEVT_UPDATER_CHECK_COMPLETE);
e->SetInt(result);
wxQueueEvent(m_parent, e);
}
return (wxThread::ExitCode)(int)result;
}
wxUpdCheckThread::wxResult wxUpdCheckThread::DoCheckForUpdate()
{
if (!m_ok)
return wxUpdUninitialized;
// Download update catalogue.
if (TestDestroy()) return wxUpdAborted;
wxXmlDocument *doc = GetCatalogue();
if (doc) {
// Parse the update catalogue to check for an update package availability.
if (TestDestroy()) return wxUpdAborted;
if (ParseCatalogue(*doc)) {
// Save update package meta-information for the next time.
if (TestDestroy()) return wxUpdAborted;
WriteUpdatePackageMeta();
} else {
wxLogStatus(_("Update check complete. Your product is up to date."));
return wxUpdUpToDate;
}
} else {
// Update download catalogue failed, or repository database didn't change. Read a cached version of update package meta-information?
if (TestDestroy()) return wxUpdAborted;
if (!ReadUpdatePackageMeta()) {
// Reset CatalogueLastModified to force update catalogue download next time.
m_config.DeleteEntry(wxT("CatalogueLastModified"));
return wxUpdRepoUnavailable;
}
if (m_version <= UPDATER_PRODUCT_VERSION) {
wxLogStatus(_("Update check complete. Your product is up to date."));
return wxUpdUpToDate;
}
}
m_fileName = m_path;
m_fileName += wxT("Updater-") wxT(UPDATER_CFG_APPLICATION) wxT("-");
m_fileName += m_versionStr;
m_fileName += wxT(".msi");
if (TestDestroy()) return wxUpdAborted;
if (!DownloadUpdatePackage())
return wxUpdPkgUnavailable;
return wxUpdUpdateAvailable;
}
wxXmlDocument* wxUpdCheckThread::GetCatalogue()
{
wxLogStatus(_("Downloading repository catalogue..."));
wxString lastModified;
m_config.Read(wxT("CatalogueLastModified"), &lastModified, wxEmptyString);
for (const wxChar *server = wxT(UPDATER_HTTP_SERVER); server[0]; server += wcslen(server) + 1) {
if (TestDestroy()) return NULL;
wxLogStatus(_("Contacting repository at %s..."), server);
// Load repository database.
wxHTTP http;
http.SetHeader(wxS("User-Agent"), wxS("Updater/") wxS(UPDATER_VERSION_STR));
if (!lastModified.IsEmpty())
http.SetHeader(wxS("If-Modified-Since"), lastModified);
if (!http.Connect(server, UPDATER_HTTP_PORT)) {
wxLogWarning(_("Error resolving %s server name."), server);
continue;
}
wxInputStream *httpStream = http.GetInputStream(wxS(UPDATER_HTTP_PATH));
if (http.GetResponse() == 304) {
wxLogStatus(_("Repository did not change since the last time."));
wxDELETE(httpStream);
return NULL;
} else if (!httpStream) {
wxLogWarning(_("Error response received from server %s (port %u) requesting %s."), server, UPDATER_HTTP_PORT, UPDATER_HTTP_PATH);
continue;
} else if (TestDestroy()) {
wxDELETE(httpStream);
return NULL;
}
wxXmlDocument *doc = new wxXmlDocument();
if (!doc->Load(*httpStream, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) {
wxLogWarning(_("Error loading repository catalogue."));
wxDELETE(httpStream);
http.Close();
delete doc;
continue;
}
wxDELETE(httpStream);
m_config.Write(wxT("CatalogueLastModified"), http.GetHeader(wxT("Last-Modified")));
http.Close();
wxLogStatus(_("Verifying repository catalogue signature..."));
// Find the (first) signature.
if (TestDestroy()) return NULL;
wxXmlNode *document = doc->GetDocumentNode();
wxMemoryBuffer sig;
for (wxXmlNode *prolog = document->GetChildren(); prolog; prolog = prolog->GetNext()) {
if (prolog->GetType() == wxXML_COMMENT_NODE) {
wxString content = prolog->GetContent();
size_t content_len = content.length();
if (content_len >= _countof(wxS(UPDATER_SIGNATURE_MARK)) - 1 &&
memcmp((const wxStringCharType*)content, wxS(UPDATER_SIGNATURE_MARK), sizeof(wxStringCharType)*(_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1)) == 0)
{
// Read the signature.
size_t signature_len = content_len - (_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1);
size_t len = wxBase64DecodedSize(signature_len);
size_t res = wxBase64Decode(sig.GetWriteBuf(len), len, content.Right(signature_len), wxBase64DecodeMode_SkipWS);
if (res != wxCONV_FAILED) {
sig.SetDataLen(res);
// Remove the signature as it is not a part of the validation check.
document->RemoveChild(prolog);
wxDELETE(prolog);
break;
} else
sig.Clear();
}
}
}
if (sig.IsEmpty()) {
wxLogWarning(_("Signature not found in the repository catalogue."));
delete doc;
continue;
}
// Hash the content.
if (TestDestroy()) {
delete doc;
return NULL;
}
wxCryptoHashSHA1 ch(*m_cs);
if (!wxXmlHashNode(ch, document)) {
delete doc;
continue;
}
// We have the hash, we have the signature, we have the public key. Now verify.
if (TestDestroy()) {
delete doc;
return NULL;
}
if (!wxCryptoVerifySignature(ch, sig, *m_ck)) {
wxLogWarning(_("Repository catalogue signature does not match its content, or signature verification failed."));
delete doc;
continue;
}
// Verify document type (root element).
const wxString &nameRoot = doc->GetRoot()->GetName();
if (nameRoot != wxT("Packages")) {
wxLogWarning(_("Invalid root element in repository catalogue (actual: %s, expected: %s)."), nameRoot.c_str(), wxT("Packages"));
delete doc;
continue;
}
// The downloaded repository database passed all checks.
return doc;
}
// No repository database downloaded successfully.
return NULL;
}
bool wxUpdCheckThread::ParseCatalogue(const wxXmlDocument &doc)
{
bool found = false;
wxLogStatus(_("Parsing repository catalogue..."));
// Iterate over packages.
for (wxXmlNode *elPackage = doc.GetRoot()->GetChildren(); elPackage; elPackage = elPackage->GetNext()) {
if (TestDestroy()) return false;
if (elPackage->GetType() == wxXML_ELEMENT_NODE && elPackage->GetName() == wxT("Package")) {
// Get package version.
wxUint32 version = 0;
wxString versionStr;
for (wxXmlNode *elVersion = elPackage->GetChildren(); elVersion; elVersion = elVersion->GetNext()) {
if (elVersion->GetType() == wxXML_ELEMENT_NODE && elVersion->GetName() == wxT("Version")) {
for (wxXmlNode *elVersionNote = elVersion->GetChildren(); elVersionNote; elVersionNote = elVersionNote->GetNext()) {
if (elVersionNote->GetType() == wxXML_ELEMENT_NODE) {
const wxString &name = elVersionNote->GetName();
if (name == wxT("hex"))
version = _tcstoul(elVersionNote->GetNodeContent(), NULL, 16);
else if (name == wxT("desc"))
versionStr = elVersionNote->GetNodeContent();
}
}
}
}
if (version <= UPDATER_PRODUCT_VERSION || version < m_version) {
// This package is older than currently installed product or the superseeded package found already.
continue;
}
// Get package download URL for our platform and language.
wxString platformId;
#if defined(__WINDOWS__)
platformId += wxT("win");
#endif
#if defined(_WIN64)
platformId += wxT("-amd64");
#else
platformId += wxT("-x86");
#endif
wxArrayString urls;
wxMemoryBuffer hash;
for (wxXmlNode *elDownloads = elPackage->GetChildren(); elDownloads; elDownloads = elDownloads->GetNext()) {
if (elDownloads->GetType() == wxXML_ELEMENT_NODE && elDownloads->GetName() == wxT("Downloads")) {
for (wxXmlNode *elPlatform = elDownloads->GetChildren(); elPlatform; elPlatform = elPlatform->GetNext()) {
if (elPlatform->GetType() == wxXML_ELEMENT_NODE) {
if (elPlatform->GetName() == wxT("Platform") && elPlatform->GetAttribute(wxT("id")) == platformId) {
// Get language.
for (wxXmlNode *elLocale = elPlatform->GetChildren(); elLocale; elLocale = elLocale->GetNext()) {
if (elLocale->GetType() == wxXML_ELEMENT_NODE && elLocale->GetName() == wxT("Localization") && elLocale->GetAttribute(wxT("lang")) == m_langId) {
for (wxXmlNode *elLocaleNote = elLocale->GetChildren(); elLocaleNote; elLocaleNote = elLocaleNote->GetNext()) {
if (elLocaleNote->GetType() == wxXML_ELEMENT_NODE) {
const wxString &name = elLocaleNote->GetName();
if (name == wxT("url"))
urls.Add(elLocaleNote->GetNodeContent());
else if (name == wxT("hash")) {
// Read the hash.
wxString content(elLocaleNote->GetNodeContent());
size_t len = wxHexDecodedSize(content.length());
size_t res = wxHexDecode(hash.GetWriteBuf(len), len, content, wxHexDecodeMode_SkipWS);
if (res != wxCONV_FAILED)
hash.SetDataLen(res);
else
hash.Clear();
}
}
}
}
}
}
}
}
}
}
if (urls.empty() || hash.IsEmpty()) {
// This package is for different architecture and/or language.
// ... or missing URL and/or hash.
continue;
}
// Save found package info.
m_version = version;
m_versionStr = versionStr;
m_urls = urls;
m_hash = hash;
found = true;
wxLogMessage(_("Update package found (version: %s)."), m_versionStr.c_str());
}
}
return found;
}
bool wxUpdCheckThread::WriteUpdatePackageMeta()
{
// Concatenate URLs.
wxString urls;
for (size_t i = 0, n = m_urls.GetCount(); i < n; i++) {
if (i) urls += wxT('\n');
urls += m_urls[i];
}
bool result = true;
if (!m_config.Write(wxT("PackageVersion" ), m_version )) result = false;
if (!m_config.Write(wxT("PackageVersionDesc"), m_versionStr )) result = false;
if (!m_config.Write(wxT("PackageURLs" ), urls )) result = false;
if (!m_config.Write(wxT("PackageHash" ), wxHexEncode(m_hash))) result = false;
return result;
}
bool wxUpdCheckThread::ReadUpdatePackageMeta()
{
long lng;
if (m_config.Read(wxT("PackageVersion"), &lng)) {
m_version = lng;
if (m_config.Read(wxT("PackageVersionDesc"), &m_versionStr)) {
wxString str;
if (m_config.Read(wxT("PackageURLs"), &str)) {
// Split URLs
m_urls.Clear();
const wxStringCharType *buf = str;
for (size_t i = 0, j = 0, n = str.Length(); i < n;) {
for (; j < n && buf[j] != wxT('\n'); j++);
m_urls.Add(str.SubString(i, j - 1));
i = ++j;
}
if (m_config.Read(wxT("PackageHash"), &str)) {
// Convert to binary.
size_t len = wxHexDecodedSize(str.length());
size_t res = wxHexDecode(m_hash.GetWriteBuf(len), len, str, wxHexDecodeMode_SkipWS);
if (res != wxCONV_FAILED) {
m_hash.SetDataLen(res);
return true;
} else
m_hash.Clear();
}
}
}
}
wxLogWarning(_("Reading update package meta-information from cache failed or incomplete."));
m_version = 0;
m_versionStr.Clear();
m_urls.Clear();
m_hash.Clear();
return false;
}
bool wxUpdCheckThread::DownloadUpdatePackage()
{
// Calculate file hash (if exists).
wxCryptoHashSHA1 ch(*m_cs);
if (ch.HashFile(m_fileName)) {
wxMemoryBuffer buf;
ch.GetValue(buf);
if (buf.GetDataLen() == m_hash.GetDataLen() &&
memcmp(buf.GetData(), m_hash.GetData(), buf.GetDataLen()) == 0)
{
// Update package file exists and its hash is correct.
wxLogStatus(_("Update package file already downloaded."));
return true;
}
}
wxFFile file(m_fileName, wxT("wb"));
if (!file.IsOpened()) {
wxLogError(_("Can not open/create %s file for writing."), m_fileName.c_str());
return false;
}
// Download update package file.
for (size_t i = 0, n = m_urls.GetCount(); i < n; i++) {
if (TestDestroy()) return false;
wxURL url(m_urls[i]);
if (!url.IsOk())
continue;
if (url.HasScheme()) {
const wxString scheme = url.GetScheme();
if (scheme == wxT("http") || scheme == wxT("https")) {
wxHTTP &http = (wxHTTP&)url.GetProtocol();
http.SetHeader(wxS("User-Agent"), wxS("Updater/") wxS(UPDATER_VERSION_STR));
}
}
wxLogStatus(_("Downloading update package from %s..."), m_urls[i].c_str());
wxInputStream *stream = url.GetInputStream();
if (!stream) {
wxLogWarning(_("Error response received."));
continue;
}
// Save update package to file, and calculate hash.
wxCryptoHashSHA1 ch(*m_cs);
wxMemoryBuffer buf(4*1024);
char *data = (char*)buf.GetData();
size_t nBlock = buf.GetBufSize();
do {
if (TestDestroy()) {
wxDELETE(stream);
return false;
}
stream->Read(data, nBlock);
size_t nRead = stream->LastRead();
file.Write(data, nRead);
if (file.Error()) {
wxLogError(_("Can not write to %s file."), m_fileName.c_str());
return false;
}
ch.Hash(data, nRead);
} while (stream->IsOk());
ch.GetValue(buf);
wxDELETE(stream);
if (buf.GetDataLen() == m_hash.GetDataLen() &&
memcmp(buf.GetData(), m_hash.GetData(), buf.GetDataLen()) == 0)
{
// Update package file downloaded and its hash is correct.
return true;
} else
wxLogWarning(_("Update package file corrupt."));
}
return false;
}
bool wxUpdCheckThread::LaunchUpdate()
{
wxLogStatus(_("Launching update..."));
// Headless Install
wxString param("/qn");
// Package
param += wxT(" /i \"");
param += m_fileName;
param += wxT("\"");
// Logging
wxString fileNameLog(m_path);
fileNameLog += wxT("Updater-") wxT(UPDATER_CFG_APPLICATION) wxT("-msiexec.log");
param += wxT(" /l* \"");
param += fileNameLog;
param += wxT("\"");
int result = (int)::ShellExecute(NULL, NULL, wxT("msiexec.exe"), param, NULL, SW_SHOWNORMAL);
if (result > 32) {
wxLogStatus(_("msiexec.exe launch succeeded. For further information, see %s file."), fileNameLog.c_str());
return true;
} else {
wxLogError(_("msiexec.exe launch failed. ShellExecute returned %i."), result);
return false;
}
}

40
Updater/src/main.cpp Normal file
View File

@ -0,0 +1,40 @@
/*
Copyright 2016 Amebis
This file is part of Updater.
Updater is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Updater is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Updater. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
///
/// Module instance
///
HINSTANCE g_hInstance = NULL;
///
/// Main function
///
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
{
UNREFERENCED_PARAMETER(lpvReserved);
if (fdwReason == DLL_PROCESS_ATTACH)
g_hInstance = hinstDLL;
return TRUE;
}

20
Updater/src/stdafx.cpp Normal file
View File

@ -0,0 +1,20 @@
/*
Copyright 2016 Amebis
This file is part of Updater.
Updater is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Updater is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Updater. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"

37
Updater/src/stdafx.h Normal file
View File

@ -0,0 +1,37 @@
/*
Copyright 2016 Amebis
This file is part of Updater.
Updater is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Updater is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Updater. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../include/Updater/chkthread.h"
#include <wx/base64.h>
#include <wx/buffer.h>
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/protocol/http.h>
#include <wx/url.h>
#include <wxex/hex.h>
#include <wxex/xml.h>
//
// Private data declaration
//
extern HINSTANCE g_hInstance;