Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,629 changes: 1,341 additions & 1,288 deletions Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Large diffs are not rendered by default.

42 changes: 30 additions & 12 deletions Source/NETworkManager.Localization/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@
<value>Apply</value>
</data>
<data name="ApplyWindowsKeyCombinationsLikeAltTab" xml:space="preserve">
<value>Apply Windows key combinations (e.g. ALT+TAB):</value>
<value>Apply Windows key combinations (e.g., ALT+TAB):</value>
</data>
<data name="Auth" xml:space="preserve">
<value>Auth</value>
Expand Down Expand Up @@ -1367,16 +1367,16 @@ Profile files are not affected!</value>
<value>Enter a valid IPv4 address!</value>
</data>
<data name="EnterValidMACAddress" xml:space="preserve">
<value>Enter a valid MAC address (like 00:F1:23:AB:F2:35)!</value>
<value>Enter a valid MAC address (e.g., 00:F1:23:AB:F2:35)!</value>
</data>
<data name="EnterValidPort" xml:space="preserve">
<value>Enter a valid port (1 - 65535)!</value>
</data>
<data name="EnterValidSubnetmask" xml:space="preserve">
<value>Enter a valid subnetmask (like 255.255.255.0)!</value>
<value>Enter a valid subnetmask (e.g., 255.255.255.0)!</value>
</data>
<data name="EnterValidSubnetmaskOrCIDR" xml:space="preserve">
<value>Enter a valid subnetmask or CIDR (like 255.255.255.0 or /24)!</value>
<value>Enter a valid subnetmask or CIDR (e.g., 255.255.255.0 or /24)!</value>
</data>
<data name="FieldCannotBeEmpty" xml:space="preserve">
<value>Field cannot be empty!</value>
Expand Down Expand Up @@ -1448,10 +1448,10 @@ Profile files are not affected!</value>
<value>Enter a valid port and/or port range (1 - 65535)!</value>
</data>
<data name="EnterValidSubnet" xml:space="preserve">
<value>Enter a valid subnet (like 192.168.178.133/26)!</value>
<value>Enter a valid subnet (e.g., 192.168.178.133/26)!</value>
</data>
<data name="EnterValidWebsiteUri" xml:space="preserve">
<value>Enter a valid website (like https://example.com/index.html)</value>
<value>Enter a valid website (e.g., https://example.com/index.html)</value>
</data>
<data name="Green" xml:space="preserve">
<value>Green</value>
Expand Down Expand Up @@ -1742,7 +1742,7 @@ Profile files are not affected!</value>
<value>Add a tab to query whois...</value>
</data>
<data name="EnterValidDomain" xml:space="preserve">
<value>Enter a valid domain (like "example.com")!</value>
<value>Enter a valid domain (e.g., "example.com")!</value>
</data>
<data name="WhoisServerNotFoundForTheDomain" xml:space="preserve">
<value>Whois server not found for the domain: "{0}"</value>
Expand Down Expand Up @@ -2032,7 +2032,7 @@ is disabled!</value>
<value>Use custom IPv4 address API</value>
</data>
<data name="HelpMessage_CustomPublicIPv4AddressAPI" xml:space="preserve">
<value>URL to a web service that can be reached via http or https and returns an IPv4 address like "xx.xx.xx.xx" as response.</value>
<value>URL to a web service that can be reached via http or https and returns an IPv4 address e.g., "xx.xx.xx.xx" as response.</value>
</data>
<data name="CouldNotParsePublicIPAddressFromXXXMessage" xml:space="preserve">
<value>Could not parse public ip address from "{0}"! Try another service or use the default... </value>
Expand Down Expand Up @@ -3192,7 +3192,7 @@ If the option is disabled again, the values are no longer modified. However, the
<value>SNTP server(s)</value>
</data>
<data name="HelpMessage_CustomPublicIPv6AddressAPI" xml:space="preserve">
<value>URL to a web service that can be reached via http or https and returns an IPv6 address like "xxxx:xx:xxx::xx" as response.</value>
<value>URL to a web service that can be reached via http or https and returns an IPv6 address e.g., "xxxx:xx:xxx::xx" as response.</value>
</data>
<data name="IPEndPoint" xml:space="preserve">
<value>IP endpoint</value>
Expand Down Expand Up @@ -3234,7 +3234,7 @@ If the option is disabled again, the values are no longer modified. However, the
<value>An SNTP server with this name already exists!</value>
</data>
<data name="EnterValidHostnameOrIPAddress" xml:space="preserve">
<value>Enter a valid hostname (like "server-01" or "example.com") or a valid IP address (like 192.168.178.1)!</value>
<value>Enter a valid hostname (e.g., "server-01" or "example.com") or a valid IP address (e.g., 192.168.178.1)!</value>
</data>
<data name="Servers" xml:space="preserve">
<value>Server(s)</value>
Expand Down Expand Up @@ -3528,10 +3528,10 @@ Changes to this value will take effect after the application is restarted. Wheth
<value>The settings on this page contain errors. Correct them to be able to save.</value>
</data>
<data name="HelpMessage_PuTTYHostkey" xml:space="preserve">
<value>SSH hostkey to use for the connection (like "71:b8:f2:6e..."). Only available if the mode is "SSH".</value>
<value>SSH hostkey to use for the connection (e.g., "71:b8:f2:6e..."). Only available if the mode is "SSH".</value>
</data>
<data name="HelpMessage_PuTTYPrivateKeyFile" xml:space="preserve">
<value>Full path to the private key file (like "C:\Users\BornToBeRoot\SSH\private_key.ppk"). Only available if the mode is "SSH".</value>
<value>Full path to the private key file (e.g., "C:\Users\BornToBeRoot\SSH\private_key.ppk"). Only available if the mode is "SSH".</value>
</data>
<data name="HelpMessage_PuTTYUsername" xml:space="preserve">
<value>Username that will be passed into the PuTTY session. Only available if the mode is "SSH", "Telnet" or "Rlogin".</value>
Expand Down Expand Up @@ -3938,4 +3938,22 @@ Right-click for more options.</value>

{0} {1} {2}</value>
</data>
<data name="ExampleHostsFileEntryComment" xml:space="preserve">
<value>Primary DNS server</value>
</data>
<data name="EnterValidHostsFileEntryHostname" xml:space="preserve">
<value>Enter a valid hostname (e.g., "server-01" or "example.com")! Multiple hostnames can be separated with a space.</value>
</data>
<data name="HostsFileReadErrorMessage" xml:space="preserve">
<value>The "hosts" file could not be read! See log file for more details.</value>
</data>
<data name="HostsFileWriteErrorMessage" xml:space="preserve">
<value>The "hosts" file could not be written to. See log file for more details.</value>
</data>
<data name="HostsFileEntryNotFoundMessage" xml:space="preserve">
<value>The entry was not found in the "hosts" file! Maybe the file has been modified.</value>
</data>
<data name="HostsFileBackupErrorMessage" xml:space="preserve">
<value>A backup of the "hosts" file could not be created! See log file for more details.</value>
</data>
</root>
208 changes: 200 additions & 8 deletions Source/NETworkManager.Models/HostsFileEditor/HostsFileEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,18 @@ private static HostsFileEntryModifyResult EnableEntry(HostsFileEntry entry)
return HostsFileEntryModifyResult.BackupError;
}

// Replace the entry in the hosts file
var hostsFileLines = File.ReadAllLines(HostsFilePath).ToList();
// Enable the entry in the hosts file
List<string> hostsFileLines;

try
{
hostsFileLines = [.. File.ReadAllLines(HostsFilePath)];
}
catch (Exception ex)
{
Log.Error($"EnableEntry - Failed to read hosts file: {HostsFilePath}", ex);
return HostsFileEntryModifyResult.ReadError;
}

bool entryFound = false;

Expand All @@ -199,7 +209,15 @@ private static HostsFileEntryModifyResult EnableEntry(HostsFileEntry entry)
{
entryFound = true;

hostsFileLines[i] = entry.Line.TrimStart('#', ' ');
hostsFileLines.RemoveAt(i);
hostsFileLines.Insert(i, CreateEntryLine(new HostsFileEntry
{
IsEnabled = true,
IPAddress = entry.IPAddress,
Hostname = entry.Hostname,
Comment = entry.Comment,
Line = entry.Line
}));

break;
}
Expand Down Expand Up @@ -250,8 +268,18 @@ private static HostsFileEntryModifyResult DisableEntry(HostsFileEntry entry)
return HostsFileEntryModifyResult.BackupError;
}

