diff --git a/Example/App.xaml b/Example/App.xaml
index 54f1c63..3db0e1c 100644
--- a/Example/App.xaml
+++ b/Example/App.xaml
@@ -23,6 +23,8 @@
x:Key="BoolToColorConverter"
FalseBrush="Gray"
TrueBrush="White" />
+
diff --git a/Example/Converters/BooleanInverseConverter.cs b/Example/Converters/BooleanInverseConverter.cs
new file mode 100644
index 0000000..19e260b
--- /dev/null
+++ b/Example/Converters/BooleanInverseConverter.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) NetProtect, LLC. All Rights Reserved.
+//
+
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace Example.Converters
+{
+ ///
+ /// Convert a boolean to boolean inverted.
+ ///
+ public class BooleanInverseConverter : IValueConverter
+ {
+ ///
+ /// Convetrs boolean to inverse.
+ ///
+ /// bool value input from the binding.
+ /// the return type.
+ /// any additional info needed.
+ /// users culture.
+ /// the converted value.
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return !(bool)value;
+ }
+
+ ///
+ /// converts the value back to a orignal bool alue.
+ ///
+ /// value from the binding.
+ /// type to convert to.
+ /// any additional info needed.
+ /// Users Culture.
+ /// the converted value.
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Example/Example.csproj b/Example/Example.csproj
index c80a463..9a990ef 100644
--- a/Example/Example.csproj
+++ b/Example/Example.csproj
@@ -72,6 +72,7 @@
MSBuild:Compile
Designer
+
@@ -93,6 +94,7 @@
+
LoginDialogView.xaml
@@ -112,6 +114,9 @@
DestinationsView.xaml
+
+ SplitTunnelingView.xaml
+
Designer
MSBuild:Compile
@@ -156,6 +161,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
@@ -201,6 +210,9 @@
runtime; build; native; contentfiles; analyzers
all
+
+ 1.1.39
+
2.5.1
@@ -231,7 +243,7 @@
1.3.7
- 2.2.29
+ 2.2.30
diff --git a/Example/ViewModel/MainViewModel.cs b/Example/ViewModel/MainViewModel.cs
index c857a17..b6e8986 100644
--- a/Example/ViewModel/MainViewModel.cs
+++ b/Example/ViewModel/MainViewModel.cs
@@ -130,6 +130,11 @@ private void InitViews()
View = new ConnectionSettingsView()
});
AvailableViews.Add(new ViewDefinition
+ {
+ DisplayName = "Split Tunneling",
+ View = new SplitTunnelingView()
+ });
+ AvailableViews.Add(new ViewDefinition
{
DisplayName = "Information",
View = new InformationView()
diff --git a/Example/ViewModel/SplitTunnelingViewModel.cs b/Example/ViewModel/SplitTunnelingViewModel.cs
new file mode 100644
index 0000000..f1fc1b7
--- /dev/null
+++ b/Example/ViewModel/SplitTunnelingViewModel.cs
@@ -0,0 +1,248 @@
+//
+// Copyright (c) NetProtect, LLC. All Rights Reserved.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Forms;
+using System.Windows.Input;
+using Example.Helpers;
+using VpnSDK.Common.Enums;
+using VpnSDK.Common.Settings;
+using VpnSDK.Interfaces;
+
+namespace Example.ViewModel
+{
+ ///
+ /// SplitTunnelingViewModel
+ ///
+ public class SplitTunnelingViewModel : BindableBase
+ {
+ private const string SupportedExtension = ".exe";
+ private const string UnsupportedApp = "Example";
+
+ private ICommand _addDomainCommand;
+ private ICommand _pickManuallyCommand;
+ private ICommand _deleteDomainCommand;
+ private ICommand _deleteApplicationCommand;
+ private ICommand _includeSubDomainsCommand;
+ private ICommand _excludeSubDomainsCommand;
+ private string _domainName;
+ private string _validationError;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SplitTunnelingViewModel()
+ {
+ SDK.SplitTunnelMode = SplitTunnelMode.RouteSelectedTrafficOutsideVpn;
+ SDK.SplitTunnelAllowedApps = new List();
+ SDK.SplitTunnelAllowedDomains = new List();
+ Applications = new ObservableCollection(SDK.SplitTunnelAllowedApps);
+ Domains = new ObservableCollection(SDK.SplitTunnelAllowedDomains);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether split tunneling feature is on or off.
+ ///
+ public bool IsSplitTunnelingOn
+ {
+ get => SDK.IsSplitTunnelEnabled;
+ set
+ {
+ SDK.IsSplitTunnelEnabled = value;
+ }
+ }
+
+ ///
+ /// Gets SDK Instance.
+ ///
+ public ISDK SDK => SDKFactory.GetInstance();
+
+ ///
+ /// Gets or sets Domain name.
+ ///
+ public string DomainName { get => _domainName; set => SetProperty(ref _domainName, value); }
+
+ ///
+ /// Gets or sets error message while adding new domain.
+ ///
+ public string ValidationError { get => _validationError; set => SetProperty(ref _validationError, value); }
+
+ ///
+ /// Gets or sets domain list.
+ ///
+ public ObservableCollection Domains { get; set; }
+
+ ///
+ /// Gets or sets application list.
+ ///
+ public ObservableCollection Applications { get; set; }
+
+ ///
+ /// Gets a value that represents the command to execute when the user add domains to split tunneling.
+ ///
+ public ICommand AddDomainCommand => _addDomainCommand ?? (_addDomainCommand = new RelayCommand(
+ execute: (p) => AddDomain()));
+
+ ///
+ /// Gets a value that represents the command to execute when the user add domains to split tunneling.
+ ///
+ public ICommand PickManuallyCommand => _pickManuallyCommand ?? (_pickManuallyCommand = new RelayCommand(
+ execute: (p) => PickManually()));
+
+ ///
+ /// Gets a value that represents the command to execute when the user delete domain from split tunneling.
+ ///
+ public ICommand DeleteDomainCommand => _deleteDomainCommand ?? (_deleteDomainCommand = new RelayCommand(
+ execute: (p) => DeleteDomain(p as SplitTunnelDomain)));
+
+ ///
+ /// Gets a value that represents the command to execute when the user delete domain from split tunneling.
+ ///
+ public ICommand DeleteApplicationCommand => _deleteApplicationCommand ?? (_deleteApplicationCommand = new RelayCommand(
+ execute: (p) => DeleteApplication(p as SplitTunnelApp)));
+
+ ///
+ /// Gets a value that represents the command to execute when the user include all subdomains for given domain in ST filter.
+ ///
+ public ICommand IncludeSubDomainsCommand => _includeSubDomainsCommand ?? (_includeSubDomainsCommand = new RelayCommand(
+ execute: (p) => UpdateIncludeSubDomains(true, p as SplitTunnelDomain)));
+
+ ///
+ /// Gets a value that represents the command to execute when the user exclude all subdomains for given domain in ST filter.
+ ///
+ public ICommand ExcludeSubDomainsCommand => _excludeSubDomainsCommand ?? (_excludeSubDomainsCommand = new RelayCommand(
+ execute: (p) => UpdateIncludeSubDomains(false, p as SplitTunnelDomain)));
+
+ ///
+ /// Deletes application from split tunneling filter
+ ///
+ /// Application that need to be removed from split tunneling.
+ public void DeleteApplication(SplitTunnelApp application)
+ {
+ try
+ {
+ Applications.Remove(application);
+ SDK.SplitTunnelAllowedApps.Remove(application);
+ }
+ catch (Exception)
+ {
+ // ignore.
+ }
+ }
+
+ ///
+ /// Deletes domain from split tunneling filter
+ ///
+ /// Domain that need to be removed from split tunneling.
+ public void DeleteDomain(SplitTunnelDomain domain)
+ {
+ try
+ {
+ Domains.Remove(domain);
+ SDK.SplitTunnelAllowedDomains.Remove(domain);
+ }
+ catch (Exception)
+ {
+ // ignore.
+ }
+ }
+
+ ///
+ /// Updates include subdomain property for domain added to split tunneling.
+ ///
+ /// bool value determining whether to include subdomains in split tunneling filter or not.
+ /// domain that needs to update includesubdomain property.
+ public void UpdateIncludeSubDomains(bool includeAllSubDomains, SplitTunnelDomain domain)
+ {
+ domain.IncludeAllSubdomains = includeAllSubDomains;
+ }
+
+ ///
+ /// Adds new domain to split tunneling filter.
+ ///
+ public void AddDomain()
+ {
+ try
+ {
+ ValidationError = string.Empty;
+ var domain = new SplitTunnelDomain(DomainName);
+ if (Domains.Any(d => d.DomainName.Equals(domain.DomainName, StringComparison.OrdinalIgnoreCase)))
+ {
+ ValidationError = "This domain already exists, please enter a different domain.";
+ return;
+ }
+
+ Domains.Add(domain);
+ SDK.SplitTunnelAllowedDomains.Add(domain);
+ DomainName = string.Empty;
+ }
+ catch (Exception ex)
+ {
+ ValidationError = ex.Message;
+ }
+ }
+
+ ///
+ /// Opens up file picker dialog from which user can select application.
+ ///
+ public void PickManually()
+ {
+ var filter = string.Join("|", SupportedExtension
+ .Select(_ => $"Executable (*{SupportedExtension})|*{SupportedExtension}"));
+
+ // Create OpenFileDialog
+ OpenFileDialog openFileDlg = new OpenFileDialog()
+ {
+ InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
+ DefaultExt = SupportedExtension,
+ Filter = filter,
+ CheckFileExists = true,
+ Title = "Add Applications",
+ CheckPathExists = true,
+ Multiselect = true,
+ };
+ openFileDlg.FileOk += OpenFileDlg_FileOk;
+
+ openFileDlg.ShowDialog();
+ }
+
+ private void OpenFileDlg_FileOk(object sender, CancelEventArgs e)
+ {
+ if (sender is OpenFileDialog openFileDlg)
+ {
+ foreach (var filePath in openFileDlg.FileNames)
+ {
+ var extension = Path.GetExtension(filePath);
+ var fileName = Path.GetFileNameWithoutExtension(filePath);
+ if (!SupportedExtension.Equals(extension, System.StringComparison.OrdinalIgnoreCase) ||
+ UnsupportedApp.Equals(fileName, System.StringComparison.OrdinalIgnoreCase))
+ {
+ var message = string.Format("Unsupported file chosen: {0}", fileName);
+ System.Windows.MessageBox.Show(message, "Invalid Application", MessageBoxButton.OK, MessageBoxImage.Error);
+ e.Cancel = true;
+ continue;
+ }
+
+ if (Applications.Any(app => app.Path == filePath))
+ {
+ var message = string.Format("Application already added: {0}", fileName);
+ System.Windows.MessageBox.Show(message, "Duplicate Application", MessageBoxButton.OK, MessageBoxImage.Error);
+ e.Cancel = true;
+ continue;
+ }
+
+ SplitTunnelApp splitTunnelApp = new SplitTunnelApp(fileName, filePath);
+ Applications.Add(splitTunnelApp);
+ SDK.SplitTunnelAllowedApps.Add(splitTunnelApp);
+ }
+ }
+ }
+ }
+}
diff --git a/Example/Views/SplitTunnelingView.xaml b/Example/Views/SplitTunnelingView.xaml
new file mode 100644
index 0000000..27464f7
--- /dev/null
+++ b/Example/Views/SplitTunnelingView.xaml
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/Views/SplitTunnelingView.xaml.cs b/Example/Views/SplitTunnelingView.xaml.cs
new file mode 100644
index 0000000..d1a23b5
--- /dev/null
+++ b/Example/Views/SplitTunnelingView.xaml.cs
@@ -0,0 +1,24 @@
+//
+// Copyright (c) NetProtect, LLC. All Rights Reserved.
+//
+
+using System.Windows.Controls;
+using Example.ViewModel;
+
+namespace Example.Views
+{
+ ///
+ /// Interaction logic for SplitTunnelingView.xaml
+ ///
+ public partial class SplitTunnelingView : UserControl
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SplitTunnelingView()
+ {
+ DataContext = new SplitTunnelingViewModel();
+ InitializeComponent();
+ }
+ }
+}