From 11ea575898903ecc7d30a0e64300ac5cb48170be Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sun, 26 Jan 2020 16:43:09 -0600 Subject: [PATCH] implemented importing of basic RDM connection types --- .../RemoteDesktopManagerDeserializerTests.cs | 200 ++++++++++ .../Properties/Resources.Designer.cs | 108 ++++++ mRemoteNGTests/Properties/Resources.resx | 15 + mRemoteNGTests/Resources/rdp.rdm | 65 ++++ mRemoteNGTests/Resources/ssh.rdm | 36 ++ mRemoteNGTests/Resources/telnet.rdm | 30 ++ mRemoteNGTests/Resources/vnc.rdm | 45 +++ mRemoteNGTests/Resources/website.rdm | 29 ++ mRemoteNGTests/mRemoteNGTests.csproj | 6 + .../Xml/XmlExtensions.cs | 41 ++ .../RemoteDesktopManagerDeserializer.cs | 366 ++++++++++++++++++ mRemoteV1/mRemoteV1.csproj | 1 + 12 files changed, 942 insertions(+) create mode 100644 mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopManagerDeserializerTests.cs create mode 100644 mRemoteNGTests/Resources/rdp.rdm create mode 100644 mRemoteNGTests/Resources/ssh.rdm create mode 100644 mRemoteNGTests/Resources/telnet.rdm create mode 100644 mRemoteNGTests/Resources/vnc.rdm create mode 100644 mRemoteNGTests/Resources/website.rdm create mode 100644 mRemoteV1/Config/Serializers/MiscSerializers/RemoteDesktopManagerDeserializer.cs diff --git a/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopManagerDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopManagerDeserializerTests.cs new file mode 100644 index 000000000..bd43715f1 --- /dev/null +++ b/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopManagerDeserializerTests.cs @@ -0,0 +1,200 @@ +using mRemoteNG.Config.Serializers.MiscSerializers; +using mRemoteNG.Connection; +using mRemoteNG.Connection.Protocol; +using mRemoteNG.Connection.Protocol.Http; +using mRemoteNG.Connection.Protocol.RDP; +using mRemoteNG.Connection.Protocol.VNC; +using mRemoteNGTests.Properties; +using NUnit.Framework; +using System.Collections.Generic; +using System.Linq; + +namespace mRemoteNGTests.Config.Serializers.MiscSerializers +{ + public class RemoteDesktopManagerDeserializerTests + { + private readonly RemoteDesktopManagerDeserializer _sut = new RemoteDesktopManagerDeserializer(); + private readonly Dictionary _rdmExports = new Dictionary + { + {ProtocolType.RDP, Resources.rdp_rdm_export}, + {ProtocolType.SSH2, Resources.ssh_rdm_export}, + {ProtocolType.VNC, Resources.vnc_rdm_export}, + {ProtocolType.Telnet, Resources.telnet_rdm_export}, + {ProtocolType.HTTPS, Resources.website_rdm_export}, + }; + + [TestCaseSource(nameof(RdpPropertiesAndValues))] + public void CorrectlyImportsRdpProperties(string propertyName, object expectedValue) + { + var rootContainer = _sut.Deserialize(_rdmExports[ProtocolType.RDP]); + var node = rootContainer.RootNodes[0].Children.FirstOrDefault(i => i.Protocol == ProtocolType.RDP); + Assert.That(node, Is.Not.Null); + + var actualValue = node.GetType().GetProperty(propertyName)?.GetValue(node); + Assert.That(actualValue, Is.EqualTo(expectedValue)); + } + + [TestCaseSource(nameof(SshPropertiesAndValues))] + public void CorrectlyImportsSshProperties(string propertyName, object expectedValue) + { + var rootContainer = _sut.Deserialize(_rdmExports[ProtocolType.SSH2]); + var node = rootContainer.RootNodes[0].Children.FirstOrDefault(i => i.Protocol == ProtocolType.SSH2); + Assert.That(node, Is.Not.Null); + + var actualValue = node.GetType().GetProperty(propertyName)?.GetValue(node); + Assert.That(actualValue, Is.EqualTo(expectedValue)); + } + + [TestCaseSource(nameof(VncPropertiesAndValues))] + public void CorrectlyImportsVncProperties(string propertyName, object expectedValue) + { + var rootContainer = _sut.Deserialize(_rdmExports[ProtocolType.VNC]); + var node = rootContainer.RootNodes[0].Children.FirstOrDefault(i => i.Protocol == ProtocolType.VNC); + Assert.That(node, Is.Not.Null); + + var actualValue = node.GetType().GetProperty(propertyName)?.GetValue(node); + Assert.That(actualValue, Is.EqualTo(expectedValue)); + } + + [TestCaseSource(nameof(TelnetPropertiesAndValues))] + public void CorrectlyImportsTelnetProperties(string propertyName, object expectedValue) + { + var rootContainer = _sut.Deserialize(_rdmExports[ProtocolType.Telnet]); + var node = rootContainer.RootNodes[0].Children.FirstOrDefault(i => i.Protocol == ProtocolType.Telnet); + Assert.That(node, Is.Not.Null); + + var actualValue = node.GetType().GetProperty(propertyName)?.GetValue(node); + Assert.That(actualValue, Is.EqualTo(expectedValue)); + } + + [TestCaseSource(nameof(WebsitePropertiesAndValues))] + public void CorrectlyImportsWebsiteProperties(string propertyName, object expectedValue) + { + var rootContainer = _sut.Deserialize(_rdmExports[ProtocolType.HTTPS]); + var node = rootContainer.RootNodes[0].Children.FirstOrDefault(i => i.Protocol == ProtocolType.HTTPS); + Assert.That(node, Is.Not.Null); + + var actualValue = node.GetType().GetProperty(propertyName)?.GetValue(node); + Assert.That(actualValue, Is.EqualTo(expectedValue)); + } + + private static IEnumerable RdpPropertiesAndValues() + { + return new[] + { + new TestCaseData(nameof(ConnectionInfo.ConstantID), "1f36e6f0-90ca-4607-b6ec-86227738486d"), + new TestCaseData(nameof(ConnectionInfo.Name), "rdp connection"), + new TestCaseData(nameof(ConnectionInfo.Protocol), ProtocolType.RDP), + new TestCaseData(nameof(ConnectionInfo.Hostname), "my.rdp.host.com"), + new TestCaseData(nameof(ConnectionInfo.Description), "This is a general description"), + new TestCaseData(nameof(ConnectionInfo.Username), "user1"), + new TestCaseData(nameof(ConnectionInfo.Domain), "domain1"), + //new TestCaseData(nameof(ConnectionInfo.Password), "password1"), + new TestCaseData(nameof(ConnectionInfo.Resolution), RDPResolutions.FitToWindow), + new TestCaseData(nameof(ConnectionInfo.AutomaticResize), true), + new TestCaseData(nameof(ConnectionInfo.CacheBitmaps), false), + new TestCaseData(nameof(ConnectionInfo.DisplayThemes), false), + new TestCaseData(nameof(ConnectionInfo.DisplayWallpaper), false), + new TestCaseData(nameof(ConnectionInfo.EnableDesktopComposition), true), + new TestCaseData(nameof(ConnectionInfo.EnableFontSmoothing), true), + new TestCaseData(nameof(ConnectionInfo.RedirectSound), RDPSounds.DoNotPlay), + new TestCaseData(nameof(ConnectionInfo.RedirectAudioCapture), true), + new TestCaseData(nameof(ConnectionInfo.RedirectClipboard), false), + new TestCaseData(nameof(ConnectionInfo.RedirectDiskDrives), false), + new TestCaseData(nameof(ConnectionInfo.RedirectPrinters), true), + new TestCaseData(nameof(ConnectionInfo.RedirectPorts), false), + new TestCaseData(nameof(ConnectionInfo.RedirectSmartCards), true), + new TestCaseData(nameof(ConnectionInfo.RDPMinutesToIdleTimeout), 17), + new TestCaseData(nameof(ConnectionInfo.MacAddress), "mac-add-goes-here"), + new TestCaseData(nameof(ConnectionInfo.UseCredSsp), true), + new TestCaseData(nameof(ConnectionInfo.Port), 1234), + new TestCaseData(nameof(ConnectionInfo.RdpVersion), RdpVersion.Rdc7), + new TestCaseData(nameof(ConnectionInfo.RedirectKeys), true), + new TestCaseData(nameof(ConnectionInfo.UseConsoleSession), true), + new TestCaseData(nameof(ConnectionInfo.RDPAuthenticationLevel), AuthenticationLevel.WarnOnFailedAuth), + new TestCaseData(nameof(ConnectionInfo.RDGatewayUsageMethod), RDGatewayUsageMethod.Always), + new TestCaseData(nameof(ConnectionInfo.RDGatewayUseConnectionCredentials), RDGatewayUseConnectionCredentials.Yes), + new TestCaseData(nameof(ConnectionInfo.RDGatewayHostname), "rdhost1"), + new TestCaseData(nameof(ConnectionInfo.RDGatewayUsername), "rduser1"), + new TestCaseData(nameof(ConnectionInfo.RDGatewayDomain), "rddomain1"), + //new TestCaseData(nameof(ConnectionInfo.RDGatewayPassword), "rdpassword1"), + new TestCaseData(nameof(ConnectionInfo.UseEnhancedMode), true), + new TestCaseData(nameof(ConnectionInfo.UseVmId), true), + new TestCaseData(nameof(ConnectionInfo.VmId), "instance-id-here"), + }; + } + + private static IEnumerable SshPropertiesAndValues() + { + return new[] + { + new TestCaseData(nameof(ConnectionInfo.ConstantID), "44ae261f-0094-48a2-93cb-bc5abcfcc394"), + new TestCaseData(nameof(ConnectionInfo.Name), "ssh connection"), + new TestCaseData(nameof(ConnectionInfo.Protocol), ProtocolType.SSH2), + new TestCaseData(nameof(ConnectionInfo.Hostname), "mysshhost"), + new TestCaseData(nameof(ConnectionInfo.Description), "This is a linux host description"), + new TestCaseData(nameof(ConnectionInfo.Username), "linuxuser1"), + //new TestCaseData(nameof(ConnectionInfo.Password), "linuxpassword1"), + new TestCaseData(nameof(ConnectionInfo.MacAddress), "some-mac-here"), + new TestCaseData(nameof(ConnectionInfo.Port), 4321), + }; + } + + private static IEnumerable VncPropertiesAndValues() + { + return new[] + { + new TestCaseData(nameof(ConnectionInfo.ConstantID), "16ff7dd6-2ac3-4e27-96cf-435b5bfe5a00"), + new TestCaseData(nameof(ConnectionInfo.Name), "vnc connection"), + new TestCaseData(nameof(ConnectionInfo.Protocol), ProtocolType.VNC), + new TestCaseData(nameof(ConnectionInfo.Hostname), "vnchost1"), + new TestCaseData(nameof(ConnectionInfo.Port), 9987), + new TestCaseData(nameof(ConnectionInfo.Description), "This is a VNC description"), + new TestCaseData(nameof(ConnectionInfo.Username), "winuser1"), + new TestCaseData(nameof(ConnectionInfo.Domain), "windomain1"), + //new TestCaseData(nameof(ConnectionInfo.Password), "vncpassword1"), + new TestCaseData(nameof(ConnectionInfo.MacAddress), "some-mac-here"), + + new TestCaseData(nameof(ConnectionInfo.VNCEncoding), ProtocolVNC.Encoding.EncTight), + new TestCaseData(nameof(ConnectionInfo.VNCAuthMode), ProtocolVNC.AuthMode.AuthWin), + new TestCaseData(nameof(ConnectionInfo.VNCCompression), ProtocolVNC.Compression.Comp4), + new TestCaseData(nameof(ConnectionInfo.VNCViewOnly), true), + new TestCaseData(nameof(ConnectionInfo.VNCProxyIP), "proxyhost"), + new TestCaseData(nameof(ConnectionInfo.VNCProxyPort), 7777), + }; + } + + private static IEnumerable TelnetPropertiesAndValues() + { + return new[] + { + new TestCaseData(nameof(ConnectionInfo.ConstantID), "8d77d3ac-b414-4b51-ac10-60304d63cc6f"), + new TestCaseData(nameof(ConnectionInfo.Name), "telnet connection"), + new TestCaseData(nameof(ConnectionInfo.Protocol), ProtocolType.Telnet), + new TestCaseData(nameof(ConnectionInfo.Hostname), "telnethost1"), + new TestCaseData(nameof(ConnectionInfo.Description), "Telnet description"), + new TestCaseData(nameof(ConnectionInfo.Username), "user1"), + //new TestCaseData(nameof(ConnectionInfo.Password), "password1"), + new TestCaseData(nameof(ConnectionInfo.MacAddress), "some-mac-here"), + new TestCaseData(nameof(ConnectionInfo.Port), 7648), + }; + } + + private static IEnumerable WebsitePropertiesAndValues() + { + return new[] + { + new TestCaseData(nameof(ConnectionInfo.ConstantID), "65092747-6870-42c9-b8bc-35ec9fb5b3fb"), + new TestCaseData(nameof(ConnectionInfo.Name), "website connection"), + new TestCaseData(nameof(ConnectionInfo.Protocol), ProtocolType.HTTPS), + new TestCaseData(nameof(ConnectionInfo.Hostname), "www.google.com"), + new TestCaseData(nameof(ConnectionInfo.Description), "Website description"), + new TestCaseData(nameof(ConnectionInfo.Username), "user1"), + new TestCaseData(nameof(ConnectionInfo.Domain), "domain1"), + //new TestCaseData(nameof(ConnectionInfo.Password), "password1"), + new TestCaseData(nameof(ConnectionInfo.Port), 8080), + new TestCaseData(nameof(ConnectionInfo.RenderingEngine), HTTPBase.RenderingEngine.Gecko), + }; + } + } +} diff --git a/mRemoteNGTests/Properties/Resources.Designer.cs b/mRemoteNGTests/Properties/Resources.Designer.cs index 78299efd7..7cb39b48d 100644 --- a/mRemoteNGTests/Properties/Resources.Designer.cs +++ b/mRemoteNGTests/Properties/Resources.Designer.cs @@ -194,6 +194,71 @@ namespace mRemoteNGTests.Properties { } } + /// + /// Looks up a localized string similar to <?xml version="1.0"?> + ///<ArrayOfConnection> + /// <Connection> + /// <AuthentificationLevel>WarnMe</AuthentificationLevel> + /// <AutomaticallyClose>true</AutomaticallyClose> + /// <AutomaticallyCloseInterval>17</AutomaticallyCloseInterval> + /// <ConnectionType>RDPConfigured</ConnectionType> + /// <Console>true</Console> + /// <Description>This is a general description</Description> + /// <DesktopComposition>true</DesktopComposition> + /// <DisableBitmapCache>true</DisableBitmapCache> + /// <DisableThemes>true</Disable [rest of string was truncated]";. + /// + internal static string rdp_rdm_export { + get { + return ResourceManager.GetString("rdp_rdm_export", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0"?> + ///<ArrayOfConnection> + /// <Connection> + /// <ConnectionType>SSHShell</ConnectionType> + /// <Description>This is a linux host description</Description> + /// <ID>44ae261f-0094-48a2-93cb-bc5abcfcc394</ID> + /// <Name>ssh connection</Name> + /// <OpenEmbedded>true</OpenEmbedded> + /// <Stamp>ab007011-2836-426f-a8e0-9fef1ff88865</Stamp> + /// <MetaInformation> + /// <MAC>some-mac-here</MAC> + /// <PasswordHistory> + /// <PasswordHistory> + /// <LoggedModifiedBy>LEVIATHAN\David</LoggedMo [rest of string was truncated]";. + /// + internal static string ssh_rdm_export { + get { + return ResourceManager.GetString("ssh_rdm_export", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0"?> + ///<ArrayOfConnection> + /// <Connection> + /// <ConnectionType>Telnet</ConnectionType> + /// <Description>Telnet description</Description> + /// <ID>8d77d3ac-b414-4b51-ac10-60304d63cc6f</ID> + /// <Name>telnet connection</Name> + /// <OpenEmbedded>true</OpenEmbedded> + /// <Stamp>08aaa9ff-3583-4dc0-8622-3a0f4d37a7c4</Stamp> + /// <MetaInformation> + /// <MAC>some-mac-here</MAC> + /// <PasswordHistory> + /// <PasswordHistory> + /// <LoggedModifiedBy>LEVIATHAN\David</LoggedModifiedBy> + /// [rest of string was truncated]";. + /// + internal static string telnet_rdm_export { + get { + return ResourceManager.GetString("telnet_rdm_export", resourceCulture); + } + } + /// /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-16"?> ///<!-- ****************************************************************--> @@ -416,5 +481,48 @@ namespace mRemoteNGTests.Properties { return ResourceManager.GetString("update_portable", resourceCulture); } } + + /// + /// Looks up a localized string similar to <?xml version="1.0"?> + ///<ArrayOfConnection> + /// <Connection> + /// <ConnectionSubType>UltraVNC</ConnectionSubType> + /// <ConnectionType>VNC</ConnectionType> + /// <Description>This is a VNC description</Description> + /// <ID>16ff7dd6-2ac3-4e27-96cf-435b5bfe5a00</ID> + /// <Name>vnc connection</Name> + /// <OpenEmbedded>true</OpenEmbedded> + /// <Stamp>a44e3b58-2e14-47bf-8a79-0646b1e4ba46</Stamp> + /// <MetaInformation> + /// <MAC>some-mac-here</MAC> + /// <PasswordHistory> + /// <PasswordHistory> + /// <L [rest of string was truncated]";. + /// + internal static string vnc_rdm_export { + get { + return ResourceManager.GetString("vnc_rdm_export", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0"?> + ///<ArrayOfConnection> + /// <Connection> + /// <ConnectionSubType>FireFox</ConnectionSubType> + /// <ConnectionType>WebBrowser</ConnectionType> + /// <Description>Website description</Description> + /// <ID>65092747-6870-42c9-b8bc-35ec9fb5b3fb</ID> + /// <Name>website connection</Name> + /// <OpenEmbedded>true</OpenEmbedded> + /// <Stamp>d04a9abb-4d4c-4ba5-8ef8-1699bd84b734</Stamp> + /// <WebBrowserApplication>FireFox</WebBrowserApplication> + /// <WebBrowserUrl>https://www.google.com:8080</WebBrow [rest of string was truncated]";. + /// + internal static string website_rdm_export { + get { + return ResourceManager.GetString("website_rdm_export", resourceCulture); + } + } } } diff --git a/mRemoteNGTests/Properties/Resources.resx b/mRemoteNGTests/Properties/Resources.resx index af1fa4ec5..3b973b2e8 100644 --- a/mRemoteNGTests/Properties/Resources.resx +++ b/mRemoteNGTests/Properties/Resources.resx @@ -190,4 +190,19 @@ ..\Resources\update-portable.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + ..\Resources\rdp.rdm;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + + ..\Resources\ssh.rdm;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;iso-8859-1 + + + ..\Resources\telnet.rdm;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + + ..\Resources\vnc.rdm;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + + ..\Resources\website.rdm;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + \ No newline at end of file diff --git a/mRemoteNGTests/Resources/rdp.rdm b/mRemoteNGTests/Resources/rdp.rdm new file mode 100644 index 000000000..1be651b81 --- /dev/null +++ b/mRemoteNGTests/Resources/rdp.rdm @@ -0,0 +1,65 @@ + + + + WarnMe + true + 17 + RDPConfigured + true + This is a general description + true + true + true + true + true + 1f36e6f0-90ca-4607-b6ec-86227738486d + OnTheRemoteComputer + rdp connection + true + C256 + CurrentScreenSize + DoNotPlay + 22400949-a0b8-46c1-8f27-49232ac1fb27 + my.rdp.host.com:1234 + false + false + true + false + true + + mac-add-goes-here + + + LEVIATHAN\David + LEVIATHAN\David + 2020-01-26T14:40:54 + T2/JO7Aps+GMZG0vk5Jo8A== + + + + + true + domain1 + True + UserPassword + rddomain1 + rdhost1 + Explicit + kLaEwH4EDrZEaaK21HgqHg== + ModeDirect + rduser1 + instance-id-here + 6 + Default + true + true + HyperV + tW7TUhUf1KUzuzuAdANJvg== + AutoScale + false + true + user1 + RDP70 + + + \ No newline at end of file diff --git a/mRemoteNGTests/Resources/ssh.rdm b/mRemoteNGTests/Resources/ssh.rdm new file mode 100644 index 000000000..ceee6d63d --- /dev/null +++ b/mRemoteNGTests/Resources/ssh.rdm @@ -0,0 +1,36 @@ + + + + SSHShell + This is a linux host description + 44ae261f-0094-48a2-93cb-bc5abcfcc394 + ssh connection + true + ab007011-2836-426f-a8e0-9fef1ff88865 + + some-mac-here + + + LEVIATHAN\David + LEVIATHAN\David + 2020-01-26T20:46:26 + rV4oZM9LrfZ6SYHIlwTwvA== + + + LEVIATHAN\David + LEVIATHAN\David + 2020-01-26T14:42:38 + rV4oZM9LrfbUvSh5Y2lNDw== + + + + + mysshhost + 4321 + false + /et4Enc7v6HUQXJP6chuhA== + linuxuser1 + + + + \ No newline at end of file diff --git a/mRemoteNGTests/Resources/telnet.rdm b/mRemoteNGTests/Resources/telnet.rdm new file mode 100644 index 000000000..89fda33fb --- /dev/null +++ b/mRemoteNGTests/Resources/telnet.rdm @@ -0,0 +1,30 @@ + + + + Telnet + Telnet description + 8d77d3ac-b414-4b51-ac10-60304d63cc6f + telnet connection + true + 08aaa9ff-3583-4dc0-8622-3a0f4d37a7c4 + + some-mac-here + + + LEVIATHAN\David + LEVIATHAN\David + 2020-01-26T21:50:48 + T2/JO7Aps+GMZG0vk5Jo8A== + + + + + telnethost1 + 7648 + Custom + yLHh9AVRl8TilluPKnidMw== + user1 + + + + \ No newline at end of file diff --git a/mRemoteNGTests/Resources/vnc.rdm b/mRemoteNGTests/Resources/vnc.rdm new file mode 100644 index 000000000..519c5cac4 --- /dev/null +++ b/mRemoteNGTests/Resources/vnc.rdm @@ -0,0 +1,45 @@ + + + + UltraVNC + VNC + This is a VNC description + 16ff7dd6-2ac3-4e27-96cf-435b5bfe5a00 + vnc connection + true + a44e3b58-2e14-47bf-8a79-0646b1e4ba46 + + some-mac-here + + + LEVIATHAN\David + LEVIATHAN\David + 2020-01-26T21:12:44 + aHjuKKmVmbivKM+wovI3aA== + + + LEVIATHAN\David + LEVIATHAN\David + 2020-01-26T21:08:16 + uEGtwH/QfjSvKM+wovI3aA== + + + + + 4 + true + vnchost1 + 3 + windomain1 + bUWiWCi7qod7YQ5XXK5LFQ== + winuser1 + 9987 + Tight + proxyhost:7777 + oGzaL3q/dK17YQ5XXK5LFQ== + C8 + true + UltraVNC + + + \ No newline at end of file diff --git a/mRemoteNGTests/Resources/website.rdm b/mRemoteNGTests/Resources/website.rdm new file mode 100644 index 000000000..f0d6f2595 --- /dev/null +++ b/mRemoteNGTests/Resources/website.rdm @@ -0,0 +1,29 @@ + + + + FireFox + WebBrowser + Website description + 65092747-6870-42c9-b8bc-35ec9fb5b3fb + website connection + true + d04a9abb-4d4c-4ba5-8ef8-1699bd84b734 + FireFox + https://www.google.com:8080 + + + + LEVIATHAN\David + LEVIATHAN\David + 2020-01-26T22:01:26 + T2/JO7Aps+GMZG0vk5Jo8A== + + + + + domain1 + 24ifhl01e7Le3Aj8NxM4ww== + user1 + + + \ No newline at end of file diff --git a/mRemoteNGTests/mRemoteNGTests.csproj b/mRemoteNGTests/mRemoteNGTests.csproj index 05d4d1ba8..190cec07f 100644 --- a/mRemoteNGTests/mRemoteNGTests.csproj +++ b/mRemoteNGTests/mRemoteNGTests.csproj @@ -123,6 +123,7 @@ + @@ -254,6 +255,10 @@ + + + + @@ -273,6 +278,7 @@ + diff --git a/mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlExtensions.cs b/mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlExtensions.cs index 06520b1cd..5ef967d17 100644 --- a/mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlExtensions.cs +++ b/mRemoteV1/Config/Serializers/ConnectionSerializers/Xml/XmlExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Xml; +using System.Xml.Linq; namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml { @@ -44,5 +45,45 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml ? valueAsEnum : defaultValue; } + + public static string GetChildElementAsString(this XElement xmlNode, string elementName, string defaultValue = "") + { + var value = xmlNode?.Element(elementName)?.Value; + return value ?? defaultValue; + } + + public static bool GetChildElementAsBool(this XElement xmlNode, string elementName, bool defaultValue = false) + { + var value = xmlNode?.GetChildElementAsString(elementName); + if (string.IsNullOrWhiteSpace(value)) + return defaultValue; + + return bool.TryParse(value, out var valueAsBool) + ? valueAsBool + : defaultValue; + } + + public static int GetChildElementAsInt(this XElement xmlNode, string elementName, int defaultValue = 0) + { + var value = xmlNode?.GetChildElementAsString(elementName); + if (string.IsNullOrWhiteSpace(value)) + return defaultValue; + + return int.TryParse(value, out var valueAsBool) + ? valueAsBool + : defaultValue; + } + + public static T GetChildElementAsEnum(this XElement xmlNode, string elementName, T defaultValue = default(T)) + where T : struct + { + var value = xmlNode?.GetChildElementAsString(elementName); + if (string.IsNullOrWhiteSpace(value)) + return defaultValue; + + return Enum.TryParse(value, true, out var valueAsEnum) + ? valueAsEnum + : defaultValue; + } } } \ No newline at end of file diff --git a/mRemoteV1/Config/Serializers/MiscSerializers/RemoteDesktopManagerDeserializer.cs b/mRemoteV1/Config/Serializers/MiscSerializers/RemoteDesktopManagerDeserializer.cs new file mode 100644 index 000000000..48c7b34d3 --- /dev/null +++ b/mRemoteV1/Config/Serializers/MiscSerializers/RemoteDesktopManagerDeserializer.cs @@ -0,0 +1,366 @@ +using System; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using mRemoteNG.Config.Serializers.ConnectionSerializers.Xml; +using mRemoteNG.Connection; +using mRemoteNG.Connection.Protocol; +using mRemoteNG.Connection.Protocol.Http; +using mRemoteNG.Connection.Protocol.RDP; +using mRemoteNG.Connection.Protocol.VNC; +using mRemoteNG.Tree; +using mRemoteNG.Tree.Root; + +namespace mRemoteNG.Config.Serializers.MiscSerializers +{ + public class RemoteDesktopManagerDeserializer : IDeserializer + { + public ConnectionTreeModel Deserialize(string serializedData) + { + var connectionTreeModel = new ConnectionTreeModel(); + var root = new RootNodeInfo(RootNodeType.Connection); + connectionTreeModel.AddRootNode(root); + + var xdoc = XDocument.Parse(serializedData); + var descendants = xdoc.Root.Descendants("Connection"); + foreach (var descendant in descendants) + { + var connection = CreateConnectionFromXml(descendant); + root.AddChild(connection); + } + + return connectionTreeModel; + } + + private ConnectionInfo CreateConnectionFromXml(XElement xml) + { + var protocol = ParseProtocol(xml); + var protocolSpecificNode = GetProtocolSpecificNode(xml, protocol); + var metaInfoNode = xml.Element("MetaInformation"); + var connectionId = xml.GetChildElementAsString("ID", Guid.NewGuid().ToString()); + + + var connection = new ConnectionInfo(connectionId) + { + Name = xml.GetChildElementAsString("Name"), + Protocol = protocol, + Description = xml.GetChildElementAsString("Description"), + Username = protocolSpecificNode.GetChildElementAsString("UserName", + protocolSpecificNode.GetChildElementAsString("Username", + protocolSpecificNode.GetChildElementAsString("MsUser"))), + Domain = protocolSpecificNode.GetChildElementAsString("Domain", + protocolSpecificNode.GetChildElementAsString("MsDomain")), + Password = protocolSpecificNode.GetChildElementAsString("SafePassword"), + Resolution = ParseResolution(xml), + AutomaticResize = protocolSpecificNode.GetChildElementAsString("ScreenSizingMode") == "AutoScale", + RedirectSound = ParseSoundRedirection(xml), + Colors = ParseScreenColors(xml), + CacheBitmaps = !xml.GetChildElementAsBool("DisableBitmapCache"), + DisplayThemes = !xml.GetChildElementAsBool("DisableThemes"), + DisplayWallpaper = !xml.GetChildElementAsBool("DisableWallpaper"), + EnableDesktopComposition = xml.GetChildElementAsBool("DesktopComposition"), + EnableFontSmoothing = xml.GetChildElementAsBool("FontSmoothing"), + RedirectAudioCapture = protocolSpecificNode.GetChildElementAsBool("AudioCaptureRedirectionMode"), + RedirectClipboard = xml.GetChildElementAsBool("UsesClipboard", DefaultConnectionInfo.Instance.RedirectClipboard), + RedirectDiskDrives = xml.GetChildElementAsBool("UsesHardDrives", DefaultConnectionInfo.Instance.RedirectDiskDrives), + RedirectPrinters = xml.GetChildElementAsBool("UsesPrinters", DefaultConnectionInfo.Instance.RedirectPrinters), + RedirectPorts = xml.GetChildElementAsBool("UsesSerialPorts", DefaultConnectionInfo.Instance.RedirectPorts), + RedirectSmartCards = xml.GetChildElementAsBool("UsesSmartDevices", DefaultConnectionInfo.Instance.RedirectSmartCards), + RDPMinutesToIdleTimeout = xml.GetChildElementAsInt("AutomaticallyCloseInterval"), + MacAddress = metaInfoNode.GetChildElementAsString("MAC"), + RdpVersion = ParseRdpVersion(protocolSpecificNode), + RedirectKeys = xml.GetChildElementAsString("KeyboardHook") == "OnTheRemoteComputer", + UseConsoleSession = xml.GetChildElementAsBool("Console"), + RDPAuthenticationLevel = ParseAuthenticationLevel(xml), + UseCredSsp = protocolSpecificNode.GetChildElementAsBool("EnableCredSSPSupport"), + + RDGatewayUsageMethod = ParseRdpGatewayUsageMethod(protocolSpecificNode), + RDGatewayHostname = protocolSpecificNode.GetChildElementAsString("GatewayHostname"), + UseEnhancedMode = protocolSpecificNode.GetChildElementAsBool("UseEnhancedSessionMode"), + UseVmId = protocolSpecificNode.GetChildElementAsString("RDPType") == "HyperV", + VmId = protocolSpecificNode.GetChildElementAsString("HyperVInstanceID"), + + VNCEncoding = ParseVncEncoding(protocolSpecificNode), + VNCCompression = ParseVncCompressionLevel(protocolSpecificNode), + VNCAuthMode = DetermineVncAuthMode(protocolSpecificNode), + VNCViewOnly = protocolSpecificNode.GetChildElementAsBool("ViewOnly"), + RenderingEngine = ParseWebRenderingEngine(xml), + }; + + SetHostnameAndPort(connection, xml, protocolSpecificNode); + SetRdpGatewayCredentials(connection, protocolSpecificNode); + SetVncProxyHostAndPort(connection, protocolSpecificNode); + + return connection; + } + + private ProtocolType ParseProtocol(XElement xml) + { + switch (xml.GetChildElementAsString("ConnectionType")) + { + case "RDPConfigured": + return ProtocolType.RDP; + case "SSHShell": + return ProtocolType.SSH2; + case "VNC": + return ProtocolType.VNC; + case "Telnet": + return ProtocolType.Telnet; + case "WebBrowser": + return xml.GetChildElementAsString("WebBrowserUrl").StartsWith("http://") + ? ProtocolType.HTTP + : ProtocolType.HTTPS; + default: + throw new Exception(); + } + } + + private XElement GetProtocolSpecificNode(XElement xml, ProtocolType protocol) + { + switch (protocol) + { + case ProtocolType.RDP: + return xml.Element("RDP"); + case ProtocolType.SSH1: + case ProtocolType.SSH2: + case ProtocolType.Telnet: + return xml.Element("Terminal"); + case ProtocolType.VNC: + return xml.Element("VNC"); + case ProtocolType.HTTP: + case ProtocolType.HTTPS: + return xml.Element("Web"); + default: + throw new Exception(); + } + } + + private void SetHostnameAndPort(ConnectionInfo connection, XElement xml, XElement protocolSpecificNode) + { + var urlElement = xml.Element("Url") ?? xml.Element("WebBrowserUrl"); + if (urlElement != null) + { + var urlRegex = new Regex(@"((?https?)://)?(?[\w\.]+):?(?\d*)"); + var urlMatch = urlRegex.Match(urlElement.Value); + + connection.Hostname = urlMatch.Groups["host"]?.Value ?? string.Empty; + if (int.TryParse(urlMatch.Groups["port"]?.Value, out var port)) + { + connection.Port = port; + } + } + else + { + connection.Hostname = protocolSpecificNode.GetChildElementAsString("Host"); + if (protocolSpecificNode.Element("HostPort") != null) + { + connection.Port = protocolSpecificNode.GetChildElementAsInt("HostPort"); + } + if (protocolSpecificNode.Element("Port") != null) + { + connection.Port = protocolSpecificNode.GetChildElementAsInt("Port"); + } + } + } + + private RDPResolutions ParseResolution(XElement xml) + { + var resolution = xml.GetChildElementAsString("ScreenSize"); + + // attempt to convert "R800x600" style descriptors + // to our "Res800x600" style enum + if (resolution.StartsWith("R")) + { + var fixedResolution = resolution.Replace("R", "Res"); + if (Enum.TryParse(fixedResolution, out var convertedRes)) + { + return convertedRes; + } + } + + switch (resolution) + { + case "FullScreen": + return RDPResolutions.Fullscreen; + case "CurrentWorkAreaSize": + case "CurrentScreenSize": + return RDPResolutions.FitToWindow; + } + + switch (xml.GetChildElementAsString("ScreenSizingMode")) + { + // This is confusing, but what they show as "Smart sizing" in the UI + // gets serialized to FitToWindow in the xml. + case "FitToWindow": + return RDPResolutions.SmartSize; + } + + return DefaultConnectionInfo.Instance.Resolution; + } + + private RDPSounds ParseSoundRedirection(XElement xml) + { + switch (xml.GetChildElementAsString("SoundHook")) + { + case "LeaveAtRemoteComputer": + return RDPSounds.LeaveAtRemoteComputer; + case "DoNotPlay": + return RDPSounds.DoNotPlay; + default: // there is no xml element when set to BringToThisComputer + return RDPSounds.BringToThisComputer; + } + } + + private RDPColors ParseScreenColors(XElement xml) + { + switch (xml.GetChildElementAsString("ScreenColor")) + { + case "C15Bits": + return RDPColors.Colors15Bit; + case "C16Bits": + return RDPColors.Colors16Bit; + case "C24Bits": + return RDPColors.Colors24Bit; + case "C32Bits": + return RDPColors.Colors32Bit; + case "C256": + return RDPColors.Colors256; + default: + return DefaultConnectionInfo.Instance.Colors; + } + } + + private RdpVersion ParseRdpVersion(XElement xml) + { + switch (xml.GetChildElementAsString("Version")) + { + case "RDP50": + case "RDP60": + case "RDP61": + return RdpVersion.Rdc6; + case "RDP70": + return RdpVersion.Rdc7; + case "RDP80": + case "RDP81": + return RdpVersion.Rdc8; + default: // no xml node = use latest + return RdpVersion.Highest; + } + } + + private AuthenticationLevel ParseAuthenticationLevel(XElement xml) + { + switch (xml.GetChildElementAsString("AuthentificationLevel")) + { + case "WarnMe": + return AuthenticationLevel.WarnOnFailedAuth; + case "DontConnect": + return AuthenticationLevel.AuthRequired; + default: // "Connect and don't warn me" + return AuthenticationLevel.NoAuth; + } + } + + private RDGatewayUsageMethod ParseRdpGatewayUsageMethod(XElement xml) + { + switch (xml.GetChildElementAsString("GatewayUsageMethod")) + { + case "ModeDirect": + return RDGatewayUsageMethod.Always; + case "NoneDetect": + return RDGatewayUsageMethod.Never; + default: // no xml node = detect + return RDGatewayUsageMethod.Detect; + } + } + + private void SetRdpGatewayCredentials(ConnectionInfo connection, XElement xml) + { + if (xml.GetChildElementAsString("GatewayCredentialsSource") == "Smartcard") + { + connection.RDGatewayUseConnectionCredentials = RDGatewayUseConnectionCredentials.SmartCard; + } + else + { + connection.RDGatewayUseConnectionCredentials = + xml.GetChildElementAsBool("PromptCredentialOnce") + ? RDGatewayUseConnectionCredentials.Yes + : RDGatewayUseConnectionCredentials.No; + } + + connection.RDGatewayUsername = xml.GetChildElementAsString("GatewayUserName"); + connection.RDGatewayDomain = xml.GetChildElementAsString("GatewayDomain"); + connection.RDGatewayPassword = xml.GetChildElementAsString("GatewaySafePassword"); + } + + private ProtocolVNC.Encoding ParseVncEncoding(XElement xml) + { + switch (xml.GetChildElementAsString("PreferredEncoding")) + { + case "CoRRE": + return ProtocolVNC.Encoding.EncCorre; + case "Hextile": + return ProtocolVNC.Encoding.EncHextile; + case "Raw": + return ProtocolVNC.Encoding.EncRaw; + case "RRE": + return ProtocolVNC.Encoding.EncRRE; + case "Tight": + return ProtocolVNC.Encoding.EncTight; + case "Zlib": + return ProtocolVNC.Encoding.EncZlib; + case "ZlibHEX": + return ProtocolVNC.Encoding.EncZLibHex; + case "ZRLE": + return ProtocolVNC.Encoding.EncZRLE; + default: + return DefaultConnectionInfo.Instance.VNCEncoding; + } + } + + private ProtocolVNC.Compression ParseVncCompressionLevel(XElement xml) + { + var compressionXml = xml.GetChildElementAsString("CustomCompressionLevel"); + return Enum.TryParse("Comp" + compressionXml, out var compression) + ? compression + : DefaultConnectionInfo.Instance.VNCCompression; + } + + private ProtocolVNC.AuthMode DetermineVncAuthMode(XElement xml) + { + return xml.Element("MsUser") != null + ? ProtocolVNC.AuthMode.AuthWin + : ProtocolVNC.AuthMode.AuthVNC; + } + + private void SetVncProxyHostAndPort(ConnectionInfo connection, XElement xml) + { + var proxy = xml.GetChildElementAsString("ProxyHost").Split(':'); + + if (proxy.Length >= 1) + { + connection.VNCProxyIP = proxy[0]; + } + + if (proxy.Length >= 2 && int.TryParse(proxy[1], out var port)) + { + connection.VNCProxyPort = port; + } + } + + private HTTPBase.RenderingEngine ParseWebRenderingEngine(XElement xml) + { + if (xml.GetChildElementAsString("ConnectionType") != "WebBrowser") + return DefaultConnectionInfo.Instance.RenderingEngine; + + switch (xml.GetChildElementAsString("ConnectionSubType")) + { + case "IE": + return HTTPBase.RenderingEngine.IE; + case "FireFox": + return HTTPBase.RenderingEngine.Gecko; + default: + return DefaultConnectionInfo.Instance.RenderingEngine; + } + } + } +} diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index 5a26a6c2a..ba332f8b6 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -203,6 +203,7 @@ +