using System; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Windows; using Microsoft.Win32; using System.IO; using System.Windows.Threading; using System.Windows.Forms; using Hardcodet.Wpf.TaskbarNotification; using LD_SysInfo.Services; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Windows.Media.Animation; using LD_SysInfo.Models; using MaterialDesignColors; using MaterialDesignThemes.Wpf; using System.Windows.Input; using Application = System.Windows.Application; using System.Windows.Media; namespace LD_SysInfo { public partial class MainWindow : Window { private static readonly HttpClient httpClient = new HttpClient(); private static AppConfig _config; private static string jwtToken = null; // 🔹 Store the JWT token globally in the app private readonly DispatcherTimer messageClearTimer; private readonly DispatcherTimer postTimer; private readonly DispatcherTimer keepAliveTimer; private readonly Uri LightThemeUri = new Uri("pack://application:,,,/Themes/LightTheme.xaml", UriKind.Absolute); private readonly Uri DarkThemeUri = new Uri("pack://application:,,,/Themes/DarkTheme.xaml", UriKind.Absolute); private bool isDarkTheme = false; private static readonly string ConfigPath = Path.Combine(AppContext.BaseDirectory, "config.json"); public MainWindow() { // ⚠️ Temporary SSL Certificate Bypass - For Testing Only System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; InitializeComponent(); LoadConfig(); DisplaySystemInfo(); AutoLogin(); // 🔍 Perform initial connectivity check Task.Run(async () => await InitialCheckConnectivity()); // Initialize the system tray icon event TrayIcon.TrayMouseDoubleClick += TrayIcon_DoubleClick; // 🔥 Initialize the DispatcherTimer for fading out messages (statically set to 5 seconds) messageClearTimer = new DispatcherTimer(); messageClearTimer.Interval = TimeSpan.FromSeconds(5); // Set to 5 seconds (static) messageClearTimer.Tick += MessageClearTimer_Tick; // 🔥 Initialize the DispatcherTimer for periodic POSTs using _config.PollingInterval postTimer = new DispatcherTimer(); postTimer.Interval = TimeSpan.FromSeconds(_config.SystemInfoInterval); postTimer.Tick += PostTimer_Tick; postTimer.Start(); // 🔁 Initialize the KeepAlive timer using KeepAlivePeriod from config keepAliveTimer = new DispatcherTimer(); keepAliveTimer.Interval = TimeSpan.FromSeconds(_config.KeepAlivePeriod); // Set from config keepAliveTimer.Tick += KeepAliveTimer_Tick; keepAliveTimer.Start(); } private void LoadConfig() { try { //System.Windows.MessageBox.Show($"DEBUG: AppContext.BaseDirectory = {AppContext.BaseDirectory}"); //System.Windows.MessageBox.Show($"DEBUG: ConfigPath = {ConfigPath}"); if (!File.Exists((string?)ConfigPath)) { System.Windows.MessageBox.Show("❌ config.json not found at resolved path!"); } _config = ConfigManager.LoadConfig(ConfigPath); if (_config == null || string.IsNullOrWhiteSpace(_config.ClientIdentifier)) { throw new Exception("❌ Invalid or missing ClientIdentifier in config.json."); } } catch (Exception ex) { System.Windows.MessageBox.Show($"❌ Error loading config: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); _config = new AppConfig(); // Prevent application from crashing } } private async Task InitialCheckConnectivity() { var apiClient = new ApiClient(_config); bool isConnected = await apiClient.CheckConnectivity(); // You must update the UI from the UI thread Dispatcher.Invoke(() => { if (isConnected) { ConnectionStatusTextBlock.Text = "Connected to Server"; ConnectionStatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green); } else { ConnectionStatusTextBlock.Text = "No Connection"; ConnectionStatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red); } }); } private async void AutoLogin() { if (_config == null || _config.Auth == null || string.IsNullOrEmpty(_config.Auth.Username) || string.IsNullOrEmpty(_config.Auth.Password)) { UpdateStatusUI(false); return; } var apiClient = new ApiClient(_config); LoginResponse loginResponse = await apiClient.AuthenticateAsync(_config.Auth.Username, _config.Auth.Password); if (loginResponse != null && !string.IsNullOrEmpty(loginResponse.Token)) { ApiClient.SetJwtToken(loginResponse.Token); UpdateStatusUI(true); } else { UpdateStatusUI(false); } } private async void AuthenticateButton_Click(object sender, RoutedEventArgs e) { var apiClient = new ApiClient(_config); LoginResponse loginResponse = await apiClient.AuthenticateAsync(txtUsername.Text, pwdPassword.Password); if (loginResponse != null && !string.IsNullOrEmpty(loginResponse.Token)) { ApiClient.SetJwtToken(loginResponse.Token); UpdateStatusUI(true); // 🔥 Successfully authenticated } else { UpdateStatusUI(false); } } private async void CheckConnectivityButton_Click(object sender, RoutedEventArgs e) { var apiClient = new ApiClient(_config); bool isConnected = await apiClient.CheckConnectivity(); if (isConnected) { ConnectionStatusTextBlock.Text = "Connected to Server"; ConnectionStatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green); } else { ConnectionStatusTextBlock.Text = "No Connection"; ConnectionStatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red); } } private void GenerateLogsButton_Click(object sender, RoutedEventArgs e) { try { string logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LD_SysInfo", "Logs"); Directory.CreateDirectory(logDir); // Ensure directory exists string logFile = Path.Combine(logDir, $"log_{DateTime.Now:yyyyMMdd_HHmmss}.txt"); File.WriteAllText(logFile, "=== LD SysInfo Diagnostic Log ===\n"); File.AppendAllText(logFile, $"Timestamp: {DateTime.Now}\n"); File.AppendAllText(logFile, $"Hostname: {Environment.MachineName}\n"); File.AppendAllText(logFile, $"OS: {Environment.OSVersion}\n"); File.AppendAllText(logFile, $"Server Status: {ConnectionStatusTextBlock.Text}\n"); System.Windows.MessageBox.Show($"Logs generated successfully!\nPath: {logFile}", "Log Generated", MessageBoxButton.OK, MessageBoxImage.Information); } catch (Exception ex) { System.Windows.MessageBox.Show($"Failed to generate logs.\nError: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } private void DisplaySystemInfo() { var sysInfo = SystemInfo.GetSystemInfo(); OSNameTextBlock.Text = $"OS Name: {sysInfo.OSName}"; OSVersionTextBlock.Text = $"OS Version: {sysInfo.OSVersion}"; WindowsVersionTextBlock.Text = $"Windows Version: {sysInfo.WindowsVersion}"; WindowsBuildTextBlock.Text = $"Windows Build: {sysInfo.WindowsBuild}"; OSArchitectureTextBlock.Text = $"OS Architecture: {sysInfo.OSArchitecture}"; ProcessorNameTextBlock.Text = $"Processor Name: {sysInfo.ProcessorName}"; ProcessorArchitectureTextBlock.Text = $"Processor Architecture: {sysInfo.ProcessorArchitecture}"; HostnameTextBlock.Text = $"Hostname: {sysInfo.Hostname}"; } private void LoadInstalledAppsButton_Click(object sender, RoutedEventArgs e) { var applications = SystemInfo.GetInstalledApplicationsFromRegistry(); InstalledAppsListBox.ItemsSource = applications; } private async void StoreSystemInfoButton_Click(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(ApiClient.GetJwtToken())) // Check token via ApiClient { System.Windows.MessageBox.Show("❌ Please log in first.", "Not Authenticated", MessageBoxButton.OK, MessageBoxImage.Warning); return; } try { var sysInfo = SystemInfo.GetSystemInfo(); var applications = SystemInfo.GetInstalledApplicationsFromRegistry(); var formattedApplications = new List(); foreach (var app in applications) { formattedApplications.Add(new { app_name = app.Name, app_version = app.Version, publisher = app.Publisher }); } var payload = new { clientIdentifier = _config.ClientIdentifier, hostname = sysInfo.Hostname, osName = sysInfo.OSName, osVersion = sysInfo.OSVersion, windowsVersion = sysInfo.WindowsVersion, windowsBuild = sysInfo.WindowsBuild, osArchitecture = sysInfo.OSArchitecture, processorName = sysInfo.ProcessorName, processorArchitecture = sysInfo.ProcessorArchitecture, gpuName = sysInfo.GpuNames, totalMemory = sysInfo.TotalMemory, ipAddresses = sysInfo.IpAddresses, lastBootTime = sysInfo.LastBootTime, drives = sysInfo.Drives, installedApplications = formattedApplications }; var apiClient = new ApiClient(_config); string jsonPayload = JsonConvert.SerializeObject(payload); Console.WriteLine("🔍 [DEBUG] Raw JSON Payload:\n" + jsonPayload); string encryptedPayload = EncryptionHelper.EncryptData(jsonPayload); string response = await apiClient.StoreSystemInfoAsync(encryptedPayload); if (response.StartsWith("Error")) { StatusTextBlock.Text = $"❌ {response}"; StatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red); } else { StatusTextBlock.Text = "✅ System info stored successfully!"; StatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green); // 🔥 Trigger Fade Out Animation messageClearTimer.Stop(); // Stop existing timer FadeOutStatusMessage(); // Start the fade-out animation } } catch (Exception ex) { StatusTextBlock.Text = $"❌ Error: {ex.Message}"; StatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red); } } private void LoadThemeResources(string resourcePath) { var dictionaries = Application.Current.Resources.MergedDictionaries; dictionaries.Clear(); // Add required MaterialDesign v5 resource dictionaries dictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml", UriKind.Absolute) }); dictionaries.Add(new ResourceDictionary { Source = new Uri(isDarkTheme ? "pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Dark.xaml" : "pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Light.xaml", UriKind.Absolute) }); // Optional: Your custom theme overrides (LightTheme.xaml or DarkTheme.xaml) dictionaries.Add(new ResourceDictionary { Source = new Uri(resourcePath, UriKind.RelativeOrAbsolute) }); } private void ThemeToggleButton_Click(object sender, RoutedEventArgs e) { isDarkTheme = !isDarkTheme; LoadThemeResources(isDarkTheme ? "Themes/DarkTheme.xaml" : "Themes/LightTheme.xaml"); } private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) { this.DragMove(); } } private void MinimizeWindow_Click(object sender, RoutedEventArgs e) { WindowState = WindowState.Minimized; } private void MaximizeRestoreWindow_Click(object sender, RoutedEventArgs e) { if (WindowState == WindowState.Maximized) WindowState = WindowState.Normal; else WindowState = WindowState.Maximized; } private void CloseWindow_Click(object sender, RoutedEventArgs e) { Close(); } private void ExportToCsvButton_Click(object sender, RoutedEventArgs e) { var saveFileDialog = new Microsoft.Win32.SaveFileDialog { Filter = "CSV files (*.csv)|*.csv", Title = "Save System Info as CSV", FileName = "SystemInfo.csv" }; if (saveFileDialog.ShowDialog() == true) { var sysInfo = SystemInfo.GetSystemInfo(); try { using (var writer = new StreamWriter(saveFileDialog.FileName)) { writer.WriteLine("Property,Value"); writer.WriteLine($"Hostname,{sysInfo.Hostname}"); writer.WriteLine($"OS Name,{sysInfo.OSName}"); writer.WriteLine($"OS Version,{sysInfo.OSVersion}"); writer.WriteLine($"Windows Version,{sysInfo.WindowsVersion}"); writer.WriteLine($"Windows Build,{sysInfo.WindowsBuild}"); writer.WriteLine($"OS Architecture,{sysInfo.OSArchitecture}"); writer.WriteLine($"Processor Name,{sysInfo.ProcessorName}"); writer.WriteLine($"Processor Architecture,{sysInfo.ProcessorArchitecture}"); if (IncludeInstalledAppsCheckBox.IsChecked == true) { writer.WriteLine(); writer.WriteLine("Installed Applications:"); writer.WriteLine("Name,Version,Publisher"); var applications = SystemInfo.GetInstalledApplicationsFromRegistry(); foreach (var app in applications) { writer.WriteLine($"{app.Name},{app.Version},{app.Publisher}"); } } } StatusTextBlock.Text = "System info exported successfully!"; messageClearTimer.Start(); // Start the timer to clear the message after 5 seconds } catch (Exception ex) { StatusTextBlock.Text = $"Error saving file: {ex.Message}"; } } } private async void UpdateStatusUI(bool isAuthenticated) { // Ensure we're on the UI thread before updating UI elements Dispatcher.Invoke(() => { if (isAuthenticated) { txtUsername.Visibility = Visibility.Collapsed; pwdPassword.Visibility = Visibility.Collapsed; AuthenticateButton.Visibility = Visibility.Collapsed; StatusTextBlock.Text = "✅ Authenticated - Connected to Server"; StatusTextBlock.Foreground = (SolidColorBrush)FindResource("StatusConnectedBrush"); // Use your custom brush // 🔍 Check connectivity to the server var apiClient = new ApiClient(_config); Task.Run(async () => { bool isConnected = await apiClient.CheckConnectivity(); // Update connection status on the UI thread Dispatcher.Invoke(() => { if (isConnected) { ConnectionStatusTextBlock.Text = "Connected to Server"; ConnectionStatusTextBlock.Foreground = (SolidColorBrush)FindResource("StatusConnectedBrush"); // Use your custom brush } else { ConnectionStatusTextBlock.Text = "No Connection"; ConnectionStatusTextBlock.Foreground = (SolidColorBrush)FindResource("ErrorBrush"); // Use your custom error color } }); }); // 🔥 Start the timer to clear the message after a delay messageClearTimer.Stop(); // Stop any running timer first messageClearTimer.Start(); } else { txtUsername.Visibility = Visibility.Visible; pwdPassword.Visibility = Visibility.Visible; AuthenticateButton.Visibility = Visibility.Visible; StatusTextBlock.Text = "❌ Not Authenticated - Please Log In"; StatusTextBlock.Foreground = (SolidColorBrush)FindResource("ErrorBrush"); // Use your custom error color ConnectionStatusTextBlock.Text = "No Connection"; ConnectionStatusTextBlock.Foreground = (SolidColorBrush)FindResource("ErrorBrush"); // Use your custom error color // Stop the timer if the user is not authenticated messageClearTimer.Stop(); } }); } private void FadeOutStatusMessage() { var fadeOutAnimation = new DoubleAnimation { From = 1.0, To = 0.0, Duration = TimeSpan.FromSeconds(3), AutoReverse = false }; fadeOutAnimation.Completed += (s, e) => { StatusTextBlock.Text = string.Empty; // Clear the text after fading StatusTextBlock.Opacity = 1.0; // Reset the opacity for future use }; StatusTextBlock.BeginAnimation(OpacityProperty, fadeOutAnimation); messageClearTimer.Stop(); } private async void PostTimer_Tick(object sender, EventArgs e) { if (string.IsNullOrEmpty(ApiClient.GetJwtToken())) // Ensure authentication is active { StatusTextBlock.Text = "❌ Not authenticated. Please log in first."; return; } var apiClient = new ApiClient(_config); // Create an instance of ApiClient var sysInfo = SystemInfo.GetSystemInfo(); var applications = SystemInfo.GetInstalledApplicationsFromRegistry(); var formattedApplications = new List(); foreach (var app in applications) { formattedApplications.Add(new { app_name = app.Name, app_version = app.Version, publisher = app.Publisher }); } var payload = new { clientIdentifier = _config.ClientIdentifier, hostname = sysInfo.Hostname, osName = sysInfo.OSName, osVersion = sysInfo.OSVersion, windowsVersion = sysInfo.WindowsVersion, windowsBuild = sysInfo.WindowsBuild, osArchitecture = sysInfo.OSArchitecture, processorName = sysInfo.ProcessorName, processorArchitecture = sysInfo.ProcessorArchitecture, gpuName = sysInfo.GpuNames, totalMemory = sysInfo.TotalMemory, ipAddresses = sysInfo.IpAddresses, lastBootTime = sysInfo.LastBootTime, drives = sysInfo.Drives, installedApplications = formattedApplications }; string jsonPayload = JsonConvert.SerializeObject(payload); Console.WriteLine("🔍 [DEBUG] Raw JSON Payload:\n" + jsonPayload); string encryptedPayload = EncryptionHelper.EncryptData(jsonPayload); string response = await apiClient.StoreSystemInfoAsync(encryptedPayload); if (response.StartsWith("Error")) { StatusTextBlock.Text = $"❌ {response}"; StatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red); } else { StatusTextBlock.Text = "✅ System info stored successfully!"; StatusTextBlock.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green); messageClearTimer.Stop(); FadeOutStatusMessage(); } } private async void KeepAliveTimer_Tick(object sender, EventArgs e) { var apiClient = new ApiClient(_config); bool isConnected = await apiClient.CheckConnectivity(); Dispatcher.Invoke(() => { if (isConnected) { ConnectionStatusTextBlock.Text = "Connected to Server (Ping)"; // Use the custom gold color for connection status ConnectionStatusTextBlock.Foreground = (SolidColorBrush)FindResource("StatusConnectedBrush"); } else { ConnectionStatusTextBlock.Text = "No Connection"; // Use the custom error color for no connection ConnectionStatusTextBlock.Foreground = (SolidColorBrush)FindResource("ErrorBrush"); } }); } private void MessageClearTimer_Tick(object sender, EventArgs e) { FadeOutStatusMessage(); // Trigger the fade-out effect } private void TrayIcon_DoubleClick(object sender, RoutedEventArgs e) { ShowWindow(); } private void ShowWindow_Click(object sender, RoutedEventArgs e) { ShowWindow(); } private void ShowWindow() { this.Show(); this.WindowState = WindowState.Normal; } private void Exit_Click(object sender, RoutedEventArgs e) { TrayIcon.Dispose(); System.Windows.Application.Current.Shutdown(); } // Minimize to system tray instead of closing protected override void OnStateChanged(EventArgs e) { if (WindowState == WindowState.Minimized) { this.Hide(); // Hide the window when minimized } base.OnStateChanged(e); } } }