/******************************************************************************/ /* SlunkCrypt, by LoRd_MuldeR */ /* This work has been released under the CC0 1.0 Universal license! */ /******************************************************************************/ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Threading; using com.muldersoft.slunkcrypt.gui.utils; namespace com.muldersoft.slunkcrypt.gui.process { internal class SlunkCryptRunner : ProcessRunner { public enum Mode { Encrypt, Decrypt } public enum Error { Checksum, Password } public struct SlunkOptions { public SlunkOptions(bool keepIncompleteFiles, int threadCount, bool enableLegacyCompat) { this.keepIncompleteFiles = keepIncompleteFiles; this.threadCount = threadCount; this.enableLegacyCompat = enableLegacyCompat; } public readonly bool keepIncompleteFiles, enableLegacyCompat; public readonly int threadCount; } private const string COMMAND_ENCRYPT = "-e"; private const string COMMAND_DECRYPT = "-d"; #if DEBUG private const bool ENABLE_DEBUG_LOGGING = true; #else private const bool ENABLE_DEBUG_LOGGING = false; #endif private static readonly Regex RX_PROGRESS = new Regex(@"(\d+)\.(\d)%", RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly IReadOnlyDictionary RX_ERROR = new Dictionary { { Error.Checksum, new Regex(@"\bChecksum\s+mismatch\s+detected\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) }, { Error.Password, new Regex(@"\bThe\s+given\s+passphrase\s+is\s+forbidden\s+as\s+a\s+precautionary\s+measure\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) } } .ToReadOnlyDictionary(); private readonly FileStream m_executableFile; // ============================================================================= // Constructor // ============================================================================= public SlunkCryptRunner(Dispatcher dispatcher, ProcessPriorityClass? priorityClass = null) : base(dispatcher, priorityClass) { m_executableFile = ExecutableHelper.GetExecutableFile(); } // ============================================================================= // Public methods // ============================================================================= public async Task ExecuteAsync(Mode mode, string inputFile, string outputFile, string password, SlunkOptions? options = null) { if ((!EnumHelper.IsDefined(mode)) || string.IsNullOrWhiteSpace(inputFile) || string.IsNullOrWhiteSpace(outputFile) || string.IsNullOrWhiteSpace(password)) { throw new ArgumentException("Invalid SlunkCrypt parameters!"); } Dictionary environmentVariables = new Dictionary { { "SLUNK_PASSPHRASE", password }, { "SLUNK_KEEP_INCOMPLETE", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.keepIncompleteFiles : false)) }, { "SLUNK_THREADS", Convert.ToString(Math.Max(0, Math.Min(32, options.HasValue ? options.Value.threadCount : 0))) }, { "SLUNK_LEGACY_COMPAT", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.enableLegacyCompat : false)) }, { "SLUNK_DEBUG_LOGGING", Convert.ToString(Convert.ToInt32(ENABLE_DEBUG_LOGGING)) } }; ErrorState = null; return await ExecAsnyc(m_executableFile.Name, new string[] { GetCommandString(mode), inputFile, outputFile }, Path.GetDirectoryName(outputFile), environmentVariables); } public override void Dispose() { base.Dispose(); try { m_executableFile.Dispose(); } catch { } } public Error? ErrorState { get; private set; } = null; // ============================================================================= // Internal methods // ============================================================================= protected override double ParseProgressString(ref string currentLine, bool stream) { int index = int.MaxValue; double temp = double.NaN, result = double.NaN; if (!ErrorState.HasValue) { foreach (KeyValuePair errorRegex in RX_ERROR) { if (errorRegex.Value.IsMatch(currentLine)) { ErrorState = errorRegex.Key; break; } } } for (Match match = RX_PROGRESS.Match(currentLine); match.Success; match = match.NextMatch()) { if (TryParseProgressValue(match, out temp)) { result = double.IsNaN(result) ? temp : Math.Max(result, temp); } index = Math.Min(index, match.Index); } if (index != int.MaxValue) { currentLine = (index > 0) ? currentLine.Substring(0, index - 1).TrimEnd() : string.Empty; } return result; } private static bool TryParseProgressValue(Match match, out double progress) { uint intPart, fractPart; if (uint.TryParse(match.Groups[1].Value, out intPart) && uint.TryParse(match.Groups[2].Value, out fractPart)) { progress = ((intPart * 10) + fractPart) / 1000.0; return true; } progress = double.NaN; return false; } private static string GetCommandString(Mode mode) { switch(mode) { case Mode.Encrypt: return COMMAND_ENCRYPT; case Mode.Decrypt: return COMMAND_DECRYPT; default: throw new ArgumentException("Invalid mode!"); } } } }