// // LauncherManager.cpp // // Created by Luis Cuenca on 6/5/2019. // Copyright 2019 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "stdafx.h" #include #include #include "LauncherManager.h" LauncherManager::LauncherManager() { int tokenPos = 0; _launcherVersion = CString(LAUNCHER_BUILD_VERSION).Tokenize(_T("-"), tokenPos); } LauncherManager::~LauncherManager() { } void LauncherManager::init(BOOL allowUpdate, ContinueActionOnStart continueAction) { initLog(); _updateLauncherAllowed = allowUpdate; _continueAction = continueAction; CString msg; msg.Format(_T("Start Screen: %s"), getContinueActionParam(continueAction)); addToLog(msg); _shouldWait = _continueAction == ContinueActionOnStart::ContinueNone; if (_continueAction == ContinueActionOnStart::ContinueUpdate) { _progressOffset = CONTINUE_UPDATING_GLOBAL_OFFSET; } addToLog(_T("Launcher is running version: " + _launcherVersion)); addToLog(_T("Getting most recent builds")); _isInstalled = isApplicationInstalled(_currentVersion, _domainURL, _contentURL, _loggedIn, _organizationBuildTag); getMostRecentBuilds(_latestLauncherURL, _latestLauncherVersion, _latestApplicationURL, _latestVersion); } CString LauncherManager::getContinueActionParam(LauncherManager::ContinueActionOnStart continueAction) { switch (continueAction) { case LauncherManager::ContinueActionOnStart::ContinueNone: return _T(""); case LauncherManager::ContinueActionOnStart::ContinueLogIn: return _T("LogIn"); case LauncherManager::ContinueActionOnStart::ContinueUpdate: return _T("Update"); case LauncherManager::ContinueActionOnStart::ContinueFinish: return _T("Finish"); default: return _T(""); } } LauncherManager::ContinueActionOnStart LauncherManager::getContinueActionFromParam(const CString& param) { if (param.Compare(_T("LogIn")) == 0) { return ContinueActionOnStart::ContinueLogIn; } else if (param.Compare(_T("Update")) == 0) { return ContinueActionOnStart::ContinueUpdate; } else if (param.Compare(_T("Finish")) == 0) { return ContinueActionOnStart::ContinueFinish; } else { return ContinueActionOnStart::ContinueNone; } } BOOL LauncherManager::initLog() { CString logPath; auto result = getAndCreatePaths(PathType::Launcher_Directory, logPath); if (result) { logPath += _T("log.txt"); return result = _logFile.Open(logPath, CFile::modeCreate | CFile::modeReadWrite); } return FALSE; } BOOL LauncherManager::addToLog(const CString& line) { if (_logFile.m_hFile != CStdioFile::hFileNull) { char buff[100]; time_t now = time(0); tm ltm; localtime_s(<m, &now); strftime(buff, 100, "%Y-%m-%d %H:%M:%S", <m); CString timeStr = CString(buff); _logFile.WriteString(timeStr + _T(" ") + line + _T("\n")); return TRUE; } return FALSE; } void LauncherManager::closeLog() { if (_logFile.m_hFile != CStdioFile::hFileNull) { _logFile.Close(); } } void LauncherManager::saveErrorLog() { CString logPath = _logFile.GetFilePath(); CString errorLogPath; auto result = getAndCreatePaths(PathType::Launcher_Directory, errorLogPath); if (result) { CString filename; errorLogPath += _T("log_error.txt"); closeLog(); CopyFile(logPath, errorLogPath, FALSE); } } void LauncherManager::tryToInstallLauncher(BOOL retry) { CString appPath; BOOL result = getAndCreatePaths(PathType::Running_Path, appPath); if (!result) { MessageBox(NULL, L"Error getting app directory", L"Path Error", MB_OK | MB_ICONERROR); } CString installDirectory; CString appDirectory = appPath.Left(appPath.ReverseFind('\\') + 1); result = getAndCreatePaths(PathType::Launcher_Directory, installDirectory); if (!result) { MessageBox(NULL, L"Error getting app desired directory", L"Path Error", MB_OK | MB_ICONERROR); } CString instalationPath = installDirectory + LAUNCHER_EXE_FILENAME; if (!installDirectory.Compare(appDirectory) == 0) { if (!_shouldUninstall) { // The installer is not running on the desired location and has to be installed // Kill of running before self-copy addToLog(_T("Trying to install launcher.")); int launcherPID = -1; if (LauncherUtils::isProcessRunning(LAUNCHER_EXE_FILENAME, launcherPID)) { if (!LauncherUtils::shutdownProcess(launcherPID, 0)) { addToLog(_T("Error shutting down the Launcher")); } } const int LAUNCHER_INSTALL_RETRYS = 10; const int WAIT_BETWEEN_RETRYS_MS = 10; int installTrys = retry ? LAUNCHER_INSTALL_RETRYS : 0; for (int i = 0; i <= installTrys; i++) { _retryLauncherInstall = !CopyFile(appPath, instalationPath, FALSE); if (!_retryLauncherInstall) { addToLog(_T("Launcher installed successfully.")); break; } else if (i < installTrys) { CString msg; msg.Format(_T("Installing launcher try: %d"), i); addToLog(msg); Sleep(WAIT_BETWEEN_RETRYS_MS); } else if (installTrys > 0) { addToLog(_T("Error installing launcher.")); _retryLauncherInstall = false; _hasFailed = true; } else { addToLog(_T("Old launcher is still running. Install could not be completed.")); } } } } else if (_shouldUninstall) { addToLog(_T("Launching Uninstall mode.")); CString tempPath; if (getAndCreatePaths(PathType::Temp_Directory, tempPath)) { tempPath += _T("\\HQ_uninstaller_tmp.exe"); if (!CopyFile(instalationPath, tempPath, false)) { addToLog(_T("Error copying uninstaller to tmp directory.")); _hasFailed = true; } else { LauncherUtils::launchApplication(tempPath, _T(" --uninstall")); exit(0); } } } } BOOL LauncherManager::restartLauncher() { addToLog(_T("Restarting Launcher.")); CString installDirectory; if (getAndCreatePaths(PathType::Launcher_Directory, installDirectory)) { CString installPath = installDirectory + LAUNCHER_EXE_FILENAME; LauncherUtils::launchApplication(installPath, _T(" --restart")); exit(0); } addToLog(_T("Error restarting Launcher.")); return FALSE; } void LauncherManager::updateProgress(ProcessType processType, float progress) { switch (processType) { case ProcessType::DownloadLauncher: break; case ProcessType::Uninstall: _progress = progress; break; case ProcessType::DownloadContent: _progress = DOWNLOAD_CONTENT_INSTALL_WEIGHT * progress; break; case ProcessType::UnzipContent: _progress = DOWNLOAD_CONTENT_INSTALL_WEIGHT + EXTRACT_CONTENT_INSTALL_WEIGHT * progress; break; case ProcessType::DownloadApplication: _progress = !_shouldUpdate ? (DOWNLOAD_CONTENT_INSTALL_WEIGHT + EXTRACT_CONTENT_INSTALL_WEIGHT + DOWNLOAD_APPLICATION_INSTALL_WEIGHT * progress) : DOWNLOAD_APPLICATION_UPDATE_WEIGHT * progress; break; case ProcessType::UnzipApplication: _progress = !_shouldUpdate ? (DOWNLOAD_CONTENT_INSTALL_WEIGHT + EXTRACT_CONTENT_INSTALL_WEIGHT + DOWNLOAD_APPLICATION_INSTALL_WEIGHT + EXTRACT_APPLICATION_INSTALL_WEIGHT * progress) : (DOWNLOAD_APPLICATION_UPDATE_WEIGHT + EXTRACT_APPLICATION_UPDATE_WEIGHT * progress); break; default: break; } _progress = _progressOffset + (1.0f - _progressOffset) * _progress; TRACE("progress = %f\n", _progress); } BOOL LauncherManager::createShortcuts() { CString desktopLnkPath; addToLog(_T("Creating shortcuts.")); getAndCreatePaths(PathType::Desktop_Directory, desktopLnkPath); desktopLnkPath += _T("\\HQ Launcher.lnk"); CString installDir; getAndCreatePaths(PathType::Launcher_Directory, installDir); CString installPath = installDir + LAUNCHER_EXE_FILENAME; if (!LauncherUtils::createLink(installPath, (LPCSTR)CStringA(desktopLnkPath), _T("CLick to Setup and Launch HQ."))) { return FALSE; } CString startLinkPath; getAndCreatePaths(PathType::StartMenu_Directory, startLinkPath); CString appStartLinkPath = startLinkPath + _T("HQ Launcher.lnk"); CString uniStartLinkPath = startLinkPath + _T("Uninstall HQ.lnk"); CString uniLinkPath = installDir + _T("Uninstall HQ.lnk"); if (!LauncherUtils::createLink(installPath, (LPCSTR)CStringA(appStartLinkPath), _T("CLick to Setup and Launch HQ."))) { return FALSE; } if (!LauncherUtils::createLink(installPath, (LPCSTR)CStringA(uniStartLinkPath), _T("CLick to Uninstall HQ."), _T("--uninstall"))) { return FALSE; } if (!LauncherUtils::createLink(installPath, (LPCSTR)CStringA(uniLinkPath), _T("CLick to Uninstall HQ."), _T("--uninstall"))) { return FALSE; } return TRUE; } BOOL LauncherManager::deleteShortcuts() { CString desktopLnkPath; addToLog(_T("Deleting shortcuts.")); getAndCreatePaths(PathType::Desktop_Directory, desktopLnkPath); desktopLnkPath += _T("\\HQ Launcher.lnk"); BOOL success = LauncherUtils::deleteFileOrDirectory(desktopLnkPath); CString startLinkPath; getAndCreatePaths(PathType::StartMenu_Directory, startLinkPath); return success && LauncherUtils::deleteFileOrDirectory(startLinkPath); } BOOL LauncherManager::isApplicationInstalled(CString& version, CString& domain, CString& content, bool& loggedIn, CString& organizationBuildTag) { CString applicationDir; getAndCreatePaths(PathType::Launcher_Directory, applicationDir); CString applicationPath = applicationDir + "interface\\interface.exe"; BOOL isInstalled = PathFileExistsW(applicationPath); BOOL configFileExist = PathFileExistsW(applicationDir + _T("interface\\config.json")); if (configFileExist) { LauncherUtils::ResponseError status = readConfigJSON(version, domain, content, loggedIn, organizationBuildTag); return isInstalled && status == LauncherUtils::ResponseError::NoError; } return FALSE; } BOOL LauncherManager::getAndCreatePaths(PathType type, CString& outPath) { if (type == PathType::Running_Path) { char appPath[MAX_PATH]; DWORD size = GetModuleFileNameA(NULL, appPath, MAX_PATH); if (size) { outPath = CString(appPath); return TRUE; } } else if (type == PathType::Desktop_Directory) { TCHAR desktopPath[MAX_PATH]; auto success = SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath)); outPath = CString(desktopPath); return success; } else if (type == PathType::Temp_Directory) { TCHAR tempPath[MAX_PATH]; auto success = GetTempPath(MAX_PATH, tempPath); outPath = CString(tempPath); return success; } else { TCHAR localDataPath[MAX_PATH]; if (type == PathType::StartMenu_Directory) { TCHAR startMenuPath[MAX_PATH]; auto success = SUCCEEDED(SHGetFolderPath(NULL, CSIDL_STARTMENU, NULL, 0, startMenuPath)); outPath = CString(startMenuPath) + _T("\\Programs\\HQ\\"); success = SHCreateDirectoryEx(NULL, outPath, NULL) || ERROR_ALREADY_EXISTS == GetLastError(); return success; } else if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, localDataPath))) { _tcscat_s(localDataPath, _T("\\") + DIRECTORY_NAME_APP + _T("\\")); outPath = CString(localDataPath); if (type == PathType::Download_Directory) { outPath += DIRECTORY_NAME_DOWNLOADS + _T("\\"); } else if (type == PathType::Interface_Directory) { outPath += DIRECTORY_NAME_INTERFACE; } else if (type == PathType::Content_Directory) { outPath += DIRECTORY_NAME_CONTENT; } return (CreateDirectory(outPath, NULL) || ERROR_ALREADY_EXISTS == GetLastError()); } } return FALSE; } BOOL LauncherManager::getInstalledVersion(const CString& path, CString& version) { CStdioFile cfile; BOOL success = cfile.Open(path, CFile::modeRead); if (success) { cfile.ReadString(version); cfile.Close(); } return success; } HWND LauncherManager::launchApplication() { CString installDir; LauncherManager::getAndCreatePaths(PathType::Interface_Directory, installDir); CString interfaceExe = installDir + _T("\\interface.exe"); CString urlParam = _T("--url \"") + _domainURL + ("\" "); CString scriptsURL = installDir + _T("\\scripts\\simplifiedUIBootstrapper.js"); scriptsURL.Replace(_T("\\"), _T("/")); CString scriptsParam = _T("--defaultScriptsOverride \"") + scriptsURL + ("\" "); CString cacheDir; LauncherManager::getAndCreatePaths(PathType::Content_Directory, cacheDir); CString cacheParam = _T("--cache \"") + cacheDir + ("\" "); CString nameParam = !_displayName.IsEmpty() ? _T("--displayName \"") + _displayName + ("\" ") : _T(""); CString tokensParam = _T(""); if (!_tokensJSON.IsEmpty()) { CString parsedTokens = _tokensJSON; parsedTokens.Replace(_T("\""), _T("\\\"")); tokensParam = _T("--tokens \""); tokensParam += parsedTokens + _T("\" "); } CString bookmarkParam = _T("--setBookmark hqhome=\"") + _domainURL + ("\" "); CString params = urlParam + scriptsParam + cacheParam + nameParam + tokensParam + bookmarkParam + EXTRA_PARAMETERS; _shouldLaunch = FALSE; return LauncherUtils::executeOnForeground(interfaceExe, params); } BOOL LauncherManager::createConfigJSON() { CString configPath; LauncherManager::getAndCreatePaths(PathType::Interface_Directory, configPath); configPath += "\\config.json"; std::ofstream configFile(configPath, std::ofstream::binary); if (configFile.fail()) { return FALSE; } Json::Value config; CString applicationPath; LauncherManager::getAndCreatePaths(PathType::Launcher_Directory, applicationPath); applicationPath += LAUNCHER_EXE_FILENAME; config["loggedIn"] = _loggedIn; config["launcherPath"] = LauncherUtils::cStringToStd(applicationPath); config["version"] = LauncherUtils::cStringToStd(_latestVersion); config["domain"] = LauncherUtils::cStringToStd(_domainURL); config["organizationBuildTag"] = LauncherUtils::cStringToStd(_organizationBuildTag); CString content; getAndCreatePaths(PathType::Content_Directory, content); config["content"] = LauncherUtils::cStringToStd(content); configFile << config; configFile.close(); return TRUE; } LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain, CString& content, bool& loggedIn, CString& organizationBuildTag) { CString configPath; getAndCreatePaths(PathType::Interface_Directory, configPath); configPath += "\\config.json"; std::ifstream configFile(configPath, std::ifstream::binary); if (configFile.fail()) { return LauncherUtils::ResponseError::Open; } Json::Value config; configFile >> config; if (config["version"].isString() && config["domain"].isString() && config["content"].isString()) { loggedIn = config["loggedIn"].asBool(); version = config["version"].asCString(); domain = config["domain"].asCString(); content = config["content"].asCString(); if (config["organizationBuildTag"].isString()) { organizationBuildTag = config["organizationBuildTag"].asCString(); } else { organizationBuildTag = ""; } configFile.close(); return LauncherUtils::ResponseError::NoError; } configFile.close(); return LauncherUtils::ResponseError::ParsingJSON; } bool findBuildInResponse(const Json::Value& json, const CString& tag, CString& interfaceUrlOut, CString& interfaceVersionOut) { if (json["results"].isArray()) { auto& results = json["results"]; int count = results.size(); for (int i = 0; i < count; i++) { if (results[i].isObject()) { Json::Value result = results[i]; if (result["name"].asCString() == tag) { if (result["latest_version"].isInt()) { std::string version = std::to_string(result["latest_version"].asInt()); interfaceVersionOut = CString(version.c_str()); } else { return false; } if (result["installers"].isObject() && result["installers"]["windows"].isObject() && result["installers"]["windows"]["zip_url"].isString()) { interfaceUrlOut = result["installers"]["windows"]["zip_url"].asCString(); } else { return false; } return true; } } } } return false; } LauncherUtils::ResponseError LauncherManager::readOrganizationJSON(const CString& hash) { CString contentTypeJson = L"content-type:application/json"; CString response; CString url = _T("/organizations/") + hash + _T(".json"); LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(), true, L"orgs.highfidelity.com", url, contentTypeJson, CStringA(), response, false); if (error != LauncherUtils::ResponseError::NoError) { return error; } Json::Value json; if (LauncherUtils::parseJSON(response, json)) { if (json["content_set_url"].isString() && json["domain"].isString()) { _contentURL = json["content_set_url"].asCString(); _domainURL = json["domain"].asCString(); _organizationBuildTag = json.get("build_tag", "").asCString(); auto buildTag = _organizationBuildTag.IsEmpty() ? _defaultBuildTag : _organizationBuildTag; if (!findBuildInResponse(_latestBuilds, buildTag, _latestApplicationURL, _latestVersion)) { return LauncherUtils::ResponseError::ParsingJSON; } return LauncherUtils::ResponseError::NoError; } } return LauncherUtils::ResponseError::ParsingJSON; } void LauncherManager::getMostRecentBuilds(CString& launcherUrlOut, CString& launcherVersionOut, CString& interfaceUrlOut, CString& interfaceVersionOut) { CString contentTypeJson = L"content-type:application/json"; std::function httpCallback = [&](CString response, int err) { LauncherUtils::ResponseError error = LauncherUtils::ResponseError(err); if (error == LauncherUtils::ResponseError::NoError) { Json::Value& json = _latestBuilds; if (LauncherUtils::parseJSON(response, json)) { _defaultBuildTag = json.get("default_tag", "").asCString(); auto buildTag = _organizationBuildTag.IsEmpty() ? _defaultBuildTag : _organizationBuildTag; addToLog(_T("Build tag is: ") + buildTag); if (json["launcher"].isObject()) { if (json["launcher"]["windows"].isObject() && json["launcher"]["windows"]["url"].isString()) { launcherUrlOut = json["launcher"]["windows"]["url"].asCString(); } if (json["launcher"]["version"].isInt()) { std::string version = std::to_string(json["launcher"]["version"].asInt()); launcherVersionOut = CString(version.c_str()); } } if (launcherUrlOut.IsEmpty() || launcherVersionOut.IsEmpty()) { error = LauncherUtils::ResponseError::ParsingJSON; } if (!findBuildInResponse(json, buildTag, _latestApplicationURL, _latestVersion)) { addToLog(_T("Failed to find build")); error = LauncherUtils::ResponseError::ParsingJSON; } } } onMostRecentBuildsReceived(response, error); }; bool useHTTPS{ true }; CString domainName; if (domainName.GetEnvironmentVariable(L"HQ_LAUNCHER_BUILDS_DOMAIN")) { addToLog(_T("Using overridden builds domain: ") + domainName); useHTTPS = false; } else { // We have 2 ways to adjust the domain, one of which forces http (HQ_LAUNCHER_BUILDS_DOMAIN), // the other (HIFI_THUNDER_URL) of which uses https. They represent different use-cases, but // would ideally be combined by including the protocol in the url, but because // code is going to be replaced soon, we will leave it like this for now. if (domainName.GetEnvironmentVariable(L"HIFI_THUNDER_URL")) { addToLog(_T("Using overridden thunder url: ") + domainName); } else { domainName = L"thunder.highfidelity.com"; } } CString pathName; if (pathName.GetEnvironmentVariable(L"HQ_LAUNCHER_BUILDS_PATH")) { addToLog(_T("Using overridden builds path: ") + pathName); useHTTPS = false; } else { pathName = L"/builds/api/tags/latest?format=json"; } LauncherUtils::httpCallOnThread(getHttpUserAgent(), useHTTPS, domainName, pathName, contentTypeJson, CStringA(), false, httpCallback); } void LauncherManager::onMostRecentBuildsReceived(const CString& response, LauncherUtils::ResponseError error) { if (error == LauncherUtils::ResponseError::NoError) { addToLog(_T("Latest launcher version: ") + _latestLauncherVersion); CString currentVersion = _currentVersion; BOOL isInstalled = _isInstalled && _loggedIn; bool newInterfaceVersion = _latestVersion.Compare(currentVersion) != 0; bool newLauncherVersion = _latestLauncherVersion.Compare(_launcherVersion) != 0 && _updateLauncherAllowed; if (newLauncherVersion) { CString updatingMsg; updatingMsg.Format(_T("Updating Launcher from version: %s to version: %s"), _launcherVersion, _latestLauncherVersion); addToLog(updatingMsg); _shouldUpdateLauncher = TRUE; _shouldDownloadLauncher = TRUE; _keepLoggingIn = !isInstalled; _keepUpdating = isInstalled && newInterfaceVersion; } else { if (_updateLauncherAllowed) { addToLog(_T("Already running most recent build. Launching interface.exe")); } else { addToLog(_T("Updating the launcher was not allowed --noUpdate")); } if (isInstalled) { addToLog(_T("Installed version: ") + currentVersion); if (!newInterfaceVersion) { addToLog(_T("Already running most recent build. Launching interface.exe")); _shouldLaunch = TRUE; _shouldShutdown = TRUE; } else { addToLog(_T("New build found. Updating")); _shouldUpdate = TRUE; } } else if (_loggedIn) { addToLog(_T("Interface not found but logged in. Reinstalling")); _shouldUpdate = TRUE; } else { _shouldInstall = TRUE; } } _shouldWait = FALSE; } else { setFailed(true); CString msg; msg.Format(_T("Getting most recent builds has failed with error: %d"), error); addToLog(msg); msg.Format(_T("Response: %s"), response); addToLog(msg); } } LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString& username, const CString& password) { CStringA post = "grant_type=password&username="; post += username; post += "&password="; post += password; post += "&scope=owner"; CString contentTypeText = L"content-type:application/x-www-form-urlencoded"; CString response; LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(), true, L"metaverse.highfidelity.com", L"/oauth/token", contentTypeText, post, response, true); if (error != LauncherUtils::ResponseError::NoError) { return error; } Json::Value json; if (LauncherUtils::parseJSON(response, json)) { if (json["error"].isString()) { return LauncherUtils::ResponseError::BadCredentials; } else if (json["access_token"].isString()) { _tokensJSON = response; return LauncherUtils::ResponseError::NoError; } } return LauncherUtils::ResponseError::ParsingJSON; } BOOL LauncherManager::createApplicationRegistryKeys(int size) { const std::string REGISTRY_PATH = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\HQ"; BOOL success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayName", "HQ"); CString installDir; getAndCreatePaths(PathType::Launcher_Directory, installDir); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "InstallLocation", LauncherUtils::cStringToStd(installDir)); std::string applicationExe = LauncherUtils::cStringToStd(installDir + LAUNCHER_EXE_FILENAME); std::string uninstallPath = '"' + applicationExe + '"' + " --uninstall"; success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "UninstallString", uninstallPath); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayVersion", LauncherUtils::cStringToStd(_latestVersion)); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayIcon", applicationExe); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "Publisher", "Overte"); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "InstallDate", LauncherUtils::cStringToStd(CTime::GetCurrentTime().Format("%Y%m%d"))); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "EstimatedSize", (DWORD)size); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "NoModify", (DWORD)1); success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "NoRepair", (DWORD)1); return success; } BOOL LauncherManager::deleteApplicationRegistryKeys() { const CString REGISTRY_PATH = _T("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\HQ"); return LauncherUtils::deleteRegistryKey(REGISTRY_PATH); } BOOL LauncherManager::uninstallApplication() { CString installDir; getAndCreatePaths(PathType::Launcher_Directory, installDir); BOOL success = LauncherUtils::deleteFileOrDirectory(installDir); if (success) { deleteShortcuts(); deleteApplicationRegistryKeys(); } return success; } void LauncherManager::onZipExtracted(ProcessType type, int size) { if (type == ProcessType::UnzipContent) { addToLog(_T("Downloading application.")); downloadApplication(); } else if (type == ProcessType::UnzipApplication) { createShortcuts(); addToLog(_T("Launching application.")); _shouldLaunch = TRUE; if (!_shouldUpdate) { addToLog(_T("Creating registry keys.")); createApplicationRegistryKeys(size); } _shouldShutdown = TRUE; } } BOOL LauncherManager::extractApplication() { CString installPath; getAndCreatePaths(LauncherManager::PathType::Interface_Directory, installPath); std::function onExtractFinished = [&](int type, int size) { addToLog(_T("Creating config.json")); createConfigJSON(); if (size > 0) { onZipExtracted((ProcessType)type, size); } else { addToLog(_T("Error decompressing application zip file.")); setFailed(true); } }; std::function onProgress = [&](float progress) { updateProgress(ProcessType::UnzipApplication, max(progress, 0.0f)); }; _currentProcess = ProcessType::UnzipApplication; BOOL success = LauncherUtils::unzipFileOnThread(ProcessType::UnzipApplication, LauncherUtils::cStringToStd(_applicationZipPath), LauncherUtils::cStringToStd(installPath), onExtractFinished, onProgress); if (success) { addToLog(_T("Created thread for unzipping application.")); } else { addToLog(_T("Failed to create thread for unzipping application.")); } return success; } void LauncherManager::onFileDownloaded(ProcessType type) { if (type == ProcessType::DownloadContent) { addToLog(_T("Deleting content directory before install")); CString contentDir; getAndCreatePaths(PathType::Content_Directory, contentDir); LauncherUtils::deleteDirectoryOnThread(contentDir, [&](bool error) { if (!error) { addToLog(_T("Installing content.")); installContent(); } else { addToLog(_T("Error deleting content directory.")); setFailed(true); } }); } else if (type == ProcessType::DownloadApplication) { addToLog(_T("Deleting application directory before install")); CString applicationDir; getAndCreatePaths(PathType::Interface_Directory, applicationDir); LauncherUtils::deleteDirectoryOnThread(applicationDir, [&](bool error) { if (!error) { addToLog(_T("Installing application.")); extractApplication(); } else { addToLog(_T("Error deleting install directory.")); setFailed(true); } }); } else if (type == ProcessType::DownloadLauncher) { _shouldRestartNewLauncher = true; } } void LauncherManager::restartNewLauncher() { closeLog(); ContinueActionOnStart continueAction = ContinueActionOnStart::ContinueFinish; if (_keepUpdating) { continueAction = ContinueActionOnStart::ContinueUpdate; } else if (_keepLoggingIn) { continueAction = ContinueActionOnStart::ContinueLogIn; } CStringW params; params.Format(_T(" --restart --noUpdate --continueAction %s"), getContinueActionParam(continueAction)); LauncherUtils::launchApplication(_tempLauncherPath, params.GetBuffer()); Sleep(500); } BOOL LauncherManager::installContent() { std::string contentZipFile = LauncherUtils::cStringToStd(_contentZipPath); CString contentPath; getAndCreatePaths(LauncherManager::PathType::Content_Directory, contentPath); std::function onInstallFinished = [&](int type, int size) { if (size > 0) { addToLog(_T("Content zip decompresed.")); onZipExtracted((ProcessType)type, size); } else { addToLog(_T("Error decompressing content zip file.")); setFailed(true); } }; std::function onProgress = [&](float progress) { updateProgress(ProcessType::UnzipContent, max(progress, 0.0f)); }; _currentProcess = ProcessType::UnzipContent; BOOL success = LauncherUtils::unzipFileOnThread(ProcessType::UnzipContent, contentZipFile, LauncherUtils::cStringToStd(contentPath), onInstallFinished, onProgress); if (success) { addToLog(_T("Created thread for unzipping content.")); } else { addToLog(_T("Failed to create thread for unzipping content.")); } return success; } BOOL LauncherManager::downloadFile(ProcessType type, const CString& url, CString& outPath) { BOOL success = TRUE; if (outPath.IsEmpty()) { CString fileName = url.Mid(url.ReverseFind('/') + 1); CString downloadDirectory; BOOL success = getAndCreatePaths(LauncherManager::PathType::Download_Directory, downloadDirectory); outPath = downloadDirectory + fileName; } _currentProcess = type; if (success) { addToLog(_T("Downloading: ") + url); std::function onDownloadFinished = [&](int type, bool error) { if (!error) { onFileDownloaded((ProcessType)type); } else { if (type == ProcessType::DownloadApplication) { addToLog(_T("Error downloading content.")); } else if (type == ProcessType::DownloadLauncher) { addToLog(_T("Error downloading launcher.")); } else { addToLog(_T("Error downloading application.")); } setFailed(true); } }; std::function onProgress = [&, type](float progress) { updateProgress(_currentProcess, max(progress, 0.0f)); }; success = LauncherUtils::downloadFileOnThread(type, url, outPath, onDownloadFinished, onProgress); } return success; } BOOL LauncherManager::downloadContent() { addToLog(_T("Downloading content.")); CString contentURL = getContentURL(); return downloadFile(ProcessType::DownloadContent, contentURL, _contentZipPath); } BOOL LauncherManager::downloadApplication() { CString applicationURL = getLatestInterfaceURL(); return downloadFile(ProcessType::DownloadApplication, applicationURL, _applicationZipPath); } BOOL LauncherManager::downloadNewLauncher() { _shouldDownloadLauncher = FALSE; getAndCreatePaths(PathType::Temp_Directory, _tempLauncherPath); CString tempName = _T("HQLauncher") + _launcherVersion + _T(".exe"); _tempLauncherPath += tempName; return downloadFile(ProcessType::DownloadLauncher, _latestLauncherURL, _tempLauncherPath); } void LauncherManager::onCancel() { if (_currentProcess == ProcessType::UnzipApplication) { _latestVersion = _T(""); _version = _T(""); createConfigJSON(); } }