Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 2 additions & 2 deletions Source/GlobalAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

[assembly: AssemblyVersion("2026.5.30.0")]
[assembly: AssemblyFileVersion("2026.5.30.0")]
[assembly: AssemblyVersion("2026.7.4.0")]
[assembly: AssemblyFileVersion("2026.7.4.0")]
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@
<value>iexplore.exe</value>
</data>
<data name="ExampleCustomCommandVariableHostname" xml:space="preserve">
<value>https://$$HOSTNAME$$/</value>
<value>https://{{Hostname}}/</value>
</data>
<data name="InternetExplorer" xml:space="preserve">
<value>Internet Explorer</value>
Expand Down
50 changes: 37 additions & 13 deletions Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 12 additions & 6 deletions Source/NETworkManager.Localization/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,18 @@
<data name="Description" xml:space="preserve">
<value>Description</value>
</data>
<data name="Placeholder" xml:space="preserve">
<value>Placeholder</value>
</data>
<data name="HelpMessage_CustomCommandFilePath" xml:space="preserve">
<value>Path to the file or executable to run. The following placeholders can be used:</value>
</data>
<data name="HelpMessage_CustomCommandArguments" xml:space="preserve">
<value>Arguments passed to the file or executable. The following placeholders can be used:</value>
</data>
<data name="HelpMessage_ProfileHostPlaceholder" xml:space="preserve">
<value>The following placeholder can be used:</value>
</data>
<data name="DHCPEnabled" xml:space="preserve">
<value>DHCP enabled</value>
</data>
Expand Down Expand Up @@ -2244,12 +2256,6 @@ is disabled!</value>
<data name="DeleteCustomCommandMessage" xml:space="preserve">
<value>The selected custom command will be deleted permanently.</value>
</data>
<data name="HelpMessage_CustomCommandVariables" xml:space="preserve">
<value>The following variables are available:

$$ipaddress$$ --&gt; IP adresse
$$hostname$$ --&gt; Hostname</value>
</data>
<data name="Restart" xml:space="preserve">
<value>Restart</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ public static List<CustomCommandInfo> GetDefaultList()
{
return new List<CustomCommandInfo>
{
new(Guid.NewGuid(), "Edge", "cmd.exe", @"/c start microsoft-edge:http://$$ipaddress$$/"),
new(Guid.NewGuid(), "Edge", "cmd.exe", @"/c start microsoft-edge:http://{{IPAddress}}/"),
new(Guid.NewGuid(), "Edge (https)", "cmd.exe",
@"/c start microsoft-edge:https://$$ipaddress$$/"),
new(Guid.NewGuid(), "Windows Explorer (c$)", "explorer.exe", @"\\$$ipaddress$$\c$")
@"/c start microsoft-edge:https://{{IPAddress}}/"),
new(Guid.NewGuid(), "Windows Explorer (c$)", "explorer.exe", @"\\{{IPAddress}}\c$")
};
}
}
13 changes: 8 additions & 5 deletions Source/NETworkManager.Profiles/Application/PowerShell.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using NETworkManager.Models.PowerShell;
using NETworkManager.Settings;
using NETworkManager.Utilities;

namespace NETworkManager.Profiles.Application;

Expand All @@ -20,11 +21,13 @@ public static PowerShellSessionInfo CreateSessionInfo(ProfileInfo profile)
: group.PowerShell_OverrideCommand
? group.PowerShell_Command
: SettingsManager.Current.PowerShell_Command,
AdditionalCommandLine = profile.PowerShell_OverrideAdditionalCommandLine
? profile.PowerShell_AdditionalCommandLine
: group.PowerShell_OverrideAdditionalCommandLine
? group.PowerShell_AdditionalCommandLine
: SettingsManager.Current.PowerShell_AdditionalCommandLine,
AdditionalCommandLine = PlaceholderHelper.Resolve(
profile.PowerShell_OverrideAdditionalCommandLine
? profile.PowerShell_AdditionalCommandLine
: group.PowerShell_OverrideAdditionalCommandLine
? group.PowerShell_AdditionalCommandLine
: SettingsManager.Current.PowerShell_AdditionalCommandLine,
(PlaceholderHelper.Host, profile.Host)),
ExecutionPolicy = profile.PowerShell_OverrideExecutionPolicy
? profile.PowerShell_ExecutionPolicy
: group.PowerShell_OverrideExecutionPolicy
Expand Down
13 changes: 8 additions & 5 deletions Source/NETworkManager.Profiles/Application/PuTTY.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using NETworkManager.Models.PuTTY;
using NETworkManager.Settings;
using NETworkManager.Utilities;

namespace NETworkManager.Profiles.Application;

