BOOL FetchUpdaterModule() { int responseCode; TCHAR updateFilePath[MAX_PATH]; BYTE updateFileHash[20]; TCHAR extraHeaders[256]; tsprintf_s (updateFilePath, _countof(updateFilePath)-1, TEXT("%s\\updates\\updater.exe"), lpAppDataPath); if (CalculateFileHash(updateFilePath, updateFileHash)) { TCHAR hashString[41]; HashToString(updateFileHash, hashString); tsprintf_s (extraHeaders, _countof(extraHeaders)-1, TEXT("If-None-Match: %s"), hashString); } else extraHeaders[0] = 0; if (HTTPGetFile(TEXT("https://obsproject.com/update/updater.exe"), updateFilePath, extraHeaders, &responseCode)) { if (responseCode != 200 && responseCode != 304) return FALSE; } return TRUE; }
bool BuilderFileInfo::CheckOutOfDate(BuilderFileInfo& info, uint64_t configurationHash, bool bNoIntermediateFiles) { if (!bNoIntermediateFiles && (!GetCachedPathExists(info.ManifestPath) || !GetCachedPathExists(info.OutputPath))) { info.bOutOfDate = true; if (!GetCachedPathExists(info.ManifestPath)) { Log(LogSeverity::Verbose, "[%s] Out of date because manifest path is non-existant.\n", info.SourcePath.GetFilename().c_str()); } else if (!GetCachedPathExists(info.OutputPath)) { Log(LogSeverity::Verbose, "[%s] Out of date because output path is non-existant.\n", info.SourcePath.GetFilename().c_str()); } } else { uint64_t currentHash = info.Hash; if (!bNoIntermediateFiles && !info.LoadManifest()) { info.bOutOfDate = true; Log(LogSeverity::Verbose, "[%s] Out of date because could not load manifest.\n", info.SourcePath.ToString().c_str()); } else if (!info.SourcePath.IsEmpty() && info.Hash != currentHash) { Log(LogSeverity::Verbose, "[%s] Out of date because file hash was different.\n", info.SourcePath.ToString().c_str()); info.bOutOfDate = true; info.Hash = currentHash; } else { for (const BuilderDependencyInfo& dependencyInfo : info.Dependencies) { uint64_t dependencyHash = CalculateFileHash(dependencyInfo.SourcePath, configurationHash); if (!GetCachedPathExists(dependencyInfo.SourcePath) || dependencyInfo.Hash != dependencyHash) { Log(LogSeverity::Verbose, "[%s] Out of date because dependency was out of date (hash=%i exists=%i): %s.\n", info.SourcePath.GetFilename().c_str(), dependencyInfo.Hash != dependencyHash, GetCachedPathExists(dependencyInfo.SourcePath), dependencyInfo.SourcePath.ToString().c_str() ); info.bOutOfDate = true; break; } } } } return info.bOutOfDate; }
std::vector<BuilderFileInfo> BuilderFileInfo::GetMultipleFileInfos( const std::vector<Platform::Path>& paths, Platform::Path rootDirectory, Platform::Path outputDirectory, uint64_t configurationHash, bool bNoIntermediateFiles ) { std::vector<BuilderFileInfo> result; for (const Platform::Path& path : paths) { BuilderFileInfo info; info.SourcePath = path; Platform::Path relativePath = rootDirectory.RelativeTo(path); assert(relativePath.IsRelative()); info.OutputPath = outputDirectory.AppendFragment(path.ChangeExtension("o").GetFilename(), true); info.ManifestPath = info.OutputPath.ChangeExtension("build.manifest"); info.bOutOfDate = false; info.Hash = CalculateFileHash(info.SourcePath, configurationHash); Platform::Path baseDirectory = info.OutputPath.GetDirectory(); //if (!baseDirectory.Exists()) if (!GetCachedPathExists(baseDirectory)) { baseDirectory.CreateAsDirectory(); } info.bOutOfDate = CheckOutOfDate(info, configurationHash, bNoIntermediateFiles); result.push_back(info); } return result; }
DWORD WINAPI CheckUpdateThread (VOID *arg) { int responseCode; TCHAR extraHeaders[256]; BYTE manifestHash[20]; TCHAR manifestPath[MAX_PATH]; tsprintf_s (manifestPath, _countof(manifestPath)-1, TEXT("%s\\updates\\packages.xconfig"), lpAppDataPath); if (!CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { Log (TEXT("Updater: CryptAcquireContext failed: %08x"), GetLastError()); return 1; } extraHeaders[0] = 0; if (CalculateFileHash(manifestPath, manifestHash)) { TCHAR hashString[41]; HashToString(manifestHash, hashString); tsprintf_s (extraHeaders, _countof(extraHeaders)-1, TEXT("If-None-Match: %s"), hashString); } //this is an arbitrary random number that we use to count the number of unique OBS installations //and is not associated with any kind of identifiable information String strGUID = GlobalConfig->GetString(TEXT("General"), TEXT("InstallGUID")); if (strGUID.IsEmpty()) { GenerateGUID(strGUID); if (strGUID.IsValid()) GlobalConfig->SetString(TEXT("General"), TEXT("InstallGUID"), strGUID); } if (strGUID.IsValid()) { if (extraHeaders[0]) scat(extraHeaders, TEXT("\n")); scat(extraHeaders, TEXT("X-OBS-GUID: ")); scat(extraHeaders, strGUID); } if (HTTPGetFile(TEXT("https://obsproject.com/update/packages.xconfig"), manifestPath, extraHeaders, &responseCode)) { if (responseCode == 200 || responseCode == 304) { String updateInfo; BOOL updatesAvailable; updateInfo = Str("Updater.NewUpdates"); if (ParseUpdateManifest(manifestPath, &updatesAvailable, updateInfo)) { if (updatesAvailable) { updateInfo << TEXT("\r\n") << Str("Updater.DownloadNow"); if (MessageBox (NULL, updateInfo.Array(), Str("Updater.UpdatesAvailable"), MB_ICONQUESTION|MB_YESNO) == IDYES) { if (App->IsRunning()) { if (MessageBox (NULL, Str("Updater.RunningWarning"), NULL, MB_ICONEXCLAMATION|MB_YESNO) == IDNO) goto abortUpdate; } TCHAR updateFilePath[MAX_PATH]; TCHAR cwd[MAX_PATH]; GetModuleFileName(NULL, cwd, _countof(cwd)-1); TCHAR *p = srchr(cwd, '\\'); if (p) *p = 0; tsprintf_s (updateFilePath, _countof(updateFilePath)-1, TEXT("%s\\updates\\updater.exe"), lpAppDataPath); //note, can't use CreateProcess to launch as admin. SHELLEXECUTEINFO execInfo; zero(&execInfo, sizeof(execInfo)); execInfo.cbSize = sizeof(execInfo); execInfo.lpFile = updateFilePath; #ifndef _WIN64 execInfo.lpParameters = TEXT("Win32"); #else execInfo.lpParameters = TEXT("Win64"); #endif execInfo.lpDirectory = cwd; execInfo.nShow = SW_SHOWNORMAL; if (!ShellExecuteEx (&execInfo)) { AppWarning(TEXT("Can't launch updater '%s': %d"), updateFilePath, GetLastError()); goto abortUpdate; } //force OBS to perform another update check immediately after updating in case of issues //with the new version GlobalConfig->SetInt(TEXT("General"), TEXT("LastUpdateCheck"), 0); //since we're in a separate thread we can't just PostQuitMessage ourselves SendMessage(hwndMain, WM_CLOSE, 0, 0); } } } } } abortUpdate: CryptReleaseContext(hProvider, 0); return 0; }
BOOL ParseUpdateManifest (TCHAR *path, BOOL *updatesAvailable, String &description) { XConfig manifest; XElement *root; if (!manifest.Open(path)) return FALSE; root = manifest.GetRootElement(); DWORD numPackages = root->NumElements(); DWORD totalUpdatableFiles = 0; int priority, bestPriority = 999; for (DWORD i = 0; i < numPackages; i++) { XElement *package; package = root->GetElementByID(i); CTSTR packageName = package->GetName(); //find out if this package is relevant to us String platform = package->GetString(TEXT("platform")); if (!platform) continue; if (scmp(platform, TEXT("all"))) { #ifndef _WIN64 if (scmp(platform, TEXT("Win32"))) continue; #else if (scmp(platform, TEXT("Win64"))) continue; #endif } //what is it? String name = package->GetString(TEXT("name")); String version = package->GetString(TEXT("version")); //figure out where the files belong XDataItem *pathElement = package->GetDataItem(TEXT("path")); if (!pathElement) continue; CTSTR path = pathElement->GetData(); if (path == NULL) path = TEXT(""); if (!IsSafePath(path)) continue; priority = package->GetInt(TEXT("priority"), 999); //get the file list for this package XElement *files = package->GetElement(TEXT("files")); if (!files) continue; DWORD numFiles = files->NumElements(); DWORD numUpdatableFiles = 0; for (DWORD j = 0; j < numFiles; j++) { XElement *file = files->GetElementByID(j); String hash = file->GetString(TEXT("hash")); if (!hash || hash.Length() != 40) continue; String fileName = file->GetName(); if (!fileName) continue; if (!IsSafeFilename(fileName)) continue; String filePath; filePath << path; filePath << fileName; BYTE fileHash[20]; TCHAR fileHashString[41]; if (OSFileExists(filePath)) { if (!CalculateFileHash(filePath, fileHash)) continue; HashToString(fileHash, fileHashString); if (!scmp(fileHashString, hash)) continue; } numUpdatableFiles++; } if (numUpdatableFiles) { if (version.Length()) description << name << TEXT(" (") << version << TEXT(")\r\n"); else description << name << TEXT("\r\n"); if (priority < bestPriority) bestPriority = priority; } totalUpdatableFiles += numUpdatableFiles; numUpdatableFiles = 0; } manifest.Close(); if (totalUpdatableFiles) { if (!FetchUpdaterModule()) return FALSE; } if (bestPriority <= 5) *updatesAvailable = TRUE; else *updatesAvailable = FALSE; return TRUE; }