From f560fb86d81be0904785d7a4acc507fe83b5f025 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Thu, 1 Nov 2018 15:00:51 -0500 Subject: [PATCH 01/19] moved database serializers to more logical place --- .../Config/Serializers/DataTableDeserializerTests.cs | 1 + mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs | 1 + mRemoteV1/Config/Connections/SqlConnectionsLoader.cs | 1 + mRemoteV1/Config/Connections/SqlConnectionsSaver.cs | 1 + .../MsSql}/DataTableDeserializer.cs | 2 +- .../{ => ConnectionSerializers/MsSql}/DataTableSerializer.cs | 2 +- mRemoteV1/mRemoteV1.csproj | 4 ++-- 7 files changed, 8 insertions(+), 4 deletions(-) rename mRemoteV1/Config/Serializers/{ => ConnectionSerializers/MsSql}/DataTableDeserializer.cs (99%) rename mRemoteV1/Config/Serializers/{ => ConnectionSerializers/MsSql}/DataTableSerializer.cs (99%) diff --git a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs index edba56162..85d149db8 100644 --- a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs @@ -1,6 +1,7 @@ using System.Data; using System.Linq; using mRemoteNG.Config.Serializers; +using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Connection; using mRemoteNG.Security; using mRemoteNG.Tree; diff --git a/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs index 5176fa1a9..ab42493e1 100644 --- a/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs @@ -1,5 +1,6 @@ using System.Linq; using mRemoteNG.Config.Serializers; +using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Connection; using mRemoteNG.Container; using mRemoteNG.Security; diff --git a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs index eb2107d8f..ad2a5a99e 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs @@ -1,6 +1,7 @@ using mRemoteNG.Config.DatabaseConnectors; using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers; +using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Config.Serializers.Versioning; using mRemoteNG.Tree; diff --git a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs index 93a5868b3..52baedf79 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs @@ -8,6 +8,7 @@ using mRemoteNG.App.Info; using mRemoteNG.Config.DatabaseConnectors; using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers; +using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Config.Serializers.Versioning; using mRemoteNG.Container; using mRemoteNG.Messages; diff --git a/mRemoteV1/Config/Serializers/DataTableDeserializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs similarity index 99% rename from mRemoteV1/Config/Serializers/DataTableDeserializer.cs rename to mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs index 5dc97aa78..1d251b44c 100644 --- a/mRemoteV1/Config/Serializers/DataTableDeserializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs @@ -13,7 +13,7 @@ using mRemoteNG.Container; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; -namespace mRemoteNG.Config.Serializers +namespace mRemoteNG.Config.Serializers.MsSql { public class DataTableDeserializer : IDeserializer { diff --git a/mRemoteV1/Config/Serializers/DataTableSerializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs similarity index 99% rename from mRemoteV1/Config/Serializers/DataTableSerializer.cs rename to mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs index db43749c1..9c11db3c3 100644 --- a/mRemoteV1/Config/Serializers/DataTableSerializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs @@ -8,7 +8,7 @@ using mRemoteNG.Security; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; -namespace mRemoteNG.Config.Serializers +namespace mRemoteNG.Config.Serializers.MsSql { public class DataTableSerializer : ISerializer { diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index 12ab41d8c..18f0770ec 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -169,8 +169,8 @@ - - + + From 2ebf654973bf4ea8cf60acf5627eb5566889b382 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Thu, 1 Nov 2018 16:36:56 -0500 Subject: [PATCH 02/19] added a ForEach extension for enumerables --- mRemoteV1/Tools/Extensions.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/mRemoteV1/Tools/Extensions.cs b/mRemoteV1/Tools/Extensions.cs index 76bcba778..ad4eb66b2 100644 --- a/mRemoteV1/Tools/Extensions.cs +++ b/mRemoteV1/Tools/Extensions.cs @@ -1,9 +1,11 @@  using System; +using System.Collections.Generic; +using System.Linq; namespace mRemoteNG.Tools { - public static class Extensions + public static class Extensions { public static Optional Maybe(this T value) { @@ -50,5 +52,23 @@ namespace mRemoteNG.Tools throw new ArgumentException("Value cannot be null or empty", argName); return value; } + + /// + /// Perform an action for each item in the given collection. The item + /// is the pass along the processing chain. + /// + /// + /// + /// + /// + public static IEnumerable ForEach(this IEnumerable collection, Action action) + { + collection = collection.ToList(); + + foreach (var item in collection) + action(item); + + return collection; + } } } From 4aba36b756dbb7dd60922b10867ecf809ba8ee27 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Thu, 1 Nov 2018 16:37:30 -0500 Subject: [PATCH 03/19] added classes to handle local connection properties when dealing with the DB --- .../MsSql/LocalConnectionPropertiesModel.cs | 8 +++ .../LocalConnectionPropertiesXmlSerializer.cs | 58 +++++++++++++++++++ mRemoteV1/mRemoteV1.csproj | 2 + 3 files changed, 68 insertions(+) create mode 100644 mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesModel.cs create mode 100644 mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesXmlSerializer.cs diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesModel.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesModel.cs new file mode 100644 index 000000000..2209d3f60 --- /dev/null +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesModel.cs @@ -0,0 +1,8 @@ +namespace mRemoteNG.Config.Serializers.MsSql +{ + public class LocalConnectionPropertiesModel + { + public string ConnectionId { get; set; } + public bool Connected { get; set; } + } +} diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesXmlSerializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesXmlSerializer.cs new file mode 100644 index 000000000..542b3f2ef --- /dev/null +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesXmlSerializer.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; + +namespace mRemoteNG.Config.Serializers.MsSql +{ + public class LocalConnectionPropertiesXmlSerializer : + ISerializer, string>, + IDeserializer> + { + public string Serialize(IEnumerable models) + { + var localConnections = models + .Select(m => new XElement("Node", + new XAttribute("ConnectionId", m.ConnectionId), + new XAttribute("Connected", m.Connected))); + + var root = new XElement("LocalConnections", localConnections); + var xdoc = new XDocument(new XDeclaration("1.0", "utf-8", null), root); + return WriteXmlToString(xdoc); + } + + public IEnumerable Deserialize(string serializedData) + { + if (string.IsNullOrWhiteSpace(serializedData)) + return Enumerable.Empty(); + + var xdoc = XDocument.Parse(serializedData); + return xdoc + .Descendants("Node") + .Where(e => e.Attribute("ConnectionId") != null) + .Select(e => new LocalConnectionPropertiesModel + { + ConnectionId = e.Attribute("ConnectionId")?.Value, + Connected = bool.Parse(e.Attribute("Connected")?.Value ?? "False") + }); + } + + private static string WriteXmlToString(XNode xmlDocument) + { + string xmlString; + var xmlWriterSettings = new XmlWriterSettings { Indent = true, IndentChars = " ", Encoding = Encoding.UTF8 }; + var memoryStream = new MemoryStream(); + using (var xmlTextWriter = XmlWriter.Create(memoryStream, xmlWriterSettings)) + { + xmlDocument.WriteTo(xmlTextWriter); + xmlTextWriter.Flush(); + var streamReader = new StreamReader(memoryStream, Encoding.UTF8, true); + memoryStream.Seek(0, SeekOrigin.Begin); + xmlString = streamReader.ReadToEnd(); + } + return xmlString; + } + } +} diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index 18f0770ec..a82778051 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -159,6 +159,8 @@ + + From e99b8ed453a4340672a2e8c5515ac291f1a95449 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Thu, 1 Nov 2018 19:14:25 -0500 Subject: [PATCH 04/19] hooked up the local settings serializer --- mRemoteV1/App/Info/SettingsFileInfo.cs | 2 +- mRemoteV1/App/Runtime.cs | 11 ++--- .../Connections/SqlConnectionsLoader.cs | 35 +++++++++++++++- .../Config/Connections/SqlConnectionsSaver.cs | 41 ++++++++++++++----- .../MsSql/DataTableDeserializer.cs | 1 - .../MsSql/DataTableSerializer.cs | 12 +++--- mRemoteV1/Connection/ConnectionsService.cs | 20 +++++---- 7 files changed, 91 insertions(+), 31 deletions(-) diff --git a/mRemoteV1/App/Info/SettingsFileInfo.cs b/mRemoteV1/App/Info/SettingsFileInfo.cs index 812259a7a..19776123c 100644 --- a/mRemoteV1/App/Info/SettingsFileInfo.cs +++ b/mRemoteV1/App/Info/SettingsFileInfo.cs @@ -9,7 +9,7 @@ namespace mRemoteNG.App.Info { private static readonly string ExePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); - public static string SettingsPath { get; } = Runtime.IsPortableEdition ? ExePath : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + Application.ProductName; + public static string SettingsPath => Runtime.IsPortableEdition ? ExePath : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + Application.ProductName; public static string LayoutFileName { get; } = "pnlLayout.xml"; public static string ExtAppsFilesName { get; } = "extApps.xml"; public static string ThemesFileName { get; } = "Themes.xml"; diff --git a/mRemoteV1/App/Runtime.cs b/mRemoteV1/App/Runtime.cs index b1921a8c0..249a8a2a1 100644 --- a/mRemoteV1/App/Runtime.cs +++ b/mRemoteV1/App/Runtime.cs @@ -1,8 +1,3 @@ -using System; -using System.IO; -using System.Security; -using System.Threading; -using System.Windows.Forms; using mRemoteNG.App.Info; using mRemoteNG.Config.Putty; using mRemoteNG.Connection; @@ -15,6 +10,11 @@ using mRemoteNG.Tree.Root; using mRemoteNG.UI; using mRemoteNG.UI.Forms; using mRemoteNG.UI.TaskDialog; +using System; +using System.IO; +using System.Security; +using System.Threading; +using System.Windows.Forms; namespace mRemoteNG.App { @@ -32,6 +32,7 @@ namespace mRemoteNG.App } } + private static string SettingsPath { get; } = SettingsFileInfo.SettingsPath; public static WindowList WindowList { get; set; } public static MessageCollector MessageCollector { get; } = new MessageCollector(); public static NotificationAreaIcon NotificationAreaIcon { get; set; } diff --git a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs index ad2a5a99e..ae614e20c 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs @@ -3,12 +3,28 @@ using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers; using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Config.Serializers.Versioning; +using mRemoteNG.Container; +using mRemoteNG.Tools; using mRemoteNG.Tree; +using mRemoteNG.Tree.Root; +using System.Collections.Generic; +using System.Linq; namespace mRemoteNG.Config.Connections { public class SqlConnectionsLoader { + private readonly IDeserializer> _localConnectionPropertiesDeserializer; + private readonly IDataProvider _dataProvider; + + public SqlConnectionsLoader( + IDeserializer> localConnectionPropertiesDeserializer, + IDataProvider dataProvider) + { + _localConnectionPropertiesDeserializer = localConnectionPropertiesDeserializer.ThrowIfNull(nameof(localConnectionPropertiesDeserializer)); + _dataProvider = dataProvider.ThrowIfNull(nameof(dataProvider)); + } + public ConnectionTreeModel Load() { var connector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings(); @@ -17,7 +33,24 @@ namespace mRemoteNG.Config.Connections databaseVersionVerifier.VerifyDatabaseVersion(); var dataTable = dataProvider.Load(); var deserializer = new DataTableDeserializer(); - return deserializer.Deserialize(dataTable); + var connectionTree = deserializer.Deserialize(dataTable); + ApplyLocalConnectionProperties(connectionTree.RootNodes.First(i => i is RootNodeInfo)); + return connectionTree; + } + + private void ApplyLocalConnectionProperties(ContainerInfo rootNode) + { + var localPropertiesXml = _dataProvider.Load(); + var localConnectionProperties = _localConnectionPropertiesDeserializer.Deserialize(localPropertiesXml); + + rootNode + .GetRecursiveChildList() + .Join(localConnectionProperties, + con => con.ConstantID, + locals => locals.ConnectionId, + (con, locals) => new {Connection = con, LocalProperties = locals}) + .ForEach(x => + x.Connection.PleaseConnect = x.LocalProperties.Connected); } } } \ No newline at end of file diff --git a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs index 52baedf79..859ed81ce 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs @@ -1,9 +1,4 @@ -using System; -using System.Data.SqlClient; -using System.Globalization; -using System.Linq; -using System.Security; -using mRemoteNG.App; +using mRemoteNG.App; using mRemoteNG.App.Info; using mRemoteNG.Config.DatabaseConnectors; using mRemoteNG.Config.DataProviders; @@ -17,6 +12,12 @@ using mRemoteNG.Security.SymmetricEncryption; using mRemoteNG.Tools; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Globalization; +using System.Linq; +using System.Security; namespace mRemoteNG.Config.Connections { @@ -24,22 +25,32 @@ namespace mRemoteNG.Config.Connections { private SecureString _password = Runtime.EncryptionKey; private readonly SaveFilter _saveFilter; + private readonly ISerializer, string> _localPropertiesSerializer; + private readonly IDataProvider _dataProvider; - public SqlConnectionsSaver(SaveFilter saveFilter) + public SqlConnectionsSaver( + SaveFilter saveFilter, + ISerializer, string> localPropertieSerializer, + IDataProvider localPropertiesDataProvider) { if (saveFilter == null) throw new ArgumentNullException(nameof(saveFilter)); _saveFilter = saveFilter; + _localPropertiesSerializer = localPropertieSerializer.ThrowIfNull(nameof(localPropertieSerializer)); + _dataProvider = localPropertiesDataProvider.ThrowIfNull(nameof(localPropertiesDataProvider)); } public void Save(ConnectionTreeModel connectionTreeModel) { + var rootTreeNode = connectionTreeModel.RootNodes.OfType().First(); + + UpdateLocalConnectionProperties(rootTreeNode); + if (SqlUserIsReadOnly()) { Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, "Trying to save connection tree but the SQL read only checkbox is checked, aborting!"); return; } - using (var sqlConnector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings()) { @@ -52,14 +63,24 @@ namespace mRemoteNG.Config.Connections return; } - var rootTreeNode = connectionTreeModel.RootNodes.OfType().First(); - UpdateRootNodeTable(rootTreeNode, sqlConnector); UpdateConnectionsTable(rootTreeNode, sqlConnector); UpdateUpdatesTable(sqlConnector); } } + private void UpdateLocalConnectionProperties(ContainerInfo rootNode) + { + var a = rootNode.GetRecursiveChildList().Select(info => new LocalConnectionPropertiesModel + { + ConnectionId = info.ConstantID, + Connected = info.OpenConnections.Count > 0 + }); + + var serializedProperties = _localPropertiesSerializer.Serialize(a); + _dataProvider.Save(serializedProperties); + } + private void UpdateRootNodeTable(RootNodeInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector) { var cryptographyProvider = new LegacyRijndaelCryptographyProvider(); diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs index 1d251b44c..ce47ba668 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs @@ -105,7 +105,6 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.RedirectSound = (RdpProtocol.RDPSounds)Enum.Parse(typeof(RdpProtocol.RDPSounds), (string)dataRow["RedirectSound"]); connectionInfo.SoundQuality = (RdpProtocol.RDPSoundQuality)Enum.Parse(typeof(RdpProtocol.RDPSoundQuality), (string)dataRow["SoundQuality"]); connectionInfo.RedirectKeys = (bool)dataRow["RedirectKeys"]; - connectionInfo.PleaseConnect = (bool)dataRow["Connected"]; connectionInfo.PreExtApp = (string)dataRow["PreExtApp"]; connectionInfo.PostExtApp = (string)dataRow["PostExtApp"]; connectionInfo.MacAddress = (string)dataRow["MacAddress"]; diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs index 9c11db3c3..5e4af0731 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs @@ -1,12 +1,12 @@ -using System; -using System.Data; -using System.Data.SqlTypes; -using System.Linq; -using mRemoteNG.Connection; +using mRemoteNG.Connection; using mRemoteNG.Container; using mRemoteNG.Security; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; +using System; +using System.Data; +using System.Data.SqlTypes; +using System.Linq; namespace mRemoteNG.Config.Serializers.MsSql { @@ -239,7 +239,7 @@ namespace mRemoteNG.Config.Serializers.MsSql dataRow["RedirectSound"] = connectionInfo.RedirectSound; dataRow["SoundQuality"] = connectionInfo.SoundQuality; dataRow["RedirectKeys"] = connectionInfo.RedirectKeys; - dataRow["Connected"] = connectionInfo.OpenConnections.Count > 0; + dataRow["Connected"] = false; // TODO: this column can eventually be removed dataRow["PreExtApp"] = connectionInfo.PreExtApp; dataRow["PostExtApp"] = connectionInfo.PostExtApp; dataRow["MacAddress"] = connectionInfo.MacAddress; diff --git a/mRemoteV1/Connection/ConnectionsService.cs b/mRemoteV1/Connection/ConnectionsService.cs index c6240ec92..b7554b401 100644 --- a/mRemoteV1/Connection/ConnectionsService.cs +++ b/mRemoteV1/Connection/ConnectionsService.cs @@ -1,12 +1,10 @@ -using System; -using System.IO; -using System.Threading; -using System.Windows.Forms; -using mRemoteNG.App; +using mRemoteNG.App; using mRemoteNG.App.Info; using mRemoteNG.Config.Connections; using mRemoteNG.Config.Connections.Multiuser; +using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Putty; +using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Connection.Protocol; using mRemoteNG.Messages; using mRemoteNG.Security; @@ -14,6 +12,10 @@ using mRemoteNG.Tools; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; using mRemoteNG.UI; +using System; +using System.IO; +using System.Threading; +using System.Windows.Forms; namespace mRemoteNG.Connection { @@ -21,6 +23,8 @@ namespace mRemoteNG.Connection { private static readonly object SaveLock = new object(); private readonly PuttySessionsManager _puttySessionsManager; + private readonly IDataProvider _localConnectionPropertiesDataProvider; + private readonly LocalConnectionPropertiesXmlSerializer _localConnectionPropertiesSerializer; private bool _batchingSaves = false; private bool _saveRequested = false; private bool _saveAsyncRequested = false; @@ -39,6 +43,8 @@ namespace mRemoteNG.Connection throw new ArgumentNullException(nameof(puttySessionsManager)); _puttySessionsManager = puttySessionsManager; + _localConnectionPropertiesDataProvider = new FileDataProvider(Path.Combine(SettingsFileInfo.SettingsPath, "LocalConnectionProperties.xml")); + _localConnectionPropertiesSerializer = new LocalConnectionPropertiesXmlSerializer(); } public void NewConnectionsFile(string filename) @@ -107,7 +113,7 @@ namespace mRemoteNG.Connection var oldIsUsingDatabaseValue = UsingDatabase; var newConnectionTreeModel = useDatabase - ? new SqlConnectionsLoader().Load() + ? new SqlConnectionsLoader(_localConnectionPropertiesSerializer, _localConnectionPropertiesDataProvider).Load() : new XmlConnectionsLoader(connectionFileName).Load(); if (newConnectionTreeModel == null) @@ -196,7 +202,7 @@ namespace mRemoteNG.Connection var previouslyUsingDatabase = UsingDatabase; if (useDatabase) - new SqlConnectionsSaver(saveFilter).Save(connectionTreeModel); + new SqlConnectionsSaver(saveFilter, _localConnectionPropertiesSerializer, _localConnectionPropertiesDataProvider).Save(connectionTreeModel); else new XmlConnectionsSaver(connectionFileName, saveFilter).Save(connectionTreeModel); From 80e43e863405bcf40053461c2dd87cce9d735cdc Mon Sep 17 00:00:00 2001 From: David Sparer Date: Fri, 2 Nov 2018 09:30:18 -0500 Subject: [PATCH 05/19] added a safety check to the previous session opener to only open sessions that aren't already open --- mRemoteV1/App/Runtime.cs | 1 - .../Multiuser/SqlConnectionsUpdateChecker.cs | 19 ++++++++-------- mRemoteV1/Connection/ConnectionInitiator.cs | 22 ++++++++++++++----- mRemoteV1/Connection/ConnectionsService.cs | 3 +++ mRemoteV1/Connection/IConnectionInitiator.cs | 3 +++ mRemoteV1/Tree/PreviousSessionOpener.cs | 13 +++++++---- 6 files changed, 40 insertions(+), 21 deletions(-) diff --git a/mRemoteV1/App/Runtime.cs b/mRemoteV1/App/Runtime.cs index 249a8a2a1..3b580abd3 100644 --- a/mRemoteV1/App/Runtime.cs +++ b/mRemoteV1/App/Runtime.cs @@ -32,7 +32,6 @@ namespace mRemoteNG.App } } - private static string SettingsPath { get; } = SettingsFileInfo.SettingsPath; public static WindowList WindowList { get; set; } public static MessageCollector MessageCollector { get; } = new MessageCollector(); public static NotificationAreaIcon NotificationAreaIcon { get; set; } diff --git a/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs b/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs index fdd8dc1bb..30276ec85 100644 --- a/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs +++ b/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs @@ -1,11 +1,11 @@ using mRemoteNG.App; +using mRemoteNG.Config.Connections.Multiuser; +using mRemoteNG.Config.DatabaseConnectors; using mRemoteNG.Messages; using System; using System.Data; using System.Data.SqlClient; using System.Threading; -using mRemoteNG.Config.Connections.Multiuser; -using mRemoteNG.Config.DatabaseConnectors; namespace mRemoteNG.Config.Connections { @@ -13,7 +13,7 @@ namespace mRemoteNG.Config.Connections { private readonly SqlDatabaseConnector _sqlConnector; private readonly SqlCommand _sqlQuery; - private DateTime _lastUpdateTime; + private DateTime LastUpdateTime => Runtime.ConnectionsService.LastSqlUpdate; private DateTime _lastDatabaseUpdateTime; @@ -21,7 +21,6 @@ namespace mRemoteNG.Config.Connections { _sqlConnector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings(); _sqlQuery = new SqlCommand("SELECT * FROM tblUpdate", _sqlConnector.SqlConnection); - _lastUpdateTime = default(DateTime); _lastDatabaseUpdateTime = default(DateTime); } @@ -58,14 +57,14 @@ namespace mRemoteNG.Config.Connections private bool DatabaseIsMoreUpToDateThanUs() { var lastUpdateInDb = GetLastUpdateTimeFromDbResponse(); - var IAmTheLastoneUpdated = CheckIfIAmTheLastOneUpdated(lastUpdateInDb); - return (lastUpdateInDb > _lastUpdateTime && !IAmTheLastoneUpdated); + var amTheLastoneUpdated = CheckIfIAmTheLastOneUpdated(lastUpdateInDb); + return (lastUpdateInDb > LastUpdateTime && !amTheLastoneUpdated); } private bool CheckIfIAmTheLastOneUpdated(DateTime lastUpdateInDb) { - DateTime LastSqlUpdateWithoutMilliseconds = new DateTime(Runtime.ConnectionsService.LastSqlUpdate.Ticks - (Runtime.ConnectionsService.LastSqlUpdate.Ticks % TimeSpan.TicksPerSecond), Runtime.ConnectionsService.LastSqlUpdate.Kind); - return lastUpdateInDb == LastSqlUpdateWithoutMilliseconds; + DateTime lastSqlUpdateWithoutMilliseconds = new DateTime(LastUpdateTime.Ticks - (LastUpdateTime.Ticks % TimeSpan.TicksPerSecond), LastUpdateTime.Kind); + return lastUpdateInDb == lastSqlUpdateWithoutMilliseconds; } private DateTime GetLastUpdateTimeFromDbResponse() @@ -106,8 +105,8 @@ namespace mRemoteNG.Config.Connections { var args = new ConnectionsUpdateAvailableEventArgs(_sqlConnector, _lastDatabaseUpdateTime); ConnectionsUpdateAvailable?.Invoke(this, args); - if(args.Handled) - _lastUpdateTime = _lastDatabaseUpdateTime; + //if(args.Handled) + // LastUpdateTime = _lastDatabaseUpdateTime; } public void Dispose() diff --git a/mRemoteV1/Connection/ConnectionInitiator.cs b/mRemoteV1/Connection/ConnectionInitiator.cs index 19a8813b7..14f6fa778 100644 --- a/mRemoteV1/Connection/ConnectionInitiator.cs +++ b/mRemoteV1/Connection/ConnectionInitiator.cs @@ -1,6 +1,4 @@ -using System; -using System.Windows.Forms; -using mRemoteNG.App; +using mRemoteNG.App; using mRemoteNG.Connection.Protocol; using mRemoteNG.Connection.Protocol.RDP; using mRemoteNG.Container; @@ -8,14 +6,23 @@ using mRemoteNG.Messages; using mRemoteNG.UI.Forms; using mRemoteNG.UI.Panels; using mRemoteNG.UI.Window; +using System; +using System.Collections.Generic; +using System.Windows.Forms; using TabPage = Crownwood.Magic.Controls.TabPage; namespace mRemoteNG.Connection { - public class ConnectionInitiator : IConnectionInitiator + public class ConnectionInitiator : IConnectionInitiator { private readonly PanelAdder _panelAdder = new PanelAdder(); + private readonly List _activeConnections = new List(); + + /// + /// List of unique IDs of the currently active connections + /// + public IEnumerable ActiveConnections => _activeConnections; public void OpenConnection(ContainerInfo containerInfo, ConnectionInfo.Force force = ConnectionInfo.Force.None) { @@ -118,6 +125,7 @@ namespace mRemoteNG.Connection } connectionInfo.OpenConnections.Add(newProtocol); + _activeConnections.Add(connectionInfo.ConstantID); FrmMain.Default.SelectedConnection = connectionInfo; } catch (Exception ex) @@ -210,7 +218,7 @@ namespace mRemoteNG.Connection newProtocol.Closed += ((ConnectionWindow)connectionForm).Prot_Event_Closed; } - private static void SetConnectionEventHandlers(ProtocolBase newProtocol) + private void SetConnectionEventHandlers(ProtocolBase newProtocol) { newProtocol.Disconnected += Prot_Event_Disconnected; newProtocol.Connected += Prot_Event_Connected; @@ -251,7 +259,7 @@ namespace mRemoteNG.Connection } } - private static void Prot_Event_Closed(object sender) + private void Prot_Event_Closed(object sender) { try { @@ -267,6 +275,8 @@ namespace mRemoteNG.Connection Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, string.Format(Language.strConnenctionClosedByUser, connDetail, prot.InterfaceControl.Info.Protocol, Environment.UserName)); prot.InterfaceControl.Info.OpenConnections.Remove(prot); + if (_activeConnections.Contains(prot.InterfaceControl.Info.ConstantID)) + _activeConnections.Remove(prot.InterfaceControl.Info.ConstantID); if (prot.InterfaceControl.Info.PostExtApp == "") return; var extA = Runtime.ExternalToolsService.GetExtAppByName(prot.InterfaceControl.Info.PostExtApp); diff --git a/mRemoteV1/Connection/ConnectionsService.cs b/mRemoteV1/Connection/ConnectionsService.cs index b7554b401..c73708f15 100644 --- a/mRemoteV1/Connection/ConnectionsService.cs +++ b/mRemoteV1/Connection/ConnectionsService.cs @@ -116,6 +116,9 @@ namespace mRemoteNG.Connection ? new SqlConnectionsLoader(_localConnectionPropertiesSerializer, _localConnectionPropertiesDataProvider).Load() : new XmlConnectionsLoader(connectionFileName).Load(); + if (useDatabase) + LastSqlUpdate = DateTime.Now; + if (newConnectionTreeModel == null) { DialogFactory.ShowLoadConnectionsFailedDialog(connectionFileName, "Decrypting connection file failed", IsConnectionsFileLoaded); diff --git a/mRemoteV1/Connection/IConnectionInitiator.cs b/mRemoteV1/Connection/IConnectionInitiator.cs index 5a0ff1d32..485a89409 100644 --- a/mRemoteV1/Connection/IConnectionInitiator.cs +++ b/mRemoteV1/Connection/IConnectionInitiator.cs @@ -1,9 +1,12 @@ using mRemoteNG.Container; +using System.Collections.Generic; namespace mRemoteNG.Connection { public interface IConnectionInitiator { + IEnumerable ActiveConnections { get; } + void OpenConnection(ConnectionInfo connectionInfo); void OpenConnection(ContainerInfo containerInfo, ConnectionInfo.Force force = ConnectionInfo.Force.None); diff --git a/mRemoteV1/Tree/PreviousSessionOpener.cs b/mRemoteV1/Tree/PreviousSessionOpener.cs index 5df20c8ac..ff664b989 100644 --- a/mRemoteV1/Tree/PreviousSessionOpener.cs +++ b/mRemoteV1/Tree/PreviousSessionOpener.cs @@ -1,8 +1,8 @@ -using System; -using System.Linq; -using mRemoteNG.Connection; +using mRemoteNG.Connection; using mRemoteNG.Container; using mRemoteNG.UI.Controls; +using System; +using System.Linq; namespace mRemoteNG.Tree @@ -21,7 +21,12 @@ namespace mRemoteNG.Tree public void Execute(IConnectionTree connectionTree) { var connectionInfoList = connectionTree.GetRootConnectionNode().GetRecursiveChildList().Where(node => !(node is ContainerInfo)); - var previouslyOpenedConnections = connectionInfoList.Where(item => item.PleaseConnect); + var previouslyOpenedConnections = connectionInfoList + .Where(item => + item.PleaseConnect && + // ignore items that have already connected + !_connectionInitiator.ActiveConnections.Contains(item.ConstantID)); + foreach (var connectionInfo in previouslyOpenedConnections) { _connectionInitiator.OpenConnection(connectionInfo); From 083456e33ae1064e415da5bad57ee38956cc61f6 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Fri, 2 Nov 2018 10:29:09 -0500 Subject: [PATCH 06/19] fixed several event handle leaks #1173 --- .../Controls/ConnectionTree/ConnectionTree.cs | 45 +++++++++++-------- mRemoteV1/UI/Window/ConnectionTreeWindow.cs | 14 +++--- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs b/mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs index c7759def3..1af714aaf 100644 --- a/mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs +++ b/mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs @@ -1,16 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using System.Windows.Forms; -using BrightIdeasSoftware; +using BrightIdeasSoftware; using mRemoteNG.App; using mRemoteNG.Config.Putty; using mRemoteNG.Connection; using mRemoteNG.Container; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Windows.Forms; // ReSharper disable ArrangeAccessorOwnerBody namespace mRemoteNG.UI.Controls @@ -43,8 +43,12 @@ namespace mRemoteNG.UI.Controls get { return _connectionTreeModel; } set { + if (_connectionTreeModel == value) + return; + + UnregisterModelUpdateHandlers(_connectionTreeModel); _connectionTreeModel = value; - PopulateTreeView(); + PopulateTreeView(value); } } @@ -156,28 +160,31 @@ namespace mRemoteNG.UI.Controls padding; } - private void PopulateTreeView() + private void PopulateTreeView(ConnectionTreeModel newModel) { - UnregisterModelUpdateHandlers(); - SetObjects(ConnectionTreeModel.RootNodes); - RegisterModelUpdateHandlers(); - NodeSearcher = new NodeSearcher(ConnectionTreeModel); + SetObjects(newModel.RootNodes); + RegisterModelUpdateHandlers(newModel); + NodeSearcher = new NodeSearcher(newModel); ExecutePostSetupActions(); AutoResizeColumn(Columns[0]); } - private void RegisterModelUpdateHandlers() + private void RegisterModelUpdateHandlers(ConnectionTreeModel newModel) { _puttySessionsManager.PuttySessionsCollectionChanged += OnPuttySessionsCollectionChanged; - ConnectionTreeModel.CollectionChanged += HandleCollectionChanged; - ConnectionTreeModel.PropertyChanged += HandleCollectionPropertyChanged; + newModel.CollectionChanged += HandleCollectionChanged; + newModel.PropertyChanged += HandleCollectionPropertyChanged; } - private void UnregisterModelUpdateHandlers() + private void UnregisterModelUpdateHandlers(ConnectionTreeModel oldConnectionTreeModel) { _puttySessionsManager.PuttySessionsCollectionChanged -= OnPuttySessionsCollectionChanged; - ConnectionTreeModel.CollectionChanged -= HandleCollectionChanged; - ConnectionTreeModel.PropertyChanged -= HandleCollectionPropertyChanged; + + if (oldConnectionTreeModel == null) + return; + + oldConnectionTreeModel.CollectionChanged -= HandleCollectionChanged; + oldConnectionTreeModel.PropertyChanged -= HandleCollectionPropertyChanged; } private void OnPuttySessionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) diff --git a/mRemoteV1/UI/Window/ConnectionTreeWindow.cs b/mRemoteV1/UI/Window/ConnectionTreeWindow.cs index cb42408ae..e5976d136 100644 --- a/mRemoteV1/UI/Window/ConnectionTreeWindow.cs +++ b/mRemoteV1/UI/Window/ConnectionTreeWindow.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Linq; -using System.Windows.Forms; using mRemoteNG.App; using mRemoteNG.Config.Connections; using mRemoteNG.Connection; @@ -11,6 +5,12 @@ using mRemoteNG.Themes; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; using mRemoteNG.UI.Controls; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; using WeifenLuo.WinFormsUI.Docking; // ReSharper disable ArrangeAccessorOwnerBody @@ -50,8 +50,6 @@ namespace mRemoteNG.UI.Window ConnectionTree.UseFiltering = Settings.Default.UseFilterSearch; ApplyFiltering(); } - - SetConnectionTreeEventHandlers(); } From dc2d6b81606d7d3964701db23183849abeeea93e Mon Sep 17 00:00:00 2001 From: David Sparer Date: Fri, 2 Nov 2018 10:45:18 -0500 Subject: [PATCH 07/19] added a few debug messages to track db connection loading events --- .../Config/Connections/IConnectionsLoader.cs | 9 +++++++++ .../Multiuser/RemoteConnectionsSyncronizer.cs | 5 +++-- .../Multiuser/SqlConnectionsUpdateChecker.cs | 5 +++-- .../Connections/SqlConnectionsLoader.cs | 2 +- .../Connections/XmlConnectionsLoader.cs | 10 +++++----- mRemoteV1/Connection/ConnectionsService.cs | 19 +++++++++++-------- mRemoteV1/mRemoteV1.csproj | 1 + 7 files changed, 33 insertions(+), 18 deletions(-) create mode 100644 mRemoteV1/Config/Connections/IConnectionsLoader.cs diff --git a/mRemoteV1/Config/Connections/IConnectionsLoader.cs b/mRemoteV1/Config/Connections/IConnectionsLoader.cs new file mode 100644 index 000000000..a4669757d --- /dev/null +++ b/mRemoteV1/Config/Connections/IConnectionsLoader.cs @@ -0,0 +1,9 @@ +using mRemoteNG.Tree; + +namespace mRemoteNG.Config.Connections +{ + public interface IConnectionsLoader + { + ConnectionTreeModel Load(); + } +} \ No newline at end of file diff --git a/mRemoteV1/Config/Connections/Multiuser/RemoteConnectionsSyncronizer.cs b/mRemoteV1/Config/Connections/Multiuser/RemoteConnectionsSyncronizer.cs index a52ffea37..1a4c2075c 100644 --- a/mRemoteV1/Config/Connections/Multiuser/RemoteConnectionsSyncronizer.cs +++ b/mRemoteV1/Config/Connections/Multiuser/RemoteConnectionsSyncronizer.cs @@ -1,6 +1,7 @@ -using System; +using mRemoteNG.App; +using System; using System.Timers; -using mRemoteNG.App; + // ReSharper disable ArrangeAccessorOwnerBody namespace mRemoteNG.Config.Connections.Multiuser diff --git a/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs b/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs index fdd8dc1bb..e649628ca 100644 --- a/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs +++ b/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs @@ -1,11 +1,11 @@ using mRemoteNG.App; +using mRemoteNG.Config.Connections.Multiuser; +using mRemoteNG.Config.DatabaseConnectors; using mRemoteNG.Messages; using System; using System.Data; using System.Data.SqlClient; using System.Threading; -using mRemoteNG.Config.Connections.Multiuser; -using mRemoteNG.Config.DatabaseConnectors; namespace mRemoteNG.Config.Connections { @@ -104,6 +104,7 @@ namespace mRemoteNG.Config.Connections public event ConnectionsUpdateAvailableEventHandler ConnectionsUpdateAvailable; private void RaiseConnectionsUpdateAvailableEvent() { + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Remote connection update is available"); var args = new ConnectionsUpdateAvailableEventArgs(_sqlConnector, _lastDatabaseUpdateTime); ConnectionsUpdateAvailable?.Invoke(this, args); if(args.Handled) diff --git a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs index eb2107d8f..5e8652d20 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs @@ -6,7 +6,7 @@ using mRemoteNG.Tree; namespace mRemoteNG.Config.Connections { - public class SqlConnectionsLoader + public class SqlConnectionsLoader : IConnectionsLoader { public ConnectionTreeModel Load() { diff --git a/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs b/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs index c79e13d81..71a02ebee 100644 --- a/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/XmlConnectionsLoader.cs @@ -1,14 +1,14 @@ -using System; -using System.IO; -using System.Security; -using mRemoteNG.Config.DataProviders; +using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers.Xml; using mRemoteNG.Tools; using mRemoteNG.Tree; +using System; +using System.IO; +using System.Security; namespace mRemoteNG.Config.Connections { - public class XmlConnectionsLoader + public class XmlConnectionsLoader : IConnectionsLoader { private readonly string _connectionFilePath; diff --git a/mRemoteV1/Connection/ConnectionsService.cs b/mRemoteV1/Connection/ConnectionsService.cs index c6240ec92..e2d90c599 100644 --- a/mRemoteV1/Connection/ConnectionsService.cs +++ b/mRemoteV1/Connection/ConnectionsService.cs @@ -1,8 +1,4 @@ -using System; -using System.IO; -using System.Threading; -using System.Windows.Forms; -using mRemoteNG.App; +using mRemoteNG.App; using mRemoteNG.App.Info; using mRemoteNG.Config.Connections; using mRemoteNG.Config.Connections.Multiuser; @@ -14,6 +10,10 @@ using mRemoteNG.Tools; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; using mRemoteNG.UI; +using System; +using System.IO; +using System.Threading; +using System.Windows.Forms; namespace mRemoteNG.Connection { @@ -106,9 +106,11 @@ namespace mRemoteNG.Connection var oldConnectionTreeModel = ConnectionTreeModel; var oldIsUsingDatabaseValue = UsingDatabase; - var newConnectionTreeModel = useDatabase - ? new SqlConnectionsLoader().Load() - : new XmlConnectionsLoader(connectionFileName).Load(); + var connectionLoader = useDatabase + ? (IConnectionsLoader)new SqlConnectionsLoader() + : new XmlConnectionsLoader(connectionFileName); + + var newConnectionTreeModel = connectionLoader.Load(); if (newConnectionTreeModel == null) { @@ -129,6 +131,7 @@ namespace mRemoteNG.Connection ConnectionTreeModel = newConnectionTreeModel; UpdateCustomConsPathSetting(connectionFileName); RaiseConnectionsLoadedEvent(oldConnectionTreeModel, newConnectionTreeModel, oldIsUsingDatabaseValue, useDatabase, connectionFileName); + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Connections loaded using {connectionLoader.GetType().Name}"); } /// diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index 12ab41d8c..544264735 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -136,6 +136,7 @@ + From 6c6a82f8e694a7d6f60761cfceac33ae4ae6644d Mon Sep 17 00:00:00 2001 From: David Sparer Date: Fri, 2 Nov 2018 11:03:25 -0500 Subject: [PATCH 08/19] minor cleanup --- .../Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs b/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs index 98a05992d..af3ab56fe 100644 --- a/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs +++ b/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs @@ -106,8 +106,6 @@ namespace mRemoteNG.Config.Connections Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Remote connection update is available"); var args = new ConnectionsUpdateAvailableEventArgs(_sqlConnector, _lastDatabaseUpdateTime); ConnectionsUpdateAvailable?.Invoke(this, args); - //if(args.Handled) - // LastUpdateTime = _lastDatabaseUpdateTime; } public void Dispose() From 78bf40282a8b9ce8e31fd9bb09d48a321817d08c Mon Sep 17 00:00:00 2001 From: David Sparer Date: Fri, 2 Nov 2018 11:30:39 -0500 Subject: [PATCH 09/19] the IsExapnded property is no longer saved to the DB --- .../Connections/SqlConnectionsLoader.cs | 8 ++++++-- .../Config/Connections/SqlConnectionsSaver.cs | 6 +++++- .../MsSql/DataTableDeserializer.cs | 20 ++++++++----------- .../MsSql/DataTableSerializer.cs | 5 ++--- .../MsSql/LocalConnectionPropertiesModel.cs | 12 +++++++++++ .../LocalConnectionPropertiesXmlSerializer.cs | 6 ++++-- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs index 5ada38f8f..9e7866c67 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs @@ -49,8 +49,12 @@ namespace mRemoteNG.Config.Connections con => con.ConstantID, locals => locals.ConnectionId, (con, locals) => new {Connection = con, LocalProperties = locals}) - .ForEach(x => - x.Connection.PleaseConnect = x.LocalProperties.Connected); + .ForEach(x => + { + x.Connection.PleaseConnect = x.LocalProperties.Connected; + if (x.Connection is ContainerInfo container) + container.IsExpanded = x.LocalProperties.Expanded; + }); } } } \ No newline at end of file diff --git a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs index 859ed81ce..d73f9fa73 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs @@ -67,6 +67,8 @@ namespace mRemoteNG.Config.Connections UpdateConnectionsTable(rootTreeNode, sqlConnector); UpdateUpdatesTable(sqlConnector); } + + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Saved connections to database"); } private void UpdateLocalConnectionProperties(ContainerInfo rootNode) @@ -74,11 +76,13 @@ namespace mRemoteNG.Config.Connections var a = rootNode.GetRecursiveChildList().Select(info => new LocalConnectionPropertiesModel { ConnectionId = info.ConstantID, - Connected = info.OpenConnections.Count > 0 + Connected = info.OpenConnections.Count > 0, + Expanded = info is ContainerInfo c && c.IsExpanded }); var serializedProperties = _localPropertiesSerializer.Serialize(a); _dataProvider.Save(serializedProperties); + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Saved local connection properties"); } private void UpdateRootNodeTable(RootNodeInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector) diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs index ce47ba668..f3a58d180 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using mRemoteNG.App; +using mRemoteNG.App; using mRemoteNG.Connection; using mRemoteNG.Connection.Protocol; using mRemoteNG.Connection.Protocol.Http; @@ -12,10 +8,14 @@ using mRemoteNG.Connection.Protocol.VNC; using mRemoteNG.Container; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; namespace mRemoteNG.Config.Serializers.MsSql { - public class DataTableDeserializer : IDeserializer + public class DataTableDeserializer : IDeserializer { public ConnectionTreeModel Deserialize(DataTable table) { @@ -34,10 +34,10 @@ namespace mRemoteNG.Config.Serializers.MsSql switch ((string)row["Type"]) { case "Connection": - nodeList.Add(DeserializeConnectionInfo(row)); + nodeList.Add(DeserializeConnectionInfo(row)); break; case "Container": - nodeList.Add(DeserializeContainerInfo(row)); + nodeList.Add(DeserializeContainerInfo(row)); break; } } @@ -68,10 +68,6 @@ namespace mRemoteNG.Config.Serializers.MsSql // The Parent object is linked properly later in CreateNodeHierarchy() //connectionInfo.Parent.ConstantID = (string)dataRow["ParentID"]; - var info = connectionInfo as ContainerInfo; - if(info != null) - info.IsExpanded = (bool)dataRow["Expanded"]; - connectionInfo.Description = (string)dataRow["Description"]; connectionInfo.Icon = (string)dataRow["Icon"]; connectionInfo.Panel = (string)dataRow["Panel"]; diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs index 5e4af0731..c829deaf8 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs @@ -204,8 +204,7 @@ namespace mRemoteNG.Config.Serializers.MsSql dataRow["ParentID"] = connectionInfo.Parent?.ConstantID ?? ""; dataRow["PositionID"] = _currentNodeIndex; dataRow["LastChange"] = (SqlDateTime)DateTime.Now; - var info = connectionInfo as ContainerInfo; - dataRow["Expanded"] = info != null && info.IsExpanded; + dataRow["Expanded"] = false; // TODO: this column can eventually be removed. we now save this property locally dataRow["Description"] = connectionInfo.Description; dataRow["Icon"] = connectionInfo.Icon; dataRow["Panel"] = connectionInfo.Panel; @@ -239,7 +238,7 @@ namespace mRemoteNG.Config.Serializers.MsSql dataRow["RedirectSound"] = connectionInfo.RedirectSound; dataRow["SoundQuality"] = connectionInfo.SoundQuality; dataRow["RedirectKeys"] = connectionInfo.RedirectKeys; - dataRow["Connected"] = false; // TODO: this column can eventually be removed + dataRow["Connected"] = false; // TODO: this column can eventually be removed. we now save this property locally dataRow["PreExtApp"] = connectionInfo.PreExtApp; dataRow["PostExtApp"] = connectionInfo.PostExtApp; dataRow["MacAddress"] = connectionInfo.MacAddress; diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesModel.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesModel.cs index 2209d3f60..f8cdb35f4 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesModel.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesModel.cs @@ -2,7 +2,19 @@ { public class LocalConnectionPropertiesModel { + /// + /// The unique Id of this tree node + /// public string ConnectionId { get; set; } + + /// + /// Indicates whether this connection is connected + /// public bool Connected { get; set; } + + /// + /// Indicates whether this container is expanded in the tree + /// + public bool Expanded { get; set; } } } diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesXmlSerializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesXmlSerializer.cs index 542b3f2ef..78a82371c 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesXmlSerializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/LocalConnectionPropertiesXmlSerializer.cs @@ -16,7 +16,8 @@ namespace mRemoteNG.Config.Serializers.MsSql var localConnections = models .Select(m => new XElement("Node", new XAttribute("ConnectionId", m.ConnectionId), - new XAttribute("Connected", m.Connected))); + new XAttribute("Connected", m.Connected), + new XAttribute("Expanded", m.Expanded))); var root = new XElement("LocalConnections", localConnections); var xdoc = new XDocument(new XDeclaration("1.0", "utf-8", null), root); @@ -35,7 +36,8 @@ namespace mRemoteNG.Config.Serializers.MsSql .Select(e => new LocalConnectionPropertiesModel { ConnectionId = e.Attribute("ConnectionId")?.Value, - Connected = bool.Parse(e.Attribute("Connected")?.Value ?? "False") + Connected = bool.Parse(e.Attribute("Connected")?.Value ?? "False"), + Expanded = bool.Parse(e.Attribute("Expanded")?.Value ?? "False") }); } From 0427956e8bb2f5e6f111962016b05f8c7334fee1 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sat, 3 Nov 2018 10:34:30 -0500 Subject: [PATCH 10/19] we can now prevent saving to the database when a save was triggered for a local-only property change this will prevent unnecessary db saves --- .../Config/Connections/CsvConnectionsSaver.cs | 2 +- .../Connections/SaveConnectionsOnEdit.cs | 6 +-- .../Config/Connections/SqlConnectionsSaver.cs | 24 ++++++++- .../Config/Connections/XmlConnectionsSaver.cs | 2 +- .../Config/CredentialRepositoryListSaver.cs | 2 +- mRemoteV1/Config/ISaver.cs | 2 +- .../Connection/AbstractConnectionRecord.cs | 2 +- mRemoteV1/Connection/ConnectionsService.cs | 54 +++++++++++++------ mRemoteV1/Container/ContainerInfo.cs | 14 +++-- 9 files changed, 80 insertions(+), 28 deletions(-) diff --git a/mRemoteV1/Config/Connections/CsvConnectionsSaver.cs b/mRemoteV1/Config/Connections/CsvConnectionsSaver.cs index 28e7b79e2..598cea19f 100644 --- a/mRemoteV1/Config/Connections/CsvConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/CsvConnectionsSaver.cs @@ -24,7 +24,7 @@ namespace mRemoteNG.Config.Connections _saveFilter = saveFilter; } - public void Save(ConnectionTreeModel connectionTreeModel) + public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "") { var csvConnectionsSerializer = new CsvConnectionsSerializerMremotengFormat(_saveFilter, Runtime.CredentialProviderCatalog); var dataProvider = new FileDataProvider(_connectionFileName); diff --git a/mRemoteV1/Config/Connections/SaveConnectionsOnEdit.cs b/mRemoteV1/Config/Connections/SaveConnectionsOnEdit.cs index 038b7fff9..d36648af3 100644 --- a/mRemoteV1/Config/Connections/SaveConnectionsOnEdit.cs +++ b/mRemoteV1/Config/Connections/SaveConnectionsOnEdit.cs @@ -33,7 +33,7 @@ namespace mRemoteNG.Config.Connections private void ConnectionTreeModelOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) { - SaveConnectionOnEdit(); + SaveConnectionOnEdit(propertyChangedEventArgs.PropertyName); } private void ConnectionTreeModelOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) @@ -41,14 +41,14 @@ namespace mRemoteNG.Config.Connections SaveConnectionOnEdit(); } - private void SaveConnectionOnEdit() + private void SaveConnectionOnEdit(string propertyName = "") { if (!mRemoteNG.Settings.Default.SaveConnectionsAfterEveryEdit) return; if (FrmMain.Default.IsClosing) return; - _connectionsService.SaveConnectionsAsync(); + _connectionsService.SaveConnectionsAsync(propertyName); } } } diff --git a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs index d73f9fa73..5c1b295a6 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs @@ -18,6 +18,7 @@ using System.Data.SqlClient; using System.Globalization; using System.Linq; using System.Security; +using mRemoteNG.Connection; namespace mRemoteNG.Config.Connections { @@ -40,12 +41,19 @@ namespace mRemoteNG.Config.Connections _dataProvider = localPropertiesDataProvider.ThrowIfNull(nameof(localPropertiesDataProvider)); } - public void Save(ConnectionTreeModel connectionTreeModel) + public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "") { var rootTreeNode = connectionTreeModel.RootNodes.OfType().First(); UpdateLocalConnectionProperties(rootTreeNode); + if (PropertyIsLocalOnly(propertyNameTrigger)) + { + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, + $"Property {propertyNameTrigger} is local only. Not saving to database."); + return; + } + if (SqlUserIsReadOnly()) { Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, "Trying to save connection tree but the SQL read only checkbox is checked, aborting!"); @@ -71,6 +79,20 @@ namespace mRemoteNG.Config.Connections Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Saved connections to database"); } + /// + /// Determines if a given property name should be only saved + /// locally. + /// + /// + /// The name of the property that triggered the save event + /// + /// + private bool PropertyIsLocalOnly(string property) + { + return property == nameof(ConnectionInfo.OpenConnections) || + property == nameof(ContainerInfo.IsExpanded); + } + private void UpdateLocalConnectionProperties(ContainerInfo rootNode) { var a = rootNode.GetRecursiveChildList().Select(info => new LocalConnectionPropertiesModel diff --git a/mRemoteV1/Config/Connections/XmlConnectionsSaver.cs b/mRemoteV1/Config/Connections/XmlConnectionsSaver.cs index 08fa5c0c0..9e6d90cb0 100644 --- a/mRemoteV1/Config/Connections/XmlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/XmlConnectionsSaver.cs @@ -27,7 +27,7 @@ namespace mRemoteNG.Config.Connections _saveFilter = saveFilter; } - public void Save(ConnectionTreeModel connectionTreeModel) + public void Save(ConnectionTreeModel connectionTreeModel, string propertyNameTrigger = "") { try { diff --git a/mRemoteV1/Config/CredentialRepositoryListSaver.cs b/mRemoteV1/Config/CredentialRepositoryListSaver.cs index c5000024c..6200eb729 100644 --- a/mRemoteV1/Config/CredentialRepositoryListSaver.cs +++ b/mRemoteV1/Config/CredentialRepositoryListSaver.cs @@ -18,7 +18,7 @@ namespace mRemoteNG.Config _dataProvider = dataProvider; } - public void Save(IEnumerable repositories) + public void Save(IEnumerable repositories, string propertyNameTrigger = "") { var serializer = new CredentialRepositoryListSerializer(); var data = serializer.Serialize(repositories); diff --git a/mRemoteV1/Config/ISaver.cs b/mRemoteV1/Config/ISaver.cs index cf4caea8d..1f9700be8 100644 --- a/mRemoteV1/Config/ISaver.cs +++ b/mRemoteV1/Config/ISaver.cs @@ -2,6 +2,6 @@ { public interface ISaver { - void Save(T model); + void Save(T model, string propertyNameTrigger = ""); } } \ No newline at end of file diff --git a/mRemoteV1/Connection/AbstractConnectionRecord.cs b/mRemoteV1/Connection/AbstractConnectionRecord.cs index 8020169a4..9a29be75b 100644 --- a/mRemoteV1/Connection/AbstractConnectionRecord.cs +++ b/mRemoteV1/Connection/AbstractConnectionRecord.cs @@ -674,7 +674,7 @@ namespace mRemoteNG.Connection PropertyChanged?.Invoke(sender, new PropertyChangedEventArgs(args.PropertyName)); } - private void SetField(ref T field, T value, string propertyName = null) + protected void SetField(ref T field, T value, string propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return; field = value; diff --git a/mRemoteV1/Connection/ConnectionsService.cs b/mRemoteV1/Connection/ConnectionsService.cs index 3042b97d2..93fa7bcc1 100644 --- a/mRemoteV1/Connection/ConnectionsService.cs +++ b/mRemoteV1/Connection/ConnectionsService.cs @@ -16,6 +16,7 @@ using System; using System.IO; using System.Threading; using System.Windows.Forms; +using mRemoteNG.Config; namespace mRemoteNG.Connection { @@ -187,7 +188,17 @@ namespace mRemoteNG.Connection /// /// /// Bypasses safety checks that prevent saving if a connection file isn't loaded. - public void SaveConnections(ConnectionTreeModel connectionTreeModel, bool useDatabase, SaveFilter saveFilter, string connectionFileName, bool forceSave = false) + /// + /// Optional. The name of the property that triggered + /// this save. + /// + public void SaveConnections( + ConnectionTreeModel connectionTreeModel, + bool useDatabase, + SaveFilter saveFilter, + string connectionFileName, + bool forceSave = false, + string propertyNameTrigger = "") { if (connectionTreeModel == null) return; @@ -207,10 +218,13 @@ namespace mRemoteNG.Connection RemoteConnectionsSyncronizer?.Disable(); var previouslyUsingDatabase = UsingDatabase; - if (useDatabase) - new SqlConnectionsSaver(saveFilter, _localConnectionPropertiesSerializer, _localConnectionPropertiesDataProvider).Save(connectionTreeModel); - else - new XmlConnectionsSaver(connectionFileName, saveFilter).Save(connectionTreeModel); + + var saver = useDatabase + ? (ISaver)new SqlConnectionsSaver(saveFilter, _localConnectionPropertiesSerializer, + _localConnectionPropertiesDataProvider) + : new XmlConnectionsSaver(connectionFileName, saveFilter); + + saver.Save(connectionTreeModel, propertyNameTrigger); if (UsingDatabase) LastSqlUpdate = DateTime.Now; @@ -230,7 +244,14 @@ namespace mRemoteNG.Connection } } - public void SaveConnectionsAsync() + /// + /// Save the currently loaded connections asynchronously + /// + /// + /// Optional. The name of the property that triggered + /// this save. + /// + public void SaveConnectionsAsync(string propertyNameTrigger = "") { if (_batchingSaves) { @@ -238,19 +259,22 @@ namespace mRemoteNG.Connection return; } - var t = new Thread(SaveConnectionsBGd); + var t = new Thread(() => + { + lock (SaveLock) + { + SaveConnections( + ConnectionTreeModel, + UsingDatabase, + new SaveFilter(), + ConnectionFileName, + propertyNameTrigger: propertyNameTrigger); + } + }); t.SetApartmentState(ApartmentState.STA); t.Start(); } - private void SaveConnectionsBGd() - { - lock (SaveLock) - { - SaveConnections(); - } - } - public string GetStartupConnectionFileName() { return Settings.Default.LoadConsFromCustomLocation == false diff --git a/mRemoteV1/Container/ContainerInfo.cs b/mRemoteV1/Container/ContainerInfo.cs index c6c6b4c8f..989fc9113 100644 --- a/mRemoteV1/Container/ContainerInfo.cs +++ b/mRemoteV1/Container/ContainerInfo.cs @@ -12,13 +12,19 @@ namespace mRemoteNG.Container [DefaultProperty("Name")] public class ContainerInfo : ConnectionInfo, INotifyCollectionChanged { - [Browsable(false)] + private bool _isExpanded; + + [Browsable(false)] public List Children { get; } = new List(); - [Category(""), Browsable(false), ReadOnly(false), Bindable(false), DefaultValue(""), DesignOnly(false)] - public bool IsExpanded { get; set; } + [Category(""), Browsable(false), ReadOnly(false), Bindable(false), DefaultValue(""), DesignOnly(false)] + public bool IsExpanded + { + get => _isExpanded; + set => SetField(ref _isExpanded, value, "IsExpanded"); + } - [Browsable(false)] + [Browsable(false)] public override bool IsContainer { get { return true; } set {} } public ContainerInfo(string uniqueId) From 19bb8f7595b02f819c60705d64f3652f046057e3 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sat, 3 Nov 2018 11:02:26 -0500 Subject: [PATCH 11/19] refactored db classes to expose tblRoot data --- .../Connections/SqlConnectionsLoader.cs | 7 +++++-- .../Config/Connections/SqlConnectionsSaver.cs | 4 +++- .../MsSql/SqlConnectionListMetaData.cs | 12 +++++++++++ .../MsSql/SqlDatabaseMetaDataRetriever.cs} | 21 ++++++++++++------- .../Versioning/SqlDatabaseVersionVerifier.cs | 6 ++---- mRemoteV1/mRemoteV1.csproj | 3 ++- 6 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs rename mRemoteV1/Config/Serializers/{Versioning/SqlDatabaseVersionRetriever.cs => ConnectionSerializers/MsSql/SqlDatabaseMetaDataRetriever.cs} (57%) diff --git a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs index 9e7866c67..4ecb47f9d 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs @@ -29,10 +29,13 @@ namespace mRemoteNG.Config.Connections { var connector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings(); var dataProvider = new SqlDataProvider(connector); + var metaDataRetriever = new SqlDatabaseMetaDataRetriever(); var databaseVersionVerifier = new SqlDatabaseVersionVerifier(connector); - databaseVersionVerifier.VerifyDatabaseVersion(); - var dataTable = dataProvider.Load(); var deserializer = new DataTableDeserializer(); + + var metaData = metaDataRetriever.GetDatabaseMetaData(connector); + databaseVersionVerifier.VerifyDatabaseVersion(metaData.ConfVersion); + var dataTable = dataProvider.Load(); var connectionTree = deserializer.Deserialize(dataTable); ApplyLocalConnectionProperties(connectionTree.RootNodes.First(i => i is RootNodeInfo)); return connectionTree; diff --git a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs index 5c1b295a6..08f89bc3f 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs @@ -64,8 +64,10 @@ namespace mRemoteNG.Config.Connections { sqlConnector.Connect(); var databaseVersionVerifier = new SqlDatabaseVersionVerifier(sqlConnector); + var metaDataRetriever = new SqlDatabaseMetaDataRetriever(); + var metaData = metaDataRetriever.GetDatabaseMetaData(sqlConnector); - if (!databaseVersionVerifier.VerifyDatabaseVersion()) + if (!databaseVersionVerifier.VerifyDatabaseVersion(metaData.ConfVersion)) { Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.strErrorConnectionListSaveFailed); return; diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs new file mode 100644 index 000000000..8cf28d0ea --- /dev/null +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs @@ -0,0 +1,12 @@ +using System; + +namespace mRemoteNG.Config.Serializers.MsSql +{ + public class SqlConnectionListMetaData + { + public string Name { get; set; } + public string Protected { get; set; } + public bool Export { get; set; } + public Version ConfVersion { get; set; } + } +} diff --git a/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionRetriever.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlDatabaseMetaDataRetriever.cs similarity index 57% rename from mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionRetriever.cs rename to mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlDatabaseMetaDataRetriever.cs index 61c1a778c..8c97292d1 100644 --- a/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionRetriever.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlDatabaseMetaDataRetriever.cs @@ -5,13 +5,13 @@ using mRemoteNG.App; using mRemoteNG.Config.DatabaseConnectors; using mRemoteNG.Messages; -namespace mRemoteNG.Config.Serializers.Versioning +namespace mRemoteNG.Config.Serializers.MsSql { - public class SqlDatabaseVersionRetriever + public class SqlDatabaseMetaDataRetriever { - public Version GetDatabaseVersion(SqlDatabaseConnector sqlDatabaseConnector) + public SqlConnectionListMetaData GetDatabaseMetaData(SqlDatabaseConnector sqlDatabaseConnector) { - Version databaseVersion; + SqlConnectionListMetaData metaData; SqlDataReader sqlDataReader = null; try { @@ -20,10 +20,17 @@ namespace mRemoteNG.Config.Serializers.Versioning sqlDatabaseConnector.Connect(); sqlDataReader = sqlCommand.ExecuteReader(); if (!sqlDataReader.HasRows) - return new Version(); // assume new empty database + return null; // assume new empty database else sqlDataReader.Read(); - databaseVersion = new Version(Convert.ToString(sqlDataReader["confVersion"], CultureInfo.InvariantCulture)); + + metaData = new SqlConnectionListMetaData + { + Name = sqlDataReader["Name"] as string ?? "", + Protected = sqlDataReader["Protected"] as string ?? "", + Export = (bool)sqlDataReader["Export"], + ConfVersion = new Version(Convert.ToString(sqlDataReader["confVersion"], CultureInfo.InvariantCulture)) + }; } catch (Exception ex) { @@ -35,7 +42,7 @@ namespace mRemoteNG.Config.Serializers.Versioning if (sqlDataReader != null && !sqlDataReader.IsClosed) sqlDataReader.Close(); } - return databaseVersion; + return metaData; } } } \ No newline at end of file diff --git a/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs b/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs index a2ede1bb4..b269f27e4 100644 --- a/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs +++ b/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs @@ -9,7 +9,6 @@ namespace mRemoteNG.Config.Serializers.Versioning public class SqlDatabaseVersionVerifier { private readonly SqlDatabaseConnector _sqlDatabaseConnector; - private readonly SqlDatabaseVersionRetriever _versionRetriever; public SqlDatabaseVersionVerifier(SqlDatabaseConnector sqlDatabaseConnector) { @@ -17,15 +16,14 @@ namespace mRemoteNG.Config.Serializers.Versioning throw new ArgumentNullException(nameof(sqlDatabaseConnector)); _sqlDatabaseConnector = sqlDatabaseConnector; - _versionRetriever = new SqlDatabaseVersionRetriever(); } - public bool VerifyDatabaseVersion() + public bool VerifyDatabaseVersion(Version dbVersion) { var isVerified = false; try { - var databaseVersion = _versionRetriever.GetDatabaseVersion(_sqlDatabaseConnector); + var databaseVersion = dbVersion; if (databaseVersion.Equals(new Version())) { diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index 968722d7c..b29cd1710 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -162,6 +162,7 @@ + @@ -179,7 +180,7 @@ - + From abcab2aadf197a1ec4de0b7887e9f44dd29695a3 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sat, 3 Nov 2018 18:28:53 -0500 Subject: [PATCH 12/19] sql now encrypts and decrypts passwords --- .../Serializers/DataTableDeserializerTests.cs | 22 +++++++---- .../Serializers/DataTableSerializerTests.cs | 8 +++- mRemoteV1/App/Info/SettingsFileInfo.cs | 3 +- .../Connections/SqlConnectionsLoader.cs | 30 ++++++++++++++- .../Config/Connections/SqlConnectionsSaver.cs | 10 +++-- .../MsSql/DataTableDeserializer.cs | 37 +++++++++++++++++-- .../MsSql/DataTableSerializer.cs | 18 ++++++--- .../MsSql/SqlConnectionListMetaData.cs | 1 + mRemoteV1/Connection/ConnectionsService.cs | 3 +- mRemoteV1/mRemoteV1.csproj | 2 +- 10 files changed, 108 insertions(+), 26 deletions(-) diff --git a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs index 85d149db8..8d7cc77f1 100644 --- a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs @@ -1,11 +1,12 @@ using System.Data; -using System.Linq; -using mRemoteNG.Config.Serializers; +using System.Security; using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Connection; using mRemoteNG.Security; +using mRemoteNG.Security.SymmetricEncryption; using mRemoteNG.Tree; using mRemoteNGTests.TestHelpers; +using NSubstitute; using NUnit.Framework; namespace mRemoteNGTests.Config.Serializers @@ -13,30 +14,37 @@ namespace mRemoteNGTests.Config.Serializers public class DataTableDeserializerTests { private DataTableDeserializer _deserializer; + private ICryptographyProvider _cryptographyProvider; + + [SetUp] + public void Setup() + { + _cryptographyProvider = new LegacyRijndaelCryptographyProvider(); + } [Test] public void WeCanDeserializeATree() { var model = CreateConnectionTreeModel(); var dataTable = CreateDataTable(model.RootNodes[0]); - _deserializer = new DataTableDeserializer(); + _deserializer = new DataTableDeserializer(_cryptographyProvider, new SecureString()); var output = _deserializer.Deserialize(dataTable); - Assert.That(output.GetRecursiveChildList().Count(), Is.EqualTo(model.GetRecursiveChildList().Count())); + Assert.That(output.GetRecursiveChildList().Count, Is.EqualTo(model.GetRecursiveChildList().Count)); } [Test] public void WeCanDeserializeASingleEntry() { var dataTable = CreateDataTable(new ConnectionInfo()); - _deserializer = new DataTableDeserializer(); + _deserializer = new DataTableDeserializer(_cryptographyProvider, new SecureString()); var output = _deserializer.Deserialize(dataTable); - Assert.That(output.GetRecursiveChildList().Count(), Is.EqualTo(1)); + Assert.That(output.GetRecursiveChildList().Count, Is.EqualTo(1)); } private DataTable CreateDataTable(ConnectionInfo tableContent) { - var serializer = new DataTableSerializer(new SaveFilter()); + var serializer = new DataTableSerializer(new SaveFilter(), _cryptographyProvider, new SecureString()); return serializer.Serialize(tableContent); } diff --git a/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs index ab42493e1..6292419a0 100644 --- a/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs @@ -1,12 +1,15 @@ using System.Linq; +using System.Security; using mRemoteNG.Config.Serializers; using mRemoteNG.Config.Serializers.MsSql; using mRemoteNG.Connection; using mRemoteNG.Container; using mRemoteNG.Security; +using mRemoteNG.Security.SymmetricEncryption; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; using mRemoteNGTests.TestHelpers; +using NSubstitute; using NUnit.Framework; namespace mRemoteNGTests.Config.Serializers @@ -20,7 +23,10 @@ namespace mRemoteNGTests.Config.Serializers public void Setup() { _saveFilter = new SaveFilter(); - _dataTableSerializer = new DataTableSerializer(_saveFilter); + _dataTableSerializer = new DataTableSerializer( + _saveFilter, + new LegacyRijndaelCryptographyProvider(), + new SecureString()); } [Test] diff --git a/mRemoteV1/App/Info/SettingsFileInfo.cs b/mRemoteV1/App/Info/SettingsFileInfo.cs index 19776123c..a91d9497a 100644 --- a/mRemoteV1/App/Info/SettingsFileInfo.cs +++ b/mRemoteV1/App/Info/SettingsFileInfo.cs @@ -2,12 +2,13 @@ using System.IO; using System.Reflection; using System.Windows.Forms; +using mRemoteNG.Connection; namespace mRemoteNG.App.Info { public static class SettingsFileInfo { - private static readonly string ExePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); + private static readonly string ExePath = Path.GetDirectoryName(Assembly.GetAssembly(typeof(ConnectionInfo))?.Location); public static string SettingsPath => Runtime.IsPortableEdition ? ExePath : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + Application.ProductName; public static string LayoutFileName { get; } = "pnlLayout.xml"; diff --git a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs index 4ecb47f9d..ed566424e 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsLoader.cs @@ -1,4 +1,5 @@ -using mRemoteNG.Config.DatabaseConnectors; +using System; +using mRemoteNG.Config.DatabaseConnectors; using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers; using mRemoteNG.Config.Serializers.MsSql; @@ -9,6 +10,10 @@ using mRemoteNG.Tree; using mRemoteNG.Tree.Root; using System.Collections.Generic; using System.Linq; +using System.Security; +using mRemoteNG.Security; +using mRemoteNG.Security.Authentication; +using mRemoteNG.Security.SymmetricEncryption; namespace mRemoteNG.Config.Connections { @@ -17,6 +22,9 @@ namespace mRemoteNG.Config.Connections private readonly IDeserializer> _localConnectionPropertiesDeserializer; private readonly IDataProvider _dataProvider; + public Func> AuthenticationRequestor { get; set; } = + () => MiscTools.PasswordDialog("", false); + public SqlConnectionsLoader( IDeserializer> localConnectionPropertiesDeserializer, IDataProvider dataProvider) @@ -31,16 +39,34 @@ namespace mRemoteNG.Config.Connections var dataProvider = new SqlDataProvider(connector); var metaDataRetriever = new SqlDatabaseMetaDataRetriever(); var databaseVersionVerifier = new SqlDatabaseVersionVerifier(connector); - var deserializer = new DataTableDeserializer(); + var cryptoProvider = new LegacyRijndaelCryptographyProvider(); var metaData = metaDataRetriever.GetDatabaseMetaData(connector); + var decryptionKey = GetDecryptionKey(metaData); + + if (!decryptionKey.Any()) + throw new Exception("Could not load SQL connections"); + databaseVersionVerifier.VerifyDatabaseVersion(metaData.ConfVersion); var dataTable = dataProvider.Load(); + var deserializer = new DataTableDeserializer(cryptoProvider, decryptionKey.First()); var connectionTree = deserializer.Deserialize(dataTable); ApplyLocalConnectionProperties(connectionTree.RootNodes.First(i => i is RootNodeInfo)); return connectionTree; } + private Optional GetDecryptionKey(SqlConnectionListMetaData metaData) + { + var cryptographyProvider = new LegacyRijndaelCryptographyProvider(); + var cipherText = metaData.Protected; + var authenticator = new PasswordAuthenticator(cryptographyProvider, cipherText, AuthenticationRequestor); + var authenticated = authenticator.Authenticate(new RootNodeInfo(RootNodeType.Connection).DefaultPassword.ConvertToSecureString()); + + if (authenticated) + return authenticator.LastAuthenticatedPassword; + return Optional.Empty; + } + private void ApplyLocalConnectionProperties(ContainerInfo rootNode) { var localPropertiesXml = _dataProvider.Load(); diff --git a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs index 08f89bc3f..922bee10f 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs @@ -149,13 +149,15 @@ namespace mRemoteNG.Config.Connections } } - private void UpdateConnectionsTable(ContainerInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector) + private void UpdateConnectionsTable(RootNodeInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector) { - var sqlQuery = new SqlCommand("DELETE FROM tblCons", sqlDatabaseConnector.SqlConnection); - sqlQuery.ExecuteNonQuery(); - var serializer = new DataTableSerializer(_saveFilter); + var cryptoProvider = new LegacyRijndaelCryptographyProvider(); + var serializer = new DataTableSerializer(_saveFilter, cryptoProvider, rootTreeNode.PasswordString.ConvertToSecureString()); var dataTable = serializer.Serialize(rootTreeNode); var dataProvider = new SqlDataProvider(sqlDatabaseConnector); + + var sqlQuery = new SqlCommand("DELETE FROM tblCons", sqlDatabaseConnector.SqlConnection); + sqlQuery.ExecuteNonQuery(); dataProvider.Save(dataTable); } diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs index f3a58d180..1938ad0d0 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs @@ -12,11 +12,24 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Security; +using mRemoteNG.Security; +using mRemoteNG.Security.SymmetricEncryption; +using mRemoteNG.Tools; namespace mRemoteNG.Config.Serializers.MsSql { public class DataTableDeserializer : IDeserializer { + private readonly ICryptographyProvider _cryptographyProvider; + private readonly SecureString _decryptionKey; + + public DataTableDeserializer(ICryptographyProvider cryptographyProvider, SecureString decryptionKey) + { + _cryptographyProvider = cryptographyProvider.ThrowIfNull(nameof(cryptographyProvider)); + _decryptionKey = decryptionKey.ThrowIfNull(nameof(decryptionKey)); + } + public ConnectionTreeModel Deserialize(DataTable table) { var connectionList = CreateNodesFromTable(table); @@ -73,7 +86,7 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.Panel = (string)dataRow["Panel"]; connectionInfo.Username = (string)dataRow["Username"]; connectionInfo.Domain = (string)dataRow["DomainName"]; - connectionInfo.Password = (string)dataRow["Password"]; + connectionInfo.Password = DecryptValue((string)dataRow["Password"]); connectionInfo.Hostname = (string)dataRow["Hostname"]; connectionInfo.Protocol = (ProtocolType)Enum.Parse(typeof(ProtocolType), (string)dataRow["Protocol"]); connectionInfo.PuttySession = (string)dataRow["PuttySession"]; @@ -113,7 +126,7 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.VNCProxyIP = (string)dataRow["VNCProxyIP"]; connectionInfo.VNCProxyPort = (int)dataRow["VNCProxyPort"]; connectionInfo.VNCProxyUsername = (string)dataRow["VNCProxyUsername"]; - connectionInfo.VNCProxyPassword = (string)dataRow["VNCProxyPassword"]; + connectionInfo.VNCProxyPassword = DecryptValue((string)dataRow["VNCProxyPassword"]); connectionInfo.VNCColors = (ProtocolVNC.Colors)Enum.Parse(typeof(ProtocolVNC.Colors), (string)dataRow["VNCColors"]); connectionInfo.VNCSmartSizeMode = (ProtocolVNC.SmartSizeMode)Enum.Parse(typeof(ProtocolVNC.SmartSizeMode), (string)dataRow["VNCSmartSizeMode"]); connectionInfo.VNCViewOnly = (bool)dataRow["VNCViewOnly"]; @@ -121,7 +134,7 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.RDGatewayHostname = (string)dataRow["RDGatewayHostname"]; connectionInfo.RDGatewayUseConnectionCredentials = (RdpProtocol.RDGatewayUseConnectionCredentials)Enum.Parse(typeof(RdpProtocol.RDGatewayUseConnectionCredentials), (string)dataRow["RDGatewayUseConnectionCredentials"]); connectionInfo.RDGatewayUsername = (string)dataRow["RDGatewayUsername"]; - connectionInfo.RDGatewayPassword = (string)dataRow["RDGatewayPassword"]; + connectionInfo.RDGatewayPassword = DecryptValue((string)dataRow["RDGatewayPassword"]); connectionInfo.RDGatewayDomain = (string)dataRow["RDGatewayDomain"]; connectionInfo.Inheritance.CacheBitmaps = (bool)dataRow["InheritCacheBitmaps"]; @@ -180,10 +193,26 @@ namespace mRemoteNG.Config.Serializers.MsSql connectionInfo.Inheritance.RDGatewayDomain = (bool)dataRow["InheritRDGatewayDomain"]; } + private string DecryptValue(string cipherText) + { + try + { + return _cryptographyProvider.Decrypt(cipherText, _decryptionKey); + } + catch (EncryptionException) + { + // value may not be encrypted + return cipherText; + } + } + private ConnectionTreeModel CreateNodeHierarchy(List connectionList, DataTable dataTable) { var connectionTreeModel = new ConnectionTreeModel(); - var rootNode = new RootNodeInfo(RootNodeType.Connection, "0"); + var rootNode = new RootNodeInfo(RootNodeType.Connection, "0") + { + PasswordString = _decryptionKey.ConvertToUnsecureString() + }; connectionTreeModel.AddRootNode(rootNode); foreach (DataRow row in dataTable.Rows) diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs index c829deaf8..c059afd78 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs @@ -7,19 +7,25 @@ using System; using System.Data; using System.Data.SqlTypes; using System.Linq; +using System.Security; +using mRemoteNG.Tools; namespace mRemoteNG.Config.Serializers.MsSql { public class DataTableSerializer : ISerializer { + private readonly ICryptographyProvider _cryptographyProvider; + private readonly SecureString _encryptionKey; private DataTable _dataTable; private const string TableName = "tblCons"; private readonly SaveFilter _saveFilter; private int _currentNodeIndex; - public DataTableSerializer(SaveFilter saveFilter) + public DataTableSerializer(SaveFilter saveFilter, ICryptographyProvider cryptographyProvider, SecureString encryptionKey) { - _saveFilter = saveFilter; + _saveFilter = saveFilter.ThrowIfNull(nameof(saveFilter)); + _cryptographyProvider = cryptographyProvider.ThrowIfNull(nameof(cryptographyProvider)); + _encryptionKey = encryptionKey.ThrowIfNull(nameof(encryptionKey)); } @@ -210,7 +216,9 @@ namespace mRemoteNG.Config.Serializers.MsSql dataRow["Panel"] = connectionInfo.Panel; dataRow["Username"] = _saveFilter.SaveUsername ? connectionInfo.Username : ""; dataRow["DomainName"] = _saveFilter.SaveDomain ? connectionInfo.Domain : ""; - dataRow["Password"] = _saveFilter.SavePassword ? connectionInfo.Password : ""; + dataRow["Password"] = _saveFilter.SavePassword + ? _cryptographyProvider.Encrypt(connectionInfo.Password, _encryptionKey) + : ""; dataRow["Hostname"] = connectionInfo.Hostname; dataRow["Protocol"] = connectionInfo.Protocol; dataRow["PuttySession"] = connectionInfo.PuttySession; @@ -251,14 +259,14 @@ namespace mRemoteNG.Config.Serializers.MsSql dataRow["VNCProxyIP"] = connectionInfo.VNCProxyIP; dataRow["VNCProxyPort"] = connectionInfo.VNCProxyPort; dataRow["VNCProxyUsername"] = connectionInfo.VNCProxyUsername; - dataRow["VNCProxyPassword"] = connectionInfo.VNCProxyPassword; + dataRow["VNCProxyPassword"] = _cryptographyProvider.Encrypt(connectionInfo.VNCProxyPassword, _encryptionKey); dataRow["VNCColors"] = connectionInfo.VNCColors; dataRow["VNCSmartSizeMode"] = connectionInfo.VNCSmartSizeMode; dataRow["VNCViewOnly"] = connectionInfo.VNCViewOnly; dataRow["RDGatewayUsageMethod"] = connectionInfo.RDGatewayUsageMethod; dataRow["RDGatewayHostname"] = connectionInfo.RDGatewayHostname; dataRow["RDGatewayUseConnectionCredentials"] = connectionInfo.RDGatewayUseConnectionCredentials; - dataRow["RDGatewayUsername"] = connectionInfo.RDGatewayUsername; + dataRow["RDGatewayUsername"] = _cryptographyProvider.Encrypt(connectionInfo.RDGatewayUsername, _encryptionKey); dataRow["RDGatewayPassword"] = connectionInfo.RDGatewayPassword; dataRow["RDGatewayDomain"] = connectionInfo.RDGatewayDomain; if (_saveFilter.SaveInheritance) diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs index 8cf28d0ea..12e8ce86e 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/SqlConnectionListMetaData.cs @@ -1,4 +1,5 @@ using System; +using System.Security; namespace mRemoteNG.Config.Serializers.MsSql { diff --git a/mRemoteV1/Connection/ConnectionsService.cs b/mRemoteV1/Connection/ConnectionsService.cs index 93fa7bcc1..41221a020 100644 --- a/mRemoteV1/Connection/ConnectionsService.cs +++ b/mRemoteV1/Connection/ConnectionsService.cs @@ -44,7 +44,8 @@ namespace mRemoteNG.Connection throw new ArgumentNullException(nameof(puttySessionsManager)); _puttySessionsManager = puttySessionsManager; - _localConnectionPropertiesDataProvider = new FileDataProvider(Path.Combine(SettingsFileInfo.SettingsPath, "LocalConnectionProperties.xml")); + var path = SettingsFileInfo.SettingsPath; + _localConnectionPropertiesDataProvider = new FileDataProvider(Path.Combine(path, "LocalConnectionProperties.xml")); _localConnectionPropertiesSerializer = new LocalConnectionPropertiesXmlSerializer(); } diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index b29cd1710..896383b6c 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -180,7 +180,7 @@ - + From 25e30672c8892407647778d768e19f9ccfd85036 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sun, 4 Nov 2018 16:38:14 -0600 Subject: [PATCH 13/19] bumped version to v1.76.12 --- mRemoteV1/Properties/AssemblyInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mRemoteV1/Properties/AssemblyInfo.cs b/mRemoteV1/Properties/AssemblyInfo.cs index 6f84fe84f..04ae9d810 100644 --- a/mRemoteV1/Properties/AssemblyInfo.cs +++ b/mRemoteV1/Properties/AssemblyInfo.cs @@ -33,5 +33,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // -[assembly: AssemblyVersion("1.76.11.*")] +[assembly: AssemblyVersion("1.76.12.*")] [assembly: NeutralResourcesLanguage("en")] \ No newline at end of file From 6ca52a0db175591d84c57e47537e1bdb6c0e3ebf Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sun, 4 Nov 2018 16:39:25 -0600 Subject: [PATCH 14/19] updated changelog --- CHANGELOG.TXT | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 134e8f946..147a2b6f4 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -1,3 +1,16 @@ +1.76.12 (2018-xx-xx): + +Features/Enhancements: +---------------------- +#1180: Allow saving certain connection properties locally when using database + +Fixes: +------ +#1173: Fixed memory leak when loading connections multiple times +#1134: Fixed issue where opening a connection opens same connection on other clients when using database feature +#449: Encrypt passwords saved to database + + 1.76.11 (2018-10-18): Fixes: From 8cb31ad524abbceed078b3d5e82138f9bd549079 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sun, 4 Nov 2018 18:03:29 -0600 Subject: [PATCH 15/19] slight cleanup of Runtime class --- mRemoteV1/App/Runtime.cs | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/mRemoteV1/App/Runtime.cs b/mRemoteV1/App/Runtime.cs index 3b580abd3..f637a0013 100644 --- a/mRemoteV1/App/Runtime.cs +++ b/mRemoteV1/App/Runtime.cs @@ -43,19 +43,23 @@ namespace mRemoteNG.App #region Connections Loading/Saving public static void LoadConnectionsAsync() { - _withDialog = false; - var t = new Thread(LoadConnectionsBGd); t.SetApartmentState(ApartmentState.STA); t.Start(); } - private static bool _withDialog; private static void LoadConnectionsBGd() { - LoadConnections(_withDialog); + LoadConnections(); } + /// + /// + /// + /// + /// Should we show the file selection dialog to allow the user to select + /// a connection file + /// public static void LoadConnections(bool withDialog = false) { var connectionFileName = ""; @@ -65,18 +69,19 @@ namespace mRemoteNG.App // disable sql update checking while we are loading updates ConnectionsService.RemoteConnectionsSyncronizer?.Disable(); - if (!Settings.Default.UseSQLServer) + if (withDialog) { - if (withDialog) - { - var loadDialog = DialogFactory.BuildLoadConnectionsDialog(); - if (loadDialog.ShowDialog() != DialogResult.OK) return; - connectionFileName = loadDialog.FileName; - } - else - { - connectionFileName = ConnectionsService.GetStartupConnectionFileName(); - } + var loadDialog = DialogFactory.BuildLoadConnectionsDialog(); + if (loadDialog.ShowDialog() != DialogResult.OK) + return; + + connectionFileName = loadDialog.FileName; + Settings.Default.UseSQLServer = false; + Settings.Default.Save(); + } + else if (!Settings.Default.UseSQLServer) + { + connectionFileName = ConnectionsService.GetStartupConnectionFileName(); } ConnectionsService.LoadConnections(Settings.Default.UseSQLServer, false, connectionFileName); From cb5447f86eafc301bd977bcd11c4541f715065af Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sun, 4 Nov 2018 18:03:39 -0600 Subject: [PATCH 16/19] fixes #1181 --- mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs b/mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs index ad59e290d..71022a18a 100644 --- a/mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs +++ b/mRemoteV1/UI/Forms/OptionsPages/SqlServerPage.cs @@ -79,7 +79,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages { Runtime.ConnectionsService.RemoteConnectionsSyncronizer?.Dispose(); Runtime.ConnectionsService.RemoteConnectionsSyncronizer = new RemoteConnectionsSyncronizer(new SqlConnectionsUpdateChecker()); - Runtime.ConnectionsService.RemoteConnectionsSyncronizer.Enable(); + Runtime.ConnectionsService.LoadConnections(true, false, ""); } private void DisableSql() From eea79da1ae6ed6675c6a9dcbf3ba1e90deb6bdce Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sun, 4 Nov 2018 18:06:14 -0600 Subject: [PATCH 17/19] updated changelog --- CHANGELOG.TXT | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 147a2b6f4..520d36262 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -6,6 +6,7 @@ Features/Enhancements: Fixes: ------ +#1181: Connections sometimes dont immediately load when switching to sql feature #1173: Fixed memory leak when loading connections multiple times #1134: Fixed issue where opening a connection opens same connection on other clients when using database feature #449: Encrypt passwords saved to database From 8d29ff2d61e19470ab57e6f7fe3c80413ea3193e Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sun, 4 Nov 2018 19:13:10 -0600 Subject: [PATCH 18/19] fixes #1168 --- CHANGELOG.TXT | 1 + mRemoteV1/UI/Forms/frmMain.cs | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 520d36262..34e7562ec 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -8,6 +8,7 @@ Fixes: ------ #1181: Connections sometimes dont immediately load when switching to sql feature #1173: Fixed memory leak when loading connections multiple times +#1168: Autohide Connection and Config tab won't open when ssh connection active #1134: Fixed issue where opening a connection opens same connection on other clients when using database feature #449: Encrypt passwords saved to database diff --git a/mRemoteV1/UI/Forms/frmMain.cs b/mRemoteV1/UI/Forms/frmMain.cs index 4be80b354..bb2138216 100644 --- a/mRemoteV1/UI/Forms/frmMain.cs +++ b/mRemoteV1/UI/Forms/frmMain.cs @@ -429,7 +429,8 @@ namespace mRemoteNG.UI.Forms // Only handle this msg if it was triggered by a click if (NativeMethods.LOWORD(m.WParam) == NativeMethods.WA_CLICKACTIVE) { - var controlThatWasClicked = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition)); + var controlThatWasClicked = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition)) + ?? GetChildAtPoint(MousePosition); if (controlThatWasClicked != null) { if (controlThatWasClicked is TreeView || @@ -444,9 +445,14 @@ namespace mRemoteNG.UI.Forms controlThatWasClicked is Crownwood.Magic.Controls.TabControl || controlThatWasClicked is Crownwood.Magic.Controls.InertButton) { - // Simulate a mouse event since one wasn't generated by Windows - SimulateClick(controlThatWasClicked); - controlThatWasClicked.Focus(); + // Simulate a mouse event since one wasn't generated by Windows + SimulateClick(controlThatWasClicked); + controlThatWasClicked.Focus(); + } + else if (controlThatWasClicked is AutoHideStripBase) + { + // only focus the autohide toolstrip + controlThatWasClicked.Focus(); } else { From 75e0b8f4c2f3e580de333ec7a65cf13cfc4a6ec5 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Thu, 8 Nov 2018 15:09:08 -0600 Subject: [PATCH 19/19] set release date in changelog --- CHANGELOG.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 34e7562ec..6e1db3681 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -1,4 +1,4 @@ -1.76.12 (2018-xx-xx): +1.76.12 (2018-11-08): Features/Enhancements: ----------------------