Expand Down Expand Up @@ -55,11 +56,13 @@ public static PuTTYSessionInfo CreateSessionInfo(ProfileInfo profile)
: group.PuTTY_OverrideLogFileName
? group.PuTTY_LogFileName
: SettingsManager.Current.PuTTY_LogFileName,
AdditionalCommandLine = profile.PuTTY_OverrideAdditionalCommandLine
? profile.PuTTY_AdditionalCommandLine
: group.PuTTY_OverrideAdditionalCommandLine
? group.PuTTY_AdditionalCommandLine
: SettingsManager.Current.PuTTY_AdditionalCommandLine
AdditionalCommandLine = PlaceholderHelper.Resolve(
profile.PuTTY_OverrideAdditionalCommandLine
? profile.PuTTY_AdditionalCommandLine
: group.PuTTY_OverrideAdditionalCommandLine
? group.PuTTY_AdditionalCommandLine
: SettingsManager.Current.PuTTY_AdditionalCommandLine,
(PlaceholderHelper.Host, profile.Host))
};
}
}
3 changes: 2 additions & 1 deletion Source/NETworkManager.Profiles/Application/WebConsole.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NETworkManager.Models.WebConsole;
using NETworkManager.Utilities;

namespace NETworkManager.Profiles.Application;

Expand All @@ -8,7 +9,7 @@ public static WebConsoleSessionInfo CreateSessionInfo(ProfileInfo profileInfo)
{
var info = new WebConsoleSessionInfo
{
Url = profileInfo.WebConsole_Url
Url = PlaceholderHelper.Resolve(profileInfo.WebConsole_Url, (PlaceholderHelper.Host, profileInfo.Host))
};

return info;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@
<ItemGroup>
<ProjectReference Include="..\NETworkManager.Models\NETworkManager.Models.csproj" />
<ProjectReference Include="..\NETworkManager.Settings\NETworkManager.Settings.csproj" />
<ProjectReference Include="..\NETworkManager.Utilities\NETworkManager.Utilities.csproj" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion Source/NETworkManager.Profiles/ProfileInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ public ProfileInfo(ProfileInfo profile)
public int TigerVNC_Port { get; set; } = GlobalStaticConfiguration.TigerVNC_DefaultVNCPort;

public bool WebConsole_Enabled { get; set; }
public string WebConsole_Url { get; set; }
public string WebConsole_Url { get; set; } = "https://{{Host}}";

public bool SNMP_Enabled { get; set; }
public bool SNMP_InheritHost { get; set; } = true;
Expand Down
31 changes: 31 additions & 0 deletions Source/NETworkManager.Settings/SettingsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Xml.Serialization;

namespace NETworkManager.Settings;
Expand Down Expand Up @@ -698,6 +699,36 @@ private static void UpgradeToLatest(Version version)

Current.General_ApplicationList[index] = neighborTableEntry;
}

// IP Scanner - Custom commands
Log.Info("Migrating IP Scanner custom command placeholder syntax from \"$$var$$\" to \"{{Var}}\"...");

foreach (var customCommand in Current.IPScanner_CustomCommands)
{
var filePath = customCommand.FilePath;

if (!string.IsNullOrEmpty(filePath))
{
filePath = Regex.Replace(filePath, "\\$\\$ipaddress\\$\\$", "{{IPAddress}}", RegexOptions.IgnoreCase);
filePath = Regex.Replace(filePath, "\\$\\$hostname\\$\\$", "{{Hostname}}", RegexOptions.IgnoreCase);
}

var arguments = customCommand.Arguments;

if (!string.IsNullOrEmpty(arguments))
{
arguments = Regex.Replace(arguments, "\\$\\$ipaddress\\$\\$", "{{IPAddress}}", RegexOptions.IgnoreCase);
arguments = Regex.Replace(arguments, "\\$\\$hostname\\$\\$", "{{Hostname}}", RegexOptions.IgnoreCase);
}

if (filePath == customCommand.FilePath && arguments == customCommand.Arguments)
continue;

Log.Info($"Migrated custom command \"{customCommand.Name}\"...");

customCommand.FilePath = filePath;
customCommand.Arguments = arguments;
}
}
#endregion
}
53 changes: 53 additions & 0 deletions Source/NETworkManager.Utilities/PlaceholderHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Text.RegularExpressions;

namespace NETworkManager.Utilities;

/// <summary>
/// Helper class to resolve <c>{{name}}</c> placeholders in a string.
/// </summary>
public static class PlaceholderHelper
{
/// <summary>
/// Placeholder name for the host (e.g. of a profile).
/// </summary>
public const string Host = "host";

/// <summary>
/// Placeholder name for a hostname.
/// </summary>
public const string Hostname = "hostname";

/// <summary>
/// Placeholder name for an IP address.
/// </summary>
public const string IPAddress = "ipaddress";

/// <summary>
/// Replaces a <c>{{name}}</c> placeholder (case-insensitive) with the given value.
/// </summary>
/// <param name="input">Input string that may contain the placeholder.</param>
/// <param name="name">Name of the placeholder, without the surrounding braces (e.g. <c>"hostname"</c>).</param>
/// <param name="value">Value to insert in place of the placeholder.</param>
/// <returns>The input with the placeholder resolved.</returns>
public static string Replace(string input, string name, string value)
{
return string.IsNullOrEmpty(input)
? input
: Regex.Replace(input, $"\\{{\\{{{Regex.Escape(name)}\\}}\\}}", value ?? string.Empty,
RegexOptions.IgnoreCase);
}

/// <summary>
/// Replaces multiple <c>{{name}}</c> placeholders (case-insensitive) with the given values.
/// </summary>
/// <param name="input">Input string that may contain the placeholders.</param>
/// <param name="placeholders">Placeholder name/value pairs to resolve, e.g. <c>(PlaceholderHelper.Host, "10.0.0.1")</c>.</param>
/// <returns>The input with all placeholders resolved.</returns>
public static string Resolve(string input, params (string name, string value)[] placeholders)
{
foreach (var (name, value) in placeholders)
input = Replace(input, name, value);

return input;
}
}
26 changes: 26 additions & 0 deletions Source/NETworkManager.Validators/WebConsoleProfileUrlValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows.Controls;
using NETworkManager.Localization.Resources;

