more refactoring. most connection saving/loading calls now go through the connection service class

This commit is contained in:
David Sparer
2017-11-12 14:23:00 -06:00
parent e9d47f046d
commit e2c82086be
19 changed files with 370 additions and 387 deletions

View File

@@ -67,14 +67,14 @@ namespace mRemoteNG.App
}
}
private static void SaveExportFile(string fileName, ConnectionsSaver.Format saveFormat, SaveFilter saveFilter, ConnectionInfo exportTarget)
private static void SaveExportFile(string fileName, SaveFormat saveFormat, SaveFilter saveFilter, ConnectionInfo exportTarget)
{
try
{
ISerializer<ConnectionInfo, string> serializer;
switch (saveFormat)
{
case ConnectionsSaver.Format.mRXML:
case SaveFormat.mRXML:
var cryptographyProvider = new CryptoProviderFactoryFromSettings().Build();
var rootNode = exportTarget.GetRootParent() as RootNodeInfo;
var connectionNodeSerializer = new XmlConnectionNodeSerializer26(
@@ -83,7 +83,7 @@ namespace mRemoteNG.App
saveFilter);
serializer = new XmlConnectionsSerializer(cryptographyProvider, connectionNodeSerializer);
break;
case ConnectionsSaver.Format.mRCSV:
case SaveFormat.mRCSV:
serializer = new CsvConnectionsSerializerMremotengFormat(saveFilter, Runtime.CredentialProviderCatalog);
break;
default:

View File

@@ -4,7 +4,6 @@ using System.Security;
using System.Threading;
using System.Windows.Forms;
using mRemoteNG.App.Info;
using mRemoteNG.Config.Connections;
using mRemoteNG.Config.Connections.Multiuser;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Putty;
@@ -13,7 +12,6 @@ using mRemoteNG.Credential;
using mRemoteNG.Credential.Repositories;
using mRemoteNG.Messages;
using mRemoteNG.Security;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI;
@@ -40,8 +38,6 @@ namespace mRemoteNG.App
public static MessageCollector MessageCollector { get; } = new MessageCollector();
public static NotificationAreaIcon NotificationAreaIcon { get; set; }
public static RemoteConnectionsSyncronizer RemoteConnectionsSyncronizer { get; set; }
// ReSharper disable once UnusedAutoPropertyAccessor.Local
public static DateTime LastSqlUpdate { get; set; }
public static ExternalToolsService ExternalToolsService { get; } = new ExternalToolsService();
public static SecureString EncryptionKey { get; set; } = new RootNodeInfo(RootNodeType.Connection).PasswordString.ConvertToSecureString();
public static ICredentialRepositoryList CredentialProviderCatalog { get; } = new CredentialRepositoryList();
@@ -97,7 +93,7 @@ namespace mRemoteNG.App
if (Settings.Default.UseSQLServer)
{
LastSqlUpdate = DateTime.Now;
ConnectionsService.LastSqlUpdate = DateTime.Now;
}
else
{
@@ -210,98 +206,9 @@ namespace mRemoteNG.App
private static void SaveConnectionsBGd()
{
Monitor.Enter(SaveLock);
SaveConnections();
ConnectionsService.SaveConnections();
Monitor.Exit(SaveLock);
}
public static void SaveConnections()
{
if (ConnectionsService.ConnectionTreeModel == null) return;
if (!ConnectionsService.IsConnectionsFileLoaded) return;
try
{
RemoteConnectionsSyncronizer?.Disable();
var connectionsSaver = new ConnectionsSaver();
if (!Settings.Default.UseSQLServer)
connectionsSaver.ConnectionFileName = ConnectionsService.GetStartupConnectionFileName();
connectionsSaver.SaveFilter = new SaveFilter();
connectionsSaver.ConnectionTreeModel = ConnectionsService.ConnectionTreeModel;
if (Settings.Default.UseSQLServer)
{
connectionsSaver.SaveFormat = ConnectionsSaver.Format.SQL;
connectionsSaver.SQLHost = Settings.Default.SQLHost;
connectionsSaver.SQLDatabaseName = Settings.Default.SQLDatabaseName;
connectionsSaver.SQLUsername = Settings.Default.SQLUser;
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
connectionsSaver.SQLPassword = cryptographyProvider.Decrypt(Settings.Default.SQLPass, EncryptionKey);
}
connectionsSaver.SaveConnections();
if (Settings.Default.UseSQLServer)
LastSqlUpdate = DateTime.Now;
}
catch (Exception ex)
{
MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.strConnectionsFileCouldNotBeSaved + Environment.NewLine + ex.Message);
}
finally
{
RemoteConnectionsSyncronizer?.Enable();
}
}
public static void SaveConnectionsAs()
{
var connectionsSave = new ConnectionsSaver();
try
{
RemoteConnectionsSyncronizer?.Disable();
using (var saveFileDialog = new SaveFileDialog())
{
saveFileDialog.CheckPathExists = true;
saveFileDialog.InitialDirectory = ConnectionsFileInfo.DefaultConnectionsPath;
saveFileDialog.FileName = ConnectionsFileInfo.DefaultConnectionsFile;
saveFileDialog.OverwritePrompt = true;
saveFileDialog.Filter = $@"{Language.strFiltermRemoteXML}|*.xml|{Language.strFilterAll}|*.*";
if (saveFileDialog.ShowDialog(FrmMain.Default) != DialogResult.OK) return;
connectionsSave.SaveFormat = ConnectionsSaver.Format.mRXML;
connectionsSave.ConnectionFileName = saveFileDialog.FileName;
connectionsSave.SaveFilter = new SaveFilter();
connectionsSave.ConnectionTreeModel = ConnectionsService.ConnectionTreeModel;
connectionsSave.SaveConnections();
if (saveFileDialog.FileName == ConnectionsService.GetDefaultStartupConnectionFileName())
{
Settings.Default.LoadConsFromCustomLocation = false;
}
else
{
Settings.Default.LoadConsFromCustomLocation = true;
Settings.Default.CustomConsPath = saveFileDialog.FileName;
}
}
}
catch (Exception ex)
{
MessageCollector.AddExceptionMessage(string.Format(Language.strConnectionsFileCouldNotSaveAs, connectionsSave.ConnectionFileName), ex);
}
finally
{
RemoteConnectionsSyncronizer?.Enable();
}
}
#endregion
}
}

View File

@@ -55,7 +55,7 @@ namespace mRemoteNG.App
private static void SaveConnections()
{
if (Settings.Default.SaveConsOnExit)
Runtime.SaveConnections();
Runtime.ConnectionsService.SaveConnections();
}
private static void SaveSettings(Control quickConnectToolStrip, ExternalToolsToolStrip externalToolsToolStrip, FrmMain frmMain)

View File

@@ -57,8 +57,6 @@ namespace mRemoteNG.App
public void CreateConnectionsProvider(MessageCollector messageCollector)
{
messageCollector.AddMessage(MessageClass.DebugMsg, "Determining if we need a database syncronizer");
_frmMain.AreWeUsingSqlServerForSavingConnections = Settings.Default.UseSQLServer;
if (!Settings.Default.UseSQLServer) return;
messageCollector.AddMessage(MessageClass.DebugMsg, "Creating database syncronizer");
Runtime.RemoteConnectionsSyncronizer = new RemoteConnectionsSyncronizer(new SqlConnectionsUpdateChecker());

View File

@@ -1,45 +0,0 @@
using mRemoteNG.Config.Putty;
using mRemoteNG.Tree;
namespace mRemoteNG.Config.Connections
{
public class ConnectionsLoader
{
/// <summary>
/// Load connections from a source. <see cref="connectionFileName"/> is ignored if
/// <see cref="useDatabase"/> is true.
/// </summary>
/// <param name="useDatabase"></param>
/// <param name="import"></param>
/// <param name="connectionFileName"></param>
public ConnectionTreeModel LoadConnections(bool useDatabase, bool import, string connectionFileName)
{
ConnectionTreeModel connectionTreeModel;
if (useDatabase)
{
var sqlLoader = new SqlConnectionsLoader();
connectionTreeModel = sqlLoader.Load();
}
else
{
var xmlLoader = new XmlConnectionsLoader(connectionFileName);
connectionTreeModel = xmlLoader.Load();
}
if (connectionTreeModel == null)
connectionTreeModel = new ConnectionTreeModel();
if (!import)
AddPuttySessions(connectionTreeModel);
return connectionTreeModel;
}
private void AddPuttySessions(ConnectionTreeModel connectionTreeModel)
{
PuttySessionsManager.Instance.AddSessions();
connectionTreeModel.RootNodes.AddRange(PuttySessionsManager.Instance.RootPuttySessionsNodes);
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using mRemoteNG.Tree;
namespace mRemoteNG.Config.Connections
{
public class ConnectionsSavedEventArgs
{
public ConnectionTreeModel ModelThatWasSaved { get; }
public bool PreviouslyUsingDatabase { get; }
public bool UsingDatabase { get; }
public string ConnectionFileName { get; }
public ConnectionsSavedEventArgs(ConnectionTreeModel modelThatWasSaved, bool previouslyUsingDatabase, bool usingDatabase, string connectionFileName)
{
if (modelThatWasSaved == null)
throw new ArgumentNullException(nameof(modelThatWasSaved));
ModelThatWasSaved = modelThatWasSaved;
PreviouslyUsingDatabase = previouslyUsingDatabase;
UsingDatabase = usingDatabase;
ConnectionFileName = connectionFileName;
}
}
}

View File

@@ -1,186 +0,0 @@
using System;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Security;
using System.Windows.Forms;
using System.Xml;
using mRemoteNG.App;
using mRemoteNG.App.Info;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Config.Serializers.Versioning;
using mRemoteNG.Container;
using mRemoteNG.Messages;
using mRemoteNG.Security;
using mRemoteNG.Security.Factories;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
using mRemoteNG.UI.Forms;
// ReSharper disable UnusedAutoPropertyAccessor.Global
namespace mRemoteNG.Config.Connections
{
public class ConnectionsSaver
{
public enum Format
{
None,
mRXML,
mRCSV,
SQL
}
private SecureString _password = Runtime.EncryptionKey;
#region Public Properties
public string SQLHost {get; set;}
public string SQLDatabaseName {get; set;}
public string SQLUsername {get; set;}
public string SQLPassword {get; set;}
public string ConnectionFileName {get; set;}
public TreeNode RootTreeNode {get; set;}
public Format SaveFormat {get; set;}
public SaveFilter SaveFilter {get; set;}
public ConnectionTreeModel ConnectionTreeModel { get; set; }
#endregion
#region Public Methods
public void SaveConnections()
{
switch (SaveFormat)
{
case Format.SQL:
SaveToSql();
break;
case Format.mRCSV:
SaveToMremotengFormattedCsv();
break;
default:
SaveToXml();
FrmMain.Default.ConnectionsFileName = ConnectionFileName;
break;
}
FrmMain.Default.AreWeUsingSqlServerForSavingConnections = SaveFormat == Format.SQL;
}
#endregion
#region SQL
private void SaveToSql()
{
var sqlConnector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings();
sqlConnector.Connect();
var databaseVersionVerifier = new SqlDatabaseVersionVerifier(sqlConnector);
if (!databaseVersionVerifier.VerifyDatabaseVersion())
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.strErrorConnectionListSaveFailed);
return;
}
var rootTreeNode = Runtime.ConnectionsService.ConnectionTreeModel.RootNodes.OfType<RootNodeInfo>().First();
UpdateRootNodeTable(rootTreeNode, sqlConnector);
UpdateConnectionsTable(rootTreeNode, sqlConnector);
UpdateUpdatesTable(sqlConnector);
sqlConnector.Disconnect();
sqlConnector.Dispose();
}
private void UpdateRootNodeTable(RootNodeInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector)
{
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
string strProtected;
if (rootTreeNode != null)
{
if (rootTreeNode.Password)
{
_password = rootTreeNode.PasswordString.ConvertToSecureString();
strProtected = cryptographyProvider.Encrypt("ThisIsProtected", _password);
}
else
{
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", _password);
}
}
else
{
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", _password);
}
var sqlQuery = new SqlCommand("DELETE FROM tblRoot", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
if (rootTreeNode != null)
{
sqlQuery =
new SqlCommand(
"INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES(\'" +
MiscTools.PrepareValueForDB(rootTreeNode.Name) + "\', 0, \'" + strProtected + "\'," +
ConnectionsFileInfo.ConnectionFileVersion.ToString(CultureInfo.InvariantCulture) + ")",
sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
}
else
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, $"UpdateRootNodeTable: rootTreeNode was null. Could not insert!");
}
}
private void UpdateConnectionsTable(ContainerInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector)
{
var sqlQuery = new SqlCommand("DELETE FROM tblCons", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
var serializer = new DataTableSerializer(SaveFilter);
var dataTable = serializer.Serialize(rootTreeNode);
var dataProvider = new SqlDataProvider(sqlDatabaseConnector);
dataProvider.Save(dataTable);
}
private void UpdateUpdatesTable(SqlDatabaseConnector sqlDatabaseConnector)
{
var sqlQuery = new SqlCommand("DELETE FROM tblUpdate", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
sqlQuery = new SqlCommand("INSERT INTO tblUpdate (LastUpdate) VALUES(\'" + MiscTools.DBDate(DateTime.Now) + "\')", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
}
#endregion
private void SaveToXml()
{
try
{
var cryptographyProvider = new CryptoProviderFactoryFromSettings().Build();
var connectionNodeSerializer = new XmlConnectionNodeSerializer26(
cryptographyProvider,
ConnectionTreeModel.RootNodes.OfType<RootNodeInfo>().First().PasswordString.ConvertToSecureString(),
SaveFilter);
var xmlConnectionsSerializer = new XmlConnectionsSerializer(cryptographyProvider, connectionNodeSerializer)
{
UseFullEncryption = mRemoteNG.Settings.Default.EncryptCompleteConnectionsFile
};
var xml = xmlConnectionsSerializer.Serialize(ConnectionTreeModel);
var fileDataProvider = new FileDataProviderWithRollingBackup(ConnectionFileName);
fileDataProvider.Save(xml);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace("SaveToXml failed", ex);
}
}
private void SaveToMremotengFormattedCsv()
{
var csvConnectionsSerializer = new CsvConnectionsSerializerMremotengFormat(SaveFilter, Runtime.CredentialProviderCatalog);
var dataProvider = new FileDataProvider(ConnectionFileName);
var csvContent = csvConnectionsSerializer.Serialize(ConnectionTreeModel);
dataProvider.Save(csvContent);
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using mRemoteNG.App;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Security;
using mRemoteNG.Tree;
namespace mRemoteNG.Config.Connections
{
public class CsvConnectionsSaver : ISaver<ConnectionTreeModel>
{
private readonly string _connectionFileName;
private readonly SaveFilter _saveFilter;
public CsvConnectionsSaver(string connectionFileName, SaveFilter saveFilter)
{
if (string.IsNullOrEmpty(connectionFileName))
throw new ArgumentException($"Argument '{nameof(connectionFileName)}' cannot be null or empty");
if (saveFilter == null)
throw new ArgumentNullException(nameof(saveFilter));
_connectionFileName = connectionFileName;
_saveFilter = saveFilter;
}
public void Save(ConnectionTreeModel connectionTreeModel)
{
var csvConnectionsSerializer = new CsvConnectionsSerializerMremotengFormat(_saveFilter, Runtime.CredentialProviderCatalog);
var dataProvider = new FileDataProvider(_connectionFileName);
var csvContent = csvConnectionsSerializer.Serialize(connectionTreeModel);
dataProvider.Save(csvContent);
}
}
}

View File

@@ -9,7 +9,6 @@ namespace mRemoteNG.Config.Connections.Multiuser
{
private readonly Timer _updateTimer;
private readonly IConnectionsUpdateChecker _updateChecker;
private readonly ConnectionsSaver _connectionsSaver;
public double TimerIntervalInMilliseconds
{
@@ -20,7 +19,6 @@ namespace mRemoteNG.Config.Connections.Multiuser
{
_updateChecker = updateChecker;
_updateTimer = new Timer(3000);
_connectionsSaver = new ConnectionsSaver { SaveFormat = ConnectionsSaver.Format.SQL };
SetEventListeners();
}
@@ -33,22 +31,12 @@ namespace mRemoteNG.Config.Connections.Multiuser
ConnectionsUpdateAvailable += Load;
}
public void Load()
{
Runtime.ConnectionsService.ConnectionTreeModel = Runtime.ConnectionsService.LoadConnections(mRemoteNG.Settings.Default.UseSQLServer, false, "");
}
private void Load(object sender, ConnectionsUpdateAvailableEventArgs args)
{
Load();
Runtime.ConnectionsService.ConnectionTreeModel = Runtime.ConnectionsService.LoadConnections(true, false, "");
args.Handled = true;
}
public void Save()
{
_connectionsSaver.SaveConnections();
}
public void Enable()
{
_updateTimer.Start();

View File

@@ -64,7 +64,7 @@ namespace mRemoteNG.Config.Connections
private bool CheckIfIAmTheLastOneUpdated(DateTime lastUpdateInDb)
{
DateTime LastSqlUpdateWithoutMilliseconds = new DateTime(Runtime.LastSqlUpdate.Ticks - (Runtime.LastSqlUpdate.Ticks % TimeSpan.TicksPerSecond), Runtime.LastSqlUpdate.Kind);
DateTime LastSqlUpdateWithoutMilliseconds = new DateTime(Runtime.ConnectionsService.LastSqlUpdate.Ticks - (Runtime.ConnectionsService.LastSqlUpdate.Ticks % TimeSpan.TicksPerSecond), Runtime.ConnectionsService.LastSqlUpdate.Kind);
return lastUpdateInDb == LastSqlUpdateWithoutMilliseconds;
}

View File

@@ -0,0 +1,10 @@
namespace mRemoteNG.Config.Connections
{
public enum SaveFormat
{
None,
mRXML,
mRCSV,
SQL
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Security;
using mRemoteNG.App;
using mRemoteNG.App.Info;
using mRemoteNG.Config.DatabaseConnectors;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Config.Serializers.Versioning;
using mRemoteNG.Container;
using mRemoteNG.Messages;
using mRemoteNG.Security;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Connections
{
public class SqlConnectionsSaver : ISaver<ConnectionTreeModel>
{
private SecureString _password = Runtime.EncryptionKey;
private readonly SaveFilter _saveFilter;
public SqlConnectionsSaver(SaveFilter saveFilter)
{
if (saveFilter == null)
throw new ArgumentNullException(nameof(saveFilter));
_saveFilter = saveFilter;
}
public void Save(ConnectionTreeModel connectionTreeModel)
{
using (var sqlConnector = DatabaseConnectorFactory.SqlDatabaseConnectorFromSettings())
{
sqlConnector.Connect();
var databaseVersionVerifier = new SqlDatabaseVersionVerifier(sqlConnector);
if (!databaseVersionVerifier.VerifyDatabaseVersion())
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, Language.strErrorConnectionListSaveFailed);
return;
}
var rootTreeNode = connectionTreeModel.RootNodes.OfType<RootNodeInfo>().First();
UpdateRootNodeTable(rootTreeNode, sqlConnector);
UpdateConnectionsTable(rootTreeNode, sqlConnector);
UpdateUpdatesTable(sqlConnector);
}
}
private void UpdateRootNodeTable(RootNodeInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector)
{
var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
string strProtected;
if (rootTreeNode != null)
{
if (rootTreeNode.Password)
{
_password = rootTreeNode.PasswordString.ConvertToSecureString();
strProtected = cryptographyProvider.Encrypt("ThisIsProtected", _password);
}
else
{
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", _password);
}
}
else
{
strProtected = cryptographyProvider.Encrypt("ThisIsNotProtected", _password);
}
var sqlQuery = new SqlCommand("DELETE FROM tblRoot", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
if (rootTreeNode != null)
{
sqlQuery =
new SqlCommand(
"INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES(\'" +
MiscTools.PrepareValueForDB(rootTreeNode.Name) + "\', 0, \'" + strProtected + "\'," +
ConnectionsFileInfo.ConnectionFileVersion.ToString(CultureInfo.InvariantCulture) + ")",
sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
}
else
{
Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, $"UpdateRootNodeTable: rootTreeNode was null. Could not insert!");
}
}
private void UpdateConnectionsTable(ContainerInfo rootTreeNode, SqlDatabaseConnector sqlDatabaseConnector)
{
var sqlQuery = new SqlCommand("DELETE FROM tblCons", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
var serializer = new DataTableSerializer(_saveFilter);
var dataTable = serializer.Serialize(rootTreeNode);
var dataProvider = new SqlDataProvider(sqlDatabaseConnector);
dataProvider.Save(dataTable);
}
private void UpdateUpdatesTable(SqlDatabaseConnector sqlDatabaseConnector)
{
var sqlQuery = new SqlCommand("DELETE FROM tblUpdate", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
sqlQuery = new SqlCommand("INSERT INTO tblUpdate (LastUpdate) VALUES(\'" + MiscTools.DBDate(DateTime.Now) + "\')", sqlDatabaseConnector.SqlConnection);
sqlQuery.ExecuteNonQuery();
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Linq;
using mRemoteNG.App;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Security;
using mRemoteNG.Security.Factories;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
namespace mRemoteNG.Config.Connections
{
public class XmlConnectionsSaver : ISaver<ConnectionTreeModel>
{
private readonly string _connectionFileName;
private readonly SaveFilter _saveFilter;
public XmlConnectionsSaver(string connectionFileName, SaveFilter saveFilter)
{
if (string.IsNullOrEmpty(connectionFileName))
throw new ArgumentException($"Argument '{nameof(connectionFileName)}' cannot be null or empty");
if (saveFilter == null)
throw new ArgumentNullException(nameof(saveFilter));
_connectionFileName = connectionFileName;
_saveFilter = saveFilter;
}
public void Save(ConnectionTreeModel connectionTreeModel)
{
try
{
var cryptographyProvider = new CryptoProviderFactoryFromSettings().Build();
var connectionNodeSerializer = new XmlConnectionNodeSerializer26(
cryptographyProvider,
connectionTreeModel.RootNodes.OfType<RootNodeInfo>().First().PasswordString.ConvertToSecureString(),
_saveFilter);
var xmlConnectionsSerializer = new XmlConnectionsSerializer(cryptographyProvider, connectionNodeSerializer)
{
UseFullEncryption = mRemoteNG.Settings.Default.EncryptCompleteConnectionsFile
};
var xml = xmlConnectionsSerializer.Serialize(connectionTreeModel);
var fileDataProvider = new FileDataProviderWithRollingBackup(_connectionFileName);
fileDataProvider.Save(xml);
}
catch (Exception ex)
{
Runtime.MessageCollector?.AddExceptionStackTrace("SaveToXml failed", ex);
}
}
}
}

View File

@@ -6,6 +6,7 @@ using mRemoteNG.App.Info;
using mRemoteNG.Config.Connections;
using mRemoteNG.Config.Putty;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Security;
using mRemoteNG.Tree;
using mRemoteNG.Tree.Root;
@@ -14,12 +15,11 @@ namespace mRemoteNG.Connection
public class ConnectionsService
{
private readonly PuttySessionsManager _puttySessionsManager;
private readonly ConnectionsLoader _connectionsLoader;
public bool IsConnectionsFileLoaded { get; set; }
public bool UsingDatabase { get; private set; }
public string ConnectionFileName { get; private set; }
public static DateTime LastSqlUpdate { get; private set; }
public DateTime LastSqlUpdate { get; set; }
public ConnectionTreeModel ConnectionTreeModel
{
@@ -31,27 +31,19 @@ namespace mRemoteNG.Connection
{
if (puttySessionsManager == null)
throw new ArgumentNullException(nameof(puttySessionsManager));
_puttySessionsManager = puttySessionsManager;
_connectionsLoader = new ConnectionsLoader();
}
public void NewConnectionsFile(string filename)
{
try
{
UpdateCustomConsPathSetting(filename);
var newConnectionsModel = new ConnectionTreeModel();
newConnectionsModel.AddRootNode(new RootNodeInfo(RootNodeType.Connection));
var connectionSaver = new ConnectionsSaver
{
ConnectionTreeModel = newConnectionsModel,
ConnectionFileName = filename,
SaveFilter = new Security.SaveFilter()
};
connectionSaver.SaveConnections();
SaveConnections(newConnectionsModel, false, new SaveFilter(), filename);
LoadConnections(false, false, filename);
UpdateCustomConsPathSetting(filename);
}
catch (Exception ex)
{
@@ -103,7 +95,19 @@ namespace mRemoteNG.Connection
{
var oldConnectionTreeModel = ConnectionTreeModel;
var oldIsUsingDatabaseValue = UsingDatabase;
var newConnectionTreeModel = _connectionsLoader.LoadConnections(useDatabase, import, connectionFileName);
var newConnectionTreeModel =
(useDatabase
? new SqlConnectionsLoader().Load()
: new XmlConnectionsLoader(connectionFileName).Load())
?? new ConnectionTreeModel();
if (!import)
{
_puttySessionsManager.AddSessions();
newConnectionTreeModel.RootNodes.AddRange(_puttySessionsManager.RootPuttySessionsNodes);
}
IsConnectionsFileLoaded = true;
ConnectionFileName = connectionFileName;
UsingDatabase = useDatabase;
@@ -112,6 +116,66 @@ namespace mRemoteNG.Connection
return newConnectionTreeModel;
}
/// <summary>
/// Saves the currently loaded <see cref="ConnectionTreeModel"/> with
/// no <see cref="SaveFilter"/>.
/// </summary>
public void SaveConnections()
{
if (!IsConnectionsFileLoaded)
return;
SaveConnections(ConnectionTreeModel, UsingDatabase, new SaveFilter(), ConnectionFileName);
}
/// <summary>
/// Saves the given <see cref="ConnectionTreeModel"/>.
/// If <see cref="useDatabase"/> is true, <see cref="connectionFileName"/> is ignored
/// </summary>
/// <param name="connectionTreeModel"></param>
/// <param name="useDatabase"></param>
/// <param name="saveFilter"></param>
/// <param name="connectionFileName"></param>
public void SaveConnections(ConnectionTreeModel connectionTreeModel, bool useDatabase, SaveFilter saveFilter, string connectionFileName)
{
if (connectionTreeModel == null) return;
try
{
Runtime.RemoteConnectionsSyncronizer?.Disable();
var previouslyUsingDatabase = UsingDatabase;
if (useDatabase)
new SqlConnectionsSaver(saveFilter).Save(connectionTreeModel);
else
new XmlConnectionsSaver(connectionFileName, saveFilter).Save(connectionTreeModel);
if (UsingDatabase)
LastSqlUpdate = DateTime.Now;
UsingDatabase = useDatabase;
ConnectionFileName = connectionFileName;
RaiseConnectionsSavedEvent(connectionTreeModel, previouslyUsingDatabase, UsingDatabase, connectionFileName);
}
catch (Exception ex)
{
Runtime.MessageCollector?.AddExceptionMessage(string.Format(Language.strConnectionsFileCouldNotSaveAs, connectionFileName), ex, logOnly:false);
}
finally
{
Runtime.RemoteConnectionsSyncronizer?.Enable();
}
}
public string GetStartupConnectionFileName()
{
return Settings.Default.LoadConsFromCustomLocation == false ? GetDefaultStartupConnectionFileName() : Settings.Default.CustomConsPath;
}
public string GetDefaultStartupConnectionFileName()
{
return Runtime.IsPortableEdition ? GetDefaultStartupConnectionFileNamePortableEdition() : GetDefaultStartupConnectionFileNameNormalEdition();
}
private void UpdateCustomConsPathSetting(string filename)
{
if (filename == GetDefaultStartupConnectionFileName())
@@ -125,16 +189,6 @@ namespace mRemoteNG.Connection
}
}
public string GetStartupConnectionFileName()
{
return Settings.Default.LoadConsFromCustomLocation == false ? GetDefaultStartupConnectionFileName() : Settings.Default.CustomConsPath;
}
public string GetDefaultStartupConnectionFileName()
{
return Runtime.IsPortableEdition ? GetDefaultStartupConnectionFileNamePortableEdition() : GetDefaultStartupConnectionFileNameNormalEdition();
}
private string GetDefaultStartupConnectionFileNameNormalEdition()
{
var appDataPath = Path.Combine(
@@ -151,8 +205,9 @@ namespace mRemoteNG.Connection
#region Events
public event EventHandler<ConnectionsLoadedEventArgs> ConnectionsLoaded;
public event EventHandler<ConnectionsSavedEventArgs> ConnectionsSaved;
protected virtual void RaiseConnectionsLoadedEvent(ConnectionTreeModel previousTreeModel, ConnectionTreeModel newTreeModel,
private void RaiseConnectionsLoadedEvent(ConnectionTreeModel previousTreeModel, ConnectionTreeModel newTreeModel,
bool previousSourceWasDatabase, bool newSourceIsDatabase,
string newSourcePath)
{
@@ -163,6 +218,11 @@ namespace mRemoteNG.Connection
newSourceIsDatabase,
newSourcePath));
}
private void RaiseConnectionsSavedEvent(ConnectionTreeModel modelThatWasSaved, bool previouslyUsingDatabase, bool usingDatabase, string connectionFileName)
{
ConnectionsSaved?.Invoke(this, new ConnectionsSavedEventArgs(modelThatWasSaved, previouslyUsingDatabase, usingDatabase, connectionFileName));
}
#endregion
}
}

View File

@@ -26,12 +26,12 @@ namespace mRemoteNG.UI.Forms
}
}
public ConnectionsSaver.Format SaveFormat
public SaveFormat SaveFormat
{
get
{
var exportFormat = cboFileFormat.SelectedItem as ExportFormat;
return exportFormat?.Format ?? ConnectionsSaver.Format.mRXML;
return exportFormat?.Format ?? SaveFormat.mRXML;
}
set
{
@@ -173,8 +173,8 @@ namespace mRemoteNG.UI.Forms
private void ExportForm_Load(object sender, EventArgs e)
{
cboFileFormat.Items.Clear();
cboFileFormat.Items.Add(new ExportFormat(ConnectionsSaver.Format.mRXML));
cboFileFormat.Items.Add(new ExportFormat(ConnectionsSaver.Format.mRCSV));
cboFileFormat.Items.Add(new ExportFormat(SaveFormat.mRXML));
cboFileFormat.Items.Add(new ExportFormat(SaveFormat.mRCSV));
cboFileFormat.SelectedIndex = 0;
ApplyTheme();
ThemeManager.getInstance().ThemeChanged += ApplyTheme;
@@ -211,7 +211,7 @@ namespace mRemoteNG.UI.Forms
private void SelectFileTypeBasedOnSaveFormat(FileDialog saveFileDialog)
{
saveFileDialog.FilterIndex = SaveFormat == ConnectionsSaver.Format.mRCSV ? 2 : 1;
saveFileDialog.FilterIndex = SaveFormat == SaveFormat.mRCSV ? 2 : 1;
}
private void btnOK_Click(object sender, EventArgs e)
@@ -226,7 +226,7 @@ namespace mRemoteNG.UI.Forms
private void cboFileformat_SelectedIndexChanged(object sender, EventArgs e)
{
if (SaveFormat == ConnectionsSaver.Format.mRXML)
if (SaveFormat == SaveFormat.mRXML)
{
chkUsername.Enabled = false;
chkPassword.Enabled = false;
@@ -296,12 +296,12 @@ namespace mRemoteNG.UI.Forms
{
#region Public Properties
public ConnectionsSaver.Format Format { get; }
public SaveFormat Format { get; }
#endregion
#region Constructors
public ExportFormat(ConnectionsSaver.Format format)
public ExportFormat(SaveFormat format)
{
Format = format;
}
@@ -312,9 +312,9 @@ namespace mRemoteNG.UI.Forms
{
switch (Format)
{
case ConnectionsSaver.Format.mRXML:
case SaveFormat.mRXML:
return Language.strMremoteNgXml;
case ConnectionsSaver.Format.mRCSV:
case SaveFormat.mRCSV:
return Language.strMremoteNgCsv;
default:
return Format.ToString();

View File

@@ -69,7 +69,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
private static void ReinitializeSqlUpdater()
{
Runtime.RemoteConnectionsSyncronizer?.Dispose();
FrmMain.Default.AreWeUsingSqlServerForSavingConnections = Settings.Default.UseSQLServer;
if (Settings.Default.UseSQLServer)
{
@@ -80,6 +79,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
{
Runtime.RemoteConnectionsSyncronizer?.Dispose();
Runtime.RemoteConnectionsSyncronizer = null;
Runtime.LoadConnections(true);
}
}

View File

@@ -449,7 +449,7 @@ namespace mRemoteNG.UI.Forms
if (Runtime.ConnectionsService.IsConnectionsFileLoaded)
{
if (AreWeUsingSqlServerForSavingConnections)
if (Runtime.ConnectionsService.UsingDatabase)
{
titleBuilder.Append(separator);
titleBuilder.Append(Language.strSQLServer.TrimEnd(':'));

View File

@@ -6,7 +6,9 @@ using mRemoteNG.App;
using mRemoteNG.App.Info;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Security;
using mRemoteNG.Tree;
using mRemoteNG.UI.Forms;
using mRemoteNG.UI.Window;
namespace mRemoteNG.UI.Menu
@@ -358,7 +360,7 @@ namespace mRemoteNG.UI.Menu
switch (msgBoxResult)
{
case DialogResult.Yes:
Runtime.SaveConnections();
Runtime.ConnectionsService.SaveConnections();
break;
case DialogResult.Cancel:
return;
@@ -375,7 +377,29 @@ namespace mRemoteNG.UI.Menu
private void mMenFileSaveAs_Click(object sender, EventArgs e)
{
Runtime.SaveConnectionsAs();
using (var saveFileDialog = new SaveFileDialog())
{
saveFileDialog.CheckPathExists = true;
saveFileDialog.InitialDirectory = ConnectionsFileInfo.DefaultConnectionsPath;
saveFileDialog.FileName = ConnectionsFileInfo.DefaultConnectionsFile;
saveFileDialog.OverwritePrompt = true;
saveFileDialog.Filter = $@"{Language.strFiltermRemoteXML}|*.xml|{Language.strFilterAll}|*.*";
if (saveFileDialog.ShowDialog(FrmMain.Default) != DialogResult.OK) return;
var newFileName = saveFileDialog.FileName;
Runtime.ConnectionsService.SaveConnections(Runtime.ConnectionsService.ConnectionTreeModel, false, new SaveFilter(), newFileName);
if (newFileName == Runtime.ConnectionsService.GetDefaultStartupConnectionFileName())
{
Settings.Default.LoadConsFromCustomLocation = false;
}
else
{
Settings.Default.LoadConsFromCustomLocation = true;
Settings.Default.CustomConsPath = newFileName;
}
}
}
private void mMenFileDelete_Click(object sender, EventArgs e)

View File

@@ -134,12 +134,17 @@
<Compile Include="App\Update\UpdateInfo.cs" />
<Compile Include="App\Windows.cs" />
<Compile Include="Config\Connections\ConnectionsLoadedEventArgs.cs" />
<Compile Include="Config\Connections\ConnectionsSavedEventArgs.cs" />
<Compile Include="Config\Connections\CsvConnectionsSaver.cs" />
<Compile Include="Config\Connections\SaveFormat.cs" />
<Compile Include="Config\Connections\Multiuser\ConnectionsUpdateCheckFinishedEventArgs.cs" />
<Compile Include="Config\Connections\Multiuser\IConnectionsUpdateChecker.cs" />
<Compile Include="Config\Connections\Multiuser\ConnectionsUpdateAvailableEventArgs.cs" />
<Compile Include="Config\Connections\Multiuser\RemoteConnectionsSyncronizer.cs" />
<Compile Include="Config\Connections\SqlConnectionsLoader.cs" />
<Compile Include="Config\Connections\SqlConnectionsSaver.cs" />
<Compile Include="Config\Connections\XmlConnectionsLoader.cs" />
<Compile Include="Config\Connections\XmlConnectionsSaver.cs" />
<Compile Include="Config\CredentialHarvester.cs" />
<Compile Include="Config\CredentialRecordLoader.cs" />
<Compile Include="Config\CredentialRecordSaver.cs" />
@@ -175,8 +180,6 @@
<Compile Include="Config\Serializers\Versioning\SqlVersion24To25Upgrader.cs" />
<Compile Include="Config\Serializers\Versioning\SqlVersion25To26Upgrader.cs" />
<Compile Include="Config\Serializers\XmlConnectionsDecryptor.cs" />
<Compile Include="Config\Connections\ConnectionsLoader.cs" />
<Compile Include="Config\Connections\ConnectionsSaver.cs" />
<Compile Include="Config\DataProviders\FileDataProviderWithRollingBackup.cs" />
<Compile Include="Config\DataProviders\SqlDataProvider.cs" />
<Compile Include="Config\Serializers\IDeserializer.cs" />