@ -1,10 +1,23 @@
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Drawing ;
using System.Globalization ;
using System.IO ;
using System.Linq ;
using System.Text ;
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
{
@ -18,25 +31,360 @@ namespace TrustedUninstaller.Shared
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 ; } = 2 5 ;
# nullable enable
public string [ ] ? SupportedBuilds { get ; set ; }
public Requirements . Requirement [ ] ? Requirements { 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 = 3 0 0 0 0 0 0 ;
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 ( 1 0 ) ;
// 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 ( 1 0 + ( 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 ; }
}
}
}