namespace NETworkManager.Validators;

/// <summary>
/// Validates the WebConsole URL field on a profile, which may contain the <c>{{Host}}</c> placeholder.
/// </summary>
public class WebConsoleProfileUrlValidator : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
// Substitute the {{Host}} placeholder with a dummy host so the structural
// URL validation (scheme, format) still applies, without rejecting the placeholder itself.
var input = Regex.Replace(value as string ?? "", "\\{\\{host\\}\\}", "placeholder-host",
RegexOptions.IgnoreCase);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest commit — replaced the inline Regex.Replace with PlaceholderHelper.Replace(…, PlaceholderHelper.Host, "placeholder-host") and dropped the now-unused System.Text.RegularExpressions import.


return Uri.TryCreate(input, UriKind.Absolute, out var uriResult) &&
(uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps)
? ValidationResult.ValidResult
: new ValidationResult(false, Strings.EnterValidWebsiteUri);
}
}
13 changes: 6 additions & 7 deletions Source/NETworkManager/ViewModels/IPScannerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
Expand Down Expand Up @@ -581,15 +580,15 @@ private async Task CustomCommand(object guid)
var hostname = !string.IsNullOrEmpty(SelectedResult.Hostname) ? SelectedResult.Hostname.TrimEnd('.') : "";
var ipAddress = SelectedResult.PingInfo.IPAddress.ToString();

info.FilePath = Regex.Replace(info.FilePath, "\\$\\$hostname\\$\\$", hostname, RegexOptions.IgnoreCase);
info.FilePath = Regex.Replace(info.FilePath, "\\$\\$ipaddress\\$\\$", ipAddress, RegexOptions.IgnoreCase);
info.FilePath = PlaceholderHelper.Resolve(info.FilePath,
(PlaceholderHelper.Hostname, hostname),
(PlaceholderHelper.IPAddress, ipAddress));

if (!string.IsNullOrEmpty(info.Arguments))
{
info.Arguments = Regex.Replace(info.Arguments, "\\$\\$hostname\\$\\$", hostname,
RegexOptions.IgnoreCase);
info.Arguments = Regex.Replace(info.Arguments, "\\$\\$ipaddress\\$\\$", ipAddress,
RegexOptions.IgnoreCase);
info.Arguments = PlaceholderHelper.Resolve(info.Arguments,
(PlaceholderHelper.Hostname, hostname),
(PlaceholderHelper.IPAddress, ipAddress));
}

try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ private static ObservableCollection<ImportApplicationToggleItem> BuildApplicatio
new ImportApplicationToggleItem(ProfileName.PowerShell, true),
new ImportApplicationToggleItem(ProfileName.PuTTY),
new ImportApplicationToggleItem(ProfileName.TigerVNC),
new ImportApplicationToggleItem(ProfileName.WebConsole),
new ImportApplicationToggleItem(ProfileName.SNMP),
new ImportApplicationToggleItem(ProfileName.Whois),
new ImportApplicationToggleItem(ProfileName.IPGeolocation)
Expand Down Expand Up @@ -251,6 +252,9 @@ private static void ApplyToolDefaults(ProfileInfo profile, ProfileName tool)
profile.TigerVNC_Enabled = true;
profile.TigerVNC_Host = profile.Host;
break;
case ProfileName.WebConsole:
profile.WebConsole_Enabled = true;
break;
case ProfileName.SNMP:
profile.SNMP_Enabled = true;
profile.SNMP_Host = profile.Host;
Expand Down
3 changes: 2 additions & 1 deletion Source/NETworkManager/ViewModels/PowerShellHostViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,8 @@ private Task Connect(string host = null)
EnableRemoteConsole = instance.EnableRemoteConsole,
Host = instance.Host,
Command = instance.Command,
AdditionalCommandLine = instance.AdditionalCommandLine,
AdditionalCommandLine = PlaceholderHelper.Resolve(instance.AdditionalCommandLine,
(PlaceholderHelper.Host, instance.Host)),
ExecutionPolicy = instance.ExecutionPolicy
};

Expand Down
Loading
Loading