// Replace the entry in the hosts file
var hostsFileLines = File.ReadAllLines(HostsFilePath).ToList();
// Disable the entry in the hosts file
List<string> hostsFileLines;

try
{
hostsFileLines = [.. File.ReadAllLines(HostsFilePath)];
}
catch (Exception ex)
{
Log.Error($"DisableEntry - Failed to read hosts file: {HostsFilePath}", ex);
return HostsFileEntryModifyResult.ReadError;
}

bool entryFound = false;

Expand All @@ -261,7 +289,15 @@ private static HostsFileEntryModifyResult DisableEntry(HostsFileEntry entry)
{
entryFound = true;

hostsFileLines[i] = "# " + entry.Line;
hostsFileLines.RemoveAt(i);
hostsFileLines.Insert(i, CreateEntryLine(new HostsFileEntry
{
IsEnabled = false,
IPAddress = entry.IPAddress,
Hostname = entry.Hostname,
Comment = entry.Comment,
Line = entry.Line
}));

break;
}
Expand All @@ -287,6 +323,133 @@ private static HostsFileEntryModifyResult DisableEntry(HostsFileEntry entry)
return HostsFileEntryModifyResult.Success;
}

/// <summary>
/// Add a hosts file entry asynchronously.
/// </summary>
/// <param name="entry">Entry to add.</param>
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was added successfully, otherwise an error result.</returns>"/>
public static Task<HostsFileEntryModifyResult> AddEntryAsync(HostsFileEntry entry)
{
return Task.Run(() => AddEntry(entry));
}

