diff --git a/UpdCheck/MSIBuild/Makefile b/UpdCheck/MSIBuild/Makefile index e8c2f85..194f552 100644 --- a/UpdCheck/MSIBuild/Makefile +++ b/UpdCheck/MSIBuild/Makefile @@ -82,6 +82,7 @@ s38 s$(MSIBUILD_LENGTH_ID) FeatureComponents Feature_ Component_ featUpdCheck compUpdCheck.exe.$(PLAT) featUpdCheck compwxExtend.dll.$(PLAT) +featUpdCheck compUpdater.dll.$(PLAT) < - ..\..\lib\wxExtend\include;%(AdditionalIncludeDirectories) + ..\Updater\include;..\..\lib\wxExtend\include;%(AdditionalIncludeDirectories) diff --git a/UpdCheck/UpdCheck.rc b/UpdCheck/UpdCheck.rc index e654d0c..671cc05 100644 Binary files a/UpdCheck/UpdCheck.rc and b/UpdCheck/UpdCheck.rc differ diff --git a/UpdCheck/UpdCheck.vcxproj b/UpdCheck/UpdCheck.vcxproj index 6468b51..dfe1067 100644 --- a/UpdCheck/UpdCheck.vcxproj +++ b/UpdCheck/UpdCheck.vcxproj @@ -85,7 +85,6 @@ - @@ -103,6 +102,9 @@ {a3a36689-ac35-4026-93da-a3ba0c0e767c} + + {990d8cf9-4457-4dc0-aa18-4968ef434741} + diff --git a/UpdCheck/UpdCheck.vcxproj.filters b/UpdCheck/UpdCheck.vcxproj.filters index 1a5479f..6fd5bfc 100644 --- a/UpdCheck/UpdCheck.vcxproj.filters +++ b/UpdCheck/UpdCheck.vcxproj.filters @@ -30,9 +30,6 @@ Header Files - - Header Files - diff --git a/UpdCheck/locale/UpdCheck.pot b/UpdCheck/locale/UpdCheck.pot index 9039e82..3c99afb 100644 --- a/UpdCheck/locale/UpdCheck.pot +++ b/UpdCheck/locale/UpdCheck.pot @@ -2,7 +2,7 @@ msgid "" msgstr "" "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" "Last-Translator: Simon Rozman \n" "Language-Team: Amebis, d. o. o., Kamnik \n" @@ -18,33 +18,45 @@ msgstr "" "X-Poedit-KeywordsList: _\n" "X-Poedit-SearchPath-0: .\n" +#: MSIBuild/En.Win32.Debug.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 #, fuzzy msgid "[SimpleProductName] Update" 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.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.ScheduledTask-2.idtx:3 #, fuzzy msgid "1252" msgstr "1250" +#: MSIBuild/En.Win32.Debug.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 #, fuzzy msgid "Automatic Updating" msgstr "Samodejno posodabljanje" +#: MSIBuild/En.Win32.Debug.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 #, fuzzy msgid "Regularly checks for program updates, downloads, and installs them" 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.x64.Debug.Feature-2.idtx:4 #: MSIBuild/En.x64.Release.Feature-2.idtx:4 #, fuzzy msgid "Task for background product updating" diff --git a/UpdCheck/main.cpp b/UpdCheck/main.cpp index 06e628b..b9f9fa0 100644 --- a/UpdCheck/main.cpp +++ b/UpdCheck/main.cpp @@ -20,558 +20,6 @@ #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 /// @@ -584,7 +32,34 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In if (!initializer.IsOk()) return -1; - // Do everything. - wxUpdCheckApp app; - return app.Run(); + // Create output folder. + wxString path(wxFileName::GetTempDir()); + 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; + } } diff --git a/UpdCheck/stdafx.h b/UpdCheck/stdafx.h index fd67c2b..8e00fb0 100644 --- a/UpdCheck/stdafx.h +++ b/UpdCheck/stdafx.h @@ -19,23 +19,13 @@ #pragma once -#include "../../include/UpdaterCfg.h" - -#include "UpdCheck.h" -#include "../include/Updater.h" +#include #include -#include -#include -#include #include #include #include #include -#include -#include -#include +#include -#include -#include -#include +#include diff --git a/UpdPublish/UpdPublish.rc b/UpdPublish/UpdPublish.rc index c37804f..4b3622b 100644 Binary files a/UpdPublish/UpdPublish.rc and b/UpdPublish/UpdPublish.rc differ diff --git a/UpdPublish/stdafx.h b/UpdPublish/stdafx.h index e4bc9fd..bfc7962 100644 --- a/UpdPublish/stdafx.h +++ b/UpdPublish/stdafx.h @@ -19,10 +19,9 @@ #pragma once -#include "../../include/UpdaterCfg.h" +#include "../Updater/include/Updater/common.h" #include "UpdPublish.h" -#include "../include/Updater.h" #include #include diff --git a/UpdSignXML/UpdSignXML.rc b/UpdSignXML/UpdSignXML.rc index 7b7ad3c..df8f52d 100644 Binary files a/UpdSignXML/UpdSignXML.rc and b/UpdSignXML/UpdSignXML.rc differ diff --git a/UpdSignXML/stdafx.h b/UpdSignXML/stdafx.h index 2d76bd1..9a2cd9c 100644 --- a/UpdSignXML/stdafx.h +++ b/UpdSignXML/stdafx.h @@ -19,8 +19,9 @@ #pragma once +#include "../Updater/include/Updater/common.h" + #include "UpdSignXML.h" -#include "../include/Updater.h" #include #include diff --git a/Updater.sln b/Updater.sln index 5719922..c00a821 100644 --- a/Updater.sln +++ b/Updater.sln @@ -3,11 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{A7D28E0C-BB96-444D-AAB0-F22A6483FE5F}" 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}" EndProject 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 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdPublish", "UpdPublish\UpdPublish.vcxproj", "{C5829278-D55E-41EB-B866-1E7D814694D1}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Updater", "Updater\build\Updater.vcxproj", "{990D8CF9-4457-4DC0-AA18-4968EF434741}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution 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|x64.ActiveCfg = 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 GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {A3A36689-AC35-4026-93DA-A3BA0C0E767C} = {A7D28E0C-BB96-444D-AAB0-F22A6483FE5F} + {990D8CF9-4457-4DC0-AA18-4968EF434741} = {A7D28E0C-BB96-444D-AAB0-F22A6483FE5F} EndGlobalSection EndGlobal diff --git a/Updater/MSIBuild/.gitignore b/Updater/MSIBuild/.gitignore new file mode 100644 index 0000000..80ea676 --- /dev/null +++ b/Updater/MSIBuild/.gitignore @@ -0,0 +1,9 @@ +/*-1.idt +/*-2.idt +/*-2.idtx +/*.Binary-1 +/*.Binary-2 +/*.Icon-1 +/*.Icon-2 +/*.lst +/*.msm diff --git a/Updater/MSIBuild/Makefile b/Updater/MSIBuild/Makefile new file mode 100644 index 0000000..25cfc3c --- /dev/null +++ b/Updater/MSIBuild/Makefile @@ -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 . +# + +!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 +< NUL +Directory Directory_Parent DefaultDir +s$(MSIBUILD_LENGTH_ID) S$(MSIBUILD_LENGTH_ID) l255 +Directory Directory +UPDATERLOCSLSIDIR $(UPDATER_LOC_DIR) sl_SI +< 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 +< + + + + 10 + + + ..\..\..\output\$(Platform).$(Configuration)\ + + + + UPDATER;%(PreprocessorDefinitions) + ..\..\..\lib\wxExtend\include;%(AdditionalIncludeDirectories) + + + $(OutDir)..\locale\%(Filename)\$(ProjectName)$(UpdaterVersion).mo + + + + + $(UpdaterVersion) + + + \ No newline at end of file diff --git a/Updater/build/Updater.vcxproj b/Updater/build/Updater.vcxproj new file mode 100644 index 0000000..1241e73 --- /dev/null +++ b/Updater/build/Updater.vcxproj @@ -0,0 +1,124 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + {a3a36689-ac35-4026-93da-a3ba0c0e767c} + + + + {990D8CF9-4457-4DC0-AA18-4968EF434741} + Updater + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(ProjectName)$(UpdaterVersion)ud_vc$(PlatformToolsetVersion) + + + $(ProjectName)$(UpdaterVersion)ud_vc$(PlatformToolsetVersion)_$(Platform) + + + $(ProjectName)$(UpdaterVersion)u_vc$(PlatformToolsetVersion) + + + $(ProjectName)$(UpdaterVersion)u_vc$(PlatformToolsetVersion)_$(Platform) + + + + + + + \ No newline at end of file diff --git a/Updater/build/Updater.vcxproj.filters b/Updater/build/Updater.vcxproj.filters new file mode 100644 index 0000000..5117ec7 --- /dev/null +++ b/Updater/build/Updater.vcxproj.filters @@ -0,0 +1,64 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {e43059ae-37ac-4b28-84fb-18d1b3972b30} + po;pot + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files\Localization + + + + + Resource Files\Localization + + + Resource Files\Localization + + + Resource Files\Localization + + + + + Resource Files + + + \ No newline at end of file diff --git a/Updater/include/Updater/chkthread.h b/Updater/include/Updater/chkthread.h new file mode 100644 index 0000000..8563c99 --- /dev/null +++ b/Updater/include/Updater/chkthread.h @@ -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 . +*/ + +#pragma once + +#include "common.h" + +#include +#include +#include +#include +#include +#include + +#include + + +/// +/// 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 +}; diff --git a/include/Updater.h b/Updater/include/Updater/common.h similarity index 73% rename from include/Updater.h rename to Updater/include/Updater/common.h index 79b8d76..cb27cbd 100644 --- a/include/Updater.h +++ b/Updater/include/Updater/common.h @@ -17,7 +17,11 @@ along with Updater. If not, see . */ -#pragma once +#if !defined(__UPDATER_common_h__) +#define __UPDATER_common_h__ + +#include "../../../../include/UpdaterCfg.h" + // // Product version as a single DWORD @@ -42,9 +46,28 @@ #define UPDATER_VERSION_STR "1.0" #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) +/// +/// Public function calling convention +/// +#ifdef UPDATER +#define UPDATER_API __declspec(dllexport) +#else +#define UPDATER_API __declspec(dllimport) +#endif + #define UPDATER_SIGNATURE_MARK "SHA1SIGN:" #endif // !defined(RC_INVOKED) && !defined(MIDL_PASS) +#endif // !defined(__UPDATER_common_h__) diff --git a/Updater/locale/.gitignore b/Updater/locale/.gitignore new file mode 100644 index 0000000..85ebba8 --- /dev/null +++ b/Updater/locale/.gitignore @@ -0,0 +1 @@ +/*.mo diff --git a/Updater/locale/Updater.pot b/Updater/locale/Updater.pot new file mode 100644 index 0000000..2324743 --- /dev/null +++ b/Updater/locale/Updater.pot @@ -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 \n" +"Language-Team: Amebis, d. o. o., Kamnik \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 "" diff --git a/Updater/locale/de_DE.po b/Updater/locale/de_DE.po new file mode 100644 index 0000000..133e07f --- /dev/null +++ b/Updater/locale/de_DE.po @@ -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 \n" +"Language-Team: Amebis, d. o. o., Kamnik \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 "" diff --git a/Updater/locale/ru_RU.po b/Updater/locale/ru_RU.po new file mode 100644 index 0000000..129d2f3 --- /dev/null +++ b/Updater/locale/ru_RU.po @@ -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 \n" +"Language-Team: Amebis, d. o. o., Kamnik \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 "" diff --git a/Updater/locale/sl_SI.po b/Updater/locale/sl_SI.po new file mode 100644 index 0000000..b69561f --- /dev/null +++ b/Updater/locale/sl_SI.po @@ -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 \n" +"Language-Team: Amebis, d. o. o., Kamnik \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." diff --git a/Updater/res/Updater.rc b/Updater/res/Updater.rc new file mode 100644 index 0000000..958e3fc Binary files /dev/null and b/Updater/res/Updater.rc differ diff --git a/Updater/src/chkthread.cpp b/Updater/src/chkthread.cpp new file mode 100644 index 0000000..8cda341 --- /dev/null +++ b/Updater/src/chkthread.cpp @@ -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 . +*/ + +#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; + } +} diff --git a/Updater/src/main.cpp b/Updater/src/main.cpp new file mode 100644 index 0000000..ee51159 --- /dev/null +++ b/Updater/src/main.cpp @@ -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 . +*/ + +#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; +} diff --git a/Updater/src/stdafx.cpp b/Updater/src/stdafx.cpp new file mode 100644 index 0000000..6ade609 --- /dev/null +++ b/Updater/src/stdafx.cpp @@ -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 . +*/ + +#include "stdafx.h" diff --git a/Updater/src/stdafx.h b/Updater/src/stdafx.h new file mode 100644 index 0000000..d6534ae --- /dev/null +++ b/Updater/src/stdafx.h @@ -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 . +*/ + +#pragma once + +#include "../include/Updater/chkthread.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +// +// Private data declaration +// +extern HINSTANCE g_hInstance;