#pragma once #include "commonresourceconstants.h" #include "Maindialog.h" #include #include #include #include #include "boost/thread.hpp" #include #include #include "FileSystem.h" #include "format_string.h" #include #include "Progress.h" #include "SharedHelpers.h" #include "FileDeployer.h" #include "ComModule.h" #include "InfluxDbHelper.h" class Bootstrapper : public boost::enable_shared_from_this, public IInstallerSite { boost::condition_variable done; boost::mutex mut; CHandle latestProcess; // A shared file that contains the PID of the latest instance of this module const DWORD mainThreadId; int windowDelay; int cancelDelay; bool editMode; bool service; bool force; bool fullCheck; bool isUninstall; bool isFailIfNotUpToDate; bool debug; bool windowed; bool dontStartApp; bool perUser; bool adminInstallDetected; bool userCancelled; bool isNop; bool requestedInstall; // indicates that the -install command line flag was used bool performedNewInstall; bool performedUpdateInstall; bool performedBootstrapperUpdateAndQuit; time_t startTime; time_t userCancelledTime; DWORD startTickCounter; int stage; std::vector errors; unsigned long reportStatGuid; std::string moduleVersionNumber; // The version number of this module bool silentMode; // only used if silentMode is true, which means we don't show dialog at all CEvent robloxReady; // this mean that bootstrapper should not run anything except preDeploy bool noRun; bool waitOnStart; virtual std::string Type() = 0; virtual std::string BinaryType() { return ""; } std::string baseHost; std::string installHost; // HTTP host for getting installation components std::string installVersion; // version string of latest installer std::string browserTrackerId; // passed in from website int exitCode; //will be used to track all versions of this component to do proper clean up std::list prevVersions; int influxdbLottery; std::wstring prevVersionRegKey(); std::wstring prevVersionValueKey(int index); void loadPrevVersions(); void storePrevVersions(); void postData(std::fstream &data); void LogMACaddress(); void showWindowAfterDelay() { { boost::unique_lock lock(mut); if (done.timed_wait(lock, boost::get_system_time() + boost::posix_time::time_duration(0, 0, windowDelay))) return; } LOG_ENTRY1("Bootstrapper::showWindowAfterDelay showing window, thread id = %d", ::GetCurrentThreadId()); if (!this->isSilentMode()) { dialog->ShowWindow(CMainDialog::WindowTimeDelayShow); } } void showCancelAfterDelay() { { boost::unique_lock lock(mut); if (done.timed_wait(lock, boost::get_system_time() + boost::posix_time::time_duration(0, 0, cancelDelay))) return; } dialog->ShowCancelButton(CMainDialog::CancelTimeDelayShow); } void userCancel(); void checkCancel(); bool isIEUpToDate(); void checkIEPrerequisit(); void checkDirectXPrerequisit(); bool isDirectXUpToDate(); void checkOSPrerequisit(); void checkCPUPrerequisit(); void parseCmdLine(); void run(); void install(); void uninstall(); void uninstall(bool isPerUser); bool checkBootstrapperVersion(); void shutdownRobloxApp(std::wstring appExeName); void checkDiskSpace(); bool shutdownProgress(int& time, int pos, int max); void deleteVersionFolder(std::string version); void setLatestProcess(); bool isLatestProcess(); void deleteVersionsDirectoryContents(); void forceUninstallMFCStudio(); void forceMFCStudioCleanup(); // If size is set to 0, size will not be included in the log. // Reporting eventually boils down to http requests, which will be made // with normal strings (not unicode strings), so this method also uses // normal strings to that the caller will consider and handle unicode as needed. void reportDurationAndSizeEvent(const char* category, const char* result, DWORD duration, DWORD size); void reportFinishingHelper(const char* category, const char* result); void ReportToInfluxDbImpl(const std::string& json); virtual bool EarlyInstall(bool isUninstall) { return false; } protected: HINSTANCE hInstance; HWND launchedAppHwnd; boost::scoped_ptr dialog; boost::scoped_ptr deployer; CRegKey classesKey; //TODO move this into client part std::wstring robloxAppArgs; // If empty, then don't launch RobloxApp.exe InfluxDb influxDb; Bootstrapper(HINSTANCE hInstance); static void dummyProgress(int pos, int max) {} static void createDirectory(const TCHAR* dir); void setStage(int s) { stage = s; } bool isPerUser() const { return perUser; } bool isDontStartApp() { return dontStartApp; } CMainDialog *Dialog() { return dialog.get(); } void setInstallVersion(std::string version) { installVersion = version; } std::wstring GetSelfName() const; virtual void initialize(); virtual void createDialog() {} ; // lets make sure that this guy will be 100% client only (RCC won't run this code) virtual HRESULT SheduleRobloxUpdater() { return S_OK; }; virtual HRESULT UninstallRobloxUpdater() { return S_OK; }; // will load settings from server virtual void LoadSettings() {} virtual bool CanRunBgTask() { return true; } virtual void SetupInfluxDb(); virtual std::string InfluxDbUrl() { return ""; } virtual std::string InfluxDbDatabase() { return ""; } virtual std::string InfluxDbUser() { return ""; } virtual std::string InfluxDbPassword() { return ""; } virtual int InfluxDbReportHundredthsPercentage() { return 0; } virtual int InfluxDbInstallHundredthsPercentage() { return 0; } void ReportToInfluxDb(int throttle, const std::string& json, bool blocking = false); void ReportInstallMetricsToInfluxDb(bool isUpdate, const std::string& result, const std::string& message); virtual void SetupGoogleAnalytics() {} virtual bool ValidateInstalledExeVersion() { return false; } virtual bool IsInstalledVersionUptodate() { return true; } virtual bool IsPreDeployOn() { return false; } virtual bool NeedPreDeployRun() { return false; } virtual void RunPreDeploy() {} virtual bool ForceNoDialogMode() { return false; } virtual bool GetLogChromeProtocolFix() { return false; } virtual bool GetUseNewVersionFetch() { return false; } virtual bool GetUseDataDomain() { return true; } virtual bool CreateEdgeRegistry() { return true; } virtual bool DeleteEdgeRegistry() { return false; } std::string fetchVersionGuid(std::string product); virtual std::string fetchVersionGuidFromWeb(std::string product); void writeAppSettings(); void CreateProcess(const TCHAR* module, const TCHAR* args, PROCESS_INFORMATION& pi); void RegisterElevationPolicy(const TCHAR* appName, const TCHAR* appPath); void RegisterUninstall(const TCHAR *productName); void RegisterProtocolHandler(const std::wstring& protocolScheme, const std::wstring& exePath, const std::wstring& installKeyRegPath); void UnregisterProtocolHandler(const std::wstring& protocolScheme); bool isInstalled(std::wstring productKey); void CreateShortcuts(bool forceDesktopIconCreation, const TCHAR *linkName, const TCHAR *exeName, const TCHAR *params); void DeleteOldVersionsDirectories(); void DoDeployComponents(const std::vector > &files, bool isUpdating, bool commitData); bool deleteDirectory(const std::wstring &refcstrRootDirectory, bool childrenOnly = false); std::string queryInstalledInstallHost(); std::string queryInstalledVersion(const std::wstring& RegistryPath); std::string queryInstalledVersion() { return queryInstalledVersion(GetRegistryPath()); } std::wstring getInstalledBootstrapperPath(const std::wstring& RegistryPath); std::wstring getInstalledBootstrapperPath() { return getInstalledBootstrapperPath(GetRegistryPath()); } void deploySelf(bool commitData); void message(const std::string& message); void deleteLegacyShortcuts(); void validateAndFixChromeState(); HWND GetHwndFromPID(DWORD pid); public: simple_logger logger; virtual ~Bootstrapper(void); static boost::shared_ptr Create(HINSTANCE hInstance, Bootstrapper*(*newBootstrapper)(HINSTANCE)); static bool hasSse2(); virtual std::wstring programDirectory() const; // Local location for installing components to std::wstring programDirectory(bool isPerUser) const; // Local location for installing components to std::wstring baseProgramDirectory(bool isPerUser) const; //place where all installed versions are bool hasReturnValue() const { return isFailIfNotUpToDate; } bool hasLegacyStudioDesktopShortcut(); bool isSilentMode() const { return silentMode; } bool isWindowed() const { return windowed; } bool WaitForCompletion() { DWORD result = WaitForSingleObject(robloxReady, 30*60*1000); return (result == WAIT_TIMEOUT); } int ExitCode() const { return exitCode; } void cleanKey(const std::wstring regPathToDelete, CRegKey &hk); std::string BrowserTrackerId() { return browserTrackerId; } HWND getLaunchedAppHwnd() { return launchedAppHwnd; } // IInstallerSite virtual std::string BaseHost() { return baseHost; } virtual std::string InstallHost() { return installHost; } virtual std::string InstallVersion() { return installVersion; } virtual simple_logger &Logger() { return logger; } virtual void progressBar(int pos, int max); virtual void CheckCancel() { checkCancel(); } virtual void Error(const std::exception& e) { error(e); } virtual std::wstring ProgramDirectory() { return programDirectory(); } virtual bool UseNewCdn() { return false; } virtual bool ReplaceCdnTxt() { return false; } void Close(); void postLogFile(bool uploadApp); bool hasErrors() const { return errors.size()>0; } bool hasLongUserCancel() const { return userCancelled && ((userCancelledTime - startTime) > 60); } bool hasUserCancel() const { return userCancelled; } bool stopRequested() const { return userCancelled || hasErrors(); } void error(const std::exception& e); DWORD GetElapsedTime(); void ReportElapsedEvent(bool startingGameClient); virtual void RegisterEvent(const TCHAR *eventName) {}; virtual void FlushEvents() {}; // Returns true if we want to enhance remote event reporting with // information needed to separate different bootstrapper use cases // (e.g. to be able to separate installs vs plays vs check-if-up-to-date). virtual bool PerModeLoggingEnabled() { return false; } void ReportFinishing(const char* status); virtual std::wstring GetRegistryPath() const = 0; virtual std::wstring GetRegistrySubPath() const = 0; virtual const wchar_t* GetVersionFileName() const = 0; virtual const wchar_t* GetGuidName() const = 0; virtual const wchar_t* GetStartAppMutexName() const = 0; virtual const wchar_t* GetBootstrapperMutexName() const = 0; virtual const wchar_t* GetFriendlyName() const = 0; virtual const wchar_t* GetProtocolHandlerUrlScheme() const = 0; virtual void DoInstallApp() {}; virtual void DoUninstallApp(CRegKey &hk) {}; //component specific uninstall actions virtual std::wstring GetRobloxAppFileName() const { return std::wstring(_T("")); } virtual std::wstring GetBootstrapperFileName() const { return std::wstring(_T("")); } virtual std::wstring GetProductCodeKey() const { return std::wstring(_T("")); } virtual void DeployComponents(bool isUpdating, bool commitData) {}; virtual void StartRobloxApp(bool fromInstall) {} virtual bool IsPlayMode() { return false; } virtual bool HasUnhideGuid() {return false; } virtual bool ProcessProtocolHandlerArgs(const std::map& args) { return false; } virtual bool ProcessArg(wchar_t** args, int &pos, int count) { return false; } }; int BootstrapperMain(HINSTANCE hInstance, int nCmdShow, Bootstrapper*(*newBootstrapper)(HINSTANCE));