[win] onlinstaller: switch from hard URL to appcast

This commit is contained in:
SimplestStudio
2025-04-30 14:21:41 +03:00
parent a6f38eba15
commit 88f0a8f2b3
10 changed files with 3809 additions and 42 deletions

View File

@ -7,11 +7,11 @@ DEFINES += APP_LANG_PATH=\"./langs/langs.iss\"
OTHER_FILES += $$PWD/res/langs/langs.iss OTHER_FILES += $$PWD/res/langs/langs.iss
ENV_URL_INSTALL = $$(DESKTOP_URL_INSTALL_CHANNEL) ENV_URL_INSTALL = $$(DESKTOP_URL_UPDATES_MAIN_CHANNEL)
isEmpty(ENV_URL_INSTALL): DEFINES += URL_INSTALL=\\\"\\\" isEmpty(ENV_URL_INSTALL): DEFINES += URL_INSTALL=\\\"\\\"
else: DEFINES += URL_INSTALL=\\\"$${ENV_URL_INSTALL}\\\" else: DEFINES += URL_INSTALL=\\\"$${ENV_URL_INSTALL}\\\"
ENV_URL_INSTALL_DEV = $$(DESKTOP_URL_INSTALL_DEV_CHANNEL) ENV_URL_INSTALL_DEV = $$(DESKTOP_URL_UPDATES_DEV_CHANNEL)
isEmpty(ENV_URL_INSTALL_DEV): DEFINES += URL_INSTALL_DEV=\\\"\\\" isEmpty(ENV_URL_INSTALL_DEV): DEFINES += URL_INSTALL_DEV=\\\"\\\"
else: DEFINES += URL_INSTALL_DEV=\\\"$${ENV_URL_INSTALL_DEV}\\\" else: DEFINES += URL_INSTALL_DEV=\\\"$${ENV_URL_INSTALL_DEV}\\\"

View File

@ -22,6 +22,8 @@ HEADERS += $$PWD/src/version.h \
$$PWD/src/mainwindow.h \ $$PWD/src/mainwindow.h \
$$PWD/src/cdownloader.h \ $$PWD/src/cdownloader.h \
$$PWD/src/translator.h \ $$PWD/src/translator.h \
$$PWD/src/cjson_p.h \
$$PWD/src/cjson.h \
$$PWD/src/utils.h \ $$PWD/src/utils.h \
$$UICLASSES/commondefines.h \ $$UICLASSES/commondefines.h \
$$UICLASSES/baseutils.h \ $$UICLASSES/baseutils.h \
@ -49,6 +51,7 @@ SOURCES += $$PWD/src/main.cpp \
$$PWD/src/mainwindow.cpp \ $$PWD/src/mainwindow.cpp \
$$PWD/src/cdownloader.cpp \ $$PWD/src/cdownloader.cpp \
$$PWD/src/translator.cpp \ $$PWD/src/translator.cpp \
$$PWD/src/cjson.cpp \
$$PWD/src/utils.cpp \ $$PWD/src/utils.cpp \
$$UICLASSES/baseutils.cpp \ $$UICLASSES/baseutils.cpp \
$$UICLASSES/common.cpp \ $$UICLASSES/common.cpp \

View File

