using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Management.Automation.Runspaces;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using System.Security.Policy;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using System.Xml.Linq;
|
|
using System.Xml.Serialization;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using MessageBox = System.Windows.MessageBox;
|
|
|
|
namespace TrustedUninstaller.Shared
|
|
{
|
|
public class Playbook
|
|
{
|
|
public string Name { get; set; }
|
|
public string ShortDescription { get; set; }
|
|
public string Description { get; set; }
|
|
|
|
public string Title { get; set; }
|
|
public string Username { get; set; }
|
|
public string Details { get; set; }
|
|
public string Version { get; set; }
|
|
|
|
[XmlArray]
|
|
[XmlArrayItem(ElementName = "CheckboxPage", Type = typeof(CheckboxPage))]
|
|
[XmlArrayItem(ElementName = "RadioPage", Type = typeof(RadioPage))]
|
|
[XmlArrayItem(ElementName = "RadioImagePage", Type = typeof(RadioImagePage))]
|
|
public FeaturePage[] FeaturePages { get; set; }
|
|
|
|
public string ProgressText { get; set; } = "Deploying the selected Playbook configuration onto the system.";
|
|
public int EstimatedMinutes { get; set; } = 25;
|
|
|
|
#nullable enable
|
|
public string[]? SupportedBuilds { get; set; }
|
|
public Requirements.Requirement[] Requirements { get; set; } = new Requirements.Requirement[] {};
|
|
public string? Git { get; set; }
|
|
public string? DonateLink { get; set; }
|
|
public string? Website { get; set; }
|
|
public string? ProductCode { get; set; }
|
|
public string? PasswordReplace { get; set; }
|
|
#nullable disable
|
|
public bool Overhaul { get; set; } = false;
|
|
|
|
public string Path { get; set; }
|
|
|
|
public List<string> Options { get; set; } = null;
|
|
|
|
public string Validate()
|
|
{
|
|
if (FeaturePages == null)
|
|
return null;
|
|
|
|
foreach (var rawPage in FeaturePages)
|
|
{
|
|
if (rawPage.GetType() == typeof(CheckboxPage))
|
|
{
|
|
var page = (CheckboxPage)rawPage;
|
|
|
|
if (page.Options.Length > 2 && page.TopLine != null && page.BottomLine != null)
|
|
return @$"CheckboxPage with a TopLine and BottomLine must not have more than 2 options.";
|
|
if (page.Options.Length > 3 && (page.TopLine != null || page.BottomLine != null))
|
|
return @$"CheckboxPage with a TopLine or BottomLine must not have more than 3 options.";
|
|
if (page.Options.Length > 4)
|
|
return @$"CheckboxPage must not have more than 4 options.";
|
|
}
|
|
else if (rawPage.GetType() == typeof(RadioPage))
|
|
{
|
|
var page = (RadioPage)rawPage;
|
|
|
|
if (page.Options.Length > 2 && page.TopLine != null && page.BottomLine != null)
|
|
return @$"RadioPage with a TopLine and BottomLine must not have more than 2 options.";
|
|
if (page.Options.Length > 3 && (page.TopLine != null || page.BottomLine != null))
|
|
return @$"RadioPage with a TopLine or BottomLine must not have more than 3 options.";
|
|
if (page.Options.Length > 4)
|
|
return @$"RadioPage must not have more than 4 options.";
|
|
|
|
if (page.DefaultOption != null && !page.Options.Any(x => x.Name == page.DefaultOption))
|
|
return @$"No option matching DefaultOption {page.DefaultOption} in RadioPage.";
|
|
}
|
|
else if (rawPage.GetType() == typeof(RadioImagePage))
|
|
{
|
|
var page = (RadioImagePage)rawPage;
|
|
|
|
if (page.Options.Length > 4)
|
|
return @$"RadioImagePage must not have more than 4 options.";
|
|
if (page.DefaultOption != null && !page.Options.Any(x => x.Name == page.DefaultOption))
|
|
return @$"No option matching DefaultOption {page.DefaultOption} in RadioImagePage.";
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static double GetVersionNumber(string toBeParsed)
|
|
{
|
|
// Examples:
|
|
// 0.4
|
|
// 0.4 Alpha
|
|
// 1.0.5
|
|
// 1.0.5 Beta
|
|
|
|
|
|
// Remove characters after first space (and the space itself)
|
|
if (toBeParsed.IndexOf(' ') >= 0)
|
|
toBeParsed = toBeParsed.Substring(0, toBeParsed.IndexOf(' '));
|
|
|
|
if (toBeParsed.LastIndexOf('.') != toBeParsed.IndexOf('.'))
|
|
{
|
|
// Example: 1.0.5
|
|
toBeParsed = toBeParsed.Remove(toBeParsed.LastIndexOf('.'), 1);
|
|
// Result: 1.05
|
|
}
|
|
|
|
return double.Parse(toBeParsed, CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
public double GetVersionNumber()
|
|
{
|
|
return GetVersionNumber(Version);
|
|
}
|
|
|
|
public async Task<string> LatestPlaybookVersion()
|
|
{
|
|
|
|
if (!IsValidGit())
|
|
{
|
|
throw new ArgumentException("Link provided is not a proper Git link.");
|
|
}
|
|
|
|
string gitPlatform = GetPlaybookGitPlatform();
|
|
|
|
string repo = GetRepository();
|
|
|
|
using var httpClient = new HttpClient();
|
|
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("curl/7.55.1"); //Required for GitHub
|
|
|
|
string url = gitPlatform switch
|
|
{
|
|
"github.com" => $"https://api.github.com/repos/{repo}/releases",
|
|
"gitlab.com" => $"https://gitlab.com/api/v4/projects/{Uri.EscapeDataString(repo)}/releases",
|
|
_ => $"https://{gitPlatform}/api/v1/repos/{repo}/releases"
|
|
};
|
|
|
|
var response = await httpClient.GetAsync(url);
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var array = JArray.Parse(json);
|
|
|
|
return (string) array.FirstOrDefault()?["tag_name"];
|
|
}
|
|
public async Task<List<string>> GetPlaybookVersions()
|
|
{
|
|
|
|
if (!IsValidGit())
|
|
{
|
|
throw new ArgumentException("Link provided is not a proper Git link.");
|
|
}
|
|
|
|
string gitPlatform = GetPlaybookGitPlatform();
|
|
|
|
string repo = GetRepository();
|
|
|
|
using var httpClient = new HttpClient();
|
|
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("curl/7.55.1"); //Required for GitHub
|
|
|
|
string url = gitPlatform switch
|
|
{
|
|
"github.com" => $"https://api.github.com/repos/{repo}/releases",
|
|
"gitlab.com" => $"https://gitlab.com/api/v4/projects/{Uri.EscapeDataString(repo)}/releases",
|
|
_ => $"https://{gitPlatform}/api/v1/repos/{repo}/releases"
|
|
};
|
|
|
|
var response = await httpClient.GetAsync(url);
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var json = await response.Content.ReadAsStringAsync();
|
|
var array = JArray.Parse(json);
|
|
|
|
var result = new List<string>();
|
|
foreach (var releaseToken in array)
|
|
result.Add((string)releaseToken["tag_name"]);
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task DownloadLatestPlaybook(BackgroundWorker worker = null)
|
|
{
|
|
string repo = GetRepository();
|
|
string gitPlatform = GetPlaybookGitPlatform();
|
|
|
|
var httpClient = new WinUtil.HttpProgressClient();
|
|
httpClient.Client.DefaultRequestHeaders.UserAgent.ParseAdd("curl/7.55.1"); //Required for GitHub
|
|
|
|
var downloadUrl = string.Empty;
|
|
var downloadDir = System.IO.Path.Combine(Environment.GetEnvironmentVariable("TEMP"), "AME");
|
|
var downloadPath = System.IO.Path.Combine(downloadDir, "playbook.apbx");
|
|
|
|
string baseUrl;
|
|
string releasesUrl;
|
|
string assetsKey;
|
|
string browserDownloadUrlKey;
|
|
|
|
switch (gitPlatform)
|
|
{
|
|
case "github.com":
|
|
baseUrl = "https://api.github.com";
|
|
releasesUrl = $"{baseUrl}/repos/{repo}/releases";
|
|
assetsKey = "assets";
|
|
browserDownloadUrlKey = "browser_download_url";
|
|
break;
|
|
|
|
case "gitlab.com":
|
|
baseUrl = "https://gitlab.com/api/v4";
|
|
releasesUrl = $"{baseUrl}/projects/{Uri.EscapeDataString(repo)}/releases";
|
|
assetsKey = "assets.links";
|
|
browserDownloadUrlKey = "direct_asset_url";
|
|
break;
|
|
|
|
default:
|
|
baseUrl = $"https://{gitPlatform}/api/v1";
|
|
releasesUrl = $"{baseUrl}/repos/{repo}/releases";
|
|
assetsKey = "assets";
|
|
browserDownloadUrlKey = "browser_download_url";
|
|
break;
|
|
}
|
|
|
|
var releasesResponse = await httpClient.GetAsync(releasesUrl);
|
|
releasesResponse.EnsureSuccessStatusCode();
|
|
|
|
var releasesContent = await releasesResponse.Content.ReadAsStringAsync();
|
|
var releases = JArray.Parse(releasesContent);
|
|
var release = releases.FirstOrDefault();
|
|
|
|
long size = 3000000;
|
|
|
|
if (release?.SelectToken(assetsKey) is JArray assets)
|
|
{
|
|
var asset = assets.FirstOrDefault(a => a["name"].ToString().EndsWith(".apbx"));
|
|
if (asset != null)
|
|
{
|
|
downloadUrl = asset[browserDownloadUrlKey]?.ToString();
|
|
|
|
if (asset["size"] != null)
|
|
long.TryParse(asset["size"].ToString(), out size);
|
|
}
|
|
}
|
|
|
|
if (worker != null)
|
|
worker.ReportProgress(10);
|
|
|
|
// Download the release asset
|
|
if (!string.IsNullOrEmpty(downloadUrl))
|
|
{
|
|
httpClient.Client.DefaultRequestHeaders.Clear();
|
|
|
|
httpClient.ProgressChanged += (totalFileSize, totalBytesDownloaded, progressPercentage) => {
|
|
if (progressPercentage.HasValue && worker != null)
|
|
worker.ReportProgress((int)Math.Ceiling(10 + (progressPercentage.Value * 0.7)));
|
|
};
|
|
|
|
await httpClient.StartDownload(downloadUrl, downloadPath, size);
|
|
}
|
|
httpClient.Dispose();
|
|
}
|
|
|
|
public string GetRepository()
|
|
{
|
|
if (Git == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var urlSegments = Git.Replace("https://", "").Replace("http://", "").Split('/');
|
|
return urlSegments[1] +"/"+ urlSegments[2];
|
|
}
|
|
|
|
public string GetPlaybookGitPlatform()
|
|
{
|
|
if (this.Git == null)
|
|
{
|
|
throw new NullReferenceException("No Git link available.");
|
|
}
|
|
|
|
return new Uri(Git).Host;
|
|
}
|
|
|
|
public bool IsValidGit()
|
|
{
|
|
if (Git == null)
|
|
{
|
|
throw new NullReferenceException("No Git link available.");
|
|
}
|
|
|
|
return Regex.IsMatch(Git, "((git|ssh|http(s)?)|(git@[\\w\\.]+))(:(//)?)([\\w\\.@\\:/\\-~]+)(/)?");;
|
|
}
|
|
public override string ToString()
|
|
{
|
|
return $"Name: {Name}\nDescription: {Description}\nUsername: {Username}\nDetails: {Details}\nRequirements: {Requirements}.";
|
|
}
|
|
[XmlType("CheckboxPage")]
|
|
public class CheckboxPage : FeaturePage
|
|
{
|
|
public class CheckboxOption : Option
|
|
{
|
|
[XmlAttribute]
|
|
public bool IsChecked { get; set; } = true;
|
|
}
|
|
|
|
[XmlArray]
|
|
[XmlArrayItem(ElementName = "CheckboxOption", Type = typeof(CheckboxOption))]
|
|
public Option[] Options { get; set; }
|
|
}
|
|
public class RadioPage : FeaturePage
|
|
{
|
|
[XmlAttribute]
|
|
public string DefaultOption { get; set; } = null;
|
|
public class RadioOption : Option
|
|
{
|
|
}
|
|
|
|
[XmlArray]
|
|
[XmlArrayItem(ElementName = "RadioOption", Type = typeof(RadioOption))]
|
|
public Option[] Options { get; set; }
|
|
}
|
|
public class RadioImagePage : FeaturePage
|
|
{
|
|
[XmlAttribute]
|
|
public string DefaultOption { get; set; } = null;
|
|
public class RadioImageOption : Option
|
|
{
|
|
public string FileName { get; set; } = null;
|
|
|
|
public bool Fill { get; set; } = false;
|
|
[XmlAttribute]
|
|
public bool None { get; set; } = false;
|
|
|
|
public string GradientTopColor { get; set; } = null;
|
|
public string GradientBottomColor { get; set; } = null;
|
|
}
|
|
|
|
[XmlArray]
|
|
[XmlArrayItem(ElementName = "RadioImageOption", Type = typeof(RadioImageOption))]
|
|
public Option[] Options { get; set; }
|
|
|
|
[XmlAttribute]
|
|
public bool CheckDefaultBrowser { get; set; } = false;
|
|
}
|
|
|
|
public class FeaturePage
|
|
{
|
|
[XmlAttribute]
|
|
public string DependsOn { get; set; } = null;
|
|
[XmlAttribute]
|
|
public bool IsRequired { get; set; } = false;
|
|
public Line TopLine { get; set; } = null;
|
|
public Line BottomLine { get; set; } = null;
|
|
|
|
public class Option
|
|
{
|
|
public string Name { get; set; } = null;
|
|
public virtual string Text { get; set; }
|
|
|
|
[XmlAttribute]
|
|
public string DependsOn { get; set; } = null;
|
|
}
|
|
public class Line
|
|
{
|
|
[XmlAttribute("Text")]
|
|
public string Text { get; set; }
|
|
[XmlAttribute("Link")]
|
|
public string Link { get; set; } = null;
|
|
}
|
|
[XmlAttribute]
|
|
public string Description { get; set; }
|
|
}
|
|
}
|
|
}
|