/
BirdLeon
/
ROBLOX2016
Обзор
Документация
Войти
/
BirdLeon
/
ROBLOX2016
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
Installer/Bootstrapper/FileDeployer.cpp
244 строки
7 KB
PatoFlamejanteTV
full source code
19 дек 2024, 19:11
19 дек 2024, 19:11
05db15d
Код
Авторство
О чём код?
#include "stdafx.h" #include <boost/function.hpp> #include <boost/bind.hpp> #include "FileDeployer.h" #include "FileSystem.h" #include "SharedHelpers.h" #include "AtlPath.h" #include "AtlFile.h" #include "atlutil.h" #include "MD5Hasher.h" #include "HttpTools.h" #include "Unzipper.h" FileDeployer::FileDeployer(IInstallerSite *site, bool perUser) : _site(site), _perUser(perUser), _bytesDownloaded(0) { } // Downloads a file from the install site. Uses a cached file when possible std::wstring FileDeployer::downloadVersionedFile(const TCHAR* name, Progress& progress, bool usePrimaryCdn) { std::wstring downloadsDirectory = FileSystem::getSpecialFolder(_perUser ? FileSystem::RobloxUserApplicationData : FileSystem::RobloxCommonApplicationData, true, "Downloads"); if (downloadsDirectory.empty()) throw std::runtime_error("Failed to create Downloads folder"); CRegKey key = CreateKey(_perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, _T("Software\\RobloxReg\\ETags")); std::string etag = QueryStringValue(key, name); std::wstring oldFileName; if (!etag.empty()) { // Make sure the cached file hasn't been deleted // TODO: Lock the file so it can't be deleted! oldFileName = downloadsDirectory + convert_s2w(etag); if (!ATLPath::FileExists(oldFileName.c_str())) { etag.clear(); } else { try { MD5Hasher hasher; hasher.addData(std::ifstream(oldFileName.c_str(), std::ios::binary)); std::string hash = hasher.toString(); if (hash!=etag) { LLOG_ENTRY3(_site->Logger(), "WARNING: etag and hash don't match for %S: etag=%s hash=%s", name, etag.c_str(), hash.c_str()); etag.clear(); ::DeleteFile(oldFileName.c_str()); } } catch (std::exception& e) { LLOG_ENTRY2(_site->Logger(), "WARNING: Failed to get MD5 hash for %S: %s", oldFileName.c_str(), exceptionToString(e).c_str()); etag.clear(); } } } if (etag.empty()) oldFileName.clear(); std::wstring tempFileName = simple_logger<wchar_t>::get_temp_filename(_T("tmp")); int status_code; { std::ofstream result(tempFileName.c_str(), std::ios::binary); std::string path; if (_site->InstallVersion().empty()) { path = format_string("/%S", name); } else { path = format_string("/%s-%S", _site->InstallVersion().c_str(), name); } if(usePrimaryCdn) { status_code = HttpTools::httpGetCdn(_site, _site->InstallHost(), path, etag, result, false, boost::bind(&Progress::update, &progress, _site, _1, _2)); } else { status_code = HttpTools::httpGet(_site, _site->InstallHost(), path, etag, result, false, boost::bind(&Progress::update, &progress, _site, _1, _2)); } } _site->CheckCancel(); std::wstring fileName = downloadsDirectory + convert_s2w(etag); // WinInet converts a 304 code to a 200 code, so ignore status_code and check for change in etag if (oldFileName != fileName) { // New file. Cache it please if (!oldFileName.empty()) { if (!::DeleteFile(oldFileName.c_str())) { if (::GetLastError() != ERROR_FILE_NOT_FOUND) { LLOG_ENTRY2(_site->Logger(), "Failed to delete old %S: %S", oldFileName.c_str(), (LPCTSTR)GetLastErrorDescription()); } } } if (!::DeleteFile(fileName.c_str())) { if (::GetLastError() != ERROR_FILE_NOT_FOUND) { LLOG_ENTRY2(_site->Logger(), "Failed to delete %S: %S", fileName.c_str(), (LPCTSTR)GetLastErrorDescription()); return tempFileName; } } // try to read downloaded size for logging WIN32_FILE_ATTRIBUTE_DATA fileAttributeData; if (GetFileAttributesEx(tempFileName.c_str(), GetFileExInfoStandard, &fileAttributeData)) { _bytesDownloaded += fileAttributeData.nFileSizeLow; } copyFile(_site->Logger(), tempFileName.c_str(), fileName.c_str()); key.SetStringValue(name, convert_s2w(etag).c_str()); } if (!::DeleteFile(tempFileName.c_str())) { LLOG_ENTRY2(_site->Logger(), "Failed to delete %S: %S", tempFileName.c_str(), (LPCTSTR)GetLastErrorDescription()); } return fileName; } // Downloads a file from the install site. Uses a cached file when possible void FileDeployer::deployVersionedFile(const TCHAR* name, const TCHAR* subfolder, Progress& progress, bool commitData) { std::wstring path; std::wstring fileName; try { deployVersionedFileInternal(name, subfolder, progress, true /*usePrimaryCdn*/, path, fileName, commitData); } catch( CAtlException e ) { _site->Error(std::runtime_error(format_string("AtlException 0x%x", e.m_hr))); } catch(silent_exception&) { } catch (std::exception& e) { LLOG_ENTRY3(_site->Logger(), "Failure when attempting to deploy %S from %S to %S", name, fileName.c_str(), path.c_str()); cleanupVersionedFile(name,path,fileName); try { //Try to download/unzip one more time, this time do not use the CDN deployVersionedFileInternal(name, subfolder, progress, false /*usePrimaryCdn*/, path, fileName, commitData); } catch(std::exception&) { cleanupVersionedFile(name,path,fileName); //It failed again, so report the first error we got _site->Error(std::runtime_error(format_string("deployVersionedFile %S to \"%S\" failed: %s", name, path.c_str(), exceptionToString(e).c_str()))); } } progress.done = true; } void FileDeployer::deployVersionedFileInternal(const TCHAR* name, const TCHAR* subfolder, Progress& progress, bool usePrimaryCdn, /*output*/ std::wstring& path, /*output*/ std::wstring& fileName, bool commitData) { path = _site->ProgramDirectory(); if (subfolder) path += subfolder; LLOG_ENTRY1(_site->Logger(), "deployVersionedFile %S", name); fileName = downloadVersionedFile(name, progress, usePrimaryCdn); if (!ATLPath::FileExists(fileName.c_str())) throw std::runtime_error(format_string("File doesn't exist: %s", fileName.c_str())); { CAtlFile file; throwHRESULT(file.Create(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING), format_string("Failed to open %S", fileName.c_str())); LARGE_INTEGER len; if (::GetFileSizeEx(file, &len)) { // I don't believe that we will ever have file bigger that 32 bits, so lets skip hi part of the size LLOG_ENTRY3(_site->Logger(), "File size is %d for %S from %S", len.LowPart, fileName.c_str(), name); } else { LLOG_ENTRY3(_site->Logger(), "Failed to get file size for %S: %S from %S", fileName.c_str(), (LPCTSTR)::GetLastErrorDescription(), name); } } _site->CheckCancel(); if (commitData) { CUnzipper unzipper(fileName.c_str()); unzipper.skipExistingFiles = true; unzipper.UnzipTo(path.c_str()); } } void FileDeployer::cleanupVersionedFile(const std::wstring& name, const std::wstring& path, const std::wstring& fileName) { try { if(ATLPath::FileExists(fileName.c_str())) { ::DeleteFile(fileName.c_str()); if (::GetLastError() != ERROR_FILE_NOT_FOUND) { LLOG_ENTRY2(_site->Logger(), "Failed to delete fileName(%S): %S", fileName.c_str(), (LPCTSTR)GetLastErrorDescription()); } } if(ATLPath::FileExists(path.c_str())) { ::DeleteFile(path.c_str()); if (::GetLastError() != ERROR_FILE_NOT_FOUND) { LLOG_ENTRY2(_site->Logger(), "Failed to delete path(%S): %S", path.c_str(), (LPCTSTR)GetLastErrorDescription()); } } //Clear out the etag registry cache, just in case our file delete did not work CRegKey key = CreateKey(_perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, _T("Software\\RobloxReg\\ETags")); key.SetStringValue(name.c_str(), _T("")); } catch(std::exception&) { LLOG_ENTRY3(_site->Logger(), "Error when trying to clean up the deploy of %S from %S to %S", name.c_str(), fileName.c_str(), path.c_str()); } }