@ -0,0 +1,185 @@
/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "cjson.h"
#include "cjson_p.h"
#include <codecvt>
static std::string TStrToUtf8(const tstring &str)
{
#ifdef _WIN32
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> utf8_conv;
return utf8_conv.to_bytes(str);
#else
return str;
#endif
}
class JsonObjectPrivate
{
public:
json_object_s *obj = nullptr;
};
class JsonValuePrivate
{
public:
json_value_s *val = nullptr;
};
class JsonDocumentPrivate
{
public:
json_value_s *root = nullptr;
};
JsonValue::JsonValue() : pimpl(new JsonValuePrivate)
{}
JsonValue::JsonValue(const JsonValue &jval) : JsonValue()
{
pimpl->val = jval.pimpl->val;
}
JsonValue::~JsonValue()
{
delete pimpl, pimpl = nullptr;
}
JsonValue& JsonValue::operator=(const JsonValue &jval)
{
if (this == &jval)
return *this;
pimpl->val = jval.pimpl->val;
return *this;
}
JsonObject JsonValue::toObject()
{
JsonObject jobj;
if (pimpl->val && pimpl->val->type == json_type_object)
jobj.pimpl->obj = (json_object_s*)pimpl->val->payload;
return jobj;
}
tstring JsonValue::toTString()
{
tstring str;
if (pimpl->val && pimpl->val->type == json_type_string) {
json_string_s *jstr = (json_string_s*)pimpl->val->payload;
#ifdef _WIN32
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
str = converter.from_bytes(std::string(jstr->string, jstr->string_size));
#else
str = std::string(jstr->string, jstr->string_size);
#endif
}
return str;
}
JsonObject::JsonObject() : pimpl(new JsonObjectPrivate)
{}
JsonObject::JsonObject(const JsonObject &jobj) : JsonObject()
{
pimpl->obj = jobj.pimpl->obj;
}
JsonObject::~JsonObject()
{
delete pimpl, pimpl = nullptr;
}
JsonObject& JsonObject::operator=(const JsonObject &jobj)
{
if (this == &jobj)
return *this;
pimpl->obj = jobj.pimpl->obj;
return *this;
}
JsonValue JsonObject::value(const tstring &key)
{
std::string utf8_key = TStrToUtf8(key);
JsonValue jval;
json_object_element_s *element;
if (pimpl->obj && (element = pimpl->obj->start) != NULL) {
do {
if (strcmp(element->name->string, utf8_key.c_str()) == 0) {
jval.pimpl->val = element->value;
break;
}
} while ((element = element->next) != NULL);
}
return jval;
}
bool JsonObject::contains(const tstring &key)
{
std::string utf8_key = TStrToUtf8(key);
json_object_element_s *element;
if (pimpl->obj && (element = pimpl->obj->start) != NULL) {
do {
if (strcmp(element->name->string, utf8_key.c_str()) == 0)
return true;
} while ((element = element->next) != NULL);
}
return false;
}
JsonDocument::JsonDocument() : pimpl(new JsonDocumentPrivate)
{}
JsonDocument::JsonDocument(const tstring &json) : JsonDocument()
{
std::string utf8_json = TStrToUtf8(json);
pimpl->root = json_parse(utf8_json.c_str(), utf8_json.length());
}
JsonDocument::~JsonDocument()
{
if (pimpl->root)
free(pimpl->root);
delete pimpl, pimpl = nullptr;
}
JsonObject JsonDocument::object()
{
JsonObject obj;
if (pimpl->root && pimpl->root->type == json_type_object && pimpl->root->payload)
obj.pimpl->obj = (json_object_s*)pimpl->root->payload;
return obj;
}

View File

@ -0,0 +1,96 @@
/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#ifndef CJSON_H
#define CJSON_H
#include <string>
#ifdef _WIN32
# include <tchar.h>
# define tstring std::wstring
#else
# define _T(str) str
# define tstring std::string
#endif
class JsonObject;
class JsonObjectPrivate;
class JsonValuePrivate;
class JsonDocumentPrivate;
class JsonValue
{
public:
JsonValue();
JsonValue(const JsonValue&);
~JsonValue();
JsonValue& operator=(const JsonValue&);
JsonObject toObject();
tstring toTString();
private:
friend class JsonObject;
JsonValuePrivate *pimpl;
};
class JsonObject
{
public:
JsonObject();
JsonObject(const JsonObject&);
~JsonObject();
JsonObject& operator=(const JsonObject&);
JsonValue value(const tstring&);
bool contains(const tstring&);
private:
friend class JsonDocument;
friend class JsonValue;
JsonObjectPrivate *pimpl;
};
class JsonDocument
{
public:
JsonDocument(const tstring&);
~JsonDocument();
JsonObject object();
private:
JsonDocument();
JsonDocumentPrivate *pimpl;
};
#endif // CJSON_H

File diff suppressed because it is too large Load Diff

View File

