using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Input;
using Comal.Classes;
using InABox.Clients;
using InABox.Configuration;
using InABox.Core;
using InABox.Wpf;
using InABox.WPF;
using PRSDesktop.Forms;
using ValidationResult = InABox.Clients.ValidationResult;
namespace PRSDesktop;
///
/// Interaction logic for PinLogin.xaml
///
public partial class PinLogin : ThemableWindow
{
private bool _isloggedin = false;
private enum Page
{
PIN,
PASSWORD,
_2FA
}
private bool PINPage;
private Page CurrentPage;
private bool Checking = false;
private List codeInputs = new();
public PinLogin(string version, ValidationResult status)
{
InitializeComponent();
Title = $"{(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})";
foreach (var codeInput in CodeInput.Children)
{
var tb = codeInput as TextBox;
tb.PreviewKeyDown += Code2FA_PreviewKeyDown;
tb.PreviewTextInput += Code2FA_Preview;
tb.TextChanged += Code2FA_TextChanged;
codeInputs.Add(tb);
}
UserID.Text = App.DatabaseSettings.UserID;
Password.Password = App.DatabaseSettings.Password;
if (status == ValidationResult.PASSWORD_EXPIRED)
{
status = TryValidation("", status);
}
PINPage = App.DatabaseSettings.LoginType == LoginType.PIN;
if (status == ValidationResult.REQUIRE_2FA)
Show2FAPage();
else if (PINPage)
ShowPINPage();
else
ShowPasswordPage();
//AutoLogin.IsChecked = _settings.Autologin;
}
private void ShowPINPage()
{
CurrentPage = Page.PIN;
OverallLayout.ColumnDefinitions[0].Width = new GridLength(1, GridUnitType.Star);
OverallLayout.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Star);
OverallLayout.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Star);
PINLayout.RowDefinitions[3].Height = new GridLength(70, GridUnitType.Pixel);
PINLayout.RowDefinitions[4].Height = new GridLength(70, GridUnitType.Pixel);
Layout_2FA.RowDefinitions[3].Height = new GridLength(0, GridUnitType.Pixel);
}
private void ShowPasswordPage()
{
CurrentPage = Page.PASSWORD;
OverallLayout.ColumnDefinitions[0].Width = new GridLength(0, GridUnitType.Star);
OverallLayout.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
OverallLayout.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Star);
PINLayout.RowDefinitions[3].Height = new GridLength(0, GridUnitType.Pixel);
PINLayout.RowDefinitions[4].Height = new GridLength(0, GridUnitType.Pixel);
Layout_2FA.RowDefinitions[3].Height = new GridLength(0, GridUnitType.Pixel);
}
private void Show2FAPage()
{
CurrentPage = Page._2FA;
OverallLayout.ColumnDefinitions[0].Width = new GridLength(0, GridUnitType.Star);
OverallLayout.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Star);
OverallLayout.ColumnDefinitions[2].Width = new GridLength(1, GridUnitType.Star);
PINLayout.RowDefinitions[3].Height = new GridLength(0, GridUnitType.Pixel);
PINLayout.RowDefinitions[4].Height = new GridLength(0, GridUnitType.Pixel);
Layout_2FA.RowDefinitions[3].Height = new GridLength(70, GridUnitType.Pixel);
foreach (var codeInput in codeInputs)
{
codeInput.Text = "";
}
codeInputs[0].Focus();
Label_2FA.Content = string.Format("Please enter the code that was sent to {0}", ClientFactory.Recipient2FA);
}
private void ViewPINClick(object sender, RoutedEventArgs e)
{
ShowPINPage();
PIN.Focus();
PINPage = true;
}
private void ViewPasswordClick(object sender, RoutedEventArgs e)
{
ShowPasswordPage();
UserID.Focus();
PINPage = false;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
PIN.Password += button.Content;
}
private void Back_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(PIN.Password))
PIN.Password = PIN.Password[..^1]; //.Reverse().Skip(1).Reverse().ToString();
}
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(CurrentPage == Page._2FA)
{
if (e.Key == Key.Enter || e.Key == Key.Return)
{
e.Handled = true;
Do2FA();
}
return;
}
if (CurrentPage != Page.PIN)
return;
e.Handled = true;
Button button = null;
if (e.Key == Key.Enter || e.Key == Key.Return)
button = OK;
else if (e.Key == Key.D1 || e.Key == Key.NumPad1)
button = Key1;
else if (e.Key == Key.D2 || e.Key == Key.NumPad2)
button = Key2;
else if (e.Key == Key.D3 || e.Key == Key.NumPad3)
button = Key3;
else if (e.Key == Key.D4 || e.Key == Key.NumPad4)
button = Key4;
else if (e.Key == Key.D5 || e.Key == Key.NumPad5)
button = Key5;
else if (e.Key == Key.D6 || e.Key == Key.NumPad6)
button = Key6;
else if (e.Key == Key.D7 || e.Key == Key.NumPad7)
button = Key7;
else if (e.Key == Key.D8 || e.Key == Key.NumPad8)
button = Key8;
else if (e.Key == Key.D9 || e.Key == Key.NumPad9)
button = Key9;
else if (e.Key == Key.D0 || e.Key == Key.NumPad0)
button = Key0;
else if (e.Key == Key.Back)
button = Back;
if (button == null || !button.IsEnabled)
return;
var peer = new ButtonAutomationPeer(button);
var invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
invokeProv.Invoke();
}
private void PINChanged(object sender, RoutedEventArgs e)
{
Back.IsEnabled = !string.IsNullOrEmpty(PIN.Password);
OK.IsEnabled = !string.IsNullOrEmpty(PIN.Password);
}
private string? ShowChangePassword(string userID)
{
using (var session = new BypassSession())
{
var user = new Client().Query(
new Filter(x => x.UserID).IsEqualTo(userID),
new Columns(x => x.ID)).Rows.FirstOrDefault()?.ToObject();
if(user != null)
{
var changePassword = new ChangePassword(user.ID);
if (changePassword.ShowDialog() == true && changePassword.Password != null)
{
ChangePassword.ChangeUserPassword(user, changePassword.Password);
new Client().Save(user, "Changed password");
return changePassword.Password;
}
else
{
if (PINPage)
ShowPINPage();
else
ShowPasswordPage();
}
}
else
{
MessageBox.Show($"No user with username {userID}");
}
}
return null;
}
private void OK_Click(object sender, RoutedEventArgs e)
{
ValidationResult? result;
using (new WaitCursor())
{
try
{
result = ClientFactory.Validate(PIN.Password);
}
catch(Exception err)
{
Logger.Send(LogType.Error, ClientFactory.UserID, $"Error connecting to server: {CoreUtils.FormatException(err)}");
MessageBox.Show("Error connecting to server! Check the server URL and port number.");
result = null;
}
}
if (result == ValidationResult.REQUIRE_2FA)
{
Show2FAPage();
return;
}
var bOK = result == ValidationResult.VALID;
if (bOK)
{
App.DatabaseSettings.LoginType = LoginType.PIN;
App.DatabaseSettings.Autologin = false;
App.DatabaseSettings.UserID = "";
App.DatabaseSettings.Password = "";
DialogResult = true;
Close();
}
else
{
if(result != null)
{
MessageBox.Show("Invalid PIN!");
}
PIN.Password = "";
}
}
private void UserID_TextChanged(object sender, TextChangedEventArgs e)
{
Login.IsEnabled = !string.IsNullOrEmpty(UserID.Text) && !string.IsNullOrEmpty(Password.Password);
}
private void Password_PasswordChanged(object sender, RoutedEventArgs e)
{
Login.IsEnabled = !string.IsNullOrEmpty(UserID.Text) && !string.IsNullOrEmpty(Password.Password);
}
private void AutoLogin_Checked(object sender, RoutedEventArgs e)
{
}
private ValidationResult TryValidation(string password, ValidationResult? result = null)
{
while (true)
{
if(result == null)
{
using (new WaitCursor())
{
try
{
result = ClientFactory.Validate(UserID.Text, password);
}
catch (Exception e)
{
Logger.Send(LogType.Error, ClientFactory.UserID, $"Error connecting to server: {CoreUtils.FormatException(e)}");
MessageBox.Show("Error connecting to server! Check the server URL and port number.");
}
}
}
if (result == ValidationResult.PASSWORD_EXPIRED)
{
MessageBox.Show("Your password has expired!");
var newPassword = ShowChangePassword(UserID.Text);
if (newPassword == null)
{
break;
}
else
{
password = newPassword;
Password.Clear();
}
}
else
{
break;
}
result = null;
}
return result ?? ValidationResult.INVALID;
}
private void Login_Click(object sender, RoutedEventArgs e)
{
ValidationResult result = TryValidation(Password.Password);
if (result == ValidationResult.REQUIRE_2FA)
{
Show2FAPage();
return;
}
var bOK = result == ValidationResult.VALID;
if (bOK)
{
//if (AutoLogin.IsChecked == true)
//{
// _settings.LoginType = LoginType.UserID;
// _settings.UserID = UserID.Text;
// _settings.Password = Password.Password;
// _settings.Autologin = true;
// new LocalConfiguration().Save(_settings);
//}
DialogResult = true;
Close();
}
else
{
MessageBox.Show("Login Failed!");
}
}
private void Window_Closing(object sender, CancelEventArgs e)
{
//e.Cancel = ClientFactory.UserGuid == Guid.Empty;
if(!PINPage && DialogResult == true && App.DatabaseSettings.Autologin
&& App.DatabaseSettings.UserID == UserID.Text
&& App.DatabaseSettings.Password != Password.Password)
{
if(MessageBox.Show("You have logged in with a new password. Would you like to update your password in your settings so that you login automatically next time?",
"Update password?",
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
App.DatabaseSettings.Password = Password.Password;
App.SaveDatabaseSettings();
}
}
}
private void Password_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter || e.Key == Key.Return)
{
e.Handled = true;
var peer = new ButtonAutomationPeer(Login);
var invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
invokeProv.Invoke();
}
}
private void UserID_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter || e.Key == Key.Return)
{
e.Handled = true;
Password.Focus();
}
}
private bool HasFilled2FABoxes()
{
foreach (var codeInput in CodeInput.Children)
{
var digit = (codeInput as TextBox)?.Text;
if (digit == null || digit.Length != 1 || !char.IsDigit(digit[0]))
{
return false;
}
}
return true;
}
private void Do2FA(bool showMessage = true)
{
if (Checking) return;
Checking = true;
var code = "";
foreach (var codeInput in CodeInput.Children)
{
var digit = (codeInput as TextBox)?.Text;
if (digit != null && digit.Length == 1 && char.IsDigit(digit[0]))
{
code += digit[0];
}
else
{
MessageBox.Show("All boxes must be filled and must contain a number");
return;
}
}
Task.Run(() =>
{
if (ClientFactory.Check2FA(code))
{
Dispatcher.Invoke(() =>
{
DialogResult = true;
Close();
});
}
else
{
MessageBox.Show("Code is incorrect!");
Checking = false;
}
});
}
private void Click_2FA(object sender, RoutedEventArgs e)
{
Do2FA();
}
private void Back_2FA(object sender, RoutedEventArgs e)
{
ClientFactory.InvalidateUser();
if (PINPage)
ShowPINPage();
else
ShowPasswordPage();
}
private void MoveFocus()
{
var element = Keyboard.FocusedElement;
if (element is TextBox)
{
var tb = element as TextBox;
if (tb.Text.Length == 1)
{
var index = codeInputs.IndexOf(tb) + 1;
if (index >= codeInputs.Count)
{
Focus();
if (HasFilled2FABoxes())
{
Do2FA();
}
}
else
{
var nextChild = codeInputs[index];
nextChild.Focus();
}
}
}
}
private void Code2FA_Preview(object sender, TextCompositionEventArgs e)
{
var tb = sender as TextBox;
var oldText = tb!.Text;
var newText = e.Text;
var valid = newText.Length == 0 || (newText.Length == 1 && char.IsDigit(newText[0]));
if (oldText.Length != 0 && valid)
{
tb.Text = "";
}
e.Handled = !valid;
if (!valid && oldText.Length + newText.Length > 0)
{
MoveFocus();
}
}
private void Code2FA_TextChanged(object sender, TextChangedEventArgs e)
{
MoveFocus();
}
private void Code2FA_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (sender is TextBox tb)
{
var currentIndex = codeInputs.IndexOf(tb);
int? newIndex = null;
switch (e.Key)
{
case Key.Back:
tb.Text = "";
newIndex = Math.Max(currentIndex - 1, 0);
e.Handled = true;
break;
case Key.Left:
newIndex = Math.Max(currentIndex - 1, 0);
e.Handled = true;
break;
case Key.Right:
newIndex = Math.Min(currentIndex + 1, codeInputs.Count - 1);
e.Handled = true;
break;
}
if(newIndex != null)
{
var nextChild = codeInputs[newIndex ?? 0];
nextChild.Focus();
}
}
}
}