Skip to content

Commit 0ea864c

Browse files
committed
Feature: Add translation
1 parent 7cf2ffd commit 0ea864c

7 files changed

Lines changed: 350 additions & 49 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows.Data;
4+
using NETworkManager.Localization;
5+
using NETworkManager.Models.Firewall;
6+
7+
namespace NETworkManager.Converters;
8+
9+
/// <summary>
10+
/// Convert <see cref="FirewallInterfaceType" /> to translated <see cref="string" /> or vice versa.
11+
/// </summary>
12+
public sealed class FirewallInterfaceTypeToStringConverter : IValueConverter
13+
{
14+
/// <summary>
15+
/// Convert <see cref="FirewallInterfaceType" /> to translated <see cref="string" />.
16+
/// </summary>
17+
/// <param name="value">Object from type <see cref="FirewallInterfaceType" />.</param>
18+
/// <param name="targetType"></param>
19+
/// <param name="parameter"></param>
20+
/// <param name="culture"></param>
21+
/// <returns>Translated <see cref="FirewallInterfaceType" />.</returns>
22+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
23+
{
24+
return value is not FirewallInterfaceType interfaceType
25+
? "-/-"
26+
: ResourceTranslator.Translate(ResourceIdentifier.FirewallInterfaceType, interfaceType);
27+
}
28+
29+
/// <summary>
30+
/// !!! Method not implemented !!!
31+
/// </summary>
32+
/// <param name="value"></param>
33+
/// <param name="targetType"></param>
34+
/// <param name="parameter"></param>
35+
/// <param name="culture"></param>
36+
/// <returns></returns>
37+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
38+
{
39+
throw new NotImplementedException();
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows.Data;
4+
using NETworkManager.Localization;
5+
using NETworkManager.Models.Firewall;
6+
7+
namespace NETworkManager.Converters;
8+
9+
/// <summary>
10+
/// Convert <see cref="FirewallProtocol" /> to translated <see cref="string" /> or vice versa.
11+
/// </summary>
12+
public sealed class FirewallProtocolToStringConverter : IValueConverter
13+
{
14+
/// <summary>
15+
/// Convert <see cref="FirewallProtocol" /> to translated <see cref="string" />.
16+
/// </summary>
17+
/// <param name="value">Object from type <see cref="FirewallProtocol" />.</param>
18+
/// <param name="targetType"></param>
19+
/// <param name="parameter"></param>
20+
/// <param name="culture"></param>
21+
/// <returns>Translated <see cref="FirewallProtocol" />.</returns>
22+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
23+
{
24+
return value is not FirewallProtocol protocol
25+
? "-/-"
26+
: ResourceTranslator.Translate(ResourceIdentifier.FirewallProtocol, protocol);
27+
}
28+
29+
/// <summary>
30+
/// !!! Method not implemented !!!
31+
/// </summary>
32+
/// <param name="value"></param>
33+
/// <param name="targetType"></param>
34+
/// <param name="parameter"></param>
35+
/// <param name="culture"></param>
36+
/// <returns></returns>
37+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
38+
{
39+
throw new NotImplementedException();
40+
}
41+
}

Source/NETworkManager.Localization/ResourceIdentifier.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,7 @@ public enum ResourceIdentifier
2222
TcpState,
2323
Theme,
2424
TimeUnit,
25-
WiFiConnectionStatus
25+
WiFiConnectionStatus,
26+
FirewallProtocol,
27+
FirewallInterfaceType
2628
}

Source/NETworkManager.Localization/Resources/Strings.resx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4046,4 +4046,61 @@ You can copy your profile files from “{0}” to “{1}” to migrate your exis
40464046
<data name="FailedToLoadFirewallRulesMessage" xml:space="preserve">
40474047
<value>Failed to load firewall rules. {0}</value>
40484048
</data>
4049+
<data name="FirewallProtocol_Any" xml:space="preserve">
4050+
<value>Any</value>
4051+
</data>
4052+
<data name="FirewallProtocol_TCP" xml:space="preserve">
4053+
<value>TCP</value>
4054+
</data>
4055+
<data name="FirewallProtocol_UDP" xml:space="preserve">
4056+
<value>UDP</value>
4057+
</data>
4058+
<data name="FirewallProtocol_ICMPv4" xml:space="preserve">
4059+
<value>ICMPv4</value>
4060+
</data>
4061+
<data name="FirewallProtocol_ICMPv6" xml:space="preserve">
4062+
<value>ICMPv6</value>
4063+
</data>
4064+
<data name="FirewallProtocol_HOPOPT" xml:space="preserve">
4065+
<value>HOPOPT</value>
4066+
</data>
4067+
<data name="FirewallProtocol_GRE" xml:space="preserve">
4068+
<value>GRE</value>
4069+
</data>
4070+
<data name="FirewallProtocol_IPv6" xml:space="preserve">
4071+
<value>IPv6</value>
4072+
</data>
4073+
<data name="FirewallProtocol_IPv6_Route" xml:space="preserve">
4074+
<value>IPv6-Route</value>
4075+
</data>
4076+
<data name="FirewallProtocol_IPv6_Frag" xml:space="preserve">
4077+
<value>IPv6-Frag</value>
4078+
</data>
4079+
<data name="FirewallProtocol_IPv6_NoNxt" xml:space="preserve">
4080+
<value>IPv6-NoNxt</value>
4081+
</data>
4082+
<data name="FirewallProtocol_IPv6_Opts" xml:space="preserve">
4083+
<value>IPv6-Opts</value>
4084+
</data>
4085+
<data name="FirewallProtocol_VRRP" xml:space="preserve">
4086+
<value>VRRP</value>
4087+
</data>
4088+
<data name="FirewallProtocol_PGM" xml:space="preserve">
4089+
<value>PGM</value>
4090+
</data>
4091+
<data name="FirewallProtocol_L2TP" xml:space="preserve">
4092+
<value>L2TP</value>
4093+
</data>
4094+
<data name="FirewallInterfaceType_Any" xml:space="preserve">
4095+
<value>Any</value>
4096+
</data>
4097+
<data name="FirewallInterfaceType_Wired" xml:space="preserve">
4098+
<value>Wired</value>
4099+
</data>
4100+
<data name="FirewallInterfaceType_Wireless" xml:space="preserve">
4101+
<value>Wireless</value>
4102+
</data>
4103+
<data name="FirewallInterfaceType_RemoteAccess" xml:space="preserve">
4104+
<value>Remote access</value>
4105+
</data>
40494106
</root>

Source/NETworkManager.Models/Firewall/Firewall.cs

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ public static async Task<List<FirewallRule>> GetRulesAsync()
4343
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
4444
Import-Module NetSecurity -ErrorAction Stop
4545
Get-NetFirewallRule -DisplayName '{RuleIdentifier}*' | ForEach-Object {{
46-
$rule = $_
47-
$portFilter = $rule | Get-NetFirewallPortFilter
48-
$appFilter = $rule | Get-NetFirewallApplicationFilter
46+
$rule = $_
47+
$portFilter = $rule | Get-NetFirewallPortFilter
48+
$addressFilter = $rule | Get-NetFirewallAddressFilter
49+
$appFilter = $rule | Get-NetFirewallApplicationFilter
50+
$ifTypeFilter = $rule | Get-NetFirewallInterfaceTypeFilter
4951
[PSCustomObject]@{{
5052
Id = $rule.ID
5153
DisplayName = $rule.DisplayName
@@ -54,10 +56,12 @@ public static async Task<List<FirewallRule>> GetRulesAsync()
5456
Direction = [string]$rule.Direction
5557
Action = [string]$rule.Action
5658
Protocol = $portFilter.Protocol
57-
LocalPort = ($portFilter.LocalPort -join ',')
58-
RemotePort = ($portFilter.RemotePort -join ',')
59+
LocalPort = ($portFilter.LocalPort -join ',')
60+
RemotePort = ($portFilter.RemotePort -join ',')
61+
LocalAddress = ($addressFilter.LocalAddress -join ',')
62+
RemoteAddress = ($addressFilter.RemoteAddress -join ',')
5963
Profile = [string]$rule.Profile
60-
InterfaceType = [string]$rule.InterfaceType
64+
InterfaceType = [string]$ifTypeFilter.InterfaceType
6165
Program = $appFilter.Program
6266
}}
6367
}}");
@@ -74,19 +78,25 @@ public static async Task<List<FirewallRule>> GetRulesAsync()
7478
{
7579
try
7680
{
81+
var displayName = result.Properties["DisplayName"]?.Value?.ToString() ?? string.Empty;
82+
7783
var rule = new FirewallRule
7884
{
79-
Id = result.Properties["Id"]?.Value?.ToString() ?? string.Empty,
80-
IsEnabled = result.Properties["Enabled"]?.Value as bool? == true,
81-
Name = result.Properties["DisplayName"]?.Value?.ToString() ?? string.Empty,
82-
Description = result.Properties["Description"]?.Value?.ToString() ?? string.Empty,
83-
Direction = ParseDirection(result.Properties["Direction"]?.Value?.ToString()),
84-
Action = ParseAction(result.Properties["Action"]?.Value?.ToString()),
85-
Protocol = ParseProtocol(result.Properties["Protocol"]?.Value?.ToString()),
86-
LocalPorts = ParsePorts(result.Properties["LocalPort"]?.Value?.ToString()),
87-
RemotePorts = ParsePorts(result.Properties["RemotePort"]?.Value?.ToString()),
85+
Id = result.Properties["Id"]?.Value?.ToString() ?? string.Empty,
86+
IsEnabled = result.Properties["Enabled"]?.Value as bool? == true,
87+
Name = displayName.StartsWith(RuleIdentifier, StringComparison.Ordinal)
88+
? displayName[RuleIdentifier.Length..]
89+
: displayName,
90+
Description = result.Properties["Description"]?.Value?.ToString() ?? string.Empty,
91+
Direction = ParseDirection(result.Properties["Direction"]?.Value?.ToString()),
92+
Action = ParseAction(result.Properties["Action"]?.Value?.ToString()),
93+
Protocol = ParseProtocol(result.Properties["Protocol"]?.Value?.ToString()),
94+
LocalPorts = ParsePorts(result.Properties["LocalPort"]?.Value?.ToString()),
95+
RemotePorts = ParsePorts(result.Properties["RemotePort"]?.Value?.ToString()),
96+
LocalAddresses = ParseAddresses(result.Properties["LocalAddress"]?.Value?.ToString()),
97+
RemoteAddresses = ParseAddresses(result.Properties["RemoteAddress"]?.Value?.ToString()),
8898
NetworkProfiles = ParseProfile(result.Properties["Profile"]?.Value?.ToString()),
89-
InterfaceType = ParseInterfaceType(result.Properties["InterfaceType"]?.Value?.ToString()),
99+
InterfaceType = ParseInterfaceType(result.Properties["InterfaceType"]?.Value?.ToString()),
90100
};
91101

92102
var program = result.Properties["Program"]?.Value as string;
@@ -205,6 +215,18 @@ private static bool[] ParseProfile(string value)
205215
return profiles;
206216
}
207217

218+
/// <summary>
219+
/// Parses a comma-separated address string (e.g. <c>"192.168.1.0/24,LocalSubnet"</c>) to a list of
220+
/// address strings. Returns an empty list for <c>Any</c> or blank input.
221+
/// </summary>
222+
private static List<string> ParseAddresses(string value)
223+
{
224+
if (string.IsNullOrWhiteSpace(value) || value.Equals("Any", StringComparison.OrdinalIgnoreCase))
225+
return [];
226+
227+
return [.. value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
228+
}
229+
208230
/// <summary>Parses a PowerShell interface-type string to <see cref="FirewallInterfaceType"/>.</summary>
209231
private static FirewallInterfaceType ParseInterfaceType(string value)
210232
{

Source/NETworkManager.Models/Firewall/FirewallRule.cs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,42 @@ public class FirewallRule
4949
/// </summary>
5050
public FirewallRuleProgram Program { get; set; }
5151

52+
/// <summary>
53+
/// Local IP addresses or address specifiers (e.g. "192.168.1.0/24", "LocalSubnet").
54+
/// An empty list means "Any".
55+
/// </summary>
56+
public List<string> LocalAddresses
57+
{
58+
get;
59+
set
60+
{
61+
if (value is null)
62+
{
63+
field = [];
64+
return;
65+
}
66+
field = value;
67+
}
68+
} = [];
69+
70+
/// <summary>
71+
/// Remote IP addresses or address specifiers (e.g. "10.0.0.0/8", "Internet").
72+
/// An empty list means "Any".
73+
/// </summary>
74+
public List<string> RemoteAddresses
75+
{
76+
get;
77+
set
78+
{
79+
if (value is null)
80+
{
81+
field = [];
82+
return;
83+
}
84+
field = value;
85+
}
86+
} = [];
87+
5288
/// <summary>
5389
/// Defines the local ports associated with the firewall rule.
5490
/// </summary>
@@ -121,11 +157,20 @@ public FirewallRule()
121157

122158
#region Display properties
123159

124-
/// <summary>Local ports as a human-readable string (e.g. "80; 443; 8080-8090").</summary>
125-
public string LocalPortsDisplay => PortsToString(LocalPorts);
160+
/// <summary>Program path, or <c>null</c> when no program restriction is set.</summary>
161+
public string ProgramDisplay => Program?.ToString();
162+
163+
/// <summary>Local addresses as a human-readable string (e.g. "192.168.1.0/24; LocalSubnet"). Returns <c>null</c> when unrestricted.</summary>
164+
public string LocalAddressesDisplay => LocalAddresses.Count == 0 ? null : string.Join("; ", LocalAddresses);
165+
166+
/// <summary>Remote addresses as a human-readable string (e.g. "10.0.0.0/8"). Returns <c>null</c> when unrestricted.</summary>
167+
public string RemoteAddressesDisplay => RemoteAddresses.Count == 0 ? null : string.Join("; ", RemoteAddresses);
168+
169+
/// <summary>Local ports as a human-readable string (e.g. "80; 443; 8080-8090"). Returns <c>null</c> when unrestricted.</summary>
170+
public string LocalPortsDisplay => LocalPorts.Count == 0 ? null : PortsToString(LocalPorts);
126171

127-
/// <summary>Remote ports as a human-readable string (e.g. "80; 443").</summary>
128-
public string RemotePortsDisplay => PortsToString(RemotePorts);
172+
/// <summary>Remote ports as a human-readable string (e.g. "80; 443"). Returns <c>null</c> when unrestricted.</summary>
173+
public string RemotePortsDisplay => RemotePorts.Count == 0 ? null : PortsToString(RemotePorts);
129174

130175
/// <summary>
131176
/// Network profiles (Domain / Private / Public) as a comma-separated string.

0 commit comments

Comments
 (0)