/// <summary>
/// Add a hosts file entry.
/// </summary>
/// <param name="entry">Entry to add.</param>
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was added successfully, otherwise an error result.</returns>"/>
private static HostsFileEntryModifyResult AddEntry(HostsFileEntry entry)
{
// Create a backup of the hosts file before making changes
if (CreateBackup() == false)
{
Log.Error("AddEntry - Failed to create backup before adding entry.");
return HostsFileEntryModifyResult.BackupError;
}

// Add the entry to the hosts file
List<string> hostsFileLines;

try
{
hostsFileLines = [.. File.ReadAllLines(HostsFilePath)];
}
catch (Exception ex)
{
Log.Error($"AddEntry - Failed to read hosts file: {HostsFilePath}", ex);
return HostsFileEntryModifyResult.ReadError;
}

hostsFileLines.Add(CreateEntryLine(entry));

try
{
Log.Debug($"AddEntry - Writing changes to hosts file: {HostsFilePath}");
File.WriteAllLines(HostsFilePath, hostsFileLines);
}
catch (Exception ex)
{
Log.Error($"AddEntry - Failed to write changes to hosts file: {HostsFilePath}", ex);
return HostsFileEntryModifyResult.WriteError;
}

return HostsFileEntryModifyResult.Success;
}

/// <summary>
/// Edit a hosts file entry asynchronously.
/// </summary>
/// <param name="entry">Entry to edit.</param>
/// <param name="newEntry">New entry to replace the old one.</param>
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was edited successfully, otherwise an error result.</returns>"/>
public static Task<HostsFileEntryModifyResult> EditEntryAsync(HostsFileEntry entry, HostsFileEntry newEntry)
{
return Task.Run(() => EditEntry(entry, newEntry));
}

