2019-09-23 21:43:32 +01:00
using Microsoft.Deployment.WindowsInstaller ;
using System ;
2019-09-30 14:07:37 +01:00
using System.Globalization ;
2019-09-23 21:43:32 +01:00
using System.IO ;
2019-11-14 12:02:03 +00:00
using System.IO.Abstractions ;
2019-10-29 19:31:20 +00:00
using System.Text.RegularExpressions ;
2019-09-23 21:43:32 +01:00
namespace Storj
{
public class CustomActions
{
[CustomAction]
public static ActionResult ValidateIdentityDir ( Session session )
{
string identityDir = session [ "IDENTITYDIR" ] ;
2019-11-08 23:38:06 +00:00
try
2019-09-23 21:43:32 +01:00
{
2019-11-14 12:02:03 +00:00
new CustomActionRunner ( ) . ValidateIdentityDir ( identityDir ) ;
2019-09-23 21:43:32 +01:00
}
2019-11-08 23:38:06 +00:00
catch ( ArgumentException e )
2019-09-23 21:43:32 +01:00
{
2019-11-08 23:38:06 +00:00
// Identity dir is invalid
session [ "STORJ_IDENTITYDIR_VALID" ] = e . Message ;
2019-09-23 21:43:32 +01:00
return ActionResult . Success ;
}
2019-11-08 23:38:06 +00:00
// Identity dir is valid
session [ "STORJ_IDENTITYDIR_VALID" ] = "1" ;
return ActionResult . Success ;
}
[CustomAction]
public static ActionResult ValidateWallet ( Session session )
{
string wallet = session [ "STORJ_WALLET" ] ;
try
{
2019-11-14 12:02:03 +00:00
new CustomActionRunner ( ) . ValidateWallet ( wallet ) ;
2019-11-08 23:38:06 +00:00
} catch ( ArgumentException e )
2019-09-23 21:43:32 +01:00
{
2019-11-08 23:38:06 +00:00
// Wallet is invalid
session [ "STORJ_WALLET_VALID" ] = e . Message ;
2019-09-23 21:43:32 +01:00
return ActionResult . Success ;
}
2019-11-08 23:38:06 +00:00
// Wallet is valid
session [ "STORJ_WALLET_VALID" ] = "1" ;
return ActionResult . Success ;
}
[CustomAction]
public static ActionResult ValidateStorageDir ( Session session )
{
string storageDir = session [ "STORAGEDIR" ] ;
try
{
2019-11-14 12:02:03 +00:00
new CustomActionRunner ( ) . ValidateStorageDir ( storageDir ) ;
2019-11-08 23:38:06 +00:00
}
catch ( ArgumentException e )
2019-09-23 21:43:32 +01:00
{
2019-11-08 23:38:06 +00:00
// Storage dir is invalid
session [ "STORJ_STORAGEDIR_VALID" ] = e . Message ;
2019-09-23 21:43:32 +01:00
return ActionResult . Success ;
}
2019-11-08 23:38:06 +00:00
// Storage dir is valid
session [ "STORJ_STORAGEDIR_VALID" ] = "1" ;
return ActionResult . Success ;
}
[CustomAction]
public static ActionResult ValidateStorage ( Session session )
{
string storageStr = session [ "STORJ_STORAGE" ] ;
string storageDir = session [ "STORAGEDIR" ] ;
try
2019-09-23 21:43:32 +01:00
{
2019-11-14 12:02:03 +00:00
new CustomActionRunner ( ) . ValidateStorage ( storageStr , storageDir ) ;
2019-11-08 23:38:06 +00:00
}
catch ( ArgumentException e )
{
// Allocated Storage is invalid
session [ "STORJ_STORAGE_VALID" ] = e . Message ;
2019-09-23 21:43:32 +01:00
return ActionResult . Success ;
}
2019-11-08 23:38:06 +00:00
// Allocated Storage value is valid
session [ "STORJ_STORAGE_VALID" ] = "1" ;
return ActionResult . Success ;
}
2019-09-23 21:43:32 +01:00
[CustomAction]
2019-11-08 23:38:06 +00:00
public static ActionResult ExtractInstallDir ( Session session )
2019-09-23 21:43:32 +01:00
{
2019-11-08 23:38:06 +00:00
string line = session [ "STORJ_SERVICE_COMMAND" ] ;
session . Log ( $"ExtractInstallDir registry value: {line}" ) ;
2019-11-14 12:02:03 +00:00
string path = new CustomActionRunner ( ) . ExtractInstallDir ( line ) ;
2019-11-08 23:38:06 +00:00
session . Log ( $"ExtractInstallDir extracted path: {path}" ) ;
session [ "STORJ_INSTALLDIR" ] = path ;
return ActionResult . Success ;
}
}
public class CustomActionRunner
{
2019-11-14 12:02:03 +00:00
public const long GB = 1000 * 1000 * 1000 ;
public const long TB = ( long ) 1000 * 1000 * 1000 * 1000 ;
public const long MinFreeSpace = 550 * GB ; // (500 GB + 10% overhead)
2019-11-08 23:38:06 +00:00
2019-11-14 12:02:03 +00:00
private readonly IFileSystem fs ;
public CustomActionRunner ( ) : this ( fs : new FileSystem ( ) )
{
}
public CustomActionRunner ( IFileSystem fs )
{
this . fs = fs ;
}
public void ValidateIdentityDir ( string identityDir )
2019-11-08 23:38:06 +00:00
{
if ( string . IsNullOrEmpty ( identityDir ) )
{
throw new ArgumentException ( "You must select an identity folder." ) ;
}
2019-11-14 12:02:03 +00:00
if ( ! fs . Directory . Exists ( identityDir ) )
2019-11-08 23:38:06 +00:00
{
throw new ArgumentException ( string . Format ( "Folder '{0}' does not exist." , identityDir ) ) ;
}
2019-11-14 12:02:03 +00:00
if ( ! fs . File . Exists ( Path . Combine ( identityDir , "ca.cert" ) ) )
2019-11-08 23:38:06 +00:00
{
throw new ArgumentException ( "File 'ca.cert' not found in the selected folder." ) ;
}
2019-09-23 21:43:32 +01:00
2019-11-14 12:02:03 +00:00
if ( ! fs . File . Exists ( Path . Combine ( identityDir , "ca.key" ) ) )
2019-11-08 23:38:06 +00:00
{
throw new ArgumentException ( "File 'ca.key' not found in the selected folder." ) ;
}
2019-11-14 12:02:03 +00:00
if ( ! fs . File . Exists ( Path . Combine ( identityDir , "identity.cert" ) ) )
2019-11-08 23:38:06 +00:00
{
throw new ArgumentException ( "File 'identity.cert' not found in the selected folder." ) ;
}
2019-11-14 12:02:03 +00:00
if ( ! fs . File . Exists ( Path . Combine ( identityDir , "identity.key" ) ) )
2019-11-08 23:38:06 +00:00
{
throw new ArgumentException ( "File 'identity.key' not found in the selected folder." ) ;
}
}
2019-11-14 12:02:03 +00:00
public void ValidateWallet ( string wallet )
2019-11-08 23:38:06 +00:00
{
2019-09-23 21:43:32 +01:00
if ( string . IsNullOrEmpty ( wallet ) )
{
2019-11-08 23:38:06 +00:00
throw new ArgumentException ( "The payout address cannot be empty." ) ;
2019-09-23 21:43:32 +01:00
}
if ( ! wallet . StartsWith ( "0x" ) )
{
2019-11-08 23:38:06 +00:00
throw new ArgumentException ( "The payout address must start with a '0x' prefix." ) ;
2019-09-23 21:43:32 +01:00
}
// Remove 0x prefix
wallet = wallet . Substring ( 2 ) ;
if ( wallet . Length ! = 40 )
{
2019-11-08 23:38:06 +00:00
throw new ArgumentException ( "The payout address must have 40 characters after the '0x' prefix." ) ;
2019-09-23 21:43:32 +01:00
}
// TODO validate address checksum
}
2019-11-14 12:02:03 +00:00
public void ValidateStorageDir ( string storageDir )
2019-11-08 23:38:06 +00:00
{
if ( string . IsNullOrEmpty ( storageDir ) )
2019-09-23 21:43:32 +01:00
{
2019-11-08 23:38:06 +00:00
throw new ArgumentException ( "You must select a storage folder." ) ;
2019-09-23 21:43:32 +01:00
}
2019-11-14 12:02:03 +00:00
IDirectoryInfo dir = fs . DirectoryInfo . FromDirectoryName ( storageDir ) ;
IDriveInfo drive = fs . DriveInfo . FromDriveName ( dir . Root . FullName ) ;
2019-09-23 21:43:32 +01:00
// TODO: Find a way to calculate the available free space + total size of existing pieces
if ( drive . TotalSize < MinFreeSpace )
{
2019-11-08 23:38:06 +00:00
throw new ArgumentException ( string . Format ( "The selected drive '{0}' has only {1:0.##} GB disk size. The minimum required is 550 GB." ,
drive . Name , decimal . Divide ( drive . TotalSize , GB ) ) ) ;
2019-09-23 21:43:32 +01:00
}
}
2019-11-14 12:02:03 +00:00
public void ValidateStorage ( string storageStr , string storageDir )
2019-09-23 21:43:32 +01:00
{
if ( string . IsNullOrEmpty ( storageStr ) )
{
2019-11-08 23:38:06 +00:00
throw new ArgumentException ( "The value cannot be empty." ) ;
2019-09-23 21:43:32 +01:00
}
2019-09-30 14:07:37 +01:00
if ( ! double . TryParse ( storageStr , NumberStyles . Number , CultureInfo . CreateSpecificCulture ( "en-US" ) , out double storage ) )
2019-09-23 21:43:32 +01:00
{
2019-11-08 23:38:06 +00:00
throw new ArgumentException ( string . Format ( "'{0}' is not a valid number." , storageStr ) ) ;
2019-09-23 21:43:32 +01:00
}
2019-11-08 23:38:06 +00:00
if ( storage < 0.5 )
{
throw new ArgumentException ( "The allocated disk space cannot be less than 0.5 TB." ) ;
}
if ( string . IsNullOrEmpty ( storageDir ) )
{
throw new ArgumentException ( "The storage directory cannot be empty" ) ;
}
long storagePlusOverhead ;
try
{
storagePlusOverhead = Convert . ToInt64 ( storage * 1.1 * TB ) ;
}
catch ( OverflowException )
{
throw new ArgumentException ( string . Format ( "{0} TB is too large value for allocated storage." , storage ) ) ;
2019-09-23 21:43:32 +01:00
}
2019-11-14 12:02:03 +00:00
IDirectoryInfo dir = fs . DirectoryInfo . FromDirectoryName ( storageDir ) ;
IDriveInfo drive = fs . DriveInfo . FromDriveName ( dir . Root . FullName ) ;
2019-09-23 21:43:32 +01:00
// TODO: Find a way to calculate the available free space + total size of existing pieces
if ( drive . TotalSize < storagePlusOverhead )
{
2019-11-08 23:38:06 +00:00
throw new ArgumentException ( string . Format ( "The disk size ({0:0.##} TB) on the selected drive {1} is less than the allocated disk space plus the 10% overhead ({2:0.##} TB total)." ,
decimal . Divide ( drive . TotalSize , TB ) , drive . Name , decimal . Divide ( storagePlusOverhead , TB ) ) ) ;
2019-09-23 21:43:32 +01:00
}
}
2019-11-14 12:02:03 +00:00
public string ExtractInstallDir ( string serviceCmd )
2019-10-29 19:31:20 +00:00
{
2019-11-08 23:38:06 +00:00
if ( string . IsNullOrEmpty ( serviceCmd ) )
{
return null ;
}
2019-10-29 19:31:20 +00:00
Regex pattern = new Regex ( @"--config-dir ""(?<installDir>.*)""" ) ;
2019-11-08 23:38:06 +00:00
Match match = pattern . Match ( serviceCmd ) ;
string installDir = match . Groups [ "installDir" ] . Value ;
2019-10-29 19:31:20 +00:00
2019-11-08 23:38:06 +00:00
if ( string . IsNullOrEmpty ( installDir ) )
{
return null ;
}
return installDir ;
2019-10-29 19:31:20 +00:00
}
2019-09-23 21:43:32 +01:00
}
2019-11-08 23:38:06 +00:00
2019-09-23 21:43:32 +01:00
}