diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..3bbdd20
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,18 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build MeshCentralRouter",
+ "type": "shell",
+ "command": "& '${env:ProgramFiles(x86)}\\Microsoft Visual Studio\\2022\\BuildTools\\MSBuild\\Current\\Bin\\MSBuild.exe' MeshCentralRouter.sln",
+ "isBackground": false,
+ "problemMatcher": [
+ "$msCompile"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/MeshCentralRouter.csproj b/MeshCentralRouter.csproj
index 9a6fccf..0f49d39 100644
--- a/MeshCentralRouter.csproj
+++ b/MeshCentralRouter.csproj
@@ -45,7 +45,6 @@
DEBUG;TRACE
prompt
4
- AllRules.ruleset
false
@@ -55,7 +54,6 @@
TRACE
prompt
4
- AllRules.ruleset
false
@@ -262,6 +260,7 @@
+
diff --git a/MeshUtils.cs b/MeshUtils.cs
index 2e1f667..5f74fe9 100644
--- a/MeshUtils.cs
+++ b/MeshUtils.cs
@@ -395,5 +395,36 @@ namespace MeshMiniRouterTool
public static StringBuilder GetStringBuilder() { lock (StringBuilderRecycleList) { return (StringBuilderRecycleList.Count == 0) ? new StringBuilder(16000) : StringBuilderRecycleList.Pop(); } }
public static void RecycleStringBuilder(StringBuilder obj) { lock (StringBuilderRecycleList) { obj.Length = 0; StringBuilderRecycleList.Push(obj); } }
+ // Password encryption/decryption using DPAPI (Data Protection API)
+ public static string EncryptPassword(string password)
+ {
+ if (string.IsNullOrEmpty(password)) return null;
+ try
+ {
+ byte[] data = Encoding.UTF8.GetBytes(password);
+ byte[] encrypted = ProtectedData.Protect(data, null, DataProtectionScope.CurrentUser);
+ return Convert.ToBase64String(encrypted);
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public static string DecryptPassword(string encryptedPassword)
+ {
+ if (string.IsNullOrEmpty(encryptedPassword)) return null;
+ try
+ {
+ byte[] data = Convert.FromBase64String(encryptedPassword);
+ byte[] decrypted = ProtectedData.Unprotect(data, null, DataProtectionScope.CurrentUser);
+ return Encoding.UTF8.GetString(decrypted);
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
}
}
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index 09526a6..af6197f 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -31,6 +31,6 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("1.8.*")]
+[assembly: AssemblyVersion("1.9.*")]
//[assembly: AssemblyVersion("1.0.0.0")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/KVMControl.cs b/src/KVMControl.cs
index 7fa6fc3..d942215 100644
--- a/src/KVMControl.cs
+++ b/src/KVMControl.cs
@@ -61,8 +61,8 @@ namespace MeshCentralRouter
private long killNextKeyPress = 0;
private bool controlLoaded = false;
public Rectangle[] displayInfo = null;
- public Rectangle displayCrop = Rectangle.Empty;
- public Point displayOrigin = Point.Empty;
+ public Rectangle displayCrop { get; set; } = Rectangle.Empty;
+ public Point displayOrigin { get; set; } = Point.Empty;
//System level functions to be used for hook and unhook keyboard input
@@ -949,7 +949,8 @@ namespace MeshCentralRouter
return false;
}
- // Structure contain information about low-level keyboard input event
+ // Structure contain information about low-level keyboard input event
+#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value (filled by Windows via P/Invoke)
private struct KBDLLHOOKSTRUCT
{
public Keys key;
@@ -958,6 +959,7 @@ namespace MeshCentralRouter
public int time;
public IntPtr extra;
}
+#pragma warning restore CS0649
public void cropDisplay(Point o, Rectangle r)
{
diff --git a/src/KVMResizeControl.cs b/src/KVMResizeControl.cs
index c1369bb..08a8d39 100644
--- a/src/KVMResizeControl.cs
+++ b/src/KVMResizeControl.cs
@@ -29,7 +29,9 @@ namespace MeshCentralRouter
[Category("Action")]
[Description("Fires when the connection state changes.")]
+#pragma warning disable CS0067 // Event is declared but never invoked in this class
public event EventHandler StateChanged;
+#pragma warning restore CS0067
[Category("Action")]
[Description("Fires when the display list is received.")]
diff --git a/src/MainForm.Designer.cs b/src/MainForm.Designer.cs
index a0dc636..d128e21 100644
--- a/src/MainForm.Designer.cs
+++ b/src/MainForm.Designer.cs
@@ -42,6 +42,7 @@
this.label27 = new System.Windows.Forms.Label();
this.label26 = new System.Windows.Forms.Label();
this.passwordTextBox = new System.Windows.Forms.TextBox();
+ this.rememberPasswordCheckBox = new System.Windows.Forms.CheckBox();
this.serverNameComboBox = new System.Windows.Forms.ComboBox();
this.userNameTextBox = new System.Windows.Forms.TextBox();
this.licenseLinkLabel = new System.Windows.Forms.LinkLabel();
@@ -213,6 +214,7 @@
this.panel1.Controls.Add(this.label27);
this.panel1.Controls.Add(this.label26);
this.panel1.Controls.Add(this.passwordTextBox);
+ this.panel1.Controls.Add(this.rememberPasswordCheckBox);
this.panel1.Controls.Add(this.serverNameComboBox);
this.panel1.Controls.Add(this.userNameTextBox);
this.panel1.Controls.Add(this.licenseLinkLabel);
@@ -266,6 +268,12 @@
this.passwordTextBox.TextChanged += new System.EventHandler(this.updatePanel1);
this.passwordTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.passwordTextBox_KeyPress);
//
+ // rememberPasswordCheckBox
+ //
+ resources.ApplyResources(this.rememberPasswordCheckBox, "rememberPasswordCheckBox");
+ this.rememberPasswordCheckBox.Name = "rememberPasswordCheckBox";
+ this.rememberPasswordCheckBox.UseVisualStyleBackColor = true;
+ //
// serverNameComboBox
//
resources.ApplyResources(this.serverNameComboBox, "serverNameComboBox");
@@ -1094,6 +1102,7 @@
private System.Windows.Forms.ComboBox serverNameComboBox;
private System.Windows.Forms.TextBox userNameTextBox;
private System.Windows.Forms.TextBox passwordTextBox;
+ private System.Windows.Forms.CheckBox rememberPasswordCheckBox;
private System.Windows.Forms.Label label28;
private System.Windows.Forms.Label label27;
private System.Windows.Forms.Label label26;
diff --git a/src/MainForm.cs b/src/MainForm.cs
index 8b869ed..582e921 100644
--- a/src/MainForm.cs
+++ b/src/MainForm.cs
@@ -31,6 +31,7 @@ using System.Drawing;
using System.Text;
using System.Web;
using System.Threading.Tasks;
+using MeshMiniRouterTool;
namespace MeshCentralRouter
{
@@ -253,6 +254,33 @@ namespace MeshCentralRouter
userNameTextBox.Text = Settings.GetRegValue("UserName", "");
notifyIcon.Visible = Settings.GetRegValue("NotifyIcon", false);
+ // Load saved password if available and not expired (30 days)
+ string savedPassword = Settings.GetRegValue("SavedPassword", "");
+ string savedPasswordDate = Settings.GetRegValue("SavedPasswordDate", "");
+ if (!string.IsNullOrEmpty(savedPassword) && !string.IsNullOrEmpty(savedPasswordDate))
+ {
+ try
+ {
+ DateTime savedDate = DateTime.Parse(savedPasswordDate);
+ if ((DateTime.Now - savedDate).TotalDays <= 30)
+ {
+ string decryptedPassword = MeshUtils.DecryptPassword(savedPassword);
+ if (decryptedPassword != null)
+ {
+ passwordTextBox.Text = decryptedPassword;
+ rememberPasswordCheckBox.Checked = true;
+ }
+ }
+ else
+ {
+ // Password expired, clear it
+ Settings.SetRegValue("SavedPassword", "");
+ Settings.SetRegValue("SavedPasswordDate", "");
+ }
+ }
+ catch (Exception) { }
+ }
+
title = this.Text;
initialHeight = this.Height;
@@ -1158,6 +1186,22 @@ namespace MeshCentralRouter
{
Settings.SetRegValue("ServerName", serverNameComboBox.Text);
Settings.SetRegValue("UserName", userNameTextBox.Text);
+
+ // Save or clear password based on remember checkbox
+ if (rememberPasswordCheckBox.Checked)
+ {
+ string encryptedPassword = MeshUtils.EncryptPassword(passwordTextBox.Text);
+ if (encryptedPassword != null)
+ {
+ Settings.SetRegValue("SavedPassword", encryptedPassword);
+ Settings.SetRegValue("SavedPasswordDate", DateTime.Now.ToString("o"));
+ }
+ }
+ else
+ {
+ Settings.SetRegValue("SavedPassword", "");
+ Settings.SetRegValue("SavedPasswordDate", "");
+ }
}
if (meshcentral.username != null)
{
diff --git a/src/MainForm.resx b/src/MainForm.resx
index 5c1f7e1..baad787 100644
--- a/src/MainForm.resx
+++ b/src/MainForm.resx
@@ -348,6 +348,36 @@
6
+
+ Top, Left
+
+
+ True
+
+
+ 241, 218
+
+
+ 200, 17
+
+
+ 107
+
+
+ Remember password for 30 days
+
+
+ rememberPasswordCheckBox
+
+
+ System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ panel1
+
+
+ 7
+
Top, Left, Right
@@ -373,7 +403,7 @@
panel1
- 7
+ 8
Top, Left, Right
@@ -397,7 +427,7 @@
panel1
- 8
+ 9
Bottom, Right
@@ -430,7 +460,7 @@
panel1
- 9
+ 10
Bottom, Left
@@ -463,7 +493,7 @@
panel1
- 10
+ 11
Bottom, Right
@@ -493,7 +523,7 @@
panel1
- 11
+ 12
Bottom, Left, Right
@@ -523,7 +553,7 @@
panel1
- 12
+ 13
Top, Left, Right
@@ -553,7 +583,7 @@
panel1
- 13
+ 14
Top, Bottom, Left, Right
@@ -583,7 +613,7 @@
panel1
- 14
+ 15
Fill