GUI: Replaced the built-in password generator with call to SlunkCrypt CLI program...
[slunkcrypt.git] / gui / SlunkCryptGUI.xaml.cs
blob1c0a5a3f780dde66d20d35c728e8b73adfa2ae17
1 /******************************************************************************/
2 /* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
3 /* This work has been released under the CC0 1.0 Universal license! */
4 /******************************************************************************/
6 using System;
7 using System.Collections.ObjectModel;
8 using System.ComponentModel;
9 using System.Diagnostics;
10 using System.Globalization;
11 using System.IO;
12 using System.Media;
13 using System.Text;
14 using System.Threading.Tasks;
15 using System.Windows;
16 using System.Windows.Controls;
17 using System.Windows.Input;
18 using System.Windows.Media;
19 using System.Windows.Media.Effects;
20 using System.Windows.Shell;
21 using System.Windows.Threading;
22 using Microsoft.Win32;
24 using com.muldersoft.slunkcrypt.gui.ctrls;
25 using com.muldersoft.slunkcrypt.gui.process;
26 using com.muldersoft.slunkcrypt.gui.utils;
28 namespace com.muldersoft.slunkcrypt.gui
30 public enum ModeOfOperation { Encrypt, Decrypt }
32 public partial class SlunkCryptGUI : Window, INotifyBusyChanged
34 private enum Status { Default, Success, Failure }
35 private delegate Task<bool> SlunkProcessor(string inputFile, string outputFile, string password);
37 public event PropertyChangedEventHandler PropertyChanged;
38 public const int MIN_PASSWD_LENGTH = 8, REC_PASSWD_LENGTH = 12, GEN_PASSWD_LENGTH = 24, MAX_PASSWD_LENGTH = 256, MAX_PATH = 259;
40 private readonly ApplicationConfig m_config = new ApplicationConfig();
41 private readonly Lazy<string> m_about = new Lazy<string>(CreateAboutText);
42 private readonly Random m_random = new Random();
43 private readonly ObservableCollection<string> m_logFile = new ObservableCollection<string>();
44 private readonly string m_defaultStatusText;
45 private readonly DispatcherTimer m_dispatcherTimer;
46 private readonly ReadOnlyObservableCollection<string> m_logFileReadOnly;
48 private volatile ModeOfOperation m_modeOfOperation = (ModeOfOperation)(-1);
49 private volatile bool m_busyFlag = false, m_checksumError = false, m_processReceived = false, m_disableAnimation = false;
50 private volatile SlunkCryptRunner m_processRunner = null;
51 private uint? m_menuId_disableAnimation = null, m_menuId_enableExpertMode = null;
53 public const string ASCII_CHARS = "!#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~";
55 // =============================================================================
56 // Constructor
57 // =============================================================================
59 public SlunkCryptGUI()
61 InitializeComponent();
62 m_defaultStatusText = Label_Status.Text;
63 m_dispatcherTimer = new DispatcherTimer(DispatcherPriority.ApplicationIdle, Dispatcher);
64 m_dispatcherTimer.Tick += DispatcherTimer_Tick;
65 m_dispatcherTimer.Interval = TimeSpan.FromMilliseconds(200);
66 m_logFileReadOnly = new ReadOnlyObservableCollection<string>(m_logFile);
67 m_disableAnimation = m_config.DisableBusyIndicator;
70 // =============================================================================
71 // Properties
72 // =============================================================================
74 public bool IsBusy
76 get
78 return m_busyFlag;
80 set
82 if (m_busyFlag != value)
84 m_dispatcherTimer.IsEnabled = (m_busyFlag = value) && (!m_disableAnimation);
85 NotifyPropertyChanged("IsBusy");
86 NotifyPropertyChanged("IsBusyIndicatorVisible");
91 public bool IsBusyIndicatorVisible
93 get
95 return m_busyFlag && (!m_disableAnimation);
99 public ReadOnlyObservableCollection<string> LogFile
103 return m_logFileReadOnly;
107 // =============================================================================
108 // Event handlers
109 // =============================================================================
111 protected override void OnContentRendered(EventArgs e)
113 base.OnContentRendered(e);
114 TabControl.MinHeight = TabControl.MaxHeight = TabControl.ActualHeight;
115 MinWidth = MaxWidth = ActualWidth;
116 MinHeight = MaxHeight = ActualHeight;
119 private void Button_Encrypt_InputFile_Click(object sender, RoutedEventArgs e)
121 string fileName;
122 if (!string.IsNullOrEmpty(fileName = BrowseForFile(Edit_Encrypt_InputFile.Text, false)))
124 Edit_Encrypt_InputFile.Text = fileName;
125 Edit_Encrypt_OutputFile.Text = GenerateEncryptOutputFileName(fileName);
129 private void Button_Encrypt_OutputFile_Click(object sender, RoutedEventArgs e)
131 string fileName;
132 if (!string.IsNullOrEmpty(fileName = BrowseForFile(Edit_Encrypt_OutputFile.Text, true, "Encrypted file (*.enc)|*.enc")))
134 Edit_Encrypt_OutputFile.Text = fileName;
138 private void Button_Decrypt_InputFile_Click(object sender, RoutedEventArgs e)
140 string fileName;
141 if (!string.IsNullOrEmpty(fileName = BrowseForFile(Edit_Decrypt_InputFile.Text, false)))
143 Edit_Decrypt_InputFile.Text = fileName;
144 Edit_Decrypt_OutputFile.Text = GenerateDecryptOutputFileName(fileName);
148 private void Button_Decrypt_OutputFile_Click(object sender, RoutedEventArgs e)
150 string fileName;
151 if (!string.IsNullOrEmpty(fileName = BrowseForFile(Edit_Decrypt_OutputFile.Text, true, "Decrypted file (*.out)|*.out")))
153 Edit_Decrypt_OutputFile.Text = fileName;
157 private async void Button_GeneratePasswd_Click(object sender, RoutedEventArgs e)
159 using (BusyManager busy = new BusyManager(this))
161 Edit_Encrypt_Password.Password = string.Empty;
162 string password;
163 if (!string.IsNullOrEmpty(password = await GeneratePassword()))
165 Button_Encrypt_Toggle.IsChecked = true;
166 Edit_Encrypt_Password.Password = password;
171 private async void Button_Start_Click(object sender, RoutedEventArgs e)
173 if (!IsBusy)
175 ResetKeyboardFocus(Button_Start);
176 switch (GetModeOfOperation(TabControl.SelectedItem))
178 case ModeOfOperation.Encrypt:
179 Debug.Assert(m_modeOfOperation == ModeOfOperation.Encrypt);
180 await ValidateInputFile(Edit_Encrypt_InputFile, Edit_Encrypt_OutputFile, Edit_Encrypt_Password, Encrypt, true);
181 break;
182 case ModeOfOperation.Decrypt:
183 Debug.Assert(m_modeOfOperation == ModeOfOperation.Decrypt);
184 await ValidateInputFile(Edit_Decrypt_InputFile, Edit_Decrypt_OutputFile, Edit_Decrypt_Password, Decrypt, false);
185 break;
186 default:
187 TabControl.SelectedIndex = GetTabIndexOf(m_modeOfOperation);
188 break;
193 private void Button_About_Click(object sender, RoutedEventArgs e)
195 if (!IsBusy)
197 MessageBox.Show(this, m_about.Value, "About...", MessageBoxButton.OK, MessageBoxImage.Information);
201 private void Button_Exit_Click(object sender, RoutedEventArgs e)
203 if (!IsBusy)
205 Application.Current.Shutdown();
209 private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
211 foreach (object currrentItem in e.AddedItems)
213 ModeOfOperation? modeOfOperation = GetModeOfOperation(currrentItem);
214 if (modeOfOperation.HasValue)
216 if (m_modeOfOperation != modeOfOperation.Value)
218 SetStatus(m_defaultStatusText);
219 ClearProgress();
220 m_modeOfOperation = modeOfOperation.Value;
223 break;
227 private void Button_AbortProcess_Click(object sender, RoutedEventArgs e)
229 AbortProcess();
232 private void Link_CopyToClipboard_Click(object sender, MouseButtonEventArgs e)
234 if (m_logFile.Count > 0)
236 StringBuilder builder = new StringBuilder();
237 foreach (string logItem in m_logFile)
239 builder.AppendLine(logItem);
241 Clipboard.SetText(builder.ToString());
242 SystemSounds.Beep.Play();
246 private void Link_ClearLog_Click(object sender, MouseButtonEventArgs e)
248 if (m_logFile.Count > 0)
250 ClearLogFile();
251 SystemSounds.Beep.Play();
255 private void Process_OutputAvailable(string line, bool stderr)
257 AppendLogFile(line);
258 if (line.IndexOf("Checksum mismatch detected!", StringComparison.OrdinalIgnoreCase) >= 0)
260 m_checksumError = true;
264 private void Porcess_ProgressChanged(double progress)
266 if (!m_processReceived)
268 switch (m_modeOfOperation)
270 case ModeOfOperation.Encrypt:
271 SetStatus("Encrypting file contents. Please be patient, this can take a few moments...");
272 goto default;
273 case ModeOfOperation.Decrypt:
274 SetStatus("Decrypting file contents. Please be patient, this can take a few moments...");
275 goto default;
276 default:
277 m_processReceived = true;
278 break;
281 SetProgress(progress);
284 private void Edit_FileName_KeyDown(object sender, KeyEventArgs e)
286 if (e.Key == Key.Return)
288 FrameworkElement source = sender as FrameworkElement;
289 if (!ReferenceEquals(source, null))
291 FrameworkElement target = source.Tag as FrameworkElement;
292 if (!ReferenceEquals(target, null))
294 SetFocusAndSelectAll(target);
297 e.Handled = true;
301 private void Edit_Password_Entered(object sender, KeyEventArgs e)
303 if (!IsBusy)
305 Button_Start_Click(sender, e);
309 private void Edit_FileName_LostFocus(object sender, RoutedEventArgs e)
311 TextBox textBox;
312 if (!ReferenceEquals(textBox = sender as TextBox, null))
314 textBox.Text = PathUtils.CleanUpFilePathString(textBox.Text);
318 private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
320 DragMove();
323 private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
325 if (e.Key == Key.Escape)
327 AbortProcess();
331 protected void Window_PreviewDragEnter(object sender, DragEventArgs e)
333 e.Effects = ((!IsBusy) && e.Data.GetDataPresent(DataFormats.FileDrop)) ? DragDropEffects.Copy : DragDropEffects.None;
334 e.Handled = true;
337 private void Window_PreviewDragLeave(object sender, DragEventArgs e)
339 e.Handled = true;
342 private void Window_PreviewDrop(object sender, DragEventArgs e)
344 if (!IsBusy)
346 string[] droppedFiles = e.Data.GetData(DataFormats.FileDrop) as string[];
347 if (!ReferenceEquals(droppedFiles, null))
349 foreach (string currentFile in droppedFiles)
351 string fullFilePath = PathUtils.CleanUpFilePathString(currentFile);
352 if ((!string.IsNullOrEmpty(fullFilePath)) && File.Exists(fullFilePath))
354 TabControl.SelectedIndex = GetTabIndexOf(m_modeOfOperation);
355 switch (m_modeOfOperation)
357 case ModeOfOperation.Encrypt:
358 Edit_Encrypt_InputFile .Text = fullFilePath;
359 Edit_Encrypt_OutputFile.Text = GenerateEncryptOutputFileName(fullFilePath);
360 break;
361 case ModeOfOperation.Decrypt:
362 Edit_Decrypt_InputFile .Text = fullFilePath;
363 Edit_Decrypt_OutputFile.Text = GenerateDecryptOutputFileName(fullFilePath);
364 break;
366 break;
371 e.Handled = true;
374 private async void Window_Loaded(object sender, RoutedEventArgs e)
376 await Task.Yield();
377 SystemMenu systemMenu = new SystemMenu(this, SystemMenu_Activated);
378 m_menuId_disableAnimation = systemMenu.AppendMenu("Disable Busy Indicator");
379 m_menuId_enableExpertMode = systemMenu.AppendMenu("Expert Settings");
380 if (m_disableAnimation && m_menuId_disableAnimation.HasValue)
382 systemMenu.ModifyMenu(m_menuId_disableAnimation.Value, m_disableAnimation);
384 CreateIndicatorElements();
387 private void Window_Closing(object sender, CancelEventArgs e)
389 if (IsBusy)
391 SystemSounds.Hand.Play();
392 e.Cancel = true;
396 private void DispatcherTimer_Tick(object sender, EventArgs e)
398 ShuffleIndicatorElements();
401 private void SystemMenu_Activated(SystemMenu sender, uint menuId)
403 if (m_menuId_disableAnimation.HasValue && (menuId == m_menuId_disableAnimation.Value))
405 sender.ModifyMenu(menuId, m_disableAnimation = !m_disableAnimation);
406 if (m_busyFlag)
408 m_dispatcherTimer.IsEnabled = !m_disableAnimation;
409 NotifyPropertyChanged("IsBusyIndicatorVisible");
412 else if (m_menuId_enableExpertMode.HasValue && (menuId == m_menuId_enableExpertMode.Value))
416 Process.Start("https://youtu.be/Is_8bjYVmnA").Dispose();
418 catch { }
422 // =============================================================================
423 // Internal methods
424 // =============================================================================
426 private async Task ValidateInputFile(TextBox inputFileEdit, TextBox outputFileEdit, PasswordToggleBox passwordEdit, SlunkProcessor processor, bool checkStrongPasswd)
428 string inputFilePath;
429 if (string.IsNullOrEmpty(inputFileEdit.Text = inputFilePath = PathUtils.CleanUpFilePathString(inputFileEdit.Text)))
431 MessageBox.Show(this, "Input file must be selected first!", "Input File Missing", MessageBoxButton.OK, MessageBoxImage.Warning);
432 SetFocusAndSelectAll(inputFileEdit);
433 return;
435 if (PathUtils.IsInvalidPath(inputFilePath))
437 MessageBox.Show(this, "The specified input file path is invalid!", "Input File Invalid", MessageBoxButton.OK, MessageBoxImage.Warning);
438 SetFocusAndSelectAll(inputFileEdit);
439 return;
441 if (Directory.Exists(inputFilePath))
443 MessageBox.Show(this, "Specified input file appears to be a directory!", "Input File Invalid", MessageBoxButton.OK, MessageBoxImage.Warning);
444 SetFocusAndSelectAll(inputFileEdit);
445 return;
447 if (!File.Exists(inputFilePath))
449 MessageBox.Show(this, "Specified input file could not be found!", "Input Not Found", MessageBoxButton.OK, MessageBoxImage.Warning);
450 SetFocusAndSelectAll(inputFileEdit);
451 return;
453 await ValidateOutputFile(inputFilePath, outputFileEdit, passwordEdit, processor, checkStrongPasswd);
456 private async Task ValidateOutputFile(string inputFilePath, TextBox outputFileEdit, PasswordToggleBox passwordEdit, SlunkProcessor processor, bool checkStrongPasswd)
458 string outputFilePath;
459 if (string.IsNullOrEmpty(outputFileEdit.Text = outputFilePath = PathUtils.CleanUpFilePathString(outputFileEdit.Text)))
461 MessageBox.Show(this, "Output file must be selected first!", "Output File Missing", MessageBoxButton.OK, MessageBoxImage.Warning);
462 SetFocusAndSelectAll(outputFileEdit);
463 return;
465 if (PathUtils.IsInvalidPath(outputFilePath))
467 MessageBox.Show(this, "The specified output file path is invalid!", "Output File Invalid", MessageBoxButton.OK, MessageBoxImage.Warning);
468 SetFocusAndSelectAll(outputFileEdit);
469 return;
471 if (Directory.Exists(outputFilePath))
473 MessageBox.Show(this, "Specified output file appears to be a directory!", "Output File Invalid", MessageBoxButton.OK, MessageBoxImage.Warning);
474 SetFocusAndSelectAll(outputFileEdit);
475 return;
477 if (string.Equals(inputFilePath, outputFilePath, StringComparison.OrdinalIgnoreCase))
479 MessageBox.Show(this, "Input and output file can not be the same!", "File Name Conflict", MessageBoxButton.OK, MessageBoxImage.Warning);
480 SetFocusAndSelectAll(outputFileEdit);
481 return;
483 if (File.Exists(outputFilePath))
485 if (MessageBox.Show(this, "Specified output file already existst! Overwrite?", "Output File Exists", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) != MessageBoxResult.Yes)
487 SetFocusAndSelectAll(outputFileEdit);
488 return;
491 await ValidateOutputDirectory(inputFilePath, outputFilePath, passwordEdit, processor, checkStrongPasswd);
495 private async Task ValidateOutputDirectory(string inputFilePath, string outputFilePath, PasswordToggleBox passwordEdit, SlunkProcessor processor, bool checkStrongPasswd)
497 string outputDirectory;
498 if (string.IsNullOrEmpty(outputDirectory = PathUtils.TryGetDirectoryName(outputFilePath)))
500 MessageBox.Show(this, "The output directory could not be determined!", "Output Directory Invalid", MessageBoxButton.OK, MessageBoxImage.Warning);
501 return;
503 while (!Directory.Exists(outputDirectory))
505 if (MessageBox.Show(this, "Output directory does not exist yet! Create it now?", "Output Directory Nonexistent", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes) != MessageBoxResult.Yes)
507 return;
509 if (!PathUtils.TryCreateDirectory(outputDirectory))
511 MessageBox.Show(this, "The output directory could not be created!", "Directory Creation Failed", MessageBoxButton.OK, MessageBoxImage.Warning);
512 return;
515 await ValidatePassword(inputFilePath, outputFilePath, passwordEdit, processor, checkStrongPasswd);
518 private async Task ValidatePassword(string inputFilePath, string outputFilePath, PasswordToggleBox passwordEdit, SlunkProcessor processor, bool checkStrongPasswd)
520 string passwordStr;
521 if (string.IsNullOrEmpty(passwordStr = passwordEdit.Password) || (passwordStr.Length < MIN_PASSWD_LENGTH))
523 MessageBox.Show(this, String.Format("Passphrase must be at least {0:D} characters in length!", MIN_PASSWD_LENGTH), "Passphrase Missing", MessageBoxButton.OK, MessageBoxImage.Warning);
524 SetFocusAndSelectAll(passwordEdit);
525 return;
527 if (checkStrongPasswd)
529 if (passwordStr.Length < REC_PASSWD_LENGTH)
531 if (MessageBox.Show(this, String.Format("Recommended passphrase length is at least {0:D} characters!", REC_PASSWD_LENGTH), "Short Passphrase", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) != MessageBoxResult.OK)
533 SetFocusAndSelectAll(passwordEdit);
534 return;
537 else if (PasswordGen.IsWeakPassword(passwordStr))
539 if (MessageBox.Show(this, "Passphrase should contain a mix of upper case characters, lower case characters, digits and other characters!", "Weak Passphrase", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) != MessageBoxResult.OK)
541 SetFocusAndSelectAll(passwordEdit);
542 return;
546 await InvokeProcessor(inputFilePath, outputFilePath, passwordStr, processor);
549 private async Task InvokeProcessor(string inputFile, string outputFile, string password, SlunkProcessor processor)
551 using (BusyManager busyManager = new BusyManager(this))
553 ResetKeyboardFocus(this);
554 SetProgress(double.PositiveInfinity);
555 ClearLogFile();
556 Button_Decrypt_Toggle.IsChecked = Button_Encrypt_Toggle.IsChecked = m_checksumError = m_processReceived = false;
557 if (!await processor(inputFile, outputFile, password))
559 SetProgress(double.NaN, true);
560 PathUtils.TryRemoveFile(outputFile);
562 await Task.Yield();
566 private async Task<bool> Encrypt(string inputFile, string outputFile, string password)
568 SetStatus("Please wait while the encryption process is initializing...");
569 int? exitCode = await RunProcess(SlunkCryptRunner.Mode.Encrypt, inputFile, outputFile, password);
570 if (exitCode.HasValue)
572 if (exitCode.Value == 0)
574 SetProgress(1);
575 SetStatus("Completed: The file has been encrypted successfully.", Status.Success);
576 SystemSounds.Asterisk.Play();
578 else
580 SetProgress(1, true);
581 SetStatus("Error: Failed to enecrypt the file. Please see the log file for details!", Status.Failure);
582 SystemSounds.Hand.Play();
584 return true;
586 return false;
589 private async Task<bool> Decrypt(string inputFile, string outputFile, string password)
591 SetStatus("Please wait while the decryption process is initializing...");
592 int? exitCode = await RunProcess(SlunkCryptRunner.Mode.Decrypt, inputFile, outputFile, password);
593 if (exitCode.HasValue)
595 if (exitCode.Value == 0)
597 SetStatus("Completed: The file has been decrypted successfully (checksum is correct).", Status.Success);
598 SetProgress(1);
599 SystemSounds.Asterisk.Play();
601 else
603 if (m_checksumError)
605 SetStatus("Error: Checksum mismatch detected! Wrong passphrase or corrupted file?", Status.Failure);
607 else
609 SetStatus("Error: Failed to decrypt the file. Please see the log file for details!", Status.Failure);
611 SetProgress(1, true);
612 SystemSounds.Hand.Play();
614 return true;
616 return false;
619 private async Task<int?> RunProcess(SlunkCryptRunner.Mode mode, string inputFile, string outputFile, string password)
621 if (!ReferenceEquals(m_processRunner, null))
623 throw new InvalidOperationException("Process has already been started!");
627 using (m_processRunner = new SlunkCryptRunner(Dispatcher))
629 m_processRunner.OutputAvailable += Process_OutputAvailable;
630 m_processRunner.ProgressChanged += Porcess_ProgressChanged;
631 SetProcessPriority(ProcessPriorityClass.AboveNormal);
632 return await m_processRunner.ExecuteAsync(mode, inputFile, outputFile, password);
635 catch (ProcessRunner.ProcessStartException err)
637 SetStatus(string.Format("Error: The {0} process could not be created! (Error code: {1:D})", GetModeString(m_modeOfOperation), GetWin32ErrorCode(err)), Status.Failure);
638 MessageBox.Show(this, "Failed to create SlunkCrypt process:\n\n" + err.InnerException?.Message ?? err.Message, "Process Creation Error", MessageBoxButton.OK, MessageBoxImage.Error);
640 catch (ExecutableHelper.ExecutableNotFoundException)
642 SetStatus("Error: The required SlunkCrypt executable file could not be found!", Status.Failure);
643 MessageBox.Show(this, "The SlunkCrypt executable file could not be found.\n\nPlease make sure that the SlunkCrypt CLI executable file is located in the same directory as the GUI program!", "Executable Not Found", MessageBoxButton.OK, MessageBoxImage.Error);
645 catch (ProcessRunner.ProcessInterruptedException)
647 SetStatus(string.Format("Aborted: The {0} process was aborted by the user!", GetModeString(m_modeOfOperation)), Status.Failure);
648 SystemSounds.Hand.Play();
650 finally
652 m_processRunner = null; /*final clean-up*/
654 return null;
657 private async Task<string> GeneratePassword()
659 string password = string.Empty;
662 Task<string> passwordTask = Task.Run(() => PasswordGen.GeneratePassword(GEN_PASSWD_LENGTH));
663 await Task.WhenAll(passwordTask, Task.Delay(1000));
664 password = passwordTask.Result;
666 catch (ExecutableHelper.ExecutableNotFoundException)
668 MessageBox.Show(this, "The SlunkCrypt executable file could not be found.\n\nPlease make sure that the SlunkCrypt CLI executable file is located in the same directory as the GUI program!", "Executable Not Found", MessageBoxButton.OK, MessageBoxImage.Error);
670 catch (PasswordGen.GenerationFailedException)
672 MessageBox.Show(this, "Error: The password could not be generated!", "Generation Failed", MessageBoxButton.OK, MessageBoxImage.Error);
674 return password;
677 private void SetStatus(string text, Status status = Status.Default)
679 switch (status)
681 case Status.Success:
682 Label_Status.Foreground = Brushes.DarkGreen;
683 break;
684 case Status.Failure:
685 Label_Status.Foreground = Brushes.DarkRed;
686 break;
687 default:
688 Label_Status.Foreground = SystemColors.WindowTextBrush;
689 break;
691 Label_Status.Text = text;
694 private void SetProgress(double progress, bool failed = false)
696 if (!(double.IsNaN(progress) || double.IsInfinity(progress)))
698 ProgressBar.IsIndeterminate = false;
699 ProgressBar.Value = progress;
700 TaskbarItemInfo.ProgressState = failed ? TaskbarItemProgressState.Error : TaskbarItemProgressState.Normal;
701 TaskbarItemInfo.ProgressValue = progress;
702 Label_Progress.Text = string.Format(CultureInfo.InvariantCulture, "{0:0.0}%", progress * 100.0);
704 else
706 if (double.IsInfinity(progress))
708 ProgressBar.IsIndeterminate = true;
709 ProgressBar.Value = 0;
710 TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Indeterminate;
711 Label_Progress.Text = string.Empty;
713 else
715 ProgressBar.IsIndeterminate = false;
716 TaskbarItemInfo.ProgressState = failed ? TaskbarItemProgressState.Error : TaskbarItemProgressState.None;
721 private void ClearProgress()
723 ProgressBar.IsIndeterminate = false;
724 ProgressBar.Value = 0;
725 TaskbarItemInfo.ProgressState = TaskbarItemProgressState.None;
726 Label_Progress.Text = string.Empty;
729 private void AbortProcess()
731 ProcessRunner processRunner;
732 if (!ReferenceEquals(processRunner = m_processRunner, null))
736 processRunner.AbortProcess();
738 catch { }
742 private string BrowseForFile(string fileName, bool saveDialog, string filterString = null)
744 FileDialog openFileDialog = saveDialog ? new SaveFileDialog() { OverwritePrompt = false } : (FileDialog) new OpenFileDialog();
745 openFileDialog.Filter = string.IsNullOrEmpty(filterString) ? "All files (*.*)|*.*" : filterString;
746 if (!string.IsNullOrEmpty(fileName))
748 openFileDialog.FileName = fileName;
750 if (openFileDialog.ShowDialog().GetValueOrDefault(false))
752 return openFileDialog.FileName;
754 return null;
757 private void CreateIndicatorElements()
759 FontFamily hackFont = new FontFamily(new Uri("pack://application:,,,/"), "./Resources/Fonts/#Hack");
760 DropShadowEffect dropShadowEffect = CreateShadowEffect(Colors.Black, 3.0);
761 TextBlock reference = CreateTextBlock('0', Brushes.Gold, hackFont, dropShadowEffect);
762 reference.Measure(new Size(double.MaxValue, double.MaxValue));
763 Size desiredSize = reference.DesiredSize;
764 double actualWidth = Canvas.ActualWidth, actualHeight = Canvas.ActualHeight;
765 int lenX = (int)Math.Ceiling(desiredSize.Width * 1.25);
766 int lenY = (int)Math.Ceiling(desiredSize.Height * 1.25);
767 int numX = (int)Math.Floor(actualWidth / lenX);
768 int numY = (int)Math.Floor(actualHeight / lenY);
769 int offX = (int)Math.Round((actualWidth - (numX * lenX)) / 2.0);
770 int offY = (int)Math.Round((actualHeight - (numY * lenY)) / 2.0);
771 for (int i = 0; i < numX; ++i)
773 for (int j = 0; j < numY; ++j)
775 TextBlock element = CreateTextBlock('0', Brushes.Gold, hackFont, dropShadowEffect);
776 Canvas.Children.Add(element);
777 Canvas.SetLeft(element, offX + (i * lenX));
778 Canvas.SetTop (element, offY + (j * lenY));
781 ShuffleIndicatorElements();
784 private void ShuffleIndicatorElements()
786 char[] chars = ASCII_CHARS.ToCharArray();
787 UIElementCollection children = Canvas.Children;
788 for (int i = 0; i < children.Count; ++i)
790 TextBlock element;
791 if (!ReferenceEquals(element = children[i] as TextBlock, null))
793 if (m_random.Next(7) == 0)
795 element.Visibility = Visibility.Visible;
796 element.Text = char.ToString(chars[m_random.Next(chars.Length)]);
798 else
800 element.Visibility = Visibility.Hidden;
806 private void AppendLogFile(string line)
808 if (!string.IsNullOrEmpty(line))
810 m_logFile.Add(line);
814 private int GetTabIndexOf(ModeOfOperation modeOfOperation)
816 ItemCollection collection = TabControl.Items;
817 for (int index = 0; index < collection.Count; ++index)
819 ModeOfOperation? current = GetModeOfOperation(collection[index]);
820 if (current.HasValue && (current.Value == modeOfOperation))
822 return index;
825 return -1;
828 private void ClearLogFile()
830 m_logFile.Clear();
833 private void NotifyPropertyChanged(string name)
835 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
838 // -----------------------------------------------------------------------------
839 // Static methods
840 // -----------------------------------------------------------------------------
842 private static string CreateAboutText()
844 CPUFeatures cpuFeatures = CPUFeatures.Features;
845 return new StringBuilder()
846 .AppendLine("SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>")
847 .AppendLine(VersionInfo.VersionStr)
848 .AppendLine("This work has been released under the \u201CCC0 1.0\u201D license!")
849 .AppendLine()
850 .AppendLine("Source-code repository:")
851 .AppendLine("https://gitlab.com/lord_mulder/slunkcrypt/")
852 .AppendLine()
853 .AppendLine("Operating System: " + Environment.OSVersion.VersionString)
854 .AppendLine(string.Format("Operating System Bitness: {0:D}, Process Bitness: {1:D}", Environment.Is64BitOperatingSystem ? 64 : 32, Environment.Is64BitProcess ? 64 : 32))
855 .AppendLine(".NET Runtime Version: " + Environment.Version)
856 .AppendLine(string.Format("CPU Count: {0:D}, Architecture: {1}, SSE2 Supported: {2}", Environment.ProcessorCount, cpuFeatures.x64 ? "x64" : "x86", cpuFeatures.sse2 ? "Yes" : "No"))
857 .AppendLine()
858 .AppendLine("Using “Silk” icons, by Mark James")
859 .ToString();
862 private static string GenerateEncryptOutputFileName(string inputFilePath)
864 string directoryPath = Path.GetDirectoryName(inputFilePath), fileName = Path.GetFileNameWithoutExtension(inputFilePath), extension = Path.GetExtension(inputFilePath);
865 string outputFile = Path.Combine(directoryPath, string.Format("{0}{1}.enc", fileName, extension));
866 for (int count = 2; File.Exists(outputFile); ++count)
868 outputFile = Path.Combine(directoryPath, string.Format("{0} ({1:D}){2}.enc", fileName, count, extension));
870 return outputFile;
873 private static string GenerateDecryptOutputFileName(string inputFilePath)
875 string directoryPath = Path.GetDirectoryName(inputFilePath), fileName = Path.GetFileNameWithoutExtension(inputFilePath), extension = Path.GetExtension(inputFilePath);
876 while (extension.Equals(".enc", StringComparison.OrdinalIgnoreCase))
878 extension = Path.GetExtension(fileName);
879 fileName = Path.GetFileNameWithoutExtension(fileName);
881 if (string.IsNullOrEmpty(extension))
883 extension = ".out";
885 string outputFile = Path.Combine(directoryPath, string.Concat(fileName, extension));
886 for (int count = 2; File.Exists(outputFile); ++count)
888 outputFile = Path.Combine(directoryPath, String.Format("{0} ({1:D}){2}", fileName, count, extension));
890 return outputFile;
893 private static ModeOfOperation? GetModeOfOperation(object selectedItem)
895 TabItem selectedTabItem = selectedItem as TabItem;
896 if (!ReferenceEquals(selectedTabItem, null))
898 return selectedTabItem.Tag as ModeOfOperation?;
900 return null;
903 private static string GetModeString(ModeOfOperation modeOfOperation)
905 switch(modeOfOperation)
907 case ModeOfOperation.Encrypt:
908 return "encryption";
909 case ModeOfOperation.Decrypt:
910 return "decryption";
911 default:
912 throw new ArgumentException("modeOfOperation");
916 private static void SetFocusAndSelectAll(FrameworkElement element)
918 TextBox textBox;
919 if (!ReferenceEquals(textBox = element as TextBox, null))
921 textBox.Focus();
922 textBox.SelectAll();
924 else
926 PasswordToggleBox passwordToggleBox;
927 if (!ReferenceEquals(passwordToggleBox = element as PasswordToggleBox, null))
929 passwordToggleBox.Focus();
930 passwordToggleBox.SelectAll();
935 private static void SetProcessPriority(ProcessPriorityClass priorityClass)
939 using (Process currentProcess = Process.GetCurrentProcess())
941 currentProcess.PriorityClass = priorityClass;
944 catch { }
947 private static int GetWin32ErrorCode(Exception err)
949 while (!ReferenceEquals(err, null))
951 if (err is Win32Exception)
953 return ((Win32Exception)err).NativeErrorCode;
955 err = err.InnerException;
957 return 0;
960 private static TextBlock CreateTextBlock(char c, Brush foreground, FontFamily fontFamily, Effect effect)
962 return new TextBlock()
964 Text = char.ToString(c),
965 Foreground = foreground,
966 FontFamily = fontFamily,
967 Effect = effect
971 private static DropShadowEffect CreateShadowEffect(Color color, double blurRadius)
973 return new DropShadowEffect()
975 Color = color,
976 BlurRadius = blurRadius,
977 Direction = 0.0,
978 ShadowDepth = 0.0
982 private static void ResetKeyboardFocus(UIElement element)
984 Keyboard.ClearFocus();
985 element.Focus();