mirror of
https://github.com/ONLYOFFICE/desktop-apps.git
synced 2026-02-10 18:05:16 +08:00
521 lines
17 KiB
C++
521 lines
17 KiB
C++
/*
|
|
* (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 "utils.h"
|
|
#include "baseutils.h"
|
|
#include "resource.h"
|
|
#include "translator.h"
|
|
#include <shlwapi.h>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <Softpub.h>
|
|
#include <TlHelp32.h>
|
|
#include <Msi.h>
|
|
#include <ShlObj.h>
|
|
// #include <sstream>
|
|
#include "../../src/defines.h"
|
|
#include "../../src/prop/defines_p.h"
|
|
|
|
#define APP_REG_PATH "\\" REG_GROUP_KEY "\\" REG_APP_NAME
|
|
#define BIT123_LAYOUTRTL 0x08000000
|
|
#ifndef LOCALE_IREADINGLAYOUT
|
|
# define LOCALE_IREADINGLAYOUT 0x70
|
|
#endif
|
|
|
|
|
|
static void RegQueryStringValue(HKEY rootKey, LPCWSTR subkey, REGSAM advFlags, LPCWSTR value, wstring &result)
|
|
{
|
|
HKEY hKey;
|
|
if (RegOpenKeyExW(rootKey, subkey, 0, KEY_READ | advFlags, &hKey) == ERROR_SUCCESS) {
|
|
DWORD type = REG_SZ, cbData = 0;
|
|
if (SHGetValue(hKey, L"", value, &type, NULL, &cbData) == ERROR_SUCCESS) {
|
|
wchar_t *pvData = (wchar_t*)malloc(cbData);
|
|
if (SHGetValue(hKey, L"", value, &type, (void*)pvData, &cbData) == ERROR_SUCCESS)
|
|
result = pvData;
|
|
free(pvData);
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
namespace NS_Utils
|
|
{
|
|
std::vector<wstring> cmd_args;
|
|
|
|
void parseCmdArgs(int argc, wchar_t *argv[])
|
|
{
|
|
for (int i = 0; i < argc; i++)
|
|
cmd_args.push_back(argv[i]);
|
|
}
|
|
|
|
bool cmdArgContains(const wstring ¶m)
|
|
{
|
|
auto len = param.length();
|
|
return std::any_of(cmd_args.cbegin(), cmd_args.cend(), [¶m, len](const wstring &arg) {
|
|
return arg.find(param) == 0 && (len == arg.length() || arg[len] == L'=' || arg[len] == L':' || arg[len] == L'|');
|
|
});
|
|
}
|
|
|
|
wstring cmdArgValue(const wstring ¶m)
|
|
{
|
|
auto len = param.length();
|
|
for (const auto &arg : cmd_args) {
|
|
if (arg.find(param) == 0 && len < arg.length() && (arg[len] == L'=' || arg[len] == L':' || arg[len] == L'|'))
|
|
return arg.substr(len + 1);
|
|
}
|
|
return L"";
|
|
}
|
|
|
|
wstring GetLastErrorAsString(DWORD _errID)
|
|
{
|
|
DWORD errID = _errID != 0 ? _errID : ::GetLastError();
|
|
if (errID == 0)
|
|
return _T("");
|
|
|
|
LPTSTR msgBuff = NULL;
|
|
size_t size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, errID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msgBuff, 0, NULL);
|
|
wstring msg = _TR(LABEL_ERR_COMMON) + wstring(L" ") + std::to_wstring(errID);
|
|
if (size > 0) {
|
|
msg.append(L"\n" + wstring(msgBuff, (int)size));
|
|
LocalFree(msgBuff);
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
void ShowMessage(wstring str, bool showError)
|
|
{
|
|
if (showError)
|
|
str += _T(" ") + GetLastErrorAsString();
|
|
wstring caption(_T(" "));
|
|
caption.append(_TR(CAPTION));
|
|
LCID lcid = MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT);
|
|
UINT flags = MB_ICONERROR | MB_SERVICE_NOTIFICATION_NT3X | MB_SETFOREGROUND;
|
|
if (IsRtlLanguage(lcid))
|
|
flags |= MB_RTLREADING;
|
|
MessageBox(NULL, str.c_str(), caption.c_str(), flags);
|
|
}
|
|
|
|
int ShowTaskDialog(HWND parent, const wstring &msg, PCWSTR icon)
|
|
{
|
|
HWND fakeParent = NULL;
|
|
HMODULE hInst = GetModuleHandle(NULL);
|
|
if (!parent) {
|
|
WNDCLASS wc = {0};
|
|
wc.lpfnWndProc = DefWindowProc;
|
|
wc.hInstance = hInst;
|
|
wc.lpszClassName = L"FakeWindowClass";
|
|
RegisterClass(&wc);
|
|
fakeParent = CreateWindowEx(0, wc.lpszClassName, _TR(CAPTION), WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, hInst, NULL);
|
|
HICON hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_MAINICON), IMAGE_ICON, 96, 96, LR_DEFAULTCOLOR | LR_SHARED);
|
|
SendMessage(fakeParent, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
|
|
SendMessage(fakeParent, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
|
|
ShowWindow(fakeParent, SW_SHOWMINIMIZED);
|
|
UpdateWindow(fakeParent);
|
|
parent = fakeParent;
|
|
}
|
|
|
|
int result = IDCANCEL;
|
|
wstring caption(_T(" "));
|
|
caption.append(_TR(CAPTION));
|
|
if (HMODULE lib = LoadLibrary(L"Comctl32")) {
|
|
HRESULT (WINAPI *_TaskDialog)(HWND, HINSTANCE, PCWSTR, PCWSTR, PCWSTR, TASKDIALOG_COMMON_BUTTON_FLAGS, PCWSTR, int*);
|
|
*(FARPROC*)&_TaskDialog = GetProcAddress(lib, "TaskDialog");
|
|
if (_TaskDialog)
|
|
_TaskDialog(parent, hInst, caption.c_str(), msg.c_str(), NULL, TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON, icon, &result);
|
|
FreeLibrary(lib);
|
|
}
|
|
|
|
if (fakeParent)
|
|
DestroyWindow(fakeParent);
|
|
return result;
|
|
}
|
|
|
|
bool IsRtlLanguage(unsigned long lcid)
|
|
{
|
|
if (Utils::getWinVersion() >= Utils::WinVer::Win7) {
|
|
DWORD layout = 0;
|
|
if (GetLocaleInfo(lcid, LOCALE_IREADINGLAYOUT | LOCALE_RETURN_NUMBER, (LPWSTR)&layout, sizeof(layout)/sizeof(WCHAR)) > 0)
|
|
return layout == 1;
|
|
} else {
|
|
LOCALESIGNATURE lsig;
|
|
if (GetLocaleInfo(lcid, LOCALE_FONTSIGNATURE, (LPWSTR)&lsig, sizeof(lsig)/sizeof(WCHAR)) > 0)
|
|
return (lsig.lsUsb[3] & BIT123_LAYOUTRTL) != 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsWin64()
|
|
{
|
|
#ifdef _WIN64
|
|
return true;
|
|
#else
|
|
BOOL wow64 = FALSE;
|
|
return IsWow64Process(GetCurrentProcess(), &wow64) && wow64;
|
|
#endif
|
|
}
|
|
|
|
bool IsAppInstalled(wstring &path, wstring *arch)
|
|
{
|
|
std::vector<REGSAM> flags{0};
|
|
if (NS_Utils::IsWin64()) {
|
|
#ifdef _WIN64
|
|
flags.push_back(KEY_WOW64_32KEY);
|
|
#else
|
|
flags.push_back(KEY_WOW64_64KEY);
|
|
#endif
|
|
}
|
|
wstring subkey(L"Software");
|
|
subkey += _T(APP_REG_PATH);
|
|
for (auto &flag : flags) {
|
|
RegQueryStringValue(HKEY_LOCAL_MACHINE, subkey.c_str(), flag, L"AppPath", path);
|
|
if (!path.empty() && (path.back() == L'\\' || path.back() == L'/'))
|
|
path.pop_back();
|
|
if (!path.empty() /*&& NS_File::fileExists(path + _T(APP_LAUNCH_NAME))*/) {
|
|
if (arch) {
|
|
#ifdef _WIN64
|
|
*arch = (flag == 0) ? L"x64" : L"x86";
|
|
#else
|
|
*arch = (flag == 0) ? L"x86" : L"x64";
|
|
#endif
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool checkAndWaitForAppClosure(HWND parent)
|
|
{
|
|
bool accept = true;
|
|
if (HWND app_hwnd = FindWindow(WINDOW_CLASS_NAME, NULL)) {
|
|
wstring msg(_TR(MSG_ERR_TRY_CLOSE_APP));
|
|
NS_Utils::Replace(msg, L"%1", _T(WINDOW_NAME));
|
|
accept = (IDOK == NS_Utils::ShowTaskDialog(parent, msg.c_str(), TD_INFORMATION_ICON));
|
|
if (accept) {
|
|
PostMessage(app_hwnd, UM_INSTALL_UPDATE, 0, 0);
|
|
Sleep(3000);
|
|
while(true) {
|
|
if ((app_hwnd = FindWindow(WINDOW_CLASS_NAME, NULL)) != nullptr) {
|
|
wstring msg(_TR(MSG_ERR_CLOSE_APP));
|
|
NS_Utils::Replace(msg, L"%1", _T(WINDOW_NAME));
|
|
int result = NS_Utils::ShowTaskDialog(parent, msg.c_str(), TD_WARNING_ICON);
|
|
if (result != IDOK) {
|
|
accept = false;
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return accept;
|
|
}
|
|
|
|
void InstalledVerInfo(LPCWSTR value, wstring &name, wstring &arch)
|
|
{
|
|
if (!name.empty())
|
|
name.clear();
|
|
std::vector<REGSAM> flags{0};
|
|
if (NS_Utils::IsWin64()) {
|
|
#ifdef _WIN64
|
|
flags.push_back(KEY_WOW64_32KEY);
|
|
#else
|
|
flags.push_back(KEY_WOW64_64KEY);
|
|
#endif
|
|
}
|
|
for (auto &flag : flags) {
|
|
wstring subkey(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\");
|
|
subkey += _T(REG_UNINST_KEY);
|
|
for (int i = 0; i < 2; i++) {
|
|
RegQueryStringValue(HKEY_LOCAL_MACHINE, subkey.c_str(), flag, value, name);
|
|
if (!name.empty()) {
|
|
if (arch.empty()) {
|
|
#ifdef _WIN64
|
|
arch = (flag == 0) ? L"x64" : L"x86";
|
|
#else
|
|
arch = (flag == 0) ? L"x86" : L"x64";
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
subkey += L"_is1";
|
|
}
|
|
}
|
|
}
|
|
|
|
void Replace(wstring &str, const wstring &from, const wstring &to) {
|
|
if (from.empty())
|
|
return;
|
|
size_t start_pos = 0;
|
|
while((start_pos = str.find(from, start_pos)) != std::wstring::npos) {
|
|
str.replace(start_pos, from.length(), to);
|
|
start_pos += to.length();
|
|
}
|
|
}
|
|
|
|
wstring MsiGetProperty(LPCWSTR prodCode, LPCWSTR propName)
|
|
{
|
|
DWORD buffSize = 0;
|
|
UINT res = MsiGetProductInfoW(prodCode, propName, NULL, &buffSize);
|
|
if ((res == ERROR_MORE_DATA || res == ERROR_SUCCESS) && buffSize > 0) {
|
|
++buffSize;
|
|
wchar_t *value = new wchar_t[buffSize];
|
|
if (MsiGetProductInfoW(prodCode, propName, value, &buffSize) == ERROR_SUCCESS) {
|
|
wstring propValue = value;
|
|
delete[] value;
|
|
return propValue;
|
|
}
|
|
delete[] value;
|
|
}
|
|
return wstring();
|
|
}
|
|
|
|
wstring MsiProductCode(const wstring &prodName)
|
|
{
|
|
DWORD ind = 0;
|
|
WCHAR prodCode[39];
|
|
while (MsiEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_MACHINE, ind++, prodCode, NULL, NULL, NULL) == ERROR_SUCCESS) {
|
|
if (MsiGetProperty(prodCode, INSTALLPROPERTY_PRODUCTNAME) == prodName)
|
|
return prodCode;
|
|
}
|
|
return wstring();
|
|
}
|
|
}
|
|
|
|
namespace NS_File
|
|
{
|
|
DWORD runProcess(const wstring &fileName, const wstring &args, bool runAsAdmin, bool wait)
|
|
{
|
|
SHELLEXECUTEINFO shExInfo = {0};
|
|
shExInfo.cbSize = sizeof(shExInfo);
|
|
shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE /*| SEE_MASK_FLAG_NO_UI*/;
|
|
shExInfo.hwnd = NULL;
|
|
shExInfo.lpVerb = runAsAdmin ? _T("runas") : _T("open");
|
|
shExInfo.lpFile = fileName.c_str();
|
|
shExInfo.lpParameters = args.c_str();
|
|
shExInfo.lpDirectory = NULL;
|
|
shExInfo.nShow = SW_HIDE;
|
|
shExInfo.hInstApp = NULL;
|
|
if (ShellExecuteEx(&shExInfo)) {
|
|
DWORD exitCode = 0;
|
|
if (wait && (WaitForSingleObject(shExInfo.hProcess, INFINITE) == WAIT_FAILED || !GetExitCodeProcess(shExInfo.hProcess, &exitCode)))
|
|
exitCode = GetLastError();
|
|
CloseHandle(shExInfo.hProcess);
|
|
return exitCode;
|
|
}
|
|
return GetLastError() | ERROR_LAUNCH;
|
|
}
|
|
|
|
// bool isProcessRunning(const wstring &fileName)
|
|
// {
|
|
// HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
// if (snapShot == INVALID_HANDLE_VALUE)
|
|
// return false;
|
|
|
|
// PROCESSENTRY32 entry;
|
|
// entry.dwSize = sizeof(PROCESSENTRY32);
|
|
// if (!Process32First(snapShot, &entry)) {
|
|
// CloseHandle(snapShot);
|
|
// return false;
|
|
// }
|
|
|
|
// do {
|
|
// if (lstrcmpi(entry.szExeFile, fileName.c_str()) == 0) {
|
|
// CloseHandle(snapShot);
|
|
// return true;
|
|
// }
|
|
// } while (Process32Next(snapShot, &entry));
|
|
|
|
// CloseHandle(snapShot);
|
|
// 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)
|
|
{
|
|
DWORD attr = ::GetFileAttributes(filePath.c_str());
|
|
return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY));
|
|
}
|
|
|
|
bool removeFile(const wstring &filePath)
|
|
{
|
|
return DeleteFile(filePath.c_str()) != 0;
|
|
}
|
|
|
|
bool removeDirRecursively(const wstring &dir)
|
|
{
|
|
WCHAR pFrom[_MAX_PATH + 1] = {0};
|
|
swprintf_s(pFrom, sizeof(pFrom)/sizeof(WCHAR), L"%s%c", dir.c_str(), L'\0');
|
|
SHFILEOPSTRUCT fop = {
|
|
NULL,
|
|
FO_DELETE,
|
|
pFrom,
|
|
NULL,
|
|
FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT,
|
|
FALSE,
|
|
0,
|
|
NULL
|
|
};
|
|
return SHFileOperation(&fop) == 0;
|
|
}
|
|
|
|
wstring fromNativeSeparators(const wstring &path)
|
|
{
|
|
wstring _path(path);
|
|
std::replace(_path.begin(), _path.end(), L'\\', L'/');
|
|
return _path;
|
|
}
|
|
|
|
wstring toNativeSeparators(const wstring &path)
|
|
{
|
|
wstring _path(path);
|
|
std::replace(_path.begin(), _path.end(), L'/', L'\\');
|
|
return _path;
|
|
}
|
|
|
|
wstring parentPath(const wstring &path)
|
|
{
|
|
auto delim = (path.size() > 2) ? path.find_last_of(_T("\\/"), path.size() - 2) : wstring::npos;
|
|
return (delim == wstring::npos) ? _T("") : path.substr(0, delim);
|
|
}
|
|
|
|
wstring tempPath()
|
|
{
|
|
TCHAR buff[MAX_PATH + 1] = {0};
|
|
DWORD res = ::GetTempPath(MAX_PATH + 1, buff);
|
|
if (res != 0) {
|
|
buff[res - 1] = '\0';
|
|
return fromNativeSeparators(buff);
|
|
}
|
|
return _T("");
|
|
}
|
|
|
|
wstring appPath()
|
|
{
|
|
TCHAR buff[MAX_PATH] = {0};
|
|
DWORD res = ::GetModuleFileName(NULL, buff, MAX_PATH);
|
|
return (res != 0) ? fromNativeSeparators(parentPath(buff)) : _T("");
|
|
}
|
|
|
|
wstring generateTmpFileName(const wstring &ext)
|
|
{
|
|
wstring uuid_tstr;
|
|
UUID uuid = {0};
|
|
RPC_WSTR wszUuid = NULL;
|
|
if (UuidCreate(&uuid) == RPC_S_OK && UuidToStringW(&uuid, &wszUuid) == RPC_S_OK) {
|
|
uuid_tstr = ((wchar_t*)wszUuid);
|
|
RpcStringFreeW(&wszUuid);
|
|
} else
|
|
uuid_tstr = L"00000000-0000-0000-0000-000000000000";
|
|
return NS_File::tempPath() + _T("/") + _T(FILE_PREFIX) + uuid_tstr + ext;
|
|
}
|
|
|
|
bool verifyEmbeddedSignature(const wstring &fileName)
|
|
{
|
|
WINTRUST_FILE_INFO wfi;
|
|
ZeroMemory(&wfi, sizeof(wfi));
|
|
wfi.cbStruct = sizeof(WINTRUST_FILE_INFO);
|
|
wfi.pcwszFilePath = fileName.c_str();
|
|
wfi.hFile = NULL;
|
|
wfi.pgKnownSubject = NULL;
|
|
|
|
GUID guidAction = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
|
WINTRUST_DATA wtd;
|
|
ZeroMemory(&wtd, sizeof(wtd));
|
|
wtd.cbStruct = sizeof(WINTRUST_DATA);
|
|
wtd.pPolicyCallbackData = NULL;
|
|
wtd.pSIPClientData = NULL;
|
|
wtd.dwUIChoice = WTD_UI_NONE;
|
|
wtd.fdwRevocationChecks = WTD_REVOKE_NONE;
|
|
wtd.dwUnionChoice = WTD_CHOICE_FILE;
|
|
wtd.dwStateAction = WTD_STATEACTION_VERIFY;
|
|
wtd.hWVTStateData = NULL;
|
|
wtd.pwszURLReference = NULL;
|
|
wtd.dwUIContext = 0;
|
|
wtd.pFile = &wfi;
|
|
return WinVerifyTrust(NULL, &guidAction, &wtd) == ERROR_SUCCESS;
|
|
}
|
|
|
|
wstring appDataPath()
|
|
{
|
|
TCHAR buff[MAX_PATH] = {0};
|
|
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, buff))) {
|
|
wstring path(buff);
|
|
path.append(_T(APP_REG_PATH));
|
|
path.append(_T("\\data"));
|
|
return path;
|
|
}
|
|
return _T("");
|
|
}
|
|
}
|
|
|
|
namespace NS_Logger
|
|
{
|
|
bool allow_write_log = false;
|
|
|
|
void AllowWriteLog()
|
|
{
|
|
allow_write_log = true;
|
|
}
|
|
|
|
void WriteLog(const wstring &log, bool showMessage)
|
|
{
|
|
if (allow_write_log) {
|
|
wstring filpPath(NS_File::appPath() + _T("/installer_log.txt"));
|
|
std::wofstream file(filpPath.c_str(), std::ios::app);
|
|
if (!file.is_open()) {
|
|
return;
|
|
}
|
|
file << log << std::endl;
|
|
file.close();
|
|
}
|
|
if (showMessage)
|
|
NS_Utils::ShowMessage(log);
|
|
}
|
|
}
|