@ -44,15 +44,8 @@ int WINAPI _tWinMain(_In_ HINSTANCE hInst, _In_opt_ HINSTANCE hPrevInstance, _In
return 0; return 0;
} }
wstring url_or_path, arch; wstring path, arch;
bool app_installed = NS_Utils::IsAppInstalled(url_or_path, &arch); bool app_installed = NS_Utils::IsAppInstalled(path, &arch);
if (!app_installed) {
url_or_path = NS_Utils::cmdArgContains(_T("--appcast-dev-channel")) ? _T(URL_INSTALL_DEV) : _T(URL_INSTALL);
wstring url_filename = L"DesktopEditors_";
url_filename += NS_Utils::IsWin64() ? _T("x64") : _T("x86");
url_filename += _T(".exe");
NS_Utils::Replace(url_or_path, _T("<file>"), url_filename);
}
Application app(hInst, lpCmdLine, nCmdShow); Application app(hInst, lpCmdLine, nCmdShow);
app.setFont(L"Segoe UI"); app.setFont(L"Segoe UI");
@ -67,7 +60,7 @@ int WINAPI _tWinMain(_In_ HINSTANCE hInst, _In_opt_ HINSTANCE hPrevInstance, _In
app.exit(0); app.exit(0);
}); });
if (!app_installed) if (!app_installed)
w.initInstallationMode(url_or_path); w.initInstallationMode();
else else
w.initControlMode(arch); w.initControlMode(arch);
w.showAll(); w.showAll();

View File

