diff --git a/CHANGELOG.md b/CHANGELOG.md index e408385c..6262c21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed - #1610: Menu bar changes to english when cancelling options form - #1595: Unhandled exception when trying to browse through non existent multi ssh history with keyboard key strokes +- #1589: Update SQL tables instead of rewriting them - #1337: Unhandled exception after closing mRemoteNG - #359: Making a VNC connection to an unreachable host causes the application to not respond for 20-30 seconds diff --git a/CREDITS.md b/CREDITS.md index 43a1fa4c..dfc75b7c 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -26,6 +26,7 @@ Stephan (http://github.com/st-schuler) Aleksey Reytsman (http://github.com/areytsman) Cristian Abelleira (http://github.com/CrAbelleira) http://github.com/MitchellBot +Filippo Ferrazini (http://github.com/Filippo125) ## Past Contributors Felix Deimel - mRemote original developer diff --git a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs index a3a24dd1..f858ccae 100644 --- a/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteV1/Config/Connections/SqlConnectionsSaver.cs @@ -153,14 +153,16 @@ namespace mRemoteNG.Config.Connections private void UpdateConnectionsTable(RootNodeInfo rootTreeNode, IDatabaseConnector databaseConnector) { + var dataProvider = new SqlDataProvider(databaseConnector); + var currentDataTable = dataProvider.Load(); + var cryptoProvider = new LegacyRijndaelCryptographyProvider(); var serializer = new DataTableSerializer(_saveFilter, cryptoProvider, rootTreeNode.PasswordString.ConvertToSecureString()); + serializer.SetSourceDataTable(currentDataTable); var dataTable = serializer.Serialize(rootTreeNode); - var dataProvider = new SqlDataProvider(databaseConnector); - - var dbQuery = databaseConnector.DbCommand("DELETE FROM tblCons"); - dbQuery.ExecuteNonQuery(); + //var dbQuery = databaseConnector.DbCommand("DELETE FROM tblCons"); + //dbQuery.ExecuteNonQuery(); dataProvider.Save(dataTable); } diff --git a/mRemoteV1/Config/DataProviders/SqlDataProvider.cs b/mRemoteV1/Config/DataProviders/SqlDataProvider.cs index 68e43324..93f73f29 100644 --- a/mRemoteV1/Config/DataProviders/SqlDataProvider.cs +++ b/mRemoteV1/Config/DataProviders/SqlDataProvider.cs @@ -44,12 +44,31 @@ namespace mRemoteNG.Config.DataProviders OpenConnection(); if (DatabaseConnector.GetType() == typeof(MSSqlDatabaseConnector)) { - using (var sqlBulkCopy = new SqlBulkCopy((SqlConnection)DatabaseConnector.DbConnection())) + SqlConnection sqlConnection = (SqlConnection)DatabaseConnector.DbConnection(); + using (SqlTransaction transaction = sqlConnection.BeginTransaction(System.Data.IsolationLevel.Serializable)) { - foreach (DataColumn col in dataTable.Columns) - sqlBulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName); - sqlBulkCopy.DestinationTableName = "dbo.tblCons"; - sqlBulkCopy.WriteToServer(dataTable); + using (SqlCommand sqlCommand = new SqlCommand()) + { + sqlCommand.Connection = sqlConnection; + sqlCommand.Transaction = transaction; + sqlCommand.CommandText = "SELECT * FROM tblCons"; + using (SqlDataAdapter dataAdpater = new SqlDataAdapter()) + { + dataAdpater.SelectCommand = sqlCommand; + + SqlCommandBuilder builder = new SqlCommandBuilder(dataAdpater); + // Avoid optimistic concurrency, check if it is necessary. + builder.ConflictOption = ConflictOption.OverwriteChanges; + + dataAdpater.UpdateCommand = builder.GetUpdateCommand(); + + dataAdpater.DeleteCommand = builder.GetDeleteCommand(); + dataAdpater.InsertCommand = builder.GetInsertCommand(); + + dataAdpater.Update(dataTable); + transaction.Commit(); + } + } } } diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs index 489c6c5a..6ebc3bc0 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/MsSql/DataTableSerializer.cs @@ -5,6 +5,7 @@ using mRemoteNG.Tools; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; using System; +using System.Collections.Generic; using System.Data; using System.Linq; using System.Security; @@ -13,14 +14,17 @@ namespace mRemoteNG.Config.Serializers.MsSql { public class DataTableSerializer : ISerializer { + public readonly int DELETE = 0; private readonly ICryptographyProvider _cryptographyProvider; private readonly SecureString _encryptionKey; private DataTable _dataTable; + private DataTable _sourceDataTable; + private Dictionary sourcePrimaryKeyDict = new Dictionary(); private const string TableName = "tblCons"; private readonly SaveFilter _saveFilter; private int _currentNodeIndex; - public Version Version { get; } = new Version(2, 7); + public Version Version { get; } = new Version(2, 8); public DataTableSerializer(SaveFilter saveFilter, ICryptographyProvider cryptographyProvider, @@ -31,6 +35,11 @@ namespace mRemoteNG.Config.Serializers.MsSql _encryptionKey = encryptionKey.ThrowIfNull(nameof(encryptionKey)); } + public void SetSourceDataTable(DataTable sourceDataTable) + { + _sourceDataTable = sourceDataTable; + } + public DataTable Serialize(ConnectionTreeModel connectionTreeModel) { @@ -51,15 +60,33 @@ namespace mRemoteNG.Config.Serializers.MsSql { _dataTable = BuildTable(); _currentNodeIndex = 0; + // Register add or update row SerializeNodesRecursive(serializationTarget); + var entryToDelete = sourcePrimaryKeyDict.Keys.ToList(); + foreach( var entry in entryToDelete) + { + _dataTable.Rows.Find(entry).Delete(); + } return _dataTable; } private DataTable BuildTable() { - var dataTable = new DataTable(TableName); - CreateSchema(dataTable); - SetPrimaryKey(dataTable); + DataTable dataTable; + if (_sourceDataTable != null) + { + dataTable = _sourceDataTable; + }else + { + dataTable = new DataTable(TableName); + + } + if (dataTable.Columns.Count == 0) CreateSchema(dataTable); + if (dataTable.PrimaryKey.Length == 0 ) SetPrimaryKey(dataTable); + foreach(DataRow row in dataTable.Rows) + { + sourcePrimaryKeyDict.Add((string)row["ConstantID"], DELETE); + } return dataTable; } @@ -216,14 +243,244 @@ namespace mRemoteNG.Config.Serializers.MsSql SerializeNodesRecursive(child); } + public bool isRowUpdated(ConnectionInfo connectionInfo, DataRow dataRow) + { + var isFieldNotChange = dataRow["Name"].Equals(connectionInfo.Name) && + dataRow["Type"].Equals(connectionInfo.GetTreeNodeType().ToString()) && + dataRow["ParentID"].Equals(connectionInfo.Parent?.ConstantID ?? "") && + dataRow["PositionID"].Equals(_currentNodeIndex) && + dataRow["Expanded"].Equals(false) && + dataRow["Description"].Equals(connectionInfo.Description) && + dataRow["Icon"].Equals(connectionInfo.Icon) && + dataRow["Panel"].Equals(connectionInfo.Panel) && + dataRow["Username"].Equals(_saveFilter.SaveUsername ? connectionInfo.Username : "") && + dataRow["DomainName"].Equals(_saveFilter.SaveDomain ? connectionInfo.Domain : ""); + + isFieldNotChange = isFieldNotChange && dataRow["Hostname"].Equals(connectionInfo.Hostname); + isFieldNotChange = isFieldNotChange && dataRow["VmId"].Equals(connectionInfo.VmId); + isFieldNotChange = isFieldNotChange && dataRow["Protocol"].Equals(connectionInfo.Protocol.ToString()); + isFieldNotChange = isFieldNotChange && dataRow["PuttySession"].Equals(connectionInfo.PuttySession); + isFieldNotChange = isFieldNotChange && + dataRow["Port"].Equals(connectionInfo.Port); + isFieldNotChange = isFieldNotChange && + dataRow["ConnectToConsole"].Equals(connectionInfo.UseConsoleSession); + isFieldNotChange = isFieldNotChange && + dataRow["UseCredSsp"].Equals(connectionInfo.UseCredSsp); + isFieldNotChange = isFieldNotChange && + dataRow["UseVmId"].Equals(connectionInfo.UseVmId); + isFieldNotChange = isFieldNotChange && + dataRow["UseEnhancedMode"].Equals(connectionInfo.UseEnhancedMode); + isFieldNotChange = isFieldNotChange && + dataRow["RenderingEngine"].Equals(connectionInfo.RenderingEngine.ToString()); + isFieldNotChange = isFieldNotChange && + dataRow["ICAEncryptionStrength"].Equals(connectionInfo.ICAEncryptionStrength.ToString()); + isFieldNotChange = isFieldNotChange && + dataRow["RDPAuthenticationLevel"].Equals(connectionInfo.RDPAuthenticationLevel.ToString()); + isFieldNotChange = isFieldNotChange && + dataRow["RDPMinutesToIdleTimeout"].Equals(connectionInfo.RDPMinutesToIdleTimeout); + isFieldNotChange = isFieldNotChange && + dataRow["RDPAlertIdleTimeout"].Equals(connectionInfo.RDPAlertIdleTimeout); + isFieldNotChange = isFieldNotChange && + dataRow["LoadBalanceInfo"].Equals(connectionInfo.LoadBalanceInfo); + isFieldNotChange = isFieldNotChange && + dataRow["Colors"].Equals(connectionInfo.Colors.ToString()); + isFieldNotChange = isFieldNotChange && + dataRow["Resolution"].Equals(connectionInfo.Resolution.ToString()); + isFieldNotChange = isFieldNotChange && + dataRow["AutomaticResize"].Equals(connectionInfo.AutomaticResize); + isFieldNotChange = isFieldNotChange && + dataRow["DisplayWallpaper"].Equals(connectionInfo.DisplayWallpaper) && + dataRow["DisplayThemes"].Equals(connectionInfo.DisplayThemes) && + dataRow["EnableFontSmoothing"].Equals(connectionInfo.EnableFontSmoothing) && + dataRow["EnableDesktopComposition"].Equals(connectionInfo.EnableDesktopComposition) && + dataRow["CacheBitmaps"].Equals(connectionInfo.CacheBitmaps) && + dataRow["RedirectDiskDrives"].Equals(connectionInfo.RedirectDiskDrives) && + dataRow["RedirectPorts"].Equals(connectionInfo.RedirectPorts) && + dataRow["RedirectPrinters"].Equals(connectionInfo.RedirectPrinters) && + dataRow["RedirectClipboard"].Equals(connectionInfo.RedirectClipboard) && + dataRow["RedirectSmartCards"].Equals(connectionInfo.RedirectSmartCards) && + dataRow["RedirectSound"].Equals(connectionInfo.RedirectSound.ToString()) && + dataRow["SoundQuality"].Equals(connectionInfo.SoundQuality.ToString()) && + dataRow["RedirectAudioCapture"].Equals(connectionInfo.RedirectAudioCapture) && + dataRow["RedirectKeys"].Equals(connectionInfo.RedirectKeys); + + isFieldNotChange = isFieldNotChange && + dataRow["Connected"].Equals(false) && // TODO: this column can eventually be removed. we now save this property locally + dataRow["PreExtApp"].Equals(connectionInfo.PreExtApp) && + dataRow["PostExtApp"].Equals(connectionInfo.PostExtApp) && + dataRow["MacAddress"].Equals(connectionInfo.MacAddress) && + dataRow["UserField"].Equals(connectionInfo.UserField) && + dataRow["ExtApp"].Equals(connectionInfo.ExtApp) && + dataRow["VNCCompression"].Equals(connectionInfo.VNCCompression.ToString()) && + dataRow["VNCEncoding"].Equals(connectionInfo.VNCEncoding.ToString()) && + dataRow["VNCAuthMode"].Equals(connectionInfo.VNCAuthMode.ToString()) && + dataRow["VNCProxyType"].Equals(connectionInfo.VNCProxyType.ToString()) && + dataRow["VNCProxyIP"].Equals(connectionInfo.VNCProxyIP) && + dataRow["VNCProxyPort"].Equals(connectionInfo.VNCProxyPort) && + dataRow["VNCProxyUsername"].Equals(connectionInfo.VNCProxyUsername) && + dataRow["VNCColors"].Equals(connectionInfo.VNCColors.ToString()) && + dataRow["VNCSmartSizeMode"].Equals(connectionInfo.VNCSmartSizeMode.ToString()) && + dataRow["VNCViewOnly"].Equals(connectionInfo.VNCViewOnly) && + dataRow["RDGatewayUsageMethod"].Equals(connectionInfo.RDGatewayUsageMethod.ToString()) && + dataRow["RDGatewayHostname"].Equals(connectionInfo.RDGatewayHostname) && + dataRow["RDGatewayUseConnectionCredentials"].Equals(connectionInfo.RDGatewayUseConnectionCredentials.ToString()) && + dataRow["RDGatewayUsername"].Equals(connectionInfo.RDGatewayUsername) && + + dataRow["RDGatewayDomain"].Equals(connectionInfo.RDGatewayDomain) && + dataRow["RdpVersion"].Equals(connectionInfo.RdpVersion.ToString()); + + var isInheritanceFieldNotChange = false; + if (_saveFilter.SaveInheritance) + { + isInheritanceFieldNotChange = (dataRow["InheritCacheBitmaps"].Equals(connectionInfo.Inheritance.CacheBitmaps) && + dataRow["InheritColors"].Equals(connectionInfo.Inheritance.Colors) && + dataRow["InheritDescription"].Equals(connectionInfo.Inheritance.Description) && + dataRow["InheritDisplayThemes"].Equals(connectionInfo.Inheritance.DisplayThemes) && + dataRow["InheritDisplayWallpaper"].Equals(connectionInfo.Inheritance.DisplayWallpaper) && + dataRow["InheritEnableFontSmoothing"].Equals(connectionInfo.Inheritance.EnableFontSmoothing) && + dataRow["InheritEnableDesktopComposition"].Equals(connectionInfo.Inheritance.EnableDesktopComposition) && + dataRow["InheritDomain"].Equals(connectionInfo.Inheritance.Domain) && + dataRow["InheritIcon"].Equals(connectionInfo.Inheritance.Icon) && + dataRow["InheritPanel"].Equals(connectionInfo.Inheritance.Panel) && + dataRow["InheritPassword"].Equals(connectionInfo.Inheritance.Password) && + dataRow["InheritPort"].Equals(connectionInfo.Inheritance.Port) && + dataRow["InheritProtocol"].Equals(connectionInfo.Inheritance.Protocol) && + dataRow["InheritPuttySession"].Equals(connectionInfo.Inheritance.PuttySession) && + dataRow["InheritRedirectDiskDrives"].Equals(connectionInfo.Inheritance.RedirectDiskDrives) && + dataRow["InheritRedirectKeys"].Equals(connectionInfo.Inheritance.RedirectKeys) && + dataRow["InheritRedirectPorts"].Equals(connectionInfo.Inheritance.RedirectPorts) && + dataRow["InheritRedirectPrinters"].Equals(connectionInfo.Inheritance.RedirectPrinters) && + dataRow["InheritRedirectClipboard"].Equals(connectionInfo.Inheritance.RedirectClipboard) && + dataRow["InheritRedirectSmartCards"].Equals(connectionInfo.Inheritance.RedirectSmartCards) && + dataRow["InheritRedirectSound"].Equals(connectionInfo.Inheritance.RedirectSound) && + dataRow["InheritSoundQuality"].Equals(connectionInfo.Inheritance.SoundQuality) && + dataRow["InheritRedirectAudioCapture"].Equals(connectionInfo.Inheritance.RedirectAudioCapture) && + dataRow["InheritResolution"].Equals(connectionInfo.Inheritance.Resolution) && + dataRow["InheritAutomaticResize"].Equals(connectionInfo.Inheritance.AutomaticResize) && + dataRow["InheritUseConsoleSession"].Equals(connectionInfo.Inheritance.UseConsoleSession) && + dataRow["InheritUseCredSsp"].Equals(connectionInfo.Inheritance.UseCredSsp) && + dataRow["InheritRenderingEngine"].Equals(connectionInfo.Inheritance.RenderingEngine) && + dataRow["InheritUsername"].Equals(connectionInfo.Inheritance.Username) && + dataRow["InheritVmId"].Equals(connectionInfo.Inheritance.VmId) && + dataRow["InheritUseVmId"].Equals(connectionInfo.Inheritance.UseVmId) && + dataRow["InheritUseEnhancedMode"].Equals(connectionInfo.Inheritance.UseEnhancedMode) && + dataRow["InheritICAEncryptionStrength"].Equals(connectionInfo.Inheritance.ICAEncryptionStrength) && + dataRow["InheritRDPAuthenticationLevel"].Equals(connectionInfo.Inheritance.RDPAuthenticationLevel) && + dataRow["InheritRDPMinutesToIdleTimeout"].Equals(connectionInfo.Inheritance.RDPMinutesToIdleTimeout) && + dataRow["InheritRDPAlertIdleTimeout"].Equals(connectionInfo.Inheritance.RDPAlertIdleTimeout) && + dataRow["InheritLoadBalanceInfo"].Equals(connectionInfo.Inheritance.LoadBalanceInfo) && + dataRow["InheritPreExtApp"].Equals(connectionInfo.Inheritance.PreExtApp) && + dataRow["InheritPostExtApp"].Equals(connectionInfo.Inheritance.PostExtApp) && + dataRow["InheritMacAddress"].Equals(connectionInfo.Inheritance.MacAddress) && + dataRow["InheritUserField"].Equals(connectionInfo.Inheritance.UserField) && + dataRow["InheritExtApp"].Equals(connectionInfo.Inheritance.ExtApp) && + dataRow["InheritVNCCompression"].Equals(connectionInfo.Inheritance.VNCCompression) && + dataRow["InheritVNCEncoding"].Equals(connectionInfo.Inheritance.VNCEncoding) && + dataRow["InheritVNCAuthMode"].Equals(connectionInfo.Inheritance.VNCAuthMode) && + dataRow["InheritVNCProxyType"].Equals(connectionInfo.Inheritance.VNCProxyType) && + dataRow["InheritVNCProxyIP"].Equals(connectionInfo.Inheritance.VNCProxyIP) && + dataRow["InheritVNCProxyPort"].Equals(connectionInfo.Inheritance.VNCProxyPort) && + dataRow["InheritVNCProxyUsername"].Equals(connectionInfo.Inheritance.VNCProxyUsername) && + dataRow["InheritVNCProxyPassword"].Equals(connectionInfo.Inheritance.VNCProxyPassword) && + dataRow["InheritVNCColors"].Equals(connectionInfo.Inheritance.VNCColors) && + dataRow["InheritVNCSmartSizeMode"].Equals(connectionInfo.Inheritance.VNCSmartSizeMode) && + dataRow["InheritVNCViewOnly"].Equals(connectionInfo.Inheritance.VNCViewOnly) && + dataRow["InheritRDGatewayUsageMethod"].Equals(connectionInfo.Inheritance.RDGatewayUsageMethod) && + dataRow["InheritRDGatewayHostname"].Equals(connectionInfo.Inheritance.RDGatewayHostname) && + dataRow["InheritRDGatewayUseConnectionCredentials"].Equals(connectionInfo.Inheritance.RDGatewayUseConnectionCredentials) && + dataRow["InheritRDGatewayUsername"].Equals(connectionInfo.Inheritance.RDGatewayUsername) && + dataRow["InheritRDGatewayPassword"].Equals(connectionInfo.Inheritance.RDGatewayPassword) && + dataRow["InheritRDGatewayDomain"].Equals(connectionInfo.Inheritance.RDGatewayDomain) && + dataRow["InheritRdpVersion"].Equals(connectionInfo.Inheritance.RdpVersion)); + } + else + { + isInheritanceFieldNotChange = (dataRow["InheritCacheBitmaps"].Equals(false) && + dataRow["InheritColors"].Equals(false) && + dataRow["InheritDescription"].Equals(false) && + dataRow["InheritDisplayThemes"].Equals(false) && + dataRow["InheritDisplayWallpaper"].Equals(false) && + dataRow["InheritEnableFontSmoothing"].Equals(false) && + dataRow["InheritEnableDesktopComposition"].Equals(false) && + dataRow["InheritDomain"].Equals(false) && + dataRow["InheritIcon"].Equals(false) && + dataRow["InheritPanel"].Equals(false) && + dataRow["InheritPassword"].Equals(false) && + dataRow["InheritPort"].Equals(false) && + dataRow["InheritProtocol"].Equals(false) && + dataRow["InheritPuttySession"].Equals(false) && + dataRow["InheritRedirectDiskDrives"].Equals(false) && + dataRow["InheritRedirectKeys"].Equals(false) && + dataRow["InheritRedirectPorts"].Equals(false) && + dataRow["InheritRedirectPrinters"].Equals(false) && + dataRow["InheritRedirectClipboard"].Equals(false) && + dataRow["InheritRedirectSmartCards"].Equals(false) && + dataRow["InheritRedirectSound"].Equals(false) && + dataRow["InheritSoundQuality"].Equals(false) && + dataRow["InheritRedirectAudioCapture"].Equals(false) && + dataRow["InheritResolution"].Equals(false) && + dataRow["InheritAutomaticResize"].Equals(false) && + dataRow["InheritUseConsoleSession"].Equals(false) && + dataRow["InheritUseCredSsp"].Equals(false) && + dataRow["InheritRenderingEngine"].Equals(false) && + dataRow["InheritUsername"].Equals(false) && + dataRow["InheritICAEncryptionStrength"].Equals(false) && + dataRow["InheritRDPAuthenticationLevel"].Equals(false) && + dataRow["InheritRDPMinutesToIdleTimeout"].Equals(false) && + dataRow["InheritRDPAlertIdleTimeout"].Equals(false) && + dataRow["InheritLoadBalanceInfo"].Equals(false) && + dataRow["InheritPreExtApp"].Equals(false) && + dataRow["InheritPostExtApp"].Equals(false) && + dataRow["InheritMacAddress"].Equals(false) && + dataRow["InheritUserField"].Equals(false) && + dataRow["InheritExtApp"].Equals(false) && + dataRow["InheritVNCCompression"].Equals(false) && + dataRow["InheritVNCEncoding"].Equals(false) && + dataRow["InheritVNCAuthMode"].Equals(false) && + dataRow["InheritVNCProxyType"].Equals(false) && + dataRow["InheritVNCProxyIP"].Equals(false) && + dataRow["InheritVNCProxyPort"].Equals(false) && + dataRow["InheritVNCProxyUsername"].Equals(false) && + dataRow["InheritVNCProxyPassword"].Equals(false) && + dataRow["InheritVNCColors"].Equals(false) && + dataRow["InheritVNCSmartSizeMode"].Equals(false) && + dataRow["InheritVNCViewOnly"].Equals(false) && + dataRow["InheritRDGatewayUsageMethod"].Equals(false) && + dataRow["InheritRDGatewayHostname"].Equals(false) && + dataRow["InheritRDGatewayUseConnectionCredentials"].Equals(false) && + dataRow["InheritRDGatewayUsername"].Equals(false) && + dataRow["InheritRDGatewayPassword"].Equals(false) && + dataRow["InheritRDGatewayDomain"].Equals(false) && + dataRow["InheritRdpVersion"].Equals(false)); + } + + var pwd = dataRow["Password"].Equals(_saveFilter.SavePassword ? _cryptographyProvider.Encrypt(connectionInfo.Password, _encryptionKey) : "") && + dataRow["VNCProxyPassword"].Equals(_cryptographyProvider.Encrypt(connectionInfo.VNCProxyPassword, _encryptionKey)) && + dataRow["RDGatewayPassword"].Equals(_cryptographyProvider.Encrypt(connectionInfo.RDGatewayPassword, _encryptionKey)); + return !(pwd && isFieldNotChange && isInheritanceFieldNotChange); + } + private void SerializeConnectionInfo(ConnectionInfo connectionInfo) { _currentNodeIndex++; - var dataRow = _dataTable.NewRow(); - dataRow["ID"] = DBNull.Value; + var isNewRow = false; + DataRow dataRow = _dataTable.Rows.Find(connectionInfo.ConstantID); + if (dataRow == null) + { + dataRow = _dataTable.NewRow(); + dataRow["ConstantID"] = connectionInfo.ConstantID; + isNewRow = true; + } + else + { + sourcePrimaryKeyDict.Remove(connectionInfo.ConstantID); + } + var tmp = isRowUpdated(connectionInfo, dataRow); + if (!tmp){ + return; + } dataRow["Name"] = connectionInfo.Name; dataRow["Type"] = connectionInfo.GetTreeNodeType().ToString(); - dataRow["ConstantID"] = connectionInfo.ConstantID; dataRow["ParentID"] = connectionInfo.Parent?.ConstantID ?? ""; dataRow["PositionID"] = _currentNodeIndex; dataRow["LastChange"] = MiscTools.DBTimeStampNow(); @@ -419,8 +676,7 @@ namespace mRemoteNG.Config.Serializers.MsSql dataRow["InheritRDGatewayDomain"] = false; dataRow["InheritRdpVersion"] = false; } - - _dataTable.Rows.Add(dataRow); + if (isNewRow)_dataTable.Rows.Add(dataRow); } } } \ No newline at end of file diff --git a/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs b/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs index 40f622f6..999b7711 100644 --- a/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs +++ b/mRemoteV1/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs @@ -37,6 +37,7 @@ namespace mRemoteNG.Config.Serializers.Versioning new SqlVersion24To25Upgrader(_databaseConnector), new SqlVersion25To26Upgrader(_databaseConnector), new SqlVersion26To27Upgrader(_databaseConnector), + new SqlVersion27To28Upgrader(_databaseConnector), }; foreach (var upgrader in dbUpgraders) @@ -48,7 +49,7 @@ namespace mRemoteNG.Config.Serializers.Versioning } // DB is at the highest current supported version - if (databaseVersion.CompareTo(new Version(2, 7)) == 0) + if (databaseVersion.CompareTo(new Version(2, 8)) == 0) isVerified = true; if (isVerified == false) diff --git a/mRemoteV1/Config/Serializers/Versioning/SqlVersion27To28Upgrader.cs b/mRemoteV1/Config/Serializers/Versioning/SqlVersion27To28Upgrader.cs new file mode 100644 index 00000000..7f850bd8 --- /dev/null +++ b/mRemoteV1/Config/Serializers/Versioning/SqlVersion27To28Upgrader.cs @@ -0,0 +1,64 @@ +using mRemoteNG.App; +using mRemoteNG.Config.DatabaseConnectors; +using mRemoteNG.Messages; +using System; +using System.Data.Common; + +namespace mRemoteNG.Config.Serializers.Versioning +{ + public class SqlVersion27To28Upgrader : IVersionUpgrader + { + private readonly IDatabaseConnector _databaseConnector; + + public SqlVersion27To28Upgrader(IDatabaseConnector databaseConnector) + { + _databaseConnector = databaseConnector ?? throw new ArgumentNullException(nameof(databaseConnector)); + } + + public bool CanUpgrade(Version currentVersion) + { + return currentVersion.CompareTo(new Version(2, 7)) == 0; + } + + public Version Upgrade() + { + Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, + "Upgrading database from version 2.7 to version 2.8."); + + const string mySqlText = @" +ALTER TABLE tblCons MODIFY COLUMN ID INT; +ALTER TABLE tblCons DROP PRIMARY KEY, ADD PRIMARY KEY (ConstantID); +ALTER TABLE tblCons ADD INDEX `id` (ID), MODIFY ID int auto_increment; +ALTER TABLE tblCons MODIFY COLUMN ID INT AUTO_INCREMENT, ADD UNIQUE(ID); +UPDATE tblRoot SET ConfVersion='2.8'"; + const string msSqlText1 = @" +ALTER TABLE tblCons ADD DEFAULT 0 FOR UseEnhancedMode; +ALTER TABLE tblCons ADD DEFAULT 0 FOR InheritUseEnhancedMode; +UPDATE tblCons SET UseEnhancedMode = 0 WHERE UseEnhancedMode IS NULL; +UPDATE tblCons SET InheritUseEnhancedMode = 0 WHERE InheritUseEnhancedMode IS NULL; +ALTER TABLE tblCons ALTER COLUMN ConstantID varchar(128) NOT NULL;"; + const string msSqlText2 = @" +ALTER TABLE tblCons ADD CONSTRAINT PK_tblCons PRIMARY KEY (ConstantID); +UPDATE tblRoot SET ConfVersion='2.8';"; + DbCommand dbCommand; + + if (_databaseConnector.GetType() == typeof(MSSqlDatabaseConnector)) + { + dbCommand = _databaseConnector.DbCommand(msSqlText1); + dbCommand.ExecuteNonQuery(); + dbCommand = _databaseConnector.DbCommand(msSqlText2); + } else if (_databaseConnector.GetType() == typeof(MySqlDatabaseConnector)) + { + dbCommand = _databaseConnector.DbCommand(mySqlText); + } else + { + throw new Exception("Unknow database backend"); + } + + + + dbCommand.ExecuteNonQuery(); + return new Version(2, 8); + } + } +} \ No newline at end of file diff --git a/mRemoteV1/Documentation/mssql_db_setup.sql b/mRemoteV1/Documentation/mssql_db_setup.sql index 17ec6724..b9268549 100644 --- a/mRemoteV1/Documentation/mssql_db_setup.sql +++ b/mRemoteV1/Documentation/mssql_db_setup.sql @@ -15,7 +15,7 @@ GO CREATE TABLE [dbo].[tblCons] ( ID int NOT NULL IDENTITY(1,1), - ConstantID varchar(128), + ConstantID varchar(128) NOT NULL PRIMARY KEY, PositionID int NOT NULL, ParentID varchar(128), LastChange datetime NOT NULL, @@ -140,11 +140,12 @@ CREATE TABLE [dbo].[tblCons] ( InheritRdpVersion bit DEFAULT ((0)) NOT NULL, VmId varchar(100), UseVmId bit, - UseEnhancedMode bit, - InheritVmId bit, + UseEnhancedMode bit DEFAULT ((0)), + InheritVmId bit , InheritUseVmId bit, - InheritUseEnhancedMode bit -) GO + InheritUseEnhancedMode bit DEFAULT ((0)) +) ON [PRIMARY] +GO CREATE TABLE [dbo].[tblRoot] ( [Name] [varchar] (2048) NOT NULL , diff --git a/mRemoteV1/Documentation/mysql_db_setup.sql b/mRemoteV1/Documentation/mysql_db_setup.sql index 545de737..be28001a 100644 --- a/mRemoteV1/Documentation/mysql_db_setup.sql +++ b/mRemoteV1/Documentation/mysql_db_setup.sql @@ -18,7 +18,7 @@ DROP TABLE IF EXISTS `tblCons`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `tblCons` ( `ID` int(11) NOT NULL AUTO_INCREMENT, - `ConstantID` varchar(128) DEFAULT NULL, + `ConstantID` varchar(128) NOT NULL, `PositionID` int(11) NOT NULL, `ParentID` varchar(128) DEFAULT NULL, `LastChange` datetime NOT NULL, @@ -147,7 +147,8 @@ CREATE TABLE `tblCons` ( `InheritVmId` tinyint(1) DEFAULT NULL, `InheritUseVmId` tinyint(1) DEFAULT NULL, `InheritUseEnhancedMode` tinyint(1) DEFAULT NULL, - PRIMARY KEY (`ID`) + PRIMARY KEY (`ConstantID`), + UNIQUE (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=3324 DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/mRemoteV1/Tools/MiscTools.cs b/mRemoteV1/Tools/MiscTools.cs index e3a0899d..6d4c93f5 100644 --- a/mRemoteV1/Tools/MiscTools.cs +++ b/mRemoteV1/Tools/MiscTools.cs @@ -92,7 +92,7 @@ namespace mRemoteNG.Tools return new MySqlDateTime(DateTime.Now); case "mssql": default: - return (SqlDateTime)DateTime.Now; + return DateTime.Now; } } diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index 4bf31d9a..5a26a6c2 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -208,6 +208,7 @@ +