/*
* 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 System.Text.RegularExpressions;
using Microsoft.Win32;
using ProtonVPN.Client.Settings.Contracts.Models;
namespace ProtonVPN.Client.Settings.Contracts.Helpers;
public static class DefaultAppsHelper
{
private static readonly Regex _filenameRegex = new(@"^\s*""?([^""]*)""?");
public static IEnumerable GetBrowserApps()
{
return GetInternetExplorer()
.Union(GetMicrosoftEdge())
.Union(GetClientApps(Registry.LocalMachine, @"SOFTWARE\Clients\StartMenuInternet"))
.Union(GetClientApps(Registry.LocalMachine, @"SOFTWARE\WOW6432Node\Clients\StartMenuInternet"))
.Union(GetClientApps(Registry.CurrentUser, @"SOFTWARE\Clients\StartMenuInternet"))
.Distinct();
}
private static IEnumerable GetMicrosoftEdge()
{
const string microsoftEdgeFolder = @"%WinDir%\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe";
string path = Environment.ExpandEnvironmentVariables(Path.Combine(microsoftEdgeFolder, "GetMicrosoftEdge.exe"));
if (!IsValidPath(path))
{
yield break;
}
SplitTunnelingApp app = new(path, false);
string altPath1 = Environment.ExpandEnvironmentVariables(Path.Combine(microsoftEdgeFolder, "MicrosoftEdgeCP.exe"));
if (IsValidPath(altPath1))
{
app.AlternateAppFilePaths.Add(altPath1);
}
string altPath2 = Environment.ExpandEnvironmentVariables(@"%WinDir%\System32\MicrosoftEdgeCP.exe");
if (IsValidPath(altPath2))
{
app.AlternateAppFilePaths.Add(altPath2);
}
yield return app;
}
private static IEnumerable GetInternetExplorer()
{
string? path = GetMachineAppPath(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\IEXPLORE.EXE");
if (!IsValidPath(path))
{
yield break;
}
SplitTunnelingApp app = new(path, false);
string? x86Path = GetMachineAppPath(@"SOFTWARE\Microsoft\Internet Explorer\Main", "x86AppPath");
if (IsValidPath(x86Path))
{
app.AlternateAppFilePaths.Add(x86Path);
}
yield return app;
}
private static IEnumerable GetClientApps(RegistryKey? parentKey, string subKeyName)
{
return FailSafeRegistryAccess(() =>
{
using (RegistryKey? subKey = parentKey?.OpenSubKey(subKeyName))
{
return GetClientApps(subKey);
}
}, Enumerable.Empty());
}
private static List GetClientApps(RegistryKey? parentKey)
{
List list = [];
if (parentKey == null)
{
return list;
}
string[] subKeys = parentKey.GetSubKeyNames();
foreach (string subKey in subKeys)
{
SplitTunnelingApp? app = GetClientApp(parentKey, subKey);
if (app != null)
{
list.Add(app.Value);
}
}
return list;
}
private static SplitTunnelingApp? GetClientApp(RegistryKey parentKey, string subKeyName)
{
return FailSafeRegistryAccess(() =>
{
using (RegistryKey? clientKey = parentKey?.OpenSubKey(subKeyName))
{
return GetClientApp(clientKey);
}
}, null);
}
private static SplitTunnelingApp? GetClientApp(RegistryKey? key)
{
if (key == null)
{
return null;
}
string path = GetClientAppPath(key);
return IsValidPath(path)
? new SplitTunnelingApp(path, false)
: null;
}
private static string GetClientAppPath(RegistryKey? key)
{
using (RegistryKey? commandKey = key?.OpenSubKey(@"shell\open\command"))
{
string? command = commandKey?.GetValue(null)?.ToString();
string filename = ParseFilename(command);
return Environment.ExpandEnvironmentVariables(filename);
}
}
private static string ParseFilename(string? command)
{
if (string.IsNullOrEmpty(command))
{
return string.Empty;
}
Match match = _filenameRegex.Match(command);
return match.Success ? match.Groups[1].Value.Trim() : string.Empty;
}
private static bool IsValidPath(string? path)
{
return !string.IsNullOrEmpty(path) && File.Exists(path);
}
private static string GetMachineAppPath(string subKeyName, string? valueName = null)
{
string? path = SafeGetRegistryValue(Registry.LocalMachine, subKeyName, valueName);
return !string.IsNullOrEmpty(path) && File.Exists(path)
? path
: string.Empty;
}
private static string? SafeGetRegistryValue(RegistryKey key, string subKeyName, string? valueName = null)
{
return FailSafeRegistryAccess(() => GetRegistryValue(key, subKeyName, valueName), null);
}
private static string? GetRegistryValue(RegistryKey key, string subKeyName, string? valueName = null)
{
using (RegistryKey? subKey = key.OpenSubKey(subKeyName))
{
return subKey?.GetValue(valueName)?.ToString();
}
}
private static T FailSafeRegistryAccess(Func function, T defaultResult)
{
try
{
return function();
}
catch (Exception) { }
return defaultResult;
}
}