@ -14,9 +14,11 @@
#include "translator.h" #include "translator.h"
#include "cdownloader.h" #include "cdownloader.h"
#include "baseutils.h" #include "baseutils.h"
#include "cjson.h"
#include <Msi.h> #include <Msi.h>
#include <ShlObj.h> #include <ShlObj.h>
#include <Shlwapi.h> #include <Shlwapi.h>
#include <numeric>
#include "../../src/defines.h" #include "../../src/defines.h"
#include "../../src/prop/defines_p.h" #include "../../src/prop/defines_p.h"
@ -174,7 +176,7 @@ MainWindow::~MainWindow()
m_future.wait(); m_future.wait();
} }
void MainWindow::initInstallationMode(const std::wstring &url) void MainWindow::initInstallationMode()
{ {
m_is_checked = true; m_is_checked = true;
m_mode = Mode::Install; m_mode = Mode::Install;
@ -215,7 +217,7 @@ void MainWindow::initInstallationMode(const std::wstring &url)
chkBox->close(); chkBox->close();
comntLbl->close(); comntLbl->close();
instlBtn->close(); instlBtn->close();
startInstall(url); startInstall();
}); });
m_resize_conn = m_cenPanel->onResize([chkBox, comntLbl, instlBtn](int w, int h) { m_resize_conn = m_cenPanel->onResize([chkBox, comntLbl, instlBtn](int w, int h) {
@ -266,7 +268,7 @@ bool MainWindow::event(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *result)
return Window::event(msg, wParam, lParam, result); return Window::event(msg, wParam, lParam, result);
} }
void MainWindow::startInstall(const std::wstring &url) void MainWindow::startInstall()
{ {
/* Comment section */ /* Comment section */
m_comntLbl = new Label(m_cenPanel); m_comntLbl = new Label(m_cenPanel);
@ -297,7 +299,7 @@ void MainWindow::startInstall(const std::wstring &url)
m_bar->show(); m_bar->show();
wstring path = NS_File::generateTmpFileName(L".exe"); wstring path = NS_File::generateTmpFileName(L".exe");
startDownload(url, path, [=]() { startDownload(L"iss", NS_Utils::IsWin64() ? _T("x64") : _T("x86"), path, [=]() {
wstring args; wstring args;
if (m_is_checked) { if (m_is_checked) {
args = _T("/VERYSILENT"); args = _T("/VERYSILENT");
@ -399,12 +401,7 @@ void MainWindow::startUpdate()
tmp_path = NS_File::toNativeSeparators(NS_File::generateTmpFileName(L"." + m_package)); tmp_path = NS_File::toNativeSeparators(NS_File::generateTmpFileName(L"." + m_package));
} }
wstring url = NS_Utils::cmdArgContains(_T("--appcast-dev-channel")) ? _T(URL_INSTALL_DEV) : _T(URL_INSTALL); CDownloader *dnl = startDownload(m_package == L"msi" ? L"msi" : L"iss", m_arch, tmp_path, [=]() {
wstring url_filename = L"DesktopEditors_" + m_arch;
url_filename.append(L"." + m_package);
NS_Utils::Replace(url, _T("<file>"), url_filename);
CDownloader *dnl = startDownload(url, tmp_path, [=]() {
if (!NS_Utils::checkAndWaitForAppClosure(nativeWindowHandle())) { if (!NS_Utils::checkAndWaitForAppClosure(nativeWindowHandle())) {
m_bar->setProgress(0); m_bar->setProgress(0);
m_comntInfoLbl->setText(_TR(LABEL_ERR_CANCELLED), true); m_comntInfoLbl->setText(_TR(LABEL_ERR_CANCELLED), true);
@ -481,7 +478,7 @@ void MainWindow::startUpdate()
// NS_Utils::Replace(url, L"%4", url_filename); // NS_Utils::Replace(url, L"%4", url_filename);
// } // }
// CDownloader *dnl = startDownload(url, tmp_path, [=]() { // CDownloader *dnl = startDownload(m_package == L"msi" ? L"msi" : L"iss", m_arch, tmp_path, [=]() {
// if (!NS_Utils::checkAndWaitForAppClosure(nativeWindowHandle())) { // if (!NS_Utils::checkAndWaitForAppClosure(nativeWindowHandle())) {
// m_bar->setProgress(0); // m_bar->setProgress(0);
// m_comntInfoLbl->setText(_TR(LABEL_ERR_CANCELLED), true); // m_comntInfoLbl->setText(_TR(LABEL_ERR_CANCELLED), true);
@ -823,35 +820,79 @@ wstring MainWindow::fillInstalledVerInfo()
return text; return text;
} }
CDownloader* MainWindow::startDownload(const std::wstring &url, const std::wstring &path, const std::function<void()> &onComplete) CDownloader* MainWindow::startDownload(const std::wstring &install_type, const std::wstring &arch, const std::wstring &path, const std::function<void()> &onComplete)
{ {
wstring appcast_url = NS_Utils::cmdArgContains(_T("--appcast-dev-channel")) ? _T(URL_INSTALL_DEV) : _T(URL_INSTALL);
wstring tmp_path = NS_File::toNativeSeparators(NS_File::generateTmpFileName(L".json"));
CDownloader *dnl = new CDownloader(); CDownloader *dnl = new CDownloader();
dnl->onProgress([=](int percent) {
m_bar->setProgress(percent);
});
dnl->onComplete([=](ulong error) { dnl->onComplete([=](ulong error) {
if (m_mode == Mode::Control)
m_cancelBtn->setDisabled(true);
if (error == ERROR_SUCCESS) { if (error == ERROR_SUCCESS) {
if (NS_File::verifyEmbeddedSignature(path)) { list<tstring> lst;
onComplete(); if (NS_File::readFile(tmp_path, lst)) {
tstring json = std::accumulate(lst.begin(), lst.end(), tstring());
JsonDocument doc(json);
JsonObject root = doc.object();
// tstring version = root.value(_T("version")).toTString();
JsonObject package = root.value(_T("package")).toObject();
#ifdef _WIN32
JsonObject win = package.value(arch == _T("x64") ? _T("win_64") : _T("win_32")).toObject();
#else
JsonObject win = package.value(_T("linux_64")).toObject();
#endif
JsonObject package_type = win.value(install_type).toObject();
tstring url = package_type.value(_T("url")).toTString();
// tstring hash = package_type.value(_T("md5")).toTString();
// std::transform(hash.begin(), hash.end(), hash.begin(), ::tolower);
NS_File::removeFile(tmp_path);
invokeMethod([=]() {
dnl->stop();
dnl->onProgress([=](int percent) {
m_bar->setProgress(percent);
});
dnl->onComplete([=](ulong error) {
if (m_mode == Mode::Control)
m_cancelBtn->setDisabled(true);
if (error == ERROR_SUCCESS) {
if (NS_File::verifyEmbeddedSignature(path)) {
onComplete();
} else {
m_bar->setProgress(0);
m_comntInfoLbl->setText(_TR(LABEL_NO_VER_AVAIL), true);
}
if (NS_File::fileExists(path))
NS_File::removeFile(path);
} else
if (error == ERROR_CANCELLED) {
m_comntInfoLbl->setText(_TR(LABEL_ERR_CANCELLED), true);
} else {
m_comntInfoLbl->setText(NS_Utils::GetLastErrorAsString(error), true);
}
if (m_mode == Mode::Control)
createCloseAndBackButtons();
});
dnl->downloadFile(url, path);
});
} else { } else {
m_bar->setProgress(0); NS_File::removeFile(tmp_path);
m_comntInfoLbl->setText(_TR(LABEL_NO_VER_AVAIL), true); m_comntInfoLbl->setText(_TR(LABEL_ERR_COMMON), true);
if (m_mode == Mode::Control)
createCloseAndBackButtons();
} }
if (NS_File::fileExists(path))
NS_File::removeFile(path);
} else } else
if (error == ERROR_CANCELLED) { if (error == ERROR_CANCELLED) {
m_comntInfoLbl->setText(_TR(LABEL_ERR_CANCELLED), true); m_comntInfoLbl->setText(_TR(LABEL_ERR_CANCELLED), true);
if (m_mode == Mode::Control)
createCloseAndBackButtons();
} else { } else {
m_comntInfoLbl->setText(NS_Utils::GetLastErrorAsString(error), true); m_comntInfoLbl->setText(NS_Utils::GetLastErrorAsString(error), true);
if (m_mode == Mode::Control)
createCloseAndBackButtons();
} }
if (m_mode == Mode::Control)
createCloseAndBackButtons();
}); });
dnl->downloadFile(url, path); dnl->downloadFile(appcast_url, tmp_path);
onAboutToDestroy([=]() { onAboutToDestroy([=]() {
delete dnl; delete dnl;
}); });

View File

@ -19,14 +19,14 @@ public:
MainWindow(Widget *parent, const Rect &rc); MainWindow(Widget *parent, const Rect &rc);
~MainWindow(); ~MainWindow();
void initInstallationMode(const std::wstring &url); void initInstallationMode();
void initControlMode(const std::wstring &arch); void initControlMode(const std::wstring &arch);
protected: protected:
virtual bool event(UINT, WPARAM, LPARAM, LRESULT*) override; virtual bool event(UINT, WPARAM, LPARAM, LRESULT*) override;
private: private:
void startInstall(const std::wstring &url); void startInstall();
void finishInstall(const std::wstring &app_path); void finishInstall(const std::wstring &app_path);
void startUpdate(); void startUpdate();
// void startRepair(); // void startRepair();
@ -36,7 +36,7 @@ private:
void createCloseButton(); void createCloseButton();
void createCloseAndBackButtons(); void createCloseAndBackButtons();
std::wstring fillInstalledVerInfo(); std::wstring fillInstalledVerInfo();
CDownloader* startDownload(const std::wstring &url, const std::wstring &path, const std::function<void()> &onComplete); CDownloader* startDownload(const std::wstring &install_type, const std::wstring &arch, const std::wstring &path, const std::function<void()> &onComplete);
template<typename Fn, typename... Args> template<typename Fn, typename... Args>
void invokeMethod(Fn&& fn, Args&&... args); void invokeMethod(Fn&& fn, Args&&... args);

View File

@ -356,6 +356,21 @@ namespace NS_File
// return false; // return false;
// } // }
bool readFile(const wstring &filePath, list<wstring> &linesList)
{
std::wifstream file(filePath.c_str(), std::ios_base::in);
if (!file.is_open()) {
NS_Logger::WriteLog(L"An error occurred while opening: " + filePath);
return false;
}
wstring line;
while (std::getline(file, line))
linesList.push_back(line);
file.close();
return true;
}
bool fileExists(const wstring &filePath) bool fileExists(const wstring &filePath)
{ {
DWORD attr = ::GetFileAttributes(filePath.c_str()); DWORD attr = ::GetFileAttributes(filePath.c_str());

View File

@ -35,10 +35,12 @@
#include <Windows.h> #include <Windows.h>
#include <string> #include <string>
#include <list>
using std::string; using std::string;
using std::wstring; using std::wstring;
using std::to_wstring; using std::to_wstring;
using std::list;
#define ERROR_LAUNCH 0x20000000 #define ERROR_LAUNCH 0x20000000
#define DEFAULT_ERROR_MESSAGE _T("An error occurred: ") + \ #define DEFAULT_ERROR_MESSAGE _T("An error occurred: ") + \
@ -69,6 +71,7 @@ namespace NS_File
{ {
DWORD runProcess(const wstring &fileName, const wstring &args, bool runAsAdmin = false, bool wait = true); DWORD runProcess(const wstring &fileName, const wstring &args, bool runAsAdmin = false, bool wait = true);
// bool isProcessRunning(const wstring &fileName); // bool isProcessRunning(const wstring &fileName);
bool readFile(const wstring &filePath, list<wstring> &linesList);
bool fileExists(const wstring &filePath); bool fileExists(const wstring &filePath);
bool removeFile(const wstring &filePath); bool removeFile(const wstring &filePath);
bool removeDirRecursively(const wstring &dir); bool removeDirRecursively(const wstring &dir);