using LANCommander.SDK.Enums; using LANCommander.SDK.Extensions; using LANCommander.SDK.Helpers; using LANCommander.SDK.Models; using LANCommander.SDK.PowerShell; using Microsoft.Extensions.Logging; using SharpCompress.Common; using SharpCompress.Readers; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace LANCommander.SDK { public class RedistributableManager { private readonly ILogger Logger; private Client Client { get; set; } public delegate void OnArchiveEntryExtractionProgressHandler(object sender, ArchiveEntryExtractionProgressArgs e); public event OnArchiveEntryExtractionProgressHandler OnArchiveEntryExtractionProgress; public delegate void OnArchiveExtractionProgressHandler(long position, long length); public event OnArchiveExtractionProgressHandler OnArchiveExtractionProgress; public RedistributableManager(Client client) { Client = client; } public RedistributableManager(Client client, ILogger logger) { Client = client; Logger = logger; } public void Install(Game game) { foreach (var redistributable in game.Redistributables) { Install(redistributable); } } public void Install(Redistributable redistributable) { string installScriptTempFile = null; string detectionScriptTempFile = null; string extractTempPath = null; try { var installScript = redistributable.Scripts.FirstOrDefault(s => s.Type == ScriptType.Install); installScriptTempFile = ScriptHelper.SaveTempScript(installScript); var detectionScript = redistributable.Scripts.FirstOrDefault(s => s.Type == ScriptType.DetectInstall); detectionScriptTempFile = ScriptHelper.SaveTempScript(detectionScript); var detectionResult = RunScript(detectionScriptTempFile, redistributable, detectionScript.RequiresAdmin); // Redistributable is not installed if (detectionResult == 0) { if (redistributable.Archives.Count() > 0) { var extractionResult = DownloadAndExtract(redistributable); if (extractionResult.Success) { extractTempPath = extractionResult.Directory; RunScript(installScriptTempFile, redistributable, installScript.RequiresAdmin, extractTempPath); } } else { RunScript(installScriptTempFile, redistributable, installScript.RequiresAdmin, extractTempPath); } } } catch (Exception ex) { Logger?.LogError(ex, "Redistributable {Redistributable} failed to install", redistributable.Name); } finally { if (File.Exists(installScriptTempFile)) File.Delete(installScriptTempFile); if (File.Exists(detectionScriptTempFile)) File.Delete(detectionScriptTempFile); if (Directory.Exists(extractTempPath)) Directory.Delete(extractTempPath); } } private ExtractionResult DownloadAndExtract(Redistributable redistributable) { if (redistributable == null) { Logger?.LogTrace("Redistributable failed to download! No redistributable was specified"); throw new ArgumentNullException("No redistributable was specified"); } var destination = Path.Combine(Path.GetTempPath(), redistributable.Name.SanitizeFilename()); Logger?.LogTrace("Downloading and extracting {Redistributable} to path {Destination}", redistributable.Name, destination); try { Directory.CreateDirectory(destination); using (var redistributableStream = Client.StreamRedistributable(redistributable.Id)) using (var reader = ReaderFactory.Open(redistributableStream)) { redistributableStream.OnProgress += (pos, len) => { OnArchiveExtractionProgress?.Invoke(pos, len); }; reader.EntryExtractionProgress += (object sender, ReaderExtractionEventArgs e) => { OnArchiveEntryExtractionProgress?.Invoke(this, new ArchiveEntryExtractionProgressArgs { Entry = e.Item, Progress = e.ReaderProgress, }); }; reader.WriteAllToDirectory(destination, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true }); } } catch (Exception ex) { Logger?.LogError(ex, "Could not extract to path {Destination}", destination); if (Directory.Exists(destination)) { Logger?.LogTrace("Cleaning up orphaned files after bad install"); Directory.Delete(destination, true); } throw new Exception("The redistributable archive could not be extracted, is it corrupted? Please try again"); } var extractionResult = new ExtractionResult { Canceled = false }; if (!extractionResult.Canceled) { extractionResult.Success = true; extractionResult.Directory = destination; Logger?.LogTrace("Redistributable {Redistributable} successfully downloaded and extracted to {Destination}", redistributable.Name, destination); } return extractionResult; } private int RunScript(string path, Redistributable redistributable, bool requiresAdmin = false, string workingDirectory = "") { var script = new PowerShellScript(); script.AddVariable("Redistributable", redistributable); script.UseWorkingDirectory(workingDirectory); script.UseFile(path); if (requiresAdmin) script.RunAsAdmin(); return script.Execute(); } } }