/// <summary>
/// Edit a hosts file entry.
/// </summary>
/// <param name="entry">Entry to edit.</param>
/// <param name="newEntry">New entry to replace the old one.</param>
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was edited successfully, otherwise an error result.</returns>"/>
private static HostsFileEntryModifyResult EditEntry(HostsFileEntry entry, HostsFileEntry newEntry)
{
// Create a backup of the hosts file before making changes
if (CreateBackup() == false)
{
Log.Error("EditEntry - Failed to create backup before editing entry.");
return HostsFileEntryModifyResult.BackupError;
}

// Replace the entry from the hosts file
List<string> hostsFileLines;

try
{
hostsFileLines = [.. File.ReadAllLines(HostsFilePath)];
}
catch (Exception ex)
{
Log.Error($"EditEntry - Failed to read hosts file: {HostsFilePath}", ex);
return HostsFileEntryModifyResult.ReadError;
}

bool entryFound = false;

for (var i = 0; i < hostsFileLines.Count; i++)
{
if (hostsFileLines[i] == entry.Line)
{
entryFound = true;

hostsFileLines.RemoveAt(i);
hostsFileLines.Insert(i, CreateEntryLine(newEntry));

break;
}
}

if (!entryFound)
{
Log.Warn($"EditEntry - Entry not found in hosts file: {entry.Line}");
return HostsFileEntryModifyResult.NotFound;
}

try
{
Log.Debug($"EditEntry - Writing changes to hosts file: {HostsFilePath}");
File.WriteAllLines(HostsFilePath, hostsFileLines);
}
catch (Exception ex)
{
Log.Error($"EditEntry - Failed to write changes to hosts file: {HostsFilePath}", ex);
return HostsFileEntryModifyResult.WriteError;
}

return HostsFileEntryModifyResult.Success;
}

/// <summary>
/// Delete a hosts file entry asynchronously.
/// </summary>
Expand All @@ -312,7 +475,17 @@ private static HostsFileEntryModifyResult DeleteEntry(HostsFileEntry entry)
}

// Remove the entry from the hosts file
var hostsFileLines = File.ReadAllLines(HostsFilePath).ToList();
List<string> hostsFileLines;

try
{
hostsFileLines = [.. File.ReadAllLines(HostsFilePath)];
}
catch (Exception ex)
{
Log.Error($"DeleteEntry - Failed to read hosts file: {HostsFilePath}", ex);
return HostsFileEntryModifyResult.ReadError;
}

bool entryFound = false;

Expand Down Expand Up @@ -344,10 +517,29 @@ private static HostsFileEntryModifyResult DeleteEntry(HostsFileEntry entry)
Log.Error($"DeleteEntry - Failed to write changes to hosts file: {HostsFilePath}", ex);
return HostsFileEntryModifyResult.WriteError;
}
OnHostsFileChanged();

return HostsFileEntryModifyResult.Success;
}

/// <summary>
/// Create a line for the hosts file entry.
/// </summary>
/// <param name="entry">Entry to create the line for.</param>
/// <returns>Line for the hosts file entry.</returns>
private static string CreateEntryLine(HostsFileEntry entry)
{
var line = entry.IsEnabled ? "" : "# ";

line += $"{entry.IPAddress} {entry.Hostname}";

if (!string.IsNullOrWhiteSpace(entry.Comment))
{
line += $" # {entry.Comment}";
}

return line.Trim();
}

/// <summary>
/// Create a "daily" backup of the hosts file (before making a change).
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public enum HostsFileEntryModifyResult
/// </summary>
NotFound,

/// <summary>
/// An error occurred while reading the hosts file.
/// </summary>
ReadError,

/// <summary>
/// An error occurred while writing to the hosts file.
/// </summary>
Expand Down
Loading