/* * Copyright (c) 2023 Proton AG * * This file is part of ProtonVPN. * * ProtonVPN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ProtonVPN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ProtonVPN. If not, see . */ using Microsoft.Win32; using ProtonVPN.Logging.Contracts; using ProtonVPN.Logging.Contracts.Events.AppLogs; using ProtonVPN.Logging.Contracts.Events.OperatingSystemLogs; using ProtonVPN.OperatingSystems.Registries.Contracts; namespace ProtonVPN.OperatingSystems.Registries; public class RegistryEditor : IRegistryEditor { private readonly ILogger _logger; public RegistryEditor(ILogger logger) { _logger = logger; } public object? ReadObject(RegistryUri uri) { try { using RegistryKey? key = OpenBaseKey(uri.HiveKey).OpenSubKey(uri.Path); return key?.GetValue(uri.Key); } catch (Exception ex) { _logger.Error($"Failed to read registry {uri}.", ex); return null; } } public int? ReadInt(RegistryUri uri) { object? value = ReadObject(uri); if (value == null) { return null; } if (value is int intValue) { return intValue; } if (int.TryParse(value.ToString(), out int parsedValue)) { return parsedValue; } _logger.Error($"Failed to parse registry value '{value}' to int."); return null; } public string? ReadString(RegistryUri uri) { object? value = ReadObject(uri); return value?.ToString(); } private RegistryKey OpenBaseKey(RegistryHive registryHive) { return RegistryKey.OpenBaseKey(registryHive, RegistryView.Registry64); } public bool WriteInt(RegistryUri uri, int value) { return WriteObject(uri, value, RegistryValueKind.DWord); } public bool WriteString(RegistryUri uri, string value) { return WriteObject(uri, value, RegistryValueKind.String); } private bool WriteObject(RegistryUri uri, object value, RegistryValueKind valueKind) { try { using RegistryKey? key = OpenBaseKey(uri.HiveKey).CreateSubKey(uri.Path, writable: true); if (key == null) { _logger.Error($"Failed to open registry path before writing to {uri}."); return false; } key.SetValue(uri.Key, value, valueKind); return true; } catch (Exception ex) { _logger.Error($"Failed to write {value} to {uri}.", ex); return false; } } public bool? Delete(RegistryUri uri) { try { using RegistryKey? key = OpenBaseKey(uri.HiveKey).OpenSubKey(uri.Path, RegistryKeyPermissionCheck.ReadWriteSubTree); if (key == null) { return null; } key.DeleteValue(uri.Key, throwOnMissingValue: false); return true; } catch (Exception ex) { _logger.Error($"Failed deleting registry {uri}.", ex); return false; } } public Dictionary ReadAll(RegistryUri uri) { var result = new Dictionary(); try { using RegistryKey? key = OpenBaseKey(uri.HiveKey).OpenSubKey(uri.Path); if (key != null) { foreach (string valueName in key.GetValueNames()) { object? value = key.GetValue(valueName); result[valueName] = ConvertRegistryValueToString(value); } } else { _logger.Error($"Failed to open registry path before reading all values: {uri}."); } } catch (Exception ex) { _logger.Error($"Failed to read all registry values from {uri}.", ex); } return result; } private static string ConvertRegistryValueToString(object? value) { return value switch { null => "NULL", string str => str, int i => i.ToString(), long l => l.ToString(), byte[] bytes => BitConverter.ToString(bytes), // Converts bytes to a hex string: "AA-BB-CC" _ => value.ToString() ?? "UNKNOWN" }; } }