/******************************************************************************/
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-using System.Text;
using System.Threading.Tasks;
+using com.muldersoft.slunkcrypt.gui.utils;
+
namespace com.muldersoft.slunkcrypt.gui.process
{
public static class PasswordGen
{
- private static readonly TimeSpan TIMEOUT_MSEC = TimeSpan.FromSeconds(90);
+ private static readonly TimeSpan TIMEOUT = TimeSpan.FromSeconds(180);
- private const string COMMAND_PASSWRD = "-p {0:D}";
+ private const string COMMAND_PASSWRD = "-p";
// =============================================================================
// Exception classes
public class GenerationFailedException : IOException
{
- public GenerationFailedException(string message, Exception innerException) : base(message, innerException)
+ public GenerationFailedException(string message) : base(message)
{
}
}
public static async Task<string> GeneratePassword(int length)
{
+ if (length <= 0)
+ {
+ throw new ArgumentOutOfRangeException("Password length must be a positive value!");
+ }
using (FileStream executableFile = ExecutableHelper.GetExecutableFile())
{
- try
+ string[] arguments = new string[] { COMMAND_PASSWRD, string.Format("{0:D}", length) };
+ Tuple<int, string[]> result = await ProcessRunner.ExecAsnyc(executableFile.Name, arguments, null, ProcessPriorityClass.BelowNormal, TIMEOUT);
+ if (result.Item1 == 0)
{
- string password = await StartProcess(executableFile, length);
- if (IsWeakPassword(password))
+ foreach (string password in result.Item2)
{
- throw new InvalidDataException("The generated password string is invalid!");
+ if ((password.Length >= length) && (!IsWeakPassword(password)))
+ {
+ return password;
+ }
}
- return password;
- }
- catch (Exception e)
- {
- throw new GenerationFailedException("Failed to generate password string!", e);
}
+ throw new GenerationFailedException("Failed to generate password string!");
}
}
}
return (flags != 0xF);
}
-
- // =============================================================================
- // Internal methods
- // =============================================================================
-
- private static async Task<string> StartProcess(FileStream executableFile, int length)
- {
- using (Process process = new Process())
- {
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.CreateNoWindow = true;
- process.StartInfo.RedirectStandardOutput = true;
- process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
- process.StartInfo.FileName = executableFile.Name;
- process.StartInfo.Arguments = string.Format(COMMAND_PASSWRD, length);
- process.Start();
-
- Stack<string> outputLines = await WaitForExit(process, TIMEOUT_MSEC);
-
- if (process.ExitCode == 0)
- {
- while (outputLines.Count > 0)
- {
- string line = outputLines.Pop();
- if (line.Length >= length)
- {
- return line;
- }
- }
- }
- return string.Empty;
- }
- }
-
- private static async Task<Stack<string>> WaitForExit(Process process, TimeSpan timeout)
- {
- Task<Stack<string>> readTask;
- await Task.WhenAny(readTask = Task.Run(() => ReadOutput(process)), Task.Delay(timeout));
- if (!process.WaitForExit(125))
- {
- process.Kill();
- process.WaitForExit();
- }
- return await readTask;
- }
-
- private static Stack<string> ReadOutput(Process process)
- {
- Stack<string> result = new Stack<string>();
- using (StreamReader reader = process.StandardOutput)
- {
- string line;
- while ((line = reader.ReadLine()) != null)
- {
- line = line.Trim();
- if (line.Length != 0)
- {
- result.Push(line.Trim());
- }
- }
- }
- return result;
- }
}
}
/******************************************************************************/
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
private readonly Dispatcher m_dispatcher;
private readonly ProcessPriorityClass? m_priorityClass;
- private volatile bool m_running = false, m_aborted = false, m_disposed = false;
+ private volatile bool m_running = false, m_finished = false, m_aborted = false, m_disposed = false;
// =============================================================================
// Exception classes
{
throw new ArgumentException("The given ProcessPriorityClass is undefined!");
}
- m_process.StartInfo.UseShellExecute = false;
- m_process.StartInfo.CreateNoWindow = true;
- m_process.StartInfo.RedirectStandardOutput = true;
- m_process.StartInfo.RedirectStandardError = true;
- m_process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
- m_process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
- m_process.EnableRaisingEvents = true;
- m_process.Exited += ProcessExitedEventHandler;
+ InitializeProcess(m_process, m_hasExited, true);
}
~ProcessRunner()
{
throw new ObjectDisposedException("ProcessRunner");
}
- if (m_running)
+ if (m_running || m_finished)
{
- throw new InvalidOperationException("Process is already running!");
+ throw new InvalidOperationException("Process is still running or has already finished!");
}
m_running = true;
try
}
finally
{
+ m_finished = true;
m_running = false;
}
}
+ public static async Task<Tuple<int, string[]>> ExecAsnyc(string executableFile, string[] arguments = null, IReadOnlyDictionary<string, string> environmentVariables = null, ProcessPriorityClass? priorityClass = null, TimeSpan? timeout = null)
+ {
+ TaskCompletionSource<int> completionSource = new TaskCompletionSource<int>();
+ using (Process process = new Process())
+ {
+ InitializeProcess(process, completionSource);
+ return await DoExecAsnyc(process, completionSource.Task, executableFile, arguments, environmentVariables, priorityClass, timeout);
+ }
+ }
+
public void AbortProcess()
{
if ((!m_disposed) && m_running)
// Protected methods
// =============================================================================
- protected virtual double ParseProgressString(StringBuilder currentLine)
+ protected virtual double ParseProgressString(ref string currentLine, bool stream)
{
return double.NaN;
}
// =============================================================================
- // Event handlers
+ // Internal methods
// =============================================================================
- private void ProcessExitedEventHandler(object sender, EventArgs e)
+ private static void InitializeProcess(Process process, TaskCompletionSource<int> completionSource = null, bool redirStdErr = false)
{
- if (m_process.HasExited)
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
+ if (redirStdErr)
{
- m_hasExited.TrySetResult(m_process.ExitCode);
+ process.StartInfo.RedirectStandardError = true;
+ process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
+ }
+ if (!ReferenceEquals(completionSource, null))
+ {
+ process.EnableRaisingEvents = true;
+ process.Exited += (sndr, e) => completionSource.TrySetResult(process.ExitCode);
}
}
- // =============================================================================
- // Internal methods
- // =============================================================================
-
private async Task<int> DoExecAsnyc(string executablePath, string[] arguments, IReadOnlyDictionary<string, string> environmentVariables)
{
try
{
- StartProcess(executablePath, arguments, environmentVariables);
+ StartProcess(m_process, executablePath, arguments, environmentVariables, m_priorityClass);
}
catch (Exception err)
{
return await WaitForExit();
}
- private void StartProcess(string executablePath, string[] arguments, IReadOnlyDictionary<string, string> environmentVariables)
+ private static async Task<Tuple<int,string[]>> DoExecAsnyc(Process process, Task<int> hasExited, string executablePath, string[] arguments, IReadOnlyDictionary<string, string> environmentVariables, ProcessPriorityClass? priorityClass, TimeSpan? timeout)
+ {
+ try
+ {
+ StartProcess(process, executablePath, arguments, environmentVariables, priorityClass);
+ }
+ catch (Exception err)
+ {
+ throw new ProcessStartException("Failed to create the process!", err);
+ }
+ string[] outputLines = await WaitForExit(process, hasExited, timeout.GetValueOrDefault(TimeSpan.MaxValue));
+ return Tuple.Create(hasExited.Result, outputLines);
+ }
+
+ private static void StartProcess(Process process, string executablePath, string[] arguments, IReadOnlyDictionary<string, string> environmentVariables, ProcessPriorityClass? priorityClass)
{
- m_process.StartInfo.FileName = executablePath;
- m_process.StartInfo.Arguments = CreateArgumentList(arguments);
- SetupEnvironment(m_process.StartInfo.EnvironmentVariables, environmentVariables);
- m_process.StartInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
- m_process.Start();
- SetProcessPriority(m_priorityClass);
+ process.StartInfo.FileName = executablePath;
+ process.StartInfo.Arguments = CreateArgumentList(arguments);
+ SetupEnvironment(process.StartInfo.EnvironmentVariables, environmentVariables);
+ process.StartInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
+ process.Start();
+ SetProcessPriority(process, priorityClass);
}
private async Task<int> WaitForExit()
{
- Task readStdOutTask = Task.Run(() => ReadProcessOutput(m_process.StandardOutput, STDOUT));
- Task readStdErrTask = Task.Run(() => ReadProcessOutput(m_process.StandardError, STDERR));
+ Task readStdOutTask = Task.Run(() => ReadProcessOutput(m_process.StandardOutput, (line) => HandleOutput(line, STDOUT)));
+ Task readStdErrTask = Task.Run(() => ReadProcessOutput(m_process.StandardError, (line) => HandleOutput(line, STDERR)));
Task<int> hasExited = m_hasExited.Task;
await Task.WhenAll(readStdOutTask, readStdErrTask, hasExited);
if (m_aborted || m_disposed)
return hasExited.Result;
}
- private void ReadProcessOutput(StreamReader reader, bool stream)
+ private static async Task<string[]> WaitForExit(Process process, Task<int> hasExited, TimeSpan timeout)
{
- using(reader)
+ ConcurrentStack<string> outputLines = new ConcurrentStack<string>();
+ Task readerTask = Task.Run(() => ReadProcessOutput(process.StandardOutput, (line) => outputLines.Push(line)));
+ if (await Task.WhenAny(hasExited, Task.Delay(timeout)) != hasExited)
+ {
+ KillProcess(process);
+ }
+ await Task.WhenAll(readerTask, hasExited);
+ return outputLines.ToArray();
+ }
+
+ private static void ReadProcessOutput(StreamReader reader, Action<string> outputHandler)
+ {
+ using (reader)
{
char[] buffer = new char[1024];
- StringBuilder currentLine = new StringBuilder();
+ StringBuilder lineBuffer = new StringBuilder();
while (!reader.EndOfStream)
{
- ReadNextChunk(reader, buffer, currentLine, stream);
+ ReadNextChunk(reader, buffer, lineBuffer, outputHandler);
}
- NotifyOutputAvailable(currentLine, stream);
+ ProcessLine(lineBuffer, outputHandler);
}
}
- private void ReadNextChunk(StreamReader reader, char[] buffer, StringBuilder currentLine, bool stderr)
+ private static void ReadNextChunk(StreamReader reader, char[] buffer, StringBuilder lineBuffer, Action<string> outputHandler)
{
int count = reader.Read(buffer, 0, buffer.Length);
if (count > 0)
char c = buffer[i];
if ((c != '\0') && (c != '\n') && (c != '\r') && (c != '\b'))
{
- currentLine.Append(c);
+ lineBuffer.Append(c);
}
else
{
- CheckForProgressUpdate(currentLine);
- NotifyOutputAvailable(currentLine, stderr);
- currentLine.Clear();
+ ProcessLine(lineBuffer, outputHandler);
+ lineBuffer.Clear();
}
}
- CheckForProgressUpdate(currentLine);
}
}
- private void CheckForProgressUpdate(StringBuilder currentLine)
+ private static void ProcessLine(StringBuilder lineBuffer, Action<string> outputHandler)
{
- if (currentLine.Length > 0)
+ if (lineBuffer.Length > 0)
{
- double progress = ParseProgressString(currentLine);
- if (!(double.IsNaN(progress) || double.IsInfinity(progress)))
+ String currentLine;
+ if (!string.IsNullOrWhiteSpace(currentLine = lineBuffer.ToString()))
{
- NotifyProgressChanged(progress);
+ outputHandler.Invoke(currentLine.Trim());
}
}
}
- private void NotifyOutputAvailable(StringBuilder currentLine, bool stream)
+ private void HandleOutput(string currentLine, bool stream)
{
- if (currentLine.Length > 0)
+ CheckForProgressUpdate(ref currentLine, stream);
+ if (!String.IsNullOrEmpty(currentLine))
{
- String line = currentLine.ToString().Trim();
- if (!string.IsNullOrEmpty(line))
- {
- NotifyOutputAvailable(line, stream);
- }
+ NotifyOutputAvailable(currentLine, stream);
+ }
+ }
+
+ private void CheckForProgressUpdate(ref string currentLine, bool stream)
+ {
+ double progress = ParseProgressString(ref currentLine, stream);
+ if (!double.IsNaN(progress))
+ {
+ NotifyProgressChanged(progress);
}
}
catch { }
}
- private void SetProcessPriority(ProcessPriorityClass? priorityClass)
+ private static void KillProcess(Process process)
+ {
+ try
+ {
+ process.Kill();
+ }
+ catch { }
+ }
+
+ private static void SetProcessPriority(Process process, ProcessPriorityClass? priorityClass)
{
try
{
if (priorityClass.HasValue)
{
- m_process.PriorityClass = priorityClass.Value;
+ process.PriorityClass = priorityClass.Value;
}
}
catch { }