2019-09-29 04:41:47 +00:00
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Linq ;
2019-09-29 22:21:18 +00:00
using System.Net.Http ;
2019-09-29 04:41:47 +00:00
using Wabbajack.Common ;
2019-10-16 03:10:34 +00:00
using Wabbajack.Lib.Downloaders ;
2019-09-29 22:21:18 +00:00
using Path = Alphaleonis . Win32 . Filesystem . Path ;
2019-09-29 04:41:47 +00:00
2019-10-16 03:10:34 +00:00
namespace Wabbajack.Lib.Validation
2019-09-29 04:41:47 +00:00
{
/// <summary>
/// Core class for rights management. Given a Wabbajack modlist this class will return a list of all the
/// know rights violations of the modlist
/// </summary>
public class ValidateModlist
{
2019-10-03 03:08:12 +00:00
public Dictionary < string , Author > AuthorPermissions { get ; set ; } = new Dictionary < string , Author > ( ) ;
2019-11-17 04:16:42 +00:00
private WorkQueue Queue = new WorkQueue ( ) ;
2019-10-03 03:08:12 +00:00
public ServerWhitelist ServerWhitelist { get ; set ; } = new ServerWhitelist ( ) ;
2019-09-29 04:41:47 +00:00
public void LoadAuthorPermissionsFromString ( string s )
{
2019-10-16 21:36:14 +00:00
AuthorPermissions = s . FromYaml < Dictionary < string , Author > > ( ) ;
2019-09-29 04:41:47 +00:00
}
2019-09-29 20:53:25 +00:00
public void LoadServerWhitelist ( string s )
{
2019-10-16 21:36:14 +00:00
ServerWhitelist = s . FromYaml < ServerWhitelist > ( ) ;
2019-09-29 20:53:25 +00:00
}
2019-09-29 22:21:18 +00:00
public void LoadListsFromGithub ( )
{
var client = new HttpClient ( ) ;
Utils . Log ( "Loading Nexus Mod Permissions" ) ;
2019-10-16 21:36:14 +00:00
using ( var result = client . GetStreamSync ( Consts . ModPermissionsURL ) )
2019-09-29 22:21:18 +00:00
{
2019-10-16 21:36:14 +00:00
AuthorPermissions = result . FromYaml < Dictionary < string , Author > > ( ) ;
2019-10-01 22:39:25 +00:00
Utils . Log ( $"Loaded permissions for {AuthorPermissions.Count} authors" ) ;
2019-09-29 22:21:18 +00:00
}
Utils . Log ( "Loading Server Whitelist" ) ;
2019-10-16 21:36:14 +00:00
using ( var result = client . GetStreamSync ( Consts . ServerWhitelistURL ) )
2019-09-29 22:21:18 +00:00
{
2019-10-16 21:36:14 +00:00
ServerWhitelist = result . FromYaml < ServerWhitelist > ( ) ;
2019-10-01 22:39:25 +00:00
Utils . Log ( $"Loaded permissions for {ServerWhitelist.AllowedPrefixes.Count} servers and {ServerWhitelist.GoogleIDs.Count} GDrive files" ) ;
2019-09-29 22:21:18 +00:00
}
}
public static void RunValidation ( ModList modlist )
{
var validator = new ValidateModlist ( ) ;
2019-10-03 02:55:16 +00:00
2019-10-03 03:23:11 +00:00
validator . LoadListsFromGithub ( ) ;
2019-10-03 02:55:16 +00:00
2019-10-01 22:39:25 +00:00
Utils . Log ( "Running validation checks" ) ;
2019-09-29 22:21:18 +00:00
var errors = validator . Validate ( modlist ) ;
errors . Do ( e = > Utils . Log ( e ) ) ;
2019-09-30 23:39:41 +00:00
if ( errors . Count ( ) > 0 )
{
Utils . Log ( $"{errors.Count()} validation errors found, cannot continue." ) ;
2019-10-01 22:39:25 +00:00
throw new Exception ( $"{errors.Count()} validation errors found, cannot continue." ) ;
}
else
{
Utils . Log ( "No Validation failures" ) ;
2019-09-30 23:39:41 +00:00
}
2019-09-29 22:21:18 +00:00
}
2019-09-29 04:41:47 +00:00
/// <summary>
/// Takes all the permissions for a given Nexus mods and merges them down to a single permissions record
/// the more specific record having precedence in each field.
/// </summary>
/// <param name="mod"></param>
/// <returns></returns>
2019-10-12 22:15:20 +00:00
public Permissions FilePermissions ( NexusDownloader . State mod )
2019-09-29 04:41:47 +00:00
{
2019-11-22 05:19:42 +00:00
if ( mod . Author = = null | | mod . GameName = = null | | mod . ModID = = null | | mod . FileID = = null )
{
Utils . Error ( $"Error: Null data for {mod.Author} {mod.GameName} {mod.ModID} {mod.FileID}" ) ;
}
2019-09-29 04:41:47 +00:00
var author_permissions = AuthorPermissions . GetOrDefault ( mod . Author ) ? . Permissions ;
var game_permissions = AuthorPermissions . GetOrDefault ( mod . Author ) ? . Games . GetOrDefault ( mod . GameName ) ? . Permissions ;
var mod_permissions = AuthorPermissions . GetOrDefault ( mod . Author ) ? . Games . GetOrDefault ( mod . GameName ) ? . Mods . GetOrDefault ( mod . ModID )
? . Permissions ;
var file_permissions = AuthorPermissions . GetOrDefault ( mod . Author ) ? . Games . GetOrDefault ( mod . GameName ) ? . Mods
. GetOrDefault ( mod . ModID ) ? . Files . GetOrDefault ( mod . FileID ) ? . Permissions ;
return new Permissions
{
CanExtractBSAs = file_permissions ? . CanExtractBSAs ? ? mod_permissions ? . CanExtractBSAs ? ?
game_permissions ? . CanExtractBSAs ? ? author_permissions ? . CanExtractBSAs ? ? true ,
CanModifyAssets = file_permissions ? . CanModifyAssets ? ? mod_permissions ? . CanModifyAssets ? ?
game_permissions ? . CanModifyAssets ? ? author_permissions ? . CanModifyAssets ? ? true ,
CanModifyESPs = file_permissions ? . CanModifyESPs ? ? mod_permissions ? . CanModifyESPs ? ?
game_permissions ? . CanModifyESPs ? ? author_permissions ? . CanModifyESPs ? ? true ,
CanUseInOtherGames = file_permissions ? . CanUseInOtherGames ? ? mod_permissions ? . CanUseInOtherGames ? ?
game_permissions ? . CanUseInOtherGames ? ? author_permissions ? . CanUseInOtherGames ? ? true ,
} ;
}
public IEnumerable < string > Validate ( ModList modlist )
{
ConcurrentStack < string > ValidationErrors = new ConcurrentStack < string > ( ) ;
2019-10-12 22:15:20 +00:00
2019-09-29 04:41:47 +00:00
var nexus_mod_permissions = modlist . Archives
2019-10-12 22:15:20 +00:00
. Where ( a = > a . State is NexusDownloader . State )
2019-11-17 04:16:42 +00:00
. PMap ( Queue , a = > ( a . Hash , FilePermissions ( ( NexusDownloader . State ) a . State ) , a ) )
2019-10-07 17:33:34 +00:00
. ToDictionary ( a = > a . Hash , a = > new { permissions = a . Item2 , archive = a . a } ) ;
2019-09-29 04:41:47 +00:00
modlist . Directives
. OfType < PatchedFromArchive > ( )
2019-11-17 04:16:42 +00:00
. PMap ( Queue , p = >
2019-09-29 04:41:47 +00:00
{
if ( nexus_mod_permissions . TryGetValue ( p . ArchiveHashPath [ 0 ] , out var archive ) )
{
var ext = Path . GetExtension ( p . ArchiveHashPath . Last ( ) ) ;
2019-10-12 22:15:20 +00:00
var url = ( archive . archive . State as NexusDownloader . State ) . NexusURL ;
2019-09-29 04:41:47 +00:00
if ( Consts . AssetFileExtensions . Contains ( ext ) & & ! ( archive . permissions . CanModifyAssets ? ? true ) )
{
2019-10-12 22:15:20 +00:00
ValidationErrors . Push ( $"{p.To} from {url} is set to disallow asset modification" ) ;
2019-09-29 04:41:47 +00:00
}
else if ( Consts . ESPFileExtensions . Contains ( ext ) & & ! ( archive . permissions . CanModifyESPs ? ? true ) )
{
2019-10-12 22:15:20 +00:00
ValidationErrors . Push ( $"{p.To} from {url} is set to disallow asset ESP modification" ) ;
2019-09-29 04:41:47 +00:00
}
}
} ) ;
modlist . Directives
. OfType < FromArchive > ( )
2019-11-17 04:16:42 +00:00
. PMap ( Queue , p = >
2019-09-29 04:41:47 +00:00
{
if ( nexus_mod_permissions . TryGetValue ( p . ArchiveHashPath [ 0 ] , out var archive ) )
{
2019-10-12 22:15:20 +00:00
var url = ( archive . archive . State as NexusDownloader . State ) . NexusURL ;
2019-10-07 17:33:34 +00:00
if ( ! ( archive . permissions . CanExtractBSAs ? ? true ) & &
2019-10-11 23:31:36 +00:00
p . ArchiveHashPath . Skip ( 1 ) . ButLast ( ) . Any ( a = > Consts . SupportedBSAs . Contains ( Path . GetExtension ( a ) . ToLower ( ) ) ) )
2019-09-29 04:41:47 +00:00
{
2019-10-12 22:15:20 +00:00
ValidationErrors . Push ( $"{p.To} from {url} is set to disallow BSA Extraction" ) ;
2019-09-29 04:41:47 +00:00
}
}
} ) ;
var nexus = NexusApi . NexusApiUtils . ConvertGameName ( GameRegistry . Games [ modlist . GameType ] . NexusName ) ;
modlist . Archives
2019-10-12 22:15:20 +00:00
. Where ( a = > a . State is NexusDownloader . State )
. Where ( m = > NexusApi . NexusApiUtils . ConvertGameName ( ( ( NexusDownloader . State ) m . State ) . GameName ) ! = nexus )
2019-09-29 22:21:18 +00:00
. Do ( m = >
{
2019-10-12 22:15:20 +00:00
var permissions = FilePermissions ( ( NexusDownloader . State ) m . State ) ;
2019-09-29 22:21:18 +00:00
if ( ! ( permissions . CanUseInOtherGames ? ? true ) )
{
ValidationErrors . Push (
2019-10-12 22:15:20 +00:00
$"The modlist is for {nexus} but {m.Name} is for game type {((NexusDownloader.State)m.State).GameName} and is not allowed to be converted to other game types" ) ;
2019-09-29 22:21:18 +00:00
}
} ) ;
2019-09-29 04:41:47 +00:00
2019-09-29 20:53:25 +00:00
modlist . Archives
2019-10-12 22:15:20 +00:00
. Where ( m = > ! m . State . IsWhitelisted ( ServerWhitelist ) )
. Do ( m = >
2019-09-29 20:53:25 +00:00
{
2019-10-12 22:15:20 +00:00
ValidationErrors . Push ( $"{m.Name} is not a whitelisted download" ) ;
2019-09-29 20:53:25 +00:00
} ) ;
2019-10-12 22:15:20 +00:00
2019-09-29 04:41:47 +00:00
return ValidationErrors . ToList ( ) ;
}
}
}